hefesto-widgets 0.3.0

Ratatui widgets for the Hefesto TUI toolkit
Documentation
use ratatui::{
    buffer::Buffer,
    layout::{Constraint, Layout, Rect},
    style::{Color, Style},
    widgets::{Borders, StatefulWidget},
};

use crate::popup::{Popup, PopupSize};
use crate::text_input::{TextInput, TextInputState};
use crate::BorderType;

#[derive(Clone)]
pub struct TextInputPopup<'a> {
    popup: Popup<'a>,
    text_input: TextInput<'a>,
}

impl<'a> TextInputPopup<'a> {
    pub fn new() -> Self {
        Self {
            popup: Popup::new(Color::White)
                .padding(0)
                .border_type(BorderType::Rounded),
            text_input: TextInput::new()
                .borders(Borders::NONE)
                .border_type(BorderType::None),
        }
    }

    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.text_input = self.text_input.cursor_style(style);
        self
    }

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

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

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

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

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

    pub fn scroll_reserve(mut self, reserve: u16) -> Self {
        self.text_input = self.text_input.scroll_reserve(reserve);
        self
    }

    pub fn rows(mut self, rows: u16) -> Self {
        self.text_input = self.text_input.rows(rows);
        self
    }

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

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

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

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

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

    #[allow(dead_code)]
    pub fn position(mut self, pos: Rect) -> Self {
        self.popup = self.popup.position(pos);
        self
    }

    #[allow(dead_code)]
    pub fn resolve_rect(&self, area: Rect) -> Rect {
        self.popup.resolve_rect(area)
    }
}

impl StatefulWidget for TextInputPopup<'_> {
    type State = TextInputState;

    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
        let input_height = self.text_input.required_height();
        let total_height = input_height + 2; // popup border top/bottom

        let inner = self
            .popup
            .height(PopupSize::Fixed(total_height))
            .render_inner(area, buf);

        // Split inner area to place text_input in center
        let chunks = Layout::vertical([
            Constraint::Min(0),
            Constraint::Length(input_height),
            Constraint::Min(0),
        ]).split(inner);

        self.text_input.render(chunks[1], buf, state);
    }
}