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