1use std::any::{Any, TypeId};
4use std::fmt::{self, Debug, Display};
5use std::rc::Rc;
6
7use inset_embedder::{Matrix4, Offset};
8use inset_foundation::{App, Handle, PRECISION_ERROR_TOLERANCE};
9use inset_painting::axis_direction_is_reversed;
10
11use crate::box_::{AnyRenderBox, BoxConstraints};
12use crate::object::{
13 AnyRenderObject, ContainerParentData, ContainerParentDataMixin, ContainerRenderObjectData,
14 ContainerRenderObjectMixin, ParentData, RenderHandle, RenderObject, RenderObjectData,
15};
16use crate::painting_context::PaintingContext;
17use crate::pipeline_owner::PipelineOwner;
18use crate::sliver::{
19 RenderSliver, RenderSliverData, RenderSliverHelpers, SliverConstraints, SliverGeometry,
20 SliverHitTestResult, calculate_cache_offset, calculate_paint_offset,
21};
22use crate::sliver_multi_box_adaptor::{
23 RenderSliverBoxChildManager, RenderSliverMultiBoxAdaptor, RenderSliverMultiBoxAdaptorData,
24 RenderSliverWithKeepAliveMixin, SliverMultiBoxAdaptorParentData,
25};
26
27#[derive(Clone, Copy)]
40pub struct SliverGridGeometry {
41 pub scroll_offset: f64,
44 pub cross_axis_offset: f64,
50 pub main_axis_extent: f64,
55 pub cross_axis_extent: f64,
60}
61
62impl SliverGridGeometry {
63 pub const fn new(
65 scroll_offset: f64,
66 cross_axis_offset: f64,
67 main_axis_extent: f64,
68 cross_axis_extent: f64,
69 ) -> SliverGridGeometry {
70 SliverGridGeometry {
71 scroll_offset,
72 cross_axis_offset,
73 main_axis_extent,
74 cross_axis_extent,
75 }
76 }
77
78 pub fn trailing_scroll_offset(&self) -> f64 {
81 self.scroll_offset + self.main_axis_extent
82 }
83
84 pub fn get_box_constraints(&self, constraints: SliverConstraints) -> BoxConstraints {
87 constraints.as_box_constraints(
88 self.main_axis_extent,
89 self.main_axis_extent,
90 Some(self.cross_axis_extent),
91 )
92 }
93}
94
95impl Debug for SliverGridGeometry {
96 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97 write!(
98 f,
99 "SliverGridGeometry(scrollOffset: {}, crossAxisOffset: {}, mainAxisExtent: {}, crossAxisExtent: {})",
100 self.scroll_offset,
101 self.cross_axis_offset,
102 self.main_axis_extent,
103 self.cross_axis_extent
104 )
105 }
106}
107
108pub trait SliverGridLayout: Debug {
128 fn get_min_child_index_for_scroll_offset(&self, scroll_offset: f64) -> i32;
130
131 fn get_max_child_index_for_scroll_offset(&self, scroll_offset: f64) -> i32;
133
134 fn get_geometry_for_child_index(&self, index: i32) -> SliverGridGeometry;
136
137 fn compute_max_scroll_offset(&self, child_count: i32) -> f64;
140}
141
142#[derive(Clone, Copy, Debug)]
151pub struct SliverGridRegularTileLayout {
152 pub cross_axis_count: i32,
154 pub main_axis_stride: f64,
157 pub cross_axis_stride: f64,
160 pub child_main_axis_extent: f64,
163 pub child_cross_axis_extent: f64,
166 pub reverse_cross_axis: bool,
176}
177
178impl SliverGridRegularTileLayout {
179 pub fn new(
184 cross_axis_count: i32,
185 main_axis_stride: f64,
186 cross_axis_stride: f64,
187 child_main_axis_extent: f64,
188 child_cross_axis_extent: f64,
189 reverse_cross_axis: bool,
190 ) -> SliverGridRegularTileLayout {
191 debug_assert!(cross_axis_count > 0);
192 debug_assert!(main_axis_stride >= 0.0);
193 debug_assert!(cross_axis_stride >= 0.0);
194 debug_assert!(child_main_axis_extent >= 0.0);
195 debug_assert!(child_cross_axis_extent >= 0.0);
196 SliverGridRegularTileLayout {
197 cross_axis_count,
198 main_axis_stride,
199 cross_axis_stride,
200 child_main_axis_extent,
201 child_cross_axis_extent,
202 reverse_cross_axis,
203 }
204 }
205
206 fn get_offset_from_start_in_cross_axis(&self, cross_axis_start: f64) -> f64 {
207 if self.reverse_cross_axis {
208 return f64::from(self.cross_axis_count) * self.cross_axis_stride
209 - cross_axis_start
210 - self.child_cross_axis_extent
211 - (self.cross_axis_stride - self.child_cross_axis_extent);
212 }
213 cross_axis_start
214 }
215}
216
217impl SliverGridLayout for SliverGridRegularTileLayout {
218 fn get_min_child_index_for_scroll_offset(&self, scroll_offset: f64) -> i32 {
219 if self.main_axis_stride > PRECISION_ERROR_TOLERANCE {
220 self.cross_axis_count * (scroll_offset / self.main_axis_stride).trunc() as i32
221 } else {
222 0
223 }
224 }
225
226 fn get_max_child_index_for_scroll_offset(&self, scroll_offset: f64) -> i32 {
227 if self.main_axis_stride > 0.0 {
228 let main_axis_count = (scroll_offset / self.main_axis_stride).ceil() as i32;
229 return (self.cross_axis_count * main_axis_count - 1).max(0);
230 }
231 0
232 }
233
234 fn get_geometry_for_child_index(&self, index: i32) -> SliverGridGeometry {
235 let cross_axis_start = f64::from(index % self.cross_axis_count) * self.cross_axis_stride;
236 SliverGridGeometry::new(
237 f64::from(index / self.cross_axis_count) * self.main_axis_stride,
238 self.get_offset_from_start_in_cross_axis(cross_axis_start),
239 self.child_main_axis_extent,
240 self.child_cross_axis_extent,
241 )
242 }
243
244 fn compute_max_scroll_offset(&self, child_count: i32) -> f64 {
245 if child_count == 0 {
246 return 0.0;
248 }
249 let main_axis_count = ((child_count - 1) / self.cross_axis_count) + 1;
250 let main_axis_spacing = self.main_axis_stride - self.child_main_axis_extent;
251 self.main_axis_stride * f64::from(main_axis_count) - main_axis_spacing
252 }
253}
254
255pub trait SliverGridDelegate: Debug {
271 fn get_layout(&self, constraints: SliverConstraints) -> Box<dyn SliverGridLayout>;
273
274 fn should_relayout(&self, old_delegate: &dyn SliverGridDelegate) -> bool;
280
281 fn as_any(&self) -> &dyn Any;
283}
284
285#[derive(Clone, Debug)]
302pub struct SliverGridDelegateWithFixedCrossAxisCount {
303 pub cross_axis_count: i32,
305 pub main_axis_spacing: f64,
307 pub cross_axis_spacing: f64,
309 pub child_aspect_ratio: f64,
311 pub main_axis_extent: Option<f64>,
316}
317
318impl SliverGridDelegateWithFixedCrossAxisCount {
319 pub fn new(cross_axis_count: i32) -> SliverGridDelegateWithFixedCrossAxisCount {
323 debug_assert!(cross_axis_count > 0);
324 SliverGridDelegateWithFixedCrossAxisCount {
325 cross_axis_count,
326 main_axis_spacing: 0.0,
327 cross_axis_spacing: 0.0,
328 child_aspect_ratio: 1.0,
329 main_axis_extent: None,
330 }
331 }
332
333 pub fn main_axis_spacing(mut self, value: f64) -> Self {
335 debug_assert!(value >= 0.0);
336 self.main_axis_spacing = value;
337 self
338 }
339
340 pub fn cross_axis_spacing(mut self, value: f64) -> Self {
342 debug_assert!(value >= 0.0);
343 self.cross_axis_spacing = value;
344 self
345 }
346
347 pub fn child_aspect_ratio(mut self, value: f64) -> Self {
350 debug_assert!(value > 0.0);
351 self.child_aspect_ratio = value;
352 self
353 }
354
355 pub fn main_axis_extent(mut self, value: f64) -> Self {
357 debug_assert!(value >= 0.0);
358 self.main_axis_extent = Some(value);
359 self
360 }
361
362 fn debug_assert_is_valid(&self) -> bool {
363 debug_assert!(self.cross_axis_count > 0);
364 debug_assert!(self.main_axis_spacing >= 0.0);
365 debug_assert!(self.cross_axis_spacing >= 0.0);
366 debug_assert!(self.child_aspect_ratio > 0.0);
367 true
368 }
369}
370
371impl SliverGridDelegate for SliverGridDelegateWithFixedCrossAxisCount {
372 fn get_layout(&self, constraints: SliverConstraints) -> Box<dyn SliverGridLayout> {
373 debug_assert!(self.debug_assert_is_valid());
374 let usable_cross_axis_extent = (constraints.cross_axis_extent
375 - self.cross_axis_spacing * f64::from(self.cross_axis_count - 1))
376 .max(0.0);
377 let child_cross_axis_extent = usable_cross_axis_extent / f64::from(self.cross_axis_count);
378 let child_main_axis_extent = self
379 .main_axis_extent
380 .unwrap_or(child_cross_axis_extent / self.child_aspect_ratio);
381 Box::new(SliverGridRegularTileLayout::new(
382 self.cross_axis_count,
383 child_main_axis_extent + self.main_axis_spacing,
384 child_cross_axis_extent + self.cross_axis_spacing,
385 child_main_axis_extent,
386 child_cross_axis_extent,
387 axis_direction_is_reversed(constraints.cross_axis_direction),
388 ))
389 }
390
391 fn should_relayout(&self, old_delegate: &dyn SliverGridDelegate) -> bool {
392 let Some(old) = old_delegate.as_any().downcast_ref::<Self>() else {
393 return true;
394 };
395 old.cross_axis_count != self.cross_axis_count
396 || old.main_axis_spacing != self.main_axis_spacing
397 || old.cross_axis_spacing != self.cross_axis_spacing
398 || old.child_aspect_ratio != self.child_aspect_ratio
399 || old.main_axis_extent != self.main_axis_extent
400 }
401
402 fn as_any(&self) -> &dyn Any {
403 self
404 }
405}
406
407#[derive(Clone, Debug)]
430pub struct SliverGridDelegateWithMaxCrossAxisExtent {
431 pub max_cross_axis_extent: f64,
433 pub main_axis_spacing: f64,
435 pub cross_axis_spacing: f64,
437 pub child_aspect_ratio: f64,
439 pub main_axis_extent: Option<f64>,
444}
445
446impl SliverGridDelegateWithMaxCrossAxisExtent {
447 pub fn new(max_cross_axis_extent: f64) -> SliverGridDelegateWithMaxCrossAxisExtent {
452 debug_assert!(max_cross_axis_extent > 0.0);
453 SliverGridDelegateWithMaxCrossAxisExtent {
454 max_cross_axis_extent,
455 main_axis_spacing: 0.0,
456 cross_axis_spacing: 0.0,
457 child_aspect_ratio: 1.0,
458 main_axis_extent: None,
459 }
460 }
461
462 pub fn main_axis_spacing(mut self, value: f64) -> Self {
464 debug_assert!(value >= 0.0);
465 self.main_axis_spacing = value;
466 self
467 }
468
469 pub fn cross_axis_spacing(mut self, value: f64) -> Self {
471 debug_assert!(value >= 0.0);
472 self.cross_axis_spacing = value;
473 self
474 }
475
476 pub fn child_aspect_ratio(mut self, value: f64) -> Self {
479 debug_assert!(value > 0.0);
480 self.child_aspect_ratio = value;
481 self
482 }
483
484 pub fn main_axis_extent(mut self, value: f64) -> Self {
486 debug_assert!(value >= 0.0);
487 self.main_axis_extent = Some(value);
488 self
489 }
490
491 fn debug_assert_is_valid(&self, cross_axis_extent: f64) -> bool {
492 debug_assert!(cross_axis_extent > 0.0);
493 debug_assert!(self.max_cross_axis_extent > 0.0);
494 debug_assert!(self.main_axis_spacing >= 0.0);
495 debug_assert!(self.cross_axis_spacing >= 0.0);
496 debug_assert!(self.child_aspect_ratio > 0.0);
497 true
498 }
499}
500
501impl SliverGridDelegate for SliverGridDelegateWithMaxCrossAxisExtent {
502 fn get_layout(&self, constraints: SliverConstraints) -> Box<dyn SliverGridLayout> {
503 debug_assert!(self.debug_assert_is_valid(constraints.cross_axis_extent));
504 let cross_axis_count = (constraints.cross_axis_extent
505 / (self.max_cross_axis_extent + self.cross_axis_spacing))
506 .ceil() as i32;
507 let cross_axis_count = cross_axis_count.max(1);
510 let usable_cross_axis_extent = (constraints.cross_axis_extent
511 - self.cross_axis_spacing * f64::from(cross_axis_count - 1))
512 .max(0.0);
513 let child_cross_axis_extent = usable_cross_axis_extent / f64::from(cross_axis_count);
514 let child_main_axis_extent = self
515 .main_axis_extent
516 .unwrap_or(child_cross_axis_extent / self.child_aspect_ratio);
517 Box::new(SliverGridRegularTileLayout::new(
518 cross_axis_count,
519 child_main_axis_extent + self.main_axis_spacing,
520 child_cross_axis_extent + self.cross_axis_spacing,
521 child_main_axis_extent,
522 child_cross_axis_extent,
523 axis_direction_is_reversed(constraints.cross_axis_direction),
524 ))
525 }
526
527 fn should_relayout(&self, old_delegate: &dyn SliverGridDelegate) -> bool {
528 let Some(old) = old_delegate.as_any().downcast_ref::<Self>() else {
529 return true;
530 };
531 old.max_cross_axis_extent != self.max_cross_axis_extent
532 || old.main_axis_spacing != self.main_axis_spacing
533 || old.cross_axis_spacing != self.cross_axis_spacing
534 || old.child_aspect_ratio != self.child_aspect_ratio
535 || old.main_axis_extent != self.main_axis_extent
536 }
537
538 fn as_any(&self) -> &dyn Any {
539 self
540 }
541}
542
543#[derive(Debug, Default)]
545pub struct SliverGridParentData {
546 adaptor: SliverMultiBoxAdaptorParentData,
547 pub cross_axis_offset: Option<f64>,
553}
554
555impl SliverGridParentData {
556 pub const fn new() -> SliverGridParentData {
558 SliverGridParentData {
559 adaptor: SliverMultiBoxAdaptorParentData::new(),
560 cross_axis_offset: None,
561 }
562 }
563}
564
565impl ParentData for SliverGridParentData {
566 fn detach(&mut self) {
567 ParentData::detach(&mut self.adaptor);
568 }
569
570 fn provide(&self, id: TypeId) -> Option<&dyn Any> {
571 if id == TypeId::of::<SliverGridParentData>() {
572 return Some(self);
573 }
574 self.adaptor.provide(id)
575 }
576
577 fn provide_mut(&mut self, id: TypeId) -> Option<&mut dyn Any> {
578 if id == TypeId::of::<SliverGridParentData>() {
579 return Some(self);
580 }
581 self.adaptor.provide_mut(id)
582 }
583}
584
585impl ContainerParentDataMixin for SliverGridParentData {
586 type ChildType = AnyRenderBox;
587
588 fn container_parent_data(&self) -> &ContainerParentData<AnyRenderBox> {
589 self.adaptor.container_parent_data()
590 }
591
592 fn container_parent_data_mut(&mut self) -> &mut ContainerParentData<AnyRenderBox> {
593 self.adaptor.container_parent_data_mut()
594 }
595}
596
597impl Display for SliverGridParentData {
598 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
599 write!(
600 f,
601 "crossAxisOffset={:?}; {}",
602 self.cross_axis_offset, self.adaptor
603 )
604 }
605}
606
607pub struct RenderSliverGrid {
619 render_object: RenderObjectData,
620 render_sliver: RenderSliverData,
621 container: ContainerRenderObjectData<AnyRenderBox>,
622 adaptor: RenderSliverMultiBoxAdaptorData,
623 grid_delegate: Rc<dyn SliverGridDelegate>,
624}
625
626impl RenderSliverGrid {
627 pub fn new(
630 app: &mut App,
631 child_manager: Rc<dyn RenderSliverBoxChildManager>,
632 grid_delegate: Rc<dyn SliverGridDelegate>,
633 ) -> RenderHandle<RenderSliverGrid> {
634 RenderHandle::new_sliver(
635 app,
636 RenderSliverGrid {
637 render_object: RenderObjectData::new(),
638 render_sliver: RenderSliverData::new(),
639 container: ContainerRenderObjectData::new(),
640 adaptor: RenderSliverMultiBoxAdaptorData::new(child_manager),
641 grid_delegate,
642 },
643 )
644 }
645
646 pub fn grid_delegate(self: RenderHandle<Self>, app: &App) -> Rc<dyn SliverGridDelegate> {
648 Rc::clone(&self.get(app).grid_delegate)
649 }
650
651 pub fn set_grid_delegate(
653 self: RenderHandle<Self>,
654 app: &mut App,
655 value: Rc<dyn SliverGridDelegate>,
656 ) {
657 let old = self.grid_delegate(app);
658 if Rc::ptr_eq(&old, &value) {
659 return;
660 }
661 if value.as_any().type_id() != old.as_any().type_id() || value.should_relayout(&*old) {
662 self.mark_needs_layout(app);
663 }
664 self.get_mut(app).grid_delegate = value;
665 }
666
667 fn place(app: &mut App, child: AnyRenderBox, geometry: SliverGridGeometry) {
669 let child_parent_data = child
670 .as_object()
671 .parent_data_of_mut::<SliverGridParentData>(app);
672 child_parent_data
673 .adaptor
674 .set_layout_offset(Some(geometry.scroll_offset));
675 child_parent_data.cross_axis_offset = Some(geometry.cross_axis_offset);
676 }
677}
678
679impl ContainerRenderObjectMixin for RenderSliverGrid {
680 type ChildType = AnyRenderBox;
681 type ParentDataType = SliverMultiBoxAdaptorParentData;
682
683 fn container_data(
684 self: RenderHandle<Self>,
685 app: &App,
686 ) -> &ContainerRenderObjectData<AnyRenderBox> {
687 &self.get(app).container
688 }
689
690 fn container_data_mut(
691 self: RenderHandle<Self>,
692 app: &mut App,
693 ) -> &mut ContainerRenderObjectData<AnyRenderBox> {
694 &mut self.get_mut(app).container
695 }
696
697 fn insert(
698 self: RenderHandle<Self>,
699 app: &mut App,
700 child: AnyRenderBox,
701 after: Option<AnyRenderBox>,
702 ) {
703 RenderSliverMultiBoxAdaptor::insert(self, app, child, after)
704 }
705
706 fn move_child(
707 self: RenderHandle<Self>,
708 app: &mut App,
709 child: AnyRenderBox,
710 after: Option<AnyRenderBox>,
711 ) {
712 RenderSliverMultiBoxAdaptor::move_child(self, app, child, after)
713 }
714
715 fn remove(self: RenderHandle<Self>, app: &mut App, child: AnyRenderBox) {
716 RenderSliverMultiBoxAdaptor::remove(self, app, child)
717 }
718
719 fn remove_all(self: RenderHandle<Self>, app: &mut App) {
720 RenderSliverMultiBoxAdaptor::remove_all(self, app)
721 }
722}
723
724impl RenderSliverHelpers for RenderSliverGrid {}
725
726impl RenderSliverWithKeepAliveMixin for RenderSliverGrid {}
727
728impl RenderSliverMultiBoxAdaptor for RenderSliverGrid {
729 crate::render_sliver_multi_box_adaptor_accessors!();
730}
731
732impl RenderObject for RenderSliverGrid {
733 crate::render_object_accessors!();
734
735 fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
736 if !child.parent_data_is::<SliverGridParentData>(app) {
737 child.set_parent_data(app, SliverGridParentData::new());
738 }
739 }
740
741 fn visit_children(
742 self: RenderHandle<Self>,
743 app: &App,
744 visitor: &mut dyn FnMut(AnyRenderObject),
745 ) {
746 RenderSliverMultiBoxAdaptor::visit_children(self, app, visitor)
747 }
748
749 fn did_attach(self: RenderHandle<Self>, app: &mut App, owner: Handle<PipelineOwner>) {
750 RenderSliverMultiBoxAdaptor::did_attach(self, app, owner)
751 }
752
753 fn did_detach(self: RenderHandle<Self>, app: &mut App) {
754 RenderSliverMultiBoxAdaptor::did_detach(self, app)
755 }
756
757 fn redepth_children(self: RenderHandle<Self>, app: &mut App) {
758 RenderSliverMultiBoxAdaptor::redepth_children(self, app)
759 }
760
761 fn apply_paint_transform(
762 self: RenderHandle<Self>,
763 app: &App,
764 child: AnyRenderObject,
765 transform: &mut Matrix4,
766 ) {
767 RenderSliverMultiBoxAdaptor::apply_paint_transform(self, app, child, transform)
768 }
769
770 fn paint(
771 self: RenderHandle<Self>,
772 app: &mut App,
773 context: &mut PaintingContext,
774 offset: Offset,
775 ) {
776 RenderSliverMultiBoxAdaptor::paint(self, app, context, offset)
777 }
778
779 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
780 let constraints = self.constraints(app);
781 let child_manager = self.child_manager(app);
782 child_manager.did_start_layout(app);
783 child_manager.set_did_underflow(app, false);
784
785 let scroll_offset = constraints.scroll_offset + constraints.cache_origin;
786 debug_assert!(scroll_offset >= 0.0);
787 let remaining_extent = constraints.remaining_cache_extent;
788 debug_assert!(remaining_extent >= 0.0);
789 let target_end_scroll_offset = scroll_offset + remaining_extent;
790
791 let layout = self.grid_delegate(app).get_layout(constraints);
792
793 let first_index = layout.get_min_child_index_for_scroll_offset(scroll_offset);
794 let target_last_index = target_end_scroll_offset
795 .is_finite()
796 .then(|| layout.get_max_child_index_for_scroll_offset(target_end_scroll_offset));
797
798 if self.first_child(app).is_some() {
799 let leading_garbage = self.calculate_leading_garbage(app, first_index);
800 let trailing_garbage = match target_last_index {
801 Some(last_index) => self.calculate_trailing_garbage(app, last_index),
802 None => 0,
803 };
804 self.collect_garbage(app, leading_garbage, trailing_garbage);
805 } else {
806 self.collect_garbage(app, 0, 0);
807 }
808
809 let first_child_grid_geometry = layout.get_geometry_for_child_index(first_index);
810
811 if self.first_child(app).is_none()
812 && !self.add_initial_child(app, first_index, first_child_grid_geometry.scroll_offset)
813 {
814 let max = layout.compute_max_scroll_offset(child_manager.child_count(app));
816 self.set_geometry(
817 app,
818 SliverGeometry::builder()
819 .scroll_extent(max)
820 .max_paint_extent(max)
821 .build(),
822 );
823 child_manager.did_finish_layout(app);
824 return;
825 }
826
827 let leading_scroll_offset = first_child_grid_geometry.scroll_offset;
828 let mut trailing_scroll_offset = first_child_grid_geometry.trailing_scroll_offset();
829 let mut trailing_child_with_layout: Option<AnyRenderBox> = None;
830 let mut reached_end = false;
831
832 let first_child = self.first_child(app).expect("added above");
833 let mut index = self.index_of(app, first_child) - 1;
834 while index >= first_index {
835 let grid_geometry = layout.get_geometry_for_child_index(index);
836 let child = self
837 .insert_and_layout_leading_child(
838 app,
839 grid_geometry.get_box_constraints(constraints),
840 false,
841 )
842 .expect("the children before the first one are there");
843 Self::place(app, child, grid_geometry);
844 debug_assert_eq!(
845 child
846 .as_object()
847 .parent_data_of::<SliverMultiBoxAdaptorParentData>(app)
848 .index,
849 Some(index)
850 );
851 trailing_child_with_layout.get_or_insert(child);
852 trailing_scroll_offset =
853 trailing_scroll_offset.max(grid_geometry.trailing_scroll_offset());
854 index -= 1;
855 }
856
857 if trailing_child_with_layout.is_none() {
858 let first_child = self.first_child(app).expect("the child list is not empty");
859 first_child.layout(
860 app,
861 first_child_grid_geometry.get_box_constraints(constraints),
862 false,
863 );
864 Self::place(app, first_child, first_child_grid_geometry);
865 trailing_child_with_layout = Some(first_child);
866 }
867
868 let mut index = self.index_of(app, trailing_child_with_layout.expect("set above")) + 1;
869 while target_last_index.is_none_or(|last_index| index <= last_index) {
870 let grid_geometry = layout.get_geometry_for_child_index(index);
871 let child_constraints = grid_geometry.get_box_constraints(constraints);
872 let after = trailing_child_with_layout.expect("set above");
873 let mut child = self.child_after(app, after);
874 if child.is_none_or(|current| self.index_of(app, current) != index) {
875 child = self.insert_and_layout_child(app, child_constraints, Some(after), false);
876 if child.is_none() {
877 reached_end = true;
878 break;
880 }
881 } else {
882 child
883 .expect("checked")
884 .layout(app, child_constraints, false);
885 }
886 let child = child.expect("set above");
887 trailing_child_with_layout = Some(child);
888 Self::place(app, child, grid_geometry);
889 debug_assert_eq!(
890 child
891 .as_object()
892 .parent_data_of::<SliverMultiBoxAdaptorParentData>(app)
893 .index,
894 Some(index)
895 );
896 trailing_scroll_offset =
897 trailing_scroll_offset.max(grid_geometry.trailing_scroll_offset());
898 index += 1;
899 }
900
901 let last_index = self.index_of(app, self.last_child(app).expect("not empty"));
902
903 debug_assert!(self.debug_assert_child_list_is_non_empty_and_contiguous(app));
904 debug_assert_eq!(
905 self.index_of(app, self.first_child(app).expect("not empty")),
906 first_index
907 );
908 debug_assert!(target_last_index.is_none_or(|target| last_index <= target));
909
910 let estimated_total_extent = if reached_end {
911 trailing_scroll_offset
912 } else {
913 child_manager.estimate_max_scroll_offset(
914 app,
915 constraints,
916 Some(first_index),
917 Some(last_index),
918 Some(leading_scroll_offset),
919 Some(trailing_scroll_offset),
920 )
921 };
922 let paint_extent = calculate_paint_offset(
923 constraints,
924 constraints.scroll_offset.min(leading_scroll_offset),
925 trailing_scroll_offset,
926 );
927 let cache_extent =
928 calculate_cache_offset(constraints, leading_scroll_offset, trailing_scroll_offset);
929
930 self.set_geometry(
931 app,
932 SliverGeometry::builder()
933 .scroll_extent(estimated_total_extent)
934 .paint_extent(paint_extent)
935 .max_paint_extent(estimated_total_extent)
936 .cache_extent(cache_extent)
937 .has_visual_overflow(
938 estimated_total_extent > paint_extent
939 || constraints.scroll_offset > 0.0
940 || constraints.overlap != 0.0,
941 )
942 .build(),
943 );
944
945 if estimated_total_extent == trailing_scroll_offset {
948 child_manager.set_did_underflow(app, true);
949 }
950 child_manager.did_finish_layout(app);
951 }
952}
953
954impl RenderSliver for RenderSliverGrid {
955 crate::render_sliver_accessors!();
956
957 fn hit_test_children(
958 self: RenderHandle<Self>,
959 app: &mut App,
960 result: &mut SliverHitTestResult<'_>,
961 main_axis_position: f64,
962 cross_axis_position: f64,
963 ) -> bool {
964 RenderSliverMultiBoxAdaptor::hit_test_children(
965 self,
966 app,
967 result,
968 main_axis_position,
969 cross_axis_position,
970 )
971 }
972
973 fn child_main_axis_position(
974 self: RenderHandle<Self>,
975 app: &App,
976 child: AnyRenderObject,
977 ) -> f64 {
978 RenderSliverMultiBoxAdaptor::child_main_axis_position(self, app, child)
979 }
980
981 fn child_cross_axis_position(
982 self: RenderHandle<Self>,
983 app: &App,
984 child: AnyRenderObject,
985 ) -> f64 {
986 child
987 .parent_data_of::<SliverGridParentData>(app)
988 .cross_axis_offset
989 .expect("laid out")
990 }
991
992 fn child_scroll_offset(
993 self: RenderHandle<Self>,
994 app: &App,
995 child: AnyRenderObject,
996 ) -> Option<f64> {
997 RenderSliverMultiBoxAdaptor::child_scroll_offset(self, app, child)
998 }
999}
1000
1001#[cfg(test)]
1002mod tests {
1003 use inset_embedder::Size;
1004 use inset_foundation::AppCell;
1005 use inset_gestures::HitTestResult;
1006 use inset_painting::AxisDirection;
1007
1008 use super::*;
1009 use crate::sliver::GrowthDirection;
1010 use crate::sliver_multi_box_adaptor::test_support::TestChildManager;
1011 use crate::sliver_multi_box_adaptor::viewport_test_support::ScrollHarness;
1012 use crate::viewport_offset::ScrollDirection;
1013
1014 const VIEWPORT: Size = Size::new(500.0, 200.0);
1015
1016 fn sliver_grid(
1017 app: &mut App,
1018 count: usize,
1019 delegate: Rc<dyn SliverGridDelegate>,
1020 ) -> (
1021 RenderHandle<RenderSliverGrid>,
1022 Rc<TestChildManager<RenderSliverGrid>>,
1023 ScrollHarness,
1024 ) {
1025 let manager = TestChildManager::new(vec![1000.0; count]);
1026 let grid = RenderSliverGrid::new(app, manager.clone(), delegate);
1027 manager.attach(grid);
1028 let harness = ScrollHarness::new(app, grid.as_sliver(), VIEWPORT);
1029 (grid, manager, harness)
1030 }
1031
1032 fn constraints(cross_axis_extent: f64) -> SliverConstraints {
1034 SliverConstraints::new(
1035 AxisDirection::Down,
1036 GrowthDirection::Forward,
1037 ScrollDirection::Idle,
1038 0.0,
1039 0.0,
1040 0.0,
1041 600.0,
1042 cross_axis_extent,
1043 AxisDirection::Right,
1044 600.0,
1045 1100.0,
1046 0.0,
1047 )
1048 }
1049
1050 fn placed(app: &App, grid: RenderHandle<RenderSliverGrid>) -> Vec<(i32, f64, f64, Size)> {
1052 let mut placed = Vec::new();
1053 let mut child = grid.first_child(app);
1054 while let Some(current) = child {
1055 let data = current
1056 .as_object()
1057 .parent_data_of::<SliverGridParentData>(app);
1058 placed.push((
1059 data.adaptor.index.expect("indexed"),
1060 data.adaptor.layout_offset().expect("laid out"),
1061 data.cross_axis_offset.expect("laid out"),
1062 current.size(app),
1063 ));
1064 child = grid.child_after(app, current);
1065 }
1066 placed
1067 }
1068
1069 #[test]
1072 fn a_max_extent_grid_fits_as_many_columns_as_it_can() {
1073 let delegate = SliverGridDelegateWithMaxCrossAxisExtent::new(150.0);
1074 let layout = delegate.get_layout(constraints(500.0));
1075 let geometry = layout.get_geometry_for_child_index(5);
1076 assert_eq!(geometry.cross_axis_extent, 125.0, "4 columns of 125 in 500");
1077 assert_eq!(geometry.cross_axis_offset, 125.0);
1078 assert_eq!(geometry.scroll_offset, 125.0, "the second row");
1079 assert_eq!(layout.compute_max_scroll_offset(9), 375.0, "3 rows");
1080 assert_eq!(layout.compute_max_scroll_offset(0), 0.0);
1081 }
1082
1083 #[test]
1085 fn a_reversed_cross_axis_places_the_first_column_last() {
1086 let mut leftward = constraints(400.0);
1087 leftward.cross_axis_direction = AxisDirection::Left;
1088 let layout = SliverGridDelegateWithFixedCrossAxisCount::new(4)
1089 .cross_axis_spacing(20.0)
1090 .get_layout(leftward);
1091 assert_eq!(
1093 layout.get_geometry_for_child_index(0).cross_axis_offset,
1094 315.0
1095 );
1096 assert_eq!(
1097 layout.get_geometry_for_child_index(3).cross_axis_offset,
1098 0.0
1099 );
1100 }
1101
1102 #[test]
1103 fn a_grid_lays_out_only_the_rows_it_shows_with_their_spacing() {
1104 let cell = AppCell::new();
1105 let mut app = cell.borrow_mut();
1106 let delegate = SliverGridDelegateWithFixedCrossAxisCount::new(2)
1107 .main_axis_spacing(10.0)
1108 .cross_axis_spacing(20.0)
1109 .child_aspect_ratio(2.0);
1110 let (grid, manager, _harness) = sliver_grid(&mut app, 100, Rc::new(delegate));
1111 let placed = placed(&app, grid);
1114 assert_eq!(
1115 placed[..3],
1116 [
1117 (0, 0.0, 0.0, Size::new(240.0, 120.0)),
1118 (1, 0.0, 260.0, Size::new(240.0, 120.0)),
1119 (2, 130.0, 0.0, Size::new(240.0, 120.0)),
1120 ]
1121 );
1122 assert_eq!(placed.len(), 8, "four rows of two");
1123 assert!(manager.created.borrow().iter().all(|index| *index < 8));
1124 }
1125
1126 #[test]
1127 fn scrolling_a_grid_replaces_its_rows() {
1128 let cell = AppCell::new();
1129 let mut app = cell.borrow_mut();
1130 let delegate = SliverGridDelegateWithFixedCrossAxisCount::new(4).main_axis_extent(100.0);
1131 let (grid, _, harness) = sliver_grid(&mut app, 100, Rc::new(delegate));
1132 harness.scroll_to(&mut app, 1000.0);
1133 let placed = placed(&app, grid);
1134 assert_eq!((placed[0].0, placed[0].1), (28, 700.0));
1136 assert!(placed.iter().all(|(index, ..)| *index >= 28));
1137 assert!(grid.geometry(&app).has_visual_overflow);
1138 }
1139
1140 #[test]
1141 fn a_short_grid_reports_its_exact_extent() {
1142 let cell = AppCell::new();
1143 let mut app = cell.borrow_mut();
1144 let delegate = SliverGridDelegateWithFixedCrossAxisCount::new(4).main_axis_extent(50.0);
1145 let (grid, manager, _harness) = sliver_grid(&mut app, 6, Rc::new(delegate));
1146 let geometry = grid.geometry(&app);
1147 assert_eq!(geometry.scroll_extent, 100.0, "two rows of 50");
1148 assert_eq!(geometry.paint_extent, 100.0);
1149 assert!(!geometry.has_visual_overflow);
1150 assert!(manager.did_underflow.get());
1151 }
1152
1153 #[test]
1156 fn a_grid_hit_tests_the_tile_under_the_point() {
1157 let cell = AppCell::new();
1158 let mut app = cell.borrow_mut();
1159 let delegate = SliverGridDelegateWithFixedCrossAxisCount::new(5).main_axis_extent(100.0);
1160 let (grid, _, _harness) = sliver_grid(&mut app, 20, Rc::new(delegate));
1161 let tile = placed(&app, grid)
1162 .iter()
1163 .position(|(index, ..)| *index == 8)
1164 .expect("tile 8 is laid out");
1165 let mut child = grid.first_child(&app).expect("tiles");
1166 for _ in 0..tile {
1167 child = grid.child_after(&app, child).expect("tile");
1168 }
1169 assert_eq!(
1170 RenderSliver::child_cross_axis_position(grid, &app, child.as_object()),
1171 300.0
1172 );
1173
1174 let mut result = HitTestResult::new();
1175 let hit = grid.hit_test(
1176 &mut app,
1177 &mut SliverHitTestResult::wrap(&mut result),
1178 150.0,
1179 350.0,
1180 );
1181 assert!(hit);
1182 assert_eq!(result.path().len(), 2, "tile 8 and the grid");
1183 }
1184
1185 #[test]
1186 fn a_new_delegate_relays_out_only_when_it_changes_the_tiles() {
1187 let cell = AppCell::new();
1188 let mut app = cell.borrow_mut();
1189 let delegate = SliverGridDelegateWithFixedCrossAxisCount::new(2);
1190 let (grid, _, harness) = sliver_grid(&mut app, 10, Rc::new(delegate.clone()));
1191 grid.set_grid_delegate(&mut app, Rc::new(delegate.clone()));
1192 assert!(!grid.as_object().debug_needs_layout(&app), "the same tiles");
1193 grid.set_grid_delegate(&mut app, Rc::new(delegate.main_axis_spacing(4.0)));
1194 assert!(grid.as_object().debug_needs_layout(&app));
1195 harness.owner.flush_layout(&mut app);
1196 grid.set_grid_delegate(
1197 &mut app,
1198 Rc::new(SliverGridDelegateWithMaxCrossAxisExtent::new(250.0)),
1199 );
1200 assert!(
1201 grid.as_object().debug_needs_layout(&app),
1202 "another kind of delegate"
1203 );
1204 }
1205}