torin/
measure.rs

1pub use euclid::Rect;
2use rustc_hash::FxHashMap;
3
4use crate::{
5    custom_measurer::LayoutMeasurer,
6    dom_adapter::{
7        LayoutNode,
8        NodeKey,
9        TreeAdapter,
10    },
11    geometry::{
12        Area,
13        Size2D,
14    },
15    node::Node,
16    prelude::{
17        AlignAxis,
18        Alignment,
19        AlignmentDirection,
20        AreaModel,
21        Direction,
22        LayoutMetadata,
23        Length,
24        Position,
25        Torin,
26    },
27    size::Size,
28    torin::DirtyReason,
29};
30
31/// Some layout strategies require two-phase measurements
32/// Example: Alignments or content-fit.
33#[derive(Clone, Copy, PartialEq)]
34pub enum Phase {
35    Initial,
36    Final,
37}
38
39pub struct MeasureContext<'a, Key, L, D>
40where
41    Key: NodeKey,
42    L: LayoutMeasurer<Key>,
43    D: TreeAdapter<Key>,
44{
45    pub layout: &'a mut Torin<Key>,
46    pub measurer: &'a mut Option<L>,
47    pub dom_adapter: &'a mut D,
48    pub layout_metadata: LayoutMetadata,
49}
50
51impl<Key, L, D> MeasureContext<'_, Key, L, D>
52where
53    Key: NodeKey,
54    L: LayoutMeasurer<Key>,
55    D: TreeAdapter<Key>,
56{
57    fn recursive_translate(
58        &mut self,
59        // ID for this Node
60        node_id: Key,
61        // X axis translation offset
62        offset_x: Length,
63        // Y axis translation offset
64        offset_y: Length,
65    ) {
66        let mut buffer = self
67            .dom_adapter
68            .children_of(&node_id)
69            .into_iter()
70            .map(|id| (node_id, id))
71            .collect::<Vec<(Key, Key)>>();
72        while let Some((parent, child)) = buffer.pop() {
73            let node = self.dom_adapter.get_node(&child).unwrap();
74            let translate = match node.position {
75                Position::Global(_) => false,
76                Position::Absolute(_) => parent != node_id,
77                Position::Stacked(_) => true,
78            };
79            if translate {
80                let layout_node = self.layout.get_mut(&child).unwrap();
81
82                layout_node.area.origin.x += offset_x.get();
83                layout_node.area.origin.y += offset_y.get();
84                layout_node.inner_area.origin.x += offset_x.get();
85                layout_node.inner_area.origin.y += offset_y.get();
86
87                buffer.extend(
88                    self.dom_adapter
89                        .children_of(&child)
90                        .into_iter()
91                        .map(|id| (node_id, id)),
92                );
93            }
94        }
95    }
96
97    /// Measure a Node.
98    #[allow(clippy::too_many_arguments, clippy::missing_panics_doc)]
99    pub fn measure_node(
100        &mut self,
101        // ID for this Node
102        node_id: Key,
103        // Data of this Node
104        node: &Node,
105        // Area occupied by it's parent
106        parent_area: &Area,
107        // Area that is available to use by the children of the parent
108        available_parent_area: &Area,
109        // Whether to cache the measurements of this Node's children
110        must_cache_children: bool,
111        // Parent Node is dirty.
112        parent_is_dirty: bool,
113        // Current phase of measurement
114        phase: Phase,
115    ) -> (bool, LayoutNode) {
116        let reason = self.layout.dirty.get(&node_id).copied();
117
118        // If possible translate all this node's descendants to avoid relayout
119        if let Some(layout_node) = self.layout.get_mut(&node_id)
120            && reason == Some(DirtyReason::InnerLayout)
121            && must_cache_children
122        {
123            // Get the offset difference since the last layout
124            let offset_x = node.offset_x - layout_node.offset_x;
125            let offset_y = node.offset_y - layout_node.offset_y;
126
127            layout_node.offset_x = node.offset_x;
128            layout_node.offset_y = node.offset_y;
129
130            let layout_node = layout_node.clone();
131
132            self.recursive_translate(node_id, offset_x, offset_y);
133
134            return (must_cache_children, layout_node);
135        }
136
137        // 1. If parent is dirty
138        // 2. If this Node has been marked as dirty
139        // 3. If there is no know cached data about this Node.
140        let must_revalidate =
141            parent_is_dirty || reason.is_some() || !self.layout.results.contains_key(&node_id);
142        if must_revalidate {
143            // Create the initial Node area size
144            let mut area_size = Size2D::new(node.padding.horizontal(), node.padding.vertical());
145
146            // Compute the width and height given the size, the minimum size, the maximum size and margins
147            area_size.width = node.width.min_max(
148                area_size.width,
149                parent_area.size.width,
150                available_parent_area.size.width,
151                node.margin.left(),
152                node.margin.horizontal(),
153                &node.minimum_width,
154                &node.maximum_width,
155                self.layout_metadata.root_area.width(),
156                phase,
157            );
158            area_size.height = node.height.min_max(
159                area_size.height,
160                parent_area.size.height,
161                available_parent_area.size.height,
162                node.margin.top(),
163                node.margin.vertical(),
164                &node.minimum_height,
165                &node.maximum_height,
166                self.layout_metadata.root_area.height(),
167                phase,
168            );
169
170            // If available, run a custom layout measure function
171            // This is useful when you use third-party libraries (e.g. rust-skia, cosmic-text) to measure text layouts
172            let node_data = if let Some(measurer) = self.measurer {
173                if measurer.should_hook_measurement(node_id) {
174                    let available_width =
175                        Size::Pixels(Length::new(available_parent_area.size.width)).min_max(
176                            area_size.width,
177                            parent_area.size.width,
178                            available_parent_area.size.width,
179                            node.margin.left(),
180                            node.margin.horizontal(),
181                            &node.minimum_width,
182                            &node.maximum_width,
183                            self.layout_metadata.root_area.width(),
184                            phase,
185                        );
186                    let available_height =
187                        Size::Pixels(Length::new(available_parent_area.size.height)).min_max(
188                            area_size.height,
189                            parent_area.size.height,
190                            available_parent_area.size.height,
191                            node.margin.top(),
192                            node.margin.vertical(),
193                            &node.minimum_height,
194                            &node.maximum_height,
195                            self.layout_metadata.root_area.height(),
196                            phase,
197                        );
198                    let most_fitting_width = *node
199                        .width
200                        .most_fitting_size(&area_size.width, &available_width);
201                    let most_fitting_height = *node
202                        .height
203                        .most_fitting_size(&area_size.height, &available_height);
204
205                    let most_fitting_area_size =
206                        Size2D::new(most_fitting_width, most_fitting_height);
207                    let res = measurer.measure(node_id, node, &most_fitting_area_size);
208
209                    // Compute the width and height again using the new custom area sizes
210                    #[allow(clippy::float_cmp)]
211                    if let Some((custom_size, node_data)) = res {
212                        if node.width.inner_sized() {
213                            area_size.width = node.width.min_max(
214                                custom_size.width,
215                                parent_area.size.width,
216                                available_parent_area.size.width,
217                                node.margin.left(),
218                                node.margin.horizontal(),
219                                &node.minimum_width,
220                                &node.maximum_width,
221                                self.layout_metadata.root_area.width(),
222                                phase,
223                            );
224                        }
225                        if node.height.inner_sized() {
226                            area_size.height = node.height.min_max(
227                                custom_size.height,
228                                parent_area.size.height,
229                                available_parent_area.size.height,
230                                node.margin.top(),
231                                node.margin.vertical(),
232                                &node.minimum_height,
233                                &node.maximum_height,
234                                self.layout_metadata.root_area.height(),
235                                phase,
236                            );
237                        }
238
239                        // Do not measure inner children
240                        Some(node_data)
241                    } else {
242                        None
243                    }
244                } else {
245                    None
246                }
247            } else {
248                None
249            };
250
251            let measure_inner_children = if let Some(measurer) = self.measurer {
252                measurer.should_measure_inner_children(node_id)
253            } else {
254                true
255            };
256
257            // There is no need to measure inner children in the initial phase if this Node size
258            // isn't decided by his children
259            let phase_measure_inner_children = if phase == Phase::Initial {
260                node.width.inner_sized() || node.height.inner_sized()
261            } else {
262                true
263            };
264
265            // Compute the inner size of the Node, which is basically the size inside the margins and paddings
266            let inner_size = {
267                let mut inner_size = area_size;
268
269                // When having an unsized bound we set it to whatever is still available in the parent's area
270                if node.width.inner_sized() {
271                    inner_size.width = node.width.min_max(
272                        available_parent_area.width(),
273                        parent_area.size.width,
274                        available_parent_area.width(),
275                        node.margin.left(),
276                        node.margin.horizontal(),
277                        &node.minimum_width,
278                        &node.maximum_width,
279                        self.layout_metadata.root_area.width(),
280                        phase,
281                    );
282                }
283                if node.height.inner_sized() {
284                    inner_size.height = node.height.min_max(
285                        available_parent_area.height(),
286                        parent_area.size.height,
287                        available_parent_area.height(),
288                        node.margin.top(),
289                        node.margin.vertical(),
290                        &node.minimum_height,
291                        &node.maximum_height,
292                        self.layout_metadata.root_area.height(),
293                        phase,
294                    );
295                }
296                inner_size
297            };
298
299            // Create the areas
300            let area_origin = node.position.get_origin(
301                available_parent_area,
302                parent_area,
303                area_size,
304                &self.layout_metadata.root_area,
305            );
306            let mut area = Rect::new(area_origin, area_size);
307            let mut inner_area = Rect::new(area_origin, inner_size)
308                .without_gaps(&node.padding)
309                .without_gaps(&node.margin);
310
311            let mut inner_sizes = Size2D::default();
312
313            if measure_inner_children && phase_measure_inner_children {
314                // Create an area containing the available space inside the inner area
315                let mut available_area = inner_area;
316
317                available_area.move_with_offsets(&node.offset_x, &node.offset_y);
318
319                // Measure the layout of this Node's children
320                self.measure_children(
321                    &node_id,
322                    node,
323                    &mut available_area,
324                    &mut inner_sizes,
325                    must_cache_children,
326                    &mut area,
327                    &mut inner_area,
328                    true,
329                );
330
331                // Re apply min max values after measurin with inner sized
332                // Margins are set to 0 because area.size already contains the margins
333                if node.width.inner_sized() {
334                    area.size.width = node.width.min_max(
335                        area.size.width,
336                        parent_area.size.width,
337                        available_parent_area.size.width,
338                        0.,
339                        0.,
340                        &node.minimum_width,
341                        &node.maximum_width,
342                        self.layout_metadata.root_area.width(),
343                        phase,
344                    );
345                }
346                if node.height.inner_sized() {
347                    area.size.height = node.height.min_max(
348                        area.size.height,
349                        parent_area.size.height,
350                        available_parent_area.size.height,
351                        0.,
352                        0.,
353                        &node.minimum_height,
354                        &node.maximum_height,
355                        self.layout_metadata.root_area.height(),
356                        phase,
357                    );
358                }
359            }
360
361            let layout_node = LayoutNode {
362                area,
363                margin: node.margin,
364                offset_x: node.offset_x,
365                offset_y: node.offset_y,
366                inner_area,
367                data: node_data,
368            };
369
370            // In case of any layout listener, notify it with the new areas.
371            if must_cache_children
372                && phase == Phase::Final
373                && node.has_layout_references
374                && let Some(measurer) = self.measurer
375            {
376                inner_sizes.width += node.padding.horizontal();
377                inner_sizes.height += node.padding.vertical();
378                measurer.notify_layout_references(
379                    node_id,
380                    layout_node.area,
381                    layout_node.visible_area(),
382                    inner_sizes,
383                );
384            }
385
386            (must_cache_children, layout_node)
387        } else {
388            let layout_node = self.layout.get(&node_id).unwrap().clone();
389
390            let mut inner_sizes = Size2D::default();
391            let mut available_area = layout_node.inner_area;
392            let mut area = layout_node.area;
393            let mut inner_area = layout_node.inner_area;
394
395            available_area.move_with_offsets(&node.offset_x, &node.offset_y);
396
397            let measure_inner_children = if let Some(measurer) = self.measurer {
398                measurer.should_measure_inner_children(node_id)
399            } else {
400                true
401            };
402
403            if measure_inner_children {
404                self.measure_children(
405                    &node_id,
406                    node,
407                    &mut available_area,
408                    &mut inner_sizes,
409                    must_cache_children,
410                    &mut area,
411                    &mut inner_area,
412                    false,
413                );
414            }
415
416            (false, layout_node)
417        }
418    }
419
420    /// Measure the children layouts of a Node
421    #[allow(clippy::too_many_arguments)]
422    pub fn measure_children(
423        &mut self,
424        parent_node_id: &Key,
425        parent_node: &Node,
426        // Area available inside the Node
427        available_area: &mut Area,
428        // Accumulated sizes in both axis in the Node
429        inner_sizes: &mut Size2D,
430        // Whether to cache the measurements of this Node's children
431        must_cache_children: bool,
432        // Parent area.
433        area: &mut Area,
434        // Inner area of the parent.
435        inner_area: &mut Area,
436        // Parent Node is dirty.
437        parent_is_dirty: bool,
438    ) {
439        let children = self.dom_adapter.children_of(parent_node_id);
440
441        let mut initial_phase_flex_grows = FxHashMap::default();
442        let mut initial_phase_sizes = FxHashMap::default();
443        let mut initial_phase_inner_sizes = Size2D::default();
444
445        // Used to calculate the spacing and some alignments
446        let (non_absolute_children_len, first_child, last_child) = if parent_node.spacing.get() > 0.
447        {
448            let mut last_child = None;
449            let mut first_child = None;
450            let len = children
451                .iter()
452                .filter(|child_id| {
453                    let Some(child_data) = self.dom_adapter.get_node(child_id) else {
454                        return false;
455                    };
456                    let is_stacked = child_data.position.is_stacked();
457                    if is_stacked {
458                        last_child = Some(**child_id);
459
460                        if first_child.is_none() {
461                            first_child = Some(**child_id);
462                        }
463                    }
464                    is_stacked
465                })
466                .count();
467            (len, first_child, last_child)
468        } else {
469            (
470                children.len(),
471                children.first().copied(),
472                children.last().copied(),
473            )
474        };
475
476        let needs_initial_phase = parent_node.cross_alignment.is_not_start()
477            || parent_node.main_alignment.is_not_start()
478            || parent_node.content.is_fit()
479            || parent_node.content.is_flex();
480
481        let mut initial_phase_area = *area;
482        let mut initial_phase_inner_area = *inner_area;
483        let mut initial_phase_available_area = *available_area;
484
485        // Initial phase: Measure the size and position of the children if the parent has a
486        // non-start cross alignment, non-start main aligment of a fit-content.
487        if needs_initial_phase {
488            //  Measure the children
489            for child_id in &children {
490                let Some(child_data) = self.dom_adapter.get_node(child_id) else {
491                    continue;
492                };
493
494                // No need to consider this Node for a two-phasing
495                // measurements as it will float on its own.
496                if !child_data.position.is_stacked() {
497                    continue;
498                }
499
500                let is_last_child = last_child == Some(*child_id);
501
502                let inner_area = initial_phase_inner_area;
503
504                let (_, mut child_areas) = self.measure_node(
505                    *child_id,
506                    &child_data,
507                    &inner_area,
508                    &initial_phase_available_area,
509                    false,
510                    parent_is_dirty,
511                    Phase::Initial,
512                );
513
514                child_areas.area.adjust_size(&child_data);
515
516                // Stack this child into the parent
517                Self::stack_child(
518                    &mut initial_phase_available_area,
519                    parent_node,
520                    &child_data,
521                    &mut initial_phase_area,
522                    &mut initial_phase_inner_area,
523                    &mut initial_phase_inner_sizes,
524                    &child_areas.area,
525                    is_last_child,
526                    Phase::Initial,
527                );
528
529                if parent_node.cross_alignment.is_not_start()
530                    || parent_node.main_alignment.is_spaced()
531                {
532                    initial_phase_sizes.insert(*child_id, child_areas.area.size);
533                }
534
535                if parent_node.content.is_flex() {
536                    match parent_node.direction {
537                        Direction::Vertical => {
538                            if let Some(ff) = child_data.height.flex_grow() {
539                                initial_phase_flex_grows.insert(*child_id, ff);
540                            }
541                        }
542                        Direction::Horizontal => {
543                            if let Some(ff) = child_data.width.flex_grow() {
544                                initial_phase_flex_grows.insert(*child_id, ff);
545                            }
546                        }
547                    }
548                }
549            }
550        }
551
552        let initial_available_area = *available_area;
553
554        let flex_grows = initial_phase_flex_grows
555            .values()
556            .copied()
557            .reduce(|acc, v| acc + v)
558            .unwrap_or_default()
559            .max(Length::new(1.0));
560
561        let flex_axis = AlignAxis::new(&parent_node.direction, AlignmentDirection::Main);
562
563        let flex_available_width = initial_available_area.width() - initial_phase_inner_sizes.width;
564        let flex_available_height =
565            initial_available_area.height() - initial_phase_inner_sizes.height;
566
567        let initial_phase_inner_sizes_with_flex =
568            initial_phase_flex_grows
569                .values()
570                .fold(initial_phase_inner_sizes, |mut acc, f| {
571                    let flex_grow_per = f.get() / flex_grows.get() * 100.;
572
573                    match flex_axis {
574                        AlignAxis::Height => {
575                            let size = flex_available_height / 100. * flex_grow_per;
576                            acc.height += size;
577                        }
578                        AlignAxis::Width => {
579                            let size = flex_available_width / 100. * flex_grow_per;
580                            acc.width += size;
581                        }
582                    }
583
584                    acc
585                });
586
587        if needs_initial_phase {
588            if parent_node.main_alignment.is_not_start() {
589                // Adjust the available and inner areas of the Main axis
590                Self::shrink_area_to_fit_when_unbounded(
591                    available_area,
592                    &initial_phase_area,
593                    &mut initial_phase_inner_area,
594                    parent_node,
595                    AlignmentDirection::Main,
596                );
597
598                // Align the Main axis
599                Self::align_content(
600                    available_area,
601                    &initial_phase_inner_area,
602                    initial_phase_inner_sizes_with_flex,
603                    &parent_node.main_alignment,
604                    parent_node.direction,
605                    AlignmentDirection::Main,
606                );
607            }
608
609            if parent_node.cross_alignment.is_not_start() || parent_node.content.is_fit() {
610                // Adjust the available and inner areas of the Cross axis
611                Self::shrink_area_to_fit_when_unbounded(
612                    available_area,
613                    &initial_phase_area,
614                    &mut initial_phase_inner_area,
615                    parent_node,
616                    AlignmentDirection::Cross,
617                );
618            }
619        }
620
621        let initial_available_area = *available_area;
622
623        // Final phase: measure the children with all the axis and sizes adjusted
624        for child_id in children {
625            let Some(child_data) = self.dom_adapter.get_node(&child_id) else {
626                continue;
627            };
628
629            let is_first_child = first_child == Some(child_id);
630            let is_last_child = last_child == Some(child_id);
631
632            let mut adapted_available_area = *available_area;
633
634            if parent_node.content.is_flex() {
635                let flex_grow = initial_phase_flex_grows.get(&child_id);
636
637                if let Some(flex_grow) = flex_grow {
638                    let flex_grow_per = flex_grow.get() / flex_grows.get() * 100.;
639
640                    match flex_axis {
641                        AlignAxis::Height => {
642                            let size = flex_available_height / 100. * flex_grow_per;
643                            adapted_available_area.size.height = size;
644                        }
645                        AlignAxis::Width => {
646                            let size = flex_available_width / 100. * flex_grow_per;
647                            adapted_available_area.size.width = size;
648                        }
649                    }
650                }
651            }
652
653            // Only the stacked children will be aligned
654            if parent_node.main_alignment.is_spaced() && child_data.position.is_stacked() {
655                // Align the Main axis if necessary
656                Self::align_position(
657                    AlignmentDirection::Main,
658                    &mut adapted_available_area,
659                    &initial_available_area,
660                    initial_phase_inner_sizes_with_flex,
661                    &parent_node.main_alignment,
662                    parent_node.direction,
663                    non_absolute_children_len,
664                    is_first_child,
665                );
666            }
667
668            if parent_node.cross_alignment.is_not_start() {
669                let initial_phase_size = initial_phase_sizes.get(&child_id);
670
671                if let Some(initial_phase_size) = initial_phase_size {
672                    // Align the Cross axis if necessary
673                    Self::align_content(
674                        &mut adapted_available_area,
675                        available_area,
676                        *initial_phase_size,
677                        &parent_node.cross_alignment,
678                        parent_node.direction,
679                        AlignmentDirection::Cross,
680                    );
681                }
682            }
683
684            // Final measurement
685            let (child_revalidated, mut child_areas) = self.measure_node(
686                child_id,
687                &child_data,
688                inner_area,
689                &adapted_available_area,
690                must_cache_children,
691                parent_is_dirty,
692                Phase::Final,
693            );
694
695            // Adjust the size of the area if needed
696            child_areas.area.adjust_size(&child_data);
697
698            // Stack this child into the parent
699            if child_data.position.is_stacked() {
700                Self::stack_child(
701                    available_area,
702                    parent_node,
703                    &child_data,
704                    area,
705                    inner_area,
706                    inner_sizes,
707                    &child_areas.area,
708                    is_last_child,
709                    Phase::Final,
710                );
711            }
712
713            // Cache the child layout if it was mutated and children must be cached
714            if child_revalidated && must_cache_children {
715                // Finally cache this node areas into Torin
716                self.layout.cache_node(child_id, child_areas);
717            }
718        }
719    }
720
721    /// Align the content of this node.
722    fn align_content(
723        available_area: &mut Area,
724        inner_area: &Area,
725        contents_size: Size2D,
726        alignment: &Alignment,
727        direction: Direction,
728        alignment_direction: AlignmentDirection,
729    ) {
730        let axis = AlignAxis::new(&direction, alignment_direction);
731
732        match axis {
733            AlignAxis::Height => match alignment {
734                Alignment::Center => {
735                    let new_origin_y = (inner_area.height() / 2.0) - (contents_size.height / 2.0);
736                    available_area.origin.y = inner_area.min_y() + new_origin_y;
737                }
738                Alignment::End => {
739                    available_area.origin.y = inner_area.max_y() - contents_size.height;
740                }
741                _ => {}
742            },
743            AlignAxis::Width => match alignment {
744                Alignment::Center => {
745                    let new_origin_x = (inner_area.width() / 2.0) - (contents_size.width / 2.0);
746                    available_area.origin.x = inner_area.min_x() + new_origin_x;
747                }
748                Alignment::End => {
749                    available_area.origin.x = inner_area.max_x() - contents_size.width;
750                }
751                _ => {}
752            },
753        }
754    }
755
756    /// Align the position of this node.
757    #[allow(clippy::too_many_arguments)]
758    fn align_position(
759        alignment_direction: AlignmentDirection,
760        available_area: &mut Area,
761        initial_available_area: &Area,
762        inner_sizes: Size2D,
763        alignment: &Alignment,
764        direction: Direction,
765        siblings_len: usize,
766        is_first_sibling: bool,
767    ) {
768        let axis = AlignAxis::new(&direction, alignment_direction);
769
770        match axis {
771            AlignAxis::Height => match alignment {
772                Alignment::SpaceBetween if !is_first_sibling => {
773                    let all_gaps_sizes = initial_available_area.height() - inner_sizes.height;
774                    let gap_size = all_gaps_sizes / (siblings_len - 1) as f32;
775                    available_area.origin.y += gap_size;
776                }
777                Alignment::SpaceEvenly => {
778                    let all_gaps_sizes = initial_available_area.height() - inner_sizes.height;
779                    let gap_size = all_gaps_sizes / (siblings_len + 1) as f32;
780                    available_area.origin.y += gap_size;
781                }
782                Alignment::SpaceAround => {
783                    let all_gaps_sizes = initial_available_area.height() - inner_sizes.height;
784                    let one_gap_size = all_gaps_sizes / siblings_len as f32;
785                    let gap_size = if is_first_sibling {
786                        one_gap_size / 2.
787                    } else {
788                        one_gap_size
789                    };
790                    available_area.origin.y += gap_size;
791                }
792                _ => {}
793            },
794            AlignAxis::Width => match alignment {
795                Alignment::SpaceBetween if !is_first_sibling => {
796                    let all_gaps_sizes = initial_available_area.width() - inner_sizes.width;
797                    let gap_size = all_gaps_sizes / (siblings_len - 1) as f32;
798                    available_area.origin.x += gap_size;
799                }
800                Alignment::SpaceEvenly => {
801                    let all_gaps_sizes = initial_available_area.width() - inner_sizes.width;
802                    let gap_size = all_gaps_sizes / (siblings_len + 1) as f32;
803                    available_area.origin.x += gap_size;
804                }
805                Alignment::SpaceAround => {
806                    let all_gaps_sizes = initial_available_area.width() - inner_sizes.width;
807                    let one_gap_size = all_gaps_sizes / siblings_len as f32;
808                    let gap_size = if is_first_sibling {
809                        one_gap_size / 2.
810                    } else {
811                        one_gap_size
812                    };
813                    available_area.origin.x += gap_size;
814                }
815                _ => {}
816            },
817        }
818    }
819
820    /// Stack a child Node into its parent
821    #[allow(clippy::too_many_arguments)]
822    fn stack_child(
823        available_area: &mut Area,
824        parent_node: &Node,
825        child_node: &Node,
826        parent_area: &mut Area,
827        inner_area: &mut Area,
828        inner_sizes: &mut Size2D,
829        child_area: &Area,
830        is_last_sibiling: bool,
831        phase: Phase,
832    ) {
833        // Only apply the spacing to elements after `i > 0` and `i < len - 1`
834        let spacing = if is_last_sibiling {
835            Length::default()
836        } else {
837            parent_node.spacing
838        };
839
840        match parent_node.direction {
841            Direction::Horizontal => {
842                // Move the available area
843                available_area.origin.x = child_area.max_x() + spacing.get();
844                available_area.size.width -= child_area.size.width + spacing.get();
845
846                inner_sizes.height = child_area.height().max(inner_sizes.height);
847                inner_sizes.width += spacing.get();
848                if !child_node.width.is_flex() || phase == Phase::Final {
849                    inner_sizes.width += child_area.width();
850                }
851
852                // Keep the biggest height
853                if parent_node.height.inner_sized() {
854                    parent_area.size.height = parent_area.size.height.max(
855                        child_area.size.height
856                            + parent_node.padding.vertical()
857                            + parent_node.margin.vertical(),
858                    );
859                    // Keep the inner area in sync
860                    inner_area.size.height = parent_area.size.height
861                        - parent_node.padding.vertical()
862                        - parent_node.margin.vertical();
863                }
864
865                // Accumulate width
866                if parent_node.width.inner_sized() {
867                    parent_area.size.width += child_area.size.width + spacing.get();
868                }
869            }
870            Direction::Vertical => {
871                // Move the available area
872                available_area.origin.y = child_area.max_y() + spacing.get();
873                available_area.size.height -= child_area.size.height + spacing.get();
874
875                inner_sizes.width = child_area.width().max(inner_sizes.width);
876                inner_sizes.height += spacing.get();
877                if !child_node.height.is_flex() || phase == Phase::Final {
878                    inner_sizes.height += child_area.height();
879                }
880
881                // Keep the biggest width
882                if parent_node.width.inner_sized() {
883                    parent_area.size.width = parent_area.size.width.max(
884                        child_area.size.width
885                            + parent_node.padding.horizontal()
886                            + parent_node.margin.horizontal(),
887                    );
888                    // Keep the inner area in sync
889                    inner_area.size.width = parent_area.size.width
890                        - parent_node.padding.horizontal()
891                        - parent_node.margin.horizontal();
892                }
893
894                // Accumulate height
895                if parent_node.height.inner_sized() {
896                    parent_area.size.height += child_area.size.height + spacing.get();
897                }
898            }
899        }
900    }
901
902    /// Shrink the available area and inner area of a parent node when for example height is set to "auto",
903    /// direction is vertical and main_alignment is set to "center" or "end" or the content is set to "fit".
904    /// The intended usage is to call this after the first measurement and before the second,
905    /// this way the second measurement will align the content relatively to the parent element instead
906    /// of overflowing due to being aligned relatively to the upper parent element
907    fn shrink_area_to_fit_when_unbounded(
908        available_area: &mut Area,
909        parent_area: &Area,
910        inner_area: &mut Area,
911        parent_node: &Node,
912        alignment_direction: AlignmentDirection,
913    ) {
914        struct NodeData<'a> {
915            pub inner_origin: &'a mut f32,
916            pub inner_size: &'a mut f32,
917            pub area_origin: f32,
918            pub area_size: f32,
919            pub one_side_padding: f32,
920            pub two_sides_padding: f32,
921            pub one_side_margin: f32,
922            pub two_sides_margin: f32,
923            pub available_size: &'a mut f32,
924        }
925
926        let axis = AlignAxis::new(&parent_node.direction, alignment_direction);
927        let (is_vertical_not_start, is_horizontal_not_start) = match parent_node.direction {
928            Direction::Vertical => (
929                parent_node.main_alignment.is_not_start(),
930                parent_node.cross_alignment.is_not_start() || parent_node.content.is_fit(),
931            ),
932            Direction::Horizontal => (
933                parent_node.cross_alignment.is_not_start() || parent_node.content.is_fit(),
934                parent_node.main_alignment.is_not_start(),
935            ),
936        };
937        let NodeData {
938            inner_origin,
939            inner_size,
940            area_origin,
941            area_size,
942            one_side_padding,
943            two_sides_padding,
944            one_side_margin,
945            two_sides_margin,
946            available_size,
947        } = match axis {
948            AlignAxis::Height if parent_node.height.inner_sized() && is_vertical_not_start => {
949                NodeData {
950                    inner_origin: &mut inner_area.origin.y,
951                    inner_size: &mut inner_area.size.height,
952                    area_origin: parent_area.origin.y,
953                    area_size: parent_area.size.height,
954                    one_side_padding: parent_node.padding.top(),
955                    two_sides_padding: parent_node.padding.vertical(),
956                    one_side_margin: parent_node.margin.top(),
957                    two_sides_margin: parent_node.margin.vertical(),
958                    available_size: &mut available_area.size.height,
959                }
960            }
961            AlignAxis::Width if parent_node.width.inner_sized() && is_horizontal_not_start => {
962                NodeData {
963                    inner_origin: &mut inner_area.origin.x,
964                    inner_size: &mut inner_area.size.width,
965                    area_origin: parent_area.origin.x,
966                    area_size: parent_area.size.width,
967                    one_side_padding: parent_node.padding.left(),
968                    two_sides_padding: parent_node.padding.horizontal(),
969                    one_side_margin: parent_node.margin.left(),
970                    two_sides_margin: parent_node.margin.horizontal(),
971                    available_size: &mut available_area.size.width,
972                }
973            }
974            _ => return,
975        };
976
977        // Set the origin of the inner area to the origin of the area plus the padding and margin for the given axis
978        *inner_origin = area_origin + one_side_padding + one_side_margin;
979        // Set the size of the inner area to the size of the area minus the padding and margin for the given axis
980        *inner_size = area_size - two_sides_padding - two_sides_margin;
981        // Set the same available size as the inner area for the given axis
982        *available_size = *inner_size;
983    }
984}