pub struct Table<'a> { /* private fields */ }
Expand description
A widget to display data in formatted columns.
A Table
is a collection of Row
s, each composed of Cell
s:
You can consutrct a Table
using either Table::new
or Table::default
and then chain
builder style methods to set the desired properties.
Make sure to call the Table::widths
method, otherwise the columns will all have a width of 0
and thus not be visible.
Table
implements Widget
and so it can be drawn using Frame::render_widget
.
Table
is also a StatefulWidget
, which means you can use it with TableState
to allow
the user to scroll through the rows and select one of them.
See the table example and the recipe and traceroute tabs in the demo2 example for a more in depth example of the various configuration options and for how to handle state.
Constructor methods
Table::new
creates a newTable
with the given rows.Table::default
creates an emptyTable
. You can then add rows usingTable::rows
.
Setter methods
These methods a fluent setters. They return a new Table
with the specified property set.
Table::rows
sets the rows of theTable
.Table::header
sets the header row of theTable
.Table::widths
sets the width constraints of each column.Table::column_spacing
sets the spacing between each column.Table::block
wraps the table in aBlock
widget.Table::style
sets the base style of the widget.Table::highlight_style
sets the style of the selected row.Table::highlight_symbol
sets the symbol to be displayed in front of the selected row.Table::highlight_spacing
sets when to show the highlight spacing.
Example
use ratatui::{prelude::*, widgets::*};
let rows = [Row::new(vec!["Cell1", "Cell2", "Cell3"])];
// Columns widths are constrained in the same way as Layout...
let widths = [
Constraint::Length(5),
Constraint::Length(5),
Constraint::Length(10),
];
let table = Table::new(rows, widths)
// ...and they can be separated by a fixed spacing.
.column_spacing(1)
// You can set the style of the entire Table.
.style(Style::new().blue())
// It has an optional header, which is simply a Row always visible at the top.
.header(
Row::new(vec!["Col1", "Col2", "Col3"])
.style(Style::new().bold())
// To add space between the header and the rest of the rows, specify the margin
.bottom_margin(1),
)
// As any other widget, a Table can be wrapped in a Block.
.block(Block::default().title("Table"))
// The selected row and its content can also be styled.
.highlight_style(Style::new().reversed())
// ...and potentially show a symbol in front of the selection.
.highlight_symbol(">>");
Rows can be created from an iterator of Cell
s. Each row can have an associated height,
bottom margin, and style. See Row
for more details.
// a Row can be created from simple strings.
let row = Row::new(vec!["Row11", "Row12", "Row13"]);
// You can style the entire row.
let row = Row::new(vec!["Row21", "Row22", "Row23"]).style(Style::new().red());
// If you need more control over the styling, create Cells directly
let row = Row::new(vec![
Cell::from("Row31"),
Cell::from("Row32").style(Style::default().fg(Color::Yellow)),
Cell::from(Line::from(vec![
Span::raw("Row"),
Span::styled("33", Style::default().fg(Color::Green)),
])),
]);
// If a Row need to display some content over multiple lines, specify the height.
let row = Row::new(vec![
Cell::from("Row\n41"),
Cell::from("Row\n42"),
Cell::from("Row\n43"),
])
.height(2);
Cells can be created from anything that can be converted to Text
. See Cell
for more
details.
Cell::from("simple string");
Cell::from("simple styled span".red());
Cell::from(Span::raw("raw span"));
Cell::from(Span::styled("styled span", Style::new().red()));
Cell::from(Line::from(vec![
Span::raw("a vec of "),
Span::styled("spans", Style::new().bold()),
]));
Cell::from(Text::from("text"));
Table
also implements the Styled
trait, which means you can use style shorthands from
the Stylize
trait to set the style of the widget more concisely.
use ratatui::{prelude::*, widgets::*};
let rows = [Row::new(vec!["Cell1", "Cell2", "Cell3"])];
let widths = [
Constraint::Length(5),
Constraint::Length(5),
Constraint::Length(10),
];
let table = Table::new(rows, widths).red().italic();
Stateful example
Table
is a StatefulWidget
, which means you can use it with TableState
to allow the
user to scroll through the rows and select one of them.
// Note: TableState should be stored in your application state (not constructed in your render
// method) so that the selected row is preserved across renders
let mut table_state = TableState::default();
let rows = [
Row::new(vec!["Row11", "Row12", "Row13"]),
Row::new(vec!["Row21", "Row22", "Row23"]),
Row::new(vec!["Row31", "Row32", "Row33"]),
];
let widths = [Constraint::Length(5), Constraint::Length(5), Constraint::Length(10)];
let table = Table::new(rows, widths)
.block(Block::default().title("Table"))
.highlight_style(Style::new().add_modifier(Modifier::REVERSED))
.highlight_symbol(">>");
frame.render_stateful_widget(table, area, &mut table_state);
Implementations§
source§impl<'a> Table<'a>
impl<'a> Table<'a>
sourcepub fn new<R, C>(rows: R, widths: C) -> Self
pub fn new<R, C>(rows: R, widths: C) -> Self
Creates a new Table
widget with the given rows.
The rows
parameter accepts any value that can be converted into an iterator of Row
s.
This includes arrays, slices, and Vec
s.
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
let rows = [
Row::new(vec!["Cell1", "Cell2"]),
Row::new(vec!["Cell3", "Cell4"]),
];
let widths = [Constraint::Length(5), Constraint::Length(5)];
let table = Table::new(rows, widths);
Examples found in repository?
144 145 146 147 148 149 150 151 152 153 154 155 156 157
fn render_ingredients(selected_row: usize, area: Rect, buf: &mut Buffer) {
let mut state = TableState::default().with_selected(Some(selected_row));
let rows = INGREDIENTS.iter().map(|&i| i.into()).collect_vec();
let theme = THEME.recipe;
StatefulWidget::render(
Table::new(rows, [Constraint::Length(7), Constraint::Length(30)])
.block(Block::new().style(theme.ingredients))
.header(Row::new(vec!["Qty", "Ingredient"]).style(theme.ingredients_header))
.highlight_style(Style::new().light_yellow()),
area,
buf,
&mut state,
);
}
More examples
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
fn render_hops(selected_row: usize, area: Rect, buf: &mut Buffer) {
let mut state = TableState::default().with_selected(Some(selected_row));
let rows = HOPS
.iter()
.map(|hop| Row::new(vec![hop.host, hop.address]))
.collect_vec();
let block = Block::default()
.title("Traceroute bad.horse".bold().white())
.title_alignment(Alignment::Center)
.padding(Padding::new(1, 1, 1, 1));
StatefulWidget::render(
Table::new(rows, [Constraint::Max(100), Constraint::Length(15)])
.header(Row::new(vec!["Host", "Address"]).set_style(THEME.traceroute.header))
.highlight_style(THEME.traceroute.selected)
.block(block),
area,
buf,
&mut state,
);
let mut scrollbar_state = ScrollbarState::default()
.content_length(HOPS.len())
.position(selected_row);
let area = Rect {
width: area.width + 1,
y: area.y + 3,
height: area.height - 4,
..area
};
Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalLeft)
.begin_symbol(None)
.end_symbol(None)
.track_symbol(None)
.thumb_symbol("▌")
.render(area, buf, &mut scrollbar_state);
}
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
fn ui(f: &mut Frame, app: &mut App) {
let rects = Layout::default()
.constraints([Constraint::Percentage(100)])
.split(f.size());
let selected_style = Style::default().add_modifier(Modifier::REVERSED);
let normal_style = Style::default().bg(Color::Blue);
let header_cells = ["Header1", "Header2", "Header3"]
.iter()
.map(|h| Cell::from(*h).style(Style::default().fg(Color::Red)));
let header = Row::new(header_cells)
.style(normal_style)
.height(1)
.bottom_margin(1);
let rows = app.items.iter().map(|item| {
let height = item
.iter()
.map(|content| content.chars().filter(|c| *c == '\n').count())
.max()
.unwrap_or(0)
+ 1;
let cells = item.iter().map(|c| Cell::from(*c));
Row::new(cells).height(height as u16).bottom_margin(1)
});
let t = Table::new(
rows,
[
Constraint::Percentage(50),
Constraint::Max(30),
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);
}
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
fn draw_second_tab(f: &mut Frame, app: &mut App, area: Rect) {
let chunks = Layout::default()
.constraints([Constraint::Percentage(30), Constraint::Percentage(70)])
.direction(Direction::Horizontal)
.split(area);
let up_style = Style::default().fg(Color::Green);
let failure_style = Style::default()
.fg(Color::Red)
.add_modifier(Modifier::RAPID_BLINK | Modifier::CROSSED_OUT);
let rows = app.servers.iter().map(|s| {
let style = if s.status == "Up" {
up_style
} else {
failure_style
};
Row::new(vec![s.name, s.location, s.status]).style(style)
});
let table = Table::new(
rows,
[
Constraint::Length(15),
Constraint::Length(15),
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]);
let map = Canvas::default()
.block(Block::default().title("World").borders(Borders::ALL))
.paint(|ctx| {
ctx.draw(&Map {
color: Color::White,
resolution: MapResolution::High,
});
ctx.layer();
ctx.draw(&Rectangle {
x: 0.0,
y: 30.0,
width: 10.0,
height: 10.0,
color: Color::Yellow,
});
ctx.draw(&Circle {
x: app.servers[2].coords.1,
y: app.servers[2].coords.0,
radius: 10.0,
color: Color::Green,
});
for (i, s1) in app.servers.iter().enumerate() {
for s2 in &app.servers[i + 1..] {
ctx.draw(&canvas::Line {
x1: s1.coords.1,
y1: s1.coords.0,
y2: s2.coords.0,
x2: s2.coords.1,
color: Color::Yellow,
});
}
}
for server in &app.servers {
let color = if server.status == "Up" {
Color::Green
} else {
Color::Red
};
ctx.print(
server.coords.1,
server.coords.0,
Span::styled("X", Style::default().fg(color)),
);
}
})
.marker(if app.enhanced_graphics {
symbols::Marker::Braille
} else {
symbols::Marker::Dot
})
.x_bounds([-180.0, 180.0])
.y_bounds([-90.0, 90.0]);
f.render_widget(map, chunks[1]);
}
fn draw_third_tab(f: &mut Frame, _app: &mut App, area: Rect) {
let chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)])
.split(area);
let colors = [
Color::Reset,
Color::Black,
Color::Red,
Color::Green,
Color::Yellow,
Color::Blue,
Color::Magenta,
Color::Cyan,
Color::Gray,
Color::DarkGray,
Color::LightRed,
Color::LightGreen,
Color::LightYellow,
Color::LightBlue,
Color::LightMagenta,
Color::LightCyan,
Color::White,
];
let items: Vec<Row> = colors
.iter()
.map(|c| {
let cells = vec![
Cell::from(Span::raw(format!("{c:?}: "))),
Cell::from(Span::styled("Foreground", Style::default().fg(*c))),
Cell::from(Span::styled("Background", Style::default().bg(*c))),
];
Row::new(cells)
})
.collect();
let table = Table::new(
items,
[
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]);
}
sourcepub fn rows<T>(self, rows: T) -> Selfwhere
T: IntoIterator<Item = Row<'a>>,
pub fn rows<T>(self, rows: T) -> Selfwhere
T: IntoIterator<Item = Row<'a>>,
Set the rows
The rows
parameter accepts any value that can be converted into an iterator of Row
s.
This includes arrays, slices, and Vec
s.
Warning
This method does not currently set the column widths. You will need to set them manually by
calling Table::widths
.
This is a fluent setter method which must be chained or used as it consumes self
Examples
let rows = [
Row::new(vec!["Cell1", "Cell2"]),
Row::new(vec!["Cell3", "Cell4"]),
];
let table = Table::default().rows(rows);
sourcepub fn header(self, header: Row<'a>) -> Self
pub fn header(self, header: Row<'a>) -> Self
Sets the header row
The header
parameter is a Row
which will be displayed at the top of the Table
This is a fluent setter method which must be chained or used as it consumes self
Examples
let header = Row::new(vec![
Cell::from("Header Cell 1"),
Cell::from("Header Cell 2"),
]);
let table = Table::default().header(header);
Examples found in repository?
144 145 146 147 148 149 150 151 152 153 154 155 156 157
fn render_ingredients(selected_row: usize, area: Rect, buf: &mut Buffer) {
let mut state = TableState::default().with_selected(Some(selected_row));
let rows = INGREDIENTS.iter().map(|&i| i.into()).collect_vec();
let theme = THEME.recipe;
StatefulWidget::render(
Table::new(rows, [Constraint::Length(7), Constraint::Length(30)])
.block(Block::new().style(theme.ingredients))
.header(Row::new(vec!["Qty", "Ingredient"]).style(theme.ingredients_header))
.highlight_style(Style::new().light_yellow()),
area,
buf,
&mut state,
);
}
More examples
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
fn render_hops(selected_row: usize, area: Rect, buf: &mut Buffer) {
let mut state = TableState::default().with_selected(Some(selected_row));
let rows = HOPS
.iter()
.map(|hop| Row::new(vec![hop.host, hop.address]))
.collect_vec();
let block = Block::default()
.title("Traceroute bad.horse".bold().white())
.title_alignment(Alignment::Center)
.padding(Padding::new(1, 1, 1, 1));
StatefulWidget::render(
Table::new(rows, [Constraint::Max(100), Constraint::Length(15)])
.header(Row::new(vec!["Host", "Address"]).set_style(THEME.traceroute.header))
.highlight_style(THEME.traceroute.selected)
.block(block),
area,
buf,
&mut state,
);
let mut scrollbar_state = ScrollbarState::default()
.content_length(HOPS.len())
.position(selected_row);
let area = Rect {
width: area.width + 1,
y: area.y + 3,
height: area.height - 4,
..area
};
Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalLeft)
.begin_symbol(None)
.end_symbol(None)
.track_symbol(None)
.thumb_symbol("▌")
.render(area, buf, &mut scrollbar_state);
}
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
fn ui(f: &mut Frame, app: &mut App) {
let rects = Layout::default()
.constraints([Constraint::Percentage(100)])
.split(f.size());
let selected_style = Style::default().add_modifier(Modifier::REVERSED);
let normal_style = Style::default().bg(Color::Blue);
let header_cells = ["Header1", "Header2", "Header3"]
.iter()
.map(|h| Cell::from(*h).style(Style::default().fg(Color::Red)));
let header = Row::new(header_cells)
.style(normal_style)
.height(1)
.bottom_margin(1);
let rows = app.items.iter().map(|item| {
let height = item
.iter()
.map(|content| content.chars().filter(|c| *c == '\n').count())
.max()
.unwrap_or(0)
+ 1;
let cells = item.iter().map(|c| Cell::from(*c));
Row::new(cells).height(height as u16).bottom_margin(1)
});
let t = Table::new(
rows,
[
Constraint::Percentage(50),
Constraint::Max(30),
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);
}
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
fn draw_second_tab(f: &mut Frame, app: &mut App, area: Rect) {
let chunks = Layout::default()
.constraints([Constraint::Percentage(30), Constraint::Percentage(70)])
.direction(Direction::Horizontal)
.split(area);
let up_style = Style::default().fg(Color::Green);
let failure_style = Style::default()
.fg(Color::Red)
.add_modifier(Modifier::RAPID_BLINK | Modifier::CROSSED_OUT);
let rows = app.servers.iter().map(|s| {
let style = if s.status == "Up" {
up_style
} else {
failure_style
};
Row::new(vec![s.name, s.location, s.status]).style(style)
});
let table = Table::new(
rows,
[
Constraint::Length(15),
Constraint::Length(15),
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]);
let map = Canvas::default()
.block(Block::default().title("World").borders(Borders::ALL))
.paint(|ctx| {
ctx.draw(&Map {
color: Color::White,
resolution: MapResolution::High,
});
ctx.layer();
ctx.draw(&Rectangle {
x: 0.0,
y: 30.0,
width: 10.0,
height: 10.0,
color: Color::Yellow,
});
ctx.draw(&Circle {
x: app.servers[2].coords.1,
y: app.servers[2].coords.0,
radius: 10.0,
color: Color::Green,
});
for (i, s1) in app.servers.iter().enumerate() {
for s2 in &app.servers[i + 1..] {
ctx.draw(&canvas::Line {
x1: s1.coords.1,
y1: s1.coords.0,
y2: s2.coords.0,
x2: s2.coords.1,
color: Color::Yellow,
});
}
}
for server in &app.servers {
let color = if server.status == "Up" {
Color::Green
} else {
Color::Red
};
ctx.print(
server.coords.1,
server.coords.0,
Span::styled("X", Style::default().fg(color)),
);
}
})
.marker(if app.enhanced_graphics {
symbols::Marker::Braille
} else {
symbols::Marker::Dot
})
.x_bounds([-180.0, 180.0])
.y_bounds([-90.0, 90.0]);
f.render_widget(map, chunks[1]);
}
sourcepub fn widths<I>(self, widths: I) -> Self
pub fn widths<I>(self, widths: I) -> Self
Set the widths of the columns
The widths
parameter accepts anything which be converted to an Iterator of Constraints
which can be an array, slice, Vec etc.
This is a fluent setter method which must be chained or used as it consumes self
Examples
let table = Table::default().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
let widths = [10, 10, 20].into_iter().map(|c| Constraint::Length(c));
let table = Table::default().widths(widths);
sourcepub fn column_spacing(self, spacing: u16) -> Self
pub fn column_spacing(self, spacing: u16) -> Self
Set the spacing between columns
This is a fluent setter method which must be chained or used as it consumes self
Examples
let table = Table::new(rows, widths).column_spacing(1);
sourcepub fn block(self, block: Block<'a>) -> Self
pub fn block(self, block: Block<'a>) -> Self
Wraps the table with a custom Block
widget.
The block
parameter is of type Block
. This holds the specified block to be
created around the Table
This is a fluent setter method which must be chained or used as it consumes self
Examples
let block = Block::default().title("Table").borders(Borders::ALL);
let table = Table::new(rows, widths).block(block);
Examples found in repository?
144 145 146 147 148 149 150 151 152 153 154 155 156 157
fn render_ingredients(selected_row: usize, area: Rect, buf: &mut Buffer) {
let mut state = TableState::default().with_selected(Some(selected_row));
let rows = INGREDIENTS.iter().map(|&i| i.into()).collect_vec();
let theme = THEME.recipe;
StatefulWidget::render(
Table::new(rows, [Constraint::Length(7), Constraint::Length(30)])
.block(Block::new().style(theme.ingredients))
.header(Row::new(vec!["Qty", "Ingredient"]).style(theme.ingredients_header))
.highlight_style(Style::new().light_yellow()),
area,
buf,
&mut state,
);
}
More examples
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
fn render_hops(selected_row: usize, area: Rect, buf: &mut Buffer) {
let mut state = TableState::default().with_selected(Some(selected_row));
let rows = HOPS
.iter()
.map(|hop| Row::new(vec![hop.host, hop.address]))
.collect_vec();
let block = Block::default()
.title("Traceroute bad.horse".bold().white())
.title_alignment(Alignment::Center)
.padding(Padding::new(1, 1, 1, 1));
StatefulWidget::render(
Table::new(rows, [Constraint::Max(100), Constraint::Length(15)])
.header(Row::new(vec!["Host", "Address"]).set_style(THEME.traceroute.header))
.highlight_style(THEME.traceroute.selected)
.block(block),
area,
buf,
&mut state,
);
let mut scrollbar_state = ScrollbarState::default()
.content_length(HOPS.len())
.position(selected_row);
let area = Rect {
width: area.width + 1,
y: area.y + 3,
height: area.height - 4,
..area
};
Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalLeft)
.begin_symbol(None)
.end_symbol(None)
.track_symbol(None)
.thumb_symbol("▌")
.render(area, buf, &mut scrollbar_state);
}
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
fn ui(f: &mut Frame, app: &mut App) {
let rects = Layout::default()
.constraints([Constraint::Percentage(100)])
.split(f.size());
let selected_style = Style::default().add_modifier(Modifier::REVERSED);
let normal_style = Style::default().bg(Color::Blue);
let header_cells = ["Header1", "Header2", "Header3"]
.iter()
.map(|h| Cell::from(*h).style(Style::default().fg(Color::Red)));
let header = Row::new(header_cells)
.style(normal_style)
.height(1)
.bottom_margin(1);
let rows = app.items.iter().map(|item| {
let height = item
.iter()
.map(|content| content.chars().filter(|c| *c == '\n').count())
.max()
.unwrap_or(0)
+ 1;
let cells = item.iter().map(|c| Cell::from(*c));
Row::new(cells).height(height as u16).bottom_margin(1)
});
let t = Table::new(
rows,
[
Constraint::Percentage(50),
Constraint::Max(30),
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);
}
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
fn draw_second_tab(f: &mut Frame, app: &mut App, area: Rect) {
let chunks = Layout::default()
.constraints([Constraint::Percentage(30), Constraint::Percentage(70)])
.direction(Direction::Horizontal)
.split(area);
let up_style = Style::default().fg(Color::Green);
let failure_style = Style::default()
.fg(Color::Red)
.add_modifier(Modifier::RAPID_BLINK | Modifier::CROSSED_OUT);
let rows = app.servers.iter().map(|s| {
let style = if s.status == "Up" {
up_style
} else {
failure_style
};
Row::new(vec![s.name, s.location, s.status]).style(style)
});
let table = Table::new(
rows,
[
Constraint::Length(15),
Constraint::Length(15),
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]);
let map = Canvas::default()
.block(Block::default().title("World").borders(Borders::ALL))
.paint(|ctx| {
ctx.draw(&Map {
color: Color::White,
resolution: MapResolution::High,
});
ctx.layer();
ctx.draw(&Rectangle {
x: 0.0,
y: 30.0,
width: 10.0,
height: 10.0,
color: Color::Yellow,
});
ctx.draw(&Circle {
x: app.servers[2].coords.1,
y: app.servers[2].coords.0,
radius: 10.0,
color: Color::Green,
});
for (i, s1) in app.servers.iter().enumerate() {
for s2 in &app.servers[i + 1..] {
ctx.draw(&canvas::Line {
x1: s1.coords.1,
y1: s1.coords.0,
y2: s2.coords.0,
x2: s2.coords.1,
color: Color::Yellow,
});
}
}
for server in &app.servers {
let color = if server.status == "Up" {
Color::Green
} else {
Color::Red
};
ctx.print(
server.coords.1,
server.coords.0,
Span::styled("X", Style::default().fg(color)),
);
}
})
.marker(if app.enhanced_graphics {
symbols::Marker::Braille
} else {
symbols::Marker::Dot
})
.x_bounds([-180.0, 180.0])
.y_bounds([-90.0, 90.0]);
f.render_widget(map, chunks[1]);
}
fn draw_third_tab(f: &mut Frame, _app: &mut App, area: Rect) {
let chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)])
.split(area);
let colors = [
Color::Reset,
Color::Black,
Color::Red,
Color::Green,
Color::Yellow,
Color::Blue,
Color::Magenta,
Color::Cyan,
Color::Gray,
Color::DarkGray,
Color::LightRed,
Color::LightGreen,
Color::LightYellow,
Color::LightBlue,
Color::LightMagenta,
Color::LightCyan,
Color::White,
];
let items: Vec<Row> = colors
.iter()
.map(|c| {
let cells = vec![
Cell::from(Span::raw(format!("{c:?}: "))),
Cell::from(Span::styled("Foreground", Style::default().fg(*c))),
Cell::from(Span::styled("Background", Style::default().bg(*c))),
];
Row::new(cells)
})
.collect();
let table = Table::new(
items,
[
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]);
}
sourcepub fn style(self, style: Style) -> Self
pub fn style(self, style: Style) -> Self
Sets the base style of the widget
All text rendered by the widget will use this style, unless overridden by Block::style
,
Row::style
, Cell::style
, or the styles of cell’s content.
This is a fluent setter method which must be chained or used as it consumes self
Examples
let table = Table::new(rows, widths).style(Style::new().red().italic());
Table
also implements the Styled
trait, which means you can use style shorthands from
the Stylize
trait to set the style of the widget more concisely.
let table = Table::new(rows, widths).red().italic();
sourcepub fn highlight_style(self, highlight_style: Style) -> Self
pub fn highlight_style(self, highlight_style: Style) -> Self
Set the style of the selected row
This style will be applied to the entire row, including the selection symbol if it is displayed, and will override any style set on the row or on the individual cells.
This is a fluent setter method which must be chained or used as it consumes self
Examples
let table = Table::new(rows, widths).highlight_style(Style::new().red().italic());
Examples found in repository?
144 145 146 147 148 149 150 151 152 153 154 155 156 157
fn render_ingredients(selected_row: usize, area: Rect, buf: &mut Buffer) {
let mut state = TableState::default().with_selected(Some(selected_row));
let rows = INGREDIENTS.iter().map(|&i| i.into()).collect_vec();
let theme = THEME.recipe;
StatefulWidget::render(
Table::new(rows, [Constraint::Length(7), Constraint::Length(30)])
.block(Block::new().style(theme.ingredients))
.header(Row::new(vec!["Qty", "Ingredient"]).style(theme.ingredients_header))
.highlight_style(Style::new().light_yellow()),
area,
buf,
&mut state,
);
}
More examples
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
fn render_hops(selected_row: usize, area: Rect, buf: &mut Buffer) {
let mut state = TableState::default().with_selected(Some(selected_row));
let rows = HOPS
.iter()
.map(|hop| Row::new(vec![hop.host, hop.address]))
.collect_vec();
let block = Block::default()
.title("Traceroute bad.horse".bold().white())
.title_alignment(Alignment::Center)
.padding(Padding::new(1, 1, 1, 1));
StatefulWidget::render(
Table::new(rows, [Constraint::Max(100), Constraint::Length(15)])
.header(Row::new(vec!["Host", "Address"]).set_style(THEME.traceroute.header))
.highlight_style(THEME.traceroute.selected)
.block(block),
area,
buf,
&mut state,
);
let mut scrollbar_state = ScrollbarState::default()
.content_length(HOPS.len())
.position(selected_row);
let area = Rect {
width: area.width + 1,
y: area.y + 3,
height: area.height - 4,
..area
};
Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalLeft)
.begin_symbol(None)
.end_symbol(None)
.track_symbol(None)
.thumb_symbol("▌")
.render(area, buf, &mut scrollbar_state);
}
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
fn ui(f: &mut Frame, app: &mut App) {
let rects = Layout::default()
.constraints([Constraint::Percentage(100)])
.split(f.size());
let selected_style = Style::default().add_modifier(Modifier::REVERSED);
let normal_style = Style::default().bg(Color::Blue);
let header_cells = ["Header1", "Header2", "Header3"]
.iter()
.map(|h| Cell::from(*h).style(Style::default().fg(Color::Red)));
let header = Row::new(header_cells)
.style(normal_style)
.height(1)
.bottom_margin(1);
let rows = app.items.iter().map(|item| {
let height = item
.iter()
.map(|content| content.chars().filter(|c| *c == '\n').count())
.max()
.unwrap_or(0)
+ 1;
let cells = item.iter().map(|c| Cell::from(*c));
Row::new(cells).height(height as u16).bottom_margin(1)
});
let t = Table::new(
rows,
[
Constraint::Percentage(50),
Constraint::Max(30),
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);
}
sourcepub fn highlight_symbol(self, highlight_symbol: &'a str) -> Self
pub fn highlight_symbol(self, highlight_symbol: &'a str) -> Self
Set the symbol to be displayed in front of the selected row
This is a fluent setter method which must be chained or used as it consumes self
Examples
let table = Table::new(rows, widths).highlight_symbol(">>");
Examples found in repository?
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
fn ui(f: &mut Frame, app: &mut App) {
let rects = Layout::default()
.constraints([Constraint::Percentage(100)])
.split(f.size());
let selected_style = Style::default().add_modifier(Modifier::REVERSED);
let normal_style = Style::default().bg(Color::Blue);
let header_cells = ["Header1", "Header2", "Header3"]
.iter()
.map(|h| Cell::from(*h).style(Style::default().fg(Color::Red)));
let header = Row::new(header_cells)
.style(normal_style)
.height(1)
.bottom_margin(1);
let rows = app.items.iter().map(|item| {
let height = item
.iter()
.map(|content| content.chars().filter(|c| *c == '\n').count())
.max()
.unwrap_or(0)
+ 1;
let cells = item.iter().map(|c| Cell::from(*c));
Row::new(cells).height(height as u16).bottom_margin(1)
});
let t = Table::new(
rows,
[
Constraint::Percentage(50),
Constraint::Max(30),
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);
}
sourcepub fn highlight_spacing(self, value: HighlightSpacing) -> Self
pub fn highlight_spacing(self, value: HighlightSpacing) -> Self
Set when to show the highlight spacing
The highlight spacing is the spacing that is allocated for the selection symbol column (if enabled) and is used to shift the table when a row is selected. This method allows you to configure when this spacing is allocated.
HighlightSpacing::Always
will always allocate the spacing, regardless of whether a row is selected or not. This means that the table will never change size, regardless of if a row is selected or not.HighlightSpacing::WhenSelected
will only allocate the spacing if a row is selected. This means that the table will shift when a row is selected. This is the default setting for backwards compatibility, but it is recommended to useHighlightSpacing::Always
for a better user experience.HighlightSpacing::Never
will never allocate the spacing, regardless of whether a row is selected or not. This means that the highlight symbol will never be drawn.
This is a fluent setter method which must be chained or used as it consumes self
Examples
let table = Table::new(rows, widths).highlight_spacing(HighlightSpacing::Always);
sourcepub const fn segment_size(self, segment_size: SegmentSize) -> Self
Available on crate feature unstable-segment-size
only.
pub const fn segment_size(self, segment_size: SegmentSize) -> Self
unstable-segment-size
only.Set how extra space is distributed amongst columns.
This determines how the space is distributed when the constraints are satisfied. By default, the extra space is not distributed at all. But this can be changed to distribute all extra space to the last column or to distribute it equally.
This is a fluent setter method which must be chained or used as it consumes self
Examples
Create a table that needs at least 30 columns to display. Any extra space will be assigned to the last column.
let widths = [Constraint::Min(10), Constraint::Min(10), Constraint::Min(10)];
let table = Table::new([], widths)
.segment_size(SegmentSize::LastTakesRemainder);
Availability
This API is marked as unstable and is only available when the unstable-segment-size
crate feature is enabled. This comes with no stability guarantees, and could be changed or removed at any time.
Trait Implementations§
source§impl<'a> PartialEq for Table<'a>
impl<'a> PartialEq for Table<'a>
source§impl<'a> StatefulWidget for Table<'a>
impl<'a> StatefulWidget for Table<'a>
impl<'a> Eq for Table<'a>
impl<'a> StructuralEq for Table<'a>
impl<'a> StructuralPartialEq for Table<'a>
Auto Trait Implementations§
impl<'a> RefUnwindSafe for Table<'a>
impl<'a> Send for Table<'a>
impl<'a> Sync for Table<'a>
impl<'a> Unpin for Table<'a>
impl<'a> UnwindSafe for Table<'a>
Blanket Implementations§
source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
source§impl<'a, T, U> Stylize<'a, T> for Uwhere
U: Styled<Item = T>,
impl<'a, T, U> Stylize<'a, T> for Uwhere
U: Styled<Item = T>,
fn bg(self, color: Color) -> T
fn fg<S>(self, color: S) -> T
fn add_modifier(self, modifier: Modifier) -> T
fn remove_modifier(self, modifier: Modifier) -> T
fn reset(self) -> T
source§fn on_magenta(self) -> T
fn on_magenta(self) -> T
magenta
.source§fn on_dark_gray(self) -> T
fn on_dark_gray(self) -> T
dark_gray
.source§fn on_light_red(self) -> T
fn on_light_red(self) -> T
light_red
.source§fn light_green(self) -> T
fn light_green(self) -> T
light_green
.source§fn on_light_green(self) -> T
fn on_light_green(self) -> T
light_green
.source§fn light_yellow(self) -> T
fn light_yellow(self) -> T
light_yellow
.source§fn on_light_yellow(self) -> T
fn on_light_yellow(self) -> T
light_yellow
.source§fn light_blue(self) -> T
fn light_blue(self) -> T
light_blue
.source§fn on_light_blue(self) -> T
fn on_light_blue(self) -> T
light_blue
.source§fn light_magenta(self) -> T
fn light_magenta(self) -> T
light_magenta
.source§fn on_light_magenta(self) -> T
fn on_light_magenta(self) -> T
light_magenta
.source§fn light_cyan(self) -> T
fn light_cyan(self) -> T
light_cyan
.source§fn on_light_cyan(self) -> T
fn on_light_cyan(self) -> T
light_cyan
.source§fn not_italic(self) -> T
fn not_italic(self) -> T
ITALIC
modifier.source§fn underlined(self) -> T
fn underlined(self) -> T
UNDERLINED
modifier.source§fn not_underlined(self) -> T
fn not_underlined(self) -> T
UNDERLINED
modifier.source§fn slow_blink(self) -> T
fn slow_blink(self) -> T
SLOW_BLINK
modifier.source§fn not_slow_blink(self) -> T
fn not_slow_blink(self) -> T
SLOW_BLINK
modifier.source§fn rapid_blink(self) -> T
fn rapid_blink(self) -> T
RAPID_BLINK
modifier.source§fn not_rapid_blink(self) -> T
fn not_rapid_blink(self) -> T
RAPID_BLINK
modifier.source§fn not_reversed(self) -> T
fn not_reversed(self) -> T
REVERSED
modifier.HIDDEN
modifier.HIDDEN
modifier.source§fn crossed_out(self) -> T
fn crossed_out(self) -> T
CROSSED_OUT
modifier.source§fn not_crossed_out(self) -> T
fn not_crossed_out(self) -> T
CROSSED_OUT
modifier.