Skip to main content

inset_rendering/
shifted_box.rs

1//! Flutter counterpart: `rendering/shifted_box.dart` (`BoxConstraintsTransform`,
2//! `RenderShiftedBox`, `RenderPadding`, `RenderAligningShiftedBox`, `RenderPositionedBox`,
3//! `OverflowBoxFit`, `RenderConstrainedOverflowBox`, `RenderConstraintsTransformBox`,
4//! `RenderSizedOverflowBox`, `RenderFractionallySizedOverflowBox`,
5//! `RenderCustomSingleChildLayoutBox`, `SingleChildLayoutDelegate`, `RenderBaseline`).
6
7use std::any::Any;
8use std::fmt::Debug;
9use std::rc::Rc;
10
11use inset_embedder::{Clip, Offset, Rect, Size, TextBaseline};
12use inset_foundation::{App, Handle, Listenable, Listener};
13use inset_painting::{
14    Alignment, AlignmentGeometry, EdgeInsets, EdgeInsetsGeometry, TextDirection,
15};
16
17use crate::box_::{
18    AnyRenderBox, BoxConstraints, BoxHitTestResult, BoxParentData, RenderBox, RenderBoxData,
19};
20use crate::layer::{ClipRectLayer, LayerHandle};
21use crate::layout_helper::{ChildBaselineGetter, ChildLayoutHelper, ChildLayouter};
22use crate::object::{
23    AnyRenderObject, Constraints, RenderHandle, RenderObject, RenderObjectData,
24    RenderObjectWithChildData, RenderObjectWithChildMixin,
25};
26use crate::painting_context::PaintingContext;
27use crate::pipeline_owner::PipelineOwner;
28use crate::stack::RelativeRect;
29
30/// Signature for a function that transforms a [`BoxConstraints`] to another [`BoxConstraints`].
31///
32/// Used by [`RenderConstraintsTransformBox`]. Typically the caller requires the returned
33/// [`BoxConstraints`] to be [`Constraints::is_normalized`].
34pub type BoxConstraintsTransform = fn(BoxConstraints) -> BoxConstraints;
35
36/// Abstract class for one-child-layout render boxes that provide control over
37/// the child's position.
38///
39/// Flutter's `RenderShiftedBox`: the shared bodies. A leaf implements the marker and calls
40/// these where Dart would run the inherited method: `RenderShiftedBox::paint(self, …)`.
41pub trait RenderShiftedBox:
42    RenderObjectWithChildMixin<ChildType = AnyRenderBox> + RenderBox
43{
44    /// The child's minimum intrinsic width, or 0 without a child.
45    fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
46        match self.child(app) {
47            Some(child) => child.get_min_intrinsic_width(app, height),
48            None => 0.0,
49        }
50    }
51
52    /// The child's maximum intrinsic width, or 0 without a child.
53    fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
54        match self.child(app) {
55            Some(child) => child.get_max_intrinsic_width(app, height),
56            None => 0.0,
57        }
58    }
59
60    /// The child's minimum intrinsic height, or 0 without a child.
61    fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
62        match self.child(app) {
63            Some(child) => child.get_min_intrinsic_height(app, width),
64            None => 0.0,
65        }
66    }
67
68    /// The child's maximum intrinsic height, or 0 without a child.
69    fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
70        match self.child(app) {
71            Some(child) => child.get_max_intrinsic_height(app, width),
72            None => 0.0,
73        }
74    }
75
76    /// The child's baseline shifted by the child's paint offset, or none without a child.
77    fn compute_distance_to_actual_baseline(
78        self: RenderHandle<Self>,
79        app: &mut App,
80        baseline: TextBaseline,
81    ) -> Option<f64> {
82        debug_assert!(!self.as_object().debug_needs_layout(app));
83        let child = self.child(app)?;
84        debug_assert!(!child.as_object().debug_needs_layout(app));
85        let result = child.get_distance_to_actual_baseline(app, baseline)?;
86        let offset = child
87            .as_object()
88            .parent_data_of::<BoxParentData>(app)
89            .offset;
90        Some(result + offset.dy())
91    }
92
93    /// The child's dry baseline at this box's constraints. The base applies no transform;
94    /// subclasses override to add their offset.
95    fn compute_dry_baseline(
96        self: RenderHandle<Self>,
97        app: &mut App,
98        constraints: BoxConstraints,
99        baseline: TextBaseline,
100    ) -> Option<f64> {
101        self.child(app)
102            .and_then(|child| child.get_dry_baseline(app, constraints, baseline))
103    }
104
105    /// Hit tests the child at its parent-data offset.
106    fn hit_test_children(
107        self: RenderHandle<Self>,
108        app: &mut App,
109        result: &mut BoxHitTestResult<'_>,
110        position: Offset,
111    ) -> bool {
112        let Some(child) = self.child(app) else {
113            return false;
114        };
115        let child_offset = child
116            .as_object()
117            .parent_data_of::<BoxParentData>(app)
118            .offset;
119        result.add_with_paint_offset(Some(child_offset), position, |result, transformed| {
120            debug_assert_eq!(transformed, position - child_offset);
121            child.hit_test(app, result, transformed)
122        })
123    }
124
125    /// Paints the child at its parent-data offset.
126    fn paint(
127        self: RenderHandle<Self>,
128        app: &mut App,
129        context: &mut PaintingContext,
130        offset: Offset,
131    ) {
132        if let Some(child) = self.child(app) {
133            let child_offset = child
134                .as_object()
135                .parent_data_of::<BoxParentData>(app)
136                .offset;
137            context.paint_child(app, child.as_object(), child_offset + offset);
138        }
139    }
140}
141
142/// Insets its child by the given padding.
143pub struct RenderPadding {
144    render_object: RenderObjectData,
145    render_box: RenderBoxData,
146    child: RenderObjectWithChildData<AnyRenderBox>,
147    resolved_padding_cache: Option<EdgeInsets>,
148    padding: EdgeInsetsGeometry,
149    text_direction: Option<TextDirection>,
150}
151
152impl RenderPadding {
153    /// Creates a render object that insets its child.
154    ///
155    /// `padding` must have non-negative insets.
156    pub fn new(
157        app: &mut App,
158        padding: EdgeInsetsGeometry,
159        text_direction: Option<TextDirection>,
160        child: Option<AnyRenderBox>,
161    ) -> RenderHandle<RenderPadding> {
162        debug_assert!(padding.is_non_negative());
163        let this = RenderHandle::new_box(
164            app,
165            RenderPadding {
166                render_object: RenderObjectData::new(),
167                render_box: RenderBoxData::new(),
168                child: RenderObjectWithChildData::new(),
169                resolved_padding_cache: None,
170                padding,
171                text_direction,
172            },
173        );
174        this.set_child(app, child);
175        this
176    }
177
178    /// The amount to pad the child in each dimension.
179    pub fn padding(self: RenderHandle<Self>, app: &App) -> EdgeInsetsGeometry {
180        self.get(app).padding
181    }
182
183    /// Sets [`padding`](Self::padding).
184    pub fn set_padding(self: RenderHandle<Self>, app: &mut App, value: EdgeInsetsGeometry) {
185        debug_assert!(value.is_non_negative());
186        if self.get(app).padding == value {
187            return;
188        }
189        self.get_mut(app).padding = value;
190        self.mark_need_resolution(app);
191    }
192
193    /// The text direction with which to resolve [`padding`](Self::padding).
194    pub fn text_direction(self: RenderHandle<Self>, app: &App) -> Option<TextDirection> {
195        self.get(app).text_direction
196    }
197
198    /// Sets [`text_direction`](Self::text_direction).
199    pub fn set_text_direction(
200        self: RenderHandle<Self>,
201        app: &mut App,
202        value: Option<TextDirection>,
203    ) {
204        if self.get(app).text_direction == value {
205            return;
206        }
207        self.get_mut(app).text_direction = value;
208        self.mark_need_resolution(app);
209    }
210
211    fn resolved_padding(&mut self) -> EdgeInsets {
212        if let Some(cached) = self.resolved_padding_cache {
213            return cached;
214        }
215        let resolved = self.padding.resolve(self.text_direction);
216        debug_assert!(resolved.is_non_negative());
217        self.resolved_padding_cache = Some(resolved);
218        resolved
219    }
220
221    fn mark_need_resolution(self: RenderHandle<Self>, app: &mut App) {
222        self.get_mut(app).resolved_padding_cache = None;
223        self.mark_needs_layout(app);
224    }
225}
226
227impl RenderObjectWithChildMixin for RenderPadding {
228    type ChildType = AnyRenderBox;
229
230    fn child_data(self: RenderHandle<Self>, app: &App) -> &RenderObjectWithChildData<AnyRenderBox> {
231        &self.get(app).child
232    }
233
234    fn child_data_mut(
235        self: RenderHandle<Self>,
236        app: &mut App,
237    ) -> &mut RenderObjectWithChildData<AnyRenderBox> {
238        &mut self.get_mut(app).child
239    }
240}
241
242impl RenderShiftedBox for RenderPadding {}
243
244impl RenderObject for RenderPadding {
245    crate::render_object_accessors!();
246
247    fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
248        let constraints = self.constraints(app);
249        let padding = self.get_mut(app).resolved_padding();
250        let child = self.child(app);
251        if child.is_none() {
252            self.set_size(
253                app,
254                constraints.constrain(Size::new(padding.horizontal(), padding.vertical())),
255            );
256            return;
257        }
258        let child = child.expect("checked");
259        let inner_constraints = constraints.deflate(EdgeInsetsGeometry::Insets(padding));
260        child.layout(app, inner_constraints, true);
261        child.parent_data_of_mut::<BoxParentData>(app).offset =
262            Offset::new(padding.left, padding.top);
263        self.set_size(
264            app,
265            constraints.constrain(Size::new(
266                padding.horizontal() + child.size(app).width(),
267                padding.vertical() + child.size(app).height(),
268            )),
269        );
270    }
271
272    fn paint(
273        self: RenderHandle<Self>,
274        app: &mut App,
275        context: &mut PaintingContext,
276        offset: Offset,
277    ) {
278        RenderShiftedBox::paint(self, app, context, offset);
279    }
280
281    fn visit_children(
282        self: RenderHandle<Self>,
283        app: &App,
284        visitor: &mut dyn FnMut(AnyRenderObject),
285    ) {
286        if let Some(child) = self.child(app) {
287            visitor(child.as_object());
288        }
289    }
290}
291
292impl RenderBox for RenderPadding {
293    crate::render_box_accessors!();
294
295    fn hit_test_children(
296        self: RenderHandle<Self>,
297        app: &mut App,
298        result: &mut BoxHitTestResult<'_>,
299        position: Offset,
300    ) -> bool {
301        RenderShiftedBox::hit_test_children(self, app, result, position)
302    }
303
304    fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
305        let padding = self.get_mut(app).resolved_padding();
306        match self.child(app) {
307            // Relies on f64::INFINITY absorption.
308            Some(child) => {
309                child.get_min_intrinsic_width(app, (height - padding.vertical()).max(0.0))
310                    + padding.horizontal()
311            }
312            None => padding.horizontal(),
313        }
314    }
315
316    fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
317        let padding = self.get_mut(app).resolved_padding();
318        match self.child(app) {
319            Some(child) => {
320                child.get_max_intrinsic_width(app, (height - padding.vertical()).max(0.0))
321                    + padding.horizontal()
322            }
323            None => padding.horizontal(),
324        }
325    }
326
327    fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
328        let padding = self.get_mut(app).resolved_padding();
329        match self.child(app) {
330            Some(child) => {
331                child.get_min_intrinsic_height(app, (width - padding.horizontal()).max(0.0))
332                    + padding.vertical()
333            }
334            None => padding.vertical(),
335        }
336    }
337
338    fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
339        let padding = self.get_mut(app).resolved_padding();
340        match self.child(app) {
341            Some(child) => {
342                child.get_max_intrinsic_height(app, (width - padding.horizontal()).max(0.0))
343                    + padding.vertical()
344            }
345            None => padding.vertical(),
346        }
347    }
348
349    fn compute_distance_to_actual_baseline(
350        self: RenderHandle<Self>,
351        app: &mut App,
352        baseline: TextBaseline,
353    ) -> Option<f64> {
354        RenderShiftedBox::compute_distance_to_actual_baseline(self, app, baseline)
355    }
356
357    fn compute_dry_baseline(
358        self: RenderHandle<Self>,
359        app: &mut App,
360        constraints: BoxConstraints,
361        baseline: TextBaseline,
362    ) -> Option<f64> {
363        let child = self.child(app)?;
364        let padding = self.get_mut(app).resolved_padding();
365        let inner_constraints = constraints.deflate(EdgeInsetsGeometry::Insets(padding));
366        let child_baseline = child.get_dry_baseline(app, inner_constraints, baseline)?;
367        Some(child_baseline + padding.top)
368    }
369
370    fn compute_dry_layout(
371        self: RenderHandle<Self>,
372        app: &mut App,
373        constraints: BoxConstraints,
374    ) -> Size {
375        let padding = self.get_mut(app).resolved_padding();
376        let Some(child) = self.child(app) else {
377            return constraints.constrain(Size::new(padding.horizontal(), padding.vertical()));
378        };
379        let inner_constraints = constraints.deflate(EdgeInsetsGeometry::Insets(padding));
380        let child_size = child.get_dry_layout(app, inner_constraints);
381        constraints.constrain(Size::new(
382            padding.horizontal() + child_size.width(),
383            padding.vertical() + child_size.height(),
384        ))
385    }
386}
387
388/// The mixin fields of [`RenderAligningShiftedBox`].
389pub struct RenderAligningShiftedBoxData {
390    resolved_alignment: Option<Alignment>,
391    alignment: AlignmentGeometry,
392    text_direction: Option<TextDirection>,
393}
394
395impl RenderAligningShiftedBoxData {
396    /// Flutter's constructor arguments; `alignment` defaults to center.
397    pub const fn new(
398        alignment: AlignmentGeometry,
399        text_direction: Option<TextDirection>,
400    ) -> RenderAligningShiftedBoxData {
401        RenderAligningShiftedBoxData {
402            resolved_alignment: None,
403            alignment,
404            text_direction,
405        }
406    }
407}
408
409/// Abstract class for one-child-layout render boxes that use a
410/// [`AlignmentGeometry`] to align their children.
411pub trait RenderAligningShiftedBox: RenderShiftedBox {
412    /// Mixin field access.
413    fn aligning_data(self: RenderHandle<Self>, app: &App) -> &RenderAligningShiftedBoxData;
414
415    /// See [`aligning_data`](Self::aligning_data).
416    fn aligning_data_mut(
417        self: RenderHandle<Self>,
418        app: &mut App,
419    ) -> &mut RenderAligningShiftedBoxData;
420
421    /// The [`alignment`](Self::alignment) resolved against [`text_direction`](Self::text_direction).
422    fn resolved_alignment(self: RenderHandle<Self>, app: &mut App) -> Alignment {
423        let data = self.aligning_data(app);
424        if let Some(resolved) = data.resolved_alignment {
425            return resolved;
426        }
427        let resolved = data.alignment.resolve(data.text_direction);
428        self.aligning_data_mut(app).resolved_alignment = Some(resolved);
429        resolved
430    }
431
432    fn mark_need_resolution(self: RenderHandle<Self>, app: &mut App) {
433        self.aligning_data_mut(app).resolved_alignment = None;
434        self.mark_needs_layout(app);
435    }
436
437    /// How to align the child.
438    ///
439    /// The x and y values of the alignment control the horizontal and vertical
440    /// alignment, respectively. An x value of -1.0 means that the left edge of
441    /// the child is aligned with the left edge of the parent whereas an x value
442    /// of 1.0 means that the right edge of the child is aligned with the right
443    /// edge of the parent. Other values interpolate (and extrapolate) linearly.
444    fn alignment(self: RenderHandle<Self>, app: &App) -> AlignmentGeometry {
445        self.aligning_data(app).alignment
446    }
447
448    /// Sets [`alignment`](Self::alignment).
449    fn set_alignment(self: RenderHandle<Self>, app: &mut App, value: AlignmentGeometry) {
450        if self.aligning_data(app).alignment == value {
451            return;
452        }
453        self.aligning_data_mut(app).alignment = value;
454        self.mark_need_resolution(app);
455    }
456
457    /// The text direction with which to resolve [`alignment`](Self::alignment).
458    fn text_direction(self: RenderHandle<Self>, app: &App) -> Option<TextDirection> {
459        self.aligning_data(app).text_direction
460    }
461
462    /// Sets [`text_direction`](Self::text_direction).
463    fn set_text_direction(self: RenderHandle<Self>, app: &mut App, value: Option<TextDirection>) {
464        if self.aligning_data(app).text_direction == value {
465            return;
466        }
467        self.aligning_data_mut(app).text_direction = value;
468        self.mark_need_resolution(app);
469    }
470
471    /// Apply the current [`alignment`](Self::alignment) to the child.
472    ///
473    /// Subclasses should call this method if they have a child, to have this
474    /// class perform the actual alignment. If they don't have a child, they
475    /// should not call this method.
476    fn align_child(self: RenderHandle<Self>, app: &mut App) {
477        let child = self.child(app).expect("align_child requires a child");
478        debug_assert!(!child.as_object().debug_needs_layout(app));
479        let offset = self
480            .resolved_alignment(app)
481            .along_offset(self.size(app) - child.size(app));
482        child
483            .as_object()
484            .parent_data_of_mut::<BoxParentData>(app)
485            .offset = offset;
486    }
487}
488
489/// Positions its child using an [`AlignmentGeometry`].
490///
491/// For example, to align a box at the bottom right, you would pass this box a
492/// tight constraint that is bigger than the child's natural size, with an
493/// alignment of `Alignment::BOTTOM_RIGHT`.
494///
495/// By default, sizes to be as big as possible in both axes. If either axis is
496/// unconstrained, then in that direction it will be sized to fit the child's
497/// dimensions. Using `width_factor` and `height_factor` you can force this
498/// latter behavior in all cases.
499pub struct RenderPositionedBox {
500    render_object: RenderObjectData,
501    render_box: RenderBoxData,
502    child: RenderObjectWithChildData<AnyRenderBox>,
503    aligning: RenderAligningShiftedBoxData,
504    width_factor: Option<f64>,
505    height_factor: Option<f64>,
506}
507
508impl RenderPositionedBox {
509    /// Creates a render object that positions its child.
510    ///
511    /// `width_factor` and `height_factor` must be non-negative when given.
512    pub fn new(
513        app: &mut App,
514        alignment: AlignmentGeometry,
515        text_direction: Option<TextDirection>,
516        width_factor: Option<f64>,
517        height_factor: Option<f64>,
518        child: Option<AnyRenderBox>,
519    ) -> RenderHandle<Self> {
520        debug_assert!(width_factor.is_none_or(|factor| factor >= 0.0));
521        debug_assert!(height_factor.is_none_or(|factor| factor >= 0.0));
522        let this = RenderHandle::new_box(
523            app,
524            RenderPositionedBox {
525                render_object: RenderObjectData::new(),
526                render_box: RenderBoxData::new(),
527                child: RenderObjectWithChildData::new(),
528                aligning: RenderAligningShiftedBoxData::new(alignment, text_direction),
529                width_factor,
530                height_factor,
531            },
532        );
533        this.set_child(app, child);
534        this
535    }
536
537    /// If non-null, sets its width to the child's width multiplied by this factor.
538    ///
539    /// Can be both greater and less than 1.0 but must be positive.
540    pub fn width_factor(self: RenderHandle<Self>, app: &App) -> Option<f64> {
541        self.get(app).width_factor
542    }
543
544    /// Sets [`width_factor`](Self::width_factor).
545    pub fn set_width_factor(self: RenderHandle<Self>, app: &mut App, value: Option<f64>) {
546        debug_assert!(value.is_none_or(|factor| factor >= 0.0));
547        if self.get(app).width_factor == value {
548            return;
549        }
550        self.get_mut(app).width_factor = value;
551        self.mark_needs_layout(app);
552    }
553
554    /// If non-null, sets its height to the child's height multiplied by this factor.
555    ///
556    /// Can be both greater and less than 1.0 but must be positive.
557    pub fn height_factor(self: RenderHandle<Self>, app: &App) -> Option<f64> {
558        self.get(app).height_factor
559    }
560
561    /// Sets [`height_factor`](Self::height_factor).
562    pub fn set_height_factor(self: RenderHandle<Self>, app: &mut App, value: Option<f64>) {
563        debug_assert!(value.is_none_or(|factor| factor >= 0.0));
564        if self.get(app).height_factor == value {
565            return;
566        }
567        self.get_mut(app).height_factor = value;
568        self.mark_needs_layout(app);
569    }
570}
571
572impl RenderObjectWithChildMixin for RenderPositionedBox {
573    type ChildType = AnyRenderBox;
574
575    fn child_data(self: RenderHandle<Self>, app: &App) -> &RenderObjectWithChildData<AnyRenderBox> {
576        &self.get(app).child
577    }
578
579    fn child_data_mut(
580        self: RenderHandle<Self>,
581        app: &mut App,
582    ) -> &mut RenderObjectWithChildData<AnyRenderBox> {
583        &mut self.get_mut(app).child
584    }
585}
586
587impl RenderShiftedBox for RenderPositionedBox {}
588
589impl RenderAligningShiftedBox for RenderPositionedBox {
590    fn aligning_data(self: RenderHandle<Self>, app: &App) -> &RenderAligningShiftedBoxData {
591        &self.get(app).aligning
592    }
593
594    fn aligning_data_mut(
595        self: RenderHandle<Self>,
596        app: &mut App,
597    ) -> &mut RenderAligningShiftedBoxData {
598        &mut self.get_mut(app).aligning
599    }
600}
601
602impl RenderPositionedBox {
603    /// Dart's sizing line of `performLayout` / `computeDryLayout`: shrink-wrap an axis when it
604    /// has a factor or is unbounded, and `child_size` is zero without a child.
605    fn size_for_child(
606        self: RenderHandle<Self>,
607        app: &App,
608        constraints: BoxConstraints,
609        child_size: Size,
610    ) -> Size {
611        let (width_factor, height_factor) = {
612            let this = self.get(app);
613            (this.width_factor, this.height_factor)
614        };
615        let shrink_wrap_width = width_factor.is_some() || constraints.max_width == f64::INFINITY;
616        let shrink_wrap_height = height_factor.is_some() || constraints.max_height == f64::INFINITY;
617        constraints.constrain(Size::new(
618            if shrink_wrap_width {
619                child_size.width() * width_factor.unwrap_or(1.0)
620            } else {
621                f64::INFINITY
622            },
623            if shrink_wrap_height {
624                child_size.height() * height_factor.unwrap_or(1.0)
625            } else {
626                f64::INFINITY
627            },
628        ))
629    }
630}
631
632impl RenderObject for RenderPositionedBox {
633    crate::render_object_accessors!();
634
635    fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
636        let constraints = self.constraints(app);
637        match self.child(app) {
638            Some(child) => {
639                child.layout(app, constraints.loosen(), true);
640                let child_size = child.size(app);
641                let size = self.size_for_child(app, constraints, child_size);
642                self.set_size(app, size);
643                self.align_child(app);
644            }
645            None => {
646                let size = self.size_for_child(app, constraints, Size::ZERO);
647                self.set_size(app, size);
648            }
649        }
650    }
651
652    fn paint(
653        self: RenderHandle<Self>,
654        app: &mut App,
655        context: &mut PaintingContext,
656        offset: Offset,
657    ) {
658        RenderShiftedBox::paint(self, app, context, offset);
659    }
660
661    fn visit_children(
662        self: RenderHandle<Self>,
663        app: &App,
664        visitor: &mut dyn FnMut(AnyRenderObject),
665    ) {
666        if let Some(child) = self.child(app) {
667            visitor(child.as_object());
668        }
669    }
670}
671
672impl RenderBox for RenderPositionedBox {
673    crate::render_box_accessors!();
674
675    fn hit_test_children(
676        self: RenderHandle<Self>,
677        app: &mut App,
678        result: &mut BoxHitTestResult<'_>,
679        position: Offset,
680    ) -> bool {
681        RenderShiftedBox::hit_test_children(self, app, result, position)
682    }
683
684    fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
685        let factor = self.get(app).width_factor.unwrap_or(1.0);
686        RenderShiftedBox::compute_min_intrinsic_width(self, app, height) * factor
687    }
688
689    fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
690        let factor = self.get(app).width_factor.unwrap_or(1.0);
691        RenderShiftedBox::compute_max_intrinsic_width(self, app, height) * factor
692    }
693
694    fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
695        let factor = self.get(app).height_factor.unwrap_or(1.0);
696        RenderShiftedBox::compute_min_intrinsic_height(self, app, width) * factor
697    }
698
699    fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
700        let factor = self.get(app).height_factor.unwrap_or(1.0);
701        RenderShiftedBox::compute_max_intrinsic_height(self, app, width) * factor
702    }
703
704    fn compute_distance_to_actual_baseline(
705        self: RenderHandle<Self>,
706        app: &mut App,
707        baseline: TextBaseline,
708    ) -> Option<f64> {
709        RenderShiftedBox::compute_distance_to_actual_baseline(self, app, baseline)
710    }
711
712    fn compute_dry_baseline(
713        self: RenderHandle<Self>,
714        app: &mut App,
715        constraints: BoxConstraints,
716        baseline: TextBaseline,
717    ) -> Option<f64> {
718        let child = self.child(app)?;
719        let child_constraints = constraints.loosen();
720        let result = child.get_dry_baseline(app, child_constraints, baseline)?;
721        let child_size = child.get_dry_layout(app, child_constraints);
722        let size = self.size_for_child(app, constraints, child_size);
723        let child_offset = self.resolved_alignment(app).along_offset(size - child_size);
724        Some(result + child_offset.dy())
725    }
726
727    fn compute_dry_layout(
728        self: RenderHandle<Self>,
729        app: &mut App,
730        constraints: BoxConstraints,
731    ) -> Size {
732        match self.child(app) {
733            Some(child) => {
734                let child_size = child.get_dry_layout(app, constraints.loosen());
735                self.size_for_child(app, constraints, child_size)
736            }
737            None => self.size_for_child(app, constraints, Size::ZERO),
738        }
739    }
740}
741
742/// How much space should be occupied by the [`RenderConstrainedOverflowBox`] when it is not
743/// overflowing.
744#[derive(Clone, Copy, Debug, PartialEq, Eq)]
745pub enum OverflowBoxFit {
746    /// The widget will size itself to be as large as the parent allows.
747    Max,
748
749    /// The widget will follow the child's size.
750    ///
751    /// More specifically, the render object will size itself to match the size of its child
752    /// within the constraints of its parent, or as small as the parent allows if no child is
753    /// set.
754    DeferToChild,
755}
756
757/// A render object that imposes different constraints on its child than it gets from its parent,
758/// possibly allowing the child to overflow the parent.
759///
760/// A render overflow box proxies most functions in the render box protocol to its child, except
761/// that when laying out its child, it passes constraints based on the `min_width`, `max_width`,
762/// `min_height`, and `max_height` fields instead of just passing the parent's constraints in.
763/// Specifically, it overrides any of the equivalent fields on the constraints given by the parent
764/// with the constraints given by these fields, as long as those values are not null. If a value is
765/// null, then the original constraint is used.
766///
767/// Additionally, this render object allows the child to overflow the bounds of this box. If the
768/// child overflows, this box will not clip the overflowing part.
769pub struct RenderConstrainedOverflowBox {
770    render_object: RenderObjectData,
771    render_box: RenderBoxData,
772    child: RenderObjectWithChildData<AnyRenderBox>,
773    aligning: RenderAligningShiftedBoxData,
774    min_width: Option<f64>,
775    max_width: Option<f64>,
776    min_height: Option<f64>,
777    max_height: Option<f64>,
778    fit: OverflowBoxFit,
779}
780
781impl RenderConstrainedOverflowBox {
782    /// Creates a render object that lets its child overflow itself.
783    pub fn new(
784        app: &mut App,
785        alignment: AlignmentGeometry,
786        text_direction: Option<TextDirection>,
787        child: Option<AnyRenderBox>,
788    ) -> RenderHandle<Self> {
789        let this = RenderHandle::new_box(
790            app,
791            RenderConstrainedOverflowBox {
792                render_object: RenderObjectData::new(),
793                render_box: RenderBoxData::new(),
794                child: RenderObjectWithChildData::new(),
795                aligning: RenderAligningShiftedBoxData::new(alignment, text_direction),
796                min_width: None,
797                max_width: None,
798                min_height: None,
799                max_height: None,
800                fit: OverflowBoxFit::Max,
801            },
802        );
803        this.set_child(app, child);
804        this
805    }
806
807    /// The minimum width constraint to give the child. `None` (the default) uses the constraint
808    /// from the parent instead.
809    pub fn min_width(self: RenderHandle<Self>, app: &App) -> Option<f64> {
810        self.get(app).min_width
811    }
812
813    /// Sets [`min_width`](Self::min_width).
814    pub fn set_min_width(self: RenderHandle<Self>, app: &mut App, value: Option<f64>) {
815        if self.get(app).min_width == value {
816            return;
817        }
818        self.get_mut(app).min_width = value;
819        self.mark_needs_layout(app);
820    }
821
822    /// The maximum width constraint to give the child. `None` (the default) uses the constraint
823    /// from the parent instead.
824    pub fn max_width(self: RenderHandle<Self>, app: &App) -> Option<f64> {
825        self.get(app).max_width
826    }
827
828    /// Sets [`max_width`](Self::max_width).
829    pub fn set_max_width(self: RenderHandle<Self>, app: &mut App, value: Option<f64>) {
830        if self.get(app).max_width == value {
831            return;
832        }
833        self.get_mut(app).max_width = value;
834        self.mark_needs_layout(app);
835    }
836
837    /// The minimum height constraint to give the child. `None` (the default) uses the constraint
838    /// from the parent instead.
839    pub fn min_height(self: RenderHandle<Self>, app: &App) -> Option<f64> {
840        self.get(app).min_height
841    }
842
843    /// Sets [`min_height`](Self::min_height).
844    pub fn set_min_height(self: RenderHandle<Self>, app: &mut App, value: Option<f64>) {
845        if self.get(app).min_height == value {
846            return;
847        }
848        self.get_mut(app).min_height = value;
849        self.mark_needs_layout(app);
850    }
851
852    /// The maximum height constraint to give the child. `None` (the default) uses the constraint
853    /// from the parent instead.
854    pub fn max_height(self: RenderHandle<Self>, app: &App) -> Option<f64> {
855        self.get(app).max_height
856    }
857
858    /// Sets [`max_height`](Self::max_height).
859    pub fn set_max_height(self: RenderHandle<Self>, app: &mut App, value: Option<f64>) {
860        if self.get(app).max_height == value {
861            return;
862        }
863        self.get_mut(app).max_height = value;
864        self.mark_needs_layout(app);
865    }
866
867    /// The way to size the render object.
868    ///
869    /// This only affects the scenario where the child does not indeed overflow. If set to
870    /// [`OverflowBoxFit::DeferToChild`], the render object will size itself to match the size of
871    /// its child within the constraints of its parent, or as small as the parent allows if no
872    /// child is set. If set to [`OverflowBoxFit::Max`] (the default), the render object will size
873    /// itself to be as large as the parent allows.
874    pub fn fit(self: RenderHandle<Self>, app: &App) -> OverflowBoxFit {
875        self.get(app).fit
876    }
877
878    /// Sets [`fit`](Self::fit).
879    pub fn set_fit(self: RenderHandle<Self>, app: &mut App, value: OverflowBoxFit) {
880        if self.get(app).fit == value {
881            return;
882        }
883        self.get_mut(app).fit = value;
884        self.as_object()
885            .mark_needs_layout_for_sized_by_parent_change(app);
886    }
887
888    fn get_inner_constraints(
889        self: RenderHandle<Self>,
890        app: &App,
891        constraints: BoxConstraints,
892    ) -> BoxConstraints {
893        let this = self.get(app);
894        BoxConstraints {
895            min_width: this.min_width.unwrap_or(constraints.min_width),
896            max_width: this.max_width.unwrap_or(constraints.max_width),
897            min_height: this.min_height.unwrap_or(constraints.min_height),
898            max_height: this.max_height.unwrap_or(constraints.max_height),
899        }
900    }
901}
902
903impl RenderObjectWithChildMixin for RenderConstrainedOverflowBox {
904    type ChildType = AnyRenderBox;
905
906    fn child_data(self: RenderHandle<Self>, app: &App) -> &RenderObjectWithChildData<AnyRenderBox> {
907        &self.get(app).child
908    }
909
910    fn child_data_mut(
911        self: RenderHandle<Self>,
912        app: &mut App,
913    ) -> &mut RenderObjectWithChildData<AnyRenderBox> {
914        &mut self.get_mut(app).child
915    }
916}
917
918impl RenderShiftedBox for RenderConstrainedOverflowBox {}
919
920impl RenderAligningShiftedBox for RenderConstrainedOverflowBox {
921    fn aligning_data(self: RenderHandle<Self>, app: &App) -> &RenderAligningShiftedBoxData {
922        &self.get(app).aligning
923    }
924
925    fn aligning_data_mut(
926        self: RenderHandle<Self>,
927        app: &mut App,
928    ) -> &mut RenderAligningShiftedBoxData {
929        &mut self.get_mut(app).aligning
930    }
931}
932
933impl RenderObject for RenderConstrainedOverflowBox {
934    crate::render_object_accessors!();
935
936    fn sized_by_parent(self: RenderHandle<Self>, app: &App) -> bool {
937        match self.fit(app) {
938            OverflowBoxFit::Max => true,
939            // If defer_to_child, the size will be as small as its child when non-overflowing,
940            // thus it cannot be sized_by_parent.
941            OverflowBoxFit::DeferToChild => false,
942        }
943    }
944
945    fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
946        let constraints = self.constraints(app);
947        match self.child(app) {
948            Some(child) => {
949                let inner = self.get_inner_constraints(app, constraints);
950                child.layout(app, inner, true);
951                match self.fit(app) {
952                    OverflowBoxFit::Max => debug_assert!(self.sized_by_parent(app)),
953                    OverflowBoxFit::DeferToChild => {
954                        let size = constraints.constrain(child.size(app));
955                        self.set_size(app, size);
956                    }
957                }
958                self.align_child(app);
959            }
960            None => match self.fit(app) {
961                OverflowBoxFit::Max => debug_assert!(self.sized_by_parent(app)),
962                OverflowBoxFit::DeferToChild => self.set_size(app, constraints.smallest()),
963            },
964        }
965    }
966
967    fn paint(
968        self: RenderHandle<Self>,
969        app: &mut App,
970        context: &mut PaintingContext,
971        offset: Offset,
972    ) {
973        RenderShiftedBox::paint(self, app, context, offset);
974    }
975
976    fn visit_children(
977        self: RenderHandle<Self>,
978        app: &App,
979        visitor: &mut dyn FnMut(AnyRenderObject),
980    ) {
981        if let Some(child) = self.child(app) {
982            visitor(child.as_object());
983        }
984    }
985}
986
987impl RenderBox for RenderConstrainedOverflowBox {
988    crate::render_box_accessors!();
989
990    fn hit_test_children(
991        self: RenderHandle<Self>,
992        app: &mut App,
993        result: &mut BoxHitTestResult<'_>,
994        position: Offset,
995    ) -> bool {
996        RenderShiftedBox::hit_test_children(self, app, result, position)
997    }
998
999    fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1000        RenderShiftedBox::compute_min_intrinsic_width(self, app, height)
1001    }
1002
1003    fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1004        RenderShiftedBox::compute_max_intrinsic_width(self, app, height)
1005    }
1006
1007    fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1008        RenderShiftedBox::compute_min_intrinsic_height(self, app, width)
1009    }
1010
1011    fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1012        RenderShiftedBox::compute_max_intrinsic_height(self, app, width)
1013    }
1014
1015    fn compute_distance_to_actual_baseline(
1016        self: RenderHandle<Self>,
1017        app: &mut App,
1018        baseline: TextBaseline,
1019    ) -> Option<f64> {
1020        RenderShiftedBox::compute_distance_to_actual_baseline(self, app, baseline)
1021    }
1022
1023    fn compute_dry_layout(
1024        self: RenderHandle<Self>,
1025        app: &mut App,
1026        constraints: BoxConstraints,
1027    ) -> Size {
1028        match self.fit(app) {
1029            OverflowBoxFit::Max => constraints.biggest(),
1030            OverflowBoxFit::DeferToChild => match self.child(app) {
1031                Some(child) => child.get_dry_layout(app, constraints),
1032                None => constraints.smallest(),
1033            },
1034        }
1035    }
1036
1037    fn compute_dry_baseline(
1038        self: RenderHandle<Self>,
1039        app: &mut App,
1040        constraints: BoxConstraints,
1041        baseline: TextBaseline,
1042    ) -> Option<f64> {
1043        let child = self.child(app)?;
1044        let child_constraints = self.get_inner_constraints(app, constraints);
1045        let result = child.get_dry_baseline(app, child_constraints, baseline)?;
1046        let child_size = child.get_dry_layout(app, child_constraints);
1047        let size = self.get_dry_layout(app, constraints);
1048        Some(
1049            result
1050                + self
1051                    .resolved_alignment(app)
1052                    .along_offset(size - child_size)
1053                    .dy(),
1054        )
1055    }
1056}
1057
1058/// A [`RenderBox`] that applies an arbitrary transform to its constraints, and sizes its child
1059/// using the resulting [`BoxConstraints`], optionally clipping, or treating the overflow as an
1060/// error.
1061///
1062/// This [`RenderBox`] sizes its child using a [`BoxConstraints`] created by applying
1063/// [`constraints_transform`](Self::constraints_transform) to this [`RenderBox`]'s own constraints.
1064/// This box will then attempt to adopt the same size, within the limits of its own constraints. If
1065/// it ends up with a different size, it will align the child based on
1066/// [`RenderAligningShiftedBox::alignment`]. If the box cannot expand enough to accommodate the
1067/// entire child, the child will be clipped if
1068/// [`clip_behavior`](Self::clip_behavior) is not [`Clip::None`].
1069///
1070/// When the child is `None`, this [`RenderBox`] takes the smallest possible size and never
1071/// overflows.
1072pub struct RenderConstraintsTransformBox {
1073    render_object: RenderObjectData,
1074    render_box: RenderBoxData,
1075    child: RenderObjectWithChildData<AnyRenderBox>,
1076    aligning: RenderAligningShiftedBoxData,
1077    constraints_transform: BoxConstraintsTransform,
1078    clip_behavior: Clip,
1079    clip_rect_layer: LayerHandle<Handle<ClipRectLayer>>,
1080    overflow_container_rect: Rect,
1081    overflow_child_rect: Rect,
1082    is_overflowing: bool,
1083    child_constraints: Option<BoxConstraints>,
1084}
1085
1086impl RenderConstraintsTransformBox {
1087    /// Creates a [`RenderBox`] that sizes itself to the child and modifies the constraints before
1088    /// passing them down to that child.
1089    pub fn new(
1090        app: &mut App,
1091        alignment: AlignmentGeometry,
1092        text_direction: Option<TextDirection>,
1093        constraints_transform: BoxConstraintsTransform,
1094        child: Option<AnyRenderBox>,
1095    ) -> RenderHandle<Self> {
1096        let this = RenderHandle::new_box(
1097            app,
1098            RenderConstraintsTransformBox {
1099                render_object: RenderObjectData::new(),
1100                render_box: RenderBoxData::new(),
1101                child: RenderObjectWithChildData::new(),
1102                aligning: RenderAligningShiftedBoxData::new(alignment, text_direction),
1103                constraints_transform,
1104                clip_behavior: Clip::None,
1105                clip_rect_layer: LayerHandle::new(),
1106                overflow_container_rect: Rect::ZERO,
1107                overflow_child_rect: Rect::ZERO,
1108                is_overflowing: false,
1109                child_constraints: None,
1110            },
1111        );
1112        this.set_child(app, child);
1113        this
1114    }
1115
1116    /// The transform this box applies to its own constraints before laying its child out.
1117    pub fn constraints_transform(self: RenderHandle<Self>, app: &App) -> BoxConstraintsTransform {
1118        self.get(app).constraints_transform
1119    }
1120
1121    /// Sets [`constraints_transform`](Self::constraints_transform).
1122    pub fn set_constraints_transform(
1123        self: RenderHandle<Self>,
1124        app: &mut App,
1125        value: BoxConstraintsTransform,
1126    ) {
1127        if std::ptr::fn_addr_eq(self.get(app).constraints_transform, value) {
1128            return;
1129        }
1130        self.get_mut(app).constraints_transform = value;
1131        // The render object only needs layout if the new transform maps the current constraints
1132        // to a different value, or the render object has never been laid out before.
1133        let needs_layout = match self.get(app).child_constraints {
1134            Some(child_constraints) => child_constraints != value(self.constraints(app)),
1135            None => true,
1136        };
1137        if needs_layout {
1138            self.mark_needs_layout(app);
1139        }
1140    }
1141
1142    /// How to clip a child that overflows this box. Defaults to [`Clip::None`].
1143    pub fn clip_behavior(self: RenderHandle<Self>, app: &App) -> Clip {
1144        self.get(app).clip_behavior
1145    }
1146
1147    /// Sets [`clip_behavior`](Self::clip_behavior).
1148    pub fn set_clip_behavior(self: RenderHandle<Self>, app: &mut App, value: Clip) {
1149        if self.get(app).clip_behavior == value {
1150            return;
1151        }
1152        self.get_mut(app).clip_behavior = value;
1153        self.mark_needs_paint(app);
1154    }
1155}
1156
1157impl RenderObjectWithChildMixin for RenderConstraintsTransformBox {
1158    type ChildType = AnyRenderBox;
1159
1160    fn child_data(self: RenderHandle<Self>, app: &App) -> &RenderObjectWithChildData<AnyRenderBox> {
1161        &self.get(app).child
1162    }
1163
1164    fn child_data_mut(
1165        self: RenderHandle<Self>,
1166        app: &mut App,
1167    ) -> &mut RenderObjectWithChildData<AnyRenderBox> {
1168        &mut self.get_mut(app).child
1169    }
1170}
1171
1172impl RenderShiftedBox for RenderConstraintsTransformBox {}
1173
1174impl RenderAligningShiftedBox for RenderConstraintsTransformBox {
1175    fn aligning_data(self: RenderHandle<Self>, app: &App) -> &RenderAligningShiftedBoxData {
1176        &self.get(app).aligning
1177    }
1178
1179    fn aligning_data_mut(
1180        self: RenderHandle<Self>,
1181        app: &mut App,
1182    ) -> &mut RenderAligningShiftedBoxData {
1183        &mut self.get_mut(app).aligning
1184    }
1185}
1186
1187impl RenderObject for RenderConstraintsTransformBox {
1188    crate::render_object_accessors!();
1189
1190    fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
1191        let constraints = self.constraints(app);
1192        match self.child(app) {
1193            Some(child) => {
1194                let child_constraints = (self.constraints_transform(app))(constraints);
1195                debug_assert!(
1196                    child_constraints.is_normalized(),
1197                    "{child_constraints} is not normalized"
1198                );
1199                self.get_mut(app).child_constraints = Some(child_constraints);
1200                child.layout(app, child_constraints, true);
1201                let size = constraints.constrain(child.size(app));
1202                self.set_size(app, size);
1203                self.align_child(app);
1204                let child_offset = child
1205                    .as_object()
1206                    .parent_data_of::<BoxParentData>(app)
1207                    .offset;
1208                let child_rect = child_offset & child.size(app);
1209                let this = self.get_mut(app);
1210                this.overflow_container_rect = Offset::ZERO & size;
1211                this.overflow_child_rect = child_rect;
1212            }
1213            None => {
1214                self.set_size(app, constraints.smallest());
1215                let this = self.get_mut(app);
1216                this.overflow_container_rect = Rect::ZERO;
1217                this.overflow_child_rect = Rect::ZERO;
1218            }
1219        }
1220        let this = self.get_mut(app);
1221        this.is_overflowing =
1222            RelativeRect::from_rect(this.overflow_container_rect, this.overflow_child_rect)
1223                .has_insets();
1224    }
1225
1226    fn paint(
1227        self: RenderHandle<Self>,
1228        app: &mut App,
1229        context: &mut PaintingContext,
1230        offset: Offset,
1231    ) {
1232        if self.child(app).is_none() {
1233            return;
1234        }
1235        if !self.get(app).is_overflowing {
1236            RenderShiftedBox::paint(self, app, context, offset);
1237            return;
1238        }
1239        // We have overflow and the clipBehavior isn't none. Clip it.
1240        let clip_rect = Offset::ZERO & self.size(app);
1241        let clip_behavior = self.clip_behavior(app);
1242        let old = self.get(app).clip_rect_layer.layer();
1243        let layer = context.push_clip_rect(
1244            app,
1245            self.as_object().needs_compositing(app),
1246            offset,
1247            clip_rect,
1248            |app, context, offset| RenderShiftedBox::paint(self, app, context, offset),
1249            clip_behavior,
1250            old,
1251        );
1252        LayerHandle::set_layer(app, |app| &mut self.get_mut(app).clip_rect_layer, layer);
1253    }
1254
1255    fn dispose(self: RenderHandle<Self>, app: &mut App) {
1256        LayerHandle::set_layer(app, |app| &mut self.get_mut(app).clip_rect_layer, None);
1257        crate::object::RenderObjectBase::dispose(self, app);
1258    }
1259
1260    fn visit_children(
1261        self: RenderHandle<Self>,
1262        app: &App,
1263        visitor: &mut dyn FnMut(AnyRenderObject),
1264    ) {
1265        if let Some(child) = self.child(app) {
1266            visitor(child.as_object());
1267        }
1268    }
1269}
1270
1271impl RenderBox for RenderConstraintsTransformBox {
1272    crate::render_box_accessors!();
1273
1274    fn hit_test_children(
1275        self: RenderHandle<Self>,
1276        app: &mut App,
1277        result: &mut BoxHitTestResult<'_>,
1278        position: Offset,
1279    ) -> bool {
1280        RenderShiftedBox::hit_test_children(self, app, result, position)
1281    }
1282
1283    fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1284        let transformed =
1285            (self.constraints_transform(app))(BoxConstraints::new().max_height(height));
1286        RenderShiftedBox::compute_min_intrinsic_width(self, app, transformed.max_height)
1287    }
1288
1289    fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1290        let transformed =
1291            (self.constraints_transform(app))(BoxConstraints::new().max_height(height));
1292        RenderShiftedBox::compute_max_intrinsic_width(self, app, transformed.max_height)
1293    }
1294
1295    fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1296        let transformed = (self.constraints_transform(app))(BoxConstraints::new().max_width(width));
1297        RenderShiftedBox::compute_min_intrinsic_height(self, app, transformed.max_width)
1298    }
1299
1300    fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1301        let transformed = (self.constraints_transform(app))(BoxConstraints::new().max_width(width));
1302        RenderShiftedBox::compute_max_intrinsic_height(self, app, transformed.max_width)
1303    }
1304
1305    fn compute_distance_to_actual_baseline(
1306        self: RenderHandle<Self>,
1307        app: &mut App,
1308        baseline: TextBaseline,
1309    ) -> Option<f64> {
1310        RenderShiftedBox::compute_distance_to_actual_baseline(self, app, baseline)
1311    }
1312
1313    fn compute_dry_layout(
1314        self: RenderHandle<Self>,
1315        app: &mut App,
1316        constraints: BoxConstraints,
1317    ) -> Size {
1318        match self.child(app) {
1319            Some(child) => {
1320                let child_constraints = (self.constraints_transform(app))(constraints);
1321                let child_size = child.get_dry_layout(app, child_constraints);
1322                constraints.constrain(child_size)
1323            }
1324            None => constraints.smallest(),
1325        }
1326    }
1327
1328    fn compute_dry_baseline(
1329        self: RenderHandle<Self>,
1330        app: &mut App,
1331        constraints: BoxConstraints,
1332        baseline: TextBaseline,
1333    ) -> Option<f64> {
1334        let child = self.child(app)?;
1335        let child_constraints = (self.constraints_transform(app))(constraints);
1336        let result = child.get_dry_baseline(app, child_constraints, baseline)?;
1337        let child_size = child.get_dry_layout(app, child_constraints);
1338        let size = constraints.constrain(child_size);
1339        Some(
1340            result
1341                + self
1342                    .resolved_alignment(app)
1343                    .along_offset(size - child_size)
1344                    .dy(),
1345        )
1346    }
1347}
1348
1349/// A render object that is a specific size but passes its original constraints through to its
1350/// child, which it allows to overflow.
1351///
1352/// If the child's resulting size differs from this render object's size, then the child is aligned
1353/// according to [`RenderAligningShiftedBox::alignment`].
1354pub struct RenderSizedOverflowBox {
1355    render_object: RenderObjectData,
1356    render_box: RenderBoxData,
1357    child: RenderObjectWithChildData<AnyRenderBox>,
1358    aligning: RenderAligningShiftedBoxData,
1359    requested_size: Size,
1360}
1361
1362impl RenderSizedOverflowBox {
1363    /// Creates a render box of a given size that lets its child overflow.
1364    pub fn new(
1365        app: &mut App,
1366        requested_size: Size,
1367        alignment: AlignmentGeometry,
1368        text_direction: Option<TextDirection>,
1369        child: Option<AnyRenderBox>,
1370    ) -> RenderHandle<Self> {
1371        let this = RenderHandle::new_box(
1372            app,
1373            RenderSizedOverflowBox {
1374                render_object: RenderObjectData::new(),
1375                render_box: RenderBoxData::new(),
1376                child: RenderObjectWithChildData::new(),
1377                aligning: RenderAligningShiftedBoxData::new(alignment, text_direction),
1378                requested_size,
1379            },
1380        );
1381        this.set_child(app, child);
1382        this
1383    }
1384
1385    /// The size this render box should attempt to be.
1386    pub fn requested_size(self: RenderHandle<Self>, app: &App) -> Size {
1387        self.get(app).requested_size
1388    }
1389
1390    /// Sets [`requested_size`](Self::requested_size).
1391    pub fn set_requested_size(self: RenderHandle<Self>, app: &mut App, value: Size) {
1392        if self.get(app).requested_size == value {
1393            return;
1394        }
1395        self.get_mut(app).requested_size = value;
1396        self.mark_needs_layout(app);
1397    }
1398}
1399
1400impl RenderObjectWithChildMixin for RenderSizedOverflowBox {
1401    type ChildType = AnyRenderBox;
1402
1403    fn child_data(self: RenderHandle<Self>, app: &App) -> &RenderObjectWithChildData<AnyRenderBox> {
1404        &self.get(app).child
1405    }
1406
1407    fn child_data_mut(
1408        self: RenderHandle<Self>,
1409        app: &mut App,
1410    ) -> &mut RenderObjectWithChildData<AnyRenderBox> {
1411        &mut self.get_mut(app).child
1412    }
1413}
1414
1415impl RenderShiftedBox for RenderSizedOverflowBox {}
1416
1417impl RenderAligningShiftedBox for RenderSizedOverflowBox {
1418    fn aligning_data(self: RenderHandle<Self>, app: &App) -> &RenderAligningShiftedBoxData {
1419        &self.get(app).aligning
1420    }
1421
1422    fn aligning_data_mut(
1423        self: RenderHandle<Self>,
1424        app: &mut App,
1425    ) -> &mut RenderAligningShiftedBoxData {
1426        &mut self.get_mut(app).aligning
1427    }
1428}
1429
1430impl RenderObject for RenderSizedOverflowBox {
1431    crate::render_object_accessors!();
1432
1433    fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
1434        let constraints = self.constraints(app);
1435        let size = constraints.constrain(self.requested_size(app));
1436        self.set_size(app, size);
1437        if let Some(child) = self.child(app) {
1438            child.layout(app, constraints, true);
1439            self.align_child(app);
1440        }
1441    }
1442
1443    fn paint(
1444        self: RenderHandle<Self>,
1445        app: &mut App,
1446        context: &mut PaintingContext,
1447        offset: Offset,
1448    ) {
1449        RenderShiftedBox::paint(self, app, context, offset);
1450    }
1451
1452    fn visit_children(
1453        self: RenderHandle<Self>,
1454        app: &App,
1455        visitor: &mut dyn FnMut(AnyRenderObject),
1456    ) {
1457        if let Some(child) = self.child(app) {
1458            visitor(child.as_object());
1459        }
1460    }
1461}
1462
1463impl RenderBox for RenderSizedOverflowBox {
1464    crate::render_box_accessors!();
1465
1466    fn hit_test_children(
1467        self: RenderHandle<Self>,
1468        app: &mut App,
1469        result: &mut BoxHitTestResult<'_>,
1470        position: Offset,
1471    ) -> bool {
1472        RenderShiftedBox::hit_test_children(self, app, result, position)
1473    }
1474
1475    fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, _height: f64) -> f64 {
1476        self.requested_size(app).width()
1477    }
1478
1479    fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, _height: f64) -> f64 {
1480        self.requested_size(app).width()
1481    }
1482
1483    fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, _width: f64) -> f64 {
1484        self.requested_size(app).height()
1485    }
1486
1487    fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, _width: f64) -> f64 {
1488        self.requested_size(app).height()
1489    }
1490
1491    fn compute_distance_to_actual_baseline(
1492        self: RenderHandle<Self>,
1493        app: &mut App,
1494        baseline: TextBaseline,
1495    ) -> Option<f64> {
1496        RenderShiftedBox::compute_distance_to_actual_baseline(self, app, baseline)
1497    }
1498
1499    fn compute_dry_layout(
1500        self: RenderHandle<Self>,
1501        app: &mut App,
1502        constraints: BoxConstraints,
1503    ) -> Size {
1504        constraints.constrain(self.requested_size(app))
1505    }
1506
1507    fn compute_dry_baseline(
1508        self: RenderHandle<Self>,
1509        app: &mut App,
1510        constraints: BoxConstraints,
1511        baseline: TextBaseline,
1512    ) -> Option<f64> {
1513        let child = self.child(app)?;
1514        let result = child.get_dry_baseline(app, constraints, baseline)?;
1515        let child_size = child.get_dry_layout(app, constraints);
1516        let size = self.get_dry_layout(app, constraints);
1517        Some(
1518            result
1519                + self
1520                    .resolved_alignment(app)
1521                    .along_offset(size - child_size)
1522                    .dy(),
1523        )
1524    }
1525}
1526
1527/// Sizes its child to a fraction of the total available space.
1528///
1529/// For both its width and height, this render object imposes a tight constraint on its child that
1530/// is a multiple (typically less than 1.0) of the maximum constraint it received from its parent
1531/// on that axis. If the factor for a given axis is `None`, then the constraints from the parent
1532/// are just passed through instead.
1533///
1534/// It then tries to size itself to the size of its child. Where this is not possible (e.g. if the
1535/// constraints from the parent are themselves tight), the child is aligned according to
1536/// [`RenderAligningShiftedBox::alignment`].
1537pub struct RenderFractionallySizedOverflowBox {
1538    render_object: RenderObjectData,
1539    render_box: RenderBoxData,
1540    child: RenderObjectWithChildData<AnyRenderBox>,
1541    aligning: RenderAligningShiftedBoxData,
1542    width_factor: Option<f64>,
1543    height_factor: Option<f64>,
1544}
1545
1546impl RenderFractionallySizedOverflowBox {
1547    /// Creates a render box that sizes its child to a fraction of the total available space.
1548    pub fn new(
1549        app: &mut App,
1550        alignment: AlignmentGeometry,
1551        text_direction: Option<TextDirection>,
1552        child: Option<AnyRenderBox>,
1553    ) -> RenderHandle<Self> {
1554        let this = RenderHandle::new_box(
1555            app,
1556            RenderFractionallySizedOverflowBox {
1557                render_object: RenderObjectData::new(),
1558                render_box: RenderBoxData::new(),
1559                child: RenderObjectWithChildData::new(),
1560                aligning: RenderAligningShiftedBoxData::new(alignment, text_direction),
1561                width_factor: None,
1562                height_factor: None,
1563            },
1564        );
1565        this.set_child(app, child);
1566        this
1567    }
1568
1569    /// If non-null, the factor of the incoming width to use.
1570    ///
1571    /// If non-null, the child is given a tight width constraint that is the max incoming width
1572    /// constraint multiplied by this factor. If null, the child is given the incoming width
1573    /// constraints.
1574    pub fn width_factor(self: RenderHandle<Self>, app: &App) -> Option<f64> {
1575        self.get(app).width_factor
1576    }
1577
1578    /// Sets [`width_factor`](Self::width_factor).
1579    pub fn set_width_factor(self: RenderHandle<Self>, app: &mut App, value: Option<f64>) {
1580        debug_assert!(value.is_none_or(|factor| factor >= 0.0));
1581        if self.get(app).width_factor == value {
1582            return;
1583        }
1584        self.get_mut(app).width_factor = value;
1585        self.mark_needs_layout(app);
1586    }
1587
1588    /// If non-null, the factor of the incoming height to use.
1589    ///
1590    /// If non-null, the child is given a tight height constraint that is the max incoming height
1591    /// constraint multiplied by this factor. If null, the child is given the incoming height
1592    /// constraints.
1593    pub fn height_factor(self: RenderHandle<Self>, app: &App) -> Option<f64> {
1594        self.get(app).height_factor
1595    }
1596
1597    /// Sets [`height_factor`](Self::height_factor).
1598    pub fn set_height_factor(self: RenderHandle<Self>, app: &mut App, value: Option<f64>) {
1599        debug_assert!(value.is_none_or(|factor| factor >= 0.0));
1600        if self.get(app).height_factor == value {
1601            return;
1602        }
1603        self.get_mut(app).height_factor = value;
1604        self.mark_needs_layout(app);
1605    }
1606
1607    fn get_inner_constraints(
1608        self: RenderHandle<Self>,
1609        app: &App,
1610        constraints: BoxConstraints,
1611    ) -> BoxConstraints {
1612        let (width_factor, height_factor) = {
1613            let this = self.get(app);
1614            (this.width_factor, this.height_factor)
1615        };
1616        let mut min_width = constraints.min_width;
1617        let mut max_width = constraints.max_width;
1618        if let Some(factor) = width_factor {
1619            let width = max_width * factor;
1620            min_width = width;
1621            max_width = width;
1622        }
1623        let mut min_height = constraints.min_height;
1624        let mut max_height = constraints.max_height;
1625        if let Some(factor) = height_factor {
1626            let height = max_height * factor;
1627            min_height = height;
1628            max_height = height;
1629        }
1630        BoxConstraints {
1631            min_width,
1632            max_width,
1633            min_height,
1634            max_height,
1635        }
1636    }
1637}
1638
1639impl RenderObjectWithChildMixin for RenderFractionallySizedOverflowBox {
1640    type ChildType = AnyRenderBox;
1641
1642    fn child_data(self: RenderHandle<Self>, app: &App) -> &RenderObjectWithChildData<AnyRenderBox> {
1643        &self.get(app).child
1644    }
1645
1646    fn child_data_mut(
1647        self: RenderHandle<Self>,
1648        app: &mut App,
1649    ) -> &mut RenderObjectWithChildData<AnyRenderBox> {
1650        &mut self.get_mut(app).child
1651    }
1652}
1653
1654impl RenderShiftedBox for RenderFractionallySizedOverflowBox {}
1655
1656impl RenderAligningShiftedBox for RenderFractionallySizedOverflowBox {
1657    fn aligning_data(self: RenderHandle<Self>, app: &App) -> &RenderAligningShiftedBoxData {
1658        &self.get(app).aligning
1659    }
1660
1661    fn aligning_data_mut(
1662        self: RenderHandle<Self>,
1663        app: &mut App,
1664    ) -> &mut RenderAligningShiftedBoxData {
1665        &mut self.get_mut(app).aligning
1666    }
1667}
1668
1669impl RenderObject for RenderFractionallySizedOverflowBox {
1670    crate::render_object_accessors!();
1671
1672    fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
1673        let constraints = self.constraints(app);
1674        let inner = self.get_inner_constraints(app, constraints);
1675        match self.child(app) {
1676            Some(child) => {
1677                child.layout(app, inner, true);
1678                let size = constraints.constrain(child.size(app));
1679                self.set_size(app, size);
1680                self.align_child(app);
1681            }
1682            None => {
1683                let size = constraints.constrain(inner.constrain(Size::ZERO));
1684                self.set_size(app, size);
1685            }
1686        }
1687    }
1688
1689    fn paint(
1690        self: RenderHandle<Self>,
1691        app: &mut App,
1692        context: &mut PaintingContext,
1693        offset: Offset,
1694    ) {
1695        RenderShiftedBox::paint(self, app, context, offset);
1696    }
1697
1698    fn visit_children(
1699        self: RenderHandle<Self>,
1700        app: &App,
1701        visitor: &mut dyn FnMut(AnyRenderObject),
1702    ) {
1703        if let Some(child) = self.child(app) {
1704            visitor(child.as_object());
1705        }
1706    }
1707}
1708
1709impl RenderBox for RenderFractionallySizedOverflowBox {
1710    crate::render_box_accessors!();
1711
1712    fn hit_test_children(
1713        self: RenderHandle<Self>,
1714        app: &mut App,
1715        result: &mut BoxHitTestResult<'_>,
1716        position: Offset,
1717    ) -> bool {
1718        RenderShiftedBox::hit_test_children(self, app, result, position)
1719    }
1720
1721    fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1722        let (width_factor, height_factor) = {
1723            let this = self.get(app);
1724            (this.width_factor, this.height_factor)
1725        };
1726        let result = match self.child(app) {
1727            // Relies on f64::INFINITY absorption.
1728            Some(child) => {
1729                child.get_min_intrinsic_width(app, height * height_factor.unwrap_or(1.0))
1730            }
1731            None => RenderShiftedBox::compute_min_intrinsic_width(self, app, height),
1732        };
1733        debug_assert!(result.is_finite());
1734        result / width_factor.unwrap_or(1.0)
1735    }
1736
1737    fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1738        let (width_factor, height_factor) = {
1739            let this = self.get(app);
1740            (this.width_factor, this.height_factor)
1741        };
1742        let result = match self.child(app) {
1743            Some(child) => {
1744                child.get_max_intrinsic_width(app, height * height_factor.unwrap_or(1.0))
1745            }
1746            None => RenderShiftedBox::compute_max_intrinsic_width(self, app, height),
1747        };
1748        debug_assert!(result.is_finite());
1749        result / width_factor.unwrap_or(1.0)
1750    }
1751
1752    fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1753        let (width_factor, height_factor) = {
1754            let this = self.get(app);
1755            (this.width_factor, this.height_factor)
1756        };
1757        let result = match self.child(app) {
1758            Some(child) => child.get_min_intrinsic_height(app, width * width_factor.unwrap_or(1.0)),
1759            None => RenderShiftedBox::compute_min_intrinsic_height(self, app, width),
1760        };
1761        debug_assert!(result.is_finite());
1762        result / height_factor.unwrap_or(1.0)
1763    }
1764
1765    fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1766        let (width_factor, height_factor) = {
1767            let this = self.get(app);
1768            (this.width_factor, this.height_factor)
1769        };
1770        let result = match self.child(app) {
1771            Some(child) => child.get_max_intrinsic_height(app, width * width_factor.unwrap_or(1.0)),
1772            None => RenderShiftedBox::compute_max_intrinsic_height(self, app, width),
1773        };
1774        debug_assert!(result.is_finite());
1775        result / height_factor.unwrap_or(1.0)
1776    }
1777
1778    fn compute_distance_to_actual_baseline(
1779        self: RenderHandle<Self>,
1780        app: &mut App,
1781        baseline: TextBaseline,
1782    ) -> Option<f64> {
1783        RenderShiftedBox::compute_distance_to_actual_baseline(self, app, baseline)
1784    }
1785
1786    fn compute_dry_layout(
1787        self: RenderHandle<Self>,
1788        app: &mut App,
1789        constraints: BoxConstraints,
1790    ) -> Size {
1791        let inner = self.get_inner_constraints(app, constraints);
1792        match self.child(app) {
1793            Some(child) => {
1794                let child_size = child.get_dry_layout(app, inner);
1795                constraints.constrain(child_size)
1796            }
1797            None => constraints.constrain(inner.constrain(Size::ZERO)),
1798        }
1799    }
1800
1801    fn compute_dry_baseline(
1802        self: RenderHandle<Self>,
1803        app: &mut App,
1804        constraints: BoxConstraints,
1805        baseline: TextBaseline,
1806    ) -> Option<f64> {
1807        let child = self.child(app)?;
1808        let child_constraints = self.get_inner_constraints(app, constraints);
1809        let result = child.get_dry_baseline(app, child_constraints, baseline)?;
1810        let child_size = child.get_dry_layout(app, child_constraints);
1811        let size = self.get_dry_layout(app, constraints);
1812        Some(
1813            result
1814                + self
1815                    .resolved_alignment(app)
1816                    .along_offset(size - child_size)
1817                    .dy(),
1818        )
1819    }
1820}
1821
1822/// Shifts the child down such that the child's baseline (or the bottom of the child, if the child
1823/// has no baseline) is [`baseline`](Self::baseline) logical pixels below the top of this box, then
1824/// sizes this box to contain the child.
1825///
1826/// If [`baseline`](Self::baseline) is less than the distance from the top of the child to the
1827/// baseline of the child, then the child will overflow the top of the box. This is typically not
1828/// desirable, in particular, that part of the child will not be found when doing hit tests, so
1829/// the user cannot interact with that part of the child.
1830///
1831/// This box will be sized so that its bottom is coincident with the bottom of the child. This
1832/// means if this box shifts the child down, there will be space between the top of this box and
1833/// the top of the child, but there is never space between the bottom of the child and the bottom
1834/// of the box.
1835pub struct RenderBaseline {
1836    render_object: RenderObjectData,
1837    render_box: RenderBoxData,
1838    child: RenderObjectWithChildData<AnyRenderBox>,
1839    baseline: f64,
1840    baseline_type: TextBaseline,
1841}
1842
1843/// What [`RenderBaseline::compute_sizes`] worked out: Dart's `({Size size, double top})`.
1844struct BaselineSizes {
1845    size: Size,
1846    top: f64,
1847}
1848
1849impl RenderBaseline {
1850    /// Creates a [`RenderBaseline`] object.
1851    pub fn new(
1852        app: &mut App,
1853        baseline: f64,
1854        baseline_type: TextBaseline,
1855        child: Option<AnyRenderBox>,
1856    ) -> RenderHandle<Self> {
1857        let this = RenderHandle::new_box(
1858            app,
1859            RenderBaseline {
1860                render_object: RenderObjectData::new(),
1861                render_box: RenderBoxData::new(),
1862                child: RenderObjectWithChildData::new(),
1863                baseline,
1864                baseline_type,
1865            },
1866        );
1867        this.set_child(app, child);
1868        this
1869    }
1870
1871    /// The number of logical pixels from the top of this box at which to position the child's
1872    /// baseline.
1873    pub fn baseline(self: RenderHandle<Self>, app: &App) -> f64 {
1874        self.get(app).baseline
1875    }
1876
1877    /// Sets [`baseline`](Self::baseline).
1878    pub fn set_baseline(self: RenderHandle<Self>, app: &mut App, value: f64) {
1879        if self.get(app).baseline == value {
1880            return;
1881        }
1882        self.get_mut(app).baseline = value;
1883        self.mark_needs_layout(app);
1884    }
1885
1886    /// The type of baseline to use for positioning the child.
1887    pub fn baseline_type(self: RenderHandle<Self>, app: &App) -> TextBaseline {
1888        self.get(app).baseline_type
1889    }
1890
1891    /// Sets [`baseline_type`](Self::baseline_type).
1892    pub fn set_baseline_type(self: RenderHandle<Self>, app: &mut App, value: TextBaseline) {
1893        if self.get(app).baseline_type == value {
1894            return;
1895        }
1896        self.get_mut(app).baseline_type = value;
1897        self.mark_needs_layout(app);
1898    }
1899
1900    /// Flutter's `_computeSizes`: this box's size and the offset the child is shifted down by.
1901    fn compute_sizes(
1902        self: RenderHandle<Self>,
1903        app: &mut App,
1904        constraints: BoxConstraints,
1905        layout_child: ChildLayouter,
1906        get_baseline: ChildBaselineGetter,
1907    ) -> BaselineSizes {
1908        let Some(child) = self.child(app) else {
1909            return BaselineSizes {
1910                size: constraints.smallest(),
1911                top: 0.0,
1912            };
1913        };
1914        let child_constraints = constraints.loosen();
1915        let child_size = layout_child(app, child, child_constraints);
1916        let baseline_type = self.baseline_type(app);
1917        let child_baseline = get_baseline(app, child, child_constraints, baseline_type)
1918            .unwrap_or(child_size.height());
1919        let top = self.baseline(app) - child_baseline;
1920        BaselineSizes {
1921            size: constraints.constrain(Size::new(child_size.width(), top + child_size.height())),
1922            top,
1923        }
1924    }
1925}
1926
1927impl RenderObjectWithChildMixin for RenderBaseline {
1928    type ChildType = AnyRenderBox;
1929
1930    fn child_data(self: RenderHandle<Self>, app: &App) -> &RenderObjectWithChildData<AnyRenderBox> {
1931        &self.get(app).child
1932    }
1933
1934    fn child_data_mut(
1935        self: RenderHandle<Self>,
1936        app: &mut App,
1937    ) -> &mut RenderObjectWithChildData<AnyRenderBox> {
1938        &mut self.get_mut(app).child
1939    }
1940}
1941
1942impl RenderShiftedBox for RenderBaseline {}
1943
1944impl RenderObject for RenderBaseline {
1945    crate::render_object_accessors!();
1946
1947    fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
1948        let constraints = self.constraints(app);
1949        let sizes = self.compute_sizes(
1950            app,
1951            constraints,
1952            ChildLayoutHelper::layout_child,
1953            ChildLayoutHelper::get_baseline,
1954        );
1955        self.set_size(app, sizes.size);
1956        if let Some(child) = self.child(app) {
1957            child.parent_data_of_mut::<BoxParentData>(app).offset = Offset::new(0.0, sizes.top);
1958        }
1959    }
1960
1961    fn paint(
1962        self: RenderHandle<Self>,
1963        app: &mut App,
1964        context: &mut PaintingContext,
1965        offset: Offset,
1966    ) {
1967        RenderShiftedBox::paint(self, app, context, offset);
1968    }
1969
1970    fn visit_children(
1971        self: RenderHandle<Self>,
1972        app: &App,
1973        visitor: &mut dyn FnMut(AnyRenderObject),
1974    ) {
1975        if let Some(child) = self.child(app) {
1976            visitor(child.as_object());
1977        }
1978    }
1979}
1980
1981impl RenderBox for RenderBaseline {
1982    crate::render_box_accessors!();
1983
1984    fn hit_test_children(
1985        self: RenderHandle<Self>,
1986        app: &mut App,
1987        result: &mut BoxHitTestResult<'_>,
1988        position: Offset,
1989    ) -> bool {
1990        RenderShiftedBox::hit_test_children(self, app, result, position)
1991    }
1992
1993    fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1994        RenderShiftedBox::compute_min_intrinsic_width(self, app, height)
1995    }
1996
1997    fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1998        RenderShiftedBox::compute_max_intrinsic_width(self, app, height)
1999    }
2000
2001    fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
2002        RenderShiftedBox::compute_min_intrinsic_height(self, app, width)
2003    }
2004
2005    fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
2006        RenderShiftedBox::compute_max_intrinsic_height(self, app, width)
2007    }
2008
2009    fn compute_distance_to_actual_baseline(
2010        self: RenderHandle<Self>,
2011        app: &mut App,
2012        baseline: TextBaseline,
2013    ) -> Option<f64> {
2014        RenderShiftedBox::compute_distance_to_actual_baseline(self, app, baseline)
2015    }
2016
2017    fn compute_dry_layout(
2018        self: RenderHandle<Self>,
2019        app: &mut App,
2020        constraints: BoxConstraints,
2021    ) -> Size {
2022        self.compute_sizes(
2023            app,
2024            constraints,
2025            ChildLayoutHelper::dry_layout_child,
2026            ChildLayoutHelper::get_dry_baseline,
2027        )
2028        .size
2029    }
2030
2031    fn compute_dry_baseline(
2032        self: RenderHandle<Self>,
2033        app: &mut App,
2034        constraints: BoxConstraints,
2035        baseline: TextBaseline,
2036    ) -> Option<f64> {
2037        let child = self.child(app)?;
2038        let child_constraints = constraints.loosen();
2039        let baseline_type = self.baseline_type(app);
2040        let result1 = child.get_dry_baseline(app, child_constraints, baseline)?;
2041        let result2 = child.get_dry_baseline(app, child_constraints, baseline_type)?;
2042        Some(self.baseline(app) + result1 - result2)
2043    }
2044}
2045
2046/// A delegate for computing the layout of a render object with a single child.
2047///
2048/// Used by [`RenderCustomSingleChildLayoutBox`] and `CustomSingleChildLayout` (in the widgets
2049/// library).
2050///
2051/// When asked to layout, [`RenderCustomSingleChildLayoutBox`] first calls
2052/// [`get_size`](Self::get_size) with its incoming constraints to determine its size. It then
2053/// uses [`get_constraints_for_child`](Self::get_constraints_for_child) to determine the
2054/// constraints to apply to the child. After the child completes its layout,
2055/// [`RenderCustomSingleChildLayoutBox`] calls [`get_position_for_child`](Self::get_position_for_child)
2056/// to determine the child's position.
2057///
2058/// The [`relayout`](Self::relayout) listenable causes the layout to update whenever it
2059/// notifies its listeners.
2060pub trait SingleChildLayoutDelegate: Debug + 'static {
2061    /// The [`Listenable`] the layout will update on, Dart's `relayout` constructor argument.
2062    ///
2063    /// Defaults to `None`.
2064    fn relayout(&self) -> Option<&Rc<dyn Listenable>> {
2065        None
2066    }
2067
2068    /// The size of this object given the incoming constraints.
2069    ///
2070    /// Defaults to the biggest size that satisfies the given constraints.
2071    fn get_size(&self, constraints: BoxConstraints) -> Size {
2072        constraints.biggest()
2073    }
2074
2075    /// The constraints for the child given the incoming constraints.
2076    ///
2077    /// During layout, the child is given the layout constraints returned by this function. The
2078    /// child is required to pick a size for itself that satisfies these constraints.
2079    ///
2080    /// Defaults to the given constraints.
2081    fn get_constraints_for_child(&self, constraints: BoxConstraints) -> BoxConstraints {
2082        constraints
2083    }
2084
2085    /// The position where the child should be placed.
2086    ///
2087    /// The `size` argument is the size of the parent, which might be different from the value
2088    /// returned by [`get_size`](Self::get_size) if that size doesn't satisfy the constraints
2089    /// passed to [`get_size`](Self::get_size). The `child_size` argument is the size of the
2090    /// child, which will satisfy the constraints returned by
2091    /// [`get_constraints_for_child`](Self::get_constraints_for_child).
2092    ///
2093    /// Defaults to positioning the child in the upper left corner of the parent.
2094    fn get_position_for_child(&self, size: Size, child_size: Size) -> Offset {
2095        let _ = (size, child_size);
2096        Offset::ZERO
2097    }
2098
2099    /// Called whenever a new instance of the custom layout delegate class is provided to the
2100    /// [`RenderCustomSingleChildLayoutBox`] object, or any time that a new `CustomSingleChildLayout`
2101    /// object is created with a new instance of the custom layout delegate class (which amounts
2102    /// to the same thing, because the latter is implemented in terms of the former).
2103    ///
2104    /// If the new instance represents different information than the old instance, then the
2105    /// method should return true, otherwise it should return false.
2106    ///
2107    /// If the method returns false, then the [`get_size`](Self::get_size),
2108    /// [`get_constraints_for_child`](Self::get_constraints_for_child), and
2109    /// [`get_position_for_child`](Self::get_position_for_child) calls might be optimized away.
2110    ///
2111    /// It's possible that the layout methods will get called even if [`should_relayout`](Self::should_relayout)
2112    /// returns false (e.g. if an ancestor changed its layout). It's also possible that the
2113    /// layout method will get called without [`should_relayout`](Self::should_relayout) being
2114    /// called at all (e.g. if the parent changes size).
2115    ///
2116    /// `old_delegate` is of the same concrete type, Dart's `covariant`: narrow it with
2117    /// [`as_any`](Self::as_any).
2118    fn should_relayout(&self, old_delegate: &dyn SingleChildLayoutDelegate) -> bool;
2119
2120    /// The concrete delegate, for Dart's `runtimeType` comparison and its `covariant`
2121    /// narrowing of [`should_relayout`](Self::should_relayout)'s argument.
2122    fn as_any(&self) -> &dyn Any;
2123}
2124
2125/// Defers the layout of its single child to a delegate.
2126///
2127/// The delegate can determine the layout constraints for the child and can decide where to
2128/// position the child. The delegate can also determine the size of the parent, but the size of
2129/// the parent cannot depend on the size of the child.
2130pub struct RenderCustomSingleChildLayoutBox {
2131    render_object: RenderObjectData,
2132    render_box: RenderBoxData,
2133    child: RenderObjectWithChildData<AnyRenderBox>,
2134    delegate: Rc<dyn SingleChildLayoutDelegate>,
2135}
2136
2137impl RenderCustomSingleChildLayoutBox {
2138    /// Creates a render box that defers its layout to a delegate.
2139    pub fn new(
2140        app: &mut App,
2141        delegate: Rc<dyn SingleChildLayoutDelegate>,
2142        child: Option<AnyRenderBox>,
2143    ) -> RenderHandle<RenderCustomSingleChildLayoutBox> {
2144        let this = RenderHandle::new_box(
2145            app,
2146            RenderCustomSingleChildLayoutBox {
2147                render_object: RenderObjectData::new(),
2148                render_box: RenderBoxData::new(),
2149                child: RenderObjectWithChildData::new(),
2150                delegate,
2151            },
2152        );
2153        this.set_child(app, child);
2154        this
2155    }
2156
2157    /// A delegate that controls this object's layout.
2158    pub fn delegate(self: RenderHandle<Self>, app: &App) -> Rc<dyn SingleChildLayoutDelegate> {
2159        Rc::clone(&self.get(app).delegate)
2160    }
2161
2162    /// Sets [`delegate`](Self::delegate).
2163    pub fn set_delegate(
2164        self: RenderHandle<Self>,
2165        app: &mut App,
2166        new_delegate: Rc<dyn SingleChildLayoutDelegate>,
2167    ) {
2168        let old_delegate = Rc::clone(&self.get(app).delegate);
2169        if Rc::ptr_eq(&old_delegate, &new_delegate) {
2170            return;
2171        }
2172        if new_delegate.as_any().type_id() != old_delegate.as_any().type_id()
2173            || new_delegate.should_relayout(&*old_delegate)
2174        {
2175            self.mark_needs_layout(app);
2176        }
2177        self.get_mut(app).delegate = Rc::clone(&new_delegate);
2178        if self.attached(app) {
2179            if let Some(relayout) = old_delegate.relayout() {
2180                relayout.remove_listener(app, &self.relayout_listener());
2181            }
2182            if let Some(relayout) = new_delegate.relayout() {
2183                relayout.add_listener(app, self.relayout_listener());
2184            }
2185        }
2186    }
2187
2188    /// The `markNeedsLayout` tear-off, equal to itself across registrations.
2189    fn relayout_listener(self: RenderHandle<Self>) -> Listener {
2190        Listener::handle_method(self.handle(), mark_needs_layout_custom_single_child)
2191    }
2192
2193    /// Dart's `_getSize`.
2194    fn get_size(self: RenderHandle<Self>, app: &App, constraints: BoxConstraints) -> Size {
2195        constraints.constrain(self.get(app).delegate.get_size(constraints))
2196    }
2197}
2198
2199fn mark_needs_layout_custom_single_child(
2200    this: Handle<RenderCustomSingleChildLayoutBox>,
2201    app: &mut App,
2202) {
2203    RenderHandle::from_handle(this).mark_needs_layout(app);
2204}
2205
2206impl RenderObjectWithChildMixin for RenderCustomSingleChildLayoutBox {
2207    type ChildType = AnyRenderBox;
2208
2209    fn child_data(self: RenderHandle<Self>, app: &App) -> &RenderObjectWithChildData<AnyRenderBox> {
2210        &self.get(app).child
2211    }
2212
2213    fn child_data_mut(
2214        self: RenderHandle<Self>,
2215        app: &mut App,
2216    ) -> &mut RenderObjectWithChildData<AnyRenderBox> {
2217        &mut self.get_mut(app).child
2218    }
2219}
2220
2221impl RenderShiftedBox for RenderCustomSingleChildLayoutBox {}
2222
2223impl RenderObject for RenderCustomSingleChildLayoutBox {
2224    crate::render_object_accessors!();
2225
2226    fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
2227        let constraints = self.constraints(app);
2228        let size = self.get_size(app, constraints);
2229        self.set_size(app, size);
2230        if let Some(child) = self.child(app) {
2231            let child_constraints = self
2232                .get(app)
2233                .delegate
2234                .get_constraints_for_child(constraints);
2235            debug_assert!(child_constraints.debug_assert_is_valid(true));
2236            child.layout(app, child_constraints, !child_constraints.is_tight());
2237            let child_size = if child_constraints.is_tight() {
2238                child_constraints.smallest()
2239            } else {
2240                child.size(app)
2241            };
2242            child.parent_data_of_mut::<BoxParentData>(app).offset = self
2243                .get(app)
2244                .delegate
2245                .get_position_for_child(size, child_size);
2246        }
2247    }
2248
2249    fn paint(
2250        self: RenderHandle<Self>,
2251        app: &mut App,
2252        context: &mut PaintingContext,
2253        offset: Offset,
2254    ) {
2255        RenderShiftedBox::paint(self, app, context, offset);
2256    }
2257
2258    fn visit_children(
2259        self: RenderHandle<Self>,
2260        app: &App,
2261        visitor: &mut dyn FnMut(AnyRenderObject),
2262    ) {
2263        if let Some(child) = self.child(app) {
2264            visitor(child.as_object());
2265        }
2266    }
2267
2268    fn did_attach(self: RenderHandle<Self>, app: &mut App, owner: Handle<PipelineOwner>) {
2269        if let Some(child) = self.child(app) {
2270            child.as_object().attach(app, owner);
2271        }
2272        if let Some(relayout) = self.delegate(app).relayout() {
2273            relayout.add_listener(app, self.relayout_listener());
2274        }
2275    }
2276
2277    fn did_detach(self: RenderHandle<Self>, app: &mut App) {
2278        if let Some(relayout) = self.delegate(app).relayout() {
2279            relayout.remove_listener(app, &self.relayout_listener());
2280        }
2281        if let Some(child) = self.child(app) {
2282            child.as_object().detach(app);
2283        }
2284    }
2285}
2286
2287impl RenderBox for RenderCustomSingleChildLayoutBox {
2288    crate::render_box_accessors!();
2289
2290    fn hit_test_children(
2291        self: RenderHandle<Self>,
2292        app: &mut App,
2293        result: &mut BoxHitTestResult<'_>,
2294        position: Offset,
2295    ) -> bool {
2296        RenderShiftedBox::hit_test_children(self, app, result, position)
2297    }
2298
2299    // TODO(ianh): It's a bit dubious to be using the getSize function from the delegate to
2300    // figure out the intrinsic dimensions. We really should either not support intrinsics,
2301    // or we should expose intrinsic delegate callbacks and throw if they're not implemented.
2302
2303    fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
2304        let width = self
2305            .get_size(app, BoxConstraints::tight_for_finite(f64::INFINITY, height))
2306            .width();
2307        if width.is_finite() { width } else { 0.0 }
2308    }
2309
2310    fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
2311        let width = self
2312            .get_size(app, BoxConstraints::tight_for_finite(f64::INFINITY, height))
2313            .width();
2314        if width.is_finite() { width } else { 0.0 }
2315    }
2316
2317    fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
2318        let height = self
2319            .get_size(app, BoxConstraints::tight_for_finite(width, f64::INFINITY))
2320            .height();
2321        if height.is_finite() { height } else { 0.0 }
2322    }
2323
2324    fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
2325        let height = self
2326            .get_size(app, BoxConstraints::tight_for_finite(width, f64::INFINITY))
2327            .height();
2328        if height.is_finite() { height } else { 0.0 }
2329    }
2330
2331    fn compute_dry_layout(
2332        self: RenderHandle<Self>,
2333        app: &mut App,
2334        constraints: BoxConstraints,
2335    ) -> Size {
2336        self.get_size(app, constraints)
2337    }
2338
2339    fn compute_dry_baseline(
2340        self: RenderHandle<Self>,
2341        app: &mut App,
2342        constraints: BoxConstraints,
2343        baseline: TextBaseline,
2344    ) -> Option<f64> {
2345        let child = self.child(app)?;
2346        let child_constraints = self
2347            .get(app)
2348            .delegate
2349            .get_constraints_for_child(constraints);
2350        let result = child.get_dry_baseline(app, child_constraints, baseline)?;
2351        let child_size = if child_constraints.is_tight() {
2352            child_constraints.smallest()
2353        } else {
2354            child.get_dry_layout(app, child_constraints)
2355        };
2356        Some(
2357            result
2358                + self
2359                    .get(app)
2360                    .delegate
2361                    .get_position_for_child(self.get_size(app, constraints), child_size)
2362                    .dy(),
2363        )
2364    }
2365}
2366
2367#[cfg(test)]
2368mod tests {
2369    use super::*;
2370    use inset_embedder::Size;
2371    use inset_foundation::AppCell;
2372
2373    use crate::box_::BoxConstraints;
2374    use crate::layer::{ContainerLayer, ErasedLayer, OffsetLayer, PictureLayer};
2375    use crate::pipeline_owner::PipelineOwner;
2376    use crate::proxy_box::{RenderConstrainedBox, RenderRepaintBoundary};
2377    use inset_embedder::valo::Op;
2378
2379    /// `padding_test.dart`: the padding is added to the child's intrinsics, and taken off the
2380    /// extent handed to the child.
2381    #[test]
2382    fn padding_intrinsics_add_the_insets() {
2383        let cell = AppCell::new();
2384        let mut app = cell.borrow_mut();
2385        let child =
2386            RenderConstrainedBox::new(&mut app, BoxConstraints::tight(Size::new(80.0, 40.0)), None);
2387        let padding = RenderPadding::new(
2388            &mut app,
2389            EdgeInsetsGeometry::all(8.0),
2390            None,
2391            Some(child.as_box()),
2392        );
2393        let padded = padding.as_box();
2394        assert_eq!(padded.get_min_intrinsic_width(&mut app, 100.0), 96.0);
2395        assert_eq!(padded.get_max_intrinsic_width(&mut app, 100.0), 96.0);
2396        assert_eq!(padded.get_min_intrinsic_height(&mut app, 100.0), 56.0);
2397        assert_eq!(padded.get_max_intrinsic_height(&mut app, 100.0), 56.0);
2398    }
2399
2400    /// Without a child, the padding is all there is.
2401    #[test]
2402    fn padding_intrinsics_without_a_child_are_the_insets() {
2403        let cell = AppCell::new();
2404        let mut app = cell.borrow_mut();
2405        let padding = RenderPadding::new(&mut app, EdgeInsetsGeometry::all(10.0), None, None);
2406        assert_eq!(
2407            padding.as_box().get_min_intrinsic_width(&mut app, 0.0),
2408            20.0
2409        );
2410        assert_eq!(
2411            padding.as_box().get_max_intrinsic_height(&mut app, 0.0),
2412            20.0
2413        );
2414    }
2415
2416    /// The dry layout of a padding is the size it lays out to.
2417    #[test]
2418    fn padding_dry_layout_matches_its_layout() {
2419        let cell = AppCell::new();
2420        let mut app = cell.borrow_mut();
2421        let child =
2422            RenderConstrainedBox::new(&mut app, BoxConstraints::tight(Size::new(80.0, 40.0)), None);
2423        let padding = RenderPadding::new(
2424            &mut app,
2425            EdgeInsetsGeometry::all(8.0),
2426            None,
2427            Some(child.as_box()),
2428        );
2429        let constraints = BoxConstraints::new().max_width(500.0).max_height(500.0);
2430        let dry = padding.as_box().get_dry_layout(&mut app, constraints);
2431        padding.layout(&mut app, constraints, false);
2432        assert_eq!(dry, padding.size(&app));
2433    }
2434
2435    /// `unconstrained_box_test.dart`: an `UnconstrainedBox` is a
2436    /// `RenderConstraintsTransformBox` whose transform drops every constraint, so the child takes
2437    /// its natural size and is aligned within what the parent allows.
2438    #[test]
2439    fn constraints_transform_box_leaves_the_child_unconstrained_and_aligns_it() {
2440        let cell = AppCell::new();
2441        let mut app = cell.borrow_mut();
2442        let child =
2443            RenderConstrainedBox::new(&mut app, BoxConstraints::tight(Size::new(50.0, 20.0)), None);
2444        let unconstrained = RenderConstraintsTransformBox::new(
2445            &mut app,
2446            AlignmentGeometry::CENTER,
2447            None,
2448            |_| BoxConstraints::new(),
2449            Some(child.as_box()),
2450        );
2451        unconstrained.layout(
2452            &mut app,
2453            BoxConstraints::tight(Size::new(100.0, 100.0)),
2454            false,
2455        );
2456        assert_eq!(child.size(&app), Size::new(50.0, 20.0));
2457        assert_eq!(unconstrained.size(&app), Size::new(100.0, 100.0));
2458        assert_eq!(
2459            child.as_box().box_parent_data(&app).offset,
2460            Offset::new(25.0, 40.0)
2461        );
2462    }
2463
2464    /// A child that does not fit is clipped when the clip behavior asks for it.
2465    #[test]
2466    fn constraints_transform_box_clips_an_overflowing_child() {
2467        let cell = AppCell::new();
2468        let mut app = cell.borrow_mut();
2469        let child = RenderConstrainedBox::new(
2470            &mut app,
2471            BoxConstraints::tight(Size::new(200.0, 200.0)),
2472            None,
2473        );
2474        let unconstrained = RenderConstraintsTransformBox::new(
2475            &mut app,
2476            AlignmentGeometry::CENTER,
2477            None,
2478            |_| BoxConstraints::new(),
2479            Some(child.as_box()),
2480        );
2481        unconstrained.set_clip_behavior(&mut app, Clip::HardEdge);
2482        let root = RenderRepaintBoundary::new(&mut app, Some(unconstrained.as_box()));
2483        let owner = PipelineOwner::new(&mut app, None);
2484        owner.set_root_node(&mut app, Some(root.as_object()));
2485        root.layout(
2486            &mut app,
2487            BoxConstraints::tight(Size::new(100.0, 100.0)),
2488            false,
2489        );
2490        let paint_root = OffsetLayer::new(&mut app, Offset::ZERO);
2491        paint_root
2492            .as_layer()
2493            .attach(&mut app, root.as_object().id());
2494        root.as_object()
2495            .schedule_initial_paint(&mut app, paint_root.as_container_layer());
2496        owner.flush_compositing_bits(&mut app);
2497        owner.flush_paint(&mut app);
2498
2499        assert_eq!(unconstrained.size(&app), Size::new(100.0, 100.0));
2500        let layer = root.as_object().debug_layer(&app).expect("painted");
2501        let clipped = layer
2502            .depth_first_iterate_children(&app)
2503            .into_iter()
2504            .filter_map(|child| {
2505                app.handle::<PictureLayer>(child.id())
2506                    .and_then(|picture| picture.picture(&app).map(|p| p.ops().to_vec()))
2507            })
2508            .flatten()
2509            .any(|op| matches!(op, Op::ClipPath { .. }));
2510        assert!(clipped, "the overflowing child is clipped");
2511    }
2512
2513    /// A `RenderConstrainedOverflowBox` lets its child overflow and keeps the parent's size.
2514    #[test]
2515    fn constrained_overflow_box_sizes_itself_to_the_parent() {
2516        let cell = AppCell::new();
2517        let mut app = cell.borrow_mut();
2518        let child = RenderConstrainedBox::new(&mut app, BoxConstraints::new(), None);
2519        let overflow = RenderConstrainedOverflowBox::new(
2520            &mut app,
2521            AlignmentGeometry::CENTER,
2522            None,
2523            Some(child.as_box()),
2524        );
2525        overflow.set_min_width(&mut app, Some(200.0));
2526        overflow.set_max_width(&mut app, Some(200.0));
2527        overflow.layout(
2528            &mut app,
2529            BoxConstraints::tight(Size::new(100.0, 100.0)),
2530            false,
2531        );
2532        assert_eq!(overflow.size(&app), Size::new(100.0, 100.0));
2533        assert_eq!(child.size(&app).width(), 200.0);
2534        assert_eq!(
2535            child.as_box().box_parent_data(&app).offset.dx(),
2536            -50.0,
2537            "the child is centered, so it overflows both sides"
2538        );
2539    }
2540
2541    /// A `RenderSizedOverflowBox` takes its requested size and passes the original constraints
2542    /// through, and reports the requested size as its intrinsics.
2543    #[test]
2544    fn sized_overflow_box_takes_its_requested_size() {
2545        let cell = AppCell::new();
2546        let mut app = cell.borrow_mut();
2547        let child =
2548            RenderConstrainedBox::new(&mut app, BoxConstraints::tight(Size::new(30.0, 30.0)), None);
2549        let sized = RenderSizedOverflowBox::new(
2550            &mut app,
2551            Size::new(50.0, 50.0),
2552            AlignmentGeometry::CENTER,
2553            None,
2554            Some(child.as_box()),
2555        );
2556        sized.layout(&mut app, BoxConstraints::new().max_width(100.0), false);
2557        assert_eq!(sized.size(&app), Size::new(50.0, 50.0));
2558        assert_eq!(sized.as_box().get_min_intrinsic_width(&mut app, 0.0), 50.0);
2559        assert_eq!(sized.as_box().get_max_intrinsic_height(&mut app, 0.0), 50.0);
2560    }
2561
2562    /// A `RenderFractionallySizedOverflowBox` tightens the child to a fraction of the incoming
2563    /// maximum, and divides its intrinsics by the factor.
2564    #[test]
2565    fn fractionally_sized_box_tightens_the_child_to_a_fraction() {
2566        let cell = AppCell::new();
2567        let mut app = cell.borrow_mut();
2568        let child = RenderConstrainedBox::new(&mut app, BoxConstraints::new(), None);
2569        let fraction = RenderFractionallySizedOverflowBox::new(
2570            &mut app,
2571            AlignmentGeometry::CENTER,
2572            None,
2573            Some(child.as_box()),
2574        );
2575        fraction.set_width_factor(&mut app, Some(0.5));
2576        fraction.layout(
2577            &mut app,
2578            BoxConstraints::new().max_width(200.0).max_height(80.0),
2579            false,
2580        );
2581        assert_eq!(child.size(&app).width(), 100.0);
2582        assert_eq!(fraction.size(&app).width(), 100.0);
2583    }
2584
2585    /// `baseline_test.dart`: the child is shifted down so that its baseline sits at the given
2586    /// distance from the top.
2587    #[test]
2588    fn baseline_shifts_the_child_down_to_its_baseline() {
2589        let cell = AppCell::new();
2590        let mut app = cell.borrow_mut();
2591        let child =
2592            RenderConstrainedBox::new(&mut app, BoxConstraints::tight(Size::new(20.0, 20.0)), None);
2593        let baseline = RenderBaseline::new(
2594            &mut app,
2595            30.0,
2596            TextBaseline::Alphabetic,
2597            Some(child.as_box()),
2598        );
2599        baseline.layout(&mut app, BoxConstraints::new(), false);
2600        // The child has no baseline, so its bottom is used: the shift is 30 - 20.
2601        assert_eq!(
2602            child.as_box().box_parent_data(&app).offset,
2603            Offset::new(0.0, 10.0)
2604        );
2605        assert_eq!(baseline.size(&app), Size::new(20.0, 30.0));
2606    }
2607
2608    #[test]
2609    fn padding_around_tight_child() {
2610        let cell = AppCell::new();
2611        let mut app = cell.borrow_mut();
2612        let child =
2613            RenderConstrainedBox::new(&mut app, BoxConstraints::tight(Size::new(80.0, 40.0)), None);
2614        let padding = RenderPadding::new(
2615            &mut app,
2616            EdgeInsetsGeometry::all(8.0),
2617            None,
2618            Some(child.as_box()),
2619        );
2620        padding.layout(&mut app, BoxConstraints::new(), false);
2621        assert_eq!(child.size(&app), Size::new(80.0, 40.0));
2622        assert_eq!(padding.size(&app), Size::new(96.0, 56.0));
2623        assert_eq!(
2624            child.as_box().box_parent_data(&app).offset,
2625            Offset::new(8.0, 8.0)
2626        );
2627    }
2628
2629    #[test]
2630    fn positioned_box_centers_its_child() {
2631        let cell = AppCell::new();
2632        let mut app = cell.borrow_mut();
2633        let child =
2634            RenderConstrainedBox::new(&mut app, BoxConstraints::tight(Size::new(20.0, 20.0)), None);
2635        let positioned = RenderPositionedBox::new(
2636            &mut app,
2637            AlignmentGeometry::CENTER,
2638            None,
2639            None,
2640            None,
2641            Some(child.as_box()),
2642        );
2643        positioned.layout(
2644            &mut app,
2645            BoxConstraints::tight(Size::new(100.0, 100.0)),
2646            false,
2647        );
2648        assert_eq!(positioned.size(&app), Size::new(100.0, 100.0));
2649        assert_eq!(
2650            child.as_box().box_parent_data(&app).offset,
2651            Offset::new(40.0, 40.0)
2652        );
2653    }
2654
2655    #[test]
2656    fn positioned_box_shrink_wraps_with_a_factor() {
2657        let cell = AppCell::new();
2658        let mut app = cell.borrow_mut();
2659        let child =
2660            RenderConstrainedBox::new(&mut app, BoxConstraints::tight(Size::new(20.0, 20.0)), None);
2661        let positioned = RenderPositionedBox::new(
2662            &mut app,
2663            AlignmentGeometry::CENTER,
2664            None,
2665            Some(2.0),
2666            None,
2667            Some(child.as_box()),
2668        );
2669        positioned.layout(
2670            &mut app,
2671            BoxConstraints::new().max_width(100.0).max_height(100.0),
2672            false,
2673        );
2674        assert_eq!(positioned.size(&app), Size::new(40.0, 100.0));
2675        assert_eq!(
2676            child.as_box().box_parent_data(&app).offset,
2677            Offset::new(10.0, 40.0)
2678        );
2679    }
2680
2681    #[test]
2682    fn padding_without_child() {
2683        let cell = AppCell::new();
2684        let mut app = cell.borrow_mut();
2685        let padding = RenderPadding::new(&mut app, EdgeInsetsGeometry::all(10.0), None, None);
2686        padding.layout(
2687            &mut app,
2688            BoxConstraints::tight(Size::new(100.0, 100.0)),
2689            false,
2690        );
2691        assert_eq!(padding.size(&app), Size::new(100.0, 100.0));
2692    }
2693
2694    #[derive(Debug)]
2695    struct OffsetDelegate {
2696        offset: Offset,
2697    }
2698
2699    impl SingleChildLayoutDelegate for OffsetDelegate {
2700        fn get_size(&self, constraints: BoxConstraints) -> Size {
2701            constraints.biggest()
2702        }
2703
2704        fn get_constraints_for_child(&self, constraints: BoxConstraints) -> BoxConstraints {
2705            constraints.loosen()
2706        }
2707
2708        fn get_position_for_child(&self, _size: Size, _child_size: Size) -> Offset {
2709            self.offset
2710        }
2711
2712        fn should_relayout(&self, old_delegate: &dyn SingleChildLayoutDelegate) -> bool {
2713            old_delegate
2714                .as_any()
2715                .downcast_ref::<OffsetDelegate>()
2716                .is_none_or(|old| old.offset != self.offset)
2717        }
2718
2719        fn as_any(&self) -> &dyn Any {
2720            self
2721        }
2722    }
2723
2724    #[test]
2725    fn custom_single_child_layout_positions_the_child() {
2726        let cell = AppCell::new();
2727        let mut app = cell.borrow_mut();
2728        let child =
2729            RenderConstrainedBox::new(&mut app, BoxConstraints::tight(Size::new(20.0, 10.0)), None);
2730        let parent = RenderCustomSingleChildLayoutBox::new(
2731            &mut app,
2732            Rc::new(OffsetDelegate {
2733                offset: Offset::new(8.0, 4.0),
2734            }),
2735            Some(child.as_box()),
2736        );
2737        parent.layout(
2738            &mut app,
2739            BoxConstraints::tight(Size::new(100.0, 50.0)),
2740            false,
2741        );
2742        assert_eq!(parent.size(&app), Size::new(100.0, 50.0));
2743        assert_eq!(
2744            child.as_box().box_parent_data(&app).offset,
2745            Offset::new(8.0, 4.0)
2746        );
2747    }
2748}