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");
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                {
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            // Re-measure flex children that have an inner (auto) cross-axis size
617            // with their actual flex-grown main-axis dimensions to get accurate
618            // cross-axis sizes for alignment.
619            //
620            // During the initial phase, Size::Flex resolves to None so flex children
621            // get a near-zero main-axis dimension. This causes inner content
622            // (e.g. text measured by a custom measurer) to wrap differently and
623            // produce an incorrect cross-axis size, which then pollutes both
624            // the per-child alignment sizes and the parent's accumulated cross-axis size.
625            //
626            // See: https://github.com/marc2332/freya/issues/1098
627            if parent_node.cross_alignment.is_not_start() {
628                let cross_axis = AlignAxis::new(&parent_node.direction, AlignmentDirection::Cross);
629
630                for child_id in &children {
631                    let Some(flex_grow) = initial_phase_flex_grows.get(child_id) else {
632                        continue;
633                    };
634                    let Some(child_data) = self.tree_adapter.get_node(child_id) else {
635                        continue;
636                    };
637                    if !child_data.position.is_stacked() {
638                        continue;
639                    }
640
641                    let has_inner_cross = match cross_axis {
642                        AlignAxis::Height => child_data.height.inner_sized(),
643                        AlignAxis::Width => child_data.width.inner_sized(),
644                    };
645                    if !has_inner_cross {
646                        continue;
647                    }
648
649                    let flex_grow_per = flex_grow.get() / flex_grows.get() * 100.;
650                    let mut corrected_available_area = initial_area.as_available();
651
652                    match flex_axis {
653                        AlignAxis::Height => {
654                            corrected_available_area.size.height =
655                                flex_available_height / 100. * flex_grow_per;
656                        }
657                        AlignAxis::Width => {
658                            corrected_available_area.size.width =
659                                flex_available_width / 100. * flex_grow_per;
660                        }
661                    }
662
663                    let (_, mut child_areas) = self.measure_node(
664                        *child_id,
665                        &child_data,
666                        initial_area.as_parent(),
667                        corrected_available_area,
668                        false,
669                        parent_is_dirty,
670                        Phase::Final,
671                    );
672
673                    child_areas.area.adjust_size(&child_data);
674                    initial_phase_sizes.insert(*child_id, child_areas.area.size);
675                }
676
677                // Recompute the parent's cross-axis size from corrected child sizes
678                let max_cross = children
679                    .iter()
680                    .filter_map(|id| initial_phase_sizes.get(id))
681                    .map(|size| match cross_axis {
682                        AlignAxis::Height => size.height,
683                        AlignAxis::Width => size.width,
684                    })
685                    .fold(0.0, f32::max);
686
687                match cross_axis {
688                    AlignAxis::Height if parent_node.height.inner_sized() => {
689                        let gaps = parent_node.padding.vertical() + parent_node.margin.vertical();
690                        initial_phase_parent_area.size.height = max_cross + gaps;
691                        initial_phase_inner_area.size.height = max_cross;
692                    }
693                    AlignAxis::Width if parent_node.width.inner_sized() => {
694                        let gaps =
695                            parent_node.padding.horizontal() + parent_node.margin.horizontal();
696                        initial_phase_parent_area.size.width = max_cross + gaps;
697                        initial_phase_inner_area.size.width = max_cross;
698                    }
699                    _ => {}
700                }
701            }
702        }
703
704        if needs_initial_phase {
705            if parent_node.main_alignment.is_not_start() && parent_node.content.allows_alignments()
706            {
707                // Adjust the available and inner areas of the Main axis
708                Self::shrink_area_to_fit_when_unbounded(
709                    available_area,
710                    &initial_phase_parent_area,
711                    &mut initial_phase_inner_area,
712                    parent_node,
713                    AlignmentDirection::Main,
714                );
715
716                // Align the Main axis
717                Self::align_content(
718                    available_area,
719                    &initial_phase_inner_area,
720                    initial_phase_inner_sizes,
721                    &parent_node.main_alignment,
722                    parent_node.direction,
723                    AlignmentDirection::Main,
724                );
725            }
726
727            if (parent_node.cross_alignment.is_not_start() || parent_node.content.is_fit())
728                && parent_node.content.allows_alignments()
729            {
730                // Adjust the available and inner areas of the Cross axis
731                Self::shrink_area_to_fit_when_unbounded(
732                    available_area,
733                    &initial_phase_parent_area,
734                    &mut initial_phase_inner_area,
735                    parent_node,
736                    AlignmentDirection::Cross,
737                );
738            }
739        }
740
741        let initial_available_area = *available_area;
742
743        // Final phase: measure the children with all the axis and sizes adjusted
744        for child_id in children {
745            let Some(child_data) = self.tree_adapter.get_node(&child_id) else {
746                continue;
747            };
748
749            let is_first_child = first_child == Some(child_id);
750            let is_last_child = last_child == Some(child_id);
751
752            let mut adapted_available_area = *available_area;
753
754            if parent_node.content.is_flex() {
755                let flex_grow = initial_phase_flex_grows.get(&child_id);
756
757                if let Some(flex_grow) = flex_grow {
758                    let flex_grow_per = flex_grow.get() / flex_grows.get() * 100.;
759
760                    match flex_axis {
761                        AlignAxis::Height => {
762                            let size = flex_available_height / 100. * flex_grow_per;
763                            adapted_available_area.size.height = size;
764                        }
765                        AlignAxis::Width => {
766                            let size = flex_available_width / 100. * flex_grow_per;
767                            adapted_available_area.size.width = size;
768                        }
769                    }
770                }
771            }
772
773            // Only the stacked children will be aligned
774            if parent_node.main_alignment.is_spaced()
775                && child_data.position.is_stacked()
776                && parent_node.content.allows_alignments()
777            {
778                // Align the Main axis if necessary
779                Self::align_position(
780                    AlignmentDirection::Main,
781                    &mut adapted_available_area,
782                    &initial_available_area,
783                    initial_phase_inner_sizes,
784                    &parent_node.main_alignment,
785                    parent_node.direction,
786                    non_absolute_children_len,
787                    is_first_child,
788                );
789            }
790
791            if parent_node.cross_alignment.is_not_start() && parent_node.content.allows_alignments()
792            {
793                let initial_phase_size = initial_phase_sizes.get(&child_id);
794
795                if let Some(initial_phase_size) = initial_phase_size {
796                    // Align the Cross axis if necessary
797                    Self::align_content(
798                        &mut adapted_available_area,
799                        &available_area.as_inner(),
800                        *initial_phase_size,
801                        &parent_node.cross_alignment,
802                        parent_node.direction,
803                        AlignmentDirection::Cross,
804                    );
805                }
806            }
807
808            if let Content::Wrap { wrap_spacing } = parent_node.content {
809                let initial_phase_size = initial_phase_sizes.get(&child_id);
810                Self::wrap_child(
811                    wrap_spacing.unwrap_or_default(),
812                    parent_node.direction,
813                    initial_phase_size,
814                    &initial_available_area,
815                    parent_area,
816                    available_area,
817                    &mut adapted_available_area,
818                    *inner_sizes,
819                );
820            }
821
822            // Final measurement
823            let (child_revalidated, mut child_areas) = self.measure_node(
824                child_id,
825                &child_data,
826                initial_area.as_parent(),
827                adapted_available_area,
828                must_cache_children,
829                parent_is_dirty,
830                Phase::Final,
831            );
832
833            // Adjust the size of the area if needed
834            child_areas.area.adjust_size(&child_data);
835
836            // Stack this child into the parent
837            if child_data.position.is_stacked() {
838                Self::stack_child(
839                    available_area,
840                    parent_node,
841                    &child_data,
842                    parent_area,
843                    inner_area,
844                    inner_sizes,
845                    &child_areas.area,
846                    is_last_child,
847                    Phase::Final,
848                );
849            }
850
851            // Cache the child layout if it was mutated and children must be cached
852            if child_revalidated && must_cache_children {
853                // Finally cache this node areas into Torin
854                self.layout.cache_node(child_id, child_areas);
855            }
856        }
857    }
858
859    #[allow(clippy::too_many_arguments)]
860    fn wrap_child(
861        wrap_spacing: f32,
862        direction: Direction,
863        initial_phase_size: Option<&Size2D>,
864        initial_available_area: &AreaOf<Available>,
865        parent_area: &mut AreaOf<Parent>,
866        available_area: &mut AreaOf<Available>,
867        adapted_available_area: &mut AreaOf<Available>,
868        inner_sizes: Size2D,
869    ) {
870        if let Some(initial_phase_size) = initial_phase_size {
871            match direction {
872                Direction::Vertical => {
873                    if adapted_available_area.height() - initial_phase_size.height < 0. {
874                        let advance = inner_sizes.width + wrap_spacing;
875                        available_area.origin.y = initial_available_area.origin.y;
876                        available_area.size.height = initial_available_area.size.height;
877                        available_area.origin.x += advance;
878                        adapted_available_area.origin.y = initial_available_area.origin.y;
879                        adapted_available_area.size.height = initial_available_area.size.height;
880                        adapted_available_area.origin.x += advance;
881                        parent_area.size.width += advance;
882                    }
883                }
884                Direction::Horizontal => {
885                    if adapted_available_area.width() - initial_phase_size.width < 0. {
886                        let advance = inner_sizes.height + wrap_spacing;
887                        available_area.origin.x = initial_available_area.origin.x;
888                        available_area.size.width = initial_available_area.size.width;
889                        available_area.origin.y += advance;
890                        adapted_available_area.origin.x = initial_available_area.origin.x;
891                        adapted_available_area.size.width = initial_available_area.size.width;
892                        adapted_available_area.origin.y += advance;
893                        parent_area.size.height += advance;
894                    }
895                }
896            }
897        }
898    }
899
900    /// Align the content of this node.
901    fn align_content(
902        available_area: &mut AreaOf<Available>,
903        inner_area: &AreaOf<Inner>,
904        contents_size: Size2D,
905        alignment: &Alignment,
906        direction: Direction,
907        alignment_direction: AlignmentDirection,
908    ) {
909        let axis = AlignAxis::new(&direction, alignment_direction);
910
911        match axis {
912            AlignAxis::Height => match alignment {
913                Alignment::Center => {
914                    let new_origin_y = (inner_area.height() / 2.0) - (contents_size.height / 2.0);
915                    available_area.origin.y = inner_area.min_y() + new_origin_y;
916                }
917                Alignment::End => {
918                    available_area.origin.y = inner_area.max_y() - contents_size.height;
919                }
920                _ => {}
921            },
922            AlignAxis::Width => match alignment {
923                Alignment::Center => {
924                    let new_origin_x = (inner_area.width() / 2.0) - (contents_size.width / 2.0);
925                    available_area.origin.x = inner_area.min_x() + new_origin_x;
926                }
927                Alignment::End => {
928                    available_area.origin.x = inner_area.max_x() - contents_size.width;
929                }
930                _ => {}
931            },
932        }
933    }
934
935    /// Align the position of this node.
936    #[allow(clippy::too_many_arguments)]
937    fn align_position(
938        alignment_direction: AlignmentDirection,
939        available_area: &mut AreaOf<Available>,
940        initial_available_area: &AreaOf<Available>,
941        inner_sizes: Size2D,
942        alignment: &Alignment,
943        direction: Direction,
944        siblings_len: usize,
945        is_first_sibling: bool,
946    ) {
947        let axis = AlignAxis::new(&direction, alignment_direction);
948
949        match axis {
950            AlignAxis::Height => match alignment {
951                Alignment::SpaceBetween if !is_first_sibling => {
952                    let all_gaps_sizes = initial_available_area.height() - inner_sizes.height;
953                    let gap_size = all_gaps_sizes / (siblings_len - 1) as f32;
954                    available_area.origin.y += gap_size;
955                }
956                Alignment::SpaceEvenly => {
957                    let all_gaps_sizes = initial_available_area.height() - inner_sizes.height;
958                    let gap_size = all_gaps_sizes / (siblings_len + 1) as f32;
959                    available_area.origin.y += gap_size;
960                }
961                Alignment::SpaceAround => {
962                    let all_gaps_sizes = initial_available_area.height() - inner_sizes.height;
963                    let one_gap_size = all_gaps_sizes / siblings_len as f32;
964                    let gap_size = if is_first_sibling {
965                        one_gap_size / 2.
966                    } else {
967                        one_gap_size
968                    };
969                    available_area.origin.y += gap_size;
970                }
971                _ => {}
972            },
973            AlignAxis::Width => match alignment {
974                Alignment::SpaceBetween if !is_first_sibling => {
975                    let all_gaps_sizes = initial_available_area.width() - inner_sizes.width;
976                    let gap_size = all_gaps_sizes / (siblings_len - 1) as f32;
977                    available_area.origin.x += gap_size;
978                }
979                Alignment::SpaceEvenly => {
980                    let all_gaps_sizes = initial_available_area.width() - inner_sizes.width;
981                    let gap_size = all_gaps_sizes / (siblings_len + 1) as f32;
982                    available_area.origin.x += gap_size;
983                }
984                Alignment::SpaceAround => {
985                    let all_gaps_sizes = initial_available_area.width() - inner_sizes.width;
986                    let one_gap_size = all_gaps_sizes / siblings_len as f32;
987                    let gap_size = if is_first_sibling {
988                        one_gap_size / 2.
989                    } else {
990                        one_gap_size
991                    };
992                    available_area.origin.x += gap_size;
993                }
994                _ => {}
995            },
996        }
997    }
998
999    /// Stack a child Node into its parent
1000    #[allow(clippy::too_many_arguments)]
1001    fn stack_child(
1002        available_area: &mut AreaOf<Available>,
1003        parent_node: &Node,
1004        child_node: &Node,
1005        parent_area: &mut AreaOf<Parent>,
1006        inner_area: &mut AreaOf<Inner>,
1007        inner_sizes: &mut Size2D,
1008        child_area: &Area,
1009        is_last_sibilin: bool,
1010        phase: Phase,
1011    ) {
1012        // Only apply the spacing to elements after `i > 0` and `i < len - 1`
1013        let spacing = if is_last_sibilin {
1014            Length::default()
1015        } else {
1016            parent_node.spacing
1017        };
1018
1019        match parent_node.direction {
1020            Direction::Horizontal => {
1021                // Move the available area
1022                available_area.origin.x = child_area.max_x() + spacing.get();
1023                available_area.size.width -= child_area.size.width + spacing.get();
1024
1025                inner_sizes.height = child_area.height().max(inner_sizes.height);
1026                inner_sizes.width += spacing.get();
1027                if !child_node.width.is_flex() || phase == Phase::Final {
1028                    inner_sizes.width += child_area.width();
1029                }
1030
1031                // Keep the biggest height
1032                if parent_node.height.inner_sized() {
1033                    parent_area.size.height = parent_area.size.height.max(
1034                        child_area.size.height
1035                            + parent_node.padding.vertical()
1036                            + parent_node.margin.vertical(),
1037                    );
1038                    // Keep the inner area in sync
1039                    inner_area.size.height = parent_area.size.height
1040                        - parent_node.padding.vertical()
1041                        - parent_node.margin.vertical();
1042                }
1043
1044                // Accumulate width
1045                if parent_node.width.inner_sized() {
1046                    parent_area.size.width += child_area.size.width + spacing.get();
1047                }
1048            }
1049            Direction::Vertical => {
1050                // Move the available area
1051                available_area.origin.y = child_area.max_y() + spacing.get();
1052                available_area.size.height -= child_area.size.height + spacing.get();
1053
1054                inner_sizes.width = child_area.width().max(inner_sizes.width);
1055                inner_sizes.height += spacing.get();
1056                if !child_node.height.is_flex() || phase == Phase::Final {
1057                    inner_sizes.height += child_area.height();
1058                }
1059
1060                // Keep the biggest width
1061                if parent_node.width.inner_sized() {
1062                    parent_area.size.width = parent_area.size.width.max(
1063                        child_area.size.width
1064                            + parent_node.padding.horizontal()
1065                            + parent_node.margin.horizontal(),
1066                    );
1067                    // Keep the inner area in sync
1068                    inner_area.size.width = parent_area.size.width
1069                        - parent_node.padding.horizontal()
1070                        - parent_node.margin.horizontal();
1071                }
1072
1073                // Accumulate height
1074                if parent_node.height.inner_sized() {
1075                    parent_area.size.height += child_area.size.height + spacing.get();
1076                }
1077            }
1078        }
1079    }
1080
1081    /// Shrink the available area and inner area of a parent node when for example height is set to "auto",
1082    /// direction is vertical and main_alignment is set to "center" or "end" or the content is set to "fit".
1083    /// The intended usage is to call this after the first measurement and before the second,
1084    /// this way the second measurement will align the content relatively to the parent element instead
1085    /// of overflowing due to being aligned relatively to the upper parent element
1086    fn shrink_area_to_fit_when_unbounded(
1087        available_area: &mut AreaOf<Available>,
1088        parent_area: &AreaOf<Parent>,
1089        inner_area: &mut AreaOf<Inner>,
1090        parent_node: &Node,
1091        alignment_direction: AlignmentDirection,
1092    ) {
1093        struct NodeData<'a> {
1094            pub inner_origin: &'a mut f32,
1095            pub inner_size: &'a mut f32,
1096            pub area_origin: f32,
1097            pub area_size: f32,
1098            pub one_side_padding: f32,
1099            pub two_sides_padding: f32,
1100            pub one_side_margin: f32,
1101            pub two_sides_margin: f32,
1102            pub available_size: &'a mut f32,
1103        }
1104
1105        let axis = AlignAxis::new(&parent_node.direction, alignment_direction);
1106        let (is_vertical_not_start, is_horizontal_not_start) = match parent_node.direction {
1107            Direction::Vertical => (
1108                parent_node.main_alignment.is_not_start(),
1109                parent_node.cross_alignment.is_not_start() || parent_node.content.is_fit(),
1110            ),
1111            Direction::Horizontal => (
1112                parent_node.cross_alignment.is_not_start() || parent_node.content.is_fit(),
1113                parent_node.main_alignment.is_not_start(),
1114            ),
1115        };
1116        let NodeData {
1117            inner_origin,
1118            inner_size,
1119            area_origin,
1120            area_size,
1121            one_side_padding,
1122            two_sides_padding,
1123            one_side_margin,
1124            two_sides_margin,
1125            available_size,
1126        } = match axis {
1127            AlignAxis::Height if parent_node.height.inner_sized() && is_vertical_not_start => {
1128                NodeData {
1129                    inner_origin: &mut inner_area.origin.y,
1130                    inner_size: &mut inner_area.size.height,
1131                    area_origin: parent_area.origin.y,
1132                    area_size: parent_area.size.height,
1133                    one_side_padding: parent_node.padding.top(),
1134                    two_sides_padding: parent_node.padding.vertical(),
1135                    one_side_margin: parent_node.margin.top(),
1136                    two_sides_margin: parent_node.margin.vertical(),
1137                    available_size: &mut available_area.size.height,
1138                }
1139            }
1140            AlignAxis::Width if parent_node.width.inner_sized() && is_horizontal_not_start => {
1141                NodeData {
1142                    inner_origin: &mut inner_area.origin.x,
1143                    inner_size: &mut inner_area.size.width,
1144                    area_origin: parent_area.origin.x,
1145                    area_size: parent_area.size.width,
1146                    one_side_padding: parent_node.padding.left(),
1147                    two_sides_padding: parent_node.padding.horizontal(),
1148                    one_side_margin: parent_node.margin.left(),
1149                    two_sides_margin: parent_node.margin.horizontal(),
1150                    available_size: &mut available_area.size.width,
1151                }
1152            }
1153            _ => return,
1154        };
1155
1156        // Set the origin of the inner area to the origin of the area plus the padding and margin for the given axis
1157        *inner_origin = area_origin + one_side_padding + one_side_margin;
1158        // Set the size of the inner area to the size of the area minus the padding and margin for the given axis
1159        *inner_size = area_size - two_sides_padding - two_sides_margin;
1160        // Set the same available size as the inner area for the given axis
1161        *available_size = *inner_size;
1162    }
1163}