use crate::{
buffer::Buffer,
layout::Rect,
style::Style,
widgets::{Block, Widget},
};
use sauron_vdom::{Attribute, Event};
use std::{fmt::Display, iter::Iterator};
pub enum Row<D, I>
where
D: Iterator<Item = I>,
I: Display,
{
Data(D),
StyledData(D, Style),
}
pub struct Table<'a, T, H, I, D, R, MSG>
where
T: Display,
H: Iterator<Item = T>,
I: Display,
D: Iterator<Item = I>,
R: Iterator<Item = Row<D, I>>,
{
block: Option<Block<'a, MSG>>,
style: Style,
header: H,
header_style: Style,
widths: &'a [u16],
column_spacing: u16,
rows: R,
area: Rect,
pub events: Vec<Attribute<Event, MSG>>,
}
impl<'a, T, H, I, D, R, MSG> Default for Table<'a, T, H, I, D, R, MSG>
where
T: Display,
H: Iterator<Item = T> + Default,
I: Display,
D: Iterator<Item = I>,
R: Iterator<Item = Row<D, I>> + Default,
{
fn default() -> Self {
Table {
block: None,
style: Style::default(),
header: H::default(),
header_style: Style::default(),
widths: &[],
rows: R::default(),
column_spacing: 1,
area: Default::default(),
events: vec![],
}
}
}
impl<'a, T, H, I, D, R, MSG> Table<'a, T, H, I, D, R, MSG>
where
T: Display,
H: Iterator<Item = T>,
I: Display,
D: Iterator<Item = I>,
R: Iterator<Item = Row<D, I>>,
{
pub fn new(header: H, rows: R) -> Self {
Table {
block: None,
style: Style::default(),
header,
header_style: Style::default(),
widths: &[],
rows,
column_spacing: 1,
area: Default::default(),
events: vec![],
}
}
pub fn block(mut self, block: Block<'a, MSG>) -> Self {
self.block = Some(block);
self
}
pub fn header<II>(mut self, header: II) -> Self
where
II: IntoIterator<Item = T, IntoIter = H>,
{
self.header = header.into_iter();
self
}
pub fn header_style(mut self, style: Style) -> Self {
self.header_style = style;
self
}
pub fn widths(mut self, widths: &'a [u16]) -> Self {
self.widths = widths;
self
}
pub fn rows<II>(mut self, rows: II) -> Self
where
II: IntoIterator<Item = Row<D, I>, IntoIter = R>,
{
self.rows = rows.into_iter();
self
}
pub fn style(mut self, style: Style) -> Self {
self.style = style;
self
}
pub fn column_spacing(mut self, spacing: u16) -> Self {
self.column_spacing = spacing;
self
}
pub fn area(mut self, area: Rect) -> Self {
self.area = area;
self
}
}
impl<'a, T, H, I, D, R, MSG> Widget for Table<'a, T, H, I, D, R, MSG>
where
T: Display,
H: Iterator<Item = T>,
I: Display,
D: Iterator<Item = I>,
R: Iterator<Item = Row<D, I>>,
MSG: 'static,
{
fn get_area(&self) -> Rect {
self.area
}
fn draw(&mut self, buf: &mut Buffer) {
let table_area = match self.block {
Some(ref mut b) => {
b.draw(buf);
b.inner()
}
None => self.area,
};
self.background(buf, self.style.bg);
let mut x = 0;
let mut widths = Vec::with_capacity(self.widths.len());
for width in self.widths.iter() {
if x + width < table_area.width {
widths.push(*width);
}
x += *width;
}
let mut y = table_area.top();
if y < table_area.bottom() {
x = table_area.left();
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;
let default_style = Style::default();
if y < table_area.bottom() {
let remaining = (table_area.bottom() - y) as usize;
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(data) {
buf.set_stringn(x, y + i as u16, format!("{}", elt), *w as usize, style);
x += *w + self.column_spacing;
}
}
}
}
}