1use std::any::{Any, TypeId};
7use std::fmt::{self, Debug};
8use std::ops::Add;
9
10use inset_embedder::{Clip, Offset, Size, TextBaseline, TextDirection};
11use inset_foundation::{App, Handle, PRECISION_ERROR_TOLERANCE};
12use inset_painting::{Axis, VerticalDirection};
13
14use crate::box_::{
15 AnyRenderBox, BoxConstraints, BoxHitTestResult, BoxParentData, ContainerBoxParentData,
16 DryLayoutFailure, RenderBox, RenderBoxContainerDefaultsMixin, RenderBoxData,
17};
18use crate::layer::{ClipRectLayer, LayerHandle};
19use crate::layout_helper::ChildLayoutHelper;
20use crate::object::{
21 AnyRenderObject, ContainerParentData, ContainerParentDataMixin, ContainerRenderObjectData,
22 ContainerRenderObjectMixin, ParentData, RenderHandle, RenderObject, RenderObjectData,
23};
24use crate::painting_context::PaintingContext;
25use crate::pipeline_owner::PipelineOwner;
26
27#[derive(Clone, Copy)]
30struct AxisSize(Size);
31
32impl AxisSize {
33 fn new(main_axis_extent: f64, cross_axis_extent: f64) -> AxisSize {
34 AxisSize(Size::new(main_axis_extent, cross_axis_extent))
35 }
36
37 fn from_size(size: Size, direction: Axis) -> AxisSize {
38 AxisSize(AxisSize::convert(size, direction))
39 }
40
41 fn convert(size: Size, direction: Axis) -> Size {
42 match direction {
43 Axis::Horizontal => size,
44 Axis::Vertical => size.flipped(),
45 }
46 }
47
48 fn main_axis_extent(self) -> f64 {
49 self.0.width()
50 }
51
52 fn cross_axis_extent(self) -> f64 {
53 self.0.height()
54 }
55
56 fn to_size(self, direction: Axis) -> Size {
57 AxisSize::convert(self.0, direction)
58 }
59
60 fn apply_constraints(self, constraints: BoxConstraints, direction: Axis) -> AxisSize {
61 let effective_constraints = match direction {
62 Axis::Horizontal => constraints,
63 Axis::Vertical => constraints.flipped(),
64 };
65 AxisSize(effective_constraints.constrain(self.0))
66 }
67}
68
69impl Add for AxisSize {
70 type Output = AxisSize;
71
72 fn add(self, other: AxisSize) -> AxisSize {
73 AxisSize(Size::new(
74 self.0.width() + other.0.width(),
75 self.0.height().max(other.0.height()),
76 ))
77 }
78}
79
80type ChildSizingFunction = fn(AnyRenderBox, &mut App, f64) -> f64;
83
84pub struct LayoutSizes {
86 axis_size: AxisSize,
88
89 main_axis_free_space: f64,
93}
94
95#[derive(Clone, Copy, Debug, PartialEq, Eq)]
101pub enum FlexFit {
102 Tight,
104
105 Loose,
107}
108
109#[derive(Debug)]
111pub struct FlexParentData {
112 box_parent_data: BoxParentData,
113 container_parent_data: ContainerParentData<AnyRenderBox>,
114
115 pub flex: Option<i32>,
122
123 pub fit: Option<FlexFit>,
130}
131
132impl FlexParentData {
133 pub const fn new() -> FlexParentData {
135 FlexParentData {
136 box_parent_data: BoxParentData::new(),
137 container_parent_data: ContainerParentData::new(),
138 flex: None,
139 fit: None,
140 }
141 }
142}
143
144impl Default for FlexParentData {
145 fn default() -> FlexParentData {
146 FlexParentData::new()
147 }
148}
149
150impl ParentData for FlexParentData {
151 fn detach(&mut self) {
152 ContainerParentDataMixin::detach(self);
153 }
154
155 fn provide(&self, id: TypeId) -> Option<&dyn Any> {
156 if id == TypeId::of::<FlexParentData>() {
157 return Some(self);
158 }
159 self.box_parent_data.provide(id)
160 }
161
162 fn provide_mut(&mut self, id: TypeId) -> Option<&mut dyn Any> {
163 if id == TypeId::of::<FlexParentData>() {
164 return Some(self);
165 }
166 self.box_parent_data.provide_mut(id)
167 }
168}
169
170impl ContainerParentDataMixin for FlexParentData {
171 type ChildType = AnyRenderBox;
172
173 fn container_parent_data(&self) -> &ContainerParentData<AnyRenderBox> {
174 &self.container_parent_data
175 }
176
177 fn container_parent_data_mut(&mut self) -> &mut ContainerParentData<AnyRenderBox> {
178 &mut self.container_parent_data
179 }
180}
181
182impl ContainerBoxParentData for FlexParentData {
183 fn box_parent_data(&self) -> &BoxParentData {
184 &self.box_parent_data
185 }
186
187 fn box_parent_data_mut(&mut self) -> &mut BoxParentData {
188 &mut self.box_parent_data
189 }
190}
191
192impl fmt::Display for FlexParentData {
193 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194 write!(
195 f,
196 "{}; flex={:?}; fit={:?}",
197 self.box_parent_data, self.flex, self.fit
198 )
199 }
200}
201
202#[derive(Clone, Copy, Debug, PartialEq, Eq)]
213pub enum MainAxisSize {
214 Min,
225
226 Max,
235}
236
237#[derive(Clone, Copy, Debug, PartialEq, Eq)]
243pub enum MainAxisAlignment {
244 Start,
252
253 End,
261
262 Center,
264
265 SpaceBetween,
267
268 SpaceAround,
271
272 SpaceEvenly,
275}
276
277impl MainAxisAlignment {
278 fn distribute_space(
279 self,
280 free_space: f64,
281 item_count: usize,
282 flipped: bool,
283 spacing: f64,
284 ) -> (f64, f64) {
285 let items = item_count as f64;
286 match self {
287 MainAxisAlignment::Start => {
288 if flipped {
289 (free_space, spacing)
290 } else {
291 (0.0, spacing)
292 }
293 }
294 MainAxisAlignment::End => {
295 MainAxisAlignment::Start.distribute_space(free_space, item_count, !flipped, spacing)
296 }
297 MainAxisAlignment::SpaceBetween if item_count < 2 => {
298 MainAxisAlignment::Start.distribute_space(free_space, item_count, flipped, spacing)
299 }
300 MainAxisAlignment::SpaceAround if item_count == 0 => {
301 MainAxisAlignment::Start.distribute_space(free_space, item_count, flipped, spacing)
302 }
303 MainAxisAlignment::Center => (free_space / 2.0, spacing),
304 MainAxisAlignment::SpaceBetween => (0.0, free_space / (items - 1.0) + spacing),
305 MainAxisAlignment::SpaceAround => {
306 (free_space / items / 2.0, free_space / items + spacing)
307 }
308 MainAxisAlignment::SpaceEvenly => (
309 free_space / (items + 1.0),
310 free_space / (items + 1.0) + spacing,
311 ),
312 }
313 }
314}
315
316#[derive(Clone, Copy, Debug, PartialEq, Eq)]
322pub enum CrossAxisAlignment {
323 Start,
335
336 End,
348
349 Center,
353
354 Stretch,
358
359 Baseline,
371}
372
373impl CrossAxisAlignment {
374 fn get_child_cross_axis_offset(self, free_space: f64, flipped: bool) -> f64 {
376 match self {
377 CrossAxisAlignment::Stretch | CrossAxisAlignment::Baseline => 0.0,
378 CrossAxisAlignment::Start => {
379 if flipped {
380 free_space
381 } else {
382 0.0
383 }
384 }
385 CrossAxisAlignment::Center => free_space / 2.0,
386 CrossAxisAlignment::End => {
387 CrossAxisAlignment::Start.get_child_cross_axis_offset(free_space, !flipped)
388 }
389 }
390 }
391}
392
393pub struct RenderFlexData {
398 direction: Axis,
399 main_axis_alignment: MainAxisAlignment,
400 main_axis_size: MainAxisSize,
401 cross_axis_alignment: CrossAxisAlignment,
402 text_direction: Option<TextDirection>,
403 vertical_direction: VerticalDirection,
404 text_baseline: Option<TextBaseline>,
405 clip_behavior: Clip,
406 spacing: f64,
407 overflow: f64,
409 clip_rect_layer: LayerHandle<Handle<ClipRectLayer>>,
410}
411
412impl RenderFlexData {
413 pub fn new() -> RenderFlexData {
418 RenderFlexData {
419 direction: Axis::Horizontal,
420 main_axis_alignment: MainAxisAlignment::Start,
421 main_axis_size: MainAxisSize::Max,
422 cross_axis_alignment: CrossAxisAlignment::Center,
423 text_direction: None,
424 vertical_direction: VerticalDirection::Down,
425 text_baseline: None,
426 clip_behavior: Clip::None,
427 spacing: 0.0,
428 overflow: 0.0,
429 clip_rect_layer: LayerHandle::new(),
430 }
431 }
432}
433
434impl Default for RenderFlexData {
435 fn default() -> RenderFlexData {
436 RenderFlexData::new()
437 }
438}
439
440impl RenderFlexData {
441 pub fn direction(mut self, direction: Axis) -> RenderFlexData {
443 self.direction = direction;
444 self
445 }
446
447 pub fn main_axis_alignment(mut self, main_axis_alignment: MainAxisAlignment) -> RenderFlexData {
449 self.main_axis_alignment = main_axis_alignment;
450 self
451 }
452
453 pub fn main_axis_size(mut self, main_axis_size: MainAxisSize) -> RenderFlexData {
455 self.main_axis_size = main_axis_size;
456 self
457 }
458
459 pub fn cross_axis_alignment(
461 mut self,
462 cross_axis_alignment: CrossAxisAlignment,
463 ) -> RenderFlexData {
464 self.cross_axis_alignment = cross_axis_alignment;
465 self
466 }
467
468 pub fn text_direction(mut self, text_direction: Option<TextDirection>) -> RenderFlexData {
470 self.text_direction = text_direction;
471 self
472 }
473
474 pub fn vertical_direction(mut self, vertical_direction: VerticalDirection) -> RenderFlexData {
476 self.vertical_direction = vertical_direction;
477 self
478 }
479
480 pub fn text_baseline(mut self, text_baseline: Option<TextBaseline>) -> RenderFlexData {
482 self.text_baseline = text_baseline;
483 self
484 }
485
486 pub fn clip_behavior(mut self, clip_behavior: Clip) -> RenderFlexData {
488 self.clip_behavior = clip_behavior;
489 self
490 }
491
492 pub fn spacing(mut self, spacing: f64) -> RenderFlexData {
494 self.spacing = spacing;
495 self
496 }
497}
498
499pub trait RenderFlexMixin:
507 RenderBox
508 + ContainerRenderObjectMixin<ChildType = AnyRenderBox, ParentDataType = FlexParentData>
509 + RenderBoxContainerDefaultsMixin
510{
511 fn flex_data(self: RenderHandle<Self>, app: &App) -> &RenderFlexData;
513
514 fn flex_data_mut(self: RenderHandle<Self>, app: &mut App) -> &mut RenderFlexData;
516
517 fn direction(self: RenderHandle<Self>, app: &App) -> Axis {
519 self.flex_data(app).direction
520 }
521
522 fn set_direction(self: RenderHandle<Self>, app: &mut App, value: Axis) {
524 if self.flex_data(app).direction != value {
525 self.flex_data_mut(app).direction = value;
526 self.mark_needs_layout(app);
527 }
528 }
529
530 fn main_axis_alignment(self: RenderHandle<Self>, app: &App) -> MainAxisAlignment {
536 self.flex_data(app).main_axis_alignment
537 }
538
539 fn set_main_axis_alignment(self: RenderHandle<Self>, app: &mut App, value: MainAxisAlignment) {
541 if self.flex_data(app).main_axis_alignment != value {
542 self.flex_data_mut(app).main_axis_alignment = value;
543 self.mark_needs_layout(app);
544 }
545 }
546
547 fn main_axis_size(self: RenderHandle<Self>, app: &App) -> MainAxisSize {
553 self.flex_data(app).main_axis_size
554 }
555
556 fn set_main_axis_size(self: RenderHandle<Self>, app: &mut App, value: MainAxisSize) {
558 if self.flex_data(app).main_axis_size != value {
559 self.flex_data_mut(app).main_axis_size = value;
560 self.mark_needs_layout(app);
561 }
562 }
563
564 fn cross_axis_alignment(self: RenderHandle<Self>, app: &App) -> CrossAxisAlignment {
570 self.flex_data(app).cross_axis_alignment
571 }
572
573 fn set_cross_axis_alignment(
575 self: RenderHandle<Self>,
576 app: &mut App,
577 value: CrossAxisAlignment,
578 ) {
579 if self.flex_data(app).cross_axis_alignment != value {
580 self.flex_data_mut(app).cross_axis_alignment = value;
581 self.mark_needs_layout(app);
582 }
583 }
584
585 fn text_direction(self: RenderHandle<Self>, app: &App) -> Option<TextDirection> {
597 self.flex_data(app).text_direction
598 }
599
600 fn set_text_direction(self: RenderHandle<Self>, app: &mut App, value: Option<TextDirection>) {
602 if self.flex_data(app).text_direction != value {
603 self.flex_data_mut(app).text_direction = value;
604 self.mark_needs_layout(app);
605 }
606 }
607
608 fn vertical_direction(self: RenderHandle<Self>, app: &App) -> VerticalDirection {
620 self.flex_data(app).vertical_direction
621 }
622
623 fn set_vertical_direction(self: RenderHandle<Self>, app: &mut App, value: VerticalDirection) {
625 if self.flex_data(app).vertical_direction != value {
626 self.flex_data_mut(app).vertical_direction = value;
627 self.mark_needs_layout(app);
628 }
629 }
630
631 fn text_baseline(self: RenderHandle<Self>, app: &App) -> Option<TextBaseline> {
636 self.flex_data(app).text_baseline
637 }
638
639 fn set_text_baseline(self: RenderHandle<Self>, app: &mut App, value: Option<TextBaseline>) {
641 debug_assert!(
642 self.flex_data(app).cross_axis_alignment != CrossAxisAlignment::Baseline
643 || value.is_some()
644 );
645 if self.flex_data(app).text_baseline != value {
646 self.flex_data_mut(app).text_baseline = value;
647 self.mark_needs_layout(app);
648 }
649 }
650
651 fn has_overflow(self: RenderHandle<Self>, app: &App) -> bool {
656 self.flex_data(app).overflow > PRECISION_ERROR_TOLERANCE
657 }
658
659 fn clip_behavior(self: RenderHandle<Self>, app: &App) -> Clip {
663 self.flex_data(app).clip_behavior
664 }
665
666 fn set_clip_behavior(self: RenderHandle<Self>, app: &mut App, value: Clip) {
668 if value != self.flex_data(app).clip_behavior {
669 self.flex_data_mut(app).clip_behavior = value;
670 self.mark_needs_paint(app);
671 }
672 }
673
674 fn spacing(self: RenderHandle<Self>, app: &App) -> f64 {
683 self.flex_data(app).spacing
684 }
685
686 fn set_spacing(self: RenderHandle<Self>, app: &mut App, value: f64) {
688 debug_assert!(value >= 0.0);
689 if self.flex_data(app).spacing == value {
690 return;
691 }
692 self.flex_data_mut(app).spacing = value;
693 self.mark_needs_layout(app);
694 }
695
696 fn debug_has_necessary_directions(self: RenderHandle<Self>, app: &App) -> bool {
699 let direction = self.direction(app);
700 if self.first_child(app).is_some() && self.last_child(app) != self.first_child(app) {
701 if direction == Axis::Horizontal {
703 debug_assert!(
704 self.text_direction(app).is_some(),
705 "Horizontal RenderFlex with multiple children has a null textDirection, so \
706 the layout order is undefined."
707 );
708 }
709 }
710 let main_axis_alignment = self.main_axis_alignment(app);
711 if (main_axis_alignment == MainAxisAlignment::Start
712 || main_axis_alignment == MainAxisAlignment::End)
713 && direction == Axis::Horizontal
714 {
715 debug_assert!(
716 self.text_direction(app).is_some(),
717 "Horizontal RenderFlex with {main_axis_alignment:?} has a null textDirection, so \
718 the alignment cannot be resolved."
719 );
720 }
721 let cross_axis_alignment = self.cross_axis_alignment(app);
722 if (cross_axis_alignment == CrossAxisAlignment::Start
723 || cross_axis_alignment == CrossAxisAlignment::End)
724 && direction == Axis::Vertical
725 {
726 debug_assert!(
727 self.text_direction(app).is_some(),
728 "Vertical RenderFlex with {cross_axis_alignment:?} has a null textDirection, so \
729 the alignment cannot be resolved."
730 );
731 }
732 true
733 }
734
735 fn get_cross_size(self: RenderHandle<Self>, app: &App, size: Size) -> f64 {
737 match self.direction(app) {
738 Axis::Horizontal => size.height(),
739 Axis::Vertical => size.width(),
740 }
741 }
742
743 fn get_main_size(self: RenderHandle<Self>, app: &App, size: Size) -> f64 {
745 match self.direction(app) {
746 Axis::Horizontal => size.width(),
747 Axis::Vertical => size.height(),
748 }
749 }
750
751 fn flip_main_axis(self: RenderHandle<Self>, app: &App) -> bool {
755 self.first_child(app).is_some()
756 && match self.direction(app) {
757 Axis::Horizontal => self.text_direction(app) == Some(TextDirection::Rtl),
758 Axis::Vertical => self.vertical_direction(app) == VerticalDirection::Up,
759 }
760 }
761
762 fn flip_cross_axis(self: RenderHandle<Self>, app: &App) -> bool {
765 self.first_child(app).is_some()
766 && match self.direction(app) {
767 Axis::Vertical => self.text_direction(app) == Some(TextDirection::Rtl),
768 Axis::Horizontal => self.vertical_direction(app) == VerticalDirection::Up,
769 }
770 }
771
772 fn constraints_for_non_flex_child(
774 self: RenderHandle<Self>,
775 app: &App,
776 constraints: BoxConstraints,
777 ) -> BoxConstraints {
778 let fill_cross_axis = self.cross_axis_alignment(app) == CrossAxisAlignment::Stretch;
779 match self.direction(app) {
780 Axis::Horizontal => {
781 if fill_cross_axis {
782 BoxConstraints::tight_for(None, Some(constraints.max_height))
783 } else {
784 BoxConstraints::new().max_height(constraints.max_height)
785 }
786 }
787 Axis::Vertical => {
788 if fill_cross_axis {
789 BoxConstraints::tight_for(Some(constraints.max_width), None)
790 } else {
791 BoxConstraints::new().max_width(constraints.max_width)
792 }
793 }
794 }
795 }
796
797 fn constraints_for_flex_child(
800 self: RenderHandle<Self>,
801 app: &App,
802 child: AnyRenderBox,
803 constraints: BoxConstraints,
804 max_child_extent: f64,
805 ) -> BoxConstraints {
806 debug_assert!(get_flex(app, child) > 0);
807 debug_assert!(max_child_extent >= 0.0);
808 let min_child_extent = match get_fit(app, child) {
809 FlexFit::Tight => max_child_extent,
810 FlexFit::Loose => 0.0,
811 };
812 let fill_cross_axis = self.cross_axis_alignment(app) == CrossAxisAlignment::Stretch;
813 match self.direction(app) {
814 Axis::Horizontal => BoxConstraints {
815 min_width: min_child_extent,
816 max_width: max_child_extent,
817 min_height: if fill_cross_axis {
818 constraints.max_height
819 } else {
820 0.0
821 },
822 max_height: constraints.max_height,
823 },
824 Axis::Vertical => BoxConstraints {
825 min_width: if fill_cross_axis {
826 constraints.max_width
827 } else {
828 0.0
829 },
830 max_width: constraints.max_width,
831 min_height: min_child_extent,
832 max_height: max_child_extent,
833 },
834 }
835 }
836
837 fn get_intrinsic_size(
841 self: RenderHandle<Self>,
842 app: &mut App,
843 sizing_direction: Axis,
844 extent: f64,
845 child_size: ChildSizingFunction,
846 ) -> f64 {
847 let direction = self.direction(app);
848 if direction == sizing_direction {
849 let mut total_flex = 0.0;
853 let mut inflexible_space = self.spacing(app) * (self.child_count(app) as f64 - 1.0);
854 let mut max_flex_fraction_so_far: f64 = 0.0;
855 let mut child = self.first_child(app);
856 while let Some(current) = child {
857 let flex = get_flex(app, current);
858 total_flex += f64::from(flex);
859 if flex > 0 {
860 let flex_fraction = child_size(current, app, extent) / f64::from(flex);
861 max_flex_fraction_so_far = max_flex_fraction_so_far.max(flex_fraction);
862 } else {
863 inflexible_space += child_size(current, app, extent);
864 }
865 child = self.child_after(app, current);
866 }
867 return max_flex_fraction_so_far * total_flex + inflexible_space;
868 }
869
870 let is_horizontal = direction == Axis::Horizontal;
875 let layout_child = |app: &mut App, child: AnyRenderBox, constraints: BoxConstraints| {
876 let main_axis_size_from_constraints = if is_horizontal {
877 constraints.max_width
878 } else {
879 constraints.max_height
880 };
881 debug_assert_eq!(
884 get_flex(app, child) != 0 && extent.is_finite(),
885 main_axis_size_from_constraints.is_finite()
886 );
887 let max_main_axis_size = if main_axis_size_from_constraints.is_finite() {
888 main_axis_size_from_constraints
889 } else if is_horizontal {
890 child.get_max_intrinsic_width(app, f64::INFINITY)
891 } else {
892 child.get_max_intrinsic_height(app, f64::INFINITY)
893 };
894 let cross_size = child_size(child, app, max_main_axis_size);
895 if is_horizontal {
896 Size::new(max_main_axis_size, cross_size)
897 } else {
898 Size::new(cross_size, max_main_axis_size)
899 }
900 };
901 let constraints = if is_horizontal {
902 BoxConstraints::new().max_width(extent)
903 } else {
904 BoxConstraints::new().max_height(extent)
905 };
906 self.compute_sizes(app, constraints, layout_child)
907 .axis_size
908 .cross_axis_extent()
909 }
910
911 fn debug_check_constraints(
915 self: RenderHandle<Self>,
916 app: &App,
917 constraints: BoxConstraints,
918 ) -> Option<String> {
919 let direction = self.direction(app);
920 let max_main_size = match direction {
921 Axis::Horizontal => constraints.max_width,
922 Axis::Vertical => constraints.max_height,
923 };
924 let can_flex = max_main_size < f64::INFINITY;
925 let mut child = self.first_child(app);
926 while let Some(current) = child {
927 if get_flex(app, current) > 0
928 && !can_flex
929 && (self.main_axis_size(app) == MainAxisSize::Max
930 || get_fit(app, current) == FlexFit::Tight)
931 {
932 let (identity, axis, dimension) = match direction {
933 Axis::Horizontal => ("row", "horizontal", "width"),
934 Axis::Vertical => ("column", "vertical", "height"),
935 };
936 return Some(format!(
937 "RenderFlex children have non-zero flex but incoming {dimension} constraints \
938 are unbounded.\n\
939 When a {identity} is in a parent that does not provide a finite {dimension} \
940 constraint, for example if it is in a {axis} scrollable, it will try to \
941 shrink-wrap its children along the {axis} axis. Setting a flex on a child \
942 (e.g. using Expanded) indicates that the child is to expand to fill the \
943 remaining space in the {axis} direction.\n\
944 These two directives are mutually exclusive. If a parent is to shrink-wrap \
945 its child, the child cannot simultaneously expand to fit its parent.\n\
946 Consider setting mainAxisSize to MainAxisSize.min and using FlexFit.loose \
947 fits for the flexible children (using Flexible rather than Expanded). This \
948 will allow the flexible children to size themselves to less than the \
949 infinite remaining space they would otherwise be forced to take, and then \
950 will cause the RenderFlex to shrink-wrap the children rather than expanding \
951 to fit the maximum constraints provided by the parent."
952 ));
953 }
954 child = self.child_after(app, current);
955 }
956 None
957 }
958
959 fn compute_sizes(
962 self: RenderHandle<Self>,
963 app: &mut App,
964 constraints: BoxConstraints,
965 mut layout_child: impl FnMut(&mut App, AnyRenderBox, BoxConstraints) -> Size,
966 ) -> LayoutSizes {
967 debug_assert!(self.debug_has_necessary_directions(app));
968
969 let direction = self.direction(app);
971 let max_main_size = self.get_main_size(app, constraints.biggest());
972 let can_flex = max_main_size.is_finite();
973 let non_flex_child_constraints = self.constraints_for_non_flex_child(app, constraints);
974
975 let mut total_flex = 0;
977 let mut first_flex_child: Option<AnyRenderBox> = None;
978 let mut accumulated_size = AxisSize::new(
980 self.spacing(app) * (self.child_count(app) as f64 - 1.0),
981 0.0,
982 );
983 let mut child = self.first_child(app);
984 while let Some(current) = child {
985 let flex = get_flex(app, current);
986 if can_flex && flex > 0 {
987 total_flex += flex;
988 first_flex_child.get_or_insert(current);
989 } else {
990 let child_size = layout_child(app, current, non_flex_child_constraints);
991 accumulated_size = accumulated_size + AxisSize::from_size(child_size, direction);
992 }
993 child = self.child_after(app, current);
994 }
995
996 debug_assert_eq!(total_flex == 0, first_flex_child.is_none());
997 debug_assert!(first_flex_child.is_none() || can_flex);
999
1000 let flex_space = (max_main_size - accumulated_size.main_axis_extent()).max(0.0);
1002 let space_per_flex = flex_space / total_flex as f64;
1003 let mut child = first_flex_child;
1004 while let Some(current) = child {
1005 if total_flex == 0 {
1006 break;
1007 }
1008 let flex = get_flex(app, current);
1009 if flex != 0 {
1010 total_flex -= flex;
1011 debug_assert!(space_per_flex.is_finite());
1012 let max_child_extent = space_per_flex * f64::from(flex);
1013 debug_assert!(
1014 get_fit(app, current) == FlexFit::Loose || max_child_extent < f64::INFINITY
1015 );
1016 let child_constraints =
1017 self.constraints_for_flex_child(app, current, constraints, max_child_extent);
1018 let child_size = layout_child(app, current, child_constraints);
1019 accumulated_size = accumulated_size + AxisSize::from_size(child_size, direction);
1020 }
1021 child = self.child_after(app, current);
1022 }
1023 debug_assert_eq!(total_flex, 0);
1024
1025 let ideal_main_size = match self.main_axis_size(app) {
1026 MainAxisSize::Max if max_main_size.is_finite() => max_main_size,
1027 MainAxisSize::Max | MainAxisSize::Min => accumulated_size.main_axis_extent(),
1028 };
1029
1030 let constrained_size = AxisSize::new(ideal_main_size, accumulated_size.cross_axis_extent())
1031 .apply_constraints(constraints, direction);
1032 LayoutSizes {
1033 axis_size: constrained_size,
1034 main_axis_free_space: constrained_size.main_axis_extent()
1035 - accumulated_size.main_axis_extent(),
1036 }
1037 }
1038
1039 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
1041 let constraints = self.constraints(app);
1042 if cfg!(debug_assertions)
1043 && let Some(error) = self.debug_check_constraints(app, constraints)
1044 {
1045 panic!("{error}");
1046 }
1047
1048 let sizes = self.compute_sizes(app, constraints, ChildLayoutHelper::layout_child);
1049
1050 let direction = self.direction(app);
1051 let cross_axis_extent = sizes.axis_size.cross_axis_extent();
1052 self.set_size(app, sizes.axis_size.to_size(direction));
1053 self.flex_data_mut(app).overflow = (-sizes.main_axis_free_space).max(0.0);
1054
1055 let remaining_space = sizes.main_axis_free_space.max(0.0);
1056 let flip_main_axis = self.flip_main_axis(app);
1057 let flip_cross_axis = self.flip_cross_axis(app);
1058 let cross_axis_alignment = self.cross_axis_alignment(app);
1059 let (leading_space, between_space) = self.main_axis_alignment(app).distribute_space(
1060 remaining_space,
1061 self.child_count(app),
1062 flip_main_axis,
1063 self.spacing(app),
1064 );
1065 let top_left_child = if flip_main_axis {
1066 self.last_child(app)
1067 } else {
1068 self.first_child(app)
1069 };
1070
1071 let mut child_main_position = leading_space;
1074 let mut child = top_left_child;
1075 while let Some(current) = child {
1076 let child_size = current.size(app);
1077 let free_cross_space = cross_axis_extent - self.get_cross_size(app, child_size);
1078 let child_cross_position = if cross_axis_alignment == CrossAxisAlignment::Baseline
1079 && direction == Axis::Horizontal
1080 {
1081 CrossAxisAlignment::Start.get_child_cross_axis_offset(free_cross_space, false)
1085 } else {
1086 cross_axis_alignment.get_child_cross_axis_offset(free_cross_space, flip_cross_axis)
1088 };
1089 let offset = match direction {
1090 Axis::Horizontal => Offset::new(child_main_position, child_cross_position),
1091 Axis::Vertical => Offset::new(child_cross_position, child_main_position),
1092 };
1093 current
1094 .parent_data_of_mut::<FlexParentData>(app)
1095 .set_offset(offset);
1096 child_main_position += self.get_main_size(app, child_size) + between_space;
1097 child = if flip_main_axis {
1098 self.child_before(app, current)
1099 } else {
1100 self.child_after(app, current)
1101 };
1102 }
1103 }
1104
1105 fn paint(
1107 self: RenderHandle<Self>,
1108 app: &mut App,
1109 context: &mut PaintingContext,
1110 offset: Offset,
1111 ) {
1112 if !self.has_overflow(app) {
1113 self.default_paint(app, context, offset);
1114 return;
1115 }
1116
1117 let size = self.size(app);
1119 if size.is_empty() {
1120 return;
1121 }
1122
1123 let clip_behavior = self.clip_behavior(app);
1124 let old = self.flex_data(app).clip_rect_layer.layer();
1125 let layer = context.push_clip_rect(
1126 app,
1127 self.as_object().needs_compositing(app),
1128 offset,
1129 Offset::ZERO & size,
1130 |app, context, offset| self.default_paint(app, context, offset),
1131 clip_behavior,
1132 old,
1133 );
1134 LayerHandle::set_layer(
1135 app,
1136 |app| &mut self.flex_data_mut(app).clip_rect_layer,
1137 layer,
1138 );
1139 }
1140
1141 fn dispose(self: RenderHandle<Self>, app: &mut App) {
1143 LayerHandle::set_layer(
1144 app,
1145 |app| &mut self.flex_data_mut(app).clip_rect_layer,
1146 None,
1147 );
1148 crate::object::RenderObjectBase::dispose(self, app);
1149 }
1150
1151 fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
1153 if !child.parent_data_is::<FlexParentData>(app) {
1154 child.set_parent_data(app, FlexParentData::new());
1155 }
1156 }
1157
1158 fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1160 self.get_intrinsic_size(
1161 app,
1162 Axis::Horizontal,
1163 height,
1164 AnyRenderBox::get_min_intrinsic_width,
1165 )
1166 }
1167
1168 fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1170 self.get_intrinsic_size(
1171 app,
1172 Axis::Horizontal,
1173 height,
1174 AnyRenderBox::get_max_intrinsic_width,
1175 )
1176 }
1177
1178 fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1180 self.get_intrinsic_size(
1181 app,
1182 Axis::Vertical,
1183 width,
1184 AnyRenderBox::get_min_intrinsic_height,
1185 )
1186 }
1187
1188 fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1190 self.get_intrinsic_size(
1191 app,
1192 Axis::Vertical,
1193 width,
1194 AnyRenderBox::get_max_intrinsic_height,
1195 )
1196 }
1197
1198 fn compute_dry_layout(
1200 self: RenderHandle<Self>,
1201 app: &mut App,
1202 constraints: BoxConstraints,
1203 ) -> Size {
1204 if cfg!(debug_assertions)
1205 && let Some(error) = self.debug_check_constraints(app, constraints)
1206 {
1207 self.debug_cannot_compute_dry_layout(DryLayoutFailure::Error(&error));
1208 return Size::ZERO;
1209 }
1210 let direction = self.direction(app);
1211 self.compute_sizes(app, constraints, ChildLayoutHelper::dry_layout_child)
1212 .axis_size
1213 .to_size(direction)
1214 }
1215
1216 fn hit_test_children(
1218 self: RenderHandle<Self>,
1219 app: &mut App,
1220 result: &mut BoxHitTestResult<'_>,
1221 position: Offset,
1222 ) -> bool {
1223 self.default_hit_test_children(app, result, position)
1224 }
1225}
1226
1227fn get_flex(app: &App, child: AnyRenderBox) -> i32 {
1229 child
1230 .as_object()
1231 .parent_data_of::<FlexParentData>(app)
1232 .flex
1233 .unwrap_or(0)
1234}
1235
1236fn get_fit(app: &App, child: AnyRenderBox) -> FlexFit {
1238 child
1239 .as_object()
1240 .parent_data_of::<FlexParentData>(app)
1241 .fit
1242 .unwrap_or(FlexFit::Tight)
1243}
1244
1245pub struct RenderFlex {
1280 render_object: RenderObjectData,
1281 render_box: RenderBoxData,
1282 container: ContainerRenderObjectData<AnyRenderBox>,
1283 flex: RenderFlexData,
1284}
1285
1286impl RenderFlex {
1287 pub fn new(app: &mut App) -> RenderHandle<RenderFlex> {
1294 RenderHandle::new_box(
1295 app,
1296 RenderFlex {
1297 render_object: RenderObjectData::new(),
1298 render_box: RenderBoxData::new(),
1299 container: ContainerRenderObjectData::new(),
1300 flex: RenderFlexData::new(),
1301 },
1302 )
1303 }
1304
1305 pub fn direction(self: RenderHandle<Self>, app: &App) -> Axis {
1307 RenderFlexMixin::direction(self, app)
1308 }
1309
1310 pub fn set_direction(self: RenderHandle<Self>, app: &mut App, value: Axis) {
1312 RenderFlexMixin::set_direction(self, app, value)
1313 }
1314
1315 pub fn main_axis_alignment(self: RenderHandle<Self>, app: &App) -> MainAxisAlignment {
1317 RenderFlexMixin::main_axis_alignment(self, app)
1318 }
1319
1320 pub fn set_main_axis_alignment(
1322 self: RenderHandle<Self>,
1323 app: &mut App,
1324 value: MainAxisAlignment,
1325 ) {
1326 RenderFlexMixin::set_main_axis_alignment(self, app, value)
1327 }
1328
1329 pub fn main_axis_size(self: RenderHandle<Self>, app: &App) -> MainAxisSize {
1331 RenderFlexMixin::main_axis_size(self, app)
1332 }
1333
1334 pub fn set_main_axis_size(self: RenderHandle<Self>, app: &mut App, value: MainAxisSize) {
1336 RenderFlexMixin::set_main_axis_size(self, app, value)
1337 }
1338
1339 pub fn cross_axis_alignment(self: RenderHandle<Self>, app: &App) -> CrossAxisAlignment {
1341 RenderFlexMixin::cross_axis_alignment(self, app)
1342 }
1343
1344 pub fn set_cross_axis_alignment(
1346 self: RenderHandle<Self>,
1347 app: &mut App,
1348 value: CrossAxisAlignment,
1349 ) {
1350 RenderFlexMixin::set_cross_axis_alignment(self, app, value)
1351 }
1352
1353 pub fn text_direction(self: RenderHandle<Self>, app: &App) -> Option<TextDirection> {
1355 RenderFlexMixin::text_direction(self, app)
1356 }
1357
1358 pub fn set_text_direction(
1360 self: RenderHandle<Self>,
1361 app: &mut App,
1362 value: Option<TextDirection>,
1363 ) {
1364 RenderFlexMixin::set_text_direction(self, app, value)
1365 }
1366
1367 pub fn vertical_direction(self: RenderHandle<Self>, app: &App) -> VerticalDirection {
1369 RenderFlexMixin::vertical_direction(self, app)
1370 }
1371
1372 pub fn set_vertical_direction(
1374 self: RenderHandle<Self>,
1375 app: &mut App,
1376 value: VerticalDirection,
1377 ) {
1378 RenderFlexMixin::set_vertical_direction(self, app, value)
1379 }
1380
1381 pub fn text_baseline(self: RenderHandle<Self>, app: &App) -> Option<TextBaseline> {
1383 RenderFlexMixin::text_baseline(self, app)
1384 }
1385
1386 pub fn set_text_baseline(self: RenderHandle<Self>, app: &mut App, value: Option<TextBaseline>) {
1388 RenderFlexMixin::set_text_baseline(self, app, value)
1389 }
1390
1391 pub fn clip_behavior(self: RenderHandle<Self>, app: &App) -> Clip {
1393 RenderFlexMixin::clip_behavior(self, app)
1394 }
1395
1396 pub fn set_clip_behavior(self: RenderHandle<Self>, app: &mut App, value: Clip) {
1398 RenderFlexMixin::set_clip_behavior(self, app, value)
1399 }
1400
1401 pub fn spacing(self: RenderHandle<Self>, app: &App) -> f64 {
1403 RenderFlexMixin::spacing(self, app)
1404 }
1405
1406 pub fn set_spacing(self: RenderHandle<Self>, app: &mut App, value: f64) {
1408 RenderFlexMixin::set_spacing(self, app, value)
1409 }
1410}
1411
1412impl ContainerRenderObjectMixin for RenderFlex {
1413 type ChildType = AnyRenderBox;
1414 type ParentDataType = FlexParentData;
1415
1416 fn container_data(
1417 self: RenderHandle<Self>,
1418 app: &App,
1419 ) -> &ContainerRenderObjectData<AnyRenderBox> {
1420 &self.get(app).container
1421 }
1422
1423 fn container_data_mut(
1424 self: RenderHandle<Self>,
1425 app: &mut App,
1426 ) -> &mut ContainerRenderObjectData<AnyRenderBox> {
1427 &mut self.get_mut(app).container
1428 }
1429}
1430
1431impl RenderBoxContainerDefaultsMixin for RenderFlex {}
1432
1433impl RenderFlexMixin for RenderFlex {
1434 fn flex_data(self: RenderHandle<Self>, app: &App) -> &RenderFlexData {
1435 &self.get(app).flex
1436 }
1437
1438 fn flex_data_mut(self: RenderHandle<Self>, app: &mut App) -> &mut RenderFlexData {
1439 &mut self.get_mut(app).flex
1440 }
1441}
1442
1443impl RenderObject for RenderFlex {
1444 crate::render_object_accessors!();
1445
1446 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
1447 RenderFlexMixin::perform_layout(self, app)
1448 }
1449
1450 fn visit_children(
1451 self: RenderHandle<Self>,
1452 app: &App,
1453 visitor: &mut dyn FnMut(AnyRenderObject),
1454 ) {
1455 ContainerRenderObjectMixin::visit_children(self, app, visitor)
1456 }
1457
1458 fn did_attach(self: RenderHandle<Self>, app: &mut App, owner: Handle<PipelineOwner>) {
1459 ContainerRenderObjectMixin::did_attach(self, app, owner)
1460 }
1461
1462 fn did_detach(self: RenderHandle<Self>, app: &mut App) {
1463 ContainerRenderObjectMixin::did_detach(self, app)
1464 }
1465
1466 fn redepth_children(self: RenderHandle<Self>, app: &mut App) {
1467 ContainerRenderObjectMixin::redepth_children(self, app)
1468 }
1469
1470 fn paint(
1471 self: RenderHandle<Self>,
1472 app: &mut App,
1473 context: &mut PaintingContext,
1474 offset: Offset,
1475 ) {
1476 RenderFlexMixin::paint(self, app, context, offset)
1477 }
1478
1479 fn dispose(self: RenderHandle<Self>, app: &mut App) {
1480 RenderFlexMixin::dispose(self, app)
1481 }
1482}
1483
1484impl RenderBox for RenderFlex {
1485 crate::render_box_accessors!();
1486
1487 fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
1488 RenderFlexMixin::setup_parent_data(self, app, child)
1489 }
1490
1491 fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1492 RenderFlexMixin::compute_min_intrinsic_width(self, app, height)
1493 }
1494
1495 fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1496 RenderFlexMixin::compute_max_intrinsic_width(self, app, height)
1497 }
1498
1499 fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1500 RenderFlexMixin::compute_min_intrinsic_height(self, app, width)
1501 }
1502
1503 fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1504 RenderFlexMixin::compute_max_intrinsic_height(self, app, width)
1505 }
1506
1507 fn compute_dry_layout(
1508 self: RenderHandle<Self>,
1509 app: &mut App,
1510 constraints: BoxConstraints,
1511 ) -> Size {
1512 RenderFlexMixin::compute_dry_layout(self, app, constraints)
1513 }
1514
1515 fn hit_test_children(
1516 self: RenderHandle<Self>,
1517 app: &mut App,
1518 result: &mut BoxHitTestResult<'_>,
1519 position: Offset,
1520 ) -> bool {
1521 RenderFlexMixin::hit_test_children(self, app, result, position)
1522 }
1523}
1524
1525#[cfg(test)]
1526mod tests {
1527 use inset_foundation::AppCell;
1528 use std::any::Any;
1529
1530 use inset_foundation::Handle;
1531 use inset_gestures::{HitTestEntry, HitTestResult};
1532
1533 use super::*;
1534 use crate::box_::BoxHitTestEntry;
1535 use crate::layer::{ContainerLayer, ErasedLayer, OffsetLayer, PictureLayer};
1536 use crate::pipeline_owner::PipelineOwner;
1537 use crate::proxy_box::RenderRepaintBoundary;
1538 use inset_embedder::valo::Op;
1539
1540 struct SizedTestBox {
1543 render_object: RenderObjectData,
1544 render_box: RenderBoxData,
1545 preferred_size: Size,
1546 }
1547
1548 impl RenderObject for SizedTestBox {
1549 crate::render_object_accessors!();
1550
1551 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
1552 let preferred_size = self.get(app).preferred_size;
1553 let size = self.constraints(app).constrain(preferred_size);
1554 self.set_size(app, size);
1555 }
1556 }
1557
1558 impl RenderBox for SizedTestBox {
1559 crate::render_box_accessors!();
1560
1561 fn hit_test_self(self: RenderHandle<Self>, _app: &App, _position: Offset) -> bool {
1562 true
1563 }
1564 }
1565
1566 fn sized_box(app: &mut App, size: Size) -> AnyRenderBox {
1567 RenderHandle::new_box(
1568 app,
1569 SizedTestBox {
1570 render_object: RenderObjectData::new(),
1571 render_box: RenderBoxData::new(),
1572 preferred_size: size,
1573 },
1574 )
1575 .as_box()
1576 }
1577
1578 fn row(app: &mut App, children: Vec<AnyRenderBox>) -> RenderHandle<RenderFlex> {
1580 let flex = RenderFlex::new(app);
1581 flex.set_text_direction(app, Some(TextDirection::Ltr));
1582 flex.add_all(app, Some(children));
1583 flex
1584 }
1585
1586 fn offset_of(app: &App, child: AnyRenderBox) -> Offset {
1587 child.box_parent_data(app).offset
1588 }
1589
1590 fn schedule_root_paint(app: &mut App, node: AnyRenderObject) {
1591 let root = OffsetLayer::new(app, Offset::ZERO);
1592 root.as_layer().attach(app, node.id());
1593 node.schedule_initial_paint(app, root.as_container_layer());
1594 }
1595
1596 fn first_frame(
1598 app: &mut App,
1599 root: AnyRenderBox,
1600 constraints: BoxConstraints,
1601 ) -> Handle<PipelineOwner> {
1602 let owner = PipelineOwner::new(app, None, None);
1603 owner.set_root_node(app, Some(root.as_object()));
1604 root.layout(app, constraints, false);
1605 schedule_root_paint(app, root.as_object());
1606 owner.flush_compositing_bits(app);
1607 owner.flush_paint(app);
1608 owner
1609 }
1610
1611 fn picture_has_clip(app: &App, boundary: AnyRenderObject) -> bool {
1612 let layer = boundary.debug_layer(app).expect("painted");
1613 layer
1614 .depth_first_iterate_children(app)
1615 .into_iter()
1616 .filter_map(|child| {
1617 app.handle::<PictureLayer>(child.id())
1618 .and_then(|picture| picture.picture(app).map(|p| p.ops().to_vec()))
1619 })
1620 .flatten()
1621 .any(|op| matches!(op, Op::ClipPath { .. }))
1622 }
1623
1624 fn hit_boxes(result: &HitTestResult) -> Vec<AnyRenderBox> {
1625 result
1626 .path()
1627 .iter()
1628 .filter_map(|entry: &HitTestEntry| {
1629 let target: &dyn Any = entry.target();
1630 target
1631 .downcast_ref::<BoxHitTestEntry>()
1632 .map(BoxHitTestEntry::target)
1633 })
1634 .collect()
1635 }
1636
1637 #[test]
1640 fn child_list_insert_move_and_remove() {
1641 let cell = AppCell::new();
1642 let mut app = cell.borrow_mut();
1643 let first = sized_box(&mut app, Size::new(10.0, 10.0));
1644 let middle = sized_box(&mut app, Size::new(10.0, 10.0));
1645 let last = sized_box(&mut app, Size::new(10.0, 10.0));
1646 let flex = RenderFlex::new(&mut app);
1647
1648 flex.add(&mut app, first);
1649 flex.insert(&mut app, last, Some(first));
1650 flex.insert(&mut app, middle, Some(first));
1651 assert_eq!(flex.child_count(&app), 3);
1652 assert_eq!(flex.children_as_list(&app), vec![first, middle, last]);
1653 assert_eq!(flex.first_child(&app), Some(first));
1654 assert_eq!(flex.last_child(&app), Some(last));
1655 assert_eq!(flex.child_after(&app, first), Some(middle));
1656 assert_eq!(flex.child_before(&app, last), Some(middle));
1657
1658 let mut visited = Vec::new();
1659 flex.as_object()
1660 .visit_children(&app, &mut |child| visited.push(child));
1661 assert_eq!(
1662 visited,
1663 vec![first.as_object(), middle.as_object(), last.as_object()]
1664 );
1665
1666 flex.move_child(&mut app, first, Some(last));
1667 assert_eq!(flex.children_as_list(&app), vec![middle, last, first]);
1668 flex.move_child(&mut app, first, None);
1669 assert_eq!(flex.children_as_list(&app), vec![first, middle, last]);
1670
1671 flex.remove(&mut app, middle);
1672 assert_eq!(flex.children_as_list(&app), vec![first, last]);
1673 assert_eq!(flex.child_count(&app), 2);
1674 assert!(middle.as_object().parent(&app).is_none());
1675
1676 flex.remove_all(&mut app);
1677 assert_eq!(flex.child_count(&app), 0);
1678 assert!(flex.first_child(&app).is_none());
1679 assert!(flex.last_child(&app).is_none());
1680 assert!(first.as_object().parent(&app).is_none());
1681 assert!(last.as_object().parent(&app).is_none());
1682 }
1683
1684 struct IntrinsicTestBox {
1689 render_object: RenderObjectData,
1690 render_box: RenderBoxData,
1691 intrinsic: Size,
1692 }
1693
1694 impl RenderObject for IntrinsicTestBox {
1695 crate::render_object_accessors!();
1696
1697 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
1698 let intrinsic = self.get(app).intrinsic;
1699 let size = self.constraints(app).constrain(intrinsic);
1700 self.set_size(app, size);
1701 }
1702 }
1703
1704 impl RenderBox for IntrinsicTestBox {
1705 crate::render_box_accessors!();
1706
1707 fn compute_min_intrinsic_width(
1708 self: RenderHandle<Self>,
1709 app: &mut App,
1710 _height: f64,
1711 ) -> f64 {
1712 self.get(app).intrinsic.width()
1713 }
1714
1715 fn compute_max_intrinsic_width(
1716 self: RenderHandle<Self>,
1717 app: &mut App,
1718 _height: f64,
1719 ) -> f64 {
1720 self.get(app).intrinsic.width()
1721 }
1722
1723 fn compute_min_intrinsic_height(
1724 self: RenderHandle<Self>,
1725 app: &mut App,
1726 _width: f64,
1727 ) -> f64 {
1728 self.get(app).intrinsic.height()
1729 }
1730
1731 fn compute_max_intrinsic_height(
1732 self: RenderHandle<Self>,
1733 app: &mut App,
1734 _width: f64,
1735 ) -> f64 {
1736 self.get(app).intrinsic.height()
1737 }
1738
1739 fn compute_dry_layout(
1740 self: RenderHandle<Self>,
1741 app: &mut App,
1742 constraints: BoxConstraints,
1743 ) -> Size {
1744 constraints.constrain(self.get(app).intrinsic)
1745 }
1746 }
1747
1748 fn intrinsic_box(app: &mut App, intrinsic: Size) -> AnyRenderBox {
1749 RenderHandle::new_box(
1750 app,
1751 IntrinsicTestBox {
1752 render_object: RenderObjectData::new(),
1753 render_box: RenderBoxData::new(),
1754 intrinsic,
1755 },
1756 )
1757 .as_box()
1758 }
1759
1760 #[test]
1764 fn row_intrinsics_add_the_inflexible_space_to_the_largest_flex_fraction() {
1765 let cell = AppCell::new();
1766 let mut app = cell.borrow_mut();
1767 let fixed = intrinsic_box(&mut app, Size::new(50.0, 20.0));
1768 let flexible = intrinsic_box(&mut app, Size::new(30.0, 10.0));
1769 let flex = row(&mut app, vec![fixed, flexible]);
1770 flexible.parent_data_of_mut::<FlexParentData>(&mut app).flex = Some(1);
1771
1772 let row = flex.as_box();
1773 assert_eq!(row.get_max_intrinsic_width(&mut app, f64::INFINITY), 80.0);
1774 assert_eq!(row.get_min_intrinsic_width(&mut app, f64::INFINITY), 80.0);
1775 assert_eq!(row.get_max_intrinsic_height(&mut app, 100.0), 20.0);
1776 }
1777
1778 #[test]
1780 fn row_intrinsics_include_the_spacing() {
1781 let cell = AppCell::new();
1782 let mut app = cell.borrow_mut();
1783 let first = intrinsic_box(&mut app, Size::new(50.0, 20.0));
1784 let second = intrinsic_box(&mut app, Size::new(30.0, 10.0));
1785 let flex = row(&mut app, vec![first, second]);
1786 flex.set_spacing(&mut app, 10.0);
1787 assert_eq!(
1788 flex.as_box()
1789 .get_max_intrinsic_width(&mut app, f64::INFINITY),
1790 90.0
1791 );
1792 }
1793
1794 #[test]
1797 fn row_dry_layout_follows_the_main_axis_size() {
1798 for (main_axis_size, expected) in [
1799 (MainAxisSize::Max, Size::new(200.0, 20.0)),
1800 (MainAxisSize::Min, Size::new(80.0, 20.0)),
1801 ] {
1802 let cell = AppCell::new();
1803 let mut app = cell.borrow_mut();
1804 let fixed = intrinsic_box(&mut app, Size::new(50.0, 20.0));
1805 let flexible = intrinsic_box(&mut app, Size::new(30.0, 10.0));
1806 let flex = row(&mut app, vec![fixed, flexible]);
1807 flex.set_main_axis_size(&mut app, main_axis_size);
1808 {
1809 let parent_data = flexible.parent_data_of_mut::<FlexParentData>(&mut app);
1810 parent_data.flex = Some(1);
1811 parent_data.fit = Some(FlexFit::Loose);
1812 }
1813
1814 let constraints = BoxConstraints::new().max_width(200.0).max_height(100.0);
1815 let dry = flex.as_box().get_dry_layout(&mut app, constraints);
1816 flex.layout(&mut app, constraints, false);
1817 assert_eq!(dry, expected);
1818 assert_eq!(dry, flex.size(&app), "the dry layout is the real layout");
1819 }
1820 }
1821
1822 #[test]
1823 fn row_lays_out_fixed_and_flexible_children_with_spacing() {
1824 let cell = AppCell::new();
1825 let mut app = cell.borrow_mut();
1826 let fixed = sized_box(&mut app, Size::new(50.0, 30.0));
1827 let flexible = sized_box(&mut app, Size::new(20.0, 10.0));
1828 let flex = row(&mut app, vec![fixed, flexible]);
1829 flexible.parent_data_of_mut::<FlexParentData>(&mut app).flex = Some(1);
1830 flex.set_spacing(&mut app, 10.0);
1831
1832 flex.layout(
1833 &mut app,
1834 BoxConstraints::tight(Size::new(200.0, 100.0)),
1835 false,
1836 );
1837
1838 assert_eq!(flex.size(&app), Size::new(200.0, 100.0));
1839 assert_eq!(fixed.size(&app), Size::new(50.0, 30.0));
1840 assert_eq!(flexible.size(&app), Size::new(140.0, 10.0));
1842 assert_eq!(offset_of(&app, fixed), Offset::new(0.0, 35.0));
1843 assert_eq!(offset_of(&app, flexible), Offset::new(60.0, 45.0));
1844 }
1845
1846 #[test]
1848 fn main_axis_alignment_positions_children() {
1849 let cases = [
1850 (MainAxisAlignment::Start, 0.0, 20.0),
1851 (MainAxisAlignment::End, 60.0, 80.0),
1852 (MainAxisAlignment::Center, 30.0, 50.0),
1853 (MainAxisAlignment::SpaceBetween, 0.0, 80.0),
1854 (MainAxisAlignment::SpaceAround, 15.0, 65.0),
1855 (MainAxisAlignment::SpaceEvenly, 20.0, 60.0),
1856 ];
1857 for (alignment, first_x, second_x) in cases {
1858 let cell = AppCell::new();
1859 let mut app = cell.borrow_mut();
1860 let first = sized_box(&mut app, Size::new(20.0, 20.0));
1861 let second = sized_box(&mut app, Size::new(20.0, 20.0));
1862 let flex = row(&mut app, vec![first, second]);
1863 flex.set_main_axis_alignment(&mut app, alignment);
1864
1865 flex.layout(
1866 &mut app,
1867 BoxConstraints::tight(Size::new(100.0, 20.0)),
1868 false,
1869 );
1870
1871 assert_eq!(
1872 (offset_of(&app, first).dx(), offset_of(&app, second).dx()),
1873 (first_x, second_x),
1874 "{alignment:?}"
1875 );
1876 }
1877 }
1878
1879 #[test]
1882 fn cross_axis_alignment_positions_children() {
1883 let cases = [
1884 (CrossAxisAlignment::Start, 0.0, 20.0),
1885 (CrossAxisAlignment::Center, 40.0, 20.0),
1886 (CrossAxisAlignment::End, 80.0, 20.0),
1887 (CrossAxisAlignment::Stretch, 0.0, 100.0),
1888 (CrossAxisAlignment::Baseline, 0.0, 20.0),
1889 ];
1890 for (alignment, y, height) in cases {
1891 let cell = AppCell::new();
1892 let mut app = cell.borrow_mut();
1893 let child = sized_box(&mut app, Size::new(20.0, 20.0));
1894 let flex = row(&mut app, vec![child]);
1895 flex.set_cross_axis_alignment(&mut app, alignment);
1896 if alignment == CrossAxisAlignment::Baseline {
1897 flex.set_text_baseline(&mut app, Some(TextBaseline::Alphabetic));
1898 }
1899
1900 flex.layout(
1901 &mut app,
1902 BoxConstraints::tight(Size::new(100.0, 100.0)),
1903 false,
1904 );
1905
1906 assert_eq!(offset_of(&app, child).dy(), y, "{alignment:?}");
1907 assert_eq!(child.size(&app).height(), height, "{alignment:?}");
1908 }
1909 }
1910
1911 #[test]
1913 fn overflow_is_clipped() {
1914 for (clip_behavior, clips) in [(Clip::None, false), (Clip::HardEdge, true)] {
1915 let cell = AppCell::new();
1916 let mut app = cell.borrow_mut();
1917 let first = sized_box(&mut app, Size::new(80.0, 20.0));
1918 let second = sized_box(&mut app, Size::new(80.0, 20.0));
1919 let flex = row(&mut app, vec![first, second]);
1920 flex.set_clip_behavior(&mut app, clip_behavior);
1921 let root = RenderRepaintBoundary::new(&mut app, Some(flex.as_box()));
1922
1923 let owner = first_frame(
1924 &mut app,
1925 root.as_box(),
1926 BoxConstraints::tight(Size::new(100.0, 20.0)),
1927 );
1928
1929 assert_eq!(flex.size(&app), Size::new(100.0, 20.0));
1930 let clipped = picture_has_clip(&app, root.as_object());
1931 assert_eq!(clipped, clips, "{clip_behavior:?}");
1932 let _ = owner;
1933 }
1934 }
1935
1936 #[test]
1938 fn hit_test_reaches_the_child_under_the_position() {
1939 let cell = AppCell::new();
1940 let mut app = cell.borrow_mut();
1941 let first = sized_box(&mut app, Size::new(50.0, 50.0));
1942 let second = sized_box(&mut app, Size::new(50.0, 50.0));
1943 let flex = row(&mut app, vec![first, second]);
1944
1945 flex.layout(
1946 &mut app,
1947 BoxConstraints::tight(Size::new(100.0, 50.0)),
1948 false,
1949 );
1950
1951 let mut result = HitTestResult::new();
1952 let hit = flex.hit_test(
1953 &mut app,
1954 &mut BoxHitTestResult::wrap(&mut result),
1955 Offset::new(75.0, 25.0),
1956 );
1957 assert!(hit);
1958 assert_eq!(hit_boxes(&result), vec![second, flex.as_box()]);
1959 }
1960}