lv-tui 0.4.0

A reactive TUI framework for Rust
Documentation
use crate::component::{Component, EventCx, LayoutCx, MeasureCx};
use crate::event::Event;
use crate::geom::{Rect, Size};
use crate::layout::{layout_horizontal, Constraint, LayoutItem};
use crate::node::Node;
use crate::render::RenderCx;
use crate::style::{Layout, Length, Style};

/// 水平排列容器 widget
pub struct Row {
    children: Vec<Node>,
    style: Style,
}

impl Row {
    pub fn new() -> Self {
        Self {
            children: Vec::new(),
            style: Style::default().layout(Layout::Horizontal),
        }
    }

    pub fn child(mut self, component: impl Component + 'static) -> Self {
        self.children.push(Node::new(component));
        self
    }

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

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

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

impl Component for Row {
    fn render(&self, cx: &mut RenderCx) {
        for child in &self.children {
            child.render_with_parent(cx.buffer, cx.focused_id, cx.clip_rect, cx.wrap, cx.truncate, cx.align, Some(&cx.style));
        }
    }

    fn for_each_child(&self, f: &mut dyn FnMut(&Node)) {
        for child in &self.children {
            f(child);
        }
    }

    fn for_each_child_mut(&mut self, f: &mut dyn FnMut(&mut Node)) {
        for child in &mut self.children {
            f(child);
        }
    }

    fn measure(&self, constraint: Constraint, _cx: &mut MeasureCx) -> Size {
        let mut width = self.style.padding.left + self.style.padding.right;
        let mut max_height: u16 = 0;

        for (i, child) in self.children.iter().enumerate() {
            let child_size = child.measure(constraint);
            width = width.saturating_add(child_size.width);
            max_height = max_height.max(child_size.height);
            if i < self.children.len().saturating_sub(1) {
                width = width.saturating_add(self.style.gap);
            }
        }

        let height = max_height
            .saturating_add(self.style.padding.top)
            .saturating_add(self.style.padding.bottom)
            .min(constraint.max.height);

        Size { width, height }
    }

    fn focusable(&self) -> bool {
        false
    }


    fn event(&mut self, event: &Event, cx: &mut EventCx) {
        if matches!(event, Event::Focus | Event::Blur | Event::Tick) { return; }

        for child in &mut self.children {
            let mut child_cx = EventCx::new(
                &mut child.dirty,
                cx.global_dirty,
                cx.quit,
                cx.phase,
                cx.propagation_stopped,
            );
            child.component.event(event, &mut child_cx);
        }
    }

    fn layout(&mut self, rect: Rect, _cx: &mut LayoutCx) {
        let inner = rect.inner(self.style.padding);

        let child_constraint = Constraint::loose(inner.width, inner.height);

        let items: Vec<LayoutItem> = self
            .children
            .iter()
            .map(|child| {
                let s = child.component.style();
                LayoutItem {
                    width: match s.width {
                        Length::Auto => {
                            let measured = child.measure(child_constraint);
                            Length::Fixed(measured.width)
                        }
                        other => other,
                    },
                    height: s.height,
                    margin: s.margin,
                    flex_grow: s.flex_grow,
                    flex_shrink: s.flex_shrink,
                }
            })
            .collect();

        let child_rects = layout_horizontal(inner, &items, self.style.gap);

        for (child, child_rect) in self.children.iter_mut().zip(child_rects.iter()) {
            child.layout(*child_rect);
        }
    }

    fn style(&self) -> Style {
        self.style.clone()
    }
}