torin/
measure.rs

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