refactor(List): start_corner is now direction (#673)

The previous name `start_corner` did not communicate clearly the intent of the method.
A new method `direction` and a new enum `ListDirection` were added.

`start_corner` is now deprecated
This commit is contained in:
Valentin271 2023-12-11 01:50:13 +01:00 committed by GitHub
parent 0576a8aa32
commit f767ea7d37
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 155 additions and 60 deletions

View file

@ -11,6 +11,7 @@ github with a [breaking change] label.
This is a quick summary of the sections below:
- Unreleased (0.24.1)
- `List::start_corner` is renamed to `List::direction`
- `List::new()` now accepts `IntoIterator<Item = Into<ListItem<'a>>>`
- `Table::new()` now requires specifying the widths
- `Table::widths()` now accepts `IntoIterator<Item = AsRef<Constraint>>`
@ -41,6 +42,29 @@ This is a quick summary of the sections below:
## Unreleased (v0.24.1)
### `List::start_corner` is renamed to `List::direction` ([#673])
[#673]: https://github.com/ratatui-org/ratatui/pull/673
Previously `List::start_corner` didn't communicate the intent of the method. It also used an
inadequate `Corner` enum. The method is now renamed `direction` and a new enum
`ListDirection` has been added.
```diff
- List::new(/* items */).start_corner(Corner::TopLeft);
- List::new(/* items */).start_corner(Corner::TopRight);
// This is not an error, BottomRight rendered top to bottom previously
- List::new(/* items */).start_corner(Corner::BottomRight);
// all becomes
+ List::new(/* items */).direction(ListDirection::TopToBottom);
```
```diff
- List::new(/* items */).start_corner(Corner::BottomLeft);
// becomes
+ List::new(/* items */).direction(ListDirection::BottomToTop);
```
### `List::new()` now accepts `IntoIterator<Item = Into<ListItem<'a>>>` ([#672])
[#672]: https://github.com/ratatui-org/ratatui/pull/672
@ -50,10 +74,10 @@ error for `IntoIterator`s with an indeterminate item (e.g. empty vecs).
E.g.
```rust
let list = List::new(vec![]);
```diff
- let list = List::new(vec![]);
// becomes
let list = List::default();
+ let list = List::default();
```
### The default `Tabs::highlight_style` is now `Style::new().reversed()` ([#635])
@ -76,23 +100,23 @@ widget in the default configuration would not show any indication of the selecte
Previously `Table`s could be constructed without widths. In almost all cases this is an error.
A new widths parameter is now mandatory on `Table::new()`. Existing code of the form:
```rust
Table::new(rows).widths(widths)
```diff
- Table::new(rows).widths(widths)
```
Should be updated to:
```rust
Table::new(rows, widths)
```diff
+ Table::new(rows, widths)
```
For ease of automated replacement in cases where the amount of code broken by this change is large
or complex, it may be convenient to replace `Table::new` with `Table::default().rows`.
```rust
Table::new(rows).block(block).widths(widths);
```diff
- Table::new(rows).block(block).widths(widths);
// becomes
Table::default().rows(rows).widths(widths)
+ Table::default().rows(rows).widths(widths)
```
### `Table::widths()` now accepts `IntoIterator<Item = AsRef<Constraint>>` ([#663])
@ -105,10 +129,10 @@ the `&`.
E.g.
```rust
let table = Table::new(rows).widths(&[Constraint::Length(1)]);
```diff
- let table = Table::new(rows).widths(&[Constraint::Length(1)]);
// becomes
let table = Table::new(rows).widths([Constraint::Length(1)]);
+ let table = Table::new(rows).widths([Constraint::Length(1)]);
```
### Layout::new() now accepts direction and constraint parameters ([#557])
@ -147,10 +171,10 @@ Applications can now set custom borders on a `Block` by calling `border_set()`.
`BorderType::line_symbols()` is renamed to `border_symbols()` and now returns a new struct
`symbols::border::Set`. E.g.:
```rust
let line_set: symbols::line::Set = BorderType::line_symbols(BorderType::Plain);
```diff
- let line_set: symbols::line::Set = BorderType::line_symbols(BorderType::Plain);
// becomes
let border_set: symbols::border::Set = BorderType::border_symbols(BorderType::Plain);
+ let border_set: symbols::border::Set = BorderType::border_symbols(BorderType::Plain);
```
### Generic `Backend` parameter removed from `Frame` ([#530])
@ -161,10 +185,10 @@ let border_set: symbols::border::Set = BorderType::border_symbols(BorderType::Pl
accept `Frame`. To migrate existing code, remove any generic parameters from code that uses an
instance of a Frame. E.g.:
```rust
fn ui<B: Backend>(frame: &mut Frame<B>) { ... }
```diff
- fn ui<B: Backend>(frame: &mut Frame<B>) { ... }
// becomes
fn ui(frame: Frame) { ... }
+ fn ui(frame: Frame) { ... }
```
### `Stylize` shorthands now consume rather than borrow `String` ([#466])
@ -176,13 +200,13 @@ new implementation of `Stylize` was added that returns a `Span<'static>`. This c
be consumed rather than borrowed. Existing code that expects to use the string after a call will no
longer compile. E.g.
```rust
let s = String::new("foo");
let span1 = s.red();
let span2 = s.blue(); // will no longer compile as s is consumed by the previous line
```diff
- let s = String::new("foo");
- let span1 = s.red();
- let span2 = s.blue(); // will no longer compile as s is consumed by the previous line
// becomes
let span1 = s.clone().red();
let span2 = s.blue();
+ let span1 = s.clone().red();
+ let span2 = s.blue();
```
### Deprecated `Spans` type removed (replaced with `Line`) ([#426])
@ -192,12 +216,12 @@ let span2 = s.blue();
`Spans` was replaced with `Line` in 0.21.0. `Buffer::set_spans` was replaced with
`Buffer::set_line`.
```rust
let spans = Spans::from(some_string_str_span_or_vec_span);
buffer.set_spans(0, 0, spans, 10);
```diff
- let spans = Spans::from(some_string_str_span_or_vec_span);
- buffer.set_spans(0, 0, spans, 10);
// becomes
let line - Line::from(some_string_str_span_or_vec_span);
buffer.set_line(0, 0, line, 10);
+ let line - Line::from(some_string_str_span_or_vec_span);
+ buffer.set_line(0, 0, line, 10);
```
## [v0.23.0](https://github.com/ratatui-org/ratatui/releases/tag/v0.23.0)
@ -208,10 +232,10 @@ buffer.set_line(0, 0, line, 10);
The track symbol of `Scrollbar` is now optional, this method now takes an optional value.
```rust
let scrollbar = Scrollbar::default().track_symbol("|");
```diff
- let scrollbar = Scrollbar::default().track_symbol("|");
// becomes
let scrollbar = Scrollbar::default().track_symbol(Some("|"));
+ let scrollbar = Scrollbar::default().track_symbol(Some("|"));
```
### `Scrollbar` symbols moved to `symbols::scrollbar` and `widgets::scrollbar` module is private ([#330])
@ -222,10 +246,10 @@ The symbols for defining scrollbars have been moved to the `symbols` module from
`widgets::scrollbar` module which is no longer public. To update your code update any imports to the
new module locations. E.g.:
```rust
use ratatui::{widgets::scrollbar::{Scrollbar, Set}};
```diff
- use ratatui::{widgets::scrollbar::{Scrollbar, Set}};
// becomes
use ratatui::{widgets::Scrollbar, symbols::scrollbar::Set}
+ use ratatui::{widgets::Scrollbar, symbols::scrollbar::Set}
```
### MSRV updated to 1.67 ([#361])
@ -259,13 +283,13 @@ The minimum supported rust version is now 1.65.0.
In order to support inline viewports, the unstable method `Terminal::with_options()` was stabilized
and `ViewPort` was changed from a struct to an enum.
```rust
```diff
let terminal = Terminal::with_options(backend, TerminalOptions {
viewport: Viewport::fixed(area),
- viewport: Viewport::fixed(area),
});
// becomes
let terminal = Terminal::with_options(backend, TerminalOptions {
viewport: Viewport::Fixed(area),
+ viewport: Viewport::Fixed(area),
});
```
@ -277,10 +301,10 @@ A new type `Masked` was introduced that implements `From<Text<'a>>`. This causes
previously did not need to use type annotations to fail to compile. To fix this, annotate or call
to_string() / to_owned() / as_str() on the value. E.g.:
```rust
let paragraph = Paragraph::new("".as_ref());
```diff
- let paragraph = Paragraph::new("".as_ref());
// becomes
let paragraph = Paragraph::new("".as_str());
+ let paragraph = Paragraph::new("".as_str());
```
### `Marker::Block` now renders as a block rather than a bar character ([#133])
@ -291,10 +315,10 @@ Code using the `Block` marker that previously rendered using a half block charac
renders using the full block character (`'█'`). A new marker variant`Bar` is introduced to replace
the existing code.
```rust
let canvas = Canvas::default().marker(Marker::Block);
```diff
- let canvas = Canvas::default().marker(Marker::Block);
// becomes
let canvas = Canvas::default().marker(Marker::Bar);
+ let canvas = Canvas::default().marker(Marker::Bar);
```
## [v0.20.0](https://github.com/ratatui-org/ratatui/releases/tag/v0.20.0)

View file

@ -273,6 +273,6 @@ fn ui(f: &mut Frame, app: &mut App) {
.collect();
let events_list = List::new(events)
.block(Block::default().borders(Borders::ALL).title("List"))
.start_corner(Corner::BottomLeft);
.direction(ListDirection::BottomToTop);
f.render_widget(events_list, chunks[1]);
}

View file

@ -46,7 +46,7 @@ pub use self::{
chart::{Axis, Chart, Dataset, GraphType},
clear::Clear,
gauge::{Gauge, LineGauge},
list::{List, ListItem, ListState},
list::{List, ListDirection, ListItem, ListState},
paragraph::{Paragraph, Wrap},
scrollbar::{ScrollDirection, Scrollbar, ScrollbarOrientation, ScrollbarState},
sparkline::{RenderDirection, Sparkline},

View file

@ -1,4 +1,5 @@
#![warn(missing_docs)]
use strum::{Display, EnumString};
use unicode_width::UnicodeWidthStr;
use crate::{
@ -360,7 +361,7 @@ where
/// - [`List::highlight_symbol`] sets the symbol to be displayed in front of the selected item.
/// - [`List::repeat_highlight_symbol`] sets whether to repeat the symbol and style over selected
/// multi-line items
/// - [`List::start_corner`] sets the list direction
/// - [`List::direction`] sets the list direction
///
/// # Examples
///
@ -375,7 +376,7 @@ where
/// .highlight_style(Style::default().add_modifier(Modifier::ITALIC))
/// .highlight_symbol(">>")
/// .repeat_highlight_symbol(true)
/// .start_corner(Corner::TopLeft);
/// .direction(ListDirection::BottomToTop);
///
/// frame.render_widget(list, area);
/// # }
@ -404,8 +405,8 @@ pub struct List<'a> {
items: Vec<ListItem<'a>>,
/// Style used as a base style for the widget
style: Style,
/// List display direction, *top to bottom* or *bottom to top*
start_corner: Corner,
/// List display direction
direction: ListDirection,
/// Style used to render selected item
highlight_style: Style,
/// Symbol in front of the selected item (Shift all items to the right)
@ -416,6 +417,20 @@ pub struct List<'a> {
highlight_spacing: HighlightSpacing,
}
/// Defines the direction in which the list will be rendered.
///
/// If there are too few items to fill the screen, the list will stick to the starting edge.
///
/// See [`List::direction`].
#[derive(Debug, Default, Display, EnumString, Clone, Copy, Eq, PartialEq, Hash)]
pub enum ListDirection {
/// The first value is on the top, going to the bottom
#[default]
TopToBottom,
/// The first value is on the bottom, going to the top.
BottomToTop,
}
impl<'a> List<'a> {
/// Creates a new list from [`ListItem`]s
///
@ -458,11 +473,8 @@ impl<'a> List<'a> {
block: None,
style: Style::default(),
items: items.into_iter().map(|i| i.into()).collect(),
start_corner: Corner::TopLeft,
highlight_style: Style::default(),
highlight_symbol: None,
repeat_highlight_symbol: false,
highlight_spacing: HighlightSpacing::default(),
direction: ListDirection::default(),
..Self::default()
}
}
@ -622,10 +634,33 @@ impl<'a> List<'a> {
self
}
/// Defines the list direction (up or down)
///
/// Defines if the `List` is displayed *top to bottom* (default) or *bottom to top*.
/// If there is too few items to fill the screen, the list will stick to the starting edge.
///
/// This is a fluent setter method which must be chained or used as it consumes self
///
/// # Example
///
/// Bottom to top
///
/// ```rust
/// # use ratatui::{prelude::*, widgets::*};
/// # let items = vec!["Item 1"];
/// let list = List::new(items).direction(ListDirection::BottomToTop);
/// ```
#[must_use = "method moves the value of self and returns the modified value"]
pub fn direction(mut self, direction: ListDirection) -> List<'a> {
self.direction = direction;
self
}
/// Defines the list direction (up or down)
///
/// Defines if the `List` is displayed *top to bottom* (default) or *bottom to top*. Use
/// [`Corner::BottomLeft`] to go *bottom to top*. **Any** other variant will go *top to bottom*.
/// If there is too few items to fill the screen, the list will stick to the starting edge.
///
/// This is set to [`Corner::TopLeft`] by default.
///
@ -654,9 +689,13 @@ impl<'a> List<'a> {
/// let list = List::new(items).start_corner(Corner::BottomLeft);
/// ```
#[must_use = "method moves the value of self and returns the modified value"]
pub fn start_corner(mut self, corner: Corner) -> List<'a> {
self.start_corner = corner;
self
#[deprecated(since = "0.25.0", note = "You should use `List::direction` instead.")]
pub fn start_corner(self, corner: Corner) -> List<'a> {
if corner == Corner::BottomLeft {
self.direction(ListDirection::BottomToTop)
} else {
self.direction(ListDirection::TopToBottom)
}
}
/// Returns the number of [`ListItem`]s in the list
@ -746,7 +785,7 @@ impl<'a> StatefulWidget for List<'a> {
.skip(state.offset)
.take(end - start)
{
let (x, y) = if self.start_corner == Corner::BottomLeft {
let (x, y) = if self.direction == ListDirection::BottomToTop {
current_height += item.height() as u16;
(list_area.left(), list_area.bottom() - current_height)
} else {
@ -1488,9 +1527,40 @@ mod tests {
);
}
#[test]
fn test_list_direction_top_to_bottom() {
let items = list_items(vec!["Item 0", "Item 1", "Item 2"]);
let list = List::new(items).direction(ListDirection::TopToBottom);
let buffer = render_widget(list, 10, 5);
let expected = Buffer::with_lines(vec![
"Item 0 ",
"Item 1 ",
"Item 2 ",
" ",
" ",
]);
assert_buffer_eq!(buffer, expected);
}
#[test]
fn test_list_direction_bottom_to_top() {
let items = list_items(vec!["Item 0", "Item 1", "Item 2"]);
let list = List::new(items).direction(ListDirection::BottomToTop);
let buffer = render_widget(list, 10, 5);
let expected = Buffer::with_lines(vec![
" ",
" ",
"Item 2 ",
"Item 1 ",
"Item 0 ",
]);
assert_buffer_eq!(buffer, expected);
}
#[test]
fn test_list_start_corner_top_left() {
let items = list_items(vec!["Item 0", "Item 1", "Item 2"]);
#[allow(deprecated)] // For start_corner
let list = List::new(items).start_corner(Corner::TopLeft);
let buffer = render_widget(list, 10, 5);
let expected = Buffer::with_lines(vec![
@ -1506,6 +1576,7 @@ mod tests {
#[test]
fn test_list_start_corner_bottom_left() {
let items = list_items(vec!["Item 0", "Item 1", "Item 2"]);
#[allow(deprecated)] // For start_corner
let list = List::new(items).start_corner(Corner::BottomLeft);
let buffer = render_widget(list, 10, 5);
let expected = Buffer::with_lines(vec![