Skip to main content

inset_rendering/
sliver.rs

1//! Flutter counterpart: `rendering/sliver.dart`.
2
3use std::any::{Any, TypeId};
4use std::fmt::{self, Debug, Display};
5use std::hash::Hasher;
6use std::ops::{Deref, DerefMut};
7
8use inset_embedder::{Matrix4, Offset, Rect, Size, clamp_double};
9use inset_foundation::{App, Handle, HandleId, PRECISION_ERROR_TOLERANCE};
10use inset_gestures::{HitTestEntry, HitTestResult, HitTestTarget, PointerEvent};
11use inset_painting::{
12    Axis, AxisDirection, axis_direction_is_reversed, axis_direction_to_axis, flip_axis_direction,
13};
14
15use crate::box_::{AnyRenderBox, BoxConstraints, BoxHitTestResult};
16use crate::object::{
17    AnyRenderObject, Constraints, ContainerParentData, ContainerParentDataMixin, ParentData,
18    RenderHandle, RenderObject, RenderObjectVTable, RenderObjectWithChildData,
19    RenderObjectWithChildMixin, create, resolve, translate,
20};
21use crate::painting_context::PaintingContext;
22use crate::pipeline_owner::PipelineOwner;
23use crate::viewport_offset::{ScrollDirection, flip_scroll_direction};
24
25/// Signature for a callback that reports the main-axis extent of the item at `index`.
26///
27/// Used by `ListView.itemExtentBuilder` and `SliverVariedExtentList.itemExtentBuilder`.
28///
29/// Dart compares two builders with `==`; an [`Rc`](std::rc::Rc) compares by pointer.
30pub type ItemExtentBuilder = std::rc::Rc<dyn Fn(i32, SliverLayoutDimensions) -> Option<f64>>;
31
32/// Relates the dimensions of the [`RenderSliver`] during layout.
33#[derive(Clone, Copy, Debug, PartialEq)]
34pub struct SliverLayoutDimensions {
35    /// Scroll offset in this sliver's coordinate system of the earliest visible part of this
36    /// sliver.
37    pub scroll_offset: f64,
38
39    /// Scroll distance consumed by all slivers that came before this one.
40    pub preceding_scroll_extent: f64,
41
42    /// The number of pixels the viewport can display in the main axis.
43    ///
44    /// For a vertical list, this is the height of the viewport.
45    pub viewport_main_axis_extent: f64,
46
47    /// The number of pixels in the cross-axis.
48    ///
49    /// For a vertical list, this is the width of the sliver.
50    pub cross_axis_extent: f64,
51}
52
53impl SliverLayoutDimensions {
54    /// Constructs the layout dimensions with the specified parameters.
55    pub const fn new(
56        scroll_offset: f64,
57        preceding_scroll_extent: f64,
58        viewport_main_axis_extent: f64,
59        cross_axis_extent: f64,
60    ) -> SliverLayoutDimensions {
61        SliverLayoutDimensions {
62            scroll_offset,
63            preceding_scroll_extent,
64            viewport_main_axis_extent,
65            cross_axis_extent,
66        }
67    }
68}
69
70impl Display for SliverLayoutDimensions {
71    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72        write!(
73            f,
74            "scrollOffset: {} precedingScrollExtent: {} viewportMainAxisExtent: {} \
75             crossAxisExtent: {}",
76            self.scroll_offset,
77            self.preceding_scroll_extent,
78            self.viewport_main_axis_extent,
79            self.cross_axis_extent
80        )
81    }
82}
83
84/// The direction in which a sliver's contents are ordered, relative to the
85/// scroll offset axis.
86#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
87pub enum GrowthDirection {
88    /// Contents are ordered in the same direction as the [`AxisDirection`].
89    Forward,
90
91    /// Contents are ordered in the opposite direction of the [`AxisDirection`].
92    Reverse,
93}
94
95/// Flips the [`AxisDirection`] if the [`GrowthDirection`] is
96/// [`GrowthDirection::Reverse`].
97pub fn apply_growth_direction_to_axis_direction(
98    axis_direction: AxisDirection,
99    growth_direction: GrowthDirection,
100) -> AxisDirection {
101    match growth_direction {
102        GrowthDirection::Forward => axis_direction,
103        GrowthDirection::Reverse => flip_axis_direction(axis_direction),
104    }
105}
106
107/// Flips the [`ScrollDirection`] if the [`GrowthDirection`] is
108/// [`GrowthDirection::Reverse`].
109pub fn apply_growth_direction_to_scroll_direction(
110    scroll_direction: ScrollDirection,
111    growth_direction: GrowthDirection,
112) -> ScrollDirection {
113    match growth_direction {
114        GrowthDirection::Forward => scroll_direction,
115        GrowthDirection::Reverse => flip_scroll_direction(scroll_direction),
116    }
117}
118
119/// Immutable layout constraints for [`RenderSliver`] layout.
120#[derive(Clone, Copy, Debug)]
121pub struct SliverConstraints {
122    /// The direction in which [`scroll_offset`](Self::scroll_offset) and
123    /// [`remaining_paint_extent`](Self::remaining_paint_extent) increase.
124    pub axis_direction: AxisDirection,
125    /// The direction in which the contents of slivers are ordered.
126    pub growth_direction: GrowthDirection,
127    /// The direction in which the user is attempting to scroll.
128    pub user_scroll_direction: ScrollDirection,
129    /// Scroll offset in this sliver's coordinate system of the earliest visible
130    /// part of this sliver.
131    pub scroll_offset: f64,
132    /// Scroll distance consumed by all slivers that came before this one.
133    pub preceding_scroll_extent: f64,
134    /// Pixels from the painted [`scroll_offset`](Self::scroll_offset) up to the
135    /// first pixel not yet painted by an earlier sliver.
136    pub overlap: f64,
137    /// Pixels of content the sliver should consider providing.
138    pub remaining_paint_extent: f64,
139    /// Pixels in the cross-axis.
140    pub cross_axis_extent: f64,
141    /// Direction in which children should be placed in the cross axis.
142    pub cross_axis_direction: AxisDirection,
143    /// Pixels the viewport can display in the main axis.
144    pub viewport_main_axis_extent: f64,
145    /// How much content the sliver should provide starting from
146    /// [`cache_origin`](Self::cache_origin).
147    pub remaining_cache_extent: f64,
148    /// Where the cache area starts relative to the
149    /// [`scroll_offset`](Self::scroll_offset). Always negative or zero.
150    pub cache_origin: f64,
151}
152
153impl SliverConstraints {
154    /// Creates sliver constraints with the given information.
155    #[allow(clippy::too_many_arguments)]
156    pub const fn new(
157        axis_direction: AxisDirection,
158        growth_direction: GrowthDirection,
159        user_scroll_direction: ScrollDirection,
160        scroll_offset: f64,
161        preceding_scroll_extent: f64,
162        overlap: f64,
163        remaining_paint_extent: f64,
164        cross_axis_extent: f64,
165        cross_axis_direction: AxisDirection,
166        viewport_main_axis_extent: f64,
167        remaining_cache_extent: f64,
168        cache_origin: f64,
169    ) -> SliverConstraints {
170        SliverConstraints {
171            axis_direction,
172            growth_direction,
173            user_scroll_direction,
174            scroll_offset,
175            preceding_scroll_extent,
176            overlap,
177            remaining_paint_extent,
178            cross_axis_extent,
179            cross_axis_direction,
180            viewport_main_axis_extent,
181            remaining_cache_extent,
182            cache_origin,
183        }
184    }
185
186    /// Creates a copy of this object. Chain field replacements.
187    pub const fn copy_with(self) -> SliverConstraints {
188        self
189    }
190
191    /// Sets [`axis_direction`](Self::axis_direction).
192    pub const fn axis_direction(mut self, value: AxisDirection) -> SliverConstraints {
193        self.axis_direction = value;
194        self
195    }
196
197    /// Sets [`growth_direction`](Self::growth_direction).
198    pub const fn growth_direction(mut self, value: GrowthDirection) -> SliverConstraints {
199        self.growth_direction = value;
200        self
201    }
202
203    /// Sets [`user_scroll_direction`](Self::user_scroll_direction).
204    pub const fn user_scroll_direction(mut self, value: ScrollDirection) -> SliverConstraints {
205        self.user_scroll_direction = value;
206        self
207    }
208
209    /// Sets [`scroll_offset`](Self::scroll_offset).
210    pub const fn scroll_offset(mut self, value: f64) -> SliverConstraints {
211        self.scroll_offset = value;
212        self
213    }
214
215    /// Sets [`preceding_scroll_extent`](Self::preceding_scroll_extent).
216    pub const fn preceding_scroll_extent(mut self, value: f64) -> SliverConstraints {
217        self.preceding_scroll_extent = value;
218        self
219    }
220
221    /// Sets [`overlap`](Self::overlap).
222    pub const fn overlap(mut self, value: f64) -> SliverConstraints {
223        self.overlap = value;
224        self
225    }
226
227    /// Sets [`remaining_paint_extent`](Self::remaining_paint_extent).
228    pub const fn remaining_paint_extent(mut self, value: f64) -> SliverConstraints {
229        self.remaining_paint_extent = value;
230        self
231    }
232
233    /// Sets [`cross_axis_extent`](Self::cross_axis_extent).
234    pub const fn cross_axis_extent(mut self, value: f64) -> SliverConstraints {
235        self.cross_axis_extent = value;
236        self
237    }
238
239    /// Sets [`cross_axis_direction`](Self::cross_axis_direction).
240    pub const fn cross_axis_direction(mut self, value: AxisDirection) -> SliverConstraints {
241        self.cross_axis_direction = value;
242        self
243    }
244
245    /// Sets [`viewport_main_axis_extent`](Self::viewport_main_axis_extent).
246    pub const fn viewport_main_axis_extent(mut self, value: f64) -> SliverConstraints {
247        self.viewport_main_axis_extent = value;
248        self
249    }
250
251    /// Sets [`remaining_cache_extent`](Self::remaining_cache_extent).
252    pub const fn remaining_cache_extent(mut self, value: f64) -> SliverConstraints {
253        self.remaining_cache_extent = value;
254        self
255    }
256
257    /// Sets [`cache_origin`](Self::cache_origin).
258    pub const fn cache_origin(mut self, value: f64) -> SliverConstraints {
259        self.cache_origin = value;
260        self
261    }
262
263    /// The axis along which the scroll offset is measured.
264    pub fn axis(self) -> Axis {
265        axis_direction_to_axis(self.axis_direction)
266    }
267
268    /// [`growth_direction`](Self::growth_direction) as if the axis were down or
269    /// right.
270    pub fn normalized_growth_direction(self) -> GrowthDirection {
271        if axis_direction_is_reversed(self.axis_direction) {
272            match self.growth_direction {
273                GrowthDirection::Forward => GrowthDirection::Reverse,
274                GrowthDirection::Reverse => GrowthDirection::Forward,
275            }
276        } else {
277            self.growth_direction
278        }
279    }
280
281    /// [`BoxConstraints`] that reflect these sliver constraints.
282    ///
283    /// Dart named defaults: `min_extent` 0, `max_extent` infinity, `cross_axis_extent`
284    /// this sliver's cross extent.
285    pub fn as_box_constraints(
286        self,
287        min_extent: f64,
288        max_extent: f64,
289        cross_axis_extent: Option<f64>,
290    ) -> BoxConstraints {
291        let cross = cross_axis_extent.unwrap_or(self.cross_axis_extent);
292        match self.axis() {
293            Axis::Horizontal => BoxConstraints {
294                min_width: min_extent,
295                max_width: max_extent,
296                min_height: cross,
297                max_height: cross,
298            },
299            Axis::Vertical => BoxConstraints {
300                min_width: cross,
301                max_width: cross,
302                min_height: min_extent,
303                max_height: max_extent,
304            },
305        }
306    }
307}
308
309impl Constraints for SliverConstraints {
310    fn is_tight(&self) -> bool {
311        false
312    }
313
314    fn is_normalized(&self) -> bool {
315        self.scroll_offset >= 0.0
316            && self.cross_axis_extent >= 0.0
317            && axis_direction_to_axis(self.axis_direction)
318                != axis_direction_to_axis(self.cross_axis_direction)
319            && self.viewport_main_axis_extent >= 0.0
320            && self.remaining_paint_extent >= 0.0
321    }
322}
323
324impl PartialEq for SliverConstraints {
325    fn eq(&self, other: &SliverConstraints) -> bool {
326        debug_assert!(self.debug_assert_is_valid(false));
327        debug_assert!(other.debug_assert_is_valid(false));
328        self.axis_direction == other.axis_direction
329            && self.growth_direction == other.growth_direction
330            && self.user_scroll_direction == other.user_scroll_direction
331            && self.scroll_offset == other.scroll_offset
332            && self.preceding_scroll_extent == other.preceding_scroll_extent
333            && self.overlap == other.overlap
334            && self.remaining_paint_extent == other.remaining_paint_extent
335            && self.cross_axis_extent == other.cross_axis_extent
336            && self.cross_axis_direction == other.cross_axis_direction
337            && self.viewport_main_axis_extent == other.viewport_main_axis_extent
338            && self.remaining_cache_extent == other.remaining_cache_extent
339            && self.cache_origin == other.cache_origin
340    }
341}
342
343/// The amount of space a sliver occupies.
344#[derive(Clone, Copy, Debug)]
345pub struct SliverGeometry {
346    /// Estimated total scrollable extent this sliver has content for.
347    pub scroll_extent: f64,
348    /// Visual location of the first visible part relative to the layout
349    /// position.
350    pub paint_origin: f64,
351    /// Currently visible visual space taken by this sliver.
352    pub paint_extent: f64,
353    /// Distance from the first visible part of this sliver to the first visible
354    /// part of the next.
355    pub layout_extent: f64,
356    /// Estimated total paint extent if remaining paint extent were infinite.
357    pub max_paint_extent: f64,
358    /// Maximum extent by which a pinned sliver can reduce the scrollable area.
359    pub max_scroll_obstruction_extent: f64,
360    /// Distance from where this sliver started painting to the bottom of where
361    /// it should accept hits.
362    pub hit_test_extent: f64,
363    /// Whether this sliver should be painted.
364    pub visible: bool,
365    /// Whether this sliver has visual overflow.
366    pub has_visual_overflow: bool,
367    /// Non-zero asks the parent to correct the scroll offset and relayout.
368    pub scroll_offset_correction: Option<f64>,
369    /// Pixels consumed in the remaining cache extent.
370    pub cache_extent: f64,
371    /// Cross-axis extent, if the sliver reports one.
372    pub cross_axis_extent: Option<f64>,
373}
374
375impl SliverGeometry {
376    /// A sliver that occupies no space at all.
377    pub const ZERO: SliverGeometry = SliverGeometry {
378        scroll_extent: 0.0,
379        paint_origin: 0.0,
380        paint_extent: 0.0,
381        layout_extent: 0.0,
382        max_paint_extent: 0.0,
383        max_scroll_obstruction_extent: 0.0,
384        hit_test_extent: 0.0,
385        visible: false,
386        has_visual_overflow: false,
387        scroll_offset_correction: None,
388        cache_extent: 0.0,
389        cross_axis_extent: None,
390    };
391
392    /// Creates geometry with Flutter's constructor defaults.
393    pub const fn new() -> SliverGeometry {
394        SliverGeometry::ZERO
395    }
396
397    /// Sets [`scroll_extent`](Self::scroll_extent).
398    pub const fn scroll_extent(mut self, value: f64) -> SliverGeometry {
399        self.scroll_extent = value;
400        self
401    }
402
403    /// Sets [`paint_origin`](Self::paint_origin).
404    pub const fn paint_origin(mut self, value: f64) -> SliverGeometry {
405        self.paint_origin = value;
406        self
407    }
408
409    /// Sets [`paint_extent`](Self::paint_extent). Also fills layout, hit-test,
410    /// cache, and visible the way Flutter's constructor does when those
411    /// arguments are omitted.
412    pub const fn paint_extent(mut self, value: f64) -> SliverGeometry {
413        self.paint_extent = value;
414        self.layout_extent = value;
415        self.hit_test_extent = value;
416        self.cache_extent = value;
417        self.visible = value > 0.0;
418        self
419    }
420
421    /// Sets [`layout_extent`](Self::layout_extent).
422    pub const fn layout_extent(mut self, value: f64) -> SliverGeometry {
423        self.layout_extent = value;
424        self
425    }
426
427    /// Sets [`max_paint_extent`](Self::max_paint_extent).
428    pub const fn max_paint_extent(mut self, value: f64) -> SliverGeometry {
429        self.max_paint_extent = value;
430        self
431    }
432
433    /// Sets [`max_scroll_obstruction_extent`](Self::max_scroll_obstruction_extent).
434    pub const fn max_scroll_obstruction_extent(mut self, value: f64) -> SliverGeometry {
435        self.max_scroll_obstruction_extent = value;
436        self
437    }
438
439    /// Sets [`hit_test_extent`](Self::hit_test_extent).
440    pub const fn hit_test_extent(mut self, value: f64) -> SliverGeometry {
441        self.hit_test_extent = value;
442        self
443    }
444
445    /// Sets [`visible`](Self::visible).
446    pub const fn visible(mut self, value: bool) -> SliverGeometry {
447        self.visible = value;
448        self
449    }
450
451    /// Sets [`has_visual_overflow`](Self::has_visual_overflow).
452    pub const fn has_visual_overflow(mut self, value: bool) -> SliverGeometry {
453        self.has_visual_overflow = value;
454        self
455    }
456
457    /// Sets [`scroll_offset_correction`](Self::scroll_offset_correction).
458    pub fn scroll_offset_correction(mut self, value: f64) -> SliverGeometry {
459        debug_assert!(value != 0.0);
460        self.scroll_offset_correction = Some(value);
461        self
462    }
463
464    /// Sets [`cache_extent`](Self::cache_extent).
465    pub const fn cache_extent(mut self, value: f64) -> SliverGeometry {
466        self.cache_extent = value;
467        self
468    }
469
470    /// Sets [`cross_axis_extent`](Self::cross_axis_extent).
471    pub const fn cross_axis_extent(mut self, value: f64) -> SliverGeometry {
472        self.cross_axis_extent = Some(value);
473        self
474    }
475
476    /// Creates a copy of this object. Chain field replacements.
477    pub const fn copy_with(self) -> SliverGeometry {
478        self
479    }
480
481    /// Dart's `geometry == SliverGeometry.zero`: whether this geometry occupies no space at
482    /// all.
483    pub fn is_zero(&self) -> bool {
484        self.scroll_extent == SliverGeometry::ZERO.scroll_extent
485            && self.paint_origin == SliverGeometry::ZERO.paint_origin
486            && self.paint_extent == SliverGeometry::ZERO.paint_extent
487            && self.layout_extent == SliverGeometry::ZERO.layout_extent
488            && self.max_paint_extent == SliverGeometry::ZERO.max_paint_extent
489            && self.max_scroll_obstruction_extent
490                == SliverGeometry::ZERO.max_scroll_obstruction_extent
491            && self.hit_test_extent == SliverGeometry::ZERO.hit_test_extent
492            && self.visible == SliverGeometry::ZERO.visible
493            && self.has_visual_overflow == SliverGeometry::ZERO.has_visual_overflow
494            && self.scroll_offset_correction == SliverGeometry::ZERO.scroll_offset_correction
495            && self.cache_extent == SliverGeometry::ZERO.cache_extent
496            && self.cross_axis_extent == SliverGeometry::ZERO.cross_axis_extent
497    }
498
499    /// Asserts that this geometry is internally consistent.
500    ///
501    /// Does nothing if asserts are disabled. Always returns true.
502    pub fn debug_assert_is_valid(&self) -> bool {
503        debug_assert!(self.scroll_extent >= 0.0, "The scroll_extent is negative.");
504        debug_assert!(self.paint_extent >= 0.0, "The paint_extent is negative.");
505        debug_assert!(self.layout_extent >= 0.0, "The layout_extent is negative.");
506        debug_assert!(self.cache_extent >= 0.0, "The cache_extent is negative.");
507        debug_assert!(
508            self.layout_extent <= self.paint_extent,
509            "The layout_extent ({}) exceeds the paint_extent ({}).",
510            self.layout_extent,
511            self.paint_extent
512        );
513        // If the paint_extent is slightly more than the max_paint_extent, but the difference is
514        // still less than PRECISION_ERROR_TOLERANCE, we will not fail the assert below.
515        debug_assert!(
516            self.paint_extent - self.max_paint_extent <= PRECISION_ERROR_TOLERANCE,
517            "The max_paint_extent ({}) is less than the paint_extent ({}). By definition, a \
518             sliver can't paint more than the maximum that it can paint!",
519            self.max_paint_extent,
520            self.paint_extent
521        );
522        debug_assert!(
523            self.hit_test_extent >= 0.0,
524            "The hit_test_extent is negative."
525        );
526        debug_assert!(
527            self.scroll_offset_correction != Some(0.0),
528            "The scroll_offset_correction is zero."
529        );
530        true
531    }
532}
533
534impl Default for SliverGeometry {
535    fn default() -> SliverGeometry {
536        SliverGeometry::new()
537    }
538}
539
540impl Display for SliverGeometry {
541    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
542        write!(
543            f,
544            "SliverGeometry({}x{})",
545            self.scroll_extent, self.paint_extent
546        )
547    }
548}
549
550/// Method signature for hit testing a [`RenderSliver`].
551///
552/// Used by [`SliverHitTestResult::add_with_axis_offset`] to hit test [`RenderSliver`] children.
553///
554/// See also:
555///
556///  * [`RenderSliver::hit_test`], which documents more details around hit testing slivers.
557pub type SliverHitTest<'r> = dyn FnOnce(&mut SliverHitTestResult<'r>, f64, f64) -> bool;
558
559/// The result of performing a hit test on [`RenderSliver`]s.
560///
561/// A view over a [`HitTestResult`]: Dart's `SliverHitTestResult.wrap`. Dart's bare
562/// `SliverHitTestResult()` constructor is `wrap` over a fresh [`HitTestResult`].
563pub struct SliverHitTestResult<'a>(&'a mut HitTestResult);
564
565impl<'a> SliverHitTestResult<'a> {
566    /// Wraps `result` to create a [`SliverHitTestResult`] that shares its path.
567    ///
568    /// This is used by render objects that adapt between the sliver world and the non-sliver
569    /// world.
570    pub fn wrap(result: &'a mut HitTestResult) -> SliverHitTestResult<'a> {
571        SliverHitTestResult(result)
572    }
573
574    /// Transforms `main_axis_position` and `cross_axis_position` to the local coordinate system
575    /// of a child for hit-testing the child.
576    ///
577    /// The actual hit testing of the child needs to be implemented in the provided `hit_test`
578    /// callback, which is invoked with the transformed positions as arguments.
579    ///
580    /// For the transform `main_axis_offset` is subtracted from `main_axis_position` and
581    /// `cross_axis_offset` is subtracted from `cross_axis_position`.
582    ///
583    /// The `paint_offset` describes how the paint position of a point painted at the provided
584    /// `main_axis_position` and `cross_axis_position` would change after `main_axis_offset` and
585    /// `cross_axis_offset` have been applied. It is used to properly convert pointer events to
586    /// the local coordinate system of the event receiver.
587    ///
588    /// The `paint_offset` may be `None` if `main_axis_offset` and `cross_axis_offset` are both
589    /// zero.
590    ///
591    /// The function returns the return value of `hit_test`.
592    pub fn add_with_axis_offset(
593        &mut self,
594        paint_offset: Option<Offset>,
595        main_axis_offset: f64,
596        cross_axis_offset: f64,
597        main_axis_position: f64,
598        cross_axis_position: f64,
599        hit_test: impl FnOnce(&mut SliverHitTestResult<'_>, f64, f64) -> bool,
600    ) -> bool {
601        if let Some(paint_offset) = paint_offset {
602            self.0.push_offset(Offset::ZERO - paint_offset);
603        }
604        let is_hit = hit_test(
605            self,
606            main_axis_position - main_axis_offset,
607            cross_axis_position - cross_axis_offset,
608        );
609        if paint_offset.is_some() {
610            self.0.pop_transform();
611        }
612        is_hit
613    }
614}
615
616impl Deref for SliverHitTestResult<'_> {
617    type Target = HitTestResult;
618
619    fn deref(&self) -> &HitTestResult {
620        self.0
621    }
622}
623
624impl DerefMut for SliverHitTestResult<'_> {
625    fn deref_mut(&mut self) -> &mut HitTestResult {
626        self.0
627    }
628}
629
630/// A hit test entry used by [`RenderSliver`].
631///
632/// The coordinate system used by this hit test entry is relative to the [`AxisDirection`] of
633/// the target sliver.
634///
635/// Dart's `SliverHitTestEntry` subclasses `HitTestEntry`; here it is the entry's target,
636/// carrying the sliver and the two positions. It becomes a [`HitTestEntry`] with [`From`].
637#[derive(Clone, Copy, Debug)]
638pub struct SliverHitTestEntry {
639    target: AnyRenderSliver,
640    main_axis_position: f64,
641    cross_axis_position: f64,
642}
643
644impl SliverHitTestEntry {
645    /// Creates a sliver hit test entry.
646    pub fn new(
647        target: AnyRenderSliver,
648        main_axis_position: f64,
649        cross_axis_position: f64,
650    ) -> SliverHitTestEntry {
651        SliverHitTestEntry {
652            target,
653            main_axis_position,
654            cross_axis_position,
655        }
656    }
657
658    /// The [`RenderSliver`] that was hit.
659    pub fn target(&self) -> AnyRenderSliver {
660        self.target
661    }
662
663    /// The distance in the [`AxisDirection`] from the edge of the sliver's painted area (as
664    /// given by the [`SliverConstraints::scroll_offset`]) to the hit point.
665    ///
666    /// This can be an unusual direction, for example in the [`AxisDirection::Up`] case this is
667    /// a distance from the _bottom_ of the sliver's painted area.
668    pub fn main_axis_position(&self) -> f64 {
669        self.main_axis_position
670    }
671
672    /// The distance to the hit point in the axis opposite the [`SliverConstraints::axis`].
673    ///
674    /// If the cross axis is horizontal (i.e. the [`SliverConstraints::axis_direction`] is
675    /// either [`AxisDirection::Down`] or [`AxisDirection::Up`]), then this is a distance from
676    /// the left edge of the sliver. If the cross axis is vertical, then this is a distance
677    /// from the top edge of the sliver.
678    ///
679    /// This is always a distance from the left or top of the parent, never a distance from the
680    /// right or bottom.
681    pub fn cross_axis_position(&self) -> f64 {
682        self.cross_axis_position
683    }
684}
685
686impl HitTestTarget for SliverHitTestEntry {
687    fn retained_handle(&self) -> Option<HandleId> {
688        Some(self.target.id)
689    }
690
691    fn handle_event(&self, app: &mut App, event: &PointerEvent, _entry: &HitTestEntry) {
692        self.target.handle_event(app, event, self);
693    }
694}
695
696impl From<SliverHitTestEntry> for HitTestEntry {
697    fn from(entry: SliverHitTestEntry) -> HitTestEntry {
698        HitTestEntry::new(entry)
699    }
700}
701
702/// Parent data structure used by parents of slivers that position their children using layout
703/// offsets.
704///
705/// This data structure is optimized for fast layout. It is best used by parents that expect to
706/// have many children whose relative positions don't change even when the scroll offset does.
707#[derive(Clone, Copy, Debug, Default)]
708pub struct SliverLogicalParentData {
709    /// The position of the child relative to the zero scroll offset.
710    ///
711    /// The number of pixels from the zero scroll offset of the parent sliver (the line at
712    /// which its [`SliverConstraints::scroll_offset`] is zero) to the side of the child closest
713    /// to that offset. It can be `None` when it cannot be determined. The value will be set
714    /// after layout.
715    ///
716    /// In a typical list, this does not change as the parent is scrolled.
717    pub layout_offset: Option<f64>,
718}
719
720impl SliverLogicalParentData {
721    /// Creates parent data with no layout offset.
722    pub const fn new() -> SliverLogicalParentData {
723        SliverLogicalParentData {
724            layout_offset: None,
725        }
726    }
727}
728
729impl ParentData for SliverLogicalParentData {
730    fn provide(&self, id: TypeId) -> Option<&dyn Any> {
731        (id == TypeId::of::<SliverLogicalParentData>()).then_some(self)
732    }
733
734    fn provide_mut(&mut self, id: TypeId) -> Option<&mut dyn Any> {
735        (id == TypeId::of::<SliverLogicalParentData>()).then_some(self)
736    }
737}
738
739impl Display for SliverLogicalParentData {
740    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
741        match self.layout_offset {
742            None => f.write_str("layoutOffset=None"),
743            Some(offset) => write!(f, "layoutOffset={offset:.1}"),
744        }
745    }
746}
747
748/// Parent data for slivers that have multiple children and that position their children using
749/// layout offsets.
750#[derive(Debug)]
751pub struct SliverLogicalContainerParentData {
752    sliver_logical: SliverLogicalParentData,
753    container: ContainerParentData<AnyRenderSliver>,
754}
755
756impl SliverLogicalContainerParentData {
757    /// Creates parent data with no layout offset and no siblings.
758    pub const fn new() -> SliverLogicalContainerParentData {
759        SliverLogicalContainerParentData {
760            sliver_logical: SliverLogicalParentData::new(),
761            container: ContainerParentData::new(),
762        }
763    }
764
765    /// The [`SliverLogicalParentData`] half.
766    pub fn sliver_logical_parent_data(&self) -> &SliverLogicalParentData {
767        &self.sliver_logical
768    }
769
770    /// See [`sliver_logical_parent_data`](Self::sliver_logical_parent_data).
771    pub fn sliver_logical_parent_data_mut(&mut self) -> &mut SliverLogicalParentData {
772        &mut self.sliver_logical
773    }
774
775    /// See [`SliverLogicalParentData::layout_offset`].
776    pub fn layout_offset(&self) -> Option<f64> {
777        self.sliver_logical.layout_offset
778    }
779
780    /// Sets [`layout_offset`](Self::layout_offset).
781    pub fn set_layout_offset(&mut self, value: Option<f64>) {
782        self.sliver_logical.layout_offset = value;
783    }
784}
785
786impl Default for SliverLogicalContainerParentData {
787    fn default() -> SliverLogicalContainerParentData {
788        SliverLogicalContainerParentData::new()
789    }
790}
791
792impl ParentData for SliverLogicalContainerParentData {
793    fn detach(&mut self) {
794        ContainerParentDataMixin::detach(self);
795    }
796
797    fn provide(&self, id: TypeId) -> Option<&dyn Any> {
798        if id == TypeId::of::<SliverLogicalContainerParentData>() {
799            return Some(self);
800        }
801        self.sliver_logical.provide(id)
802    }
803
804    fn provide_mut(&mut self, id: TypeId) -> Option<&mut dyn Any> {
805        if id == TypeId::of::<SliverLogicalContainerParentData>() {
806            return Some(self);
807        }
808        self.sliver_logical.provide_mut(id)
809    }
810}
811
812impl ContainerParentDataMixin for SliverLogicalContainerParentData {
813    type ChildType = AnyRenderSliver;
814
815    fn container_parent_data(&self) -> &ContainerParentData<AnyRenderSliver> {
816        &self.container
817    }
818
819    fn container_parent_data_mut(&mut self) -> &mut ContainerParentData<AnyRenderSliver> {
820        &mut self.container
821    }
822}
823
824impl Display for SliverLogicalContainerParentData {
825    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
826        Display::fmt(&self.sliver_logical, f)
827    }
828}
829
830/// Parent data structure used by parents of slivers that position their children using absolute
831/// coordinates.
832///
833/// For example, used by [`crate::RenderViewport`].
834///
835/// This data structure is optimized for fast painting, at the cost of requiring additional work
836/// during layout when the children change their offsets. It is best used by parents that expect
837/// to have few children, especially if those children will themselves be very tall relative to
838/// the parent.
839#[derive(Clone, Copy, Debug)]
840pub struct SliverPhysicalParentData {
841    /// The position of the child relative to the parent.
842    ///
843    /// This is the distance from the top left visible corner of the parent to the top left
844    /// visible corner of the sliver.
845    pub paint_offset: Offset,
846
847    /// The cross axis flex factor to use for this sliver child.
848    ///
849    /// If used outside of a `SliverCrossAxisGroup` widget, this value has no meaning.
850    ///
851    /// If `None` or zero, the child is inflexible and determines its own size in the cross
852    /// axis. If non-zero, the amount of space the child can occupy in the cross axis is
853    /// determined by dividing the free space (after placing the inflexible children) according
854    /// to the flex factors of the flexible children.
855    pub cross_axis_flex: Option<i32>,
856}
857
858impl SliverPhysicalParentData {
859    /// Creates parent data with a zero paint offset.
860    pub const fn new() -> SliverPhysicalParentData {
861        SliverPhysicalParentData {
862            paint_offset: Offset::ZERO,
863            cross_axis_flex: None,
864        }
865    }
866
867    /// Apply the [`paint_offset`](Self::paint_offset) to the given `transform`.
868    ///
869    /// Used to implement [`RenderObject::apply_paint_transform`] by slivers that use
870    /// [`SliverPhysicalParentData`].
871    pub fn apply_paint_transform(&self, transform: &mut Matrix4) {
872        // Hit test logic relies on this always providing an invertible matrix.
873        translate(transform, self.paint_offset.dx(), self.paint_offset.dy());
874    }
875}
876
877impl Default for SliverPhysicalParentData {
878    fn default() -> SliverPhysicalParentData {
879        SliverPhysicalParentData::new()
880    }
881}
882
883impl ParentData for SliverPhysicalParentData {
884    fn provide(&self, id: TypeId) -> Option<&dyn Any> {
885        (id == TypeId::of::<SliverPhysicalParentData>()).then_some(self)
886    }
887
888    fn provide_mut(&mut self, id: TypeId) -> Option<&mut dyn Any> {
889        (id == TypeId::of::<SliverPhysicalParentData>()).then_some(self)
890    }
891}
892
893impl Display for SliverPhysicalParentData {
894    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
895        write!(f, "paintOffset={:?}", self.paint_offset)
896    }
897}
898
899/// Parent data for slivers that have multiple children and that position their children using
900/// absolute coordinates.
901#[derive(Debug)]
902pub struct SliverPhysicalContainerParentData {
903    sliver_physical: SliverPhysicalParentData,
904    container: ContainerParentData<AnyRenderSliver>,
905}
906
907impl SliverPhysicalContainerParentData {
908    /// Creates parent data with a zero paint offset and no siblings.
909    pub const fn new() -> SliverPhysicalContainerParentData {
910        SliverPhysicalContainerParentData {
911            sliver_physical: SliverPhysicalParentData::new(),
912            container: ContainerParentData::new(),
913        }
914    }
915
916    /// The [`SliverPhysicalParentData`] half.
917    pub fn sliver_physical_parent_data(&self) -> &SliverPhysicalParentData {
918        &self.sliver_physical
919    }
920
921    /// See [`sliver_physical_parent_data`](Self::sliver_physical_parent_data).
922    pub fn sliver_physical_parent_data_mut(&mut self) -> &mut SliverPhysicalParentData {
923        &mut self.sliver_physical
924    }
925
926    /// See [`SliverPhysicalParentData::paint_offset`].
927    pub fn paint_offset(&self) -> Offset {
928        self.sliver_physical.paint_offset
929    }
930
931    /// Sets [`paint_offset`](Self::paint_offset).
932    pub fn set_paint_offset(&mut self, value: Offset) {
933        self.sliver_physical.paint_offset = value;
934    }
935
936    /// See [`SliverPhysicalParentData::apply_paint_transform`].
937    pub fn apply_paint_transform(&self, transform: &mut Matrix4) {
938        self.sliver_physical.apply_paint_transform(transform);
939    }
940}
941
942impl Default for SliverPhysicalContainerParentData {
943    fn default() -> SliverPhysicalContainerParentData {
944        SliverPhysicalContainerParentData::new()
945    }
946}
947
948impl ParentData for SliverPhysicalContainerParentData {
949    fn detach(&mut self) {
950        ContainerParentDataMixin::detach(self);
951    }
952
953    fn provide(&self, id: TypeId) -> Option<&dyn Any> {
954        if id == TypeId::of::<SliverPhysicalContainerParentData>() {
955            return Some(self);
956        }
957        self.sliver_physical.provide(id)
958    }
959
960    fn provide_mut(&mut self, id: TypeId) -> Option<&mut dyn Any> {
961        if id == TypeId::of::<SliverPhysicalContainerParentData>() {
962            return Some(self);
963        }
964        self.sliver_physical.provide_mut(id)
965    }
966}
967
968impl ContainerParentDataMixin for SliverPhysicalContainerParentData {
969    type ChildType = AnyRenderSliver;
970
971    fn container_parent_data(&self) -> &ContainerParentData<AnyRenderSliver> {
972        &self.container
973    }
974
975    fn container_parent_data_mut(&mut self) -> &mut ContainerParentData<AnyRenderSliver> {
976        &mut self.container
977    }
978}
979
980impl Display for SliverPhysicalContainerParentData {
981    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
982        Display::fmt(&self.sliver_physical, f)
983    }
984}
985
986/// The body of [`RenderSliver::calculate_paint_offset`], reachable without a sliver.
987pub(crate) fn calculate_paint_offset(constraints: SliverConstraints, from: f64, to: f64) -> f64 {
988    debug_assert!(from <= to);
989    let a = constraints.scroll_offset;
990    let b = constraints.scroll_offset + constraints.remaining_paint_extent;
991    // the clamp on the next line is to avoid floating point rounding errors
992    clamp_double(
993        clamp_double(to, a, b) - clamp_double(from, a, b),
994        0.0,
995        constraints.remaining_paint_extent,
996    )
997}
998
999/// The body of [`RenderSliver::calculate_cache_offset`], reachable without a sliver.
1000pub(crate) fn calculate_cache_offset(constraints: SliverConstraints, from: f64, to: f64) -> f64 {
1001    debug_assert!(from <= to);
1002    let a = constraints.scroll_offset + constraints.cache_origin;
1003    let b = constraints.scroll_offset + constraints.remaining_cache_extent;
1004    // the clamp on the next line is to avoid floating point rounding errors
1005    clamp_double(
1006        clamp_double(to, a, b) - clamp_double(from, a, b),
1007        0.0,
1008        constraints.remaining_cache_extent,
1009    )
1010}
1011
1012/// Flutter's `RenderSliver` fields.
1013pub struct RenderSliverData {
1014    pub(crate) geometry: Option<SliverGeometry>,
1015    pub(crate) constraints: Option<SliverConstraints>,
1016}
1017
1018impl RenderSliverData {
1019    /// Unlaid-out sliver state.
1020    pub fn new() -> RenderSliverData {
1021        RenderSliverData {
1022            geometry: None,
1023            constraints: None,
1024        }
1025    }
1026}
1027
1028impl Default for RenderSliverData {
1029    fn default() -> RenderSliverData {
1030        RenderSliverData::new()
1031    }
1032}
1033
1034/// Implements [`RenderSliver`] field accessors for a `render_sliver` field.
1035#[macro_export]
1036macro_rules! render_sliver_accessors {
1037    () => {
1038        fn render_sliver_data(
1039            self: $crate::RenderHandle<Self>,
1040            app: &::inset_foundation::App,
1041        ) -> &$crate::RenderSliverData {
1042            &self.get(app).render_sliver
1043        }
1044        fn render_sliver_data_mut(
1045            self: $crate::RenderHandle<Self>,
1046            app: &mut ::inset_foundation::App,
1047        ) -> &mut $crate::RenderSliverData {
1048            &mut self.get_mut(app).render_sliver
1049        }
1050    };
1051}
1052
1053/// A render object using the sliver protocol.
1054///
1055/// Flutter's counterpart is `RenderSliver`. [`RenderObject::perform_layout`] is the leaf
1056/// override; [`AnyRenderSliver::layout`] is the framework wrapper. `RenderObject`'s tree
1057/// operations are provided here and forward to [`as_object`](Self::as_object).
1058pub trait RenderSliver: RenderObject {
1059    /// Mixin field access.
1060    fn render_sliver_data(self: RenderHandle<Self>, app: &App) -> &RenderSliverData;
1061
1062    /// See [`render_sliver_data`](Self::render_sliver_data).
1063    fn render_sliver_data_mut(self: RenderHandle<Self>, app: &mut App) -> &mut RenderSliverData;
1064
1065    /// The sliver constraints most recently supplied by the parent.
1066    fn constraints(self: RenderHandle<Self>, app: &App) -> SliverConstraints {
1067        self.render_sliver_data(app).constraints.unwrap_or_else(|| {
1068            panic!("A RenderObject does not have any constraints before it has been laid out.")
1069        })
1070    }
1071
1072    /// The amount of space this sliver occupies.
1073    fn geometry(self: RenderHandle<Self>, app: &App) -> SliverGeometry {
1074        self.as_sliver().geometry(app)
1075    }
1076
1077    /// Sets the geometry of this sliver. Call from [`RenderObject::perform_layout`].
1078    fn set_geometry(self: RenderHandle<Self>, app: &mut App, geometry: SliverGeometry) {
1079        self.as_sliver().set_geometry(app, geometry)
1080    }
1081
1082    /// An estimate of the bounds within which this render object will paint: the paint extent
1083    /// along the main axis by the cross axis extent.
1084    fn paint_bounds(self: RenderHandle<Self>, app: &App) -> Rect {
1085        let constraints = self.constraints(app);
1086        let geometry = self.geometry(app);
1087        match constraints.axis() {
1088            Axis::Horizontal => Rect::from_ltwh(
1089                0.0,
1090                0.0,
1091                geometry.paint_extent,
1092                constraints.cross_axis_extent,
1093            ),
1094            Axis::Vertical => Rect::from_ltwh(
1095                0.0,
1096                0.0,
1097                constraints.cross_axis_extent,
1098                geometry.paint_extent,
1099            ),
1100        }
1101    }
1102
1103    /// For a center sliver, the distance before the absolute zero scroll offset that this
1104    /// sliver can cover.
1105    ///
1106    /// For example, if an [`AxisDirection::Down`] viewport with a
1107    /// [`crate::RenderViewport::anchor`] of 0.5 has a single sliver with a height of 100.0 and
1108    /// its `center_offset_adjustment` returns 50.0, then the sliver will be centered in the
1109    /// viewport when the scroll offset is 0.0.
1110    ///
1111    /// The distance here is in the opposite direction of the viewport's axis direction, so
1112    /// values will typically be positive.
1113    fn center_offset_adjustment(self: RenderHandle<Self>, _app: &App) -> f64 {
1114        let _ = self;
1115        0.0
1116    }
1117
1118    /// Determines the set of render objects located at the given position.
1119    ///
1120    /// Returns true if the given point is contained in this render object or one of its
1121    /// descendants. Adds any render objects that contain the point to the given hit test
1122    /// result.
1123    ///
1124    /// The caller is responsible for providing the position in the local coordinate space of
1125    /// the callee. The callee is responsible for checking whether the given position is within
1126    /// its bounds.
1127    ///
1128    /// # Coordinates for `RenderSliver` objects
1129    ///
1130    /// The `main_axis_position` is the distance in the [`AxisDirection`] (after applying the
1131    /// [`GrowthDirection`]) from the edge of the sliver's painted area. This can be an unusual
1132    /// direction, for example in the [`AxisDirection::Up`] case this is a distance from the
1133    /// _bottom_ of the sliver's painted area.
1134    ///
1135    /// The `cross_axis_position` is the distance in the other axis. If the cross axis is
1136    /// horizontal (i.e. the [`SliverConstraints::axis_direction`] is either
1137    /// [`AxisDirection::Down`] or [`AxisDirection::Up`]), then the `cross_axis_position` is a
1138    /// distance from the left edge of the sliver. Otherwise it is a distance from the top edge.
1139    ///
1140    /// # Implementing hit testing for slivers
1141    ///
1142    /// The most straight-forward way to implement hit testing for a new sliver render object is
1143    /// to override its [`hit_test_self`](Self::hit_test_self) and
1144    /// [`hit_test_children`](Self::hit_test_children) methods.
1145    fn hit_test(
1146        self: RenderHandle<Self>,
1147        app: &mut App,
1148        result: &mut SliverHitTestResult<'_>,
1149        main_axis_position: f64,
1150        cross_axis_position: f64,
1151    ) -> bool {
1152        if main_axis_position >= 0.0
1153            && main_axis_position < self.geometry(app).hit_test_extent
1154            && cross_axis_position >= 0.0
1155            && cross_axis_position < self.constraints(app).cross_axis_extent
1156            && (self.hit_test_children(app, result, main_axis_position, cross_axis_position)
1157                || self.hit_test_self(app, main_axis_position, cross_axis_position))
1158        {
1159            result.add(
1160                SliverHitTestEntry::new(self.as_sliver(), main_axis_position, cross_axis_position)
1161                    .into(),
1162            );
1163            return true;
1164        }
1165        false
1166    }
1167
1168    /// Override this method if this render object can be hit even if its children were not hit.
1169    ///
1170    /// Used by [`hit_test`](Self::hit_test). If you override `hit_test` and do not call this
1171    /// function, then you don't need to implement this function.
1172    fn hit_test_self(
1173        self: RenderHandle<Self>,
1174        _app: &App,
1175        _main_axis_position: f64,
1176        _cross_axis_position: f64,
1177    ) -> bool {
1178        let _ = self;
1179        false
1180    }
1181
1182    /// Override this method to check whether any children are located at the given position.
1183    ///
1184    /// Typically children should be hit-tested in reverse paint order so that hit tests at
1185    /// locations where children overlap hit the child that is visually "on top" (i.e., paints
1186    /// later).
1187    ///
1188    /// Used by [`hit_test`](Self::hit_test). If you override `hit_test` and do not call this
1189    /// function, then you don't need to implement this function.
1190    fn hit_test_children(
1191        self: RenderHandle<Self>,
1192        _app: &mut App,
1193        _result: &mut SliverHitTestResult<'_>,
1194        _main_axis_position: f64,
1195        _cross_axis_position: f64,
1196    ) -> bool {
1197        let _ = self;
1198        false
1199    }
1200
1201    /// Override to handle pointer events that hit this render object.
1202    fn handle_event(
1203        self: RenderHandle<Self>,
1204        _app: &mut App,
1205        _event: &PointerEvent,
1206        _entry: &SliverHitTestEntry,
1207    ) {
1208        let _ = self;
1209    }
1210
1211    /// Computes the portion of the region from `from` to `to` that is visible, assuming that
1212    /// only the region from the [`SliverConstraints::scroll_offset`] that is
1213    /// [`SliverConstraints::remaining_paint_extent`] high is visible, and that the relationship
1214    /// between scroll offsets and paint offsets is linear.
1215    ///
1216    /// For example, if the constraints have a scroll offset of 100 and a remaining paint extent
1217    /// of 100, and the arguments to this method describe the region 50..150, then the returned
1218    /// value would be 50 (from scroll offset 100 to scroll offset 150).
1219    ///
1220    /// This method is not useful if there is not a 1:1 relationship between consumed scroll
1221    /// offset and consumed paint extent.
1222    fn calculate_paint_offset(
1223        self: RenderHandle<Self>,
1224        _app: &App,
1225        constraints: SliverConstraints,
1226        from: f64,
1227        to: f64,
1228    ) -> f64 {
1229        let _ = self;
1230        calculate_paint_offset(constraints, from, to)
1231    }
1232
1233    /// Computes the portion of the region from `from` to `to` that is within the cache extent
1234    /// of the viewport, assuming that only the region from the
1235    /// [`SliverConstraints::cache_origin`] that is
1236    /// [`SliverConstraints::remaining_cache_extent`] high is visible, and that the relationship
1237    /// between scroll offsets and paint offsets is linear.
1238    ///
1239    /// This method is not useful if there is not a 1:1 relationship between consumed scroll
1240    /// offset and consumed cache extent.
1241    fn calculate_cache_offset(
1242        self: RenderHandle<Self>,
1243        _app: &App,
1244        constraints: SliverConstraints,
1245        from: f64,
1246        to: f64,
1247    ) -> f64 {
1248        let _ = self;
1249        calculate_cache_offset(constraints, from, to)
1250    }
1251
1252    /// Returns the distance from the leading _visible_ edge of the sliver to the side of the
1253    /// given child closest to that edge.
1254    ///
1255    /// For example, if the [`constraints`](Self::constraints) describe this sliver as having an
1256    /// axis direction of [`AxisDirection::Down`], then this is the distance from the top of the
1257    /// visible portion of the sliver to the top of the child. On the other hand, if the axis
1258    /// direction is [`AxisDirection::Up`], then this is the distance from the bottom of the
1259    /// visible portion of the sliver to the bottom of the child. In both cases, this is the
1260    /// direction of increasing [`SliverConstraints::scroll_offset`] and
1261    /// [`SliverLogicalParentData::layout_offset`].
1262    ///
1263    /// For children that are slivers, the leading edge of the _child_ will be the leading
1264    /// _visible_ edge of the child, not the part of the child that would locally be at scroll
1265    /// offset 0.0. For box children it is the actual distance to the edge of the box, since
1266    /// those boxes do not know how to handle being scrolled.
1267    ///
1268    /// This method differs from [`child_scroll_offset`](Self::child_scroll_offset) in that it
1269    /// gives the distance from the leading _visible_ edge of the sliver whereas
1270    /// `child_scroll_offset` gives the distance from the sliver's zero scroll offset.
1271    ///
1272    /// Calling this for a child that is not visible is not valid.
1273    fn child_main_axis_position(
1274        self: RenderHandle<Self>,
1275        _app: &App,
1276        _child: AnyRenderObject,
1277    ) -> f64 {
1278        let _ = self;
1279        panic!("this RenderSliver does not implement child_main_axis_position")
1280    }
1281
1282    /// Returns the distance along the cross axis from the zero of the cross axis in this
1283    /// sliver's paint coordinate space to the nearest side of the given child.
1284    ///
1285    /// For example, if the [`constraints`](Self::constraints) describe this sliver as having an
1286    /// axis direction of [`AxisDirection::Down`] or [`AxisDirection::Up`], then this is the
1287    /// distance from the left of the sliver to the left of the child. If the axis direction is
1288    /// [`AxisDirection::Left`] or [`AxisDirection::Right`], then it is the distance from the
1289    /// top of the sliver to the top of the child.
1290    ///
1291    /// Calling this for a child that is not visible is not valid.
1292    fn child_cross_axis_position(
1293        self: RenderHandle<Self>,
1294        _app: &App,
1295        _child: AnyRenderObject,
1296    ) -> f64 {
1297        let _ = self;
1298        0.0
1299    }
1300
1301    /// Returns the scroll offset for the leading edge of the given child.
1302    ///
1303    /// The `child` must be a child of this sliver.
1304    ///
1305    /// If there are pinned slivers before `child`, the offset should be reduced by the extent
1306    /// of the pinned children.
1307    ///
1308    /// This method differs from [`child_main_axis_position`](Self::child_main_axis_position) in
1309    /// that `child_main_axis_position` gives the distance from the leading _visible_ edge of
1310    /// the sliver whereas this gives the distance from the sliver's zero scroll offset.
1311    fn child_scroll_offset(
1312        self: RenderHandle<Self>,
1313        app: &App,
1314        child: AnyRenderObject,
1315    ) -> Option<f64> {
1316        debug_assert_eq!(child.parent(app), Some(self.as_object()));
1317        Some(0.0)
1318    }
1319
1320    /// This returns a [`Size`] with dimensions relative to the leading edge of the sliver,
1321    /// specifically the same offset that is given to the [`RenderObject::paint`] method. This
1322    /// means that the dimensions may be negative.
1323    ///
1324    /// This is only valid after layout has completed.
1325    ///
1326    /// See also:
1327    ///
1328    ///  * [`get_absolute_size`](Self::get_absolute_size), which returns absolute size.
1329    fn get_absolute_size_relative_to_origin(self: RenderHandle<Self>, app: &App) -> Size {
1330        debug_assert!(!self.as_object().debug_needs_layout(app));
1331        let constraints = self.constraints(app);
1332        let paint_extent = self.geometry(app).paint_extent;
1333        match apply_growth_direction_to_axis_direction(
1334            constraints.axis_direction,
1335            constraints.growth_direction,
1336        ) {
1337            AxisDirection::Up => Size::new(constraints.cross_axis_extent, -paint_extent),
1338            AxisDirection::Down => Size::new(constraints.cross_axis_extent, paint_extent),
1339            AxisDirection::Left => Size::new(-paint_extent, constraints.cross_axis_extent),
1340            AxisDirection::Right => Size::new(paint_extent, constraints.cross_axis_extent),
1341        }
1342    }
1343
1344    /// This returns the absolute [`Size`] of the sliver.
1345    ///
1346    /// The dimensions are always positive and calling this is only valid after layout has
1347    /// completed.
1348    ///
1349    /// See also:
1350    ///
1351    ///  * [`get_absolute_size_relative_to_origin`](Self::get_absolute_size_relative_to_origin),
1352    ///    which returns the size relative to the leading edge of the sliver.
1353    fn get_absolute_size(self: RenderHandle<Self>, app: &App) -> Size {
1354        debug_assert!(!self.as_object().debug_needs_layout(app));
1355        let constraints = self.constraints(app);
1356        let paint_extent = self.geometry(app).paint_extent;
1357        match constraints.axis_direction {
1358            AxisDirection::Up | AxisDirection::Down => {
1359                Size::new(constraints.cross_axis_extent, paint_extent)
1360            }
1361            AxisDirection::Right | AxisDirection::Left => {
1362                Size::new(paint_extent, constraints.cross_axis_extent)
1363            }
1364        }
1365    }
1366
1367    /// Returns the [`Rect`] that covers the total paint extent of the sliver.
1368    ///
1369    /// The rect is expressed in the sliver's local coordinate system, which is axis-aligned
1370    /// with the painting context's canvas. The coordinate system's origin (0,0) corresponds to
1371    /// the `offset` argument passed to [`RenderObject::paint`].
1372    fn get_max_paint_rect(self: RenderHandle<Self>, app: &App) -> Rect {
1373        let Some(geometry) = self.render_sliver_data(app).geometry else {
1374            return Rect::ZERO;
1375        };
1376        if geometry.is_zero() {
1377            return Rect::ZERO;
1378        }
1379        let constraints = self.constraints(app);
1380        let mut max_paint_extent = geometry.max_paint_extent;
1381        if max_paint_extent.is_infinite() {
1382            max_paint_extent =
1383                constraints.scroll_offset + geometry.cache_extent + constraints.cache_origin;
1384        }
1385        let paint_extent = geometry.paint_extent;
1386        // To ensure the computed rect remains visible when pinned, the leading offset is capped
1387        // at the sliver's `scroll_extent - max_scroll_obstruction_extent`.
1388        let leading_offset = clamp_double(
1389            constraints.scroll_offset,
1390            0.0,
1391            geometry.scroll_extent - geometry.max_scroll_obstruction_extent,
1392        );
1393        let cross_axis_extent = geometry
1394            .cross_axis_extent
1395            .unwrap_or(constraints.cross_axis_extent);
1396
1397        let rect = match constraints.axis() {
1398            Axis::Horizontal => {
1399                Rect::from_ltwh(-leading_offset, 0.0, max_paint_extent, cross_axis_extent)
1400            }
1401            Axis::Vertical => {
1402                Rect::from_ltwh(0.0, -leading_offset, cross_axis_extent, max_paint_extent)
1403            }
1404        };
1405
1406        match apply_growth_direction_to_axis_direction(
1407            constraints.axis_direction,
1408            constraints.growth_direction,
1409        ) {
1410            AxisDirection::Right | AxisDirection::Down => rect,
1411            AxisDirection::Left => Rect::from_ltrb(
1412                paint_extent - rect.right,
1413                rect.top,
1414                paint_extent - rect.left,
1415                rect.bottom,
1416            ),
1417            AxisDirection::Up => Rect::from_ltrb(
1418                rect.left,
1419                paint_extent - rect.bottom,
1420                rect.right,
1421                paint_extent - rect.top,
1422            ),
1423        }
1424    }
1425
1426    /// See [`AnyRenderSliver::layout`].
1427    fn layout(
1428        self: RenderHandle<Self>,
1429        app: &mut App,
1430        constraints: SliverConstraints,
1431        parent_uses_size: bool,
1432    ) {
1433        self.as_sliver().layout(app, constraints, parent_uses_size)
1434    }
1435
1436    /// The type-erased `RenderSliver` handle. Free: the vtable is a `const`, and the id is copied.
1437    fn as_sliver(self: RenderHandle<Self>) -> AnyRenderSliver {
1438        AnyRenderSliver {
1439            id: self.id(),
1440            vtable: const { &RenderSliverVTable::of::<Self>() },
1441        }
1442    }
1443
1444    /// The type-erased `RenderObject` handle, through [`as_sliver`](Self::as_sliver).
1445    fn as_object(self: RenderHandle<Self>) -> AnyRenderObject {
1446        self.as_sliver().as_object()
1447    }
1448
1449    /// See [`AnyRenderObject::adopt_child`].
1450    fn adopt_child(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
1451        self.as_object().adopt_child(app, child)
1452    }
1453
1454    /// See [`AnyRenderObject::drop_child`].
1455    fn drop_child(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
1456        self.as_object().drop_child(app, child)
1457    }
1458
1459    /// See [`AnyRenderObject::mark_needs_layout`].
1460    fn mark_needs_layout(self: RenderHandle<Self>, app: &mut App) {
1461        self.as_object().mark_needs_layout(app)
1462    }
1463
1464    /// See [`AnyRenderObject::mark_needs_paint`].
1465    fn mark_needs_paint(self: RenderHandle<Self>, app: &mut App) {
1466        self.as_object().mark_needs_paint(app)
1467    }
1468
1469    /// See [`AnyRenderObject::mark_needs_composited_layer_update`].
1470    fn mark_needs_composited_layer_update(self: RenderHandle<Self>, app: &mut App) {
1471        self.as_object().mark_needs_composited_layer_update(app)
1472    }
1473
1474    /// See [`AnyRenderObject::schedule_initial_layout`].
1475    fn schedule_initial_layout(self: RenderHandle<Self>, app: &mut App) {
1476        self.as_object().schedule_initial_layout(app)
1477    }
1478
1479    /// See [`AnyRenderObject::parent`].
1480    fn parent(self: RenderHandle<Self>, app: &App) -> Option<AnyRenderObject> {
1481        self.as_object().parent(app)
1482    }
1483
1484    /// See [`AnyRenderObject::owner`].
1485    fn owner(self: RenderHandle<Self>, app: &App) -> Option<Handle<PipelineOwner>> {
1486        self.as_object().owner(app)
1487    }
1488
1489    /// See [`AnyRenderObject::attached`].
1490    fn attached(self: RenderHandle<Self>, app: &App) -> bool {
1491        self.as_object().attached(app)
1492    }
1493
1494    /// See [`AnyRenderObject::debug_needs_layout`].
1495    fn debug_needs_layout(self: RenderHandle<Self>, app: &App) -> bool {
1496        self.as_object().debug_needs_layout(app)
1497    }
1498}
1499
1500/// The vtable of an [`AnyRenderSliver`]: the object vtable plus the sliver accessors.
1501pub(crate) struct RenderSliverVTable {
1502    pub object: RenderObjectVTable,
1503    pub sliver_data: fn(&App, HandleId) -> &RenderSliverData,
1504    pub sliver_data_mut: fn(&mut App, HandleId) -> &mut RenderSliverData,
1505    pub hit_test: fn(&mut App, HandleId, &mut SliverHitTestResult<'_>, f64, f64) -> bool,
1506    pub handle_event: fn(&mut App, HandleId, &PointerEvent, &SliverHitTestEntry),
1507    pub center_offset_adjustment: fn(&App, HandleId) -> f64,
1508    pub child_scroll_offset: fn(&App, HandleId, AnyRenderObject) -> Option<f64>,
1509    pub child_main_axis_position: fn(&App, HandleId, AnyRenderObject) -> f64,
1510}
1511
1512impl RenderSliverVTable {
1513    const fn of<T: RenderSliver>() -> RenderSliverVTable {
1514        RenderSliverVTable {
1515            object: RenderObjectVTable::of::<T>(
1516                |app, id, child| <T as RenderObject>::setup_parent_data(resolve(id), app, child),
1517                |app, id| T::paint_bounds(resolve(id), app),
1518                |app, id, child, transform| {
1519                    <T as RenderObject>::apply_paint_transform(resolve(id), app, child, transform)
1520                },
1521                |app, id| <T as RenderObject>::perform_resize(resolve(id), app),
1522                |app, id| crate::object::RenderObjectBase::mark_needs_layout(resolve::<T>(id), app),
1523                None,
1524                Some(|| const { &RenderSliverVTable::of::<T>() }),
1525            ),
1526            sliver_data: |app, id| T::render_sliver_data(resolve(id), app),
1527            sliver_data_mut: |app, id| T::render_sliver_data_mut(resolve(id), app),
1528            hit_test: |app, id, result, main, cross| {
1529                T::hit_test(resolve(id), app, result, main, cross)
1530            },
1531            handle_event: |app, id, event, entry| T::handle_event(resolve(id), app, event, entry),
1532            center_offset_adjustment: |app, id| T::center_offset_adjustment(resolve(id), app),
1533            child_scroll_offset: |app, id, child| T::child_scroll_offset(resolve(id), app, child),
1534            child_main_axis_position: |app, id, child| {
1535                T::child_main_axis_position(resolve(id), app, child)
1536            },
1537        }
1538    }
1539}
1540
1541impl<T: RenderSliver> RenderHandle<T> {
1542    /// Creates a sliver-protocol render object in `app`.
1543    pub fn new_sliver(app: &mut App, object: T) -> RenderHandle<T> {
1544        let this = create(app, object);
1545        let data = this.render_object_data_mut(app);
1546        data.object_vtable = Some(&const { RenderSliverVTable::of::<T>() }.object);
1547        // Flutter's `RenderObject()` constructor:
1548        // `_needsCompositing = isRepaintBoundary || alwaysNeedsCompositing` and
1549        // `_wasRepaintBoundary = isRepaintBoundary`.
1550        let is_repaint_boundary = this.is_repaint_boundary(app);
1551        let always_needs_compositing = this.always_needs_compositing(app);
1552        let data = this.render_object_data_mut(app);
1553        data.was_repaint_boundary = is_repaint_boundary;
1554        data.needs_compositing = is_repaint_boundary || always_needs_compositing;
1555        this
1556    }
1557}
1558
1559/// Erased `RenderSliver`.
1560#[derive(Clone, Copy)]
1561pub struct AnyRenderSliver {
1562    id: HandleId,
1563    vtable: &'static RenderSliverVTable,
1564}
1565
1566impl PartialEq for AnyRenderSliver {
1567    fn eq(&self, other: &AnyRenderSliver) -> bool {
1568        self.id == other.id
1569    }
1570}
1571
1572impl Eq for AnyRenderSliver {}
1573
1574impl std::hash::Hash for AnyRenderSliver {
1575    fn hash<H: Hasher>(&self, state: &mut H) {
1576        self.id.hash(state);
1577    }
1578}
1579
1580impl Debug for AnyRenderSliver {
1581    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1582        write!(f, "AnyRenderSliver({:?})", self.id)
1583    }
1584}
1585
1586impl AnyRenderSliver {
1587    pub(crate) fn from_vtable(
1588        id: HandleId,
1589        vtable: &'static RenderSliverVTable,
1590    ) -> AnyRenderSliver {
1591        AnyRenderSliver { id, vtable }
1592    }
1593
1594    /// The `RenderObject` view of this sliver. Free: points into the nested table.
1595    pub fn as_object(self) -> AnyRenderObject {
1596        AnyRenderObject::from_vtable(self.id, &self.vtable.object)
1597    }
1598
1599    fn sliver_data(self, app: &App) -> &RenderSliverData {
1600        (self.vtable.sliver_data)(app, self.id)
1601    }
1602
1603    fn sliver_data_mut(self, app: &mut App) -> &mut RenderSliverData {
1604        (self.vtable.sliver_data_mut)(app, self.id)
1605    }
1606
1607    /// Compute the layout for this sliver.
1608    pub fn layout(self, app: &mut App, constraints: SliverConstraints, parent_uses_size: bool) {
1609        debug_assert!(constraints.debug_assert_is_valid(true));
1610        let same = self.sliver_data(app).constraints == Some(constraints);
1611        self.as_object()
1612            .run_layout(app, parent_uses_size, constraints.is_tight(), same, |app| {
1613                self.sliver_data_mut(app).constraints = Some(constraints)
1614            });
1615    }
1616
1617    /// The sliver constraints most recently supplied by the parent.
1618    ///
1619    /// # Panics
1620    ///
1621    /// If this sliver has not been laid out.
1622    pub fn constraints(self, app: &App) -> SliverConstraints {
1623        self.sliver_data(app).constraints.unwrap_or_else(|| {
1624            panic!("A RenderObject does not have any constraints before it has been laid out.")
1625        })
1626    }
1627
1628    /// See [`RenderSliver::hit_test`].
1629    pub fn hit_test(
1630        self,
1631        app: &mut App,
1632        result: &mut SliverHitTestResult<'_>,
1633        main_axis_position: f64,
1634        cross_axis_position: f64,
1635    ) -> bool {
1636        (self.vtable.hit_test)(
1637            app,
1638            self.id,
1639            result,
1640            main_axis_position,
1641            cross_axis_position,
1642        )
1643    }
1644
1645    /// See [`RenderSliver::handle_event`].
1646    pub fn handle_event(self, app: &mut App, event: &PointerEvent, entry: &SliverHitTestEntry) {
1647        (self.vtable.handle_event)(app, self.id, event, entry)
1648    }
1649
1650    /// See [`RenderSliver::center_offset_adjustment`].
1651    pub fn center_offset_adjustment(self, app: &App) -> f64 {
1652        (self.vtable.center_offset_adjustment)(app, self.id)
1653    }
1654
1655    /// See [`RenderSliver::child_scroll_offset`].
1656    pub fn child_scroll_offset(self, app: &App, child: AnyRenderObject) -> Option<f64> {
1657        (self.vtable.child_scroll_offset)(app, self.id, child)
1658    }
1659
1660    /// See [`RenderSliver::child_main_axis_position`].
1661    pub fn child_main_axis_position(self, app: &App, child: AnyRenderObject) -> f64 {
1662        (self.vtable.child_main_axis_position)(app, self.id, child)
1663    }
1664
1665    /// The amount of space this sliver occupies.
1666    ///
1667    /// # Panics
1668    ///
1669    /// If this sliver has not been laid out.
1670    pub fn geometry(self, app: &App) -> SliverGeometry {
1671        self.sliver_data(app)
1672            .geometry
1673            .unwrap_or_else(|| panic!("RenderSliver was not laid out: {self:?}"))
1674    }
1675
1676    /// Sets the geometry of this sliver.
1677    pub fn set_geometry(self, app: &mut App, geometry: SliverGeometry) {
1678        self.sliver_data_mut(app).geometry = Some(geometry);
1679    }
1680}
1681
1682impl crate::object::ErasedRenderObject for AnyRenderSliver {
1683    fn as_object(self) -> AnyRenderObject {
1684        AnyRenderSliver::as_object(self)
1685    }
1686
1687    fn from_object(object: AnyRenderObject) -> AnyRenderSliver {
1688        object
1689            .as_sliver()
1690            .expect("the render object is not a RenderSliver")
1691    }
1692}
1693
1694impl From<AnyRenderSliver> for AnyRenderObject {
1695    fn from(sliver: AnyRenderSliver) -> AnyRenderObject {
1696        sliver.as_object()
1697    }
1698}
1699
1700/// Whether the sliver's growth direction and axis direction agree, so that a box child is
1701/// positioned from the sliver's leading edge.
1702///
1703/// Flutter's private `RenderSliverHelpers._getRightWayUp`.
1704fn get_right_way_up(constraints: SliverConstraints) -> bool {
1705    let reversed = axis_direction_is_reversed(constraints.axis_direction);
1706    match constraints.growth_direction {
1707        GrowthDirection::Forward => !reversed,
1708        GrowthDirection::Reverse => reversed,
1709    }
1710}
1711
1712/// Mixin for [`RenderSliver`] subclasses that provides some utility functions.
1713pub trait RenderSliverHelpers: RenderSliver {
1714    /// Utility function for [`RenderSliver::hit_test_children`] for use when the children are
1715    /// [`crate::RenderBox`] widgets.
1716    ///
1717    /// This function takes care of converting the position from the sliver coordinate system to
1718    /// the Cartesian coordinate system used by the box protocol.
1719    ///
1720    /// This function relies on [`RenderSliver::child_main_axis_position`] to determine the
1721    /// position of the child in question.
1722    ///
1723    /// Calling this for a child that is not visible is not valid.
1724    fn hit_test_box_child(
1725        self: RenderHandle<Self>,
1726        app: &mut App,
1727        result: &mut BoxHitTestResult<'_>,
1728        child: AnyRenderBox,
1729        main_axis_position: f64,
1730        cross_axis_position: f64,
1731    ) -> bool {
1732        let constraints = self.constraints(app);
1733        let right_way_up = get_right_way_up(constraints);
1734        let mut delta = self.child_main_axis_position(app, child.as_object());
1735        let cross_axis_delta = self.child_cross_axis_position(app, child.as_object());
1736        let mut absolute_position = main_axis_position - delta;
1737        let absolute_cross_axis_position = cross_axis_position - cross_axis_delta;
1738        let size = child.size(app);
1739        let paint_extent = self.geometry(app).paint_extent;
1740        let (paint_offset, transformed_position) = match constraints.axis() {
1741            Axis::Horizontal => {
1742                if !right_way_up {
1743                    absolute_position = size.width() - absolute_position;
1744                    delta = paint_extent - size.width() - delta;
1745                }
1746                (
1747                    Offset::new(delta, cross_axis_delta),
1748                    Offset::new(absolute_position, absolute_cross_axis_position),
1749                )
1750            }
1751            Axis::Vertical => {
1752                if !right_way_up {
1753                    absolute_position = size.height() - absolute_position;
1754                    delta = paint_extent - size.height() - delta;
1755                }
1756                (
1757                    Offset::new(cross_axis_delta, delta),
1758                    Offset::new(absolute_cross_axis_position, absolute_position),
1759                )
1760            }
1761        };
1762        result.add_with_out_of_band_position(Some(paint_offset), None, None, |result| {
1763            child.hit_test(app, result, transformed_position)
1764        })
1765    }
1766
1767    /// Utility function for [`RenderObject::apply_paint_transform`] for use when the children
1768    /// are [`crate::RenderBox`] widgets.
1769    ///
1770    /// This function turns the value returned by [`RenderSliver::child_main_axis_position`] and
1771    /// [`RenderSliver::child_cross_axis_position`] for the child in question into a translation
1772    /// that it then applies to the given matrix.
1773    ///
1774    /// Calling this for a child that is not visible is not valid.
1775    fn apply_paint_transform_for_box_child(
1776        self: RenderHandle<Self>,
1777        app: &App,
1778        child: AnyRenderBox,
1779        transform: &mut Matrix4,
1780    ) {
1781        let constraints = self.constraints(app);
1782        let right_way_up = get_right_way_up(constraints);
1783        let mut delta = self.child_main_axis_position(app, child.as_object());
1784        let cross_axis_delta = self.child_cross_axis_position(app, child.as_object());
1785        let size = child.size(app);
1786        let paint_extent = self.geometry(app).paint_extent;
1787        match constraints.axis() {
1788            Axis::Horizontal => {
1789                if !right_way_up {
1790                    delta = paint_extent - size.width() - delta;
1791                }
1792                translate(transform, delta, cross_axis_delta);
1793            }
1794            Axis::Vertical => {
1795                if !right_way_up {
1796                    delta = paint_extent - size.height() - delta;
1797                }
1798                translate(transform, cross_axis_delta, delta);
1799            }
1800        }
1801    }
1802}
1803
1804// ADAPTER FOR RENDER BOXES INSIDE SLIVERS
1805// Transitions from the RenderSliver world to the RenderBox world.
1806
1807/// A [`RenderSliver`] that contains a single [`crate::RenderBox`].
1808///
1809/// See also:
1810///
1811///  * [`RenderSliverToBoxAdapter`], which implements this trait to size the child according to
1812///    its preferred size.
1813pub trait RenderSliverSingleBoxAdapter:
1814    RenderSliver + RenderObjectWithChildMixin<ChildType = AnyRenderBox> + RenderSliverHelpers
1815{
1816    /// The body of Flutter's `setupParentData` override; call it from
1817    /// [`RenderObject::setup_parent_data`].
1818    fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
1819        if !child.parent_data_is::<SliverPhysicalParentData>(app) {
1820            child.set_parent_data(app, SliverPhysicalParentData::new());
1821        }
1822    }
1823
1824    /// Sets the [`SliverPhysicalParentData::paint_offset`] for the given child according to the
1825    /// [`SliverConstraints::axis_direction`] and [`SliverConstraints::growth_direction`] and
1826    /// the given geometry.
1827    fn set_child_parent_data(
1828        self: RenderHandle<Self>,
1829        app: &mut App,
1830        child: AnyRenderObject,
1831        constraints: SliverConstraints,
1832        geometry: SliverGeometry,
1833    ) {
1834        let paint_offset = match apply_growth_direction_to_axis_direction(
1835            constraints.axis_direction,
1836            constraints.growth_direction,
1837        ) {
1838            AxisDirection::Up => Offset::new(
1839                0.0,
1840                geometry.paint_extent + constraints.scroll_offset - geometry.scroll_extent,
1841            ),
1842            AxisDirection::Left => Offset::new(
1843                geometry.paint_extent + constraints.scroll_offset - geometry.scroll_extent,
1844                0.0,
1845            ),
1846            AxisDirection::Right => Offset::new(-constraints.scroll_offset, 0.0),
1847            AxisDirection::Down => Offset::new(0.0, -constraints.scroll_offset),
1848        };
1849        child
1850            .parent_data_of_mut::<SliverPhysicalParentData>(app)
1851            .paint_offset = paint_offset;
1852    }
1853
1854    /// The body of Flutter's `hitTestChildren` override.
1855    fn hit_test_children(
1856        self: RenderHandle<Self>,
1857        app: &mut App,
1858        result: &mut SliverHitTestResult<'_>,
1859        main_axis_position: f64,
1860        cross_axis_position: f64,
1861    ) -> bool {
1862        debug_assert!(self.geometry(app).hit_test_extent > 0.0);
1863        let Some(child) = self.child(app) else {
1864            return false;
1865        };
1866        self.hit_test_box_child(
1867            app,
1868            &mut BoxHitTestResult::wrap(result),
1869            child,
1870            main_axis_position,
1871            cross_axis_position,
1872        )
1873    }
1874
1875    /// The body of Flutter's `childMainAxisPosition` override.
1876    fn child_main_axis_position(
1877        self: RenderHandle<Self>,
1878        app: &App,
1879        child: AnyRenderObject,
1880    ) -> f64 {
1881        debug_assert_eq!(Some(child), self.child(app).map(AnyRenderBox::as_object));
1882        -self.constraints(app).scroll_offset
1883    }
1884
1885    /// The body of Flutter's `applyPaintTransform` override.
1886    fn apply_paint_transform(
1887        self: RenderHandle<Self>,
1888        app: &App,
1889        child: AnyRenderObject,
1890        transform: &mut Matrix4,
1891    ) {
1892        debug_assert_eq!(Some(child), self.child(app).map(AnyRenderBox::as_object));
1893        child
1894            .parent_data_of::<SliverPhysicalParentData>(app)
1895            .apply_paint_transform(transform);
1896    }
1897
1898    /// The body of Flutter's `paint` override.
1899    fn paint(
1900        self: RenderHandle<Self>,
1901        app: &mut App,
1902        context: &mut PaintingContext,
1903        offset: Offset,
1904    ) {
1905        let Some(child) = self.child(app) else {
1906            return;
1907        };
1908        if !self.geometry(app).visible {
1909            return;
1910        }
1911        let paint_offset = child
1912            .as_object()
1913            .parent_data_of::<SliverPhysicalParentData>(app)
1914            .paint_offset;
1915        context.paint_child(app, child.as_object(), offset + paint_offset);
1916    }
1917}
1918
1919/// A [`RenderSliver`] that contains a single [`crate::RenderBox`].
1920///
1921/// The child will not be laid out if it is not visible. It is sized according to the child's
1922/// preferences in the main axis, and with a tight constraint forcing it to the dimensions of
1923/// the viewport in the cross axis.
1924///
1925/// See also:
1926///
1927///  * [`crate::RenderViewport`], which allows [`RenderSliver`] objects to be placed inside a
1928///    [`crate::RenderBox`] (the opposite of this class).
1929pub struct RenderSliverToBoxAdapter {
1930    render_object: crate::object::RenderObjectData,
1931    render_sliver: RenderSliverData,
1932    child: RenderObjectWithChildData<AnyRenderBox>,
1933}
1934
1935impl RenderSliverToBoxAdapter {
1936    /// Creates a [`RenderSliver`] that wraps a [`crate::RenderBox`].
1937    pub fn new(
1938        app: &mut App,
1939        child: Option<AnyRenderBox>,
1940    ) -> RenderHandle<RenderSliverToBoxAdapter> {
1941        let this = RenderHandle::new_sliver(
1942            app,
1943            RenderSliverToBoxAdapter {
1944                render_object: crate::object::RenderObjectData::new(),
1945                render_sliver: RenderSliverData::new(),
1946                child: RenderObjectWithChildData::new(),
1947            },
1948        );
1949        this.set_child(app, child);
1950        this
1951    }
1952}
1953
1954impl RenderObjectWithChildMixin for RenderSliverToBoxAdapter {
1955    type ChildType = AnyRenderBox;
1956
1957    fn child_data(self: RenderHandle<Self>, app: &App) -> &RenderObjectWithChildData<AnyRenderBox> {
1958        &self.get(app).child
1959    }
1960
1961    fn child_data_mut(
1962        self: RenderHandle<Self>,
1963        app: &mut App,
1964    ) -> &mut RenderObjectWithChildData<AnyRenderBox> {
1965        &mut self.get_mut(app).child
1966    }
1967}
1968
1969impl RenderSliverHelpers for RenderSliverToBoxAdapter {}
1970
1971impl RenderSliverSingleBoxAdapter for RenderSliverToBoxAdapter {}
1972
1973impl RenderObject for RenderSliverToBoxAdapter {
1974    crate::render_object_accessors!();
1975
1976    fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
1977        RenderSliverSingleBoxAdapter::setup_parent_data(self, app, child)
1978    }
1979
1980    fn visit_children(
1981        self: RenderHandle<Self>,
1982        app: &App,
1983        visitor: &mut dyn FnMut(AnyRenderObject),
1984    ) {
1985        if let Some(child) = self.child(app) {
1986            visitor(child.as_object());
1987        }
1988    }
1989
1990    fn apply_paint_transform(
1991        self: RenderHandle<Self>,
1992        app: &App,
1993        child: AnyRenderObject,
1994        transform: &mut Matrix4,
1995    ) {
1996        RenderSliverSingleBoxAdapter::apply_paint_transform(self, app, child, transform)
1997    }
1998
1999    fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
2000        let Some(child) = self.child(app) else {
2001            self.set_geometry(app, SliverGeometry::ZERO);
2002            return;
2003        };
2004        let constraints = self.constraints(app);
2005        child.layout(
2006            app,
2007            constraints.as_box_constraints(0.0, f64::INFINITY, None),
2008            true,
2009        );
2010        let child_extent = match constraints.axis() {
2011            Axis::Horizontal => child.size(app).width(),
2012            Axis::Vertical => child.size(app).height(),
2013        };
2014        let painted_child_size = calculate_paint_offset(constraints, 0.0, child_extent);
2015        let cache_extent = calculate_cache_offset(constraints, 0.0, child_extent);
2016
2017        debug_assert!(painted_child_size.is_finite());
2018        debug_assert!(painted_child_size >= 0.0);
2019        let geometry = SliverGeometry::new()
2020            .scroll_extent(child_extent)
2021            .paint_extent(painted_child_size)
2022            .cache_extent(cache_extent)
2023            .max_paint_extent(child_extent)
2024            .hit_test_extent(painted_child_size)
2025            .has_visual_overflow(
2026                child_extent > constraints.remaining_paint_extent
2027                    || constraints.scroll_offset > 0.0,
2028            );
2029        self.set_geometry(app, geometry);
2030        self.set_child_parent_data(app, child.as_object(), constraints, geometry);
2031    }
2032
2033    fn paint(
2034        self: RenderHandle<Self>,
2035        app: &mut App,
2036        context: &mut PaintingContext,
2037        offset: Offset,
2038    ) {
2039        RenderSliverSingleBoxAdapter::paint(self, app, context, offset)
2040    }
2041}
2042
2043impl RenderSliver for RenderSliverToBoxAdapter {
2044    render_sliver_accessors!();
2045
2046    fn hit_test_children(
2047        self: RenderHandle<Self>,
2048        app: &mut App,
2049        result: &mut SliverHitTestResult<'_>,
2050        main_axis_position: f64,
2051        cross_axis_position: f64,
2052    ) -> bool {
2053        RenderSliverSingleBoxAdapter::hit_test_children(
2054            self,
2055            app,
2056            result,
2057            main_axis_position,
2058            cross_axis_position,
2059        )
2060    }
2061
2062    fn child_main_axis_position(
2063        self: RenderHandle<Self>,
2064        app: &App,
2065        child: AnyRenderObject,
2066    ) -> f64 {
2067        RenderSliverSingleBoxAdapter::child_main_axis_position(self, app, child)
2068    }
2069}
2070
2071#[cfg(test)]
2072mod tests {
2073    use inset_foundation::AppCell;
2074    use inset_gestures::HitTestResult;
2075
2076    use super::*;
2077    use crate::box_::{RenderBox, RenderBoxData};
2078    use crate::proxy_box::RenderConstrainedBox;
2079
2080    struct TestSliver {
2081        render_object: crate::object::RenderObjectData,
2082        render_sliver: RenderSliverData,
2083    }
2084
2085    impl RenderObject for TestSliver {
2086        crate::render_object_accessors!();
2087
2088        fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
2089            let constraints = self.constraints(app);
2090            let extent = constraints.remaining_paint_extent.min(20.0);
2091            self.set_geometry(
2092                app,
2093                SliverGeometry::new()
2094                    .scroll_extent(20.0)
2095                    .paint_extent(extent)
2096                    .max_paint_extent(20.0),
2097            );
2098        }
2099    }
2100
2101    impl RenderSliver for TestSliver {
2102        render_sliver_accessors!();
2103    }
2104
2105    fn vertical_constraints(remaining_paint: f64) -> SliverConstraints {
2106        SliverConstraints::new(
2107            AxisDirection::Down,
2108            GrowthDirection::Forward,
2109            ScrollDirection::Idle,
2110            0.0,
2111            0.0,
2112            0.0,
2113            remaining_paint,
2114            100.0,
2115            AxisDirection::Right,
2116            400.0,
2117            remaining_paint,
2118            0.0,
2119        )
2120    }
2121
2122    #[test]
2123    fn sliver_layout_sets_geometry() {
2124        let cell = AppCell::new();
2125        let mut app = cell.borrow_mut();
2126        let sliver = RenderHandle::new_sliver(
2127            &mut app,
2128            TestSliver {
2129                render_object: crate::object::RenderObjectData::new(),
2130                render_sliver: RenderSliverData::new(),
2131            },
2132        );
2133        sliver.layout(&mut app, vertical_constraints(50.0), false);
2134        let geometry = sliver.geometry(&app);
2135        assert_eq!(geometry.scroll_extent, 20.0);
2136        assert_eq!(geometry.paint_extent, 20.0);
2137        assert!(geometry.visible);
2138    }
2139
2140    /// A box that fills the constraints it is given and accepts hits, so that a hit test walks
2141    /// all the way through the sliver adapter.
2142    struct HitTestableBox {
2143        render_object: crate::object::RenderObjectData,
2144        render_box: RenderBoxData,
2145    }
2146
2147    impl RenderObject for HitTestableBox {
2148        crate::render_object_accessors!();
2149
2150        fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
2151            let size = self.constraints(app).constrain(Size::new(60.0, 60.0));
2152            self.set_size(app, size);
2153        }
2154    }
2155
2156    impl RenderBox for HitTestableBox {
2157        crate::render_box_accessors!();
2158
2159        fn hit_test_self(self: RenderHandle<Self>, _app: &App, _position: Offset) -> bool {
2160            let _ = self;
2161            true
2162        }
2163    }
2164
2165    fn hit_testable_box(app: &mut App) -> AnyRenderBox {
2166        RenderHandle::new_box(
2167            app,
2168            HitTestableBox {
2169                render_object: crate::object::RenderObjectData::new(),
2170                render_box: RenderBoxData::new(),
2171            },
2172        )
2173        .as_box()
2174    }
2175
2176    fn adapter_constraints(scroll_offset: f64, remaining_paint: f64) -> SliverConstraints {
2177        SliverConstraints::new(
2178            AxisDirection::Down,
2179            GrowthDirection::Forward,
2180            ScrollDirection::Idle,
2181            scroll_offset,
2182            0.0,
2183            0.0,
2184            remaining_paint,
2185            100.0,
2186            AxisDirection::Right,
2187            remaining_paint,
2188            remaining_paint,
2189            0.0,
2190        )
2191    }
2192
2193    /// `sliver_test.dart`: a `RenderSliverToBoxAdapter` sizes itself to its box child in the main
2194    /// axis and reports the visible portion as its paint extent.
2195    #[test]
2196    fn a_box_adapter_reports_its_child_extent() {
2197        let cell = AppCell::new();
2198        let mut app = cell.borrow_mut();
2199        let child =
2200            RenderConstrainedBox::new(&mut app, BoxConstraints::tight_for(None, Some(60.0)), None);
2201        let adapter = RenderSliverToBoxAdapter::new(&mut app, Some(child.as_box()));
2202
2203        adapter.layout(&mut app, adapter_constraints(0.0, 400.0), true);
2204
2205        let geometry = adapter.geometry(&app);
2206        assert_eq!(geometry.scroll_extent, 60.0);
2207        assert_eq!(geometry.paint_extent, 60.0);
2208        assert_eq!(geometry.max_paint_extent, 60.0);
2209        assert!(!geometry.has_visual_overflow);
2210        // The cross axis is tight to the sliver's cross axis extent.
2211        assert_eq!(child.size(&app), Size::new(100.0, 60.0));
2212        assert_eq!(
2213            child
2214                .as_box()
2215                .as_object()
2216                .parent_data_of::<SliverPhysicalParentData>(&app)
2217                .paint_offset,
2218            Offset::ZERO
2219        );
2220    }
2221
2222    /// A scrolled adapter moves its child up by the scroll offset and shrinks its paint extent.
2223    #[test]
2224    fn a_scrolled_box_adapter_offsets_its_child() {
2225        let cell = AppCell::new();
2226        let mut app = cell.borrow_mut();
2227        let child =
2228            RenderConstrainedBox::new(&mut app, BoxConstraints::tight_for(None, Some(60.0)), None);
2229        let adapter = RenderSliverToBoxAdapter::new(&mut app, Some(child.as_box()));
2230
2231        adapter.layout(&mut app, adapter_constraints(20.0, 400.0), true);
2232
2233        let geometry = adapter.geometry(&app);
2234        assert_eq!(geometry.scroll_extent, 60.0);
2235        assert_eq!(geometry.paint_extent, 40.0);
2236        assert!(geometry.has_visual_overflow);
2237        assert_eq!(
2238            child
2239                .as_box()
2240                .as_object()
2241                .parent_data_of::<SliverPhysicalParentData>(&app)
2242                .paint_offset,
2243            Offset::new(0.0, -20.0)
2244        );
2245        assert_eq!(
2246            RenderSliver::child_main_axis_position(adapter, &app, child.as_object()),
2247            -20.0
2248        );
2249    }
2250
2251    /// An empty adapter reports zero geometry.
2252    #[test]
2253    fn a_childless_box_adapter_is_zero() {
2254        let cell = AppCell::new();
2255        let mut app = cell.borrow_mut();
2256        let adapter = RenderSliverToBoxAdapter::new(&mut app, None);
2257        adapter.layout(&mut app, adapter_constraints(0.0, 400.0), true);
2258        assert!(adapter.geometry(&app).is_zero());
2259    }
2260
2261    /// A hit inside the visible part of the child lands on both the child and the sliver.
2262    #[test]
2263    fn a_box_adapter_hit_tests_its_child() {
2264        let cell = AppCell::new();
2265        let mut app = cell.borrow_mut();
2266        let child = hit_testable_box(&mut app);
2267        let adapter = RenderSliverToBoxAdapter::new(&mut app, Some(child));
2268        adapter.layout(&mut app, adapter_constraints(0.0, 400.0), true);
2269
2270        let mut result = HitTestResult::new();
2271        let hit = adapter.hit_test(
2272            &mut app,
2273            &mut SliverHitTestResult::wrap(&mut result),
2274            30.0,
2275            50.0,
2276        );
2277
2278        assert!(hit);
2279        assert_eq!(result.path().len(), 2);
2280    }
2281
2282    /// `calculate_paint_offset` clamps the requested region to the visible window.
2283    #[test]
2284    fn paint_and_cache_offsets_clamp_to_the_window() {
2285        let constraints = adapter_constraints(20.0, 50.0);
2286        assert_eq!(calculate_paint_offset(constraints, 0.0, 100.0), 50.0);
2287        assert_eq!(calculate_paint_offset(constraints, 0.0, 30.0), 10.0);
2288        assert_eq!(calculate_paint_offset(constraints, 0.0, 10.0), 0.0);
2289        assert_eq!(calculate_cache_offset(constraints, 0.0, 100.0), 50.0);
2290    }
2291
2292    /// `SliverGeometry.debugAssertIsValid` accepts a consistent geometry.
2293    #[test]
2294    fn a_consistent_geometry_is_valid() {
2295        let geometry = SliverGeometry::new()
2296            .scroll_extent(100.0)
2297            .paint_extent(40.0)
2298            .max_paint_extent(100.0);
2299        assert!(geometry.debug_assert_is_valid());
2300        assert!(!geometry.is_zero());
2301        assert!(SliverGeometry::ZERO.is_zero());
2302    }
2303}