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 a single Node's cached areas by the given offset, notifying layout references.
65    fn translate_node(&mut self, node_id: Key, offset_x: Length, offset_y: Length) {
66        let Some((area, visible_area, inner_sizes)) =
67            self.layout.get_mut(&node_id).map(|layout_node| {
68                layout_node.area.origin.x += offset_x.get();
69                layout_node.area.origin.y += offset_y.get();
70                layout_node.inner_area.origin.x += offset_x.get();
71                layout_node.inner_area.origin.y += offset_y.get();
72                (
73                    layout_node.area,
74                    layout_node.visible_area(),
75                    layout_node.inner_sizes,
76                )
77            })
78        else {
79            return;
80        };
81
82        if let Some(measurer) = self.measurer {
83            measurer.notify_layout_references(node_id, area, visible_area, inner_sizes);
84        }
85    }
86
87    /// Translate all the children of the given Node by the specified X and Y offsets.
88    fn recursive_translate(&mut self, node_id: Key, offset_x: Length, offset_y: Length) {
89        let mut buffer = self.tree_adapter.children_of(&node_id);
90        while let Some(child) = buffer.pop() {
91            let node = self
92                .tree_adapter
93                .get_node(&child)
94                .expect("Node does not exist");
95
96            let translate = match node.position {
97                Position::Global(_) => false,
98                Position::Stacked(_) | Position::Absolute(_) => true,
99            };
100
101            if translate {
102                self.translate_node(child, offset_x, offset_y);
103                buffer.extend(self.tree_adapter.children_of(&child));
104            }
105        }
106    }
107
108    /// Set the hidden state of a Node and all its descendants.
109    fn set_hidden(&mut self, node_id: Key, hidden: bool) {
110        let mut buffer = vec![node_id];
111        while let Some(child) = buffer.pop() {
112            if let Some(layout_node) = self.layout.get_mut(&child) {
113                layout_node.hidden = hidden;
114            }
115            buffer.extend(self.tree_adapter.children_of(&child));
116        }
117    }
118
119    /// Run the measurer's post-measure step once a Node's subtree is measured. Applies the child
120    /// offsets and hidden states it returns and gives back its corrected content size, if any.
121    fn apply_post_measure(&mut self, node_id: Key, node_layout: &LayoutNode) -> Option<Size2D> {
122        let post_measure = {
123            let measurer = self.measurer.as_mut()?;
124            if !measurer.should_post_measure(node_id) {
125                return None;
126            }
127            let children = self.tree_adapter.children_of(&node_id);
128            measurer.post_measure(node_id, node_layout, &children, self.layout)
129        };
130
131        for (child_id, offset_x, offset_y) in post_measure.offsets {
132            self.set_hidden(child_id, false);
133            self.translate_node(child_id, offset_x, offset_y);
134            self.recursive_translate(child_id, offset_x, offset_y);
135        }
136
137        for child_id in post_measure.hidden_children {
138            self.set_hidden(child_id, true);
139        }
140
141        post_measure.content_size
142    }
143
144    /// Measure a Node and all its children.
145    #[allow(clippy::too_many_arguments, clippy::missing_panics_doc)]
146    pub fn measure_node(
147        &mut self,
148        node_id: Key,
149        node: &Node,
150        // Initial area occupied by it's parent
151        initial_parent_area: AreaOf<Parent>,
152        // Area that is available to use by the children of the parent
153        available_parent_area: AreaOf<Available>,
154        // Whether to cache the measurements of this Node's children
155        must_cache_children: bool,
156        // Parent Node is dirty.
157        parent_is_dirty: bool,
158        // Current phase of measurement
159        phase: Phase,
160    ) -> (bool, LayoutNode) {
161        let reason = self.layout.dirty.get(&node_id).copied();
162
163        // If possible translate all this Node's descendants to avoid relayout
164        if let Some(layout_node) = self.layout.get_mut(&node_id)
165            && reason == Some(DirtyReason::InnerLayout)
166            && must_cache_children
167        {
168            // Get the offset difference since the last layout
169            let offset_x = node.offset_x - layout_node.offset_x;
170            let offset_y = node.offset_y - layout_node.offset_y;
171
172            layout_node.offset_x = node.offset_x;
173            layout_node.offset_y = node.offset_y;
174
175            let layout_node = layout_node.clone();
176
177            self.recursive_translate(node_id, offset_x, offset_y);
178
179            return (must_cache_children, layout_node);
180        }
181
182        // 1. If parent is dirty
183        // 2. If this Node has been marked as dirty
184        // 3. If there is no know cached data about this Node.
185        let must_revalidate =
186            parent_is_dirty || reason.is_some() || !self.layout.results.contains_key(&node_id);
187        if must_revalidate {
188            // Create the initial Node area size
189            let mut area_size = Size2D::new(node.padding.horizontal(), node.padding.vertical());
190
191            // Compute the width and height given the size, the minimum size, the maximum size and margins
192            area_size.width = node.width.min_max(
193                area_size.width,
194                initial_parent_area.size.width,
195                available_parent_area.size.width,
196                node.margin.left(),
197                node.margin.horizontal(),
198                &node.minimum_width,
199                &node.maximum_width,
200                self.layout_metadata.root_area.width(),
201                phase,
202            );
203            area_size.height = node.height.min_max(
204                area_size.height,
205                initial_parent_area.size.height,
206                available_parent_area.size.height,
207                node.margin.top(),
208                node.margin.vertical(),
209                &node.minimum_height,
210                &node.maximum_height,
211                self.layout_metadata.root_area.height(),
212                phase,
213            );
214
215            // If available, run a custom layout measure function
216            // This is useful when you use third-party libraries (e.g. rust-skia, cosmic-text) to measure text layouts
217            let node_data = if let Some(measurer) = self.measurer {
218                if measurer.should_hook_measurement(node_id) {
219                    let available_width =
220                        Size::Pixels(Length::new(available_parent_area.size.width)).min_max(
221                            area_size.width,
222                            initial_parent_area.size.width,
223                            available_parent_area.size.width,
224                            node.margin.left(),
225                            node.margin.horizontal(),
226                            &node.minimum_width,
227                            &node.maximum_width,
228                            self.layout_metadata.root_area.width(),
229                            phase,
230                        );
231                    let available_height =
232                        Size::Pixels(Length::new(available_parent_area.size.height)).min_max(
233                            area_size.height,
234                            initial_parent_area.size.height,
235                            available_parent_area.size.height,
236                            node.margin.top(),
237                            node.margin.vertical(),
238                            &node.minimum_height,
239                            &node.maximum_height,
240                            self.layout_metadata.root_area.height(),
241                            phase,
242                        );
243                    let most_fitting_width = *node
244                        .width
245                        .most_fitting_size(&area_size.width, &available_width);
246                    let most_fitting_height = *node
247                        .height
248                        .most_fitting_size(&area_size.height, &available_height);
249
250                    let most_fitting_area_size =
251                        Size2D::new(most_fitting_width, most_fitting_height);
252                    let res = measurer.measure(node_id, node, &most_fitting_area_size);
253
254                    // Compute the width and height again using the new custom area sizes
255                    #[allow(clippy::float_cmp)]
256                    if let Some((custom_size, node_data)) = res {
257                        if node.width.inner_sized() {
258                            area_size.width = node.width.min_max(
259                                custom_size.width,
260                                initial_parent_area.size.width,
261                                available_parent_area.size.width,
262                                node.margin.left(),
263                                node.margin.horizontal(),
264                                &node.minimum_width,
265                                &node.maximum_width,
266                                self.layout_metadata.root_area.width(),
267                                phase,
268                            );
269                        }
270                        if node.height.inner_sized() {
271                            area_size.height = node.height.min_max(
272                                custom_size.height,
273                                initial_parent_area.size.height,
274                                available_parent_area.size.height,
275                                node.margin.top(),
276                                node.margin.vertical(),
277                                &node.minimum_height,
278                                &node.maximum_height,
279                                self.layout_metadata.root_area.height(),
280                                phase,
281                            );
282                        }
283
284                        // Do not measure inner children
285                        Some(node_data)
286                    } else {
287                        None
288                    }
289                } else {
290                    None
291                }
292            } else {
293                None
294            };
295
296            let measure_inner_children = if let Some(measurer) = self.measurer {
297                measurer.should_measure_inner_children(node_id)
298            } else {
299                true
300            };
301
302            // There is no need to measure inner children in the initial phase if this Node size
303            // isn't decided by his children
304            let phase_measure_inner_children = if phase == Phase::Initial {
305                node.width.inner_sized() || node.height.inner_sized()
306            } else {
307                true
308            };
309
310            // Compute the inner size of the Node, which is basically the size inside the margins and paddings
311            let inner_size = {
312                let mut inner_size = area_size;
313
314                // When having an unsized bound we set it to whatever is still available in the parent's area
315                if node.width.inner_sized() {
316                    inner_size.width = node.width.min_max(
317                        available_parent_area.width(),
318                        initial_parent_area.size.width,
319                        available_parent_area.width(),
320                        node.margin.left(),
321                        node.margin.horizontal(),
322                        &node.minimum_width,
323                        &node.maximum_width,
324                        self.layout_metadata.root_area.width(),
325                        phase,
326                    );
327                }
328                if node.height.inner_sized() {
329                    inner_size.height = node.height.min_max(
330                        available_parent_area.height(),
331                        initial_parent_area.size.height,
332                        available_parent_area.height(),
333                        node.margin.top(),
334                        node.margin.vertical(),
335                        &node.minimum_height,
336                        &node.maximum_height,
337                        self.layout_metadata.root_area.height(),
338                        phase,
339                    );
340                }
341                inner_size
342            };
343
344            // Create the areas
345            let area_origin = node.position.get_origin(
346                &available_parent_area,
347                &initial_parent_area,
348                area_size,
349                &self.layout_metadata.root_area,
350            );
351            let mut area = Area::new(area_origin, area_size);
352            let mut inner_area = Rect::new(area_origin, inner_size)
353                .without_gaps(&node.padding)
354                .without_gaps(&node.margin)
355                .as_inner();
356            inner_area.move_with_offsets(&node.offset_x, &node.offset_y);
357
358            let mut inner_sizes = Size2D::default();
359
360            if measure_inner_children && phase_measure_inner_children {
361                // Create an area containing the available space inside the inner area
362                let mut available_area = inner_area.as_available();
363
364                let mut parent_area = area.as_parent();
365
366                // Measure the layout of this Node's children
367                self.measure_children(
368                    &node_id,
369                    node,
370                    &mut parent_area,
371                    &mut inner_area,
372                    &mut available_area,
373                    &mut inner_sizes,
374                    must_cache_children,
375                    true,
376                );
377
378                // Re apply min max values after measuring with inner sized
379                // Margins are set to 0 because area.size already contains the margins
380                if node.width.inner_sized() {
381                    parent_area.size.width = node.width.min_max(
382                        parent_area.size.width,
383                        initial_parent_area.size.width,
384                        available_parent_area.size.width,
385                        0.,
386                        0.,
387                        &node.minimum_width,
388                        &node.maximum_width,
389                        self.layout_metadata.root_area.width(),
390                        phase,
391                    );
392                }
393                if node.height.inner_sized() {
394                    parent_area.size.height = node.height.min_max(
395                        parent_area.size.height,
396                        initial_parent_area.size.height,
397                        available_parent_area.size.height,
398                        0.,
399                        0.,
400                        &node.minimum_height,
401                        &node.maximum_height,
402                        self.layout_metadata.root_area.height(),
403                        phase,
404                    );
405                }
406
407                area = parent_area.cast_unit();
408
409                // Recompute the origin for non-stacked inner-sized elements
410                // now that the final size is known, and translate cached children accordingly.
411                if !node.position.is_stacked()
412                    && (node.width.inner_sized() || node.height.inner_sized())
413                    && must_cache_children
414                {
415                    let new_origin = node.position.get_origin(
416                        &available_parent_area,
417                        &initial_parent_area,
418                        area.size,
419                        &self.layout_metadata.root_area,
420                    );
421                    let diff_x = new_origin.x - area.origin.x;
422                    let diff_y = new_origin.y - area.origin.y;
423                    area.origin = new_origin;
424                    inner_area.origin.x += diff_x;
425                    inner_area.origin.y += diff_y;
426
427                    if diff_x != 0.0 || diff_y != 0.0 {
428                        self.recursive_translate(node_id, Length::new(diff_x), Length::new(diff_y));
429                    }
430                }
431            }
432
433            let mut layout_node = LayoutNode {
434                area,
435                margin: node.margin,
436                offset_x: node.offset_x,
437                offset_y: node.offset_y,
438                inner_area,
439                hidden: false,
440                data: node_data,
441                inner_sizes,
442            };
443
444            if must_cache_children
445                && phase == Phase::Final
446                && let Some(content_size) = self.apply_post_measure(node_id, &layout_node)
447            {
448                // The post-measure content size accounts for inline children, so re-apply the
449                // sizing of inner-sized axes. The inner area keeps holding the available space.
450                if node.width.inner_sized() {
451                    layout_node.area.size.width = node.width.min_max(
452                        content_size.width,
453                        initial_parent_area.size.width,
454                        available_parent_area.size.width,
455                        node.margin.left(),
456                        node.margin.horizontal(),
457                        &node.minimum_width,
458                        &node.maximum_width,
459                        self.layout_metadata.root_area.width(),
460                        phase,
461                    );
462                }
463                if node.height.inner_sized() {
464                    layout_node.area.size.height = node.height.min_max(
465                        content_size.height,
466                        initial_parent_area.size.height,
467                        available_parent_area.size.height,
468                        node.margin.top(),
469                        node.margin.vertical(),
470                        &node.minimum_height,
471                        &node.maximum_height,
472                        self.layout_metadata.root_area.height(),
473                        phase,
474                    );
475                }
476            }
477
478            // In case of any layout listener, notify it with the new areas.
479            if must_cache_children
480                && phase == Phase::Final
481                && node.has_layout_references
482                && let Some(measurer) = self.measurer
483            {
484                inner_sizes.width += node.padding.horizontal();
485                inner_sizes.height += node.padding.vertical();
486                measurer.notify_layout_references(
487                    node_id,
488                    layout_node.area,
489                    layout_node.visible_area(),
490                    inner_sizes,
491                );
492            }
493
494            (must_cache_children, layout_node)
495        } else {
496            let layout_node = self
497                .layout
498                .get(&node_id)
499                .expect("Cached node does not exist")
500                .clone();
501
502            let mut inner_sizes = Size2D::default();
503            let mut area = layout_node.area.as_parent();
504            let mut inner_area = layout_node.inner_area.as_inner();
505            let mut available_area = inner_area.as_available();
506
507            let measure_inner_children = if let Some(measurer) = self.measurer {
508                measurer.should_measure_inner_children(node_id)
509            } else {
510                true
511            };
512
513            if measure_inner_children {
514                self.measure_children(
515                    &node_id,
516                    node,
517                    &mut area,
518                    &mut inner_area,
519                    &mut available_area,
520                    &mut inner_sizes,
521                    must_cache_children,
522                    false,
523                );
524            }
525
526            (false, layout_node)
527        }
528    }
529
530    /// Measure the children layouts of a Node.
531    #[allow(clippy::too_many_arguments)]
532    pub fn measure_children(
533        &mut self,
534        parent_node_id: &Key,
535        parent_node: &Node,
536        parent_area: &mut AreaOf<Parent>,
537        inner_area: &mut AreaOf<Inner>,
538        available_area: &mut AreaOf<Available>,
539        // Accumulated sizes in both axis in the Node
540        inner_sizes: &mut Size2D,
541        // Whether to cache the measurements of this Node's children
542        must_cache_children: bool,
543        // Parent Node is dirty.
544        parent_is_dirty: bool,
545    ) {
546        let mut children = self.tree_adapter.children_of(parent_node_id);
547        if parent_node.order.is_reverse() {
548            children.reverse();
549        }
550
551        let initial_area = *inner_area;
552
553        let mut initial_phase_flex_grows = FxHashMap::default();
554        let mut initial_phase_sizes = FxHashMap::default();
555        let mut initial_phase_inner_sizes = Size2D::default();
556
557        // Used to calculate the spacing and some alignments
558        let (non_absolute_children_len, first_child, last_child) = if parent_node.spacing.get() > 0.
559        {
560            let mut last_child = None;
561            let mut first_child = None;
562            let len = children
563                .iter()
564                .filter(|child_id| {
565                    let Some(child_data) = self.tree_adapter.get_node(child_id) else {
566                        return false;
567                    };
568                    let is_stacked = child_data.position.is_stacked();
569                    if is_stacked {
570                        last_child = Some(**child_id);
571
572                        if first_child.is_none() {
573                            first_child = Some(**child_id);
574                        }
575                    }
576                    is_stacked
577                })
578                .count();
579            (len, first_child, last_child)
580        } else {
581            (
582                children.len(),
583                children.first().copied(),
584                children.last().copied(),
585            )
586        };
587
588        let needs_initial_phase = parent_node.cross_alignment.is_not_start()
589            || parent_node.main_alignment.is_not_start()
590            || parent_node.content.is_fit()
591            || parent_node.content.is_flex()
592            || parent_node.content.is_wrap();
593
594        let mut initial_phase_parent_area = *parent_area;
595        let mut initial_phase_inner_area = *inner_area;
596        let mut initial_phase_available_area = *available_area;
597
598        // Initial phase: Measure the size and position of the children if the parent has a
599        // non-start cross alignment, non-start main alignment of a fit-content.
600        if needs_initial_phase {
601            //  Measure the children
602            for child_id in &children {
603                let Some(child_data) = self.tree_adapter.get_node(child_id) else {
604                    continue;
605                };
606
607                // No need to consider this Node for a two-phasing
608                // measurements as it will float on its own.
609                if !child_data.position.is_stacked() {
610                    continue;
611                }
612
613                let is_last_child = last_child == Some(*child_id);
614
615                let (_, mut child_areas) = self.measure_node(
616                    *child_id,
617                    &child_data,
618                    initial_area.as_parent(),
619                    initial_phase_available_area,
620                    false,
621                    parent_is_dirty,
622                    Phase::Initial,
623                );
624
625                child_areas.area.adjust_size(&child_data);
626
627                // Stack this child into the parent
628                Self::stack_child(
629                    &mut initial_phase_available_area,
630                    parent_node,
631                    &child_data,
632                    &mut initial_phase_parent_area,
633                    &mut initial_phase_inner_area,
634                    &mut initial_phase_inner_sizes,
635                    &child_areas.area,
636                    is_last_child,
637                    Phase::Initial,
638                );
639
640                if parent_node.cross_alignment.is_not_start()
641                    || parent_node.main_alignment.is_spaced()
642                    || parent_node.content.is_wrap()
643                {
644                    initial_phase_sizes.insert(*child_id, child_areas.area.size);
645                }
646
647                if parent_node.content.is_flex() {
648                    match parent_node.direction {
649                        Direction::Vertical => {
650                            if let Some(ff) = child_data.height.flex_grow() {
651                                initial_phase_flex_grows.insert(*child_id, ff);
652                            }
653                        }
654                        Direction::Horizontal => {
655                            if let Some(ff) = child_data.width.flex_grow() {
656                                initial_phase_flex_grows.insert(*child_id, ff);
657                            }
658                        }
659                    }
660                }
661            }
662        }
663
664        let flex_grows = initial_phase_flex_grows
665            .values()
666            .copied()
667            .reduce(|acc, v| acc + v)
668            .unwrap_or_default()
669            .max(Length::new(1.0));
670
671        let flex_axis = AlignAxis::new(&parent_node.direction, AlignmentDirection::Main);
672
673        let flex_available_width = available_area.width() - initial_phase_inner_sizes.width;
674        let flex_available_height = available_area.height() - initial_phase_inner_sizes.height;
675
676        if parent_node.content.is_flex() {
677            initial_phase_inner_sizes =
678                initial_phase_flex_grows
679                    .values()
680                    .fold(initial_phase_inner_sizes, |mut acc, f| {
681                        let flex_grow_per = f.get() / flex_grows.get() * 100.;
682
683                        match flex_axis {
684                            AlignAxis::Height => {
685                                let size = flex_available_height / 100. * flex_grow_per;
686                                acc.height += size;
687                            }
688                            AlignAxis::Width => {
689                                let size = flex_available_width / 100. * flex_grow_per;
690                                acc.width += size;
691                            }
692                        }
693
694                        acc
695                    });
696
697            // Re-measure flex children that have an inner (auto) cross-axis size
698            // with their actual flex-grown main-axis dimensions to get accurate
699            // cross-axis sizes for alignment.
700            //
701            // During the initial phase, Size::Flex resolves to None so flex children
702            // get a near-zero main-axis dimension. This causes inner content
703            // (e.g. text measured by a custom measurer) to wrap differently and
704            // produce an incorrect cross-axis size, which then pollutes both
705            // the per-child alignment sizes and the parent's accumulated cross-axis size.
706            //
707            // See: https://github.com/marc2332/freya/issues/1098
708            if parent_node.cross_alignment.is_not_start() {
709                let cross_axis = AlignAxis::new(&parent_node.direction, AlignmentDirection::Cross);
710
711                for child_id in &children {
712                    let Some(flex_grow) = initial_phase_flex_grows.get(child_id) else {
713                        continue;
714                    };
715                    let Some(child_data) = self.tree_adapter.get_node(child_id) else {
716                        continue;
717                    };
718                    if !child_data.position.is_stacked() {
719                        continue;
720                    }
721
722                    let has_inner_cross = match cross_axis {
723                        AlignAxis::Height => child_data.height.inner_sized(),
724                        AlignAxis::Width => child_data.width.inner_sized(),
725                    };
726                    if !has_inner_cross {
727                        continue;
728                    }
729
730                    let flex_grow_per = flex_grow.get() / flex_grows.get() * 100.;
731                    let mut corrected_available_area = initial_area.as_available();
732
733                    match flex_axis {
734                        AlignAxis::Height => {
735                            corrected_available_area.size.height =
736                                flex_available_height / 100. * flex_grow_per;
737                        }
738                        AlignAxis::Width => {
739                            corrected_available_area.size.width =
740                                flex_available_width / 100. * flex_grow_per;
741                        }
742                    }
743
744                    let (_, mut child_areas) = self.measure_node(
745                        *child_id,
746                        &child_data,
747                        initial_area.as_parent(),
748                        corrected_available_area,
749                        false,
750                        parent_is_dirty,
751                        Phase::Final,
752                    );
753
754                    child_areas.area.adjust_size(&child_data);
755                    initial_phase_sizes.insert(*child_id, child_areas.area.size);
756                }
757
758                // Recompute the parent's cross-axis size from corrected child sizes
759                let max_cross = children
760                    .iter()
761                    .filter_map(|id| initial_phase_sizes.get(id))
762                    .map(|size| match cross_axis {
763                        AlignAxis::Height => size.height,
764                        AlignAxis::Width => size.width,
765                    })
766                    .fold(0.0, f32::max);
767
768                match cross_axis {
769                    AlignAxis::Height if parent_node.height.inner_sized() => {
770                        let gaps = parent_node.padding.vertical() + parent_node.margin.vertical();
771                        initial_phase_parent_area.size.height = max_cross + gaps;
772                        initial_phase_inner_area.size.height = max_cross;
773                    }
774                    AlignAxis::Width if parent_node.width.inner_sized() => {
775                        let gaps =
776                            parent_node.padding.horizontal() + parent_node.margin.horizontal();
777                        initial_phase_parent_area.size.width = max_cross + gaps;
778                        initial_phase_inner_area.size.width = max_cross;
779                    }
780                    _ => {}
781                }
782            }
783        }
784
785        if needs_initial_phase {
786            if parent_node.main_alignment.is_not_start() && parent_node.content.allows_alignments()
787            {
788                // Adjust the available and inner areas of the Main axis
789                Self::shrink_area_to_fit_when_unbounded(
790                    available_area,
791                    &initial_phase_parent_area,
792                    &mut initial_phase_inner_area,
793                    parent_node,
794                    AlignmentDirection::Main,
795                );
796
797                // Align the Main axis
798                Self::align_content(
799                    available_area,
800                    &initial_phase_inner_area,
801                    initial_phase_inner_sizes,
802                    &parent_node.main_alignment,
803                    parent_node.direction,
804                    AlignmentDirection::Main,
805                );
806            }
807
808            if (parent_node.cross_alignment.is_not_start() || parent_node.content.is_fit())
809                && parent_node.content.allows_alignments()
810            {
811                // Adjust the available and inner areas of the Cross axis
812                Self::shrink_area_to_fit_when_unbounded(
813                    available_area,
814                    &initial_phase_parent_area,
815                    &mut initial_phase_inner_area,
816                    parent_node,
817                    AlignmentDirection::Cross,
818                );
819            }
820        }
821
822        let initial_available_area = *available_area;
823
824        // Final phase: measure the children with all the axis and sizes adjusted
825        for child_id in children {
826            let Some(child_data) = self.tree_adapter.get_node(&child_id) else {
827                continue;
828            };
829
830            let is_first_child = first_child == Some(child_id);
831            let is_last_child = last_child == Some(child_id);
832
833            let mut adapted_available_area = *available_area;
834
835            if parent_node.content.is_flex() {
836                let flex_grow = initial_phase_flex_grows.get(&child_id);
837
838                if let Some(flex_grow) = flex_grow {
839                    let flex_grow_per = flex_grow.get() / flex_grows.get() * 100.;
840
841                    match flex_axis {
842                        AlignAxis::Height => {
843                            let size = flex_available_height / 100. * flex_grow_per;
844                            adapted_available_area.size.height = size;
845                        }
846                        AlignAxis::Width => {
847                            let size = flex_available_width / 100. * flex_grow_per;
848                            adapted_available_area.size.width = size;
849                        }
850                    }
851                }
852            }
853
854            // Only the stacked children will be aligned
855            if parent_node.main_alignment.is_spaced()
856                && child_data.position.is_stacked()
857                && parent_node.content.allows_alignments()
858            {
859                // Align the Main axis if necessary
860                Self::align_position(
861                    AlignmentDirection::Main,
862                    &mut adapted_available_area,
863                    &initial_available_area,
864                    initial_phase_inner_sizes,
865                    &parent_node.main_alignment,
866                    parent_node.direction,
867                    non_absolute_children_len,
868                    is_first_child,
869                );
870            }
871
872            if parent_node.cross_alignment.is_not_start() && parent_node.content.allows_alignments()
873            {
874                let initial_phase_size = initial_phase_sizes.get(&child_id);
875
876                if let Some(initial_phase_size) = initial_phase_size {
877                    // Align the Cross axis if necessary
878                    Self::align_content(
879                        &mut adapted_available_area,
880                        &available_area.as_inner(),
881                        *initial_phase_size,
882                        &parent_node.cross_alignment,
883                        parent_node.direction,
884                        AlignmentDirection::Cross,
885                    );
886                }
887            }
888
889            if let Content::Wrap { wrap_spacing } = parent_node.content {
890                let initial_phase_size = initial_phase_sizes.get(&child_id);
891                Self::wrap_child(
892                    wrap_spacing.unwrap_or_default(),
893                    parent_node.direction,
894                    initial_phase_size,
895                    &initial_available_area,
896                    parent_area,
897                    available_area,
898                    &mut adapted_available_area,
899                    *inner_sizes,
900                );
901            }
902
903            // Final measurement
904            let (child_revalidated, mut child_areas) = self.measure_node(
905                child_id,
906                &child_data,
907                initial_area.as_parent(),
908                adapted_available_area,
909                must_cache_children,
910                parent_is_dirty,
911                Phase::Final,
912            );
913
914            // Adjust the size of the area if needed
915            child_areas.area.adjust_size(&child_data);
916
917            // Stack this child into the parent
918            if child_data.position.is_stacked() {
919                Self::stack_child(
920                    available_area,
921                    parent_node,
922                    &child_data,
923                    parent_area,
924                    inner_area,
925                    inner_sizes,
926                    &child_areas.area,
927                    is_last_child,
928                    Phase::Final,
929                );
930            }
931
932            // Cache the child layout if it was mutated and children must be cached
933            if child_revalidated && must_cache_children {
934                // Finally cache this node areas into Torin
935                self.layout.cache_node(child_id, child_areas);
936            }
937        }
938    }
939
940    #[allow(clippy::too_many_arguments)]
941    fn wrap_child(
942        wrap_spacing: f32,
943        direction: Direction,
944        initial_phase_size: Option<&Size2D>,
945        initial_available_area: &AreaOf<Available>,
946        parent_area: &mut AreaOf<Parent>,
947        available_area: &mut AreaOf<Available>,
948        adapted_available_area: &mut AreaOf<Available>,
949        inner_sizes: Size2D,
950    ) {
951        if let Some(initial_phase_size) = initial_phase_size {
952            match direction {
953                Direction::Vertical => {
954                    if adapted_available_area.height() - initial_phase_size.height < 0. {
955                        let advance = inner_sizes.width + wrap_spacing;
956                        available_area.origin.y = initial_available_area.origin.y;
957                        available_area.size.height = initial_available_area.size.height;
958                        available_area.origin.x += advance;
959                        adapted_available_area.origin.y = initial_available_area.origin.y;
960                        adapted_available_area.size.height = initial_available_area.size.height;
961                        adapted_available_area.origin.x += advance;
962                        parent_area.size.width += advance;
963                    }
964                }
965                Direction::Horizontal => {
966                    if adapted_available_area.width() - initial_phase_size.width < 0. {
967                        let advance = inner_sizes.height + wrap_spacing;
968                        available_area.origin.x = initial_available_area.origin.x;
969                        available_area.size.width = initial_available_area.size.width;
970                        available_area.origin.y += advance;
971                        adapted_available_area.origin.x = initial_available_area.origin.x;
972                        adapted_available_area.size.width = initial_available_area.size.width;
973                        adapted_available_area.origin.y += advance;
974                        parent_area.size.height += advance;
975                    }
976                }
977            }
978        }
979    }
980
981    /// Align the content of this node.
982    fn align_content(
983        available_area: &mut AreaOf<Available>,
984        inner_area: &AreaOf<Inner>,
985        contents_size: Size2D,
986        alignment: &Alignment,
987        direction: Direction,
988        alignment_direction: AlignmentDirection,
989    ) {
990        let axis = AlignAxis::new(&direction, alignment_direction);
991
992        match axis {
993            AlignAxis::Height => match alignment {
994                Alignment::Center => {
995                    let new_origin_y = (inner_area.height() / 2.0) - (contents_size.height / 2.0);
996                    available_area.origin.y = inner_area.min_y() + new_origin_y;
997                }
998                Alignment::End => {
999                    available_area.origin.y = inner_area.max_y() - contents_size.height;
1000                }
1001                _ => {}
1002            },
1003            AlignAxis::Width => match alignment {
1004                Alignment::Center => {
1005                    let new_origin_x = (inner_area.width() / 2.0) - (contents_size.width / 2.0);
1006                    available_area.origin.x = inner_area.min_x() + new_origin_x;
1007                }
1008                Alignment::End => {
1009                    available_area.origin.x = inner_area.max_x() - contents_size.width;
1010                }
1011                _ => {}
1012            },
1013        }
1014    }
1015
1016    /// Align the position of this node.
1017    #[allow(clippy::too_many_arguments)]
1018    fn align_position(
1019        alignment_direction: AlignmentDirection,
1020        available_area: &mut AreaOf<Available>,
1021        initial_available_area: &AreaOf<Available>,
1022        inner_sizes: Size2D,
1023        alignment: &Alignment,
1024        direction: Direction,
1025        siblings_len: usize,
1026        is_first_sibling: bool,
1027    ) {
1028        let axis = AlignAxis::new(&direction, alignment_direction);
1029
1030        match axis {
1031            AlignAxis::Height => match alignment {
1032                Alignment::SpaceBetween if !is_first_sibling => {
1033                    let all_gaps_sizes = initial_available_area.height() - inner_sizes.height;
1034                    let gap_size = all_gaps_sizes / (siblings_len - 1) as f32;
1035                    available_area.origin.y += gap_size;
1036                }
1037                Alignment::SpaceEvenly => {
1038                    let all_gaps_sizes = initial_available_area.height() - inner_sizes.height;
1039                    let gap_size = all_gaps_sizes / (siblings_len + 1) as f32;
1040                    available_area.origin.y += gap_size;
1041                }
1042                Alignment::SpaceAround => {
1043                    let all_gaps_sizes = initial_available_area.height() - inner_sizes.height;
1044                    let one_gap_size = all_gaps_sizes / siblings_len as f32;
1045                    let gap_size = if is_first_sibling {
1046                        one_gap_size / 2.
1047                    } else {
1048                        one_gap_size
1049                    };
1050                    available_area.origin.y += gap_size;
1051                }
1052                _ => {}
1053            },
1054            AlignAxis::Width => match alignment {
1055                Alignment::SpaceBetween if !is_first_sibling => {
1056                    let all_gaps_sizes = initial_available_area.width() - inner_sizes.width;
1057                    let gap_size = all_gaps_sizes / (siblings_len - 1) as f32;
1058                    available_area.origin.x += gap_size;
1059                }
1060                Alignment::SpaceEvenly => {
1061                    let all_gaps_sizes = initial_available_area.width() - inner_sizes.width;
1062                    let gap_size = all_gaps_sizes / (siblings_len + 1) as f32;
1063                    available_area.origin.x += gap_size;
1064                }
1065                Alignment::SpaceAround => {
1066                    let all_gaps_sizes = initial_available_area.width() - inner_sizes.width;
1067                    let one_gap_size = all_gaps_sizes / siblings_len as f32;
1068                    let gap_size = if is_first_sibling {
1069                        one_gap_size / 2.
1070                    } else {
1071                        one_gap_size
1072                    };
1073                    available_area.origin.x += gap_size;
1074                }
1075                _ => {}
1076            },
1077        }
1078    }
1079
1080    /// Stack a child Node into its parent
1081    #[allow(clippy::too_many_arguments)]
1082    fn stack_child(
1083        available_area: &mut AreaOf<Available>,
1084        parent_node: &Node,
1085        child_node: &Node,
1086        parent_area: &mut AreaOf<Parent>,
1087        inner_area: &mut AreaOf<Inner>,
1088        inner_sizes: &mut Size2D,
1089        child_area: &Area,
1090        is_last_sibilin: bool,
1091        phase: Phase,
1092    ) {
1093        // Only apply the spacing to elements after `i > 0` and `i < len - 1`
1094        let spacing = if is_last_sibilin {
1095            Length::default()
1096        } else {
1097            parent_node.spacing
1098        };
1099
1100        match parent_node.direction {
1101            Direction::Horizontal => {
1102                // Move the available area
1103                available_area.origin.x = child_area.max_x() + spacing.get();
1104                available_area.size.width -= child_area.size.width + spacing.get();
1105
1106                inner_sizes.height = child_area.height().max(inner_sizes.height);
1107                inner_sizes.width += spacing.get();
1108                if !child_node.width.is_flex() || phase == Phase::Final {
1109                    inner_sizes.width += child_area.width();
1110                }
1111
1112                // Keep the biggest height
1113                if parent_node.height.inner_sized() {
1114                    parent_area.size.height = parent_area.size.height.max(
1115                        child_area.size.height
1116                            + parent_node.padding.vertical()
1117                            + parent_node.margin.vertical(),
1118                    );
1119                    // Keep the inner area in sync
1120                    inner_area.size.height = parent_area.size.height
1121                        - parent_node.padding.vertical()
1122                        - parent_node.margin.vertical();
1123                }
1124
1125                // Accumulate width
1126                if parent_node.width.inner_sized() {
1127                    parent_area.size.width += child_area.size.width + spacing.get();
1128                }
1129            }
1130            Direction::Vertical => {
1131                // Move the available area
1132                available_area.origin.y = child_area.max_y() + spacing.get();
1133                available_area.size.height -= child_area.size.height + spacing.get();
1134
1135                inner_sizes.width = child_area.width().max(inner_sizes.width);
1136                inner_sizes.height += spacing.get();
1137                if !child_node.height.is_flex() || phase == Phase::Final {
1138                    inner_sizes.height += child_area.height();
1139                }
1140
1141                // Keep the biggest width
1142                if parent_node.width.inner_sized() {
1143                    parent_area.size.width = parent_area.size.width.max(
1144                        child_area.size.width
1145                            + parent_node.padding.horizontal()
1146                            + parent_node.margin.horizontal(),
1147                    );
1148                    // Keep the inner area in sync
1149                    inner_area.size.width = parent_area.size.width
1150                        - parent_node.padding.horizontal()
1151                        - parent_node.margin.horizontal();
1152                }
1153
1154                // Accumulate height
1155                if parent_node.height.inner_sized() {
1156                    parent_area.size.height += child_area.size.height + spacing.get();
1157                }
1158            }
1159        }
1160    }
1161
1162    /// Shrink the available area and inner area of a parent node when for example height is set to "auto",
1163    /// direction is vertical and main_alignment is set to "center" or "end" or the content is set to "fit".
1164    /// The intended usage is to call this after the first measurement and before the second,
1165    /// this way the second measurement will align the content relatively to the parent element instead
1166    /// of overflowing due to being aligned relatively to the upper parent element
1167    fn shrink_area_to_fit_when_unbounded(
1168        available_area: &mut AreaOf<Available>,
1169        parent_area: &AreaOf<Parent>,
1170        inner_area: &mut AreaOf<Inner>,
1171        parent_node: &Node,
1172        alignment_direction: AlignmentDirection,
1173    ) {
1174        struct NodeData<'a> {
1175            pub inner_origin: &'a mut f32,
1176            pub inner_size: &'a mut f32,
1177            pub area_origin: f32,
1178            pub area_size: f32,
1179            pub one_side_padding: f32,
1180            pub two_sides_padding: f32,
1181            pub one_side_margin: f32,
1182            pub two_sides_margin: f32,
1183            pub available_size: &'a mut f32,
1184        }
1185
1186        let axis = AlignAxis::new(&parent_node.direction, alignment_direction);
1187        let (is_vertical_not_start, is_horizontal_not_start) = match parent_node.direction {
1188            Direction::Vertical => (
1189                parent_node.main_alignment.is_not_start(),
1190                parent_node.cross_alignment.is_not_start() || parent_node.content.is_fit(),
1191            ),
1192            Direction::Horizontal => (
1193                parent_node.cross_alignment.is_not_start() || parent_node.content.is_fit(),
1194                parent_node.main_alignment.is_not_start(),
1195            ),
1196        };
1197        let NodeData {
1198            inner_origin,
1199            inner_size,
1200            area_origin,
1201            area_size,
1202            one_side_padding,
1203            two_sides_padding,
1204            one_side_margin,
1205            two_sides_margin,
1206            available_size,
1207        } = match axis {
1208            AlignAxis::Height if parent_node.height.inner_sized() && is_vertical_not_start => {
1209                NodeData {
1210                    inner_origin: &mut inner_area.origin.y,
1211                    inner_size: &mut inner_area.size.height,
1212                    area_origin: parent_area.origin.y,
1213                    area_size: parent_area.size.height,
1214                    one_side_padding: parent_node.padding.top(),
1215                    two_sides_padding: parent_node.padding.vertical(),
1216                    one_side_margin: parent_node.margin.top(),
1217                    two_sides_margin: parent_node.margin.vertical(),
1218                    available_size: &mut available_area.size.height,
1219                }
1220            }
1221            AlignAxis::Width if parent_node.width.inner_sized() && is_horizontal_not_start => {
1222                NodeData {
1223                    inner_origin: &mut inner_area.origin.x,
1224                    inner_size: &mut inner_area.size.width,
1225                    area_origin: parent_area.origin.x,
1226                    area_size: parent_area.size.width,
1227                    one_side_padding: parent_node.padding.left(),
1228                    two_sides_padding: parent_node.padding.horizontal(),
1229                    one_side_margin: parent_node.margin.left(),
1230                    two_sides_margin: parent_node.margin.horizontal(),
1231                    available_size: &mut available_area.size.width,
1232                }
1233            }
1234            _ => return,
1235        };
1236
1237        // Set the origin of the inner area to the origin of the area plus the padding and margin for the given axis
1238        *inner_origin = area_origin + one_side_padding + one_side_margin;
1239        // Set the size of the inner area to the size of the area minus the padding and margin for the given axis
1240        *inner_size = area_size - two_sides_padding - two_sides_margin;
1241        // Set the same available size as the inner area for the given axis
1242        *available_size = *inner_size;
1243    }
1244}