Skip to main content

inset_rendering/
flex.rs

1//! Flutter counterpart: `rendering/flex.dart` (`FlexFit`, `FlexParentData`, `MainAxisSize`,
2//! `MainAxisAlignment`, `CrossAxisAlignment`, `RenderFlex`).
3//!
4//! Baseline alignment waits; see `PORTING.md`.
5
6use std::any::{Any, TypeId};
7use std::fmt::{self, Debug};
8use std::ops::Add;
9
10use inset_embedder::{Clip, Offset, Size, TextBaseline, TextDirection};
11use inset_foundation::{App, Handle, PRECISION_ERROR_TOLERANCE};
12use inset_painting::{Axis, VerticalDirection};
13
14use crate::box_::{
15    AnyRenderBox, BoxConstraints, BoxHitTestResult, BoxParentData, ContainerBoxParentData,
16    DryLayoutFailure, RenderBox, RenderBoxContainerDefaultsMixin, RenderBoxData,
17};
18use crate::layer::{ClipRectLayer, LayerHandle};
19use crate::layout_helper::ChildLayoutHelper;
20use crate::object::{
21    AnyRenderObject, ContainerParentData, ContainerParentDataMixin, ContainerRenderObjectData,
22    ContainerRenderObjectMixin, ParentData, RenderHandle, RenderObject, RenderObjectData,
23};
24use crate::painting_context::PaintingContext;
25use crate::pipeline_owner::PipelineOwner;
26
27/// A 2D vector that uses a [`RenderFlex`]'s main axis and cross axis as its first and second
28/// coordinate axes. It represents the same vector as `(main_axis_extent, cross_axis_extent)`.
29#[derive(Clone, Copy)]
30struct AxisSize(Size);
31
32impl AxisSize {
33    fn new(main_axis_extent: f64, cross_axis_extent: f64) -> AxisSize {
34        AxisSize(Size::new(main_axis_extent, cross_axis_extent))
35    }
36
37    fn from_size(size: Size, direction: Axis) -> AxisSize {
38        AxisSize(AxisSize::convert(size, direction))
39    }
40
41    fn convert(size: Size, direction: Axis) -> Size {
42        match direction {
43            Axis::Horizontal => size,
44            Axis::Vertical => size.flipped(),
45        }
46    }
47
48    fn main_axis_extent(self) -> f64 {
49        self.0.width()
50    }
51
52    fn cross_axis_extent(self) -> f64 {
53        self.0.height()
54    }
55
56    fn to_size(self, direction: Axis) -> Size {
57        AxisSize::convert(self.0, direction)
58    }
59
60    fn apply_constraints(self, constraints: BoxConstraints, direction: Axis) -> AxisSize {
61        let effective_constraints = match direction {
62            Axis::Horizontal => constraints,
63            Axis::Vertical => constraints.flipped(),
64        };
65        AxisSize(effective_constraints.constrain(self.0))
66    }
67}
68
69impl Add for AxisSize {
70    type Output = AxisSize;
71
72    fn add(self, other: AxisSize) -> AxisSize {
73        AxisSize(Size::new(
74            self.0.width() + other.0.width(),
75            self.0.height().max(other.0.height()),
76        ))
77    }
78}
79
80/// Flutter's `_ChildSizingFunction`: a method that finds a child's size in the sizing
81/// direction, given the extent in the other direction.
82type ChildSizingFunction = fn(AnyRenderBox, &mut App, f64) -> f64;
83
84/// What [`RenderFlexMixin::compute_sizes`] worked out about a layout.
85pub struct LayoutSizes {
86    /// The final constrained [`AxisSize`] of the [`RenderFlex`].
87    axis_size: AxisSize,
88
89    /// The free space along the main axis. If the value is positive, the free space will be
90    /// distributed according to the [`MainAxisAlignment`] specified. A negative value indicates
91    /// the [`RenderFlex`] overflows along the main axis.
92    main_axis_free_space: f64,
93}
94
95/// How the child is inscribed into the available space.
96///
97/// See also:
98///
99///  * [`RenderFlex`], the flex render object.
100#[derive(Clone, Copy, Debug, PartialEq, Eq)]
101pub enum FlexFit {
102    /// The child is forced to fill the available space.
103    Tight,
104
105    /// The child can be at most as large as the available space (but is allowed to be smaller).
106    Loose,
107}
108
109/// Parent data for use with [`RenderFlex`].
110#[derive(Debug)]
111pub struct FlexParentData {
112    box_parent_data: BoxParentData,
113    container_parent_data: ContainerParentData<AnyRenderBox>,
114
115    /// The flex factor to use for this child.
116    ///
117    /// If `None` or zero, the child is inflexible and determines its own size. If non-zero, the
118    /// amount of space the child's can occupy in the main axis is determined by dividing the
119    /// free space (after placing the inflexible children) according to the flex factors of the
120    /// flexible children.
121    pub flex: Option<i32>,
122
123    /// How a flexible child is inscribed into the available space.
124    ///
125    /// If [`flex`](Self::flex) is non-zero, the fit determines whether the child fills the space
126    /// the parent makes available during layout. If the fit is [`FlexFit::Tight`], the child is
127    /// required to fill the available space. If the fit is [`FlexFit::Loose`], the child can be
128    /// at most as large as the available space (but is allowed to be smaller).
129    pub fit: Option<FlexFit>,
130}
131
132impl FlexParentData {
133    /// Creates flex parent data with no flex factor and no fit.
134    pub const fn new() -> FlexParentData {
135        FlexParentData {
136            box_parent_data: BoxParentData::new(),
137            container_parent_data: ContainerParentData::new(),
138            flex: None,
139            fit: None,
140        }
141    }
142}
143
144impl Default for FlexParentData {
145    fn default() -> FlexParentData {
146        FlexParentData::new()
147    }
148}
149
150impl ParentData for FlexParentData {
151    fn detach(&mut self) {
152        ContainerParentDataMixin::detach(self);
153    }
154
155    fn provide(&self, id: TypeId) -> Option<&dyn Any> {
156        if id == TypeId::of::<FlexParentData>() {
157            return Some(self);
158        }
159        self.box_parent_data.provide(id)
160    }
161
162    fn provide_mut(&mut self, id: TypeId) -> Option<&mut dyn Any> {
163        if id == TypeId::of::<FlexParentData>() {
164            return Some(self);
165        }
166        self.box_parent_data.provide_mut(id)
167    }
168}
169
170impl ContainerParentDataMixin for FlexParentData {
171    type ChildType = AnyRenderBox;
172
173    fn container_parent_data(&self) -> &ContainerParentData<AnyRenderBox> {
174        &self.container_parent_data
175    }
176
177    fn container_parent_data_mut(&mut self) -> &mut ContainerParentData<AnyRenderBox> {
178        &mut self.container_parent_data
179    }
180}
181
182impl ContainerBoxParentData for FlexParentData {
183    fn box_parent_data(&self) -> &BoxParentData {
184        &self.box_parent_data
185    }
186
187    fn box_parent_data_mut(&mut self) -> &mut BoxParentData {
188        &mut self.box_parent_data
189    }
190}
191
192impl fmt::Display for FlexParentData {
193    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194        write!(
195            f,
196            "{}; flex={:?}; fit={:?}",
197            self.box_parent_data, self.flex, self.fit
198        )
199    }
200}
201
202/// How much space should be occupied in the main axis.
203///
204/// During a flex layout, available space along the main axis is allocated to children. After
205/// allocating space, there might be some remaining free space. This value controls whether to
206/// maximize or minimize the amount of free space, subject to the incoming layout constraints.
207///
208/// See also:
209///
210///  * [`RenderFlex`], the flex render object.
211///  * [`MainAxisAlignment`], which controls how the free space is distributed.
212#[derive(Clone, Copy, Debug, PartialEq, Eq)]
213pub enum MainAxisSize {
214    /// Minimize the amount of free space along the main axis, subject to the incoming layout
215    /// constraints.
216    ///
217    /// If the incoming layout constraints have a large enough [`BoxConstraints::min_width`] or
218    /// [`BoxConstraints::min_height`], there might still be a non-zero amount of free space.
219    ///
220    /// If the incoming layout constraints are unbounded, and any children have a non-zero
221    /// [`FlexParentData::flex`] and a [`FlexFit::Tight`] fit, the [`RenderFlex`] will assert,
222    /// because there would be infinite remaining free space and boxes cannot be given infinite
223    /// size.
224    Min,
225
226    /// Maximize the amount of free space along the main axis, subject to the incoming layout
227    /// constraints.
228    ///
229    /// If the incoming layout constraints have a small enough [`BoxConstraints::max_width`] or
230    /// [`BoxConstraints::max_height`], there might still be no free space.
231    ///
232    /// If the incoming layout constraints are unbounded, the [`RenderFlex`] will assert, because
233    /// there would be infinite remaining free space and boxes cannot be given infinite size.
234    Max,
235}
236
237/// How the children should be placed along the main axis in a flex layout.
238///
239/// See also:
240///
241///  * [`RenderFlex`], the flex render object.
242#[derive(Clone, Copy, Debug, PartialEq, Eq)]
243pub enum MainAxisAlignment {
244    /// Place the children as close to the start of the main axis as possible.
245    ///
246    /// If this value is used in a horizontal direction, a [`TextDirection`] must be available to
247    /// determine if the start is the left or the right.
248    ///
249    /// If this value is used in a vertical direction, a [`VerticalDirection`] must be available
250    /// to determine if the start is the top or the bottom.
251    Start,
252
253    /// Place the children as close to the end of the main axis as possible.
254    ///
255    /// If this value is used in a horizontal direction, a [`TextDirection`] must be available to
256    /// determine if the end is the left or the right.
257    ///
258    /// If this value is used in a vertical direction, a [`VerticalDirection`] must be available
259    /// to determine if the end is the top or the bottom.
260    End,
261
262    /// Place the children as close to the middle of the main axis as possible.
263    Center,
264
265    /// Place the free space evenly between the children.
266    SpaceBetween,
267
268    /// Place the free space evenly between the children as well as half of that space before and
269    /// after the first and last child.
270    SpaceAround,
271
272    /// Place the free space evenly between the children as well as before and after the first
273    /// and last child.
274    SpaceEvenly,
275}
276
277impl MainAxisAlignment {
278    fn distribute_space(
279        self,
280        free_space: f64,
281        item_count: usize,
282        flipped: bool,
283        spacing: f64,
284    ) -> (f64, f64) {
285        let items = item_count as f64;
286        match self {
287            MainAxisAlignment::Start => {
288                if flipped {
289                    (free_space, spacing)
290                } else {
291                    (0.0, spacing)
292                }
293            }
294            MainAxisAlignment::End => {
295                MainAxisAlignment::Start.distribute_space(free_space, item_count, !flipped, spacing)
296            }
297            MainAxisAlignment::SpaceBetween if item_count < 2 => {
298                MainAxisAlignment::Start.distribute_space(free_space, item_count, flipped, spacing)
299            }
300            MainAxisAlignment::SpaceAround if item_count == 0 => {
301                MainAxisAlignment::Start.distribute_space(free_space, item_count, flipped, spacing)
302            }
303            MainAxisAlignment::Center => (free_space / 2.0, spacing),
304            MainAxisAlignment::SpaceBetween => (0.0, free_space / (items - 1.0) + spacing),
305            MainAxisAlignment::SpaceAround => {
306                (free_space / items / 2.0, free_space / items + spacing)
307            }
308            MainAxisAlignment::SpaceEvenly => (
309                free_space / (items + 1.0),
310                free_space / (items + 1.0) + spacing,
311            ),
312        }
313    }
314}
315
316/// How the children should be placed along the cross axis in a flex layout.
317///
318/// See also:
319///
320///  * [`RenderFlex`], the flex render object.
321#[derive(Clone, Copy, Debug, PartialEq, Eq)]
322pub enum CrossAxisAlignment {
323    /// Place the children with their start edge aligned with the start side of the cross axis.
324    ///
325    /// For example, in a column (a flex with a vertical axis) whose [`TextDirection`] is
326    /// [`TextDirection::Ltr`], this aligns the left edge of the children along the left edge of
327    /// the column.
328    ///
329    /// If this value is used in a horizontal direction, a [`TextDirection`] must be available to
330    /// determine if the start is the left or the right.
331    ///
332    /// If this value is used in a vertical direction, a [`VerticalDirection`] must be available
333    /// to determine if the start is the top or the bottom.
334    Start,
335
336    /// Place the children as close to the end of the cross axis as possible.
337    ///
338    /// For example, in a column (a flex with a vertical axis) whose [`TextDirection`] is
339    /// [`TextDirection::Ltr`], this aligns the right edge of the children along the right edge
340    /// of the column.
341    ///
342    /// If this value is used in a horizontal direction, a [`TextDirection`] must be available to
343    /// determine if the end is the left or the right.
344    ///
345    /// If this value is used in a vertical direction, a [`VerticalDirection`] must be available
346    /// to determine if the end is the top or the bottom.
347    End,
348
349    /// Place the children so that their centers align with the middle of the cross axis.
350    ///
351    /// This is the default cross-axis alignment.
352    Center,
353
354    /// Require the children to fill the cross axis.
355    ///
356    /// This causes the constraints passed to the children to be tight in the cross axis.
357    Stretch,
358
359    /// Place the children along the cross axis such that their baselines match.
360    ///
361    /// Consider using this value for any horizontal main axis (as with a row) where the children
362    /// primarily contain text.
363    ///
364    /// Because baselines are always horizontal, this alignment is intended for horizontal main
365    /// axes. If the main axis is vertical, then this value is treated like
366    /// [`Start`](Self::Start).
367    ///
368    /// Children who report no baseline will be top-aligned. No [`RenderBox`] reports a baseline
369    /// yet, so every child is top-aligned; see `PORTING.md`.
370    Baseline,
371}
372
373impl CrossAxisAlignment {
374    /// This method should not be used to position baseline-aligned children.
375    fn get_child_cross_axis_offset(self, free_space: f64, flipped: bool) -> f64 {
376        match self {
377            CrossAxisAlignment::Stretch | CrossAxisAlignment::Baseline => 0.0,
378            CrossAxisAlignment::Start => {
379                if flipped {
380                    free_space
381                } else {
382                    0.0
383                }
384            }
385            CrossAxisAlignment::Center => free_space / 2.0,
386            CrossAxisAlignment::End => {
387                CrossAxisAlignment::Start.get_child_cross_axis_offset(free_space, !flipped)
388            }
389        }
390    }
391}
392
393/// The fields of a [`RenderFlex`].
394///
395/// A render object that inherits the flex layout holds this bag and implements
396/// [`RenderFlexMixin`].
397pub struct RenderFlexData {
398    direction: Axis,
399    main_axis_alignment: MainAxisAlignment,
400    main_axis_size: MainAxisSize,
401    cross_axis_alignment: CrossAxisAlignment,
402    text_direction: Option<TextDirection>,
403    vertical_direction: VerticalDirection,
404    text_baseline: Option<TextBaseline>,
405    clip_behavior: Clip,
406    spacing: f64,
407    /// Set during layout if overflow occurred on the main axis.
408    overflow: f64,
409    clip_rect_layer: LayerHandle<Handle<ClipRectLayer>>,
410}
411
412impl RenderFlexData {
413    /// Creates the flex fields with their defaults.
414    ///
415    /// The layout is horizontal and children are aligned to the start of the main axis and the
416    /// center of the cross axis.
417    pub fn new() -> RenderFlexData {
418        RenderFlexData {
419            direction: Axis::Horizontal,
420            main_axis_alignment: MainAxisAlignment::Start,
421            main_axis_size: MainAxisSize::Max,
422            cross_axis_alignment: CrossAxisAlignment::Center,
423            text_direction: None,
424            vertical_direction: VerticalDirection::Down,
425            text_baseline: None,
426            clip_behavior: Clip::None,
427            spacing: 0.0,
428            overflow: 0.0,
429            clip_rect_layer: LayerHandle::new(),
430        }
431    }
432}
433
434impl Default for RenderFlexData {
435    fn default() -> RenderFlexData {
436        RenderFlexData::new()
437    }
438}
439
440impl RenderFlexData {
441    /// Dart `RenderFlex(direction:)`.
442    pub fn direction(mut self, direction: Axis) -> RenderFlexData {
443        self.direction = direction;
444        self
445    }
446
447    /// Dart `RenderFlex(mainAxisAlignment:)`.
448    pub fn main_axis_alignment(mut self, main_axis_alignment: MainAxisAlignment) -> RenderFlexData {
449        self.main_axis_alignment = main_axis_alignment;
450        self
451    }
452
453    /// Dart `RenderFlex(mainAxisSize:)`.
454    pub fn main_axis_size(mut self, main_axis_size: MainAxisSize) -> RenderFlexData {
455        self.main_axis_size = main_axis_size;
456        self
457    }
458
459    /// Dart `RenderFlex(crossAxisAlignment:)`.
460    pub fn cross_axis_alignment(
461        mut self,
462        cross_axis_alignment: CrossAxisAlignment,
463    ) -> RenderFlexData {
464        self.cross_axis_alignment = cross_axis_alignment;
465        self
466    }
467
468    /// Dart `RenderFlex(textDirection:)`.
469    pub fn text_direction(mut self, text_direction: Option<TextDirection>) -> RenderFlexData {
470        self.text_direction = text_direction;
471        self
472    }
473
474    /// Dart `RenderFlex(verticalDirection:)`.
475    pub fn vertical_direction(mut self, vertical_direction: VerticalDirection) -> RenderFlexData {
476        self.vertical_direction = vertical_direction;
477        self
478    }
479
480    /// Dart `RenderFlex(textBaseline:)`.
481    pub fn text_baseline(mut self, text_baseline: Option<TextBaseline>) -> RenderFlexData {
482        self.text_baseline = text_baseline;
483        self
484    }
485
486    /// Dart `RenderFlex(clipBehavior:)`.
487    pub fn clip_behavior(mut self, clip_behavior: Clip) -> RenderFlexData {
488        self.clip_behavior = clip_behavior;
489        self
490    }
491
492    /// Dart `RenderFlex(spacing:)`.
493    pub fn spacing(mut self, spacing: f64) -> RenderFlexData {
494        self.spacing = spacing;
495        self
496    }
497}
498
499/// Displays its children in a one-dimensional array.
500///
501/// Flutter's `RenderFlex`: the shared bodies of the flex layout. A leaf holds a
502/// [`RenderFlexData`], implements this trait, and calls these where Dart would run the inherited
503/// method: `RenderFlexMixin::perform_layout(self, app)`.
504///
505/// See [`RenderFlex`] for the layout algorithm.
506pub trait RenderFlexMixin:
507    RenderBox
508    + ContainerRenderObjectMixin<ChildType = AnyRenderBox, ParentDataType = FlexParentData>
509    + RenderBoxContainerDefaultsMixin
510{
511    /// Mixin field access.
512    fn flex_data(self: RenderHandle<Self>, app: &App) -> &RenderFlexData;
513
514    /// See [`flex_data`](Self::flex_data).
515    fn flex_data_mut(self: RenderHandle<Self>, app: &mut App) -> &mut RenderFlexData;
516
517    /// The direction to use as the main axis.
518    fn direction(self: RenderHandle<Self>, app: &App) -> Axis {
519        self.flex_data(app).direction
520    }
521
522    /// Sets [`direction`](Self::direction).
523    fn set_direction(self: RenderHandle<Self>, app: &mut App, value: Axis) {
524        if self.flex_data(app).direction != value {
525            self.flex_data_mut(app).direction = value;
526            self.mark_needs_layout(app);
527        }
528    }
529
530    /// How the children should be placed along the main axis.
531    ///
532    /// If the [`direction`](Self::direction) is [`Axis::Horizontal`], and the alignment is either
533    /// [`MainAxisAlignment::Start`] or [`MainAxisAlignment::End`], then the
534    /// [`text_direction`](Self::text_direction) must not be `None`.
535    fn main_axis_alignment(self: RenderHandle<Self>, app: &App) -> MainAxisAlignment {
536        self.flex_data(app).main_axis_alignment
537    }
538
539    /// Sets [`main_axis_alignment`](Self::main_axis_alignment).
540    fn set_main_axis_alignment(self: RenderHandle<Self>, app: &mut App, value: MainAxisAlignment) {
541        if self.flex_data(app).main_axis_alignment != value {
542            self.flex_data_mut(app).main_axis_alignment = value;
543            self.mark_needs_layout(app);
544        }
545    }
546
547    /// How much space should be occupied in the main axis.
548    ///
549    /// After allocating space to children, there might be some remaining free space. This value
550    /// controls whether to maximize or minimize the amount of free space, subject to the incoming
551    /// layout constraints.
552    fn main_axis_size(self: RenderHandle<Self>, app: &App) -> MainAxisSize {
553        self.flex_data(app).main_axis_size
554    }
555
556    /// Sets [`main_axis_size`](Self::main_axis_size).
557    fn set_main_axis_size(self: RenderHandle<Self>, app: &mut App, value: MainAxisSize) {
558        if self.flex_data(app).main_axis_size != value {
559            self.flex_data_mut(app).main_axis_size = value;
560            self.mark_needs_layout(app);
561        }
562    }
563
564    /// How the children should be placed along the cross axis.
565    ///
566    /// If the [`direction`](Self::direction) is [`Axis::Vertical`], and the alignment is either
567    /// [`CrossAxisAlignment::Start`] or [`CrossAxisAlignment::End`], then the
568    /// [`text_direction`](Self::text_direction) must not be `None`.
569    fn cross_axis_alignment(self: RenderHandle<Self>, app: &App) -> CrossAxisAlignment {
570        self.flex_data(app).cross_axis_alignment
571    }
572
573    /// Sets [`cross_axis_alignment`](Self::cross_axis_alignment).
574    fn set_cross_axis_alignment(
575        self: RenderHandle<Self>,
576        app: &mut App,
577        value: CrossAxisAlignment,
578    ) {
579        if self.flex_data(app).cross_axis_alignment != value {
580            self.flex_data_mut(app).cross_axis_alignment = value;
581            self.mark_needs_layout(app);
582        }
583    }
584
585    /// Determines the order to lay children out horizontally and how to interpret `start` and
586    /// `end` in the horizontal direction.
587    ///
588    /// If the [`direction`](Self::direction) is [`Axis::Horizontal`], this controls the order in
589    /// which children are positioned (left-to-right or right-to-left), and the meaning of the
590    /// [`main_axis_alignment`](Self::main_axis_alignment) property's
591    /// [`MainAxisAlignment::Start`] and [`MainAxisAlignment::End`] values.
592    ///
593    /// If the [`direction`](Self::direction) is [`Axis::Vertical`], this controls the meaning of
594    /// the [`cross_axis_alignment`](Self::cross_axis_alignment) property's
595    /// [`CrossAxisAlignment::Start`] and [`CrossAxisAlignment::End`] values.
596    fn text_direction(self: RenderHandle<Self>, app: &App) -> Option<TextDirection> {
597        self.flex_data(app).text_direction
598    }
599
600    /// Sets [`text_direction`](Self::text_direction).
601    fn set_text_direction(self: RenderHandle<Self>, app: &mut App, value: Option<TextDirection>) {
602        if self.flex_data(app).text_direction != value {
603            self.flex_data_mut(app).text_direction = value;
604            self.mark_needs_layout(app);
605        }
606    }
607
608    /// Determines the order to lay children out vertically and how to interpret `start` and `end`
609    /// in the vertical direction.
610    ///
611    /// If the [`direction`](Self::direction) is [`Axis::Vertical`], this controls which order
612    /// children are painted in (down or up), and the meaning of the
613    /// [`main_axis_alignment`](Self::main_axis_alignment) property's
614    /// [`MainAxisAlignment::Start`] and [`MainAxisAlignment::End`] values.
615    ///
616    /// If the [`direction`](Self::direction) is [`Axis::Horizontal`], this controls the meaning
617    /// of the [`cross_axis_alignment`](Self::cross_axis_alignment) property's
618    /// [`CrossAxisAlignment::Start`] and [`CrossAxisAlignment::End`] values.
619    fn vertical_direction(self: RenderHandle<Self>, app: &App) -> VerticalDirection {
620        self.flex_data(app).vertical_direction
621    }
622
623    /// Sets [`vertical_direction`](Self::vertical_direction).
624    fn set_vertical_direction(self: RenderHandle<Self>, app: &mut App, value: VerticalDirection) {
625        if self.flex_data(app).vertical_direction != value {
626            self.flex_data_mut(app).vertical_direction = value;
627            self.mark_needs_layout(app);
628        }
629    }
630
631    /// If aligning items according to their baseline, which baseline to use.
632    ///
633    /// Must not be `None` if [`cross_axis_alignment`](Self::cross_axis_alignment) is
634    /// [`CrossAxisAlignment::Baseline`].
635    fn text_baseline(self: RenderHandle<Self>, app: &App) -> Option<TextBaseline> {
636        self.flex_data(app).text_baseline
637    }
638
639    /// Sets [`text_baseline`](Self::text_baseline).
640    fn set_text_baseline(self: RenderHandle<Self>, app: &mut App, value: Option<TextBaseline>) {
641        debug_assert!(
642            self.flex_data(app).cross_axis_alignment != CrossAxisAlignment::Baseline
643                || value.is_some()
644        );
645        if self.flex_data(app).text_baseline != value {
646            self.flex_data_mut(app).text_baseline = value;
647            self.mark_needs_layout(app);
648        }
649    }
650
651    /// Whether the flex overflowed along the main axis.
652    ///
653    /// Values below an epsilon are treated as not overflowing. Internal to the flex layout; a
654    /// leaf does not republish it.
655    fn has_overflow(self: RenderHandle<Self>, app: &App) -> bool {
656        self.flex_data(app).overflow > PRECISION_ERROR_TOLERANCE
657    }
658
659    /// How to clip the children that overflow along the main axis.
660    ///
661    /// Defaults to [`Clip::None`].
662    fn clip_behavior(self: RenderHandle<Self>, app: &App) -> Clip {
663        self.flex_data(app).clip_behavior
664    }
665
666    /// Sets [`clip_behavior`](Self::clip_behavior).
667    fn set_clip_behavior(self: RenderHandle<Self>, app: &mut App, value: Clip) {
668        if value != self.flex_data(app).clip_behavior {
669            self.flex_data_mut(app).clip_behavior = value;
670            self.mark_needs_paint(app);
671        }
672    }
673
674    /// How much space to place between children in the main axis.
675    ///
676    /// The spacing is only applied between children in the main axis: not before the first child
677    /// or after the last one. When it is non-zero, the layout size will be larger than the sum of
678    /// the children's layout sizes in the main axis, and the children overflow when their sizes
679    /// plus the total spacing exceed the maximum main axis constraint.
680    ///
681    /// Defaults to 0.0.
682    fn spacing(self: RenderHandle<Self>, app: &App) -> f64 {
683        self.flex_data(app).spacing
684    }
685
686    /// Sets [`spacing`](Self::spacing).
687    fn set_spacing(self: RenderHandle<Self>, app: &mut App, value: f64) {
688        debug_assert!(value >= 0.0);
689        if self.flex_data(app).spacing == value {
690            return;
691        }
692        self.flex_data_mut(app).spacing = value;
693        self.mark_needs_layout(app);
694    }
695
696    /// Flutter's `_debugHasNecessaryDirections`: asserts the directions the current alignment
697    /// needs are available. Always returns true. Internal to the flex layout.
698    fn debug_has_necessary_directions(self: RenderHandle<Self>, app: &App) -> bool {
699        let direction = self.direction(app);
700        if self.first_child(app).is_some() && self.last_child(app) != self.first_child(app) {
701            // i.e. there's more than one child
702            if direction == Axis::Horizontal {
703                debug_assert!(
704                    self.text_direction(app).is_some(),
705                    "Horizontal RenderFlex with multiple children has a null textDirection, so \
706                     the layout order is undefined."
707                );
708            }
709        }
710        let main_axis_alignment = self.main_axis_alignment(app);
711        if (main_axis_alignment == MainAxisAlignment::Start
712            || main_axis_alignment == MainAxisAlignment::End)
713            && direction == Axis::Horizontal
714        {
715            debug_assert!(
716                self.text_direction(app).is_some(),
717                "Horizontal RenderFlex with {main_axis_alignment:?} has a null textDirection, so \
718                 the alignment cannot be resolved."
719            );
720        }
721        let cross_axis_alignment = self.cross_axis_alignment(app);
722        if (cross_axis_alignment == CrossAxisAlignment::Start
723            || cross_axis_alignment == CrossAxisAlignment::End)
724            && direction == Axis::Vertical
725        {
726            debug_assert!(
727                self.text_direction(app).is_some(),
728                "Vertical RenderFlex with {cross_axis_alignment:?} has a null textDirection, so \
729                 the alignment cannot be resolved."
730            );
731        }
732        true
733    }
734
735    /// The extent of `size` along the cross axis. Internal to the flex layout.
736    fn get_cross_size(self: RenderHandle<Self>, app: &App, size: Size) -> f64 {
737        match self.direction(app) {
738            Axis::Horizontal => size.height(),
739            Axis::Vertical => size.width(),
740        }
741    }
742
743    /// The extent of `size` along the main axis. Internal to the flex layout.
744    fn get_main_size(self: RenderHandle<Self>, app: &App, size: Size) -> f64 {
745        match self.direction(app) {
746            Axis::Horizontal => size.width(),
747            Axis::Vertical => size.height(),
748        }
749    }
750
751    /// Whether to lay out left-to-right / top-to-bottom (`false`), or right-to-left /
752    /// bottom-to-top (`true`). `false` when the layout direction does not matter (for instance,
753    /// there is no child). Internal to the flex layout.
754    fn flip_main_axis(self: RenderHandle<Self>, app: &App) -> bool {
755        self.first_child(app).is_some()
756            && match self.direction(app) {
757                Axis::Horizontal => self.text_direction(app) == Some(TextDirection::Rtl),
758                Axis::Vertical => self.vertical_direction(app) == VerticalDirection::Up,
759            }
760    }
761
762    /// The cross axis counterpart of [`flip_main_axis`](Self::flip_main_axis). Internal to the
763    /// flex layout.
764    fn flip_cross_axis(self: RenderHandle<Self>, app: &App) -> bool {
765        self.first_child(app).is_some()
766            && match self.direction(app) {
767                Axis::Vertical => self.text_direction(app) == Some(TextDirection::Rtl),
768                Axis::Horizontal => self.vertical_direction(app) == VerticalDirection::Up,
769            }
770    }
771
772    /// The constraints an inflexible child is laid out with. Internal to the flex layout.
773    fn constraints_for_non_flex_child(
774        self: RenderHandle<Self>,
775        app: &App,
776        constraints: BoxConstraints,
777    ) -> BoxConstraints {
778        let fill_cross_axis = self.cross_axis_alignment(app) == CrossAxisAlignment::Stretch;
779        match self.direction(app) {
780            Axis::Horizontal => {
781                if fill_cross_axis {
782                    BoxConstraints::tight_for(None, Some(constraints.max_height))
783                } else {
784                    BoxConstraints::new().max_height(constraints.max_height)
785                }
786            }
787            Axis::Vertical => {
788                if fill_cross_axis {
789                    BoxConstraints::tight_for(Some(constraints.max_width), None)
790                } else {
791                    BoxConstraints::new().max_width(constraints.max_width)
792                }
793            }
794        }
795    }
796
797    /// The constraints a flexible child is laid out with, given the main axis extent it was
798    /// allocated. Internal to the flex layout.
799    fn constraints_for_flex_child(
800        self: RenderHandle<Self>,
801        app: &App,
802        child: AnyRenderBox,
803        constraints: BoxConstraints,
804        max_child_extent: f64,
805    ) -> BoxConstraints {
806        debug_assert!(get_flex(app, child) > 0);
807        debug_assert!(max_child_extent >= 0.0);
808        let min_child_extent = match get_fit(app, child) {
809            FlexFit::Tight => max_child_extent,
810            FlexFit::Loose => 0.0,
811        };
812        let fill_cross_axis = self.cross_axis_alignment(app) == CrossAxisAlignment::Stretch;
813        match self.direction(app) {
814            Axis::Horizontal => BoxConstraints {
815                min_width: min_child_extent,
816                max_width: max_child_extent,
817                min_height: if fill_cross_axis {
818                    constraints.max_height
819                } else {
820                    0.0
821                },
822                max_height: constraints.max_height,
823            },
824            Axis::Vertical => BoxConstraints {
825                min_width: if fill_cross_axis {
826                    constraints.max_width
827                } else {
828                    0.0
829                },
830                max_width: constraints.max_width,
831                min_height: min_child_extent,
832                max_height: max_child_extent,
833            },
834        }
835    }
836
837    /// Flutter's `_getIntrinsicSize`: the intrinsic extent along `sizing_direction`, given the
838    /// `extent` in the other direction and a `child_size` that measures one child. Internal to
839    /// the flex layout.
840    fn get_intrinsic_size(
841        self: RenderHandle<Self>,
842        app: &mut App,
843        sizing_direction: Axis,
844        extent: f64,
845        child_size: ChildSizingFunction,
846    ) -> f64 {
847        let direction = self.direction(app);
848        if direction == sizing_direction {
849            // INTRINSIC MAIN SIZE
850            // Intrinsic main size is the smallest size the flex container can take while
851            // maintaining the min/max-content contributions of its flex items.
852            let mut total_flex = 0.0;
853            let mut inflexible_space = self.spacing(app) * (self.child_count(app) as f64 - 1.0);
854            let mut max_flex_fraction_so_far: f64 = 0.0;
855            let mut child = self.first_child(app);
856            while let Some(current) = child {
857                let flex = get_flex(app, current);
858                total_flex += f64::from(flex);
859                if flex > 0 {
860                    let flex_fraction = child_size(current, app, extent) / f64::from(flex);
861                    max_flex_fraction_so_far = max_flex_fraction_so_far.max(flex_fraction);
862                } else {
863                    inflexible_space += child_size(current, app, extent);
864                }
865                child = self.child_after(app, current);
866            }
867            return max_flex_fraction_so_far * total_flex + inflexible_space;
868        }
869
870        // INTRINSIC CROSS SIZE
871        // Intrinsic cross size is the max of the intrinsic cross sizes of the children, after
872        // the flexible children are fit into the available space, with the children sized using
873        // their max intrinsic dimensions.
874        let is_horizontal = direction == Axis::Horizontal;
875        let layout_child = |app: &mut App, child: AnyRenderBox, constraints: BoxConstraints| {
876            let main_axis_size_from_constraints = if is_horizontal {
877                constraints.max_width
878            } else {
879                constraints.max_height
880            };
881            // An infinite main_axis_size_from_constraints means this child is flexible (or
882            // extent is infinite).
883            debug_assert_eq!(
884                get_flex(app, child) != 0 && extent.is_finite(),
885                main_axis_size_from_constraints.is_finite()
886            );
887            let max_main_axis_size = if main_axis_size_from_constraints.is_finite() {
888                main_axis_size_from_constraints
889            } else if is_horizontal {
890                child.get_max_intrinsic_width(app, f64::INFINITY)
891            } else {
892                child.get_max_intrinsic_height(app, f64::INFINITY)
893            };
894            let cross_size = child_size(child, app, max_main_axis_size);
895            if is_horizontal {
896                Size::new(max_main_axis_size, cross_size)
897            } else {
898                Size::new(cross_size, max_main_axis_size)
899            }
900        };
901        let constraints = if is_horizontal {
902            BoxConstraints::new().max_width(extent)
903        } else {
904            BoxConstraints::new().max_height(extent)
905        };
906        self.compute_sizes(app, constraints, layout_child)
907            .axis_size
908            .cross_axis_extent()
909    }
910
911    /// Flutter's `_debugCheckConstraints`: the error to report when a child has a non-zero flex
912    /// factor while the incoming main axis constraint is unbounded and that child would have to
913    /// fill it, or `None` when the constraints are usable. Internal to the flex layout.
914    fn debug_check_constraints(
915        self: RenderHandle<Self>,
916        app: &App,
917        constraints: BoxConstraints,
918    ) -> Option<String> {
919        let direction = self.direction(app);
920        let max_main_size = match direction {
921            Axis::Horizontal => constraints.max_width,
922            Axis::Vertical => constraints.max_height,
923        };
924        let can_flex = max_main_size < f64::INFINITY;
925        let mut child = self.first_child(app);
926        while let Some(current) = child {
927            if get_flex(app, current) > 0
928                && !can_flex
929                && (self.main_axis_size(app) == MainAxisSize::Max
930                    || get_fit(app, current) == FlexFit::Tight)
931            {
932                let (identity, axis, dimension) = match direction {
933                    Axis::Horizontal => ("row", "horizontal", "width"),
934                    Axis::Vertical => ("column", "vertical", "height"),
935                };
936                return Some(format!(
937                    "RenderFlex children have non-zero flex but incoming {dimension} constraints \
938                     are unbounded.\n\
939                     When a {identity} is in a parent that does not provide a finite {dimension} \
940                     constraint, for example if it is in a {axis} scrollable, it will try to \
941                     shrink-wrap its children along the {axis} axis. Setting a flex on a child \
942                     (e.g. using Expanded) indicates that the child is to expand to fill the \
943                     remaining space in the {axis} direction.\n\
944                     These two directives are mutually exclusive. If a parent is to shrink-wrap \
945                     its child, the child cannot simultaneously expand to fit its parent.\n\
946                     Consider setting mainAxisSize to MainAxisSize.min and using FlexFit.loose \
947                     fits for the flexible children (using Flexible rather than Expanded). This \
948                     will allow the flexible children to size themselves to less than the \
949                     infinite remaining space they would otherwise be forced to take, and then \
950                     will cause the RenderFlex to shrink-wrap the children rather than expanding \
951                     to fit the maximum constraints provided by the parent."
952                ));
953            }
954            child = self.child_after(app, current);
955        }
956        None
957    }
958
959    /// Flutter's `_computeSizes`: measures the children with `layout_child` and works out this
960    /// flex's own size. Internal to the flex layout.
961    fn compute_sizes(
962        self: RenderHandle<Self>,
963        app: &mut App,
964        constraints: BoxConstraints,
965        mut layout_child: impl FnMut(&mut App, AnyRenderBox, BoxConstraints) -> Size,
966    ) -> LayoutSizes {
967        debug_assert!(self.debug_has_necessary_directions(app));
968
969        // Determine used flex factor, size inflexible items, calculate free space.
970        let direction = self.direction(app);
971        let max_main_size = self.get_main_size(app, constraints.biggest());
972        let can_flex = max_main_size.is_finite();
973        let non_flex_child_constraints = self.constraints_for_non_flex_child(app, constraints);
974
975        // The first pass lays out non-flex children and computes total flex.
976        let mut total_flex = 0;
977        let mut first_flex_child: Option<AnyRenderBox> = None;
978        // Initially, accumulated_size is the sum of the spaces between children in the main axis.
979        let mut accumulated_size = AxisSize::new(
980            self.spacing(app) * (self.child_count(app) as f64 - 1.0),
981            0.0,
982        );
983        let mut child = self.first_child(app);
984        while let Some(current) = child {
985            let flex = get_flex(app, current);
986            if can_flex && flex > 0 {
987                total_flex += flex;
988                first_flex_child.get_or_insert(current);
989            } else {
990                let child_size = layout_child(app, current, non_flex_child_constraints);
991                accumulated_size = accumulated_size + AxisSize::from_size(child_size, direction);
992            }
993            child = self.child_after(app, current);
994        }
995
996        debug_assert_eq!(total_flex == 0, first_flex_child.is_none());
997        // If we are given infinite space there's no need for this extra step.
998        debug_assert!(first_flex_child.is_none() || can_flex);
999
1000        // The second pass distributes free space to flexible children.
1001        let flex_space = (max_main_size - accumulated_size.main_axis_extent()).max(0.0);
1002        let space_per_flex = flex_space / total_flex as f64;
1003        let mut child = first_flex_child;
1004        while let Some(current) = child {
1005            if total_flex == 0 {
1006                break;
1007            }
1008            let flex = get_flex(app, current);
1009            if flex != 0 {
1010                total_flex -= flex;
1011                debug_assert!(space_per_flex.is_finite());
1012                let max_child_extent = space_per_flex * f64::from(flex);
1013                debug_assert!(
1014                    get_fit(app, current) == FlexFit::Loose || max_child_extent < f64::INFINITY
1015                );
1016                let child_constraints =
1017                    self.constraints_for_flex_child(app, current, constraints, max_child_extent);
1018                let child_size = layout_child(app, current, child_constraints);
1019                accumulated_size = accumulated_size + AxisSize::from_size(child_size, direction);
1020            }
1021            child = self.child_after(app, current);
1022        }
1023        debug_assert_eq!(total_flex, 0);
1024
1025        let ideal_main_size = match self.main_axis_size(app) {
1026            MainAxisSize::Max if max_main_size.is_finite() => max_main_size,
1027            MainAxisSize::Max | MainAxisSize::Min => accumulated_size.main_axis_extent(),
1028        };
1029
1030        let constrained_size = AxisSize::new(ideal_main_size, accumulated_size.cross_axis_extent())
1031            .apply_constraints(constraints, direction);
1032        LayoutSizes {
1033            axis_size: constrained_size,
1034            main_axis_free_space: constrained_size.main_axis_extent()
1035                - accumulated_size.main_axis_extent(),
1036        }
1037    }
1038
1039    /// Sizes this flex and positions its children; see [`RenderFlex`] for the algorithm.
1040    fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
1041        let constraints = self.constraints(app);
1042        if cfg!(debug_assertions)
1043            && let Some(error) = self.debug_check_constraints(app, constraints)
1044        {
1045            panic!("{error}");
1046        }
1047
1048        let sizes = self.compute_sizes(app, constraints, ChildLayoutHelper::layout_child);
1049
1050        let direction = self.direction(app);
1051        let cross_axis_extent = sizes.axis_size.cross_axis_extent();
1052        self.set_size(app, sizes.axis_size.to_size(direction));
1053        self.flex_data_mut(app).overflow = (-sizes.main_axis_free_space).max(0.0);
1054
1055        let remaining_space = sizes.main_axis_free_space.max(0.0);
1056        let flip_main_axis = self.flip_main_axis(app);
1057        let flip_cross_axis = self.flip_cross_axis(app);
1058        let cross_axis_alignment = self.cross_axis_alignment(app);
1059        let (leading_space, between_space) = self.main_axis_alignment(app).distribute_space(
1060            remaining_space,
1061            self.child_count(app),
1062            flip_main_axis,
1063            self.spacing(app),
1064        );
1065        let top_left_child = if flip_main_axis {
1066            self.last_child(app)
1067        } else {
1068            self.first_child(app)
1069        };
1070
1071        // Position all children in visual order: starting from the top-left child and work
1072        // towards the child that's farthest away from the origin.
1073        let mut child_main_position = leading_space;
1074        let mut child = top_left_child;
1075        while let Some(current) = child {
1076            let child_size = current.size(app);
1077            let free_cross_space = cross_axis_extent - self.get_cross_size(app, child_size);
1078            let child_cross_position = if cross_axis_alignment == CrossAxisAlignment::Baseline
1079                && direction == Axis::Horizontal
1080            {
1081                // In a baseline-aligned horizontal flex, a child without a baseline is aligned to
1082                // the top of the cross axis (dy = 0), regardless of verticalDirection or
1083                // crossAxisAlignment. That is, we intentionally ignore flip_cross_axis here.
1084                CrossAxisAlignment::Start.get_child_cross_axis_offset(free_cross_space, false)
1085            } else {
1086                // Non-baseline alignment respects the configured cross_axis_alignment.
1087                cross_axis_alignment.get_child_cross_axis_offset(free_cross_space, flip_cross_axis)
1088            };
1089            let offset = match direction {
1090                Axis::Horizontal => Offset::new(child_main_position, child_cross_position),
1091                Axis::Vertical => Offset::new(child_cross_position, child_main_position),
1092            };
1093            current
1094                .parent_data_of_mut::<FlexParentData>(app)
1095                .set_offset(offset);
1096            child_main_position += self.get_main_size(app, child_size) + between_space;
1097            child = if flip_main_axis {
1098                self.child_before(app, current)
1099            } else {
1100                self.child_after(app, current)
1101            };
1102        }
1103    }
1104
1105    /// Paints the children, clipping them to this flex when they overflow the main axis.
1106    fn paint(
1107        self: RenderHandle<Self>,
1108        app: &mut App,
1109        context: &mut PaintingContext,
1110        offset: Offset,
1111    ) {
1112        if !self.has_overflow(app) {
1113            self.default_paint(app, context, offset);
1114            return;
1115        }
1116
1117        // There's no point in drawing the children if we're empty.
1118        let size = self.size(app);
1119        if size.is_empty() {
1120            return;
1121        }
1122
1123        let clip_behavior = self.clip_behavior(app);
1124        let old = self.flex_data(app).clip_rect_layer.layer();
1125        let layer = context.push_clip_rect(
1126            app,
1127            self.as_object().needs_compositing(app),
1128            offset,
1129            Offset::ZERO & size,
1130            |app, context, offset| self.default_paint(app, context, offset),
1131            clip_behavior,
1132            old,
1133        );
1134        LayerHandle::set_layer(
1135            app,
1136            |app| &mut self.flex_data_mut(app).clip_rect_layer,
1137            layer,
1138        );
1139    }
1140
1141    /// Flutter's `dispose`: drop the clip layer handle, then `super.dispose()`.
1142    fn dispose(self: RenderHandle<Self>, app: &mut App) {
1143        LayerHandle::set_layer(
1144            app,
1145            |app| &mut self.flex_data_mut(app).clip_rect_layer,
1146            None,
1147        );
1148        crate::object::RenderObjectBase::dispose(self, app);
1149    }
1150
1151    /// A flex child gets a [`FlexParentData`].
1152    fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
1153        if !child.parent_data_is::<FlexParentData>(app) {
1154            child.set_parent_data(app, FlexParentData::new());
1155        }
1156    }
1157
1158    /// The minimum intrinsic width of the children laid out in a flex.
1159    fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1160        self.get_intrinsic_size(
1161            app,
1162            Axis::Horizontal,
1163            height,
1164            AnyRenderBox::get_min_intrinsic_width,
1165        )
1166    }
1167
1168    /// The maximum intrinsic width of the children laid out in a flex.
1169    fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1170        self.get_intrinsic_size(
1171            app,
1172            Axis::Horizontal,
1173            height,
1174            AnyRenderBox::get_max_intrinsic_width,
1175        )
1176    }
1177
1178    /// The minimum intrinsic height of the children laid out in a flex.
1179    fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1180        self.get_intrinsic_size(
1181            app,
1182            Axis::Vertical,
1183            width,
1184            AnyRenderBox::get_min_intrinsic_height,
1185        )
1186    }
1187
1188    /// The maximum intrinsic height of the children laid out in a flex.
1189    fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1190        self.get_intrinsic_size(
1191            app,
1192            Axis::Vertical,
1193            width,
1194            AnyRenderBox::get_max_intrinsic_height,
1195        )
1196    }
1197
1198    /// The size this flex would take, measuring the children dry.
1199    fn compute_dry_layout(
1200        self: RenderHandle<Self>,
1201        app: &mut App,
1202        constraints: BoxConstraints,
1203    ) -> Size {
1204        if cfg!(debug_assertions)
1205            && let Some(error) = self.debug_check_constraints(app, constraints)
1206        {
1207            self.debug_cannot_compute_dry_layout(DryLayoutFailure::Error(&error));
1208            return Size::ZERO;
1209        }
1210        let direction = self.direction(app);
1211        self.compute_sizes(app, constraints, ChildLayoutHelper::dry_layout_child)
1212            .axis_size
1213            .to_size(direction)
1214    }
1215
1216    /// Hit tests the children in paint order.
1217    fn hit_test_children(
1218        self: RenderHandle<Self>,
1219        app: &mut App,
1220        result: &mut BoxHitTestResult<'_>,
1221        position: Offset,
1222    ) -> bool {
1223        self.default_hit_test_children(app, result, position)
1224    }
1225}
1226
1227/// Flutter's `RenderFlex._getFlex`.
1228fn get_flex(app: &App, child: AnyRenderBox) -> i32 {
1229    child
1230        .as_object()
1231        .parent_data_of::<FlexParentData>(app)
1232        .flex
1233        .unwrap_or(0)
1234}
1235
1236/// Flutter's `RenderFlex._getFit`.
1237fn get_fit(app: &App, child: AnyRenderBox) -> FlexFit {
1238    child
1239        .as_object()
1240        .parent_data_of::<FlexParentData>(app)
1241        .fit
1242        .unwrap_or(FlexFit::Tight)
1243}
1244
1245/// Displays its children in a one-dimensional array.
1246///
1247/// ## Layout algorithm
1248///
1249/// _This section describes how the framework causes [`RenderFlex`] to position its children._
1250/// _See [`BoxConstraints`] for an introduction to box layout models._
1251///
1252/// Layout for a [`RenderFlex`] proceeds in six steps:
1253///
1254/// 1. Layout each child with a null or zero flex factor with unbounded main axis constraints and
1255///    the incoming cross axis constraints. If the [`cross_axis_alignment`](Self::cross_axis_alignment)
1256///    is [`CrossAxisAlignment::Stretch`], instead use tight cross axis constraints that match the
1257///    incoming max extent in the cross axis.
1258/// 2. Divide the remaining main axis space among the children with non-zero flex factors
1259///    according to their flex factor. For example, a child with a flex factor of 2.0 will receive
1260///    twice the amount of main axis space as a child with a flex factor of 1.0.
1261/// 3. Layout each of the remaining children with the same cross axis constraints as in step 1,
1262///    but instead of using unbounded main axis constraints, use max axis constraints based on the
1263///    amount of space allocated in step 2. Children with [`FlexFit::Tight`] are given tight
1264///    constraints (i.e., forced to fill the allocated space), and children with [`FlexFit::Loose`]
1265///    are given loose constraints (i.e., not forced to fill the allocated space).
1266/// 4. The cross axis extent of the [`RenderFlex`] is the maximum cross axis extent of the
1267///    children (which will always satisfy the incoming constraints).
1268/// 5. The main axis extent of the [`RenderFlex`] is determined by the
1269///    [`main_axis_size`](Self::main_axis_size) property. If it is [`MainAxisSize::Max`], then the
1270///    main axis extent of the [`RenderFlex`] is the max extent of the incoming main axis
1271///    constraints. If it is [`MainAxisSize::Min`], then the main axis extent is the sum of the
1272///    main axis extents of the children (subject to the incoming constraints).
1273/// 6. Determine the position for each child according to the
1274///    [`main_axis_alignment`](Self::main_axis_alignment) and the
1275///    [`cross_axis_alignment`](Self::cross_axis_alignment). For example, if the
1276///    [`main_axis_alignment`](Self::main_axis_alignment) is [`MainAxisAlignment::SpaceBetween`],
1277///    any main axis space that has not been allocated to children is divided evenly and placed
1278///    between the children.
1279pub struct RenderFlex {
1280    render_object: RenderObjectData,
1281    render_box: RenderBoxData,
1282    container: ContainerRenderObjectData<AnyRenderBox>,
1283    flex: RenderFlexData,
1284}
1285
1286impl RenderFlex {
1287    /// Creates a flex render object.
1288    ///
1289    /// By default, the flex layout is horizontal and children are aligned to the start of the
1290    /// main axis and the center of the cross axis. Add children with
1291    /// [`add`](ContainerRenderObjectMixin::add) or
1292    /// [`add_all`](ContainerRenderObjectMixin::add_all); Dart's constructor takes them.
1293    pub fn new(app: &mut App) -> RenderHandle<RenderFlex> {
1294        RenderHandle::new_box(
1295            app,
1296            RenderFlex {
1297                render_object: RenderObjectData::new(),
1298                render_box: RenderBoxData::new(),
1299                container: ContainerRenderObjectData::new(),
1300                flex: RenderFlexData::new(),
1301            },
1302        )
1303    }
1304
1305    /// See [`RenderFlexMixin::direction`].
1306    pub fn direction(self: RenderHandle<Self>, app: &App) -> Axis {
1307        RenderFlexMixin::direction(self, app)
1308    }
1309
1310    /// See [`RenderFlexMixin::set_direction`].
1311    pub fn set_direction(self: RenderHandle<Self>, app: &mut App, value: Axis) {
1312        RenderFlexMixin::set_direction(self, app, value)
1313    }
1314
1315    /// See [`RenderFlexMixin::main_axis_alignment`].
1316    pub fn main_axis_alignment(self: RenderHandle<Self>, app: &App) -> MainAxisAlignment {
1317        RenderFlexMixin::main_axis_alignment(self, app)
1318    }
1319
1320    /// See [`RenderFlexMixin::set_main_axis_alignment`].
1321    pub fn set_main_axis_alignment(
1322        self: RenderHandle<Self>,
1323        app: &mut App,
1324        value: MainAxisAlignment,
1325    ) {
1326        RenderFlexMixin::set_main_axis_alignment(self, app, value)
1327    }
1328
1329    /// See [`RenderFlexMixin::main_axis_size`].
1330    pub fn main_axis_size(self: RenderHandle<Self>, app: &App) -> MainAxisSize {
1331        RenderFlexMixin::main_axis_size(self, app)
1332    }
1333
1334    /// See [`RenderFlexMixin::set_main_axis_size`].
1335    pub fn set_main_axis_size(self: RenderHandle<Self>, app: &mut App, value: MainAxisSize) {
1336        RenderFlexMixin::set_main_axis_size(self, app, value)
1337    }
1338
1339    /// See [`RenderFlexMixin::cross_axis_alignment`].
1340    pub fn cross_axis_alignment(self: RenderHandle<Self>, app: &App) -> CrossAxisAlignment {
1341        RenderFlexMixin::cross_axis_alignment(self, app)
1342    }
1343
1344    /// See [`RenderFlexMixin::set_cross_axis_alignment`].
1345    pub fn set_cross_axis_alignment(
1346        self: RenderHandle<Self>,
1347        app: &mut App,
1348        value: CrossAxisAlignment,
1349    ) {
1350        RenderFlexMixin::set_cross_axis_alignment(self, app, value)
1351    }
1352
1353    /// See [`RenderFlexMixin::text_direction`].
1354    pub fn text_direction(self: RenderHandle<Self>, app: &App) -> Option<TextDirection> {
1355        RenderFlexMixin::text_direction(self, app)
1356    }
1357
1358    /// See [`RenderFlexMixin::set_text_direction`].
1359    pub fn set_text_direction(
1360        self: RenderHandle<Self>,
1361        app: &mut App,
1362        value: Option<TextDirection>,
1363    ) {
1364        RenderFlexMixin::set_text_direction(self, app, value)
1365    }
1366
1367    /// See [`RenderFlexMixin::vertical_direction`].
1368    pub fn vertical_direction(self: RenderHandle<Self>, app: &App) -> VerticalDirection {
1369        RenderFlexMixin::vertical_direction(self, app)
1370    }
1371
1372    /// See [`RenderFlexMixin::set_vertical_direction`].
1373    pub fn set_vertical_direction(
1374        self: RenderHandle<Self>,
1375        app: &mut App,
1376        value: VerticalDirection,
1377    ) {
1378        RenderFlexMixin::set_vertical_direction(self, app, value)
1379    }
1380
1381    /// See [`RenderFlexMixin::text_baseline`].
1382    pub fn text_baseline(self: RenderHandle<Self>, app: &App) -> Option<TextBaseline> {
1383        RenderFlexMixin::text_baseline(self, app)
1384    }
1385
1386    /// See [`RenderFlexMixin::set_text_baseline`].
1387    pub fn set_text_baseline(self: RenderHandle<Self>, app: &mut App, value: Option<TextBaseline>) {
1388        RenderFlexMixin::set_text_baseline(self, app, value)
1389    }
1390
1391    /// See [`RenderFlexMixin::clip_behavior`].
1392    pub fn clip_behavior(self: RenderHandle<Self>, app: &App) -> Clip {
1393        RenderFlexMixin::clip_behavior(self, app)
1394    }
1395
1396    /// See [`RenderFlexMixin::set_clip_behavior`].
1397    pub fn set_clip_behavior(self: RenderHandle<Self>, app: &mut App, value: Clip) {
1398        RenderFlexMixin::set_clip_behavior(self, app, value)
1399    }
1400
1401    /// See [`RenderFlexMixin::spacing`].
1402    pub fn spacing(self: RenderHandle<Self>, app: &App) -> f64 {
1403        RenderFlexMixin::spacing(self, app)
1404    }
1405
1406    /// See [`RenderFlexMixin::set_spacing`].
1407    pub fn set_spacing(self: RenderHandle<Self>, app: &mut App, value: f64) {
1408        RenderFlexMixin::set_spacing(self, app, value)
1409    }
1410}
1411
1412impl ContainerRenderObjectMixin for RenderFlex {
1413    type ChildType = AnyRenderBox;
1414    type ParentDataType = FlexParentData;
1415
1416    fn container_data(
1417        self: RenderHandle<Self>,
1418        app: &App,
1419    ) -> &ContainerRenderObjectData<AnyRenderBox> {
1420        &self.get(app).container
1421    }
1422
1423    fn container_data_mut(
1424        self: RenderHandle<Self>,
1425        app: &mut App,
1426    ) -> &mut ContainerRenderObjectData<AnyRenderBox> {
1427        &mut self.get_mut(app).container
1428    }
1429}
1430
1431impl RenderBoxContainerDefaultsMixin for RenderFlex {}
1432
1433impl RenderFlexMixin for RenderFlex {
1434    fn flex_data(self: RenderHandle<Self>, app: &App) -> &RenderFlexData {
1435        &self.get(app).flex
1436    }
1437
1438    fn flex_data_mut(self: RenderHandle<Self>, app: &mut App) -> &mut RenderFlexData {
1439        &mut self.get_mut(app).flex
1440    }
1441}
1442
1443impl RenderObject for RenderFlex {
1444    crate::render_object_accessors!();
1445
1446    fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
1447        RenderFlexMixin::perform_layout(self, app)
1448    }
1449
1450    fn visit_children(
1451        self: RenderHandle<Self>,
1452        app: &App,
1453        visitor: &mut dyn FnMut(AnyRenderObject),
1454    ) {
1455        ContainerRenderObjectMixin::visit_children(self, app, visitor)
1456    }
1457
1458    fn did_attach(self: RenderHandle<Self>, app: &mut App, owner: Handle<PipelineOwner>) {
1459        ContainerRenderObjectMixin::did_attach(self, app, owner)
1460    }
1461
1462    fn did_detach(self: RenderHandle<Self>, app: &mut App) {
1463        ContainerRenderObjectMixin::did_detach(self, app)
1464    }
1465
1466    fn redepth_children(self: RenderHandle<Self>, app: &mut App) {
1467        ContainerRenderObjectMixin::redepth_children(self, app)
1468    }
1469
1470    fn paint(
1471        self: RenderHandle<Self>,
1472        app: &mut App,
1473        context: &mut PaintingContext,
1474        offset: Offset,
1475    ) {
1476        RenderFlexMixin::paint(self, app, context, offset)
1477    }
1478
1479    fn dispose(self: RenderHandle<Self>, app: &mut App) {
1480        RenderFlexMixin::dispose(self, app)
1481    }
1482}
1483
1484impl RenderBox for RenderFlex {
1485    crate::render_box_accessors!();
1486
1487    fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
1488        RenderFlexMixin::setup_parent_data(self, app, child)
1489    }
1490
1491    fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1492        RenderFlexMixin::compute_min_intrinsic_width(self, app, height)
1493    }
1494
1495    fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1496        RenderFlexMixin::compute_max_intrinsic_width(self, app, height)
1497    }
1498
1499    fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1500        RenderFlexMixin::compute_min_intrinsic_height(self, app, width)
1501    }
1502
1503    fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1504        RenderFlexMixin::compute_max_intrinsic_height(self, app, width)
1505    }
1506
1507    fn compute_dry_layout(
1508        self: RenderHandle<Self>,
1509        app: &mut App,
1510        constraints: BoxConstraints,
1511    ) -> Size {
1512        RenderFlexMixin::compute_dry_layout(self, app, constraints)
1513    }
1514
1515    fn hit_test_children(
1516        self: RenderHandle<Self>,
1517        app: &mut App,
1518        result: &mut BoxHitTestResult<'_>,
1519        position: Offset,
1520    ) -> bool {
1521        RenderFlexMixin::hit_test_children(self, app, result, position)
1522    }
1523}
1524
1525#[cfg(test)]
1526mod tests {
1527    use inset_foundation::AppCell;
1528    use std::any::Any;
1529
1530    use inset_foundation::Handle;
1531    use inset_gestures::{HitTestEntry, HitTestResult};
1532
1533    use super::*;
1534    use crate::box_::BoxHitTestEntry;
1535    use crate::layer::{ContainerLayer, ErasedLayer, OffsetLayer, PictureLayer};
1536    use crate::pipeline_owner::PipelineOwner;
1537    use crate::proxy_box::RenderRepaintBoundary;
1538    use inset_embedder::valo::Op;
1539
1540    /// A leaf that takes its preferred size where the constraints allow, and answers hit tests
1541    /// within its bounds.
1542    struct SizedTestBox {
1543        render_object: RenderObjectData,
1544        render_box: RenderBoxData,
1545        preferred_size: Size,
1546    }
1547
1548    impl RenderObject for SizedTestBox {
1549        crate::render_object_accessors!();
1550
1551        fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
1552            let preferred_size = self.get(app).preferred_size;
1553            let size = self.constraints(app).constrain(preferred_size);
1554            self.set_size(app, size);
1555        }
1556    }
1557
1558    impl RenderBox for SizedTestBox {
1559        crate::render_box_accessors!();
1560
1561        fn hit_test_self(self: RenderHandle<Self>, _app: &App, _position: Offset) -> bool {
1562            true
1563        }
1564    }
1565
1566    fn sized_box(app: &mut App, size: Size) -> AnyRenderBox {
1567        RenderHandle::new_box(
1568            app,
1569            SizedTestBox {
1570                render_object: RenderObjectData::new(),
1571                render_box: RenderBoxData::new(),
1572                preferred_size: size,
1573            },
1574        )
1575        .as_box()
1576    }
1577
1578    /// A left-to-right [`RenderFlex`] holding `children`.
1579    fn row(app: &mut App, children: Vec<AnyRenderBox>) -> RenderHandle<RenderFlex> {
1580        let flex = RenderFlex::new(app);
1581        flex.set_text_direction(app, Some(TextDirection::Ltr));
1582        flex.add_all(app, Some(children));
1583        flex
1584    }
1585
1586    fn offset_of(app: &App, child: AnyRenderBox) -> Offset {
1587        child.box_parent_data(app).offset
1588    }
1589
1590    fn schedule_root_paint(app: &mut App, node: AnyRenderObject) {
1591        let root = OffsetLayer::new(app, Offset::ZERO);
1592        root.as_layer().attach(app, node.id());
1593        node.schedule_initial_paint(app, root.as_container_layer());
1594    }
1595
1596    /// The test binding's first frame: attach, lay the root out, schedule and flush paint.
1597    fn first_frame(
1598        app: &mut App,
1599        root: AnyRenderBox,
1600        constraints: BoxConstraints,
1601    ) -> Handle<PipelineOwner> {
1602        let owner = PipelineOwner::new(app, None, None);
1603        owner.set_root_node(app, Some(root.as_object()));
1604        root.layout(app, constraints, false);
1605        schedule_root_paint(app, root.as_object());
1606        owner.flush_compositing_bits(app);
1607        owner.flush_paint(app);
1608        owner
1609    }
1610
1611    fn picture_has_clip(app: &App, boundary: AnyRenderObject) -> bool {
1612        let layer = boundary.debug_layer(app).expect("painted");
1613        layer
1614            .depth_first_iterate_children(app)
1615            .into_iter()
1616            .filter_map(|child| {
1617                app.handle::<PictureLayer>(child.id())
1618                    .and_then(|picture| picture.picture(app).map(|p| p.ops().to_vec()))
1619            })
1620            .flatten()
1621            .any(|op| matches!(op, Op::ClipPath { .. }))
1622    }
1623
1624    fn hit_boxes(result: &HitTestResult) -> Vec<AnyRenderBox> {
1625        result
1626            .path()
1627            .iter()
1628            .filter_map(|entry: &HitTestEntry| {
1629                let target: &dyn Any = entry.target();
1630                target
1631                    .downcast_ref::<BoxHitTestEntry>()
1632                    .map(BoxHitTestEntry::target)
1633            })
1634            .collect()
1635    }
1636
1637    /// `object.dart`'s `ContainerRenderObjectMixin`: the child list is a doubly-linked list that
1638    /// `insert` / `move` / `remove` keep consistent from both ends.
1639    #[test]
1640    fn child_list_insert_move_and_remove() {
1641        let cell = AppCell::new();
1642        let mut app = cell.borrow_mut();
1643        let first = sized_box(&mut app, Size::new(10.0, 10.0));
1644        let middle = sized_box(&mut app, Size::new(10.0, 10.0));
1645        let last = sized_box(&mut app, Size::new(10.0, 10.0));
1646        let flex = RenderFlex::new(&mut app);
1647
1648        flex.add(&mut app, first);
1649        flex.insert(&mut app, last, Some(first));
1650        flex.insert(&mut app, middle, Some(first));
1651        assert_eq!(flex.child_count(&app), 3);
1652        assert_eq!(flex.children_as_list(&app), vec![first, middle, last]);
1653        assert_eq!(flex.first_child(&app), Some(first));
1654        assert_eq!(flex.last_child(&app), Some(last));
1655        assert_eq!(flex.child_after(&app, first), Some(middle));
1656        assert_eq!(flex.child_before(&app, last), Some(middle));
1657
1658        let mut visited = Vec::new();
1659        flex.as_object()
1660            .visit_children(&app, &mut |child| visited.push(child));
1661        assert_eq!(
1662            visited,
1663            vec![first.as_object(), middle.as_object(), last.as_object()]
1664        );
1665
1666        flex.move_child(&mut app, first, Some(last));
1667        assert_eq!(flex.children_as_list(&app), vec![middle, last, first]);
1668        flex.move_child(&mut app, first, None);
1669        assert_eq!(flex.children_as_list(&app), vec![first, middle, last]);
1670
1671        flex.remove(&mut app, middle);
1672        assert_eq!(flex.children_as_list(&app), vec![first, last]);
1673        assert_eq!(flex.child_count(&app), 2);
1674        assert!(middle.as_object().parent(&app).is_none());
1675
1676        flex.remove_all(&mut app);
1677        assert_eq!(flex.child_count(&app), 0);
1678        assert!(flex.first_child(&app).is_none());
1679        assert!(flex.last_child(&app).is_none());
1680        assert!(first.as_object().parent(&app).is_none());
1681        assert!(last.as_object().parent(&app).is_none());
1682    }
1683
1684    /// `flex_test.dart`: a row gives the flexible child the space the fixed one leaves, and the
1685    /// spacing goes between them.
1686    /// A box with fixed intrinsic dimensions that takes the size its constraints allow around
1687    /// them.
1688    struct IntrinsicTestBox {
1689        render_object: RenderObjectData,
1690        render_box: RenderBoxData,
1691        intrinsic: Size,
1692    }
1693
1694    impl RenderObject for IntrinsicTestBox {
1695        crate::render_object_accessors!();
1696
1697        fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
1698            let intrinsic = self.get(app).intrinsic;
1699            let size = self.constraints(app).constrain(intrinsic);
1700            self.set_size(app, size);
1701        }
1702    }
1703
1704    impl RenderBox for IntrinsicTestBox {
1705        crate::render_box_accessors!();
1706
1707        fn compute_min_intrinsic_width(
1708            self: RenderHandle<Self>,
1709            app: &mut App,
1710            _height: f64,
1711        ) -> f64 {
1712            self.get(app).intrinsic.width()
1713        }
1714
1715        fn compute_max_intrinsic_width(
1716            self: RenderHandle<Self>,
1717            app: &mut App,
1718            _height: f64,
1719        ) -> f64 {
1720            self.get(app).intrinsic.width()
1721        }
1722
1723        fn compute_min_intrinsic_height(
1724            self: RenderHandle<Self>,
1725            app: &mut App,
1726            _width: f64,
1727        ) -> f64 {
1728            self.get(app).intrinsic.height()
1729        }
1730
1731        fn compute_max_intrinsic_height(
1732            self: RenderHandle<Self>,
1733            app: &mut App,
1734            _width: f64,
1735        ) -> f64 {
1736            self.get(app).intrinsic.height()
1737        }
1738
1739        fn compute_dry_layout(
1740            self: RenderHandle<Self>,
1741            app: &mut App,
1742            constraints: BoxConstraints,
1743        ) -> Size {
1744            constraints.constrain(self.get(app).intrinsic)
1745        }
1746    }
1747
1748    fn intrinsic_box(app: &mut App, intrinsic: Size) -> AnyRenderBox {
1749        RenderHandle::new_box(
1750            app,
1751            IntrinsicTestBox {
1752                render_object: RenderObjectData::new(),
1753                render_box: RenderBoxData::new(),
1754                intrinsic,
1755            },
1756        )
1757        .as_box()
1758    }
1759
1760    /// A row of a fixed child and a flexible one: `flex_test.dart`'s intrinsics, where the main
1761    /// axis intrinsic is the inflexible space plus the largest flex fraction, and the cross axis
1762    /// intrinsic is the tallest child.
1763    #[test]
1764    fn row_intrinsics_add_the_inflexible_space_to_the_largest_flex_fraction() {
1765        let cell = AppCell::new();
1766        let mut app = cell.borrow_mut();
1767        let fixed = intrinsic_box(&mut app, Size::new(50.0, 20.0));
1768        let flexible = intrinsic_box(&mut app, Size::new(30.0, 10.0));
1769        let flex = row(&mut app, vec![fixed, flexible]);
1770        flexible.parent_data_of_mut::<FlexParentData>(&mut app).flex = Some(1);
1771
1772        let row = flex.as_box();
1773        assert_eq!(row.get_max_intrinsic_width(&mut app, f64::INFINITY), 80.0);
1774        assert_eq!(row.get_min_intrinsic_width(&mut app, f64::INFINITY), 80.0);
1775        assert_eq!(row.get_max_intrinsic_height(&mut app, 100.0), 20.0);
1776    }
1777
1778    /// The spacing between the children counts towards the main axis intrinsic.
1779    #[test]
1780    fn row_intrinsics_include_the_spacing() {
1781        let cell = AppCell::new();
1782        let mut app = cell.borrow_mut();
1783        let first = intrinsic_box(&mut app, Size::new(50.0, 20.0));
1784        let second = intrinsic_box(&mut app, Size::new(30.0, 10.0));
1785        let flex = row(&mut app, vec![first, second]);
1786        flex.set_spacing(&mut app, 10.0);
1787        assert_eq!(
1788            flex.as_box()
1789                .get_max_intrinsic_width(&mut app, f64::INFINITY),
1790            90.0
1791        );
1792    }
1793
1794    /// The dry layout takes the main axis size into account: a `Max` row fills the constraints,
1795    /// a `Min` row shrink-wraps its children, and both match what layout produces.
1796    #[test]
1797    fn row_dry_layout_follows_the_main_axis_size() {
1798        for (main_axis_size, expected) in [
1799            (MainAxisSize::Max, Size::new(200.0, 20.0)),
1800            (MainAxisSize::Min, Size::new(80.0, 20.0)),
1801        ] {
1802            let cell = AppCell::new();
1803            let mut app = cell.borrow_mut();
1804            let fixed = intrinsic_box(&mut app, Size::new(50.0, 20.0));
1805            let flexible = intrinsic_box(&mut app, Size::new(30.0, 10.0));
1806            let flex = row(&mut app, vec![fixed, flexible]);
1807            flex.set_main_axis_size(&mut app, main_axis_size);
1808            {
1809                let parent_data = flexible.parent_data_of_mut::<FlexParentData>(&mut app);
1810                parent_data.flex = Some(1);
1811                parent_data.fit = Some(FlexFit::Loose);
1812            }
1813
1814            let constraints = BoxConstraints::new().max_width(200.0).max_height(100.0);
1815            let dry = flex.as_box().get_dry_layout(&mut app, constraints);
1816            flex.layout(&mut app, constraints, false);
1817            assert_eq!(dry, expected);
1818            assert_eq!(dry, flex.size(&app), "the dry layout is the real layout");
1819        }
1820    }
1821
1822    #[test]
1823    fn row_lays_out_fixed_and_flexible_children_with_spacing() {
1824        let cell = AppCell::new();
1825        let mut app = cell.borrow_mut();
1826        let fixed = sized_box(&mut app, Size::new(50.0, 30.0));
1827        let flexible = sized_box(&mut app, Size::new(20.0, 10.0));
1828        let flex = row(&mut app, vec![fixed, flexible]);
1829        flexible.parent_data_of_mut::<FlexParentData>(&mut app).flex = Some(1);
1830        flex.set_spacing(&mut app, 10.0);
1831
1832        flex.layout(
1833            &mut app,
1834            BoxConstraints::tight(Size::new(200.0, 100.0)),
1835            false,
1836        );
1837
1838        assert_eq!(flex.size(&app), Size::new(200.0, 100.0));
1839        assert_eq!(fixed.size(&app), Size::new(50.0, 30.0));
1840        // 200 - 50 fixed - 10 spacing goes to the one flexible child, tightly.
1841        assert_eq!(flexible.size(&app), Size::new(140.0, 10.0));
1842        assert_eq!(offset_of(&app, fixed), Offset::new(0.0, 35.0));
1843        assert_eq!(offset_of(&app, flexible), Offset::new(60.0, 45.0));
1844    }
1845
1846    /// `flex_test.dart`: `Test Flex Widget MainAxisAlignment`.
1847    #[test]
1848    fn main_axis_alignment_positions_children() {
1849        let cases = [
1850            (MainAxisAlignment::Start, 0.0, 20.0),
1851            (MainAxisAlignment::End, 60.0, 80.0),
1852            (MainAxisAlignment::Center, 30.0, 50.0),
1853            (MainAxisAlignment::SpaceBetween, 0.0, 80.0),
1854            (MainAxisAlignment::SpaceAround, 15.0, 65.0),
1855            (MainAxisAlignment::SpaceEvenly, 20.0, 60.0),
1856        ];
1857        for (alignment, first_x, second_x) in cases {
1858            let cell = AppCell::new();
1859            let mut app = cell.borrow_mut();
1860            let first = sized_box(&mut app, Size::new(20.0, 20.0));
1861            let second = sized_box(&mut app, Size::new(20.0, 20.0));
1862            let flex = row(&mut app, vec![first, second]);
1863            flex.set_main_axis_alignment(&mut app, alignment);
1864
1865            flex.layout(
1866                &mut app,
1867                BoxConstraints::tight(Size::new(100.0, 20.0)),
1868                false,
1869            );
1870
1871            assert_eq!(
1872                (offset_of(&app, first).dx(), offset_of(&app, second).dx()),
1873                (first_x, second_x),
1874                "{alignment:?}"
1875            );
1876        }
1877    }
1878
1879    /// `flex_test.dart`: `Test Flex Widget CrossAxisAlignment`. Baseline alignment top-aligns
1880    /// because no `RenderBox` reports a baseline yet; see `PORTING.md`.
1881    #[test]
1882    fn cross_axis_alignment_positions_children() {
1883        let cases = [
1884            (CrossAxisAlignment::Start, 0.0, 20.0),
1885            (CrossAxisAlignment::Center, 40.0, 20.0),
1886            (CrossAxisAlignment::End, 80.0, 20.0),
1887            (CrossAxisAlignment::Stretch, 0.0, 100.0),
1888            (CrossAxisAlignment::Baseline, 0.0, 20.0),
1889        ];
1890        for (alignment, y, height) in cases {
1891            let cell = AppCell::new();
1892            let mut app = cell.borrow_mut();
1893            let child = sized_box(&mut app, Size::new(20.0, 20.0));
1894            let flex = row(&mut app, vec![child]);
1895            flex.set_cross_axis_alignment(&mut app, alignment);
1896            if alignment == CrossAxisAlignment::Baseline {
1897                flex.set_text_baseline(&mut app, Some(TextBaseline::Alphabetic));
1898            }
1899
1900            flex.layout(
1901                &mut app,
1902                BoxConstraints::tight(Size::new(100.0, 100.0)),
1903                false,
1904            );
1905
1906            assert_eq!(offset_of(&app, child).dy(), y, "{alignment:?}");
1907            assert_eq!(child.size(&app).height(), height, "{alignment:?}");
1908        }
1909    }
1910
1911    /// `flex_test.dart`: `Clip.none` versus a clip when the children overflow the main axis.
1912    #[test]
1913    fn overflow_is_clipped() {
1914        for (clip_behavior, clips) in [(Clip::None, false), (Clip::HardEdge, true)] {
1915            let cell = AppCell::new();
1916            let mut app = cell.borrow_mut();
1917            let first = sized_box(&mut app, Size::new(80.0, 20.0));
1918            let second = sized_box(&mut app, Size::new(80.0, 20.0));
1919            let flex = row(&mut app, vec![first, second]);
1920            flex.set_clip_behavior(&mut app, clip_behavior);
1921            let root = RenderRepaintBoundary::new(&mut app, Some(flex.as_box()));
1922
1923            let owner = first_frame(
1924                &mut app,
1925                root.as_box(),
1926                BoxConstraints::tight(Size::new(100.0, 20.0)),
1927            );
1928
1929            assert_eq!(flex.size(&app), Size::new(100.0, 20.0));
1930            let clipped = picture_has_clip(&app, root.as_object());
1931            assert_eq!(clipped, clips, "{clip_behavior:?}");
1932            let _ = owner;
1933        }
1934    }
1935
1936    /// `flex_test.dart`: a hit lands on the child that covers the position.
1937    #[test]
1938    fn hit_test_reaches_the_child_under_the_position() {
1939        let cell = AppCell::new();
1940        let mut app = cell.borrow_mut();
1941        let first = sized_box(&mut app, Size::new(50.0, 50.0));
1942        let second = sized_box(&mut app, Size::new(50.0, 50.0));
1943        let flex = row(&mut app, vec![first, second]);
1944
1945        flex.layout(
1946            &mut app,
1947            BoxConstraints::tight(Size::new(100.0, 50.0)),
1948            false,
1949        );
1950
1951        let mut result = HitTestResult::new();
1952        let hit = flex.hit_test(
1953            &mut app,
1954            &mut BoxHitTestResult::wrap(&mut result),
1955            Offset::new(75.0, 25.0),
1956        );
1957        assert!(hit);
1958        assert_eq!(hit_boxes(&result), vec![second, flex.as_box()]);
1959    }
1960}