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
12pub struct Column<'a, T> {
14 children: Vec<Node<'a, T>>,
15 layout: Vec<Rectangle>,
16}
17
18impl<'a, T: 'a> Column<'a, T> {
19 pub fn new() -> Self {
21 Default::default()
22 }
23
24 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 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}