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