fix(table)!: Add widths parameter to new() (#664)

This prevents creating a table that doesn't actually render anything.

Fixes: https://github.com/ratatui-org/ratatui/issues/537

BREAKING CHANGE: Table::new() now takes an additional widths parameter.
This commit is contained in:
Josh McKinney 2023-12-04 15:22:52 -08:00 committed by GitHub
parent 91c67eb100
commit 37c70dbb8e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 306 additions and 226 deletions

View file

@ -11,7 +11,8 @@ github with a [breaking change] label.
This is a quick summary of the sections below: This is a quick summary of the sections below:
- Unreleased (0.24.1) - Unreleased (0.24.1)
- `Table::widths()` now accepts `AsRef<[Constraint]>` - `Table::new()` now requires specifying the widths
-`Table::widths()` now accepts `IntoIterator<Item = AsRef<Constraint>>`
- Layout::new() now accepts direction and constraint parameters - Layout::new() now accepts direction and constraint parameters
- The default `Tabs::highlight_style` is now `Style::new().reversed()` - The default `Tabs::highlight_style` is now `Style::new().reversed()`
@ -46,9 +47,39 @@ widget in the default configuration would not show any indication of the selecte
[#635]: https://github.com/ratatui-org/ratatui/pull/635 [#635]: https://github.com/ratatui-org/ratatui/pull/635
### `Table::widths()` now accepts `AsRef<[Constraint]>` ([#628]) ### The default `Tabs::highlight_style` is now `Style::new().reversed()` ([#635])
[#628]: https://github.com/ratatui-org/ratatui/pull/628 Previously the default highlight style for tabs was `Style::default()`, which meant that a `Tabs`
widget in the default configuration would not show any indication of the selected tab.
### `Table::new()` now requires specifying the widths of the columrs (#664)
Previously `Table`s could be constructed without widths. In almost all cases this is an error.
A new widths parameter is now manadatory on `Table::new()`. Existing code of the form:
```rust
Table::new(rows).widths(widths)
```
Should be updated to:
```rust
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);
// becomes
Table::default().rows(rows).widths(widths)
```
### `Table::widths()` now accepts `IntoIterator<Item = AsRef<Constraint>>` ([#663])
[#663]: https://github.com/ratatui-org/ratatui/pull/663
Previously `Table::widths()` took a slice (`&'a [Constraint]`). This change will introduce clippy Previously `Table::widths()` took a slice (`&'a [Constraint]`). This change will introduce clippy
`needless_borrow` warnings for places where slices are passed to this method. To fix these, remove `needless_borrow` warnings for places where slices are passed to this method. To fix these, remove

View file

@ -289,18 +289,20 @@ fn draw_second_tab(f: &mut Frame, app: &mut App, area: Rect) {
}; };
Row::new(vec![s.name, s.location, s.status]).style(style) Row::new(vec![s.name, s.location, s.status]).style(style)
}); });
let table = Table::new(rows) let table = Table::new(
.header( rows,
Row::new(vec!["Server", "Location", "Status"]) [
.style(Style::default().fg(Color::Yellow))
.bottom_margin(1),
)
.block(Block::default().title("Servers").borders(Borders::ALL))
.widths([
Constraint::Length(15), Constraint::Length(15),
Constraint::Length(15), Constraint::Length(15),
Constraint::Length(10), Constraint::Length(10),
]); ],
)
.header(
Row::new(vec!["Server", "Location", "Status"])
.style(Style::default().fg(Color::Yellow))
.bottom_margin(1),
)
.block(Block::default().title("Servers").borders(Borders::ALL));
f.render_widget(table, chunks[0]); f.render_widget(table, chunks[0]);
let map = Canvas::default() let map = Canvas::default()
@ -393,12 +395,14 @@ fn draw_third_tab(f: &mut Frame, _app: &mut App, area: Rect) {
Row::new(cells) Row::new(cells)
}) })
.collect(); .collect();
let table = Table::new(items) let table = Table::new(
.block(Block::default().title("Colors").borders(Borders::ALL)) items,
.widths([ [
Constraint::Ratio(1, 3), Constraint::Ratio(1, 3),
Constraint::Ratio(1, 3), Constraint::Ratio(1, 3),
Constraint::Ratio(1, 3), Constraint::Ratio(1, 3),
]); ],
)
.block(Block::default().title("Colors").borders(Borders::ALL));
f.render_widget(table, chunks[0]); f.render_widget(table, chunks[0]);
} }

View file

@ -146,10 +146,9 @@ fn render_ingredients(selected_row: usize, area: Rect, buf: &mut Buffer) {
let rows = INGREDIENTS.iter().map(|&i| i.into()).collect_vec(); let rows = INGREDIENTS.iter().map(|&i| i.into()).collect_vec();
let theme = THEME.recipe; let theme = THEME.recipe;
StatefulWidget::render( StatefulWidget::render(
Table::new(rows) Table::new(rows, [Constraint::Length(7), Constraint::Length(30)])
.block(Block::new().style(theme.ingredients)) .block(Block::new().style(theme.ingredients))
.header(Row::new(vec!["Qty", "Ingredient"]).style(theme.ingredients_header)) .header(Row::new(vec!["Qty", "Ingredient"]).style(theme.ingredients_header))
.widths([Constraint::Length(7), Constraint::Length(30)])
.highlight_style(Style::new().light_yellow()), .highlight_style(Style::new().light_yellow()),
area, area,
buf, buf,

View file

@ -50,9 +50,8 @@ fn render_hops(selected_row: usize, area: Rect, buf: &mut Buffer) {
.title_alignment(Alignment::Center) .title_alignment(Alignment::Center)
.padding(Padding::new(1, 1, 1, 1)); .padding(Padding::new(1, 1, 1, 1));
StatefulWidget::render( StatefulWidget::render(
Table::new(rows) Table::new(rows, [Constraint::Max(100), Constraint::Length(15)])
.header(Row::new(vec!["Host", "Address"]).set_style(THEME.traceroute.header)) .header(Row::new(vec!["Host", "Address"]).set_style(THEME.traceroute.header))
.widths([Constraint::Max(100), Constraint::Length(15)])
.highlight_style(THEME.traceroute.selected) .highlight_style(THEME.traceroute.selected)
.block(block), .block(block),
area, area,

View file

@ -137,15 +137,17 @@ fn ui(f: &mut Frame, app: &mut App) {
let cells = item.iter().map(|c| Cell::from(*c)); let cells = item.iter().map(|c| Cell::from(*c));
Row::new(cells).height(height as u16).bottom_margin(1) Row::new(cells).height(height as u16).bottom_margin(1)
}); });
let t = Table::new(rows) let t = Table::new(
.header(header) rows,
.block(Block::default().borders(Borders::ALL).title("Table")) [
.highlight_style(selected_style)
.highlight_symbol(">> ")
.widths([
Constraint::Percentage(50), Constraint::Percentage(50),
Constraint::Max(30), Constraint::Max(30),
Constraint::Min(10), Constraint::Min(10),
]); ],
)
.header(header)
.block(Block::default().borders(Borders::ALL).title("Table"))
.highlight_style(selected_style)
.highlight_symbol(">> ");
f.render_stateful_widget(t, rects[0], &mut app.state); f.render_stateful_widget(t, rects[0], &mut app.state);
} }

View file

@ -204,28 +204,33 @@ impl HighlightSpacing {
/// ```rust /// ```rust
/// use ratatui::{prelude::*, widgets::*}; /// use ratatui::{prelude::*, widgets::*};
/// ///
/// Table::new(vec![ /// Table::new(
/// // Row can be created from simple strings. /// vec![
/// Row::new(vec!["Row11", "Row12", "Row13"]), /// // Row can be created from simple strings.
/// // You can style the entire row. /// Row::new(vec!["Row11", "Row12", "Row13"]),
/// Row::new(vec!["Row21", "Row22", "Row23"]).style(Style::default().fg(Color::Blue)), /// // You can style the entire row.
/// // If you need more control over the styling you may need to create Cells directly /// Row::new(vec!["Row21", "Row22", "Row23"]).style(Style::default().fg(Color::Blue)),
/// Row::new(vec![ /// // If you need more control over the styling you may need to create Cells directly
/// Cell::from("Row31"), /// Row::new(vec![
/// Cell::from("Row32").style(Style::default().fg(Color::Yellow)), /// Cell::from("Row31"),
/// Cell::from(Line::from(vec![ /// Cell::from("Row32").style(Style::default().fg(Color::Yellow)),
/// Span::raw("Row"), /// Cell::from(Line::from(vec![
/// Span::styled("33", Style::default().fg(Color::Green)) /// Span::raw("Row"),
/// ])), /// Span::styled("33", Style::default().fg(Color::Green))
/// ]), /// ])),
/// // If a Row need to display some content over multiple lines, you just have to change /// ]),
/// // its height. /// // If a Row need to display some content over multiple lines, you just have to change
/// Row::new(vec![ /// // its height.
/// Cell::from("Row\n41"), /// Row::new(vec![
/// Cell::from("Row\n42"), /// Cell::from("Row\n41"),
/// Cell::from("Row\n43"), /// Cell::from("Row\n42"),
/// ]).height(2), /// Cell::from("Row\n43"),
/// ]) /// ]).height(2),
/// ],
/// // Columns widths are constrained in the same way as Layout...
/// [Constraint::Length(5), Constraint::Length(5), Constraint::Length(10)])
/// // ...and they can be separated by a fixed spacing.
/// .column_spacing(1)
/// // You can set the style of the entire Table. /// // You can set the style of the entire Table.
/// .style(Style::default().fg(Color::White)) /// .style(Style::default().fg(Color::White))
/// // It has an optional header, which is simply a Row always visible at the top. /// // It has an optional header, which is simply a Row always visible at the top.
@ -238,10 +243,6 @@ impl HighlightSpacing {
/// ) /// )
/// // As any other widget, a Table can be wrapped in a Block. /// // As any other widget, a Table can be wrapped in a Block.
/// .block(Block::default().title("Table")) /// .block(Block::default().title("Table"))
/// // Columns widths are constrained in the same way as Layout...
/// .widths(&[Constraint::Length(5), Constraint::Length(5), Constraint::Length(10)])
/// // ...and they can be separated by a fixed spacing.
/// .column_spacing(1)
/// // If you wish to highlight a row in any specific way when it is selected... /// // If you wish to highlight a row in any specific way when it is selected...
/// .highlight_style(Style::default().add_modifier(Modifier::BOLD)) /// .highlight_style(Style::default().add_modifier(Modifier::BOLD))
/// // ...and potentially show a symbol in front of the selection. /// // ...and potentially show a symbol in front of the selection.
@ -275,31 +276,41 @@ impl<'a> Table<'a> {
/// Creates a new [`Table`] widget with the given rows. /// Creates a new [`Table`] widget with the given rows.
/// ///
/// The `rows` parameter is a Vector of [`Row`], this holds the data to be displayed by the /// The `rows` parameter is a Vector of [`Row`], this holds the data to be displayed by the
/// table /// table.
///
/// The `widths` parameter is an array (or any other type that implements IntoIterator) of
/// [`Constraint`]s, this holds the widths of each column. This parameter was added in 0.25.0.
/// ///
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// # use ratatui::{prelude::*, widgets::*}; /// # use ratatui::{prelude::*, widgets::*};
/// let table = Table::new(vec![ /// let table = Table::new(
/// Row::new(vec![ /// vec![
/// Cell::from("Cell1"), /// Row::new(vec![
/// Cell::from("Cell2") /// Cell::from("Cell1"),
/// ]), /// Cell::from("Cell2")
/// Row::new(vec![ /// ]),
/// Cell::from("Cell3"), /// Row::new(vec![
/// Cell::from("Cell4") /// Cell::from("Cell3"),
/// ]), /// Cell::from("Cell4")
/// ]); /// ]),
/// ],
/// [Constraint::Length(5), Constraint::Length(5)]
/// );
/// ``` /// ```
pub fn new<T>(rows: T) -> Self pub fn new<R, C>(rows: R, widths: C) -> Self
where where
T: IntoIterator<Item = Row<'a>>, R: IntoIterator<Item = Row<'a>>,
C: IntoIterator,
C::Item: AsRef<Constraint>,
{ {
let widths = widths.into_iter().map(|c| *c.as_ref()).collect_vec();
ensure_percentages_less_than_100(&widths);
Self { Self {
block: None, block: None,
style: Style::default(), style: Style::default(),
widths: vec![], widths,
column_spacing: 1, column_spacing: 1,
highlight_style: Style::default(), highlight_style: Style::default(),
highlight_symbol: None, highlight_symbol: None,
@ -319,16 +330,20 @@ impl<'a> Table<'a> {
/// ///
/// ```rust /// ```rust
/// # use ratatui::{prelude::*, widgets::*}; /// # use ratatui::{prelude::*, widgets::*};
/// let table = Table::new(vec![ /// let table = Table::new(
/// Row::new(vec![ /// vec![
/// Cell::from("Cell1"), /// Row::new(vec![
/// Cell::from("Cell2") /// Cell::from("Cell1"),
/// ]), /// Cell::from("Cell2")
/// Row::new(vec![ /// ]),
/// Cell::from("Cell3"), /// Row::new(vec![
/// Cell::from("Cell4") /// Cell::from("Cell3"),
/// ]), /// Cell::from("Cell4")
/// ]).block(Block::default().title("Table")); /// ]),
/// ],
/// [Constraint::Length(5), Constraint::Length(5)]
/// )
/// .block(Block::default().title("Table"));
/// ``` /// ```
pub fn block(mut self, block: Block<'a>) -> Self { pub fn block(mut self, block: Block<'a>) -> Self {
self.block = Some(block); self.block = Some(block);
@ -344,12 +359,13 @@ impl<'a> Table<'a> {
/// ///
/// ```rust /// ```rust
/// # use ratatui::{prelude::*, widgets::*}; /// # use ratatui::{prelude::*, widgets::*};
/// let table = Table::new(vec![ /// let table = Table::new(
/// Row::new(vec![ /// vec![
/// Cell::from("Cell1"), /// Row::new(vec![Cell::from("Cell1"), Cell::from("Cell2")])
/// Cell::from("Cell2") /// ],
/// ]) /// [Constraint::Length(20), Constraint::Length(20)]
/// ]).header( /// )
/// .header(
/// Row::new(vec![ /// Row::new(vec![
/// Cell::from("Header Cell 1"), /// Cell::from("Header Cell 1"),
/// Cell::from("Header Cell 2") /// Cell::from("Header Cell 2")
@ -370,13 +386,13 @@ impl<'a> Table<'a> {
/// ///
/// ```rust /// ```rust
/// # use ratatui::{prelude::*, widgets::*}; /// # use ratatui::{prelude::*, widgets::*};
/// let table = Table::new(vec![]).widths([Constraint::Length(5), Constraint::Length(5)]); /// let table = Table::default().widths([Constraint::Length(5), Constraint::Length(5)]);
/// let table = Table::new(vec![]).widths(&[Constraint::Length(5), Constraint::Length(5)]); /// let table = Table::default().widths(&[Constraint::Length(5), Constraint::Length(5)]);
/// ///
/// // widths could also be computed at runtime /// // widths could also be computed at runtime
/// let widths = vec![Constraint::Length(5), Constraint::Length(5)]; /// let widths = vec![Constraint::Length(5), Constraint::Length(5)];
/// let table = Table::new(vec![]).widths(widths.clone()); /// let table = Table::default().widths(widths.clone());
/// let table = Table::new(vec![]).widths(&widths); /// let table = Table::default().widths(&widths);
/// ``` /// ```
pub fn widths<I, C>(mut self, widths: I) -> Self pub fn widths<I, C>(mut self, widths: I) -> Self
where where
@ -384,14 +400,7 @@ impl<'a> Table<'a> {
C: AsRef<Constraint>, C: AsRef<Constraint>,
{ {
let widths = widths.into_iter().map(|c| *c.as_ref()).collect_vec(); let widths = widths.into_iter().map(|c| *c.as_ref()).collect_vec();
let between_0_and_100 = |&w| match w { ensure_percentages_less_than_100(&widths);
Constraint::Percentage(p) => p <= 100,
_ => true,
};
assert!(
widths.iter().all(between_0_and_100),
"Percentages should be between 0 and 100 inclusively."
);
self.widths = widths; self.widths = widths;
self self
} }
@ -491,6 +500,7 @@ impl<'a> Table<'a> {
/// space to the last column or to distribute it equally. /// space to the last column or to distribute it equally.
/// ///
/// # Examples /// # Examples
///
/// Create a table that needs at least 30 columns to display. Any extra space will be assigned /// Create a table that needs at least 30 columns to display. Any extra space will be assigned
/// to the last column. /// to the last column.
#[cfg_attr(feature = "unstable", doc = " ```")] #[cfg_attr(feature = "unstable", doc = " ```")]
@ -499,8 +509,7 @@ impl<'a> Table<'a> {
/// # use ratatui::layout::SegmentSize; /// # use ratatui::layout::SegmentSize;
/// # use ratatui::widgets::Table; /// # use ratatui::widgets::Table;
/// let widths = [Constraint::Min(10), Constraint::Min(10), Constraint::Min(10)]; /// let widths = [Constraint::Min(10), Constraint::Min(10), Constraint::Min(10)];
/// let table = Table::new([]) /// let table = Table::new([], widths)
/// .widths(widths)
/// .segment_size(SegmentSize::LastTakesRemainder); /// .segment_size(SegmentSize::LastTakesRemainder);
/// ``` /// ```
#[stability::unstable( #[stability::unstable(
@ -514,6 +523,17 @@ impl<'a> Table<'a> {
} }
} }
fn ensure_percentages_less_than_100(widths: &[Constraint]) {
let between_0_and_100 = |&w| match w {
Constraint::Percentage(p) => p <= 100,
_ => true,
};
assert!(
widths.iter().all(between_0_and_100),
"Percentages should be between 0 and 100 inclusively."
);
}
impl<'a> Styled for Table<'a> { impl<'a> Styled for Table<'a> {
type Item = Table<'a>; type Item = Table<'a>;
@ -710,30 +730,30 @@ mod tests {
#[test] #[test]
#[should_panic] #[should_panic]
fn table_invalid_percentages() { fn table_invalid_percentages() {
Table::new(vec![]).widths([Constraint::Percentage(110)]); Table::new(vec![], [Constraint::Percentage(110)]);
} }
#[test] #[test]
fn widths_conversions() { fn widths_conversions() {
let array = [Constraint::Percentage(100)]; let array = [Constraint::Percentage(100)];
let table = Table::new(vec![]).widths(array); let table = Table::new(vec![], array);
assert_eq!(table.widths, vec![Constraint::Percentage(100)], "array"); assert_eq!(table.widths, vec![Constraint::Percentage(100)], "array");
let array_ref = &[Constraint::Percentage(100)]; let array_ref = &[Constraint::Percentage(100)];
let table = Table::new(vec![]).widths(array_ref); let table = Table::new(vec![], array_ref);
assert_eq!(table.widths, vec![Constraint::Percentage(100)], "array ref"); assert_eq!(table.widths, vec![Constraint::Percentage(100)], "array ref");
let vec = vec![Constraint::Percentage(100)]; let vec = vec![Constraint::Percentage(100)];
let slice = vec.as_slice(); let slice = vec.as_slice();
let table = Table::new(vec![]).widths(slice); let table = Table::new(vec![], slice);
assert_eq!(table.widths, vec![Constraint::Percentage(100)], "slice"); assert_eq!(table.widths, vec![Constraint::Percentage(100)], "slice");
let vec = vec![Constraint::Percentage(100)]; let vec = vec![Constraint::Percentage(100)];
let table = Table::new(vec![]).widths(vec); let table = Table::new(vec![], vec);
assert_eq!(table.widths, vec![Constraint::Percentage(100)], "vec"); assert_eq!(table.widths, vec![Constraint::Percentage(100)], "vec");
let vec_ref = &vec![Constraint::Percentage(100)]; let vec_ref = &vec![Constraint::Percentage(100)];
let table = Table::new(vec![]).widths(vec_ref); let table = Table::new(vec![], vec_ref);
assert_eq!(table.widths, vec![Constraint::Percentage(100)], "vec ref"); assert_eq!(table.widths, vec![Constraint::Percentage(100)], "vec ref");
} }
@ -751,9 +771,7 @@ mod tests {
selection_width: u16, selection_width: u16,
expected: &[(u16, u16)], expected: &[(u16, u16)],
) { ) {
let table = Table::new(vec![]) let table = Table::new(vec![], constraints).segment_size(segment_size);
.segment_size(segment_size)
.widths(constraints);
let widths = table.get_columns_widths(available_width, selection_width); let widths = table.get_columns_widths(available_width, selection_width);
assert_eq!(widths, expected); assert_eq!(widths, expected);
@ -997,12 +1015,14 @@ mod tests {
#[test] #[test]
fn test_render_table_with_alignment() { fn test_render_table_with_alignment() {
let mut buf = Buffer::empty(Rect::new(0, 0, 20, 3)); let mut buf = Buffer::empty(Rect::new(0, 0, 20, 3));
let table = Table::new(vec![ let table = Table::new(
Row::new(vec![Line::from("Left").alignment(Alignment::Left)]), vec![
Row::new(vec![Line::from("Center").alignment(Alignment::Center)]), Row::new(vec![Line::from("Left").alignment(Alignment::Left)]),
Row::new(vec![Line::from("Right").alignment(Alignment::Right)]), Row::new(vec![Line::from("Center").alignment(Alignment::Center)]),
]) Row::new(vec![Line::from("Right").alignment(Alignment::Right)]),
.widths([Percentage(100)]); ],
[Percentage(100)],
);
Widget::render(table, Rect::new(0, 0, 20, 3), &mut buf); Widget::render(table, Rect::new(0, 0, 20, 3), &mut buf);
@ -1047,7 +1067,7 @@ mod tests {
#[test] #[test]
fn table_can_be_stylized() { fn table_can_be_stylized() {
assert_eq!( assert_eq!(
Table::new(vec![Row::new(vec![Cell::from("")])]) Table::new(vec![Row::new(vec![Cell::from("")])], [Percentage(100)])
.black() .black()
.on_white() .on_white()
.bold() .bold()

View file

@ -19,19 +19,21 @@ fn widgets_table_column_spacing_can_be_changed() {
terminal terminal
.draw(|f| { .draw(|f| {
let size = f.size(); let size = f.size();
let table = Table::new(vec![ let table = Table::new(
Row::new(vec!["Row11", "Row12", "Row13"]), vec![
Row::new(vec!["Row21", "Row22", "Row23"]), Row::new(vec!["Row11", "Row12", "Row13"]),
Row::new(vec!["Row31", "Row32", "Row33"]), Row::new(vec!["Row21", "Row22", "Row23"]),
Row::new(vec!["Row41", "Row42", "Row43"]), Row::new(vec!["Row31", "Row32", "Row33"]),
]) Row::new(vec!["Row41", "Row42", "Row43"]),
],
[
Constraint::Length(5),
Constraint::Length(5),
Constraint::Length(5),
],
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1)) .header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::default().borders(Borders::ALL)) .block(Block::default().borders(Borders::ALL))
.widths([
Constraint::Length(5),
Constraint::Length(5),
Constraint::Length(5),
])
.column_spacing(column_spacing); .column_spacing(column_spacing);
f.render_widget(table, size); f.render_widget(table, size);
}) })
@ -117,15 +119,17 @@ fn widgets_table_columns_widths_can_use_fixed_length_constraints() {
terminal terminal
.draw(|f| { .draw(|f| {
let size = f.size(); let size = f.size();
let table = Table::new(vec![ let table = Table::new(
Row::new(vec!["Row11", "Row12", "Row13"]), vec![
Row::new(vec!["Row21", "Row22", "Row23"]), Row::new(vec!["Row11", "Row12", "Row13"]),
Row::new(vec!["Row31", "Row32", "Row33"]), Row::new(vec!["Row21", "Row22", "Row23"]),
Row::new(vec!["Row41", "Row42", "Row43"]), Row::new(vec!["Row31", "Row32", "Row33"]),
]) Row::new(vec!["Row41", "Row42", "Row43"]),
],
widths,
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1)) .header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::default().borders(Borders::ALL)) .block(Block::default().borders(Borders::ALL));
.widths(widths);
f.render_widget(table, size); f.render_widget(table, size);
}) })
.unwrap(); .unwrap();
@ -206,15 +210,17 @@ fn widgets_table_columns_widths_can_use_percentage_constraints() {
terminal terminal
.draw(|f| { .draw(|f| {
let size = f.size(); let size = f.size();
let table = Table::new(vec![ let table = Table::new(
Row::new(vec!["Row11", "Row12", "Row13"]), vec![
Row::new(vec!["Row21", "Row22", "Row23"]), Row::new(vec!["Row11", "Row12", "Row13"]),
Row::new(vec!["Row31", "Row32", "Row33"]), Row::new(vec!["Row21", "Row22", "Row23"]),
Row::new(vec!["Row41", "Row42", "Row43"]), Row::new(vec!["Row31", "Row32", "Row33"]),
]) Row::new(vec!["Row41", "Row42", "Row43"]),
],
widths,
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1)) .header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::default().borders(Borders::ALL)) .block(Block::default().borders(Borders::ALL))
.widths(widths)
.column_spacing(0); .column_spacing(0);
f.render_widget(table, size); f.render_widget(table, size);
}) })
@ -313,15 +319,17 @@ fn widgets_table_columns_widths_can_use_mixed_constraints() {
terminal terminal
.draw(|f| { .draw(|f| {
let size = f.size(); let size = f.size();
let table = Table::new(vec![ let table = Table::new(
Row::new(vec!["Row11", "Row12", "Row13"]), vec![
Row::new(vec!["Row21", "Row22", "Row23"]), Row::new(vec!["Row11", "Row12", "Row13"]),
Row::new(vec!["Row31", "Row32", "Row33"]), Row::new(vec!["Row21", "Row22", "Row23"]),
Row::new(vec!["Row41", "Row42", "Row43"]), Row::new(vec!["Row31", "Row32", "Row33"]),
]) Row::new(vec!["Row41", "Row42", "Row43"]),
],
widths,
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1)) .header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::default().borders(Borders::ALL)) .block(Block::default().borders(Borders::ALL));
.widths(widths);
f.render_widget(table, size); f.render_widget(table, size);
}) })
.unwrap(); .unwrap();
@ -423,15 +431,17 @@ fn widgets_table_columns_widths_can_use_ratio_constraints() {
terminal terminal
.draw(|f| { .draw(|f| {
let size = f.size(); let size = f.size();
let table = Table::new(vec![ let table = Table::new(
Row::new(vec!["Row11", "Row12", "Row13"]), vec![
Row::new(vec!["Row21", "Row22", "Row23"]), Row::new(vec!["Row11", "Row12", "Row13"]),
Row::new(vec!["Row31", "Row32", "Row33"]), Row::new(vec!["Row21", "Row22", "Row23"]),
Row::new(vec!["Row41", "Row42", "Row43"]), Row::new(vec!["Row31", "Row32", "Row33"]),
]) Row::new(vec!["Row41", "Row42", "Row43"]),
],
widths,
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1)) .header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::default().borders(Borders::ALL)) .block(Block::default().borders(Borders::ALL))
.widths(widths)
.column_spacing(0); .column_spacing(0);
f.render_widget(table, size); f.render_widget(table, size);
}) })
@ -528,20 +538,22 @@ fn widgets_table_can_have_rows_with_multi_lines() {
terminal terminal
.draw(|f| { .draw(|f| {
let size = f.size(); let size = f.size();
let table = Table::new(vec![ let table = Table::new(
Row::new(vec!["Row11", "Row12", "Row13"]), vec![
Row::new(vec!["Row21", "Row22", "Row23"]).height(2), Row::new(vec!["Row11", "Row12", "Row13"]),
Row::new(vec!["Row31", "Row32", "Row33"]), Row::new(vec!["Row21", "Row22", "Row23"]).height(2),
Row::new(vec!["Row41", "Row42", "Row43"]).height(2), Row::new(vec!["Row31", "Row32", "Row33"]),
]) Row::new(vec!["Row41", "Row42", "Row43"]).height(2),
],
[
Constraint::Length(5),
Constraint::Length(5),
Constraint::Length(5),
],
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1)) .header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::default().borders(Borders::ALL)) .block(Block::default().borders(Borders::ALL))
.highlight_symbol(">> ") .highlight_symbol(">> ")
.widths([
Constraint::Length(5),
Constraint::Length(5),
Constraint::Length(5),
])
.column_spacing(1); .column_spacing(1);
f.render_stateful_widget(table, size, state); f.render_stateful_widget(table, size, state);
}) })
@ -622,21 +634,23 @@ fn widgets_table_enable_always_highlight_spacing() {
terminal terminal
.draw(|f| { .draw(|f| {
let size = f.size(); let size = f.size();
let table = Table::new(vec![ let table = Table::new(
Row::new(vec!["Row11", "Row12", "Row13"]), vec![
Row::new(vec!["Row21", "Row22", "Row23"]).height(2), Row::new(vec!["Row11", "Row12", "Row13"]),
Row::new(vec!["Row31", "Row32", "Row33"]), Row::new(vec!["Row21", "Row22", "Row23"]).height(2),
Row::new(vec!["Row41", "Row42", "Row43"]).height(2), Row::new(vec!["Row31", "Row32", "Row33"]),
]) Row::new(vec!["Row41", "Row42", "Row43"]).height(2),
],
[
Constraint::Length(5),
Constraint::Length(5),
Constraint::Length(5),
],
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1)) .header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::default().borders(Borders::ALL)) .block(Block::default().borders(Borders::ALL))
.highlight_symbol(">> ") .highlight_symbol(">> ")
.highlight_spacing(space) .highlight_spacing(space)
.widths([
Constraint::Length(5),
Constraint::Length(5),
Constraint::Length(5),
])
.column_spacing(1); .column_spacing(1);
f.render_stateful_widget(table, size, state); f.render_stateful_widget(table, size, state);
}) })
@ -756,28 +770,31 @@ fn widgets_table_can_have_elements_styled_individually() {
terminal terminal
.draw(|f| { .draw(|f| {
let size = f.size(); let size = f.size();
let table = Table::new(vec![ let table = Table::new(
Row::new(vec!["Row11", "Row12", "Row13"]).style(Style::default().fg(Color::Green)), vec![
Row::new(vec![ Row::new(vec!["Row11", "Row12", "Row13"])
Cell::from("Row21"), .style(Style::default().fg(Color::Green)),
Cell::from("Row22").style(Style::default().fg(Color::Yellow)), Row::new(vec![
Cell::from(Line::from(vec![ Cell::from("Row21"),
Span::raw("Row"), Cell::from("Row22").style(Style::default().fg(Color::Yellow)),
Span::styled("23", Style::default().fg(Color::Blue)), Cell::from(Line::from(vec![
])) Span::raw("Row"),
.style(Style::default().fg(Color::Red)), Span::styled("23", Style::default().fg(Color::Blue)),
]) ]))
.style(Style::default().fg(Color::LightGreen)), .style(Style::default().fg(Color::Red)),
]) ])
.style(Style::default().fg(Color::LightGreen)),
],
[
Constraint::Length(6),
Constraint::Length(6),
Constraint::Length(6),
],
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1)) .header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::default().borders(Borders::LEFT | Borders::RIGHT)) .block(Block::default().borders(Borders::LEFT | Borders::RIGHT))
.highlight_symbol(">> ") .highlight_symbol(">> ")
.highlight_style(Style::default().add_modifier(Modifier::BOLD)) .highlight_style(Style::default().add_modifier(Modifier::BOLD))
.widths([
Constraint::Length(6),
Constraint::Length(6),
Constraint::Length(6),
])
.column_spacing(1); .column_spacing(1);
f.render_stateful_widget(table, size, &mut state); f.render_stateful_widget(table, size, &mut state);
}) })
@ -831,15 +848,17 @@ fn widgets_table_should_render_even_if_empty() {
terminal terminal
.draw(|f| { .draw(|f| {
let size = f.size(); let size = f.size();
let table = Table::new(vec![]) let table = Table::new(
.header(Row::new(vec!["Head1", "Head2", "Head3"])) vec![],
.block(Block::default().borders(Borders::LEFT | Borders::RIGHT)) [
.widths([
Constraint::Length(6), Constraint::Length(6),
Constraint::Length(6), Constraint::Length(6),
Constraint::Length(6), Constraint::Length(6),
]) ],
.column_spacing(1); )
.header(Row::new(vec!["Head1", "Head2", "Head3"]))
.block(Block::default().borders(Borders::LEFT | Borders::RIGHT))
.column_spacing(1);
f.render_widget(table, size); f.render_widget(table, size);
}) })
.unwrap(); .unwrap();
@ -869,17 +888,19 @@ fn widgets_table_columns_dont_panic() {
// based on https://github.com/fdehau/tui-rs/issues/470#issuecomment-852562848 // based on https://github.com/fdehau/tui-rs/issues/470#issuecomment-852562848
let table1_width = 98; let table1_width = 98;
let table1 = Table::new(vec![Row::new(vec!["r1", "r2", "r3", "r4"])]) let table1 = Table::new(
.header(Row::new(vec!["h1", "h2", "h3", "h4"])) vec![Row::new(vec!["r1", "r2", "r3", "r4"])],
.block(Block::default().borders(Borders::ALL)) [
.highlight_symbol(">> ")
.column_spacing(1)
.widths([
Constraint::Percentage(15), Constraint::Percentage(15),
Constraint::Percentage(15), Constraint::Percentage(15),
Constraint::Percentage(25), Constraint::Percentage(25),
Constraint::Percentage(45), Constraint::Percentage(45),
]); ],
)
.header(Row::new(vec!["h1", "h2", "h3", "h4"]))
.block(Block::default().borders(Borders::ALL))
.highlight_symbol(">> ")
.column_spacing(1);
let mut state = TableState::default(); let mut state = TableState::default();
@ -899,21 +920,23 @@ fn widgets_table_should_clamp_offset_if_rows_are_removed() {
terminal terminal
.draw(|f| { .draw(|f| {
let size = f.size(); let size = f.size();
let table = Table::new(vec![ let table = Table::new(
Row::new(vec!["Row01", "Row02", "Row03"]), vec![
Row::new(vec!["Row11", "Row12", "Row13"]), Row::new(vec!["Row01", "Row02", "Row03"]),
Row::new(vec!["Row21", "Row22", "Row23"]), Row::new(vec!["Row11", "Row12", "Row13"]),
Row::new(vec!["Row31", "Row32", "Row33"]), Row::new(vec!["Row21", "Row22", "Row23"]),
Row::new(vec!["Row41", "Row42", "Row43"]), Row::new(vec!["Row31", "Row32", "Row33"]),
Row::new(vec!["Row51", "Row52", "Row53"]), Row::new(vec!["Row41", "Row42", "Row43"]),
]) Row::new(vec!["Row51", "Row52", "Row53"]),
],
[
Constraint::Length(5),
Constraint::Length(5),
Constraint::Length(5),
],
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1)) .header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::default().borders(Borders::ALL)) .block(Block::default().borders(Borders::ALL))
.widths([
Constraint::Length(5),
Constraint::Length(5),
Constraint::Length(5),
])
.column_spacing(1); .column_spacing(1);
f.render_stateful_widget(table, size, &mut state); f.render_stateful_widget(table, size, &mut state);
}) })
@ -935,15 +958,17 @@ fn widgets_table_should_clamp_offset_if_rows_are_removed() {
terminal terminal
.draw(|f| { .draw(|f| {
let size = f.size(); let size = f.size();
let table = Table::new(vec![Row::new(vec!["Row31", "Row32", "Row33"])]) let table = Table::new(
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1)) vec![Row::new(vec!["Row31", "Row32", "Row33"])],
.block(Block::default().borders(Borders::ALL)) [
.widths([
Constraint::Length(5), Constraint::Length(5),
Constraint::Length(5), Constraint::Length(5),
Constraint::Length(5), Constraint::Length(5),
]) ],
.column_spacing(1); )
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::default().borders(Borders::ALL))
.column_spacing(1);
f.render_stateful_widget(table, size, &mut state); f.render_stateful_widget(table, size, &mut state);
}) })
.unwrap(); .unwrap();