Skip to main content

lv_tui/widgets/
column.rs

1use crate::component::{Component, EventCx, LayoutCx, MeasureCx};
2use crate::event::Event;
3use crate::geom::{Rect, Size};
4use crate::layout::{layout_vertical, Constraint, LayoutItem};
5use crate::node::Node;
6use crate::render::RenderCx;
7use crate::style::{Layout, Length, Style};
8
9/// A vertical layout container.
10///
11/// Children are stacked top-to-bottom. Use [`Column::child`] to add children,
12/// [`Column::padding`] and [`Column::gap`] to control spacing.
13pub struct Column {
14    children: Vec<Node>,
15    style: Style,
16}
17
18impl Column {
19    /// Creates an empty vertical layout.
20    pub fn new() -> Self {
21        Self {
22            children: Vec::new(),
23            style: Style::default().layout(Layout::Vertical),
24        }
25    }
26
27    pub fn child(mut self, component: impl Component + 'static) -> Self {
28        self.children.push(Node::new(component));
29        self
30    }
31
32    pub fn padding(mut self, value: u16) -> Self {
33        self.style = self.style.padding(value);
34        self
35    }
36
37    pub fn gap(mut self, value: u16) -> Self {
38        self.style = self.style.gap(value);
39        self
40    }
41
42    pub fn style(mut self, style: Style) -> Self {
43        self.style = style;
44        self
45    }
46}
47
48impl Component for Column {
49    fn render(&self, cx: &mut RenderCx) {
50        for child in &self.children {
51            child.render_with_parent(cx.buffer, cx.focused_id, cx.clip_rect, cx.wrap, cx.truncate, cx.align, Some(&cx.style));
52        }
53    }
54
55    fn measure(&self, constraint: Constraint, _cx: &mut MeasureCx) -> Size {
56        let mut height = self.style.padding.top + self.style.padding.bottom;
57        let mut max_width: u16 = 0;
58
59        for (i, child) in self.children.iter().enumerate() {
60            let child_size = child.measure(constraint);
61            height = height.saturating_add(child_size.height);
62            max_width = max_width.max(child_size.width);
63            if i < self.children.len().saturating_sub(1) {
64                height = height.saturating_add(self.style.gap);
65            }
66        }
67
68        let width = max_width
69            .saturating_add(self.style.padding.left)
70            .saturating_add(self.style.padding.right)
71            .min(constraint.max.width);
72
73        Size { width, height }
74    }
75
76    fn focusable(&self) -> bool {
77        false
78    }
79
80    fn event(&mut self, event: &Event, cx: &mut EventCx) {
81        if matches!(event, Event::Focus | Event::Blur | Event::Tick) { return; }
82        for child in &mut self.children {
83            let mut child_cx = EventCx::new(
84                &mut child.dirty,
85                cx.global_dirty,
86                cx.quit,
87                cx.phase,
88                cx.propagation_stopped,
89            );
90            child.component.event(event, &mut child_cx);
91        }
92    }
93
94    fn layout(&mut self, rect: Rect, _cx: &mut LayoutCx) {
95        let inner = rect.inner(self.style.padding);
96
97        let child_constraint = Constraint::loose(inner.width, inner.height);
98
99        let items: Vec<LayoutItem> = self
100            .children
101            .iter()
102            .map(|child| {
103                let s = child.component.style();
104                LayoutItem {
105                    width: s.width,
106                    height: match s.height {
107                        Length::Auto => {
108                            let measured = child.measure(child_constraint);
109                            Length::Fixed(measured.height)
110                        }
111                        other => other,
112                    },
113                    margin: s.margin,
114                    flex_grow: s.flex_grow,
115                    flex_shrink: s.flex_shrink,
116                }
117            })
118            .collect();
119
120        let child_rects = layout_vertical(inner, &items, self.style.gap);
121
122        for (child, child_rect) in self.children.iter_mut().zip(child_rects.iter()) {
123            child.layout(*child_rect);
124        }
125    }
126
127    fn for_each_child(&self, f: &mut dyn FnMut(&Node)) {
128        for child in &self.children {
129            f(child);
130        }
131    }
132
133    fn for_each_child_mut(&mut self, f: &mut dyn FnMut(&mut Node)) {
134        for child in &mut self.children {
135            f(child);
136        }
137    }
138
139    fn style(&self) -> Style {
140        self.style.clone()
141    }
142}