rtlibs-tui 0.1.5

rtools library: ratatui widgets
Documentation
use std::marker::PhantomData;

use ratatui::buffer::Buffer;
use ratatui::layout::Constraint;
use ratatui::layout::Layout;
use ratatui::layout::Rect;
use ratatui::style::Color;
use ratatui::style::Style;
use ratatui::style::Stylize;
use ratatui::symbols;
use ratatui::text::Line;
use ratatui::text::Text;
use ratatui::widgets::Block;
use ratatui::widgets::Borders;
use ratatui::widgets::Clear;
use ratatui::widgets::HighlightSpacing;
use ratatui::widgets::List;
use ratatui::widgets::StatefulWidget;
use ratatui::widgets::Widget;

use super::ListItem;
use super::ListState;

pub struct ListWidget<T>
{
    active: bool,
    style: Style,
    header_style: Style,
    header_centered: bool,
    alternate_background: Option<Color>,
    highlight_style: Style,
    highlight_symbol: String,
    ds: PhantomData<T>,
}

impl<T> ListWidget<T>
{
    pub fn new(active: bool) -> Self
    {
        Self {
            active,
            style: Style::default(),
            header_style: Style::default(),
            header_centered: false,
            alternate_background: None,
            highlight_style: Style::default(),
            highlight_symbol: ">".to_string(),
            ds: PhantomData,
        }
    }

    pub fn style<S: Into<Style>>(
        mut self,
        style: S,
    ) -> Self
    {
        self.style = style.into();
        self
    }

    pub fn set_header_style<S: Into<Style>>(
        &mut self,
        style: S,
    )
    {
        self.header_style = style.into();
    }

    pub fn with_header_style<S: Into<Style>>(
        mut self,
        style: S,
    ) -> Self
    {
        self.set_header_style(style);
        self
    }

    pub fn header_style(&self) -> Style
    {
        self.header_style
    }

    pub fn set_header_centered(
        &mut self,
        centered: bool,
    )
    {
        self.header_centered = centered;
    }

    pub fn with_header_centered(mut self) -> Self
    {
        self.set_header_centered(true);
        self
    }

    pub fn header_centered(&self) -> bool
    {
        self.header_centered
    }

    pub fn set_alternate_background(
        &mut self,
        color: Color,
    )
    {
        self.alternate_background = Some(color);
    }

    pub fn with_alternate_background(
        mut self,
        color: Color,
    ) -> Self
    {
        self.set_alternate_background(color);
        self
    }

    pub fn clear_alternate_background(&mut self)
    {
        self.alternate_background = None;
    }

    pub fn alternate_background(&self) -> Option<Color>
    {
        self.alternate_background
    }

    fn get_alternate_color(
        &self,
        i: usize,
    ) -> Option<Color>
    {
        if let Some(color) = self.alternate_background
        {
            if i % 2 == 0
            {
                self.style
                    .bg
            }
            else
            {
                Some(color)
            }
        }
        else
        {
            self.style
                .bg
        }
    }

    pub fn set_highlight_style<S: Into<Style>>(
        &mut self,
        style: S,
    )
    {
        self.highlight_style = style.into();
    }

    pub fn with_highlight_style<S: Into<Style>>(
        mut self,
        style: S,
    ) -> Self
    {
        self.set_highlight_style(style);
        self
    }

    pub fn highlight_style(&self) -> Style
    {
        self.highlight_style
    }

    pub fn set_highlight_symbol<S>(
        &mut self,
        symbol: S,
    ) where
        S: AsRef<str>,
    {
        self.highlight_symbol = symbol
            .as_ref()
            .to_string();
    }

    pub fn with_highlight_symbol<S>(
        mut self,
        symbol: S,
    ) -> Self
    where
        S: AsRef<str>,
    {
        self.set_highlight_symbol(symbol);
        self
    }

    pub fn highlight_symbol(&self) -> &String
    {
        &self.highlight_symbol
    }
}

impl<T> StatefulWidget for ListWidget<T>
where
    T: ListItem,
{
    type State = ListState<T>;

    fn render(
        self,
        area: Rect,
        buf: &mut Buffer,
        state: &mut Self::State,
    )
    {
        Clear.render(
            area, buf,
        );

        let mut block = Block::new().style(self.style);

        if let Some(title) = state
            .title
            .as_ref()
        {
            let mut line = Line::raw(title);
            if self.header_centered
            {
                line = line.centered();
            }
            block = block
                .borders(Borders::TOP)
                .border_set(symbols::border::EMPTY)
                .border_style(self.header_style)
                .title(line);
        }

        let items = state
            .items
            .iter()
            .filter(
                |i| {
                    i.filter(
                        state
                            .filter
                            .as_ref(),
                    )
                },
            )
            .enumerate()
            .map(
                |(i, s)| {
                    let item = s.to_listitem(
                        area.width,
                        i,
                        self.active,
                    );
                    // let fg = Color::Green;
                    // let fg = Color::Green;
                    // let aaa = palette::Srgb::new(0, 255, 0);
                    // let mut lch = palette::Lch::from_color(aaa);
                    // let bbb: Hsl = aaa.into_color();
                    // let h = Hsl::from_color(fg.into_color());
                    if let Some(color) = self.get_alternate_color(i)
                    {
                        item.bg(color)
                    }
                    else
                    {
                        item
                        // item.bg(ratatui::style::Color::from_hsl(134.4, 113.3, 87.8))
                        // item.bg(utils::colored("#00FF00", false))
                        // item.fg(Color::from_u32(aaa.uints_into()))
                    }
                },
            )
            .collect::<Vec<_>>();

        let list = List::new(items)
            .block(block)
            .highlight_style(self.highlight_style)
            .highlight_symbol(&self.highlight_symbol)
            .highlight_spacing(HighlightSpacing::Always);

        let mut area = area;
        if state.has_scrollbar() && area.height > 2
        {
            area.width -= 1;
        }

        StatefulWidget::render(
            list,
            area,
            buf,
            &mut state.state,
        );

        if state.has_scrollbar()
        {
            let mut scrollbar_area = area;
            scrollbar_area.x += area.width;
            scrollbar_area.width = 1;
            if scrollbar_area.height >= 3
            {
                let first_char = if state
                    .state
                    .offset()
                    == 0
                {
                    " "
                }
                else
                {
                    ""
                };

                let last_char = if state
                    .state
                    .offset()
                    + (scrollbar_area.height as usize)
                    >= state
                        .items
                        .len()
                {
                    " "
                }
                else
                {
                    ""
                };

                let [first_area, inner_area, last_area] = Layout::vertical([
                    Constraint::Length(1),
                    Constraint::Fill(1),
                    Constraint::Length(1),
                ])
                .areas(scrollbar_area);
                Text::styled(
                    first_char,
                    Style::new().bg(
                        self.style
                            .bg
                            .unwrap_or_default(),
                    ),
                )
                .render(
                    first_area, buf,
                );
                Block::new()
                    .style(
                        Style::new().bg(
                            self.style
                                .bg
                                .unwrap_or_default(),
                        ),
                    )
                    .render(
                        inner_area, buf,
                    );
                Text::styled(
                    last_char,
                    Style::new().bg(
                        self.style
                            .bg
                            .unwrap_or_default(),
                    ),
                )
                .render(
                    last_area, buf,
                );
            }
        }
    }
}