1use alloc::{boxed::Box, vec::Vec};
8use rlvgl_core::{
9 event::Event,
10 renderer::Renderer,
11 widget::{Rect, Widget},
12};
13use rlvgl_widgets::container::Container;
14
15pub struct VStack {
20 bounds: Rect,
21 spacing: i32,
22 children: Vec<Box<dyn Widget>>,
23 next_y: i32,
24}
25
26impl VStack {
27 pub fn new(width: i32) -> Self {
29 Self {
30 bounds: Rect {
31 x: 0,
32 y: 0,
33 width,
34 height: 0,
35 },
36 spacing: 0,
37 children: Vec::new(),
38 next_y: 0,
39 }
40 }
41
42 pub fn spacing(mut self, spacing: i32) -> Self {
44 self.spacing = spacing;
45 self
46 }
47
48 pub fn child<W, F>(mut self, height: i32, builder: F) -> Self
50 where
51 W: Widget + 'static,
52 F: FnOnce(Rect) -> W,
53 {
54 let rect = Rect {
55 x: 0,
56 y: self.next_y,
57 width: self.bounds.width,
58 height,
59 };
60 self.next_y += height + self.spacing;
61 self.bounds.height = self.next_y - self.spacing;
62 self.children.push(Box::new(builder(rect)));
63 self
64 }
65}
66
67impl Widget for VStack {
68 fn bounds(&self) -> Rect {
69 self.bounds
70 }
71
72 fn draw(&self, renderer: &mut dyn Renderer) {
73 for child in &self.children {
74 child.draw(renderer);
75 }
76 }
77
78 fn handle_event(&mut self, event: &Event) -> bool {
79 for child in &mut self.children {
80 if child.handle_event(event) {
81 return true;
82 }
83 }
84 false
85 }
86}
87
88pub struct HStack {
94 bounds: Rect,
95 spacing: i32,
96 children: Vec<Box<dyn Widget>>,
97 next_x: i32,
98}
99
100impl HStack {
101 pub fn new(height: i32) -> Self {
103 Self {
104 bounds: Rect {
105 x: 0,
106 y: 0,
107 width: 0,
108 height,
109 },
110 spacing: 0,
111 children: Vec::new(),
112 next_x: 0,
113 }
114 }
115
116 pub fn spacing(mut self, spacing: i32) -> Self {
118 self.spacing = spacing;
119 self
120 }
121
122 pub fn child<W, F>(mut self, width: i32, builder: F) -> Self
124 where
125 W: Widget + 'static,
126 F: FnOnce(Rect) -> W,
127 {
128 let rect = Rect {
129 x: self.next_x,
130 y: 0,
131 width,
132 height: self.bounds.height,
133 };
134 self.next_x += width + self.spacing;
135 self.bounds.width = self.next_x - self.spacing;
136 self.children.push(Box::new(builder(rect)));
137 self
138 }
139}
140
141impl Widget for HStack {
142 fn bounds(&self) -> Rect {
143 self.bounds
144 }
145
146 fn draw(&self, renderer: &mut dyn Renderer) {
147 for child in &self.children {
148 child.draw(renderer);
149 }
150 }
151
152 fn handle_event(&mut self, event: &Event) -> bool {
153 for child in &mut self.children {
154 if child.handle_event(event) {
155 return true;
156 }
157 }
158 false
159 }
160}
161
162pub struct Grid {
164 bounds: Rect,
165 cols: i32,
166 cell_w: i32,
167 cell_h: i32,
168 spacing: i32,
169 children: Vec<Box<dyn Widget>>,
170 next: i32,
171}
172
173impl Grid {
174 pub fn new(cols: i32, cell_w: i32, cell_h: i32) -> Self {
176 Self {
177 bounds: Rect {
178 x: 0,
179 y: 0,
180 width: 0,
181 height: 0,
182 },
183 cols,
184 cell_w,
185 cell_h,
186 spacing: 0,
187 children: Vec::new(),
188 next: 0,
189 }
190 }
191
192 pub fn spacing(mut self, spacing: i32) -> Self {
194 self.spacing = spacing;
195 self
196 }
197
198 pub fn child<W, F>(mut self, builder: F) -> Self
200 where
201 W: Widget + 'static,
202 F: FnOnce(Rect) -> W,
203 {
204 let col = self.next % self.cols;
205 let row = self.next / self.cols;
206 let x = col * (self.cell_w + self.spacing);
207 let y = row * (self.cell_h + self.spacing);
208 let rect = Rect {
209 x,
210 y,
211 width: self.cell_w,
212 height: self.cell_h,
213 };
214 self.children.push(Box::new(builder(rect)));
215 self.next += 1;
216 let w = x + self.cell_w;
217 let h = y + self.cell_h;
218 if w > self.bounds.width {
219 self.bounds.width = w;
220 }
221 if h > self.bounds.height {
222 self.bounds.height = h;
223 }
224 self
225 }
226}
227
228impl Widget for Grid {
229 fn bounds(&self) -> Rect {
230 self.bounds
231 }
232
233 fn draw(&self, renderer: &mut dyn Renderer) {
234 for child in &self.children {
235 child.draw(renderer);
236 }
237 }
238
239 fn handle_event(&mut self, event: &Event) -> bool {
240 for child in &mut self.children {
241 if child.handle_event(event) {
242 return true;
243 }
244 }
245 false
246 }
247}
248
249pub struct BoxLayout {
251 inner: Container,
252}
253
254impl BoxLayout {
255 pub fn new(bounds: Rect) -> Self {
257 Self {
258 inner: Container::new(bounds),
259 }
260 }
261
262 pub fn style_mut(&mut self) -> &mut rlvgl_core::style::Style {
264 &mut self.inner.style
265 }
266}
267
268impl Widget for BoxLayout {
269 fn bounds(&self) -> Rect {
270 self.inner.bounds()
271 }
272
273 fn draw(&self, renderer: &mut dyn Renderer) {
274 self.inner.draw(renderer);
275 }
276
277 fn handle_event(&mut self, event: &Event) -> bool {
278 self.inner.handle_event(event)
279 }
280}
281
282#[cfg(test)]
283mod tests {
284 use super::*;
285 use rlvgl_widgets::label::Label;
286
287 #[test]
288 fn vstack_stacks_vertically() {
289 let stack = VStack::new(20)
290 .spacing(2)
291 .child(10, |r| Label::new("a", r))
292 .child(10, |r| Label::new("b", r));
293 assert_eq!(stack.bounds().height, 22);
294 }
295
296 #[test]
297 fn hstack_stacks_horizontally() {
298 let stack = HStack::new(10)
299 .spacing(1)
300 .child(5, |r| Label::new("a", r))
301 .child(5, |r| Label::new("b", r));
302 assert_eq!(stack.bounds().width, 11);
303 }
304
305 #[test]
306 fn grid_places_cells() {
307 let grid = Grid::new(2, 5, 5)
308 .spacing(1)
309 .child(|r| Label::new("a", r))
310 .child(|r| Label::new("b", r))
311 .child(|r| Label::new("c", r));
312 assert_eq!(grid.bounds().height, 11);
313 assert_eq!(grid.bounds().width, 11);
314 }
315}