Skip to main content

inset_rendering/
flex.rs

1//! Flutter counterpart: `rendering/flex.dart` (`FlexFit`, `FlexParentData`, `MainAxisSize`,
2//! `MainAxisAlignment`, `CrossAxisAlignment`, `RenderFlex`).
3
4use std::any::{Any, TypeId};
5use std::collections::HashMap;
6use std::fmt::{self, Debug};
7use std::ops::Add;
8
9use inset_embedder::{Clip, Offset, Size, TextBaseline, TextDirection};
10use inset_foundation::{App, Handle, PRECISION_ERROR_TOLERANCE};
11use inset_painting::{Axis, VerticalDirection};
12
13use crate::box_::{
14    AnyRenderBox, BaselineOffset, BoxConstraints, BoxHitTestResult, BoxParentData,
15    ContainerBoxParentData, DryLayoutFailure, RenderBox, RenderBoxContainerDefaultsMixin,
16    RenderBoxData,
17};
18use crate::layer::{ClipRectLayer, LayerHandle};
19use crate::layout_helper::{ChildBaselineGetter, ChildLayoutHelper};
20use crate::object::{
21    AnyRenderObject, ContainerParentData, ContainerParentDataMixin, ContainerRenderObjectData,
22    ContainerRenderObjectMixin, ParentData, RenderHandle, RenderObject, RenderObjectData,
23    debug_checking_intrinsics,
24};
25use crate::painting_context::PaintingContext;
26use crate::pipeline_owner::PipelineOwner;
27
28/// A 2D vector that uses a [`RenderFlex`]'s main axis and cross axis as its first and second
29/// coordinate axes. It represents the same vector as `(main_axis_extent, cross_axis_extent)`.
30#[derive(Clone, Copy)]
31struct AxisSize(Size);
32
33impl AxisSize {
34    const EMPTY: AxisSize = AxisSize(Size::ZERO);
35
36    fn new(main_axis_extent: f64, cross_axis_extent: f64) -> AxisSize {
37        AxisSize(Size::new(main_axis_extent, cross_axis_extent))
38    }
39
40    fn from_size(size: Size, direction: Axis) -> AxisSize {
41        AxisSize(AxisSize::convert(size, direction))
42    }
43
44    fn convert(size: Size, direction: Axis) -> Size {
45        match direction {
46            Axis::Horizontal => size,
47            Axis::Vertical => size.flipped(),
48        }
49    }
50
51    fn main_axis_extent(self) -> f64 {
52        self.0.width()
53    }
54
55    fn cross_axis_extent(self) -> f64 {
56        self.0.height()
57    }
58
59    fn to_size(self, direction: Axis) -> Size {
60        AxisSize::convert(self.0, direction)
61    }
62
63    fn apply_constraints(self, constraints: BoxConstraints, direction: Axis) -> AxisSize {
64        let effective_constraints = match direction {
65            Axis::Horizontal => constraints,
66            Axis::Vertical => constraints.flipped(),
67        };
68        AxisSize(effective_constraints.constrain(self.0))
69    }
70}
71
72impl Add for AxisSize {
73    type Output = AxisSize;
74
75    fn add(self, other: AxisSize) -> AxisSize {
76        AxisSize(Size::new(
77            self.0.width() + other.0.width(),
78            self.0.height().max(other.0.height()),
79        ))
80    }
81}
82
83/// The ascent and descent of a baseline-aligned child.
84///
85/// Baseline-aligned children contributes to the cross axis extent of a [`RenderFlex`]
86/// differently from children with other [`CrossAxisAlignment`]s.
87#[derive(Clone, Copy)]
88struct AscentDescent(Option<(f64, f64)>);
89
90impl AscentDescent {
91    const NONE: AscentDescent = AscentDescent(None);
92
93    fn new(baseline_offset: Option<f64>, cross_size: f64) -> AscentDescent {
94        match baseline_offset {
95            None => AscentDescent::NONE,
96            Some(baseline_offset) => {
97                AscentDescent(Some((baseline_offset, cross_size - baseline_offset)))
98            }
99        }
100    }
101
102    fn baseline_offset(self) -> Option<f64> {
103        self.0.map(|(ascent, _)| ascent)
104    }
105}
106
107impl Add for AscentDescent {
108    type Output = AscentDescent;
109
110    fn add(self, other: AscentDescent) -> AscentDescent {
111        match (self.0, other.0) {
112            (None, _) => other,
113            (_, None) => self,
114            (Some((x_ascent, x_descent)), Some((y_ascent, y_descent))) => {
115                AscentDescent(Some((x_ascent.max(y_ascent), x_descent.max(y_descent))))
116            }
117        }
118    }
119}
120
121/// Flutter's `_ChildSizingFunction`: a method that finds a child's size in the sizing
122/// direction, given the extent in the other direction.
123type ChildSizingFunction = fn(AnyRenderBox, &mut App, f64) -> f64;
124
125/// What [`RenderFlexMixin::compute_sizes`] worked out about a layout.
126pub struct LayoutSizes {
127    /// The final constrained [`AxisSize`] of the [`RenderFlex`].
128    axis_size: AxisSize,
129
130    /// The free space along the main axis. If the value is positive, the free space will be
131    /// distributed according to the [`MainAxisAlignment`] specified. A negative value indicates
132    /// the [`RenderFlex`] overflows along the main axis.
133    main_axis_free_space: f64,
134
135    /// `None` if the [`RenderFlex`] is not baseline aligned, or none of its children has a valid
136    /// baseline of the given [`TextBaseline`] type.
137    baseline_offset: Option<f64>,
138
139    /// The allocated space for flex children.
140    space_per_flex: Option<f64>,
141}
142
143/// How the child is inscribed into the available space.
144///
145/// See also:
146///
147///  * [`RenderFlex`], the flex render object.
148#[derive(Clone, Copy, Debug, PartialEq, Eq)]
149pub enum FlexFit {
150    /// The child is forced to fill the available space.
151    Tight,
152
153    /// The child can be at most as large as the available space (but is allowed to be smaller).
154    Loose,
155}
156
157/// Parent data for use with [`RenderFlex`].
158#[derive(Debug)]
159pub struct FlexParentData {
160    box_parent_data: BoxParentData,
161    container_parent_data: ContainerParentData<AnyRenderBox>,
162
163    /// The flex factor to use for this child.
164    ///
165    /// If `None` or zero, the child is inflexible and determines its own size. If non-zero, the
166    /// amount of space the child's can occupy in the main axis is determined by dividing the
167    /// free space (after placing the inflexible children) according to the flex factors of the
168    /// flexible children.
169    pub flex: Option<i32>,
170
171    /// How a flexible child is inscribed into the available space.
172    ///
173    /// If [`flex`](Self::flex) is non-zero, the fit determines whether the child fills the space
174    /// the parent makes available during layout. If the fit is [`FlexFit::Tight`], the child is
175    /// required to fill the available space. If the fit is [`FlexFit::Loose`], the child can be
176    /// at most as large as the available space (but is allowed to be smaller).
177    pub fit: Option<FlexFit>,
178}
179
180impl FlexParentData {
181    /// Creates flex parent data with no flex factor and no fit.
182    pub const fn new() -> FlexParentData {
183        FlexParentData {
184            box_parent_data: BoxParentData::new(),
185            container_parent_data: ContainerParentData::new(),
186            flex: None,
187            fit: None,
188        }
189    }
190}
191
192impl Default for FlexParentData {
193    fn default() -> FlexParentData {
194        FlexParentData::new()
195    }
196}
197
198impl ParentData for FlexParentData {
199    fn detach(&mut self) {
200        ContainerParentDataMixin::detach(self);
201    }
202
203    fn provide(&self, id: TypeId) -> Option<&dyn Any> {
204        if id == TypeId::of::<FlexParentData>() {
205            return Some(self);
206        }
207        self.box_parent_data.provide(id)
208    }
209
210    fn provide_mut(&mut self, id: TypeId) -> Option<&mut dyn Any> {
211        if id == TypeId::of::<FlexParentData>() {
212            return Some(self);
213        }
214        self.box_parent_data.provide_mut(id)
215    }
216}
217
218impl ContainerParentDataMixin for FlexParentData {
219    type ChildType = AnyRenderBox;
220
221    fn container_parent_data(&self) -> &ContainerParentData<AnyRenderBox> {
222        &self.container_parent_data
223    }
224
225    fn container_parent_data_mut(&mut self) -> &mut ContainerParentData<AnyRenderBox> {
226        &mut self.container_parent_data
227    }
228}
229
230impl ContainerBoxParentData for FlexParentData {
231    fn box_parent_data(&self) -> &BoxParentData {
232        &self.box_parent_data
233    }
234
235    fn box_parent_data_mut(&mut self) -> &mut BoxParentData {
236        &mut self.box_parent_data
237    }
238}
239
240impl fmt::Display for FlexParentData {
241    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242        write!(
243            f,
244            "{}; flex={:?}; fit={:?}",
245            self.box_parent_data, self.flex, self.fit
246        )
247    }
248}
249
250/// How much space should be occupied in the main axis.
251///
252/// During a flex layout, available space along the main axis is allocated to children. After
253/// allocating space, there might be some remaining free space. This value controls whether to
254/// maximize or minimize the amount of free space, subject to the incoming layout constraints.
255///
256/// See also:
257///
258///  * [`RenderFlex`], the flex render object.
259///  * [`MainAxisAlignment`], which controls how the free space is distributed.
260#[derive(Clone, Copy, Debug, PartialEq, Eq)]
261pub enum MainAxisSize {
262    /// Minimize the amount of free space along the main axis, subject to the incoming layout
263    /// constraints.
264    ///
265    /// If the incoming layout constraints have a large enough [`BoxConstraints::min_width`] or
266    /// [`BoxConstraints::min_height`], there might still be a non-zero amount of free space.
267    ///
268    /// If the incoming layout constraints are unbounded, and any children have a non-zero
269    /// [`FlexParentData::flex`] and a [`FlexFit::Tight`] fit, the [`RenderFlex`] will assert,
270    /// because there would be infinite remaining free space and boxes cannot be given infinite
271    /// size.
272    Min,
273
274    /// Maximize the amount of free space along the main axis, subject to the incoming layout
275    /// constraints.
276    ///
277    /// If the incoming layout constraints have a small enough [`BoxConstraints::max_width`] or
278    /// [`BoxConstraints::max_height`], there might still be no free space.
279    ///
280    /// If the incoming layout constraints are unbounded, the [`RenderFlex`] will assert, because
281    /// there would be infinite remaining free space and boxes cannot be given infinite size.
282    Max,
283}
284
285/// How the children should be placed along the main axis in a flex layout.
286///
287/// See also:
288///
289///  * [`RenderFlex`], the flex render object.
290#[derive(Clone, Copy, Debug, PartialEq, Eq)]
291pub enum MainAxisAlignment {
292    /// Place the children as close to the start of the main axis as possible.
293    ///
294    /// If this value is used in a horizontal direction, a [`TextDirection`] must be available to
295    /// determine if the start is the left or the right.
296    ///
297    /// If this value is used in a vertical direction, a [`VerticalDirection`] must be available
298    /// to determine if the start is the top or the bottom.
299    Start,
300
301    /// Place the children as close to the end of the main axis as possible.
302    ///
303    /// If this value is used in a horizontal direction, a [`TextDirection`] must be available to
304    /// determine if the end is the left or the right.
305    ///
306    /// If this value is used in a vertical direction, a [`VerticalDirection`] must be available
307    /// to determine if the end is the top or the bottom.
308    End,
309
310    /// Place the children as close to the middle of the main axis as possible.
311    Center,
312
313    /// Place the free space evenly between the children.
314    SpaceBetween,
315
316    /// Place the free space evenly between the children as well as half of that space before and
317    /// after the first and last child.
318    SpaceAround,
319
320    /// Place the free space evenly between the children as well as before and after the first
321    /// and last child.
322    SpaceEvenly,
323}
324
325impl MainAxisAlignment {
326    fn distribute_space(
327        self,
328        free_space: f64,
329        item_count: usize,
330        flipped: bool,
331        spacing: f64,
332    ) -> (f64, f64) {
333        let items = item_count as f64;
334        match self {
335            MainAxisAlignment::Start => {
336                if flipped {
337                    (free_space, spacing)
338                } else {
339                    (0.0, spacing)
340                }
341            }
342            MainAxisAlignment::End => {
343                MainAxisAlignment::Start.distribute_space(free_space, item_count, !flipped, spacing)
344            }
345            MainAxisAlignment::SpaceBetween if item_count < 2 => {
346                MainAxisAlignment::Start.distribute_space(free_space, item_count, flipped, spacing)
347            }
348            MainAxisAlignment::SpaceAround if item_count == 0 => {
349                MainAxisAlignment::Start.distribute_space(free_space, item_count, flipped, spacing)
350            }
351            MainAxisAlignment::Center => (free_space / 2.0, spacing),
352            MainAxisAlignment::SpaceBetween => (0.0, free_space / (items - 1.0) + spacing),
353            MainAxisAlignment::SpaceAround => {
354                (free_space / items / 2.0, free_space / items + spacing)
355            }
356            MainAxisAlignment::SpaceEvenly => (
357                free_space / (items + 1.0),
358                free_space / (items + 1.0) + spacing,
359            ),
360        }
361    }
362}
363
364/// How the children should be placed along the cross axis in a flex layout.
365///
366/// See also:
367///
368///  * [`RenderFlex`], the flex render object.
369#[derive(Clone, Copy, Debug, PartialEq, Eq)]
370pub enum CrossAxisAlignment {
371    /// Place the children with their start edge aligned with the start side of the cross axis.
372    ///
373    /// For example, in a column (a flex with a vertical axis) whose [`TextDirection`] is
374    /// [`TextDirection::Ltr`], this aligns the left edge of the children along the left edge of
375    /// the column.
376    ///
377    /// If this value is used in a horizontal direction, a [`TextDirection`] must be available to
378    /// determine if the start is the left or the right.
379    ///
380    /// If this value is used in a vertical direction, a [`VerticalDirection`] must be available
381    /// to determine if the start is the top or the bottom.
382    Start,
383
384    /// Place the children as close to the end of the cross axis as possible.
385    ///
386    /// For example, in a column (a flex with a vertical axis) whose [`TextDirection`] is
387    /// [`TextDirection::Ltr`], this aligns the right edge of the children along the right edge
388    /// of the column.
389    ///
390    /// If this value is used in a horizontal direction, a [`TextDirection`] must be available to
391    /// determine if the end is the left or the right.
392    ///
393    /// If this value is used in a vertical direction, a [`VerticalDirection`] must be available
394    /// to determine if the end is the top or the bottom.
395    End,
396
397    /// Place the children so that their centers align with the middle of the cross axis.
398    ///
399    /// This is the default cross-axis alignment.
400    Center,
401
402    /// Require the children to fill the cross axis.
403    ///
404    /// This causes the constraints passed to the children to be tight in the cross axis.
405    Stretch,
406
407    /// Place the children along the cross axis such that their baselines match.
408    ///
409    /// Consider using this value for any horizontal main axis (as with a row) where the children
410    /// primarily contain text. If the different children have text with different font metrics
411    /// (for example because they differ in font size or other text style properties, or because
412    /// they use different fonts due to being written in different scripts), then this typically
413    /// produces better visual alignment than the other [`CrossAxisAlignment`] values, which use
414    /// no information about where the text sits vertically within its bounding box.
415    ///
416    /// The baseline of a widget is typically the typographic baseline of the first text in the
417    /// first text widget it encloses, if any. The typographic baseline is a horizontal line used
418    /// for aligning text, which is specified by each font; for alphabetic scripts, it ordinarily
419    /// runs along the bottom of letters excluding any descenders.
420    ///
421    /// Because baselines are always horizontal, this alignment is intended for horizontal main
422    /// axes (as with a row). If the main axis is vertical (as with a column), then this value is
423    /// treated like [`Start`](Self::Start).
424    ///
425    /// For horizontal main axes, if the minimum height constraint passed to the flex layout
426    /// exceeds the intrinsic height of the cross axis, children will be aligned as close to the
427    /// top as they can be while honoring the baseline alignment. In other words, the extra space
428    /// will be below all the children.
429    ///
430    /// Children who report no baseline will be top-aligned.
431    ///
432    /// See also:
433    ///
434    ///  * [`AnyRenderBox::get_distance_to_baseline`], which defines the baseline of a box.
435    Baseline,
436}
437
438impl CrossAxisAlignment {
439    /// This method should not be used to position baseline-aligned children.
440    fn get_child_cross_axis_offset(self, free_space: f64, flipped: bool) -> f64 {
441        match self {
442            CrossAxisAlignment::Stretch | CrossAxisAlignment::Baseline => 0.0,
443            CrossAxisAlignment::Start => {
444                if flipped {
445                    free_space
446                } else {
447                    0.0
448                }
449            }
450            CrossAxisAlignment::Center => free_space / 2.0,
451            CrossAxisAlignment::End => {
452                CrossAxisAlignment::Start.get_child_cross_axis_offset(free_space, !flipped)
453            }
454        }
455    }
456}
457
458/// The fields of a [`RenderFlex`].
459///
460/// A render object that inherits the flex layout holds this bag and implements
461/// [`RenderFlexMixin`].
462pub struct RenderFlexData {
463    direction: Axis,
464    main_axis_alignment: MainAxisAlignment,
465    main_axis_size: MainAxisSize,
466    cross_axis_alignment: CrossAxisAlignment,
467    text_direction: Option<TextDirection>,
468    vertical_direction: VerticalDirection,
469    text_baseline: Option<TextBaseline>,
470    clip_behavior: Clip,
471    spacing: f64,
472    /// Set during layout if overflow occurred on the main axis.
473    overflow: f64,
474    clip_rect_layer: LayerHandle<Handle<ClipRectLayer>>,
475}
476
477impl RenderFlexData {
478    /// Creates the flex fields with their defaults.
479    ///
480    /// The layout is horizontal and children are aligned to the start of the main axis and the
481    /// center of the cross axis.
482    pub fn new() -> RenderFlexData {
483        RenderFlexData {
484            direction: Axis::Horizontal,
485            main_axis_alignment: MainAxisAlignment::Start,
486            main_axis_size: MainAxisSize::Max,
487            cross_axis_alignment: CrossAxisAlignment::Center,
488            text_direction: None,
489            vertical_direction: VerticalDirection::Down,
490            text_baseline: None,
491            clip_behavior: Clip::None,
492            spacing: 0.0,
493            overflow: 0.0,
494            clip_rect_layer: LayerHandle::new(),
495        }
496    }
497}
498
499impl Default for RenderFlexData {
500    fn default() -> RenderFlexData {
501        RenderFlexData::new()
502    }
503}
504
505impl RenderFlexData {
506    /// Dart `RenderFlex(direction:)`.
507    pub fn direction(mut self, direction: Axis) -> RenderFlexData {
508        self.direction = direction;
509        self
510    }
511
512    /// Dart `RenderFlex(mainAxisAlignment:)`.
513    pub fn main_axis_alignment(mut self, main_axis_alignment: MainAxisAlignment) -> RenderFlexData {
514        self.main_axis_alignment = main_axis_alignment;
515        self
516    }
517
518    /// Dart `RenderFlex(mainAxisSize:)`.
519    pub fn main_axis_size(mut self, main_axis_size: MainAxisSize) -> RenderFlexData {
520        self.main_axis_size = main_axis_size;
521        self
522    }
523
524    /// Dart `RenderFlex(crossAxisAlignment:)`.
525    pub fn cross_axis_alignment(
526        mut self,
527        cross_axis_alignment: CrossAxisAlignment,
528    ) -> RenderFlexData {
529        self.cross_axis_alignment = cross_axis_alignment;
530        self
531    }
532
533    /// Dart `RenderFlex(textDirection:)`.
534    pub fn text_direction(mut self, text_direction: Option<TextDirection>) -> RenderFlexData {
535        self.text_direction = text_direction;
536        self
537    }
538
539    /// Dart `RenderFlex(verticalDirection:)`.
540    pub fn vertical_direction(mut self, vertical_direction: VerticalDirection) -> RenderFlexData {
541        self.vertical_direction = vertical_direction;
542        self
543    }
544
545    /// Dart `RenderFlex(textBaseline:)`.
546    pub fn text_baseline(mut self, text_baseline: Option<TextBaseline>) -> RenderFlexData {
547        self.text_baseline = text_baseline;
548        self
549    }
550
551    /// Dart `RenderFlex(clipBehavior:)`.
552    pub fn clip_behavior(mut self, clip_behavior: Clip) -> RenderFlexData {
553        self.clip_behavior = clip_behavior;
554        self
555    }
556
557    /// Dart `RenderFlex(spacing:)`.
558    pub fn spacing(mut self, spacing: f64) -> RenderFlexData {
559        self.spacing = spacing;
560        self
561    }
562}
563
564/// Displays its children in a one-dimensional array.
565///
566/// Flutter's `RenderFlex`: the shared bodies of the flex layout. A leaf holds a
567/// [`RenderFlexData`], implements this trait, and calls these where Dart would run the inherited
568/// method: `RenderFlexMixin::perform_layout(self, app)`.
569///
570/// See [`RenderFlex`] for the layout algorithm.
571pub trait RenderFlexMixin:
572    RenderBox
573    + ContainerRenderObjectMixin<ChildType = AnyRenderBox, ParentDataType = FlexParentData>
574    + RenderBoxContainerDefaultsMixin
575{
576    /// Mixin field access.
577    fn flex_data(self: RenderHandle<Self>, app: &App) -> &RenderFlexData;
578
579    /// See [`flex_data`](Self::flex_data).
580    fn flex_data_mut(self: RenderHandle<Self>, app: &mut App) -> &mut RenderFlexData;
581
582    /// The direction to use as the main axis.
583    fn direction(self: RenderHandle<Self>, app: &App) -> Axis {
584        self.flex_data(app).direction
585    }
586
587    /// Sets [`direction`](Self::direction).
588    fn set_direction(self: RenderHandle<Self>, app: &mut App, value: Axis) {
589        if self.flex_data(app).direction != value {
590            self.flex_data_mut(app).direction = value;
591            self.mark_needs_layout(app);
592        }
593    }
594
595    /// How the children should be placed along the main axis.
596    ///
597    /// If the [`direction`](Self::direction) is [`Axis::Horizontal`], and the alignment is either
598    /// [`MainAxisAlignment::Start`] or [`MainAxisAlignment::End`], then the
599    /// [`text_direction`](Self::text_direction) must not be `None`.
600    fn main_axis_alignment(self: RenderHandle<Self>, app: &App) -> MainAxisAlignment {
601        self.flex_data(app).main_axis_alignment
602    }
603
604    /// Sets [`main_axis_alignment`](Self::main_axis_alignment).
605    fn set_main_axis_alignment(self: RenderHandle<Self>, app: &mut App, value: MainAxisAlignment) {
606        if self.flex_data(app).main_axis_alignment != value {
607            self.flex_data_mut(app).main_axis_alignment = value;
608            self.mark_needs_layout(app);
609        }
610    }
611
612    /// How much space should be occupied in the main axis.
613    ///
614    /// After allocating space to children, there might be some remaining free space. This value
615    /// controls whether to maximize or minimize the amount of free space, subject to the incoming
616    /// layout constraints.
617    fn main_axis_size(self: RenderHandle<Self>, app: &App) -> MainAxisSize {
618        self.flex_data(app).main_axis_size
619    }
620
621    /// Sets [`main_axis_size`](Self::main_axis_size).
622    fn set_main_axis_size(self: RenderHandle<Self>, app: &mut App, value: MainAxisSize) {
623        if self.flex_data(app).main_axis_size != value {
624            self.flex_data_mut(app).main_axis_size = value;
625            self.mark_needs_layout(app);
626        }
627    }
628
629    /// How the children should be placed along the cross axis.
630    ///
631    /// If the [`direction`](Self::direction) is [`Axis::Vertical`], and the alignment is either
632    /// [`CrossAxisAlignment::Start`] or [`CrossAxisAlignment::End`], then the
633    /// [`text_direction`](Self::text_direction) must not be `None`.
634    fn cross_axis_alignment(self: RenderHandle<Self>, app: &App) -> CrossAxisAlignment {
635        self.flex_data(app).cross_axis_alignment
636    }
637
638    /// Sets [`cross_axis_alignment`](Self::cross_axis_alignment).
639    fn set_cross_axis_alignment(
640        self: RenderHandle<Self>,
641        app: &mut App,
642        value: CrossAxisAlignment,
643    ) {
644        if self.flex_data(app).cross_axis_alignment != value {
645            self.flex_data_mut(app).cross_axis_alignment = value;
646            self.mark_needs_layout(app);
647        }
648    }
649
650    /// Determines the order to lay children out horizontally and how to interpret `start` and
651    /// `end` in the horizontal direction.
652    ///
653    /// If the [`direction`](Self::direction) is [`Axis::Horizontal`], this controls the order in
654    /// which children are positioned (left-to-right or right-to-left), and the meaning of the
655    /// [`main_axis_alignment`](Self::main_axis_alignment) property's
656    /// [`MainAxisAlignment::Start`] and [`MainAxisAlignment::End`] values.
657    ///
658    /// If the [`direction`](Self::direction) is [`Axis::Vertical`], this controls the meaning of
659    /// the [`cross_axis_alignment`](Self::cross_axis_alignment) property's
660    /// [`CrossAxisAlignment::Start`] and [`CrossAxisAlignment::End`] values.
661    fn text_direction(self: RenderHandle<Self>, app: &App) -> Option<TextDirection> {
662        self.flex_data(app).text_direction
663    }
664
665    /// Sets [`text_direction`](Self::text_direction).
666    fn set_text_direction(self: RenderHandle<Self>, app: &mut App, value: Option<TextDirection>) {
667        if self.flex_data(app).text_direction != value {
668            self.flex_data_mut(app).text_direction = value;
669            self.mark_needs_layout(app);
670        }
671    }
672
673    /// Determines the order to lay children out vertically and how to interpret `start` and `end`
674    /// in the vertical direction.
675    ///
676    /// If the [`direction`](Self::direction) is [`Axis::Vertical`], this controls which order
677    /// children are painted in (down or up), and the meaning of the
678    /// [`main_axis_alignment`](Self::main_axis_alignment) property's
679    /// [`MainAxisAlignment::Start`] and [`MainAxisAlignment::End`] values.
680    ///
681    /// If the [`direction`](Self::direction) is [`Axis::Horizontal`], this controls the meaning
682    /// of the [`cross_axis_alignment`](Self::cross_axis_alignment) property's
683    /// [`CrossAxisAlignment::Start`] and [`CrossAxisAlignment::End`] values.
684    fn vertical_direction(self: RenderHandle<Self>, app: &App) -> VerticalDirection {
685        self.flex_data(app).vertical_direction
686    }
687
688    /// Sets [`vertical_direction`](Self::vertical_direction).
689    fn set_vertical_direction(self: RenderHandle<Self>, app: &mut App, value: VerticalDirection) {
690        if self.flex_data(app).vertical_direction != value {
691            self.flex_data_mut(app).vertical_direction = value;
692            self.mark_needs_layout(app);
693        }
694    }
695
696    /// If aligning items according to their baseline, which baseline to use.
697    ///
698    /// Must not be `None` if [`cross_axis_alignment`](Self::cross_axis_alignment) is
699    /// [`CrossAxisAlignment::Baseline`].
700    fn text_baseline(self: RenderHandle<Self>, app: &App) -> Option<TextBaseline> {
701        self.flex_data(app).text_baseline
702    }
703
704    /// Sets [`text_baseline`](Self::text_baseline).
705    fn set_text_baseline(self: RenderHandle<Self>, app: &mut App, value: Option<TextBaseline>) {
706        debug_assert!(
707            self.flex_data(app).cross_axis_alignment != CrossAxisAlignment::Baseline
708                || value.is_some()
709        );
710        if self.flex_data(app).text_baseline != value {
711            self.flex_data_mut(app).text_baseline = value;
712            self.mark_needs_layout(app);
713        }
714    }
715
716    /// Whether the flex overflowed along the main axis.
717    ///
718    /// Values below an epsilon are treated as not overflowing. Internal to the flex layout; a
719    /// leaf does not republish it.
720    fn has_overflow(self: RenderHandle<Self>, app: &App) -> bool {
721        self.flex_data(app).overflow > PRECISION_ERROR_TOLERANCE
722    }
723
724    /// How to clip the children that overflow along the main axis.
725    ///
726    /// Defaults to [`Clip::None`].
727    fn clip_behavior(self: RenderHandle<Self>, app: &App) -> Clip {
728        self.flex_data(app).clip_behavior
729    }
730
731    /// Sets [`clip_behavior`](Self::clip_behavior).
732    fn set_clip_behavior(self: RenderHandle<Self>, app: &mut App, value: Clip) {
733        if value != self.flex_data(app).clip_behavior {
734            self.flex_data_mut(app).clip_behavior = value;
735            self.mark_needs_paint(app);
736        }
737    }
738
739    /// How much space to place between children in the main axis.
740    ///
741    /// The spacing is only applied between children in the main axis: not before the first child
742    /// or after the last one. When it is non-zero, the layout size will be larger than the sum of
743    /// the children's layout sizes in the main axis, and the children overflow when their sizes
744    /// plus the total spacing exceed the maximum main axis constraint.
745    ///
746    /// Defaults to 0.0.
747    fn spacing(self: RenderHandle<Self>, app: &App) -> f64 {
748        self.flex_data(app).spacing
749    }
750
751    /// Sets [`spacing`](Self::spacing).
752    fn set_spacing(self: RenderHandle<Self>, app: &mut App, value: f64) {
753        debug_assert!(value >= 0.0);
754        if self.flex_data(app).spacing == value {
755            return;
756        }
757        self.flex_data_mut(app).spacing = value;
758        self.mark_needs_layout(app);
759    }
760
761    /// Flutter's `_debugHasNecessaryDirections`: asserts the directions the current alignment
762    /// needs are available. Always returns true. Internal to the flex layout.
763    fn debug_has_necessary_directions(self: RenderHandle<Self>, app: &App) -> bool {
764        if debug_checking_intrinsics() {
765            return true;
766        }
767        let direction = self.direction(app);
768        if self.first_child(app).is_some() && self.last_child(app) != self.first_child(app) {
769            // i.e. there's more than one child
770            if direction == Axis::Horizontal {
771                debug_assert!(
772                    self.text_direction(app).is_some(),
773                    "Horizontal RenderFlex with multiple children has a null textDirection, so \
774                     the layout order is undefined."
775                );
776            }
777        }
778        let main_axis_alignment = self.main_axis_alignment(app);
779        if (main_axis_alignment == MainAxisAlignment::Start
780            || main_axis_alignment == MainAxisAlignment::End)
781            && direction == Axis::Horizontal
782        {
783            debug_assert!(
784                self.text_direction(app).is_some(),
785                "Horizontal RenderFlex with {main_axis_alignment:?} has a null textDirection, so \
786                 the alignment cannot be resolved."
787            );
788        }
789        let cross_axis_alignment = self.cross_axis_alignment(app);
790        if (cross_axis_alignment == CrossAxisAlignment::Start
791            || cross_axis_alignment == CrossAxisAlignment::End)
792            && direction == Axis::Vertical
793        {
794            debug_assert!(
795                self.text_direction(app).is_some(),
796                "Vertical RenderFlex with {cross_axis_alignment:?} has a null textDirection, so \
797                 the alignment cannot be resolved."
798            );
799        }
800        true
801    }
802
803    /// Whether the children are placed on a shared baseline: a [`CrossAxisAlignment::Baseline`]
804    /// along a horizontal main axis. Internal to the flex layout.
805    fn is_baseline_aligned(self: RenderHandle<Self>, app: &App) -> bool {
806        match self.cross_axis_alignment(app) {
807            CrossAxisAlignment::Baseline => match self.direction(app) {
808                Axis::Horizontal => true,
809                Axis::Vertical => false,
810            },
811            CrossAxisAlignment::Start
812            | CrossAxisAlignment::Center
813            | CrossAxisAlignment::End
814            | CrossAxisAlignment::Stretch => false,
815        }
816    }
817
818    /// The extent of `size` along the cross axis. Internal to the flex layout.
819    fn get_cross_size(self: RenderHandle<Self>, app: &App, size: Size) -> f64 {
820        match self.direction(app) {
821            Axis::Horizontal => size.height(),
822            Axis::Vertical => size.width(),
823        }
824    }
825
826    /// The extent of `size` along the main axis. Internal to the flex layout.
827    fn get_main_size(self: RenderHandle<Self>, app: &App, size: Size) -> f64 {
828        match self.direction(app) {
829            Axis::Horizontal => size.width(),
830            Axis::Vertical => size.height(),
831        }
832    }
833
834    /// Whether to lay out left-to-right / top-to-bottom (`false`), or right-to-left /
835    /// bottom-to-top (`true`). `false` when the layout direction does not matter (for instance,
836    /// there is no child). Internal to the flex layout.
837    fn flip_main_axis(self: RenderHandle<Self>, app: &App) -> bool {
838        self.first_child(app).is_some()
839            && match self.direction(app) {
840                Axis::Horizontal => self.text_direction(app) == Some(TextDirection::Rtl),
841                Axis::Vertical => self.vertical_direction(app) == VerticalDirection::Up,
842            }
843    }
844
845    /// The cross axis counterpart of [`flip_main_axis`](Self::flip_main_axis). Internal to the
846    /// flex layout.
847    fn flip_cross_axis(self: RenderHandle<Self>, app: &App) -> bool {
848        self.first_child(app).is_some()
849            && match self.direction(app) {
850                Axis::Vertical => self.text_direction(app) == Some(TextDirection::Rtl),
851                Axis::Horizontal => self.vertical_direction(app) == VerticalDirection::Up,
852            }
853    }
854
855    /// The constraints an inflexible child is laid out with. Internal to the flex layout.
856    fn constraints_for_non_flex_child(
857        self: RenderHandle<Self>,
858        app: &App,
859        constraints: BoxConstraints,
860    ) -> BoxConstraints {
861        let fill_cross_axis = self.cross_axis_alignment(app) == CrossAxisAlignment::Stretch;
862        match self.direction(app) {
863            Axis::Horizontal => {
864                if fill_cross_axis {
865                    BoxConstraints::tight_for(None, Some(constraints.max_height))
866                } else {
867                    BoxConstraints::new().max_height(constraints.max_height)
868                }
869            }
870            Axis::Vertical => {
871                if fill_cross_axis {
872                    BoxConstraints::tight_for(Some(constraints.max_width), None)
873                } else {
874                    BoxConstraints::new().max_width(constraints.max_width)
875                }
876            }
877        }
878    }
879
880    /// The constraints a flexible child is laid out with, given the main axis extent it was
881    /// allocated. Internal to the flex layout.
882    fn constraints_for_flex_child(
883        self: RenderHandle<Self>,
884        app: &App,
885        child: AnyRenderBox,
886        constraints: BoxConstraints,
887        max_child_extent: f64,
888    ) -> BoxConstraints {
889        debug_assert!(get_flex(app, child) > 0);
890        debug_assert!(max_child_extent >= 0.0);
891        let min_child_extent = match get_fit(app, child) {
892            FlexFit::Tight => max_child_extent,
893            FlexFit::Loose => 0.0,
894        };
895        let fill_cross_axis = self.cross_axis_alignment(app) == CrossAxisAlignment::Stretch;
896        match self.direction(app) {
897            Axis::Horizontal => BoxConstraints {
898                min_width: min_child_extent,
899                max_width: max_child_extent,
900                min_height: if fill_cross_axis {
901                    constraints.max_height
902                } else {
903                    0.0
904                },
905                max_height: constraints.max_height,
906            },
907            Axis::Vertical => BoxConstraints {
908                min_width: if fill_cross_axis {
909                    constraints.max_width
910                } else {
911                    0.0
912                },
913                max_width: constraints.max_width,
914                min_height: min_child_extent,
915                max_height: max_child_extent,
916            },
917        }
918    }
919
920    /// Flutter's `_getIntrinsicSize`: the intrinsic extent along `sizing_direction`, given the
921    /// `extent` in the other direction and a `child_size` that measures one child. Internal to
922    /// the flex layout.
923    fn get_intrinsic_size(
924        self: RenderHandle<Self>,
925        app: &mut App,
926        sizing_direction: Axis,
927        extent: f64,
928        child_size: ChildSizingFunction,
929    ) -> f64 {
930        let direction = self.direction(app);
931        if direction == sizing_direction {
932            // INTRINSIC MAIN SIZE
933            // Intrinsic main size is the smallest size the flex container can take while
934            // maintaining the min/max-content contributions of its flex items.
935            let mut total_flex = 0.0;
936            let mut inflexible_space = self.spacing(app) * (self.child_count(app) as f64 - 1.0);
937            let mut max_flex_fraction_so_far: f64 = 0.0;
938            let mut child = self.first_child(app);
939            while let Some(current) = child {
940                let flex = get_flex(app, current);
941                total_flex += f64::from(flex);
942                if flex > 0 {
943                    let flex_fraction = child_size(current, app, extent) / f64::from(flex);
944                    max_flex_fraction_so_far = max_flex_fraction_so_far.max(flex_fraction);
945                } else {
946                    inflexible_space += child_size(current, app, extent);
947                }
948                child = self.child_after(app, current);
949            }
950            return max_flex_fraction_so_far * total_flex + inflexible_space;
951        }
952
953        // INTRINSIC CROSS SIZE
954        // Intrinsic cross size is the max of the intrinsic cross sizes of the children, after
955        // the flexible children are fit into the available space, with the children sized using
956        // their max intrinsic dimensions.
957        let is_horizontal = direction == Axis::Horizontal;
958        let layout_child = |app: &mut App, child: AnyRenderBox, constraints: BoxConstraints| {
959            let main_axis_size_from_constraints = if is_horizontal {
960                constraints.max_width
961            } else {
962                constraints.max_height
963            };
964            // An infinite main_axis_size_from_constraints means this child is flexible (or
965            // extent is infinite).
966            debug_assert_eq!(
967                get_flex(app, child) != 0 && extent.is_finite(),
968                main_axis_size_from_constraints.is_finite()
969            );
970            let max_main_axis_size = if main_axis_size_from_constraints.is_finite() {
971                main_axis_size_from_constraints
972            } else if is_horizontal {
973                child.get_max_intrinsic_width(app, f64::INFINITY)
974            } else {
975                child.get_max_intrinsic_height(app, f64::INFINITY)
976            };
977            let cross_size = child_size(child, app, max_main_axis_size);
978            if is_horizontal {
979                Size::new(max_main_axis_size, cross_size)
980            } else {
981                Size::new(cross_size, max_main_axis_size)
982            }
983        };
984        let constraints = if is_horizontal {
985            BoxConstraints::new().max_width(extent)
986        } else {
987            BoxConstraints::new().max_height(extent)
988        };
989        self.compute_sizes(
990            app,
991            constraints,
992            layout_child,
993            ChildLayoutHelper::get_dry_baseline,
994        )
995        .axis_size
996        .cross_axis_extent()
997    }
998
999    /// Flutter's `_debugCheckConstraints`: the error to report when a child has a non-zero flex
1000    /// factor while the incoming main axis constraint is unbounded and that child would have to
1001    /// fill it, or `None` when the constraints are usable. Internal to the flex layout.
1002    fn debug_check_constraints(
1003        self: RenderHandle<Self>,
1004        app: &App,
1005        constraints: BoxConstraints,
1006    ) -> Option<String> {
1007        let direction = self.direction(app);
1008        let max_main_size = match direction {
1009            Axis::Horizontal => constraints.max_width,
1010            Axis::Vertical => constraints.max_height,
1011        };
1012        let can_flex = max_main_size < f64::INFINITY;
1013        let mut child = self.first_child(app);
1014        while let Some(current) = child {
1015            if get_flex(app, current) > 0
1016                && !can_flex
1017                && (self.main_axis_size(app) == MainAxisSize::Max
1018                    || get_fit(app, current) == FlexFit::Tight)
1019            {
1020                let (identity, axis, dimension) = match direction {
1021                    Axis::Horizontal => ("row", "horizontal", "width"),
1022                    Axis::Vertical => ("column", "vertical", "height"),
1023                };
1024                return Some(format!(
1025                    "RenderFlex children have non-zero flex but incoming {dimension} constraints \
1026                     are unbounded.\n\
1027                     When a {identity} is in a parent that does not provide a finite {dimension} \
1028                     constraint, for example if it is in a {axis} scrollable, it will try to \
1029                     shrink-wrap its children along the {axis} axis. Setting a flex on a child \
1030                     (e.g. using Expanded) indicates that the child is to expand to fill the \
1031                     remaining space in the {axis} direction.\n\
1032                     These two directives are mutually exclusive. If a parent is to shrink-wrap \
1033                     its child, the child cannot simultaneously expand to fit its parent.\n\
1034                     Consider setting mainAxisSize to MainAxisSize.min and using FlexFit.loose \
1035                     fits for the flexible children (using Flexible rather than Expanded). This \
1036                     will allow the flexible children to size themselves to less than the \
1037                     infinite remaining space they would otherwise be forced to take, and then \
1038                     will cause the RenderFlex to shrink-wrap the children rather than expanding \
1039                     to fit the maximum constraints provided by the parent."
1040                ));
1041            }
1042            child = self.child_after(app, current);
1043        }
1044        None
1045    }
1046
1047    /// Flutter's `_computeSizes`: measures the children with `layout_child`, reads their
1048    /// baselines with `get_baseline` when they are baseline aligned, and works out this flex's
1049    /// own size. Internal to the flex layout.
1050    ///
1051    /// # Panics
1052    ///
1053    /// If the children are baseline aligned and [`text_baseline`](Self::text_baseline) is `None`.
1054    fn compute_sizes(
1055        self: RenderHandle<Self>,
1056        app: &mut App,
1057        constraints: BoxConstraints,
1058        mut layout_child: impl FnMut(&mut App, AnyRenderBox, BoxConstraints) -> Size,
1059        get_baseline: ChildBaselineGetter,
1060    ) -> LayoutSizes {
1061        debug_assert!(self.debug_has_necessary_directions(app));
1062
1063        // Determine used flex factor, size inflexible items, calculate free space.
1064        let direction = self.direction(app);
1065        let max_main_size = self.get_main_size(app, constraints.biggest());
1066        let can_flex = max_main_size.is_finite();
1067        let non_flex_child_constraints = self.constraints_for_non_flex_child(app, constraints);
1068        // None indicates the children are not baseline aligned.
1069        let text_baseline = if self.is_baseline_aligned(app) {
1070            Some(self.text_baseline(app).expect(
1071                "To use CrossAxisAlignment.baseline, you must also specify which baseline to use \
1072                 using the \"textBaseline\" argument.",
1073            ))
1074        } else {
1075            None
1076        };
1077
1078        // The first pass lays out non-flex children and computes total flex.
1079        let mut total_flex = 0;
1080        let mut first_flex_child: Option<AnyRenderBox> = None;
1081        let mut accumulated_ascent_descent = AscentDescent::NONE;
1082        // Initially, accumulated_size is the sum of the spaces between children in the main axis.
1083        let mut accumulated_size = AxisSize::new(
1084            self.spacing(app) * (self.child_count(app) as f64 - 1.0),
1085            0.0,
1086        );
1087        let mut child = self.first_child(app);
1088        while let Some(current) = child {
1089            let flex = get_flex(app, current);
1090            if can_flex && flex > 0 {
1091                total_flex += flex;
1092                first_flex_child.get_or_insert(current);
1093            } else {
1094                let child_size = AxisSize::from_size(
1095                    layout_child(app, current, non_flex_child_constraints),
1096                    direction,
1097                );
1098                accumulated_size = accumulated_size + child_size;
1099                // Baseline-aligned children contributes to the cross axis extent separately.
1100                let baseline_offset = text_baseline.and_then(|text_baseline| {
1101                    get_baseline(app, current, non_flex_child_constraints, text_baseline)
1102                });
1103                accumulated_ascent_descent = accumulated_ascent_descent
1104                    + AscentDescent::new(baseline_offset, child_size.cross_axis_extent());
1105            }
1106            child = self.child_after(app, current);
1107        }
1108
1109        debug_assert_eq!(total_flex == 0, first_flex_child.is_none());
1110        // If we are given infinite space there's no need for this extra step.
1111        debug_assert!(first_flex_child.is_none() || can_flex);
1112
1113        // The second pass distributes free space to flexible children.
1114        let flex_space = (max_main_size - accumulated_size.main_axis_extent()).max(0.0);
1115        let space_per_flex = flex_space / total_flex as f64;
1116        let mut child = first_flex_child;
1117        while let Some(current) = child {
1118            if total_flex == 0 {
1119                break;
1120            }
1121            let flex = get_flex(app, current);
1122            if flex != 0 {
1123                total_flex -= flex;
1124                debug_assert!(space_per_flex.is_finite());
1125                let max_child_extent = space_per_flex * f64::from(flex);
1126                debug_assert!(
1127                    get_fit(app, current) == FlexFit::Loose || max_child_extent < f64::INFINITY
1128                );
1129                let child_constraints =
1130                    self.constraints_for_flex_child(app, current, constraints, max_child_extent);
1131                let child_size =
1132                    AxisSize::from_size(layout_child(app, current, child_constraints), direction);
1133                accumulated_size = accumulated_size + child_size;
1134                let baseline_offset = text_baseline.and_then(|text_baseline| {
1135                    get_baseline(app, current, child_constraints, text_baseline)
1136                });
1137                accumulated_ascent_descent = accumulated_ascent_descent
1138                    + AscentDescent::new(baseline_offset, child_size.cross_axis_extent());
1139            }
1140            child = self.child_after(app, current);
1141        }
1142        debug_assert_eq!(total_flex, 0);
1143
1144        // The overall height of baseline-aligned children contributes to the cross axis extent.
1145        accumulated_size = accumulated_size
1146            + match accumulated_ascent_descent.0 {
1147                None => AxisSize::EMPTY,
1148                Some((ascent, descent)) => AxisSize::new(0.0, ascent + descent),
1149            };
1150
1151        let ideal_main_size = match self.main_axis_size(app) {
1152            MainAxisSize::Max if max_main_size.is_finite() => max_main_size,
1153            MainAxisSize::Max | MainAxisSize::Min => accumulated_size.main_axis_extent(),
1154        };
1155
1156        let constrained_size = AxisSize::new(ideal_main_size, accumulated_size.cross_axis_extent())
1157            .apply_constraints(constraints, direction);
1158        let space_per_flex = first_flex_child.map(|_| space_per_flex);
1159        debug_assert!(space_per_flex.is_none_or(f64::is_finite));
1160        LayoutSizes {
1161            axis_size: constrained_size,
1162            main_axis_free_space: constrained_size.main_axis_extent()
1163                - accumulated_size.main_axis_extent(),
1164            baseline_offset: accumulated_ascent_descent.baseline_offset(),
1165            space_per_flex,
1166        }
1167    }
1168
1169    /// Sizes this flex and positions its children; see [`RenderFlex`] for the algorithm.
1170    fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
1171        let constraints = self.constraints(app);
1172        if cfg!(debug_assertions)
1173            && let Some(error) = self.debug_check_constraints(app, constraints)
1174        {
1175            panic!("{error}");
1176        }
1177
1178        let sizes = self.compute_sizes(
1179            app,
1180            constraints,
1181            ChildLayoutHelper::layout_child,
1182            ChildLayoutHelper::get_baseline,
1183        );
1184
1185        let direction = self.direction(app);
1186        let cross_axis_extent = sizes.axis_size.cross_axis_extent();
1187        self.set_size(app, sizes.axis_size.to_size(direction));
1188        self.flex_data_mut(app).overflow = (-sizes.main_axis_free_space).max(0.0);
1189
1190        let remaining_space = sizes.main_axis_free_space.max(0.0);
1191        let flip_main_axis = self.flip_main_axis(app);
1192        let flip_cross_axis = self.flip_cross_axis(app);
1193        let cross_axis_alignment = self.cross_axis_alignment(app);
1194        let (leading_space, between_space) = self.main_axis_alignment(app).distribute_space(
1195            remaining_space,
1196            self.child_count(app),
1197            flip_main_axis,
1198            self.spacing(app),
1199        );
1200        let top_left_child = if flip_main_axis {
1201            self.last_child(app)
1202        } else {
1203            self.first_child(app)
1204        };
1205        let baseline_offset = sizes.baseline_offset;
1206        debug_assert!(
1207            baseline_offset.is_none()
1208                || (cross_axis_alignment == CrossAxisAlignment::Baseline
1209                    && direction == Axis::Horizontal)
1210        );
1211        let text_baseline = self.text_baseline(app);
1212
1213        // Position all children in visual order: starting from the top-left child and work
1214        // towards the child that's farthest away from the origin.
1215        let mut child_main_position = leading_space;
1216        let mut child = top_left_child;
1217        while let Some(current) = child {
1218            let child_baseline_offset = match baseline_offset {
1219                Some(_) => current.get_distance_to_baseline(
1220                    app,
1221                    text_baseline.expect("a baseline-aligned flex has a text baseline"),
1222                    true,
1223                ),
1224                None => None,
1225            };
1226            let child_size = current.size(app);
1227            let free_cross_space = cross_axis_extent - self.get_cross_size(app, child_size);
1228            let child_cross_position = if let (Some(baseline_offset), Some(child_baseline_offset)) =
1229                (baseline_offset, child_baseline_offset)
1230            {
1231                baseline_offset - child_baseline_offset
1232            } else if cross_axis_alignment == CrossAxisAlignment::Baseline
1233                && direction == Axis::Horizontal
1234            {
1235                // In a baseline-aligned horizontal flex, a child without a baseline is aligned to
1236                // the top of the cross axis (dy = 0), regardless of verticalDirection or
1237                // crossAxisAlignment. That is, we intentionally ignore flip_cross_axis here.
1238                CrossAxisAlignment::Start.get_child_cross_axis_offset(free_cross_space, false)
1239            } else {
1240                // Non-baseline alignment respects the configured cross_axis_alignment.
1241                cross_axis_alignment.get_child_cross_axis_offset(free_cross_space, flip_cross_axis)
1242            };
1243            let offset = match direction {
1244                Axis::Horizontal => Offset::new(child_main_position, child_cross_position),
1245                Axis::Vertical => Offset::new(child_cross_position, child_main_position),
1246            };
1247            current
1248                .parent_data_of_mut::<FlexParentData>(app)
1249                .set_offset(offset);
1250            child_main_position += self.get_main_size(app, child_size) + between_space;
1251            child = if flip_main_axis {
1252                self.child_before(app, current)
1253            } else {
1254                self.child_after(app, current)
1255            };
1256        }
1257    }
1258
1259    /// Paints the children, clipping them to this flex when they overflow the main axis.
1260    fn paint(
1261        self: RenderHandle<Self>,
1262        app: &mut App,
1263        context: &mut PaintingContext,
1264        offset: Offset,
1265    ) {
1266        if !self.has_overflow(app) {
1267            self.default_paint(app, context, offset);
1268            return;
1269        }
1270
1271        // There's no point in drawing the children if we're empty.
1272        let size = self.size(app);
1273        if size.is_empty() {
1274            return;
1275        }
1276
1277        let clip_behavior = self.clip_behavior(app);
1278        let old = self.flex_data(app).clip_rect_layer.layer();
1279        let layer = context.push_clip_rect(
1280            app,
1281            self.as_object().needs_compositing(app),
1282            offset,
1283            Offset::ZERO & size,
1284            |app, context, offset| self.default_paint(app, context, offset),
1285            clip_behavior,
1286            old,
1287        );
1288        LayerHandle::set_layer(
1289            app,
1290            |app| &mut self.flex_data_mut(app).clip_rect_layer,
1291            layer,
1292        );
1293    }
1294
1295    /// Flutter's `dispose`: drop the clip layer handle, then `super.dispose()`.
1296    fn dispose(self: RenderHandle<Self>, app: &mut App) {
1297        LayerHandle::set_layer(
1298            app,
1299            |app| &mut self.flex_data_mut(app).clip_rect_layer,
1300            None,
1301        );
1302        crate::object::RenderObjectBase::dispose(self, app);
1303    }
1304
1305    /// A flex child gets a [`FlexParentData`].
1306    fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
1307        if !child.parent_data_is::<FlexParentData>(app) {
1308            child.set_parent_data(app, FlexParentData::new());
1309        }
1310    }
1311
1312    /// The minimum intrinsic width of the children laid out in a flex.
1313    fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1314        self.get_intrinsic_size(
1315            app,
1316            Axis::Horizontal,
1317            height,
1318            AnyRenderBox::get_min_intrinsic_width,
1319        )
1320    }
1321
1322    /// The maximum intrinsic width of the children laid out in a flex.
1323    fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1324        self.get_intrinsic_size(
1325            app,
1326            Axis::Horizontal,
1327            height,
1328            AnyRenderBox::get_max_intrinsic_width,
1329        )
1330    }
1331
1332    /// The minimum intrinsic height of the children laid out in a flex.
1333    fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1334        self.get_intrinsic_size(
1335            app,
1336            Axis::Vertical,
1337            width,
1338            AnyRenderBox::get_min_intrinsic_height,
1339        )
1340    }
1341
1342    /// The maximum intrinsic height of the children laid out in a flex.
1343    fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1344        self.get_intrinsic_size(
1345            app,
1346            Axis::Vertical,
1347            width,
1348            AnyRenderBox::get_max_intrinsic_height,
1349        )
1350    }
1351
1352    /// A row's highest child baseline, or a column's first child baseline.
1353    fn compute_distance_to_actual_baseline(
1354        self: RenderHandle<Self>,
1355        app: &mut App,
1356        baseline: TextBaseline,
1357    ) -> Option<f64> {
1358        match self.direction(app) {
1359            Axis::Horizontal => {
1360                self.default_compute_distance_to_highest_actual_baseline(app, baseline)
1361            }
1362            Axis::Vertical => self.default_compute_distance_to_first_actual_baseline(app, baseline),
1363        }
1364    }
1365
1366    /// The baseline [`compute_distance_to_actual_baseline`](Self::compute_distance_to_actual_baseline)
1367    /// would answer, measuring the children dry; a baseline-aligned flex answers its shared
1368    /// baseline.
1369    fn compute_dry_baseline(
1370        self: RenderHandle<Self>,
1371        app: &mut App,
1372        constraints: BoxConstraints,
1373        baseline: TextBaseline,
1374    ) -> Option<f64> {
1375        let sizes = self.compute_sizes(
1376            app,
1377            constraints,
1378            ChildLayoutHelper::dry_layout_child,
1379            ChildLayoutHelper::get_dry_baseline,
1380        );
1381
1382        if self.is_baseline_aligned(app) {
1383            return sizes.baseline_offset;
1384        }
1385
1386        // For non-baseline-aligned, match the logic of compute_distance_to_actual_baseline
1387        // exactly.
1388        match self.direction(app) {
1389            Axis::Horizontal => {
1390                self.compute_dry_distance_to_highest_baseline(app, constraints, baseline, &sizes)
1391            }
1392            Axis::Vertical => {
1393                self.compute_dry_distance_to_first_baseline(app, constraints, baseline, &sizes)
1394            }
1395        }
1396    }
1397
1398    /// Dry layout equivalent of
1399    /// [`default_compute_distance_to_highest_actual_baseline`](RenderBoxContainerDefaultsMixin::default_compute_distance_to_highest_actual_baseline).
1400    /// Internal to the flex layout.
1401    fn compute_dry_distance_to_highest_baseline(
1402        self: RenderHandle<Self>,
1403        app: &mut App,
1404        constraints: BoxConstraints,
1405        baseline: TextBaseline,
1406        sizes: &LayoutSizes,
1407    ) -> Option<f64> {
1408        // Simulate the exact logic from default_compute_distance_to_highest_actual_baseline.
1409        // The key insight: the actual method uses the child's parent data offset.dy, so we need
1410        // to simulate that.
1411
1412        let non_flex_constraints = self.constraints_for_non_flex_child(app, constraints);
1413        let space_per_flex = sizes.space_per_flex;
1414        let constraints_for_child = |app: &App, child: AnyRenderBox| {
1415            let flex = get_flex(app, child);
1416            match space_per_flex {
1417                Some(space_per_flex) if flex > 0 => self.constraints_for_flex_child(
1418                    app,
1419                    child,
1420                    constraints,
1421                    f64::from(flex) * space_per_flex,
1422                ),
1423                _ => non_flex_constraints,
1424            }
1425        };
1426
1427        // Simulate the layout positioning logic from perform_layout.
1428        let flip_main_axis = self.flip_main_axis(app);
1429        let flip_cross_axis = self.flip_cross_axis(app);
1430        let top_left_child = if flip_main_axis {
1431            self.last_child(app)
1432        } else {
1433            self.first_child(app)
1434        };
1435        let is_baseline_aligned = self.is_baseline_aligned(app);
1436        let text_baseline = self.text_baseline(app);
1437        let baseline_offset = if is_baseline_aligned && text_baseline.is_some() {
1438            sizes.baseline_offset
1439        } else {
1440            None
1441        };
1442        let cross_axis_alignment = self.cross_axis_alignment(app);
1443        let direction = self.direction(app);
1444
1445        let mut min_baseline = BaselineOffset::NO_BASELINE;
1446
1447        let mut child = top_left_child;
1448        while let Some(current) = child {
1449            let child_constraints = constraints_for_child(app, current);
1450            let child_baseline = current.get_dry_baseline(app, child_constraints, baseline);
1451            if let Some(child_baseline) = child_baseline {
1452                // Calculate cross-axis position to match perform_layout exactly.
1453                let child_baseline_offset = match text_baseline {
1454                    Some(text_baseline) if is_baseline_aligned => {
1455                        current.get_dry_baseline(app, child_constraints, text_baseline)
1456                    }
1457                    _ => None,
1458                };
1459
1460                let child_cross_position =
1461                    if let (Some(baseline_offset), Some(child_baseline_offset)) =
1462                        (baseline_offset, child_baseline_offset)
1463                    {
1464                        baseline_offset - child_baseline_offset
1465                    } else if cross_axis_alignment == CrossAxisAlignment::Baseline
1466                        && direction == Axis::Horizontal
1467                    {
1468                        // Baseline mode with a child that lacks a baseline: align to the top
1469                        // (dy = 0), regardless of vertical_direction. Ignore flip_cross_axis here to
1470                        // match perform_layout.
1471                        let child_size = current.get_dry_layout(app, child_constraints);
1472                        CrossAxisAlignment::Start.get_child_cross_axis_offset(
1473                            sizes.axis_size.cross_axis_extent()
1474                                - self.get_cross_size(app, child_size),
1475                            false,
1476                        )
1477                    } else {
1478                        // Non-baseline: respect configured cross_axis_alignment.
1479                        let child_size = current.get_dry_layout(app, child_constraints);
1480                        cross_axis_alignment.get_child_cross_axis_offset(
1481                            sizes.axis_size.cross_axis_extent()
1482                                - self.get_cross_size(app, child_size),
1483                            flip_cross_axis,
1484                        )
1485                    };
1486
1487                // For horizontal flex: offset.dy = child_cross_position. This simulates the
1488                // child's parent data offset.dy from the actual method.
1489                let candidate = BaselineOffset(Some(child_baseline)) + child_cross_position;
1490                min_baseline = min_baseline.min_of(candidate);
1491            }
1492
1493            // Main-axis advancement is irrelevant for highest-baseline computation.
1494            child = if flip_main_axis {
1495                self.child_before(app, current)
1496            } else {
1497                self.child_after(app, current)
1498            };
1499        }
1500
1501        min_baseline.offset()
1502    }
1503
1504    /// Dry layout equivalent of
1505    /// [`default_compute_distance_to_first_actual_baseline`](RenderBoxContainerDefaultsMixin::default_compute_distance_to_first_actual_baseline).
1506    /// Internal to the flex layout.
1507    fn compute_dry_distance_to_first_baseline(
1508        self: RenderHandle<Self>,
1509        app: &mut App,
1510        constraints: BoxConstraints,
1511        baseline: TextBaseline,
1512        sizes: &LayoutSizes,
1513    ) -> Option<f64> {
1514        // We must mimic default_compute_distance_to_first_actual_baseline which walks children
1515        // in child-list order (not painting order) but uses actual offsets.
1516        let non_flex_constraints = self.constraints_for_non_flex_child(app, constraints);
1517        let space_per_flex = sizes.space_per_flex;
1518        let constraints_for_child = |app: &App, child: AnyRenderBox| {
1519            let flex = get_flex(app, child);
1520            match space_per_flex {
1521                Some(space_per_flex) if flex > 0 => self.constraints_for_flex_child(
1522                    app,
1523                    child,
1524                    constraints,
1525                    f64::from(flex) * space_per_flex,
1526                ),
1527                _ => non_flex_constraints,
1528            }
1529        };
1530
1531        // First, compute each child's main-axis position in painting order.
1532        let remaining_space = sizes.main_axis_free_space.max(0.0);
1533        let flip_main_axis = self.flip_main_axis(app);
1534        let (leading_space, between_space) = self.main_axis_alignment(app).distribute_space(
1535            remaining_space,
1536            self.child_count(app),
1537            flip_main_axis,
1538            self.spacing(app),
1539        );
1540
1541        let mut main_positions: HashMap<AnyRenderBox, f64> = HashMap::new();
1542        let start_child = if flip_main_axis {
1543            self.last_child(app)
1544        } else {
1545            self.first_child(app)
1546        };
1547        let mut pos = leading_space;
1548        let mut child = start_child;
1549        while let Some(current) = child {
1550            main_positions.insert(current, pos);
1551            let cc = constraints_for_child(app, current);
1552            let cs = current.get_dry_layout(app, cc);
1553            pos += self.get_main_size(app, cs) + between_space;
1554            child = if flip_main_axis {
1555                self.child_before(app, current)
1556            } else {
1557                self.child_after(app, current)
1558            };
1559        }
1560
1561        // Then, find the first child with a baseline in child-list order and return its
1562        // baseline + position.
1563        let mut child = self.first_child(app);
1564        while let Some(current) = child {
1565            let cc = constraints_for_child(app, current);
1566            if let Some(child_baseline) = current.get_dry_baseline(app, cc, baseline) {
1567                let position = main_positions.get(&current).copied();
1568                // If somehow missing (no children), fall back to leading_space.
1569                return Some(child_baseline + position.unwrap_or(leading_space));
1570            }
1571            child = self.child_after(app, current);
1572        }
1573        None
1574    }
1575
1576    /// The size this flex would take, measuring the children dry.
1577    fn compute_dry_layout(
1578        self: RenderHandle<Self>,
1579        app: &mut App,
1580        constraints: BoxConstraints,
1581    ) -> Size {
1582        if cfg!(debug_assertions)
1583            && let Some(error) = self.debug_check_constraints(app, constraints)
1584        {
1585            self.debug_cannot_compute_dry_layout(DryLayoutFailure::Error(&error));
1586            return Size::ZERO;
1587        }
1588        let direction = self.direction(app);
1589        self.compute_sizes(
1590            app,
1591            constraints,
1592            ChildLayoutHelper::dry_layout_child,
1593            ChildLayoutHelper::get_dry_baseline,
1594        )
1595        .axis_size
1596        .to_size(direction)
1597    }
1598
1599    /// Hit tests the children in paint order.
1600    fn hit_test_children(
1601        self: RenderHandle<Self>,
1602        app: &mut App,
1603        result: &mut BoxHitTestResult<'_>,
1604        position: Offset,
1605    ) -> bool {
1606        self.default_hit_test_children(app, result, position)
1607    }
1608}
1609
1610/// Flutter's `RenderFlex._getFlex`.
1611fn get_flex(app: &App, child: AnyRenderBox) -> i32 {
1612    child
1613        .as_object()
1614        .parent_data_of::<FlexParentData>(app)
1615        .flex
1616        .unwrap_or(0)
1617}
1618
1619/// Flutter's `RenderFlex._getFit`.
1620fn get_fit(app: &App, child: AnyRenderBox) -> FlexFit {
1621    child
1622        .as_object()
1623        .parent_data_of::<FlexParentData>(app)
1624        .fit
1625        .unwrap_or(FlexFit::Tight)
1626}
1627
1628/// Displays its children in a one-dimensional array.
1629///
1630/// ## Layout algorithm
1631///
1632/// _This section describes how the framework causes [`RenderFlex`] to position its children._
1633/// _See [`BoxConstraints`] for an introduction to box layout models._
1634///
1635/// Layout for a [`RenderFlex`] proceeds in six steps:
1636///
1637/// 1. Layout each child with a null or zero flex factor with unbounded main axis constraints and
1638///    the incoming cross axis constraints. If the [`cross_axis_alignment`](Self::cross_axis_alignment)
1639///    is [`CrossAxisAlignment::Stretch`], instead use tight cross axis constraints that match the
1640///    incoming max extent in the cross axis.
1641/// 2. Divide the remaining main axis space among the children with non-zero flex factors
1642///    according to their flex factor. For example, a child with a flex factor of 2.0 will receive
1643///    twice the amount of main axis space as a child with a flex factor of 1.0.
1644/// 3. Layout each of the remaining children with the same cross axis constraints as in step 1,
1645///    but instead of using unbounded main axis constraints, use max axis constraints based on the
1646///    amount of space allocated in step 2. Children with [`FlexFit::Tight`] are given tight
1647///    constraints (i.e., forced to fill the allocated space), and children with [`FlexFit::Loose`]
1648///    are given loose constraints (i.e., not forced to fill the allocated space).
1649/// 4. The cross axis extent of the [`RenderFlex`] is the maximum cross axis extent of the
1650///    children (which will always satisfy the incoming constraints).
1651/// 5. The main axis extent of the [`RenderFlex`] is determined by the
1652///    [`main_axis_size`](Self::main_axis_size) property. If it is [`MainAxisSize::Max`], then the
1653///    main axis extent of the [`RenderFlex`] is the max extent of the incoming main axis
1654///    constraints. If it is [`MainAxisSize::Min`], then the main axis extent is the sum of the
1655///    main axis extents of the children (subject to the incoming constraints).
1656/// 6. Determine the position for each child according to the
1657///    [`main_axis_alignment`](Self::main_axis_alignment) and the
1658///    [`cross_axis_alignment`](Self::cross_axis_alignment). For example, if the
1659///    [`main_axis_alignment`](Self::main_axis_alignment) is [`MainAxisAlignment::SpaceBetween`],
1660///    any main axis space that has not been allocated to children is divided evenly and placed
1661///    between the children.
1662pub struct RenderFlex {
1663    render_object: RenderObjectData,
1664    render_box: RenderBoxData,
1665    container: ContainerRenderObjectData<AnyRenderBox>,
1666    flex: RenderFlexData,
1667}
1668
1669impl RenderFlex {
1670    /// Creates a flex render object.
1671    ///
1672    /// By default, the flex layout is horizontal and children are aligned to the start of the
1673    /// main axis and the center of the cross axis. Add children with
1674    /// [`add`](ContainerRenderObjectMixin::add) or
1675    /// [`add_all`](ContainerRenderObjectMixin::add_all); Dart's constructor takes them.
1676    pub fn new(app: &mut App) -> RenderHandle<RenderFlex> {
1677        RenderHandle::new_box(
1678            app,
1679            RenderFlex {
1680                render_object: RenderObjectData::new(),
1681                render_box: RenderBoxData::new(),
1682                container: ContainerRenderObjectData::new(),
1683                flex: RenderFlexData::new(),
1684            },
1685        )
1686    }
1687
1688    /// See [`RenderFlexMixin::direction`].
1689    pub fn direction(self: RenderHandle<Self>, app: &App) -> Axis {
1690        RenderFlexMixin::direction(self, app)
1691    }
1692
1693    /// See [`RenderFlexMixin::set_direction`].
1694    pub fn set_direction(self: RenderHandle<Self>, app: &mut App, value: Axis) {
1695        RenderFlexMixin::set_direction(self, app, value)
1696    }
1697
1698    /// See [`RenderFlexMixin::main_axis_alignment`].
1699    pub fn main_axis_alignment(self: RenderHandle<Self>, app: &App) -> MainAxisAlignment {
1700        RenderFlexMixin::main_axis_alignment(self, app)
1701    }
1702
1703    /// See [`RenderFlexMixin::set_main_axis_alignment`].
1704    pub fn set_main_axis_alignment(
1705        self: RenderHandle<Self>,
1706        app: &mut App,
1707        value: MainAxisAlignment,
1708    ) {
1709        RenderFlexMixin::set_main_axis_alignment(self, app, value)
1710    }
1711
1712    /// See [`RenderFlexMixin::main_axis_size`].
1713    pub fn main_axis_size(self: RenderHandle<Self>, app: &App) -> MainAxisSize {
1714        RenderFlexMixin::main_axis_size(self, app)
1715    }
1716
1717    /// See [`RenderFlexMixin::set_main_axis_size`].
1718    pub fn set_main_axis_size(self: RenderHandle<Self>, app: &mut App, value: MainAxisSize) {
1719        RenderFlexMixin::set_main_axis_size(self, app, value)
1720    }
1721
1722    /// See [`RenderFlexMixin::cross_axis_alignment`].
1723    pub fn cross_axis_alignment(self: RenderHandle<Self>, app: &App) -> CrossAxisAlignment {
1724        RenderFlexMixin::cross_axis_alignment(self, app)
1725    }
1726
1727    /// See [`RenderFlexMixin::set_cross_axis_alignment`].
1728    pub fn set_cross_axis_alignment(
1729        self: RenderHandle<Self>,
1730        app: &mut App,
1731        value: CrossAxisAlignment,
1732    ) {
1733        RenderFlexMixin::set_cross_axis_alignment(self, app, value)
1734    }
1735
1736    /// See [`RenderFlexMixin::text_direction`].
1737    pub fn text_direction(self: RenderHandle<Self>, app: &App) -> Option<TextDirection> {
1738        RenderFlexMixin::text_direction(self, app)
1739    }
1740
1741    /// See [`RenderFlexMixin::set_text_direction`].
1742    pub fn set_text_direction(
1743        self: RenderHandle<Self>,
1744        app: &mut App,
1745        value: Option<TextDirection>,
1746    ) {
1747        RenderFlexMixin::set_text_direction(self, app, value)
1748    }
1749
1750    /// See [`RenderFlexMixin::vertical_direction`].
1751    pub fn vertical_direction(self: RenderHandle<Self>, app: &App) -> VerticalDirection {
1752        RenderFlexMixin::vertical_direction(self, app)
1753    }
1754
1755    /// See [`RenderFlexMixin::set_vertical_direction`].
1756    pub fn set_vertical_direction(
1757        self: RenderHandle<Self>,
1758        app: &mut App,
1759        value: VerticalDirection,
1760    ) {
1761        RenderFlexMixin::set_vertical_direction(self, app, value)
1762    }
1763
1764    /// See [`RenderFlexMixin::text_baseline`].
1765    pub fn text_baseline(self: RenderHandle<Self>, app: &App) -> Option<TextBaseline> {
1766        RenderFlexMixin::text_baseline(self, app)
1767    }
1768
1769    /// See [`RenderFlexMixin::set_text_baseline`].
1770    pub fn set_text_baseline(self: RenderHandle<Self>, app: &mut App, value: Option<TextBaseline>) {
1771        RenderFlexMixin::set_text_baseline(self, app, value)
1772    }
1773
1774    /// See [`RenderFlexMixin::clip_behavior`].
1775    pub fn clip_behavior(self: RenderHandle<Self>, app: &App) -> Clip {
1776        RenderFlexMixin::clip_behavior(self, app)
1777    }
1778
1779    /// See [`RenderFlexMixin::set_clip_behavior`].
1780    pub fn set_clip_behavior(self: RenderHandle<Self>, app: &mut App, value: Clip) {
1781        RenderFlexMixin::set_clip_behavior(self, app, value)
1782    }
1783
1784    /// See [`RenderFlexMixin::spacing`].
1785    pub fn spacing(self: RenderHandle<Self>, app: &App) -> f64 {
1786        RenderFlexMixin::spacing(self, app)
1787    }
1788
1789    /// See [`RenderFlexMixin::set_spacing`].
1790    pub fn set_spacing(self: RenderHandle<Self>, app: &mut App, value: f64) {
1791        RenderFlexMixin::set_spacing(self, app, value)
1792    }
1793}
1794
1795impl ContainerRenderObjectMixin for RenderFlex {
1796    type ChildType = AnyRenderBox;
1797    type ParentDataType = FlexParentData;
1798
1799    fn container_data(
1800        self: RenderHandle<Self>,
1801        app: &App,
1802    ) -> &ContainerRenderObjectData<AnyRenderBox> {
1803        &self.get(app).container
1804    }
1805
1806    fn container_data_mut(
1807        self: RenderHandle<Self>,
1808        app: &mut App,
1809    ) -> &mut ContainerRenderObjectData<AnyRenderBox> {
1810        &mut self.get_mut(app).container
1811    }
1812}
1813
1814impl RenderBoxContainerDefaultsMixin for RenderFlex {}
1815
1816impl RenderFlexMixin for RenderFlex {
1817    fn flex_data(self: RenderHandle<Self>, app: &App) -> &RenderFlexData {
1818        &self.get(app).flex
1819    }
1820
1821    fn flex_data_mut(self: RenderHandle<Self>, app: &mut App) -> &mut RenderFlexData {
1822        &mut self.get_mut(app).flex
1823    }
1824}
1825
1826impl RenderObject for RenderFlex {
1827    crate::render_object_accessors!();
1828
1829    fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
1830        RenderFlexMixin::perform_layout(self, app)
1831    }
1832
1833    fn visit_children(
1834        self: RenderHandle<Self>,
1835        app: &App,
1836        visitor: &mut dyn FnMut(AnyRenderObject),
1837    ) {
1838        ContainerRenderObjectMixin::visit_children(self, app, visitor)
1839    }
1840
1841    fn did_attach(self: RenderHandle<Self>, app: &mut App, owner: Handle<PipelineOwner>) {
1842        ContainerRenderObjectMixin::did_attach(self, app, owner)
1843    }
1844
1845    fn did_detach(self: RenderHandle<Self>, app: &mut App) {
1846        ContainerRenderObjectMixin::did_detach(self, app)
1847    }
1848
1849    fn redepth_children(self: RenderHandle<Self>, app: &mut App) {
1850        ContainerRenderObjectMixin::redepth_children(self, app)
1851    }
1852
1853    fn paint(
1854        self: RenderHandle<Self>,
1855        app: &mut App,
1856        context: &mut PaintingContext,
1857        offset: Offset,
1858    ) {
1859        RenderFlexMixin::paint(self, app, context, offset)
1860    }
1861
1862    fn dispose(self: RenderHandle<Self>, app: &mut App) {
1863        RenderFlexMixin::dispose(self, app)
1864    }
1865}
1866
1867impl RenderBox for RenderFlex {
1868    crate::render_box_accessors!();
1869
1870    fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
1871        RenderFlexMixin::setup_parent_data(self, app, child)
1872    }
1873
1874    fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1875        RenderFlexMixin::compute_min_intrinsic_width(self, app, height)
1876    }
1877
1878    fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1879        RenderFlexMixin::compute_max_intrinsic_width(self, app, height)
1880    }
1881
1882    fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1883        RenderFlexMixin::compute_min_intrinsic_height(self, app, width)
1884    }
1885
1886    fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1887        RenderFlexMixin::compute_max_intrinsic_height(self, app, width)
1888    }
1889
1890    fn compute_distance_to_actual_baseline(
1891        self: RenderHandle<Self>,
1892        app: &mut App,
1893        baseline: TextBaseline,
1894    ) -> Option<f64> {
1895        RenderFlexMixin::compute_distance_to_actual_baseline(self, app, baseline)
1896    }
1897
1898    fn compute_dry_baseline(
1899        self: RenderHandle<Self>,
1900        app: &mut App,
1901        constraints: BoxConstraints,
1902        baseline: TextBaseline,
1903    ) -> Option<f64> {
1904        RenderFlexMixin::compute_dry_baseline(self, app, constraints, baseline)
1905    }
1906
1907    fn compute_dry_layout(
1908        self: RenderHandle<Self>,
1909        app: &mut App,
1910        constraints: BoxConstraints,
1911    ) -> Size {
1912        RenderFlexMixin::compute_dry_layout(self, app, constraints)
1913    }
1914
1915    fn hit_test_children(
1916        self: RenderHandle<Self>,
1917        app: &mut App,
1918        result: &mut BoxHitTestResult<'_>,
1919        position: Offset,
1920    ) -> bool {
1921        RenderFlexMixin::hit_test_children(self, app, result, position)
1922    }
1923}
1924
1925#[cfg(test)]
1926mod tests {
1927    use inset_foundation::AppCell;
1928    use std::any::Any;
1929
1930    use inset_foundation::Handle;
1931    use inset_gestures::{HitTestEntry, HitTestResult};
1932
1933    use super::*;
1934    use crate::box_::BoxHitTestEntry;
1935    use crate::layer::{ContainerLayer, ErasedLayer, OffsetLayer, PictureLayer};
1936    use crate::pipeline_owner::PipelineOwner;
1937    use crate::proxy_box::RenderRepaintBoundary;
1938    use inset_embedder::valo::Op;
1939
1940    /// A leaf that takes its preferred size where the constraints allow, and answers hit tests
1941    /// within its bounds.
1942    struct SizedTestBox {
1943        render_object: RenderObjectData,
1944        render_box: RenderBoxData,
1945        preferred_size: Size,
1946    }
1947
1948    impl RenderObject for SizedTestBox {
1949        crate::render_object_accessors!();
1950
1951        fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
1952            let preferred_size = self.get(app).preferred_size;
1953            let size = self.constraints(app).constrain(preferred_size);
1954            self.set_size(app, size);
1955        }
1956    }
1957
1958    impl RenderBox for SizedTestBox {
1959        crate::render_box_accessors!();
1960
1961        fn hit_test_self(self: RenderHandle<Self>, _app: &App, _position: Offset) -> bool {
1962            true
1963        }
1964    }
1965
1966    fn sized_box(app: &mut App, size: Size) -> AnyRenderBox {
1967        RenderHandle::new_box(
1968            app,
1969            SizedTestBox {
1970                render_object: RenderObjectData::new(),
1971                render_box: RenderBoxData::new(),
1972                preferred_size: size,
1973            },
1974        )
1975        .as_box()
1976    }
1977
1978    /// A left-to-right [`RenderFlex`] holding `children`.
1979    fn row(app: &mut App, children: Vec<AnyRenderBox>) -> RenderHandle<RenderFlex> {
1980        let flex = RenderFlex::new(app);
1981        flex.set_text_direction(app, Some(TextDirection::Ltr));
1982        flex.add_all(app, Some(children));
1983        flex
1984    }
1985
1986    fn offset_of(app: &App, child: AnyRenderBox) -> Offset {
1987        child.box_parent_data(app).offset
1988    }
1989
1990    fn schedule_root_paint(app: &mut App, node: AnyRenderObject) {
1991        let root = OffsetLayer::new(app, Offset::ZERO);
1992        root.as_layer().attach(app, node.id());
1993        node.schedule_initial_paint(app, root.as_container_layer());
1994    }
1995
1996    /// The test binding's first frame: attach, lay the root out, schedule and flush paint.
1997    fn first_frame(
1998        app: &mut App,
1999        root: AnyRenderBox,
2000        constraints: BoxConstraints,
2001    ) -> Handle<PipelineOwner> {
2002        let owner = PipelineOwner::new(app, None, None);
2003        owner.set_root_node(app, Some(root.as_object()));
2004        root.layout(app, constraints, false);
2005        schedule_root_paint(app, root.as_object());
2006        owner.flush_compositing_bits(app);
2007        owner.flush_paint(app);
2008        owner
2009    }
2010
2011    fn picture_has_clip(app: &App, boundary: AnyRenderObject) -> bool {
2012        let layer = boundary.debug_layer(app).expect("painted");
2013        layer
2014            .depth_first_iterate_children(app)
2015            .into_iter()
2016            .filter_map(|child| {
2017                app.handle::<PictureLayer>(child.id())
2018                    .and_then(|picture| picture.picture(app).map(|p| p.ops().to_vec()))
2019            })
2020            .flatten()
2021            .any(|op| matches!(op, Op::ClipPath { .. }))
2022    }
2023
2024    fn hit_boxes(result: &HitTestResult) -> Vec<AnyRenderBox> {
2025        result
2026            .path()
2027            .iter()
2028            .filter_map(|entry: &HitTestEntry| {
2029                let target: &dyn Any = entry.target();
2030                target
2031                    .downcast_ref::<BoxHitTestEntry>()
2032                    .map(BoxHitTestEntry::target)
2033            })
2034            .collect()
2035    }
2036
2037    /// `object.dart`'s `ContainerRenderObjectMixin`: the child list is a doubly-linked list that
2038    /// `insert` / `move` / `remove` keep consistent from both ends.
2039    #[test]
2040    fn child_list_insert_move_and_remove() {
2041        let cell = AppCell::new();
2042        let mut app = cell.borrow_mut();
2043        let first = sized_box(&mut app, Size::new(10.0, 10.0));
2044        let middle = sized_box(&mut app, Size::new(10.0, 10.0));
2045        let last = sized_box(&mut app, Size::new(10.0, 10.0));
2046        let flex = RenderFlex::new(&mut app);
2047
2048        flex.add(&mut app, first);
2049        flex.insert(&mut app, last, Some(first));
2050        flex.insert(&mut app, middle, Some(first));
2051        assert_eq!(flex.child_count(&app), 3);
2052        assert_eq!(flex.children_as_list(&app), vec![first, middle, last]);
2053        assert_eq!(flex.first_child(&app), Some(first));
2054        assert_eq!(flex.last_child(&app), Some(last));
2055        assert_eq!(flex.child_after(&app, first), Some(middle));
2056        assert_eq!(flex.child_before(&app, last), Some(middle));
2057
2058        let mut visited = Vec::new();
2059        flex.as_object()
2060            .visit_children(&app, &mut |child| visited.push(child));
2061        assert_eq!(
2062            visited,
2063            vec![first.as_object(), middle.as_object(), last.as_object()]
2064        );
2065
2066        flex.move_child(&mut app, first, Some(last));
2067        assert_eq!(flex.children_as_list(&app), vec![middle, last, first]);
2068        flex.move_child(&mut app, first, None);
2069        assert_eq!(flex.children_as_list(&app), vec![first, middle, last]);
2070
2071        flex.remove(&mut app, middle);
2072        assert_eq!(flex.children_as_list(&app), vec![first, last]);
2073        assert_eq!(flex.child_count(&app), 2);
2074        assert!(middle.as_object().parent(&app).is_none());
2075
2076        flex.remove_all(&mut app);
2077        assert_eq!(flex.child_count(&app), 0);
2078        assert!(flex.first_child(&app).is_none());
2079        assert!(flex.last_child(&app).is_none());
2080        assert!(first.as_object().parent(&app).is_none());
2081        assert!(last.as_object().parent(&app).is_none());
2082    }
2083
2084    /// A box with fixed intrinsic dimensions that takes the size its constraints allow around
2085    /// them.
2086    struct IntrinsicTestBox {
2087        render_object: RenderObjectData,
2088        render_box: RenderBoxData,
2089        intrinsic: Size,
2090    }
2091
2092    impl RenderObject for IntrinsicTestBox {
2093        crate::render_object_accessors!();
2094
2095        fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
2096            let intrinsic = self.get(app).intrinsic;
2097            let size = self.constraints(app).constrain(intrinsic);
2098            self.set_size(app, size);
2099        }
2100    }
2101
2102    impl RenderBox for IntrinsicTestBox {
2103        crate::render_box_accessors!();
2104
2105        fn compute_min_intrinsic_width(
2106            self: RenderHandle<Self>,
2107            app: &mut App,
2108            _height: f64,
2109        ) -> f64 {
2110            self.get(app).intrinsic.width()
2111        }
2112
2113        fn compute_max_intrinsic_width(
2114            self: RenderHandle<Self>,
2115            app: &mut App,
2116            _height: f64,
2117        ) -> f64 {
2118            self.get(app).intrinsic.width()
2119        }
2120
2121        fn compute_min_intrinsic_height(
2122            self: RenderHandle<Self>,
2123            app: &mut App,
2124            _width: f64,
2125        ) -> f64 {
2126            self.get(app).intrinsic.height()
2127        }
2128
2129        fn compute_max_intrinsic_height(
2130            self: RenderHandle<Self>,
2131            app: &mut App,
2132            _width: f64,
2133        ) -> f64 {
2134            self.get(app).intrinsic.height()
2135        }
2136
2137        fn compute_dry_layout(
2138            self: RenderHandle<Self>,
2139            app: &mut App,
2140            constraints: BoxConstraints,
2141        ) -> Size {
2142            constraints.constrain(self.get(app).intrinsic)
2143        }
2144    }
2145
2146    fn intrinsic_box(app: &mut App, intrinsic: Size) -> AnyRenderBox {
2147        RenderHandle::new_box(
2148            app,
2149            IntrinsicTestBox {
2150                render_object: RenderObjectData::new(),
2151                render_box: RenderBoxData::new(),
2152                intrinsic,
2153            },
2154        )
2155        .as_box()
2156    }
2157
2158    /// A row of a fixed child and a flexible one: `flex_test.dart`'s intrinsics, where the main
2159    /// axis intrinsic is the inflexible space plus the largest flex fraction, and the cross axis
2160    /// intrinsic is the tallest child.
2161    #[test]
2162    fn row_intrinsics_add_the_inflexible_space_to_the_largest_flex_fraction() {
2163        let cell = AppCell::new();
2164        let mut app = cell.borrow_mut();
2165        let fixed = intrinsic_box(&mut app, Size::new(50.0, 20.0));
2166        let flexible = intrinsic_box(&mut app, Size::new(30.0, 10.0));
2167        let flex = row(&mut app, vec![fixed, flexible]);
2168        flexible.parent_data_of_mut::<FlexParentData>(&mut app).flex = Some(1);
2169
2170        let row = flex.as_box();
2171        assert_eq!(row.get_max_intrinsic_width(&mut app, f64::INFINITY), 80.0);
2172        assert_eq!(row.get_min_intrinsic_width(&mut app, f64::INFINITY), 80.0);
2173        assert_eq!(row.get_max_intrinsic_height(&mut app, 100.0), 20.0);
2174    }
2175
2176    /// The spacing between the children counts towards the main axis intrinsic.
2177    #[test]
2178    fn row_intrinsics_include_the_spacing() {
2179        let cell = AppCell::new();
2180        let mut app = cell.borrow_mut();
2181        let first = intrinsic_box(&mut app, Size::new(50.0, 20.0));
2182        let second = intrinsic_box(&mut app, Size::new(30.0, 10.0));
2183        let flex = row(&mut app, vec![first, second]);
2184        flex.set_spacing(&mut app, 10.0);
2185        assert_eq!(
2186            flex.as_box()
2187                .get_max_intrinsic_width(&mut app, f64::INFINITY),
2188            90.0
2189        );
2190    }
2191
2192    /// The dry layout takes the main axis size into account: a `Max` row fills the constraints,
2193    /// a `Min` row shrink-wraps its children, and both match what layout produces.
2194    #[test]
2195    fn row_dry_layout_follows_the_main_axis_size() {
2196        for (main_axis_size, expected) in [
2197            (MainAxisSize::Max, Size::new(200.0, 20.0)),
2198            (MainAxisSize::Min, Size::new(80.0, 20.0)),
2199        ] {
2200            let cell = AppCell::new();
2201            let mut app = cell.borrow_mut();
2202            let fixed = intrinsic_box(&mut app, Size::new(50.0, 20.0));
2203            let flexible = intrinsic_box(&mut app, Size::new(30.0, 10.0));
2204            let flex = row(&mut app, vec![fixed, flexible]);
2205            flex.set_main_axis_size(&mut app, main_axis_size);
2206            {
2207                let parent_data = flexible.parent_data_of_mut::<FlexParentData>(&mut app);
2208                parent_data.flex = Some(1);
2209                parent_data.fit = Some(FlexFit::Loose);
2210            }
2211
2212            let constraints = BoxConstraints::new().max_width(200.0).max_height(100.0);
2213            let dry = flex.as_box().get_dry_layout(&mut app, constraints);
2214            flex.layout(&mut app, constraints, false);
2215            assert_eq!(dry, expected);
2216            assert_eq!(dry, flex.size(&app), "the dry layout is the real layout");
2217        }
2218    }
2219
2220    /// `flex_test.dart`: a row gives the flexible child the space the fixed one leaves, and the
2221    /// spacing goes between them.
2222    #[test]
2223    fn row_lays_out_fixed_and_flexible_children_with_spacing() {
2224        let cell = AppCell::new();
2225        let mut app = cell.borrow_mut();
2226        let fixed = sized_box(&mut app, Size::new(50.0, 30.0));
2227        let flexible = sized_box(&mut app, Size::new(20.0, 10.0));
2228        let flex = row(&mut app, vec![fixed, flexible]);
2229        flexible.parent_data_of_mut::<FlexParentData>(&mut app).flex = Some(1);
2230        flex.set_spacing(&mut app, 10.0);
2231
2232        flex.layout(
2233            &mut app,
2234            BoxConstraints::tight(Size::new(200.0, 100.0)),
2235            false,
2236        );
2237
2238        assert_eq!(flex.size(&app), Size::new(200.0, 100.0));
2239        assert_eq!(fixed.size(&app), Size::new(50.0, 30.0));
2240        // 200 - 50 fixed - 10 spacing goes to the one flexible child, tightly.
2241        assert_eq!(flexible.size(&app), Size::new(140.0, 10.0));
2242        assert_eq!(offset_of(&app, fixed), Offset::new(0.0, 35.0));
2243        assert_eq!(offset_of(&app, flexible), Offset::new(60.0, 45.0));
2244    }
2245
2246    /// `flex_test.dart`: `Test Flex Widget MainAxisAlignment`.
2247    #[test]
2248    fn main_axis_alignment_positions_children() {
2249        let cases = [
2250            (MainAxisAlignment::Start, 0.0, 20.0),
2251            (MainAxisAlignment::End, 60.0, 80.0),
2252            (MainAxisAlignment::Center, 30.0, 50.0),
2253            (MainAxisAlignment::SpaceBetween, 0.0, 80.0),
2254            (MainAxisAlignment::SpaceAround, 15.0, 65.0),
2255            (MainAxisAlignment::SpaceEvenly, 20.0, 60.0),
2256        ];
2257        for (alignment, first_x, second_x) in cases {
2258            let cell = AppCell::new();
2259            let mut app = cell.borrow_mut();
2260            let first = sized_box(&mut app, Size::new(20.0, 20.0));
2261            let second = sized_box(&mut app, Size::new(20.0, 20.0));
2262            let flex = row(&mut app, vec![first, second]);
2263            flex.set_main_axis_alignment(&mut app, alignment);
2264
2265            flex.layout(
2266                &mut app,
2267                BoxConstraints::tight(Size::new(100.0, 20.0)),
2268                false,
2269            );
2270
2271            assert_eq!(
2272                (offset_of(&app, first).dx(), offset_of(&app, second).dx()),
2273                (first_x, second_x),
2274                "{alignment:?}"
2275            );
2276        }
2277    }
2278
2279    /// Each cross axis alignment places a child that is smaller than the row; a child with no
2280    /// baseline in a baseline-aligned row is top-aligned.
2281    #[test]
2282    fn cross_axis_alignment_positions_children() {
2283        let cases = [
2284            (CrossAxisAlignment::Start, 0.0, 20.0),
2285            (CrossAxisAlignment::Center, 40.0, 20.0),
2286            (CrossAxisAlignment::End, 80.0, 20.0),
2287            (CrossAxisAlignment::Stretch, 0.0, 100.0),
2288            (CrossAxisAlignment::Baseline, 0.0, 20.0),
2289        ];
2290        for (alignment, y, height) in cases {
2291            let cell = AppCell::new();
2292            let mut app = cell.borrow_mut();
2293            let child = sized_box(&mut app, Size::new(20.0, 20.0));
2294            let flex = row(&mut app, vec![child]);
2295            flex.set_cross_axis_alignment(&mut app, alignment);
2296            if alignment == CrossAxisAlignment::Baseline {
2297                flex.set_text_baseline(&mut app, Some(TextBaseline::Alphabetic));
2298            }
2299
2300            flex.layout(
2301                &mut app,
2302                BoxConstraints::tight(Size::new(100.0, 100.0)),
2303                false,
2304            );
2305
2306            assert_eq!(offset_of(&app, child).dy(), y, "{alignment:?}");
2307            assert_eq!(child.size(&app).height(), height, "{alignment:?}");
2308        }
2309    }
2310
2311    /// `flex_test.dart`: `Clip.none` versus a clip when the children overflow the main axis.
2312    #[test]
2313    fn overflow_is_clipped() {
2314        for (clip_behavior, clips) in [(Clip::None, false), (Clip::HardEdge, true)] {
2315            let cell = AppCell::new();
2316            let mut app = cell.borrow_mut();
2317            let first = sized_box(&mut app, Size::new(80.0, 20.0));
2318            let second = sized_box(&mut app, Size::new(80.0, 20.0));
2319            let flex = row(&mut app, vec![first, second]);
2320            flex.set_clip_behavior(&mut app, clip_behavior);
2321            let root = RenderRepaintBoundary::new(&mut app, Some(flex.as_box()));
2322
2323            let owner = first_frame(
2324                &mut app,
2325                root.as_box(),
2326                BoxConstraints::tight(Size::new(100.0, 20.0)),
2327            );
2328
2329            assert_eq!(flex.size(&app), Size::new(100.0, 20.0));
2330            let clipped = picture_has_clip(&app, root.as_object());
2331            assert_eq!(clipped, clips, "{clip_behavior:?}");
2332            let _ = owner;
2333        }
2334    }
2335
2336    /// `flex_test.dart`: a hit lands on the child that covers the position.
2337    #[test]
2338    fn hit_test_reaches_the_child_under_the_position() {
2339        let cell = AppCell::new();
2340        let mut app = cell.borrow_mut();
2341        let first = sized_box(&mut app, Size::new(50.0, 50.0));
2342        let second = sized_box(&mut app, Size::new(50.0, 50.0));
2343        let flex = row(&mut app, vec![first, second]);
2344
2345        flex.layout(
2346            &mut app,
2347            BoxConstraints::tight(Size::new(100.0, 50.0)),
2348            false,
2349        );
2350
2351        let mut result = HitTestResult::new();
2352        let hit = flex.hit_test(
2353            &mut app,
2354            &mut BoxHitTestResult::wrap(&mut result),
2355            Offset::new(75.0, 25.0),
2356        );
2357        assert!(hit);
2358        assert_eq!(hit_boxes(&result), vec![second, flex.as_box()]);
2359    }
2360
2361    /// The size of one cell of a [`FlowBaselineTestBox`].
2362    const GRID_SIZE: Size = Size::new(10.0, 10.0);
2363
2364    /// `flex_test.dart`'s `RenderFlowBaselineTestBox`: `grid_count` cells in rows as wide as
2365    /// the width allows, with the baseline `baseline_placer` gives for its height.
2366    struct FlowBaselineTestBox {
2367        render_object: RenderObjectData,
2368        render_box: RenderBoxData,
2369        grid_count: usize,
2370        baseline_placer: fn(f64) -> Option<f64>,
2371    }
2372
2373    impl FlowBaselineTestBox {
2374        fn line_grid_count(&self, width: f64) -> usize {
2375            let grids_per_line = if width >= self.grid_count as f64 * GRID_SIZE.width() {
2376                self.grid_count
2377            } else {
2378                (width / GRID_SIZE.width()) as usize
2379            };
2380            grids_per_line.max(1)
2381        }
2382
2383        fn line_count(&self, width: f64) -> usize {
2384            (self.grid_count as f64 / self.line_grid_count(width) as f64).ceil() as usize
2385        }
2386    }
2387
2388    impl RenderObject for FlowBaselineTestBox {
2389        crate::render_object_accessors!();
2390
2391        fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
2392            let constraints = self.constraints(app);
2393            let size = RenderBox::compute_dry_layout(self, app, constraints);
2394            self.set_size(app, size);
2395        }
2396    }
2397
2398    impl RenderBox for FlowBaselineTestBox {
2399        crate::render_box_accessors!();
2400
2401        fn compute_min_intrinsic_width(
2402            self: RenderHandle<Self>,
2403            _app: &mut App,
2404            _height: f64,
2405        ) -> f64 {
2406            GRID_SIZE.width()
2407        }
2408
2409        fn compute_max_intrinsic_width(
2410            self: RenderHandle<Self>,
2411            app: &mut App,
2412            _height: f64,
2413        ) -> f64 {
2414            GRID_SIZE.width() * self.get(app).grid_count as f64
2415        }
2416
2417        fn compute_min_intrinsic_height(
2418            self: RenderHandle<Self>,
2419            app: &mut App,
2420            width: f64,
2421        ) -> f64 {
2422            GRID_SIZE.height() * self.get(app).line_count(width) as f64
2423        }
2424
2425        fn compute_max_intrinsic_height(
2426            self: RenderHandle<Self>,
2427            app: &mut App,
2428            width: f64,
2429        ) -> f64 {
2430            RenderBox::compute_min_intrinsic_height(self, app, width)
2431        }
2432
2433        fn compute_dry_layout(
2434            self: RenderHandle<Self>,
2435            app: &mut App,
2436            constraints: BoxConstraints,
2437        ) -> Size {
2438            let this = self.get(app);
2439            constraints.constrain(Size::new(
2440                GRID_SIZE.width() * this.line_grid_count(constraints.max_width) as f64,
2441                GRID_SIZE.height() * this.line_count(constraints.max_width) as f64,
2442            ))
2443        }
2444
2445        fn compute_dry_baseline(
2446            self: RenderHandle<Self>,
2447            app: &mut App,
2448            constraints: BoxConstraints,
2449            _baseline: TextBaseline,
2450        ) -> Option<f64> {
2451            let height = self.as_box().get_dry_layout(app, constraints).height();
2452            (self.get(app).baseline_placer)(height)
2453        }
2454
2455        fn compute_distance_to_actual_baseline(
2456            self: RenderHandle<Self>,
2457            app: &mut App,
2458            _baseline: TextBaseline,
2459        ) -> Option<f64> {
2460            let height = self.size(app).height();
2461            (self.get(app).baseline_placer)(height)
2462        }
2463    }
2464
2465    fn flow_baseline_box(
2466        app: &mut App,
2467        grid_count: usize,
2468        baseline_placer: fn(f64) -> Option<f64>,
2469    ) -> AnyRenderBox {
2470        RenderHandle::new_box(
2471            app,
2472            FlowBaselineTestBox {
2473                render_object: RenderObjectData::new(),
2474                render_box: RenderBoxData::new(),
2475                grid_count,
2476                baseline_placer,
2477            },
2478        )
2479        .as_box()
2480    }
2481
2482    /// A left-to-right row of `children` whose children share the alphabetic baseline.
2483    fn baseline_row(app: &mut App, children: Vec<AnyRenderBox>) -> RenderHandle<RenderFlex> {
2484        let flex = row(app, children);
2485        flex.set_cross_axis_alignment(app, CrossAxisAlignment::Baseline);
2486        flex.set_text_baseline(app, Some(TextBaseline::Alphabetic));
2487        flex
2488    }
2489
2490    /// Lays `root` out as the root of a pipeline, so that a changed property is laid out again
2491    /// by `flush_layout`.
2492    fn lay_out_root(
2493        app: &mut App,
2494        root: AnyRenderBox,
2495        constraints: BoxConstraints,
2496    ) -> Handle<PipelineOwner> {
2497        let owner = PipelineOwner::new(app, None, None);
2498        owner.set_root_node(app, Some(root.as_object()));
2499        root.as_object().schedule_initial_layout(app);
2500        root.layout(app, constraints, false);
2501        owner
2502    }
2503
2504    /// Gives `child` the flex factor `flex` and the fit `fit` in its flex.
2505    fn set_flex(app: &mut App, child: AnyRenderBox, flex: i32, fit: FlexFit) {
2506        let parent_data = child.parent_data_of_mut::<FlexParentData>(app);
2507        parent_data.flex = Some(flex);
2508        parent_data.fit = Some(fit);
2509    }
2510
2511    /// `flex_test.dart`: `children with no baselines are top-aligned`, whichever the vertical
2512    /// direction.
2513    #[test]
2514    fn children_with_no_baselines_are_top_aligned() {
2515        let cell = AppCell::new();
2516        let mut app = cell.borrow_mut();
2517        let first = sized_box(&mut app, Size::new(100.0, 100.0));
2518        let second = sized_box(&mut app, Size::new(100.0, 100.0));
2519        let flex = baseline_row(&mut app, vec![first, second]);
2520        flex.set_vertical_direction(&mut app, VerticalDirection::Up);
2521        let owner = lay_out_root(
2522            &mut app,
2523            flex.as_box(),
2524            BoxConstraints::tight(Size::new(800.0, 600.0)),
2525        );
2526
2527        // Not start-aligned.
2528        assert_eq!(offset_of(&app, first).dy(), 0.0);
2529        assert_eq!(offset_of(&app, second).dy(), 0.0);
2530
2531        flex.set_vertical_direction(&mut app, VerticalDirection::Down);
2532        owner.flush_layout(&mut app);
2533        assert_eq!(offset_of(&app, first).dy(), 0.0);
2534        assert_eq!(offset_of(&app, second).dy(), 0.0);
2535    }
2536
2537    /// `flex_test.dart`: `Vertical Flex Baseline`. A column's baseline is its first child's
2538    /// baseline, wherever the main axis alignment and the vertical direction place that child;
2539    /// the dry baseline and the laid-out one agree.
2540    #[test]
2541    fn a_column_s_baseline_is_its_first_child_s_baseline() {
2542        let cell = AppCell::new();
2543        let mut app = cell.borrow_mut();
2544        let square = BoxConstraints::tight_for(Some(100.0), Some(100.0));
2545        let boxed = |app: &mut App, baseline: Option<AnyRenderBox>| {
2546            crate::proxy_box::RenderConstrainedBox::new(app, square, baseline).as_box()
2547        };
2548        let first_inner = flow_baseline_box(&mut app, 1, |_| Some(10.0));
2549        let second_inner = flow_baseline_box(&mut app, 1, |_| Some(10.0));
2550        let children = vec![
2551            boxed(&mut app, None),
2552            boxed(&mut app, Some(first_inner)),
2553            boxed(&mut app, None),
2554            boxed(&mut app, Some(second_inner)),
2555            boxed(&mut app, None),
2556        ];
2557        let flex = RenderFlex::new(&mut app);
2558        flex.set_text_direction(&mut app, Some(TextDirection::Ltr));
2559        flex.set_direction(&mut app, Axis::Vertical);
2560        flex.add_all(&mut app, Some(children));
2561        let constraints = BoxConstraints::tight(Size::new(800.0, 600.0));
2562        let owner = lay_out_root(&mut app, flex.as_box(), constraints);
2563        let flex_height = flex.size(&app).height();
2564
2565        let expect_baseline = |app: &mut App, expected: f64| {
2566            let column = flex.as_box();
2567            assert_eq!(
2568                column.get_dry_baseline(app, constraints, TextBaseline::Alphabetic),
2569                Some(expected)
2570            );
2571            assert_eq!(
2572                column.get_distance_to_baseline(app, TextBaseline::Alphabetic, true),
2573                Some(expected)
2574            );
2575        };
2576        expect_baseline(&mut app, 100.0 + 10.0);
2577
2578        flex.set_main_axis_alignment(&mut app, MainAxisAlignment::End);
2579        owner.flush_layout(&mut app);
2580        expect_baseline(&mut app, flex_height - 400.0 + 10.0);
2581
2582        flex.set_vertical_direction(&mut app, VerticalDirection::Up);
2583        owner.flush_layout(&mut app);
2584        expect_baseline(&mut app, 300.0 + 10.0);
2585
2586        flex.set_main_axis_alignment(&mut app, MainAxisAlignment::Start);
2587        owner.flush_layout(&mut app);
2588        expect_baseline(&mut app, flex_height - 200.0 + 10.0);
2589    }
2590
2591    /// `flex_test.dart`: `baseline aligned flex flow computeDryLayout`. The cross extent is the
2592    /// tallest ascent plus the deepest descent, and the dry baseline is the tallest ascent.
2593    #[test]
2594    fn baseline_aligned_dry_layout_adds_the_largest_ascent_and_descent() {
2595        let cell = AppCell::new();
2596        let mut app = cell.borrow_mut();
2597        // The first box has its baseline at its top, the second at its bottom.
2598        let first = flow_baseline_box(&mut app, 10, |_| Some(0.0));
2599        let second = flow_baseline_box(&mut app, 10, Some);
2600        let flex = baseline_row(&mut app, vec![first, second]);
2601        set_flex(&mut app, first, 2, FlexFit::Tight);
2602        set_flex(&mut app, second, 1, FlexFit::Loose);
2603        let flex = flex.as_box();
2604
2605        // The first box one line, the second two lines.
2606        let constraints = BoxConstraints::loose(Size::new(200.0, 100.0));
2607        assert_eq!(
2608            flex.get_dry_layout(&mut app, constraints),
2609            Size::new(200.0, 30.0)
2610        );
2611        assert_eq!(
2612            flex.get_dry_baseline(&mut app, constraints, TextBaseline::Alphabetic),
2613            Some(20.0)
2614        );
2615        // Both boxes one line.
2616        let constraints = BoxConstraints::loose(Size::new(300.0, 100.0));
2617        assert_eq!(
2618            flex.get_dry_layout(&mut app, constraints),
2619            Size::new(300.0, 20.0)
2620        );
2621        assert_eq!(
2622            flex.get_dry_baseline(&mut app, constraints, TextBaseline::Alphabetic),
2623            Some(10.0)
2624        );
2625    }
2626
2627    /// `flex_test.dart`: `baseline aligned children cross intrinsic size`.
2628    #[test]
2629    fn baseline_aligned_cross_intrinsics_add_the_largest_ascent_and_descent() {
2630        let cell = AppCell::new();
2631        let mut app = cell.borrow_mut();
2632        let first = flow_baseline_box(&mut app, 10, |_| Some(0.0));
2633        let second = flow_baseline_box(&mut app, 10, Some);
2634        let flex = baseline_row(&mut app, vec![first, second]);
2635        set_flex(&mut app, first, 2, FlexFit::Tight);
2636        set_flex(&mut app, second, 1, FlexFit::Loose);
2637        let flex = flex.as_box();
2638
2639        // The first box one line, the second two lines.
2640        assert_eq!(flex.get_max_intrinsic_height(&mut app, 200.0), 30.0);
2641        assert_eq!(flex.get_min_intrinsic_height(&mut app, 200.0), 30.0);
2642        // Both boxes one line.
2643        assert_eq!(flex.get_max_intrinsic_height(&mut app, 300.0), 20.0);
2644        assert_eq!(flex.get_min_intrinsic_height(&mut app, 300.0), 20.0);
2645    }
2646
2647    /// `flex_test.dart`: `children with no baselines do not affect the baseline location`; a
2648    /// taller child without one still counts towards the cross extent.
2649    #[test]
2650    fn children_with_no_baselines_do_not_affect_the_baseline_location() {
2651        let cell = AppCell::new();
2652        let mut app = cell.borrow_mut();
2653        let first = flow_baseline_box(&mut app, 10, Some);
2654        let second = flow_baseline_box(&mut app, 10, |_| None);
2655        let flex = baseline_row(&mut app, vec![first, second]);
2656        set_flex(&mut app, first, 2, FlexFit::Tight);
2657        set_flex(&mut app, second, 1, FlexFit::Loose);
2658        let flex = flex.as_box();
2659
2660        // The first box one line, the second two lines.
2661        let constraints = BoxConstraints::loose(Size::new(200.0, 100.0));
2662        assert_eq!(
2663            flex.get_dry_layout(&mut app, constraints),
2664            Size::new(200.0, 20.0)
2665        );
2666        assert_eq!(
2667            flex.get_dry_baseline(&mut app, constraints, TextBaseline::Alphabetic),
2668            Some(10.0)
2669        );
2670        // Both boxes one line.
2671        let constraints = BoxConstraints::loose(Size::new(300.0, 100.0));
2672        assert_eq!(
2673            flex.get_dry_layout(&mut app, constraints),
2674            Size::new(300.0, 10.0)
2675        );
2676        assert_eq!(
2677            flex.get_dry_baseline(&mut app, constraints, TextBaseline::Alphabetic),
2678            Some(10.0)
2679        );
2680    }
2681
2682    /// Laying a baseline-aligned row out puts each child's baseline on the row's baseline, a
2683    /// child without one at the top, and the extra cross space below them all; the dry layout
2684    /// and the dry baseline agree with it.
2685    #[test]
2686    fn layout_places_the_children_on_a_shared_baseline() {
2687        let cell = AppCell::new();
2688        let mut app = cell.borrow_mut();
2689        let tight_box = |app: &mut App, size: Size, child: Option<AnyRenderBox>| {
2690            crate::proxy_box::RenderConstrainedBox::new(app, BoxConstraints::tight(size), child)
2691                .as_box()
2692        };
2693        // 30 above the baseline and 10 below it; 10 above and 10 below; 50 tall with none.
2694        let deep_baseline = flow_baseline_box(&mut app, 1, |_| Some(30.0));
2695        let deep = tight_box(&mut app, Size::new(10.0, 40.0), Some(deep_baseline));
2696        let shallow_baseline = flow_baseline_box(&mut app, 1, |_| Some(10.0));
2697        let shallow = tight_box(&mut app, Size::new(10.0, 20.0), Some(shallow_baseline));
2698        let without = tight_box(&mut app, Size::new(10.0, 50.0), None);
2699        let flex = baseline_row(&mut app, vec![deep, shallow, without]);
2700        let row = flex.as_box();
2701        let loose = BoxConstraints::loose(Size::new(200.0, 100.0));
2702        let dry_size = row.get_dry_layout(&mut app, loose);
2703        let dry_baseline = row.get_dry_baseline(&mut app, loose, TextBaseline::Alphabetic);
2704        let _owner = lay_out_root(&mut app, row, loose);
2705
2706        assert_eq!(flex.size(&app), Size::new(200.0, 50.0));
2707        assert_eq!(offset_of(&app, deep).dy(), 0.0);
2708        assert_eq!(offset_of(&app, shallow).dy(), 20.0);
2709        assert_eq!(offset_of(&app, without).dy(), 0.0);
2710        let baseline = row.get_distance_to_baseline(&mut app, TextBaseline::Alphabetic, true);
2711        assert_eq!(baseline, Some(30.0));
2712        assert_eq!(
2713            (dry_size, dry_baseline),
2714            (flex.size(&app), baseline),
2715            "the dry layout is the real layout"
2716        );
2717
2718        let tall = BoxConstraints {
2719            min_height: 100.0,
2720            ..loose
2721        };
2722        row.layout(&mut app, tall, false);
2723        assert_eq!(flex.size(&app), Size::new(200.0, 100.0));
2724        assert_eq!(
2725            offset_of(&app, shallow).dy(),
2726            20.0,
2727            "the extra space is below the children"
2728        );
2729    }
2730}