Skip to main content

inset_rendering/
box.rs

1//! Flutter counterpart: `rendering/box.dart` (`BoxConstraints`, `RenderBox`
2//! layout wrapper, intrinsics, dry layout and baselines).
3//!
4//! `_DebugSize` and `debugAssertDoesMeetConstraints` wait.
5
6use std::any::{Any, TypeId};
7use std::collections::HashMap;
8use std::fmt::{self, Debug, Display};
9use std::hash::{Hash, Hasher};
10
11use std::cell::Cell;
12use std::ops::{Add, Deref, DerefMut};
13
14use inset_embedder::{
15    Matrix4, Offset, Rect, Size, TextBaseline, ViewConstraints, clamp_double, lerp_double,
16};
17use inset_foundation::{App, Handle, HandleId};
18use inset_gestures::{HitTestEntry, HitTestResult, HitTestTarget, PointerEvent};
19use inset_painting::EdgeInsetsGeometry;
20use inset_painting::transform_point;
21
22use crate::object::{
23    AnyRenderObject, Constraints, ContainerParentDataMixin, ContainerRenderObjectMixin, ParentData,
24    RenderHandle, RenderObject, RenderObjectVTable, create, debug_checking_intrinsics, resolve,
25};
26use crate::painting_context::PaintingContext;
27use crate::pipeline_owner::PipelineOwner;
28
29thread_local! {
30    static DEBUG_DOING_BASELINE: Cell<bool> = const { Cell::new(false) };
31}
32
33/// Immutable layout constraints for `RenderBox` layout.
34///
35/// A [`Size`] respects a [`BoxConstraints`] if, and only if, all of the following
36/// relations hold:
37///
38/// * [`min_width`](Self::min_width) <= [`Size::width`] <= [`max_width`](Self::max_width)
39/// * [`min_height`](Self::min_height) <= [`Size::height`] <= [`max_height`](Self::max_height)
40#[derive(Clone, Copy)]
41pub struct BoxConstraints {
42    /// The minimum width that satisfies the constraints.
43    pub min_width: f64,
44    /// The maximum width that satisfies the constraints.
45    ///
46    /// Might be [`f64::INFINITY`].
47    pub max_width: f64,
48    /// The minimum height that satisfies the constraints.
49    pub min_height: f64,
50    /// The maximum height that satisfies the constraints.
51    ///
52    /// Might be [`f64::INFINITY`].
53    pub max_height: f64,
54}
55
56impl BoxConstraints {
57    /// Creates box constraints with the given constraints.
58    pub const fn new() -> BoxConstraints {
59        BoxConstraints {
60            min_width: 0.0,
61            max_width: f64::INFINITY,
62            min_height: 0.0,
63            max_height: f64::INFINITY,
64        }
65    }
66
67    /// Sets [`min_width`](Self::min_width).
68    pub const fn min_width(mut self, value: f64) -> BoxConstraints {
69        self.min_width = value;
70        self
71    }
72
73    /// Sets [`max_width`](Self::max_width).
74    pub const fn max_width(mut self, value: f64) -> BoxConstraints {
75        self.max_width = value;
76        self
77    }
78
79    /// Sets [`min_height`](Self::min_height).
80    pub const fn min_height(mut self, value: f64) -> BoxConstraints {
81        self.min_height = value;
82        self
83    }
84
85    /// Sets [`max_height`](Self::max_height).
86    pub const fn max_height(mut self, value: f64) -> BoxConstraints {
87        self.max_height = value;
88        self
89    }
90
91    /// Creates box constraints that is respected only by the given size.
92    pub fn tight(size: Size) -> BoxConstraints {
93        BoxConstraints {
94            min_width: size.width(),
95            max_width: size.width(),
96            min_height: size.height(),
97            max_height: size.height(),
98        }
99    }
100
101    /// Creates box constraints that require the given width or height.
102    pub const fn tight_for(width: Option<f64>, height: Option<f64>) -> BoxConstraints {
103        BoxConstraints {
104            min_width: match width {
105                Some(width) => width,
106                None => 0.0,
107            },
108            max_width: match width {
109                Some(width) => width,
110                None => f64::INFINITY,
111            },
112            min_height: match height {
113                Some(height) => height,
114                None => 0.0,
115            },
116            max_height: match height {
117                Some(height) => height,
118                None => f64::INFINITY,
119            },
120        }
121    }
122
123    /// Creates box constraints that require the given width or height, except if
124    /// they are infinite.
125    pub const fn tight_for_finite(width: f64, height: f64) -> BoxConstraints {
126        BoxConstraints {
127            min_width: if width != f64::INFINITY { width } else { 0.0 },
128            max_width: if width != f64::INFINITY {
129                width
130            } else {
131                f64::INFINITY
132            },
133            min_height: if height != f64::INFINITY { height } else { 0.0 },
134            max_height: if height != f64::INFINITY {
135                height
136            } else {
137                f64::INFINITY
138            },
139        }
140    }
141
142    /// Creates box constraints that forbid sizes larger than the given size.
143    pub fn loose(size: Size) -> BoxConstraints {
144        BoxConstraints {
145            min_width: 0.0,
146            max_width: size.width(),
147            min_height: 0.0,
148            max_height: size.height(),
149        }
150    }
151
152    /// Creates box constraints that expand to fill another box constraints.
153    pub const fn expand(width: Option<f64>, height: Option<f64>) -> BoxConstraints {
154        BoxConstraints {
155            min_width: match width {
156                Some(width) => width,
157                None => f64::INFINITY,
158            },
159            max_width: match width {
160                Some(width) => width,
161                None => f64::INFINITY,
162            },
163            min_height: match height {
164                Some(height) => height,
165                None => f64::INFINITY,
166            },
167            max_height: match height {
168                Some(height) => height,
169                None => f64::INFINITY,
170            },
171        }
172    }
173
174    /// Creates box constraints that match the given view constraints.
175    pub const fn from_view_constraints(constraints: ViewConstraints) -> BoxConstraints {
176        BoxConstraints {
177            min_width: constraints.min_width,
178            max_width: constraints.max_width,
179            min_height: constraints.min_height,
180            max_height: constraints.max_height,
181        }
182    }
183
184    /// Creates a copy of this box constraints. Chain field replacements.
185    pub const fn copy_with(self) -> BoxConstraints {
186        self
187    }
188
189    /// Returns new box constraints that are smaller by the given edge dimensions.
190    pub fn deflate(self, edges: EdgeInsetsGeometry) -> BoxConstraints {
191        debug_assert!(self.debug_assert_is_valid(false));
192        let horizontal = edges.horizontal();
193        let vertical = edges.vertical();
194        let deflated_min_width = (self.min_width - horizontal).max(0.0);
195        let deflated_min_height = (self.min_height - vertical).max(0.0);
196        BoxConstraints {
197            min_width: deflated_min_width,
198            max_width: (self.max_width - horizontal).max(deflated_min_width),
199            min_height: deflated_min_height,
200            max_height: (self.max_height - vertical).max(deflated_min_height),
201        }
202    }
203
204    /// Returns new box constraints that remove the minimum width and height requirements.
205    pub fn loosen(self) -> BoxConstraints {
206        debug_assert!(self.debug_assert_is_valid(false));
207        BoxConstraints {
208            min_width: 0.0,
209            max_width: self.max_width,
210            min_height: 0.0,
211            max_height: self.max_height,
212        }
213    }
214
215    /// Returns new box constraints that respect the given constraints while being
216    /// as close as possible to the original constraints.
217    pub fn enforce(self, constraints: BoxConstraints) -> BoxConstraints {
218        BoxConstraints {
219            min_width: clamp_double(self.min_width, constraints.min_width, constraints.max_width),
220            max_width: clamp_double(self.max_width, constraints.min_width, constraints.max_width),
221            min_height: clamp_double(
222                self.min_height,
223                constraints.min_height,
224                constraints.max_height,
225            ),
226            max_height: clamp_double(
227                self.max_height,
228                constraints.min_height,
229                constraints.max_height,
230            ),
231        }
232    }
233
234    /// Returns new box constraints with a tight width and/or height as close to
235    /// the given width and height as possible while still respecting the original
236    /// box constraints.
237    pub fn tighten(self, width: Option<f64>, height: Option<f64>) -> BoxConstraints {
238        BoxConstraints {
239            min_width: match width {
240                Some(width) => clamp_double(width, self.min_width, self.max_width),
241                None => self.min_width,
242            },
243            max_width: match width {
244                Some(width) => clamp_double(width, self.min_width, self.max_width),
245                None => self.max_width,
246            },
247            min_height: match height {
248                Some(height) => clamp_double(height, self.min_height, self.max_height),
249                None => self.min_height,
250            },
251            max_height: match height {
252                Some(height) => clamp_double(height, self.min_height, self.max_height),
253                None => self.max_height,
254            },
255        }
256    }
257
258    /// A box constraints with the width and height constraints flipped.
259    pub const fn flipped(self) -> BoxConstraints {
260        BoxConstraints {
261            min_width: self.min_height,
262            max_width: self.max_height,
263            min_height: self.min_width,
264            max_height: self.max_width,
265        }
266    }
267
268    /// Returns box constraints with the same width constraints but with
269    /// unconstrained height.
270    pub const fn width_constraints(self) -> BoxConstraints {
271        BoxConstraints {
272            min_width: self.min_width,
273            max_width: self.max_width,
274            min_height: 0.0,
275            max_height: f64::INFINITY,
276        }
277    }
278
279    /// Returns box constraints with the same height constraints but with
280    /// unconstrained width.
281    pub const fn height_constraints(self) -> BoxConstraints {
282        BoxConstraints {
283            min_width: 0.0,
284            max_width: f64::INFINITY,
285            min_height: self.min_height,
286            max_height: self.max_height,
287        }
288    }
289
290    /// Returns the width that both satisfies the constraints and is as close as
291    /// possible to the given width.
292    pub fn constrain_width(self, width: f64) -> f64 {
293        debug_assert!(self.debug_assert_is_valid(false));
294        clamp_double(width, self.min_width, self.max_width)
295    }
296
297    /// Returns the height that both satisfies the constraints and is as close as
298    /// possible to the given height.
299    pub fn constrain_height(self, height: f64) -> f64 {
300        debug_assert!(self.debug_assert_is_valid(false));
301        clamp_double(height, self.min_height, self.max_height)
302    }
303
304    /// Returns the size that both satisfies the constraints and is as close as
305    /// possible to the given size.
306    pub fn constrain(self, size: Size) -> Size {
307        Size::new(
308            self.constrain_width(size.width()),
309            self.constrain_height(size.height()),
310        )
311    }
312
313    /// Returns the size that both satisfies the constraints and is as close as
314    /// possible to the given width and height.
315    pub fn constrain_dimensions(self, width: f64, height: f64) -> Size {
316        Size::new(self.constrain_width(width), self.constrain_height(height))
317    }
318
319    /// Returns a size that attempts to meet the following conditions, in order:
320    ///
321    ///  * The size must satisfy these constraints.
322    ///  * The aspect ratio of the returned size matches the aspect ratio of the
323    ///    given size.
324    ///  * The returned size is as big as possible while still being equal to or
325    ///    smaller than the given size.
326    pub fn constrain_size_and_attempt_to_preserve_aspect_ratio(self, size: Size) -> Size {
327        if self.is_tight() {
328            return self.smallest();
329        }
330        if size.is_empty() {
331            return self.constrain(size);
332        }
333        let mut width = size.width();
334        let mut height = size.height();
335        let aspect_ratio = width / height;
336        if width > self.max_width {
337            width = self.max_width;
338            height = width / aspect_ratio;
339        }
340        if height > self.max_height {
341            height = self.max_height;
342            width = height * aspect_ratio;
343        }
344        if width < self.min_width {
345            width = self.min_width;
346            height = width / aspect_ratio;
347        }
348        if height < self.min_height {
349            height = self.min_height;
350            width = height * aspect_ratio;
351        }
352        Size::new(self.constrain_width(width), self.constrain_height(height))
353    }
354
355    /// The biggest size that satisfies the constraints.
356    pub fn biggest(self) -> Size {
357        Size::new(
358            self.constrain_width(f64::INFINITY),
359            self.constrain_height(f64::INFINITY),
360        )
361    }
362
363    /// The smallest size that satisfies the constraints.
364    pub fn smallest(self) -> Size {
365        Size::new(self.constrain_width(0.0), self.constrain_height(0.0))
366    }
367
368    /// Whether there is exactly one width value that satisfies the constraints.
369    pub fn has_tight_width(self) -> bool {
370        self.min_width >= self.max_width
371    }
372
373    /// Whether there is exactly one height value that satisfies the constraints.
374    pub fn has_tight_height(self) -> bool {
375        self.min_height >= self.max_height
376    }
377
378    /// Whether there is an upper bound on the maximum width.
379    pub fn has_bounded_width(self) -> bool {
380        self.max_width < f64::INFINITY
381    }
382
383    /// Whether there is an upper bound on the maximum height.
384    pub fn has_bounded_height(self) -> bool {
385        self.max_height < f64::INFINITY
386    }
387
388    /// Whether the width constraint is infinite.
389    pub fn has_infinite_width(self) -> bool {
390        self.min_width >= f64::INFINITY
391    }
392
393    /// Whether the height constraint is infinite.
394    pub fn has_infinite_height(self) -> bool {
395        self.min_height >= f64::INFINITY
396    }
397
398    /// Whether the given size satisfies the constraints.
399    pub fn is_satisfied_by(self, size: Size) -> bool {
400        debug_assert!(self.debug_assert_is_valid(false));
401        self.min_width <= size.width()
402            && size.width() <= self.max_width
403            && self.min_height <= size.height()
404            && size.height() <= self.max_height
405    }
406
407    /// Scales each constraint parameter by the inverse of the given factor,
408    /// rounded toward zero (Dart `~/`).
409    pub fn truncating_div(self, factor: f64) -> BoxConstraints {
410        BoxConstraints {
411            min_width: (self.min_width / factor).trunc(),
412            max_width: (self.max_width / factor).trunc(),
413            min_height: (self.min_height / factor).trunc(),
414            max_height: (self.max_height / factor).trunc(),
415        }
416    }
417
418    /// Linearly interpolate between two box constraints.
419    ///
420    /// If either is none, this function interpolates from a [`BoxConstraints`]
421    /// object whose fields are all set to 0.0.
422    pub fn lerp(
423        a: Option<BoxConstraints>,
424        b: Option<BoxConstraints>,
425        t: f64,
426    ) -> Option<BoxConstraints> {
427        if a == b {
428            return a;
429        }
430        let Some(a) = a else {
431            return Some((b.unwrap() * t).normalize());
432        };
433        let Some(b) = b else {
434            return Some((a * (1.0 - t)).normalize());
435        };
436        debug_assert!(a.debug_assert_is_valid(false));
437        debug_assert!(b.debug_assert_is_valid(false));
438        debug_assert!(
439            (a.min_width.is_finite() && b.min_width.is_finite())
440                || (a.min_width == f64::INFINITY && b.min_width == f64::INFINITY),
441            "Cannot interpolate between finite constraints and unbounded constraints."
442        );
443        debug_assert!(
444            (a.max_width.is_finite() && b.max_width.is_finite())
445                || (a.max_width == f64::INFINITY && b.max_width == f64::INFINITY),
446            "Cannot interpolate between finite constraints and unbounded constraints."
447        );
448        debug_assert!(
449            (a.min_height.is_finite() && b.min_height.is_finite())
450                || (a.min_height == f64::INFINITY && b.min_height == f64::INFINITY),
451            "Cannot interpolate between finite constraints and unbounded constraints."
452        );
453        debug_assert!(
454            (a.max_height.is_finite() && b.max_height.is_finite())
455                || (a.max_height == f64::INFINITY && b.max_height == f64::INFINITY),
456            "Cannot interpolate between finite constraints and unbounded constraints."
457        );
458        Some(
459            BoxConstraints {
460                min_width: if a.min_width.is_finite() {
461                    lerp_double(Some(a.min_width), Some(b.min_width), t).unwrap()
462                } else {
463                    f64::INFINITY
464                },
465                max_width: if a.max_width.is_finite() {
466                    lerp_double(Some(a.max_width), Some(b.max_width), t).unwrap()
467                } else {
468                    f64::INFINITY
469                },
470                min_height: if a.min_height.is_finite() {
471                    lerp_double(Some(a.min_height), Some(b.min_height), t).unwrap()
472                } else {
473                    f64::INFINITY
474                },
475                max_height: if a.max_height.is_finite() {
476                    lerp_double(Some(a.max_height), Some(b.max_height), t).unwrap()
477                } else {
478                    f64::INFINITY
479                },
480            }
481            .normalize(),
482        )
483    }
484
485    /// Returns a box constraints that [`is_normalized`](Self::is_normalized).
486    pub fn normalize(self) -> BoxConstraints {
487        if self.is_normalized() {
488            return self;
489        }
490        let min_width = if self.min_width >= 0.0 {
491            self.min_width
492        } else {
493            0.0
494        };
495        let min_height = if self.min_height >= 0.0 {
496            self.min_height
497        } else {
498            0.0
499        };
500        BoxConstraints {
501            min_width,
502            max_width: if min_width > self.max_width {
503                min_width
504            } else {
505                self.max_width
506            },
507            min_height,
508            max_height: if min_height > self.max_height {
509                min_height
510            } else {
511                self.max_height
512            },
513        }
514    }
515}
516
517impl Default for BoxConstraints {
518    fn default() -> BoxConstraints {
519        BoxConstraints::new()
520    }
521}
522
523impl Constraints for BoxConstraints {
524    fn is_tight(&self) -> bool {
525        self.has_tight_width() && self.has_tight_height()
526    }
527
528    fn is_normalized(&self) -> bool {
529        self.min_width >= 0.0
530            && self.min_width <= self.max_width
531            && self.min_height >= 0.0
532            && self.min_height <= self.max_height
533    }
534
535    fn debug_assert_is_valid(&self, is_applied_constraint: bool) -> bool {
536        if cfg!(debug_assertions) {
537            assert!(
538                !self.min_width.is_nan()
539                    && !self.max_width.is_nan()
540                    && !self.min_height.is_nan()
541                    && !self.max_height.is_nan(),
542                "BoxConstraints has a NaN value."
543            );
544            assert!(
545                self.min_width >= 0.0,
546                "BoxConstraints has a negative minimum width."
547            );
548            assert!(
549                self.min_height >= 0.0,
550                "BoxConstraints has a negative minimum height."
551            );
552            assert!(
553                self.max_width >= self.min_width,
554                "BoxConstraints has non-normalized width constraints."
555            );
556            assert!(
557                self.max_height >= self.min_height,
558                "BoxConstraints has non-normalized height constraints."
559            );
560            if is_applied_constraint {
561                assert!(
562                    self.min_width.is_finite(),
563                    "BoxConstraints forces an infinite width."
564                );
565                assert!(
566                    self.min_height.is_finite(),
567                    "BoxConstraints forces an infinite height."
568                );
569            }
570            debug_assert!(self.is_normalized());
571        }
572        self.is_normalized()
573    }
574}
575
576impl std::ops::Mul<f64> for BoxConstraints {
577    type Output = BoxConstraints;
578
579    fn mul(self, factor: f64) -> BoxConstraints {
580        BoxConstraints {
581            min_width: self.min_width * factor,
582            max_width: self.max_width * factor,
583            min_height: self.min_height * factor,
584            max_height: self.max_height * factor,
585        }
586    }
587}
588
589impl std::ops::Div<f64> for BoxConstraints {
590    type Output = BoxConstraints;
591
592    fn div(self, factor: f64) -> BoxConstraints {
593        BoxConstraints {
594            min_width: self.min_width / factor,
595            max_width: self.max_width / factor,
596            min_height: self.min_height / factor,
597            max_height: self.max_height / factor,
598        }
599    }
600}
601
602impl std::ops::Rem<f64> for BoxConstraints {
603    type Output = BoxConstraints;
604
605    fn rem(self, value: f64) -> BoxConstraints {
606        BoxConstraints {
607            min_width: self.min_width % value,
608            max_width: self.max_width % value,
609            min_height: self.min_height % value,
610            max_height: self.max_height % value,
611        }
612    }
613}
614
615impl PartialEq for BoxConstraints {
616    fn eq(&self, other: &BoxConstraints) -> bool {
617        debug_assert!(self.debug_assert_is_valid(false));
618        debug_assert!(other.debug_assert_is_valid(false));
619        self.min_width == other.min_width
620            && self.max_width == other.max_width
621            && self.min_height == other.min_height
622            && self.max_height == other.max_height
623    }
624}
625
626impl Eq for BoxConstraints {}
627
628impl Hash for BoxConstraints {
629    fn hash<H: Hasher>(&self, state: &mut H) {
630        debug_assert!(self.debug_assert_is_valid(false));
631        self.min_width.to_bits().hash(state);
632        self.max_width.to_bits().hash(state);
633        self.min_height.to_bits().hash(state);
634        self.max_height.to_bits().hash(state);
635    }
636}
637
638impl Display for BoxConstraints {
639    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
640        let annotation = if self.is_normalized() {
641            ""
642        } else {
643            "; NOT NORMALIZED"
644        };
645        if self.min_width == f64::INFINITY && self.min_height == f64::INFINITY {
646            return write!(f, "BoxConstraints(biggest{annotation})");
647        }
648        if self.min_width == 0.0
649            && self.max_width == f64::INFINITY
650            && self.min_height == 0.0
651            && self.max_height == f64::INFINITY
652        {
653            return write!(f, "BoxConstraints(unconstrained{annotation})");
654        }
655        fn describe(min: f64, max: f64, dim: &str) -> String {
656            if min == max {
657                format!("{dim}={min:.1}")
658            } else {
659                format!("{min:.1}<={dim}<={max:.1}")
660            }
661        }
662        write!(
663            f,
664            "BoxConstraints({}, {}{annotation})",
665            describe(self.min_width, self.max_width, "w"),
666            describe(self.min_height, self.max_height, "h")
667        )
668    }
669}
670
671impl Debug for BoxConstraints {
672    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
673        Display::fmt(self, f)
674    }
675}
676
677/// Method signature for hit testing a [`RenderBox`].
678///
679/// Used by [`BoxHitTestResult::add_with_paint_transform`] to hit test children of a
680/// [`RenderBox`].
681pub type BoxHitTest<'r> = dyn FnOnce(&mut BoxHitTestResult<'r>, Offset) -> bool;
682
683/// The result of performing a hit test on [`RenderBox`]es.
684///
685/// A view over a [`HitTestResult`]: Dart's `BoxHitTestResult.wrap`. Dart's bare
686/// `BoxHitTestResult()` constructor is `wrap` over a fresh `HitTestResult`.
687pub struct BoxHitTestResult<'a>(&'a mut HitTestResult);
688
689impl<'a> BoxHitTestResult<'a> {
690    /// Wraps `result` to create a [`BoxHitTestResult`] that shares its path.
691    pub fn wrap(result: &'a mut HitTestResult) -> BoxHitTestResult<'a> {
692        BoxHitTestResult(result)
693    }
694
695    /// Transforms `position` to the local coordinate system of a child for hit-testing the
696    /// child.
697    ///
698    /// The actual hit testing of the child needs to be implemented in the provided `hit_test`
699    /// callback, which is invoked with the transformed `position` as argument.
700    ///
701    /// The provided paint `transform` (which describes the transform from the child to the
702    /// parent in 3D) is processed by `PointerEvent.removePerspectiveTransform` to remove the
703    /// perspective component and inverted before it is used to transform `position` from the
704    /// coordinate system of the parent to the system of the child.
705    ///
706    /// If `transform` is `None` it will be treated as the identity transform and `position` is
707    /// provided to the `hit_test` callback as-is. If `transform` cannot be inverted, the
708    /// `hit_test` callback is not invoked and false is returned.
709    pub fn add_with_paint_transform(
710        &mut self,
711        transform: Option<Matrix4>,
712        position: Offset,
713        hit_test: impl FnOnce(&mut BoxHitTestResult<'_>, Offset) -> bool,
714    ) -> bool {
715        let transform = match transform {
716            Some(transform) => match transform.invert() {
717                Some(inverted) => Some(inverted),
718                None => return false,
719            },
720            None => None,
721        };
722        self.add_with_raw_transform(transform, position, hit_test)
723    }
724
725    /// Convenience method for hit testing children, that are translated by an [`Offset`].
726    ///
727    /// The actual hit testing of the child needs to be implemented in the provided `hit_test`
728    /// callback, which is invoked with the transformed `position` as argument.
729    pub fn add_with_paint_offset(
730        &mut self,
731        offset: Option<Offset>,
732        position: Offset,
733        hit_test: impl FnOnce(&mut BoxHitTestResult<'_>, Offset) -> bool,
734    ) -> bool {
735        let transformed_position = match offset {
736            Some(offset) => position - offset,
737            None => position,
738        };
739        if let Some(offset) = offset {
740            self.0.push_offset(Offset::ZERO - offset);
741        }
742        let is_hit = hit_test(self, transformed_position);
743        if offset.is_some() {
744            self.0.pop_transform();
745        }
746        is_hit
747    }
748
749    /// Transforms `position` to the local coordinate system of a child for hit-testing the
750    /// child.
751    ///
752    /// Unlike [`add_with_paint_transform`](Self::add_with_paint_transform), the provided
753    /// `transform` is used as-is to transform `position`: it must describe the transform from
754    /// the parent to the child.
755    pub fn add_with_raw_transform(
756        &mut self,
757        transform: Option<Matrix4>,
758        position: Offset,
759        hit_test: impl FnOnce(&mut BoxHitTestResult<'_>, Offset) -> bool,
760    ) -> bool {
761        let transformed_position = match transform {
762            Some(transform) => transform_point(&transform, position),
763            None => position,
764        };
765        if let Some(transform) = transform {
766            self.0.push_transform(transform);
767        }
768        let is_hit = hit_test(self, transformed_position);
769        if transform.is_some() {
770            self.0.pop_transform();
771        }
772        is_hit
773    }
774
775    /// Pass-through method for adding a hit test while manually managing the position
776    /// transformation logic.
777    ///
778    /// Exactly one of `paint_offset`, `paint_transform`, or `raw_transform` must be given.
779    pub fn add_with_out_of_band_position(
780        &mut self,
781        paint_offset: Option<Offset>,
782        paint_transform: Option<Matrix4>,
783        raw_transform: Option<Matrix4>,
784        hit_test: impl FnOnce(&mut BoxHitTestResult<'_>) -> bool,
785    ) -> bool {
786        debug_assert_eq!(
787            [
788                paint_offset.is_some(),
789                paint_transform.is_some(),
790                raw_transform.is_some()
791            ]
792            .iter()
793            .filter(|given| **given)
794            .count(),
795            1,
796            "Exactly one transform or offset argument must be provided."
797        );
798        if let Some(paint_offset) = paint_offset {
799            self.0.push_offset(Offset::ZERO - paint_offset);
800        } else if let Some(raw_transform) = raw_transform {
801            self.0.push_transform(raw_transform);
802        } else {
803            let paint_transform = paint_transform
804                .expect("checked")
805                .invert()
806                .expect("paint_transform must be invertible.");
807            self.0.push_transform(paint_transform);
808        }
809        let is_hit = hit_test(self);
810        self.0.pop_transform();
811        is_hit
812    }
813}
814
815impl Deref for BoxHitTestResult<'_> {
816    type Target = HitTestResult;
817
818    fn deref(&self) -> &HitTestResult {
819        self.0
820    }
821}
822
823impl DerefMut for BoxHitTestResult<'_> {
824    fn deref_mut(&mut self) -> &mut HitTestResult {
825        self.0
826    }
827}
828
829/// A hit test entry used by [`RenderBox`].
830///
831/// Dart's `BoxHitTestEntry` subclasses `HitTestEntry`; here it is the entry's target, carrying
832/// the box and the position of the hit test in the local coordinates of the box. It becomes a
833/// [`HitTestEntry`] with [`From`].
834#[derive(Clone, Copy, Debug)]
835pub struct BoxHitTestEntry {
836    target: AnyRenderBox,
837    local_position: Offset,
838}
839
840impl BoxHitTestEntry {
841    /// Creates a box hit test entry.
842    pub fn new(target: AnyRenderBox, local_position: Offset) -> BoxHitTestEntry {
843        BoxHitTestEntry {
844            target,
845            local_position,
846        }
847    }
848
849    /// The [`RenderBox`] that was hit.
850    pub fn target(&self) -> AnyRenderBox {
851        self.target
852    }
853
854    /// The position of the hit test in the local coordinates of [`target`](Self::target).
855    pub fn local_position(&self) -> Offset {
856        self.local_position
857    }
858}
859
860impl HitTestTarget for BoxHitTestEntry {
861    fn handle_event(&self, app: &mut App, event: &PointerEvent, _entry: &HitTestEntry) {
862        self.target.handle_event(app, event, self);
863    }
864
865    fn retained_handle(&self) -> Option<HandleId> {
866        Some(self.target.id)
867    }
868}
869
870impl From<BoxHitTestEntry> for HitTestEntry {
871    fn from(entry: BoxHitTestEntry) -> HitTestEntry {
872        HitTestEntry::new(entry)
873    }
874}
875
876/// Parent data used by [`RenderBox`] and its subclasses.
877#[derive(Clone, Copy, Debug)]
878pub struct BoxParentData {
879    /// The offset at which to paint the child in the parent's coordinate system.
880    pub offset: Offset,
881}
882
883impl BoxParentData {
884    /// Creates box parent data with a zero offset.
885    pub const fn new() -> BoxParentData {
886        BoxParentData {
887            offset: Offset::ZERO,
888        }
889    }
890}
891
892impl Default for BoxParentData {
893    fn default() -> BoxParentData {
894        BoxParentData::new()
895    }
896}
897
898impl ParentData for BoxParentData {
899    fn provide(&self, id: TypeId) -> Option<&dyn Any> {
900        (id == TypeId::of::<BoxParentData>()).then_some(self)
901    }
902
903    fn provide_mut(&mut self, id: TypeId) -> Option<&mut dyn Any> {
904        (id == TypeId::of::<BoxParentData>()).then_some(self)
905    }
906}
907
908impl fmt::Display for BoxParentData {
909    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
910        write!(f, "offset={:?}", self.offset)
911    }
912}
913
914/// Parent data for a [`RenderBox`] subclass that uses [`ContainerRenderObjectMixin`].
915///
916/// Flutter's `ContainerBoxParentData`: a convenience class that mixes
917/// [`ContainerParentDataMixin`] into a [`BoxParentData`]. Here it is the pair of accessors a
918/// container's parent data provides, one for each half.
919pub trait ContainerBoxParentData: ContainerParentDataMixin<ChildType = AnyRenderBox> {
920    /// Mixin field access: the [`BoxParentData`] half.
921    ///
922    /// [`ParentData::provide`](crate::ParentData::provide) must answer [`BoxParentData`] with
923    /// the same value, or the box protocol cannot position this child.
924    fn box_parent_data(&self) -> &BoxParentData;
925
926    /// See [`box_parent_data`](Self::box_parent_data).
927    fn box_parent_data_mut(&mut self) -> &mut BoxParentData;
928
929    /// The offset at which to paint the child in the parent's coordinate system.
930    fn offset(&self) -> Offset {
931        self.box_parent_data().offset
932    }
933
934    /// Sets [`offset`](Self::offset).
935    fn set_offset(&mut self, value: Offset) {
936        self.box_parent_data_mut().offset = value;
937    }
938}
939
940/// A wrapper that represents the baseline location of a [`RenderBox`].
941#[derive(Clone, Copy, PartialEq, Debug)]
942pub struct BaselineOffset(pub Option<f64>);
943
944impl BaselineOffset {
945    /// A value that indicates that the associated [`RenderBox`] does not have any baselines.
946    ///
947    /// [`BaselineOffset::NO_BASELINE`] is an identity element in most binary operations
948    /// involving two [`BaselineOffset`]s (such as [`min_of`](Self::min_of)), for render objects
949    /// with no baselines typically do not contribute to the baseline offset of their parents.
950    pub const NO_BASELINE: BaselineOffset = BaselineOffset(None);
951
952    /// The distance from the top of the box, or `None` for no baseline.
953    pub fn offset(self) -> Option<f64> {
954        self.0
955    }
956
957    /// Compares this [`BaselineOffset`] and `other`, and returns whichever is closer to the
958    /// origin.
959    ///
960    /// When both `self` and `other` are [`NO_BASELINE`](Self::NO_BASELINE), this method returns
961    /// [`NO_BASELINE`](Self::NO_BASELINE). When one of them is
962    /// [`NO_BASELINE`](Self::NO_BASELINE), this method returns the other operand.
963    pub fn min_of(self, other: BaselineOffset) -> BaselineOffset {
964        match (self.0, other.0) {
965            (Some(lhs), Some(rhs)) => {
966                if lhs >= rhs {
967                    other
968                } else {
969                    self
970                }
971            }
972            (Some(_), None) => self,
973            (None, _) => other,
974        }
975    }
976}
977
978impl Add<f64> for BaselineOffset {
979    type Output = BaselineOffset;
980
981    /// Returns a new baseline location that is `offset` pixels further away from the origin than
982    /// `self`, or unchanged if `self` is [`NO_BASELINE`](Self::NO_BASELINE).
983    fn add(self, offset: f64) -> BaselineOffset {
984        BaselineOffset(self.0.map(|value| value + offset))
985    }
986}
987
988/// Why a box cannot compute a dry layout, for
989/// [`RenderBox::debug_cannot_compute_dry_layout`].
990///
991/// Flutter's `debugCannotComputeDryLayout` takes a `reason` or an `error`, exactly one of them.
992#[derive(Clone, Copy, Debug)]
993pub enum DryLayoutFailure<'a> {
994    /// The box does not implement dry layout; the panic names the box and adds this reason.
995    Reason(&'a str),
996
997    /// The box cannot lay out with the given constraints; the panic is this error.
998    Error(&'a str),
999}
1000
1001/// Intrinsic dimension calculation that computes the intrinsic width given the max height, or
1002/// the intrinsic height given the max width.
1003#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
1004enum IntrinsicDimension {
1005    MinWidth,
1006    MaxWidth,
1007    MinHeight,
1008    MaxHeight,
1009}
1010
1011/// An `f64` used as a map key, as Dart uses a `double`.
1012#[derive(Clone, Copy, PartialEq, Eq, Hash)]
1013struct DoubleKey(u64);
1014
1015impl DoubleKey {
1016    fn new(value: f64) -> DoubleKey {
1017        DoubleKey(value.to_bits())
1018    }
1019}
1020
1021/// Flutter's `_LayoutCacheStorage`: what one [`RenderBox`] has memoized.
1022///
1023/// The layout cache storage is typically cleared in
1024/// [`RenderBox::mark_needs_layout`], but is usually kept across
1025/// [`AnyRenderBox::layout`] calls because the incoming [`BoxConstraints`] is always an input of
1026/// every layout computation.
1027#[derive(Default)]
1028struct LayoutCacheStorage {
1029    cached_intrinsic_dimensions: HashMap<(IntrinsicDimension, DoubleKey), f64>,
1030    cached_dry_layout_sizes: HashMap<BoxConstraints, Size>,
1031    cached_alphabetic_baseline: HashMap<BoxConstraints, BaselineOffset>,
1032    cached_ideo_baseline: HashMap<BoxConstraints, BaselineOffset>,
1033}
1034
1035impl LayoutCacheStorage {
1036    /// Empties the storage, and returns whether it had anything cached.
1037    fn clear(&mut self) -> bool {
1038        let has_cache = !self.cached_dry_layout_sizes.is_empty()
1039            || !self.cached_intrinsic_dimensions.is_empty()
1040            || !self.cached_alphabetic_baseline.is_empty()
1041            || !self.cached_ideo_baseline.is_empty();
1042        if has_cache {
1043            self.cached_dry_layout_sizes.clear();
1044            self.cached_intrinsic_dimensions.clear();
1045            self.cached_alphabetic_baseline.clear();
1046            self.cached_ideo_baseline.clear();
1047        }
1048        has_cache
1049    }
1050
1051    fn baselines(
1052        &mut self,
1053        baseline: TextBaseline,
1054    ) -> &mut HashMap<BoxConstraints, BaselineOffset> {
1055        match baseline {
1056            TextBaseline::Alphabetic => &mut self.cached_alphabetic_baseline,
1057            TextBaseline::Ideographic => &mut self.cached_ideo_baseline,
1058        }
1059    }
1060}
1061
1062/// Flutter's `RenderBox` fields.
1063pub struct RenderBoxData {
1064    pub(crate) size: Option<Size>,
1065    pub(crate) constraints: Option<BoxConstraints>,
1066    layout_cache: LayoutCacheStorage,
1067    debug_computing_this_dry_layout: bool,
1068    debug_computing_this_dry_baseline: bool,
1069}
1070
1071impl RenderBoxData {
1072    /// Unlaid-out box state.
1073    pub fn new() -> RenderBoxData {
1074        RenderBoxData {
1075            size: None,
1076            constraints: None,
1077            layout_cache: LayoutCacheStorage::default(),
1078            debug_computing_this_dry_layout: false,
1079            debug_computing_this_dry_baseline: false,
1080        }
1081    }
1082}
1083
1084impl Default for RenderBoxData {
1085    fn default() -> RenderBoxData {
1086        RenderBoxData::new()
1087    }
1088}
1089
1090/// Implements [`RenderBox`] field accessors for a `render_box` field.
1091#[macro_export]
1092macro_rules! render_box_accessors {
1093    () => {
1094        fn render_box_data(
1095            self: $crate::RenderHandle<Self>,
1096            app: &::inset_foundation::App,
1097        ) -> &$crate::RenderBoxData {
1098            &self.get(app).render_box
1099        }
1100        fn render_box_data_mut(
1101            self: $crate::RenderHandle<Self>,
1102            app: &mut ::inset_foundation::App,
1103        ) -> &mut $crate::RenderBoxData {
1104            &mut self.get_mut(app).render_box
1105        }
1106    };
1107}
1108
1109/// Dart's `child.parentData! as BoxParentData`.
1110///
1111/// # Panics
1112///
1113/// If the child has no parent data, or its parent data is not a box's.
1114fn box_parent_data_of(app: &App, child: AnyRenderObject) -> &BoxParentData {
1115    child
1116        .parent_data(app)
1117        .and_then(|parent_data| parent_data.part::<BoxParentData>())
1118        .expect("parent data is not a BoxParentData")
1119}
1120
1121/// A mixin that provides useful default behaviors for boxes with children managed by the
1122/// [`ContainerRenderObjectMixin`] mixin.
1123///
1124/// By convention, this trait doesn't override any members of the supertrait. Instead, it
1125/// provides helpful functions that render objects can call as appropriate.
1126///
1127/// Flutter's `RenderBoxContainerDefaultsMixin`.
1128pub trait RenderBoxContainerDefaultsMixin:
1129    ContainerRenderObjectMixin<ChildType = AnyRenderBox, ParentDataType: ContainerBoxParentData>
1130    + RenderBox
1131{
1132    /// Returns the baseline of the first child with a baseline.
1133    ///
1134    /// Useful when the children are displayed vertically in the same order they appear in the
1135    /// child list.
1136    fn default_compute_distance_to_first_actual_baseline(
1137        self: RenderHandle<Self>,
1138        app: &mut App,
1139        baseline: TextBaseline,
1140    ) -> Option<f64> {
1141        debug_assert!(!self.as_object().debug_needs_layout(app));
1142        let mut child = self.first_child(app);
1143        while let Some(current) = child {
1144            let child_parent_data = current
1145                .as_object()
1146                .parent_data_of::<Self::ParentDataType>(app);
1147            let (offset, next_sibling) =
1148                (child_parent_data.offset(), child_parent_data.next_sibling());
1149            if let Some(result) = current.get_distance_to_actual_baseline(app, baseline) {
1150                return Some(result + offset.dy());
1151            }
1152            child = next_sibling;
1153        }
1154        None
1155    }
1156
1157    /// Returns the minimum baseline value among every child.
1158    ///
1159    /// Useful when the vertical position of the children isn't determined by the order in the
1160    /// child list.
1161    fn default_compute_distance_to_highest_actual_baseline(
1162        self: RenderHandle<Self>,
1163        app: &mut App,
1164        baseline: TextBaseline,
1165    ) -> Option<f64> {
1166        debug_assert!(!self.as_object().debug_needs_layout(app));
1167        let mut min_baseline = BaselineOffset::NO_BASELINE;
1168        let mut child = self.first_child(app);
1169        while let Some(current) = child {
1170            let child_parent_data = current
1171                .as_object()
1172                .parent_data_of::<Self::ParentDataType>(app);
1173            let (offset, next_sibling) =
1174                (child_parent_data.offset(), child_parent_data.next_sibling());
1175            let candidate = BaselineOffset(current.get_distance_to_actual_baseline(app, baseline))
1176                + offset.dy();
1177            min_baseline = min_baseline.min_of(candidate);
1178            child = next_sibling;
1179        }
1180        min_baseline.offset()
1181    }
1182
1183    /// Performs a hit test on each child by walking the child list backwards.
1184    ///
1185    /// Stops walking once after the first child reports that it contains the given point.
1186    /// Returns whether any children contain the given point.
1187    ///
1188    /// See also:
1189    ///
1190    ///  * [`default_paint`](Self::default_paint), which paints the children appropriate for
1191    ///    this hit-testing strategy.
1192    fn default_hit_test_children(
1193        self: RenderHandle<Self>,
1194        app: &mut App,
1195        result: &mut BoxHitTestResult<'_>,
1196        position: Offset,
1197    ) -> bool {
1198        let mut child = self.last_child(app);
1199        while let Some(current) = child {
1200            // The x, y parameters have the top left of the node's box as the origin.
1201            let child_parent_data = current
1202                .as_object()
1203                .parent_data_of::<Self::ParentDataType>(app);
1204            let (offset, previous_sibling) = (
1205                child_parent_data.offset(),
1206                child_parent_data.previous_sibling(),
1207            );
1208            let is_hit =
1209                result.add_with_paint_offset(Some(offset), position, |result, transformed| {
1210                    debug_assert_eq!(transformed, position - offset);
1211                    current.hit_test(app, result, transformed)
1212                });
1213            if is_hit {
1214                return true;
1215            }
1216            child = previous_sibling;
1217        }
1218        false
1219    }
1220
1221    /// Paints each child by walking the child list forwards.
1222    ///
1223    /// See also:
1224    ///
1225    ///  * [`default_hit_test_children`](Self::default_hit_test_children), which implements
1226    ///    hit-testing of the children in a manner appropriate for this painting strategy.
1227    fn default_paint(
1228        self: RenderHandle<Self>,
1229        app: &mut App,
1230        context: &mut PaintingContext,
1231        offset: Offset,
1232    ) {
1233        let mut child = self.first_child(app);
1234        while let Some(current) = child {
1235            let child_parent_data = current
1236                .as_object()
1237                .parent_data_of::<Self::ParentDataType>(app);
1238            let (child_offset, next_sibling) =
1239                (child_parent_data.offset(), child_parent_data.next_sibling());
1240            context.paint_child(app, current.as_object(), child_offset + offset);
1241            child = next_sibling;
1242        }
1243    }
1244}
1245
1246/// A render object in a 2D Cartesian coordinate system.
1247///
1248/// Flutter's counterpart is `RenderBox`. [`RenderObject::perform_layout`] is the leaf override;
1249/// [`AnyRenderBox::layout`] is the framework wrapper. `RenderObject`'s tree operations are
1250/// provided here and forward to [`as_object`](Self::as_object).
1251pub trait RenderBox: RenderObject {
1252    /// Mixin field access.
1253    fn render_box_data(self: RenderHandle<Self>, app: &App) -> &RenderBoxData;
1254
1255    /// See [`render_box_data`](Self::render_box_data).
1256    fn render_box_data_mut(self: RenderHandle<Self>, app: &mut App) -> &mut RenderBoxData;
1257
1258    /// The box constraints most recently supplied by the parent.
1259    ///
1260    /// # Panics
1261    ///
1262    /// If layout has not yet happened.
1263    fn constraints(self: RenderHandle<Self>, app: &App) -> BoxConstraints {
1264        self.render_box_data(app).constraints.unwrap_or_else(|| {
1265            panic!("A RenderObject does not have any constraints before it has been laid out.")
1266        })
1267    }
1268
1269    /// Installs [`BoxParentData`] unless the child already has it.
1270    ///
1271    /// The box default for [`RenderObject::setup_parent_data`]; override it here.
1272    fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
1273        if !child.parent_data_is::<BoxParentData>(app) {
1274            child.set_parent_data(app, BoxParentData::new());
1275        }
1276    }
1277
1278    /// See [`AnyRenderBox::get_min_intrinsic_width`].
1279    fn get_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1280        self.as_box().get_min_intrinsic_width(app, height)
1281    }
1282
1283    /// Computes the value returned by [`get_min_intrinsic_width`](Self::get_min_intrinsic_width).
1284    /// Do not call this function directly, instead, call
1285    /// [`get_min_intrinsic_width`](Self::get_min_intrinsic_width).
1286    ///
1287    /// Override in subclasses that implement [`RenderObject::perform_layout`]. This method
1288    /// should return the minimum width that this box could be without failing to correctly
1289    /// paint its contents within itself, without clipping.
1290    ///
1291    /// If the layout algorithm is independent of the context (e.g. it always tries to be a
1292    /// particular size), or if the layout algorithm is width-in-height-out, or if the layout
1293    /// algorithm uses both the incoming width and height constraints (e.g. it always sizes
1294    /// itself to [`BoxConstraints::biggest`]), then the `height` argument should be ignored.
1295    ///
1296    /// If the layout algorithm is strictly height-in-width-out, or is height-in-width-out when
1297    /// the width is unconstrained, then the height argument is the height to use.
1298    ///
1299    /// The `height` argument will never be negative. It may be infinite.
1300    ///
1301    /// If this algorithm depends on the intrinsic dimensions of a child, the intrinsic
1302    /// dimensions of that child should be obtained using the functions whose names start with
1303    /// `get_`, not `compute_`.
1304    ///
1305    /// This function should never return a negative or infinite value.
1306    ///
1307    /// # When the intrinsic dimensions cannot be known
1308    ///
1309    /// There are cases where render objects do not have an efficient way to compute their
1310    /// intrinsic dimensions. For example, it may be prohibitively expensive to reify and
1311    /// measure every child of a lazy viewport, or the dimensions may be computed by a callback
1312    /// about which the render object cannot reason.
1313    ///
1314    /// In such cases the intrinsic functions should panic when
1315    /// [`debug_checking_intrinsics`](crate::debug_checking_intrinsics) is false and debug
1316    /// assertions are enabled, and return 0.0 otherwise. See
1317    /// [`RenderViewportBase::debug_throw_if_not_checking_intrinsics`](crate::RenderViewportBase::debug_throw_if_not_checking_intrinsics).
1318    ///
1319    /// # Aspect-ratio-driven boxes
1320    ///
1321    /// Some boxes always return a fixed size based on the constraints. For these boxes, the
1322    /// intrinsic functions should return the appropriate size when the incoming `height` or
1323    /// `width` argument is finite, treating that as a tight constraint in the respective
1324    /// direction and treating the other direction's constraints as unbounded. When the incoming
1325    /// argument is not finite, then they should return the actual intrinsic dimensions based on
1326    /// the contents, as any other box would.
1327    fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1328        let _ = (self, app, height);
1329        0.0
1330    }
1331
1332    /// See [`AnyRenderBox::get_max_intrinsic_width`].
1333    fn get_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1334        self.as_box().get_max_intrinsic_width(app, height)
1335    }
1336
1337    /// Computes the value returned by [`get_max_intrinsic_width`](Self::get_max_intrinsic_width).
1338    /// Do not call this function directly, instead, call
1339    /// [`get_max_intrinsic_width`](Self::get_max_intrinsic_width).
1340    ///
1341    /// Override in subclasses that implement [`RenderObject::perform_layout`]. This should
1342    /// return the smallest width beyond which increasing the width never decreases the preferred
1343    /// height. The preferred height is the value that would be returned by
1344    /// [`compute_min_intrinsic_height`](Self::compute_min_intrinsic_height) for that width.
1345    ///
1346    /// If the layout algorithm is strictly height-in-width-out, or is height-in-width-out when
1347    /// the width is unconstrained, then this should return the same value as
1348    /// [`compute_min_intrinsic_width`](Self::compute_min_intrinsic_width) for the same height.
1349    ///
1350    /// Otherwise, the height argument should be ignored, and the returned value should be equal
1351    /// to or bigger than the value returned by
1352    /// [`compute_min_intrinsic_width`](Self::compute_min_intrinsic_width).
1353    ///
1354    /// The value returned by this method might not match the size that the object would actually
1355    /// take. For example, a box that always exactly sizes itself using
1356    /// [`BoxConstraints::biggest`] might well size itself bigger than its max intrinsic size.
1357    fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1358        let _ = (self, app, height);
1359        0.0
1360    }
1361
1362    /// See [`AnyRenderBox::get_min_intrinsic_height`].
1363    fn get_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1364        self.as_box().get_min_intrinsic_height(app, width)
1365    }
1366
1367    /// Computes the value returned by
1368    /// [`get_min_intrinsic_height`](Self::get_min_intrinsic_height). Do not call this function
1369    /// directly, instead, call [`get_min_intrinsic_height`](Self::get_min_intrinsic_height).
1370    ///
1371    /// Override in subclasses that implement [`RenderObject::perform_layout`]. Should return the
1372    /// minimum height that this box could be without failing to correctly paint its contents
1373    /// within itself, without clipping.
1374    ///
1375    /// If the layout algorithm is independent of the context, or if the layout algorithm is
1376    /// height-in-width-out, or if the layout algorithm uses both the incoming height and width
1377    /// constraints, then the `width` argument should be ignored.
1378    ///
1379    /// If the layout algorithm is strictly width-in-height-out, or is width-in-height-out when
1380    /// the height is unconstrained, then the width argument is the width to use.
1381    ///
1382    /// The `width` argument will never be negative. It may be infinite.
1383    fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1384        let _ = (self, app, width);
1385        0.0
1386    }
1387
1388    /// See [`AnyRenderBox::get_max_intrinsic_height`].
1389    fn get_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1390        self.as_box().get_max_intrinsic_height(app, width)
1391    }
1392
1393    /// Computes the value returned by
1394    /// [`get_max_intrinsic_height`](Self::get_max_intrinsic_height). Do not call this function
1395    /// directly, instead, call [`get_max_intrinsic_height`](Self::get_max_intrinsic_height).
1396    ///
1397    /// Override in subclasses that implement [`RenderObject::perform_layout`]. Should return the
1398    /// smallest height beyond which increasing the height never decreases the preferred width.
1399    /// The preferred width is the value that would be returned by
1400    /// [`compute_min_intrinsic_width`](Self::compute_min_intrinsic_width) for that height.
1401    ///
1402    /// If the layout algorithm is strictly width-in-height-out, or is width-in-height-out when
1403    /// the height is unconstrained, then this should return the same value as
1404    /// [`compute_min_intrinsic_height`](Self::compute_min_intrinsic_height) for the same width.
1405    ///
1406    /// Otherwise, the width argument should be ignored, and the returned value should be equal
1407    /// to or bigger than the value returned by
1408    /// [`compute_min_intrinsic_height`](Self::compute_min_intrinsic_height).
1409    fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1410        let _ = (self, app, width);
1411        0.0
1412    }
1413
1414    /// See [`AnyRenderBox::get_dry_layout`].
1415    fn get_dry_layout(
1416        self: RenderHandle<Self>,
1417        app: &mut App,
1418        constraints: BoxConstraints,
1419    ) -> Size {
1420        self.as_box().get_dry_layout(app, constraints)
1421    }
1422
1423    /// Computes the value returned by [`get_dry_layout`](Self::get_dry_layout). Do not call this
1424    /// function directly, instead, call [`get_dry_layout`](Self::get_dry_layout).
1425    ///
1426    /// Override in subclasses that implement [`RenderObject::perform_layout`] or
1427    /// [`perform_resize`](Self::perform_resize), or when setting
1428    /// [`RenderObject::sized_by_parent`] to true without overriding
1429    /// [`perform_resize`](Self::perform_resize). This method should return the [`Size`] that
1430    /// this box would like to be given the provided [`BoxConstraints`].
1431    ///
1432    /// The size returned by this method must match the [`size`](Self::size) that the box will
1433    /// compute for itself in [`RenderObject::perform_layout`] (or
1434    /// [`perform_resize`](Self::perform_resize), if
1435    /// [`RenderObject::sized_by_parent`] is true).
1436    ///
1437    /// If this algorithm depends on the size of a child, the size of that child should be
1438    /// obtained using its [`get_dry_layout`](Self::get_dry_layout) method.
1439    ///
1440    /// # When the size cannot be known
1441    ///
1442    /// There are cases where render objects do not have an efficient way to compute their size.
1443    /// For example, the size may be computed by a callback about which the render object cannot
1444    /// reason. In such cases, the function should call
1445    /// [`debug_cannot_compute_dry_layout`](Self::debug_cannot_compute_dry_layout) and return
1446    /// [`Size::ZERO`].
1447    fn compute_dry_layout(
1448        self: RenderHandle<Self>,
1449        app: &mut App,
1450        constraints: BoxConstraints,
1451    ) -> Size {
1452        let _ = (app, constraints);
1453        self.debug_cannot_compute_dry_layout(DryLayoutFailure::Reason(
1454            "It does not implement RenderBox::compute_dry_layout.",
1455        ));
1456        Size::ZERO
1457    }
1458
1459    /// See [`AnyRenderBox::get_dry_baseline`].
1460    fn get_dry_baseline(
1461        self: RenderHandle<Self>,
1462        app: &mut App,
1463        constraints: BoxConstraints,
1464        baseline: TextBaseline,
1465    ) -> Option<f64> {
1466        self.as_box().get_dry_baseline(app, constraints, baseline)
1467    }
1468
1469    /// Computes the value returned by [`get_dry_baseline`](Self::get_dry_baseline).
1470    ///
1471    /// This method is for overriding only and shouldn't be called directly. To get this box's
1472    /// speculative baseline location for the given `constraints`, call
1473    /// [`get_dry_baseline`](Self::get_dry_baseline) instead.
1474    ///
1475    /// The "dry" in the method name means the implementation must not produce observable side
1476    /// effects when called. For example, it must not change the [`size`](Self::size) of the box,
1477    /// or its children's paint offsets. Moreover, accessing the current layout of this box or a
1478    /// child box usually indicates a bug in the implementation, as the current layout is
1479    /// typically calculated using a set of [`BoxConstraints`] that's different from the
1480    /// `constraints` given as the first parameter. To get the size of this box or a child box in
1481    /// this method's implementation, use [`get_dry_layout`](Self::get_dry_layout) instead.
1482    ///
1483    /// The implementation must return a value that represents the distance from the top of the
1484    /// box to the first baseline of the box's contents, for the given `constraints`, or `None`
1485    /// if the box has no baselines. It's the same exact value
1486    /// [`compute_distance_to_actual_baseline`](Self::compute_distance_to_actual_baseline) would
1487    /// return, when this box was laid out at `constraints` in the same exact state.
1488    ///
1489    /// Not all boxes support dry baseline computation. In such cases the box must call
1490    /// [`debug_cannot_compute_dry_layout`](Self::debug_cannot_compute_dry_layout) and return a
1491    /// dummy baseline offset value (such as `None`).
1492    fn compute_dry_baseline(
1493        self: RenderHandle<Self>,
1494        app: &mut App,
1495        constraints: BoxConstraints,
1496        baseline: TextBaseline,
1497    ) -> Option<f64> {
1498        let _ = (app, constraints, baseline);
1499        self.debug_cannot_compute_dry_layout(DryLayoutFailure::Reason(
1500            "It does not implement RenderBox::compute_dry_baseline.",
1501        ));
1502        None
1503    }
1504
1505    /// Called from [`compute_dry_layout`](Self::compute_dry_layout) or
1506    /// [`compute_dry_baseline`](Self::compute_dry_baseline) if this box does not support
1507    /// calculating a dry layout.
1508    ///
1509    /// When debug assertions are enabled and
1510    /// [`debug_checking_intrinsics`](crate::debug_checking_intrinsics) is not true, this method
1511    /// panics with the given [`DryLayoutFailure`].
1512    fn debug_cannot_compute_dry_layout(self: RenderHandle<Self>, failure: DryLayoutFailure<'_>) {
1513        let _ = self;
1514        if !cfg!(debug_assertions) || debug_checking_intrinsics() {
1515            return;
1516        }
1517        match failure {
1518            DryLayoutFailure::Reason(reason) => panic!(
1519                "The {} class does not support dry layout. {reason}",
1520                std::any::type_name::<Self>()
1521            ),
1522            DryLayoutFailure::Error(error) => panic!("{error}"),
1523        }
1524    }
1525
1526    /// See [`AnyRenderBox::get_distance_to_baseline`].
1527    fn get_distance_to_baseline(
1528        self: RenderHandle<Self>,
1529        app: &mut App,
1530        baseline: TextBaseline,
1531        only_real: bool,
1532    ) -> Option<f64> {
1533        self.as_box()
1534            .get_distance_to_baseline(app, baseline, only_real)
1535    }
1536
1537    /// See [`AnyRenderBox::get_distance_to_actual_baseline`].
1538    fn get_distance_to_actual_baseline(
1539        self: RenderHandle<Self>,
1540        app: &mut App,
1541        baseline: TextBaseline,
1542    ) -> Option<f64> {
1543        self.as_box().get_distance_to_actual_baseline(app, baseline)
1544    }
1545
1546    /// Returns the distance from the y-coordinate of the position of the box to the y-coordinate
1547    /// of the first given baseline in the box's contents, if any, or `None` otherwise.
1548    ///
1549    /// Do not call this function directly. If you need to know the baseline of a child from an
1550    /// invocation of [`RenderObject::perform_layout`] or [`RenderObject::paint`], call
1551    /// [`get_distance_to_baseline`](Self::get_distance_to_baseline).
1552    ///
1553    /// Subclasses should override this method to supply the distances to their baselines. When
1554    /// implementing this method, there are generally three strategies:
1555    ///
1556    ///  * For classes that use the [`ContainerRenderObjectMixin`] child model, consider
1557    ///    implementing [`RenderBoxContainerDefaultsMixin`] and using
1558    ///    [`RenderBoxContainerDefaultsMixin::default_compute_distance_to_first_actual_baseline`].
1559    ///
1560    ///  * For classes that define a particular baseline themselves, return that value directly.
1561    ///
1562    ///  * For classes that have a child to which they wish to defer the computation, call
1563    ///    [`get_distance_to_actual_baseline`](Self::get_distance_to_actual_baseline) on the
1564    ///    child.
1565    fn compute_distance_to_actual_baseline(
1566        self: RenderHandle<Self>,
1567        app: &mut App,
1568        baseline: TextBaseline,
1569    ) -> Option<f64> {
1570        let _ = (self, app, baseline);
1571        debug_assert!(
1572            DEBUG_DOING_BASELINE.get(),
1573            "Please see the documentation for compute_distance_to_actual_baseline for the \
1574             required calling conventions of this method."
1575        );
1576        None
1577    }
1578
1579    /// Mark this render object's layout information as dirty.
1580    ///
1581    /// Flutter's `RenderBox.markNeedsLayout`: it also clears the intrinsics, dry layout and
1582    /// baseline caches, and defers to the parent when they held anything.
1583    fn mark_needs_layout(self: RenderHandle<Self>, app: &mut App) {
1584        // If the cache was not empty, then this box's layout is used by the parent's layout
1585        // algorithm (it's possible that the parent only used the intrinsics for paint, but
1586        // there's no good way to detect that so we conservatively assume it's a layout
1587        // dependency).
1588        //
1589        // A render object's perform_layout implementation may depend on the baseline location or
1590        // the intrinsic dimensions of a descendant, even when there are relayout boundaries
1591        // between them.
1592        //
1593        // Some calculations may fail (dry baseline, for example). The layout dependency is still
1594        // established, but only from the box that failed to compute the dry baseline to the
1595        // ancestor that queried the dry baseline.
1596        if self.render_box_data_mut(app).layout_cache.clear() && self.parent(app).is_some() {
1597            self.as_object().mark_parent_needs_layout(app);
1598            return;
1599        }
1600        crate::object::RenderObjectBase::mark_needs_layout(self, app);
1601    }
1602
1603    /// Updates the box's size using only the constraints.
1604    ///
1605    /// By default this method sets [`size`](Self::size) to the result of
1606    /// [`compute_dry_layout`](Self::compute_dry_layout) called with the current
1607    /// [`constraints`](Self::constraints). Instead of overriding this method, consider
1608    /// overriding [`compute_dry_layout`](Self::compute_dry_layout).
1609    fn perform_resize(self: RenderHandle<Self>, app: &mut App) {
1610        RenderBoxBase::perform_resize(self, app);
1611    }
1612
1613    /// The size of this box.
1614    fn size(self: RenderHandle<Self>, app: &App) -> Size {
1615        self.as_box().size(app)
1616    }
1617
1618    /// Multiply the transform from the parent's coordinate system to this box's coordinate
1619    /// system into the given transform.
1620    ///
1621    /// This function is used to convert coordinate systems between boxes. Subclasses that
1622    /// apply transforms during painting should override this function to factor those
1623    /// transforms into the calculation.
1624    ///
1625    /// The [`RenderBox`] implementation takes care of adjusting the matrix for the position
1626    /// of the given child as determined during layout and stored on the child's parent data.
1627    fn apply_paint_transform(
1628        self: RenderHandle<Self>,
1629        app: &App,
1630        child: AnyRenderObject,
1631        transform: &mut Matrix4,
1632    ) {
1633        debug_assert!(child.parent(app).map(AnyRenderObject::id) == Some(self.id()));
1634        // Dart asserts the child's parent data is a `BoxParentData` with a message naming
1635        // this type; `box_parent_data` panics the same way.
1636        let offset = box_parent_data_of(app, child).offset;
1637        crate::object::translate(transform, offset.dx(), offset.dy());
1638    }
1639
1640    /// An estimate of the bounds within which this render object will paint: the box's own size.
1641    fn paint_bounds(self: RenderHandle<Self>, app: &App) -> Rect {
1642        Offset::ZERO & self.size(app)
1643    }
1644
1645    /// Whether this render object has undergone layout and has a [`size`](Self::size).
1646    fn has_size(self: RenderHandle<Self>, app: &App) -> bool {
1647        self.render_box_data(app).size.is_some()
1648    }
1649
1650    /// Override this method to handle pointer events that hit this render object.
1651    ///
1652    /// For [`RenderBox`] objects, the `entry` argument is a [`BoxHitTestEntry`]. From this
1653    /// object you can determine the position of the hit test in the local coordinates of the
1654    /// render object (via `entry.local_position()`).
1655    fn handle_event(
1656        self: RenderHandle<Self>,
1657        app: &mut App,
1658        event: &PointerEvent,
1659        entry: &BoxHitTestEntry,
1660    ) {
1661        let _ = (self, app, event, entry);
1662    }
1663
1664    /// Determines the set of render objects located at the given position.
1665    ///
1666    /// Returns true, and adds any render objects that contain the point to the given hit test
1667    /// result, if this render object or one of its descendants absorbs the hit (preventing
1668    /// objects below this one from being hit). Returns false if the hit can continue to other
1669    /// objects below this one.
1670    ///
1671    /// The caller is responsible for transforming `position` from global coordinates to its
1672    /// location relative to the origin of this [`RenderBox`]. This [`RenderBox`] is responsible
1673    /// for checking whether the given position is within its bounds.
1674    ///
1675    /// # Panics
1676    ///
1677    /// In debug builds, if this box has not been laid out.
1678    fn hit_test(
1679        self: RenderHandle<Self>,
1680        app: &mut App,
1681        result: &mut BoxHitTestResult<'_>,
1682        position: Offset,
1683    ) -> bool {
1684        RenderBoxBase::hit_test(self, app, result, position)
1685    }
1686
1687    /// Override this method if this render object can be hit even if its children were not hit.
1688    ///
1689    /// Returns true if the specified `position` should be considered a hit on this render
1690    /// object.
1691    fn hit_test_self(self: RenderHandle<Self>, _app: &App, _position: Offset) -> bool {
1692        let _ = self;
1693        false
1694    }
1695
1696    /// Override this method to check whether any children are located at the given position.
1697    ///
1698    /// Subclasses should return true if at least one child reported a hit at the specified
1699    /// position.
1700    fn hit_test_children(
1701        self: RenderHandle<Self>,
1702        app: &mut App,
1703        result: &mut BoxHitTestResult<'_>,
1704        position: Offset,
1705    ) -> bool {
1706        let _ = (self, app, result, position);
1707        false
1708    }
1709
1710    /// Sets the size of this box. Call from [`RenderObject::perform_layout`] or
1711    /// [`RenderObject::perform_resize`].
1712    fn set_size(self: RenderHandle<Self>, app: &mut App, size: Size) {
1713        self.as_box().set_size(app, size)
1714    }
1715
1716    /// See [`AnyRenderBox::layout`].
1717    fn layout(
1718        self: RenderHandle<Self>,
1719        app: &mut App,
1720        constraints: BoxConstraints,
1721        parent_uses_size: bool,
1722    ) {
1723        self.as_box().layout(app, constraints, parent_uses_size)
1724    }
1725
1726    /// The type-erased `RenderBox` handle. Free: the vtable is a `const`, and the id is copied.
1727    fn as_box(self: RenderHandle<Self>) -> AnyRenderBox {
1728        AnyRenderBox {
1729            id: self.id(),
1730            vtable: const { &RenderBoxVTable::of::<Self>() },
1731        }
1732    }
1733
1734    /// The type-erased `RenderObject` handle, through [`as_box`](Self::as_box).
1735    fn as_object(self: RenderHandle<Self>) -> AnyRenderObject {
1736        self.as_box().as_object()
1737    }
1738
1739    /// See [`AnyRenderObject::adopt_child`].
1740    fn adopt_child(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
1741        self.as_object().adopt_child(app, child)
1742    }
1743
1744    /// See [`AnyRenderObject::drop_child`].
1745    fn drop_child(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
1746        self.as_object().drop_child(app, child)
1747    }
1748
1749    /// See [`AnyRenderObject::mark_needs_paint`].
1750    fn mark_needs_paint(self: RenderHandle<Self>, app: &mut App) {
1751        self.as_object().mark_needs_paint(app)
1752    }
1753
1754    /// See [`AnyRenderObject::mark_needs_composited_layer_update`].
1755    fn mark_needs_composited_layer_update(self: RenderHandle<Self>, app: &mut App) {
1756        self.as_object().mark_needs_composited_layer_update(app)
1757    }
1758
1759    /// See [`AnyRenderObject::mark_needs_compositing_bits_update`].
1760    fn mark_needs_compositing_bits_update(self: RenderHandle<Self>, app: &mut App) {
1761        self.as_object().mark_needs_compositing_bits_update(app)
1762    }
1763
1764    /// See [`AnyRenderObject::schedule_initial_layout`].
1765    fn schedule_initial_layout(self: RenderHandle<Self>, app: &mut App) {
1766        self.as_object().schedule_initial_layout(app)
1767    }
1768
1769    /// See [`AnyRenderObject::parent`].
1770    fn parent(self: RenderHandle<Self>, app: &App) -> Option<AnyRenderObject> {
1771        self.as_object().parent(app)
1772    }
1773
1774    /// See [`AnyRenderObject::owner`].
1775    fn owner(self: RenderHandle<Self>, app: &App) -> Option<Handle<PipelineOwner>> {
1776        self.as_object().owner(app)
1777    }
1778
1779    /// See [`AnyRenderObject::attached`].
1780    fn attached(self: RenderHandle<Self>, app: &App) -> bool {
1781        self.as_object().attached(app)
1782    }
1783
1784    /// See [`AnyRenderObject::debug_needs_layout`].
1785    fn debug_needs_layout(self: RenderHandle<Self>, app: &App) -> bool {
1786        self.as_object().debug_needs_layout(app)
1787    }
1788}
1789
1790/// Flutter's `RenderBox` bodies that an override calls through `super`.
1791///
1792/// A trait default cannot call `super`, and a leaf's [`RenderBox::hit_test`] shadows the default
1793/// it would call; this sibling trait, blanket-implemented for every box, carries that body (the
1794/// shape `ElementBase` has for `Element`).
1795pub(crate) trait RenderBoxBase: RenderBox {
1796    /// Flutter's `RenderBox.performResize`.
1797    fn perform_resize(self: RenderHandle<Self>, app: &mut App) {
1798        // Default behavior for subclasses that have sized_by_parent = true.
1799        let constraints = RenderBox::constraints(self, app);
1800        let size = RenderBox::compute_dry_layout(self, app, constraints);
1801        debug_assert!(size.width().is_finite() && size.height().is_finite());
1802        self.set_size(app, size);
1803    }
1804
1805    /// Flutter's `RenderBox.hitTest`.
1806    fn hit_test(
1807        self: RenderHandle<Self>,
1808        app: &mut App,
1809        result: &mut BoxHitTestResult<'_>,
1810        position: Offset,
1811    ) -> bool {
1812        debug_assert!(
1813            self.has_size(app),
1814            "Cannot hit test a render box that has never been laid out: {self:?}"
1815        );
1816        if self.size(app).contains(position)
1817            && (RenderBox::hit_test_children(self, app, result, position)
1818                || RenderBox::hit_test_self(self, app, position))
1819        {
1820            result.add(BoxHitTestEntry::new(self.as_box(), position).into());
1821            return true;
1822        }
1823        false
1824    }
1825}
1826
1827impl<T: RenderBox> RenderBoxBase for T {}
1828
1829/// The vtable of an [`AnyRenderBox`]: the object vtable plus the box accessors.
1830pub(crate) struct RenderBoxVTable {
1831    pub object: RenderObjectVTable,
1832    pub box_data: fn(&App, HandleId) -> &RenderBoxData,
1833    pub box_data_mut: fn(&mut App, HandleId) -> &mut RenderBoxData,
1834    pub hit_test: fn(&mut App, HandleId, &mut BoxHitTestResult<'_>, Offset) -> bool,
1835    pub handle_event: fn(&mut App, HandleId, &PointerEvent, &BoxHitTestEntry),
1836    pub compute_min_intrinsic_width: fn(&mut App, HandleId, f64) -> f64,
1837    pub compute_max_intrinsic_width: fn(&mut App, HandleId, f64) -> f64,
1838    pub compute_min_intrinsic_height: fn(&mut App, HandleId, f64) -> f64,
1839    pub compute_max_intrinsic_height: fn(&mut App, HandleId, f64) -> f64,
1840    pub compute_dry_layout: fn(&mut App, HandleId, BoxConstraints) -> Size,
1841    pub compute_dry_baseline: fn(&mut App, HandleId, BoxConstraints, TextBaseline) -> Option<f64>,
1842    pub compute_distance_to_actual_baseline: fn(&mut App, HandleId, TextBaseline) -> Option<f64>,
1843}
1844
1845impl RenderBoxVTable {
1846    const fn of<T: RenderBox>() -> RenderBoxVTable {
1847        RenderBoxVTable {
1848            object: RenderObjectVTable::of::<T>(
1849                |app, id, child| <T as RenderBox>::setup_parent_data(resolve(id), app, child),
1850                |app, id| T::paint_bounds(resolve(id), app),
1851                |app, id, child, transform| {
1852                    <T as RenderBox>::apply_paint_transform(resolve(id), app, child, transform)
1853                },
1854                |app, id| <T as RenderBox>::perform_resize(resolve(id), app),
1855                |app, id| <T as RenderBox>::mark_needs_layout(resolve(id), app),
1856                Some(|| const { &RenderBoxVTable::of::<T>() }),
1857                None,
1858            ),
1859            box_data: |app, id| T::render_box_data(resolve(id), app),
1860            box_data_mut: |app, id| T::render_box_data_mut(resolve(id), app),
1861            hit_test: |app, id, result, position| T::hit_test(resolve(id), app, result, position),
1862            handle_event: |app, id, event, entry| T::handle_event(resolve(id), app, event, entry),
1863            compute_min_intrinsic_width: |app, id, height| {
1864                T::compute_min_intrinsic_width(resolve(id), app, height)
1865            },
1866            compute_max_intrinsic_width: |app, id, height| {
1867                T::compute_max_intrinsic_width(resolve(id), app, height)
1868            },
1869            compute_min_intrinsic_height: |app, id, width| {
1870                T::compute_min_intrinsic_height(resolve(id), app, width)
1871            },
1872            compute_max_intrinsic_height: |app, id, width| {
1873                T::compute_max_intrinsic_height(resolve(id), app, width)
1874            },
1875            compute_dry_layout: |app, id, constraints| {
1876                T::compute_dry_layout(resolve(id), app, constraints)
1877            },
1878            compute_dry_baseline: |app, id, constraints, baseline| {
1879                T::compute_dry_baseline(resolve(id), app, constraints, baseline)
1880            },
1881            compute_distance_to_actual_baseline: |app, id, baseline| {
1882                T::compute_distance_to_actual_baseline(resolve(id), app, baseline)
1883            },
1884        }
1885    }
1886}
1887
1888impl<T: RenderBox> RenderHandle<T> {
1889    /// Creates a box-protocol render object in `app`.
1890    pub fn new_box(app: &mut App, object: T) -> RenderHandle<T> {
1891        let this = create(app, object);
1892        let data = this.render_object_data_mut(app);
1893        data.object_vtable = Some(&const { RenderBoxVTable::of::<T>() }.object);
1894        // Flutter's `RenderObject()` constructor:
1895        // `_needsCompositing = isRepaintBoundary || alwaysNeedsCompositing` and
1896        // `_wasRepaintBoundary = isRepaintBoundary`.
1897        let is_repaint_boundary = this.is_repaint_boundary(app);
1898        let always_needs_compositing = this.always_needs_compositing(app);
1899        let data = this.render_object_data_mut(app);
1900        data.was_repaint_boundary = is_repaint_boundary;
1901        data.needs_compositing = is_repaint_boundary || always_needs_compositing;
1902        this
1903    }
1904}
1905
1906/// Flutter's `RenderBox._debugSetDoingBaseline(true)` and the `finally` that clears it.
1907struct DebugDoingBaseline;
1908
1909impl DebugDoingBaseline {
1910    fn enter() -> DebugDoingBaseline {
1911        DEBUG_DOING_BASELINE.set(cfg!(debug_assertions));
1912        DebugDoingBaseline
1913    }
1914}
1915
1916impl Drop for DebugDoingBaseline {
1917    fn drop(&mut self) {
1918        DEBUG_DOING_BASELINE.set(false);
1919    }
1920}
1921
1922/// Flutter's `RenderBox._computeIntrinsics` preamble: whether the result may be memoized.
1923fn should_cache_intrinsics(this: AnyRenderBox, app: &App) -> bool {
1924    // perform_resize should not depend on anything except the incoming constraints.
1925    debug_assert!(debug_checking_intrinsics() || !this.as_object().debug_doing_this_resize(app));
1926    // We don't want the debug-mode intrinsic tests to affect who gets marked dirty, etc.
1927    !cfg!(debug_assertions) || !debug_checking_intrinsics()
1928}
1929
1930/// Flutter's `_IntrinsicDimension.memoize`.
1931fn memoize_intrinsic(
1932    this: AnyRenderBox,
1933    app: &mut App,
1934    dimension: IntrinsicDimension,
1935    input: f64,
1936    computer: fn(&mut App, HandleId, f64) -> f64,
1937) -> f64 {
1938    if !should_cache_intrinsics(this, app) {
1939        return computer(app, this.id, input);
1940    }
1941    let key = (dimension, DoubleKey::new(input));
1942    if let Some(cached) = this.layout_cache(app).cached_intrinsic_dimensions.get(&key) {
1943        return *cached;
1944    }
1945    let result = computer(app, this.id, input);
1946    this.layout_cache_mut(app)
1947        .cached_intrinsic_dimensions
1948        .insert(key, result);
1949    result
1950}
1951
1952/// Flutter's `_Baseline.memoize`.
1953fn memoize_baseline(
1954    this: AnyRenderBox,
1955    app: &mut App,
1956    constraints: BoxConstraints,
1957    baseline: TextBaseline,
1958    computer: impl FnOnce(AnyRenderBox, &mut App) -> BaselineOffset,
1959) -> BaselineOffset {
1960    if !should_cache_intrinsics(this, app) {
1961        return computer(this, app);
1962    }
1963    if let Some(cached) = this
1964        .layout_cache_mut(app)
1965        .baselines(baseline)
1966        .get(&constraints)
1967    {
1968        return *cached;
1969    }
1970    let result = computer(this, app);
1971    this.layout_cache_mut(app)
1972        .baselines(baseline)
1973        .insert(constraints, result);
1974    result
1975}
1976
1977/// Erased `RenderBox`.
1978#[derive(Clone, Copy)]
1979pub struct AnyRenderBox {
1980    id: HandleId,
1981    vtable: &'static RenderBoxVTable,
1982}
1983
1984impl PartialEq for AnyRenderBox {
1985    fn eq(&self, other: &AnyRenderBox) -> bool {
1986        self.id == other.id
1987    }
1988}
1989
1990impl Eq for AnyRenderBox {}
1991
1992impl std::hash::Hash for AnyRenderBox {
1993    fn hash<H: Hasher>(&self, state: &mut H) {
1994        self.id.hash(state);
1995    }
1996}
1997
1998impl Debug for AnyRenderBox {
1999    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2000        write!(f, "AnyRenderBox({:?})", self.id)
2001    }
2002}
2003
2004impl AnyRenderBox {
2005    pub(crate) fn from_vtable(id: HandleId, vtable: &'static RenderBoxVTable) -> AnyRenderBox {
2006        AnyRenderBox { id, vtable }
2007    }
2008
2009    /// The `RenderObject` view of this box. Free: points into the nested table.
2010    /// Whether this render object has undergone layout and has a size.
2011    pub fn has_size(self, app: &App) -> bool {
2012        self.box_data(app).size.is_some()
2013    }
2014
2015    pub fn as_object(self) -> AnyRenderObject {
2016        AnyRenderObject::from_vtable(self.id, &self.vtable.object)
2017    }
2018
2019    /// See [`RenderBox::hit_test`].
2020    pub fn hit_test(
2021        self,
2022        app: &mut App,
2023        result: &mut BoxHitTestResult<'_>,
2024        position: Offset,
2025    ) -> bool {
2026        (self.vtable.hit_test)(app, self.id, result, position)
2027    }
2028
2029    /// See [`RenderBox::handle_event`].
2030    pub fn handle_event(self, app: &mut App, event: &PointerEvent, entry: &BoxHitTestEntry) {
2031        (self.vtable.handle_event)(app, self.id, event, entry)
2032    }
2033
2034    fn box_data(self, app: &App) -> &RenderBoxData {
2035        (self.vtable.box_data)(app, self.id)
2036    }
2037
2038    fn box_data_mut(self, app: &mut App) -> &mut RenderBoxData {
2039        (self.vtable.box_data_mut)(app, self.id)
2040    }
2041
2042    /// Compute the layout for this box.
2043    pub fn layout(self, app: &mut App, constraints: BoxConstraints, parent_uses_size: bool) {
2044        debug_assert!(
2045            !app.is_disposed(self.id),
2046            "layout on a disposed render object"
2047        );
2048        debug_assert!(constraints.debug_assert_is_valid(true));
2049        let same = self.box_data(app).constraints == Some(constraints);
2050        self.as_object()
2051            .run_layout(app, parent_uses_size, constraints.is_tight(), same, |app| {
2052                self.box_data_mut(app).constraints = Some(constraints)
2053            });
2054    }
2055
2056    /// The size of this box.
2057    ///
2058    /// # Panics
2059    ///
2060    /// If this box has not been laid out.
2061    pub fn size(self, app: &App) -> Size {
2062        self.box_data(app)
2063            .size
2064            .unwrap_or_else(|| panic!("RenderBox was not laid out: {self:?}"))
2065    }
2066
2067    /// Sets the size of this box.
2068    pub fn set_size(self, app: &mut App, size: Size) {
2069        self.box_data_mut(app).size = Some(size);
2070    }
2071
2072    /// The box constraints most recently supplied by the parent.
2073    ///
2074    /// # Panics
2075    ///
2076    /// If layout has not yet happened.
2077    pub fn constraints(self, app: &App) -> BoxConstraints {
2078        self.box_data(app).constraints.unwrap_or_else(|| {
2079            panic!("A RenderObject does not have any constraints before it has been laid out.")
2080        })
2081    }
2082
2083    /// Returns the minimum width that this box could be without failing to correctly paint its
2084    /// contents within itself, without clipping.
2085    ///
2086    /// The height argument may give a specific height to assume. The given height can be
2087    /// infinite, meaning that the intrinsic width in an unconstrained environment is being
2088    /// requested. The given height should never be negative.
2089    ///
2090    /// This function should only be called on one's children. Calling this function couples the
2091    /// child with the parent so that when the child's layout changes, the parent is notified
2092    /// (via [`RenderBox::mark_needs_layout`]).
2093    ///
2094    /// Calling this function is expensive as it can result in O(N^2) behavior.
2095    ///
2096    /// Do not override this method. Instead, implement
2097    /// [`RenderBox::compute_min_intrinsic_width`].
2098    pub fn get_min_intrinsic_width(self, app: &mut App, height: f64) -> f64 {
2099        debug_assert!(
2100            height >= 0.0,
2101            "The height argument to get_min_intrinsic_width was negative. If you perform \
2102             computations on another height before passing it to get_min_intrinsic_width, \
2103             consider using f64::max or clamp_double to force the value into the valid range."
2104        );
2105        memoize_intrinsic(
2106            self,
2107            app,
2108            IntrinsicDimension::MinWidth,
2109            height,
2110            self.vtable.compute_min_intrinsic_width,
2111        )
2112    }
2113
2114    /// Returns the smallest width beyond which increasing the width never decreases the
2115    /// preferred height. The preferred height is the value that would be returned by
2116    /// [`get_min_intrinsic_height`](Self::get_min_intrinsic_height) for that width.
2117    ///
2118    /// Do not override this method. Instead, implement
2119    /// [`RenderBox::compute_max_intrinsic_width`].
2120    pub fn get_max_intrinsic_width(self, app: &mut App, height: f64) -> f64 {
2121        debug_assert!(
2122            height >= 0.0,
2123            "The height argument to get_max_intrinsic_width was negative."
2124        );
2125        memoize_intrinsic(
2126            self,
2127            app,
2128            IntrinsicDimension::MaxWidth,
2129            height,
2130            self.vtable.compute_max_intrinsic_width,
2131        )
2132    }
2133
2134    /// Returns the minimum height that this box could be without failing to correctly paint its
2135    /// contents within itself, without clipping.
2136    ///
2137    /// Do not override this method. Instead, implement
2138    /// [`RenderBox::compute_min_intrinsic_height`].
2139    pub fn get_min_intrinsic_height(self, app: &mut App, width: f64) -> f64 {
2140        debug_assert!(
2141            width >= 0.0,
2142            "The width argument to get_min_intrinsic_height was negative."
2143        );
2144        memoize_intrinsic(
2145            self,
2146            app,
2147            IntrinsicDimension::MinHeight,
2148            width,
2149            self.vtable.compute_min_intrinsic_height,
2150        )
2151    }
2152
2153    /// Returns the smallest height beyond which increasing the height never decreases the
2154    /// preferred width. The preferred width is the value that would be returned by
2155    /// [`get_min_intrinsic_width`](Self::get_min_intrinsic_width) for that height.
2156    ///
2157    /// Do not override this method. Instead, implement
2158    /// [`RenderBox::compute_max_intrinsic_height`].
2159    pub fn get_max_intrinsic_height(self, app: &mut App, width: f64) -> f64 {
2160        debug_assert!(
2161            width >= 0.0,
2162            "The width argument to get_max_intrinsic_height was negative."
2163        );
2164        memoize_intrinsic(
2165            self,
2166            app,
2167            IntrinsicDimension::MaxHeight,
2168            width,
2169            self.vtable.compute_max_intrinsic_height,
2170        )
2171    }
2172
2173    /// Returns the [`Size`] that this box would like to be given the provided
2174    /// [`BoxConstraints`].
2175    ///
2176    /// The size returned by this method is guaranteed to be the same size that this box computes
2177    /// for itself during layout given the same constraints.
2178    ///
2179    /// This function should only be called on one's children. Calling this function couples the
2180    /// child with the parent so that when the child's layout changes, the parent is notified
2181    /// (via [`RenderBox::mark_needs_layout`]).
2182    ///
2183    /// This layout is called "dry" layout as opposed to the regular "wet" layout run performed
2184    /// by [`RenderObject::perform_layout`] because it computes the desired size for the given
2185    /// constraints without changing any internal state.
2186    ///
2187    /// Calling this function is expensive as it can result in O(N^2) behavior.
2188    ///
2189    /// Do not override this method. Instead, implement [`RenderBox::compute_dry_layout`].
2190    pub fn get_dry_layout(self, app: &mut App, constraints: BoxConstraints) -> Size {
2191        if !should_cache_intrinsics(self, app) {
2192            return self.compute_dry_layout(app, constraints);
2193        }
2194        if let Some(cached) = self
2195            .layout_cache(app)
2196            .cached_dry_layout_sizes
2197            .get(&constraints)
2198        {
2199            return *cached;
2200        }
2201        let result = self.compute_dry_layout(app, constraints);
2202        self.layout_cache_mut(app)
2203            .cached_dry_layout_sizes
2204            .insert(constraints, result);
2205        result
2206    }
2207
2208    /// Flutter's `RenderBox._computeDryLayout`: the re-entrancy guard around the virtual.
2209    fn compute_dry_layout(self, app: &mut App, constraints: BoxConstraints) -> Size {
2210        if cfg!(debug_assertions) {
2211            assert!(!self.box_data(app).debug_computing_this_dry_layout);
2212            self.box_data_mut(app).debug_computing_this_dry_layout = true;
2213        }
2214        let result = (self.vtable.compute_dry_layout)(app, self.id, constraints);
2215        if cfg!(debug_assertions) {
2216            self.box_data_mut(app).debug_computing_this_dry_layout = false;
2217        }
2218        result
2219    }
2220
2221    /// Returns the distance from the top of the box to the first baseline of the box's contents
2222    /// for the given `constraints`, or `None` if this box does not have any baselines.
2223    ///
2224    /// This method calls [`RenderBox::compute_dry_baseline`] under the hood and caches the
2225    /// result. Boxes typically don't override
2226    /// [`get_dry_baseline`](Self::get_dry_baseline). Instead, consider overriding
2227    /// [`RenderBox::compute_dry_baseline`] such that it returns a baseline location that is
2228    /// consistent with [`get_distance_to_actual_baseline`](Self::get_distance_to_actual_baseline).
2229    ///
2230    /// This method is usually called by the [`RenderBox::compute_dry_baseline`] or the
2231    /// [`RenderBox::compute_dry_layout`] implementation of a parent box to get the baseline
2232    /// location of a box child. Unlike
2233    /// [`get_distance_to_baseline`](Self::get_distance_to_baseline), this method takes a
2234    /// [`BoxConstraints`] as an argument and computes the baseline location as if the box was
2235    /// laid out by the parent using that [`BoxConstraints`].
2236    pub fn get_dry_baseline(
2237        self,
2238        app: &mut App,
2239        constraints: BoxConstraints,
2240        baseline: TextBaseline,
2241    ) -> Option<f64> {
2242        let baseline_offset = memoize_baseline(self, app, constraints, baseline, |this, app| {
2243            this.compute_dry_baseline(app, constraints, baseline)
2244        })
2245        .offset();
2246        // This assert makes sure compute_dry_baseline always gets called in debug mode, in case
2247        // the compute_dry_baseline implementation invokes debug_cannot_compute_dry_layout. The
2248        // check is skipped when debug_checking_intrinsics is true to avoid slowing down the app
2249        // significantly.
2250        if cfg!(debug_assertions) && !debug_checking_intrinsics() {
2251            assert_eq!(
2252                baseline_offset,
2253                (self.vtable.compute_dry_baseline)(app, self.id, constraints, baseline)
2254            );
2255        }
2256        baseline_offset
2257    }
2258
2259    /// Flutter's `RenderBox._computeDryBaseline`: the re-entrancy guard around the virtual.
2260    fn compute_dry_baseline(
2261        self,
2262        app: &mut App,
2263        constraints: BoxConstraints,
2264        baseline: TextBaseline,
2265    ) -> BaselineOffset {
2266        if cfg!(debug_assertions) {
2267            assert!(!self.box_data(app).debug_computing_this_dry_baseline);
2268            self.box_data_mut(app).debug_computing_this_dry_baseline = true;
2269        }
2270        let result = BaselineOffset((self.vtable.compute_dry_baseline)(
2271            app,
2272            self.id,
2273            constraints,
2274            baseline,
2275        ));
2276        if cfg!(debug_assertions) {
2277            self.box_data_mut(app).debug_computing_this_dry_baseline = false;
2278        }
2279        result
2280    }
2281
2282    /// Returns the distance from the y-coordinate of the position of the box to the y-coordinate
2283    /// of the first given baseline in the box's contents.
2284    ///
2285    /// Used by certain layout models to align adjacent boxes on a common baseline, regardless of
2286    /// padding, font size differences, etc. If there is no baseline, this function returns the
2287    /// distance from the y-coordinate of the position of the box to the y-coordinate of the
2288    /// bottom of the box (i.e., the height of the box) unless the caller passes true for
2289    /// `only_real`, in which case the function returns `None`.
2290    ///
2291    /// Only call this function after calling [`layout`](Self::layout) on this box. You are only
2292    /// allowed to call this from the parent of this box during that parent's
2293    /// [`RenderObject::perform_layout`] or [`RenderObject::paint`] functions.
2294    ///
2295    /// To override the baseline computation, override
2296    /// [`RenderBox::compute_distance_to_actual_baseline`].
2297    pub fn get_distance_to_baseline(
2298        self,
2299        app: &mut App,
2300        baseline: TextBaseline,
2301        only_real: bool,
2302    ) -> Option<f64> {
2303        debug_assert!(
2304            !DEBUG_DOING_BASELINE.get(),
2305            "Please see the documentation for compute_distance_to_actual_baseline for the \
2306             required calling conventions of this method."
2307        );
2308        debug_assert!(!self.as_object().debug_needs_layout(app) || debug_checking_intrinsics());
2309        let result = {
2310            let _doing_baseline = DebugDoingBaseline::enter();
2311            self.get_distance_to_actual_baseline(app, baseline)
2312        };
2313        match result {
2314            None if !only_real => Some(self.size(app).height()),
2315            result => result,
2316        }
2317    }
2318
2319    /// Calls [`RenderBox::compute_distance_to_actual_baseline`] and caches the result.
2320    ///
2321    /// This function must only be called from
2322    /// [`get_distance_to_baseline`](Self::get_distance_to_baseline) and
2323    /// [`RenderBox::compute_distance_to_actual_baseline`].
2324    pub fn get_distance_to_actual_baseline(
2325        self,
2326        app: &mut App,
2327        baseline: TextBaseline,
2328    ) -> Option<f64> {
2329        debug_assert!(
2330            DEBUG_DOING_BASELINE.get() || !cfg!(debug_assertions),
2331            "Please see the documentation for compute_distance_to_actual_baseline for the \
2332             required calling conventions of this method."
2333        );
2334        let constraints = self
2335            .box_data(app)
2336            .constraints
2337            .expect("a RenderBox has constraints once it has been laid out");
2338        memoize_baseline(self, app, constraints, baseline, |this, app| {
2339            BaselineOffset((this.vtable.compute_distance_to_actual_baseline)(
2340                app, this.id, baseline,
2341            ))
2342        })
2343        .offset()
2344    }
2345
2346    fn layout_cache(self, app: &App) -> &LayoutCacheStorage {
2347        &self.box_data(app).layout_cache
2348    }
2349
2350    fn layout_cache_mut(self, app: &mut App) -> &mut LayoutCacheStorage {
2351        &mut self.box_data_mut(app).layout_cache
2352    }
2353
2354    /// [`BoxParentData`] stored on this child by its box parent.
2355    ///
2356    /// Dart's `child.parentData! as BoxParentData`: a subclass answers with its own
2357    /// [`BoxParentData`] half through [`ParentData::provide`].
2358    pub fn box_parent_data(self, app: &App) -> &BoxParentData {
2359        box_parent_data_of(app, self.as_object())
2360    }
2361
2362    /// Convert the given point from the global coordinate system in logical pixels to the
2363    /// local coordinate system for this box.
2364    ///
2365    /// This method will un-project the point from the screen onto the widget, which makes it
2366    /// different from `MatrixUtils.transformPoint`.
2367    ///
2368    /// If the transform from global coordinates to local coordinates is degenerate, this
2369    /// function returns `Offset::ZERO`.
2370    ///
2371    /// If `ancestor` is non-null, this function converts the given point from the coordinate
2372    /// system of `ancestor` (which must be an ancestor of this render object) instead of from
2373    /// the global coordinate system.
2374    ///
2375    /// This method is implemented in terms of [`AnyRenderObject::get_transform_to`].
2376    pub fn global_to_local(
2377        self,
2378        app: &App,
2379        point: Offset,
2380        ancestor: Option<AnyRenderObject>,
2381    ) -> Offset {
2382        // We want to find the local point that corresponds to the given point on
2383        // the screen, but that also physically resides on this RenderBox's local
2384        // render plane, so that it is useful for visually accurate gesture
2385        // processing in the local space. For that, we cannot simply transform the
2386        // 2D screen point to the 3D local space since the screen space lacks the
2387        // depth component |z|, and so there are many 3D points that correspond to
2388        // the screen point. We must first unproject the screen point onto the local
2389        // render plane to find the true 3D point that corresponds to the screen
2390        // point.
2391        //
2392        // We do orthogonal unprojection after undoing perspective, in local space.
2393        // The local render plane is the XY plane with normal vector <0, 0, 1>.
2394        // Unprojection is done by finding the intersection of the view vector with
2395        // the local XY plane at z = 0.
2396        let transform = self.as_object().get_transform_to(app, ancestor);
2397        let Some(transform) = transform.invert() else {
2398            return Offset::ZERO;
2399        };
2400
2401        // Two points with the same screen x and y but different depths define the
2402        // view direction in local coordinates.
2403        let n = [0.0, 0.0, 1.0];
2404        let i = crate::object::perspective_transform(&transform, [0.0, 0.0, 0.0]);
2405        let d = subtract(
2406            crate::object::perspective_transform(&transform, [0.0, 0.0, 1.0]),
2407            i,
2408        );
2409        let s = crate::object::perspective_transform(&transform, [point.dx(), point.dy(), 0.0]);
2410        // Project the screen point onto the local render plane.
2411        let p = subtract(s, scale(d, dot(n, s) / dot(n, d)));
2412        Offset::new(p[0], p[1])
2413    }
2414
2415    /// Convert the given point from the local coordinate system for this box to the global
2416    /// coordinate system in logical pixels.
2417    ///
2418    /// If `ancestor` is non-null, this function converts the given point to the coordinate
2419    /// system of `ancestor` (which must be an ancestor of this render object) instead of to
2420    /// the global coordinate system.
2421    ///
2422    /// This method is implemented in terms of [`AnyRenderObject::get_transform_to`]. If the
2423    /// transform matrix puts the given `point` on the line at infinity (for instance, when
2424    /// the transform matrix is the zero matrix), this method returns (NaN, NaN).
2425    pub fn local_to_global(
2426        self,
2427        app: &App,
2428        point: Offset,
2429        ancestor: Option<AnyRenderObject>,
2430    ) -> Offset {
2431        inset_painting::transform_point(&self.as_object().get_transform_to(app, ancestor), point)
2432    }
2433
2434    /// See [`AnyRenderObject::parent_data_of_mut`].
2435    pub fn parent_data_of_mut<P: crate::object::ParentData + 'static>(
2436        self,
2437        app: &mut App,
2438    ) -> &mut P {
2439        self.as_object().parent_data_of_mut(app)
2440    }
2441
2442    /// See [`AnyRenderObject::parent_data_is`].
2443    pub fn parent_data_is<P: crate::object::ParentData + 'static>(self, app: &App) -> bool {
2444        self.as_object().parent_data_is::<P>(app)
2445    }
2446}
2447
2448impl crate::object::ErasedRenderObject for AnyRenderBox {
2449    fn as_object(self) -> AnyRenderObject {
2450        AnyRenderBox::as_object(self)
2451    }
2452
2453    fn from_object(object: AnyRenderObject) -> AnyRenderBox {
2454        object
2455            .as_box()
2456            .expect("the render object is not a RenderBox")
2457    }
2458}
2459
2460impl From<AnyRenderBox> for AnyRenderObject {
2461    fn from(box_: AnyRenderBox) -> AnyRenderObject {
2462        box_.as_object()
2463    }
2464}
2465
2466fn subtract(a: [f64; 3], b: [f64; 3]) -> [f64; 3] {
2467    [a[0] - b[0], a[1] - b[1], a[2] - b[2]]
2468}
2469
2470fn scale(a: [f64; 3], factor: f64) -> [f64; 3] {
2471    [a[0] * factor, a[1] * factor, a[2] * factor]
2472}
2473
2474fn dot(a: [f64; 3], b: [f64; 3]) -> f64 {
2475    a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
2476}
2477
2478#[cfg(test)]
2479mod tests {
2480    use super::*;
2481
2482    #[test]
2483    fn to_string_marks_biggest_unconstrained_and_tight() {
2484        assert!(
2485            BoxConstraints::expand(None, None)
2486                .to_string()
2487                .contains("biggest")
2488        );
2489        assert!(BoxConstraints::new().to_string().contains("unconstrained"));
2490        assert!(
2491            BoxConstraints::tight_for(Some(50.0), None)
2492                .to_string()
2493                .contains("w=50")
2494        );
2495    }
2496
2497    #[test]
2498    fn copy_with_replaces_fields() {
2499        let constraints = BoxConstraints::new()
2500            .min_width(3.0)
2501            .max_width(7.0)
2502            .min_height(11.0)
2503            .max_height(17.0);
2504        let copy = constraints.copy_with();
2505        assert_eq!(copy, constraints);
2506        let copy = constraints
2507            .copy_with()
2508            .min_width(13.0)
2509            .max_width(17.0)
2510            .min_height(111.0)
2511            .max_height(117.0);
2512        assert_eq!(copy.min_width, 13.0);
2513        assert_eq!(copy.max_width, 17.0);
2514        assert_eq!(copy.min_height, 111.0);
2515        assert_eq!(copy.max_height, 117.0);
2516        assert_ne!(copy, constraints);
2517        let mut copy_hasher = std::collections::hash_map::DefaultHasher::new();
2518        let mut original_hasher = std::collections::hash_map::DefaultHasher::new();
2519        copy.hash(&mut copy_hasher);
2520        constraints.hash(&mut original_hasher);
2521        assert_ne!(copy_hasher.finish(), original_hasher.finish());
2522    }
2523
2524    #[test]
2525    fn operators() {
2526        let constraints = BoxConstraints::new()
2527            .min_width(3.0)
2528            .max_width(7.0)
2529            .min_height(11.0)
2530            .max_height(17.0);
2531        let copy = constraints * 2.0;
2532        assert_eq!(copy.min_width, 6.0);
2533        assert_eq!(copy.max_width, 14.0);
2534        assert_eq!(copy.min_height, 22.0);
2535        assert_eq!(copy.max_height, 34.0);
2536        assert_eq!(copy / 2.0, constraints);
2537        let copy = constraints.truncating_div(2.0);
2538        assert_eq!(copy.min_width, 1.0);
2539        assert_eq!(copy.max_width, 3.0);
2540        assert_eq!(copy.min_height, 5.0);
2541        assert_eq!(copy.max_height, 8.0);
2542        let copy = constraints % 3.0;
2543        assert_eq!(copy.min_width, 0.0);
2544        assert_eq!(copy.max_width, 1.0);
2545        assert_eq!(copy.min_height, 2.0);
2546        assert_eq!(copy.max_height, 2.0);
2547    }
2548
2549    #[test]
2550    fn lerp_from_null_and_between() {
2551        assert!(BoxConstraints::lerp(None, None, 0.5).is_none());
2552        let constraints = BoxConstraints::new()
2553            .min_width(3.0)
2554            .max_width(7.0)
2555            .min_height(11.0)
2556            .max_height(17.0);
2557        let copy = BoxConstraints::lerp(None, Some(constraints), 0.5).unwrap();
2558        assert!((copy.min_width - 1.5).abs() < 1e-9);
2559        assert!((copy.max_width - 3.5).abs() < 1e-9);
2560        assert!((copy.min_height - 5.5).abs() < 1e-9);
2561        assert!((copy.max_height - 8.5).abs() < 1e-9);
2562        let copy = BoxConstraints::lerp(Some(constraints), None, 0.5).unwrap();
2563        assert!((copy.min_width - 1.5).abs() < 1e-9);
2564        let copy = BoxConstraints::lerp(
2565            Some(
2566                BoxConstraints::new()
2567                    .min_width(13.0)
2568                    .max_width(17.0)
2569                    .min_height(111.0)
2570                    .max_height(117.0),
2571            ),
2572            Some(constraints),
2573            0.2,
2574        )
2575        .unwrap();
2576        assert!((copy.min_width - 11.0).abs() < 1e-9);
2577        assert!((copy.max_width - 15.0).abs() < 1e-9);
2578        assert!((copy.min_height - 91.0).abs() < 1e-9);
2579        assert!((copy.max_height - 97.0).abs() < 1e-9);
2580    }
2581
2582    #[test]
2583    fn lerp_unbounded_axes() {
2584        let constraints1 = BoxConstraints::new()
2585            .min_width(f64::INFINITY)
2586            .min_height(10.0)
2587            .max_height(20.0);
2588        let constraints2 = BoxConstraints::new()
2589            .min_width(f64::INFINITY)
2590            .min_height(20.0)
2591            .max_height(30.0);
2592        let constraints3 = BoxConstraints::new()
2593            .min_width(f64::INFINITY)
2594            .min_height(15.0)
2595            .max_height(25.0);
2596        assert_eq!(
2597            BoxConstraints::lerp(Some(constraints1), Some(constraints2), 0.5),
2598            Some(constraints3)
2599        );
2600    }
2601
2602    #[test]
2603    fn lerp_overshoot_is_normalized() {
2604        assert_eq!(
2605            BoxConstraints::lerp(
2606                Some(BoxConstraints::tight_for(None, Some(25.0))),
2607                Some(BoxConstraints::tight_for(None, Some(0.0))),
2608                1.2,
2609            ),
2610            Some(BoxConstraints::tight_for(None, Some(0.0)))
2611        );
2612        let lerped = BoxConstraints::lerp(
2613            Some(BoxConstraints::tight(Size::new(10.0, 10.0))),
2614            Some(BoxConstraints::new().max_width(100.0).max_height(100.0)),
2615            -0.1,
2616        )
2617        .unwrap();
2618        assert!(lerped.is_normalized());
2619        assert_eq!(lerped, BoxConstraints::tight(Size::new(11.0, 11.0)));
2620    }
2621
2622    #[test]
2623    fn normalize_raises_inverted_max() {
2624        let constraints = BoxConstraints::new()
2625            .min_width(3.0)
2626            .max_width(2.0)
2627            .min_height(11.0)
2628            .max_height(18.0);
2629        let copy = constraints.normalize();
2630        assert_eq!(copy.min_width, 3.0);
2631        assert_eq!(copy.max_width, 3.0);
2632        assert_eq!(copy.min_height, 11.0);
2633        assert_eq!(copy.max_height, 18.0);
2634    }
2635
2636    #[test]
2637    fn from_view_constraints() {
2638        let unconstrained = BoxConstraints::from_view_constraints(ViewConstraints::default());
2639        assert_eq!(unconstrained, BoxConstraints::new());
2640        let constraints =
2641            BoxConstraints::from_view_constraints(ViewConstraints::new(1.0, 2.0, 3.0, 4.0));
2642        assert_eq!(
2643            constraints,
2644            BoxConstraints::new()
2645                .min_width(1.0)
2646                .max_width(2.0)
2647                .min_height(3.0)
2648                .max_height(4.0)
2649        );
2650    }
2651
2652    #[test]
2653    fn constrain_size_and_attempt_to_preserve_aspect_ratio_empty_size() {
2654        let constraints = BoxConstraints::new()
2655            .min_width(10.0)
2656            .max_width(20.0)
2657            .min_height(10.0)
2658            .max_height(20.0);
2659        let constrained =
2660            constraints.constrain_size_and_attempt_to_preserve_aspect_ratio(Size::new(15.0, 0.0));
2661        assert_eq!(constrained, Size::new(15.0, 10.0));
2662    }
2663}
2664
2665#[cfg(test)]
2666mod hit_test_tests {
2667    use super::*;
2668
2669    #[derive(Debug)]
2670    struct DummyHitTestTarget;
2671
2672    impl HitTestTarget for DummyHitTestTarget {
2673        fn handle_event(&self, _app: &mut App, _event: &PointerEvent, _entry: &HitTestEntry) {}
2674    }
2675
2676    /// `box_test.dart`: `BoxHitTestResult wrapping HitTestResult`.
2677    #[test]
2678    fn wrapping_shares_the_path() {
2679        let transform = Matrix4::translation(40.0, 150.0);
2680        let mut wrapped = HitTestResult::new();
2681        wrapped.push_transform(transform);
2682        wrapped.add(HitTestEntry::new(DummyHitTestTarget));
2683        assert_eq!(wrapped.path().len(), 1);
2684        assert_eq!(wrapped.path()[0].transform(), Some(transform));
2685
2686        let mut wrapping = BoxHitTestResult::wrap(&mut wrapped);
2687        wrapping.add(HitTestEntry::new(DummyHitTestTarget));
2688        assert_eq!(wrapping.path().len(), 2);
2689        assert_eq!(wrapping.path()[1].transform(), Some(transform));
2690
2691        wrapped.add(HitTestEntry::new(DummyHitTestTarget));
2692        assert_eq!(wrapped.path().len(), 3);
2693        assert_eq!(wrapped.path()[2].transform(), Some(transform));
2694    }
2695
2696    /// `box_test.dart`: `addWithPaintTransform`.
2697    #[test]
2698    fn add_with_paint_transform() {
2699        let mut base = HitTestResult::new();
2700        let mut result = BoxHitTestResult::wrap(&mut base);
2701        let mut positions = Vec::new();
2702
2703        let is_hit = result.add_with_paint_transform(None, Offset::ZERO, |_, position| {
2704            positions.push(position);
2705            true
2706        });
2707        assert!(is_hit);
2708        assert_eq!(positions, [Offset::ZERO]);
2709        positions.clear();
2710
2711        let is_hit = result.add_with_paint_transform(
2712            Some(Matrix4::translation(20.0, 30.0)),
2713            Offset::ZERO,
2714            |_, position| {
2715                positions.push(position);
2716                true
2717            },
2718        );
2719        assert!(is_hit);
2720        assert_eq!(positions, [Offset::new(-20.0, -30.0)]);
2721        positions.clear();
2722
2723        let position = Offset::new(3.0, 4.0);
2724        let is_hit = result.add_with_paint_transform(None, position, |_, position| {
2725            positions.push(position);
2726            false
2727        });
2728        assert!(!is_hit);
2729        assert_eq!(positions, [position]);
2730        positions.clear();
2731
2732        let is_hit = result.add_with_paint_transform(
2733            Some(Matrix4::translation(20.0, 30.0)),
2734            position,
2735            |_, position| {
2736                positions.push(position);
2737                true
2738            },
2739        );
2740        assert!(is_hit);
2741        assert_eq!(positions, [position - Offset::new(20.0, 30.0)]);
2742        positions.clear();
2743
2744        // A transform that cannot be inverted.
2745        let is_hit = result.add_with_paint_transform(
2746            Some(Matrix4::scale(0.0, 0.0)),
2747            position,
2748            |_, position| {
2749                positions.push(position);
2750                true
2751            },
2752        );
2753        assert!(!is_hit);
2754        assert!(positions.is_empty());
2755    }
2756
2757    /// `box_test.dart`: `addWithPaintOffset`.
2758    #[test]
2759    fn add_with_paint_offset() {
2760        let mut base = HitTestResult::new();
2761        let mut result = BoxHitTestResult::wrap(&mut base);
2762        let mut positions = Vec::new();
2763
2764        let is_hit = result.add_with_paint_offset(None, Offset::ZERO, |_, position| {
2765            positions.push(position);
2766            true
2767        });
2768        assert!(is_hit);
2769        assert_eq!(positions, [Offset::ZERO]);
2770        positions.clear();
2771
2772        let is_hit = result.add_with_paint_offset(
2773            Some(Offset::new(55.0, 32.0)),
2774            Offset::ZERO,
2775            |_, position| {
2776                positions.push(position);
2777                true
2778            },
2779        );
2780        assert!(is_hit);
2781        assert_eq!(positions, [Offset::new(-55.0, -32.0)]);
2782        positions.clear();
2783
2784        let position = Offset::new(3.0, 4.0);
2785        let is_hit = result.add_with_paint_offset(
2786            Some(Offset::new(55.0, 32.0)),
2787            position,
2788            |result, position| {
2789                result.add(HitTestEntry::new(DummyHitTestTarget));
2790                positions.push(position);
2791                true
2792            },
2793        );
2794        assert!(is_hit);
2795        assert_eq!(positions, [position - Offset::new(55.0, 32.0)]);
2796        assert_eq!(
2797            result.path()[0].transform(),
2798            Some(Matrix4::translation(-55.0, -32.0)),
2799            "the entry records the transform to the child"
2800        );
2801    }
2802
2803    /// `box_test.dart`: `addWithRawTransform`.
2804    #[test]
2805    fn add_with_raw_transform() {
2806        let mut base = HitTestResult::new();
2807        let mut result = BoxHitTestResult::wrap(&mut base);
2808        let mut positions = Vec::new();
2809
2810        let is_hit = result.add_with_raw_transform(
2811            Some(Matrix4::translation(20.0, 30.0)),
2812            Offset::ZERO,
2813            |_, position| {
2814                positions.push(position);
2815                true
2816            },
2817        );
2818        assert!(is_hit);
2819        assert_eq!(positions, [Offset::new(20.0, 30.0)]);
2820        positions.clear();
2821
2822        let position = Offset::new(3.0, 4.0);
2823        let is_hit =
2824            result.add_with_raw_transform(Some(Matrix4::IDENTITY), position, |_, position| {
2825                positions.push(position);
2826                true
2827            });
2828        assert!(is_hit);
2829        assert_eq!(positions, [position]);
2830    }
2831}
2832
2833#[cfg(test)]
2834mod intrinsics_tests {
2835    use inset_foundation::AppCell;
2836    use std::cell::Cell;
2837    use std::rc::Rc;
2838
2839    use super::*;
2840    use crate::object::RenderObjectData;
2841
2842    /// A leaf whose minimum intrinsic width is `100.0 + height`, counting how often it is
2843    /// actually computed.
2844    struct CountingBox {
2845        render_object: RenderObjectData,
2846        render_box: RenderBoxData,
2847        computations: Rc<Cell<u32>>,
2848    }
2849
2850    impl RenderObject for CountingBox {
2851        crate::render_object_accessors!();
2852
2853        fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
2854            let size = self.constraints(app).smallest();
2855            self.set_size(app, size);
2856        }
2857    }
2858
2859    impl RenderBox for CountingBox {
2860        crate::render_box_accessors!();
2861
2862        fn compute_min_intrinsic_width(
2863            self: RenderHandle<Self>,
2864            app: &mut App,
2865            height: f64,
2866        ) -> f64 {
2867            let computations = self.get(app).computations.clone();
2868            computations.set(computations.get() + 1);
2869            100.0 + height
2870        }
2871    }
2872
2873    /// `box_test.dart`: `Intrinsics cache' and `Intrinsics cache is cleared when the render
2874    /// object is marked as needing layout`.
2875    #[test]
2876    fn the_intrinsic_cache_is_reused_and_cleared_by_mark_needs_layout() {
2877        let cell = AppCell::new();
2878        let mut app = cell.borrow_mut();
2879        let computations = Rc::new(Cell::new(0));
2880        let box_ = RenderHandle::new_box(
2881            &mut app,
2882            CountingBox {
2883                render_object: RenderObjectData::new(),
2884                render_box: RenderBoxData::new(),
2885                computations: computations.clone(),
2886            },
2887        );
2888
2889        assert_eq!(box_.as_box().get_min_intrinsic_width(&mut app, 0.0), 100.0);
2890        assert_eq!(computations.get(), 1);
2891
2892        assert_eq!(box_.as_box().get_min_intrinsic_width(&mut app, 0.0), 100.0);
2893        assert_eq!(computations.get(), 1, "the memoized value is reused");
2894
2895        assert_eq!(box_.as_box().get_min_intrinsic_width(&mut app, 10.0), 110.0);
2896        assert_eq!(computations.get(), 2, "another height is another entry");
2897
2898        box_.mark_needs_layout(&mut app);
2899        assert_eq!(box_.as_box().get_min_intrinsic_width(&mut app, 0.0), 100.0);
2900        assert_eq!(computations.get(), 3, "mark_needs_layout cleared the cache");
2901    }
2902
2903    /// Flutter's `RenderBox.markNeedsLayout`: a box whose intrinsics the parent read defers to
2904    /// the parent, because the parent's layout depends on them.
2905    #[test]
2906    fn clearing_a_cached_intrinsic_marks_the_parent() {
2907        let cell = AppCell::new();
2908        let mut app = cell.borrow_mut();
2909        let computations = Rc::new(Cell::new(0));
2910        let child = RenderHandle::new_box(
2911            &mut app,
2912            CountingBox {
2913                render_object: RenderObjectData::new(),
2914                render_box: RenderBoxData::new(),
2915                computations,
2916            },
2917        );
2918        let parent = crate::proxy_box::RenderConstrainedBox::new(
2919            &mut app,
2920            BoxConstraints::new(),
2921            Some(child.as_box()),
2922        );
2923        parent.layout(&mut app, BoxConstraints::new().max_width(50.0), false);
2924        assert!(!parent.debug_needs_layout(&app));
2925
2926        child.as_box().get_min_intrinsic_width(&mut app, 0.0);
2927        child.mark_needs_layout(&mut app);
2928        assert!(
2929            parent.debug_needs_layout(&app),
2930            "the parent read an intrinsic, so it has to lay out again"
2931        );
2932    }
2933}