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