hefesto-widgets 0.5.2

Ratatui widgets for the Hefesto TUI toolkit: popups, scrollable lists, trees, text input and spinners
Documentation
use ratatui::{
    buffer::Buffer,
    layout::{Constraint, Layout, Rect},
    style::{Color, Style},
    widgets::StatefulWidget,
};

use crate::popup::{Popup, PopupSize};
use crate::tree::{Tree, TreeNode, TreeState};
use crate::BorderType;

/// Popup que envuelve un `Tree` para mostrar una jerarquía expandible.
///
/// Compone un `Popup` (chrome) con un `Tree` (árbol).
/// Hereda la configuración del `Tree` a través de builders delegados.
#[derive(Clone)]
pub struct TreePopup<'a> {
    popup: Popup<'a>,
    tree: Tree<'a>,
}

impl<'a> TreePopup<'a> {
    pub fn new(nodes: Vec<TreeNode<'a>>) -> Self {
        Self {
            popup: Popup::new(Color::White)
                .padding(0)
                .border_type(BorderType::Rounded),
            tree: Tree::new(nodes),
        }
    }

    pub fn title(mut self, title: &'a str) -> Self {
        self.popup = self.popup.title(title);
        self
    }

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

    pub fn border_color(mut self, color: Color) -> Self {
        self.popup = self.popup.border_color(color);
        self
    }

    pub fn border_type(mut self, bt: BorderType) -> Self {
        self.popup = self.popup.border_type(bt);
        self
    }

    pub fn bg_color(mut self, color: Color) -> Self {
        self.popup = self.popup.bg_color(color);
        self
    }

    pub fn width(mut self, w: PopupSize) -> Self {
        self.popup = self.popup.width(w);
        self
    }

    pub fn height(mut self, h: PopupSize) -> Self {
        self.popup = self.popup.height(h);
        self
    }

    pub fn position(mut self, x: u16, y: u16) -> Self {
        self.popup = self.popup.position(x, y);
        self
    }

    pub fn origin(mut self, x: u16, y: u16) -> Self {
        self.popup = self.popup.origin(x, y);
        self
    }

    pub fn header(mut self) -> Self {
        self.popup = self.popup.header();
        self
    }

    pub fn padding(mut self, p: u16) -> Self {
        self.popup = self.popup.padding(p);
        self
    }

    pub fn resolve_rect(&self, area: Rect) -> Rect {
        self.popup.resolve_rect(area)
    }

    // Tree config delegation

    pub fn indent(mut self, n: u16) -> Self {
        self.tree = self.tree.indent(n);
        self
    }

    pub fn expand_icon(mut self, icon: &'a str) -> Self {
        self.tree = self.tree.expand_icon(icon);
        self
    }

    pub fn collapse_icon(mut self, icon: &'a str) -> Self {
        self.tree = self.tree.collapse_icon(icon);
        self
    }

    pub fn leaf_icon(mut self, icon: &'a str) -> Self {
        self.tree = self.tree.leaf_icon(icon);
        self
    }

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

    pub fn highlight_symbol(mut self, symbol: &'a str) -> Self {
        self.tree = self.tree.highlight_symbol(symbol);
        self
    }

    pub fn borders(mut self, borders: ratatui::widgets::Borders) -> Self {
        self.tree = self.tree.borders(borders);
        self
    }

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

impl StatefulWidget for TreePopup<'_> {
    type State = TreeState;

    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
        let tree_height = self.tree.visible_count(&state.expanded) as u16 + 2;
        let total_height = tree_height.max(5);
        let mut popup = self.popup;
        if popup.get_height() == PopupSize::Auto {
            popup = popup.height(PopupSize::Fixed(total_height));
        }
        let inner = popup.render_inner(area, buf);

        let chunks = Layout::vertical([
            Constraint::Min(0),
            Constraint::Min(0),
        ]).split(inner);

        self.tree.render(chunks[0], buf, state);
    }
}

#[cfg(test)]
mod tests;