1use std::any::{Any, TypeId};
5use std::fmt::{self, Debug};
6
7use inset_embedder::{Clip, Offset, Rect, Size, TextBaseline, TextDirection, lerp_double};
8use inset_foundation::{App, Handle};
9use inset_painting::{Alignment, AlignmentGeometry};
10
11use crate::box_::{
12 AnyRenderBox, BaselineOffset, BoxConstraints, BoxHitTestResult, BoxParentData,
13 ContainerBoxParentData, RenderBox, RenderBoxContainerDefaultsMixin, RenderBoxData,
14};
15use crate::layer::{ClipRectLayer, LayerHandle};
16use crate::layout_helper::ChildLayoutHelper;
17use crate::object::{
18 AnyRenderObject, ContainerParentData, ContainerParentDataMixin, ContainerRenderObjectData,
19 ContainerRenderObjectMixin, ParentData, RenderHandle, RenderObject, RenderObjectData,
20};
21use crate::painting_context::PaintingContext;
22use crate::pipeline_owner::PipelineOwner;
23
24#[derive(Clone, Copy, PartialEq)]
32pub struct RelativeRect {
33 pub left: f64,
37
38 pub top: f64,
42
43 pub right: f64,
47
48 pub bottom: f64,
52}
53
54impl RelativeRect {
55 pub const fn from_ltrb(left: f64, top: f64, right: f64, bottom: f64) -> RelativeRect {
57 RelativeRect {
58 left,
59 top,
60 right,
61 bottom,
62 }
63 }
64
65 pub fn from_size(rect: Rect, container: Size) -> RelativeRect {
70 RelativeRect {
71 left: rect.left,
72 top: rect.top,
73 right: container.width() - rect.right,
74 bottom: container.height() - rect.bottom,
75 }
76 }
77
78 pub fn from_rect(rect: Rect, container: Rect) -> RelativeRect {
91 RelativeRect {
92 left: rect.left - container.left,
93 top: rect.top - container.top,
94 right: container.right - rect.right,
95 bottom: container.bottom - rect.bottom,
96 }
97 }
98
99 pub fn from_directional(
107 text_direction: TextDirection,
108 start: f64,
109 top: f64,
110 end: f64,
111 bottom: f64,
112 ) -> RelativeRect {
113 let (left, right) = match text_direction {
114 TextDirection::Rtl => (end, start),
115 TextDirection::Ltr => (start, end),
116 };
117 RelativeRect::from_ltrb(left, top, right, bottom)
118 }
119
120 pub const FILL: RelativeRect = RelativeRect::from_ltrb(0.0, 0.0, 0.0, 0.0);
122
123 pub fn has_insets(&self) -> bool {
129 self.left > 0.0 || self.top > 0.0 || self.right > 0.0 || self.bottom > 0.0
130 }
131
132 pub fn shift(&self, offset: Offset) -> RelativeRect {
134 RelativeRect::from_ltrb(
135 self.left + offset.dx(),
136 self.top + offset.dy(),
137 self.right - offset.dx(),
138 self.bottom - offset.dy(),
139 )
140 }
141
142 pub fn inflate(&self, delta: f64) -> RelativeRect {
144 RelativeRect::from_ltrb(
145 self.left - delta,
146 self.top - delta,
147 self.right - delta,
148 self.bottom - delta,
149 )
150 }
151
152 pub fn deflate(&self, delta: f64) -> RelativeRect {
154 self.inflate(-delta)
155 }
156
157 pub fn intersect(&self, other: RelativeRect) -> RelativeRect {
159 RelativeRect::from_ltrb(
160 self.left.max(other.left),
161 self.top.max(other.top),
162 self.right.max(other.right),
163 self.bottom.max(other.bottom),
164 )
165 }
166
167 pub fn to_rect(&self, container: Rect) -> Rect {
174 Rect::from_ltrb(
175 self.left,
176 self.top,
177 container.width() - self.right,
178 container.height() - self.bottom,
179 )
180 }
181
182 pub fn to_size(&self, container: Size) -> Size {
188 Size::new(
189 container.width() - self.left - self.right,
190 container.height() - self.top - self.bottom,
191 )
192 }
193
194 pub fn lerp(a: Option<RelativeRect>, b: Option<RelativeRect>, t: f64) -> Option<RelativeRect> {
198 match (a, b) {
199 (None, None) => None,
200 (None, Some(b)) => Some(RelativeRect::from_ltrb(
201 b.left * t,
202 b.top * t,
203 b.right * t,
204 b.bottom * t,
205 )),
206 (Some(a), None) => {
207 let k = 1.0 - t;
208 Some(RelativeRect::from_ltrb(
209 a.left * k,
210 a.top * k,
211 a.right * k,
212 a.bottom * k,
213 ))
214 }
215 (Some(a), Some(b)) => Some(RelativeRect::from_ltrb(
216 lerp_double(Some(a.left), Some(b.left), t).unwrap(),
217 lerp_double(Some(a.top), Some(b.top), t).unwrap(),
218 lerp_double(Some(a.right), Some(b.right), t).unwrap(),
219 lerp_double(Some(a.bottom), Some(b.bottom), t).unwrap(),
220 )),
221 }
222 }
223}
224
225impl Debug for RelativeRect {
226 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227 write!(
228 f,
229 "RelativeRect::from_ltrb({:.1}, {:.1}, {:.1}, {:.1})",
230 self.left, self.top, self.right, self.bottom
231 )
232 }
233}
234
235#[derive(Debug)]
237pub struct StackParentData {
238 box_parent_data: BoxParentData,
239 container_parent_data: ContainerParentData<AnyRenderBox>,
240
241 pub top: Option<f64>,
243
244 pub right: Option<f64>,
246
247 pub bottom: Option<f64>,
249
250 pub left: Option<f64>,
252
253 pub width: Option<f64>,
257
258 pub height: Option<f64>,
262}
263
264impl StackParentData {
265 pub const fn new() -> StackParentData {
267 StackParentData {
268 box_parent_data: BoxParentData::new(),
269 container_parent_data: ContainerParentData::new(),
270 top: None,
271 right: None,
272 bottom: None,
273 left: None,
274 width: None,
275 height: None,
276 }
277 }
278
279 pub fn rect(&self) -> RelativeRect {
285 RelativeRect::from_ltrb(
286 self.left.expect("left is set"),
287 self.top.expect("top is set"),
288 self.right.expect("right is set"),
289 self.bottom.expect("bottom is set"),
290 )
291 }
292
293 pub fn set_rect(&mut self, value: RelativeRect) {
295 self.top = Some(value.top);
296 self.right = Some(value.right);
297 self.bottom = Some(value.bottom);
298 self.left = Some(value.left);
299 }
300
301 pub fn is_positioned(&self) -> bool {
307 self.top.is_some()
308 || self.right.is_some()
309 || self.bottom.is_some()
310 || self.left.is_some()
311 || self.width.is_some()
312 || self.height.is_some()
313 }
314
315 pub fn positioned_child_constraints(&self, stack_size: Size) -> BoxConstraints {
321 debug_assert!(self.is_positioned());
322 let width = match (self.left, self.right) {
323 (Some(left), Some(right)) => Some(stack_size.width() - right - left),
324 _ => self.width,
325 };
326 let height = match (self.top, self.bottom) {
327 (Some(top), Some(bottom)) => Some(stack_size.height() - bottom - top),
328 _ => self.height,
329 };
330 debug_assert!(height.is_none_or(|height| !height.is_nan()));
331 debug_assert!(width.is_none_or(|width| !width.is_nan()));
332 BoxConstraints::tight_for(
333 width.map(|width| width.max(0.0)),
334 height.map(|height| height.max(0.0)),
335 )
336 }
337}
338
339impl Default for StackParentData {
340 fn default() -> StackParentData {
341 StackParentData::new()
342 }
343}
344
345impl ParentData for StackParentData {
346 fn detach(&mut self) {
347 ContainerParentDataMixin::detach(self);
348 }
349
350 fn provide(&self, id: TypeId) -> Option<&dyn Any> {
351 if id == TypeId::of::<StackParentData>() {
352 return Some(self);
353 }
354 self.box_parent_data.provide(id)
355 }
356
357 fn provide_mut(&mut self, id: TypeId) -> Option<&mut dyn Any> {
358 if id == TypeId::of::<StackParentData>() {
359 return Some(self);
360 }
361 self.box_parent_data.provide_mut(id)
362 }
363}
364
365impl ContainerParentDataMixin for StackParentData {
366 type ChildType = AnyRenderBox;
367
368 fn container_parent_data(&self) -> &ContainerParentData<AnyRenderBox> {
369 &self.container_parent_data
370 }
371
372 fn container_parent_data_mut(&mut self) -> &mut ContainerParentData<AnyRenderBox> {
373 &mut self.container_parent_data
374 }
375}
376
377impl ContainerBoxParentData for StackParentData {
378 fn box_parent_data(&self) -> &BoxParentData {
379 &self.box_parent_data
380 }
381
382 fn box_parent_data_mut(&mut self) -> &mut BoxParentData {
383 &mut self.box_parent_data
384 }
385}
386
387impl fmt::Display for StackParentData {
388 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
389 let mut values = Vec::new();
390 for (name, value) in [
391 ("top", self.top),
392 ("right", self.right),
393 ("bottom", self.bottom),
394 ("left", self.left),
395 ("width", self.width),
396 ("height", self.height),
397 ] {
398 if let Some(value) = value {
399 values.push(format!("{name}={value}"));
400 }
401 }
402 if values.is_empty() {
403 values.push("not positioned".to_string());
404 }
405 values.push(self.box_parent_data.to_string());
406 f.write_str(&values.join("; "))
407 }
408}
409
410#[derive(Clone, Copy, Debug, PartialEq, Eq)]
412pub enum StackFit {
413 Loose,
419
420 Expand,
427
428 Passthrough,
431}
432
433pub struct RenderStackData {
435 has_visual_overflow: bool,
436 alignment: AlignmentGeometry,
437 text_direction: Option<TextDirection>,
438 fit: StackFit,
439 clip_behavior: Clip,
440 resolved_alignment_cache: Option<Alignment>,
441 clip_rect_layer: LayerHandle<Handle<ClipRectLayer>>,
442}
443
444impl RenderStackData {
445 pub fn new() -> RenderStackData {
447 RenderStackData {
448 has_visual_overflow: false,
449 alignment: AlignmentGeometry::TOP_START,
450 text_direction: None,
451 fit: StackFit::Loose,
452 clip_behavior: Clip::HardEdge,
453 resolved_alignment_cache: None,
454 clip_rect_layer: LayerHandle::new(),
455 }
456 }
457}
458
459impl Default for RenderStackData {
460 fn default() -> RenderStackData {
461 RenderStackData::new()
462 }
463}
464
465pub fn get_intrinsic_dimension(
469 app: &mut App,
470 first_child: Option<AnyRenderBox>,
471 mut main_child_size_getter: impl FnMut(&mut App, AnyRenderBox) -> f64,
472) -> f64 {
473 let mut extent: f64 = 0.0;
474 let mut child = first_child;
475 while let Some(current) = child {
476 let child_parent_data = current.as_object().parent_data_of::<StackParentData>(app);
477 let (is_positioned, next_sibling) = (
478 child_parent_data.is_positioned(),
479 child_parent_data.next_sibling(),
480 );
481 if !is_positioned {
482 extent = extent.max(main_child_size_getter(app, current));
483 }
484 child = next_sibling;
485 }
486 extent
487}
488
489pub fn layout_positioned_child(
494 app: &mut App,
495 child: AnyRenderBox,
496 size: Size,
497 alignment: Alignment,
498) -> bool {
499 let child_constraints = {
500 let child_parent_data = child.as_object().parent_data_of::<StackParentData>(app);
501 debug_assert!(child_parent_data.is_positioned());
502 child_parent_data.positioned_child_constraints(size)
503 };
504 child.layout(app, child_constraints, true);
505
506 let child_size = child.size(app);
507 let child_parent_data = child.as_object().parent_data_of::<StackParentData>(app);
508 let x = match (child_parent_data.left, child_parent_data.right) {
509 (Some(left), _) => left,
510 (None, Some(right)) => size.width() - right - child_size.width(),
511 (None, None) => alignment.along_offset(size - child_size).dx(),
512 };
513 let y = match (child_parent_data.top, child_parent_data.bottom) {
514 (Some(top), _) => top,
515 (None, Some(bottom)) => size.height() - bottom - child_size.height(),
516 (None, None) => alignment.along_offset(size - child_size).dy(),
517 };
518
519 child
520 .parent_data_of_mut::<StackParentData>(app)
521 .set_offset(Offset::new(x, y));
522 x < 0.0
523 || x + child_size.width() > size.width()
524 || y < 0.0
525 || y + child_size.height() > size.height()
526}
527
528fn baseline_for_child(
530 app: &mut App,
531 child: AnyRenderBox,
532 stack_size: Size,
533 non_positioned_child_constraints: BoxConstraints,
534 alignment: Alignment,
535 baseline: TextBaseline,
536) -> Option<f64> {
537 let child_parent_data = child.as_object().parent_data_of::<StackParentData>(app);
538 let child_constraints = if child_parent_data.is_positioned() {
539 child_parent_data.positioned_child_constraints(stack_size)
540 } else {
541 non_positioned_child_constraints
542 };
543 let (top, bottom) = (child_parent_data.top, child_parent_data.bottom);
544 let baseline_offset = child.get_dry_baseline(app, child_constraints, baseline)?;
545 let y = match (top, bottom) {
546 (Some(top), _) => top,
547 (None, Some(bottom)) => {
548 stack_size.height() - bottom - child.get_dry_layout(app, child_constraints).height()
549 }
550 (None, None) => alignment
551 .along_offset(stack_size - child.get_dry_layout(app, child_constraints))
552 .dy(),
553 };
554 Some(baseline_offset + y)
555}
556
557pub trait RenderStackBase:
583 RenderBoxContainerDefaultsMixin<ParentDataType = StackParentData>
584{
585 fn stack_data(self: RenderHandle<Self>, app: &App) -> &RenderStackData;
587
588 fn stack_data_mut(self: RenderHandle<Self>, app: &mut App) -> &mut RenderStackData;
590
591 fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
596 if !child.parent_data_is::<StackParentData>(app) {
597 child.set_parent_data(app, StackParentData::new());
598 }
599 }
600
601 fn resolved_alignment(self: RenderHandle<Self>, app: &mut App) -> Alignment {
604 if let Some(cached) = self.stack_data(app).resolved_alignment_cache {
605 return cached;
606 }
607 let resolved = self.alignment(app).resolve(self.text_direction(app));
608 self.stack_data_mut(app).resolved_alignment_cache = Some(resolved);
609 resolved
610 }
611
612 fn mark_need_resolution(self: RenderHandle<Self>, app: &mut App) {
614 self.stack_data_mut(app).resolved_alignment_cache = None;
615 self.mark_needs_layout(app);
616 }
617
618 fn alignment(self: RenderHandle<Self>, app: &App) -> AlignmentGeometry {
632 self.stack_data(app).alignment
633 }
634
635 fn set_alignment(self: RenderHandle<Self>, app: &mut App, value: AlignmentGeometry) {
637 if self.stack_data(app).alignment == value {
638 return;
639 }
640 self.stack_data_mut(app).alignment = value;
641 self.mark_need_resolution(app);
642 }
643
644 fn text_direction(self: RenderHandle<Self>, app: &App) -> Option<TextDirection> {
649 self.stack_data(app).text_direction
650 }
651
652 fn set_text_direction(self: RenderHandle<Self>, app: &mut App, value: Option<TextDirection>) {
654 if self.stack_data(app).text_direction == value {
655 return;
656 }
657 self.stack_data_mut(app).text_direction = value;
658 self.mark_need_resolution(app);
659 }
660
661 fn fit(self: RenderHandle<Self>, app: &App) -> StackFit {
666 self.stack_data(app).fit
667 }
668
669 fn set_fit(self: RenderHandle<Self>, app: &mut App, value: StackFit) {
671 if self.stack_data(app).fit != value {
672 self.stack_data_mut(app).fit = value;
673 self.mark_needs_layout(app);
674 }
675 }
676
677 fn clip_behavior(self: RenderHandle<Self>, app: &App) -> Clip {
686 self.stack_data(app).clip_behavior
687 }
688
689 fn set_clip_behavior(self: RenderHandle<Self>, app: &mut App, value: Clip) {
691 if value != self.stack_data(app).clip_behavior {
692 self.stack_data_mut(app).clip_behavior = value;
693 self.mark_needs_paint(app);
694 }
695 }
696
697 fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
699 let first_child = self.first_child(app);
700 get_intrinsic_dimension(app, first_child, |app, child| {
701 child.get_min_intrinsic_width(app, height)
702 })
703 }
704
705 fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
707 let first_child = self.first_child(app);
708 get_intrinsic_dimension(app, first_child, |app, child| {
709 child.get_max_intrinsic_width(app, height)
710 })
711 }
712
713 fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
715 let first_child = self.first_child(app);
716 get_intrinsic_dimension(app, first_child, |app, child| {
717 child.get_min_intrinsic_height(app, width)
718 })
719 }
720
721 fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
723 let first_child = self.first_child(app);
724 get_intrinsic_dimension(app, first_child, |app, child| {
725 child.get_max_intrinsic_height(app, width)
726 })
727 }
728
729 fn compute_distance_to_actual_baseline(
731 self: RenderHandle<Self>,
732 app: &mut App,
733 baseline: TextBaseline,
734 ) -> Option<f64> {
735 self.default_compute_distance_to_highest_actual_baseline(app, baseline)
736 }
737
738 fn compute_dry_baseline(
740 self: RenderHandle<Self>,
741 app: &mut App,
742 constraints: BoxConstraints,
743 baseline: TextBaseline,
744 ) -> Option<f64> {
745 let non_positioned_child_constraints = match self.fit(app) {
746 StackFit::Loose => constraints.loosen(),
747 StackFit::Expand => BoxConstraints::tight(constraints.biggest()),
748 StackFit::Passthrough => constraints,
749 };
750
751 let alignment = self.resolved_alignment(app);
752 let size = self.as_box().get_dry_layout(app, constraints);
753
754 let mut baseline_offset = BaselineOffset::NO_BASELINE;
755 let mut child = self.first_child(app);
756 while let Some(current) = child {
757 baseline_offset = baseline_offset.min_of(BaselineOffset(baseline_for_child(
758 app,
759 current,
760 size,
761 non_positioned_child_constraints,
762 alignment,
763 baseline,
764 )));
765 child = self.child_after(app, current);
766 }
767 baseline_offset.offset()
768 }
769
770 fn compute_dry_layout(
772 self: RenderHandle<Self>,
773 app: &mut App,
774 constraints: BoxConstraints,
775 ) -> Size {
776 self.compute_size(app, constraints, ChildLayoutHelper::dry_layout_child)
777 }
778
779 fn compute_size(
782 self: RenderHandle<Self>,
783 app: &mut App,
784 constraints: BoxConstraints,
785 mut layout_child: impl FnMut(&mut App, AnyRenderBox, BoxConstraints) -> Size,
786 ) -> Size {
787 let mut has_non_positioned_children = false;
788 if self.child_count(app) == 0 {
789 return if constraints.biggest().is_finite() {
790 constraints.biggest()
791 } else {
792 constraints.smallest()
793 };
794 }
795
796 let mut width = constraints.min_width;
797 let mut height = constraints.min_height;
798
799 let non_positioned_constraints = match self.fit(app) {
800 StackFit::Loose => constraints.loosen(),
801 StackFit::Expand => BoxConstraints::tight(constraints.biggest()),
802 StackFit::Passthrough => constraints,
803 };
804
805 let mut child = self.first_child(app);
806 while let Some(current) = child {
807 let is_positioned = current
808 .as_object()
809 .parent_data_of::<StackParentData>(app)
810 .is_positioned();
811
812 if !is_positioned {
813 has_non_positioned_children = true;
814
815 let child_size = layout_child(app, current, non_positioned_constraints);
816
817 width = width.max(child_size.width());
818 height = height.max(child_size.height());
819 }
820
821 child = self.child_after(app, current);
822 }
823
824 let size = if has_non_positioned_children {
825 let size = Size::new(width, height);
826 debug_assert_eq!(size.width(), constraints.constrain_width(width));
827 debug_assert_eq!(size.height(), constraints.constrain_height(height));
828 size
829 } else {
830 constraints.biggest()
831 };
832
833 debug_assert!(
834 size.is_finite(),
835 "A Stack requires bounded constraints from its parent. This error commonly occurs \
836 when a Stack is placed inside a widget like Column, ListView, or other widgets that \
837 do not constrain their children. To fix this, wrap the Stack in a widget that \
838 provides finite height and width constraints, such as a SizedBox or ConstrainedBox. \
839 Use Expanded only if the parent is a Flex widget like Row or Column."
840 );
841 size
842 }
843
844 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
846 let constraints = self.constraints(app);
847 self.stack_data_mut(app).has_visual_overflow = false;
848
849 let size = self.compute_size(app, constraints, ChildLayoutHelper::layout_child);
850 self.set_size(app, size);
851
852 let resolved_alignment = self.resolved_alignment(app);
853 let mut child = self.first_child(app);
854 while let Some(current) = child {
855 let is_positioned = current
856 .as_object()
857 .parent_data_of::<StackParentData>(app)
858 .is_positioned();
859
860 if !is_positioned {
861 let offset = resolved_alignment.along_offset(size - current.size(app));
862 current
863 .parent_data_of_mut::<StackParentData>(app)
864 .set_offset(offset);
865 } else {
866 let overflowed = layout_positioned_child(app, current, size, resolved_alignment);
867 self.stack_data_mut(app).has_visual_overflow |= overflowed;
868 }
869
870 child = self.child_after(app, current);
871 }
872 }
873
874 fn paint_stack(
880 self: RenderHandle<Self>,
881 app: &mut App,
882 context: &mut PaintingContext,
883 offset: Offset,
884 ) {
885 self.default_paint(app, context, offset);
886 }
887
888 fn paint(
890 self: RenderHandle<Self>,
891 app: &mut App,
892 context: &mut PaintingContext,
893 offset: Offset,
894 ) {
895 let clip_behavior = self.clip_behavior(app);
896 if clip_behavior != Clip::None && self.stack_data(app).has_visual_overflow {
897 let size = self.size(app);
898 let old = self.stack_data(app).clip_rect_layer.layer();
899 let layer = context.push_clip_rect(
900 app,
901 self.as_object().needs_compositing(app),
902 offset,
903 Offset::ZERO & size,
904 |app, context, offset| self.paint_stack(app, context, offset),
905 clip_behavior,
906 old,
907 );
908 LayerHandle::set_layer(
909 app,
910 |app| &mut self.stack_data_mut(app).clip_rect_layer,
911 layer,
912 );
913 } else {
914 LayerHandle::set_layer(
915 app,
916 |app| &mut self.stack_data_mut(app).clip_rect_layer,
917 None,
918 );
919 self.paint_stack(app, context, offset);
920 }
921 }
922
923 fn dispose(self: RenderHandle<Self>, app: &mut App) {
925 LayerHandle::set_layer(
926 app,
927 |app| &mut self.stack_data_mut(app).clip_rect_layer,
928 None,
929 );
930 crate::object::RenderObjectBase::dispose(self, app);
931 }
932}
933
934pub struct RenderStack {
936 render_object: RenderObjectData,
937 render_box: RenderBoxData,
938 container: ContainerRenderObjectData<AnyRenderBox>,
939 stack: RenderStackData,
940}
941
942impl RenderStack {
943 pub fn new(app: &mut App) -> RenderHandle<RenderStack> {
949 RenderHandle::new_box(
950 app,
951 RenderStack {
952 render_object: RenderObjectData::new(),
953 render_box: RenderBoxData::new(),
954 container: ContainerRenderObjectData::new(),
955 stack: RenderStackData::new(),
956 },
957 )
958 }
959}
960
961impl ContainerRenderObjectMixin for RenderStack {
962 type ChildType = AnyRenderBox;
963 type ParentDataType = StackParentData;
964
965 fn container_data(
966 self: RenderHandle<Self>,
967 app: &App,
968 ) -> &ContainerRenderObjectData<AnyRenderBox> {
969 &self.get(app).container
970 }
971
972 fn container_data_mut(
973 self: RenderHandle<Self>,
974 app: &mut App,
975 ) -> &mut ContainerRenderObjectData<AnyRenderBox> {
976 &mut self.get_mut(app).container
977 }
978}
979
980impl RenderBoxContainerDefaultsMixin for RenderStack {}
981
982impl RenderStackBase for RenderStack {
983 fn stack_data(self: RenderHandle<Self>, app: &App) -> &RenderStackData {
984 &self.get(app).stack
985 }
986
987 fn stack_data_mut(self: RenderHandle<Self>, app: &mut App) -> &mut RenderStackData {
988 &mut self.get_mut(app).stack
989 }
990}
991
992impl RenderObject for RenderStack {
993 crate::render_object_accessors!();
994
995 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
996 RenderStackBase::perform_layout(self, app)
997 }
998
999 fn visit_children(
1000 self: RenderHandle<Self>,
1001 app: &App,
1002 visitor: &mut dyn FnMut(AnyRenderObject),
1003 ) {
1004 ContainerRenderObjectMixin::visit_children(self, app, visitor)
1005 }
1006
1007 fn did_attach(self: RenderHandle<Self>, app: &mut App, owner: Handle<PipelineOwner>) {
1008 ContainerRenderObjectMixin::did_attach(self, app, owner)
1009 }
1010
1011 fn did_detach(self: RenderHandle<Self>, app: &mut App) {
1012 ContainerRenderObjectMixin::did_detach(self, app)
1013 }
1014
1015 fn redepth_children(self: RenderHandle<Self>, app: &mut App) {
1016 ContainerRenderObjectMixin::redepth_children(self, app)
1017 }
1018
1019 fn paint(
1020 self: RenderHandle<Self>,
1021 app: &mut App,
1022 context: &mut PaintingContext,
1023 offset: Offset,
1024 ) {
1025 RenderStackBase::paint(self, app, context, offset)
1026 }
1027
1028 fn dispose(self: RenderHandle<Self>, app: &mut App) {
1029 RenderStackBase::dispose(self, app)
1030 }
1031}
1032
1033impl RenderBox for RenderStack {
1034 crate::render_box_accessors!();
1035
1036 fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
1037 RenderStackBase::setup_parent_data(self, app, child)
1038 }
1039
1040 fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1041 RenderStackBase::compute_min_intrinsic_width(self, app, height)
1042 }
1043
1044 fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1045 RenderStackBase::compute_max_intrinsic_width(self, app, height)
1046 }
1047
1048 fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1049 RenderStackBase::compute_min_intrinsic_height(self, app, width)
1050 }
1051
1052 fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1053 RenderStackBase::compute_max_intrinsic_height(self, app, width)
1054 }
1055
1056 fn compute_distance_to_actual_baseline(
1057 self: RenderHandle<Self>,
1058 app: &mut App,
1059 baseline: TextBaseline,
1060 ) -> Option<f64> {
1061 RenderStackBase::compute_distance_to_actual_baseline(self, app, baseline)
1062 }
1063
1064 fn compute_dry_baseline(
1065 self: RenderHandle<Self>,
1066 app: &mut App,
1067 constraints: BoxConstraints,
1068 baseline: TextBaseline,
1069 ) -> Option<f64> {
1070 RenderStackBase::compute_dry_baseline(self, app, constraints, baseline)
1071 }
1072
1073 fn compute_dry_layout(
1074 self: RenderHandle<Self>,
1075 app: &mut App,
1076 constraints: BoxConstraints,
1077 ) -> Size {
1078 RenderStackBase::compute_dry_layout(self, app, constraints)
1079 }
1080
1081 fn hit_test_children(
1082 self: RenderHandle<Self>,
1083 app: &mut App,
1084 result: &mut BoxHitTestResult<'_>,
1085 position: Offset,
1086 ) -> bool {
1087 self.default_hit_test_children(app, result, position)
1088 }
1089}
1090
1091pub struct RenderIndexedStack {
1097 render_object: RenderObjectData,
1098 render_box: RenderBoxData,
1099 container: ContainerRenderObjectData<AnyRenderBox>,
1100 stack: RenderStackData,
1101 index: Option<i32>,
1102}
1103
1104impl RenderIndexedStack {
1105 pub fn new(app: &mut App) -> RenderHandle<RenderIndexedStack> {
1109 RenderHandle::new_box(
1110 app,
1111 RenderIndexedStack {
1112 render_object: RenderObjectData::new(),
1113 render_box: RenderBoxData::new(),
1114 container: ContainerRenderObjectData::new(),
1115 stack: RenderStackData::new(),
1116 index: Some(0),
1117 },
1118 )
1119 }
1120
1121 pub fn index(self: RenderHandle<Self>, app: &App) -> Option<i32> {
1123 self.get(app).index
1124 }
1125
1126 pub fn set_index(self: RenderHandle<Self>, app: &mut App, value: Option<i32>) {
1128 if self.get(app).index != value {
1129 self.get_mut(app).index = value;
1130 self.mark_needs_layout(app);
1131 }
1132 }
1133
1134 fn child_at_index(self: RenderHandle<Self>, app: &App) -> Option<AnyRenderBox> {
1135 let index = self.index(app)?;
1136 let mut child = self.first_child(app);
1137 let mut i = 0;
1138 while i < index
1139 && let Some(current) = child
1140 {
1141 child = self.child_after(app, current);
1142 i += 1;
1143 }
1144 debug_assert!(self.first_child(app).is_none() || child.is_some());
1145 child
1146 }
1147}
1148
1149impl ContainerRenderObjectMixin for RenderIndexedStack {
1150 type ChildType = AnyRenderBox;
1151 type ParentDataType = StackParentData;
1152
1153 fn container_data(
1154 self: RenderHandle<Self>,
1155 app: &App,
1156 ) -> &ContainerRenderObjectData<AnyRenderBox> {
1157 &self.get(app).container
1158 }
1159
1160 fn container_data_mut(
1161 self: RenderHandle<Self>,
1162 app: &mut App,
1163 ) -> &mut ContainerRenderObjectData<AnyRenderBox> {
1164 &mut self.get_mut(app).container
1165 }
1166}
1167
1168impl RenderBoxContainerDefaultsMixin for RenderIndexedStack {}
1169
1170impl RenderStackBase for RenderIndexedStack {
1171 fn stack_data(self: RenderHandle<Self>, app: &App) -> &RenderStackData {
1172 &self.get(app).stack
1173 }
1174
1175 fn stack_data_mut(self: RenderHandle<Self>, app: &mut App) -> &mut RenderStackData {
1176 &mut self.get_mut(app).stack
1177 }
1178
1179 fn paint_stack(
1180 self: RenderHandle<Self>,
1181 app: &mut App,
1182 context: &mut PaintingContext,
1183 offset: Offset,
1184 ) {
1185 let Some(displayed_child) = self.child_at_index(app) else {
1186 return;
1187 };
1188 let child_offset = displayed_child
1189 .as_object()
1190 .parent_data_of::<StackParentData>(app)
1191 .offset();
1192 context.paint_child(app, displayed_child.as_object(), child_offset + offset);
1193 }
1194}
1195
1196impl RenderObject for RenderIndexedStack {
1197 crate::render_object_accessors!();
1198
1199 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
1200 RenderStackBase::perform_layout(self, app)
1201 }
1202
1203 fn visit_children(
1204 self: RenderHandle<Self>,
1205 app: &App,
1206 visitor: &mut dyn FnMut(AnyRenderObject),
1207 ) {
1208 ContainerRenderObjectMixin::visit_children(self, app, visitor)
1209 }
1210
1211 fn did_attach(self: RenderHandle<Self>, app: &mut App, owner: Handle<PipelineOwner>) {
1212 ContainerRenderObjectMixin::did_attach(self, app, owner)
1213 }
1214
1215 fn did_detach(self: RenderHandle<Self>, app: &mut App) {
1216 ContainerRenderObjectMixin::did_detach(self, app)
1217 }
1218
1219 fn redepth_children(self: RenderHandle<Self>, app: &mut App) {
1220 ContainerRenderObjectMixin::redepth_children(self, app)
1221 }
1222
1223 fn paint(
1224 self: RenderHandle<Self>,
1225 app: &mut App,
1226 context: &mut PaintingContext,
1227 offset: Offset,
1228 ) {
1229 RenderStackBase::paint(self, app, context, offset)
1230 }
1231
1232 fn dispose(self: RenderHandle<Self>, app: &mut App) {
1233 RenderStackBase::dispose(self, app)
1234 }
1235}
1236
1237impl RenderBox for RenderIndexedStack {
1238 crate::render_box_accessors!();
1239
1240 fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
1241 RenderStackBase::setup_parent_data(self, app, child)
1242 }
1243
1244 fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1245 RenderStackBase::compute_min_intrinsic_width(self, app, height)
1246 }
1247
1248 fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1249 RenderStackBase::compute_max_intrinsic_width(self, app, height)
1250 }
1251
1252 fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1253 RenderStackBase::compute_min_intrinsic_height(self, app, width)
1254 }
1255
1256 fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1257 RenderStackBase::compute_max_intrinsic_height(self, app, width)
1258 }
1259
1260 fn compute_distance_to_actual_baseline(
1262 self: RenderHandle<Self>,
1263 app: &mut App,
1264 baseline: TextBaseline,
1265 ) -> Option<f64> {
1266 let displayed_child = self.child_at_index(app)?;
1267 let child_offset = displayed_child
1268 .as_object()
1269 .parent_data_of::<StackParentData>(app)
1270 .offset();
1271 let offset = BaselineOffset(displayed_child.get_distance_to_actual_baseline(app, baseline))
1272 + child_offset.dy();
1273 offset.offset()
1274 }
1275
1276 fn compute_dry_baseline(
1278 self: RenderHandle<Self>,
1279 app: &mut App,
1280 constraints: BoxConstraints,
1281 baseline: TextBaseline,
1282 ) -> Option<f64> {
1283 let displayed_child = self.child_at_index(app)?;
1284 let non_positioned_child_constraints = match self.fit(app) {
1285 StackFit::Loose => constraints.loosen(),
1286 StackFit::Expand => BoxConstraints::tight(constraints.biggest()),
1287 StackFit::Passthrough => constraints,
1288 };
1289
1290 let alignment = self.resolved_alignment(app);
1291 let size = self.as_box().get_dry_layout(app, constraints);
1292
1293 baseline_for_child(
1294 app,
1295 displayed_child,
1296 size,
1297 non_positioned_child_constraints,
1298 alignment,
1299 baseline,
1300 )
1301 }
1302
1303 fn compute_dry_layout(
1304 self: RenderHandle<Self>,
1305 app: &mut App,
1306 constraints: BoxConstraints,
1307 ) -> Size {
1308 RenderStackBase::compute_dry_layout(self, app, constraints)
1309 }
1310
1311 fn hit_test_children(
1312 self: RenderHandle<Self>,
1313 app: &mut App,
1314 result: &mut BoxHitTestResult<'_>,
1315 position: Offset,
1316 ) -> bool {
1317 let Some(displayed_child) = self.child_at_index(app) else {
1318 return false;
1319 };
1320 let child_offset = displayed_child
1321 .as_object()
1322 .parent_data_of::<StackParentData>(app)
1323 .offset();
1324 result.add_with_paint_offset(Some(child_offset), position, |result, transformed| {
1325 debug_assert_eq!(transformed, position - child_offset);
1326 displayed_child.hit_test(app, result, transformed)
1327 })
1328 }
1329}
1330
1331#[cfg(test)]
1332mod tests {
1333 use inset_foundation::AppCell;
1334 use std::cell::Cell;
1335 use std::rc::Rc;
1336
1337 use super::*;
1338 use crate::flex::{MainAxisSize, RenderFlex};
1339 use crate::layer::{ContainerLayer, ErasedLayer, OffsetLayer, PictureLayer};
1340 use crate::pipeline_owner::PipelineOwner;
1341 use crate::proxy_box::{RenderConstrainedBox, RenderRepaintBoundary};
1342 use crate::shifted_box::RenderPadding;
1343 use inset_embedder::valo::Op;
1344 use inset_painting::EdgeInsetsGeometry;
1345
1346 struct SizedTestBox {
1348 render_object: RenderObjectData,
1349 render_box: RenderBoxData,
1350 preferred_size: Size,
1351 paints: Rc<Cell<u32>>,
1352 }
1353
1354 impl RenderObject for SizedTestBox {
1355 crate::render_object_accessors!();
1356
1357 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
1358 let preferred_size = self.get(app).preferred_size;
1359 let size = self.constraints(app).constrain(preferred_size);
1360 self.set_size(app, size);
1361 }
1362
1363 fn paint(
1364 self: RenderHandle<Self>,
1365 app: &mut App,
1366 _context: &mut PaintingContext,
1367 _offset: Offset,
1368 ) {
1369 let paints = self.get(app).paints.clone();
1370 paints.set(paints.get() + 1);
1371 }
1372 }
1373
1374 impl RenderBox for SizedTestBox {
1375 crate::render_box_accessors!();
1376 }
1377
1378 struct BaselineTestBox {
1381 render_object: RenderObjectData,
1382 render_box: RenderBoxData,
1383 preferred_size: Size,
1384 baseline: f64,
1385 }
1386
1387 impl RenderObject for BaselineTestBox {
1388 crate::render_object_accessors!();
1389
1390 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
1391 let preferred_size = self.get(app).preferred_size;
1392 let size = self.constraints(app).constrain(preferred_size);
1393 self.set_size(app, size);
1394 }
1395 }
1396
1397 impl RenderBox for BaselineTestBox {
1398 crate::render_box_accessors!();
1399
1400 fn compute_dry_layout(
1401 self: RenderHandle<Self>,
1402 app: &mut App,
1403 constraints: BoxConstraints,
1404 ) -> Size {
1405 constraints.constrain(self.get(app).preferred_size)
1406 }
1407
1408 fn compute_distance_to_actual_baseline(
1409 self: RenderHandle<Self>,
1410 app: &mut App,
1411 _baseline: TextBaseline,
1412 ) -> Option<f64> {
1413 Some(self.get(app).baseline)
1414 }
1415
1416 fn compute_dry_baseline(
1417 self: RenderHandle<Self>,
1418 app: &mut App,
1419 _constraints: BoxConstraints,
1420 _baseline: TextBaseline,
1421 ) -> Option<f64> {
1422 Some(self.get(app).baseline)
1423 }
1424 }
1425
1426 fn baseline_box(app: &mut App, size: Size, baseline: f64) -> AnyRenderBox {
1427 RenderHandle::new_box(
1428 app,
1429 BaselineTestBox {
1430 render_object: RenderObjectData::new(),
1431 render_box: RenderBoxData::new(),
1432 preferred_size: size,
1433 baseline,
1434 },
1435 )
1436 .as_box()
1437 }
1438
1439 fn dry_and_actual_baseline(
1441 app: &mut App,
1442 stack: AnyRenderBox,
1443 constraints: BoxConstraints,
1444 ) -> (Option<f64>, Option<f64>) {
1445 let dry = stack.get_dry_baseline(app, constraints, TextBaseline::Alphabetic);
1446 stack.layout(app, constraints, true);
1447 let actual = stack.get_distance_to_baseline(app, TextBaseline::Alphabetic, true);
1448 (dry, actual)
1449 }
1450
1451 fn sized_box(app: &mut App, size: Size) -> (AnyRenderBox, Rc<Cell<u32>>) {
1452 let paints = Rc::new(Cell::new(0));
1453 let box_ = RenderHandle::new_box(
1454 app,
1455 SizedTestBox {
1456 render_object: RenderObjectData::new(),
1457 render_box: RenderBoxData::new(),
1458 preferred_size: size,
1459 paints: paints.clone(),
1460 },
1461 )
1462 .as_box();
1463 (box_, paints)
1464 }
1465
1466 fn schedule_root_paint(app: &mut App, node: AnyRenderObject) {
1467 let root = OffsetLayer::new(app, Offset::ZERO);
1468 root.as_layer().attach(app, node.id());
1469 node.schedule_initial_paint(app, root.as_container_layer());
1470 }
1471
1472 fn first_frame(
1474 app: &mut App,
1475 root: AnyRenderBox,
1476 constraints: BoxConstraints,
1477 ) -> Handle<PipelineOwner> {
1478 let owner = PipelineOwner::new(app, None, None);
1479 owner.set_root_node(app, Some(root.as_object()));
1480 root.layout(app, constraints, false);
1481 schedule_root_paint(app, root.as_object());
1482 owner.flush_compositing_bits(app);
1483 owner.flush_paint(app);
1484 owner
1485 }
1486
1487 fn picture_has_clip(app: &App, boundary: AnyRenderObject) -> bool {
1488 let layer = boundary.debug_layer(app).expect("painted");
1489 layer
1490 .depth_first_iterate_children(app)
1491 .into_iter()
1492 .filter_map(|child| {
1493 app.handle::<PictureLayer>(child.id())
1494 .and_then(|picture| picture.picture(app).map(|p| p.ops().to_vec()))
1495 })
1496 .flatten()
1497 .any(|op| matches!(op, Op::ClipPath { .. }))
1498 }
1499
1500 #[test]
1505 fn stack_intrinsics_are_the_largest_non_positioned_child() {
1506 let cell = AppCell::new();
1507 let mut app = cell.borrow_mut();
1508 let first =
1509 RenderConstrainedBox::new(&mut app, BoxConstraints::tight(Size::new(30.0, 40.0)), None);
1510 let second =
1511 RenderConstrainedBox::new(&mut app, BoxConstraints::tight(Size::new(50.0, 20.0)), None);
1512 let positioned = RenderConstrainedBox::new(
1513 &mut app,
1514 BoxConstraints::tight(Size::new(200.0, 200.0)),
1515 None,
1516 );
1517 let stack = RenderStack::new(&mut app);
1518 stack.set_text_direction(&mut app, Some(TextDirection::Ltr));
1519 stack.add_all(
1520 &mut app,
1521 Some(vec![first.as_box(), second.as_box(), positioned.as_box()]),
1522 );
1523 positioned
1524 .as_box()
1525 .parent_data_of_mut::<StackParentData>(&mut app)
1526 .left = Some(0.0);
1527
1528 let box_ = stack.as_box();
1529 assert_eq!(box_.get_min_intrinsic_width(&mut app, 0.0), 50.0);
1530 assert_eq!(box_.get_max_intrinsic_width(&mut app, 0.0), 50.0);
1531 assert_eq!(box_.get_min_intrinsic_height(&mut app, 0.0), 40.0);
1532 assert_eq!(box_.get_max_intrinsic_height(&mut app, 0.0), 40.0);
1533 }
1534
1535 #[test]
1538 fn dry_layout_matches_layout_for_a_padding_in_a_flex_in_a_stack() {
1539 let cell = AppCell::new();
1540 let mut app = cell.borrow_mut();
1541 let leaf =
1542 RenderConstrainedBox::new(&mut app, BoxConstraints::tight(Size::new(40.0, 20.0)), None);
1543 let padding = RenderPadding::new(
1544 &mut app,
1545 EdgeInsetsGeometry::all(5.0),
1546 None,
1547 Some(leaf.as_box()),
1548 );
1549 let row = RenderFlex::new(&mut app);
1550 row.set_text_direction(&mut app, Some(TextDirection::Ltr));
1551 row.set_main_axis_size(&mut app, MainAxisSize::Min);
1552 row.add_all(&mut app, Some(vec![padding.as_box()]));
1553 let stack = RenderStack::new(&mut app);
1554 stack.set_text_direction(&mut app, Some(TextDirection::Ltr));
1555 stack.add_all(&mut app, Some(vec![row.as_box()]));
1556
1557 let constraints = BoxConstraints::new().max_width(300.0).max_height(300.0);
1558 let dry = stack.as_box().get_dry_layout(&mut app, constraints);
1559 stack.layout(&mut app, constraints, false);
1560 assert_eq!(dry, Size::new(50.0, 30.0));
1561 assert_eq!(dry, stack.size(&app));
1562 assert_eq!(row.size(&app), Size::new(50.0, 30.0));
1563 }
1564
1565 #[test]
1566 fn positioned_child_is_laid_out_by_its_rect() {
1567 let cell = AppCell::new();
1568 let mut app = cell.borrow_mut();
1569 let (child, _) = sized_box(&mut app, Size::new(10.0, 10.0));
1570 let stack = RenderStack::new(&mut app);
1571 stack.set_text_direction(&mut app, Some(TextDirection::Ltr));
1572 stack.add(&mut app, child);
1573 child
1574 .parent_data_of_mut::<StackParentData>(&mut app)
1575 .set_rect(RelativeRect::from_ltrb(10.0, 20.0, 30.0, 40.0));
1576
1577 stack.layout(
1578 &mut app,
1579 BoxConstraints::tight(Size::new(100.0, 100.0)),
1580 false,
1581 );
1582
1583 assert_eq!(stack.size(&app), Size::new(100.0, 100.0));
1585 assert_eq!(child.size(&app), Size::new(60.0, 40.0));
1586 assert_eq!(child.box_parent_data(&app).offset, Offset::new(10.0, 20.0));
1587 }
1588
1589 #[test]
1592 fn non_positioned_children_are_sized_by_the_fit() {
1593 let cases = [
1594 (
1595 StackFit::Loose,
1596 Size::new(20.0, 20.0),
1597 Size::new(50.0, 50.0),
1598 ),
1599 (
1600 StackFit::Expand,
1601 Size::new(100.0, 100.0),
1602 Size::new(100.0, 100.0),
1603 ),
1604 (
1605 StackFit::Passthrough,
1606 Size::new(50.0, 50.0),
1607 Size::new(50.0, 50.0),
1608 ),
1609 ];
1610 for (fit, child_size, stack_size) in cases {
1611 let cell = AppCell::new();
1612 let mut app = cell.borrow_mut();
1613 let (child, _) = sized_box(&mut app, Size::new(20.0, 20.0));
1614 let stack = RenderStack::new(&mut app);
1615 stack.set_text_direction(&mut app, Some(TextDirection::Ltr));
1616 stack.set_fit(&mut app, fit);
1617 stack.add(&mut app, child);
1618
1619 stack.layout(
1620 &mut app,
1621 BoxConstraints {
1622 min_width: 50.0,
1623 max_width: 100.0,
1624 min_height: 50.0,
1625 max_height: 100.0,
1626 },
1627 false,
1628 );
1629
1630 assert_eq!(child.size(&app), child_size, "{fit:?}");
1631 assert_eq!(stack.size(&app), stack_size, "{fit:?}");
1632 }
1633 }
1634
1635 #[test]
1638 fn indexed_stack_paints_one_child() {
1639 let cell = AppCell::new();
1640 let mut app = cell.borrow_mut();
1641 let (first, first_paints) = sized_box(&mut app, Size::new(20.0, 20.0));
1642 let (second, second_paints) = sized_box(&mut app, Size::new(30.0, 30.0));
1643 let stack = RenderIndexedStack::new(&mut app);
1644 stack.set_text_direction(&mut app, Some(TextDirection::Ltr));
1645 stack.add_all(&mut app, Some(vec![first, second]));
1646 stack.set_index(&mut app, Some(1));
1647 let root = RenderRepaintBoundary::new(&mut app, Some(stack.as_box()));
1648
1649 let owner = first_frame(
1650 &mut app,
1651 root.as_box(),
1652 BoxConstraints::tight(Size::new(100.0, 100.0)),
1653 );
1654
1655 assert_eq!(first.size(&app), Size::new(20.0, 20.0));
1657 assert_eq!(second.size(&app), Size::new(30.0, 30.0));
1658 assert_eq!(first_paints.get(), 0);
1659 assert_eq!(second_paints.get(), 1);
1660
1661 stack.set_index(&mut app, Some(0));
1662 owner.flush_layout(&mut app);
1663 owner.flush_compositing_bits(&mut app);
1664 owner.flush_paint(&mut app);
1665 assert_eq!(first_paints.get(), 1);
1666 assert_eq!(second_paints.get(), 1);
1667 }
1668
1669 #[test]
1671 fn overflowing_positioned_child_is_clipped() {
1672 for (clip_behavior, clips) in [(Clip::None, false), (Clip::HardEdge, true)] {
1673 let cell = AppCell::new();
1674 let mut app = cell.borrow_mut();
1675 let (child, _) = sized_box(&mut app, Size::new(10.0, 10.0));
1676 let stack = RenderStack::new(&mut app);
1677 stack.set_text_direction(&mut app, Some(TextDirection::Ltr));
1678 stack.set_clip_behavior(&mut app, clip_behavior);
1679 stack.add(&mut app, child);
1680 child.parent_data_of_mut::<StackParentData>(&mut app).left = Some(-10.0);
1681 let root = RenderRepaintBoundary::new(&mut app, Some(stack.as_box()));
1682
1683 let owner = first_frame(
1684 &mut app,
1685 root.as_box(),
1686 BoxConstraints::tight(Size::new(100.0, 100.0)),
1687 );
1688
1689 let clipped = picture_has_clip(&app, root.as_object());
1690 assert_eq!(clipped, clips, "{clip_behavior:?}");
1691 let _ = owner;
1692 }
1693 }
1694
1695 #[test]
1698 fn a_stack_baseline_is_its_highest_child_baseline() {
1699 let cell = AppCell::new();
1700 let mut app = cell.borrow_mut();
1701 let short = baseline_box(&mut app, Size::new(40.0, 20.0), 15.0);
1702 let tall = baseline_box(&mut app, Size::new(40.0, 60.0), 50.0);
1703 let pinned = baseline_box(&mut app, Size::new(40.0, 20.0), 15.0);
1704 let stack = RenderStack::new(&mut app);
1705 stack.set_text_direction(&mut app, Some(TextDirection::Ltr));
1706 stack.set_alignment(&mut app, AlignmentGeometry::from(Alignment::CENTER));
1707 stack.add_all(&mut app, Some(vec![short, tall, pinned]));
1708 pinned
1709 .parent_data_of_mut::<StackParentData>(&mut app)
1710 .bottom = Some(0.0);
1711
1712 let constraints = BoxConstraints::new().max_width(100.0).max_height(100.0);
1713 assert_eq!(
1715 dry_and_actual_baseline(&mut app, stack.as_box(), constraints),
1716 (Some(35.0), Some(35.0))
1717 );
1718 }
1719
1720 #[test]
1722 fn an_indexed_stack_baseline_is_its_displayed_child_baseline() {
1723 let cell = AppCell::new();
1724 let mut app = cell.borrow_mut();
1725 let short = baseline_box(&mut app, Size::new(40.0, 20.0), 15.0);
1726 let tall = baseline_box(&mut app, Size::new(40.0, 60.0), 50.0);
1727 let stack = RenderIndexedStack::new(&mut app);
1728 stack.set_text_direction(&mut app, Some(TextDirection::Ltr));
1729 stack.set_alignment(&mut app, AlignmentGeometry::from(Alignment::CENTER));
1730 stack.add_all(&mut app, Some(vec![short, tall]));
1731 stack.set_index(&mut app, Some(0));
1732
1733 let constraints = BoxConstraints::new().max_width(100.0).max_height(100.0);
1734 assert_eq!(
1735 dry_and_actual_baseline(&mut app, stack.as_box(), constraints),
1736 (Some(35.0), Some(35.0))
1737 );
1738 }
1739}