1use alloc::vec::Vec;
2use embedded_graphics::geometry::Point;
3
4use crate::{
5 align::{Alignment, Axis, HorizontalAlign, VerticalAlign},
6 el::El,
7 event::Event,
8 padding::Padding,
9 render::Renderer,
10 size::{Bounds, Length, Size},
11 state::StateNode,
12 ui::UiCtx,
13 widget::Widget,
14};
15
16#[derive(Clone, Copy)]
20pub enum Position {
21 Relative,
22 Absolute,
23}
24
25#[derive(Clone, Copy)]
26pub struct Viewport {
27 pub size: Size,
28}
29
30#[derive(Clone)]
31pub struct LayoutNode {
32 position: Position,
33 bounds: Bounds,
34 children: Vec<LayoutNode>,
35}
36
37impl LayoutNode {
38 pub fn new(size: Size) -> Self {
39 Self {
40 position: Position::Relative,
41 bounds: Bounds { position: Point::zero(), size },
42 children: vec![],
43 }
44 }
45
46 pub fn with_children(size: Size, children: impl IntoIterator<Item = LayoutNode>) -> Self {
47 Self {
48 position: Position::Relative,
49 bounds: Bounds { position: Point::zero(), size },
50 children: children.into_iter().collect(),
51 }
52 }
53
54 pub fn absolute(size: Size) -> Self {
55 Self {
56 position: Position::Absolute,
57 bounds: Bounds { position: Point::zero(), size },
58 children: vec![],
59 }
60 }
61
62 pub fn position(&self) -> Position {
63 self.position
64 }
65
66 pub fn moved(mut self, to: impl Into<Point>) -> Self {
67 self.move_mut(to);
68 self
69 }
70
71 pub fn move_mut(&mut self, to: impl Into<Point>) -> &mut Self {
72 self.bounds.position = to.into();
73 self
74 }
75
76 pub fn align_mut(
77 &mut self,
78 horizontal: Alignment,
79 vertical: Alignment,
80 parent_size: Size,
81 ) -> &mut Self {
82 match horizontal {
83 Alignment::Start => {},
84 Alignment::Center => {
85 self.bounds.position.x +=
86 (parent_size.width as i32 - self.bounds.size.width as i32) / 2;
87 },
88 Alignment::End => {
89 self.bounds.position.x += parent_size.width as i32 - self.bounds.size.width as i32;
90 },
91 }
92
93 match vertical {
94 Alignment::Start => {},
95 Alignment::Center => {
96 self.bounds.position.y +=
97 (parent_size.height as i32 - self.bounds.size.height as i32) / 2;
98 },
99 Alignment::End => {
100 self.bounds.position.y += parent_size.width as i32 - self.bounds.size.width as i32;
101 },
102 }
103
104 self
105 }
106
107 pub fn aligned(
108 mut self,
109 horizontal: Alignment,
110 vertical: Alignment,
111 parent_size: Size,
112 ) -> Self {
113 self.align_mut(horizontal, vertical, parent_size);
114 self
115 }
116
117 pub fn size(&self) -> Size {
118 self.bounds.size
119 }
120}
121
122impl Default for LayoutNode {
123 fn default() -> Self {
124 Self::new(Size::zero())
125 }
126}
127
128#[derive(Clone)]
129pub struct Layout<'a> {
130 viewport_position: Point,
132 node: &'a LayoutNode,
133}
134
135impl<'a> Layout<'a> {
136 pub fn new(node: &'a LayoutNode) -> Self {
137 Self { viewport_position: node.bounds.position.into(), node }
138 }
139
140 pub fn with_offset(offset: Point, node: &'a LayoutNode) -> Self {
141 let bounds = node.bounds;
142
143 let offset = match node.position {
144 Position::Relative => offset,
145 Position::Absolute => Point::zero(),
146 };
147
148 Self { viewport_position: bounds.position + offset, node }
149 }
150
151 pub fn children(self) -> impl DoubleEndedIterator<Item = Layout<'a>> {
153 self.node
154 .children
155 .iter()
156 .map(move |child| Layout::with_offset(self.viewport_position, child))
157 }
158
159 pub fn bounds(&self) -> Bounds {
161 Bounds { position: self.viewport_position, size: self.node.bounds.size }
162 }
163
164 pub fn sized(
165 limits: &Limits,
166 size: impl Into<Size<Length>>,
167 position: Position,
168 viewport: &Viewport,
169 content_limits: impl FnOnce(&Limits) -> Size,
170 ) -> LayoutNode {
171 let size = size.into();
172
173 let limits = limits
174 .for_position(position, viewport)
175 .limit_width(size.width)
176 .limit_height(size.height);
177 let content_size = content_limits(&limits);
178
179 LayoutNode::new(limits.resolve_size(size.width, size.height, content_size))
180 }
181
182 pub fn container(
183 limits: &Limits,
184 size: impl Into<Size<Length>>,
185 position: Position,
186 viewport: &Viewport,
187 padding: impl Into<Padding>,
188 border: impl Into<Padding>,
189 content_align_h: Alignment,
190 content_align_v: Alignment,
191 content_layout: impl FnOnce(&Limits) -> LayoutNode,
192 ) -> LayoutNode {
194 let size = size.into();
195 let padding = padding.into();
196 let border = border.into();
197
198 let full_padding = padding + border;
199
200 let limits = limits
201 .for_position(position, viewport)
202 .limit_width(size.width)
203 .limit_height(size.height);
204 let content = content_layout(&limits.shrink(full_padding));
205 let fit_padding = full_padding.fit(content.size(), limits.max());
206
207 let size = limits.shrink(fit_padding).resolve_size(size.width, size.height, content.size());
208 let content_offset = full_padding.top_left();
209
210 let content = content.moved(content_offset).aligned(content_align_h, content_align_v, size);
211
212 LayoutNode::with_children(size.expand(fit_padding), vec![content])
213 }
214
215 pub fn flex<Message, R: Renderer, E: Event, S>(
216 ctx: &mut UiCtx<Message>,
217 state_tree: &mut StateNode,
218 styler: &S,
219 axis: Axis,
220 limits: &Limits,
221 size: impl Into<Size<Length>>,
222 position: Position,
223 viewport: &Viewport,
224 padding: impl Into<Padding>,
225 gap: u32,
226 align: Alignment,
227 children: &[El<'_, Message, R, E, S>],
228 ) -> LayoutNode {
229 let size = size.into();
230 let padding = padding.into();
231
232 let limits = limits
233 .for_position(position, viewport)
234 .limit_width(size.width)
235 .limit_height(size.height)
236 .shrink(padding);
237 let total_gap = gap * children.len().saturating_sub(1) as u32;
238 let max_anti = axis.size_anti(limits.max());
239
240 let mut layout_children = Vec::with_capacity(children.len());
241 layout_children.resize(children.len(), LayoutNode::default());
242
243 let mut total_main_divs = 0;
244
245 let mut free_main = axis.size_main(limits.max()).saturating_sub(total_gap);
246 let mut free_anti = match axis {
247 Axis::X if size.width == Length::Shrink => 0,
248 Axis::Y if size.height == Length::Shrink => 0,
249 _ => max_anti,
250 };
251
252 for ((i, child), child_state) in
254 children.iter().enumerate().zip(state_tree.children.iter_mut())
255 {
256 match child.position() {
257 Position::Absolute => {
258 layout_children[i] = child.layout(ctx, child_state, styler, &limits, viewport);
259 },
260 Position::Relative => {
261 let (fill_main_div, fill_anti_div) = {
262 let size = child.size();
263 axis.canon(size.width.div_factor(), size.height.div_factor())
264 };
265
266 if fill_main_div == 0 {
267 let (max_width, max_height) = axis.canon(
268 free_main,
269 if fill_anti_div == 0 { max_anti } else { free_anti },
270 );
271
272 let child_limits =
273 Limits::new(Size::zero(), Size::new(max_width, max_height));
274
275 let layout =
276 child.layout(ctx, child_state, styler, &child_limits, viewport);
277 let size = layout.size();
278
279 free_main -= axis.size_main(size);
280 free_anti = free_anti.max(axis.size_anti(size));
281
282 layout_children[i] = layout;
283 } else {
284 total_main_divs += fill_main_div as u32;
285 }
286 },
287 }
288 }
289
290 let remaining = match axis {
292 Axis::X => match size.width {
293 Length::Shrink => 0,
294 _ => free_main.max(0),
295 },
296 Axis::Y => match size.height {
297 Length::Shrink => 0,
298 _ => free_main.max(0),
299 },
300 };
301 let remaining_div = remaining.checked_div(total_main_divs).unwrap_or(0);
302 let mut remaining_mod = remaining.checked_rem(total_main_divs).unwrap_or(0);
303
304 for ((i, child), child_state) in
306 children.iter().enumerate().zip(state_tree.children.iter_mut())
307 {
308 if let Position::Relative = child.position() {
309 let (fill_main_div, fill_anti_div) = {
310 let size = child.size();
311
312 axis.canon(size.width.div_factor(), size.height.div_factor())
313 };
314
315 if fill_main_div != 0 {
316 let max_main = if total_main_divs == 0 {
317 remaining
318 } else {
319 remaining_div * fill_main_div as u32
320 + if remaining_mod > 0 {
321 remaining_mod -= 1;
322 1
323 } else {
324 0
325 }
326 };
327 let min_main = 0;
328
329 let (min_width, min_height) = axis.canon(min_main, 0);
330 let (max_width, max_height) =
331 axis.canon(max_main, if fill_anti_div == 0 { max_anti } else { free_anti });
332
333 let child_limits = Limits::new(
334 Size::new(min_width, min_height),
335 Size::new(max_width, max_height),
336 );
337
338 let layout = child.layout(ctx, child_state, styler, &child_limits, viewport);
339 free_anti = free_anti.max(axis.size_anti(layout.size()));
340 layout_children[i] = layout;
341 }
342 }
343 }
344
345 let (main_padding, anti_padding) = axis.canon(padding.left, padding.right);
346 let mut main_offset = main_padding;
347
348 for (i, node) in layout_children.iter_mut().enumerate() {
349 if let Position::Relative = node.position() {
350 if i > 0 {
351 main_offset += gap;
352 }
353
354 let (x, y) = axis.canon(main_offset as i32, anti_padding as i32);
355 node.move_mut(Point::new(x, y));
356
357 match axis {
358 Axis::X => node.align_mut(align, Alignment::Start, Size::new(0, free_anti)),
359 Axis::Y => node.align_mut(Alignment::Start, align, Size::new(free_anti, 0)),
360 };
361
362 let size = node.size();
363
364 main_offset += axis.size_main(size);
365 }
366 }
367
368 let (content_width, content_height) = axis.canon(main_offset - main_padding, free_anti);
369 let size =
370 limits.resolve_size(size.width, size.height, Size::new(content_width, content_height));
371
372 LayoutNode::with_children(size.expand(padding), layout_children)
373 }
374}
375
376#[derive(Clone, Copy)]
377pub struct Limits {
378 min: Size<u32>,
379 max: Size<u32>,
380}
381
382impl Limits {
383 pub fn new(min: Size<u32>, max: Size<u32>) -> Self {
384 Self { min, max }
385 }
386
387 pub fn only_max(max: Size<u32>) -> Self {
388 Self { min: Size::zero(), max }
389 }
390
391 pub fn min(&self) -> Size<u32> {
392 self.min
393 }
394
395 pub fn max(&self) -> Size<u32> {
396 self.max
397 }
398
399 pub fn min_square(&self) -> u32 {
400 self.min().width.min(self.min().height)
401 }
402
403 pub fn max_square(&self) -> u32 {
404 self.max().width.min(self.max().height)
405 }
406
407 pub fn for_position(&self, position: Position, viewport: &Viewport) -> Self {
408 match position {
409 Position::Relative => *self,
410 Position::Absolute => Limits::new(self.min, viewport.size),
412 }
413 }
414
415 pub fn limit_width(self, width: impl Into<Length>) -> Self {
416 match width.into() {
417 Length::Shrink | Length::Div(_) | Length::Fill => self,
418 Length::Fixed(fixed) => {
419 let new_width = fixed.min(self.max.width).max(self.min.width);
420
421 Self::new(self.min.new_width(new_width), self.max.new_width(new_width))
422 },
423 }
424 }
425
426 pub fn limit_height(self, height: impl Into<Length>) -> Self {
427 match height.into() {
428 Length::Shrink | Length::Div(_) | Length::Fill => self,
429 Length::Fixed(fixed) => {
430 let new_height = fixed.min(self.max.height).max(self.min.height);
431
432 Self::new(self.min.new_height(new_height), self.max.new_height(new_height))
433 },
434 }
435 }
436
437 pub fn shrink(self, by: impl Into<Size>) -> Self {
438 let by = by.into();
439
440 Limits::new(self.min() - by, self.max() - by)
441 }
442
443 pub fn resolve_size(
444 &self,
445 width: impl Into<Length>,
446 height: impl Into<Length>,
447 content_size: Size<u32>,
448 ) -> Size<u32> {
449 let width = match width.into() {
450 Length::Fill | Length::Div(_) => self.max.width,
451 Length::Fixed(fixed) => fixed.min(self.max.width).max(self.min.width),
452 Length::Shrink => content_size.width.min(self.max.width).max(self.min.width),
453 };
454
455 let height = match height.into() {
456 Length::Fill | Length::Div(_) => self.max.height,
457 Length::Fixed(fixed) => fixed.min(self.max.height).max(self.min.height),
458 Length::Shrink => content_size.height.min(self.max.height).max(self.min.height),
459 };
460
461 Size::new(width, height)
462 }
463
464 pub fn resolve_square(&self, size: impl Into<Length>) -> u32 {
465 let min_square = self.min_square();
466 let max_square = self.max_square();
467
468 match size.into() {
469 Length::Fill | Length::Div(_) => max_square,
470 Length::Fixed(fixed) => fixed.min(max_square).max(min_square),
471 Length::Shrink => min_square,
472 }
473 }
474}
475
476impl From<Bounds> for Limits {
477 fn from(value: Bounds) -> Self {
478 Self::new(Size::zero(), value.size.into())
479 }
480}