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 recursive_translate(&mut self, node_id: Key, offset_x: Length, offset_y: Length) {
66 let mut buffer = self.tree_adapter.children_of(&node_id);
67 while let Some(child) = buffer.pop() {
68 let node = self
69 .tree_adapter
70 .get_node(&child)
71 .expect("Node does not exist");
72
73 let translate = match node.position {
74 Position::Global(_) => false,
75 Position::Stacked(_) | Position::Absolute(_) => true,
76 };
77
78 if translate {
79 let layout_node = self
80 .layout
81 .get_mut(&child)
82 .expect("Cached node does not exist");
83 layout_node.area.origin.x += offset_x.get();
84 layout_node.area.origin.y += offset_y.get();
85 layout_node.inner_area.origin.x += offset_x.get();
86 layout_node.inner_area.origin.y += offset_y.get();
87
88 if let Some(measurer) = self.measurer {
89 measurer.notify_layout_references(
90 child,
91 layout_node.area,
92 layout_node.visible_area(),
93 layout_node.inner_sizes,
94 );
95 }
96
97 buffer.extend(self.tree_adapter.children_of(&child));
98 }
99 }
100 }
101
102 #[allow(clippy::too_many_arguments, clippy::missing_panics_doc)]
104 pub fn measure_node(
105 &mut self,
106 node_id: Key,
107 node: &Node,
108 initial_parent_area: AreaOf<Parent>,
110 available_parent_area: AreaOf<Available>,
112 must_cache_children: bool,
114 parent_is_dirty: bool,
116 phase: Phase,
118 ) -> (bool, LayoutNode) {
119 let reason = self.layout.dirty.get(&node_id).copied();
120
121 if let Some(layout_node) = self.layout.get_mut(&node_id)
123 && reason == Some(DirtyReason::InnerLayout)
124 && must_cache_children
125 {
126 let offset_x = node.offset_x - layout_node.offset_x;
128 let offset_y = node.offset_y - layout_node.offset_y;
129
130 layout_node.offset_x = node.offset_x;
131 layout_node.offset_y = node.offset_y;
132
133 let layout_node = layout_node.clone();
134
135 self.recursive_translate(node_id, offset_x, offset_y);
136
137 return (must_cache_children, layout_node);
138 }
139
140 let must_revalidate =
144 parent_is_dirty || reason.is_some() || !self.layout.results.contains_key(&node_id);
145 if must_revalidate {
146 let mut area_size = Size2D::new(node.padding.horizontal(), node.padding.vertical());
148
149 area_size.width = node.width.min_max(
151 area_size.width,
152 initial_parent_area.size.width,
153 available_parent_area.size.width,
154 node.margin.left(),
155 node.margin.horizontal(),
156 &node.minimum_width,
157 &node.maximum_width,
158 self.layout_metadata.root_area.width(),
159 phase,
160 );
161 area_size.height = node.height.min_max(
162 area_size.height,
163 initial_parent_area.size.height,
164 available_parent_area.size.height,
165 node.margin.top(),
166 node.margin.vertical(),
167 &node.minimum_height,
168 &node.maximum_height,
169 self.layout_metadata.root_area.height(),
170 phase,
171 );
172
173 let node_data = if let Some(measurer) = self.measurer {
176 if measurer.should_hook_measurement(node_id) {
177 let available_width =
178 Size::Pixels(Length::new(available_parent_area.size.width)).min_max(
179 area_size.width,
180 initial_parent_area.size.width,
181 available_parent_area.size.width,
182 node.margin.left(),
183 node.margin.horizontal(),
184 &node.minimum_width,
185 &node.maximum_width,
186 self.layout_metadata.root_area.width(),
187 phase,
188 );
189 let available_height =
190 Size::Pixels(Length::new(available_parent_area.size.height)).min_max(
191 area_size.height,
192 initial_parent_area.size.height,
193 available_parent_area.size.height,
194 node.margin.top(),
195 node.margin.vertical(),
196 &node.minimum_height,
197 &node.maximum_height,
198 self.layout_metadata.root_area.height(),
199 phase,
200 );
201 let most_fitting_width = *node
202 .width
203 .most_fitting_size(&area_size.width, &available_width);
204 let most_fitting_height = *node
205 .height
206 .most_fitting_size(&area_size.height, &available_height);
207
208 let most_fitting_area_size =
209 Size2D::new(most_fitting_width, most_fitting_height);
210 let res = measurer.measure(node_id, node, &most_fitting_area_size);
211
212 #[allow(clippy::float_cmp)]
214 if let Some((custom_size, node_data)) = res {
215 if node.width.inner_sized() {
216 area_size.width = node.width.min_max(
217 custom_size.width,
218 initial_parent_area.size.width,
219 available_parent_area.size.width,
220 node.margin.left(),
221 node.margin.horizontal(),
222 &node.minimum_width,
223 &node.maximum_width,
224 self.layout_metadata.root_area.width(),
225 phase,
226 );
227 }
228 if node.height.inner_sized() {
229 area_size.height = node.height.min_max(
230 custom_size.height,
231 initial_parent_area.size.height,
232 available_parent_area.size.height,
233 node.margin.top(),
234 node.margin.vertical(),
235 &node.minimum_height,
236 &node.maximum_height,
237 self.layout_metadata.root_area.height(),
238 phase,
239 );
240 }
241
242 Some(node_data)
244 } else {
245 None
246 }
247 } else {
248 None
249 }
250 } else {
251 None
252 };
253
254 let measure_inner_children = if let Some(measurer) = self.measurer {
255 measurer.should_measure_inner_children(node_id)
256 } else {
257 true
258 };
259
260 let phase_measure_inner_children = if phase == Phase::Initial {
263 node.width.inner_sized() || node.height.inner_sized()
264 } else {
265 true
266 };
267
268 let inner_size = {
270 let mut inner_size = area_size;
271
272 if node.width.inner_sized() {
274 inner_size.width = node.width.min_max(
275 available_parent_area.width(),
276 initial_parent_area.size.width,
277 available_parent_area.width(),
278 node.margin.left(),
279 node.margin.horizontal(),
280 &node.minimum_width,
281 &node.maximum_width,
282 self.layout_metadata.root_area.width(),
283 phase,
284 );
285 }
286 if node.height.inner_sized() {
287 inner_size.height = node.height.min_max(
288 available_parent_area.height(),
289 initial_parent_area.size.height,
290 available_parent_area.height(),
291 node.margin.top(),
292 node.margin.vertical(),
293 &node.minimum_height,
294 &node.maximum_height,
295 self.layout_metadata.root_area.height(),
296 phase,
297 );
298 }
299 inner_size
300 };
301
302 let area_origin = node.position.get_origin(
304 &available_parent_area,
305 &initial_parent_area,
306 area_size,
307 &self.layout_metadata.root_area,
308 );
309 let mut area = Area::new(area_origin, area_size);
310 let mut inner_area = Rect::new(area_origin, inner_size)
311 .without_gaps(&node.padding)
312 .without_gaps(&node.margin)
313 .as_inner();
314 inner_area.move_with_offsets(&node.offset_x, &node.offset_y);
315
316 let mut inner_sizes = Size2D::default();
317
318 if measure_inner_children && phase_measure_inner_children {
319 let mut available_area = inner_area.as_available();
321
322 let mut parent_area = area.as_parent();
323
324 self.measure_children(
326 &node_id,
327 node,
328 &mut parent_area,
329 &mut inner_area,
330 &mut available_area,
331 &mut inner_sizes,
332 must_cache_children,
333 true,
334 );
335
336 if node.width.inner_sized() {
339 parent_area.size.width = node.width.min_max(
340 parent_area.size.width,
341 parent_area.size.width,
342 available_parent_area.size.width,
343 0.,
344 0.,
345 &node.minimum_width,
346 &node.maximum_width,
347 self.layout_metadata.root_area.width(),
348 phase,
349 );
350 }
351 if node.height.inner_sized() {
352 parent_area.size.height = node.height.min_max(
353 parent_area.size.height,
354 parent_area.size.height,
355 available_parent_area.size.height,
356 0.,
357 0.,
358 &node.minimum_height,
359 &node.maximum_height,
360 self.layout_metadata.root_area.height(),
361 phase,
362 );
363 }
364
365 area = parent_area.cast_unit();
366 }
367
368 let layout_node = LayoutNode {
369 area,
370 margin: node.margin,
371 offset_x: node.offset_x,
372 offset_y: node.offset_y,
373 inner_area,
374 data: node_data,
375 inner_sizes,
376 };
377
378 if must_cache_children
380 && phase == Phase::Final
381 && node.has_layout_references
382 && let Some(measurer) = self.measurer
383 {
384 inner_sizes.width += node.padding.horizontal();
385 inner_sizes.height += node.padding.vertical();
386 measurer.notify_layout_references(
387 node_id,
388 layout_node.area,
389 layout_node.visible_area(),
390 inner_sizes,
391 );
392 }
393
394 (must_cache_children, layout_node)
395 } else {
396 let layout_node = self
397 .layout
398 .get(&node_id)
399 .expect("Cached node does not exist")
400 .clone();
401
402 let mut inner_sizes = Size2D::default();
403 let mut area = layout_node.area.as_parent();
404 let mut inner_area = layout_node.inner_area.as_inner();
405 let mut available_area = inner_area.as_available();
406
407 let measure_inner_children = if let Some(measurer) = self.measurer {
408 measurer.should_measure_inner_children(node_id)
409 } else {
410 true
411 };
412
413 if measure_inner_children {
414 self.measure_children(
415 &node_id,
416 node,
417 &mut area,
418 &mut inner_area,
419 &mut available_area,
420 &mut inner_sizes,
421 must_cache_children,
422 false,
423 );
424 }
425
426 (false, layout_node)
427 }
428 }
429
430 #[allow(clippy::too_many_arguments)]
432 pub fn measure_children(
433 &mut self,
434 parent_node_id: &Key,
435 parent_node: &Node,
436 parent_area: &mut AreaOf<Parent>,
437 inner_area: &mut AreaOf<Inner>,
438 available_area: &mut AreaOf<Available>,
439 inner_sizes: &mut Size2D,
441 must_cache_children: bool,
443 parent_is_dirty: bool,
445 ) {
446 let children = self.tree_adapter.children_of(parent_node_id);
447
448 let initial_area = *inner_area;
449
450 let mut initial_phase_flex_grows = FxHashMap::default();
451 let mut initial_phase_sizes = FxHashMap::default();
452 let mut initial_phase_inner_sizes = Size2D::default();
453
454 let (non_absolute_children_len, first_child, last_child) = if parent_node.spacing.get() > 0.
456 {
457 let mut last_child = None;
458 let mut first_child = None;
459 let len = children
460 .iter()
461 .filter(|child_id| {
462 let Some(child_data) = self.tree_adapter.get_node(child_id) else {
463 return false;
464 };
465 let is_stacked = child_data.position.is_stacked();
466 if is_stacked {
467 last_child = Some(**child_id);
468
469 if first_child.is_none() {
470 first_child = Some(**child_id);
471 }
472 }
473 is_stacked
474 })
475 .count();
476 (len, first_child, last_child)
477 } else {
478 (
479 children.len(),
480 children.first().copied(),
481 children.last().copied(),
482 )
483 };
484
485 let needs_initial_phase = parent_node.cross_alignment.is_not_start()
486 || parent_node.main_alignment.is_not_start()
487 || parent_node.content.is_fit()
488 || parent_node.content.is_flex()
489 || parent_node.content.is_wrap();
490
491 let mut initial_phase_parent_area = *parent_area;
492 let mut initial_phase_inner_area = *inner_area;
493 let mut initial_phase_available_area = *available_area;
494
495 if needs_initial_phase {
498 for child_id in &children {
500 let Some(child_data) = self.tree_adapter.get_node(child_id) else {
501 continue;
502 };
503
504 if !child_data.position.is_stacked() {
507 continue;
508 }
509
510 let is_last_child = last_child == Some(*child_id);
511
512 let (_, mut child_areas) = self.measure_node(
513 *child_id,
514 &child_data,
515 initial_area.as_parent(),
516 initial_phase_available_area,
517 false,
518 parent_is_dirty,
519 Phase::Initial,
520 );
521
522 child_areas.area.adjust_size(&child_data);
523
524 Self::stack_child(
526 &mut initial_phase_available_area,
527 parent_node,
528 &child_data,
529 &mut initial_phase_parent_area,
530 &mut initial_phase_inner_area,
531 &mut initial_phase_inner_sizes,
532 &child_areas.area,
533 is_last_child,
534 Phase::Initial,
535 );
536
537 if parent_node.cross_alignment.is_not_start()
538 || parent_node.main_alignment.is_spaced()
539 || parent_node.content.is_wrap()
540 {
541 initial_phase_sizes.insert(*child_id, child_areas.area.size);
542 }
543
544 if parent_node.content.is_flex() {
545 match parent_node.direction {
546 Direction::Vertical => {
547 if let Some(ff) = child_data.height.flex_grow() {
548 initial_phase_flex_grows.insert(*child_id, ff);
549 }
550 }
551 Direction::Horizontal => {
552 if let Some(ff) = child_data.width.flex_grow() {
553 initial_phase_flex_grows.insert(*child_id, ff);
554 }
555 }
556 }
557 }
558 }
559 }
560
561 let flex_grows = initial_phase_flex_grows
562 .values()
563 .copied()
564 .reduce(|acc, v| acc + v)
565 .unwrap_or_default()
566 .max(Length::new(1.0));
567
568 let flex_axis = AlignAxis::new(&parent_node.direction, AlignmentDirection::Main);
569
570 let flex_available_width = available_area.width() - initial_phase_inner_sizes.width;
571 let flex_available_height = available_area.height() - initial_phase_inner_sizes.height;
572
573 if parent_node.content.is_flex() {
574 initial_phase_inner_sizes =
575 initial_phase_flex_grows
576 .values()
577 .fold(initial_phase_inner_sizes, |mut acc, f| {
578 let flex_grow_per = f.get() / flex_grows.get() * 100.;
579
580 match flex_axis {
581 AlignAxis::Height => {
582 let size = flex_available_height / 100. * flex_grow_per;
583 acc.height += size;
584 }
585 AlignAxis::Width => {
586 let size = flex_available_width / 100. * flex_grow_per;
587 acc.width += size;
588 }
589 }
590
591 acc
592 });
593 }
594
595 if needs_initial_phase {
596 if parent_node.main_alignment.is_not_start() && parent_node.content.allows_alignments()
597 {
598 Self::shrink_area_to_fit_when_unbounded(
600 available_area,
601 &initial_phase_parent_area,
602 &mut initial_phase_inner_area,
603 parent_node,
604 AlignmentDirection::Main,
605 );
606
607 Self::align_content(
609 available_area,
610 &initial_phase_inner_area,
611 initial_phase_inner_sizes,
612 &parent_node.main_alignment,
613 parent_node.direction,
614 AlignmentDirection::Main,
615 );
616 }
617
618 if (parent_node.cross_alignment.is_not_start() || parent_node.content.is_fit())
619 && parent_node.content.allows_alignments()
620 {
621 Self::shrink_area_to_fit_when_unbounded(
623 available_area,
624 &initial_phase_parent_area,
625 &mut initial_phase_inner_area,
626 parent_node,
627 AlignmentDirection::Cross,
628 );
629 }
630 }
631
632 let initial_available_area = *available_area;
633
634 for child_id in children {
636 let Some(child_data) = self.tree_adapter.get_node(&child_id) else {
637 continue;
638 };
639
640 let is_first_child = first_child == Some(child_id);
641 let is_last_child = last_child == Some(child_id);
642
643 let mut adapted_available_area = *available_area;
644
645 if parent_node.content.is_flex() {
646 let flex_grow = initial_phase_flex_grows.get(&child_id);
647
648 if let Some(flex_grow) = flex_grow {
649 let flex_grow_per = flex_grow.get() / flex_grows.get() * 100.;
650
651 match flex_axis {
652 AlignAxis::Height => {
653 let size = flex_available_height / 100. * flex_grow_per;
654 adapted_available_area.size.height = size;
655 }
656 AlignAxis::Width => {
657 let size = flex_available_width / 100. * flex_grow_per;
658 adapted_available_area.size.width = size;
659 }
660 }
661 }
662 }
663
664 if parent_node.main_alignment.is_spaced()
666 && child_data.position.is_stacked()
667 && parent_node.content.allows_alignments()
668 {
669 Self::align_position(
671 AlignmentDirection::Main,
672 &mut adapted_available_area,
673 &initial_available_area,
674 initial_phase_inner_sizes,
675 &parent_node.main_alignment,
676 parent_node.direction,
677 non_absolute_children_len,
678 is_first_child,
679 );
680 }
681
682 if parent_node.cross_alignment.is_not_start() && parent_node.content.allows_alignments()
683 {
684 let initial_phase_size = initial_phase_sizes.get(&child_id);
685
686 if let Some(initial_phase_size) = initial_phase_size {
687 Self::align_content(
689 &mut adapted_available_area,
690 &available_area.as_inner(),
691 *initial_phase_size,
692 &parent_node.cross_alignment,
693 parent_node.direction,
694 AlignmentDirection::Cross,
695 );
696 }
697 }
698
699 if let Content::Wrap { wrap_spacing } = parent_node.content {
700 let initial_phase_size = initial_phase_sizes.get(&child_id);
701 Self::wrap_child(
702 wrap_spacing.unwrap_or_default(),
703 parent_node.direction,
704 initial_phase_size,
705 &initial_available_area,
706 parent_area,
707 available_area,
708 &mut adapted_available_area,
709 *inner_sizes,
710 );
711 }
712
713 let (child_revalidated, mut child_areas) = self.measure_node(
715 child_id,
716 &child_data,
717 initial_area.as_parent(),
718 adapted_available_area,
719 must_cache_children,
720 parent_is_dirty,
721 Phase::Final,
722 );
723
724 child_areas.area.adjust_size(&child_data);
726
727 if child_data.position.is_stacked() {
729 Self::stack_child(
730 available_area,
731 parent_node,
732 &child_data,
733 parent_area,
734 inner_area,
735 inner_sizes,
736 &child_areas.area,
737 is_last_child,
738 Phase::Final,
739 );
740 }
741
742 if child_revalidated && must_cache_children {
744 self.layout.cache_node(child_id, child_areas);
746 }
747 }
748 }
749
750 #[allow(clippy::too_many_arguments)]
751 fn wrap_child(
752 wrap_spacing: f32,
753 direction: Direction,
754 initial_phase_size: Option<&Size2D>,
755 initial_available_area: &AreaOf<Available>,
756 parent_area: &mut AreaOf<Parent>,
757 available_area: &mut AreaOf<Available>,
758 adapted_available_area: &mut AreaOf<Available>,
759 inner_sizes: Size2D,
760 ) {
761 if let Some(initial_phase_size) = initial_phase_size {
762 match direction {
763 Direction::Vertical => {
764 if adapted_available_area.height() - initial_phase_size.height < 0. {
765 let advance = inner_sizes.width + wrap_spacing;
766 available_area.origin.y = initial_available_area.origin.y;
767 available_area.size.height = initial_available_area.size.height;
768 available_area.origin.x += advance;
769 adapted_available_area.origin.y = initial_available_area.origin.y;
770 adapted_available_area.size.height = initial_available_area.size.height;
771 adapted_available_area.origin.x += advance;
772 parent_area.size.width += advance;
773 }
774 }
775 Direction::Horizontal => {
776 if adapted_available_area.width() - initial_phase_size.width < 0. {
777 let advance = inner_sizes.height + wrap_spacing;
778 available_area.origin.x = initial_available_area.origin.x;
779 available_area.size.width = initial_available_area.size.width;
780 available_area.origin.y += advance;
781 adapted_available_area.origin.x = initial_available_area.origin.x;
782 adapted_available_area.size.width = initial_available_area.size.width;
783 adapted_available_area.origin.y += advance;
784 parent_area.size.height += advance;
785 }
786 }
787 }
788 }
789 }
790
791 fn align_content(
793 available_area: &mut AreaOf<Available>,
794 inner_area: &AreaOf<Inner>,
795 contents_size: Size2D,
796 alignment: &Alignment,
797 direction: Direction,
798 alignment_direction: AlignmentDirection,
799 ) {
800 let axis = AlignAxis::new(&direction, alignment_direction);
801
802 match axis {
803 AlignAxis::Height => match alignment {
804 Alignment::Center => {
805 let new_origin_y = (inner_area.height() / 2.0) - (contents_size.height / 2.0);
806 available_area.origin.y = inner_area.min_y() + new_origin_y;
807 }
808 Alignment::End => {
809 available_area.origin.y = inner_area.max_y() - contents_size.height;
810 }
811 _ => {}
812 },
813 AlignAxis::Width => match alignment {
814 Alignment::Center => {
815 let new_origin_x = (inner_area.width() / 2.0) - (contents_size.width / 2.0);
816 available_area.origin.x = inner_area.min_x() + new_origin_x;
817 }
818 Alignment::End => {
819 available_area.origin.x = inner_area.max_x() - contents_size.width;
820 }
821 _ => {}
822 },
823 }
824 }
825
826 #[allow(clippy::too_many_arguments)]
828 fn align_position(
829 alignment_direction: AlignmentDirection,
830 available_area: &mut AreaOf<Available>,
831 initial_available_area: &AreaOf<Available>,
832 inner_sizes: Size2D,
833 alignment: &Alignment,
834 direction: Direction,
835 siblings_len: usize,
836 is_first_sibling: bool,
837 ) {
838 let axis = AlignAxis::new(&direction, alignment_direction);
839
840 match axis {
841 AlignAxis::Height => match alignment {
842 Alignment::SpaceBetween if !is_first_sibling => {
843 let all_gaps_sizes = initial_available_area.height() - inner_sizes.height;
844 let gap_size = all_gaps_sizes / (siblings_len - 1) as f32;
845 available_area.origin.y += gap_size;
846 }
847 Alignment::SpaceEvenly => {
848 let all_gaps_sizes = initial_available_area.height() - inner_sizes.height;
849 let gap_size = all_gaps_sizes / (siblings_len + 1) as f32;
850 available_area.origin.y += gap_size;
851 }
852 Alignment::SpaceAround => {
853 let all_gaps_sizes = initial_available_area.height() - inner_sizes.height;
854 let one_gap_size = all_gaps_sizes / siblings_len as f32;
855 let gap_size = if is_first_sibling {
856 one_gap_size / 2.
857 } else {
858 one_gap_size
859 };
860 available_area.origin.y += gap_size;
861 }
862 _ => {}
863 },
864 AlignAxis::Width => match alignment {
865 Alignment::SpaceBetween if !is_first_sibling => {
866 let all_gaps_sizes = initial_available_area.width() - inner_sizes.width;
867 let gap_size = all_gaps_sizes / (siblings_len - 1) as f32;
868 available_area.origin.x += gap_size;
869 }
870 Alignment::SpaceEvenly => {
871 let all_gaps_sizes = initial_available_area.width() - inner_sizes.width;
872 let gap_size = all_gaps_sizes / (siblings_len + 1) as f32;
873 available_area.origin.x += gap_size;
874 }
875 Alignment::SpaceAround => {
876 let all_gaps_sizes = initial_available_area.width() - inner_sizes.width;
877 let one_gap_size = all_gaps_sizes / siblings_len as f32;
878 let gap_size = if is_first_sibling {
879 one_gap_size / 2.
880 } else {
881 one_gap_size
882 };
883 available_area.origin.x += gap_size;
884 }
885 _ => {}
886 },
887 }
888 }
889
890 #[allow(clippy::too_many_arguments)]
892 fn stack_child(
893 available_area: &mut AreaOf<Available>,
894 parent_node: &Node,
895 child_node: &Node,
896 parent_area: &mut AreaOf<Parent>,
897 inner_area: &mut AreaOf<Inner>,
898 inner_sizes: &mut Size2D,
899 child_area: &Area,
900 is_last_sibilin: bool,
901 phase: Phase,
902 ) {
903 let spacing = if is_last_sibilin {
905 Length::default()
906 } else {
907 parent_node.spacing
908 };
909
910 match parent_node.direction {
911 Direction::Horizontal => {
912 available_area.origin.x = child_area.max_x() + spacing.get();
914 available_area.size.width -= child_area.size.width + spacing.get();
915
916 inner_sizes.height = child_area.height().max(inner_sizes.height);
917 inner_sizes.width += spacing.get();
918 if !child_node.width.is_flex() || phase == Phase::Final {
919 inner_sizes.width += child_area.width();
920 }
921
922 if parent_node.height.inner_sized() {
924 parent_area.size.height = parent_area.size.height.max(
925 child_area.size.height
926 + parent_node.padding.vertical()
927 + parent_node.margin.vertical(),
928 );
929 inner_area.size.height = parent_area.size.height
931 - parent_node.padding.vertical()
932 - parent_node.margin.vertical();
933 }
934
935 if parent_node.width.inner_sized() {
937 parent_area.size.width += child_area.size.width + spacing.get();
938 }
939 }
940 Direction::Vertical => {
941 available_area.origin.y = child_area.max_y() + spacing.get();
943 available_area.size.height -= child_area.size.height + spacing.get();
944
945 inner_sizes.width = child_area.width().max(inner_sizes.width);
946 inner_sizes.height += spacing.get();
947 if !child_node.height.is_flex() || phase == Phase::Final {
948 inner_sizes.height += child_area.height();
949 }
950
951 if parent_node.width.inner_sized() {
953 parent_area.size.width = parent_area.size.width.max(
954 child_area.size.width
955 + parent_node.padding.horizontal()
956 + parent_node.margin.horizontal(),
957 );
958 inner_area.size.width = parent_area.size.width
960 - parent_node.padding.horizontal()
961 - parent_node.margin.horizontal();
962 }
963
964 if parent_node.height.inner_sized() {
966 parent_area.size.height += child_area.size.height + spacing.get();
967 }
968 }
969 }
970 }
971
972 fn shrink_area_to_fit_when_unbounded(
978 available_area: &mut AreaOf<Available>,
979 parent_area: &AreaOf<Parent>,
980 inner_area: &mut AreaOf<Inner>,
981 parent_node: &Node,
982 alignment_direction: AlignmentDirection,
983 ) {
984 struct NodeData<'a> {
985 pub inner_origin: &'a mut f32,
986 pub inner_size: &'a mut f32,
987 pub area_origin: f32,
988 pub area_size: f32,
989 pub one_side_padding: f32,
990 pub two_sides_padding: f32,
991 pub one_side_margin: f32,
992 pub two_sides_margin: f32,
993 pub available_size: &'a mut f32,
994 }
995
996 let axis = AlignAxis::new(&parent_node.direction, alignment_direction);
997 let (is_vertical_not_start, is_horizontal_not_start) = match parent_node.direction {
998 Direction::Vertical => (
999 parent_node.main_alignment.is_not_start(),
1000 parent_node.cross_alignment.is_not_start() || parent_node.content.is_fit(),
1001 ),
1002 Direction::Horizontal => (
1003 parent_node.cross_alignment.is_not_start() || parent_node.content.is_fit(),
1004 parent_node.main_alignment.is_not_start(),
1005 ),
1006 };
1007 let NodeData {
1008 inner_origin,
1009 inner_size,
1010 area_origin,
1011 area_size,
1012 one_side_padding,
1013 two_sides_padding,
1014 one_side_margin,
1015 two_sides_margin,
1016 available_size,
1017 } = match axis {
1018 AlignAxis::Height if parent_node.height.inner_sized() && is_vertical_not_start => {
1019 NodeData {
1020 inner_origin: &mut inner_area.origin.y,
1021 inner_size: &mut inner_area.size.height,
1022 area_origin: parent_area.origin.y,
1023 area_size: parent_area.size.height,
1024 one_side_padding: parent_node.padding.top(),
1025 two_sides_padding: parent_node.padding.vertical(),
1026 one_side_margin: parent_node.margin.top(),
1027 two_sides_margin: parent_node.margin.vertical(),
1028 available_size: &mut available_area.size.height,
1029 }
1030 }
1031 AlignAxis::Width if parent_node.width.inner_sized() && is_horizontal_not_start => {
1032 NodeData {
1033 inner_origin: &mut inner_area.origin.x,
1034 inner_size: &mut inner_area.size.width,
1035 area_origin: parent_area.origin.x,
1036 area_size: parent_area.size.width,
1037 one_side_padding: parent_node.padding.left(),
1038 two_sides_padding: parent_node.padding.horizontal(),
1039 one_side_margin: parent_node.margin.left(),
1040 two_sides_margin: parent_node.margin.horizontal(),
1041 available_size: &mut available_area.size.width,
1042 }
1043 }
1044 _ => return,
1045 };
1046
1047 *inner_origin = area_origin + one_side_padding + one_side_margin;
1049 *inner_size = area_size - two_sides_padding - two_sides_margin;
1051 *available_size = *inner_size;
1053 }
1054}