lv-tui 0.2.0

A reactive TUI framework for Rust, inspired by Textual and React
Documentation
use crate::buffer::CellStyle;
use crate::geom::Insets;

/// Theme mode for terminal colors.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Theme {
    Dark,
    Light,
}

thread_local! {
    /// Current global theme. Set via [`set_theme`].
    pub static CURRENT_THEME: std::cell::RefCell<Theme> = std::cell::RefCell::new(Theme::Dark);
}

/// Sets the global theme (Dark or Light) and forces a full repaint.
pub fn set_theme(theme: Theme) {
    CURRENT_THEME.with(|t| *t.borrow_mut() = theme);
    // Force a full repaint so color changes take effect immediately.
    crate::runtime::FORCE_FULL_PAINT.with(|f| *f.borrow_mut() = true);
}

/// Terminal color palette.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Color {
    Black,
    Red,
    Green,
    Yellow,
    Blue,
    Magenta,
    Cyan,
    White,
    Gray,
}

impl From<Color> for crossterm::style::Color {
    fn from(c: Color) -> Self {
        let dark = CURRENT_THEME.with(|t| *t.borrow() == Theme::Dark);
        match (c, dark) {
            (Color::Black, _) => crossterm::style::Color::Black,
            (Color::Red, true) => crossterm::style::Color::DarkRed,
            (Color::Red, false) => crossterm::style::Color::Red,
            (Color::Green, true) => crossterm::style::Color::DarkGreen,
            (Color::Green, false) => crossterm::style::Color::Green,
            (Color::Yellow, true) => crossterm::style::Color::DarkYellow,
            (Color::Yellow, false) => crossterm::style::Color::Yellow,
            (Color::Blue, true) => crossterm::style::Color::DarkBlue,
            (Color::Blue, false) => crossterm::style::Color::Blue,
            (Color::Magenta, true) => crossterm::style::Color::DarkMagenta,
            (Color::Magenta, false) => crossterm::style::Color::Magenta,
            (Color::Cyan, true) => crossterm::style::Color::DarkCyan,
            (Color::Cyan, false) => crossterm::style::Color::Cyan,
            (Color::White, _) => crossterm::style::Color::Grey,
            (Color::Gray, true) => crossterm::style::Color::DarkGrey,
            (Color::Gray, false) => crossterm::style::Color::Grey,
        }
    }
}

/// 长度规格
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Length {
    /// 内容自适应
    Auto,
    /// 固定字符格数
    Fixed(u16),
    /// 父容器可用空间的百分比
    Percent(u16),
    /// 参与剩余空间按比例分配
    Fraction(u16),
}

impl Default for Length {
    fn default() -> Self {
        Length::Auto
    }
}

/// 布局方向
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Layout {
    None,
    Vertical,
    Horizontal,
}

impl Default for Layout {
    fn default() -> Self {
        Layout::None
    }
}

/// 文本换行模式
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum TextWrap {
    /// 不换行,超出 clip 截断
    #[default]
    None,
    /// 按字符边界换行
    Char,
}

/// 文本对齐
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum TextAlign {
    #[default]
    Left,
    Center,
    Right,
}

/// 文本截断模式
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum TextTruncate {
    /// 不截断
    #[default]
    None,
    /// 末尾追加省略号 …
    Ellipsis,
}

/// 边框样式
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Border {
    #[default]
    None,
    Plain,
    Rounded,
    Double,
}

/// 组件样式
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Style {
    // 视觉效果
    pub fg: Option<Color>,
    pub bg: Option<Color>,
    pub bold: bool,
    pub italic: bool,
    pub underline: bool,

    // 布局
    pub width: Length,
    pub height: Length,
    pub padding: Insets,
    pub margin: Insets,
    pub layout: Layout,
    pub gap: u16,
    pub flex_grow: u16,
    pub flex_shrink: bool,
    pub border: Border,
}

impl Default for Style {
    fn default() -> Self {
        Self {
            fg: None,
            bg: None,
            bold: false,
            italic: false,
            underline: false,
            width: Length::Auto,
            height: Length::Auto,
            padding: Insets::ZERO,
            margin: Insets::ZERO,
            layout: Layout::None,
            gap: 0,
            flex_grow: 0,
            flex_shrink: true,
            border: Border::None,
        }
    }
}

impl Style {
    /// Builder: sets the foreground color.
    pub fn fg(mut self, color: Color) -> Self {
        self.fg = Some(color);
        self
    }

    /// Builder: sets the background color.
    pub fn bg(mut self, color: Color) -> Self {
        self.bg = Some(color);
        self
    }

    /// Builder: enables bold.
    pub fn bold(mut self) -> Self {
        self.bold = true;
        self
    }

    /// Builder: enables italic.
    pub fn italic(mut self) -> Self {
        self.italic = true;
        self
    }

    /// Builder: enables underline.
    pub fn underline(mut self) -> Self {
        self.underline = true;
        self
    }

    /// Builder: sets the width constraint.
    pub fn width(mut self, width: Length) -> Self {
        self.width = width;
        self
    }

    /// Builder: sets the height constraint.
    pub fn height(mut self, height: Length) -> Self {
        self.height = height;
        self
    }

    /// Builder: sets uniform padding on all four sides.
    pub fn padding(mut self, value: u16) -> Self {
        self.padding = Insets::all(value);
        self
    }

    /// Builder: sets uniform margin on all four sides.
    pub fn margin(mut self, value: u16) -> Self {
        self.margin = Insets::all(value);
        self
    }

    /// Builder: sets the layout direction.
    pub fn layout(mut self, layout: Layout) -> Self {
        self.layout = layout;
        self
    }

    /// Builder: sets the gap between children.
    pub fn gap(mut self, gap: u16) -> Self {
        self.gap = gap;
        self
    }

    /// Builder: sets flex grow factor (0 = don't grow).
    pub fn flex_grow(mut self, grow: u16) -> Self {
        self.flex_grow = grow;
        self
    }

    /// Builder: sets whether the item can shrink below intrinsic size.
    pub fn flex_shrink(mut self, shrink: bool) -> Self {
        self.flex_shrink = shrink;
        self
    }

    /// Builder: sets the border style.
    pub fn border(mut self, border: Border) -> Self {
        self.border = border;
        self
    }

    /// 转换为 CellStyle
    pub fn into_cell_style(&self) -> CellStyle {
        CellStyle {
            fg: self.fg,
            bg: self.bg,
            bold: self.bold,
            italic: self.italic,
            underline: self.underline,
        }
    }
}