1pub use euclid::Rect;
2use rustc_hash::FxHashMap;
3
4use crate::{
5 custom_measurer::LayoutMeasurer,
6 geometry::{
7 Area,
8 Size2D,
9 },
10 node::Node,
11 prelude::{
12 AlignAxis,
13 Alignment,
14 AlignmentDirection,
15 AreaConverter,
16 AreaModel,
17 AreaOf,
18 Available,
19 AvailableAreaModel,
20 Content,
21 Direction,
22 Inner,
23 LayoutMetadata,
24 Length,
25 Parent,
26 Position,
27 Torin,
28 },
29 size::Size,
30 torin::DirtyReason,
31 tree_adapter::{
32 LayoutNode,
33 NodeKey,
34 TreeAdapter,
35 },
36};
37
38#[derive(Clone, Copy, PartialEq)]
41pub enum Phase {
42 Initial,
43 Final,
44}
45
46pub struct MeasureContext<'a, Key, L, D>
47where
48 Key: NodeKey,
49 L: LayoutMeasurer<Key>,
50 D: TreeAdapter<Key>,
51{
52 pub layout: &'a mut Torin<Key>,
53 pub measurer: &'a mut Option<L>,
54 pub tree_adapter: &'a mut D,
55 pub layout_metadata: LayoutMetadata,
56}
57
58impl<Key, L, D> MeasureContext<'_, Key, L, D>
59where
60 Key: NodeKey,
61 L: LayoutMeasurer<Key>,
62 D: TreeAdapter<Key>,
63{
64 fn translate_node(&mut self, node_id: Key, offset_x: Length, offset_y: Length) {
66 let Some((area, visible_area, inner_sizes)) =
67 self.layout.get_mut(&node_id).map(|layout_node| {
68 layout_node.area.origin.x += offset_x.get();
69 layout_node.area.origin.y += offset_y.get();
70 layout_node.inner_area.origin.x += offset_x.get();
71 layout_node.inner_area.origin.y += offset_y.get();
72 (
73 layout_node.area,
74 layout_node.visible_area(),
75 layout_node.inner_sizes,
76 )
77 })
78 else {
79 return;
80 };
81
82 if let Some(measurer) = self.measurer {
83 measurer.notify_layout_references(node_id, area, visible_area, inner_sizes);
84 }
85 }
86
87 fn recursive_translate(&mut self, node_id: Key, offset_x: Length, offset_y: Length) {
89 let mut buffer = self.tree_adapter.children_of(&node_id);
90 while let Some(child) = buffer.pop() {
91 let node = self
92 .tree_adapter
93 .get_node(&child)
94 .expect("Node does not exist");
95
96 let translate = match node.position {
97 Position::Global(_) => false,
98 Position::Stacked(_) | Position::Absolute(_) => true,
99 };
100
101 if translate {
102 self.translate_node(child, offset_x, offset_y);
103 buffer.extend(self.tree_adapter.children_of(&child));
104 }
105 }
106 }
107
108 fn set_hidden(&mut self, node_id: Key, hidden: bool) {
110 let mut buffer = vec![node_id];
111 while let Some(child) = buffer.pop() {
112 if let Some(layout_node) = self.layout.get_mut(&child) {
113 layout_node.hidden = hidden;
114 }
115 buffer.extend(self.tree_adapter.children_of(&child));
116 }
117 }
118
119 fn apply_post_measure(&mut self, node_id: Key, node_layout: &LayoutNode) -> Option<Size2D> {
122 let post_measure = {
123 let measurer = self.measurer.as_mut()?;
124 if !measurer.should_post_measure(node_id) {
125 return None;
126 }
127 let children = self.tree_adapter.children_of(&node_id);
128 measurer.post_measure(node_id, node_layout, &children, self.layout)
129 };
130
131 for (child_id, offset_x, offset_y) in post_measure.offsets {
132 self.set_hidden(child_id, false);
133 self.translate_node(child_id, offset_x, offset_y);
134 self.recursive_translate(child_id, offset_x, offset_y);
135 }
136
137 for child_id in post_measure.hidden_children {
138 self.set_hidden(child_id, true);
139 }
140
141 post_measure.content_size
142 }
143
144 #[allow(clippy::too_many_arguments, clippy::missing_panics_doc)]
146 pub fn measure_node(
147 &mut self,
148 node_id: Key,
149 node: &Node,
150 initial_parent_area: AreaOf<Parent>,
152 available_parent_area: AreaOf<Available>,
154 must_cache_children: bool,
156 parent_is_dirty: bool,
158 phase: Phase,
160 ) -> (bool, LayoutNode) {
161 let reason = self.layout.dirty.get(&node_id).copied();
162
163 if let Some(layout_node) = self.layout.get_mut(&node_id)
165 && reason == Some(DirtyReason::InnerLayout)
166 && must_cache_children
167 {
168 let offset_x = node.offset_x - layout_node.offset_x;
170 let offset_y = node.offset_y - layout_node.offset_y;
171
172 layout_node.offset_x = node.offset_x;
173 layout_node.offset_y = node.offset_y;
174
175 let layout_node = layout_node.clone();
176
177 self.recursive_translate(node_id, offset_x, offset_y);
178
179 return (must_cache_children, layout_node);
180 }
181
182 let must_revalidate =
186 parent_is_dirty || reason.is_some() || !self.layout.results.contains_key(&node_id);
187 if must_revalidate {
188 let mut area_size = Size2D::new(node.padding.horizontal(), node.padding.vertical());
190
191 area_size.width = node.width.min_max(
193 area_size.width,
194 initial_parent_area.size.width,
195 available_parent_area.size.width,
196 node.margin.left(),
197 node.margin.horizontal(),
198 &node.minimum_width,
199 &node.maximum_width,
200 self.layout_metadata.root_area.width(),
201 phase,
202 );
203 area_size.height = node.height.min_max(
204 area_size.height,
205 initial_parent_area.size.height,
206 available_parent_area.size.height,
207 node.margin.top(),
208 node.margin.vertical(),
209 &node.minimum_height,
210 &node.maximum_height,
211 self.layout_metadata.root_area.height(),
212 phase,
213 );
214
215 let node_data = if let Some(measurer) = self.measurer {
218 if measurer.should_hook_measurement(node_id) {
219 let available_width =
220 Size::Pixels(Length::new(available_parent_area.size.width)).min_max(
221 area_size.width,
222 initial_parent_area.size.width,
223 available_parent_area.size.width,
224 node.margin.left(),
225 node.margin.horizontal(),
226 &node.minimum_width,
227 &node.maximum_width,
228 self.layout_metadata.root_area.width(),
229 phase,
230 );
231 let available_height =
232 Size::Pixels(Length::new(available_parent_area.size.height)).min_max(
233 area_size.height,
234 initial_parent_area.size.height,
235 available_parent_area.size.height,
236 node.margin.top(),
237 node.margin.vertical(),
238 &node.minimum_height,
239 &node.maximum_height,
240 self.layout_metadata.root_area.height(),
241 phase,
242 );
243 let most_fitting_width = *node
244 .width
245 .most_fitting_size(&area_size.width, &available_width);
246 let most_fitting_height = *node
247 .height
248 .most_fitting_size(&area_size.height, &available_height);
249
250 let most_fitting_area_size =
251 Size2D::new(most_fitting_width, most_fitting_height);
252 let res = measurer.measure(node_id, node, &most_fitting_area_size);
253
254 #[allow(clippy::float_cmp)]
256 if let Some((custom_size, node_data)) = res {
257 if node.width.inner_sized() {
258 area_size.width = node.width.min_max(
259 custom_size.width,
260 initial_parent_area.size.width,
261 available_parent_area.size.width,
262 node.margin.left(),
263 node.margin.horizontal(),
264 &node.minimum_width,
265 &node.maximum_width,
266 self.layout_metadata.root_area.width(),
267 phase,
268 );
269 }
270 if node.height.inner_sized() {
271 area_size.height = node.height.min_max(
272 custom_size.height,
273 initial_parent_area.size.height,
274 available_parent_area.size.height,
275 node.margin.top(),
276 node.margin.vertical(),
277 &node.minimum_height,
278 &node.maximum_height,
279 self.layout_metadata.root_area.height(),
280 phase,
281 );
282 }
283
284 Some(node_data)
286 } else {
287 None
288 }
289 } else {
290 None
291 }
292 } else {
293 None
294 };
295
296 let measure_inner_children = if let Some(measurer) = self.measurer {
297 measurer.should_measure_inner_children(node_id)
298 } else {
299 true
300 };
301
302 let phase_measure_inner_children = if phase == Phase::Initial {
305 node.width.inner_sized() || node.height.inner_sized()
306 } else {
307 true
308 };
309
310 let inner_size = {
312 let mut inner_size = area_size;
313
314 if node.width.inner_sized() {
316 inner_size.width = node.width.min_max(
317 available_parent_area.width(),
318 initial_parent_area.size.width,
319 available_parent_area.width(),
320 node.margin.left(),
321 node.margin.horizontal(),
322 &node.minimum_width,
323 &node.maximum_width,
324 self.layout_metadata.root_area.width(),
325 phase,
326 );
327 }
328 if node.height.inner_sized() {
329 inner_size.height = node.height.min_max(
330 available_parent_area.height(),
331 initial_parent_area.size.height,
332 available_parent_area.height(),
333 node.margin.top(),
334 node.margin.vertical(),
335 &node.minimum_height,
336 &node.maximum_height,
337 self.layout_metadata.root_area.height(),
338 phase,
339 );
340 }
341 inner_size
342 };
343
344 let area_origin = node.position.get_origin(
346 &available_parent_area,
347 &initial_parent_area,
348 area_size,
349 &self.layout_metadata.root_area,
350 );
351 let mut area = Area::new(area_origin, area_size);
352 let mut inner_area = Rect::new(area_origin, inner_size)
353 .without_gaps(&node.padding)
354 .without_gaps(&node.margin)
355 .as_inner();
356 inner_area.move_with_offsets(&node.offset_x, &node.offset_y);
357
358 let mut inner_sizes = Size2D::default();
359
360 if measure_inner_children && phase_measure_inner_children {
361 let mut available_area = inner_area.as_available();
363
364 let mut parent_area = area.as_parent();
365
366 self.measure_children(
368 &node_id,
369 node,
370 &mut parent_area,
371 &mut inner_area,
372 &mut available_area,
373 &mut inner_sizes,
374 must_cache_children,
375 true,
376 );
377
378 if node.width.inner_sized() {
381 parent_area.size.width = node.width.min_max(
382 parent_area.size.width,
383 initial_parent_area.size.width,
384 available_parent_area.size.width,
385 0.,
386 0.,
387 &node.minimum_width,
388 &node.maximum_width,
389 self.layout_metadata.root_area.width(),
390 phase,
391 );
392 }
393 if node.height.inner_sized() {
394 parent_area.size.height = node.height.min_max(
395 parent_area.size.height,
396 initial_parent_area.size.height,
397 available_parent_area.size.height,
398 0.,
399 0.,
400 &node.minimum_height,
401 &node.maximum_height,
402 self.layout_metadata.root_area.height(),
403 phase,
404 );
405 }
406
407 area = parent_area.cast_unit();
408
409 if !node.position.is_stacked()
412 && (node.width.inner_sized() || node.height.inner_sized())
413 && must_cache_children
414 {
415 let new_origin = node.position.get_origin(
416 &available_parent_area,
417 &initial_parent_area,
418 area.size,
419 &self.layout_metadata.root_area,
420 );
421 let diff_x = new_origin.x - area.origin.x;
422 let diff_y = new_origin.y - area.origin.y;
423 area.origin = new_origin;
424 inner_area.origin.x += diff_x;
425 inner_area.origin.y += diff_y;
426
427 if diff_x != 0.0 || diff_y != 0.0 {
428 self.recursive_translate(node_id, Length::new(diff_x), Length::new(diff_y));
429 }
430 }
431 }
432
433 let mut layout_node = LayoutNode {
434 area,
435 margin: node.margin,
436 offset_x: node.offset_x,
437 offset_y: node.offset_y,
438 inner_area,
439 hidden: false,
440 data: node_data,
441 inner_sizes,
442 };
443
444 if must_cache_children
445 && phase == Phase::Final
446 && let Some(content_size) = self.apply_post_measure(node_id, &layout_node)
447 {
448 if node.width.inner_sized() {
451 layout_node.area.size.width = node.width.min_max(
452 content_size.width,
453 initial_parent_area.size.width,
454 available_parent_area.size.width,
455 node.margin.left(),
456 node.margin.horizontal(),
457 &node.minimum_width,
458 &node.maximum_width,
459 self.layout_metadata.root_area.width(),
460 phase,
461 );
462 }
463 if node.height.inner_sized() {
464 layout_node.area.size.height = node.height.min_max(
465 content_size.height,
466 initial_parent_area.size.height,
467 available_parent_area.size.height,
468 node.margin.top(),
469 node.margin.vertical(),
470 &node.minimum_height,
471 &node.maximum_height,
472 self.layout_metadata.root_area.height(),
473 phase,
474 );
475 }
476 }
477
478 if must_cache_children
480 && phase == Phase::Final
481 && node.has_layout_references
482 && let Some(measurer) = self.measurer
483 {
484 inner_sizes.width += node.padding.horizontal();
485 inner_sizes.height += node.padding.vertical();
486 measurer.notify_layout_references(
487 node_id,
488 layout_node.area,
489 layout_node.visible_area(),
490 inner_sizes,
491 );
492 }
493
494 (must_cache_children, layout_node)
495 } else {
496 let layout_node = self
497 .layout
498 .get(&node_id)
499 .expect("Cached node does not exist")
500 .clone();
501
502 let mut inner_sizes = Size2D::default();
503 let mut area = layout_node.area.as_parent();
504 let mut inner_area = layout_node.inner_area.as_inner();
505 let mut available_area = inner_area.as_available();
506
507 let measure_inner_children = if let Some(measurer) = self.measurer {
508 measurer.should_measure_inner_children(node_id)
509 } else {
510 true
511 };
512
513 if measure_inner_children {
514 self.measure_children(
515 &node_id,
516 node,
517 &mut area,
518 &mut inner_area,
519 &mut available_area,
520 &mut inner_sizes,
521 must_cache_children,
522 false,
523 );
524 }
525
526 (false, layout_node)
527 }
528 }
529
530 #[allow(clippy::too_many_arguments)]
532 pub fn measure_children(
533 &mut self,
534 parent_node_id: &Key,
535 parent_node: &Node,
536 parent_area: &mut AreaOf<Parent>,
537 inner_area: &mut AreaOf<Inner>,
538 available_area: &mut AreaOf<Available>,
539 inner_sizes: &mut Size2D,
541 must_cache_children: bool,
543 parent_is_dirty: bool,
545 ) {
546 let mut children = self.tree_adapter.children_of(parent_node_id);
547 if parent_node.order.is_reverse() {
548 children.reverse();
549 }
550
551 let initial_area = *inner_area;
552
553 let mut initial_phase_flex_grows = FxHashMap::default();
554 let mut initial_phase_sizes = FxHashMap::default();
555 let mut initial_phase_inner_sizes = Size2D::default();
556
557 let (non_absolute_children_len, first_child, last_child) = if parent_node.spacing.get() > 0.
559 {
560 let mut last_child = None;
561 let mut first_child = None;
562 let len = children
563 .iter()
564 .filter(|child_id| {
565 let Some(child_data) = self.tree_adapter.get_node(child_id) else {
566 return false;
567 };
568 let is_stacked = child_data.position.is_stacked();
569 if is_stacked {
570 last_child = Some(**child_id);
571
572 if first_child.is_none() {
573 first_child = Some(**child_id);
574 }
575 }
576 is_stacked
577 })
578 .count();
579 (len, first_child, last_child)
580 } else {
581 (
582 children.len(),
583 children.first().copied(),
584 children.last().copied(),
585 )
586 };
587
588 let needs_initial_phase = parent_node.cross_alignment.is_not_start()
589 || parent_node.main_alignment.is_not_start()
590 || parent_node.content.is_fit()
591 || parent_node.content.is_flex()
592 || parent_node.content.is_wrap();
593
594 let mut initial_phase_parent_area = *parent_area;
595 let mut initial_phase_inner_area = *inner_area;
596 let mut initial_phase_available_area = *available_area;
597
598 if needs_initial_phase {
601 for child_id in &children {
603 let Some(child_data) = self.tree_adapter.get_node(child_id) else {
604 continue;
605 };
606
607 if !child_data.position.is_stacked() {
610 continue;
611 }
612
613 let is_last_child = last_child == Some(*child_id);
614
615 let (_, mut child_areas) = self.measure_node(
616 *child_id,
617 &child_data,
618 initial_area.as_parent(),
619 initial_phase_available_area,
620 false,
621 parent_is_dirty,
622 Phase::Initial,
623 );
624
625 child_areas.area.adjust_size(&child_data);
626
627 Self::stack_child(
629 &mut initial_phase_available_area,
630 parent_node,
631 &child_data,
632 &mut initial_phase_parent_area,
633 &mut initial_phase_inner_area,
634 &mut initial_phase_inner_sizes,
635 &child_areas.area,
636 is_last_child,
637 Phase::Initial,
638 );
639
640 if parent_node.cross_alignment.is_not_start()
641 || parent_node.main_alignment.is_spaced()
642 || parent_node.content.is_wrap()
643 {
644 initial_phase_sizes.insert(*child_id, child_areas.area.size);
645 }
646
647 if parent_node.content.is_flex() {
648 match parent_node.direction {
649 Direction::Vertical => {
650 if let Some(ff) = child_data.height.flex_grow() {
651 initial_phase_flex_grows.insert(*child_id, ff);
652 }
653 }
654 Direction::Horizontal => {
655 if let Some(ff) = child_data.width.flex_grow() {
656 initial_phase_flex_grows.insert(*child_id, ff);
657 }
658 }
659 }
660 }
661 }
662 }
663
664 let flex_grows = initial_phase_flex_grows
665 .values()
666 .copied()
667 .reduce(|acc, v| acc + v)
668 .unwrap_or_default()
669 .max(Length::new(1.0));
670
671 let flex_axis = AlignAxis::new(&parent_node.direction, AlignmentDirection::Main);
672
673 let flex_available_width = available_area.width() - initial_phase_inner_sizes.width;
674 let flex_available_height = available_area.height() - initial_phase_inner_sizes.height;
675
676 if parent_node.content.is_flex() {
677 initial_phase_inner_sizes =
678 initial_phase_flex_grows
679 .values()
680 .fold(initial_phase_inner_sizes, |mut acc, f| {
681 let flex_grow_per = f.get() / flex_grows.get() * 100.;
682
683 match flex_axis {
684 AlignAxis::Height => {
685 let size = flex_available_height / 100. * flex_grow_per;
686 acc.height += size;
687 }
688 AlignAxis::Width => {
689 let size = flex_available_width / 100. * flex_grow_per;
690 acc.width += size;
691 }
692 }
693
694 acc
695 });
696
697 if parent_node.cross_alignment.is_not_start() {
709 let cross_axis = AlignAxis::new(&parent_node.direction, AlignmentDirection::Cross);
710
711 for child_id in &children {
712 let Some(flex_grow) = initial_phase_flex_grows.get(child_id) else {
713 continue;
714 };
715 let Some(child_data) = self.tree_adapter.get_node(child_id) else {
716 continue;
717 };
718 if !child_data.position.is_stacked() {
719 continue;
720 }
721
722 let has_inner_cross = match cross_axis {
723 AlignAxis::Height => child_data.height.inner_sized(),
724 AlignAxis::Width => child_data.width.inner_sized(),
725 };
726 if !has_inner_cross {
727 continue;
728 }
729
730 let flex_grow_per = flex_grow.get() / flex_grows.get() * 100.;
731 let mut corrected_available_area = initial_area.as_available();
732
733 match flex_axis {
734 AlignAxis::Height => {
735 corrected_available_area.size.height =
736 flex_available_height / 100. * flex_grow_per;
737 }
738 AlignAxis::Width => {
739 corrected_available_area.size.width =
740 flex_available_width / 100. * flex_grow_per;
741 }
742 }
743
744 let (_, mut child_areas) = self.measure_node(
745 *child_id,
746 &child_data,
747 initial_area.as_parent(),
748 corrected_available_area,
749 false,
750 parent_is_dirty,
751 Phase::Final,
752 );
753
754 child_areas.area.adjust_size(&child_data);
755 initial_phase_sizes.insert(*child_id, child_areas.area.size);
756 }
757
758 let max_cross = children
760 .iter()
761 .filter_map(|id| initial_phase_sizes.get(id))
762 .map(|size| match cross_axis {
763 AlignAxis::Height => size.height,
764 AlignAxis::Width => size.width,
765 })
766 .fold(0.0, f32::max);
767
768 match cross_axis {
769 AlignAxis::Height if parent_node.height.inner_sized() => {
770 let gaps = parent_node.padding.vertical() + parent_node.margin.vertical();
771 initial_phase_parent_area.size.height = max_cross + gaps;
772 initial_phase_inner_area.size.height = max_cross;
773 }
774 AlignAxis::Width if parent_node.width.inner_sized() => {
775 let gaps =
776 parent_node.padding.horizontal() + parent_node.margin.horizontal();
777 initial_phase_parent_area.size.width = max_cross + gaps;
778 initial_phase_inner_area.size.width = max_cross;
779 }
780 _ => {}
781 }
782 }
783 }
784
785 if needs_initial_phase {
786 if parent_node.main_alignment.is_not_start() && parent_node.content.allows_alignments()
787 {
788 Self::shrink_area_to_fit_when_unbounded(
790 available_area,
791 &initial_phase_parent_area,
792 &mut initial_phase_inner_area,
793 parent_node,
794 AlignmentDirection::Main,
795 );
796
797 Self::align_content(
799 available_area,
800 &initial_phase_inner_area,
801 initial_phase_inner_sizes,
802 &parent_node.main_alignment,
803 parent_node.direction,
804 AlignmentDirection::Main,
805 );
806 }
807
808 if (parent_node.cross_alignment.is_not_start() || parent_node.content.is_fit())
809 && parent_node.content.allows_alignments()
810 {
811 Self::shrink_area_to_fit_when_unbounded(
813 available_area,
814 &initial_phase_parent_area,
815 &mut initial_phase_inner_area,
816 parent_node,
817 AlignmentDirection::Cross,
818 );
819 }
820 }
821
822 let initial_available_area = *available_area;
823
824 for child_id in children {
826 let Some(child_data) = self.tree_adapter.get_node(&child_id) else {
827 continue;
828 };
829
830 let is_first_child = first_child == Some(child_id);
831 let is_last_child = last_child == Some(child_id);
832
833 let mut adapted_available_area = *available_area;
834
835 if parent_node.content.is_flex() {
836 let flex_grow = initial_phase_flex_grows.get(&child_id);
837
838 if let Some(flex_grow) = flex_grow {
839 let flex_grow_per = flex_grow.get() / flex_grows.get() * 100.;
840
841 match flex_axis {
842 AlignAxis::Height => {
843 let size = flex_available_height / 100. * flex_grow_per;
844 adapted_available_area.size.height = size;
845 }
846 AlignAxis::Width => {
847 let size = flex_available_width / 100. * flex_grow_per;
848 adapted_available_area.size.width = size;
849 }
850 }
851 }
852 }
853
854 if parent_node.main_alignment.is_spaced()
856 && child_data.position.is_stacked()
857 && parent_node.content.allows_alignments()
858 {
859 Self::align_position(
861 AlignmentDirection::Main,
862 &mut adapted_available_area,
863 &initial_available_area,
864 initial_phase_inner_sizes,
865 &parent_node.main_alignment,
866 parent_node.direction,
867 non_absolute_children_len,
868 is_first_child,
869 );
870 }
871
872 if parent_node.cross_alignment.is_not_start() && parent_node.content.allows_alignments()
873 {
874 let initial_phase_size = initial_phase_sizes.get(&child_id);
875
876 if let Some(initial_phase_size) = initial_phase_size {
877 Self::align_content(
879 &mut adapted_available_area,
880 &available_area.as_inner(),
881 *initial_phase_size,
882 &parent_node.cross_alignment,
883 parent_node.direction,
884 AlignmentDirection::Cross,
885 );
886 }
887 }
888
889 if let Content::Wrap { wrap_spacing } = parent_node.content {
890 let initial_phase_size = initial_phase_sizes.get(&child_id);
891 Self::wrap_child(
892 wrap_spacing.unwrap_or_default(),
893 parent_node.direction,
894 initial_phase_size,
895 &initial_available_area,
896 parent_area,
897 available_area,
898 &mut adapted_available_area,
899 *inner_sizes,
900 );
901 }
902
903 let (child_revalidated, mut child_areas) = self.measure_node(
905 child_id,
906 &child_data,
907 initial_area.as_parent(),
908 adapted_available_area,
909 must_cache_children,
910 parent_is_dirty,
911 Phase::Final,
912 );
913
914 child_areas.area.adjust_size(&child_data);
916
917 if child_data.position.is_stacked() {
919 Self::stack_child(
920 available_area,
921 parent_node,
922 &child_data,
923 parent_area,
924 inner_area,
925 inner_sizes,
926 &child_areas.area,
927 is_last_child,
928 Phase::Final,
929 );
930 }
931
932 if child_revalidated && must_cache_children {
934 self.layout.cache_node(child_id, child_areas);
936 }
937 }
938 }
939
940 #[allow(clippy::too_many_arguments)]
941 fn wrap_child(
942 wrap_spacing: f32,
943 direction: Direction,
944 initial_phase_size: Option<&Size2D>,
945 initial_available_area: &AreaOf<Available>,
946 parent_area: &mut AreaOf<Parent>,
947 available_area: &mut AreaOf<Available>,
948 adapted_available_area: &mut AreaOf<Available>,
949 inner_sizes: Size2D,
950 ) {
951 if let Some(initial_phase_size) = initial_phase_size {
952 match direction {
953 Direction::Vertical => {
954 if adapted_available_area.height() - initial_phase_size.height < 0. {
955 let advance = inner_sizes.width + wrap_spacing;
956 available_area.origin.y = initial_available_area.origin.y;
957 available_area.size.height = initial_available_area.size.height;
958 available_area.origin.x += advance;
959 adapted_available_area.origin.y = initial_available_area.origin.y;
960 adapted_available_area.size.height = initial_available_area.size.height;
961 adapted_available_area.origin.x += advance;
962 parent_area.size.width += advance;
963 }
964 }
965 Direction::Horizontal => {
966 if adapted_available_area.width() - initial_phase_size.width < 0. {
967 let advance = inner_sizes.height + wrap_spacing;
968 available_area.origin.x = initial_available_area.origin.x;
969 available_area.size.width = initial_available_area.size.width;
970 available_area.origin.y += advance;
971 adapted_available_area.origin.x = initial_available_area.origin.x;
972 adapted_available_area.size.width = initial_available_area.size.width;
973 adapted_available_area.origin.y += advance;
974 parent_area.size.height += advance;
975 }
976 }
977 }
978 }
979 }
980
981 fn align_content(
983 available_area: &mut AreaOf<Available>,
984 inner_area: &AreaOf<Inner>,
985 contents_size: Size2D,
986 alignment: &Alignment,
987 direction: Direction,
988 alignment_direction: AlignmentDirection,
989 ) {
990 let axis = AlignAxis::new(&direction, alignment_direction);
991
992 match axis {
993 AlignAxis::Height => match alignment {
994 Alignment::Center => {
995 let new_origin_y = (inner_area.height() / 2.0) - (contents_size.height / 2.0);
996 available_area.origin.y = inner_area.min_y() + new_origin_y;
997 }
998 Alignment::End => {
999 available_area.origin.y = inner_area.max_y() - contents_size.height;
1000 }
1001 _ => {}
1002 },
1003 AlignAxis::Width => match alignment {
1004 Alignment::Center => {
1005 let new_origin_x = (inner_area.width() / 2.0) - (contents_size.width / 2.0);
1006 available_area.origin.x = inner_area.min_x() + new_origin_x;
1007 }
1008 Alignment::End => {
1009 available_area.origin.x = inner_area.max_x() - contents_size.width;
1010 }
1011 _ => {}
1012 },
1013 }
1014 }
1015
1016 #[allow(clippy::too_many_arguments)]
1018 fn align_position(
1019 alignment_direction: AlignmentDirection,
1020 available_area: &mut AreaOf<Available>,
1021 initial_available_area: &AreaOf<Available>,
1022 inner_sizes: Size2D,
1023 alignment: &Alignment,
1024 direction: Direction,
1025 siblings_len: usize,
1026 is_first_sibling: bool,
1027 ) {
1028 let axis = AlignAxis::new(&direction, alignment_direction);
1029
1030 match axis {
1031 AlignAxis::Height => match alignment {
1032 Alignment::SpaceBetween if !is_first_sibling => {
1033 let all_gaps_sizes = initial_available_area.height() - inner_sizes.height;
1034 let gap_size = all_gaps_sizes / (siblings_len - 1) as f32;
1035 available_area.origin.y += gap_size;
1036 }
1037 Alignment::SpaceEvenly => {
1038 let all_gaps_sizes = initial_available_area.height() - inner_sizes.height;
1039 let gap_size = all_gaps_sizes / (siblings_len + 1) as f32;
1040 available_area.origin.y += gap_size;
1041 }
1042 Alignment::SpaceAround => {
1043 let all_gaps_sizes = initial_available_area.height() - inner_sizes.height;
1044 let one_gap_size = all_gaps_sizes / siblings_len as f32;
1045 let gap_size = if is_first_sibling {
1046 one_gap_size / 2.
1047 } else {
1048 one_gap_size
1049 };
1050 available_area.origin.y += gap_size;
1051 }
1052 _ => {}
1053 },
1054 AlignAxis::Width => match alignment {
1055 Alignment::SpaceBetween if !is_first_sibling => {
1056 let all_gaps_sizes = initial_available_area.width() - inner_sizes.width;
1057 let gap_size = all_gaps_sizes / (siblings_len - 1) as f32;
1058 available_area.origin.x += gap_size;
1059 }
1060 Alignment::SpaceEvenly => {
1061 let all_gaps_sizes = initial_available_area.width() - inner_sizes.width;
1062 let gap_size = all_gaps_sizes / (siblings_len + 1) as f32;
1063 available_area.origin.x += gap_size;
1064 }
1065 Alignment::SpaceAround => {
1066 let all_gaps_sizes = initial_available_area.width() - inner_sizes.width;
1067 let one_gap_size = all_gaps_sizes / siblings_len as f32;
1068 let gap_size = if is_first_sibling {
1069 one_gap_size / 2.
1070 } else {
1071 one_gap_size
1072 };
1073 available_area.origin.x += gap_size;
1074 }
1075 _ => {}
1076 },
1077 }
1078 }
1079
1080 #[allow(clippy::too_many_arguments)]
1082 fn stack_child(
1083 available_area: &mut AreaOf<Available>,
1084 parent_node: &Node,
1085 child_node: &Node,
1086 parent_area: &mut AreaOf<Parent>,
1087 inner_area: &mut AreaOf<Inner>,
1088 inner_sizes: &mut Size2D,
1089 child_area: &Area,
1090 is_last_sibilin: bool,
1091 phase: Phase,
1092 ) {
1093 let spacing = if is_last_sibilin {
1095 Length::default()
1096 } else {
1097 parent_node.spacing
1098 };
1099
1100 match parent_node.direction {
1101 Direction::Horizontal => {
1102 available_area.origin.x = child_area.max_x() + spacing.get();
1104 available_area.size.width -= child_area.size.width + spacing.get();
1105
1106 inner_sizes.height = child_area.height().max(inner_sizes.height);
1107 inner_sizes.width += spacing.get();
1108 if !child_node.width.is_flex() || phase == Phase::Final {
1109 inner_sizes.width += child_area.width();
1110 }
1111
1112 if parent_node.height.inner_sized() {
1114 parent_area.size.height = parent_area.size.height.max(
1115 child_area.size.height
1116 + parent_node.padding.vertical()
1117 + parent_node.margin.vertical(),
1118 );
1119 inner_area.size.height = parent_area.size.height
1121 - parent_node.padding.vertical()
1122 - parent_node.margin.vertical();
1123 }
1124
1125 if parent_node.width.inner_sized() {
1127 parent_area.size.width += child_area.size.width + spacing.get();
1128 }
1129 }
1130 Direction::Vertical => {
1131 available_area.origin.y = child_area.max_y() + spacing.get();
1133 available_area.size.height -= child_area.size.height + spacing.get();
1134
1135 inner_sizes.width = child_area.width().max(inner_sizes.width);
1136 inner_sizes.height += spacing.get();
1137 if !child_node.height.is_flex() || phase == Phase::Final {
1138 inner_sizes.height += child_area.height();
1139 }
1140
1141 if parent_node.width.inner_sized() {
1143 parent_area.size.width = parent_area.size.width.max(
1144 child_area.size.width
1145 + parent_node.padding.horizontal()
1146 + parent_node.margin.horizontal(),
1147 );
1148 inner_area.size.width = parent_area.size.width
1150 - parent_node.padding.horizontal()
1151 - parent_node.margin.horizontal();
1152 }
1153
1154 if parent_node.height.inner_sized() {
1156 parent_area.size.height += child_area.size.height + spacing.get();
1157 }
1158 }
1159 }
1160 }
1161
1162 fn shrink_area_to_fit_when_unbounded(
1168 available_area: &mut AreaOf<Available>,
1169 parent_area: &AreaOf<Parent>,
1170 inner_area: &mut AreaOf<Inner>,
1171 parent_node: &Node,
1172 alignment_direction: AlignmentDirection,
1173 ) {
1174 struct NodeData<'a> {
1175 pub inner_origin: &'a mut f32,
1176 pub inner_size: &'a mut f32,
1177 pub area_origin: f32,
1178 pub area_size: f32,
1179 pub one_side_padding: f32,
1180 pub two_sides_padding: f32,
1181 pub one_side_margin: f32,
1182 pub two_sides_margin: f32,
1183 pub available_size: &'a mut f32,
1184 }
1185
1186 let axis = AlignAxis::new(&parent_node.direction, alignment_direction);
1187 let (is_vertical_not_start, is_horizontal_not_start) = match parent_node.direction {
1188 Direction::Vertical => (
1189 parent_node.main_alignment.is_not_start(),
1190 parent_node.cross_alignment.is_not_start() || parent_node.content.is_fit(),
1191 ),
1192 Direction::Horizontal => (
1193 parent_node.cross_alignment.is_not_start() || parent_node.content.is_fit(),
1194 parent_node.main_alignment.is_not_start(),
1195 ),
1196 };
1197 let NodeData {
1198 inner_origin,
1199 inner_size,
1200 area_origin,
1201 area_size,
1202 one_side_padding,
1203 two_sides_padding,
1204 one_side_margin,
1205 two_sides_margin,
1206 available_size,
1207 } = match axis {
1208 AlignAxis::Height if parent_node.height.inner_sized() && is_vertical_not_start => {
1209 NodeData {
1210 inner_origin: &mut inner_area.origin.y,
1211 inner_size: &mut inner_area.size.height,
1212 area_origin: parent_area.origin.y,
1213 area_size: parent_area.size.height,
1214 one_side_padding: parent_node.padding.top(),
1215 two_sides_padding: parent_node.padding.vertical(),
1216 one_side_margin: parent_node.margin.top(),
1217 two_sides_margin: parent_node.margin.vertical(),
1218 available_size: &mut available_area.size.height,
1219 }
1220 }
1221 AlignAxis::Width if parent_node.width.inner_sized() && is_horizontal_not_start => {
1222 NodeData {
1223 inner_origin: &mut inner_area.origin.x,
1224 inner_size: &mut inner_area.size.width,
1225 area_origin: parent_area.origin.x,
1226 area_size: parent_area.size.width,
1227 one_side_padding: parent_node.padding.left(),
1228 two_sides_padding: parent_node.padding.horizontal(),
1229 one_side_margin: parent_node.margin.left(),
1230 two_sides_margin: parent_node.margin.horizontal(),
1231 available_size: &mut available_area.size.width,
1232 }
1233 }
1234 _ => return,
1235 };
1236
1237 *inner_origin = area_origin + one_side_padding + one_side_margin;
1239 *inner_size = area_size - two_sides_padding - two_sides_margin;
1241 *available_size = *inner_size;
1243 }
1244}