rtlibs-tui 0.1.5

rtools library: ratatui widgets
Documentation
use ratatui::buffer::Buffer;
use ratatui::layout::Constraint;
use ratatui::layout::Layout;
use ratatui::layout::Rect;
use ratatui::style::Style;
use ratatui::text::Text;
use ratatui::widgets::Block;
use ratatui::widgets::Paragraph;
use ratatui::widgets::StatefulWidget;
use ratatui::widgets::Widget;

use crate::widgets::InputWidget;

use super::CommanderMode;
use super::CommanderState;

#[derive(Default)]
pub struct CommanderWidgetStyle
{
    pub input: Style,
    pub menu: Style,
    pub menu_selected: Style,
}

pub struct CommanderWidget
{
    pub style: CommanderWidgetStyle,
}

impl std::default::Default for CommanderWidget
{
    fn default() -> Self
    {
        Self::new()
    }
}

impl CommanderWidget
{
    pub fn new() -> Self
    {
        Self {
            style: CommanderWidgetStyle::default(),
        }
    }

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

impl StatefulWidget for CommanderWidget
{
    type State = CommanderState;

    fn render(
        self,
        area: Rect,
        buf: &mut Buffer,
        state: &mut Self::State,
    )
    {
        if state
            .mode
            .eq(&CommanderMode::Message)
        {
            let style = state
                .message_style
                .unwrap_or(
                    self.style
                        .input,
                );
            Text::styled(
                &state.message,
                style,
            )
            .render(
                area, buf,
            );
        }
        else
        {
            let desc_height = state.description_height(area.width);
            let menu_height = state.menu_height();

            let [mut desc_area, menu_area, bottom_area] = Layout::vertical([
                Constraint::Length(desc_height),
                Constraint::Length(menu_height),
                Constraint::Length(1),
            ])
            .areas(area);

            let [menu_left_area, menu_center_area, menu_right_area] = Layout::horizontal([
                Constraint::Fill(1),
                Constraint::Fill(1),
                Constraint::Fill(1),
            ])
            .areas(menu_area);

            let [prefix_area, input_area] = Layout::horizontal([
                Constraint::Length(
                    u16::try_from(
                        state
                            .prefix
                            .chars()
                            .count(),
                    )
                    .unwrap_or_default(),
                ),
                Constraint::Fill(1),
            ])
            .areas(bottom_area);

            let inner_width = desc_area
                .width
                .saturating_sub(4) as usize;
            let description = state.description(inner_width);
            if !description.is_empty()
            {
                // if state
                // .selected()
                // .is_some()
                // || state
                // .error_message
                // .is_some()
                // {
                desc_area.width = desc_area
                    .width
                    .saturating_sub(4);
                Block::bordered()
                    .style(
                        self.style
                            .menu,
                    )
                    .render(
                        desc_area, buf,
                    );
                // let inner_width = desc_area
                // .width
                // .saturating_sub(4) as usize;
                // let description = state.description(inner_width);
                let mut area = desc_area;
                area.y += 1;
                area.x += 2;
                Paragraph::new(description).render(
                    area, buf,
                );
            }

            // if let Some((_, _)) = state.selected()
            // {
            //     desc_area.width = desc_area
            //         .width
            //         .saturating_sub(4);
            //     Block::bordered()
            //         .style(
            //             self.style
            //                 .menu,
            //         )
            //         .render(
            //             desc_area, buf,
            //         );
            //     let inner_width = desc_area
            //         .width
            //         .saturating_sub(4) as usize;
            //     let description = state.description(inner_width);
            //     // let (_, description) = command.wrapped_description(inner_width);
            //     let mut area = desc_area;
            //     area.y += 1;
            //     area.x += 2;

            //     // let content = if let Some(message) = state
            //     //     .error_message
            //     //     .as_ref()
            //     // {
            //     //     format!("{description}\n\n{message}")
            //     // }
            //     // else
            //     // {
            //     //     description.clone()
            //     // };

            //     Paragraph::new(description).render(
            //         area, buf,
            //     );
            // }

            Block::new()
                .style(
                    self.style
                        .menu,
                )
                .render(
                    menu_area, buf,
                );

            // let selection = state.selected();
            let selected_index = state.selected_index;

            for col in 0..=2
            {
                for row in 0..menu_height
                {
                    let index = col * menu_height + row;
                    let (filtered, _) = state.filtered();
                    if let Some(item) = filtered.get(index as usize)
                    {
                        let mut area = if col == 0
                        {
                            menu_left_area
                        }
                        else if col == 1
                        {
                            menu_center_area
                        }
                        else
                        {
                            menu_right_area
                        };
                        area.y += row;
                        area.height = 1;
                        let content = item
                            .get_name()
                            // .name
                            .chars()
                            .take(area.width as usize - 1)
                            .collect::<String>();

                        let active = selected_index
                            .map(|i| i == index as usize)
                            .unwrap_or(false);

                        // let active = selection
                        //     .map(
                        //         |(_, c)| {
                        //             c.get_name()
                        //                 // .name
                        //                 .eq(
                        //                     item.get_name(), // .name
                        //                 )
                        //         },
                        //     )
                        //     .unwrap_or(false);

                        // let active = state
                        //     .input
                        //     .input()
                        //     .trim()
                        //     .eq(&item.name)
                        //     || item
                        //         .aliases
                        //         .contains(
                        //             &state
                        //                 .input
                        //                 .input()
                        //                 .trim()
                        //                 .to_string(),
                        //         );
                        let style = if active
                        {
                            self.style
                                .menu_selected
                        }
                        else
                        {
                            self.style
                                .menu
                        };
                        Text::styled(
                            &content, style,
                        )
                        .render(
                            area, buf,
                        );
                    }
                }
            }

            Text::styled(
                &state.prefix,
                self.style
                    .input,
            )
            .render(
                prefix_area,
                buf,
            );

            InputWidget::new()
                .style(
                    self.style
                        .input,
                )
                .render(
                    input_area,
                    buf,
                    &mut state.input,
                );
        }
    }
}