1use crate::{
2 layout::{CoordsMapping, Layout, LayoutEngine, LayoutItem, LayoutNode},
3 widget::{
4 unit::{
5 area::AreaBox,
6 content::ContentBox,
7 flex::FlexBox,
8 grid::GridBox,
9 image::{ImageBox, ImageBoxSizeValue},
10 size::{SizeBox, SizeBoxAspectRatio, SizeBoxSizeValue},
11 text::{TextBox, TextBoxSizeValue},
12 WidgetUnit,
13 },
14 utils::{lerp, Rect, Vec2},
15 WidgetId,
16 },
17 Scalar,
18};
19use std::collections::HashMap;
20
21#[derive(Debug, Default, Copy, Clone)]
22pub struct DefaultLayoutEngine;
23
24impl DefaultLayoutEngine {
25 pub fn layout_node(size_available: Vec2, unit: &WidgetUnit) -> Option<LayoutNode> {
26 match unit {
27 WidgetUnit::None | WidgetUnit::PortalBox(_) => None,
28 WidgetUnit::AreaBox(b) => Self::layout_area_box(size_available, b),
29 WidgetUnit::ContentBox(b) => Self::layout_content_box(size_available, b),
30 WidgetUnit::FlexBox(b) => Self::layout_flex_box(size_available, b),
31 WidgetUnit::GridBox(b) => Self::layout_grid_box(size_available, b),
32 WidgetUnit::SizeBox(b) => Self::layout_size_box(size_available, b),
33 WidgetUnit::ImageBox(b) => Self::layout_image_box(size_available, b),
34 WidgetUnit::TextBox(b) => Self::layout_text_box(size_available, b),
35 }
36 }
37
38 pub fn layout_area_box(size_available: Vec2, unit: &AreaBox) -> Option<LayoutNode> {
39 if !unit.id.is_valid() {
40 return None;
41 }
42 let (children, w, h) = if let Some(child) = Self::layout_node(size_available, &unit.slot) {
43 let w = child.local_space.width();
44 let h = child.local_space.height();
45 (vec![child], w, h)
46 } else {
47 (vec![], 0.0, 0.0)
48 };
49 let local_space = Rect {
50 left: 0.0,
51 right: w,
52 top: 0.0,
53 bottom: h,
54 };
55 Some(LayoutNode {
56 id: unit.id.to_owned(),
57 local_space,
58 children,
59 })
60 }
61
62 pub fn layout_content_box(size_available: Vec2, unit: &ContentBox) -> Option<LayoutNode> {
63 if !unit.id.is_valid() {
64 return None;
65 }
66 let children = unit
67 .items
68 .iter()
69 .filter_map(|item| {
70 let left = lerp(0.0, size_available.x, item.layout.anchors.left);
71 let left = left + item.layout.margin.left + item.layout.offset.x;
72 let right = lerp(0.0, size_available.x, item.layout.anchors.right);
73 let right = right - item.layout.margin.right + item.layout.offset.x;
74 let top = lerp(0.0, size_available.y, item.layout.anchors.top);
75 let top = top + item.layout.margin.top + item.layout.offset.y;
76 let bottom = lerp(0.0, size_available.y, item.layout.anchors.bottom);
77 let bottom = bottom - item.layout.margin.bottom + item.layout.offset.y;
78 let width = (right - left).max(0.0);
79 let height = (bottom - top).max(0.0);
80 let size = Vec2 {
81 x: width,
82 y: height,
83 };
84 if let Some(mut child) = Self::layout_node(size, &item.slot) {
85 let diff = child.local_space.width() - width;
86 let ox = lerp(0.0, diff, item.layout.align.x);
87 child.local_space.left += left - ox;
88 child.local_space.right += left - ox;
89 let diff = child.local_space.height() - height;
90 let oy = lerp(0.0, diff, item.layout.align.y);
91 child.local_space.top += top - oy;
92 child.local_space.bottom += top - oy;
93 let w = child.local_space.width().min(size_available.x);
94 let h = child.local_space.height().min(size_available.y);
95 if item.layout.keep_in_bounds.cut.left {
96 child.local_space.left = child.local_space.left.max(0.0);
97 if item.layout.keep_in_bounds.preserve.width {
98 child.local_space.right = child.local_space.left + w;
99 }
100 }
101 if item.layout.keep_in_bounds.cut.right {
102 child.local_space.right = child.local_space.right.min(size_available.x);
103 if item.layout.keep_in_bounds.preserve.width {
104 child.local_space.left = child.local_space.right - w;
105 }
106 }
107 if item.layout.keep_in_bounds.cut.top {
108 child.local_space.top = child.local_space.top.max(0.0);
109 if item.layout.keep_in_bounds.preserve.height {
110 child.local_space.bottom = child.local_space.top + h;
111 }
112 }
113 if item.layout.keep_in_bounds.cut.bottom {
114 child.local_space.bottom = child.local_space.bottom.min(size_available.y);
115 if item.layout.keep_in_bounds.preserve.height {
116 child.local_space.top = child.local_space.bottom - h;
117 }
118 }
119 Some(child)
120 } else {
121 None
122 }
123 })
124 .collect::<Vec<_>>();
125 Some(LayoutNode {
126 id: unit.id.to_owned(),
127 local_space: Rect {
128 left: 0.0,
129 right: size_available.x,
130 top: 0.0,
131 bottom: size_available.y,
132 },
133 children,
134 })
135 }
136
137 pub fn layout_flex_box(size_available: Vec2, unit: &FlexBox) -> Option<LayoutNode> {
138 if !unit.id.is_valid() {
139 return None;
140 }
141 if unit.wrap {
142 Some(Self::layout_flex_box_wrapping(size_available, unit))
143 } else {
144 Some(Self::layout_flex_box_no_wrap(size_available, unit))
145 }
146 }
147
148 pub fn layout_flex_box_wrapping(size_available: Vec2, unit: &FlexBox) -> LayoutNode {
149 let main_available = if unit.direction.is_horizontal() {
150 size_available.x
151 } else {
152 size_available.y
153 };
154 let (lines, count) = {
155 let mut main = 0.0;
156 let mut cross: Scalar = 0.0;
157 let mut grow = 0.0;
158 let items = unit
159 .items
160 .iter()
161 .filter(|item| item.slot.is_some() && item.slot.as_data().unwrap().id().is_valid())
162 .collect::<Vec<_>>();
163 let count = items.len();
164 let mut lines = vec![];
165 let mut line = vec![];
166 for item in items {
167 let local_main = item.layout.basis.unwrap_or_else(|| {
168 if unit.direction.is_horizontal() {
169 Self::calc_unit_min_width(size_available, &item.slot)
170 } else {
171 Self::calc_unit_min_height(size_available, &item.slot)
172 }
173 });
174 let local_main = local_main
175 + if unit.direction.is_horizontal() {
176 item.layout.margin.left + item.layout.margin.right
177 } else {
178 item.layout.margin.top + item.layout.margin.bottom
179 };
180 let local_cross = if unit.direction.is_horizontal() {
181 Self::calc_unit_min_height(size_available, &item.slot)
182 } else {
183 Self::calc_unit_min_width(size_available, &item.slot)
184 };
185 let local_cross = local_cross
186 + if unit.direction.is_horizontal() {
187 item.layout.margin.top + item.layout.margin.bottom
188 } else {
189 item.layout.margin.left + item.layout.margin.right
190 };
191 if !line.is_empty() && main + local_main > main_available {
192 main += line.len().saturating_sub(1) as Scalar * unit.separation;
193 lines.push((main, cross, grow, std::mem::take(&mut line)));
194 main = 0.0;
195 cross = 0.0;
196 grow = 0.0;
197 }
198 main += local_main;
199 cross = cross.max(local_cross);
200 grow += item.layout.grow;
201 line.push((item, local_main, local_cross));
202 }
203 main += line.len().saturating_sub(1) as Scalar * unit.separation;
204 lines.push((main, cross, grow, line));
205 (lines, count)
206 };
207 let mut children = Vec::with_capacity(count);
208 let mut main_max: Scalar = 0.0;
209 let mut cross_max = 0.0;
210 for (main, cross_available, grow, items) in lines {
211 let diff = main_available - main;
212 let mut new_main = 0.0;
213 let mut new_cross: Scalar = 0.0;
214 for (item, local_main, local_cross) in items {
215 let child_main = if main < main_available {
216 local_main
217 + if grow > 0.0 {
218 diff * item.layout.grow / grow
219 } else {
220 0.0
221 }
222 } else {
223 local_main
224 };
225 let child_main = (child_main
226 - if unit.direction.is_horizontal() {
227 item.layout.margin.left + item.layout.margin.right
228 } else {
229 item.layout.margin.top + item.layout.margin.bottom
230 })
231 .max(0.0);
232 let child_cross = (local_cross
233 - if unit.direction.is_horizontal() {
234 item.layout.margin.top + item.layout.margin.bottom
235 } else {
236 item.layout.margin.left + item.layout.margin.right
237 })
238 .max(0.0);
239 let child_cross = lerp(child_cross, cross_available, item.layout.fill);
240 let rect = if unit.direction.is_horizontal() {
241 Vec2 {
242 x: child_main,
243 y: child_cross,
244 }
245 } else {
246 Vec2 {
247 x: child_cross,
248 y: child_main,
249 }
250 };
251 if let Some(mut child) = Self::layout_node(rect, &item.slot) {
252 if unit.direction.is_horizontal() {
253 if unit.direction.is_order_ascending() {
254 child.local_space.left += new_main + item.layout.margin.left;
255 child.local_space.right += new_main + item.layout.margin.left;
256 } else {
257 let left = child.local_space.left;
258 let right = child.local_space.right;
259 child.local_space.left =
260 size_available.x - right - new_main - item.layout.margin.right;
261 child.local_space.right =
262 size_available.x - left - new_main - item.layout.margin.right;
263 }
264 new_main += rect.x + item.layout.margin.left + item.layout.margin.right;
265 let diff = lerp(
266 0.0,
267 cross_available - child.local_space.height(),
268 item.layout.align,
269 );
270 child.local_space.top += cross_max + item.layout.margin.top + diff;
271 child.local_space.bottom += cross_max + item.layout.margin.top + diff;
272 new_cross = new_cross.max(rect.y);
273 } else {
274 if unit.direction.is_order_ascending() {
275 child.local_space.top += new_main + item.layout.margin.top;
276 child.local_space.bottom += new_main + item.layout.margin.top;
277 } else {
278 let top = child.local_space.top;
279 let bottom = child.local_space.bottom;
280 child.local_space.top =
281 size_available.y - bottom - new_main - item.layout.margin.bottom;
282 child.local_space.bottom =
283 size_available.y - top - new_main - item.layout.margin.bottom;
284 }
285 new_main += rect.y + item.layout.margin.top + item.layout.margin.bottom;
286 let diff = lerp(
287 0.0,
288 cross_available - child.local_space.width(),
289 item.layout.align,
290 );
291 child.local_space.left += cross_max + item.layout.margin.left + diff;
292 child.local_space.right += cross_max + item.layout.margin.left + diff;
293 new_cross = new_cross.max(rect.x);
294 }
295 new_main += unit.separation;
296 children.push(child);
297 }
298 }
299 new_main = (new_main - unit.separation).max(0.0);
300 main_max = main_max.max(new_main);
301 cross_max += new_cross + unit.separation;
302 }
303 cross_max = (cross_max - unit.separation).max(0.0);
304 let local_space = if unit.direction.is_horizontal() {
305 Rect {
306 left: 0.0,
307 right: main_max,
308 top: 0.0,
309 bottom: cross_max,
310 }
311 } else {
312 Rect {
313 left: 0.0,
314 right: cross_max,
315 top: 0.0,
316 bottom: main_max,
317 }
318 };
319 LayoutNode {
320 id: unit.id.to_owned(),
321 local_space,
322 children,
323 }
324 }
325
326 pub fn layout_flex_box_no_wrap(size_available: Vec2, unit: &FlexBox) -> LayoutNode {
327 let (main_available, cross_available) = if unit.direction.is_horizontal() {
328 (size_available.x, size_available.y)
329 } else {
330 (size_available.y, size_available.x)
331 };
332 let mut main = 0.0;
333 let mut cross: Scalar = 0.0;
334 let mut grow = 0.0;
335 let mut shrink = 0.0;
336 let items = unit
337 .items
338 .iter()
339 .filter(|item| item.slot.is_some() && item.slot.as_data().unwrap().id().is_valid())
340 .collect::<Vec<_>>();
341 let mut axis_sizes = Vec::with_capacity(items.len());
342 for item in &items {
343 let local_main = item.layout.basis.unwrap_or_else(|| {
344 if unit.direction.is_horizontal() {
345 Self::calc_unit_min_width(size_available, &item.slot)
346 } else {
347 Self::calc_unit_min_height(size_available, &item.slot)
348 }
349 });
350 let local_main = local_main
351 + if unit.direction.is_horizontal() {
352 item.layout.margin.left + item.layout.margin.right
353 } else {
354 item.layout.margin.top + item.layout.margin.bottom
355 };
356 let local_cross = if unit.direction.is_horizontal() {
357 Self::calc_unit_min_height(size_available, &item.slot)
358 } else {
359 Self::calc_unit_min_width(size_available, &item.slot)
360 };
361 let local_cross = local_cross
362 + if unit.direction.is_horizontal() {
363 item.layout.margin.top + item.layout.margin.bottom
364 } else {
365 item.layout.margin.left + item.layout.margin.right
366 };
367 let local_cross = lerp(local_cross, cross_available, item.layout.fill);
368 main += local_main;
369 cross = cross.max(local_cross);
370 grow += item.layout.grow;
371 shrink += item.layout.shrink;
372 axis_sizes.push((local_main, local_cross));
373 }
374 main += items.len().saturating_sub(1) as Scalar * unit.separation;
375 let diff = main_available - main;
376 let mut new_main = 0.0;
377 let mut new_cross: Scalar = 0.0;
378 let children = items
379 .into_iter()
380 .zip(axis_sizes)
381 .filter_map(|(item, axis_size)| {
382 let child_main = if main < main_available {
383 axis_size.0
384 + if grow > 0.0 {
385 diff * item.layout.grow / grow
386 } else {
387 0.0
388 }
389 } else if main > main_available {
390 axis_size.0
391 + if shrink > 0.0 {
392 diff * item.layout.shrink / shrink
393 } else {
394 0.0
395 }
396 } else {
397 axis_size.0
398 };
399 let child_main = (child_main
400 - if unit.direction.is_horizontal() {
401 item.layout.margin.left + item.layout.margin.right
402 } else {
403 item.layout.margin.top + item.layout.margin.bottom
404 })
405 .max(0.0);
406 let child_cross = (axis_size.1
407 - if unit.direction.is_horizontal() {
408 item.layout.margin.top + item.layout.margin.bottom
409 } else {
410 item.layout.margin.left + item.layout.margin.right
411 })
412 .max(0.0);
413 let rect = if unit.direction.is_horizontal() {
414 Vec2 {
415 x: child_main,
416 y: child_cross,
417 }
418 } else {
419 Vec2 {
420 x: child_cross,
421 y: child_main,
422 }
423 };
424 if let Some(mut child) = Self::layout_node(rect, &item.slot) {
425 if unit.direction.is_horizontal() {
426 if unit.direction.is_order_ascending() {
427 child.local_space.left += new_main + item.layout.margin.left;
428 child.local_space.right += new_main + item.layout.margin.left;
429 } else {
430 let left = child.local_space.left;
431 let right = child.local_space.right;
432 child.local_space.left =
433 size_available.x - right - new_main - item.layout.margin.right;
434 child.local_space.right =
435 size_available.x - left - new_main - item.layout.margin.right;
436 }
437 new_main += rect.x + item.layout.margin.left + item.layout.margin.right;
438 let diff = lerp(
439 0.0,
440 cross_available - child.local_space.height(),
441 item.layout.align,
442 );
443 child.local_space.top += item.layout.margin.top + diff;
444 child.local_space.bottom += item.layout.margin.top + diff;
445 new_cross = new_cross.max(rect.y);
446 } else {
447 if unit.direction.is_order_ascending() {
448 child.local_space.top += new_main + item.layout.margin.top;
449 child.local_space.bottom += new_main + item.layout.margin.top;
450 } else {
451 let top = child.local_space.top;
452 let bottom = child.local_space.bottom;
453 child.local_space.top =
454 size_available.y - bottom - new_main - item.layout.margin.bottom;
455 child.local_space.bottom =
456 size_available.y - top - new_main - item.layout.margin.bottom;
457 }
458 new_main += rect.y + item.layout.margin.top + item.layout.margin.bottom;
459 let diff = lerp(
460 0.0,
461 cross_available - child.local_space.width(),
462 item.layout.align,
463 );
464 child.local_space.left += item.layout.margin.left + diff;
465 child.local_space.right += item.layout.margin.left + diff;
466 new_cross = new_cross.max(rect.x);
467 }
468 new_main += unit.separation;
469 Some(child)
470 } else {
471 None
472 }
473 })
474 .collect::<Vec<_>>();
475 new_main = (new_main - unit.separation).max(0.0);
476 let local_space = if unit.direction.is_horizontal() {
477 Rect {
478 left: 0.0,
479 right: new_main,
480 top: 0.0,
481 bottom: new_cross,
482 }
483 } else {
484 Rect {
485 left: 0.0,
486 right: new_cross,
487 top: 0.0,
488 bottom: new_main,
489 }
490 };
491 LayoutNode {
492 id: unit.id.to_owned(),
493 local_space,
494 children,
495 }
496 }
497
498 pub fn layout_grid_box(size_available: Vec2, unit: &GridBox) -> Option<LayoutNode> {
499 if !unit.id.is_valid() {
500 return None;
501 }
502 let cell_width = if unit.cols > 0 {
503 size_available.x / unit.cols as Scalar
504 } else {
505 0.0
506 };
507 let cell_height = if unit.rows > 0 {
508 size_available.y / unit.rows as Scalar
509 } else {
510 0.0
511 };
512 let children = unit
513 .items
514 .iter()
515 .filter_map(|item| {
516 let left = item.layout.space_occupancy.left as Scalar * cell_width;
517 let right = item.layout.space_occupancy.right as Scalar * cell_width;
518 let top = item.layout.space_occupancy.top as Scalar * cell_height;
519 let bottom = item.layout.space_occupancy.bottom as Scalar * cell_height;
520 let width =
521 (right - left - item.layout.margin.left - item.layout.margin.right).max(0.0);
522 let height =
523 (bottom - top - item.layout.margin.top - item.layout.margin.bottom).max(0.0);
524 let size = Vec2 {
525 x: width,
526 y: height,
527 };
528 if let Some(mut child) = Self::layout_node(size, &item.slot) {
529 let diff = size.x - child.local_space.width();
530 let ox = lerp(0.0, diff, item.layout.horizontal_align);
531 let diff = size.y - child.local_space.height();
532 let oy = lerp(0.0, diff, item.layout.vertical_align);
533 child.local_space.left += left + item.layout.margin.left - ox;
534 child.local_space.right += left + item.layout.margin.left - ox;
535 child.local_space.top += top + item.layout.margin.top - oy;
536 child.local_space.bottom += top + item.layout.margin.top - oy;
537 Some(child)
538 } else {
539 None
540 }
541 })
542 .collect::<Vec<_>>();
543 Some(LayoutNode {
544 id: unit.id.to_owned(),
545 local_space: Rect {
546 left: 0.0,
547 right: size_available.x,
548 top: 0.0,
549 bottom: size_available.y,
550 },
551 children,
552 })
553 }
554
555 pub fn layout_size_box(size_available: Vec2, unit: &SizeBox) -> Option<LayoutNode> {
556 if !unit.id.is_valid() {
557 return None;
558 }
559 let mut size = Vec2 {
560 x: match unit.width {
561 SizeBoxSizeValue::Content => Self::calc_unit_min_width(size_available, &unit.slot),
562 SizeBoxSizeValue::Fill => size_available.x - unit.margin.left - unit.margin.right,
563 SizeBoxSizeValue::Exact(v) => v,
564 },
565 y: match unit.height {
566 SizeBoxSizeValue::Content => Self::calc_unit_min_height(size_available, &unit.slot),
567 SizeBoxSizeValue::Fill => size_available.y - unit.margin.top - unit.margin.bottom,
568 SizeBoxSizeValue::Exact(v) => v,
569 },
570 };
571 match unit.keep_aspect_ratio {
572 SizeBoxAspectRatio::None => {}
573 SizeBoxAspectRatio::WidthOfHeight(factor) => {
574 size.x = (size.y * factor).max(0.0);
575 }
576 SizeBoxAspectRatio::HeightOfWidth(factor) => {
577 size.y = (size.x * factor).max(0.0);
578 }
579 }
580 let children = if let Some(mut child) = Self::layout_node(size, &unit.slot) {
581 child.local_space.left += unit.margin.left;
582 child.local_space.right += unit.margin.left;
583 child.local_space.top += unit.margin.top;
584 child.local_space.bottom += unit.margin.top;
585 vec![child]
586 } else {
587 vec![]
588 };
589 let local_space = Rect {
590 left: 0.0,
591 right: size.x,
592 top: 0.0,
593 bottom: size.y,
594 };
595 Some(LayoutNode {
596 id: unit.id.to_owned(),
597 local_space,
598 children,
599 })
600 }
601
602 pub fn layout_image_box(size_available: Vec2, unit: &ImageBox) -> Option<LayoutNode> {
603 if !unit.id.is_valid() {
604 return None;
605 }
606 let local_space = Rect {
607 left: 0.0,
608 right: match unit.width {
609 ImageBoxSizeValue::Fill => size_available.x,
610 ImageBoxSizeValue::Exact(v) => v,
611 },
612 top: 0.0,
613 bottom: match unit.height {
614 ImageBoxSizeValue::Fill => size_available.y,
615 ImageBoxSizeValue::Exact(v) => v,
616 },
617 };
618 Some(LayoutNode {
619 id: unit.id.to_owned(),
620 local_space,
621 children: vec![],
622 })
623 }
624
625 pub fn layout_text_box(size_available: Vec2, unit: &TextBox) -> Option<LayoutNode> {
626 if !unit.id.is_valid() {
627 return None;
628 }
629 let local_space = Rect {
630 left: 0.0,
631 right: match unit.width {
632 TextBoxSizeValue::Fill => size_available.x,
633 TextBoxSizeValue::Exact(v) => v,
634 },
635 top: 0.0,
636 bottom: match unit.height {
637 TextBoxSizeValue::Fill => size_available.y,
638 TextBoxSizeValue::Exact(v) => v,
639 },
640 };
641 Some(LayoutNode {
642 id: unit.id.to_owned(),
643 local_space,
644 children: vec![],
645 })
646 }
647
648 fn calc_unit_min_width(size_available: Vec2, unit: &WidgetUnit) -> Scalar {
649 match unit {
650 WidgetUnit::None | WidgetUnit::PortalBox(_) => 0.0,
651 WidgetUnit::AreaBox(b) => Self::calc_unit_min_width(size_available, &b.slot),
652 WidgetUnit::ContentBox(b) => Self::calc_content_box_min_width(size_available, b),
653 WidgetUnit::FlexBox(b) => Self::calc_flex_box_min_width(size_available, b),
654 WidgetUnit::GridBox(b) => Self::calc_grid_box_min_width(size_available, b),
655 WidgetUnit::SizeBox(b) => {
656 (match b.width {
657 SizeBoxSizeValue::Content => Self::calc_unit_min_width(size_available, &b.slot),
658 SizeBoxSizeValue::Fill => 0.0,
659 SizeBoxSizeValue::Exact(v) => v,
660 }) + b.margin.left
661 + b.margin.right
662 }
663 WidgetUnit::ImageBox(b) => match b.width {
664 ImageBoxSizeValue::Fill => 0.0,
665 ImageBoxSizeValue::Exact(v) => v,
666 },
667 WidgetUnit::TextBox(b) => match b.width {
668 TextBoxSizeValue::Fill => 0.0,
669 TextBoxSizeValue::Exact(v) => v,
670 },
671 }
672 }
673
674 fn calc_content_box_min_width(size_available: Vec2, unit: &ContentBox) -> Scalar {
675 let mut result: Scalar = 0.0;
676 for item in &unit.items {
677 let size = Self::calc_unit_min_width(size_available, &item.slot)
678 + item.layout.margin.left
679 + item.layout.margin.right;
680 let width = item.layout.anchors.right - item.layout.anchors.left;
681 let size = if width > 0.0 { size / width } else { 0.0 };
682 result = result.max(size);
683 }
684 result
685 }
686
687 fn calc_flex_box_min_width(size_available: Vec2, unit: &FlexBox) -> Scalar {
688 if unit.direction.is_horizontal() {
689 Self::calc_horizontal_flex_box_min_width(size_available, unit)
690 } else {
691 Self::calc_vertical_flex_box_min_width(size_available, unit)
692 }
693 }
694
695 fn calc_horizontal_flex_box_min_width(size_available: Vec2, unit: &FlexBox) -> Scalar {
696 if unit.wrap {
697 let mut result: Scalar = 0.0;
698 let mut line = 0.0;
699 let mut first = true;
700 for item in &unit.items {
701 let size = Self::calc_unit_min_width(size_available, &item.slot)
702 + item.layout.margin.left
703 + item.layout.margin.right;
704 if first || line + size <= size_available.x {
705 line += size;
706 if !first {
707 line += unit.separation;
708 }
709 first = false;
710 } else {
711 result = result.max(line);
712 line = 0.0;
713 first = true;
714 }
715 }
716 result.max(line)
717 } else {
718 let mut result = 0.0;
719 for item in &unit.items {
720 result += Self::calc_unit_min_width(size_available, &item.slot)
721 + item.layout.margin.left
722 + item.layout.margin.right;
723 }
724 result + (unit.items.len().saturating_sub(1) as Scalar) * unit.separation
725 }
726 }
727
728 fn calc_vertical_flex_box_min_width(size_available: Vec2, unit: &FlexBox) -> Scalar {
729 if unit.wrap {
730 let mut result = 0.0;
731 let mut line_length = 0.0;
732 let mut line: Scalar = 0.0;
733 let mut lines: usize = 0;
734 let mut first = true;
735 for item in &unit.items {
736 let width = Self::calc_unit_min_width(size_available, &item.slot)
737 + item.layout.margin.left
738 + item.layout.margin.right;
739 let height = Self::calc_unit_min_height(size_available, &item.slot)
740 + item.layout.margin.top
741 + item.layout.margin.bottom;
742 if first || line_length + height <= size_available.y {
743 line_length += height;
744 if !first {
745 line_length += unit.separation;
746 }
747 line = line.max(width);
748 first = false;
749 } else {
750 result += line;
751 line_length = 0.0;
752 line = 0.0;
753 lines += 1;
754 first = true;
755 }
756 }
757 result += line;
758 lines += 1;
759 result + (lines.saturating_sub(1) as Scalar) * unit.separation
760 } else {
761 unit.items.iter().fold(0.0, |a, item| {
762 (Self::calc_unit_min_width(size_available, &item.slot)
763 + item.layout.margin.left
764 + item.layout.margin.right)
765 .max(a)
766 })
767 }
768 }
769
770 fn calc_grid_box_min_width(size_available: Vec2, unit: &GridBox) -> Scalar {
771 let mut result: Scalar = 0.0;
772 for item in &unit.items {
773 let size = Self::calc_unit_min_width(size_available, &item.slot)
774 + item.layout.margin.left
775 + item.layout.margin.right;
776 let size = if size > 0.0 {
777 (item.layout.space_occupancy.width() as Scalar * size) / unit.cols as Scalar
778 } else {
779 0.0
780 };
781 result = result.max(size);
782 }
783 result
784 }
785
786 fn calc_unit_min_height(size_available: Vec2, unit: &WidgetUnit) -> Scalar {
787 match unit {
788 WidgetUnit::None | WidgetUnit::PortalBox(_) => 0.0,
789 WidgetUnit::AreaBox(b) => Self::calc_unit_min_height(size_available, &b.slot),
790 WidgetUnit::ContentBox(b) => Self::calc_content_box_min_height(size_available, b),
791 WidgetUnit::FlexBox(b) => Self::calc_flex_box_min_height(size_available, b),
792 WidgetUnit::GridBox(b) => Self::calc_grid_box_min_height(size_available, b),
793 WidgetUnit::SizeBox(b) => {
794 (match b.height {
795 SizeBoxSizeValue::Content => {
796 Self::calc_unit_min_height(size_available, &b.slot)
797 }
798 SizeBoxSizeValue::Fill => 0.0,
799 SizeBoxSizeValue::Exact(v) => v,
800 }) + b.margin.top
801 + b.margin.bottom
802 }
803 WidgetUnit::ImageBox(b) => match b.height {
804 ImageBoxSizeValue::Fill => 0.0,
805 ImageBoxSizeValue::Exact(v) => v,
806 },
807 WidgetUnit::TextBox(b) => match b.height {
808 TextBoxSizeValue::Fill => 0.0,
809 TextBoxSizeValue::Exact(v) => v,
810 },
811 }
812 }
813
814 fn calc_content_box_min_height(size_available: Vec2, unit: &ContentBox) -> Scalar {
815 let mut result: Scalar = 0.0;
816 for item in &unit.items {
817 let size = Self::calc_unit_min_height(size_available, &item.slot)
818 + item.layout.margin.top
819 + item.layout.margin.bottom;
820 let height = item.layout.anchors.bottom - item.layout.anchors.top;
821 let size = if height > 0.0 { size / height } else { 0.0 };
822 result = result.max(size);
823 }
824 result
825 }
826
827 fn calc_flex_box_min_height(size_available: Vec2, unit: &FlexBox) -> Scalar {
828 if unit.direction.is_horizontal() {
829 Self::calc_horizontal_flex_box_min_height(size_available, unit)
830 } else {
831 Self::calc_vertical_flex_box_min_height(size_available, unit)
832 }
833 }
834
835 fn calc_horizontal_flex_box_min_height(size_available: Vec2, unit: &FlexBox) -> Scalar {
836 if unit.wrap {
837 let mut result = 0.0;
838 let mut line_length = 0.0;
839 let mut line: Scalar = 0.0;
840 let mut lines: usize = 0;
841 let mut first = true;
842 for item in &unit.items {
843 let width = Self::calc_unit_min_width(size_available, &item.slot)
844 + item.layout.margin.left
845 + item.layout.margin.right;
846 let height = Self::calc_unit_min_height(size_available, &item.slot)
847 + item.layout.margin.top
848 + item.layout.margin.bottom;
849 if first || line_length + width <= size_available.x {
850 line_length += width;
851 if !first {
852 line_length += unit.separation;
853 }
854 line = line.max(height);
855 first = false;
856 } else {
857 result += line;
858 line_length = 0.0;
859 line = 0.0;
860 lines += 1;
861 first = true;
862 }
863 }
864 result += line;
865 lines += 1;
866 result + (lines.saturating_sub(1) as Scalar) * unit.separation
867 } else {
868 unit.items.iter().fold(0.0, |a, item| {
869 (Self::calc_unit_min_height(size_available, &item.slot)
870 + item.layout.margin.top
871 + item.layout.margin.bottom)
872 .max(a)
873 })
874 }
875 }
876
877 fn calc_vertical_flex_box_min_height(size_available: Vec2, unit: &FlexBox) -> Scalar {
878 if unit.wrap {
879 let mut result: Scalar = 0.0;
880 let mut line = 0.0;
881 let mut first = true;
882 for item in &unit.items {
883 let size = Self::calc_unit_min_height(size_available, &item.slot)
884 + item.layout.margin.top
885 + item.layout.margin.bottom;
886 if first || line + size <= size_available.y {
887 line += size;
888 if !first {
889 line += unit.separation;
890 }
891 first = false;
892 } else {
893 result = result.max(line);
894 line = 0.0;
895 first = true;
896 }
897 }
898 result.max(line)
899 } else {
900 let mut result = 0.0;
901 for item in &unit.items {
902 result += Self::calc_unit_min_height(size_available, &item.slot)
903 + item.layout.margin.top
904 + item.layout.margin.bottom;
905 }
906 result + (unit.items.len().saturating_sub(1) as Scalar) * unit.separation
907 }
908 }
909
910 fn calc_grid_box_min_height(size_available: Vec2, unit: &GridBox) -> Scalar {
911 let mut result: Scalar = 0.0;
912 for item in &unit.items {
913 let size = Self::calc_unit_min_height(size_available, &item.slot)
914 + item.layout.margin.top
915 + item.layout.margin.bottom;
916 let size = if size > 0.0 {
917 (item.layout.space_occupancy.height() as Scalar * size) / unit.cols as Scalar
918 } else {
919 0.0
920 };
921 result = result.max(size);
922 }
923 result
924 }
925
926 fn unpack_node(
927 parent: Option<&WidgetId>,
928 ui_space: Rect,
929 node: LayoutNode,
930 items: &mut HashMap<WidgetId, LayoutItem>,
931 ) {
932 let LayoutNode {
933 id,
934 local_space,
935 children,
936 } = node;
937 let ui_space = Rect {
938 left: local_space.left + ui_space.left,
939 right: local_space.right + ui_space.left,
940 top: local_space.top + ui_space.top,
941 bottom: local_space.bottom + ui_space.top,
942 };
943 for node in children {
944 Self::unpack_node(Some(&id), ui_space, node, items);
945 }
946 items.insert(
947 id,
948 LayoutItem {
949 local_space,
950 ui_space,
951 parent: parent.cloned(),
952 },
953 );
954 }
955}
956
957impl LayoutEngine<()> for DefaultLayoutEngine {
958 fn layout(&mut self, mapping: &CoordsMapping, tree: &WidgetUnit) -> Result<Layout, ()> {
959 let ui_space = mapping.virtual_area();
960 if let Some(root) = Self::layout_node(ui_space.size(), tree) {
961 let mut items = HashMap::with_capacity(root.count());
962 Self::unpack_node(None, ui_space, root, &mut items);
963 Ok(Layout { ui_space, items })
964 } else {
965 Ok(Layout {
966 ui_space,
967 items: Default::default(),
968 })
969 }
970 }
971}