1use std::any::{Any, TypeId};
6use std::fmt::{self, Debug};
7use std::rc::Rc;
8use std::time::Duration;
9
10use inset_animation::Curve;
11use inset_embedder::{Clip, Matrix4, Offset, Rect, Size, clamp_double};
12use inset_foundation::{App, Handle, HandleId, Listenable, Listener};
13use inset_painting::{Axis, AxisDirection, axis_direction_to_axis, transform_rect};
14
15use crate::box_::{BoxConstraints, BoxHitTestResult, RenderBox, RenderBoxData};
16use crate::debug::debug_check_has_bounded_axis;
17use crate::layer::{ClipRectLayer, LayerHandle};
18use crate::object::{
19 AnyRenderObject, ContainerRenderObjectData, ContainerRenderObjectMixin, RenderHandle,
20 RenderObject, RenderObjectData, debug_checking_intrinsics, resolve, translate,
21};
22use crate::painting_context::PaintingContext;
23use crate::pipeline_owner::PipelineOwner;
24use crate::sliver::{
25 AnyRenderSliver, GrowthDirection, SliverConstraints, SliverGeometry, SliverHitTestResult,
26 SliverLogicalContainerParentData, SliverPhysicalContainerParentData,
27 apply_growth_direction_to_axis_direction, apply_growth_direction_to_scroll_direction,
28};
29use crate::viewport_offset::AnyViewportOffset;
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
33pub enum CacheExtentStyle {
34 Pixel,
36
37 Viewport,
39}
40
41#[derive(Debug, Clone, Copy, PartialEq)]
52pub enum ScrollCacheExtent {
53 Pixels(f64),
55
56 Viewport(f64),
63}
64
65impl ScrollCacheExtent {
66 fn calculate_cache_offset(self, main_axis_extent: f64) -> f64 {
68 match self {
69 ScrollCacheExtent::Pixels(pixels) => pixels,
70 ScrollCacheExtent::Viewport(value) => value * main_axis_extent,
71 }
72 }
73
74 pub fn style(self) -> CacheExtentStyle {
76 match self {
77 ScrollCacheExtent::Pixels(_) => CacheExtentStyle::Pixel,
78 ScrollCacheExtent::Viewport(_) => CacheExtentStyle::Viewport,
79 }
80 }
81
82 pub fn value(self) -> f64 {
88 match self {
89 ScrollCacheExtent::Pixels(pixels) => pixels,
90 ScrollCacheExtent::Viewport(value) => value,
91 }
92 }
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
103pub enum SliverPaintOrder {
104 #[default]
111 FirstIsTop,
112
113 LastIsTop,
118}
119
120pub trait RenderAbstractViewport: RenderObject {
131 fn get_offset_to_reveal(
157 self: RenderHandle<Self>,
158 app: &App,
159 target: AnyRenderObject,
160 alignment: f64,
161 rect: Option<Rect>,
162 axis: Option<Axis>,
163 ) -> RevealedOffset;
164
165 fn as_abstract_viewport(self: RenderHandle<Self>) -> AnyRenderAbstractViewport {
168 AnyRenderAbstractViewport {
169 id: self.id(),
170 vtable: const { &RenderAbstractViewportVTable::of::<Self>() },
171 }
172 }
173}
174
175struct RenderAbstractViewportVTable {
177 get_offset_to_reveal: GetOffsetToRevealFn,
178 as_object: fn(&App, HandleId) -> AnyRenderObject,
179}
180
181type GetOffsetToRevealFn =
183 fn(&App, HandleId, AnyRenderObject, f64, Option<Rect>, Option<Axis>) -> RevealedOffset;
184
185impl RenderAbstractViewportVTable {
186 const fn of<T: RenderAbstractViewport>() -> RenderAbstractViewportVTable {
187 RenderAbstractViewportVTable {
188 get_offset_to_reveal: |app, id, target, alignment, rect, axis| {
189 T::get_offset_to_reveal(resolve(id), app, target, alignment, rect, axis)
190 },
191 as_object: |app, id| resolve::<T>(id).as_render_object(app),
192 }
193 }
194}
195
196#[derive(Clone, Copy)]
199pub struct AnyRenderAbstractViewport {
200 id: HandleId,
201 vtable: &'static RenderAbstractViewportVTable,
202}
203
204impl AnyRenderAbstractViewport {
205 pub const DEFAULT_CACHE_EXTENT: f64 = 250.0;
209
210 pub fn maybe_of(
216 app: &App,
217 object: Option<AnyRenderObject>,
218 ) -> Option<AnyRenderAbstractViewport> {
219 let mut object = object;
220 while let Some(current) = object {
221 if let Some(viewport) = current.interface::<AnyRenderAbstractViewport>() {
222 return Some(viewport);
223 }
224 object = current.parent(app);
225 }
226 None
227 }
228
229 pub fn of(app: &App, object: Option<AnyRenderObject>) -> AnyRenderAbstractViewport {
237 AnyRenderAbstractViewport::maybe_of(app, object).unwrap_or_else(|| {
238 panic!(
239 "RenderAbstractViewport::of was called with a render object that was not a \
240 descendant of a RenderAbstractViewport: {object:?}"
241 )
242 })
243 }
244
245 pub fn as_object(self, app: &App) -> AnyRenderObject {
247 (self.vtable.as_object)(app, self.id)
248 }
249
250 pub fn get_offset_to_reveal(
252 self,
253 app: &App,
254 target: AnyRenderObject,
255 alignment: f64,
256 rect: Option<Rect>,
257 axis: Option<Axis>,
258 ) -> RevealedOffset {
259 (self.vtable.get_offset_to_reveal)(app, self.id, target, alignment, rect, axis)
260 }
261}
262
263impl PartialEq for AnyRenderAbstractViewport {
264 fn eq(&self, other: &AnyRenderAbstractViewport) -> bool {
265 self.id == other.id
266 }
267}
268
269impl Eq for AnyRenderAbstractViewport {}
270
271impl Debug for AnyRenderAbstractViewport {
272 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
273 write!(f, "AnyRenderAbstractViewport({:?})", self.id)
274 }
275}
276
277#[derive(Clone, Copy, Debug)]
282pub struct RevealedOffset {
283 pub offset: f64,
285
286 pub rect: Rect,
299}
300
301impl RevealedOffset {
302 pub const fn new(offset: f64, rect: Rect) -> RevealedOffset {
304 RevealedOffset { offset, rect }
305 }
306
307 pub fn clamp_offset(
313 leading_edge_offset: RevealedOffset,
314 trailing_edge_offset: RevealedOffset,
315 current_offset: f64,
316 ) -> Option<RevealedOffset> {
317 let inverted = leading_edge_offset.offset < trailing_edge_offset.offset;
337 let (smaller, larger) = if inverted {
338 (leading_edge_offset, trailing_edge_offset)
339 } else {
340 (trailing_edge_offset, leading_edge_offset)
341 };
342 if current_offset > larger.offset {
343 Some(larger)
344 } else if current_offset < smaller.offset {
345 Some(smaller)
346 } else {
347 None
348 }
349 }
350}
351
352pub type SliverAdvance<T> =
354 dyn Fn(RenderHandle<T>, &App, AnyRenderSliver) -> Option<AnyRenderSliver>;
355
356pub struct RenderViewportBaseData {
358 axis_direction: AxisDirection,
359 cross_axis_direction: AxisDirection,
360 offset: AnyViewportOffset,
361 scroll_cache_extent: ScrollCacheExtent,
362 calculated_cache_extent: Option<f64>,
368 paint_order: SliverPaintOrder,
369 clip_behavior: Clip,
370 clip_rect_layer: LayerHandle<Handle<ClipRectLayer>>,
371}
372
373impl RenderViewportBaseData {
374 pub fn new(
378 cross_axis_direction: AxisDirection,
379 offset: AnyViewportOffset,
380 ) -> RenderViewportBaseData {
381 RenderViewportBaseData {
382 axis_direction: AxisDirection::Down,
383 cross_axis_direction,
384 offset,
385 scroll_cache_extent: ScrollCacheExtent::Pixels(
386 AnyRenderAbstractViewport::DEFAULT_CACHE_EXTENT,
387 ),
388 calculated_cache_extent: None,
389 paint_order: SliverPaintOrder::FirstIsTop,
390 clip_behavior: Clip::HardEdge,
391 clip_rect_layer: LayerHandle::new(),
392 }
393 }
394}
395
396#[macro_export]
398macro_rules! render_viewport_base_accessors {
399 () => {
400 fn viewport_data(
401 self: $crate::RenderHandle<Self>,
402 app: &::inset_foundation::App,
403 ) -> &$crate::RenderViewportBaseData {
404 &self.get(app).viewport
405 }
406 fn viewport_data_mut(
407 self: $crate::RenderHandle<Self>,
408 app: &mut ::inset_foundation::App,
409 ) -> &mut $crate::RenderViewportBaseData {
410 &mut self.get_mut(app).viewport
411 }
412 };
413}
414
415pub trait RenderViewportBase:
430 RenderBox + RenderAbstractViewport + ContainerRenderObjectMixin<ChildType = AnyRenderSliver>
431{
432 fn viewport_data(self: RenderHandle<Self>, app: &App) -> &RenderViewportBaseData;
434
435 fn viewport_data_mut(self: RenderHandle<Self>, app: &mut App) -> &mut RenderViewportBaseData;
437
438 fn axis_direction(self: RenderHandle<Self>, app: &App) -> AxisDirection {
443 self.viewport_data(app).axis_direction
444 }
445
446 fn set_axis_direction(self: RenderHandle<Self>, app: &mut App, value: AxisDirection) {
448 if value == self.axis_direction(app) {
449 return;
450 }
451 self.viewport_data_mut(app).axis_direction = value;
452 self.mark_needs_layout(app);
453 }
454
455 fn cross_axis_direction(self: RenderHandle<Self>, app: &App) -> AxisDirection {
461 self.viewport_data(app).cross_axis_direction
462 }
463
464 fn set_cross_axis_direction(self: RenderHandle<Self>, app: &mut App, value: AxisDirection) {
466 if value == self.cross_axis_direction(app) {
467 return;
468 }
469 self.viewport_data_mut(app).cross_axis_direction = value;
470 self.mark_needs_layout(app);
471 }
472
473 fn axis(self: RenderHandle<Self>, app: &App) -> Axis {
478 axis_direction_to_axis(self.axis_direction(app))
479 }
480
481 fn offset(self: RenderHandle<Self>, app: &App) -> AnyViewportOffset {
487 self.viewport_data(app).offset
488 }
489
490 fn set_offset(self: RenderHandle<Self>, app: &mut App, value: AnyViewportOffset) {
492 let old = self.offset(app);
493 if value == old {
494 return;
495 }
496 if self.attached(app) {
497 old.remove_listener(app, &self.mark_needs_layout_listener());
498 }
499 self.viewport_data_mut(app).offset = value;
500 if self.attached(app) {
501 value.add_listener(app, self.mark_needs_layout_listener());
502 }
503 self.mark_needs_layout(app);
506 }
507
508 fn mark_needs_layout_listener(self: RenderHandle<Self>) -> Listener {
510 Listener::handle_method(self.handle(), viewport_offset_changed::<Self>)
511 }
512
513 fn scroll_cache_extent(self: RenderHandle<Self>, app: &App) -> ScrollCacheExtent {
524 self.viewport_data(app).scroll_cache_extent
525 }
526
527 fn set_scroll_cache_extent(
531 self: RenderHandle<Self>,
532 app: &mut App,
533 value: Option<ScrollCacheExtent>,
534 ) {
535 let effective_value = value.unwrap_or(ScrollCacheExtent::Pixels(
536 AnyRenderAbstractViewport::DEFAULT_CACHE_EXTENT,
537 ));
538 if effective_value == self.scroll_cache_extent(app) {
539 return;
540 }
541 self.viewport_data_mut(app).scroll_cache_extent = effective_value;
542 self.mark_needs_layout(app);
543 }
544
545 #[deprecated(note = "Use scroll_cache_extent instead.")]
547 fn cache_extent(self: RenderHandle<Self>, app: &App) -> f64 {
548 self.scroll_cache_extent(app).value()
549 }
550
551 #[deprecated(note = "Use set_scroll_cache_extent instead.")]
556 fn set_cache_extent(self: RenderHandle<Self>, app: &mut App, value: Option<f64>) {
557 let current = self.scroll_cache_extent(app);
558 if value == Some(current.value()) {
559 return;
560 }
561 let replacement = match value {
562 None => ScrollCacheExtent::Pixels(AnyRenderAbstractViewport::DEFAULT_CACHE_EXTENT),
563 Some(value) => match current {
564 ScrollCacheExtent::Pixels(_) => ScrollCacheExtent::Pixels(value),
565 ScrollCacheExtent::Viewport(_) => ScrollCacheExtent::Viewport(value),
566 },
567 };
568 self.viewport_data_mut(app).scroll_cache_extent = replacement;
569 self.mark_needs_layout(app);
570 }
571
572 #[deprecated(note = "Use scroll_cache_extent instead.")]
574 fn cache_extent_style(self: RenderHandle<Self>, app: &App) -> CacheExtentStyle {
575 self.scroll_cache_extent(app).style()
576 }
577
578 #[deprecated(note = "Use set_scroll_cache_extent instead.")]
580 fn set_cache_extent_style(self: RenderHandle<Self>, app: &mut App, value: CacheExtentStyle) {
581 let current = self.scroll_cache_extent(app);
582 if value == current.style() {
583 return;
584 }
585 self.viewport_data_mut(app).scroll_cache_extent = match value {
586 CacheExtentStyle::Pixel => ScrollCacheExtent::Pixels(current.value()),
587 CacheExtentStyle::Viewport => ScrollCacheExtent::Viewport(current.value()),
588 };
589 self.mark_needs_layout(app);
590 }
591
592 fn paint_order(self: RenderHandle<Self>, app: &App) -> SliverPaintOrder {
600 self.viewport_data(app).paint_order
601 }
602
603 fn set_paint_order(self: RenderHandle<Self>, app: &mut App, value: SliverPaintOrder) {
605 if value == self.paint_order(app) {
606 return;
607 }
608 self.viewport_data_mut(app).paint_order = value;
609 self.mark_needs_paint(app);
610 }
611
612 fn clip_behavior(self: RenderHandle<Self>, app: &App) -> Clip {
616 self.viewport_data(app).clip_behavior
617 }
618
619 fn set_clip_behavior(self: RenderHandle<Self>, app: &mut App, value: Clip) {
621 if value == self.clip_behavior(app) {
622 return;
623 }
624 self.viewport_data_mut(app).clip_behavior = value;
625 self.mark_needs_paint(app);
626 }
627
628 fn did_attach(
630 self: RenderHandle<Self>,
631 app: &mut App,
632 owner: inset_foundation::Handle<PipelineOwner>,
633 ) {
634 ContainerRenderObjectMixin::did_attach(self, app, owner);
635 self.offset(app)
636 .add_listener(app, self.mark_needs_layout_listener());
637 }
638
639 fn did_detach(self: RenderHandle<Self>, app: &mut App) {
641 self.offset(app)
642 .remove_listener(app, &self.mark_needs_layout_listener());
643 ContainerRenderObjectMixin::did_detach(self, app);
644 }
645
646 fn debug_throw_if_not_checking_intrinsics(self: RenderHandle<Self>) -> bool {
653 let _ = self;
654 debug_assert!(
655 debug_checking_intrinsics(),
656 "{} does not support returning intrinsic dimensions.\n\
657 Calculating the intrinsic dimensions would require instantiating every child of \
658 the viewport, which defeats the point of viewports being lazy.\n\
659 If you are merely trying to shrink-wrap the viewport in the main axis direction, \
660 consider a RenderShrinkWrappingViewport render object (ShrinkWrappingViewport \
661 widget), which achieves that effect without implementing the intrinsic dimension \
662 API.",
663 std::any::type_name::<Self>()
664 );
665 true
666 }
667
668 fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
670 let _ = (app, height);
671 debug_assert!(self.debug_throw_if_not_checking_intrinsics());
672 0.0
673 }
674
675 fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
677 let _ = (app, height);
678 debug_assert!(self.debug_throw_if_not_checking_intrinsics());
679 0.0
680 }
681
682 fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
684 let _ = (app, width);
685 debug_assert!(self.debug_throw_if_not_checking_intrinsics());
686 0.0
687 }
688
689 fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
691 let _ = (app, width);
692 debug_assert!(self.debug_throw_if_not_checking_intrinsics());
693 0.0
694 }
695
696 #[allow(clippy::too_many_arguments)]
725 fn layout_child_sequence(
726 self: RenderHandle<Self>,
727 app: &mut App,
728 child: Option<AnyRenderSliver>,
729 scroll_offset: f64,
730 overlap: f64,
731 layout_offset: f64,
732 remaining_paint_extent: f64,
733 main_axis_extent: f64,
734 cross_axis_extent: f64,
735 growth_direction: GrowthDirection,
736 advance: &SliverAdvance<Self>,
737 remaining_cache_extent: f64,
738 cache_origin: f64,
739 ) -> f64 {
740 debug_assert!(scroll_offset.is_finite());
741 debug_assert!(scroll_offset >= 0.0);
742 let initial_layout_offset = layout_offset;
743 let adjusted_user_scroll_direction = apply_growth_direction_to_scroll_direction(
744 self.offset(app).user_scroll_direction(app),
745 growth_direction,
746 );
747 let axis_direction = self.axis_direction(app);
748 let cross_axis_direction = self.cross_axis_direction(app);
749 let mut max_paint_offset = layout_offset + overlap;
750 let mut preceding_scroll_extent = 0.0;
751 let mut scroll_offset = scroll_offset;
752 let mut layout_offset = layout_offset;
753 let mut remaining_cache_extent = remaining_cache_extent;
754 let mut cache_origin = cache_origin;
755 let mut child = child;
756
757 while let Some(current) = child {
758 let sliver_scroll_offset = scroll_offset.max(0.0);
759 let corrected_cache_origin = cache_origin.max(-sliver_scroll_offset);
762 let cache_extent_correction = cache_origin - corrected_cache_origin;
763
764 debug_assert!(sliver_scroll_offset >= corrected_cache_origin.abs());
765 debug_assert!(corrected_cache_origin <= 0.0);
766 debug_assert!(sliver_scroll_offset >= 0.0);
767 debug_assert!(cache_extent_correction <= 0.0);
768
769 current.layout(
770 app,
771 SliverConstraints::new(
772 axis_direction,
773 growth_direction,
774 adjusted_user_scroll_direction,
775 sliver_scroll_offset,
776 preceding_scroll_extent,
777 max_paint_offset - layout_offset,
778 (remaining_paint_extent - layout_offset + initial_layout_offset).max(0.0),
779 cross_axis_extent,
780 cross_axis_direction,
781 main_axis_extent,
782 (remaining_cache_extent + cache_extent_correction).max(0.0),
783 corrected_cache_origin,
784 ),
785 true,
786 );
787
788 let child_layout_geometry = current.geometry(app);
789 debug_assert!(child_layout_geometry.debug_assert_is_valid());
790
791 if let Some(correction) = child_layout_geometry.scroll_offset_correction {
793 return correction;
794 }
795
796 let effective_layout_offset = layout_offset + child_layout_geometry.paint_origin;
799
800 if child_layout_geometry.visible || scroll_offset > 0.0 {
804 self.update_child_layout_offset(
805 app,
806 current,
807 effective_layout_offset,
808 growth_direction,
809 );
810 } else {
811 self.update_child_layout_offset(
812 app,
813 current,
814 -scroll_offset + initial_layout_offset,
815 growth_direction,
816 );
817 }
818
819 max_paint_offset = (effective_layout_offset + child_layout_geometry.paint_extent)
820 .max(max_paint_offset);
821 scroll_offset -= child_layout_geometry.scroll_extent;
822 preceding_scroll_extent += child_layout_geometry.scroll_extent;
823 layout_offset += child_layout_geometry.layout_extent;
824 if child_layout_geometry.cache_extent != 0.0 {
825 remaining_cache_extent -=
826 child_layout_geometry.cache_extent - cache_extent_correction;
827 cache_origin =
828 (corrected_cache_origin + child_layout_geometry.cache_extent).min(0.0);
829 }
830
831 self.update_out_of_band_data(app, growth_direction, child_layout_geometry);
832
833 child = advance(self, app, current);
835 }
836
837 0.0
839 }
840
841 fn paint(
843 self: RenderHandle<Self>,
844 app: &mut App,
845 context: &mut PaintingContext,
846 offset: Offset,
847 ) {
848 if self.first_child(app).is_none() {
849 return;
850 }
851 let clip_behavior = self.clip_behavior(app);
852 if self.has_visual_overflow(app) && clip_behavior != Clip::None {
853 let bounds = Offset::ZERO & self.size(app);
854 let old = self.viewport_data(app).clip_rect_layer.layer();
855 let layer = context.push_clip_rect(
856 app,
857 self.as_object().needs_compositing(app),
858 offset,
859 bounds,
860 |app, context, offset| self.paint_contents(app, context, offset),
861 clip_behavior,
862 old,
863 );
864 LayerHandle::set_layer(
865 app,
866 |app| &mut self.viewport_data_mut(app).clip_rect_layer,
867 layer,
868 );
869 } else {
870 LayerHandle::set_layer(
871 app,
872 |app| &mut self.viewport_data_mut(app).clip_rect_layer,
873 None,
874 );
875 self.paint_contents(app, context, offset);
876 }
877 }
878
879 fn dispose(self: RenderHandle<Self>, app: &mut App) {
881 LayerHandle::set_layer(
882 app,
883 |app| &mut self.viewport_data_mut(app).clip_rect_layer,
884 None,
885 );
886 crate::object::RenderObjectBase::dispose(self, app);
887 }
888
889 fn paint_contents(
891 self: RenderHandle<Self>,
892 app: &mut App,
893 context: &mut PaintingContext,
894 offset: Offset,
895 ) {
896 for child in self.children_in_paint_order(app) {
897 if child.geometry(app).visible {
898 let child_offset = offset + self.paint_offset_of(app, child);
899 context.paint_child(app, child.as_object(), child_offset);
900 }
901 }
902 }
903
904 fn hit_test_children(
906 self: RenderHandle<Self>,
907 app: &mut App,
908 result: &mut BoxHitTestResult<'_>,
909 position: Offset,
910 ) -> bool {
911 let (main_axis_position, cross_axis_position) = match self.axis(app) {
912 Axis::Vertical => (position.dy(), position.dx()),
913 Axis::Horizontal => (position.dx(), position.dy()),
914 };
915 for child in self.children_in_hit_test_order(app) {
916 if !child.geometry(app).visible {
917 continue;
918 }
919 let mut transform = Matrix4::IDENTITY;
920 self.as_object()
922 .apply_paint_transform(app, child.as_object(), &mut transform);
923 let child_main_axis_position =
924 self.compute_child_main_axis_position(app, child, main_axis_position);
925 let is_hit =
926 result.add_with_out_of_band_position(None, Some(transform), None, |result| {
927 child.hit_test(
928 app,
929 &mut SliverHitTestResult::wrap(result),
930 child_main_axis_position,
931 cross_axis_position,
932 )
933 });
934 if is_hit {
935 return true;
936 }
937 }
938 false
939 }
940
941 fn get_offset_to_reveal(
943 self: RenderHandle<Self>,
944 app: &App,
945 target: AnyRenderObject,
946 alignment: f64,
947 rect: Option<Rect>,
948 _axis: Option<Axis>,
949 ) -> RevealedOffset {
950 let axis = self.axis(app);
953 let this = self.as_object();
954
955 let mut leading_scroll_offset = 0.0;
973 let mut child = target;
977 let mut pivot = None;
978 let mut only_slivers = target.as_sliver().is_some();
980 let mut rect = rect;
981 while child.parent(app) != Some(this) {
982 let parent = child
983 .parent(app)
984 .expect("target is a descendant of this viewport");
985 if let Some(box_child) = child.as_box() {
986 pivot = Some(box_child);
987 }
988 match parent.as_sliver() {
989 Some(sliver) => {
990 leading_scroll_offset += sliver
991 .child_scroll_offset(app, child)
992 .expect("a sliver parent reports its child's scroll offset");
993 }
994 None => {
995 only_slivers = false;
996 leading_scroll_offset = 0.0;
997 }
998 }
999 child = parent;
1000 }
1001
1002 let rect_local;
1004 let pivot_extent;
1006 let growth_direction;
1007
1008 if let Some(pivot) = pivot {
1011 debug_assert!(pivot.as_object().parent(app).is_some());
1012 debug_assert_ne!(pivot.as_object().parent(app), Some(this));
1013 debug_assert_ne!(pivot.as_object(), this);
1014 let pivot_parent = pivot
1015 .as_object()
1016 .parent(app)
1017 .and_then(AnyRenderObject::as_sliver)
1018 .expect("only sliver parents are supported between a pivot and the viewport");
1019 growth_direction = pivot_parent.constraints(app).growth_direction;
1020 pivot_extent = match axis {
1021 Axis::Horizontal => pivot.size(app).width(),
1022 Axis::Vertical => pivot.size(app).height(),
1023 };
1024 let rect = *rect.get_or_insert_with(|| target.paint_bounds(app));
1025 rect_local =
1026 transform_rect(&target.get_transform_to(app, Some(pivot.as_object())), rect);
1027 } else if only_slivers {
1028 let target_sliver = target.as_sliver().expect("only_slivers");
1031 growth_direction = target_sliver.constraints(app).growth_direction;
1032 pivot_extent = target_sliver.geometry(app).scroll_extent;
1033 rect_local = *rect.get_or_insert_with(|| {
1034 let constraints = target_sliver.constraints(app);
1035 let geometry = target_sliver.geometry(app);
1036 match axis {
1037 Axis::Horizontal => Rect::from_ltwh(
1038 0.0,
1039 0.0,
1040 geometry.scroll_extent,
1041 constraints.cross_axis_extent,
1042 ),
1043 Axis::Vertical => Rect::from_ltwh(
1044 0.0,
1045 0.0,
1046 constraints.cross_axis_extent,
1047 geometry.scroll_extent,
1048 ),
1049 }
1050 });
1051 } else {
1052 let rect = rect.expect("a non-sliver target must be given a rect");
1053 return RevealedOffset::new(self.offset(app).pixels(app), rect);
1054 }
1055
1056 debug_assert_eq!(child.parent(app), Some(this));
1057 let sliver = child
1058 .as_sliver()
1059 .expect("a viewport's children are slivers");
1060
1061 leading_scroll_offset += match apply_growth_direction_to_axis_direction(
1063 self.axis_direction(app),
1064 growth_direction,
1065 ) {
1066 AxisDirection::Up => pivot_extent - rect_local.bottom,
1067 AxisDirection::Left => pivot_extent - rect_local.right,
1068 AxisDirection::Right => rect_local.left,
1069 AxisDirection::Down => rect_local.top,
1070 };
1071
1072 let is_pinned = sliver.geometry(app).max_scroll_obstruction_extent > 0.0
1078 && leading_scroll_offset >= 0.0;
1079
1080 leading_scroll_offset = self.scroll_offset_of(app, sliver, leading_scroll_offset);
1082
1083 let transform = target.get_transform_to(app, Some(this));
1087 let mut target_rect = transform_rect(&transform, rect.expect("set above"));
1088 let extent_of_pinned_slivers = self.max_scroll_obstruction_extent_before(app, sliver);
1089
1090 match sliver.constraints(app).growth_direction {
1091 GrowthDirection::Forward => {
1092 if is_pinned && alignment <= 0.0 {
1093 return RevealedOffset::new(f64::INFINITY, target_rect);
1094 }
1095 leading_scroll_offset -= extent_of_pinned_slivers;
1096 }
1097 GrowthDirection::Reverse => {
1098 if is_pinned && alignment >= 1.0 {
1099 return RevealedOffset::new(f64::NEG_INFINITY, target_rect);
1100 }
1101 leading_scroll_offset -= match axis {
1105 Axis::Vertical => target_rect.height(),
1106 Axis::Horizontal => target_rect.width(),
1107 };
1108 }
1109 }
1110
1111 let size = self.size(app);
1112 let main_axis_extent_difference = match axis {
1113 Axis::Horizontal => size.width() - extent_of_pinned_slivers - rect_local.width(),
1114 Axis::Vertical => size.height() - extent_of_pinned_slivers - rect_local.height(),
1115 };
1116
1117 let target_offset = leading_scroll_offset - main_axis_extent_difference * alignment;
1118 let offset_difference = self.offset(app).pixels(app) - target_offset;
1119
1120 target_rect = match self.axis_direction(app) {
1121 AxisDirection::Up => target_rect.translate(0.0, -offset_difference),
1122 AxisDirection::Down => target_rect.translate(0.0, offset_difference),
1123 AxisDirection::Left => target_rect.translate(-offset_difference, 0.0),
1124 AxisDirection::Right => target_rect.translate(offset_difference, 0.0),
1125 };
1126
1127 RevealedOffset::new(target_offset, target_rect)
1128 }
1129
1130 fn compute_absolute_paint_offset(
1140 self: RenderHandle<Self>,
1141 app: &App,
1142 child: AnyRenderSliver,
1143 layout_offset: f64,
1144 growth_direction: GrowthDirection,
1145 ) -> Offset {
1146 debug_assert!(self.has_size(app));
1148 let paint_extent = child.geometry(app).paint_extent;
1149 let size = self.size(app);
1150 match apply_growth_direction_to_axis_direction(self.axis_direction(app), growth_direction) {
1151 AxisDirection::Up => Offset::new(0.0, size.height() - layout_offset - paint_extent),
1152 AxisDirection::Left => Offset::new(size.width() - layout_offset - paint_extent, 0.0),
1153 AxisDirection::Right => Offset::new(layout_offset, 0.0),
1154 AxisDirection::Down => Offset::new(0.0, layout_offset),
1155 }
1156 }
1157
1158 fn has_visual_overflow(self: RenderHandle<Self>, app: &App) -> bool;
1166
1167 fn update_out_of_band_data(
1172 self: RenderHandle<Self>,
1173 app: &mut App,
1174 growth_direction: GrowthDirection,
1175 child_layout_geometry: SliverGeometry,
1176 );
1177
1178 fn update_child_layout_offset(
1185 self: RenderHandle<Self>,
1186 app: &mut App,
1187 child: AnyRenderSliver,
1188 layout_offset: f64,
1189 growth_direction: GrowthDirection,
1190 );
1191
1192 fn paint_offset_of(self: RenderHandle<Self>, app: &App, child: AnyRenderSliver) -> Offset;
1203
1204 fn scroll_offset_of(
1210 self: RenderHandle<Self>,
1211 app: &App,
1212 child: AnyRenderSliver,
1213 scroll_offset_within_child: f64,
1214 ) -> f64;
1215
1216 fn max_scroll_obstruction_extent_before(
1223 self: RenderHandle<Self>,
1224 app: &App,
1225 child: AnyRenderSliver,
1226 ) -> f64;
1227
1228 fn compute_child_main_axis_position(
1241 self: RenderHandle<Self>,
1242 app: &App,
1243 child: AnyRenderSliver,
1244 parent_main_axis_position: f64,
1245 ) -> f64;
1246
1247 fn index_of_first_child(self: RenderHandle<Self>, app: &App) -> i32;
1252
1253 fn label_for_child(self: RenderHandle<Self>, index: i32) -> String;
1255
1256 fn children_in_paint_order(self: RenderHandle<Self>, app: &App) -> Vec<AnyRenderSliver> {
1261 match self.paint_order(app) {
1262 SliverPaintOrder::FirstIsTop => self.children_last_to_first(app),
1263 SliverPaintOrder::LastIsTop => self.children_as_list(app),
1264 }
1265 }
1266
1267 fn children_in_hit_test_order(self: RenderHandle<Self>, app: &App) -> Vec<AnyRenderSliver> {
1271 match self.paint_order(app) {
1272 SliverPaintOrder::FirstIsTop => self.children_as_list(app),
1273 SliverPaintOrder::LastIsTop => self.children_last_to_first(app),
1274 }
1275 }
1276
1277 fn children_last_to_first(self: RenderHandle<Self>, app: &App) -> Vec<AnyRenderSliver> {
1279 let mut children = Vec::new();
1280 let mut child = self.last_child(app);
1281 while let Some(current) = child {
1282 children.push(current);
1283 child = self.child_before(app, current);
1284 }
1285 children
1286 }
1287
1288 fn show_on_screen(
1290 self: RenderHandle<Self>,
1291 app: &mut App,
1292 descendant: Option<AnyRenderObject>,
1293 rect: Option<Rect>,
1294 duration: Duration,
1295 curve: Rc<dyn Curve>,
1296 ) {
1297 let offset = self.offset(app);
1298 if !offset.allow_implicit_scrolling(app) {
1299 return crate::object::RenderObjectBase::show_on_screen(
1300 self, app, descendant, rect, duration, curve,
1301 );
1302 }
1303
1304 let new_rect = show_in_viewport(
1305 app,
1306 descendant,
1307 rect,
1308 self.as_abstract_viewport(),
1309 offset,
1310 duration,
1311 Rc::clone(&curve),
1312 );
1313 crate::object::RenderObjectBase::show_on_screen(self, app, None, new_rect, duration, curve);
1314 }
1315}
1316
1317fn viewport_offset_changed<T: RenderViewportBase>(
1320 this: inset_foundation::Handle<T>,
1321 app: &mut App,
1322) {
1323 RenderHandle::from_handle(this).mark_needs_layout(app);
1324}
1325
1326pub fn show_in_viewport(
1343 app: &mut App,
1344 descendant: Option<AnyRenderObject>,
1345 rect: Option<Rect>,
1346 viewport: AnyRenderAbstractViewport,
1347 offset: AnyViewportOffset,
1348 duration: Duration,
1349 curve: Rc<dyn Curve>,
1350) -> Option<Rect> {
1351 let Some(descendant) = descendant else {
1352 return rect;
1353 };
1354 let leading_edge_offset = viewport.get_offset_to_reveal(app, descendant, 0.0, rect, None);
1355 let trailing_edge_offset = viewport.get_offset_to_reveal(app, descendant, 1.0, rect, None);
1356 let current_offset = offset.pixels(app);
1357 let target_offset =
1358 RevealedOffset::clamp_offset(leading_edge_offset, trailing_edge_offset, current_offset);
1359 let Some(target_offset) = target_offset else {
1360 let viewport_object = viewport.as_object(app);
1363 debug_assert!(viewport_object.parent(app).is_some());
1364 let transform = descendant.get_transform_to(app, viewport_object.parent(app));
1365 return Some(transform_rect(
1366 &transform,
1367 rect.unwrap_or_else(|| descendant.paint_bounds(app)),
1368 ));
1369 };
1370
1371 offset.move_to(app, target_offset.offset, Some(duration), Some(curve), None);
1372 Some(target_offset.rect)
1373}
1374
1375pub struct RenderViewport {
1393 render_object: RenderObjectData,
1394 render_box: RenderBoxData,
1395 container: ContainerRenderObjectData<AnyRenderSliver>,
1396 viewport: RenderViewportBaseData,
1397 anchor: f64,
1398 center: Option<AnyRenderSliver>,
1399 min_scroll_extent: f64,
1401 max_scroll_extent: f64,
1402 has_visual_overflow: bool,
1403}
1404
1405impl RenderViewport {
1406 const MAX_LAYOUT_CYCLES_PER_CHILD: usize = 10;
1408
1409 pub fn new(
1416 app: &mut App,
1417 cross_axis_direction: AxisDirection,
1418 offset: AnyViewportOffset,
1419 children: Option<Vec<AnyRenderSliver>>,
1420 center: Option<AnyRenderSliver>,
1421 ) -> RenderHandle<RenderViewport> {
1422 let this = RenderHandle::new_box(
1423 app,
1424 RenderViewport {
1425 render_object: RenderObjectData::new(),
1426 render_box: RenderBoxData::new(),
1427 container: ContainerRenderObjectData::new(),
1428 viewport: RenderViewportBaseData::new(cross_axis_direction, offset),
1429 anchor: 0.0,
1430 center,
1431 min_scroll_extent: 0.0,
1432 max_scroll_extent: 0.0,
1433 has_visual_overflow: false,
1434 },
1435 );
1436 this.add_all(app, children);
1437 if center.is_none()
1438 && let Some(first_child) = this.first_child(app)
1439 {
1440 this.get_mut(app).center = Some(first_child);
1441 }
1442 this
1443 }
1444
1445 pub fn anchor(self: RenderHandle<Self>, app: &App) -> f64 {
1452 self.get(app).anchor
1453 }
1454
1455 pub fn set_anchor(self: RenderHandle<Self>, app: &mut App, value: f64) {
1457 debug_assert!((0.0..=1.0).contains(&value));
1458 if value == self.get(app).anchor {
1459 return;
1460 }
1461 self.get_mut(app).anchor = value;
1462 self.mark_needs_layout(app);
1463 }
1464
1465 pub fn center(self: RenderHandle<Self>, app: &App) -> Option<AnyRenderSliver> {
1476 self.get(app).center
1477 }
1478
1479 pub fn set_center(self: RenderHandle<Self>, app: &mut App, value: Option<AnyRenderSliver>) {
1481 if value == self.get(app).center {
1482 return;
1483 }
1484 self.get_mut(app).center = value;
1485 self.mark_needs_layout(app);
1486 }
1487
1488 fn attempt_layout(
1490 self: RenderHandle<Self>,
1491 app: &mut App,
1492 main_axis_extent: f64,
1493 cross_axis_extent: f64,
1494 corrected_offset: f64,
1495 ) -> f64 {
1496 debug_assert!(!main_axis_extent.is_nan());
1497 debug_assert!(main_axis_extent >= 0.0);
1498 debug_assert!(main_axis_extent.is_finite());
1499 debug_assert!(cross_axis_extent.is_finite());
1500 debug_assert!(cross_axis_extent >= 0.0);
1501 debug_assert!(corrected_offset.is_finite());
1502 let this = self.get_mut(app);
1503 this.min_scroll_extent = 0.0;
1504 this.max_scroll_extent = 0.0;
1505 this.has_visual_overflow = false;
1506
1507 let anchor = self.get(app).anchor;
1510 let center_offset = main_axis_extent * anchor - corrected_offset;
1511 let reverse_direction_remaining_paint_extent =
1512 clamp_double(center_offset, 0.0, main_axis_extent);
1513 let forward_direction_remaining_paint_extent =
1514 clamp_double(main_axis_extent - center_offset, 0.0, main_axis_extent);
1515
1516 let calculated_cache_extent = self
1517 .scroll_cache_extent(app)
1518 .calculate_cache_offset(main_axis_extent);
1519 self.viewport_data_mut(app).calculated_cache_extent = Some(calculated_cache_extent);
1520
1521 let full_cache_extent = main_axis_extent + 2.0 * calculated_cache_extent;
1522 let center_cache_offset = center_offset + calculated_cache_extent;
1523 let reverse_direction_remaining_cache_extent =
1524 clamp_double(center_cache_offset, 0.0, full_cache_extent);
1525 let forward_direction_remaining_cache_extent = clamp_double(
1526 full_cache_extent - center_cache_offset,
1527 0.0,
1528 full_cache_extent,
1529 );
1530
1531 let center = self.center(app).expect("checked by perform_layout");
1532 let leading_negative_child = self.child_before(app, center);
1533
1534 if let Some(leading_negative_child) = leading_negative_child {
1535 let result = self.layout_child_sequence(
1537 app,
1538 Some(leading_negative_child),
1539 main_axis_extent.max(center_offset) - main_axis_extent,
1540 0.0,
1541 forward_direction_remaining_paint_extent,
1542 reverse_direction_remaining_paint_extent,
1543 main_axis_extent,
1544 cross_axis_extent,
1545 GrowthDirection::Reverse,
1546 &|this, app, child| this.child_before(app, child),
1547 reverse_direction_remaining_cache_extent,
1548 clamp_double(
1549 main_axis_extent - center_offset,
1550 -calculated_cache_extent,
1551 0.0,
1552 ),
1553 );
1554 if result != 0.0 {
1555 return -result;
1556 }
1557 }
1558
1559 self.layout_child_sequence(
1561 app,
1562 Some(center),
1563 (-center_offset).max(0.0),
1564 if leading_negative_child.is_none() {
1565 (-center_offset).min(0.0)
1566 } else {
1567 0.0
1568 },
1569 if center_offset >= main_axis_extent {
1570 center_offset
1571 } else {
1572 reverse_direction_remaining_paint_extent
1573 },
1574 forward_direction_remaining_paint_extent,
1575 main_axis_extent,
1576 cross_axis_extent,
1577 GrowthDirection::Forward,
1578 &|this, app, child| this.child_after(app, child),
1579 forward_direction_remaining_cache_extent,
1580 clamp_double(center_offset, -calculated_cache_extent, 0.0),
1581 )
1582 }
1583}
1584
1585impl ContainerRenderObjectMixin for RenderViewport {
1586 type ChildType = AnyRenderSliver;
1587 type ParentDataType = SliverPhysicalContainerParentData;
1588
1589 fn container_data(
1590 self: RenderHandle<Self>,
1591 app: &App,
1592 ) -> &ContainerRenderObjectData<AnyRenderSliver> {
1593 &self.get(app).container
1594 }
1595
1596 fn container_data_mut(
1597 self: RenderHandle<Self>,
1598 app: &mut App,
1599 ) -> &mut ContainerRenderObjectData<AnyRenderSliver> {
1600 &mut self.get_mut(app).container
1601 }
1602}
1603
1604impl RenderViewportBase for RenderViewport {
1605 crate::render_viewport_base_accessors!();
1606
1607 fn has_visual_overflow(self: RenderHandle<Self>, app: &App) -> bool {
1608 self.get(app).has_visual_overflow
1609 }
1610
1611 fn update_out_of_band_data(
1612 self: RenderHandle<Self>,
1613 app: &mut App,
1614 growth_direction: GrowthDirection,
1615 child_layout_geometry: SliverGeometry,
1616 ) {
1617 let this = self.get_mut(app);
1618 match growth_direction {
1619 GrowthDirection::Forward => {
1620 this.max_scroll_extent += child_layout_geometry.scroll_extent
1621 }
1622 GrowthDirection::Reverse => {
1623 this.min_scroll_extent -= child_layout_geometry.scroll_extent
1624 }
1625 }
1626 if child_layout_geometry.has_visual_overflow {
1627 this.has_visual_overflow = true;
1628 }
1629 }
1630
1631 fn update_child_layout_offset(
1632 self: RenderHandle<Self>,
1633 app: &mut App,
1634 child: AnyRenderSliver,
1635 layout_offset: f64,
1636 growth_direction: GrowthDirection,
1637 ) {
1638 let paint_offset =
1639 self.compute_absolute_paint_offset(app, child, layout_offset, growth_direction);
1640 child
1641 .as_object()
1642 .parent_data_of_mut::<SliverPhysicalContainerParentData>(app)
1643 .set_paint_offset(paint_offset);
1644 }
1645
1646 fn paint_offset_of(self: RenderHandle<Self>, app: &App, child: AnyRenderSliver) -> Offset {
1647 child
1648 .as_object()
1649 .parent_data_of::<SliverPhysicalContainerParentData>(app)
1650 .paint_offset()
1651 }
1652
1653 fn scroll_offset_of(
1654 self: RenderHandle<Self>,
1655 app: &App,
1656 child: AnyRenderSliver,
1657 scroll_offset_within_child: f64,
1658 ) -> f64 {
1659 debug_assert_eq!(child.as_object().parent(app), Some(self.as_object()));
1660 match child.constraints(app).growth_direction {
1661 GrowthDirection::Forward => {
1662 let mut scroll_offset_to_child = 0.0;
1663 let mut current = self.center(app);
1664 while current != Some(child) {
1665 let sliver = current.expect("the center precedes a forward child");
1666 scroll_offset_to_child += sliver.geometry(app).scroll_extent;
1667 current = self.child_after(app, sliver);
1668 }
1669 scroll_offset_to_child + scroll_offset_within_child
1670 }
1671 GrowthDirection::Reverse => {
1672 let mut scroll_offset_to_child = 0.0;
1673 let center = self.center(app).expect("a laid-out viewport has a center");
1674 let mut current = self.child_before(app, center);
1675 while current != Some(child) {
1676 let sliver = current.expect("a reverse child precedes the center");
1677 scroll_offset_to_child -= sliver.geometry(app).scroll_extent;
1678 current = self.child_before(app, sliver);
1679 }
1680 scroll_offset_to_child - scroll_offset_within_child
1681 }
1682 }
1683 }
1684
1685 fn max_scroll_obstruction_extent_before(
1686 self: RenderHandle<Self>,
1687 app: &App,
1688 child: AnyRenderSliver,
1689 ) -> f64 {
1690 debug_assert_eq!(child.as_object().parent(app), Some(self.as_object()));
1691 let mut pinned_extent = 0.0;
1692 match child.constraints(app).growth_direction {
1693 GrowthDirection::Forward => {
1694 let mut current = self.center(app);
1695 while current != Some(child) {
1696 let sliver = current.expect("the center precedes a forward child");
1697 pinned_extent += sliver.geometry(app).max_scroll_obstruction_extent;
1698 current = self.child_after(app, sliver);
1699 }
1700 }
1701 GrowthDirection::Reverse => {
1702 let center = self.center(app).expect("a laid-out viewport has a center");
1703 let mut current = self.child_before(app, center);
1704 while current != Some(child) {
1705 let sliver = current.expect("a reverse child precedes the center");
1706 pinned_extent += sliver.geometry(app).max_scroll_obstruction_extent;
1707 current = self.child_before(app, sliver);
1708 }
1709 }
1710 }
1711 pinned_extent
1712 }
1713
1714 fn compute_child_main_axis_position(
1715 self: RenderHandle<Self>,
1716 app: &App,
1717 child: AnyRenderSliver,
1718 parent_main_axis_position: f64,
1719 ) -> f64 {
1720 let paint_offset = child
1721 .as_object()
1722 .parent_data_of::<SliverPhysicalContainerParentData>(app)
1723 .paint_offset();
1724 let constraints = child.constraints(app);
1725 let paint_extent = child.geometry(app).paint_extent;
1726 match apply_growth_direction_to_axis_direction(
1727 constraints.axis_direction,
1728 constraints.growth_direction,
1729 ) {
1730 AxisDirection::Down => parent_main_axis_position - paint_offset.dy(),
1731 AxisDirection::Right => parent_main_axis_position - paint_offset.dx(),
1732 AxisDirection::Up => paint_extent - (parent_main_axis_position - paint_offset.dy()),
1733 AxisDirection::Left => paint_extent - (parent_main_axis_position - paint_offset.dx()),
1734 }
1735 }
1736
1737 fn index_of_first_child(self: RenderHandle<Self>, app: &App) -> i32 {
1738 let center = self.center(app).expect("a laid-out viewport has a center");
1739 debug_assert_eq!(center.as_object().parent(app), Some(self.as_object()));
1740 debug_assert!(self.first_child(app).is_some());
1741 let mut count = 0;
1742 let mut child = Some(center);
1743 while child != self.first_child(app) {
1744 count -= 1;
1745 child = self.child_before(app, child.expect("the first child terminates the walk"));
1746 }
1747 count
1748 }
1749
1750 fn label_for_child(self: RenderHandle<Self>, index: i32) -> String {
1751 if index == 0 {
1752 "center child".to_owned()
1753 } else {
1754 format!("child {index}")
1755 }
1756 }
1757}
1758
1759impl RenderAbstractViewport for RenderViewport {
1760 fn get_offset_to_reveal(
1761 self: RenderHandle<Self>,
1762 app: &App,
1763 target: AnyRenderObject,
1764 alignment: f64,
1765 rect: Option<Rect>,
1766 axis: Option<Axis>,
1767 ) -> RevealedOffset {
1768 RenderViewportBase::get_offset_to_reveal(self, app, target, alignment, rect, axis)
1769 }
1770}
1771
1772impl RenderObject for RenderViewport {
1773 crate::render_object_accessors!();
1774
1775 fn visit_children(
1776 self: RenderHandle<Self>,
1777 app: &App,
1778 visitor: &mut dyn FnMut(AnyRenderObject),
1779 ) {
1780 ContainerRenderObjectMixin::visit_children(self, app, visitor)
1781 }
1782
1783 fn did_attach(
1784 self: RenderHandle<Self>,
1785 app: &mut App,
1786 owner: inset_foundation::Handle<PipelineOwner>,
1787 ) {
1788 RenderViewportBase::did_attach(self, app, owner)
1789 }
1790
1791 fn did_detach(self: RenderHandle<Self>, app: &mut App) {
1792 RenderViewportBase::did_detach(self, app)
1793 }
1794
1795 fn redepth_children(self: RenderHandle<Self>, app: &mut App) {
1796 ContainerRenderObjectMixin::redepth_children(self, app)
1797 }
1798
1799 fn is_repaint_boundary(self: RenderHandle<Self>, _app: &App) -> bool {
1800 let _ = self;
1801 true
1802 }
1803
1804 fn interface(self: RenderHandle<Self>, id: TypeId) -> Option<Box<dyn Any>> {
1805 (id == TypeId::of::<AnyRenderAbstractViewport>())
1806 .then(|| Box::new(self.as_abstract_viewport()) as Box<dyn Any>)
1807 }
1808
1809 fn sized_by_parent(self: RenderHandle<Self>, _app: &App) -> bool {
1810 let _ = self;
1811 true
1812 }
1813
1814 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
1815 let offset = self.offset(app);
1818 let size = self.size(app);
1819 match self.axis(app) {
1820 Axis::Vertical => offset.apply_viewport_dimension(app, size.height()),
1821 Axis::Horizontal => offset.apply_viewport_dimension(app, size.width()),
1822 };
1823
1824 let Some(center) = self.center(app) else {
1825 debug_assert!(self.first_child(app).is_none());
1826 let this = self.get_mut(app);
1827 this.min_scroll_extent = 0.0;
1828 this.max_scroll_extent = 0.0;
1829 this.has_visual_overflow = false;
1830 offset.apply_content_dimensions(app, 0.0, 0.0);
1831 return;
1832 };
1833 debug_assert_eq!(center.as_object().parent(app), Some(self.as_object()));
1834
1835 let (main_axis_extent, cross_axis_extent) = match self.axis(app) {
1836 Axis::Vertical => (size.height(), size.width()),
1837 Axis::Horizontal => (size.width(), size.height()),
1838 };
1839
1840 let center_offset_adjustment = center.center_offset_adjustment(app);
1841 let max_layout_cycles = RenderViewport::MAX_LAYOUT_CYCLES_PER_CHILD * self.child_count(app);
1842
1843 let anchor = self.get(app).anchor;
1844 let mut count = 0;
1845 loop {
1846 let correction = self.attempt_layout(
1847 app,
1848 main_axis_extent,
1849 cross_axis_extent,
1850 offset.pixels(app) + center_offset_adjustment,
1851 );
1852 if correction != 0.0 {
1853 offset.correct_by(app, correction);
1854 } else {
1855 let min_scroll_extent = self.get(app).min_scroll_extent;
1856 let max_scroll_extent = self.get(app).max_scroll_extent;
1857 if offset.apply_content_dimensions(
1858 app,
1859 (min_scroll_extent + main_axis_extent * anchor).min(0.0),
1860 (max_scroll_extent - main_axis_extent * (1.0 - anchor)).max(0.0),
1861 ) {
1862 break;
1863 }
1864 }
1865 count += 1;
1866 if count >= max_layout_cycles {
1867 break;
1868 }
1869 }
1870 debug_assert!(
1871 count < max_layout_cycles,
1872 "A RenderViewport exceeded its maximum number of layout cycles.\n\
1873 RenderViewport render objects, during layout, can retry if either their slivers or \
1874 their ViewportOffset decide that the offset should be corrected to take into \
1875 account information collected during that layout.\n\
1876 In the case of this RenderViewport object, however, this happened {count} times and \
1877 still there was no consensus on the scroll offset. This usually indicates a bug."
1878 );
1879 }
1880
1881 fn paint(
1882 self: RenderHandle<Self>,
1883 app: &mut App,
1884 context: &mut PaintingContext,
1885 offset: Offset,
1886 ) {
1887 RenderViewportBase::paint(self, app, context, offset)
1888 }
1889
1890 fn dispose(self: RenderHandle<Self>, app: &mut App) {
1891 RenderViewportBase::dispose(self, app)
1892 }
1893
1894 fn show_on_screen(
1895 self: RenderHandle<Self>,
1896 app: &mut App,
1897 descendant: Option<AnyRenderObject>,
1898 rect: Option<Rect>,
1899 duration: Duration,
1900 curve: Rc<dyn Curve>,
1901 ) {
1902 RenderViewportBase::show_on_screen(self, app, descendant, rect, duration, curve)
1903 }
1904}
1905
1906impl RenderBox for RenderViewport {
1907 crate::render_box_accessors!();
1908
1909 fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
1910 if !child.parent_data_is::<SliverPhysicalContainerParentData>(app) {
1911 child.set_parent_data(app, SliverPhysicalContainerParentData::new());
1912 }
1913 }
1914
1915 fn apply_paint_transform(
1916 self: RenderHandle<Self>,
1917 app: &App,
1918 child: AnyRenderObject,
1919 transform: &mut Matrix4,
1920 ) {
1921 child
1923 .parent_data_of::<SliverPhysicalContainerParentData>(app)
1924 .apply_paint_transform(transform);
1925 }
1926
1927 fn hit_test_children(
1928 self: RenderHandle<Self>,
1929 app: &mut App,
1930 result: &mut BoxHitTestResult<'_>,
1931 position: Offset,
1932 ) -> bool {
1933 RenderViewportBase::hit_test_children(self, app, result, position)
1934 }
1935
1936 fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1937 RenderViewportBase::compute_min_intrinsic_width(self, app, height)
1938 }
1939
1940 fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1941 RenderViewportBase::compute_max_intrinsic_width(self, app, height)
1942 }
1943
1944 fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1945 RenderViewportBase::compute_min_intrinsic_height(self, app, width)
1946 }
1947
1948 fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1949 RenderViewportBase::compute_max_intrinsic_height(self, app, width)
1950 }
1951
1952 fn compute_dry_layout(
1953 self: RenderHandle<Self>,
1954 app: &mut App,
1955 constraints: BoxConstraints,
1956 ) -> Size {
1957 debug_assert!(debug_check_has_bounded_axis(self.axis(app), constraints));
1958 constraints.biggest()
1959 }
1960}
1961
1962pub struct RenderShrinkWrappingViewport {
1977 render_object: RenderObjectData,
1978 render_box: RenderBoxData,
1979 container: ContainerRenderObjectData<AnyRenderSliver>,
1980 viewport: RenderViewportBaseData,
1981 max_scroll_extent: f64,
1983 shrink_wrap_extent: f64,
1984 has_visual_overflow: bool,
1985}
1986
1987impl RenderShrinkWrappingViewport {
1988 pub fn new(
1992 app: &mut App,
1993 cross_axis_direction: AxisDirection,
1994 offset: AnyViewportOffset,
1995 children: Option<Vec<AnyRenderSliver>>,
1996 ) -> RenderHandle<RenderShrinkWrappingViewport> {
1997 let this = RenderHandle::new_box(
1998 app,
1999 RenderShrinkWrappingViewport {
2000 render_object: RenderObjectData::new(),
2001 render_box: RenderBoxData::new(),
2002 container: ContainerRenderObjectData::new(),
2003 viewport: RenderViewportBaseData::new(cross_axis_direction, offset),
2004 max_scroll_extent: 0.0,
2005 shrink_wrap_extent: 0.0,
2006 has_visual_overflow: false,
2007 },
2008 );
2009 this.add_all(app, children);
2010 this
2011 }
2012
2013 fn debug_check_has_bounded_cross_axis(self: RenderHandle<Self>, app: &App) -> bool {
2015 let constraints = self.constraints(app);
2016 match self.axis(app) {
2017 Axis::Vertical => debug_assert!(
2018 constraints.has_bounded_width(),
2019 "Vertical viewport was given unbounded width.\n\
2020 Viewports expand in the cross axis to fill their container and constrain their \
2021 children to match their extent in the cross axis. In this case, a vertical \
2022 shrinkwrapping viewport was given an unlimited amount of horizontal space in \
2023 which to expand."
2024 ),
2025 Axis::Horizontal => debug_assert!(
2026 constraints.has_bounded_height(),
2027 "Horizontal viewport was given unbounded height.\n\
2028 Viewports expand in the cross axis to fill their container and constrain their \
2029 children to match their extent in the cross axis. In this case, a horizontal \
2030 shrinkwrapping viewport was given an unlimited amount of vertical space in \
2031 which to expand."
2032 ),
2033 }
2034 true
2035 }
2036
2037 fn attempt_layout(
2039 self: RenderHandle<Self>,
2040 app: &mut App,
2041 main_axis_extent: f64,
2042 cross_axis_extent: f64,
2043 corrected_offset: f64,
2044 ) -> f64 {
2045 debug_assert!(!main_axis_extent.is_nan());
2050 debug_assert!(main_axis_extent >= 0.0);
2051 debug_assert!(cross_axis_extent.is_finite());
2052 debug_assert!(cross_axis_extent >= 0.0);
2053 debug_assert!(corrected_offset.is_finite());
2054 let this = self.get_mut(app);
2055 this.max_scroll_extent = 0.0;
2056 this.shrink_wrap_extent = 0.0;
2057 this.has_visual_overflow = corrected_offset < 0.0;
2060 let calculated_cache_extent = if main_axis_extent.is_finite() {
2061 self.scroll_cache_extent(app)
2062 .calculate_cache_offset(main_axis_extent)
2063 } else {
2064 0.0
2067 };
2068 self.viewport_data_mut(app).calculated_cache_extent = Some(calculated_cache_extent);
2069
2070 let first_child = self.first_child(app);
2071 self.layout_child_sequence(
2072 app,
2073 first_child,
2074 corrected_offset.max(0.0),
2075 corrected_offset.min(0.0),
2076 (-corrected_offset).max(0.0),
2077 main_axis_extent + corrected_offset.min(0.0),
2078 main_axis_extent,
2079 cross_axis_extent,
2080 GrowthDirection::Forward,
2081 &|this, app, child| this.child_after(app, child),
2082 main_axis_extent + 2.0 * calculated_cache_extent,
2083 -calculated_cache_extent,
2084 )
2085 }
2086}
2087
2088impl ContainerRenderObjectMixin for RenderShrinkWrappingViewport {
2089 type ChildType = AnyRenderSliver;
2090 type ParentDataType = SliverLogicalContainerParentData;
2091
2092 fn container_data(
2093 self: RenderHandle<Self>,
2094 app: &App,
2095 ) -> &ContainerRenderObjectData<AnyRenderSliver> {
2096 &self.get(app).container
2097 }
2098
2099 fn container_data_mut(
2100 self: RenderHandle<Self>,
2101 app: &mut App,
2102 ) -> &mut ContainerRenderObjectData<AnyRenderSliver> {
2103 &mut self.get_mut(app).container
2104 }
2105}
2106
2107impl RenderViewportBase for RenderShrinkWrappingViewport {
2108 crate::render_viewport_base_accessors!();
2109
2110 fn debug_throw_if_not_checking_intrinsics(self: RenderHandle<Self>) -> bool {
2111 let _ = self;
2112 debug_assert!(
2113 debug_checking_intrinsics(),
2114 "{} does not support returning intrinsic dimensions.\n\
2115 Calculating the intrinsic dimensions would require instantiating every child of \
2116 the viewport, which defeats the point of viewports being lazy.\n\
2117 If you are merely trying to shrink-wrap the viewport in the main axis direction, \
2118 you should be able to achieve that effect by just giving the viewport loose \
2119 constraints, without needing to measure its intrinsic dimensions.",
2120 std::any::type_name::<Self>()
2121 );
2122 true
2123 }
2124
2125 fn has_visual_overflow(self: RenderHandle<Self>, app: &App) -> bool {
2126 self.get(app).has_visual_overflow
2127 }
2128
2129 fn update_out_of_band_data(
2130 self: RenderHandle<Self>,
2131 app: &mut App,
2132 growth_direction: GrowthDirection,
2133 child_layout_geometry: SliverGeometry,
2134 ) {
2135 debug_assert_eq!(growth_direction, GrowthDirection::Forward);
2136 let this = self.get_mut(app);
2137 this.max_scroll_extent += child_layout_geometry.scroll_extent;
2138 if child_layout_geometry.has_visual_overflow {
2139 this.has_visual_overflow = true;
2140 }
2141 this.shrink_wrap_extent += child_layout_geometry.max_paint_extent;
2142 }
2143
2144 fn update_child_layout_offset(
2145 self: RenderHandle<Self>,
2146 app: &mut App,
2147 child: AnyRenderSliver,
2148 layout_offset: f64,
2149 growth_direction: GrowthDirection,
2150 ) {
2151 debug_assert_eq!(growth_direction, GrowthDirection::Forward);
2152 child
2153 .as_object()
2154 .parent_data_of_mut::<SliverLogicalContainerParentData>(app)
2155 .set_layout_offset(Some(layout_offset));
2156 }
2157
2158 fn paint_offset_of(self: RenderHandle<Self>, app: &App, child: AnyRenderSliver) -> Offset {
2159 let layout_offset = child
2160 .as_object()
2161 .parent_data_of::<SliverLogicalContainerParentData>(app)
2162 .layout_offset()
2163 .expect("a laid-out child has a layout offset");
2164 self.compute_absolute_paint_offset(app, child, layout_offset, GrowthDirection::Forward)
2165 }
2166
2167 fn scroll_offset_of(
2168 self: RenderHandle<Self>,
2169 app: &App,
2170 child: AnyRenderSliver,
2171 scroll_offset_within_child: f64,
2172 ) -> f64 {
2173 debug_assert_eq!(child.as_object().parent(app), Some(self.as_object()));
2174 debug_assert_eq!(
2175 child.constraints(app).growth_direction,
2176 GrowthDirection::Forward
2177 );
2178 let mut scroll_offset_to_child = 0.0;
2179 let mut current = self.first_child(app);
2180 while current != Some(child) {
2181 let sliver = current.expect("child is in the child list");
2182 scroll_offset_to_child += sliver.geometry(app).scroll_extent;
2183 current = self.child_after(app, sliver);
2184 }
2185 scroll_offset_to_child + scroll_offset_within_child
2186 }
2187
2188 fn max_scroll_obstruction_extent_before(
2189 self: RenderHandle<Self>,
2190 app: &App,
2191 child: AnyRenderSliver,
2192 ) -> f64 {
2193 debug_assert_eq!(child.as_object().parent(app), Some(self.as_object()));
2194 debug_assert_eq!(
2195 child.constraints(app).growth_direction,
2196 GrowthDirection::Forward
2197 );
2198 let mut pinned_extent = 0.0;
2199 let mut current = self.first_child(app);
2200 while current != Some(child) {
2201 let sliver = current.expect("child is in the child list");
2202 pinned_extent += sliver.geometry(app).max_scroll_obstruction_extent;
2203 current = self.child_after(app, sliver);
2204 }
2205 pinned_extent
2206 }
2207
2208 fn compute_child_main_axis_position(
2209 self: RenderHandle<Self>,
2210 app: &App,
2211 child: AnyRenderSliver,
2212 parent_main_axis_position: f64,
2213 ) -> f64 {
2214 debug_assert!(self.has_size(app));
2215 let layout_offset = child
2216 .as_object()
2217 .parent_data_of::<SliverLogicalContainerParentData>(app)
2218 .layout_offset()
2219 .expect("a laid-out child has a layout offset");
2220 let constraints = child.constraints(app);
2221 let size = self.size(app);
2222 match apply_growth_direction_to_axis_direction(
2223 constraints.axis_direction,
2224 constraints.growth_direction,
2225 ) {
2226 AxisDirection::Down | AxisDirection::Right => parent_main_axis_position - layout_offset,
2227 AxisDirection::Up => size.height() - parent_main_axis_position - layout_offset,
2228 AxisDirection::Left => size.width() - parent_main_axis_position - layout_offset,
2229 }
2230 }
2231
2232 fn index_of_first_child(self: RenderHandle<Self>, _app: &App) -> i32 {
2233 let _ = self;
2234 0
2235 }
2236
2237 fn label_for_child(self: RenderHandle<Self>, index: i32) -> String {
2238 let _ = self;
2239 format!("child {index}")
2240 }
2241}
2242
2243impl RenderAbstractViewport for RenderShrinkWrappingViewport {
2244 fn get_offset_to_reveal(
2245 self: RenderHandle<Self>,
2246 app: &App,
2247 target: AnyRenderObject,
2248 alignment: f64,
2249 rect: Option<Rect>,
2250 axis: Option<Axis>,
2251 ) -> RevealedOffset {
2252 RenderViewportBase::get_offset_to_reveal(self, app, target, alignment, rect, axis)
2253 }
2254}
2255
2256impl RenderObject for RenderShrinkWrappingViewport {
2257 crate::render_object_accessors!();
2258
2259 fn visit_children(
2260 self: RenderHandle<Self>,
2261 app: &App,
2262 visitor: &mut dyn FnMut(AnyRenderObject),
2263 ) {
2264 ContainerRenderObjectMixin::visit_children(self, app, visitor)
2265 }
2266
2267 fn did_attach(
2268 self: RenderHandle<Self>,
2269 app: &mut App,
2270 owner: inset_foundation::Handle<PipelineOwner>,
2271 ) {
2272 RenderViewportBase::did_attach(self, app, owner)
2273 }
2274
2275 fn did_detach(self: RenderHandle<Self>, app: &mut App) {
2276 RenderViewportBase::did_detach(self, app)
2277 }
2278
2279 fn redepth_children(self: RenderHandle<Self>, app: &mut App) {
2280 ContainerRenderObjectMixin::redepth_children(self, app)
2281 }
2282
2283 fn is_repaint_boundary(self: RenderHandle<Self>, _app: &App) -> bool {
2284 let _ = self;
2285 true
2286 }
2287
2288 fn interface(self: RenderHandle<Self>, id: TypeId) -> Option<Box<dyn Any>> {
2289 (id == TypeId::of::<AnyRenderAbstractViewport>())
2290 .then(|| Box::new(self.as_abstract_viewport()) as Box<dyn Any>)
2291 }
2292
2293 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
2294 let constraints = self.constraints(app);
2295 let offset = self.offset(app);
2296 if self.first_child(app).is_none() {
2297 debug_assert!(self.debug_check_has_bounded_cross_axis(app));
2299 let size = match self.axis(app) {
2300 Axis::Vertical => Size::new(constraints.max_width, constraints.min_height),
2301 Axis::Horizontal => Size::new(constraints.min_width, constraints.max_height),
2302 };
2303 self.set_size(app, size);
2304 offset.apply_viewport_dimension(app, 0.0);
2305 let this = self.get_mut(app);
2306 this.max_scroll_extent = 0.0;
2307 this.shrink_wrap_extent = 0.0;
2308 this.has_visual_overflow = false;
2309 offset.apply_content_dimensions(app, 0.0, 0.0);
2310 return;
2311 }
2312
2313 debug_assert!(self.debug_check_has_bounded_cross_axis(app));
2315 let (main_axis_extent, cross_axis_extent) = match self.axis(app) {
2316 Axis::Vertical => (constraints.max_height, constraints.max_width),
2317 Axis::Horizontal => (constraints.max_width, constraints.max_height),
2318 };
2319
2320 let effective_extent;
2321 loop {
2322 let corrected_offset = offset.pixels(app);
2323 let correction =
2324 self.attempt_layout(app, main_axis_extent, cross_axis_extent, corrected_offset);
2325 if correction != 0.0 {
2326 offset.correct_by(app, correction);
2327 continue;
2328 }
2329 let shrink_wrap_extent = self.get(app).shrink_wrap_extent;
2330 let extent = match self.axis(app) {
2331 Axis::Vertical => constraints.constrain_height(shrink_wrap_extent),
2332 Axis::Horizontal => constraints.constrain_width(shrink_wrap_extent),
2333 };
2334 let did_accept_viewport_dimension = offset.apply_viewport_dimension(app, extent);
2335 let max_scroll_extent = self.get(app).max_scroll_extent;
2336 let did_accept_content_dimension =
2337 offset.apply_content_dimensions(app, 0.0, (max_scroll_extent - extent).max(0.0));
2338 if did_accept_viewport_dimension && did_accept_content_dimension {
2339 effective_extent = extent;
2340 break;
2341 }
2342 }
2343 let size = match self.axis(app) {
2344 Axis::Vertical => constraints.constrain_dimensions(cross_axis_extent, effective_extent),
2345 Axis::Horizontal => {
2346 constraints.constrain_dimensions(effective_extent, cross_axis_extent)
2347 }
2348 };
2349 self.set_size(app, size);
2350 }
2351
2352 fn paint(
2353 self: RenderHandle<Self>,
2354 app: &mut App,
2355 context: &mut PaintingContext,
2356 offset: Offset,
2357 ) {
2358 RenderViewportBase::paint(self, app, context, offset)
2359 }
2360
2361 fn dispose(self: RenderHandle<Self>, app: &mut App) {
2362 RenderViewportBase::dispose(self, app)
2363 }
2364
2365 fn show_on_screen(
2366 self: RenderHandle<Self>,
2367 app: &mut App,
2368 descendant: Option<AnyRenderObject>,
2369 rect: Option<Rect>,
2370 duration: Duration,
2371 curve: Rc<dyn Curve>,
2372 ) {
2373 RenderViewportBase::show_on_screen(self, app, descendant, rect, duration, curve)
2374 }
2375}
2376
2377impl RenderBox for RenderShrinkWrappingViewport {
2378 crate::render_box_accessors!();
2379
2380 fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
2381 if !child.parent_data_is::<SliverLogicalContainerParentData>(app) {
2382 child.set_parent_data(app, SliverLogicalContainerParentData::new());
2383 }
2384 }
2385
2386 fn apply_paint_transform(
2387 self: RenderHandle<Self>,
2388 app: &App,
2389 child: AnyRenderObject,
2390 transform: &mut Matrix4,
2391 ) {
2392 let offset = self.paint_offset_of(
2394 app,
2395 child
2396 .as_sliver()
2397 .expect("a viewport's children are slivers"),
2398 );
2399 translate(transform, offset.dx(), offset.dy());
2400 }
2401
2402 fn hit_test_children(
2403 self: RenderHandle<Self>,
2404 app: &mut App,
2405 result: &mut BoxHitTestResult<'_>,
2406 position: Offset,
2407 ) -> bool {
2408 RenderViewportBase::hit_test_children(self, app, result, position)
2409 }
2410
2411 fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
2412 RenderViewportBase::compute_min_intrinsic_width(self, app, height)
2413 }
2414
2415 fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
2416 RenderViewportBase::compute_max_intrinsic_width(self, app, height)
2417 }
2418
2419 fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
2420 RenderViewportBase::compute_min_intrinsic_height(self, app, width)
2421 }
2422
2423 fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
2424 RenderViewportBase::compute_max_intrinsic_height(self, app, width)
2425 }
2426}
2427
2428#[cfg(test)]
2429mod tests {
2430 use inset_foundation::AppCell;
2431 use std::cell::Cell;
2432 use std::rc::Rc;
2433
2434 use inset_foundation::Handle;
2435 use inset_gestures::HitTestResult;
2436
2437 use super::*;
2438 use crate::box_::BoxConstraints;
2439 use crate::layer::{ContainerLayer, ErasedLayer, OffsetLayer};
2440 use crate::proxy_box::RenderConstrainedBox;
2441 use crate::sliver::{RenderSliver, RenderSliverToBoxAdapter};
2442 use crate::viewport_offset::{FixedViewportOffset, ViewportOffset};
2443
2444 struct TestSliver {
2446 render_object: RenderObjectData,
2447 render_sliver: crate::sliver::RenderSliverData,
2448 extent: f64,
2449 paint_offsets: Rc<Cell<Vec<Offset>>>,
2450 }
2451
2452 impl RenderObject for TestSliver {
2453 crate::render_object_accessors!();
2454
2455 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
2456 let constraints = self.constraints(app);
2457 let extent = self.get(app).extent;
2458 let painted = self.calculate_paint_offset(app, constraints, 0.0, extent);
2459 let cached = self.calculate_cache_offset(app, constraints, 0.0, extent);
2460 self.set_geometry(
2461 app,
2462 SliverGeometry::builder()
2463 .scroll_extent(extent)
2464 .paint_extent(painted)
2465 .cache_extent(cached)
2466 .max_paint_extent(extent)
2467 .build(),
2468 );
2469 }
2470
2471 fn paint(
2472 self: RenderHandle<Self>,
2473 app: &mut App,
2474 _context: &mut PaintingContext,
2475 offset: Offset,
2476 ) {
2477 let recorded = self.get(app).paint_offsets.clone();
2478 let mut offsets = recorded.take();
2479 offsets.push(offset);
2480 recorded.set(offsets);
2481 }
2482 }
2483
2484 impl RenderSliver for TestSliver {
2485 crate::render_sliver_accessors!();
2486
2487 fn hit_test_self(
2488 self: RenderHandle<Self>,
2489 _app: &App,
2490 _main_axis_position: f64,
2491 _cross_axis_position: f64,
2492 ) -> bool {
2493 let _ = self;
2494 true
2495 }
2496 }
2497
2498 fn test_sliver(app: &mut App, extent: f64) -> (AnyRenderSliver, Rc<Cell<Vec<Offset>>>) {
2499 let paint_offsets = Rc::new(Cell::new(Vec::new()));
2500 let sliver = RenderHandle::new_sliver(
2501 app,
2502 TestSliver {
2503 render_object: RenderObjectData::new(),
2504 render_sliver: crate::sliver::RenderSliverData::new(),
2505 extent,
2506 paint_offsets: paint_offsets.clone(),
2507 },
2508 )
2509 .as_sliver();
2510 (sliver, paint_offsets)
2511 }
2512
2513 fn first_frame(
2515 app: &mut App,
2516 root: crate::box_::AnyRenderBox,
2517 constraints: BoxConstraints,
2518 ) -> Handle<PipelineOwner> {
2519 let owner = PipelineOwner::new(app, None, None);
2520 owner.set_root_node(app, Some(root.as_object()));
2521 root.layout(app, constraints, false);
2522 let paint_root = OffsetLayer::new(app, Offset::ZERO);
2523 paint_root.as_layer().attach(app, root.as_object().id());
2524 root.as_object()
2525 .schedule_initial_paint(app, paint_root.as_container_layer());
2526 owner.flush_compositing_bits(app);
2527 owner.flush_paint(app);
2528 owner
2529 }
2530
2531 fn vertical_viewport(
2532 app: &mut App,
2533 offset: Handle<FixedViewportOffset>,
2534 children: Vec<AnyRenderSliver>,
2535 ) -> RenderHandle<RenderViewport> {
2536 RenderViewport::new(
2537 app,
2538 AxisDirection::Right,
2539 offset.as_viewport_offset(),
2540 Some(children),
2541 None,
2542 )
2543 }
2544
2545 #[test]
2548 #[should_panic(expected = "does not support returning intrinsic dimensions")]
2549 fn a_viewport_has_no_intrinsic_dimensions() {
2550 let cell = AppCell::new();
2551 let mut app = cell.borrow_mut();
2552 let offset = FixedViewportOffset::zero(&mut app);
2553 let viewport = vertical_viewport(&mut app, offset, vec![]);
2554 viewport.as_box().get_min_intrinsic_width(&mut app, 0.0);
2555 }
2556
2557 #[test]
2559 fn a_viewport_reports_zero_intrinsics_while_checking_them() {
2560 let cell = AppCell::new();
2561 let mut app = cell.borrow_mut();
2562 let offset = FixedViewportOffset::zero(&mut app);
2563 let viewport = vertical_viewport(&mut app, offset, vec![]);
2564 crate::object::set_debug_checking_intrinsics(true);
2565 let width = viewport.as_box().get_min_intrinsic_width(&mut app, 0.0);
2566 let height = viewport.as_box().get_max_intrinsic_height(&mut app, 0.0);
2567 crate::object::set_debug_checking_intrinsics(false);
2568 assert_eq!((width, height), (0.0, 0.0));
2569 }
2570
2571 #[test]
2574 fn a_viewport_dry_layout_is_the_biggest_size() {
2575 let cell = AppCell::new();
2576 let mut app = cell.borrow_mut();
2577 let (sliver, _paints) = test_sliver(&mut app, 100.0);
2578 let offset = FixedViewportOffset::zero(&mut app);
2579 let viewport = vertical_viewport(&mut app, offset, vec![sliver]);
2580 let constraints = BoxConstraints::new().max_width(200.0).max_height(400.0);
2581 let dry = viewport.as_box().get_dry_layout(&mut app, constraints);
2582 first_frame(&mut app, viewport.as_box(), constraints);
2583 assert_eq!(dry, Size::new(200.0, 400.0));
2584 assert_eq!(dry, viewport.size(&app));
2585 }
2586
2587 #[test]
2590 fn a_viewport_lays_its_slivers_out_in_order() {
2591 let cell = AppCell::new();
2592 let mut app = cell.borrow_mut();
2593 let (first, first_paints) = test_sliver(&mut app, 100.0);
2594 let (second, second_paints) = test_sliver(&mut app, 300.0);
2595 let offset = FixedViewportOffset::zero(&mut app);
2596 let viewport = vertical_viewport(&mut app, offset, vec![first, second]);
2597
2598 first_frame(
2599 &mut app,
2600 viewport.as_box(),
2601 BoxConstraints::tight(Size::new(200.0, 400.0)),
2602 );
2603
2604 assert_eq!(viewport.size(&app), Size::new(200.0, 400.0));
2605 assert_eq!(viewport.center(&app), Some(first));
2606 assert_eq!(first.geometry(&app).paint_extent, 100.0);
2607 assert_eq!(second.geometry(&app).paint_extent, 300.0);
2608 assert_eq!(viewport.paint_offset_of(&app, first), Offset::ZERO);
2609 assert_eq!(
2610 viewport.paint_offset_of(&app, second),
2611 Offset::new(0.0, 100.0)
2612 );
2613 assert_eq!(first_paints.take(), vec![Offset::ZERO]);
2614 assert_eq!(second_paints.take(), vec![Offset::new(0.0, 100.0)]);
2615 }
2616
2617 #[test]
2620 fn a_scrolled_viewport_moves_its_slivers() {
2621 let cell = AppCell::new();
2622 let mut app = cell.borrow_mut();
2623 let (first, _) = test_sliver(&mut app, 100.0);
2624 let (second, _) = test_sliver(&mut app, 300.0);
2625 let offset = FixedViewportOffset::new(&mut app, 60.0);
2626 let viewport = vertical_viewport(&mut app, offset, vec![first, second]);
2627
2628 first_frame(
2629 &mut app,
2630 viewport.as_box(),
2631 BoxConstraints::tight(Size::new(200.0, 400.0)),
2632 );
2633
2634 assert_eq!(first.constraints(&app).scroll_offset, 60.0);
2635 assert_eq!(first.geometry(&app).paint_extent, 40.0);
2636 assert_eq!(viewport.paint_offset_of(&app, first), Offset::ZERO);
2637 assert_eq!(
2638 viewport.paint_offset_of(&app, second),
2639 Offset::new(0.0, 40.0)
2640 );
2641 assert_eq!(viewport.scroll_offset_of(&app, second, 0.0), 100.0);
2642 }
2643
2644 #[test]
2646 fn a_viewport_hit_tests_the_sliver_under_the_position() {
2647 let cell = AppCell::new();
2648 let mut app = cell.borrow_mut();
2649 let (first, _) = test_sliver(&mut app, 100.0);
2650 let (second, _) = test_sliver(&mut app, 300.0);
2651 let offset = FixedViewportOffset::zero(&mut app);
2652 let viewport = vertical_viewport(&mut app, offset, vec![first, second]);
2653 first_frame(
2654 &mut app,
2655 viewport.as_box(),
2656 BoxConstraints::tight(Size::new(200.0, 400.0)),
2657 );
2658
2659 let mut result = HitTestResult::new();
2660 let hit = viewport.hit_test(
2661 &mut app,
2662 &mut BoxHitTestResult::wrap(&mut result),
2663 Offset::new(20.0, 150.0),
2664 );
2665
2666 assert!(hit);
2667 assert_eq!(result.path().len(), 2);
2668 assert_eq!(
2670 viewport.compute_child_main_axis_position(&app, second, 150.0),
2671 50.0
2672 );
2673 }
2674
2675 #[test]
2678 fn a_sliver_hit_entry_maps_into_the_sliver() {
2679 let cell = AppCell::new();
2680 let mut app = cell.borrow_mut();
2681 let (first, _) = test_sliver(&mut app, 100.0);
2682 let (second, _) = test_sliver(&mut app, 300.0);
2683 let offset = FixedViewportOffset::zero(&mut app);
2684 let viewport = vertical_viewport(&mut app, offset, vec![first, second]);
2685 first_frame(
2686 &mut app,
2687 viewport.as_box(),
2688 BoxConstraints::tight(Size::new(200.0, 400.0)),
2689 );
2690
2691 let mut result = HitTestResult::new();
2692 viewport.hit_test(
2693 &mut app,
2694 &mut BoxHitTestResult::wrap(&mut result),
2695 Offset::new(20.0, 150.0),
2696 );
2697
2698 let transform = result.path()[0].transform().unwrap();
2699 assert_eq!(
2700 inset_painting::transform_point(&transform, Offset::new(20.0, 150.0)),
2701 Offset::new(20.0, 50.0)
2702 );
2703 }
2704
2705 #[test]
2707 fn the_cache_extent_widens_the_laid_out_region() {
2708 let cell = AppCell::new();
2709 let mut app = cell.borrow_mut();
2710 let (sliver, _) = test_sliver(&mut app, 1000.0);
2711 let offset = FixedViewportOffset::zero(&mut app);
2712 let viewport = vertical_viewport(&mut app, offset, vec![sliver]);
2713 viewport.set_scroll_cache_extent(&mut app, Some(ScrollCacheExtent::Pixels(100.0)));
2714
2715 first_frame(
2716 &mut app,
2717 viewport.as_box(),
2718 BoxConstraints::tight(Size::new(200.0, 400.0)),
2719 );
2720
2721 assert_eq!(sliver.constraints(&app).remaining_cache_extent, 500.0);
2722 assert_eq!(sliver.geometry(&app).cache_extent, 500.0);
2723 assert_eq!(sliver.geometry(&app).paint_extent, 400.0);
2724
2725 viewport.set_scroll_cache_extent(&mut app, Some(ScrollCacheExtent::Viewport(0.5)));
2726 viewport.as_box().layout(
2727 &mut app,
2728 BoxConstraints::tight(Size::new(200.0, 400.0)),
2729 false,
2730 );
2731 assert_eq!(sliver.constraints(&app).remaining_cache_extent, 600.0);
2732 }
2733
2734 #[test]
2737 fn a_shrink_wrapping_viewport_sizes_itself_to_its_slivers() {
2738 let cell = AppCell::new();
2739 let mut app = cell.borrow_mut();
2740 let (first, _) = test_sliver(&mut app, 60.0);
2741 let (second, _) = test_sliver(&mut app, 90.0);
2742 let offset = FixedViewportOffset::zero(&mut app);
2743 let viewport = RenderShrinkWrappingViewport::new(
2744 &mut app,
2745 AxisDirection::Right,
2746 offset.as_viewport_offset(),
2747 Some(vec![first, second]),
2748 );
2749
2750 first_frame(
2751 &mut app,
2752 viewport.as_box(),
2753 BoxConstraints::new()
2754 .max_width(200.0)
2755 .min_width(200.0)
2756 .max_height(400.0),
2757 );
2758
2759 assert_eq!(viewport.size(&app), Size::new(200.0, 150.0));
2760 assert_eq!(
2761 viewport.paint_offset_of(&app, second),
2762 Offset::new(0.0, 60.0)
2763 );
2764 assert_eq!(viewport.index_of_first_child(&app), 0);
2765 assert_eq!(viewport.label_for_child(1), "child 1");
2766 }
2767
2768 #[test]
2771 fn a_viewport_hosts_a_box_through_the_adapter() {
2772 let cell = AppCell::new();
2773 let mut app = cell.borrow_mut();
2774 let child =
2775 RenderConstrainedBox::new(&mut app, BoxConstraints::tight_for(None, Some(80.0)), None);
2776 let adapter = RenderSliverToBoxAdapter::new(&mut app, Some(child.as_box()));
2777 let offset = FixedViewportOffset::zero(&mut app);
2778 let viewport = vertical_viewport(&mut app, offset, vec![adapter.as_sliver()]);
2779
2780 first_frame(
2781 &mut app,
2782 viewport.as_box(),
2783 BoxConstraints::tight(Size::new(200.0, 400.0)),
2784 );
2785
2786 assert_eq!(child.size(&app), Size::new(200.0, 80.0));
2787 assert_eq!(adapter.geometry(&app).scroll_extent, 80.0);
2788 assert_eq!(
2789 child.as_box().local_to_global(&app, Offset::ZERO, None),
2790 Offset::ZERO
2791 );
2792 }
2793
2794 #[test]
2796 fn maybe_of_finds_the_enclosing_viewport() {
2797 let cell = AppCell::new();
2798 let mut app = cell.borrow_mut();
2799 let child =
2800 RenderConstrainedBox::new(&mut app, BoxConstraints::tight_for(None, Some(80.0)), None);
2801 let adapter = RenderSliverToBoxAdapter::new(&mut app, Some(child.as_box()));
2802 let offset = FixedViewportOffset::zero(&mut app);
2803 let viewport = vertical_viewport(&mut app, offset, vec![adapter.as_sliver()]);
2804 first_frame(
2805 &mut app,
2806 viewport.as_box(),
2807 BoxConstraints::tight(Size::new(200.0, 400.0)),
2808 );
2809
2810 let found = AnyRenderAbstractViewport::maybe_of(&app, Some(child.as_object()));
2811 assert_eq!(found, Some(viewport.as_abstract_viewport()));
2812 assert!(AnyRenderAbstractViewport::maybe_of(&app, None).is_none());
2813 }
2814
2815 #[test]
2817 fn get_offset_to_reveal_reports_the_leading_edge_offset() {
2818 let cell = AppCell::new();
2819 let mut app = cell.borrow_mut();
2820 let leading =
2821 RenderConstrainedBox::new(&mut app, BoxConstraints::tight_for(None, Some(100.0)), None);
2822 let first = RenderSliverToBoxAdapter::new(&mut app, Some(leading.as_box()));
2823 let target =
2824 RenderConstrainedBox::new(&mut app, BoxConstraints::tight_for(None, Some(50.0)), None);
2825 let second = RenderSliverToBoxAdapter::new(&mut app, Some(target.as_box()));
2826 let offset = FixedViewportOffset::zero(&mut app);
2827 let viewport = vertical_viewport(
2828 &mut app,
2829 offset,
2830 vec![first.as_sliver(), second.as_sliver()],
2831 );
2832 first_frame(
2833 &mut app,
2834 viewport.as_box(),
2835 BoxConstraints::tight(Size::new(200.0, 400.0)),
2836 );
2837
2838 let revealed = RenderAbstractViewport::get_offset_to_reveal(
2839 viewport,
2840 &app,
2841 target.as_object(),
2842 0.0,
2843 None,
2844 None,
2845 );
2846 assert_eq!(revealed.offset, 100.0);
2847 assert_eq!(revealed.rect.top, 0.0);
2849 assert_eq!(revealed.rect.bottom, 50.0);
2850 }
2851
2852 #[test]
2854 fn clamp_offset_picks_the_nearer_edge() {
2855 let leading = RevealedOffset::new(100.0, Rect::ZERO);
2856 let trailing = RevealedOffset::new(20.0, Rect::ZERO);
2857 assert_eq!(
2858 RevealedOffset::clamp_offset(leading, trailing, 200.0).map(|offset| offset.offset),
2859 Some(100.0)
2860 );
2861 assert_eq!(
2862 RevealedOffset::clamp_offset(leading, trailing, 0.0).map(|offset| offset.offset),
2863 Some(20.0)
2864 );
2865 assert!(RevealedOffset::clamp_offset(leading, trailing, 50.0).is_none());
2866 }
2867
2868 #[test]
2870 fn the_paint_order_reverses_the_hit_test_order() {
2871 let cell = AppCell::new();
2872 let mut app = cell.borrow_mut();
2873 let (first, _) = test_sliver(&mut app, 100.0);
2874 let (second, _) = test_sliver(&mut app, 300.0);
2875 let offset = FixedViewportOffset::zero(&mut app);
2876 let viewport = vertical_viewport(&mut app, offset, vec![first, second]);
2877 first_frame(
2878 &mut app,
2879 viewport.as_box(),
2880 BoxConstraints::tight(Size::new(200.0, 400.0)),
2881 );
2882
2883 assert_eq!(viewport.children_in_paint_order(&app), vec![second, first]);
2884 assert_eq!(
2885 viewport.children_in_hit_test_order(&app),
2886 vec![first, second]
2887 );
2888
2889 viewport.set_paint_order(&mut app, SliverPaintOrder::LastIsTop);
2890 assert_eq!(viewport.children_in_paint_order(&app), vec![first, second]);
2891 assert_eq!(
2892 viewport.children_in_hit_test_order(&app),
2893 vec![second, first]
2894 );
2895 }
2896}