1use std::rc::Rc;
4
5use inset_embedder::{Matrix4, Offset};
6use inset_foundation::{App, Handle, PRECISION_ERROR_TOLERANCE};
7
8use crate::box_::{AnyRenderBox, BoxConstraints};
9use crate::object::{
10 AnyRenderObject, ContainerRenderObjectData, ContainerRenderObjectMixin, RenderHandle,
11 RenderObject, RenderObjectData,
12};
13use crate::painting_context::PaintingContext;
14use crate::pipeline_owner::PipelineOwner;
15use crate::sliver::{
16 ItemExtentBuilder, RenderSliver, RenderSliverData, RenderSliverHelpers, SliverConstraints,
17 SliverGeometry, SliverHitTestResult, SliverLayoutDimensions, calculate_cache_offset,
18 calculate_paint_offset,
19};
20use crate::sliver_multi_box_adaptor::{
21 RenderSliverBoxChildManager, RenderSliverMultiBoxAdaptor, RenderSliverMultiBoxAdaptorData,
22 RenderSliverWithKeepAliveMixin, SliverMultiBoxAdaptorParentData,
23};
24
25#[derive(Debug, Default)]
27pub struct RenderSliverFixedExtentBoxAdaptorData {
28 current_layout_dimensions: Option<SliverLayoutDimensions>,
29}
30
31impl RenderSliverFixedExtentBoxAdaptorData {
32 pub const fn new() -> RenderSliverFixedExtentBoxAdaptorData {
34 RenderSliverFixedExtentBoxAdaptorData {
35 current_layout_dimensions: None,
36 }
37 }
38}
39
40#[macro_export]
42macro_rules! render_sliver_fixed_extent_accessors {
43 () => {
44 fn fixed_extent_data(
45 self: $crate::RenderHandle<Self>,
46 app: &::inset_foundation::App,
47 ) -> &$crate::RenderSliverFixedExtentBoxAdaptorData {
48 &self.get(app).fixed_extent
49 }
50 fn fixed_extent_data_mut(
51 self: $crate::RenderHandle<Self>,
52 app: &mut ::inset_foundation::App,
53 ) -> &mut $crate::RenderSliverFixedExtentBoxAdaptorData {
54 &mut self.get_mut(app).fixed_extent
55 }
56 };
57}
58
59pub trait RenderSliverFixedExtentBoxAdaptor: RenderSliverMultiBoxAdaptor {
64 fn fixed_extent_data(
66 self: RenderHandle<Self>,
67 app: &App,
68 ) -> &RenderSliverFixedExtentBoxAdaptorData;
69
70 fn fixed_extent_data_mut(
72 self: RenderHandle<Self>,
73 app: &mut App,
74 ) -> &mut RenderSliverFixedExtentBoxAdaptorData;
75
76 fn item_extent(self: RenderHandle<Self>, app: &App) -> Option<f64>;
81
82 fn item_extent_builder(self: RenderHandle<Self>, app: &App) -> Option<ItemExtentBuilder> {
87 let _ = app;
88 let _ = self;
89 None
90 }
91
92 fn layout_dimensions(self: RenderHandle<Self>, app: &App) -> SliverLayoutDimensions {
97 self.fixed_extent_data(app)
98 .current_layout_dimensions
99 .unwrap_or_else(|| {
100 let constraints = self.constraints(app);
101 SliverLayoutDimensions::new(
102 constraints.scroll_offset,
103 constraints.preceding_scroll_extent,
104 constraints.viewport_main_axis_extent,
105 constraints.cross_axis_extent,
106 )
107 })
108 }
109
110 fn index_to_layout_offset(self: RenderHandle<Self>, app: &App, index: i32) -> f64 {
119 let Some(builder) = self.item_extent_builder(app) else {
120 let item_extent = self.item_extent(app).expect("one of the two is set");
121 return item_extent * f64::from(index);
122 };
123 let mut offset = 0.0;
124 let dimensions = self.layout_dimensions(app);
125 let child_manager = self.child_manager(app);
126 for i in 0..index {
127 let child_count = child_manager.estimated_child_count(app);
128 if child_count.is_some_and(|count| i > count - 1) {
129 break;
130 }
131 let Some(item_extent) = builder(i, dimensions) else {
132 break;
133 };
134 offset += item_extent;
135 }
136 offset
137 }
138
139 fn get_min_child_index_for_scroll_offset(
144 self: RenderHandle<Self>,
145 app: &App,
146 scroll_offset: f64,
147 ) -> i32 {
148 let Some(builder) = self.item_extent_builder(app) else {
149 let item_extent = self.item_extent(app).expect("one of the two is set");
150 if item_extent <= 0.0 {
151 return 0;
152 }
153 let actual = scroll_offset / item_extent;
154 let round = actual.round();
155 if (actual * item_extent - round * item_extent).abs() < PRECISION_ERROR_TOLERANCE {
156 return round as i32;
157 }
158 return actual.floor() as i32;
159 };
160 self.child_index_for_scroll_offset(app, scroll_offset, &builder)
161 }
162
163 fn get_max_child_index_for_scroll_offset(
168 self: RenderHandle<Self>,
169 app: &App,
170 scroll_offset: f64,
171 ) -> i32 {
172 let Some(builder) = self.item_extent_builder(app) else {
173 let item_extent = self.item_extent(app).expect("one of the two is set");
174 if item_extent <= 0.0 {
175 return 0;
176 }
177 let actual = scroll_offset / item_extent - 1.0;
178 let round = actual.round();
179 if (actual * item_extent - round * item_extent).abs() < PRECISION_ERROR_TOLERANCE {
180 return (round as i32).max(0);
181 }
182 return (actual.ceil() as i32).max(0);
183 };
184 self.child_index_for_scroll_offset(app, scroll_offset, &builder)
185 }
186
187 fn child_index_for_scroll_offset(
189 self: RenderHandle<Self>,
190 app: &App,
191 scroll_offset: f64,
192 builder: &ItemExtentBuilder,
193 ) -> i32 {
194 if scroll_offset == 0.0 {
195 return 0;
196 }
197 let dimensions = self.layout_dimensions(app);
198 let child_manager = self.child_manager(app);
199 let mut position = 0.0;
200 let mut index = 0;
201 while position < scroll_offset {
202 let child_count = child_manager.estimated_child_count(app);
203 if child_count.is_some_and(|count| index > count - 1) {
204 break;
205 }
206 let Some(item_extent) = builder(index, dimensions) else {
207 break;
208 };
209 position += item_extent;
210 index += 1;
211 }
212 index - 1
213 }
214
215 fn estimate_max_scroll_offset(
222 self: RenderHandle<Self>,
223 app: &App,
224 constraints: SliverConstraints,
225 first_index: Option<i32>,
226 last_index: Option<i32>,
227 leading_scroll_offset: Option<f64>,
228 trailing_scroll_offset: Option<f64>,
229 ) -> f64 {
230 self.child_manager(app).estimate_max_scroll_offset(
231 app,
232 constraints,
233 first_index,
234 last_index,
235 leading_scroll_offset,
236 trailing_scroll_offset,
237 )
238 }
239
240 fn compute_max_scroll_offset(
250 self: RenderHandle<Self>,
251 app: &mut App,
252 _constraints: SliverConstraints,
253 ) -> f64 {
254 let child_manager = self.child_manager(app);
255 let Some(builder) = self.item_extent_builder(app) else {
256 let item_extent = self.item_extent(app).expect("one of the two is set");
257 return f64::from(child_manager.child_count(app)) * item_extent;
258 };
259 let dimensions = self.layout_dimensions(app);
260 let mut offset = 0.0;
261 for i in 0..child_manager.child_count(app) {
262 let Some(item_extent) = builder(i, dimensions) else {
263 break;
264 };
265 offset += item_extent;
266 }
267 offset
268 }
269
270 fn child_constraints(self: RenderHandle<Self>, app: &App, index: i32) -> BoxConstraints {
272 let extent = match self.item_extent_builder(app) {
273 None => self.item_extent(app).expect("one of the two is set"),
274 Some(builder) => builder(index, self.layout_dimensions(app))
275 .expect("the builder must report an extent for a reified child"),
276 };
277 self.constraints(app)
278 .as_box_constraints(extent, extent, None)
279 }
280
281 fn paint_extent_of(self: RenderHandle<Self>, app: &App, child: AnyRenderBox) -> f64 {
283 match self.item_extent_builder(app) {
284 None => self.item_extent(app).expect("one of the two is set"),
285 Some(builder) => builder(self.index_of(app, child), self.layout_dimensions(app))
286 .expect("the builder must report an extent for a reified child"),
287 }
288 }
289
290 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
292 debug_assert!(
293 self.item_extent(app).is_some() != self.item_extent_builder(app).is_some(),
294 "exactly one of item_extent and item_extent_builder must be set"
295 );
296 debug_assert!(
297 self.item_extent_builder(app).is_some()
298 || self
299 .item_extent(app)
300 .is_some_and(|extent| extent.is_finite() && extent >= 0.0)
301 );
302
303 let constraints = self.constraints(app);
304 let child_manager = self.child_manager(app);
305 child_manager.did_start_layout(app);
306 child_manager.set_did_underflow(app, false);
307
308 let scroll_offset = constraints.scroll_offset + constraints.cache_origin;
309 debug_assert!(scroll_offset >= 0.0);
310 let remaining_extent = constraints.remaining_cache_extent;
311 debug_assert!(remaining_extent >= 0.0);
312 let target_end_scroll_offset = scroll_offset + remaining_extent;
313
314 self.fixed_extent_data_mut(app).current_layout_dimensions =
315 Some(SliverLayoutDimensions::new(
316 constraints.scroll_offset,
317 constraints.preceding_scroll_extent,
318 constraints.viewport_main_axis_extent,
319 constraints.cross_axis_extent,
320 ));
321
322 let first_index = self.get_min_child_index_for_scroll_offset(app, scroll_offset);
323 let target_last_index = target_end_scroll_offset
324 .is_finite()
325 .then(|| self.get_max_child_index_for_scroll_offset(app, target_end_scroll_offset));
326
327 if self.first_child(app).is_some() {
328 let leading_garbage = self.calculate_leading_garbage(app, first_index);
329 let trailing_garbage = match target_last_index {
330 Some(last_index) => self.calculate_trailing_garbage(app, last_index),
331 None => 0,
332 };
333 self.collect_garbage(app, leading_garbage, trailing_garbage);
334 } else {
335 self.collect_garbage(app, 0, 0);
336 }
337
338 if self.first_child(app).is_none() {
339 let layout_offset = self.index_to_layout_offset(app, first_index);
340 if !self.add_initial_child(app, first_index, layout_offset) {
341 let max = if first_index <= 0 {
343 0.0
344 } else {
345 self.compute_max_scroll_offset(app, constraints)
346 };
347 self.set_geometry(
348 app,
349 SliverGeometry::new()
350 .scroll_extent(max)
351 .max_paint_extent(max),
352 );
353 child_manager.did_finish_layout(app);
354 return;
355 }
356 }
357
358 let mut trailing_child_with_layout: Option<AnyRenderBox> = None;
359
360 let first_child = self.first_child(app).expect("added above");
361 let mut index = self.index_of(app, first_child) - 1;
362 while index >= first_index {
363 let child_constraints = self.child_constraints(app, index);
364 let Some(child) = self.insert_and_layout_leading_child(app, child_constraints, false)
365 else {
366 let correction = self.index_to_layout_offset(app, index);
370 self.set_geometry(
371 app,
372 SliverGeometry::new().scroll_offset_correction(correction),
373 );
374 return;
375 };
376 let layout_offset = self.index_to_layout_offset(app, index);
377 let child_parent_data = child
378 .as_object()
379 .parent_data_of_mut::<SliverMultiBoxAdaptorParentData>(app);
380 child_parent_data.set_layout_offset(Some(layout_offset));
381 debug_assert_eq!(child_parent_data.index, Some(index));
382 trailing_child_with_layout.get_or_insert(child);
383 index -= 1;
384 }
385
386 if trailing_child_with_layout.is_none() {
387 let first_child = self.first_child(app).expect("the child list is not empty");
388 let child_constraints = self.child_constraints(app, self.index_of(app, first_child));
389 first_child.layout(app, child_constraints, false);
390 let layout_offset = self.index_to_layout_offset(app, first_index);
391 first_child
392 .as_object()
393 .parent_data_of_mut::<SliverMultiBoxAdaptorParentData>(app)
394 .set_layout_offset(Some(layout_offset));
395 trailing_child_with_layout = Some(first_child);
396 }
397
398 let mut estimated_max_scroll_offset = f64::INFINITY;
399 let mut index = self.index_of(app, trailing_child_with_layout.expect("set above")) + 1;
400 while target_last_index.is_none_or(|last_index| index <= last_index) {
401 let after = trailing_child_with_layout.expect("set above");
402 let mut child = self.child_after(app, after);
403 if child.is_none_or(|current| self.index_of(app, current) != index) {
404 let child_constraints = self.child_constraints(app, index);
405 child = self.insert_and_layout_child(app, child_constraints, Some(after), false);
406 if child.is_none() {
407 estimated_max_scroll_offset = self.index_to_layout_offset(app, index);
409 break;
410 }
411 } else {
412 let child_constraints = self.child_constraints(app, index);
413 child
414 .expect("checked")
415 .layout(app, child_constraints, false);
416 }
417 let child = child.expect("set above");
418 trailing_child_with_layout = Some(child);
419 debug_assert_eq!(
420 child
421 .as_object()
422 .parent_data_of::<SliverMultiBoxAdaptorParentData>(app)
423 .index,
424 Some(index)
425 );
426 let layout_offset = self.index_to_layout_offset(app, index);
427 child
428 .as_object()
429 .parent_data_of_mut::<SliverMultiBoxAdaptorParentData>(app)
430 .set_layout_offset(Some(layout_offset));
431 index += 1;
432 }
433
434 let last_child = self.last_child(app).expect("the child list is not empty");
435 let last_index = self.index_of(app, last_child);
436 let leading_scroll_offset = self.index_to_layout_offset(app, first_index);
437 let trailing_scroll_offset = self.index_to_layout_offset(app, last_index + 1);
438
439 debug_assert!(
440 first_index == 0
441 || RenderSliver::child_scroll_offset(
442 self,
443 app,
444 self.first_child(app).expect("not empty").as_object()
445 )
446 .expect("laid out")
447 - scroll_offset
448 <= PRECISION_ERROR_TOLERANCE
449 );
450 debug_assert!(self.debug_assert_child_list_is_non_empty_and_contiguous(app));
451 debug_assert_eq!(
452 self.index_of(app, self.first_child(app).expect("not empty")),
453 first_index
454 );
455 debug_assert!(target_last_index.is_none_or(|target| last_index <= target));
456
457 estimated_max_scroll_offset =
458 estimated_max_scroll_offset.min(self.estimate_max_scroll_offset(
459 app,
460 constraints,
461 Some(first_index),
462 Some(last_index),
463 Some(leading_scroll_offset),
464 Some(trailing_scroll_offset),
465 ));
466
467 let paint_extent =
468 calculate_paint_offset(constraints, leading_scroll_offset, trailing_scroll_offset);
469 let cache_extent =
470 calculate_cache_offset(constraints, leading_scroll_offset, trailing_scroll_offset);
471
472 let target_end_scroll_offset_for_paint =
473 constraints.scroll_offset + constraints.remaining_paint_extent;
474 let target_last_index_for_paint =
475 target_end_scroll_offset_for_paint.is_finite().then(|| {
476 self.get_max_child_index_for_scroll_offset(app, target_end_scroll_offset_for_paint)
477 });
478
479 self.set_geometry(
480 app,
481 SliverGeometry::new()
482 .scroll_extent(estimated_max_scroll_offset)
483 .paint_extent(paint_extent)
484 .cache_extent(cache_extent)
485 .max_paint_extent(estimated_max_scroll_offset)
486 .has_visual_overflow(
488 target_last_index_for_paint.is_some_and(|target| last_index >= target)
489 || constraints.scroll_offset > 0.0,
490 ),
491 );
492
493 if estimated_max_scroll_offset == trailing_scroll_offset {
496 child_manager.set_did_underflow(app, true);
497 }
498 child_manager.did_finish_layout(app);
499 }
500}
501
502macro_rules! fixed_extent_box_adaptor_leaf {
505 ($leaf:ty) => {
506 impl ContainerRenderObjectMixin for $leaf {
507 type ChildType = AnyRenderBox;
508 type ParentDataType = SliverMultiBoxAdaptorParentData;
509
510 fn container_data(
511 self: RenderHandle<Self>,
512 app: &App,
513 ) -> &ContainerRenderObjectData<AnyRenderBox> {
514 &self.get(app).container
515 }
516
517 fn container_data_mut(
518 self: RenderHandle<Self>,
519 app: &mut App,
520 ) -> &mut ContainerRenderObjectData<AnyRenderBox> {
521 &mut self.get_mut(app).container
522 }
523
524 fn insert(
525 self: RenderHandle<Self>,
526 app: &mut App,
527 child: AnyRenderBox,
528 after: Option<AnyRenderBox>,
529 ) {
530 RenderSliverMultiBoxAdaptor::insert(self, app, child, after)
531 }
532
533 fn move_child(
534 self: RenderHandle<Self>,
535 app: &mut App,
536 child: AnyRenderBox,
537 after: Option<AnyRenderBox>,
538 ) {
539 RenderSliverMultiBoxAdaptor::move_child(self, app, child, after)
540 }
541
542 fn remove(self: RenderHandle<Self>, app: &mut App, child: AnyRenderBox) {
543 RenderSliverMultiBoxAdaptor::remove(self, app, child)
544 }
545
546 fn remove_all(self: RenderHandle<Self>, app: &mut App) {
547 RenderSliverMultiBoxAdaptor::remove_all(self, app)
548 }
549 }
550
551 impl RenderSliverHelpers for $leaf {}
552
553 impl RenderSliverWithKeepAliveMixin for $leaf {}
554
555 impl RenderSliverMultiBoxAdaptor for $leaf {
556 crate::render_sliver_multi_box_adaptor_accessors!();
557
558 fn paint_extent_of(self: RenderHandle<Self>, app: &App, child: AnyRenderBox) -> f64 {
559 RenderSliverFixedExtentBoxAdaptor::paint_extent_of(self, app, child)
560 }
561 }
562
563 impl RenderObject for $leaf {
564 crate::render_object_accessors!();
565
566 fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
567 RenderSliverMultiBoxAdaptor::setup_parent_data(self, app, child);
568 RenderSliverWithKeepAliveMixin::setup_parent_data(self, app, child);
569 }
570
571 fn visit_children(
572 self: RenderHandle<Self>,
573 app: &App,
574 visitor: &mut dyn FnMut(AnyRenderObject),
575 ) {
576 RenderSliverMultiBoxAdaptor::visit_children(self, app, visitor)
577 }
578
579 fn did_attach(self: RenderHandle<Self>, app: &mut App, owner: Handle<PipelineOwner>) {
580 RenderSliverMultiBoxAdaptor::did_attach(self, app, owner)
581 }
582
583 fn did_detach(self: RenderHandle<Self>, app: &mut App) {
584 RenderSliverMultiBoxAdaptor::did_detach(self, app)
585 }
586
587 fn redepth_children(self: RenderHandle<Self>, app: &mut App) {
588 RenderSliverMultiBoxAdaptor::redepth_children(self, app)
589 }
590
591 fn apply_paint_transform(
592 self: RenderHandle<Self>,
593 app: &App,
594 child: AnyRenderObject,
595 transform: &mut Matrix4,
596 ) {
597 RenderSliverMultiBoxAdaptor::apply_paint_transform(self, app, child, transform)
598 }
599
600 fn paint(
601 self: RenderHandle<Self>,
602 app: &mut App,
603 context: &mut PaintingContext,
604 offset: Offset,
605 ) {
606 RenderSliverMultiBoxAdaptor::paint(self, app, context, offset)
607 }
608
609 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
610 RenderSliverFixedExtentBoxAdaptor::perform_layout(self, app)
611 }
612 }
613
614 impl RenderSliver for $leaf {
615 crate::render_sliver_accessors!();
616
617 fn hit_test_children(
618 self: RenderHandle<Self>,
619 app: &mut App,
620 result: &mut SliverHitTestResult<'_>,
621 main_axis_position: f64,
622 cross_axis_position: f64,
623 ) -> bool {
624 RenderSliverMultiBoxAdaptor::hit_test_children(
625 self,
626 app,
627 result,
628 main_axis_position,
629 cross_axis_position,
630 )
631 }
632
633 fn child_main_axis_position(
634 self: RenderHandle<Self>,
635 app: &App,
636 child: AnyRenderObject,
637 ) -> f64 {
638 RenderSliverMultiBoxAdaptor::child_main_axis_position(self, app, child)
639 }
640
641 fn child_scroll_offset(
642 self: RenderHandle<Self>,
643 app: &App,
644 child: AnyRenderObject,
645 ) -> Option<f64> {
646 RenderSliverMultiBoxAdaptor::child_scroll_offset(self, app, child)
647 }
648 }
649 };
650}
651
652pub struct RenderSliverFixedExtentList {
662 render_object: RenderObjectData,
663 render_sliver: RenderSliverData,
664 container: ContainerRenderObjectData<AnyRenderBox>,
665 adaptor: RenderSliverMultiBoxAdaptorData,
666 fixed_extent: RenderSliverFixedExtentBoxAdaptorData,
667 item_extent: f64,
668}
669
670impl RenderSliverFixedExtentList {
671 pub fn new(
674 app: &mut App,
675 child_manager: Rc<dyn RenderSliverBoxChildManager>,
676 item_extent: f64,
677 ) -> RenderHandle<RenderSliverFixedExtentList> {
678 RenderHandle::new_sliver(
679 app,
680 RenderSliverFixedExtentList {
681 render_object: RenderObjectData::new(),
682 render_sliver: RenderSliverData::new(),
683 container: ContainerRenderObjectData::new(),
684 adaptor: RenderSliverMultiBoxAdaptorData::new(child_manager),
685 fixed_extent: RenderSliverFixedExtentBoxAdaptorData::new(),
686 item_extent,
687 },
688 )
689 }
690
691 pub fn set_item_extent(self: RenderHandle<Self>, app: &mut App, value: f64) {
693 if self.get(app).item_extent == value {
694 return;
695 }
696 self.get_mut(app).item_extent = value;
697 self.mark_needs_layout(app);
698 }
699}
700
701impl RenderSliverFixedExtentBoxAdaptor for RenderSliverFixedExtentList {
702 crate::render_sliver_fixed_extent_accessors!();
703
704 fn item_extent(self: RenderHandle<Self>, app: &App) -> Option<f64> {
705 Some(self.get(app).item_extent)
706 }
707}
708
709fixed_extent_box_adaptor_leaf!(RenderSliverFixedExtentList);
710
711pub struct RenderSliverVariedExtentList {
714 render_object: RenderObjectData,
715 render_sliver: RenderSliverData,
716 container: ContainerRenderObjectData<AnyRenderBox>,
717 adaptor: RenderSliverMultiBoxAdaptorData,
718 fixed_extent: RenderSliverFixedExtentBoxAdaptorData,
719 item_extent_builder: ItemExtentBuilder,
720}
721
722impl RenderSliverVariedExtentList {
723 pub fn new(
726 app: &mut App,
727 child_manager: Rc<dyn RenderSliverBoxChildManager>,
728 item_extent_builder: ItemExtentBuilder,
729 ) -> RenderHandle<RenderSliverVariedExtentList> {
730 RenderHandle::new_sliver(
731 app,
732 RenderSliverVariedExtentList {
733 render_object: RenderObjectData::new(),
734 render_sliver: RenderSliverData::new(),
735 container: ContainerRenderObjectData::new(),
736 adaptor: RenderSliverMultiBoxAdaptorData::new(child_manager),
737 fixed_extent: RenderSliverFixedExtentBoxAdaptorData::new(),
738 item_extent_builder,
739 },
740 )
741 }
742
743 pub fn set_item_extent_builder(
745 self: RenderHandle<Self>,
746 app: &mut App,
747 value: ItemExtentBuilder,
748 ) {
749 if Rc::ptr_eq(&self.get(app).item_extent_builder, &value) {
750 return;
751 }
752 self.get_mut(app).item_extent_builder = value;
753 self.mark_needs_layout(app);
754 }
755}
756
757impl RenderSliverFixedExtentBoxAdaptor for RenderSliverVariedExtentList {
758 crate::render_sliver_fixed_extent_accessors!();
759
760 fn item_extent(self: RenderHandle<Self>, _app: &App) -> Option<f64> {
761 let _ = self;
762 None
763 }
764
765 fn item_extent_builder(self: RenderHandle<Self>, app: &App) -> Option<ItemExtentBuilder> {
766 Some(Rc::clone(&self.get(app).item_extent_builder))
767 }
768}
769
770fixed_extent_box_adaptor_leaf!(RenderSliverVariedExtentList);
771
772#[cfg(test)]
773mod tests {
774 use inset_embedder::Size;
775 use inset_foundation::AppCell;
776 use inset_gestures::HitTestResult;
777
778 use super::*;
779 use crate::sliver_multi_box_adaptor::test_support::TestChildManager;
780 use crate::sliver_multi_box_adaptor::viewport_test_support::ScrollHarness;
781
782 const VIEWPORT: Size = Size::new(100.0, 200.0);
783
784 fn fixed_list(
785 app: &mut App,
786 extents: Vec<f64>,
787 item_extent: f64,
788 ) -> (
789 RenderHandle<RenderSliverFixedExtentList>,
790 Rc<TestChildManager<RenderSliverFixedExtentList>>,
791 ScrollHarness,
792 ) {
793 let manager = TestChildManager::new(extents);
794 let list = RenderSliverFixedExtentList::new(app, manager.clone(), item_extent);
795 manager.attach(list);
796 let harness = ScrollHarness::new(app, list.as_sliver(), VIEWPORT);
797 (list, manager, harness)
798 }
799
800 #[test]
803 fn a_fixed_extent_list_reifies_only_the_visible_children() {
804 let cell = AppCell::new();
805 let mut app = cell.borrow_mut();
806 let (list, _, _harness) = fixed_list(&mut app, vec![50.0; 40], 50.0);
807
808 assert_eq!(
809 list.index_of(&app, list.first_child(&app).expect("built")),
810 0
811 );
812 assert_eq!(list.index_to_layout_offset(&app, 3), 150.0);
813 let geometry = list.geometry(&app);
814 assert_eq!(geometry.scroll_extent, 2000.0);
815 assert_eq!(geometry.paint_extent, 200.0);
816 assert!(geometry.has_visual_overflow);
817 assert!(list.child_count(&app) < 40);
819 }
820
821 #[test]
823 fn scrolling_collects_and_creates_children() {
824 let cell = AppCell::new();
825 let mut app = cell.borrow_mut();
826 let (list, _, harness) = fixed_list(&mut app, vec![50.0; 40], 50.0);
827 assert_eq!(
828 list.index_of(&app, list.first_child(&app).expect("built")),
829 0
830 );
831
832 harness.scroll_to(&mut app, 600.0);
833
834 let first = list.first_child(&app).expect("children were created");
835 assert_eq!(list.index_of(&app, first), 7);
837 assert_eq!(
838 RenderSliver::child_scroll_offset(list, &app, first.as_object()),
839 Some(350.0)
840 );
841 assert_eq!(
842 RenderSliver::child_main_axis_position(list, &app, first.as_object()),
843 -250.0
844 );
845 }
846
847 #[test]
849 fn a_fixed_extent_list_hit_tests_its_children() {
850 let cell = AppCell::new();
851 let mut app = cell.borrow_mut();
852 let (list, _, _harness) = fixed_list(&mut app, vec![50.0; 40], 50.0);
853
854 let mut result = HitTestResult::new();
855 let hit = list.hit_test(
856 &mut app,
857 &mut SliverHitTestResult::wrap(&mut result),
858 75.0,
859 20.0,
860 );
861
862 assert!(hit);
863 assert_eq!(result.path().len(), 2);
864 }
865
866 #[test]
869 fn a_kept_alive_child_survives_leaving_the_viewport() {
870 let cell = AppCell::new();
871 let mut app = cell.borrow_mut();
872 let (list, manager, harness) = fixed_list(&mut app, vec![50.0; 40], 50.0);
873 let first = list.first_child(&app).expect("children were created");
874 first
875 .as_object()
876 .parent_data_of_mut::<SliverMultiBoxAdaptorParentData>(&mut app)
877 .set_keep_alive(true);
878
879 harness.scroll_to(&mut app, 1000.0);
880
881 assert_eq!(list.keep_alive_children(&app), vec![first]);
883 assert!(
884 first
885 .as_object()
886 .parent_data_of::<SliverMultiBoxAdaptorParentData>(&app)
887 .kept_alive()
888 );
889 assert!(!list.paints_child(&app, first));
890 let created_before = manager.created.borrow().len();
891
892 harness.scroll_to(&mut app, 0.0);
893
894 assert_eq!(list.first_child(&app), Some(first));
896 assert!(list.keep_alive_children(&app).is_empty());
897 assert!(list.paints_child(&app, first));
898 assert!(
899 !manager.created.borrow()[created_before..].contains(&0),
900 "the kept-alive child was rebuilt"
901 );
902 }
903
904 #[test]
906 fn a_short_list_reports_underflow() {
907 let cell = AppCell::new();
908 let mut app = cell.borrow_mut();
909 let (list, manager, _harness) = fixed_list(&mut app, vec![50.0; 2], 50.0);
910
911 assert_eq!(list.geometry(&app).scroll_extent, 100.0);
912 assert_eq!(list.geometry(&app).paint_extent, 100.0);
913 assert!(manager.did_underflow.get());
914 }
915
916 #[test]
918 fn a_varied_extent_list_uses_its_builder() {
919 let cell = AppCell::new();
920 let mut app = cell.borrow_mut();
921 let manager = TestChildManager::<RenderSliverVariedExtentList>::new(vec![40.0; 10]);
922 let builder: ItemExtentBuilder =
923 Rc::new(|index, _| Some(if index % 2 == 0 { 40.0 } else { 20.0 }));
924 let list = RenderSliverVariedExtentList::new(&mut app, manager.clone(), builder);
925 manager.attach(list);
926 let _harness = ScrollHarness::new(&mut app, list.as_sliver(), VIEWPORT);
927
928 assert_eq!(list.index_to_layout_offset(&app, 4), 120.0);
930 assert_eq!(list.item_extent(&app), None);
931 assert_eq!(list.geometry(&app).paint_extent, 200.0);
932 assert_eq!(list.geometry(&app).scroll_extent, 400.0);
934 }
935
936 #[test]
938 fn the_child_index_range_brackets_the_scroll_offset() {
939 let cell = AppCell::new();
940 let mut app = cell.borrow_mut();
941 let (list, _, _harness) = fixed_list(&mut app, vec![50.0; 40], 50.0);
942 let constraints = list.constraints(&app);
943
944 assert_eq!(list.get_min_child_index_for_scroll_offset(&app, 120.0), 2);
945 assert_eq!(list.get_max_child_index_for_scroll_offset(&app, 120.0), 2);
946 assert_eq!(list.get_max_child_index_for_scroll_offset(&app, 0.0), 0);
947 assert_eq!(
948 list.compute_max_scroll_offset(&mut app, constraints),
949 2000.0
950 );
951 }
952}