Skip to main content

inset_rendering/
sliver_multi_box_adaptor.rs

1//! Flutter counterpart: `rendering/sliver_multi_box_adaptor.dart`.
2//!
3//! Semantics wait; see `PORTING.md`.
4
5use std::any::{Any, TypeId};
6use std::fmt::{self, Debug, Display};
7use std::rc::Rc;
8
9use indexmap::IndexMap;
10use inset_embedder::{Matrix4, Offset};
11use inset_foundation::{App, Handle};
12use inset_painting::{Axis, AxisDirection};
13
14use crate::box_::{AnyRenderBox, BoxConstraints, BoxHitTestResult};
15use crate::object::{
16    AnyRenderObject, ContainerParentData, ContainerParentDataMixin, ContainerRenderObjectBase,
17    ContainerRenderObjectMixin, ParentData, RenderHandle, zero_matrix,
18};
19use crate::painting_context::PaintingContext;
20use crate::pipeline_owner::PipelineOwner;
21use crate::sliver::{
22    RenderSliver, RenderSliverHelpers, SliverConstraints, SliverHitTestResult,
23    SliverLogicalParentData, apply_growth_direction_to_axis_direction,
24};
25
26/// A delegate used by [`RenderSliverMultiBoxAdaptor`] to manage its children.
27///
28/// [`RenderSliverMultiBoxAdaptor`] objects reify their children lazily to avoid spending
29/// resources on children that are not visible in the viewport. This delegate lets these objects
30/// create and remove children as well as estimate the total scroll offset extent occupied by the
31/// full child list.
32///
33/// The widget layer implements this; the render object holds it as an
34/// [`Rc<dyn RenderSliverBoxChildManager>`].
35pub trait RenderSliverBoxChildManager {
36    /// Called during layout when a new child is needed.
37    ///
38    /// The child should be inserted into the child list in the appropriate position, after the
39    /// `after` child (at the start of the list if `after` is `None`). Its index and scroll
40    /// offsets will automatically be set appropriately.
41    ///
42    /// The `index` argument gives the index of the child to show. It is possible for negative
43    /// indices to be requested. For example: if the user scrolls from child 0 to child 10, and
44    /// then those children get much smaller, and then the user scrolls back up again, this
45    /// method will eventually be asked to produce a child for index -1.
46    ///
47    /// If no child corresponds to `index`, then do nothing.
48    ///
49    /// Which child is indicated by index zero depends on the
50    /// [`crate::GrowthDirection`] specified in the `constraints` of the
51    /// [`RenderSliverMultiBoxAdaptor`].
52    ///
53    /// During a call to this method it is valid to remove other children from the
54    /// [`RenderSliverMultiBoxAdaptor`] object if they were not created during this frame and have
55    /// not yet been updated during this frame. It is not valid to add any other children to this
56    /// render object.
57    fn create_child(&self, app: &mut App, index: i32, after: Option<AnyRenderBox>);
58
59    /// Remove the given child from the child list.
60    ///
61    /// Called by [`RenderSliverMultiBoxAdaptor::collect_garbage`], which itself is called from
62    /// the adaptor's `perform_layout`.
63    ///
64    /// The index of the given child can be obtained using
65    /// [`RenderSliverMultiBoxAdaptor::index_of`].
66    fn remove_child(&self, app: &mut App, child: AnyRenderBox);
67
68    /// Called to estimate the total scrollable extents of this object.
69    ///
70    /// Must return the total distance from the start of the child with the earliest possible
71    /// index to the end of the child with the last possible index.
72    fn estimate_max_scroll_offset(
73        &self,
74        app: &App,
75        constraints: SliverConstraints,
76        first_index: Option<i32>,
77        last_index: Option<i32>,
78        leading_scroll_offset: Option<f64>,
79        trailing_scroll_offset: Option<f64>,
80    ) -> f64;
81
82    /// Called to obtain a precise measure of the total number of children.
83    ///
84    /// Must return the number that is one greater than the greatest `index` for which
85    /// [`create_child`](Self::create_child) will actually create a child.
86    ///
87    /// This is used when `create_child` cannot add a child for a positive `index`, to determine
88    /// the precise dimensions of the sliver. It will not be called if `create_child` is always
89    /// able to create a child (e.g. for an infinite list).
90    fn child_count(&self, app: &mut App) -> i32;
91
92    /// The best available estimate of [`child_count`](Self::child_count), or `None` if no
93    /// estimate is available.
94    fn estimated_child_count(&self, app: &App) -> Option<i32> {
95        let _ = app;
96        None
97    }
98
99    /// Called during [`RenderSliverMultiBoxAdaptor::insert`] or
100    /// [`RenderSliverMultiBoxAdaptor::move_child`].
101    ///
102    /// Implementors must ensure that the [`SliverMultiBoxAdaptorParentData::index`] field of the
103    /// child's parent data accurately reflects the child's index in the child list after this
104    /// function returns.
105    fn did_adopt_child(&self, app: &mut App, child: AnyRenderBox);
106
107    /// Called during layout to indicate whether this object provided insufficient children for
108    /// the [`RenderSliverMultiBoxAdaptor`] to fill the
109    /// [`SliverConstraints::remaining_paint_extent`].
110    ///
111    /// Typically called unconditionally at the start of layout with `false` and then later
112    /// called with `true` when the adaptor fails to create a child required to fill the
113    /// remaining paint extent.
114    fn set_did_underflow(&self, app: &mut App, value: bool);
115
116    /// Called at the beginning of layout to indicate that layout is about to occur.
117    fn did_start_layout(&self, app: &mut App) {
118        let _ = app;
119    }
120
121    /// Called at the end of layout to indicate that layout is now complete.
122    fn did_finish_layout(&self, app: &mut App) {
123        let _ = app;
124    }
125
126    /// In debug mode, asserts that this manager is not expecting any modifications to the
127    /// [`RenderSliverMultiBoxAdaptor`]'s child list.
128    ///
129    /// This function always returns true.
130    fn debug_assert_child_list_locked(&self, app: &App) -> bool {
131        let _ = app;
132        true
133    }
134}
135
136/// Parent data structure used by [`RenderSliverWithKeepAliveMixin`].
137///
138/// Flutter's `KeepAliveParentDataMixin`: the half a parent data embeds and answers from
139/// [`ParentData::provide`], which is how `KeepAlive` finds it.
140#[derive(Clone, Copy, Debug, Default)]
141pub struct KeepAliveParentData {
142    /// Whether to keep the child alive even when it is no longer visible.
143    pub keep_alive: bool,
144    kept_alive: bool,
145}
146
147impl KeepAliveParentData {
148    /// Parent data that does not keep its child alive.
149    pub const fn new() -> KeepAliveParentData {
150        KeepAliveParentData {
151            keep_alive: false,
152            kept_alive: false,
153        }
154    }
155
156    /// Whether the widget is currently being kept alive, i.e. has
157    /// [`keep_alive`](Self::keep_alive) set to true and is offscreen.
158    pub fn kept_alive(&self) -> bool {
159        self.kept_alive
160    }
161}
162
163/// This trait exists to dissociate `KeepAlive` from [`RenderSliverMultiBoxAdaptor`].
164///
165/// [`setup_parent_data`](Self::setup_parent_data) must be implemented to use a parent data type
166/// that embeds a [`KeepAliveParentData`].
167pub trait RenderSliverWithKeepAliveMixin: RenderSliver {
168    /// Alerts the developer that the child's parent data needs to embed a
169    /// [`KeepAliveParentData`].
170    fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
171        let _ = self;
172        debug_assert!(
173            child
174                .parent_data(app)
175                .is_some_and(|parent_data| parent_data.part::<KeepAliveParentData>().is_some())
176        );
177    }
178}
179
180/// Parent data structure used by [`RenderSliverMultiBoxAdaptor`].
181#[derive(Debug)]
182pub struct SliverMultiBoxAdaptorParentData {
183    sliver_logical: SliverLogicalParentData,
184    container: ContainerParentData<AnyRenderBox>,
185    keep_alive: KeepAliveParentData,
186    /// The index of this child according to the [`RenderSliverBoxChildManager`].
187    pub index: Option<i32>,
188}
189
190impl SliverMultiBoxAdaptorParentData {
191    /// Creates parent data with no index and no layout offset.
192    pub const fn new() -> SliverMultiBoxAdaptorParentData {
193        SliverMultiBoxAdaptorParentData {
194            sliver_logical: SliverLogicalParentData::new(),
195            container: ContainerParentData::new(),
196            keep_alive: KeepAliveParentData::new(),
197            index: None,
198        }
199    }
200
201    /// See [`SliverLogicalParentData::layout_offset`].
202    pub fn layout_offset(&self) -> Option<f64> {
203        self.sliver_logical.layout_offset
204    }
205
206    /// Sets [`layout_offset`](Self::layout_offset).
207    pub fn set_layout_offset(&mut self, value: Option<f64>) {
208        self.sliver_logical.layout_offset = value;
209    }
210
211    /// See [`KeepAliveParentData::keep_alive`].
212    pub fn keep_alive(&self) -> bool {
213        self.keep_alive.keep_alive
214    }
215
216    /// Sets [`keep_alive`](Self::keep_alive).
217    pub fn set_keep_alive(&mut self, value: bool) {
218        self.keep_alive.keep_alive = value;
219    }
220
221    /// See [`KeepAliveParentData::kept_alive`].
222    pub fn kept_alive(&self) -> bool {
223        self.keep_alive.kept_alive
224    }
225}
226
227impl Default for SliverMultiBoxAdaptorParentData {
228    fn default() -> SliverMultiBoxAdaptorParentData {
229        SliverMultiBoxAdaptorParentData::new()
230    }
231}
232
233impl ParentData for SliverMultiBoxAdaptorParentData {
234    fn detach(&mut self) {
235        ContainerParentDataMixin::detach(self);
236    }
237
238    fn provide(&self, id: TypeId) -> Option<&dyn Any> {
239        if id == TypeId::of::<SliverMultiBoxAdaptorParentData>() {
240            return Some(self);
241        }
242        if id == TypeId::of::<KeepAliveParentData>() {
243            return Some(&self.keep_alive);
244        }
245        self.sliver_logical.provide(id)
246    }
247
248    fn provide_mut(&mut self, id: TypeId) -> Option<&mut dyn Any> {
249        if id == TypeId::of::<SliverMultiBoxAdaptorParentData>() {
250            return Some(self);
251        }
252        if id == TypeId::of::<KeepAliveParentData>() {
253            return Some(&mut self.keep_alive);
254        }
255        self.sliver_logical.provide_mut(id)
256    }
257}
258
259impl ContainerParentDataMixin for SliverMultiBoxAdaptorParentData {
260    type ChildType = AnyRenderBox;
261
262    fn container_parent_data(&self) -> &ContainerParentData<AnyRenderBox> {
263        &self.container
264    }
265
266    fn container_parent_data_mut(&mut self) -> &mut ContainerParentData<AnyRenderBox> {
267        &mut self.container
268    }
269}
270
271impl Display for SliverMultiBoxAdaptorParentData {
272    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
273        write!(f, "index={:?}; ", self.index)?;
274        if self.keep_alive() {
275            f.write_str("keepAlive; ")?;
276        }
277        Display::fmt(&self.sliver_logical, f)
278    }
279}
280
281/// Flutter's `RenderSliverMultiBoxAdaptor` fields.
282pub struct RenderSliverMultiBoxAdaptorData {
283    child_manager: Rc<dyn RenderSliverBoxChildManager>,
284    /// The nodes being kept alive despite not being visible.
285    keep_alive_bucket: IndexMap<i32, AnyRenderBox>,
286    debug_dangling_keep_alives: Vec<AnyRenderBox>,
287    debug_child_integrity_enabled: bool,
288}
289
290impl RenderSliverMultiBoxAdaptorData {
291    /// Creates the state of a sliver with multiple box children.
292    pub fn new(
293        child_manager: Rc<dyn RenderSliverBoxChildManager>,
294    ) -> RenderSliverMultiBoxAdaptorData {
295        RenderSliverMultiBoxAdaptorData {
296            child_manager,
297            keep_alive_bucket: IndexMap::new(),
298            debug_dangling_keep_alives: Vec::new(),
299            debug_child_integrity_enabled: true,
300        }
301    }
302}
303
304/// Implements [`RenderSliverMultiBoxAdaptor`] field accessors for an `adaptor` field.
305#[macro_export]
306macro_rules! render_sliver_multi_box_adaptor_accessors {
307    () => {
308        fn adaptor_data(
309            self: $crate::RenderHandle<Self>,
310            app: &::inset_foundation::App,
311        ) -> &$crate::RenderSliverMultiBoxAdaptorData {
312            &self.get(app).adaptor
313        }
314        fn adaptor_data_mut(
315            self: $crate::RenderHandle<Self>,
316            app: &mut ::inset_foundation::App,
317        ) -> &mut $crate::RenderSliverMultiBoxAdaptorData {
318            &mut self.get_mut(app).adaptor
319        }
320    };
321}
322
323/// A sliver with multiple box children.
324///
325/// [`RenderSliverMultiBoxAdaptor`] is a base class for slivers that have multiple box children.
326/// The children are managed by a [`RenderSliverBoxChildManager`], which lets implementors create
327/// children lazily during layout. Typically implementors will create only those children that are
328/// actually needed to fill the [`SliverConstraints::remaining_paint_extent`].
329///
330/// The contract for adding and removing children from this render object is more strict than for
331/// normal render objects:
332///
333/// * Children can be removed except during a layout pass if they have already been laid out
334///   during that layout pass.
335/// * Children cannot be added except during a call to
336///   [`child_manager`](Self::child_manager), and then only if there is no child corresponding to
337///   that index (or the child corresponding to that index was first removed).
338///
339/// See also:
340///
341///  * [`crate::RenderSliverToBoxAdapter`], which has a single box child.
342///  * [`crate::RenderSliverList`], which places its children in a linear array.
343///  * [`crate::RenderSliverFixedExtentList`], which places its children in a linear array with a
344///    fixed extent in the main axis.
345pub trait RenderSliverMultiBoxAdaptor:
346    RenderSliver
347    + RenderSliverHelpers
348    + RenderSliverWithKeepAliveMixin
349    + ContainerRenderObjectMixin<
350        ChildType = AnyRenderBox,
351        ParentDataType = SliverMultiBoxAdaptorParentData,
352    >
353{
354    /// Mixin field access.
355    fn adaptor_data(self: RenderHandle<Self>, app: &App) -> &RenderSliverMultiBoxAdaptorData;
356
357    /// See [`adaptor_data`](Self::adaptor_data).
358    fn adaptor_data_mut(
359        self: RenderHandle<Self>,
360        app: &mut App,
361    ) -> &mut RenderSliverMultiBoxAdaptorData;
362
363    /// The delegate that manages the children of this object.
364    ///
365    /// Rather than having a concrete list of children, a [`RenderSliverMultiBoxAdaptor`] uses a
366    /// [`RenderSliverBoxChildManager`] to create children during layout in order to fill the
367    /// [`SliverConstraints::remaining_paint_extent`].
368    fn child_manager(self: RenderHandle<Self>, app: &App) -> Rc<dyn RenderSliverBoxChildManager> {
369        Rc::clone(&self.adaptor_data(app).child_manager)
370    }
371
372    /// The body of Flutter's `setupParentData` override.
373    fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
374        if !child.parent_data_is::<SliverMultiBoxAdaptorParentData>(app) {
375            child.set_parent_data(app, SliverMultiBoxAdaptorParentData::new());
376        }
377    }
378
379    /// Whether the integrity check is enabled.
380    ///
381    /// The integrity check consists of:
382    ///
383    /// 1. Verify that the children index in the child list is in ascending order.
384    /// 2. Verify that there is no dangling keep-alive child as the result of
385    ///    [`move_child`](Self::move_child).
386    fn debug_child_integrity_enabled(self: RenderHandle<Self>, app: &App) -> bool {
387        self.adaptor_data(app).debug_child_integrity_enabled
388    }
389
390    /// Sets [`debug_child_integrity_enabled`](Self::debug_child_integrity_enabled), immediately
391    /// performing an integrity check.
392    fn set_debug_child_integrity_enabled(self: RenderHandle<Self>, app: &mut App, enabled: bool) {
393        if !cfg!(debug_assertions) {
394            return;
395        }
396        self.adaptor_data_mut(app).debug_child_integrity_enabled = enabled;
397        debug_assert!(self.debug_verify_child_order(app));
398        debug_assert!(
399            !enabled || self.adaptor_data(app).debug_dangling_keep_alives.is_empty(),
400            "a keep-alive child was left dangling by move_child"
401        );
402    }
403
404    /// Flutter's `_debugAssertChildListLocked`.
405    fn debug_assert_child_list_locked(self: RenderHandle<Self>, app: &App) -> bool {
406        self.child_manager(app).debug_assert_child_list_locked(app)
407    }
408
409    /// Verify that the child list index is in strictly increasing order.
410    ///
411    /// This has no effect in release builds.
412    fn debug_verify_child_order(self: RenderHandle<Self>, app: &App) -> bool {
413        if self.debug_child_integrity_enabled(app) {
414            let mut child = self.first_child(app);
415            while let Some(current) = child {
416                let index = self.index_of(app, current);
417                child = self.child_after(app, current);
418                debug_assert!(child.is_none_or(|next| self.index_of(app, next) > index));
419            }
420        }
421        true
422    }
423
424    /// The body of Flutter's `insert` override.
425    fn insert(
426        self: RenderHandle<Self>,
427        app: &mut App,
428        child: AnyRenderBox,
429        after: Option<AnyRenderBox>,
430    ) {
431        debug_assert!(
432            !self
433                .adaptor_data(app)
434                .keep_alive_bucket
435                .values()
436                .any(|kept| *kept == child)
437        );
438        ContainerRenderObjectBase::insert(self, app, child, after);
439        self.did_adopt_child(app, child);
440        debug_assert!(self.first_child(app).is_some());
441        debug_assert!(self.debug_verify_child_order(app));
442    }
443
444    /// The body of Flutter's `adoptChild` override, run after a child joins the child list.
445    ///
446    /// Flutter overrides `RenderObject.adoptChild`; here the tree operation is not virtual, so
447    /// the two callers ([`insert`](Self::insert) and Dart's `_destroyOrCacheChild`, which takes
448    /// `super.adoptChild`) decide whether it runs.
449    fn did_adopt_child(self: RenderHandle<Self>, app: &mut App, child: AnyRenderBox) {
450        let kept_alive = child
451            .as_object()
452            .parent_data_of::<SliverMultiBoxAdaptorParentData>(app)
453            .kept_alive();
454        if !kept_alive {
455            self.child_manager(app).did_adopt_child(app, child);
456        }
457    }
458
459    /// The body of Flutter's `move` override.
460    ///
461    /// There are two scenarios:
462    ///
463    /// 1. The child is not kept alive. The child is in the child list maintained by
464    ///    [`ContainerRenderObjectMixin`]. We can call the inherited body and update the parent
465    ///    data with the new slot.
466    ///
467    /// 2. The child is kept alive. In this case, the child is no longer in the child list but
468    ///    might be stored in the keep-alive bucket. We need to update the location of the child
469    ///    in the bucket.
470    fn move_child(
471        self: RenderHandle<Self>,
472        app: &mut App,
473        child: AnyRenderBox,
474        after: Option<AnyRenderBox>,
475    ) {
476        let child_parent_data = child
477            .as_object()
478            .parent_data_of::<SliverMultiBoxAdaptorParentData>(app);
479        let (kept_alive, index) = (child_parent_data.kept_alive(), child_parent_data.index);
480        if !kept_alive {
481            ContainerRenderObjectBase::move_child(self, app, child, after);
482            // updates the slot in the parent data
483            self.child_manager(app).did_adopt_child(app, child);
484            // Its slot may change even if the inherited body does not change the position. In
485            // this case, we still want to mark as needs layout.
486            self.mark_needs_layout(app);
487            return;
488        }
489        // If the child in the bucket is not the current child, that means someone has already
490        // moved and replaced the current child, and we cannot remove this child.
491        if let Some(index) = index
492            && self.adaptor_data(app).keep_alive_bucket.get(&index) == Some(&child)
493        {
494            self.adaptor_data_mut(app)
495                .keep_alive_bucket
496                .shift_remove(&index);
497        }
498        if cfg!(debug_assertions) {
499            self.adaptor_data_mut(app)
500                .debug_dangling_keep_alives
501                .retain(|dangling| *dangling != child);
502        }
503        // Update the slot and reinsert back to the keep-alive bucket in the new slot.
504        self.child_manager(app).did_adopt_child(app, child);
505        // If there is an existing child in the new slot, that means that child will be moved to
506        // another index. In other cases, the existing child should have been removed by
507        // update_child. Thus, it is ok to overwrite it.
508        let new_index = child
509            .as_object()
510            .parent_data_of::<SliverMultiBoxAdaptorParentData>(app)
511            .index
512            .expect("did_adopt_child sets the index");
513        if cfg!(debug_assertions)
514            && let Some(existing) = self
515                .adaptor_data(app)
516                .keep_alive_bucket
517                .get(&new_index)
518                .copied()
519        {
520            self.adaptor_data_mut(app)
521                .debug_dangling_keep_alives
522                .push(existing);
523        }
524        self.adaptor_data_mut(app)
525            .keep_alive_bucket
526            .insert(new_index, child);
527    }
528
529    /// The body of Flutter's `remove` override.
530    fn remove(self: RenderHandle<Self>, app: &mut App, child: AnyRenderBox) {
531        let child_parent_data = child
532            .as_object()
533            .parent_data_of::<SliverMultiBoxAdaptorParentData>(app);
534        let (kept_alive, index) = (child_parent_data.kept_alive(), child_parent_data.index);
535        if !kept_alive {
536            ContainerRenderObjectBase::remove(self, app, child);
537            return;
538        }
539        let index = index.expect("a kept-alive child has an index");
540        debug_assert_eq!(
541            self.adaptor_data(app).keep_alive_bucket.get(&index),
542            Some(&child)
543        );
544        if cfg!(debug_assertions) {
545            self.adaptor_data_mut(app)
546                .debug_dangling_keep_alives
547                .retain(|dangling| *dangling != child);
548        }
549        self.adaptor_data_mut(app)
550            .keep_alive_bucket
551            .shift_remove(&index);
552        self.drop_child(app, child.as_object());
553    }
554
555    /// The body of Flutter's `removeAll` override.
556    fn remove_all(self: RenderHandle<Self>, app: &mut App) {
557        ContainerRenderObjectBase::remove_all(self, app);
558        for child in self.keep_alive_children(app) {
559            self.drop_child(app, child.as_object());
560        }
561        self.adaptor_data_mut(app).keep_alive_bucket.clear();
562    }
563
564    /// The children currently held in the keep-alive bucket, in insertion order.
565    fn keep_alive_children(self: RenderHandle<Self>, app: &App) -> Vec<AnyRenderBox> {
566        self.adaptor_data(app)
567            .keep_alive_bucket
568            .values()
569            .copied()
570            .collect()
571    }
572
573    /// Flutter's `_createOrObtainChild`.
574    fn create_or_obtain_child(
575        self: RenderHandle<Self>,
576        app: &mut App,
577        index: i32,
578        after: Option<AnyRenderBox>,
579    ) {
580        self.as_object().invoke_layout_callback(app, |app| {
581            let cached = self
582                .adaptor_data(app)
583                .keep_alive_bucket
584                .get(&index)
585                .copied();
586            let Some(child) = cached else {
587                self.child_manager(app).create_child(app, index, after);
588                return;
589            };
590            self.adaptor_data_mut(app)
591                .keep_alive_bucket
592                .shift_remove(&index);
593            let child_parent_data = child
594                .as_object()
595                .parent_data_of::<SliverMultiBoxAdaptorParentData>(app);
596            debug_assert!(child_parent_data.kept_alive());
597            // Dart re-assigns the parent data it saved before `dropChild` nulled it; the fields
598            // are plain data, so the saved values are rebuilt instead.
599            let (layout_offset, keep_alive, index_of_child) = (
600                child_parent_data.layout_offset(),
601                child_parent_data.keep_alive(),
602                child_parent_data.index,
603            );
604            self.drop_child(app, child.as_object());
605            let mut restored = SliverMultiBoxAdaptorParentData::new();
606            restored.set_layout_offset(layout_offset);
607            restored.set_keep_alive(keep_alive);
608            restored.index = index_of_child;
609            restored.keep_alive.kept_alive = true;
610            child.as_object().set_parent_data(app, restored);
611            RenderSliverMultiBoxAdaptor::insert(self, app, child, after);
612            child
613                .as_object()
614                .parent_data_of_mut::<SliverMultiBoxAdaptorParentData>(app)
615                .keep_alive
616                .kept_alive = false;
617        });
618    }
619
620    /// Flutter's `_destroyOrCacheChild`.
621    fn destroy_or_cache_child(self: RenderHandle<Self>, app: &mut App, child: AnyRenderBox) {
622        let child_parent_data = child
623            .as_object()
624            .parent_data_of::<SliverMultiBoxAdaptorParentData>(app);
625        if !child_parent_data.keep_alive() {
626            debug_assert_eq!(child.as_object().parent(app), Some(self.as_object()));
627            self.child_manager(app).remove_child(app, child);
628            debug_assert!(child.as_object().parent(app).is_none());
629            return;
630        }
631        debug_assert!(!child_parent_data.kept_alive());
632        let (layout_offset, index) = (child_parent_data.layout_offset(), child_parent_data.index);
633        let index = index.expect("a cached child has an index");
634        RenderSliverMultiBoxAdaptor::remove(self, app, child);
635        self.adaptor_data_mut(app)
636            .keep_alive_bucket
637            .insert(index, child);
638        let mut restored = SliverMultiBoxAdaptorParentData::new();
639        restored.set_layout_offset(layout_offset);
640        restored.set_keep_alive(true);
641        restored.index = Some(index);
642        restored.keep_alive.kept_alive = true;
643        child.as_object().set_parent_data(app, restored);
644        // Dart's `super.adoptChild`: the child manager is not told, because the child keeps its
645        // slot.
646        self.as_object().adopt_child(app, child.as_object());
647    }
648
649    /// The body of Flutter's `attach` override: attaches the kept-alive children too.
650    fn did_attach(self: RenderHandle<Self>, app: &mut App, owner: Handle<PipelineOwner>) {
651        ContainerRenderObjectMixin::did_attach(self, app, owner);
652        for child in self.keep_alive_children(app) {
653            child.as_object().attach(app, owner);
654        }
655    }
656
657    /// The body of Flutter's `detach` override: detaches the kept-alive children too.
658    fn did_detach(self: RenderHandle<Self>, app: &mut App) {
659        ContainerRenderObjectMixin::did_detach(self, app);
660        for child in self.keep_alive_children(app) {
661            child.as_object().detach(app);
662        }
663    }
664
665    /// The body of Flutter's `redepthChildren` override.
666    fn redepth_children(self: RenderHandle<Self>, app: &mut App) {
667        ContainerRenderObjectMixin::redepth_children(self, app);
668        for child in self.keep_alive_children(app) {
669            self.as_object().redepth_child(app, child.as_object());
670        }
671    }
672
673    /// The body of Flutter's `visitChildren` override: visits the kept-alive children too.
674    fn visit_children(
675        self: RenderHandle<Self>,
676        app: &App,
677        visitor: &mut dyn FnMut(AnyRenderObject),
678    ) {
679        ContainerRenderObjectMixin::visit_children(self, app, visitor);
680        for child in self.keep_alive_children(app) {
681            visitor(child.as_object());
682        }
683    }
684
685    /// Called during layout to create and add the child with the given index and scroll offset.
686    ///
687    /// Calls [`RenderSliverBoxChildManager::create_child`] to actually create and add the child
688    /// if necessary. The child may instead be obtained from a cache; see
689    /// [`KeepAliveParentData::keep_alive`].
690    ///
691    /// Returns `false` if there was no cached child and `create_child` did not add any child,
692    /// otherwise returns `true`.
693    ///
694    /// Does not lay out the new child.
695    fn add_initial_child(
696        self: RenderHandle<Self>,
697        app: &mut App,
698        index: i32,
699        layout_offset: f64,
700    ) -> bool {
701        debug_assert!(self.debug_assert_child_list_locked(app));
702        debug_assert!(self.first_child(app).is_none());
703        self.create_or_obtain_child(app, index, None);
704        if let Some(first_child) = self.first_child(app) {
705            debug_assert_eq!(Some(first_child), self.last_child(app));
706            debug_assert_eq!(self.index_of(app, first_child), index);
707            first_child
708                .as_object()
709                .parent_data_of_mut::<SliverMultiBoxAdaptorParentData>(app)
710                .set_layout_offset(Some(layout_offset));
711            return true;
712        }
713        self.child_manager(app).set_did_underflow(app, true);
714        false
715    }
716
717    /// Called during layout to create, add, and lay out the child before
718    /// [`ContainerRenderObjectMixin::first_child`].
719    ///
720    /// Returns the new child or `None` if no child was obtained.
721    ///
722    /// The child that was previously the first child, as well as any subsequent children, may be
723    /// removed by this call if they have not yet been laid out during this layout pass.
724    fn insert_and_layout_leading_child(
725        self: RenderHandle<Self>,
726        app: &mut App,
727        child_constraints: BoxConstraints,
728        parent_uses_size: bool,
729    ) -> Option<AnyRenderBox> {
730        debug_assert!(self.debug_assert_child_list_locked(app));
731        let first_child = self.first_child(app).expect("there is a leading child");
732        let index = self.index_of(app, first_child) - 1;
733        self.create_or_obtain_child(app, index, None);
734        let first_child = self.first_child(app).expect("the child list is not empty");
735        if self.index_of(app, first_child) == index {
736            first_child.layout(app, child_constraints, parent_uses_size);
737            return Some(first_child);
738        }
739        self.child_manager(app).set_did_underflow(app, true);
740        None
741    }
742
743    /// Called during layout to create, add, and lay out the child after the given child.
744    ///
745    /// Returns the new child. It is the responsibility of the caller to configure the child's
746    /// scroll offset.
747    ///
748    /// Children after the `after` child may be removed in the process. Only the new child may be
749    /// added.
750    fn insert_and_layout_child(
751        self: RenderHandle<Self>,
752        app: &mut App,
753        child_constraints: BoxConstraints,
754        after: Option<AnyRenderBox>,
755        parent_uses_size: bool,
756    ) -> Option<AnyRenderBox> {
757        debug_assert!(self.debug_assert_child_list_locked(app));
758        let after = after.expect("insert_and_layout_child needs a preceding child");
759        let index = self.index_of(app, after) + 1;
760        self.create_or_obtain_child(app, index, Some(after));
761        let child = self.child_after(app, after);
762        if let Some(child) = child
763            && self.index_of(app, child) == index
764        {
765            child.layout(app, child_constraints, parent_uses_size);
766            return Some(child);
767        }
768        self.child_manager(app).set_did_underflow(app, true);
769        None
770    }
771
772    /// Returns the number of children preceding the `first_index` that need to be garbage
773    /// collected.
774    fn calculate_leading_garbage(self: RenderHandle<Self>, app: &App, first_index: i32) -> usize {
775        let mut walker = self.first_child(app);
776        let mut leading_garbage = 0;
777        while let Some(current) = walker {
778            if self.index_of(app, current) >= first_index {
779                break;
780            }
781            leading_garbage += 1;
782            walker = self.child_after(app, current);
783        }
784        leading_garbage
785    }
786
787    /// Returns the number of children following the `last_index` that need to be garbage
788    /// collected.
789    fn calculate_trailing_garbage(self: RenderHandle<Self>, app: &App, last_index: i32) -> usize {
790        let mut walker = self.last_child(app);
791        let mut trailing_garbage = 0;
792        while let Some(current) = walker {
793            if self.index_of(app, current) <= last_index {
794                break;
795            }
796            trailing_garbage += 1;
797            walker = self.child_before(app, current);
798        }
799        trailing_garbage
800    }
801
802    /// Called after layout with the number of children that can be garbage collected at the head
803    /// and tail of the child list.
804    ///
805    /// Children whose [`KeepAliveParentData::keep_alive`] property is set to true will be
806    /// removed to a cache instead of being dropped.
807    ///
808    /// This method also collects any children that were previously kept alive but are now no
809    /// longer necessary. As such, it should be called every time `perform_layout` is run, even if
810    /// the arguments are both zero.
811    fn collect_garbage(
812        self: RenderHandle<Self>,
813        app: &mut App,
814        leading_garbage: usize,
815        trailing_garbage: usize,
816    ) {
817        debug_assert!(self.debug_assert_child_list_locked(app));
818        debug_assert!(self.child_count(app) >= leading_garbage + trailing_garbage);
819        self.as_object().invoke_layout_callback(app, |app| {
820            for _ in 0..leading_garbage {
821                let first_child = self.first_child(app).expect("counted above");
822                self.destroy_or_cache_child(app, first_child);
823            }
824            for _ in 0..trailing_garbage {
825                let last_child = self.last_child(app).expect("counted above");
826                self.destroy_or_cache_child(app, last_child);
827            }
828            // Ask the child manager to remove the children that are no longer being kept alive.
829            // (This should cause the keep-alive bucket to change, so we have to prepare our list
830            // ahead of time.)
831            let expired: Vec<AnyRenderBox> = self
832                .keep_alive_children(app)
833                .into_iter()
834                .filter(|child| {
835                    !child
836                        .as_object()
837                        .parent_data_of::<SliverMultiBoxAdaptorParentData>(app)
838                        .keep_alive()
839                })
840                .collect();
841            let child_manager = self.child_manager(app);
842            for child in expired {
843                child_manager.remove_child(app, child);
844            }
845            debug_assert!(self.keep_alive_children(app).iter().all(|child| {
846                child
847                    .as_object()
848                    .parent_data_of::<SliverMultiBoxAdaptorParentData>(app)
849                    .keep_alive()
850            }));
851        });
852    }
853
854    /// Returns the index of the given child, as given by the
855    /// [`SliverMultiBoxAdaptorParentData::index`] field of the child's parent data.
856    fn index_of(self: RenderHandle<Self>, app: &App, child: AnyRenderBox) -> i32 {
857        child
858            .as_object()
859            .parent_data_of::<SliverMultiBoxAdaptorParentData>(app)
860            .index
861            .expect("a reified child has an index")
862    }
863
864    /// Returns the dimension of the given child in the main axis, as given by the child's size.
865    ///
866    /// This is only valid after layout.
867    fn paint_extent_of(self: RenderHandle<Self>, app: &App, child: AnyRenderBox) -> f64 {
868        debug_assert!(child.has_size(app));
869        match self.constraints(app).axis() {
870            Axis::Horizontal => child.size(app).width(),
871            Axis::Vertical => child.size(app).height(),
872        }
873    }
874
875    /// The body of Flutter's `hitTestChildren` override.
876    fn hit_test_children(
877        self: RenderHandle<Self>,
878        app: &mut App,
879        result: &mut SliverHitTestResult<'_>,
880        main_axis_position: f64,
881        cross_axis_position: f64,
882    ) -> bool {
883        let mut child = self.last_child(app);
884        while let Some(current) = child {
885            let previous = self.child_before(app, current);
886            if self.hit_test_box_child(
887                app,
888                &mut BoxHitTestResult::wrap(result),
889                current,
890                main_axis_position,
891                cross_axis_position,
892            ) {
893                return true;
894            }
895            child = previous;
896        }
897        false
898    }
899
900    /// The body of Flutter's `childMainAxisPosition` override.
901    fn child_main_axis_position(
902        self: RenderHandle<Self>,
903        app: &App,
904        child: AnyRenderObject,
905    ) -> f64 {
906        RenderSliverMultiBoxAdaptor::child_scroll_offset(self, app, child)
907            .expect("a laid-out child has a scroll offset")
908            - self.constraints(app).scroll_offset
909    }
910
911    /// The body of Flutter's `childScrollOffset` override.
912    fn child_scroll_offset(
913        self: RenderHandle<Self>,
914        app: &App,
915        child: AnyRenderObject,
916    ) -> Option<f64> {
917        debug_assert_eq!(child.parent(app), Some(self.as_object()));
918        child
919            .parent_data_of::<SliverMultiBoxAdaptorParentData>(app)
920            .layout_offset()
921    }
922
923    /// Whether the given child is painted, i.e. it is reified and not kept alive off-screen.
924    ///
925    /// Flutter's `paintsChild` override; `RenderObject.paintsChild` is not a virtual here (see
926    /// `PORTING.md`).
927    fn paints_child(self: RenderHandle<Self>, app: &App, child: AnyRenderBox) -> bool {
928        let index = child
929            .as_object()
930            .parent_data(app)
931            .and_then(|parent_data| {
932                (parent_data as &dyn std::any::Any)
933                    .downcast_ref::<SliverMultiBoxAdaptorParentData>()
934            })
935            .and_then(|parent_data| parent_data.index);
936        match index {
937            None => false,
938            Some(index) => !self
939                .adaptor_data(app)
940                .keep_alive_bucket
941                .contains_key(&index),
942        }
943    }
944
945    /// The body of Flutter's `applyPaintTransform` override.
946    fn apply_paint_transform(
947        self: RenderHandle<Self>,
948        app: &App,
949        child: AnyRenderObject,
950        transform: &mut Matrix4,
951    ) {
952        let child = child.as_box().expect("the children are boxes");
953        if self.paints_child(app, child) {
954            self.apply_paint_transform_for_box_child(app, child, transform);
955        } else {
956            // This can happen if some child asks for the global transform even though they are
957            // not getting painted. In that case, the transform is set to zero since
958            // apply_paint_transform_for_box_child would end up throwing due to the child not
959            // being configured correctly for applying a transform. There's no assert here because
960            // asking for the paint transform is a valid thing to do even if a child would not be
961            // painted, but there is no meaningful non-zero matrix to use in this case.
962            *transform = zero_matrix();
963        }
964    }
965
966    /// The body of Flutter's `paint` override.
967    fn paint(
968        self: RenderHandle<Self>,
969        app: &mut App,
970        context: &mut PaintingContext,
971        offset: Offset,
972    ) {
973        if self.first_child(app).is_none() {
974            return;
975        }
976        // offset is to the top-left corner, regardless of our axis direction.
977        // origin_offset gives us the delta from the real origin to the origin in the axis
978        // direction.
979        let constraints = self.constraints(app);
980        let paint_extent = self.geometry(app).paint_extent;
981        let (main_axis_unit, cross_axis_unit, origin_offset, add_extent) =
982            match apply_growth_direction_to_axis_direction(
983                constraints.axis_direction,
984                constraints.growth_direction,
985            ) {
986                AxisDirection::Up => (
987                    Offset::new(0.0, -1.0),
988                    Offset::new(1.0, 0.0),
989                    offset + Offset::new(0.0, paint_extent),
990                    true,
991                ),
992                AxisDirection::Right => {
993                    (Offset::new(1.0, 0.0), Offset::new(0.0, 1.0), offset, false)
994                }
995                AxisDirection::Down => {
996                    (Offset::new(0.0, 1.0), Offset::new(1.0, 0.0), offset, false)
997                }
998                AxisDirection::Left => (
999                    Offset::new(-1.0, 0.0),
1000                    Offset::new(0.0, 1.0),
1001                    offset + Offset::new(paint_extent, 0.0),
1002                    true,
1003                ),
1004            };
1005        let mut child = self.first_child(app);
1006        while let Some(current) = child {
1007            let main_axis_delta = RenderSliverMultiBoxAdaptor::child_main_axis_position(
1008                self,
1009                app,
1010                current.as_object(),
1011            );
1012            let cross_axis_delta = self.child_cross_axis_position(app, current.as_object());
1013            let mut child_offset = Offset::new(
1014                origin_offset.dx()
1015                    + main_axis_unit.dx() * main_axis_delta
1016                    + cross_axis_unit.dx() * cross_axis_delta,
1017                origin_offset.dy()
1018                    + main_axis_unit.dy() * main_axis_delta
1019                    + cross_axis_unit.dy() * cross_axis_delta,
1020            );
1021            let child_paint_extent = self.paint_extent_of(app, current);
1022            if add_extent {
1023                child_offset = child_offset + main_axis_unit * child_paint_extent;
1024            }
1025
1026            // If the child's visible interval (main_axis_delta, main_axis_delta +
1027            // paint_extent_of(child)) does not intersect the paint extent interval
1028            // (0, constraints.remaining_paint_extent), it's hidden.
1029            if main_axis_delta < constraints.remaining_paint_extent
1030                && main_axis_delta + child_paint_extent > 0.0
1031            {
1032                context.paint_child(app, current.as_object(), child_offset);
1033            }
1034
1035            child = self.child_after(app, current);
1036        }
1037    }
1038
1039    /// Asserts that the reified child list is not empty and has a contiguous sequence of indices.
1040    ///
1041    /// Always returns true.
1042    fn debug_assert_child_list_is_non_empty_and_contiguous(
1043        self: RenderHandle<Self>,
1044        app: &App,
1045    ) -> bool {
1046        if cfg!(debug_assertions) {
1047            let first_child = self.first_child(app).expect("the child list is not empty");
1048            let mut index = self.index_of(app, first_child);
1049            let mut child = self.child_after(app, first_child);
1050            while let Some(current) = child {
1051                index += 1;
1052                debug_assert_eq!(self.index_of(app, current), index);
1053                child = self.child_after(app, current);
1054            }
1055        }
1056        true
1057    }
1058}
1059
1060#[cfg(test)]
1061pub(crate) mod test_support {
1062    use std::cell::{Cell, RefCell};
1063
1064    use inset_embedder::Size;
1065
1066    use super::*;
1067    use crate::box_::{RenderBox, RenderBoxData};
1068    use crate::object::{RenderObject, RenderObjectData};
1069
1070    /// A box that takes the largest size the constraints allow, and accepts hits.
1071    pub(crate) struct TestChild {
1072        render_object: RenderObjectData,
1073        render_box: RenderBoxData,
1074        preferred_extent: f64,
1075    }
1076
1077    impl RenderObject for TestChild {
1078        crate::render_object_accessors!();
1079
1080        fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
1081            let extent = self.get(app).preferred_extent;
1082            let size = self.constraints(app).constrain(Size::new(extent, extent));
1083            self.set_size(app, size);
1084        }
1085    }
1086
1087    impl RenderBox for TestChild {
1088        crate::render_box_accessors!();
1089
1090        fn hit_test_self(self: RenderHandle<Self>, _app: &App, _position: Offset) -> bool {
1091            let _ = self;
1092            true
1093        }
1094    }
1095
1096    /// A box that fills the constraints it is given, up to `extent` in each dimension, and
1097    /// accepts hits.
1098    pub(crate) fn hit_testable_box(app: &mut App, extent: f64) -> AnyRenderBox {
1099        RenderHandle::new_box(
1100            app,
1101            TestChild {
1102                render_object: RenderObjectData::new(),
1103                render_box: RenderBoxData::new(),
1104                preferred_extent: extent,
1105            },
1106        )
1107        .as_box()
1108    }
1109
1110    /// A [`RenderSliverBoxChildManager`] over a fixed list of child extents, as
1111    /// `SliverMultiBoxAdaptorElement` is over a delegate.
1112    pub(crate) struct TestChildManager<T: RenderSliverMultiBoxAdaptor> {
1113        render_object: Cell<Option<RenderHandle<T>>>,
1114        extents: Vec<f64>,
1115        currently_updating_child_index: Cell<Option<i32>>,
1116        /// The indices `create_child` was asked for, in order.
1117        pub(crate) created: RefCell<Vec<i32>>,
1118        /// The last value passed to `set_did_underflow`.
1119        pub(crate) did_underflow: Cell<bool>,
1120    }
1121
1122    impl<T: RenderSliverMultiBoxAdaptor> TestChildManager<T> {
1123        pub(crate) fn new(extents: Vec<f64>) -> Rc<TestChildManager<T>> {
1124            Rc::new(TestChildManager {
1125                render_object: Cell::new(None),
1126                extents,
1127                currently_updating_child_index: Cell::new(None),
1128                created: RefCell::new(Vec::new()),
1129                did_underflow: Cell::new(false),
1130            })
1131        }
1132
1133        pub(crate) fn attach(&self, render_object: RenderHandle<T>) {
1134            self.render_object.set(Some(render_object));
1135        }
1136
1137        fn adaptor(&self) -> RenderHandle<T> {
1138            self.render_object.get().expect("attached before layout")
1139        }
1140
1141        fn total_extent(&self) -> f64 {
1142            self.extents.iter().sum()
1143        }
1144    }
1145
1146    impl<T: RenderSliverMultiBoxAdaptor> RenderSliverBoxChildManager for TestChildManager<T> {
1147        fn create_child(&self, app: &mut App, index: i32, after: Option<AnyRenderBox>) {
1148            self.created.borrow_mut().push(index);
1149            let Ok(slot) = usize::try_from(index) else {
1150                return;
1151            };
1152            let Some(extent) = self.extents.get(slot).copied() else {
1153                return;
1154            };
1155            let child = RenderHandle::new_box(
1156                app,
1157                TestChild {
1158                    render_object: RenderObjectData::new(),
1159                    render_box: RenderBoxData::new(),
1160                    preferred_extent: extent,
1161                },
1162            )
1163            .as_box();
1164            self.currently_updating_child_index.set(Some(index));
1165            RenderSliverMultiBoxAdaptor::insert(self.adaptor(), app, child, after);
1166            self.currently_updating_child_index.set(None);
1167        }
1168
1169        fn remove_child(&self, app: &mut App, child: AnyRenderBox) {
1170            // Dart's element unmounts the child and disposes its render object later; the test
1171            // leaves the arena slot alone so that assertions can still name the child.
1172            RenderSliverMultiBoxAdaptor::remove(self.adaptor(), app, child);
1173        }
1174
1175        fn estimate_max_scroll_offset(
1176            &self,
1177            _app: &App,
1178            _constraints: SliverConstraints,
1179            _first_index: Option<i32>,
1180            _last_index: Option<i32>,
1181            _leading_scroll_offset: Option<f64>,
1182            _trailing_scroll_offset: Option<f64>,
1183        ) -> f64 {
1184            self.total_extent()
1185        }
1186
1187        fn child_count(&self, _app: &mut App) -> i32 {
1188            self.extents.len() as i32
1189        }
1190
1191        fn estimated_child_count(&self, _app: &App) -> Option<i32> {
1192            Some(self.extents.len() as i32)
1193        }
1194
1195        fn did_adopt_child(&self, app: &mut App, child: AnyRenderBox) {
1196            child
1197                .as_object()
1198                .parent_data_of_mut::<SliverMultiBoxAdaptorParentData>(app)
1199                .index = self.currently_updating_child_index.get();
1200        }
1201
1202        fn set_did_underflow(&self, _app: &mut App, value: bool) {
1203            self.did_underflow.set(value);
1204        }
1205    }
1206}
1207
1208#[cfg(test)]
1209pub(crate) mod viewport_test_support {
1210    use std::rc::Rc;
1211
1212    use inset_embedder::{Picture, Size, View, ViewId, ViewMetrics, ViewRef};
1213    use inset_foundation::Handle;
1214
1215    use super::*;
1216    use crate::box_::RenderBox;
1217    use crate::pipeline_owner::PipelineOwner;
1218    use crate::sliver::AnyRenderSliver;
1219    use crate::view::{RenderView, ViewConfiguration};
1220    use crate::viewport::RenderViewport;
1221    use crate::viewport_offset::{FixedViewportOffset, ViewportOffset};
1222
1223    struct TestView;
1224
1225    impl View for TestView {
1226        fn id(&self) -> ViewId {
1227            ViewId(0)
1228        }
1229
1230        fn metrics(&self) -> ViewMetrics {
1231            ViewMetrics::default()
1232        }
1233
1234        fn present(&self, _picture: &Picture) {}
1235    }
1236
1237    /// The scrolling machinery a lazy sliver needs: a viewport inside a [`RenderView`] rooted in
1238    /// a [`PipelineOwner`], laid out once.
1239    pub(crate) struct ScrollHarness {
1240        pub(crate) owner: Handle<PipelineOwner>,
1241        pub(crate) viewport: RenderHandle<RenderViewport>,
1242        pub(crate) offset: Handle<FixedViewportOffset>,
1243    }
1244
1245    impl ScrollHarness {
1246        /// Lays `sliver` out in a downward-scrolling viewport of `size`.
1247        pub(crate) fn new(app: &mut App, sliver: AnyRenderSliver, size: Size) -> ScrollHarness {
1248            let offset = FixedViewportOffset::zero(app);
1249            let viewport = RenderViewport::new(
1250                app,
1251                AxisDirection::Right,
1252                offset.as_viewport_offset(),
1253                Some(vec![sliver]),
1254                None,
1255            );
1256            let constraints = BoxConstraints::tight(size);
1257            let configuration = ViewConfiguration::new(constraints, constraints, 1.0);
1258            let view = RenderView::new(
1259                app,
1260                Some(viewport.as_box()),
1261                Some(configuration),
1262                Rc::new(TestView) as ViewRef,
1263            );
1264            let owner = PipelineOwner::new(app, None);
1265            owner.set_root_node(app, Some(view.as_object()));
1266            view.prepare_initial_frame(app);
1267            owner.flush_layout(app);
1268            ScrollHarness {
1269                owner,
1270                viewport,
1271                offset,
1272            }
1273        }
1274
1275        /// Scrolls to `pixels` and lays out again.
1276        pub(crate) fn scroll_to(&self, app: &mut App, pixels: f64) {
1277            let current = self.offset.pixels(app);
1278            self.offset.correct_by(app, pixels - current);
1279            self.viewport.mark_needs_layout(app);
1280            self.owner.flush_layout(app);
1281        }
1282    }
1283}