Skip to main content

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