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