1use std::any::{Any, TypeId};
7use std::fmt::{self, Debug};
8
9use inset_embedder::{Clip, Offset, Rect, Size, TextDirection, lerp_double};
10use inset_foundation::{App, Handle};
11use inset_painting::{Alignment, AlignmentGeometry};
12
13use crate::box_::{
14 AnyRenderBox, BoxConstraints, BoxHitTestResult, BoxParentData, ContainerBoxParentData,
15 RenderBox, RenderBoxContainerDefaultsMixin, RenderBoxData,
16};
17use crate::layer::{ClipRectLayer, LayerHandle};
18use crate::layout_helper::ChildLayoutHelper;
19use crate::object::{
20 AnyRenderObject, ContainerParentData, ContainerParentDataMixin, ContainerRenderObjectData,
21 ContainerRenderObjectMixin, ParentData, RenderHandle, RenderObject, RenderObjectData,
22};
23use crate::painting_context::PaintingContext;
24use crate::pipeline_owner::PipelineOwner;
25
26#[derive(Clone, Copy, PartialEq)]
34pub struct RelativeRect {
35 pub left: f64,
39
40 pub top: f64,
44
45 pub right: f64,
49
50 pub bottom: f64,
54}
55
56impl RelativeRect {
57 pub const fn from_ltrb(left: f64, top: f64, right: f64, bottom: f64) -> RelativeRect {
59 RelativeRect {
60 left,
61 top,
62 right,
63 bottom,
64 }
65 }
66
67 pub fn from_size(rect: Rect, container: Size) -> RelativeRect {
72 RelativeRect {
73 left: rect.left,
74 top: rect.top,
75 right: container.width() - rect.right,
76 bottom: container.height() - rect.bottom,
77 }
78 }
79
80 pub fn from_rect(rect: Rect, container: Rect) -> RelativeRect {
93 RelativeRect {
94 left: rect.left - container.left,
95 top: rect.top - container.top,
96 right: container.right - rect.right,
97 bottom: container.bottom - rect.bottom,
98 }
99 }
100
101 pub fn from_directional(
109 text_direction: TextDirection,
110 start: f64,
111 top: f64,
112 end: f64,
113 bottom: f64,
114 ) -> RelativeRect {
115 let (left, right) = match text_direction {
116 TextDirection::Rtl => (end, start),
117 TextDirection::Ltr => (start, end),
118 };
119 RelativeRect::from_ltrb(left, top, right, bottom)
120 }
121
122 pub const FILL: RelativeRect = RelativeRect::from_ltrb(0.0, 0.0, 0.0, 0.0);
124
125 pub fn has_insets(&self) -> bool {
131 self.left > 0.0 || self.top > 0.0 || self.right > 0.0 || self.bottom > 0.0
132 }
133
134 pub fn shift(&self, offset: Offset) -> RelativeRect {
136 RelativeRect::from_ltrb(
137 self.left + offset.dx(),
138 self.top + offset.dy(),
139 self.right - offset.dx(),
140 self.bottom - offset.dy(),
141 )
142 }
143
144 pub fn inflate(&self, delta: f64) -> RelativeRect {
146 RelativeRect::from_ltrb(
147 self.left - delta,
148 self.top - delta,
149 self.right - delta,
150 self.bottom - delta,
151 )
152 }
153
154 pub fn deflate(&self, delta: f64) -> RelativeRect {
156 self.inflate(-delta)
157 }
158
159 pub fn intersect(&self, other: RelativeRect) -> RelativeRect {
161 RelativeRect::from_ltrb(
162 self.left.max(other.left),
163 self.top.max(other.top),
164 self.right.max(other.right),
165 self.bottom.max(other.bottom),
166 )
167 }
168
169 pub fn to_rect(&self, container: Rect) -> Rect {
176 Rect::from_ltrb(
177 self.left,
178 self.top,
179 container.width() - self.right,
180 container.height() - self.bottom,
181 )
182 }
183
184 pub fn to_size(&self, container: Size) -> Size {
190 Size::new(
191 container.width() - self.left - self.right,
192 container.height() - self.top - self.bottom,
193 )
194 }
195
196 pub fn lerp(a: Option<RelativeRect>, b: Option<RelativeRect>, t: f64) -> Option<RelativeRect> {
200 match (a, b) {
201 (None, None) => None,
202 (None, Some(b)) => Some(RelativeRect::from_ltrb(
203 b.left * t,
204 b.top * t,
205 b.right * t,
206 b.bottom * t,
207 )),
208 (Some(a), None) => {
209 let k = 1.0 - t;
210 Some(RelativeRect::from_ltrb(
211 a.left * k,
212 a.top * k,
213 a.right * k,
214 a.bottom * k,
215 ))
216 }
217 (Some(a), Some(b)) => Some(RelativeRect::from_ltrb(
218 lerp_double(Some(a.left), Some(b.left), t).unwrap(),
219 lerp_double(Some(a.top), Some(b.top), t).unwrap(),
220 lerp_double(Some(a.right), Some(b.right), t).unwrap(),
221 lerp_double(Some(a.bottom), Some(b.bottom), t).unwrap(),
222 )),
223 }
224 }
225}
226
227impl Debug for RelativeRect {
228 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
229 write!(
230 f,
231 "RelativeRect::from_ltrb({:.1}, {:.1}, {:.1}, {:.1})",
232 self.left, self.top, self.right, self.bottom
233 )
234 }
235}
236
237#[derive(Debug)]
239pub struct StackParentData {
240 box_parent_data: BoxParentData,
241 container_parent_data: ContainerParentData<AnyRenderBox>,
242
243 pub top: Option<f64>,
245
246 pub right: Option<f64>,
248
249 pub bottom: Option<f64>,
251
252 pub left: Option<f64>,
254
255 pub width: Option<f64>,
259
260 pub height: Option<f64>,
264}
265
266impl StackParentData {
267 pub const fn new() -> StackParentData {
269 StackParentData {
270 box_parent_data: BoxParentData::new(),
271 container_parent_data: ContainerParentData::new(),
272 top: None,
273 right: None,
274 bottom: None,
275 left: None,
276 width: None,
277 height: None,
278 }
279 }
280
281 pub fn rect(&self) -> RelativeRect {
287 RelativeRect::from_ltrb(
288 self.left.expect("left is set"),
289 self.top.expect("top is set"),
290 self.right.expect("right is set"),
291 self.bottom.expect("bottom is set"),
292 )
293 }
294
295 pub fn set_rect(&mut self, value: RelativeRect) {
297 self.top = Some(value.top);
298 self.right = Some(value.right);
299 self.bottom = Some(value.bottom);
300 self.left = Some(value.left);
301 }
302
303 pub fn is_positioned(&self) -> bool {
309 self.top.is_some()
310 || self.right.is_some()
311 || self.bottom.is_some()
312 || self.left.is_some()
313 || self.width.is_some()
314 || self.height.is_some()
315 }
316
317 pub fn positioned_child_constraints(&self, stack_size: Size) -> BoxConstraints {
323 debug_assert!(self.is_positioned());
324 let width = match (self.left, self.right) {
325 (Some(left), Some(right)) => Some(stack_size.width() - right - left),
326 _ => self.width,
327 };
328 let height = match (self.top, self.bottom) {
329 (Some(top), Some(bottom)) => Some(stack_size.height() - bottom - top),
330 _ => self.height,
331 };
332 debug_assert!(height.is_none_or(|height| !height.is_nan()));
333 debug_assert!(width.is_none_or(|width| !width.is_nan()));
334 BoxConstraints::tight_for(
335 width.map(|width| width.max(0.0)),
336 height.map(|height| height.max(0.0)),
337 )
338 }
339}
340
341impl Default for StackParentData {
342 fn default() -> StackParentData {
343 StackParentData::new()
344 }
345}
346
347impl ParentData for StackParentData {
348 fn detach(&mut self) {
349 ContainerParentDataMixin::detach(self);
350 }
351
352 fn provide(&self, id: TypeId) -> Option<&dyn Any> {
353 if id == TypeId::of::<StackParentData>() {
354 return Some(self);
355 }
356 self.box_parent_data.provide(id)
357 }
358
359 fn provide_mut(&mut self, id: TypeId) -> Option<&mut dyn Any> {
360 if id == TypeId::of::<StackParentData>() {
361 return Some(self);
362 }
363 self.box_parent_data.provide_mut(id)
364 }
365}
366
367impl ContainerParentDataMixin for StackParentData {
368 type ChildType = AnyRenderBox;
369
370 fn container_parent_data(&self) -> &ContainerParentData<AnyRenderBox> {
371 &self.container_parent_data
372 }
373
374 fn container_parent_data_mut(&mut self) -> &mut ContainerParentData<AnyRenderBox> {
375 &mut self.container_parent_data
376 }
377}
378
379impl ContainerBoxParentData for StackParentData {
380 fn box_parent_data(&self) -> &BoxParentData {
381 &self.box_parent_data
382 }
383
384 fn box_parent_data_mut(&mut self) -> &mut BoxParentData {
385 &mut self.box_parent_data
386 }
387}
388
389impl fmt::Display for StackParentData {
390 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
391 let mut values = Vec::new();
392 for (name, value) in [
393 ("top", self.top),
394 ("right", self.right),
395 ("bottom", self.bottom),
396 ("left", self.left),
397 ("width", self.width),
398 ("height", self.height),
399 ] {
400 if let Some(value) = value {
401 values.push(format!("{name}={value}"));
402 }
403 }
404 if values.is_empty() {
405 values.push("not positioned".to_string());
406 }
407 values.push(self.box_parent_data.to_string());
408 f.write_str(&values.join("; "))
409 }
410}
411
412#[derive(Clone, Copy, Debug, PartialEq, Eq)]
414pub enum StackFit {
415 Loose,
421
422 Expand,
429
430 Passthrough,
433}
434
435pub struct RenderStackData {
437 has_visual_overflow: bool,
438 alignment: AlignmentGeometry,
439 text_direction: Option<TextDirection>,
440 fit: StackFit,
441 clip_behavior: Clip,
442 resolved_alignment_cache: Option<Alignment>,
443 clip_rect_layer: LayerHandle<Handle<ClipRectLayer>>,
444}
445
446impl RenderStackData {
447 pub fn new() -> RenderStackData {
449 RenderStackData {
450 has_visual_overflow: false,
451 alignment: AlignmentGeometry::TOP_START,
452 text_direction: None,
453 fit: StackFit::Loose,
454 clip_behavior: Clip::HardEdge,
455 resolved_alignment_cache: None,
456 clip_rect_layer: LayerHandle::new(),
457 }
458 }
459}
460
461impl Default for RenderStackData {
462 fn default() -> RenderStackData {
463 RenderStackData::new()
464 }
465}
466
467pub fn get_intrinsic_dimension(
471 app: &mut App,
472 first_child: Option<AnyRenderBox>,
473 mut main_child_size_getter: impl FnMut(&mut App, AnyRenderBox) -> f64,
474) -> f64 {
475 let mut extent: f64 = 0.0;
476 let mut child = first_child;
477 while let Some(current) = child {
478 let child_parent_data = current.as_object().parent_data_of::<StackParentData>(app);
479 let (is_positioned, next_sibling) = (
480 child_parent_data.is_positioned(),
481 child_parent_data.next_sibling(),
482 );
483 if !is_positioned {
484 extent = extent.max(main_child_size_getter(app, current));
485 }
486 child = next_sibling;
487 }
488 extent
489}
490
491pub fn layout_positioned_child(
496 app: &mut App,
497 child: AnyRenderBox,
498 size: Size,
499 alignment: Alignment,
500) -> bool {
501 let child_constraints = {
502 let child_parent_data = child.as_object().parent_data_of::<StackParentData>(app);
503 debug_assert!(child_parent_data.is_positioned());
504 child_parent_data.positioned_child_constraints(size)
505 };
506 child.layout(app, child_constraints, true);
507
508 let child_size = child.size(app);
509 let child_parent_data = child.as_object().parent_data_of::<StackParentData>(app);
510 let x = match (child_parent_data.left, child_parent_data.right) {
511 (Some(left), _) => left,
512 (None, Some(right)) => size.width() - right - child_size.width(),
513 (None, None) => alignment.along_offset(size - child_size).dx(),
514 };
515 let y = match (child_parent_data.top, child_parent_data.bottom) {
516 (Some(top), _) => top,
517 (None, Some(bottom)) => size.height() - bottom - child_size.height(),
518 (None, None) => alignment.along_offset(size - child_size).dy(),
519 };
520
521 child
522 .parent_data_of_mut::<StackParentData>(app)
523 .set_offset(Offset::new(x, y));
524 x < 0.0
525 || x + child_size.width() > size.width()
526 || y < 0.0
527 || y + child_size.height() > size.height()
528}
529
530pub trait RenderStackBase:
556 RenderBoxContainerDefaultsMixin<ParentDataType = StackParentData>
557{
558 fn stack_data(self: RenderHandle<Self>, app: &App) -> &RenderStackData;
560
561 fn stack_data_mut(self: RenderHandle<Self>, app: &mut App) -> &mut RenderStackData;
563
564 fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
569 if !child.parent_data_is::<StackParentData>(app) {
570 child.set_parent_data(app, StackParentData::new());
571 }
572 }
573
574 fn resolved_alignment(self: RenderHandle<Self>, app: &mut App) -> Alignment {
577 if let Some(cached) = self.stack_data(app).resolved_alignment_cache {
578 return cached;
579 }
580 let resolved = self.alignment(app).resolve(self.text_direction(app));
581 self.stack_data_mut(app).resolved_alignment_cache = Some(resolved);
582 resolved
583 }
584
585 fn mark_need_resolution(self: RenderHandle<Self>, app: &mut App) {
587 self.stack_data_mut(app).resolved_alignment_cache = None;
588 self.mark_needs_layout(app);
589 }
590
591 fn alignment(self: RenderHandle<Self>, app: &App) -> AlignmentGeometry {
605 self.stack_data(app).alignment
606 }
607
608 fn set_alignment(self: RenderHandle<Self>, app: &mut App, value: AlignmentGeometry) {
610 if self.stack_data(app).alignment == value {
611 return;
612 }
613 self.stack_data_mut(app).alignment = value;
614 self.mark_need_resolution(app);
615 }
616
617 fn text_direction(self: RenderHandle<Self>, app: &App) -> Option<TextDirection> {
622 self.stack_data(app).text_direction
623 }
624
625 fn set_text_direction(self: RenderHandle<Self>, app: &mut App, value: Option<TextDirection>) {
627 if self.stack_data(app).text_direction == value {
628 return;
629 }
630 self.stack_data_mut(app).text_direction = value;
631 self.mark_need_resolution(app);
632 }
633
634 fn fit(self: RenderHandle<Self>, app: &App) -> StackFit {
639 self.stack_data(app).fit
640 }
641
642 fn set_fit(self: RenderHandle<Self>, app: &mut App, value: StackFit) {
644 if self.stack_data(app).fit != value {
645 self.stack_data_mut(app).fit = value;
646 self.mark_needs_layout(app);
647 }
648 }
649
650 fn clip_behavior(self: RenderHandle<Self>, app: &App) -> Clip {
659 self.stack_data(app).clip_behavior
660 }
661
662 fn set_clip_behavior(self: RenderHandle<Self>, app: &mut App, value: Clip) {
664 if value != self.stack_data(app).clip_behavior {
665 self.stack_data_mut(app).clip_behavior = value;
666 self.mark_needs_paint(app);
667 }
668 }
669
670 fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
672 let first_child = self.first_child(app);
673 get_intrinsic_dimension(app, first_child, |app, child| {
674 child.get_min_intrinsic_width(app, height)
675 })
676 }
677
678 fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
680 let first_child = self.first_child(app);
681 get_intrinsic_dimension(app, first_child, |app, child| {
682 child.get_max_intrinsic_width(app, height)
683 })
684 }
685
686 fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
688 let first_child = self.first_child(app);
689 get_intrinsic_dimension(app, first_child, |app, child| {
690 child.get_min_intrinsic_height(app, width)
691 })
692 }
693
694 fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
696 let first_child = self.first_child(app);
697 get_intrinsic_dimension(app, first_child, |app, child| {
698 child.get_max_intrinsic_height(app, width)
699 })
700 }
701
702 fn compute_dry_layout(
704 self: RenderHandle<Self>,
705 app: &mut App,
706 constraints: BoxConstraints,
707 ) -> Size {
708 self.compute_size(app, constraints, ChildLayoutHelper::dry_layout_child)
709 }
710
711 fn compute_size(
714 self: RenderHandle<Self>,
715 app: &mut App,
716 constraints: BoxConstraints,
717 mut layout_child: impl FnMut(&mut App, AnyRenderBox, BoxConstraints) -> Size,
718 ) -> Size {
719 let mut has_non_positioned_children = false;
720 if self.child_count(app) == 0 {
721 return if constraints.biggest().is_finite() {
722 constraints.biggest()
723 } else {
724 constraints.smallest()
725 };
726 }
727
728 let mut width = constraints.min_width;
729 let mut height = constraints.min_height;
730
731 let non_positioned_constraints = match self.fit(app) {
732 StackFit::Loose => constraints.loosen(),
733 StackFit::Expand => BoxConstraints::tight(constraints.biggest()),
734 StackFit::Passthrough => constraints,
735 };
736
737 let mut child = self.first_child(app);
738 while let Some(current) = child {
739 let is_positioned = current
740 .as_object()
741 .parent_data_of::<StackParentData>(app)
742 .is_positioned();
743
744 if !is_positioned {
745 has_non_positioned_children = true;
746
747 let child_size = layout_child(app, current, non_positioned_constraints);
748
749 width = width.max(child_size.width());
750 height = height.max(child_size.height());
751 }
752
753 child = self.child_after(app, current);
754 }
755
756 let size = if has_non_positioned_children {
757 let size = Size::new(width, height);
758 debug_assert_eq!(size.width(), constraints.constrain_width(width));
759 debug_assert_eq!(size.height(), constraints.constrain_height(height));
760 size
761 } else {
762 constraints.biggest()
763 };
764
765 debug_assert!(
766 size.is_finite(),
767 "A Stack requires bounded constraints from its parent. This error commonly occurs \
768 when a Stack is placed inside a widget like Column, ListView, or other widgets that \
769 do not constrain their children. To fix this, wrap the Stack in a widget that \
770 provides finite height and width constraints, such as a SizedBox or ConstrainedBox. \
771 Use Expanded only if the parent is a Flex widget like Row or Column."
772 );
773 size
774 }
775
776 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
778 let constraints = self.constraints(app);
779 self.stack_data_mut(app).has_visual_overflow = false;
780
781 let size = self.compute_size(app, constraints, ChildLayoutHelper::layout_child);
782 self.set_size(app, size);
783
784 let resolved_alignment = self.resolved_alignment(app);
785 let mut child = self.first_child(app);
786 while let Some(current) = child {
787 let is_positioned = current
788 .as_object()
789 .parent_data_of::<StackParentData>(app)
790 .is_positioned();
791
792 if !is_positioned {
793 let offset = resolved_alignment.along_offset(size - current.size(app));
794 current
795 .parent_data_of_mut::<StackParentData>(app)
796 .set_offset(offset);
797 } else {
798 let overflowed = layout_positioned_child(app, current, size, resolved_alignment);
799 self.stack_data_mut(app).has_visual_overflow |= overflowed;
800 }
801
802 child = self.child_after(app, current);
803 }
804 }
805
806 fn paint_stack(
812 self: RenderHandle<Self>,
813 app: &mut App,
814 context: &mut PaintingContext,
815 offset: Offset,
816 ) {
817 self.default_paint(app, context, offset);
818 }
819
820 fn paint(
822 self: RenderHandle<Self>,
823 app: &mut App,
824 context: &mut PaintingContext,
825 offset: Offset,
826 ) {
827 let clip_behavior = self.clip_behavior(app);
828 if clip_behavior != Clip::None && self.stack_data(app).has_visual_overflow {
829 let size = self.size(app);
830 let old = self.stack_data(app).clip_rect_layer.layer();
831 let layer = context.push_clip_rect(
832 app,
833 self.as_object().needs_compositing(app),
834 offset,
835 Offset::ZERO & size,
836 |app, context, offset| self.paint_stack(app, context, offset),
837 clip_behavior,
838 old,
839 );
840 LayerHandle::set_layer(
841 app,
842 |app| &mut self.stack_data_mut(app).clip_rect_layer,
843 layer,
844 );
845 } else {
846 LayerHandle::set_layer(
847 app,
848 |app| &mut self.stack_data_mut(app).clip_rect_layer,
849 None,
850 );
851 self.paint_stack(app, context, offset);
852 }
853 }
854
855 fn dispose(self: RenderHandle<Self>, app: &mut App) {
857 LayerHandle::set_layer(
858 app,
859 |app| &mut self.stack_data_mut(app).clip_rect_layer,
860 None,
861 );
862 crate::object::RenderObjectBase::dispose(self, app);
863 }
864}
865
866pub struct RenderStack {
868 render_object: RenderObjectData,
869 render_box: RenderBoxData,
870 container: ContainerRenderObjectData<AnyRenderBox>,
871 stack: RenderStackData,
872}
873
874impl RenderStack {
875 pub fn new(app: &mut App) -> RenderHandle<RenderStack> {
881 RenderHandle::new_box(
882 app,
883 RenderStack {
884 render_object: RenderObjectData::new(),
885 render_box: RenderBoxData::new(),
886 container: ContainerRenderObjectData::new(),
887 stack: RenderStackData::new(),
888 },
889 )
890 }
891}
892
893impl ContainerRenderObjectMixin for RenderStack {
894 type ChildType = AnyRenderBox;
895 type ParentDataType = StackParentData;
896
897 fn container_data(
898 self: RenderHandle<Self>,
899 app: &App,
900 ) -> &ContainerRenderObjectData<AnyRenderBox> {
901 &self.get(app).container
902 }
903
904 fn container_data_mut(
905 self: RenderHandle<Self>,
906 app: &mut App,
907 ) -> &mut ContainerRenderObjectData<AnyRenderBox> {
908 &mut self.get_mut(app).container
909 }
910}
911
912impl RenderBoxContainerDefaultsMixin for RenderStack {}
913
914impl RenderStackBase for RenderStack {
915 fn stack_data(self: RenderHandle<Self>, app: &App) -> &RenderStackData {
916 &self.get(app).stack
917 }
918
919 fn stack_data_mut(self: RenderHandle<Self>, app: &mut App) -> &mut RenderStackData {
920 &mut self.get_mut(app).stack
921 }
922}
923
924impl RenderObject for RenderStack {
925 crate::render_object_accessors!();
926
927 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
928 RenderStackBase::perform_layout(self, app)
929 }
930
931 fn visit_children(
932 self: RenderHandle<Self>,
933 app: &App,
934 visitor: &mut dyn FnMut(AnyRenderObject),
935 ) {
936 ContainerRenderObjectMixin::visit_children(self, app, visitor)
937 }
938
939 fn did_attach(self: RenderHandle<Self>, app: &mut App, owner: Handle<PipelineOwner>) {
940 ContainerRenderObjectMixin::did_attach(self, app, owner)
941 }
942
943 fn did_detach(self: RenderHandle<Self>, app: &mut App) {
944 ContainerRenderObjectMixin::did_detach(self, app)
945 }
946
947 fn redepth_children(self: RenderHandle<Self>, app: &mut App) {
948 ContainerRenderObjectMixin::redepth_children(self, app)
949 }
950
951 fn paint(
952 self: RenderHandle<Self>,
953 app: &mut App,
954 context: &mut PaintingContext,
955 offset: Offset,
956 ) {
957 RenderStackBase::paint(self, app, context, offset)
958 }
959
960 fn dispose(self: RenderHandle<Self>, app: &mut App) {
961 RenderStackBase::dispose(self, app)
962 }
963}
964
965impl RenderBox for RenderStack {
966 crate::render_box_accessors!();
967
968 fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
969 RenderStackBase::setup_parent_data(self, app, child)
970 }
971
972 fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
973 RenderStackBase::compute_min_intrinsic_width(self, app, height)
974 }
975
976 fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
977 RenderStackBase::compute_max_intrinsic_width(self, app, height)
978 }
979
980 fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
981 RenderStackBase::compute_min_intrinsic_height(self, app, width)
982 }
983
984 fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
985 RenderStackBase::compute_max_intrinsic_height(self, app, width)
986 }
987
988 fn compute_dry_layout(
989 self: RenderHandle<Self>,
990 app: &mut App,
991 constraints: BoxConstraints,
992 ) -> Size {
993 RenderStackBase::compute_dry_layout(self, app, constraints)
994 }
995
996 fn hit_test_children(
997 self: RenderHandle<Self>,
998 app: &mut App,
999 result: &mut BoxHitTestResult<'_>,
1000 position: Offset,
1001 ) -> bool {
1002 self.default_hit_test_children(app, result, position)
1003 }
1004}
1005
1006pub struct RenderIndexedStack {
1012 render_object: RenderObjectData,
1013 render_box: RenderBoxData,
1014 container: ContainerRenderObjectData<AnyRenderBox>,
1015 stack: RenderStackData,
1016 index: Option<i32>,
1017}
1018
1019impl RenderIndexedStack {
1020 pub fn new(app: &mut App) -> RenderHandle<RenderIndexedStack> {
1024 RenderHandle::new_box(
1025 app,
1026 RenderIndexedStack {
1027 render_object: RenderObjectData::new(),
1028 render_box: RenderBoxData::new(),
1029 container: ContainerRenderObjectData::new(),
1030 stack: RenderStackData::new(),
1031 index: Some(0),
1032 },
1033 )
1034 }
1035
1036 pub fn index(self: RenderHandle<Self>, app: &App) -> Option<i32> {
1038 self.get(app).index
1039 }
1040
1041 pub fn set_index(self: RenderHandle<Self>, app: &mut App, value: Option<i32>) {
1043 if self.get(app).index != value {
1044 self.get_mut(app).index = value;
1045 self.mark_needs_layout(app);
1046 }
1047 }
1048
1049 fn child_at_index(self: RenderHandle<Self>, app: &App) -> Option<AnyRenderBox> {
1050 let index = self.index(app)?;
1051 let mut child = self.first_child(app);
1052 let mut i = 0;
1053 while i < index
1054 && let Some(current) = child
1055 {
1056 child = self.child_after(app, current);
1057 i += 1;
1058 }
1059 debug_assert!(self.first_child(app).is_none() || child.is_some());
1060 child
1061 }
1062}
1063
1064impl ContainerRenderObjectMixin for RenderIndexedStack {
1065 type ChildType = AnyRenderBox;
1066 type ParentDataType = StackParentData;
1067
1068 fn container_data(
1069 self: RenderHandle<Self>,
1070 app: &App,
1071 ) -> &ContainerRenderObjectData<AnyRenderBox> {
1072 &self.get(app).container
1073 }
1074
1075 fn container_data_mut(
1076 self: RenderHandle<Self>,
1077 app: &mut App,
1078 ) -> &mut ContainerRenderObjectData<AnyRenderBox> {
1079 &mut self.get_mut(app).container
1080 }
1081}
1082
1083impl RenderBoxContainerDefaultsMixin for RenderIndexedStack {}
1084
1085impl RenderStackBase for RenderIndexedStack {
1086 fn stack_data(self: RenderHandle<Self>, app: &App) -> &RenderStackData {
1087 &self.get(app).stack
1088 }
1089
1090 fn stack_data_mut(self: RenderHandle<Self>, app: &mut App) -> &mut RenderStackData {
1091 &mut self.get_mut(app).stack
1092 }
1093
1094 fn paint_stack(
1095 self: RenderHandle<Self>,
1096 app: &mut App,
1097 context: &mut PaintingContext,
1098 offset: Offset,
1099 ) {
1100 let Some(displayed_child) = self.child_at_index(app) else {
1101 return;
1102 };
1103 let child_offset = displayed_child
1104 .as_object()
1105 .parent_data_of::<StackParentData>(app)
1106 .offset();
1107 context.paint_child(app, displayed_child.as_object(), child_offset + offset);
1108 }
1109}
1110
1111impl RenderObject for RenderIndexedStack {
1112 crate::render_object_accessors!();
1113
1114 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
1115 RenderStackBase::perform_layout(self, app)
1116 }
1117
1118 fn visit_children(
1119 self: RenderHandle<Self>,
1120 app: &App,
1121 visitor: &mut dyn FnMut(AnyRenderObject),
1122 ) {
1123 ContainerRenderObjectMixin::visit_children(self, app, visitor)
1124 }
1125
1126 fn did_attach(self: RenderHandle<Self>, app: &mut App, owner: Handle<PipelineOwner>) {
1127 ContainerRenderObjectMixin::did_attach(self, app, owner)
1128 }
1129
1130 fn did_detach(self: RenderHandle<Self>, app: &mut App) {
1131 ContainerRenderObjectMixin::did_detach(self, app)
1132 }
1133
1134 fn redepth_children(self: RenderHandle<Self>, app: &mut App) {
1135 ContainerRenderObjectMixin::redepth_children(self, app)
1136 }
1137
1138 fn paint(
1139 self: RenderHandle<Self>,
1140 app: &mut App,
1141 context: &mut PaintingContext,
1142 offset: Offset,
1143 ) {
1144 RenderStackBase::paint(self, app, context, offset)
1145 }
1146
1147 fn dispose(self: RenderHandle<Self>, app: &mut App) {
1148 RenderStackBase::dispose(self, app)
1149 }
1150}
1151
1152impl RenderBox for RenderIndexedStack {
1153 crate::render_box_accessors!();
1154
1155 fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
1156 RenderStackBase::setup_parent_data(self, app, child)
1157 }
1158
1159 fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1160 RenderStackBase::compute_min_intrinsic_width(self, app, height)
1161 }
1162
1163 fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1164 RenderStackBase::compute_max_intrinsic_width(self, app, height)
1165 }
1166
1167 fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1168 RenderStackBase::compute_min_intrinsic_height(self, app, width)
1169 }
1170
1171 fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1172 RenderStackBase::compute_max_intrinsic_height(self, app, width)
1173 }
1174
1175 fn compute_dry_layout(
1176 self: RenderHandle<Self>,
1177 app: &mut App,
1178 constraints: BoxConstraints,
1179 ) -> Size {
1180 RenderStackBase::compute_dry_layout(self, app, constraints)
1181 }
1182
1183 fn hit_test_children(
1184 self: RenderHandle<Self>,
1185 app: &mut App,
1186 result: &mut BoxHitTestResult<'_>,
1187 position: Offset,
1188 ) -> bool {
1189 let Some(displayed_child) = self.child_at_index(app) else {
1190 return false;
1191 };
1192 let child_offset = displayed_child
1193 .as_object()
1194 .parent_data_of::<StackParentData>(app)
1195 .offset();
1196 result.add_with_paint_offset(Some(child_offset), position, |result, transformed| {
1197 debug_assert_eq!(transformed, position - child_offset);
1198 displayed_child.hit_test(app, result, transformed)
1199 })
1200 }
1201}
1202
1203#[cfg(test)]
1204mod tests {
1205 use inset_foundation::AppCell;
1206 use std::cell::Cell;
1207 use std::rc::Rc;
1208
1209 use super::*;
1210 use crate::flex::{MainAxisSize, RenderFlex};
1211 use crate::layer::{ContainerLayer, ErasedLayer, OffsetLayer, PictureLayer};
1212 use crate::pipeline_owner::PipelineOwner;
1213 use crate::proxy_box::{RenderConstrainedBox, RenderRepaintBoundary};
1214 use crate::shifted_box::RenderPadding;
1215 use inset_embedder::valo::Op;
1216 use inset_painting::EdgeInsetsGeometry;
1217
1218 struct SizedTestBox {
1220 render_object: RenderObjectData,
1221 render_box: RenderBoxData,
1222 preferred_size: Size,
1223 paints: Rc<Cell<u32>>,
1224 }
1225
1226 impl RenderObject for SizedTestBox {
1227 crate::render_object_accessors!();
1228
1229 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
1230 let preferred_size = self.get(app).preferred_size;
1231 let size = self.constraints(app).constrain(preferred_size);
1232 self.set_size(app, size);
1233 }
1234
1235 fn paint(
1236 self: RenderHandle<Self>,
1237 app: &mut App,
1238 _context: &mut PaintingContext,
1239 _offset: Offset,
1240 ) {
1241 let paints = self.get(app).paints.clone();
1242 paints.set(paints.get() + 1);
1243 }
1244 }
1245
1246 impl RenderBox for SizedTestBox {
1247 crate::render_box_accessors!();
1248 }
1249
1250 fn sized_box(app: &mut App, size: Size) -> (AnyRenderBox, Rc<Cell<u32>>) {
1251 let paints = Rc::new(Cell::new(0));
1252 let box_ = RenderHandle::new_box(
1253 app,
1254 SizedTestBox {
1255 render_object: RenderObjectData::new(),
1256 render_box: RenderBoxData::new(),
1257 preferred_size: size,
1258 paints: paints.clone(),
1259 },
1260 )
1261 .as_box();
1262 (box_, paints)
1263 }
1264
1265 fn schedule_root_paint(app: &mut App, node: AnyRenderObject) {
1266 let root = OffsetLayer::new(app, Offset::ZERO);
1267 root.as_layer().attach(app, node.id());
1268 node.schedule_initial_paint(app, root.as_container_layer());
1269 }
1270
1271 fn first_frame(
1273 app: &mut App,
1274 root: AnyRenderBox,
1275 constraints: BoxConstraints,
1276 ) -> Handle<PipelineOwner> {
1277 let owner = PipelineOwner::new(app, None, None);
1278 owner.set_root_node(app, Some(root.as_object()));
1279 root.layout(app, constraints, false);
1280 schedule_root_paint(app, root.as_object());
1281 owner.flush_compositing_bits(app);
1282 owner.flush_paint(app);
1283 owner
1284 }
1285
1286 fn picture_has_clip(app: &App, boundary: AnyRenderObject) -> bool {
1287 let layer = boundary.debug_layer(app).expect("painted");
1288 layer
1289 .depth_first_iterate_children(app)
1290 .into_iter()
1291 .filter_map(|child| {
1292 app.handle::<PictureLayer>(child.id())
1293 .and_then(|picture| picture.picture(app).map(|p| p.ops().to_vec()))
1294 })
1295 .flatten()
1296 .any(|op| matches!(op, Op::ClipPath { .. }))
1297 }
1298
1299 #[test]
1304 fn stack_intrinsics_are_the_largest_non_positioned_child() {
1305 let cell = AppCell::new();
1306 let mut app = cell.borrow_mut();
1307 let first =
1308 RenderConstrainedBox::new(&mut app, BoxConstraints::tight(Size::new(30.0, 40.0)), None);
1309 let second =
1310 RenderConstrainedBox::new(&mut app, BoxConstraints::tight(Size::new(50.0, 20.0)), None);
1311 let positioned = RenderConstrainedBox::new(
1312 &mut app,
1313 BoxConstraints::tight(Size::new(200.0, 200.0)),
1314 None,
1315 );
1316 let stack = RenderStack::new(&mut app);
1317 stack.set_text_direction(&mut app, Some(TextDirection::Ltr));
1318 stack.add_all(
1319 &mut app,
1320 Some(vec![first.as_box(), second.as_box(), positioned.as_box()]),
1321 );
1322 positioned
1323 .as_box()
1324 .parent_data_of_mut::<StackParentData>(&mut app)
1325 .left = Some(0.0);
1326
1327 let box_ = stack.as_box();
1328 assert_eq!(box_.get_min_intrinsic_width(&mut app, 0.0), 50.0);
1329 assert_eq!(box_.get_max_intrinsic_width(&mut app, 0.0), 50.0);
1330 assert_eq!(box_.get_min_intrinsic_height(&mut app, 0.0), 40.0);
1331 assert_eq!(box_.get_max_intrinsic_height(&mut app, 0.0), 40.0);
1332 }
1333
1334 #[test]
1337 fn dry_layout_matches_layout_for_a_padding_in_a_flex_in_a_stack() {
1338 let cell = AppCell::new();
1339 let mut app = cell.borrow_mut();
1340 let leaf =
1341 RenderConstrainedBox::new(&mut app, BoxConstraints::tight(Size::new(40.0, 20.0)), None);
1342 let padding = RenderPadding::new(
1343 &mut app,
1344 EdgeInsetsGeometry::all(5.0),
1345 None,
1346 Some(leaf.as_box()),
1347 );
1348 let row = RenderFlex::new(&mut app);
1349 row.set_text_direction(&mut app, Some(TextDirection::Ltr));
1350 row.set_main_axis_size(&mut app, MainAxisSize::Min);
1351 row.add_all(&mut app, Some(vec![padding.as_box()]));
1352 let stack = RenderStack::new(&mut app);
1353 stack.set_text_direction(&mut app, Some(TextDirection::Ltr));
1354 stack.add_all(&mut app, Some(vec![row.as_box()]));
1355
1356 let constraints = BoxConstraints::new().max_width(300.0).max_height(300.0);
1357 let dry = stack.as_box().get_dry_layout(&mut app, constraints);
1358 stack.layout(&mut app, constraints, false);
1359 assert_eq!(dry, Size::new(50.0, 30.0));
1360 assert_eq!(dry, stack.size(&app));
1361 assert_eq!(row.size(&app), Size::new(50.0, 30.0));
1362 }
1363
1364 #[test]
1365 fn positioned_child_is_laid_out_by_its_rect() {
1366 let cell = AppCell::new();
1367 let mut app = cell.borrow_mut();
1368 let (child, _) = sized_box(&mut app, Size::new(10.0, 10.0));
1369 let stack = RenderStack::new(&mut app);
1370 stack.set_text_direction(&mut app, Some(TextDirection::Ltr));
1371 stack.add(&mut app, child);
1372 child
1373 .parent_data_of_mut::<StackParentData>(&mut app)
1374 .set_rect(RelativeRect::from_ltrb(10.0, 20.0, 30.0, 40.0));
1375
1376 stack.layout(
1377 &mut app,
1378 BoxConstraints::tight(Size::new(100.0, 100.0)),
1379 false,
1380 );
1381
1382 assert_eq!(stack.size(&app), Size::new(100.0, 100.0));
1384 assert_eq!(child.size(&app), Size::new(60.0, 40.0));
1385 assert_eq!(child.box_parent_data(&app).offset, Offset::new(10.0, 20.0));
1386 }
1387
1388 #[test]
1391 fn non_positioned_children_are_sized_by_the_fit() {
1392 let cases = [
1393 (
1394 StackFit::Loose,
1395 Size::new(20.0, 20.0),
1396 Size::new(50.0, 50.0),
1397 ),
1398 (
1399 StackFit::Expand,
1400 Size::new(100.0, 100.0),
1401 Size::new(100.0, 100.0),
1402 ),
1403 (
1404 StackFit::Passthrough,
1405 Size::new(50.0, 50.0),
1406 Size::new(50.0, 50.0),
1407 ),
1408 ];
1409 for (fit, child_size, stack_size) in cases {
1410 let cell = AppCell::new();
1411 let mut app = cell.borrow_mut();
1412 let (child, _) = sized_box(&mut app, Size::new(20.0, 20.0));
1413 let stack = RenderStack::new(&mut app);
1414 stack.set_text_direction(&mut app, Some(TextDirection::Ltr));
1415 stack.set_fit(&mut app, fit);
1416 stack.add(&mut app, child);
1417
1418 stack.layout(
1419 &mut app,
1420 BoxConstraints {
1421 min_width: 50.0,
1422 max_width: 100.0,
1423 min_height: 50.0,
1424 max_height: 100.0,
1425 },
1426 false,
1427 );
1428
1429 assert_eq!(child.size(&app), child_size, "{fit:?}");
1430 assert_eq!(stack.size(&app), stack_size, "{fit:?}");
1431 }
1432 }
1433
1434 #[test]
1437 fn indexed_stack_paints_one_child() {
1438 let cell = AppCell::new();
1439 let mut app = cell.borrow_mut();
1440 let (first, first_paints) = sized_box(&mut app, Size::new(20.0, 20.0));
1441 let (second, second_paints) = sized_box(&mut app, Size::new(30.0, 30.0));
1442 let stack = RenderIndexedStack::new(&mut app);
1443 stack.set_text_direction(&mut app, Some(TextDirection::Ltr));
1444 stack.add_all(&mut app, Some(vec![first, second]));
1445 stack.set_index(&mut app, Some(1));
1446 let root = RenderRepaintBoundary::new(&mut app, Some(stack.as_box()));
1447
1448 let owner = first_frame(
1449 &mut app,
1450 root.as_box(),
1451 BoxConstraints::tight(Size::new(100.0, 100.0)),
1452 );
1453
1454 assert_eq!(first.size(&app), Size::new(20.0, 20.0));
1456 assert_eq!(second.size(&app), Size::new(30.0, 30.0));
1457 assert_eq!(first_paints.get(), 0);
1458 assert_eq!(second_paints.get(), 1);
1459
1460 stack.set_index(&mut app, Some(0));
1461 owner.flush_layout(&mut app);
1462 owner.flush_compositing_bits(&mut app);
1463 owner.flush_paint(&mut app);
1464 assert_eq!(first_paints.get(), 1);
1465 assert_eq!(second_paints.get(), 1);
1466 }
1467
1468 #[test]
1470 fn overflowing_positioned_child_is_clipped() {
1471 for (clip_behavior, clips) in [(Clip::None, false), (Clip::HardEdge, true)] {
1472 let cell = AppCell::new();
1473 let mut app = cell.borrow_mut();
1474 let (child, _) = sized_box(&mut app, Size::new(10.0, 10.0));
1475 let stack = RenderStack::new(&mut app);
1476 stack.set_text_direction(&mut app, Some(TextDirection::Ltr));
1477 stack.set_clip_behavior(&mut app, clip_behavior);
1478 stack.add(&mut app, child);
1479 child.parent_data_of_mut::<StackParentData>(&mut app).left = Some(-10.0);
1480 let root = RenderRepaintBoundary::new(&mut app, Some(stack.as_box()));
1481
1482 let owner = first_frame(
1483 &mut app,
1484 root.as_box(),
1485 BoxConstraints::tight(Size::new(100.0, 100.0)),
1486 );
1487
1488 let clipped = picture_has_clip(&app, root.as_object());
1489 assert_eq!(clipped, clips, "{clip_behavior:?}");
1490 let _ = owner;
1491 }
1492 }
1493}