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::builder()
350 .scroll_extent(max)
351 .max_paint_extent(max)
352 .build(),
353 );
354 child_manager.did_finish_layout(app);
355 return;
356 }
357 }
358
359 let mut trailing_child_with_layout: Option<AnyRenderBox> = None;
360
361 let first_child = self.first_child(app).expect("added above");
362 let mut index = self.index_of(app, first_child) - 1;
363 while index >= first_index {
364 let child_constraints = self.child_constraints(app, index);
365 let Some(child) = self.insert_and_layout_leading_child(app, child_constraints, false)
366 else {
367 let correction = self.index_to_layout_offset(app, index);
371 self.set_geometry(
372 app,
373 SliverGeometry::builder()
374 .scroll_offset_correction(correction)
375 .build(),
376 );
377 return;
378 };
379 let layout_offset = self.index_to_layout_offset(app, index);
380 let child_parent_data = child
381 .as_object()
382 .parent_data_of_mut::<SliverMultiBoxAdaptorParentData>(app);
383 child_parent_data.set_layout_offset(Some(layout_offset));
384 debug_assert_eq!(child_parent_data.index, Some(index));
385 trailing_child_with_layout.get_or_insert(child);
386 index -= 1;
387 }
388
389 if trailing_child_with_layout.is_none() {
390 let first_child = self.first_child(app).expect("the child list is not empty");
391 let child_constraints = self.child_constraints(app, self.index_of(app, first_child));
392 first_child.layout(app, child_constraints, false);
393 let layout_offset = self.index_to_layout_offset(app, first_index);
394 first_child
395 .as_object()
396 .parent_data_of_mut::<SliverMultiBoxAdaptorParentData>(app)
397 .set_layout_offset(Some(layout_offset));
398 trailing_child_with_layout = Some(first_child);
399 }
400
401 let mut estimated_max_scroll_offset = f64::INFINITY;
402 let mut index = self.index_of(app, trailing_child_with_layout.expect("set above")) + 1;
403 while target_last_index.is_none_or(|last_index| index <= last_index) {
404 let after = trailing_child_with_layout.expect("set above");
405 let mut child = self.child_after(app, after);
406 if child.is_none_or(|current| self.index_of(app, current) != index) {
407 let child_constraints = self.child_constraints(app, index);
408 child = self.insert_and_layout_child(app, child_constraints, Some(after), false);
409 if child.is_none() {
410 estimated_max_scroll_offset = self.index_to_layout_offset(app, index);
412 break;
413 }
414 } else {
415 let child_constraints = self.child_constraints(app, index);
416 child
417 .expect("checked")
418 .layout(app, child_constraints, false);
419 }
420 let child = child.expect("set above");
421 trailing_child_with_layout = Some(child);
422 debug_assert_eq!(
423 child
424 .as_object()
425 .parent_data_of::<SliverMultiBoxAdaptorParentData>(app)
426 .index,
427 Some(index)
428 );
429 let layout_offset = self.index_to_layout_offset(app, index);
430 child
431 .as_object()
432 .parent_data_of_mut::<SliverMultiBoxAdaptorParentData>(app)
433 .set_layout_offset(Some(layout_offset));
434 index += 1;
435 }
436
437 let last_child = self.last_child(app).expect("the child list is not empty");
438 let last_index = self.index_of(app, last_child);
439 let leading_scroll_offset = self.index_to_layout_offset(app, first_index);
440 let trailing_scroll_offset = self.index_to_layout_offset(app, last_index + 1);
441
442 debug_assert!(
443 first_index == 0
444 || RenderSliver::child_scroll_offset(
445 self,
446 app,
447 self.first_child(app).expect("not empty").as_object()
448 )
449 .expect("laid out")
450 - scroll_offset
451 <= PRECISION_ERROR_TOLERANCE
452 );
453 debug_assert!(self.debug_assert_child_list_is_non_empty_and_contiguous(app));
454 debug_assert_eq!(
455 self.index_of(app, self.first_child(app).expect("not empty")),
456 first_index
457 );
458 debug_assert!(target_last_index.is_none_or(|target| last_index <= target));
459
460 estimated_max_scroll_offset =
461 estimated_max_scroll_offset.min(self.estimate_max_scroll_offset(
462 app,
463 constraints,
464 Some(first_index),
465 Some(last_index),
466 Some(leading_scroll_offset),
467 Some(trailing_scroll_offset),
468 ));
469
470 let paint_extent =
471 calculate_paint_offset(constraints, leading_scroll_offset, trailing_scroll_offset);
472 let cache_extent =
473 calculate_cache_offset(constraints, leading_scroll_offset, trailing_scroll_offset);
474
475 let target_end_scroll_offset_for_paint =
476 constraints.scroll_offset + constraints.remaining_paint_extent;
477 let target_last_index_for_paint =
478 target_end_scroll_offset_for_paint.is_finite().then(|| {
479 self.get_max_child_index_for_scroll_offset(app, target_end_scroll_offset_for_paint)
480 });
481
482 self.set_geometry(
483 app,
484 SliverGeometry::builder()
485 .scroll_extent(estimated_max_scroll_offset)
486 .paint_extent(paint_extent)
487 .cache_extent(cache_extent)
488 .max_paint_extent(estimated_max_scroll_offset)
489 .has_visual_overflow(
491 target_last_index_for_paint.is_some_and(|target| last_index >= target)
492 || constraints.scroll_offset > 0.0,
493 )
494 .build(),
495 );
496
497 if estimated_max_scroll_offset == trailing_scroll_offset {
500 child_manager.set_did_underflow(app, true);
501 }
502 child_manager.did_finish_layout(app);
503 }
504}
505
506macro_rules! fixed_extent_box_adaptor_leaf {
509 ($leaf:ty) => {
510 impl ContainerRenderObjectMixin for $leaf {
511 type ChildType = AnyRenderBox;
512 type ParentDataType = SliverMultiBoxAdaptorParentData;
513
514 fn container_data(
515 self: RenderHandle<Self>,
516 app: &App,
517 ) -> &ContainerRenderObjectData<AnyRenderBox> {
518 &self.get(app).container
519 }
520
521 fn container_data_mut(
522 self: RenderHandle<Self>,
523 app: &mut App,
524 ) -> &mut ContainerRenderObjectData<AnyRenderBox> {
525 &mut self.get_mut(app).container
526 }
527
528 fn insert(
529 self: RenderHandle<Self>,
530 app: &mut App,
531 child: AnyRenderBox,
532 after: Option<AnyRenderBox>,
533 ) {
534 RenderSliverMultiBoxAdaptor::insert(self, app, child, after)
535 }
536
537 fn move_child(
538 self: RenderHandle<Self>,
539 app: &mut App,
540 child: AnyRenderBox,
541 after: Option<AnyRenderBox>,
542 ) {
543 RenderSliverMultiBoxAdaptor::move_child(self, app, child, after)
544 }
545
546 fn remove(self: RenderHandle<Self>, app: &mut App, child: AnyRenderBox) {
547 RenderSliverMultiBoxAdaptor::remove(self, app, child)
548 }
549
550 fn remove_all(self: RenderHandle<Self>, app: &mut App) {
551 RenderSliverMultiBoxAdaptor::remove_all(self, app)
552 }
553 }
554
555 impl RenderSliverHelpers for $leaf {}
556
557 impl RenderSliverWithKeepAliveMixin for $leaf {}
558
559 impl RenderSliverMultiBoxAdaptor for $leaf {
560 crate::render_sliver_multi_box_adaptor_accessors!();
561
562 fn paint_extent_of(self: RenderHandle<Self>, app: &App, child: AnyRenderBox) -> f64 {
563 RenderSliverFixedExtentBoxAdaptor::paint_extent_of(self, app, child)
564 }
565 }
566
567 impl RenderObject for $leaf {
568 crate::render_object_accessors!();
569
570 fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
571 RenderSliverMultiBoxAdaptor::setup_parent_data(self, app, child);
572 RenderSliverWithKeepAliveMixin::setup_parent_data(self, app, child);
573 }
574
575 fn visit_children(
576 self: RenderHandle<Self>,
577 app: &App,
578 visitor: &mut dyn FnMut(AnyRenderObject),
579 ) {
580 RenderSliverMultiBoxAdaptor::visit_children(self, app, visitor)
581 }
582
583 fn did_attach(self: RenderHandle<Self>, app: &mut App, owner: Handle<PipelineOwner>) {
584 RenderSliverMultiBoxAdaptor::did_attach(self, app, owner)
585 }
586
587 fn did_detach(self: RenderHandle<Self>, app: &mut App) {
588 RenderSliverMultiBoxAdaptor::did_detach(self, app)
589 }
590
591 fn redepth_children(self: RenderHandle<Self>, app: &mut App) {
592 RenderSliverMultiBoxAdaptor::redepth_children(self, app)
593 }
594
595 fn apply_paint_transform(
596 self: RenderHandle<Self>,
597 app: &App,
598 child: AnyRenderObject,
599 transform: &mut Matrix4,
600 ) {
601 RenderSliverMultiBoxAdaptor::apply_paint_transform(self, app, child, transform)
602 }
603
604 fn paint(
605 self: RenderHandle<Self>,
606 app: &mut App,
607 context: &mut PaintingContext,
608 offset: Offset,
609 ) {
610 RenderSliverMultiBoxAdaptor::paint(self, app, context, offset)
611 }
612
613 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
614 RenderSliverFixedExtentBoxAdaptor::perform_layout(self, app)
615 }
616 }
617
618 impl RenderSliver for $leaf {
619 crate::render_sliver_accessors!();
620
621 fn hit_test_children(
622 self: RenderHandle<Self>,
623 app: &mut App,
624 result: &mut SliverHitTestResult<'_>,
625 main_axis_position: f64,
626 cross_axis_position: f64,
627 ) -> bool {
628 RenderSliverMultiBoxAdaptor::hit_test_children(
629 self,
630 app,
631 result,
632 main_axis_position,
633 cross_axis_position,
634 )
635 }
636
637 fn child_main_axis_position(
638 self: RenderHandle<Self>,
639 app: &App,
640 child: AnyRenderObject,
641 ) -> f64 {
642 RenderSliverMultiBoxAdaptor::child_main_axis_position(self, app, child)
643 }
644
645 fn child_scroll_offset(
646 self: RenderHandle<Self>,
647 app: &App,
648 child: AnyRenderObject,
649 ) -> Option<f64> {
650 RenderSliverMultiBoxAdaptor::child_scroll_offset(self, app, child)
651 }
652 }
653 };
654}
655
656pub struct RenderSliverFixedExtentList {
666 render_object: RenderObjectData,
667 render_sliver: RenderSliverData,
668 container: ContainerRenderObjectData<AnyRenderBox>,
669 adaptor: RenderSliverMultiBoxAdaptorData,
670 fixed_extent: RenderSliverFixedExtentBoxAdaptorData,
671 item_extent: f64,
672}
673
674impl RenderSliverFixedExtentList {
675 pub fn new(
678 app: &mut App,
679 child_manager: Rc<dyn RenderSliverBoxChildManager>,
680 item_extent: f64,
681 ) -> RenderHandle<RenderSliverFixedExtentList> {
682 RenderHandle::new_sliver(
683 app,
684 RenderSliverFixedExtentList {
685 render_object: RenderObjectData::new(),
686 render_sliver: RenderSliverData::new(),
687 container: ContainerRenderObjectData::new(),
688 adaptor: RenderSliverMultiBoxAdaptorData::new(child_manager),
689 fixed_extent: RenderSliverFixedExtentBoxAdaptorData::new(),
690 item_extent,
691 },
692 )
693 }
694
695 pub fn set_item_extent(self: RenderHandle<Self>, app: &mut App, value: f64) {
697 if self.get(app).item_extent == value {
698 return;
699 }
700 self.get_mut(app).item_extent = value;
701 self.mark_needs_layout(app);
702 }
703}
704
705impl RenderSliverFixedExtentBoxAdaptor for RenderSliverFixedExtentList {
706 crate::render_sliver_fixed_extent_accessors!();
707
708 fn item_extent(self: RenderHandle<Self>, app: &App) -> Option<f64> {
709 Some(self.get(app).item_extent)
710 }
711}
712
713fixed_extent_box_adaptor_leaf!(RenderSliverFixedExtentList);
714
715pub struct RenderSliverVariedExtentList {
718 render_object: RenderObjectData,
719 render_sliver: RenderSliverData,
720 container: ContainerRenderObjectData<AnyRenderBox>,
721 adaptor: RenderSliverMultiBoxAdaptorData,
722 fixed_extent: RenderSliverFixedExtentBoxAdaptorData,
723 item_extent_builder: ItemExtentBuilder,
724}
725
726impl RenderSliverVariedExtentList {
727 pub fn new(
730 app: &mut App,
731 child_manager: Rc<dyn RenderSliverBoxChildManager>,
732 item_extent_builder: ItemExtentBuilder,
733 ) -> RenderHandle<RenderSliverVariedExtentList> {
734 RenderHandle::new_sliver(
735 app,
736 RenderSliverVariedExtentList {
737 render_object: RenderObjectData::new(),
738 render_sliver: RenderSliverData::new(),
739 container: ContainerRenderObjectData::new(),
740 adaptor: RenderSliverMultiBoxAdaptorData::new(child_manager),
741 fixed_extent: RenderSliverFixedExtentBoxAdaptorData::new(),
742 item_extent_builder,
743 },
744 )
745 }
746
747 pub fn set_item_extent_builder(
749 self: RenderHandle<Self>,
750 app: &mut App,
751 value: ItemExtentBuilder,
752 ) {
753 if Rc::ptr_eq(&self.get(app).item_extent_builder, &value) {
754 return;
755 }
756 self.get_mut(app).item_extent_builder = value;
757 self.mark_needs_layout(app);
758 }
759}
760
761impl RenderSliverFixedExtentBoxAdaptor for RenderSliverVariedExtentList {
762 crate::render_sliver_fixed_extent_accessors!();
763
764 fn item_extent(self: RenderHandle<Self>, _app: &App) -> Option<f64> {
765 let _ = self;
766 None
767 }
768
769 fn item_extent_builder(self: RenderHandle<Self>, app: &App) -> Option<ItemExtentBuilder> {
770 Some(Rc::clone(&self.get(app).item_extent_builder))
771 }
772}
773
774fixed_extent_box_adaptor_leaf!(RenderSliverVariedExtentList);
775
776#[cfg(test)]
777mod tests {
778 use inset_embedder::Size;
779 use inset_foundation::AppCell;
780 use inset_gestures::HitTestResult;
781
782 use super::*;
783 use crate::sliver_multi_box_adaptor::test_support::TestChildManager;
784 use crate::sliver_multi_box_adaptor::viewport_test_support::ScrollHarness;
785
786 const VIEWPORT: Size = Size::new(100.0, 200.0);
787
788 fn fixed_list(
789 app: &mut App,
790 extents: Vec<f64>,
791 item_extent: f64,
792 ) -> (
793 RenderHandle<RenderSliverFixedExtentList>,
794 Rc<TestChildManager<RenderSliverFixedExtentList>>,
795 ScrollHarness,
796 ) {
797 let manager = TestChildManager::new(extents);
798 let list = RenderSliverFixedExtentList::new(app, manager.clone(), item_extent);
799 manager.attach(list);
800 let harness = ScrollHarness::new(app, list.as_sliver(), VIEWPORT);
801 (list, manager, harness)
802 }
803
804 #[test]
807 fn a_fixed_extent_list_reifies_only_the_visible_children() {
808 let cell = AppCell::new();
809 let mut app = cell.borrow_mut();
810 let (list, _, _harness) = fixed_list(&mut app, vec![50.0; 40], 50.0);
811
812 assert_eq!(
813 list.index_of(&app, list.first_child(&app).expect("built")),
814 0
815 );
816 assert_eq!(list.index_to_layout_offset(&app, 3), 150.0);
817 let geometry = list.geometry(&app);
818 assert_eq!(geometry.scroll_extent, 2000.0);
819 assert_eq!(geometry.paint_extent, 200.0);
820 assert!(geometry.has_visual_overflow);
821 assert!(list.child_count(&app) < 40);
823 }
824
825 #[test]
827 fn scrolling_collects_and_creates_children() {
828 let cell = AppCell::new();
829 let mut app = cell.borrow_mut();
830 let (list, _, harness) = fixed_list(&mut app, vec![50.0; 40], 50.0);
831 assert_eq!(
832 list.index_of(&app, list.first_child(&app).expect("built")),
833 0
834 );
835
836 harness.scroll_to(&mut app, 600.0);
837
838 let first = list.first_child(&app).expect("children were created");
839 assert_eq!(list.index_of(&app, first), 7);
841 assert_eq!(
842 RenderSliver::child_scroll_offset(list, &app, first.as_object()),
843 Some(350.0)
844 );
845 assert_eq!(
846 RenderSliver::child_main_axis_position(list, &app, first.as_object()),
847 -250.0
848 );
849 }
850
851 #[test]
853 fn a_fixed_extent_list_hit_tests_its_children() {
854 let cell = AppCell::new();
855 let mut app = cell.borrow_mut();
856 let (list, _, _harness) = fixed_list(&mut app, vec![50.0; 40], 50.0);
857
858 let mut result = HitTestResult::new();
859 let hit = list.hit_test(
860 &mut app,
861 &mut SliverHitTestResult::wrap(&mut result),
862 75.0,
863 20.0,
864 );
865
866 assert!(hit);
867 assert_eq!(result.path().len(), 2);
868 }
869
870 #[test]
873 fn a_kept_alive_child_survives_leaving_the_viewport() {
874 let cell = AppCell::new();
875 let mut app = cell.borrow_mut();
876 let (list, manager, harness) = fixed_list(&mut app, vec![50.0; 40], 50.0);
877 let first = list.first_child(&app).expect("children were created");
878 first
879 .as_object()
880 .parent_data_of_mut::<SliverMultiBoxAdaptorParentData>(&mut app)
881 .set_keep_alive(true);
882
883 harness.scroll_to(&mut app, 1000.0);
884
885 assert_eq!(list.keep_alive_children(&app), vec![first]);
887 assert!(
888 first
889 .as_object()
890 .parent_data_of::<SliverMultiBoxAdaptorParentData>(&app)
891 .kept_alive()
892 );
893 assert!(!list.paints_child(&app, first));
894 let created_before = manager.created.borrow().len();
895
896 harness.scroll_to(&mut app, 0.0);
897
898 assert_eq!(list.first_child(&app), Some(first));
900 assert!(list.keep_alive_children(&app).is_empty());
901 assert!(list.paints_child(&app, first));
902 assert!(
903 !manager.created.borrow()[created_before..].contains(&0),
904 "the kept-alive child was rebuilt"
905 );
906 }
907
908 #[test]
910 fn a_short_list_reports_underflow() {
911 let cell = AppCell::new();
912 let mut app = cell.borrow_mut();
913 let (list, manager, _harness) = fixed_list(&mut app, vec![50.0; 2], 50.0);
914
915 assert_eq!(list.geometry(&app).scroll_extent, 100.0);
916 assert_eq!(list.geometry(&app).paint_extent, 100.0);
917 assert!(manager.did_underflow.get());
918 }
919
920 #[test]
922 fn a_varied_extent_list_uses_its_builder() {
923 let cell = AppCell::new();
924 let mut app = cell.borrow_mut();
925 let manager = TestChildManager::<RenderSliverVariedExtentList>::new(vec![40.0; 10]);
926 let builder: ItemExtentBuilder =
927 Rc::new(|index, _| Some(if index % 2 == 0 { 40.0 } else { 20.0 }));
928 let list = RenderSliverVariedExtentList::new(&mut app, manager.clone(), builder);
929 manager.attach(list);
930 let _harness = ScrollHarness::new(&mut app, list.as_sliver(), VIEWPORT);
931
932 assert_eq!(list.index_to_layout_offset(&app, 4), 120.0);
934 assert_eq!(list.item_extent(&app), None);
935 assert_eq!(list.geometry(&app).paint_extent, 200.0);
936 assert_eq!(list.geometry(&app).scroll_extent, 400.0);
938 }
939
940 #[test]
942 fn the_child_index_range_brackets_the_scroll_offset() {
943 let cell = AppCell::new();
944 let mut app = cell.borrow_mut();
945 let (list, _, _harness) = fixed_list(&mut app, vec![50.0; 40], 50.0);
946 let constraints = list.constraints(&app);
947
948 assert_eq!(list.get_min_child_index_for_scroll_offset(&app, 120.0), 2);
949 assert_eq!(list.get_max_child_index_for_scroll_offset(&app, 120.0), 2);
950 assert_eq!(list.get_max_child_index_for_scroll_offset(&app, 0.0), 0);
951 assert_eq!(
952 list.compute_max_scroll_offset(&mut app, constraints),
953 2000.0
954 );
955 }
956}