scrin 0.1.74

A terminal UI toolkit with panes, widgets, overlays, animations, and Aisling-powered effects/loaders.
Documentation
use crate::core::buffer::Buffer;
use crate::core::color::Color;
use crate::core::rect::Rect;
use crate::style::Style;
use crate::widgets::Widget;

#[derive(Debug, Clone)]
pub struct Cell {
    pub content: String,
    pub style: Style,
}

impl Cell {
    pub fn new(content: &str) -> Self {
        Self {
            content: content.to_string(),
            style: Style::new(),
        }
    }

    pub fn with_style(mut self, style: Style) -> Self {
        self.style = style;
        self
    }
}

#[derive(Debug, Clone)]
pub struct Row {
    pub cells: Vec<Cell>,
    pub style: Style,
}

impl Row {
    pub fn new(cells: Vec<Cell>) -> Self {
        Self {
            cells,
            style: Style::new(),
        }
    }

    pub fn with_style(mut self, style: Style) -> Self {
        self.style = style;
        self
    }
}

#[derive(Debug, Clone)]
pub struct Table<'a> {
    pub rows: &'a [Row],
    pub widths: &'a [u16],
    pub header: Option<&'a Row>,
    pub selected: Option<usize>,
    pub header_style: Style,
    pub selected_style: Style,
}

impl<'a> Table<'a> {
    pub fn new(rows: &'a [Row], widths: &'a [u16]) -> Self {
        Self {
            rows,
            widths,
            header: None,
            selected: None,
            header_style: Style::new().bold().fg(Color::rgb(88, 166, 255)),
            selected_style: Style::new().fg(Color::WHITE).bg(Color::rgb(31, 111, 235)),
        }
    }

    pub fn with_header(mut self, header: &'a Row) -> Self {
        self.header = Some(header);
        self
    }

    pub fn with_selected(mut self, selected: usize) -> Self {
        self.selected = Some(selected);
        self
    }
}

impl<'a> Widget for Table<'a> {
    fn render(&self, buffer: &mut Buffer, area: Rect) {
        let mut current_y = area.y as usize;
        let _header_offset = if self.header.is_some() { 1 } else { 0 };

        if let Some(header) = self.header {
            if current_y < area.bottom() as usize {
                let mut x = area.x as usize;
                for (i, cell) in header.cells.iter().enumerate() {
                    let w = self.widths.get(i).copied().unwrap_or(10) as usize;
                    let display: String = cell.content.chars().take(w).collect();
                    buffer.set_str(
                        x,
                        current_y,
                        &display,
                        self.header_style.fg_or_default(),
                        self.header_style.bg,
                    );
                    x += w + 1;
                }
                current_y += 1;
            }
        }

        for (row_idx, row) in self.rows.iter().enumerate() {
            if current_y >= area.bottom() as usize {
                break;
            }
            let is_selected = self.selected == Some(row_idx);
            let mut x = area.x as usize;
            for (i, cell) in row.cells.iter().enumerate() {
                let w = self.widths.get(i).copied().unwrap_or(10) as usize;
                let display: String = cell.content.chars().take(w).collect();
                let (fg, bg) = if is_selected {
                    (self.selected_style.fg_or_default(), self.selected_style.bg)
                } else {
                    (cell.style.fg_or_default(), cell.style.bg)
                };
                buffer.set_str(x, current_y, &display, fg, bg);
                x += w + 1;
            }
            current_y += 1;
        }
    }
}