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) {
82            return;
83        }
84        for child in &mut self.children {
85            let mut child_cx = EventCx::new(
86                &mut child.dirty,
87                cx.global_dirty,
88                cx.quit,
89                cx.phase,
90                cx.propagation_stopped,
91            );
92            child.component.event(event, &mut child_cx);
93        }
94    }
95
96    fn layout(&mut self, rect: Rect, _cx: &mut LayoutCx) {
97        let inner = rect.inner(self.style.padding);
98
99        let child_constraint = Constraint::loose(inner.width, inner.height);
100
101        let items: Vec<LayoutItem> = self
102            .children
103            .iter()
104            .map(|child| {
105                let s = child.component.style();
106                LayoutItem {
107                    width: s.width,
108                    height: match s.height {
109                        Length::Auto => {
110                            let measured = child.measure(child_constraint);
111                            Length::Fixed(measured.height)
112                        }
113                        other => other,
114                    },
115                    margin: s.margin,
116                    flex_grow: s.flex_grow,
117                    flex_shrink: s.flex_shrink,
118                }
119            })
120            .collect();
121
122        let child_rects = layout_vertical(inner, &items, self.style.gap);
123
124        for (child, child_rect) in self.children.iter_mut().zip(child_rects.iter()) {
125            child.layout(*child_rect);
126        }
127    }
128
129    fn for_each_child(&self, f: &mut dyn FnMut(&Node)) {
130        for child in &self.children {
131            f(child);
132        }
133    }
134
135    fn for_each_child_mut(&mut self, f: &mut dyn FnMut(&mut Node)) {
136        for child in &mut self.children {
137            f(child);
138        }
139    }
140
141    fn style(&self) -> Style {
142        self.style.clone()
143    }
144}