pixel_widgets/widget/
column.rs

1use std::hash::{Hash, Hasher};
2
3use crate::draw::Primitive;
4use crate::event::Event;
5use crate::layout::{Rectangle, Size};
6use crate::node::{GenericNode, IntoNode, Node};
7use crate::style::Stylesheet;
8use crate::widget::Context;
9
10use super::Widget;
11
12/// Layout child widgets vertically
13pub struct Column<'a, T> {
14    children: Vec<Node<'a, T>>,
15    layout: Vec<Rectangle>,
16}
17
18impl<'a, T: 'a> Column<'a, T> {
19    /// Construct a new Column
20    pub fn new() -> Self {
21        Default::default()
22    }
23
24    /// Adds a child widget to the column
25    pub fn push<I: IntoNode<'a, T> + 'a>(mut self, item: I) -> Self {
26        self.children.push(item.into_node());
27        self
28    }
29
30    /// Adds child widgets using an iterator
31    pub fn extend<I: IntoIterator<Item = N>, N: IntoNode<'a, T> + 'a>(mut self, iter: I) -> Self {
32        self.children.extend(iter.into_iter().map(IntoNode::into_node));
33        self
34    }
35
36    fn layout(&mut self, layout: Rectangle, style: &Stylesheet) -> impl Iterator<Item = (&mut Node<'a, T>, Rectangle)> {
37        let layout = style.background.content_rect(layout, style.padding);
38        if self.layout.len() != self.children.len() {
39            let align = style.align_horizontal;
40            let available_parts = self.children.iter().map(|c| c.size().1.parts()).sum();
41            let available_space = layout.height() - self.children.iter().map(|c| c.size().1.min_size()).sum::<f32>();
42            let mut cursor = 0.0;
43            self.layout = self
44                .children
45                .iter()
46                .map(|child| {
47                    let (w, h) = child.size();
48                    let w = w.resolve(layout.width(), w.parts());
49                    let h = h
50                        .resolve(available_space, available_parts)
51                        .min(layout.height() - cursor);
52                    let x = align.resolve_start(w, layout.width());
53                    let y = cursor;
54
55                    cursor += h;
56                    Rectangle::from_xywh(x, y, w, h)
57                })
58                .collect();
59        }
60        self.children.iter_mut().zip(
61            self.layout
62                .iter()
63                .map(move |relative| relative.translate(layout.left, layout.top)),
64        )
65    }
66}
67
68impl<'a, T: 'a> Default for Column<'a, T> {
69    fn default() -> Self {
70        Self {
71            children: Vec::new(),
72            layout: Vec::new(),
73        }
74    }
75}
76
77impl<'a, T> Hash for Column<'a, T> {
78    fn hash<H: Hasher>(&self, state: &mut H) {
79        "column".hash(state)
80    }
81}
82
83impl<'a, T: 'a + Send> Widget<'a, T> for Column<'a, T> {
84    type State = ();
85
86    fn mount(&self) {}
87
88    fn widget(&self) -> &'static str {
89        "column"
90    }
91
92    fn len(&self) -> usize {
93        self.children.len()
94    }
95
96    fn visit_children(&mut self, visitor: &mut dyn FnMut(&mut dyn GenericNode<'a, T>)) {
97        self.children.iter_mut().for_each(|child| visitor(&mut **child));
98    }
99
100    fn size(&self, _: &(), style: &Stylesheet) -> (Size, Size) {
101        let width = match style.width {
102            Size::Shrink => Size::Exact(self.children.iter().fold(0.0, |size, child| match child.size().0 {
103                Size::Exact(child_size) => size.max(child_size),
104                _ => size,
105            })),
106            other => other,
107        };
108        let height = match style.height {
109            Size::Shrink => Size::Exact(self.children.iter().fold(0.0, |size, child| match child.size().1 {
110                Size::Exact(child_size) => size + child_size,
111                _ => size,
112            })),
113            other => other,
114        };
115
116        style
117            .background
118            .resolve_size((style.width, style.height), (width, height), style.padding)
119    }
120
121    fn focused(&self, _: &()) -> bool {
122        self.children.iter().any(|child| child.focused())
123    }
124
125    fn event(
126        &mut self,
127        _: &mut (),
128        layout: Rectangle,
129        clip: Rectangle,
130        stylesheet: &Stylesheet,
131        event: Event,
132        context: &mut Context<T>,
133    ) {
134        let focused = self.children.iter().position(|child| child.focused());
135
136        for (index, (child, layout)) in self.layout(layout, stylesheet).enumerate() {
137            if Some(index) == focused {
138                child.event(layout, clip, event, context);
139            } else if focused.is_none() {
140                if let Some(clip) = clip.intersect(&layout) {
141                    child.event(layout, clip, event, context);
142                }
143            }
144        }
145    }
146
147    fn draw(&mut self, _: &mut (), layout: Rectangle, clip: Rectangle, stylesheet: &Stylesheet) -> Vec<Primitive<'a>> {
148        let mut result = Vec::new();
149
150        result.extend(stylesheet.background.render(layout));
151
152        result = self
153            .layout(layout, stylesheet)
154            .fold(result, |mut result, (child, layout)| {
155                result.extend(child.draw(layout, clip));
156                result
157            });
158
159        result
160    }
161}
162
163impl<'a, T: 'a + Send> IntoNode<'a, T> for Column<'a, T> {
164    fn into_node(self) -> Node<'a, T> {
165        Node::from_widget(self)
166    }
167}