mirror of
https://github.com/ratatui-org/ratatui
synced 2025-02-16 22:18:51 +00:00
[widgets] Refactor Table Widget
* Add Row enum * Move to a generic definition of table which allow iterators to be passed as arguments and therefore reduce allocations
This commit is contained in:
parent
d6a91d1865
commit
c18885d38b
1 changed files with 91 additions and 43 deletions
|
@ -1,12 +1,20 @@
|
|||
use std::cmp::max;
|
||||
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
use std::fmt::Display;
|
||||
use std::iter::Iterator;
|
||||
|
||||
use buffer::Buffer;
|
||||
use widgets::{Widget, Block};
|
||||
use layout::Rect;
|
||||
use style::Style;
|
||||
|
||||
pub enum Row<'i, D, I>
|
||||
where
|
||||
D: Iterator<Item = I>,
|
||||
I: Display,
|
||||
{
|
||||
Data(D),
|
||||
StyledData(D, &'i Style),
|
||||
}
|
||||
|
||||
/// A widget to display data in formatted column
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -28,14 +36,20 @@ use style::Style;
|
|||
/// (&["Row31", "Row32", "Row33"], &row_style)]);
|
||||
/// # }
|
||||
/// ```
|
||||
|
||||
pub struct Table<'a> {
|
||||
pub struct Table<'a, 'i, T, H, I, D, R>
|
||||
where
|
||||
T: Display,
|
||||
H: Iterator<Item = T>,
|
||||
I: Display,
|
||||
D: Iterator<Item = I>,
|
||||
R: Iterator<Item = Row<'i, D, I>>,
|
||||
{
|
||||
/// A block to wrap the widget in
|
||||
block: Option<Block<'a>>,
|
||||
/// Base style for the widget
|
||||
style: Style,
|
||||
/// Header row for all columns
|
||||
header: &'a [&'a str],
|
||||
header: H,
|
||||
/// Style for the header
|
||||
header_style: Style,
|
||||
/// Width of each column (if the total width is greater than the widget width some columns may
|
||||
|
@ -44,73 +58,101 @@ pub struct Table<'a> {
|
|||
/// Space between each column
|
||||
column_spacing: u16,
|
||||
/// Data to display in each row
|
||||
rows: Vec<(Vec<&'a str>, &'a Style)>,
|
||||
rows: R,
|
||||
}
|
||||
|
||||
impl<'a> Default for Table<'a> {
|
||||
fn default() -> Table<'a> {
|
||||
impl<'a, 'i, T, H, I, D, R> Default for Table<'a, 'i, T, H, I, D, R>
|
||||
where
|
||||
T: Display,
|
||||
H: Iterator<Item = T> + Default,
|
||||
I: Display,
|
||||
D: Iterator<Item = I>,
|
||||
R: Iterator<Item = Row<'i, D, I>>
|
||||
+ Default,
|
||||
{
|
||||
fn default() -> Table<'a, 'i, T, H, I, D, R> {
|
||||
Table {
|
||||
block: None,
|
||||
style: Style::default(),
|
||||
header: &[],
|
||||
header: H::default(),
|
||||
header_style: Style::default(),
|
||||
widths: &[],
|
||||
rows: Vec::new(),
|
||||
rows: R::default(),
|
||||
column_spacing: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Table<'a> {
|
||||
pub fn block(&'a mut self, block: Block<'a>) -> &mut Table<'a> {
|
||||
impl<'a, 'i, T, H, I, D, R> Table<'a, 'i, T, H, I, D, R>
|
||||
where
|
||||
T: Display,
|
||||
H: Iterator<Item = T>,
|
||||
I: Display,
|
||||
D: Iterator<Item = I>,
|
||||
R: Iterator<Item = Row<'i, D, I>>,
|
||||
{
|
||||
pub fn new(header: H, rows: R) -> Table<'a, 'i, T, H, I, D, R> {
|
||||
Table {
|
||||
block: None,
|
||||
style: Style::default(),
|
||||
header: header,
|
||||
header_style: Style::default(),
|
||||
widths: &[],
|
||||
rows: rows,
|
||||
column_spacing: 1,
|
||||
}
|
||||
}
|
||||
pub fn block(&'a mut self, block: Block<'a>) -> &mut Table<'a, 'i, T, H, I, D, R> {
|
||||
self.block = Some(block);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn header(&mut self, header: &'a [&'a str]) -> &mut Table<'a> {
|
||||
self.header = header;
|
||||
pub fn header<II>(&mut self, header: II) -> &mut Table<'a, 'i, T, H, I, D, R>
|
||||
where
|
||||
II: IntoIterator<Item = T, IntoIter = H>,
|
||||
{
|
||||
self.header = header.into_iter();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn header_style(&mut self, style: Style) -> &mut Table<'a> {
|
||||
pub fn header_style(&mut self, style: Style) -> &mut Table<'a, 'i, T, H, I, D, R> {
|
||||
self.header_style = style;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn widths(&mut self, widths: &'a [u16]) -> &mut Table<'a> {
|
||||
pub fn widths(&mut self, widths: &'a [u16]) -> &mut Table<'a, 'i, T, H, I, D, R> {
|
||||
self.widths = widths;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn rows<S, R>(&mut self, rows: &'a [(R, &'a Style)]) -> &mut Table<'a>
|
||||
where S: AsRef<str> + 'a,
|
||||
R: AsRef<[S]> + 'a
|
||||
pub fn rows<II>(&mut self, rows: II) -> &mut Table<'a, 'i, T, H, I, D, R>
|
||||
where
|
||||
II: IntoIterator<Item = Row<'i, D, I>, IntoIter = R>,
|
||||
{
|
||||
self.rows = rows.iter()
|
||||
.map(|&(ref r, style)| {
|
||||
(r.as_ref()
|
||||
.iter()
|
||||
.map(|i| i.as_ref())
|
||||
.collect::<Vec<&'a str>>(),
|
||||
style)
|
||||
})
|
||||
.collect::<Vec<(Vec<&'a str>, &'a Style)>>();
|
||||
self.rows = rows.into_iter();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn style(&mut self, style: Style) -> &mut Table<'a> {
|
||||
pub fn style(&mut self, style: Style) -> &mut Table<'a, 'i, T, H, I, D, R> {
|
||||
self.style = style;
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
pub fn column_spacing(&mut self, spacing: u16) -> &mut Table<'a> {
|
||||
pub fn column_spacing(&mut self, spacing: u16) -> &mut Table<'a, 'i, T, H, I, D, R> {
|
||||
self.column_spacing = spacing;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Widget for Table<'a> {
|
||||
impl<'a, 'i, T, H, I, D, R> Widget for Table<'a, 'i, T, H, I, D, R>
|
||||
where
|
||||
T: Display,
|
||||
H: Iterator<Item = T>,
|
||||
I: Display,
|
||||
D: Iterator<Item = I>,
|
||||
R: Iterator<Item = Row<'i, D, I>>,
|
||||
{
|
||||
fn draw(&mut self, area: &Rect, buf: &mut Buffer) {
|
||||
|
||||
// Render block if necessary and get the drawing area
|
||||
|
@ -125,34 +167,40 @@ impl<'a> Widget for Table<'a> {
|
|||
// Set the background
|
||||
self.background(&table_area, buf, self.style.bg);
|
||||
|
||||
// Save the widths of the columns that will fit in the given area
|
||||
let mut x = 0;
|
||||
let mut widths = Vec::with_capacity(self.widths.len());
|
||||
for (width, title) in self.widths.iter().zip(self.header.iter()) {
|
||||
let w = max(title.width() as u16, *width);
|
||||
if x + w < table_area.width {
|
||||
widths.push(w);
|
||||
for width in self.widths.iter() {
|
||||
if x + width < table_area.width {
|
||||
widths.push(*width);
|
||||
}
|
||||
x += w;
|
||||
x += *width;
|
||||
}
|
||||
|
||||
let mut y = table_area.top();
|
||||
|
||||
// Header
|
||||
// Draw the header
|
||||
if y < table_area.bottom() {
|
||||
x = table_area.left();
|
||||
for (w, t) in widths.iter().zip(self.header.iter()) {
|
||||
buf.set_string(x, y, t, &self.header_style);
|
||||
for (w, t) in widths.iter().zip(self.header.by_ref()) {
|
||||
buf.set_string(x, y, &format!("{}", t), &self.header_style);
|
||||
x += *w + self.column_spacing;
|
||||
}
|
||||
}
|
||||
y += 2;
|
||||
|
||||
// Draw rows
|
||||
let default_style = Style::default();
|
||||
if y < table_area.bottom() {
|
||||
let remaining = (table_area.bottom() - y) as usize;
|
||||
for (i, &(ref row, style)) in self.rows.iter().take(remaining).enumerate() {
|
||||
for (i, row) in self.rows.by_ref().take(remaining).enumerate() {
|
||||
let (data, style) = match row {
|
||||
Row::Data(d) => (d, &default_style),
|
||||
Row::StyledData(d, s) => (d, s),
|
||||
};
|
||||
x = table_area.left();
|
||||
for (w, elt) in widths.iter().zip(row.iter()) {
|
||||
buf.set_stringn(x, y + i as u16, elt, *w as usize, style);
|
||||
for (w, elt) in widths.iter().zip(data) {
|
||||
buf.set_stringn(x, y + i as u16, &format!("{}", elt), *w as usize, style);
|
||||
x += *w + self.column_spacing;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue