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                // Recompute the origin for non-stacked inner-sized elements
368                // now that the final size is known, and translate cached children accordingly.
369                if !node.position.is_stacked()
370                    && (node.width.inner_sized() || node.height.inner_sized())
371                {
372                    let new_origin = node.position.get_origin(
373                        &available_parent_area,
374                        &initial_parent_area,
375                        area.size,
376                        &self.layout_metadata.root_area,
377                    );
378                    let diff_x = new_origin.x - area.origin.x;
379                    let diff_y = new_origin.y - area.origin.y;
380                    area.origin = new_origin;
381                    inner_area.origin.x += diff_x;
382                    inner_area.origin.y += diff_y;
383
384                    if diff_x != 0.0 || diff_y != 0.0 {
385                        self.recursive_translate(node_id, Length::new(diff_x), Length::new(diff_y));
386                    }
387                }
388            }
389
390            let layout_node = LayoutNode {
391                area,
392                margin: node.margin,
393                offset_x: node.offset_x,
394                offset_y: node.offset_y,
395                inner_area,
396                data: node_data,
397                inner_sizes,
398            };
399
400            // In case of any layout listener, notify it with the new areas.
401            if must_cache_children
402                && phase == Phase::Final
403                && node.has_layout_references
404                && let Some(measurer) = self.measurer
405            {
406                inner_sizes.width += node.padding.horizontal();
407                inner_sizes.height += node.padding.vertical();
408                measurer.notify_layout_references(
409                    node_id,
410                    layout_node.area,
411                    layout_node.visible_area(),
412                    inner_sizes,
413                );
414            }
415
416            (must_cache_children, layout_node)
417        } else {
418            let layout_node = self
419                .layout
420                .get(&node_id)
421                .expect("Cached node does not exist")
422                .clone();
423
424            let mut inner_sizes = Size2D::default();
425            let mut area = layout_node.area.as_parent();
426            let mut inner_area = layout_node.inner_area.as_inner();
427            let mut available_area = inner_area.as_available();
428
429            let measure_inner_children = if let Some(measurer) = self.measurer {
430                measurer.should_measure_inner_children(node_id)
431            } else {
432                true
433            };
434
435            if measure_inner_children {
436                self.measure_children(
437                    &node_id,
438                    node,
439                    &mut area,
440                    &mut inner_area,
441                    &mut available_area,
442                    &mut inner_sizes,
443                    must_cache_children,
444                    false,
445                );
446            }
447
448            (false, layout_node)
449        }
450    }
451
452    /// Measure the children layouts of a Node.
453    #[allow(clippy::too_many_arguments)]
454    pub fn measure_children(
455        &mut self,
456        parent_node_id: &Key,
457        parent_node: &Node,
458        parent_area: &mut AreaOf<Parent>,
459        inner_area: &mut AreaOf<Inner>,
460        available_area: &mut AreaOf<Available>,
461        // Accumulated sizes in both axis in the Node
462        inner_sizes: &mut Size2D,
463        // Whether to cache the measurements of this Node's children
464        must_cache_children: bool,
465        // Parent Node is dirty.
466        parent_is_dirty: bool,
467    ) {
468        let children = self.tree_adapter.children_of(parent_node_id);
469
470        let initial_area = *inner_area;
471
472        let mut initial_phase_flex_grows = FxHashMap::default();
473        let mut initial_phase_sizes = FxHashMap::default();
474        let mut initial_phase_inner_sizes = Size2D::default();
475
476        // Used to calculate the spacing and some alignments
477        let (non_absolute_children_len, first_child, last_child) = if parent_node.spacing.get() > 0.
478        {
479            let mut last_child = None;
480            let mut first_child = None;
481            let len = children
482                .iter()
483                .filter(|child_id| {
484                    let Some(child_data) = self.tree_adapter.get_node(child_id) else {
485                        return false;
486                    };
487                    let is_stacked = child_data.position.is_stacked();
488                    if is_stacked {
489                        last_child = Some(**child_id);
490
491                        if first_child.is_none() {
492                            first_child = Some(**child_id);
493                        }
494                    }
495                    is_stacked
496                })
497                .count();
498            (len, first_child, last_child)
499        } else {
500            (
501                children.len(),
502                children.first().copied(),
503                children.last().copied(),
504            )
505        };
506
507        let needs_initial_phase = parent_node.cross_alignment.is_not_start()
508            || parent_node.main_alignment.is_not_start()
509            || parent_node.content.is_fit()
510            || parent_node.content.is_flex()
511            || parent_node.content.is_wrap();
512
513        let mut initial_phase_parent_area = *parent_area;
514        let mut initial_phase_inner_area = *inner_area;
515        let mut initial_phase_available_area = *available_area;
516
517        // Initial phase: Measure the size and position of the children if the parent has a
518        // non-start cross alignment, non-start main alignment of a fit-content.
519        if needs_initial_phase {
520            //  Measure the children
521            for child_id in &children {
522                let Some(child_data) = self.tree_adapter.get_node(child_id) else {
523                    continue;
524                };
525
526                // No need to consider this Node for a two-phasing
527                // measurements as it will float on its own.
528                if !child_data.position.is_stacked() {
529                    continue;
530                }
531
532                let is_last_child = last_child == Some(*child_id);
533
534                let (_, mut child_areas) = self.measure_node(
535                    *child_id,
536                    &child_data,
537                    initial_area.as_parent(),
538                    initial_phase_available_area,
539                    false,
540                    parent_is_dirty,
541                    Phase::Initial,
542                );
543
544                child_areas.area.adjust_size(&child_data);
545
546                // Stack this child into the parent
547                Self::stack_child(
548                    &mut initial_phase_available_area,
549                    parent_node,
550                    &child_data,
551                    &mut initial_phase_parent_area,
552                    &mut initial_phase_inner_area,
553                    &mut initial_phase_inner_sizes,
554                    &child_areas.area,
555                    is_last_child,
556                    Phase::Initial,
557                );
558
559                if parent_node.cross_alignment.is_not_start()
560                    || parent_node.main_alignment.is_spaced()
561                    || parent_node.content.is_wrap()
562                {
563                    initial_phase_sizes.insert(*child_id, child_areas.area.size);
564                }
565
566                if parent_node.content.is_flex() {
567                    match parent_node.direction {
568                        Direction::Vertical => {
569                            if let Some(ff) = child_data.height.flex_grow() {
570                                initial_phase_flex_grows.insert(*child_id, ff);
571                            }
572                        }
573                        Direction::Horizontal => {
574                            if let Some(ff) = child_data.width.flex_grow() {
575                                initial_phase_flex_grows.insert(*child_id, ff);
576                            }
577                        }
578                    }
579                }
580            }
581        }
582
583        let flex_grows = initial_phase_flex_grows
584            .values()
585            .copied()
586            .reduce(|acc, v| acc + v)
587            .unwrap_or_default()
588            .max(Length::new(1.0));
589
590        let flex_axis = AlignAxis::new(&parent_node.direction, AlignmentDirection::Main);
591
592        let flex_available_width = available_area.width() - initial_phase_inner_sizes.width;
593        let flex_available_height = available_area.height() - initial_phase_inner_sizes.height;
594
595        if parent_node.content.is_flex() {
596            initial_phase_inner_sizes =
597                initial_phase_flex_grows
598                    .values()
599                    .fold(initial_phase_inner_sizes, |mut acc, f| {
600                        let flex_grow_per = f.get() / flex_grows.get() * 100.;
601
602                        match flex_axis {
603                            AlignAxis::Height => {
604                                let size = flex_available_height / 100. * flex_grow_per;
605                                acc.height += size;
606                            }
607                            AlignAxis::Width => {
608                                let size = flex_available_width / 100. * flex_grow_per;
609                                acc.width += size;
610                            }
611                        }
612
613                        acc
614                    });
615        }
616
617        if needs_initial_phase {
618            if parent_node.main_alignment.is_not_start() && parent_node.content.allows_alignments()
619            {
620                // Adjust the available and inner areas of the Main axis
621                Self::shrink_area_to_fit_when_unbounded(
622                    available_area,
623                    &initial_phase_parent_area,
624                    &mut initial_phase_inner_area,
625                    parent_node,
626                    AlignmentDirection::Main,
627                );
628
629                // Align the Main axis
630                Self::align_content(
631                    available_area,
632                    &initial_phase_inner_area,
633                    initial_phase_inner_sizes,
634                    &parent_node.main_alignment,
635                    parent_node.direction,
636                    AlignmentDirection::Main,
637                );
638            }
639
640            if (parent_node.cross_alignment.is_not_start() || parent_node.content.is_fit())
641                && parent_node.content.allows_alignments()
642            {
643                // Adjust the available and inner areas of the Cross axis
644                Self::shrink_area_to_fit_when_unbounded(
645                    available_area,
646                    &initial_phase_parent_area,
647                    &mut initial_phase_inner_area,
648                    parent_node,
649                    AlignmentDirection::Cross,
650                );
651            }
652        }
653
654        let initial_available_area = *available_area;
655
656        // Final phase: measure the children with all the axis and sizes adjusted
657        for child_id in children {
658            let Some(child_data) = self.tree_adapter.get_node(&child_id) else {
659                continue;
660            };
661
662            let is_first_child = first_child == Some(child_id);
663            let is_last_child = last_child == Some(child_id);
664
665            let mut adapted_available_area = *available_area;
666
667            if parent_node.content.is_flex() {
668                let flex_grow = initial_phase_flex_grows.get(&child_id);
669
670                if let Some(flex_grow) = flex_grow {
671                    let flex_grow_per = flex_grow.get() / flex_grows.get() * 100.;
672
673                    match flex_axis {
674                        AlignAxis::Height => {
675                            let size = flex_available_height / 100. * flex_grow_per;
676                            adapted_available_area.size.height = size;
677                        }
678                        AlignAxis::Width => {
679                            let size = flex_available_width / 100. * flex_grow_per;
680                            adapted_available_area.size.width = size;
681                        }
682                    }
683                }
684            }
685
686            // Only the stacked children will be aligned
687            if parent_node.main_alignment.is_spaced()
688                && child_data.position.is_stacked()
689                && parent_node.content.allows_alignments()
690            {
691                // Align the Main axis if necessary
692                Self::align_position(
693                    AlignmentDirection::Main,
694                    &mut adapted_available_area,
695                    &initial_available_area,
696                    initial_phase_inner_sizes,
697                    &parent_node.main_alignment,
698                    parent_node.direction,
699                    non_absolute_children_len,
700                    is_first_child,
701                );
702            }
703
704            if parent_node.cross_alignment.is_not_start() && parent_node.content.allows_alignments()
705            {
706                let initial_phase_size = initial_phase_sizes.get(&child_id);
707
708                if let Some(initial_phase_size) = initial_phase_size {
709                    // Align the Cross axis if necessary
710                    Self::align_content(
711                        &mut adapted_available_area,
712                        &available_area.as_inner(),
713                        *initial_phase_size,
714                        &parent_node.cross_alignment,
715                        parent_node.direction,
716                        AlignmentDirection::Cross,
717                    );
718                }
719            }
720
721            if let Content::Wrap { wrap_spacing } = parent_node.content {
722                let initial_phase_size = initial_phase_sizes.get(&child_id);
723                Self::wrap_child(
724                    wrap_spacing.unwrap_or_default(),
725                    parent_node.direction,
726                    initial_phase_size,
727                    &initial_available_area,
728                    parent_area,
729                    available_area,
730                    &mut adapted_available_area,
731                    *inner_sizes,
732                );
733            }
734
735            // Final measurement
736            let (child_revalidated, mut child_areas) = self.measure_node(
737                child_id,
738                &child_data,
739                initial_area.as_parent(),
740                adapted_available_area,
741                must_cache_children,
742                parent_is_dirty,
743                Phase::Final,
744            );
745
746            // Adjust the size of the area if needed
747            child_areas.area.adjust_size(&child_data);
748
749            // Stack this child into the parent
750            if child_data.position.is_stacked() {
751                Self::stack_child(
752                    available_area,
753                    parent_node,
754                    &child_data,
755                    parent_area,
756                    inner_area,
757                    inner_sizes,
758                    &child_areas.area,
759                    is_last_child,
760                    Phase::Final,
761                );
762            }
763
764            // Cache the child layout if it was mutated and children must be cached
765            if child_revalidated && must_cache_children {
766                // Finally cache this node areas into Torin
767                self.layout.cache_node(child_id, child_areas);
768            }
769        }
770    }
771
772    #[allow(clippy::too_many_arguments)]
773    fn wrap_child(
774        wrap_spacing: f32,
775        direction: Direction,
776        initial_phase_size: Option<&Size2D>,
777        initial_available_area: &AreaOf<Available>,
778        parent_area: &mut AreaOf<Parent>,
779        available_area: &mut AreaOf<Available>,
780        adapted_available_area: &mut AreaOf<Available>,
781        inner_sizes: Size2D,
782    ) {
783        if let Some(initial_phase_size) = initial_phase_size {
784            match direction {
785                Direction::Vertical => {
786                    if adapted_available_area.height() - initial_phase_size.height < 0. {
787                        let advance = inner_sizes.width + wrap_spacing;
788                        available_area.origin.y = initial_available_area.origin.y;
789                        available_area.size.height = initial_available_area.size.height;
790                        available_area.origin.x += advance;
791                        adapted_available_area.origin.y = initial_available_area.origin.y;
792                        adapted_available_area.size.height = initial_available_area.size.height;
793                        adapted_available_area.origin.x += advance;
794                        parent_area.size.width += advance;
795                    }
796                }
797                Direction::Horizontal => {
798                    if adapted_available_area.width() - initial_phase_size.width < 0. {
799                        let advance = inner_sizes.height + wrap_spacing;
800                        available_area.origin.x = initial_available_area.origin.x;
801                        available_area.size.width = initial_available_area.size.width;
802                        available_area.origin.y += advance;
803                        adapted_available_area.origin.x = initial_available_area.origin.x;
804                        adapted_available_area.size.width = initial_available_area.size.width;
805                        adapted_available_area.origin.y += advance;
806                        parent_area.size.height += advance;
807                    }
808                }
809            }
810        }
811    }
812
813    /// Align the content of this node.
814    fn align_content(
815        available_area: &mut AreaOf<Available>,
816        inner_area: &AreaOf<Inner>,
817        contents_size: Size2D,
818        alignment: &Alignment,
819        direction: Direction,
820        alignment_direction: AlignmentDirection,
821    ) {
822        let axis = AlignAxis::new(&direction, alignment_direction);
823
824        match axis {
825            AlignAxis::Height => match alignment {
826                Alignment::Center => {
827                    let new_origin_y = (inner_area.height() / 2.0) - (contents_size.height / 2.0);
828                    available_area.origin.y = inner_area.min_y() + new_origin_y;
829                }
830                Alignment::End => {
831                    available_area.origin.y = inner_area.max_y() - contents_size.height;
832                }
833                _ => {}
834            },
835            AlignAxis::Width => match alignment {
836                Alignment::Center => {
837                    let new_origin_x = (inner_area.width() / 2.0) - (contents_size.width / 2.0);
838                    available_area.origin.x = inner_area.min_x() + new_origin_x;
839                }
840                Alignment::End => {
841                    available_area.origin.x = inner_area.max_x() - contents_size.width;
842                }
843                _ => {}
844            },
845        }
846    }
847
848    /// Align the position of this node.
849    #[allow(clippy::too_many_arguments)]
850    fn align_position(
851        alignment_direction: AlignmentDirection,
852        available_area: &mut AreaOf<Available>,
853        initial_available_area: &AreaOf<Available>,
854        inner_sizes: Size2D,
855        alignment: &Alignment,
856        direction: Direction,
857        siblings_len: usize,
858        is_first_sibling: bool,
859    ) {
860        let axis = AlignAxis::new(&direction, alignment_direction);
861
862        match axis {
863            AlignAxis::Height => match alignment {
864                Alignment::SpaceBetween if !is_first_sibling => {
865                    let all_gaps_sizes = initial_available_area.height() - inner_sizes.height;
866                    let gap_size = all_gaps_sizes / (siblings_len - 1) as f32;
867                    available_area.origin.y += gap_size;
868                }
869                Alignment::SpaceEvenly => {
870                    let all_gaps_sizes = initial_available_area.height() - inner_sizes.height;
871                    let gap_size = all_gaps_sizes / (siblings_len + 1) as f32;
872                    available_area.origin.y += gap_size;
873                }
874                Alignment::SpaceAround => {
875                    let all_gaps_sizes = initial_available_area.height() - inner_sizes.height;
876                    let one_gap_size = all_gaps_sizes / siblings_len as f32;
877                    let gap_size = if is_first_sibling {
878                        one_gap_size / 2.
879                    } else {
880                        one_gap_size
881                    };
882                    available_area.origin.y += gap_size;
883                }
884                _ => {}
885            },
886            AlignAxis::Width => match alignment {
887                Alignment::SpaceBetween if !is_first_sibling => {
888                    let all_gaps_sizes = initial_available_area.width() - inner_sizes.width;
889                    let gap_size = all_gaps_sizes / (siblings_len - 1) as f32;
890                    available_area.origin.x += gap_size;
891                }
892                Alignment::SpaceEvenly => {
893                    let all_gaps_sizes = initial_available_area.width() - inner_sizes.width;
894                    let gap_size = all_gaps_sizes / (siblings_len + 1) as f32;
895                    available_area.origin.x += gap_size;
896                }
897                Alignment::SpaceAround => {
898                    let all_gaps_sizes = initial_available_area.width() - inner_sizes.width;
899                    let one_gap_size = all_gaps_sizes / siblings_len as f32;
900                    let gap_size = if is_first_sibling {
901                        one_gap_size / 2.
902                    } else {
903                        one_gap_size
904                    };
905                    available_area.origin.x += gap_size;
906                }
907                _ => {}
908            },
909        }
910    }
911
912    /// Stack a child Node into its parent
913    #[allow(clippy::too_many_arguments)]
914    fn stack_child(
915        available_area: &mut AreaOf<Available>,
916        parent_node: &Node,
917        child_node: &Node,
918        parent_area: &mut AreaOf<Parent>,
919        inner_area: &mut AreaOf<Inner>,
920        inner_sizes: &mut Size2D,
921        child_area: &Area,
922        is_last_sibilin: bool,
923        phase: Phase,
924    ) {
925        // Only apply the spacing to elements after `i > 0` and `i < len - 1`
926        let spacing = if is_last_sibilin {
927            Length::default()
928        } else {
929            parent_node.spacing
930        };
931
932        match parent_node.direction {
933            Direction::Horizontal => {
934                // Move the available area
935                available_area.origin.x = child_area.max_x() + spacing.get();
936                available_area.size.width -= child_area.size.width + spacing.get();
937
938                inner_sizes.height = child_area.height().max(inner_sizes.height);
939                inner_sizes.width += spacing.get();
940                if !child_node.width.is_flex() || phase == Phase::Final {
941                    inner_sizes.width += child_area.width();
942                }
943
944                // Keep the biggest height
945                if parent_node.height.inner_sized() {
946                    parent_area.size.height = parent_area.size.height.max(
947                        child_area.size.height
948                            + parent_node.padding.vertical()
949                            + parent_node.margin.vertical(),
950                    );
951                    // Keep the inner area in sync
952                    inner_area.size.height = parent_area.size.height
953                        - parent_node.padding.vertical()
954                        - parent_node.margin.vertical();
955                }
956
957                // Accumulate width
958                if parent_node.width.inner_sized() {
959                    parent_area.size.width += child_area.size.width + spacing.get();
960                }
961            }
962            Direction::Vertical => {
963                // Move the available area
964                available_area.origin.y = child_area.max_y() + spacing.get();
965                available_area.size.height -= child_area.size.height + spacing.get();
966
967                inner_sizes.width = child_area.width().max(inner_sizes.width);
968                inner_sizes.height += spacing.get();
969                if !child_node.height.is_flex() || phase == Phase::Final {
970                    inner_sizes.height += child_area.height();
971                }
972
973                // Keep the biggest width
974                if parent_node.width.inner_sized() {
975                    parent_area.size.width = parent_area.size.width.max(
976                        child_area.size.width
977                            + parent_node.padding.horizontal()
978                            + parent_node.margin.horizontal(),
979                    );
980                    // Keep the inner area in sync
981                    inner_area.size.width = parent_area.size.width
982                        - parent_node.padding.horizontal()
983                        - parent_node.margin.horizontal();
984                }
985
986                // Accumulate height
987                if parent_node.height.inner_sized() {
988                    parent_area.size.height += child_area.size.height + spacing.get();
989                }
990            }
991        }
992    }
993
994    /// Shrink the available area and inner area of a parent node when for example height is set to "auto",
995    /// direction is vertical and main_alignment is set to "center" or "end" or the content is set to "fit".
996    /// The intended usage is to call this after the first measurement and before the second,
997    /// this way the second measurement will align the content relatively to the parent element instead
998    /// of overflowing due to being aligned relatively to the upper parent element
999    fn shrink_area_to_fit_when_unbounded(
1000        available_area: &mut AreaOf<Available>,
1001        parent_area: &AreaOf<Parent>,
1002        inner_area: &mut AreaOf<Inner>,
1003        parent_node: &Node,
1004        alignment_direction: AlignmentDirection,
1005    ) {
1006        struct NodeData<'a> {
1007            pub inner_origin: &'a mut f32,
1008            pub inner_size: &'a mut f32,
1009            pub area_origin: f32,
1010            pub area_size: f32,
1011            pub one_side_padding: f32,
1012            pub two_sides_padding: f32,
1013            pub one_side_margin: f32,
1014            pub two_sides_margin: f32,
1015            pub available_size: &'a mut f32,
1016        }
1017
1018        let axis = AlignAxis::new(&parent_node.direction, alignment_direction);
1019        let (is_vertical_not_start, is_horizontal_not_start) = match parent_node.direction {
1020            Direction::Vertical => (
1021                parent_node.main_alignment.is_not_start(),
1022                parent_node.cross_alignment.is_not_start() || parent_node.content.is_fit(),
1023            ),
1024            Direction::Horizontal => (
1025                parent_node.cross_alignment.is_not_start() || parent_node.content.is_fit(),
1026                parent_node.main_alignment.is_not_start(),
1027            ),
1028        };
1029        let NodeData {
1030            inner_origin,
1031            inner_size,
1032            area_origin,
1033            area_size,
1034            one_side_padding,
1035            two_sides_padding,
1036            one_side_margin,
1037            two_sides_margin,
1038            available_size,
1039        } = match axis {
1040            AlignAxis::Height if parent_node.height.inner_sized() && is_vertical_not_start => {
1041                NodeData {
1042                    inner_origin: &mut inner_area.origin.y,
1043                    inner_size: &mut inner_area.size.height,
1044                    area_origin: parent_area.origin.y,
1045                    area_size: parent_area.size.height,
1046                    one_side_padding: parent_node.padding.top(),
1047                    two_sides_padding: parent_node.padding.vertical(),
1048                    one_side_margin: parent_node.margin.top(),
1049                    two_sides_margin: parent_node.margin.vertical(),
1050                    available_size: &mut available_area.size.height,
1051                }
1052            }
1053            AlignAxis::Width if parent_node.width.inner_sized() && is_horizontal_not_start => {
1054                NodeData {
1055                    inner_origin: &mut inner_area.origin.x,
1056                    inner_size: &mut inner_area.size.width,
1057                    area_origin: parent_area.origin.x,
1058                    area_size: parent_area.size.width,
1059                    one_side_padding: parent_node.padding.left(),
1060                    two_sides_padding: parent_node.padding.horizontal(),
1061                    one_side_margin: parent_node.margin.left(),
1062                    two_sides_margin: parent_node.margin.horizontal(),
1063                    available_size: &mut available_area.size.width,
1064                }
1065            }
1066            _ => return,
1067        };
1068
1069        // Set the origin of the inner area to the origin of the area plus the padding and margin for the given axis
1070        *inner_origin = area_origin + one_side_padding + one_side_margin;
1071        // Set the size of the inner area to the size of the area minus the padding and margin for the given axis
1072        *inner_size = area_size - two_sides_padding - two_sides_margin;
1073        // Set the same available size as the inner area for the given axis
1074        *available_size = *inner_size;
1075    }
1076}