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 RenderObject::apply_paint_transform(self, app, child.as_object(), &mut transform);
922 let child_main_axis_position =
923 self.compute_child_main_axis_position(app, child, main_axis_position);
924 let is_hit =
925 result.add_with_out_of_band_position(None, Some(transform), None, |result| {
926 child.hit_test(
927 app,
928 &mut SliverHitTestResult::wrap(result),
929 child_main_axis_position,
930 cross_axis_position,
931 )
932 });
933 if is_hit {
934 return true;
935 }
936 }
937 false
938 }
939
940 fn get_offset_to_reveal(
942 self: RenderHandle<Self>,
943 app: &App,
944 target: AnyRenderObject,
945 alignment: f64,
946 rect: Option<Rect>,
947 _axis: Option<Axis>,
948 ) -> RevealedOffset {
949 let axis = self.axis(app);
952 let this = self.as_object();
953
954 let mut leading_scroll_offset = 0.0;
972 let mut child = target;
976 let mut pivot = None;
977 let mut only_slivers = target.as_sliver().is_some();
979 let mut rect = rect;
980 while child.parent(app) != Some(this) {
981 let parent = child
982 .parent(app)
983 .expect("target is a descendant of this viewport");
984 if let Some(box_child) = child.as_box() {
985 pivot = Some(box_child);
986 }
987 match parent.as_sliver() {
988 Some(sliver) => {
989 leading_scroll_offset += sliver
990 .child_scroll_offset(app, child)
991 .expect("a sliver parent reports its child's scroll offset");
992 }
993 None => {
994 only_slivers = false;
995 leading_scroll_offset = 0.0;
996 }
997 }
998 child = parent;
999 }
1000
1001 let rect_local;
1003 let pivot_extent;
1005 let growth_direction;
1006
1007 if let Some(pivot) = pivot {
1010 debug_assert!(pivot.as_object().parent(app).is_some());
1011 debug_assert_ne!(pivot.as_object().parent(app), Some(this));
1012 debug_assert_ne!(pivot.as_object(), this);
1013 let pivot_parent = pivot
1014 .as_object()
1015 .parent(app)
1016 .and_then(AnyRenderObject::as_sliver)
1017 .expect("only sliver parents are supported between a pivot and the viewport");
1018 growth_direction = pivot_parent.constraints(app).growth_direction;
1019 pivot_extent = match axis {
1020 Axis::Horizontal => pivot.size(app).width(),
1021 Axis::Vertical => pivot.size(app).height(),
1022 };
1023 let rect = *rect.get_or_insert_with(|| target.paint_bounds(app));
1024 rect_local =
1025 transform_rect(&target.get_transform_to(app, Some(pivot.as_object())), rect);
1026 } else if only_slivers {
1027 let target_sliver = target.as_sliver().expect("only_slivers");
1030 growth_direction = target_sliver.constraints(app).growth_direction;
1031 pivot_extent = target_sliver.geometry(app).scroll_extent;
1032 rect_local = *rect.get_or_insert_with(|| {
1033 let constraints = target_sliver.constraints(app);
1034 let geometry = target_sliver.geometry(app);
1035 match axis {
1036 Axis::Horizontal => Rect::from_ltwh(
1037 0.0,
1038 0.0,
1039 geometry.scroll_extent,
1040 constraints.cross_axis_extent,
1041 ),
1042 Axis::Vertical => Rect::from_ltwh(
1043 0.0,
1044 0.0,
1045 constraints.cross_axis_extent,
1046 geometry.scroll_extent,
1047 ),
1048 }
1049 });
1050 } else {
1051 let rect = rect.expect("a non-sliver target must be given a rect");
1052 return RevealedOffset::new(self.offset(app).pixels(app), rect);
1053 }
1054
1055 debug_assert_eq!(child.parent(app), Some(this));
1056 let sliver = child
1057 .as_sliver()
1058 .expect("a viewport's children are slivers");
1059
1060 leading_scroll_offset += match apply_growth_direction_to_axis_direction(
1062 self.axis_direction(app),
1063 growth_direction,
1064 ) {
1065 AxisDirection::Up => pivot_extent - rect_local.bottom,
1066 AxisDirection::Left => pivot_extent - rect_local.right,
1067 AxisDirection::Right => rect_local.left,
1068 AxisDirection::Down => rect_local.top,
1069 };
1070
1071 let is_pinned = sliver.geometry(app).max_scroll_obstruction_extent > 0.0
1077 && leading_scroll_offset >= 0.0;
1078
1079 leading_scroll_offset = self.scroll_offset_of(app, sliver, leading_scroll_offset);
1081
1082 let transform = target.get_transform_to(app, Some(this));
1086 let mut target_rect = transform_rect(&transform, rect.expect("set above"));
1087 let extent_of_pinned_slivers = self.max_scroll_obstruction_extent_before(app, sliver);
1088
1089 match sliver.constraints(app).growth_direction {
1090 GrowthDirection::Forward => {
1091 if is_pinned && alignment <= 0.0 {
1092 return RevealedOffset::new(f64::INFINITY, target_rect);
1093 }
1094 leading_scroll_offset -= extent_of_pinned_slivers;
1095 }
1096 GrowthDirection::Reverse => {
1097 if is_pinned && alignment >= 1.0 {
1098 return RevealedOffset::new(f64::NEG_INFINITY, target_rect);
1099 }
1100 leading_scroll_offset -= match axis {
1104 Axis::Vertical => target_rect.height(),
1105 Axis::Horizontal => target_rect.width(),
1106 };
1107 }
1108 }
1109
1110 let size = self.size(app);
1111 let main_axis_extent_difference = match axis {
1112 Axis::Horizontal => size.width() - extent_of_pinned_slivers - rect_local.width(),
1113 Axis::Vertical => size.height() - extent_of_pinned_slivers - rect_local.height(),
1114 };
1115
1116 let target_offset = leading_scroll_offset - main_axis_extent_difference * alignment;
1117 let offset_difference = self.offset(app).pixels(app) - target_offset;
1118
1119 target_rect = match self.axis_direction(app) {
1120 AxisDirection::Up => target_rect.translate(0.0, -offset_difference),
1121 AxisDirection::Down => target_rect.translate(0.0, offset_difference),
1122 AxisDirection::Left => target_rect.translate(-offset_difference, 0.0),
1123 AxisDirection::Right => target_rect.translate(offset_difference, 0.0),
1124 };
1125
1126 RevealedOffset::new(target_offset, target_rect)
1127 }
1128
1129 fn compute_absolute_paint_offset(
1139 self: RenderHandle<Self>,
1140 app: &App,
1141 child: AnyRenderSliver,
1142 layout_offset: f64,
1143 growth_direction: GrowthDirection,
1144 ) -> Offset {
1145 debug_assert!(self.has_size(app));
1147 let paint_extent = child.geometry(app).paint_extent;
1148 let size = self.size(app);
1149 match apply_growth_direction_to_axis_direction(self.axis_direction(app), growth_direction) {
1150 AxisDirection::Up => Offset::new(0.0, size.height() - layout_offset - paint_extent),
1151 AxisDirection::Left => Offset::new(size.width() - layout_offset - paint_extent, 0.0),
1152 AxisDirection::Right => Offset::new(layout_offset, 0.0),
1153 AxisDirection::Down => Offset::new(0.0, layout_offset),
1154 }
1155 }
1156
1157 fn has_visual_overflow(self: RenderHandle<Self>, app: &App) -> bool;
1165
1166 fn update_out_of_band_data(
1171 self: RenderHandle<Self>,
1172 app: &mut App,
1173 growth_direction: GrowthDirection,
1174 child_layout_geometry: SliverGeometry,
1175 );
1176
1177 fn update_child_layout_offset(
1184 self: RenderHandle<Self>,
1185 app: &mut App,
1186 child: AnyRenderSliver,
1187 layout_offset: f64,
1188 growth_direction: GrowthDirection,
1189 );
1190
1191 fn paint_offset_of(self: RenderHandle<Self>, app: &App, child: AnyRenderSliver) -> Offset;
1202
1203 fn scroll_offset_of(
1209 self: RenderHandle<Self>,
1210 app: &App,
1211 child: AnyRenderSliver,
1212 scroll_offset_within_child: f64,
1213 ) -> f64;
1214
1215 fn max_scroll_obstruction_extent_before(
1222 self: RenderHandle<Self>,
1223 app: &App,
1224 child: AnyRenderSliver,
1225 ) -> f64;
1226
1227 fn compute_child_main_axis_position(
1240 self: RenderHandle<Self>,
1241 app: &App,
1242 child: AnyRenderSliver,
1243 parent_main_axis_position: f64,
1244 ) -> f64;
1245
1246 fn index_of_first_child(self: RenderHandle<Self>, app: &App) -> i32;
1251
1252 fn label_for_child(self: RenderHandle<Self>, index: i32) -> String;
1254
1255 fn children_in_paint_order(self: RenderHandle<Self>, app: &App) -> Vec<AnyRenderSliver> {
1260 match self.paint_order(app) {
1261 SliverPaintOrder::FirstIsTop => self.children_last_to_first(app),
1262 SliverPaintOrder::LastIsTop => self.children_as_list(app),
1263 }
1264 }
1265
1266 fn children_in_hit_test_order(self: RenderHandle<Self>, app: &App) -> Vec<AnyRenderSliver> {
1270 match self.paint_order(app) {
1271 SliverPaintOrder::FirstIsTop => self.children_as_list(app),
1272 SliverPaintOrder::LastIsTop => self.children_last_to_first(app),
1273 }
1274 }
1275
1276 fn children_last_to_first(self: RenderHandle<Self>, app: &App) -> Vec<AnyRenderSliver> {
1278 let mut children = Vec::new();
1279 let mut child = self.last_child(app);
1280 while let Some(current) = child {
1281 children.push(current);
1282 child = self.child_before(app, current);
1283 }
1284 children
1285 }
1286
1287 fn show_on_screen(
1289 self: RenderHandle<Self>,
1290 app: &mut App,
1291 descendant: Option<AnyRenderObject>,
1292 rect: Option<Rect>,
1293 duration: Duration,
1294 curve: Rc<dyn Curve>,
1295 ) {
1296 let offset = self.offset(app);
1297 if !offset.allow_implicit_scrolling(app) {
1298 return crate::object::RenderObjectBase::show_on_screen(
1299 self, app, descendant, rect, duration, curve,
1300 );
1301 }
1302
1303 let new_rect = show_in_viewport(
1304 app,
1305 descendant,
1306 rect,
1307 self.as_abstract_viewport(),
1308 offset,
1309 duration,
1310 Rc::clone(&curve),
1311 );
1312 crate::object::RenderObjectBase::show_on_screen(self, app, None, new_rect, duration, curve);
1313 }
1314}
1315
1316fn viewport_offset_changed<T: RenderViewportBase>(
1319 this: inset_foundation::Handle<T>,
1320 app: &mut App,
1321) {
1322 RenderHandle::from_handle(this).mark_needs_layout(app);
1323}
1324
1325pub fn show_in_viewport(
1342 app: &mut App,
1343 descendant: Option<AnyRenderObject>,
1344 rect: Option<Rect>,
1345 viewport: AnyRenderAbstractViewport,
1346 offset: AnyViewportOffset,
1347 duration: Duration,
1348 curve: Rc<dyn Curve>,
1349) -> Option<Rect> {
1350 let Some(descendant) = descendant else {
1351 return rect;
1352 };
1353 let leading_edge_offset = viewport.get_offset_to_reveal(app, descendant, 0.0, rect, None);
1354 let trailing_edge_offset = viewport.get_offset_to_reveal(app, descendant, 1.0, rect, None);
1355 let current_offset = offset.pixels(app);
1356 let target_offset =
1357 RevealedOffset::clamp_offset(leading_edge_offset, trailing_edge_offset, current_offset);
1358 let Some(target_offset) = target_offset else {
1359 let viewport_object = viewport.as_object(app);
1362 debug_assert!(viewport_object.parent(app).is_some());
1363 let transform = descendant.get_transform_to(app, viewport_object.parent(app));
1364 return Some(transform_rect(
1365 &transform,
1366 rect.unwrap_or_else(|| descendant.paint_bounds(app)),
1367 ));
1368 };
1369
1370 offset.move_to(app, target_offset.offset, Some(duration), Some(curve), None);
1371 Some(target_offset.rect)
1372}
1373
1374pub struct RenderViewport {
1392 render_object: RenderObjectData,
1393 render_box: RenderBoxData,
1394 container: ContainerRenderObjectData<AnyRenderSliver>,
1395 viewport: RenderViewportBaseData,
1396 anchor: f64,
1397 center: Option<AnyRenderSliver>,
1398 min_scroll_extent: f64,
1400 max_scroll_extent: f64,
1401 has_visual_overflow: bool,
1402}
1403
1404impl RenderViewport {
1405 const MAX_LAYOUT_CYCLES_PER_CHILD: usize = 10;
1407
1408 pub fn new(
1415 app: &mut App,
1416 cross_axis_direction: AxisDirection,
1417 offset: AnyViewportOffset,
1418 children: Option<Vec<AnyRenderSliver>>,
1419 center: Option<AnyRenderSliver>,
1420 ) -> RenderHandle<RenderViewport> {
1421 let this = RenderHandle::new_box(
1422 app,
1423 RenderViewport {
1424 render_object: RenderObjectData::new(),
1425 render_box: RenderBoxData::new(),
1426 container: ContainerRenderObjectData::new(),
1427 viewport: RenderViewportBaseData::new(cross_axis_direction, offset),
1428 anchor: 0.0,
1429 center,
1430 min_scroll_extent: 0.0,
1431 max_scroll_extent: 0.0,
1432 has_visual_overflow: false,
1433 },
1434 );
1435 this.add_all(app, children);
1436 if center.is_none()
1437 && let Some(first_child) = this.first_child(app)
1438 {
1439 this.get_mut(app).center = Some(first_child);
1440 }
1441 this
1442 }
1443
1444 pub fn anchor(self: RenderHandle<Self>, app: &App) -> f64 {
1451 self.get(app).anchor
1452 }
1453
1454 pub fn set_anchor(self: RenderHandle<Self>, app: &mut App, value: f64) {
1456 debug_assert!((0.0..=1.0).contains(&value));
1457 if value == self.get(app).anchor {
1458 return;
1459 }
1460 self.get_mut(app).anchor = value;
1461 self.mark_needs_layout(app);
1462 }
1463
1464 pub fn center(self: RenderHandle<Self>, app: &App) -> Option<AnyRenderSliver> {
1475 self.get(app).center
1476 }
1477
1478 pub fn set_center(self: RenderHandle<Self>, app: &mut App, value: Option<AnyRenderSliver>) {
1480 if value == self.get(app).center {
1481 return;
1482 }
1483 self.get_mut(app).center = value;
1484 self.mark_needs_layout(app);
1485 }
1486
1487 fn attempt_layout(
1489 self: RenderHandle<Self>,
1490 app: &mut App,
1491 main_axis_extent: f64,
1492 cross_axis_extent: f64,
1493 corrected_offset: f64,
1494 ) -> f64 {
1495 debug_assert!(!main_axis_extent.is_nan());
1496 debug_assert!(main_axis_extent >= 0.0);
1497 debug_assert!(main_axis_extent.is_finite());
1498 debug_assert!(cross_axis_extent.is_finite());
1499 debug_assert!(cross_axis_extent >= 0.0);
1500 debug_assert!(corrected_offset.is_finite());
1501 let this = self.get_mut(app);
1502 this.min_scroll_extent = 0.0;
1503 this.max_scroll_extent = 0.0;
1504 this.has_visual_overflow = false;
1505
1506 let anchor = self.get(app).anchor;
1509 let center_offset = main_axis_extent * anchor - corrected_offset;
1510 let reverse_direction_remaining_paint_extent =
1511 clamp_double(center_offset, 0.0, main_axis_extent);
1512 let forward_direction_remaining_paint_extent =
1513 clamp_double(main_axis_extent - center_offset, 0.0, main_axis_extent);
1514
1515 let calculated_cache_extent = self
1516 .scroll_cache_extent(app)
1517 .calculate_cache_offset(main_axis_extent);
1518 self.viewport_data_mut(app).calculated_cache_extent = Some(calculated_cache_extent);
1519
1520 let full_cache_extent = main_axis_extent + 2.0 * calculated_cache_extent;
1521 let center_cache_offset = center_offset + calculated_cache_extent;
1522 let reverse_direction_remaining_cache_extent =
1523 clamp_double(center_cache_offset, 0.0, full_cache_extent);
1524 let forward_direction_remaining_cache_extent = clamp_double(
1525 full_cache_extent - center_cache_offset,
1526 0.0,
1527 full_cache_extent,
1528 );
1529
1530 let center = self.center(app).expect("checked by perform_layout");
1531 let leading_negative_child = self.child_before(app, center);
1532
1533 if let Some(leading_negative_child) = leading_negative_child {
1534 let result = self.layout_child_sequence(
1536 app,
1537 Some(leading_negative_child),
1538 main_axis_extent.max(center_offset) - main_axis_extent,
1539 0.0,
1540 forward_direction_remaining_paint_extent,
1541 reverse_direction_remaining_paint_extent,
1542 main_axis_extent,
1543 cross_axis_extent,
1544 GrowthDirection::Reverse,
1545 &|this, app, child| this.child_before(app, child),
1546 reverse_direction_remaining_cache_extent,
1547 clamp_double(
1548 main_axis_extent - center_offset,
1549 -calculated_cache_extent,
1550 0.0,
1551 ),
1552 );
1553 if result != 0.0 {
1554 return -result;
1555 }
1556 }
1557
1558 self.layout_child_sequence(
1560 app,
1561 Some(center),
1562 (-center_offset).max(0.0),
1563 if leading_negative_child.is_none() {
1564 (-center_offset).min(0.0)
1565 } else {
1566 0.0
1567 },
1568 if center_offset >= main_axis_extent {
1569 center_offset
1570 } else {
1571 reverse_direction_remaining_paint_extent
1572 },
1573 forward_direction_remaining_paint_extent,
1574 main_axis_extent,
1575 cross_axis_extent,
1576 GrowthDirection::Forward,
1577 &|this, app, child| this.child_after(app, child),
1578 forward_direction_remaining_cache_extent,
1579 clamp_double(center_offset, -calculated_cache_extent, 0.0),
1580 )
1581 }
1582}
1583
1584impl ContainerRenderObjectMixin for RenderViewport {
1585 type ChildType = AnyRenderSliver;
1586 type ParentDataType = SliverPhysicalContainerParentData;
1587
1588 fn container_data(
1589 self: RenderHandle<Self>,
1590 app: &App,
1591 ) -> &ContainerRenderObjectData<AnyRenderSliver> {
1592 &self.get(app).container
1593 }
1594
1595 fn container_data_mut(
1596 self: RenderHandle<Self>,
1597 app: &mut App,
1598 ) -> &mut ContainerRenderObjectData<AnyRenderSliver> {
1599 &mut self.get_mut(app).container
1600 }
1601}
1602
1603impl RenderViewportBase for RenderViewport {
1604 crate::render_viewport_base_accessors!();
1605
1606 fn has_visual_overflow(self: RenderHandle<Self>, app: &App) -> bool {
1607 self.get(app).has_visual_overflow
1608 }
1609
1610 fn update_out_of_band_data(
1611 self: RenderHandle<Self>,
1612 app: &mut App,
1613 growth_direction: GrowthDirection,
1614 child_layout_geometry: SliverGeometry,
1615 ) {
1616 let this = self.get_mut(app);
1617 match growth_direction {
1618 GrowthDirection::Forward => {
1619 this.max_scroll_extent += child_layout_geometry.scroll_extent
1620 }
1621 GrowthDirection::Reverse => {
1622 this.min_scroll_extent -= child_layout_geometry.scroll_extent
1623 }
1624 }
1625 if child_layout_geometry.has_visual_overflow {
1626 this.has_visual_overflow = true;
1627 }
1628 }
1629
1630 fn update_child_layout_offset(
1631 self: RenderHandle<Self>,
1632 app: &mut App,
1633 child: AnyRenderSliver,
1634 layout_offset: f64,
1635 growth_direction: GrowthDirection,
1636 ) {
1637 let paint_offset =
1638 self.compute_absolute_paint_offset(app, child, layout_offset, growth_direction);
1639 child
1640 .as_object()
1641 .parent_data_of_mut::<SliverPhysicalContainerParentData>(app)
1642 .set_paint_offset(paint_offset);
1643 }
1644
1645 fn paint_offset_of(self: RenderHandle<Self>, app: &App, child: AnyRenderSliver) -> Offset {
1646 child
1647 .as_object()
1648 .parent_data_of::<SliverPhysicalContainerParentData>(app)
1649 .paint_offset()
1650 }
1651
1652 fn scroll_offset_of(
1653 self: RenderHandle<Self>,
1654 app: &App,
1655 child: AnyRenderSliver,
1656 scroll_offset_within_child: f64,
1657 ) -> f64 {
1658 debug_assert_eq!(child.as_object().parent(app), Some(self.as_object()));
1659 match child.constraints(app).growth_direction {
1660 GrowthDirection::Forward => {
1661 let mut scroll_offset_to_child = 0.0;
1662 let mut current = self.center(app);
1663 while current != Some(child) {
1664 let sliver = current.expect("the center precedes a forward child");
1665 scroll_offset_to_child += sliver.geometry(app).scroll_extent;
1666 current = self.child_after(app, sliver);
1667 }
1668 scroll_offset_to_child + scroll_offset_within_child
1669 }
1670 GrowthDirection::Reverse => {
1671 let mut scroll_offset_to_child = 0.0;
1672 let center = self.center(app).expect("a laid-out viewport has a center");
1673 let mut current = self.child_before(app, center);
1674 while current != Some(child) {
1675 let sliver = current.expect("a reverse child precedes the center");
1676 scroll_offset_to_child -= sliver.geometry(app).scroll_extent;
1677 current = self.child_before(app, sliver);
1678 }
1679 scroll_offset_to_child - scroll_offset_within_child
1680 }
1681 }
1682 }
1683
1684 fn max_scroll_obstruction_extent_before(
1685 self: RenderHandle<Self>,
1686 app: &App,
1687 child: AnyRenderSliver,
1688 ) -> f64 {
1689 debug_assert_eq!(child.as_object().parent(app), Some(self.as_object()));
1690 let mut pinned_extent = 0.0;
1691 match child.constraints(app).growth_direction {
1692 GrowthDirection::Forward => {
1693 let mut current = self.center(app);
1694 while current != Some(child) {
1695 let sliver = current.expect("the center precedes a forward child");
1696 pinned_extent += sliver.geometry(app).max_scroll_obstruction_extent;
1697 current = self.child_after(app, sliver);
1698 }
1699 }
1700 GrowthDirection::Reverse => {
1701 let center = self.center(app).expect("a laid-out viewport has a center");
1702 let mut current = self.child_before(app, center);
1703 while current != Some(child) {
1704 let sliver = current.expect("a reverse child precedes the center");
1705 pinned_extent += sliver.geometry(app).max_scroll_obstruction_extent;
1706 current = self.child_before(app, sliver);
1707 }
1708 }
1709 }
1710 pinned_extent
1711 }
1712
1713 fn compute_child_main_axis_position(
1714 self: RenderHandle<Self>,
1715 app: &App,
1716 child: AnyRenderSliver,
1717 parent_main_axis_position: f64,
1718 ) -> f64 {
1719 let paint_offset = child
1720 .as_object()
1721 .parent_data_of::<SliverPhysicalContainerParentData>(app)
1722 .paint_offset();
1723 let constraints = child.constraints(app);
1724 let paint_extent = child.geometry(app).paint_extent;
1725 match apply_growth_direction_to_axis_direction(
1726 constraints.axis_direction,
1727 constraints.growth_direction,
1728 ) {
1729 AxisDirection::Down => parent_main_axis_position - paint_offset.dy(),
1730 AxisDirection::Right => parent_main_axis_position - paint_offset.dx(),
1731 AxisDirection::Up => paint_extent - (parent_main_axis_position - paint_offset.dy()),
1732 AxisDirection::Left => paint_extent - (parent_main_axis_position - paint_offset.dx()),
1733 }
1734 }
1735
1736 fn index_of_first_child(self: RenderHandle<Self>, app: &App) -> i32 {
1737 let center = self.center(app).expect("a laid-out viewport has a center");
1738 debug_assert_eq!(center.as_object().parent(app), Some(self.as_object()));
1739 debug_assert!(self.first_child(app).is_some());
1740 let mut count = 0;
1741 let mut child = Some(center);
1742 while child != self.first_child(app) {
1743 count -= 1;
1744 child = self.child_before(app, child.expect("the first child terminates the walk"));
1745 }
1746 count
1747 }
1748
1749 fn label_for_child(self: RenderHandle<Self>, index: i32) -> String {
1750 if index == 0 {
1751 "center child".to_owned()
1752 } else {
1753 format!("child {index}")
1754 }
1755 }
1756}
1757
1758impl RenderAbstractViewport for RenderViewport {
1759 fn get_offset_to_reveal(
1760 self: RenderHandle<Self>,
1761 app: &App,
1762 target: AnyRenderObject,
1763 alignment: f64,
1764 rect: Option<Rect>,
1765 axis: Option<Axis>,
1766 ) -> RevealedOffset {
1767 RenderViewportBase::get_offset_to_reveal(self, app, target, alignment, rect, axis)
1768 }
1769}
1770
1771impl RenderObject for RenderViewport {
1772 crate::render_object_accessors!();
1773
1774 fn visit_children(
1775 self: RenderHandle<Self>,
1776 app: &App,
1777 visitor: &mut dyn FnMut(AnyRenderObject),
1778 ) {
1779 ContainerRenderObjectMixin::visit_children(self, app, visitor)
1780 }
1781
1782 fn did_attach(
1783 self: RenderHandle<Self>,
1784 app: &mut App,
1785 owner: inset_foundation::Handle<PipelineOwner>,
1786 ) {
1787 RenderViewportBase::did_attach(self, app, owner)
1788 }
1789
1790 fn did_detach(self: RenderHandle<Self>, app: &mut App) {
1791 RenderViewportBase::did_detach(self, app)
1792 }
1793
1794 fn redepth_children(self: RenderHandle<Self>, app: &mut App) {
1795 ContainerRenderObjectMixin::redepth_children(self, app)
1796 }
1797
1798 fn is_repaint_boundary(self: RenderHandle<Self>, _app: &App) -> bool {
1799 let _ = self;
1800 true
1801 }
1802
1803 fn interface(self: RenderHandle<Self>, id: TypeId) -> Option<Box<dyn Any>> {
1804 (id == TypeId::of::<AnyRenderAbstractViewport>())
1805 .then(|| Box::new(self.as_abstract_viewport()) as Box<dyn Any>)
1806 }
1807
1808 fn sized_by_parent(self: RenderHandle<Self>, _app: &App) -> bool {
1809 let _ = self;
1810 true
1811 }
1812
1813 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
1814 let offset = self.offset(app);
1817 let size = self.size(app);
1818 match self.axis(app) {
1819 Axis::Vertical => offset.apply_viewport_dimension(app, size.height()),
1820 Axis::Horizontal => offset.apply_viewport_dimension(app, size.width()),
1821 };
1822
1823 let Some(center) = self.center(app) else {
1824 debug_assert!(self.first_child(app).is_none());
1825 let this = self.get_mut(app);
1826 this.min_scroll_extent = 0.0;
1827 this.max_scroll_extent = 0.0;
1828 this.has_visual_overflow = false;
1829 offset.apply_content_dimensions(app, 0.0, 0.0);
1830 return;
1831 };
1832 debug_assert_eq!(center.as_object().parent(app), Some(self.as_object()));
1833
1834 let (main_axis_extent, cross_axis_extent) = match self.axis(app) {
1835 Axis::Vertical => (size.height(), size.width()),
1836 Axis::Horizontal => (size.width(), size.height()),
1837 };
1838
1839 let center_offset_adjustment = center.center_offset_adjustment(app);
1840 let max_layout_cycles = RenderViewport::MAX_LAYOUT_CYCLES_PER_CHILD * self.child_count(app);
1841
1842 let anchor = self.get(app).anchor;
1843 let mut count = 0;
1844 loop {
1845 let correction = self.attempt_layout(
1846 app,
1847 main_axis_extent,
1848 cross_axis_extent,
1849 offset.pixels(app) + center_offset_adjustment,
1850 );
1851 if correction != 0.0 {
1852 offset.correct_by(app, correction);
1853 } else {
1854 let min_scroll_extent = self.get(app).min_scroll_extent;
1855 let max_scroll_extent = self.get(app).max_scroll_extent;
1856 if offset.apply_content_dimensions(
1857 app,
1858 (min_scroll_extent + main_axis_extent * anchor).min(0.0),
1859 (max_scroll_extent - main_axis_extent * (1.0 - anchor)).max(0.0),
1860 ) {
1861 break;
1862 }
1863 }
1864 count += 1;
1865 if count >= max_layout_cycles {
1866 break;
1867 }
1868 }
1869 debug_assert!(
1870 count < max_layout_cycles,
1871 "A RenderViewport exceeded its maximum number of layout cycles.\n\
1872 RenderViewport render objects, during layout, can retry if either their slivers or \
1873 their ViewportOffset decide that the offset should be corrected to take into \
1874 account information collected during that layout.\n\
1875 In the case of this RenderViewport object, however, this happened {count} times and \
1876 still there was no consensus on the scroll offset. This usually indicates a bug."
1877 );
1878 }
1879
1880 fn paint(
1881 self: RenderHandle<Self>,
1882 app: &mut App,
1883 context: &mut PaintingContext,
1884 offset: Offset,
1885 ) {
1886 RenderViewportBase::paint(self, app, context, offset)
1887 }
1888
1889 fn dispose(self: RenderHandle<Self>, app: &mut App) {
1890 RenderViewportBase::dispose(self, app)
1891 }
1892
1893 fn show_on_screen(
1894 self: RenderHandle<Self>,
1895 app: &mut App,
1896 descendant: Option<AnyRenderObject>,
1897 rect: Option<Rect>,
1898 duration: Duration,
1899 curve: Rc<dyn Curve>,
1900 ) {
1901 RenderViewportBase::show_on_screen(self, app, descendant, rect, duration, curve)
1902 }
1903}
1904
1905impl RenderBox for RenderViewport {
1906 crate::render_box_accessors!();
1907
1908 fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
1909 if !child.parent_data_is::<SliverPhysicalContainerParentData>(app) {
1910 child.set_parent_data(app, SliverPhysicalContainerParentData::new());
1911 }
1912 }
1913
1914 fn apply_paint_transform(
1915 self: RenderHandle<Self>,
1916 app: &App,
1917 child: AnyRenderObject,
1918 transform: &mut Matrix4,
1919 ) {
1920 child
1922 .parent_data_of::<SliverPhysicalContainerParentData>(app)
1923 .apply_paint_transform(transform);
1924 }
1925
1926 fn hit_test_children(
1927 self: RenderHandle<Self>,
1928 app: &mut App,
1929 result: &mut BoxHitTestResult<'_>,
1930 position: Offset,
1931 ) -> bool {
1932 RenderViewportBase::hit_test_children(self, app, result, position)
1933 }
1934
1935 fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1936 RenderViewportBase::compute_min_intrinsic_width(self, app, height)
1937 }
1938
1939 fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1940 RenderViewportBase::compute_max_intrinsic_width(self, app, height)
1941 }
1942
1943 fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1944 RenderViewportBase::compute_min_intrinsic_height(self, app, width)
1945 }
1946
1947 fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1948 RenderViewportBase::compute_max_intrinsic_height(self, app, width)
1949 }
1950
1951 fn compute_dry_layout(
1952 self: RenderHandle<Self>,
1953 app: &mut App,
1954 constraints: BoxConstraints,
1955 ) -> Size {
1956 debug_assert!(debug_check_has_bounded_axis(self.axis(app), constraints));
1957 constraints.biggest()
1958 }
1959}
1960
1961pub struct RenderShrinkWrappingViewport {
1976 render_object: RenderObjectData,
1977 render_box: RenderBoxData,
1978 container: ContainerRenderObjectData<AnyRenderSliver>,
1979 viewport: RenderViewportBaseData,
1980 max_scroll_extent: f64,
1982 shrink_wrap_extent: f64,
1983 has_visual_overflow: bool,
1984}
1985
1986impl RenderShrinkWrappingViewport {
1987 pub fn new(
1991 app: &mut App,
1992 cross_axis_direction: AxisDirection,
1993 offset: AnyViewportOffset,
1994 children: Option<Vec<AnyRenderSliver>>,
1995 ) -> RenderHandle<RenderShrinkWrappingViewport> {
1996 let this = RenderHandle::new_box(
1997 app,
1998 RenderShrinkWrappingViewport {
1999 render_object: RenderObjectData::new(),
2000 render_box: RenderBoxData::new(),
2001 container: ContainerRenderObjectData::new(),
2002 viewport: RenderViewportBaseData::new(cross_axis_direction, offset),
2003 max_scroll_extent: 0.0,
2004 shrink_wrap_extent: 0.0,
2005 has_visual_overflow: false,
2006 },
2007 );
2008 this.add_all(app, children);
2009 this
2010 }
2011
2012 fn debug_check_has_bounded_cross_axis(self: RenderHandle<Self>, app: &App) -> bool {
2014 let constraints = self.constraints(app);
2015 match self.axis(app) {
2016 Axis::Vertical => debug_assert!(
2017 constraints.has_bounded_width(),
2018 "Vertical viewport was given unbounded width.\n\
2019 Viewports expand in the cross axis to fill their container and constrain their \
2020 children to match their extent in the cross axis. In this case, a vertical \
2021 shrinkwrapping viewport was given an unlimited amount of horizontal space in \
2022 which to expand."
2023 ),
2024 Axis::Horizontal => debug_assert!(
2025 constraints.has_bounded_height(),
2026 "Horizontal viewport was given unbounded height.\n\
2027 Viewports expand in the cross axis to fill their container and constrain their \
2028 children to match their extent in the cross axis. In this case, a horizontal \
2029 shrinkwrapping viewport was given an unlimited amount of vertical space in \
2030 which to expand."
2031 ),
2032 }
2033 true
2034 }
2035
2036 fn attempt_layout(
2038 self: RenderHandle<Self>,
2039 app: &mut App,
2040 main_axis_extent: f64,
2041 cross_axis_extent: f64,
2042 corrected_offset: f64,
2043 ) -> f64 {
2044 debug_assert!(!main_axis_extent.is_nan());
2049 debug_assert!(main_axis_extent >= 0.0);
2050 debug_assert!(cross_axis_extent.is_finite());
2051 debug_assert!(cross_axis_extent >= 0.0);
2052 debug_assert!(corrected_offset.is_finite());
2053 let this = self.get_mut(app);
2054 this.max_scroll_extent = 0.0;
2055 this.shrink_wrap_extent = 0.0;
2056 this.has_visual_overflow = corrected_offset < 0.0;
2059 let calculated_cache_extent = if main_axis_extent.is_finite() {
2060 self.scroll_cache_extent(app)
2061 .calculate_cache_offset(main_axis_extent)
2062 } else {
2063 0.0
2066 };
2067 self.viewport_data_mut(app).calculated_cache_extent = Some(calculated_cache_extent);
2068
2069 let first_child = self.first_child(app);
2070 self.layout_child_sequence(
2071 app,
2072 first_child,
2073 corrected_offset.max(0.0),
2074 corrected_offset.min(0.0),
2075 (-corrected_offset).max(0.0),
2076 main_axis_extent + corrected_offset.min(0.0),
2077 main_axis_extent,
2078 cross_axis_extent,
2079 GrowthDirection::Forward,
2080 &|this, app, child| this.child_after(app, child),
2081 main_axis_extent + 2.0 * calculated_cache_extent,
2082 -calculated_cache_extent,
2083 )
2084 }
2085}
2086
2087impl ContainerRenderObjectMixin for RenderShrinkWrappingViewport {
2088 type ChildType = AnyRenderSliver;
2089 type ParentDataType = SliverLogicalContainerParentData;
2090
2091 fn container_data(
2092 self: RenderHandle<Self>,
2093 app: &App,
2094 ) -> &ContainerRenderObjectData<AnyRenderSliver> {
2095 &self.get(app).container
2096 }
2097
2098 fn container_data_mut(
2099 self: RenderHandle<Self>,
2100 app: &mut App,
2101 ) -> &mut ContainerRenderObjectData<AnyRenderSliver> {
2102 &mut self.get_mut(app).container
2103 }
2104}
2105
2106impl RenderViewportBase for RenderShrinkWrappingViewport {
2107 crate::render_viewport_base_accessors!();
2108
2109 fn debug_throw_if_not_checking_intrinsics(self: RenderHandle<Self>) -> bool {
2110 let _ = self;
2111 debug_assert!(
2112 debug_checking_intrinsics(),
2113 "{} does not support returning intrinsic dimensions.\n\
2114 Calculating the intrinsic dimensions would require instantiating every child of \
2115 the viewport, which defeats the point of viewports being lazy.\n\
2116 If you are merely trying to shrink-wrap the viewport in the main axis direction, \
2117 you should be able to achieve that effect by just giving the viewport loose \
2118 constraints, without needing to measure its intrinsic dimensions.",
2119 std::any::type_name::<Self>()
2120 );
2121 true
2122 }
2123
2124 fn has_visual_overflow(self: RenderHandle<Self>, app: &App) -> bool {
2125 self.get(app).has_visual_overflow
2126 }
2127
2128 fn update_out_of_band_data(
2129 self: RenderHandle<Self>,
2130 app: &mut App,
2131 growth_direction: GrowthDirection,
2132 child_layout_geometry: SliverGeometry,
2133 ) {
2134 debug_assert_eq!(growth_direction, GrowthDirection::Forward);
2135 let this = self.get_mut(app);
2136 this.max_scroll_extent += child_layout_geometry.scroll_extent;
2137 if child_layout_geometry.has_visual_overflow {
2138 this.has_visual_overflow = true;
2139 }
2140 this.shrink_wrap_extent += child_layout_geometry.max_paint_extent;
2141 }
2142
2143 fn update_child_layout_offset(
2144 self: RenderHandle<Self>,
2145 app: &mut App,
2146 child: AnyRenderSliver,
2147 layout_offset: f64,
2148 growth_direction: GrowthDirection,
2149 ) {
2150 debug_assert_eq!(growth_direction, GrowthDirection::Forward);
2151 child
2152 .as_object()
2153 .parent_data_of_mut::<SliverLogicalContainerParentData>(app)
2154 .set_layout_offset(Some(layout_offset));
2155 }
2156
2157 fn paint_offset_of(self: RenderHandle<Self>, app: &App, child: AnyRenderSliver) -> Offset {
2158 let layout_offset = child
2159 .as_object()
2160 .parent_data_of::<SliverLogicalContainerParentData>(app)
2161 .layout_offset()
2162 .expect("a laid-out child has a layout offset");
2163 self.compute_absolute_paint_offset(app, child, layout_offset, GrowthDirection::Forward)
2164 }
2165
2166 fn scroll_offset_of(
2167 self: RenderHandle<Self>,
2168 app: &App,
2169 child: AnyRenderSliver,
2170 scroll_offset_within_child: f64,
2171 ) -> f64 {
2172 debug_assert_eq!(child.as_object().parent(app), Some(self.as_object()));
2173 debug_assert_eq!(
2174 child.constraints(app).growth_direction,
2175 GrowthDirection::Forward
2176 );
2177 let mut scroll_offset_to_child = 0.0;
2178 let mut current = self.first_child(app);
2179 while current != Some(child) {
2180 let sliver = current.expect("child is in the child list");
2181 scroll_offset_to_child += sliver.geometry(app).scroll_extent;
2182 current = self.child_after(app, sliver);
2183 }
2184 scroll_offset_to_child + scroll_offset_within_child
2185 }
2186
2187 fn max_scroll_obstruction_extent_before(
2188 self: RenderHandle<Self>,
2189 app: &App,
2190 child: AnyRenderSliver,
2191 ) -> f64 {
2192 debug_assert_eq!(child.as_object().parent(app), Some(self.as_object()));
2193 debug_assert_eq!(
2194 child.constraints(app).growth_direction,
2195 GrowthDirection::Forward
2196 );
2197 let mut pinned_extent = 0.0;
2198 let mut current = self.first_child(app);
2199 while current != Some(child) {
2200 let sliver = current.expect("child is in the child list");
2201 pinned_extent += sliver.geometry(app).max_scroll_obstruction_extent;
2202 current = self.child_after(app, sliver);
2203 }
2204 pinned_extent
2205 }
2206
2207 fn compute_child_main_axis_position(
2208 self: RenderHandle<Self>,
2209 app: &App,
2210 child: AnyRenderSliver,
2211 parent_main_axis_position: f64,
2212 ) -> f64 {
2213 debug_assert!(self.has_size(app));
2214 let layout_offset = child
2215 .as_object()
2216 .parent_data_of::<SliverLogicalContainerParentData>(app)
2217 .layout_offset()
2218 .expect("a laid-out child has a layout offset");
2219 let constraints = child.constraints(app);
2220 let size = self.size(app);
2221 match apply_growth_direction_to_axis_direction(
2222 constraints.axis_direction,
2223 constraints.growth_direction,
2224 ) {
2225 AxisDirection::Down | AxisDirection::Right => parent_main_axis_position - layout_offset,
2226 AxisDirection::Up => size.height() - parent_main_axis_position - layout_offset,
2227 AxisDirection::Left => size.width() - parent_main_axis_position - layout_offset,
2228 }
2229 }
2230
2231 fn index_of_first_child(self: RenderHandle<Self>, _app: &App) -> i32 {
2232 let _ = self;
2233 0
2234 }
2235
2236 fn label_for_child(self: RenderHandle<Self>, index: i32) -> String {
2237 let _ = self;
2238 format!("child {index}")
2239 }
2240}
2241
2242impl RenderAbstractViewport for RenderShrinkWrappingViewport {
2243 fn get_offset_to_reveal(
2244 self: RenderHandle<Self>,
2245 app: &App,
2246 target: AnyRenderObject,
2247 alignment: f64,
2248 rect: Option<Rect>,
2249 axis: Option<Axis>,
2250 ) -> RevealedOffset {
2251 RenderViewportBase::get_offset_to_reveal(self, app, target, alignment, rect, axis)
2252 }
2253}
2254
2255impl RenderObject for RenderShrinkWrappingViewport {
2256 crate::render_object_accessors!();
2257
2258 fn visit_children(
2259 self: RenderHandle<Self>,
2260 app: &App,
2261 visitor: &mut dyn FnMut(AnyRenderObject),
2262 ) {
2263 ContainerRenderObjectMixin::visit_children(self, app, visitor)
2264 }
2265
2266 fn did_attach(
2267 self: RenderHandle<Self>,
2268 app: &mut App,
2269 owner: inset_foundation::Handle<PipelineOwner>,
2270 ) {
2271 RenderViewportBase::did_attach(self, app, owner)
2272 }
2273
2274 fn did_detach(self: RenderHandle<Self>, app: &mut App) {
2275 RenderViewportBase::did_detach(self, app)
2276 }
2277
2278 fn redepth_children(self: RenderHandle<Self>, app: &mut App) {
2279 ContainerRenderObjectMixin::redepth_children(self, app)
2280 }
2281
2282 fn is_repaint_boundary(self: RenderHandle<Self>, _app: &App) -> bool {
2283 let _ = self;
2284 true
2285 }
2286
2287 fn interface(self: RenderHandle<Self>, id: TypeId) -> Option<Box<dyn Any>> {
2288 (id == TypeId::of::<AnyRenderAbstractViewport>())
2289 .then(|| Box::new(self.as_abstract_viewport()) as Box<dyn Any>)
2290 }
2291
2292 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
2293 let constraints = self.constraints(app);
2294 let offset = self.offset(app);
2295 if self.first_child(app).is_none() {
2296 debug_assert!(self.debug_check_has_bounded_cross_axis(app));
2298 let size = match self.axis(app) {
2299 Axis::Vertical => Size::new(constraints.max_width, constraints.min_height),
2300 Axis::Horizontal => Size::new(constraints.min_width, constraints.max_height),
2301 };
2302 self.set_size(app, size);
2303 offset.apply_viewport_dimension(app, 0.0);
2304 let this = self.get_mut(app);
2305 this.max_scroll_extent = 0.0;
2306 this.shrink_wrap_extent = 0.0;
2307 this.has_visual_overflow = false;
2308 offset.apply_content_dimensions(app, 0.0, 0.0);
2309 return;
2310 }
2311
2312 debug_assert!(self.debug_check_has_bounded_cross_axis(app));
2314 let (main_axis_extent, cross_axis_extent) = match self.axis(app) {
2315 Axis::Vertical => (constraints.max_height, constraints.max_width),
2316 Axis::Horizontal => (constraints.max_width, constraints.max_height),
2317 };
2318
2319 let effective_extent;
2320 loop {
2321 let corrected_offset = offset.pixels(app);
2322 let correction =
2323 self.attempt_layout(app, main_axis_extent, cross_axis_extent, corrected_offset);
2324 if correction != 0.0 {
2325 offset.correct_by(app, correction);
2326 continue;
2327 }
2328 let shrink_wrap_extent = self.get(app).shrink_wrap_extent;
2329 let extent = match self.axis(app) {
2330 Axis::Vertical => constraints.constrain_height(shrink_wrap_extent),
2331 Axis::Horizontal => constraints.constrain_width(shrink_wrap_extent),
2332 };
2333 let did_accept_viewport_dimension = offset.apply_viewport_dimension(app, extent);
2334 let max_scroll_extent = self.get(app).max_scroll_extent;
2335 let did_accept_content_dimension =
2336 offset.apply_content_dimensions(app, 0.0, (max_scroll_extent - extent).max(0.0));
2337 if did_accept_viewport_dimension && did_accept_content_dimension {
2338 effective_extent = extent;
2339 break;
2340 }
2341 }
2342 let size = match self.axis(app) {
2343 Axis::Vertical => constraints.constrain_dimensions(cross_axis_extent, effective_extent),
2344 Axis::Horizontal => {
2345 constraints.constrain_dimensions(effective_extent, cross_axis_extent)
2346 }
2347 };
2348 self.set_size(app, size);
2349 }
2350
2351 fn paint(
2352 self: RenderHandle<Self>,
2353 app: &mut App,
2354 context: &mut PaintingContext,
2355 offset: Offset,
2356 ) {
2357 RenderViewportBase::paint(self, app, context, offset)
2358 }
2359
2360 fn dispose(self: RenderHandle<Self>, app: &mut App) {
2361 RenderViewportBase::dispose(self, app)
2362 }
2363
2364 fn show_on_screen(
2365 self: RenderHandle<Self>,
2366 app: &mut App,
2367 descendant: Option<AnyRenderObject>,
2368 rect: Option<Rect>,
2369 duration: Duration,
2370 curve: Rc<dyn Curve>,
2371 ) {
2372 RenderViewportBase::show_on_screen(self, app, descendant, rect, duration, curve)
2373 }
2374}
2375
2376impl RenderBox for RenderShrinkWrappingViewport {
2377 crate::render_box_accessors!();
2378
2379 fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
2380 if !child.parent_data_is::<SliverLogicalContainerParentData>(app) {
2381 child.set_parent_data(app, SliverLogicalContainerParentData::new());
2382 }
2383 }
2384
2385 fn apply_paint_transform(
2386 self: RenderHandle<Self>,
2387 app: &App,
2388 child: AnyRenderObject,
2389 transform: &mut Matrix4,
2390 ) {
2391 let offset = self.paint_offset_of(
2393 app,
2394 child
2395 .as_sliver()
2396 .expect("a viewport's children are slivers"),
2397 );
2398 translate(transform, offset.dx(), offset.dy());
2399 }
2400
2401 fn hit_test_children(
2402 self: RenderHandle<Self>,
2403 app: &mut App,
2404 result: &mut BoxHitTestResult<'_>,
2405 position: Offset,
2406 ) -> bool {
2407 RenderViewportBase::hit_test_children(self, app, result, position)
2408 }
2409
2410 fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
2411 RenderViewportBase::compute_min_intrinsic_width(self, app, height)
2412 }
2413
2414 fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
2415 RenderViewportBase::compute_max_intrinsic_width(self, app, height)
2416 }
2417
2418 fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
2419 RenderViewportBase::compute_min_intrinsic_height(self, app, width)
2420 }
2421
2422 fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
2423 RenderViewportBase::compute_max_intrinsic_height(self, app, width)
2424 }
2425}
2426
2427#[cfg(test)]
2428mod tests {
2429 use inset_foundation::AppCell;
2430 use std::cell::Cell;
2431 use std::rc::Rc;
2432
2433 use inset_foundation::Handle;
2434 use inset_gestures::HitTestResult;
2435
2436 use super::*;
2437 use crate::box_::BoxConstraints;
2438 use crate::layer::{ContainerLayer, ErasedLayer, OffsetLayer};
2439 use crate::proxy_box::RenderConstrainedBox;
2440 use crate::sliver::{RenderSliver, RenderSliverToBoxAdapter};
2441 use crate::viewport_offset::{FixedViewportOffset, ViewportOffset};
2442
2443 struct TestSliver {
2445 render_object: RenderObjectData,
2446 render_sliver: crate::sliver::RenderSliverData,
2447 extent: f64,
2448 paint_offsets: Rc<Cell<Vec<Offset>>>,
2449 }
2450
2451 impl RenderObject for TestSliver {
2452 crate::render_object_accessors!();
2453
2454 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
2455 let constraints = self.constraints(app);
2456 let extent = self.get(app).extent;
2457 let painted = self.calculate_paint_offset(app, constraints, 0.0, extent);
2458 let cached = self.calculate_cache_offset(app, constraints, 0.0, extent);
2459 self.set_geometry(
2460 app,
2461 SliverGeometry::new()
2462 .scroll_extent(extent)
2463 .paint_extent(painted)
2464 .cache_extent(cached)
2465 .max_paint_extent(extent),
2466 );
2467 }
2468
2469 fn paint(
2470 self: RenderHandle<Self>,
2471 app: &mut App,
2472 _context: &mut PaintingContext,
2473 offset: Offset,
2474 ) {
2475 let recorded = self.get(app).paint_offsets.clone();
2476 let mut offsets = recorded.take();
2477 offsets.push(offset);
2478 recorded.set(offsets);
2479 }
2480 }
2481
2482 impl RenderSliver for TestSliver {
2483 crate::render_sliver_accessors!();
2484
2485 fn hit_test_self(
2486 self: RenderHandle<Self>,
2487 _app: &App,
2488 _main_axis_position: f64,
2489 _cross_axis_position: f64,
2490 ) -> bool {
2491 let _ = self;
2492 true
2493 }
2494 }
2495
2496 fn test_sliver(app: &mut App, extent: f64) -> (AnyRenderSliver, Rc<Cell<Vec<Offset>>>) {
2497 let paint_offsets = Rc::new(Cell::new(Vec::new()));
2498 let sliver = RenderHandle::new_sliver(
2499 app,
2500 TestSliver {
2501 render_object: RenderObjectData::new(),
2502 render_sliver: crate::sliver::RenderSliverData::new(),
2503 extent,
2504 paint_offsets: paint_offsets.clone(),
2505 },
2506 )
2507 .as_sliver();
2508 (sliver, paint_offsets)
2509 }
2510
2511 fn first_frame(
2513 app: &mut App,
2514 root: crate::box_::AnyRenderBox,
2515 constraints: BoxConstraints,
2516 ) -> Handle<PipelineOwner> {
2517 let owner = PipelineOwner::new(app, None);
2518 owner.set_root_node(app, Some(root.as_object()));
2519 root.layout(app, constraints, false);
2520 let paint_root = OffsetLayer::new(app, Offset::ZERO);
2521 paint_root.as_layer().attach(app, root.as_object().id());
2522 root.as_object()
2523 .schedule_initial_paint(app, paint_root.as_container_layer());
2524 owner.flush_compositing_bits(app);
2525 owner.flush_paint(app);
2526 owner
2527 }
2528
2529 fn vertical_viewport(
2530 app: &mut App,
2531 offset: Handle<FixedViewportOffset>,
2532 children: Vec<AnyRenderSliver>,
2533 ) -> RenderHandle<RenderViewport> {
2534 RenderViewport::new(
2535 app,
2536 AxisDirection::Right,
2537 offset.as_viewport_offset(),
2538 Some(children),
2539 None,
2540 )
2541 }
2542
2543 #[test]
2546 #[should_panic(expected = "does not support returning intrinsic dimensions")]
2547 fn a_viewport_has_no_intrinsic_dimensions() {
2548 let cell = AppCell::new();
2549 let mut app = cell.borrow_mut();
2550 let offset = FixedViewportOffset::zero(&mut app);
2551 let viewport = vertical_viewport(&mut app, offset, vec![]);
2552 viewport.as_box().get_min_intrinsic_width(&mut app, 0.0);
2553 }
2554
2555 #[test]
2557 fn a_viewport_reports_zero_intrinsics_while_checking_them() {
2558 let cell = AppCell::new();
2559 let mut app = cell.borrow_mut();
2560 let offset = FixedViewportOffset::zero(&mut app);
2561 let viewport = vertical_viewport(&mut app, offset, vec![]);
2562 crate::object::set_debug_checking_intrinsics(true);
2563 let width = viewport.as_box().get_min_intrinsic_width(&mut app, 0.0);
2564 let height = viewport.as_box().get_max_intrinsic_height(&mut app, 0.0);
2565 crate::object::set_debug_checking_intrinsics(false);
2566 assert_eq!((width, height), (0.0, 0.0));
2567 }
2568
2569 #[test]
2572 fn a_viewport_dry_layout_is_the_biggest_size() {
2573 let cell = AppCell::new();
2574 let mut app = cell.borrow_mut();
2575 let (sliver, _paints) = test_sliver(&mut app, 100.0);
2576 let offset = FixedViewportOffset::zero(&mut app);
2577 let viewport = vertical_viewport(&mut app, offset, vec![sliver]);
2578 let constraints = BoxConstraints::new().max_width(200.0).max_height(400.0);
2579 let dry = viewport.as_box().get_dry_layout(&mut app, constraints);
2580 first_frame(&mut app, viewport.as_box(), constraints);
2581 assert_eq!(dry, Size::new(200.0, 400.0));
2582 assert_eq!(dry, viewport.size(&app));
2583 }
2584
2585 #[test]
2588 fn a_viewport_lays_its_slivers_out_in_order() {
2589 let cell = AppCell::new();
2590 let mut app = cell.borrow_mut();
2591 let (first, first_paints) = test_sliver(&mut app, 100.0);
2592 let (second, second_paints) = test_sliver(&mut app, 300.0);
2593 let offset = FixedViewportOffset::zero(&mut app);
2594 let viewport = vertical_viewport(&mut app, offset, vec![first, second]);
2595
2596 first_frame(
2597 &mut app,
2598 viewport.as_box(),
2599 BoxConstraints::tight(Size::new(200.0, 400.0)),
2600 );
2601
2602 assert_eq!(viewport.size(&app), Size::new(200.0, 400.0));
2603 assert_eq!(viewport.center(&app), Some(first));
2604 assert_eq!(first.geometry(&app).paint_extent, 100.0);
2605 assert_eq!(second.geometry(&app).paint_extent, 300.0);
2606 assert_eq!(viewport.paint_offset_of(&app, first), Offset::ZERO);
2607 assert_eq!(
2608 viewport.paint_offset_of(&app, second),
2609 Offset::new(0.0, 100.0)
2610 );
2611 assert_eq!(first_paints.take(), vec![Offset::ZERO]);
2612 assert_eq!(second_paints.take(), vec![Offset::new(0.0, 100.0)]);
2613 }
2614
2615 #[test]
2618 fn a_scrolled_viewport_moves_its_slivers() {
2619 let cell = AppCell::new();
2620 let mut app = cell.borrow_mut();
2621 let (first, _) = test_sliver(&mut app, 100.0);
2622 let (second, _) = test_sliver(&mut app, 300.0);
2623 let offset = FixedViewportOffset::new(&mut app, 60.0);
2624 let viewport = vertical_viewport(&mut app, offset, vec![first, second]);
2625
2626 first_frame(
2627 &mut app,
2628 viewport.as_box(),
2629 BoxConstraints::tight(Size::new(200.0, 400.0)),
2630 );
2631
2632 assert_eq!(first.constraints(&app).scroll_offset, 60.0);
2633 assert_eq!(first.geometry(&app).paint_extent, 40.0);
2634 assert_eq!(viewport.paint_offset_of(&app, first), Offset::ZERO);
2635 assert_eq!(
2636 viewport.paint_offset_of(&app, second),
2637 Offset::new(0.0, 40.0)
2638 );
2639 assert_eq!(viewport.scroll_offset_of(&app, second, 0.0), 100.0);
2640 }
2641
2642 #[test]
2644 fn a_viewport_hit_tests_the_sliver_under_the_position() {
2645 let cell = AppCell::new();
2646 let mut app = cell.borrow_mut();
2647 let (first, _) = test_sliver(&mut app, 100.0);
2648 let (second, _) = test_sliver(&mut app, 300.0);
2649 let offset = FixedViewportOffset::zero(&mut app);
2650 let viewport = vertical_viewport(&mut app, offset, vec![first, second]);
2651 first_frame(
2652 &mut app,
2653 viewport.as_box(),
2654 BoxConstraints::tight(Size::new(200.0, 400.0)),
2655 );
2656
2657 let mut result = HitTestResult::new();
2658 let hit = viewport.hit_test(
2659 &mut app,
2660 &mut BoxHitTestResult::wrap(&mut result),
2661 Offset::new(20.0, 150.0),
2662 );
2663
2664 assert!(hit);
2665 assert_eq!(result.path().len(), 2);
2666 assert_eq!(
2668 viewport.compute_child_main_axis_position(&app, second, 150.0),
2669 50.0
2670 );
2671 }
2672
2673 #[test]
2675 fn the_cache_extent_widens_the_laid_out_region() {
2676 let cell = AppCell::new();
2677 let mut app = cell.borrow_mut();
2678 let (sliver, _) = test_sliver(&mut app, 1000.0);
2679 let offset = FixedViewportOffset::zero(&mut app);
2680 let viewport = vertical_viewport(&mut app, offset, vec![sliver]);
2681 viewport.set_scroll_cache_extent(&mut app, Some(ScrollCacheExtent::Pixels(100.0)));
2682
2683 first_frame(
2684 &mut app,
2685 viewport.as_box(),
2686 BoxConstraints::tight(Size::new(200.0, 400.0)),
2687 );
2688
2689 assert_eq!(sliver.constraints(&app).remaining_cache_extent, 500.0);
2690 assert_eq!(sliver.geometry(&app).cache_extent, 500.0);
2691 assert_eq!(sliver.geometry(&app).paint_extent, 400.0);
2692
2693 viewport.set_scroll_cache_extent(&mut app, Some(ScrollCacheExtent::Viewport(0.5)));
2694 viewport.as_box().layout(
2695 &mut app,
2696 BoxConstraints::tight(Size::new(200.0, 400.0)),
2697 false,
2698 );
2699 assert_eq!(sliver.constraints(&app).remaining_cache_extent, 600.0);
2700 }
2701
2702 #[test]
2705 fn a_shrink_wrapping_viewport_sizes_itself_to_its_slivers() {
2706 let cell = AppCell::new();
2707 let mut app = cell.borrow_mut();
2708 let (first, _) = test_sliver(&mut app, 60.0);
2709 let (second, _) = test_sliver(&mut app, 90.0);
2710 let offset = FixedViewportOffset::zero(&mut app);
2711 let viewport = RenderShrinkWrappingViewport::new(
2712 &mut app,
2713 AxisDirection::Right,
2714 offset.as_viewport_offset(),
2715 Some(vec![first, second]),
2716 );
2717
2718 first_frame(
2719 &mut app,
2720 viewport.as_box(),
2721 BoxConstraints::new()
2722 .max_width(200.0)
2723 .min_width(200.0)
2724 .max_height(400.0),
2725 );
2726
2727 assert_eq!(viewport.size(&app), Size::new(200.0, 150.0));
2728 assert_eq!(
2729 viewport.paint_offset_of(&app, second),
2730 Offset::new(0.0, 60.0)
2731 );
2732 assert_eq!(viewport.index_of_first_child(&app), 0);
2733 assert_eq!(viewport.label_for_child(1), "child 1");
2734 }
2735
2736 #[test]
2739 fn a_viewport_hosts_a_box_through_the_adapter() {
2740 let cell = AppCell::new();
2741 let mut app = cell.borrow_mut();
2742 let child =
2743 RenderConstrainedBox::new(&mut app, BoxConstraints::tight_for(None, Some(80.0)), None);
2744 let adapter = RenderSliverToBoxAdapter::new(&mut app, Some(child.as_box()));
2745 let offset = FixedViewportOffset::zero(&mut app);
2746 let viewport = vertical_viewport(&mut app, offset, vec![adapter.as_sliver()]);
2747
2748 first_frame(
2749 &mut app,
2750 viewport.as_box(),
2751 BoxConstraints::tight(Size::new(200.0, 400.0)),
2752 );
2753
2754 assert_eq!(child.size(&app), Size::new(200.0, 80.0));
2755 assert_eq!(adapter.geometry(&app).scroll_extent, 80.0);
2756 assert_eq!(
2757 child.as_box().local_to_global(&app, Offset::ZERO, None),
2758 Offset::ZERO
2759 );
2760 }
2761
2762 #[test]
2764 fn maybe_of_finds_the_enclosing_viewport() {
2765 let cell = AppCell::new();
2766 let mut app = cell.borrow_mut();
2767 let child =
2768 RenderConstrainedBox::new(&mut app, BoxConstraints::tight_for(None, Some(80.0)), None);
2769 let adapter = RenderSliverToBoxAdapter::new(&mut app, Some(child.as_box()));
2770 let offset = FixedViewportOffset::zero(&mut app);
2771 let viewport = vertical_viewport(&mut app, offset, vec![adapter.as_sliver()]);
2772 first_frame(
2773 &mut app,
2774 viewport.as_box(),
2775 BoxConstraints::tight(Size::new(200.0, 400.0)),
2776 );
2777
2778 let found = AnyRenderAbstractViewport::maybe_of(&app, Some(child.as_object()));
2779 assert_eq!(found, Some(viewport.as_abstract_viewport()));
2780 assert!(AnyRenderAbstractViewport::maybe_of(&app, None).is_none());
2781 }
2782
2783 #[test]
2785 fn get_offset_to_reveal_reports_the_leading_edge_offset() {
2786 let cell = AppCell::new();
2787 let mut app = cell.borrow_mut();
2788 let leading =
2789 RenderConstrainedBox::new(&mut app, BoxConstraints::tight_for(None, Some(100.0)), None);
2790 let first = RenderSliverToBoxAdapter::new(&mut app, Some(leading.as_box()));
2791 let target =
2792 RenderConstrainedBox::new(&mut app, BoxConstraints::tight_for(None, Some(50.0)), None);
2793 let second = RenderSliverToBoxAdapter::new(&mut app, Some(target.as_box()));
2794 let offset = FixedViewportOffset::zero(&mut app);
2795 let viewport = vertical_viewport(
2796 &mut app,
2797 offset,
2798 vec![first.as_sliver(), second.as_sliver()],
2799 );
2800 first_frame(
2801 &mut app,
2802 viewport.as_box(),
2803 BoxConstraints::tight(Size::new(200.0, 400.0)),
2804 );
2805
2806 let revealed = RenderAbstractViewport::get_offset_to_reveal(
2807 viewport,
2808 &app,
2809 target.as_object(),
2810 0.0,
2811 None,
2812 None,
2813 );
2814 assert_eq!(revealed.offset, 100.0);
2815 assert_eq!(revealed.rect.top, 0.0);
2817 assert_eq!(revealed.rect.bottom, 50.0);
2818 }
2819
2820 #[test]
2822 fn clamp_offset_picks_the_nearer_edge() {
2823 let leading = RevealedOffset::new(100.0, Rect::ZERO);
2824 let trailing = RevealedOffset::new(20.0, Rect::ZERO);
2825 assert_eq!(
2826 RevealedOffset::clamp_offset(leading, trailing, 200.0).map(|offset| offset.offset),
2827 Some(100.0)
2828 );
2829 assert_eq!(
2830 RevealedOffset::clamp_offset(leading, trailing, 0.0).map(|offset| offset.offset),
2831 Some(20.0)
2832 );
2833 assert!(RevealedOffset::clamp_offset(leading, trailing, 50.0).is_none());
2834 }
2835
2836 #[test]
2838 fn the_paint_order_reverses_the_hit_test_order() {
2839 let cell = AppCell::new();
2840 let mut app = cell.borrow_mut();
2841 let (first, _) = test_sliver(&mut app, 100.0);
2842 let (second, _) = test_sliver(&mut app, 300.0);
2843 let offset = FixedViewportOffset::zero(&mut app);
2844 let viewport = vertical_viewport(&mut app, offset, vec![first, second]);
2845 first_frame(
2846 &mut app,
2847 viewport.as_box(),
2848 BoxConstraints::tight(Size::new(200.0, 400.0)),
2849 );
2850
2851 assert_eq!(viewport.children_in_paint_order(&app), vec![second, first]);
2852 assert_eq!(
2853 viewport.children_in_hit_test_order(&app),
2854 vec![first, second]
2855 );
2856
2857 viewport.set_paint_order(&mut app, SliverPaintOrder::LastIsTop);
2858 assert_eq!(viewport.children_in_paint_order(&app), vec![first, second]);
2859 assert_eq!(
2860 viewport.children_in_hit_test_order(&app),
2861 vec![second, first]
2862 );
2863 }
2864}