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