1use std::any::{Any, TypeId};
5use std::collections::HashMap;
6use std::fmt::{self, Debug};
7use std::ops::Add;
8
9use inset_embedder::{Clip, Offset, Size, TextBaseline, TextDirection};
10use inset_foundation::{App, Handle, PRECISION_ERROR_TOLERANCE};
11use inset_painting::{Axis, VerticalDirection};
12
13use crate::box_::{
14 AnyRenderBox, BaselineOffset, BoxConstraints, BoxHitTestResult, BoxParentData,
15 ContainerBoxParentData, DryLayoutFailure, RenderBox, RenderBoxContainerDefaultsMixin,
16 RenderBoxData,
17};
18use crate::layer::{ClipRectLayer, LayerHandle};
19use crate::layout_helper::{ChildBaselineGetter, ChildLayoutHelper};
20use crate::object::{
21 AnyRenderObject, ContainerParentData, ContainerParentDataMixin, ContainerRenderObjectData,
22 ContainerRenderObjectMixin, ParentData, RenderHandle, RenderObject, RenderObjectData,
23 debug_checking_intrinsics,
24};
25use crate::painting_context::PaintingContext;
26use crate::pipeline_owner::PipelineOwner;
27
28#[derive(Clone, Copy)]
31struct AxisSize(Size);
32
33impl AxisSize {
34 const EMPTY: AxisSize = AxisSize(Size::ZERO);
35
36 fn new(main_axis_extent: f64, cross_axis_extent: f64) -> AxisSize {
37 AxisSize(Size::new(main_axis_extent, cross_axis_extent))
38 }
39
40 fn from_size(size: Size, direction: Axis) -> AxisSize {
41 AxisSize(AxisSize::convert(size, direction))
42 }
43
44 fn convert(size: Size, direction: Axis) -> Size {
45 match direction {
46 Axis::Horizontal => size,
47 Axis::Vertical => size.flipped(),
48 }
49 }
50
51 fn main_axis_extent(self) -> f64 {
52 self.0.width()
53 }
54
55 fn cross_axis_extent(self) -> f64 {
56 self.0.height()
57 }
58
59 fn to_size(self, direction: Axis) -> Size {
60 AxisSize::convert(self.0, direction)
61 }
62
63 fn apply_constraints(self, constraints: BoxConstraints, direction: Axis) -> AxisSize {
64 let effective_constraints = match direction {
65 Axis::Horizontal => constraints,
66 Axis::Vertical => constraints.flipped(),
67 };
68 AxisSize(effective_constraints.constrain(self.0))
69 }
70}
71
72impl Add for AxisSize {
73 type Output = AxisSize;
74
75 fn add(self, other: AxisSize) -> AxisSize {
76 AxisSize(Size::new(
77 self.0.width() + other.0.width(),
78 self.0.height().max(other.0.height()),
79 ))
80 }
81}
82
83#[derive(Clone, Copy)]
88struct AscentDescent(Option<(f64, f64)>);
89
90impl AscentDescent {
91 const NONE: AscentDescent = AscentDescent(None);
92
93 fn new(baseline_offset: Option<f64>, cross_size: f64) -> AscentDescent {
94 match baseline_offset {
95 None => AscentDescent::NONE,
96 Some(baseline_offset) => {
97 AscentDescent(Some((baseline_offset, cross_size - baseline_offset)))
98 }
99 }
100 }
101
102 fn baseline_offset(self) -> Option<f64> {
103 self.0.map(|(ascent, _)| ascent)
104 }
105}
106
107impl Add for AscentDescent {
108 type Output = AscentDescent;
109
110 fn add(self, other: AscentDescent) -> AscentDescent {
111 match (self.0, other.0) {
112 (None, _) => other,
113 (_, None) => self,
114 (Some((x_ascent, x_descent)), Some((y_ascent, y_descent))) => {
115 AscentDescent(Some((x_ascent.max(y_ascent), x_descent.max(y_descent))))
116 }
117 }
118 }
119}
120
121type ChildSizingFunction = fn(AnyRenderBox, &mut App, f64) -> f64;
124
125pub struct LayoutSizes {
127 axis_size: AxisSize,
129
130 main_axis_free_space: f64,
134
135 baseline_offset: Option<f64>,
138
139 space_per_flex: Option<f64>,
141}
142
143#[derive(Clone, Copy, Debug, PartialEq, Eq)]
149pub enum FlexFit {
150 Tight,
152
153 Loose,
155}
156
157#[derive(Debug)]
159pub struct FlexParentData {
160 box_parent_data: BoxParentData,
161 container_parent_data: ContainerParentData<AnyRenderBox>,
162
163 pub flex: Option<i32>,
170
171 pub fit: Option<FlexFit>,
178}
179
180impl FlexParentData {
181 pub const fn new() -> FlexParentData {
183 FlexParentData {
184 box_parent_data: BoxParentData::new(),
185 container_parent_data: ContainerParentData::new(),
186 flex: None,
187 fit: None,
188 }
189 }
190}
191
192impl Default for FlexParentData {
193 fn default() -> FlexParentData {
194 FlexParentData::new()
195 }
196}
197
198impl ParentData for FlexParentData {
199 fn detach(&mut self) {
200 ContainerParentDataMixin::detach(self);
201 }
202
203 fn provide(&self, id: TypeId) -> Option<&dyn Any> {
204 if id == TypeId::of::<FlexParentData>() {
205 return Some(self);
206 }
207 self.box_parent_data.provide(id)
208 }
209
210 fn provide_mut(&mut self, id: TypeId) -> Option<&mut dyn Any> {
211 if id == TypeId::of::<FlexParentData>() {
212 return Some(self);
213 }
214 self.box_parent_data.provide_mut(id)
215 }
216}
217
218impl ContainerParentDataMixin for FlexParentData {
219 type ChildType = AnyRenderBox;
220
221 fn container_parent_data(&self) -> &ContainerParentData<AnyRenderBox> {
222 &self.container_parent_data
223 }
224
225 fn container_parent_data_mut(&mut self) -> &mut ContainerParentData<AnyRenderBox> {
226 &mut self.container_parent_data
227 }
228}
229
230impl ContainerBoxParentData for FlexParentData {
231 fn box_parent_data(&self) -> &BoxParentData {
232 &self.box_parent_data
233 }
234
235 fn box_parent_data_mut(&mut self) -> &mut BoxParentData {
236 &mut self.box_parent_data
237 }
238}
239
240impl fmt::Display for FlexParentData {
241 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242 write!(
243 f,
244 "{}; flex={:?}; fit={:?}",
245 self.box_parent_data, self.flex, self.fit
246 )
247 }
248}
249
250#[derive(Clone, Copy, Debug, PartialEq, Eq)]
261pub enum MainAxisSize {
262 Min,
273
274 Max,
283}
284
285#[derive(Clone, Copy, Debug, PartialEq, Eq)]
291pub enum MainAxisAlignment {
292 Start,
300
301 End,
309
310 Center,
312
313 SpaceBetween,
315
316 SpaceAround,
319
320 SpaceEvenly,
323}
324
325impl MainAxisAlignment {
326 fn distribute_space(
327 self,
328 free_space: f64,
329 item_count: usize,
330 flipped: bool,
331 spacing: f64,
332 ) -> (f64, f64) {
333 let items = item_count as f64;
334 match self {
335 MainAxisAlignment::Start => {
336 if flipped {
337 (free_space, spacing)
338 } else {
339 (0.0, spacing)
340 }
341 }
342 MainAxisAlignment::End => {
343 MainAxisAlignment::Start.distribute_space(free_space, item_count, !flipped, spacing)
344 }
345 MainAxisAlignment::SpaceBetween if item_count < 2 => {
346 MainAxisAlignment::Start.distribute_space(free_space, item_count, flipped, spacing)
347 }
348 MainAxisAlignment::SpaceAround if item_count == 0 => {
349 MainAxisAlignment::Start.distribute_space(free_space, item_count, flipped, spacing)
350 }
351 MainAxisAlignment::Center => (free_space / 2.0, spacing),
352 MainAxisAlignment::SpaceBetween => (0.0, free_space / (items - 1.0) + spacing),
353 MainAxisAlignment::SpaceAround => {
354 (free_space / items / 2.0, free_space / items + spacing)
355 }
356 MainAxisAlignment::SpaceEvenly => (
357 free_space / (items + 1.0),
358 free_space / (items + 1.0) + spacing,
359 ),
360 }
361 }
362}
363
364#[derive(Clone, Copy, Debug, PartialEq, Eq)]
370pub enum CrossAxisAlignment {
371 Start,
383
384 End,
396
397 Center,
401
402 Stretch,
406
407 Baseline,
436}
437
438impl CrossAxisAlignment {
439 fn get_child_cross_axis_offset(self, free_space: f64, flipped: bool) -> f64 {
441 match self {
442 CrossAxisAlignment::Stretch | CrossAxisAlignment::Baseline => 0.0,
443 CrossAxisAlignment::Start => {
444 if flipped {
445 free_space
446 } else {
447 0.0
448 }
449 }
450 CrossAxisAlignment::Center => free_space / 2.0,
451 CrossAxisAlignment::End => {
452 CrossAxisAlignment::Start.get_child_cross_axis_offset(free_space, !flipped)
453 }
454 }
455 }
456}
457
458pub struct RenderFlexData {
463 direction: Axis,
464 main_axis_alignment: MainAxisAlignment,
465 main_axis_size: MainAxisSize,
466 cross_axis_alignment: CrossAxisAlignment,
467 text_direction: Option<TextDirection>,
468 vertical_direction: VerticalDirection,
469 text_baseline: Option<TextBaseline>,
470 clip_behavior: Clip,
471 spacing: f64,
472 overflow: f64,
474 clip_rect_layer: LayerHandle<Handle<ClipRectLayer>>,
475}
476
477impl RenderFlexData {
478 pub fn new() -> RenderFlexData {
483 RenderFlexData {
484 direction: Axis::Horizontal,
485 main_axis_alignment: MainAxisAlignment::Start,
486 main_axis_size: MainAxisSize::Max,
487 cross_axis_alignment: CrossAxisAlignment::Center,
488 text_direction: None,
489 vertical_direction: VerticalDirection::Down,
490 text_baseline: None,
491 clip_behavior: Clip::None,
492 spacing: 0.0,
493 overflow: 0.0,
494 clip_rect_layer: LayerHandle::new(),
495 }
496 }
497}
498
499impl Default for RenderFlexData {
500 fn default() -> RenderFlexData {
501 RenderFlexData::new()
502 }
503}
504
505impl RenderFlexData {
506 pub fn direction(mut self, direction: Axis) -> RenderFlexData {
508 self.direction = direction;
509 self
510 }
511
512 pub fn main_axis_alignment(mut self, main_axis_alignment: MainAxisAlignment) -> RenderFlexData {
514 self.main_axis_alignment = main_axis_alignment;
515 self
516 }
517
518 pub fn main_axis_size(mut self, main_axis_size: MainAxisSize) -> RenderFlexData {
520 self.main_axis_size = main_axis_size;
521 self
522 }
523
524 pub fn cross_axis_alignment(
526 mut self,
527 cross_axis_alignment: CrossAxisAlignment,
528 ) -> RenderFlexData {
529 self.cross_axis_alignment = cross_axis_alignment;
530 self
531 }
532
533 pub fn text_direction(mut self, text_direction: Option<TextDirection>) -> RenderFlexData {
535 self.text_direction = text_direction;
536 self
537 }
538
539 pub fn vertical_direction(mut self, vertical_direction: VerticalDirection) -> RenderFlexData {
541 self.vertical_direction = vertical_direction;
542 self
543 }
544
545 pub fn text_baseline(mut self, text_baseline: Option<TextBaseline>) -> RenderFlexData {
547 self.text_baseline = text_baseline;
548 self
549 }
550
551 pub fn clip_behavior(mut self, clip_behavior: Clip) -> RenderFlexData {
553 self.clip_behavior = clip_behavior;
554 self
555 }
556
557 pub fn spacing(mut self, spacing: f64) -> RenderFlexData {
559 self.spacing = spacing;
560 self
561 }
562}
563
564pub trait RenderFlexMixin:
572 RenderBox
573 + ContainerRenderObjectMixin<ChildType = AnyRenderBox, ParentDataType = FlexParentData>
574 + RenderBoxContainerDefaultsMixin
575{
576 fn flex_data(self: RenderHandle<Self>, app: &App) -> &RenderFlexData;
578
579 fn flex_data_mut(self: RenderHandle<Self>, app: &mut App) -> &mut RenderFlexData;
581
582 fn direction(self: RenderHandle<Self>, app: &App) -> Axis {
584 self.flex_data(app).direction
585 }
586
587 fn set_direction(self: RenderHandle<Self>, app: &mut App, value: Axis) {
589 if self.flex_data(app).direction != value {
590 self.flex_data_mut(app).direction = value;
591 self.mark_needs_layout(app);
592 }
593 }
594
595 fn main_axis_alignment(self: RenderHandle<Self>, app: &App) -> MainAxisAlignment {
601 self.flex_data(app).main_axis_alignment
602 }
603
604 fn set_main_axis_alignment(self: RenderHandle<Self>, app: &mut App, value: MainAxisAlignment) {
606 if self.flex_data(app).main_axis_alignment != value {
607 self.flex_data_mut(app).main_axis_alignment = value;
608 self.mark_needs_layout(app);
609 }
610 }
611
612 fn main_axis_size(self: RenderHandle<Self>, app: &App) -> MainAxisSize {
618 self.flex_data(app).main_axis_size
619 }
620
621 fn set_main_axis_size(self: RenderHandle<Self>, app: &mut App, value: MainAxisSize) {
623 if self.flex_data(app).main_axis_size != value {
624 self.flex_data_mut(app).main_axis_size = value;
625 self.mark_needs_layout(app);
626 }
627 }
628
629 fn cross_axis_alignment(self: RenderHandle<Self>, app: &App) -> CrossAxisAlignment {
635 self.flex_data(app).cross_axis_alignment
636 }
637
638 fn set_cross_axis_alignment(
640 self: RenderHandle<Self>,
641 app: &mut App,
642 value: CrossAxisAlignment,
643 ) {
644 if self.flex_data(app).cross_axis_alignment != value {
645 self.flex_data_mut(app).cross_axis_alignment = value;
646 self.mark_needs_layout(app);
647 }
648 }
649
650 fn text_direction(self: RenderHandle<Self>, app: &App) -> Option<TextDirection> {
662 self.flex_data(app).text_direction
663 }
664
665 fn set_text_direction(self: RenderHandle<Self>, app: &mut App, value: Option<TextDirection>) {
667 if self.flex_data(app).text_direction != value {
668 self.flex_data_mut(app).text_direction = value;
669 self.mark_needs_layout(app);
670 }
671 }
672
673 fn vertical_direction(self: RenderHandle<Self>, app: &App) -> VerticalDirection {
685 self.flex_data(app).vertical_direction
686 }
687
688 fn set_vertical_direction(self: RenderHandle<Self>, app: &mut App, value: VerticalDirection) {
690 if self.flex_data(app).vertical_direction != value {
691 self.flex_data_mut(app).vertical_direction = value;
692 self.mark_needs_layout(app);
693 }
694 }
695
696 fn text_baseline(self: RenderHandle<Self>, app: &App) -> Option<TextBaseline> {
701 self.flex_data(app).text_baseline
702 }
703
704 fn set_text_baseline(self: RenderHandle<Self>, app: &mut App, value: Option<TextBaseline>) {
706 debug_assert!(
707 self.flex_data(app).cross_axis_alignment != CrossAxisAlignment::Baseline
708 || value.is_some()
709 );
710 if self.flex_data(app).text_baseline != value {
711 self.flex_data_mut(app).text_baseline = value;
712 self.mark_needs_layout(app);
713 }
714 }
715
716 fn has_overflow(self: RenderHandle<Self>, app: &App) -> bool {
721 self.flex_data(app).overflow > PRECISION_ERROR_TOLERANCE
722 }
723
724 fn clip_behavior(self: RenderHandle<Self>, app: &App) -> Clip {
728 self.flex_data(app).clip_behavior
729 }
730
731 fn set_clip_behavior(self: RenderHandle<Self>, app: &mut App, value: Clip) {
733 if value != self.flex_data(app).clip_behavior {
734 self.flex_data_mut(app).clip_behavior = value;
735 self.mark_needs_paint(app);
736 }
737 }
738
739 fn spacing(self: RenderHandle<Self>, app: &App) -> f64 {
748 self.flex_data(app).spacing
749 }
750
751 fn set_spacing(self: RenderHandle<Self>, app: &mut App, value: f64) {
753 debug_assert!(value >= 0.0);
754 if self.flex_data(app).spacing == value {
755 return;
756 }
757 self.flex_data_mut(app).spacing = value;
758 self.mark_needs_layout(app);
759 }
760
761 fn debug_has_necessary_directions(self: RenderHandle<Self>, app: &App) -> bool {
764 if debug_checking_intrinsics() {
765 return true;
766 }
767 let direction = self.direction(app);
768 if self.first_child(app).is_some() && self.last_child(app) != self.first_child(app) {
769 if direction == Axis::Horizontal {
771 debug_assert!(
772 self.text_direction(app).is_some(),
773 "Horizontal RenderFlex with multiple children has a null textDirection, so \
774 the layout order is undefined."
775 );
776 }
777 }
778 let main_axis_alignment = self.main_axis_alignment(app);
779 if (main_axis_alignment == MainAxisAlignment::Start
780 || main_axis_alignment == MainAxisAlignment::End)
781 && direction == Axis::Horizontal
782 {
783 debug_assert!(
784 self.text_direction(app).is_some(),
785 "Horizontal RenderFlex with {main_axis_alignment:?} has a null textDirection, so \
786 the alignment cannot be resolved."
787 );
788 }
789 let cross_axis_alignment = self.cross_axis_alignment(app);
790 if (cross_axis_alignment == CrossAxisAlignment::Start
791 || cross_axis_alignment == CrossAxisAlignment::End)
792 && direction == Axis::Vertical
793 {
794 debug_assert!(
795 self.text_direction(app).is_some(),
796 "Vertical RenderFlex with {cross_axis_alignment:?} has a null textDirection, so \
797 the alignment cannot be resolved."
798 );
799 }
800 true
801 }
802
803 fn is_baseline_aligned(self: RenderHandle<Self>, app: &App) -> bool {
806 match self.cross_axis_alignment(app) {
807 CrossAxisAlignment::Baseline => match self.direction(app) {
808 Axis::Horizontal => true,
809 Axis::Vertical => false,
810 },
811 CrossAxisAlignment::Start
812 | CrossAxisAlignment::Center
813 | CrossAxisAlignment::End
814 | CrossAxisAlignment::Stretch => false,
815 }
816 }
817
818 fn get_cross_size(self: RenderHandle<Self>, app: &App, size: Size) -> f64 {
820 match self.direction(app) {
821 Axis::Horizontal => size.height(),
822 Axis::Vertical => size.width(),
823 }
824 }
825
826 fn get_main_size(self: RenderHandle<Self>, app: &App, size: Size) -> f64 {
828 match self.direction(app) {
829 Axis::Horizontal => size.width(),
830 Axis::Vertical => size.height(),
831 }
832 }
833
834 fn flip_main_axis(self: RenderHandle<Self>, app: &App) -> bool {
838 self.first_child(app).is_some()
839 && match self.direction(app) {
840 Axis::Horizontal => self.text_direction(app) == Some(TextDirection::Rtl),
841 Axis::Vertical => self.vertical_direction(app) == VerticalDirection::Up,
842 }
843 }
844
845 fn flip_cross_axis(self: RenderHandle<Self>, app: &App) -> bool {
848 self.first_child(app).is_some()
849 && match self.direction(app) {
850 Axis::Vertical => self.text_direction(app) == Some(TextDirection::Rtl),
851 Axis::Horizontal => self.vertical_direction(app) == VerticalDirection::Up,
852 }
853 }
854
855 fn constraints_for_non_flex_child(
857 self: RenderHandle<Self>,
858 app: &App,
859 constraints: BoxConstraints,
860 ) -> BoxConstraints {
861 let fill_cross_axis = self.cross_axis_alignment(app) == CrossAxisAlignment::Stretch;
862 match self.direction(app) {
863 Axis::Horizontal => {
864 if fill_cross_axis {
865 BoxConstraints::tight_for(None, Some(constraints.max_height))
866 } else {
867 BoxConstraints::new().max_height(constraints.max_height)
868 }
869 }
870 Axis::Vertical => {
871 if fill_cross_axis {
872 BoxConstraints::tight_for(Some(constraints.max_width), None)
873 } else {
874 BoxConstraints::new().max_width(constraints.max_width)
875 }
876 }
877 }
878 }
879
880 fn constraints_for_flex_child(
883 self: RenderHandle<Self>,
884 app: &App,
885 child: AnyRenderBox,
886 constraints: BoxConstraints,
887 max_child_extent: f64,
888 ) -> BoxConstraints {
889 debug_assert!(get_flex(app, child) > 0);
890 debug_assert!(max_child_extent >= 0.0);
891 let min_child_extent = match get_fit(app, child) {
892 FlexFit::Tight => max_child_extent,
893 FlexFit::Loose => 0.0,
894 };
895 let fill_cross_axis = self.cross_axis_alignment(app) == CrossAxisAlignment::Stretch;
896 match self.direction(app) {
897 Axis::Horizontal => BoxConstraints {
898 min_width: min_child_extent,
899 max_width: max_child_extent,
900 min_height: if fill_cross_axis {
901 constraints.max_height
902 } else {
903 0.0
904 },
905 max_height: constraints.max_height,
906 },
907 Axis::Vertical => BoxConstraints {
908 min_width: if fill_cross_axis {
909 constraints.max_width
910 } else {
911 0.0
912 },
913 max_width: constraints.max_width,
914 min_height: min_child_extent,
915 max_height: max_child_extent,
916 },
917 }
918 }
919
920 fn get_intrinsic_size(
924 self: RenderHandle<Self>,
925 app: &mut App,
926 sizing_direction: Axis,
927 extent: f64,
928 child_size: ChildSizingFunction,
929 ) -> f64 {
930 let direction = self.direction(app);
931 if direction == sizing_direction {
932 let mut total_flex = 0.0;
936 let mut inflexible_space = self.spacing(app) * (self.child_count(app) as f64 - 1.0);
937 let mut max_flex_fraction_so_far: f64 = 0.0;
938 let mut child = self.first_child(app);
939 while let Some(current) = child {
940 let flex = get_flex(app, current);
941 total_flex += f64::from(flex);
942 if flex > 0 {
943 let flex_fraction = child_size(current, app, extent) / f64::from(flex);
944 max_flex_fraction_so_far = max_flex_fraction_so_far.max(flex_fraction);
945 } else {
946 inflexible_space += child_size(current, app, extent);
947 }
948 child = self.child_after(app, current);
949 }
950 return max_flex_fraction_so_far * total_flex + inflexible_space;
951 }
952
953 let is_horizontal = direction == Axis::Horizontal;
958 let layout_child = |app: &mut App, child: AnyRenderBox, constraints: BoxConstraints| {
959 let main_axis_size_from_constraints = if is_horizontal {
960 constraints.max_width
961 } else {
962 constraints.max_height
963 };
964 debug_assert_eq!(
967 get_flex(app, child) != 0 && extent.is_finite(),
968 main_axis_size_from_constraints.is_finite()
969 );
970 let max_main_axis_size = if main_axis_size_from_constraints.is_finite() {
971 main_axis_size_from_constraints
972 } else if is_horizontal {
973 child.get_max_intrinsic_width(app, f64::INFINITY)
974 } else {
975 child.get_max_intrinsic_height(app, f64::INFINITY)
976 };
977 let cross_size = child_size(child, app, max_main_axis_size);
978 if is_horizontal {
979 Size::new(max_main_axis_size, cross_size)
980 } else {
981 Size::new(cross_size, max_main_axis_size)
982 }
983 };
984 let constraints = if is_horizontal {
985 BoxConstraints::new().max_width(extent)
986 } else {
987 BoxConstraints::new().max_height(extent)
988 };
989 self.compute_sizes(
990 app,
991 constraints,
992 layout_child,
993 ChildLayoutHelper::get_dry_baseline,
994 )
995 .axis_size
996 .cross_axis_extent()
997 }
998
999 fn debug_check_constraints(
1003 self: RenderHandle<Self>,
1004 app: &App,
1005 constraints: BoxConstraints,
1006 ) -> Option<String> {
1007 let direction = self.direction(app);
1008 let max_main_size = match direction {
1009 Axis::Horizontal => constraints.max_width,
1010 Axis::Vertical => constraints.max_height,
1011 };
1012 let can_flex = max_main_size < f64::INFINITY;
1013 let mut child = self.first_child(app);
1014 while let Some(current) = child {
1015 if get_flex(app, current) > 0
1016 && !can_flex
1017 && (self.main_axis_size(app) == MainAxisSize::Max
1018 || get_fit(app, current) == FlexFit::Tight)
1019 {
1020 let (identity, axis, dimension) = match direction {
1021 Axis::Horizontal => ("row", "horizontal", "width"),
1022 Axis::Vertical => ("column", "vertical", "height"),
1023 };
1024 return Some(format!(
1025 "RenderFlex children have non-zero flex but incoming {dimension} constraints \
1026 are unbounded.\n\
1027 When a {identity} is in a parent that does not provide a finite {dimension} \
1028 constraint, for example if it is in a {axis} scrollable, it will try to \
1029 shrink-wrap its children along the {axis} axis. Setting a flex on a child \
1030 (e.g. using Expanded) indicates that the child is to expand to fill the \
1031 remaining space in the {axis} direction.\n\
1032 These two directives are mutually exclusive. If a parent is to shrink-wrap \
1033 its child, the child cannot simultaneously expand to fit its parent.\n\
1034 Consider setting mainAxisSize to MainAxisSize.min and using FlexFit.loose \
1035 fits for the flexible children (using Flexible rather than Expanded). This \
1036 will allow the flexible children to size themselves to less than the \
1037 infinite remaining space they would otherwise be forced to take, and then \
1038 will cause the RenderFlex to shrink-wrap the children rather than expanding \
1039 to fit the maximum constraints provided by the parent."
1040 ));
1041 }
1042 child = self.child_after(app, current);
1043 }
1044 None
1045 }
1046
1047 fn compute_sizes(
1055 self: RenderHandle<Self>,
1056 app: &mut App,
1057 constraints: BoxConstraints,
1058 mut layout_child: impl FnMut(&mut App, AnyRenderBox, BoxConstraints) -> Size,
1059 get_baseline: ChildBaselineGetter,
1060 ) -> LayoutSizes {
1061 debug_assert!(self.debug_has_necessary_directions(app));
1062
1063 let direction = self.direction(app);
1065 let max_main_size = self.get_main_size(app, constraints.biggest());
1066 let can_flex = max_main_size.is_finite();
1067 let non_flex_child_constraints = self.constraints_for_non_flex_child(app, constraints);
1068 let text_baseline = if self.is_baseline_aligned(app) {
1070 Some(self.text_baseline(app).expect(
1071 "To use CrossAxisAlignment.baseline, you must also specify which baseline to use \
1072 using the \"textBaseline\" argument.",
1073 ))
1074 } else {
1075 None
1076 };
1077
1078 let mut total_flex = 0;
1080 let mut first_flex_child: Option<AnyRenderBox> = None;
1081 let mut accumulated_ascent_descent = AscentDescent::NONE;
1082 let mut accumulated_size = AxisSize::new(
1084 self.spacing(app) * (self.child_count(app) as f64 - 1.0),
1085 0.0,
1086 );
1087 let mut child = self.first_child(app);
1088 while let Some(current) = child {
1089 let flex = get_flex(app, current);
1090 if can_flex && flex > 0 {
1091 total_flex += flex;
1092 first_flex_child.get_or_insert(current);
1093 } else {
1094 let child_size = AxisSize::from_size(
1095 layout_child(app, current, non_flex_child_constraints),
1096 direction,
1097 );
1098 accumulated_size = accumulated_size + child_size;
1099 let baseline_offset = text_baseline.and_then(|text_baseline| {
1101 get_baseline(app, current, non_flex_child_constraints, text_baseline)
1102 });
1103 accumulated_ascent_descent = accumulated_ascent_descent
1104 + AscentDescent::new(baseline_offset, child_size.cross_axis_extent());
1105 }
1106 child = self.child_after(app, current);
1107 }
1108
1109 debug_assert_eq!(total_flex == 0, first_flex_child.is_none());
1110 debug_assert!(first_flex_child.is_none() || can_flex);
1112
1113 let flex_space = (max_main_size - accumulated_size.main_axis_extent()).max(0.0);
1115 let space_per_flex = flex_space / total_flex as f64;
1116 let mut child = first_flex_child;
1117 while let Some(current) = child {
1118 if total_flex == 0 {
1119 break;
1120 }
1121 let flex = get_flex(app, current);
1122 if flex != 0 {
1123 total_flex -= flex;
1124 debug_assert!(space_per_flex.is_finite());
1125 let max_child_extent = space_per_flex * f64::from(flex);
1126 debug_assert!(
1127 get_fit(app, current) == FlexFit::Loose || max_child_extent < f64::INFINITY
1128 );
1129 let child_constraints =
1130 self.constraints_for_flex_child(app, current, constraints, max_child_extent);
1131 let child_size =
1132 AxisSize::from_size(layout_child(app, current, child_constraints), direction);
1133 accumulated_size = accumulated_size + child_size;
1134 let baseline_offset = text_baseline.and_then(|text_baseline| {
1135 get_baseline(app, current, child_constraints, text_baseline)
1136 });
1137 accumulated_ascent_descent = accumulated_ascent_descent
1138 + AscentDescent::new(baseline_offset, child_size.cross_axis_extent());
1139 }
1140 child = self.child_after(app, current);
1141 }
1142 debug_assert_eq!(total_flex, 0);
1143
1144 accumulated_size = accumulated_size
1146 + match accumulated_ascent_descent.0 {
1147 None => AxisSize::EMPTY,
1148 Some((ascent, descent)) => AxisSize::new(0.0, ascent + descent),
1149 };
1150
1151 let ideal_main_size = match self.main_axis_size(app) {
1152 MainAxisSize::Max if max_main_size.is_finite() => max_main_size,
1153 MainAxisSize::Max | MainAxisSize::Min => accumulated_size.main_axis_extent(),
1154 };
1155
1156 let constrained_size = AxisSize::new(ideal_main_size, accumulated_size.cross_axis_extent())
1157 .apply_constraints(constraints, direction);
1158 let space_per_flex = first_flex_child.map(|_| space_per_flex);
1159 debug_assert!(space_per_flex.is_none_or(f64::is_finite));
1160 LayoutSizes {
1161 axis_size: constrained_size,
1162 main_axis_free_space: constrained_size.main_axis_extent()
1163 - accumulated_size.main_axis_extent(),
1164 baseline_offset: accumulated_ascent_descent.baseline_offset(),
1165 space_per_flex,
1166 }
1167 }
1168
1169 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
1171 let constraints = self.constraints(app);
1172 if cfg!(debug_assertions)
1173 && let Some(error) = self.debug_check_constraints(app, constraints)
1174 {
1175 panic!("{error}");
1176 }
1177
1178 let sizes = self.compute_sizes(
1179 app,
1180 constraints,
1181 ChildLayoutHelper::layout_child,
1182 ChildLayoutHelper::get_baseline,
1183 );
1184
1185 let direction = self.direction(app);
1186 let cross_axis_extent = sizes.axis_size.cross_axis_extent();
1187 self.set_size(app, sizes.axis_size.to_size(direction));
1188 self.flex_data_mut(app).overflow = (-sizes.main_axis_free_space).max(0.0);
1189
1190 let remaining_space = sizes.main_axis_free_space.max(0.0);
1191 let flip_main_axis = self.flip_main_axis(app);
1192 let flip_cross_axis = self.flip_cross_axis(app);
1193 let cross_axis_alignment = self.cross_axis_alignment(app);
1194 let (leading_space, between_space) = self.main_axis_alignment(app).distribute_space(
1195 remaining_space,
1196 self.child_count(app),
1197 flip_main_axis,
1198 self.spacing(app),
1199 );
1200 let top_left_child = if flip_main_axis {
1201 self.last_child(app)
1202 } else {
1203 self.first_child(app)
1204 };
1205 let baseline_offset = sizes.baseline_offset;
1206 debug_assert!(
1207 baseline_offset.is_none()
1208 || (cross_axis_alignment == CrossAxisAlignment::Baseline
1209 && direction == Axis::Horizontal)
1210 );
1211 let text_baseline = self.text_baseline(app);
1212
1213 let mut child_main_position = leading_space;
1216 let mut child = top_left_child;
1217 while let Some(current) = child {
1218 let child_baseline_offset = match baseline_offset {
1219 Some(_) => current.get_distance_to_baseline(
1220 app,
1221 text_baseline.expect("a baseline-aligned flex has a text baseline"),
1222 true,
1223 ),
1224 None => None,
1225 };
1226 let child_size = current.size(app);
1227 let free_cross_space = cross_axis_extent - self.get_cross_size(app, child_size);
1228 let child_cross_position = if let (Some(baseline_offset), Some(child_baseline_offset)) =
1229 (baseline_offset, child_baseline_offset)
1230 {
1231 baseline_offset - child_baseline_offset
1232 } else if cross_axis_alignment == CrossAxisAlignment::Baseline
1233 && direction == Axis::Horizontal
1234 {
1235 CrossAxisAlignment::Start.get_child_cross_axis_offset(free_cross_space, false)
1239 } else {
1240 cross_axis_alignment.get_child_cross_axis_offset(free_cross_space, flip_cross_axis)
1242 };
1243 let offset = match direction {
1244 Axis::Horizontal => Offset::new(child_main_position, child_cross_position),
1245 Axis::Vertical => Offset::new(child_cross_position, child_main_position),
1246 };
1247 current
1248 .parent_data_of_mut::<FlexParentData>(app)
1249 .set_offset(offset);
1250 child_main_position += self.get_main_size(app, child_size) + between_space;
1251 child = if flip_main_axis {
1252 self.child_before(app, current)
1253 } else {
1254 self.child_after(app, current)
1255 };
1256 }
1257 }
1258
1259 fn paint(
1261 self: RenderHandle<Self>,
1262 app: &mut App,
1263 context: &mut PaintingContext,
1264 offset: Offset,
1265 ) {
1266 if !self.has_overflow(app) {
1267 self.default_paint(app, context, offset);
1268 return;
1269 }
1270
1271 let size = self.size(app);
1273 if size.is_empty() {
1274 return;
1275 }
1276
1277 let clip_behavior = self.clip_behavior(app);
1278 let old = self.flex_data(app).clip_rect_layer.layer();
1279 let layer = context.push_clip_rect(
1280 app,
1281 self.as_object().needs_compositing(app),
1282 offset,
1283 Offset::ZERO & size,
1284 |app, context, offset| self.default_paint(app, context, offset),
1285 clip_behavior,
1286 old,
1287 );
1288 LayerHandle::set_layer(
1289 app,
1290 |app| &mut self.flex_data_mut(app).clip_rect_layer,
1291 layer,
1292 );
1293 }
1294
1295 fn dispose(self: RenderHandle<Self>, app: &mut App) {
1297 LayerHandle::set_layer(
1298 app,
1299 |app| &mut self.flex_data_mut(app).clip_rect_layer,
1300 None,
1301 );
1302 crate::object::RenderObjectBase::dispose(self, app);
1303 }
1304
1305 fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
1307 if !child.parent_data_is::<FlexParentData>(app) {
1308 child.set_parent_data(app, FlexParentData::new());
1309 }
1310 }
1311
1312 fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1314 self.get_intrinsic_size(
1315 app,
1316 Axis::Horizontal,
1317 height,
1318 AnyRenderBox::get_min_intrinsic_width,
1319 )
1320 }
1321
1322 fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1324 self.get_intrinsic_size(
1325 app,
1326 Axis::Horizontal,
1327 height,
1328 AnyRenderBox::get_max_intrinsic_width,
1329 )
1330 }
1331
1332 fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1334 self.get_intrinsic_size(
1335 app,
1336 Axis::Vertical,
1337 width,
1338 AnyRenderBox::get_min_intrinsic_height,
1339 )
1340 }
1341
1342 fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1344 self.get_intrinsic_size(
1345 app,
1346 Axis::Vertical,
1347 width,
1348 AnyRenderBox::get_max_intrinsic_height,
1349 )
1350 }
1351
1352 fn compute_distance_to_actual_baseline(
1354 self: RenderHandle<Self>,
1355 app: &mut App,
1356 baseline: TextBaseline,
1357 ) -> Option<f64> {
1358 match self.direction(app) {
1359 Axis::Horizontal => {
1360 self.default_compute_distance_to_highest_actual_baseline(app, baseline)
1361 }
1362 Axis::Vertical => self.default_compute_distance_to_first_actual_baseline(app, baseline),
1363 }
1364 }
1365
1366 fn compute_dry_baseline(
1370 self: RenderHandle<Self>,
1371 app: &mut App,
1372 constraints: BoxConstraints,
1373 baseline: TextBaseline,
1374 ) -> Option<f64> {
1375 let sizes = self.compute_sizes(
1376 app,
1377 constraints,
1378 ChildLayoutHelper::dry_layout_child,
1379 ChildLayoutHelper::get_dry_baseline,
1380 );
1381
1382 if self.is_baseline_aligned(app) {
1383 return sizes.baseline_offset;
1384 }
1385
1386 match self.direction(app) {
1389 Axis::Horizontal => {
1390 self.compute_dry_distance_to_highest_baseline(app, constraints, baseline, &sizes)
1391 }
1392 Axis::Vertical => {
1393 self.compute_dry_distance_to_first_baseline(app, constraints, baseline, &sizes)
1394 }
1395 }
1396 }
1397
1398 fn compute_dry_distance_to_highest_baseline(
1402 self: RenderHandle<Self>,
1403 app: &mut App,
1404 constraints: BoxConstraints,
1405 baseline: TextBaseline,
1406 sizes: &LayoutSizes,
1407 ) -> Option<f64> {
1408 let non_flex_constraints = self.constraints_for_non_flex_child(app, constraints);
1413 let space_per_flex = sizes.space_per_flex;
1414 let constraints_for_child = |app: &App, child: AnyRenderBox| {
1415 let flex = get_flex(app, child);
1416 match space_per_flex {
1417 Some(space_per_flex) if flex > 0 => self.constraints_for_flex_child(
1418 app,
1419 child,
1420 constraints,
1421 f64::from(flex) * space_per_flex,
1422 ),
1423 _ => non_flex_constraints,
1424 }
1425 };
1426
1427 let flip_main_axis = self.flip_main_axis(app);
1429 let flip_cross_axis = self.flip_cross_axis(app);
1430 let top_left_child = if flip_main_axis {
1431 self.last_child(app)
1432 } else {
1433 self.first_child(app)
1434 };
1435 let is_baseline_aligned = self.is_baseline_aligned(app);
1436 let text_baseline = self.text_baseline(app);
1437 let baseline_offset = if is_baseline_aligned && text_baseline.is_some() {
1438 sizes.baseline_offset
1439 } else {
1440 None
1441 };
1442 let cross_axis_alignment = self.cross_axis_alignment(app);
1443 let direction = self.direction(app);
1444
1445 let mut min_baseline = BaselineOffset::NO_BASELINE;
1446
1447 let mut child = top_left_child;
1448 while let Some(current) = child {
1449 let child_constraints = constraints_for_child(app, current);
1450 let child_baseline = current.get_dry_baseline(app, child_constraints, baseline);
1451 if let Some(child_baseline) = child_baseline {
1452 let child_baseline_offset = match text_baseline {
1454 Some(text_baseline) if is_baseline_aligned => {
1455 current.get_dry_baseline(app, child_constraints, text_baseline)
1456 }
1457 _ => None,
1458 };
1459
1460 let child_cross_position =
1461 if let (Some(baseline_offset), Some(child_baseline_offset)) =
1462 (baseline_offset, child_baseline_offset)
1463 {
1464 baseline_offset - child_baseline_offset
1465 } else if cross_axis_alignment == CrossAxisAlignment::Baseline
1466 && direction == Axis::Horizontal
1467 {
1468 let child_size = current.get_dry_layout(app, child_constraints);
1472 CrossAxisAlignment::Start.get_child_cross_axis_offset(
1473 sizes.axis_size.cross_axis_extent()
1474 - self.get_cross_size(app, child_size),
1475 false,
1476 )
1477 } else {
1478 let child_size = current.get_dry_layout(app, child_constraints);
1480 cross_axis_alignment.get_child_cross_axis_offset(
1481 sizes.axis_size.cross_axis_extent()
1482 - self.get_cross_size(app, child_size),
1483 flip_cross_axis,
1484 )
1485 };
1486
1487 let candidate = BaselineOffset(Some(child_baseline)) + child_cross_position;
1490 min_baseline = min_baseline.min_of(candidate);
1491 }
1492
1493 child = if flip_main_axis {
1495 self.child_before(app, current)
1496 } else {
1497 self.child_after(app, current)
1498 };
1499 }
1500
1501 min_baseline.offset()
1502 }
1503
1504 fn compute_dry_distance_to_first_baseline(
1508 self: RenderHandle<Self>,
1509 app: &mut App,
1510 constraints: BoxConstraints,
1511 baseline: TextBaseline,
1512 sizes: &LayoutSizes,
1513 ) -> Option<f64> {
1514 let non_flex_constraints = self.constraints_for_non_flex_child(app, constraints);
1517 let space_per_flex = sizes.space_per_flex;
1518 let constraints_for_child = |app: &App, child: AnyRenderBox| {
1519 let flex = get_flex(app, child);
1520 match space_per_flex {
1521 Some(space_per_flex) if flex > 0 => self.constraints_for_flex_child(
1522 app,
1523 child,
1524 constraints,
1525 f64::from(flex) * space_per_flex,
1526 ),
1527 _ => non_flex_constraints,
1528 }
1529 };
1530
1531 let remaining_space = sizes.main_axis_free_space.max(0.0);
1533 let flip_main_axis = self.flip_main_axis(app);
1534 let (leading_space, between_space) = self.main_axis_alignment(app).distribute_space(
1535 remaining_space,
1536 self.child_count(app),
1537 flip_main_axis,
1538 self.spacing(app),
1539 );
1540
1541 let mut main_positions: HashMap<AnyRenderBox, f64> = HashMap::new();
1542 let start_child = if flip_main_axis {
1543 self.last_child(app)
1544 } else {
1545 self.first_child(app)
1546 };
1547 let mut pos = leading_space;
1548 let mut child = start_child;
1549 while let Some(current) = child {
1550 main_positions.insert(current, pos);
1551 let cc = constraints_for_child(app, current);
1552 let cs = current.get_dry_layout(app, cc);
1553 pos += self.get_main_size(app, cs) + between_space;
1554 child = if flip_main_axis {
1555 self.child_before(app, current)
1556 } else {
1557 self.child_after(app, current)
1558 };
1559 }
1560
1561 let mut child = self.first_child(app);
1564 while let Some(current) = child {
1565 let cc = constraints_for_child(app, current);
1566 if let Some(child_baseline) = current.get_dry_baseline(app, cc, baseline) {
1567 let position = main_positions.get(¤t).copied();
1568 return Some(child_baseline + position.unwrap_or(leading_space));
1570 }
1571 child = self.child_after(app, current);
1572 }
1573 None
1574 }
1575
1576 fn compute_dry_layout(
1578 self: RenderHandle<Self>,
1579 app: &mut App,
1580 constraints: BoxConstraints,
1581 ) -> Size {
1582 if cfg!(debug_assertions)
1583 && let Some(error) = self.debug_check_constraints(app, constraints)
1584 {
1585 self.debug_cannot_compute_dry_layout(DryLayoutFailure::Error(&error));
1586 return Size::ZERO;
1587 }
1588 let direction = self.direction(app);
1589 self.compute_sizes(
1590 app,
1591 constraints,
1592 ChildLayoutHelper::dry_layout_child,
1593 ChildLayoutHelper::get_dry_baseline,
1594 )
1595 .axis_size
1596 .to_size(direction)
1597 }
1598
1599 fn hit_test_children(
1601 self: RenderHandle<Self>,
1602 app: &mut App,
1603 result: &mut BoxHitTestResult<'_>,
1604 position: Offset,
1605 ) -> bool {
1606 self.default_hit_test_children(app, result, position)
1607 }
1608}
1609
1610fn get_flex(app: &App, child: AnyRenderBox) -> i32 {
1612 child
1613 .as_object()
1614 .parent_data_of::<FlexParentData>(app)
1615 .flex
1616 .unwrap_or(0)
1617}
1618
1619fn get_fit(app: &App, child: AnyRenderBox) -> FlexFit {
1621 child
1622 .as_object()
1623 .parent_data_of::<FlexParentData>(app)
1624 .fit
1625 .unwrap_or(FlexFit::Tight)
1626}
1627
1628pub struct RenderFlex {
1663 render_object: RenderObjectData,
1664 render_box: RenderBoxData,
1665 container: ContainerRenderObjectData<AnyRenderBox>,
1666 flex: RenderFlexData,
1667}
1668
1669impl RenderFlex {
1670 pub fn new(app: &mut App) -> RenderHandle<RenderFlex> {
1677 RenderHandle::new_box(
1678 app,
1679 RenderFlex {
1680 render_object: RenderObjectData::new(),
1681 render_box: RenderBoxData::new(),
1682 container: ContainerRenderObjectData::new(),
1683 flex: RenderFlexData::new(),
1684 },
1685 )
1686 }
1687
1688 pub fn direction(self: RenderHandle<Self>, app: &App) -> Axis {
1690 RenderFlexMixin::direction(self, app)
1691 }
1692
1693 pub fn set_direction(self: RenderHandle<Self>, app: &mut App, value: Axis) {
1695 RenderFlexMixin::set_direction(self, app, value)
1696 }
1697
1698 pub fn main_axis_alignment(self: RenderHandle<Self>, app: &App) -> MainAxisAlignment {
1700 RenderFlexMixin::main_axis_alignment(self, app)
1701 }
1702
1703 pub fn set_main_axis_alignment(
1705 self: RenderHandle<Self>,
1706 app: &mut App,
1707 value: MainAxisAlignment,
1708 ) {
1709 RenderFlexMixin::set_main_axis_alignment(self, app, value)
1710 }
1711
1712 pub fn main_axis_size(self: RenderHandle<Self>, app: &App) -> MainAxisSize {
1714 RenderFlexMixin::main_axis_size(self, app)
1715 }
1716
1717 pub fn set_main_axis_size(self: RenderHandle<Self>, app: &mut App, value: MainAxisSize) {
1719 RenderFlexMixin::set_main_axis_size(self, app, value)
1720 }
1721
1722 pub fn cross_axis_alignment(self: RenderHandle<Self>, app: &App) -> CrossAxisAlignment {
1724 RenderFlexMixin::cross_axis_alignment(self, app)
1725 }
1726
1727 pub fn set_cross_axis_alignment(
1729 self: RenderHandle<Self>,
1730 app: &mut App,
1731 value: CrossAxisAlignment,
1732 ) {
1733 RenderFlexMixin::set_cross_axis_alignment(self, app, value)
1734 }
1735
1736 pub fn text_direction(self: RenderHandle<Self>, app: &App) -> Option<TextDirection> {
1738 RenderFlexMixin::text_direction(self, app)
1739 }
1740
1741 pub fn set_text_direction(
1743 self: RenderHandle<Self>,
1744 app: &mut App,
1745 value: Option<TextDirection>,
1746 ) {
1747 RenderFlexMixin::set_text_direction(self, app, value)
1748 }
1749
1750 pub fn vertical_direction(self: RenderHandle<Self>, app: &App) -> VerticalDirection {
1752 RenderFlexMixin::vertical_direction(self, app)
1753 }
1754
1755 pub fn set_vertical_direction(
1757 self: RenderHandle<Self>,
1758 app: &mut App,
1759 value: VerticalDirection,
1760 ) {
1761 RenderFlexMixin::set_vertical_direction(self, app, value)
1762 }
1763
1764 pub fn text_baseline(self: RenderHandle<Self>, app: &App) -> Option<TextBaseline> {
1766 RenderFlexMixin::text_baseline(self, app)
1767 }
1768
1769 pub fn set_text_baseline(self: RenderHandle<Self>, app: &mut App, value: Option<TextBaseline>) {
1771 RenderFlexMixin::set_text_baseline(self, app, value)
1772 }
1773
1774 pub fn clip_behavior(self: RenderHandle<Self>, app: &App) -> Clip {
1776 RenderFlexMixin::clip_behavior(self, app)
1777 }
1778
1779 pub fn set_clip_behavior(self: RenderHandle<Self>, app: &mut App, value: Clip) {
1781 RenderFlexMixin::set_clip_behavior(self, app, value)
1782 }
1783
1784 pub fn spacing(self: RenderHandle<Self>, app: &App) -> f64 {
1786 RenderFlexMixin::spacing(self, app)
1787 }
1788
1789 pub fn set_spacing(self: RenderHandle<Self>, app: &mut App, value: f64) {
1791 RenderFlexMixin::set_spacing(self, app, value)
1792 }
1793}
1794
1795impl ContainerRenderObjectMixin for RenderFlex {
1796 type ChildType = AnyRenderBox;
1797 type ParentDataType = FlexParentData;
1798
1799 fn container_data(
1800 self: RenderHandle<Self>,
1801 app: &App,
1802 ) -> &ContainerRenderObjectData<AnyRenderBox> {
1803 &self.get(app).container
1804 }
1805
1806 fn container_data_mut(
1807 self: RenderHandle<Self>,
1808 app: &mut App,
1809 ) -> &mut ContainerRenderObjectData<AnyRenderBox> {
1810 &mut self.get_mut(app).container
1811 }
1812}
1813
1814impl RenderBoxContainerDefaultsMixin for RenderFlex {}
1815
1816impl RenderFlexMixin for RenderFlex {
1817 fn flex_data(self: RenderHandle<Self>, app: &App) -> &RenderFlexData {
1818 &self.get(app).flex
1819 }
1820
1821 fn flex_data_mut(self: RenderHandle<Self>, app: &mut App) -> &mut RenderFlexData {
1822 &mut self.get_mut(app).flex
1823 }
1824}
1825
1826impl RenderObject for RenderFlex {
1827 crate::render_object_accessors!();
1828
1829 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
1830 RenderFlexMixin::perform_layout(self, app)
1831 }
1832
1833 fn visit_children(
1834 self: RenderHandle<Self>,
1835 app: &App,
1836 visitor: &mut dyn FnMut(AnyRenderObject),
1837 ) {
1838 ContainerRenderObjectMixin::visit_children(self, app, visitor)
1839 }
1840
1841 fn did_attach(self: RenderHandle<Self>, app: &mut App, owner: Handle<PipelineOwner>) {
1842 ContainerRenderObjectMixin::did_attach(self, app, owner)
1843 }
1844
1845 fn did_detach(self: RenderHandle<Self>, app: &mut App) {
1846 ContainerRenderObjectMixin::did_detach(self, app)
1847 }
1848
1849 fn redepth_children(self: RenderHandle<Self>, app: &mut App) {
1850 ContainerRenderObjectMixin::redepth_children(self, app)
1851 }
1852
1853 fn paint(
1854 self: RenderHandle<Self>,
1855 app: &mut App,
1856 context: &mut PaintingContext,
1857 offset: Offset,
1858 ) {
1859 RenderFlexMixin::paint(self, app, context, offset)
1860 }
1861
1862 fn dispose(self: RenderHandle<Self>, app: &mut App) {
1863 RenderFlexMixin::dispose(self, app)
1864 }
1865}
1866
1867impl RenderBox for RenderFlex {
1868 crate::render_box_accessors!();
1869
1870 fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
1871 RenderFlexMixin::setup_parent_data(self, app, child)
1872 }
1873
1874 fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1875 RenderFlexMixin::compute_min_intrinsic_width(self, app, height)
1876 }
1877
1878 fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1879 RenderFlexMixin::compute_max_intrinsic_width(self, app, height)
1880 }
1881
1882 fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1883 RenderFlexMixin::compute_min_intrinsic_height(self, app, width)
1884 }
1885
1886 fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1887 RenderFlexMixin::compute_max_intrinsic_height(self, app, width)
1888 }
1889
1890 fn compute_distance_to_actual_baseline(
1891 self: RenderHandle<Self>,
1892 app: &mut App,
1893 baseline: TextBaseline,
1894 ) -> Option<f64> {
1895 RenderFlexMixin::compute_distance_to_actual_baseline(self, app, baseline)
1896 }
1897
1898 fn compute_dry_baseline(
1899 self: RenderHandle<Self>,
1900 app: &mut App,
1901 constraints: BoxConstraints,
1902 baseline: TextBaseline,
1903 ) -> Option<f64> {
1904 RenderFlexMixin::compute_dry_baseline(self, app, constraints, baseline)
1905 }
1906
1907 fn compute_dry_layout(
1908 self: RenderHandle<Self>,
1909 app: &mut App,
1910 constraints: BoxConstraints,
1911 ) -> Size {
1912 RenderFlexMixin::compute_dry_layout(self, app, constraints)
1913 }
1914
1915 fn hit_test_children(
1916 self: RenderHandle<Self>,
1917 app: &mut App,
1918 result: &mut BoxHitTestResult<'_>,
1919 position: Offset,
1920 ) -> bool {
1921 RenderFlexMixin::hit_test_children(self, app, result, position)
1922 }
1923}
1924
1925#[cfg(test)]
1926mod tests {
1927 use inset_foundation::AppCell;
1928 use std::any::Any;
1929
1930 use inset_foundation::Handle;
1931 use inset_gestures::{HitTestEntry, HitTestResult};
1932
1933 use super::*;
1934 use crate::box_::BoxHitTestEntry;
1935 use crate::layer::{ContainerLayer, ErasedLayer, OffsetLayer, PictureLayer};
1936 use crate::pipeline_owner::PipelineOwner;
1937 use crate::proxy_box::RenderRepaintBoundary;
1938 use inset_embedder::valo::Op;
1939
1940 struct SizedTestBox {
1943 render_object: RenderObjectData,
1944 render_box: RenderBoxData,
1945 preferred_size: Size,
1946 }
1947
1948 impl RenderObject for SizedTestBox {
1949 crate::render_object_accessors!();
1950
1951 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
1952 let preferred_size = self.get(app).preferred_size;
1953 let size = self.constraints(app).constrain(preferred_size);
1954 self.set_size(app, size);
1955 }
1956 }
1957
1958 impl RenderBox for SizedTestBox {
1959 crate::render_box_accessors!();
1960
1961 fn hit_test_self(self: RenderHandle<Self>, _app: &App, _position: Offset) -> bool {
1962 true
1963 }
1964 }
1965
1966 fn sized_box(app: &mut App, size: Size) -> AnyRenderBox {
1967 RenderHandle::new_box(
1968 app,
1969 SizedTestBox {
1970 render_object: RenderObjectData::new(),
1971 render_box: RenderBoxData::new(),
1972 preferred_size: size,
1973 },
1974 )
1975 .as_box()
1976 }
1977
1978 fn row(app: &mut App, children: Vec<AnyRenderBox>) -> RenderHandle<RenderFlex> {
1980 let flex = RenderFlex::new(app);
1981 flex.set_text_direction(app, Some(TextDirection::Ltr));
1982 flex.add_all(app, Some(children));
1983 flex
1984 }
1985
1986 fn offset_of(app: &App, child: AnyRenderBox) -> Offset {
1987 child.box_parent_data(app).offset
1988 }
1989
1990 fn schedule_root_paint(app: &mut App, node: AnyRenderObject) {
1991 let root = OffsetLayer::new(app, Offset::ZERO);
1992 root.as_layer().attach(app, node.id());
1993 node.schedule_initial_paint(app, root.as_container_layer());
1994 }
1995
1996 fn first_frame(
1998 app: &mut App,
1999 root: AnyRenderBox,
2000 constraints: BoxConstraints,
2001 ) -> Handle<PipelineOwner> {
2002 let owner = PipelineOwner::new(app, None, None);
2003 owner.set_root_node(app, Some(root.as_object()));
2004 root.layout(app, constraints, false);
2005 schedule_root_paint(app, root.as_object());
2006 owner.flush_compositing_bits(app);
2007 owner.flush_paint(app);
2008 owner
2009 }
2010
2011 fn picture_has_clip(app: &App, boundary: AnyRenderObject) -> bool {
2012 let layer = boundary.debug_layer(app).expect("painted");
2013 layer
2014 .depth_first_iterate_children(app)
2015 .into_iter()
2016 .filter_map(|child| {
2017 app.handle::<PictureLayer>(child.id())
2018 .and_then(|picture| picture.picture(app).map(|p| p.ops().to_vec()))
2019 })
2020 .flatten()
2021 .any(|op| matches!(op, Op::ClipPath { .. }))
2022 }
2023
2024 fn hit_boxes(result: &HitTestResult) -> Vec<AnyRenderBox> {
2025 result
2026 .path()
2027 .iter()
2028 .filter_map(|entry: &HitTestEntry| {
2029 let target: &dyn Any = entry.target();
2030 target
2031 .downcast_ref::<BoxHitTestEntry>()
2032 .map(BoxHitTestEntry::target)
2033 })
2034 .collect()
2035 }
2036
2037 #[test]
2040 fn child_list_insert_move_and_remove() {
2041 let cell = AppCell::new();
2042 let mut app = cell.borrow_mut();
2043 let first = sized_box(&mut app, Size::new(10.0, 10.0));
2044 let middle = sized_box(&mut app, Size::new(10.0, 10.0));
2045 let last = sized_box(&mut app, Size::new(10.0, 10.0));
2046 let flex = RenderFlex::new(&mut app);
2047
2048 flex.add(&mut app, first);
2049 flex.insert(&mut app, last, Some(first));
2050 flex.insert(&mut app, middle, Some(first));
2051 assert_eq!(flex.child_count(&app), 3);
2052 assert_eq!(flex.children_as_list(&app), vec![first, middle, last]);
2053 assert_eq!(flex.first_child(&app), Some(first));
2054 assert_eq!(flex.last_child(&app), Some(last));
2055 assert_eq!(flex.child_after(&app, first), Some(middle));
2056 assert_eq!(flex.child_before(&app, last), Some(middle));
2057
2058 let mut visited = Vec::new();
2059 flex.as_object()
2060 .visit_children(&app, &mut |child| visited.push(child));
2061 assert_eq!(
2062 visited,
2063 vec![first.as_object(), middle.as_object(), last.as_object()]
2064 );
2065
2066 flex.move_child(&mut app, first, Some(last));
2067 assert_eq!(flex.children_as_list(&app), vec![middle, last, first]);
2068 flex.move_child(&mut app, first, None);
2069 assert_eq!(flex.children_as_list(&app), vec![first, middle, last]);
2070
2071 flex.remove(&mut app, middle);
2072 assert_eq!(flex.children_as_list(&app), vec![first, last]);
2073 assert_eq!(flex.child_count(&app), 2);
2074 assert!(middle.as_object().parent(&app).is_none());
2075
2076 flex.remove_all(&mut app);
2077 assert_eq!(flex.child_count(&app), 0);
2078 assert!(flex.first_child(&app).is_none());
2079 assert!(flex.last_child(&app).is_none());
2080 assert!(first.as_object().parent(&app).is_none());
2081 assert!(last.as_object().parent(&app).is_none());
2082 }
2083
2084 struct IntrinsicTestBox {
2087 render_object: RenderObjectData,
2088 render_box: RenderBoxData,
2089 intrinsic: Size,
2090 }
2091
2092 impl RenderObject for IntrinsicTestBox {
2093 crate::render_object_accessors!();
2094
2095 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
2096 let intrinsic = self.get(app).intrinsic;
2097 let size = self.constraints(app).constrain(intrinsic);
2098 self.set_size(app, size);
2099 }
2100 }
2101
2102 impl RenderBox for IntrinsicTestBox {
2103 crate::render_box_accessors!();
2104
2105 fn compute_min_intrinsic_width(
2106 self: RenderHandle<Self>,
2107 app: &mut App,
2108 _height: f64,
2109 ) -> f64 {
2110 self.get(app).intrinsic.width()
2111 }
2112
2113 fn compute_max_intrinsic_width(
2114 self: RenderHandle<Self>,
2115 app: &mut App,
2116 _height: f64,
2117 ) -> f64 {
2118 self.get(app).intrinsic.width()
2119 }
2120
2121 fn compute_min_intrinsic_height(
2122 self: RenderHandle<Self>,
2123 app: &mut App,
2124 _width: f64,
2125 ) -> f64 {
2126 self.get(app).intrinsic.height()
2127 }
2128
2129 fn compute_max_intrinsic_height(
2130 self: RenderHandle<Self>,
2131 app: &mut App,
2132 _width: f64,
2133 ) -> f64 {
2134 self.get(app).intrinsic.height()
2135 }
2136
2137 fn compute_dry_layout(
2138 self: RenderHandle<Self>,
2139 app: &mut App,
2140 constraints: BoxConstraints,
2141 ) -> Size {
2142 constraints.constrain(self.get(app).intrinsic)
2143 }
2144 }
2145
2146 fn intrinsic_box(app: &mut App, intrinsic: Size) -> AnyRenderBox {
2147 RenderHandle::new_box(
2148 app,
2149 IntrinsicTestBox {
2150 render_object: RenderObjectData::new(),
2151 render_box: RenderBoxData::new(),
2152 intrinsic,
2153 },
2154 )
2155 .as_box()
2156 }
2157
2158 #[test]
2162 fn row_intrinsics_add_the_inflexible_space_to_the_largest_flex_fraction() {
2163 let cell = AppCell::new();
2164 let mut app = cell.borrow_mut();
2165 let fixed = intrinsic_box(&mut app, Size::new(50.0, 20.0));
2166 let flexible = intrinsic_box(&mut app, Size::new(30.0, 10.0));
2167 let flex = row(&mut app, vec![fixed, flexible]);
2168 flexible.parent_data_of_mut::<FlexParentData>(&mut app).flex = Some(1);
2169
2170 let row = flex.as_box();
2171 assert_eq!(row.get_max_intrinsic_width(&mut app, f64::INFINITY), 80.0);
2172 assert_eq!(row.get_min_intrinsic_width(&mut app, f64::INFINITY), 80.0);
2173 assert_eq!(row.get_max_intrinsic_height(&mut app, 100.0), 20.0);
2174 }
2175
2176 #[test]
2178 fn row_intrinsics_include_the_spacing() {
2179 let cell = AppCell::new();
2180 let mut app = cell.borrow_mut();
2181 let first = intrinsic_box(&mut app, Size::new(50.0, 20.0));
2182 let second = intrinsic_box(&mut app, Size::new(30.0, 10.0));
2183 let flex = row(&mut app, vec![first, second]);
2184 flex.set_spacing(&mut app, 10.0);
2185 assert_eq!(
2186 flex.as_box()
2187 .get_max_intrinsic_width(&mut app, f64::INFINITY),
2188 90.0
2189 );
2190 }
2191
2192 #[test]
2195 fn row_dry_layout_follows_the_main_axis_size() {
2196 for (main_axis_size, expected) in [
2197 (MainAxisSize::Max, Size::new(200.0, 20.0)),
2198 (MainAxisSize::Min, Size::new(80.0, 20.0)),
2199 ] {
2200 let cell = AppCell::new();
2201 let mut app = cell.borrow_mut();
2202 let fixed = intrinsic_box(&mut app, Size::new(50.0, 20.0));
2203 let flexible = intrinsic_box(&mut app, Size::new(30.0, 10.0));
2204 let flex = row(&mut app, vec![fixed, flexible]);
2205 flex.set_main_axis_size(&mut app, main_axis_size);
2206 {
2207 let parent_data = flexible.parent_data_of_mut::<FlexParentData>(&mut app);
2208 parent_data.flex = Some(1);
2209 parent_data.fit = Some(FlexFit::Loose);
2210 }
2211
2212 let constraints = BoxConstraints::new().max_width(200.0).max_height(100.0);
2213 let dry = flex.as_box().get_dry_layout(&mut app, constraints);
2214 flex.layout(&mut app, constraints, false);
2215 assert_eq!(dry, expected);
2216 assert_eq!(dry, flex.size(&app), "the dry layout is the real layout");
2217 }
2218 }
2219
2220 #[test]
2223 fn row_lays_out_fixed_and_flexible_children_with_spacing() {
2224 let cell = AppCell::new();
2225 let mut app = cell.borrow_mut();
2226 let fixed = sized_box(&mut app, Size::new(50.0, 30.0));
2227 let flexible = sized_box(&mut app, Size::new(20.0, 10.0));
2228 let flex = row(&mut app, vec![fixed, flexible]);
2229 flexible.parent_data_of_mut::<FlexParentData>(&mut app).flex = Some(1);
2230 flex.set_spacing(&mut app, 10.0);
2231
2232 flex.layout(
2233 &mut app,
2234 BoxConstraints::tight(Size::new(200.0, 100.0)),
2235 false,
2236 );
2237
2238 assert_eq!(flex.size(&app), Size::new(200.0, 100.0));
2239 assert_eq!(fixed.size(&app), Size::new(50.0, 30.0));
2240 assert_eq!(flexible.size(&app), Size::new(140.0, 10.0));
2242 assert_eq!(offset_of(&app, fixed), Offset::new(0.0, 35.0));
2243 assert_eq!(offset_of(&app, flexible), Offset::new(60.0, 45.0));
2244 }
2245
2246 #[test]
2248 fn main_axis_alignment_positions_children() {
2249 let cases = [
2250 (MainAxisAlignment::Start, 0.0, 20.0),
2251 (MainAxisAlignment::End, 60.0, 80.0),
2252 (MainAxisAlignment::Center, 30.0, 50.0),
2253 (MainAxisAlignment::SpaceBetween, 0.0, 80.0),
2254 (MainAxisAlignment::SpaceAround, 15.0, 65.0),
2255 (MainAxisAlignment::SpaceEvenly, 20.0, 60.0),
2256 ];
2257 for (alignment, first_x, second_x) in cases {
2258 let cell = AppCell::new();
2259 let mut app = cell.borrow_mut();
2260 let first = sized_box(&mut app, Size::new(20.0, 20.0));
2261 let second = sized_box(&mut app, Size::new(20.0, 20.0));
2262 let flex = row(&mut app, vec![first, second]);
2263 flex.set_main_axis_alignment(&mut app, alignment);
2264
2265 flex.layout(
2266 &mut app,
2267 BoxConstraints::tight(Size::new(100.0, 20.0)),
2268 false,
2269 );
2270
2271 assert_eq!(
2272 (offset_of(&app, first).dx(), offset_of(&app, second).dx()),
2273 (first_x, second_x),
2274 "{alignment:?}"
2275 );
2276 }
2277 }
2278
2279 #[test]
2282 fn cross_axis_alignment_positions_children() {
2283 let cases = [
2284 (CrossAxisAlignment::Start, 0.0, 20.0),
2285 (CrossAxisAlignment::Center, 40.0, 20.0),
2286 (CrossAxisAlignment::End, 80.0, 20.0),
2287 (CrossAxisAlignment::Stretch, 0.0, 100.0),
2288 (CrossAxisAlignment::Baseline, 0.0, 20.0),
2289 ];
2290 for (alignment, y, height) in cases {
2291 let cell = AppCell::new();
2292 let mut app = cell.borrow_mut();
2293 let child = sized_box(&mut app, Size::new(20.0, 20.0));
2294 let flex = row(&mut app, vec![child]);
2295 flex.set_cross_axis_alignment(&mut app, alignment);
2296 if alignment == CrossAxisAlignment::Baseline {
2297 flex.set_text_baseline(&mut app, Some(TextBaseline::Alphabetic));
2298 }
2299
2300 flex.layout(
2301 &mut app,
2302 BoxConstraints::tight(Size::new(100.0, 100.0)),
2303 false,
2304 );
2305
2306 assert_eq!(offset_of(&app, child).dy(), y, "{alignment:?}");
2307 assert_eq!(child.size(&app).height(), height, "{alignment:?}");
2308 }
2309 }
2310
2311 #[test]
2313 fn overflow_is_clipped() {
2314 for (clip_behavior, clips) in [(Clip::None, false), (Clip::HardEdge, true)] {
2315 let cell = AppCell::new();
2316 let mut app = cell.borrow_mut();
2317 let first = sized_box(&mut app, Size::new(80.0, 20.0));
2318 let second = sized_box(&mut app, Size::new(80.0, 20.0));
2319 let flex = row(&mut app, vec![first, second]);
2320 flex.set_clip_behavior(&mut app, clip_behavior);
2321 let root = RenderRepaintBoundary::new(&mut app, Some(flex.as_box()));
2322
2323 let owner = first_frame(
2324 &mut app,
2325 root.as_box(),
2326 BoxConstraints::tight(Size::new(100.0, 20.0)),
2327 );
2328
2329 assert_eq!(flex.size(&app), Size::new(100.0, 20.0));
2330 let clipped = picture_has_clip(&app, root.as_object());
2331 assert_eq!(clipped, clips, "{clip_behavior:?}");
2332 let _ = owner;
2333 }
2334 }
2335
2336 #[test]
2338 fn hit_test_reaches_the_child_under_the_position() {
2339 let cell = AppCell::new();
2340 let mut app = cell.borrow_mut();
2341 let first = sized_box(&mut app, Size::new(50.0, 50.0));
2342 let second = sized_box(&mut app, Size::new(50.0, 50.0));
2343 let flex = row(&mut app, vec![first, second]);
2344
2345 flex.layout(
2346 &mut app,
2347 BoxConstraints::tight(Size::new(100.0, 50.0)),
2348 false,
2349 );
2350
2351 let mut result = HitTestResult::new();
2352 let hit = flex.hit_test(
2353 &mut app,
2354 &mut BoxHitTestResult::wrap(&mut result),
2355 Offset::new(75.0, 25.0),
2356 );
2357 assert!(hit);
2358 assert_eq!(hit_boxes(&result), vec![second, flex.as_box()]);
2359 }
2360
2361 const GRID_SIZE: Size = Size::new(10.0, 10.0);
2363
2364 struct FlowBaselineTestBox {
2367 render_object: RenderObjectData,
2368 render_box: RenderBoxData,
2369 grid_count: usize,
2370 baseline_placer: fn(f64) -> Option<f64>,
2371 }
2372
2373 impl FlowBaselineTestBox {
2374 fn line_grid_count(&self, width: f64) -> usize {
2375 let grids_per_line = if width >= self.grid_count as f64 * GRID_SIZE.width() {
2376 self.grid_count
2377 } else {
2378 (width / GRID_SIZE.width()) as usize
2379 };
2380 grids_per_line.max(1)
2381 }
2382
2383 fn line_count(&self, width: f64) -> usize {
2384 (self.grid_count as f64 / self.line_grid_count(width) as f64).ceil() as usize
2385 }
2386 }
2387
2388 impl RenderObject for FlowBaselineTestBox {
2389 crate::render_object_accessors!();
2390
2391 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
2392 let constraints = self.constraints(app);
2393 let size = RenderBox::compute_dry_layout(self, app, constraints);
2394 self.set_size(app, size);
2395 }
2396 }
2397
2398 impl RenderBox for FlowBaselineTestBox {
2399 crate::render_box_accessors!();
2400
2401 fn compute_min_intrinsic_width(
2402 self: RenderHandle<Self>,
2403 _app: &mut App,
2404 _height: f64,
2405 ) -> f64 {
2406 GRID_SIZE.width()
2407 }
2408
2409 fn compute_max_intrinsic_width(
2410 self: RenderHandle<Self>,
2411 app: &mut App,
2412 _height: f64,
2413 ) -> f64 {
2414 GRID_SIZE.width() * self.get(app).grid_count as f64
2415 }
2416
2417 fn compute_min_intrinsic_height(
2418 self: RenderHandle<Self>,
2419 app: &mut App,
2420 width: f64,
2421 ) -> f64 {
2422 GRID_SIZE.height() * self.get(app).line_count(width) as f64
2423 }
2424
2425 fn compute_max_intrinsic_height(
2426 self: RenderHandle<Self>,
2427 app: &mut App,
2428 width: f64,
2429 ) -> f64 {
2430 RenderBox::compute_min_intrinsic_height(self, app, width)
2431 }
2432
2433 fn compute_dry_layout(
2434 self: RenderHandle<Self>,
2435 app: &mut App,
2436 constraints: BoxConstraints,
2437 ) -> Size {
2438 let this = self.get(app);
2439 constraints.constrain(Size::new(
2440 GRID_SIZE.width() * this.line_grid_count(constraints.max_width) as f64,
2441 GRID_SIZE.height() * this.line_count(constraints.max_width) as f64,
2442 ))
2443 }
2444
2445 fn compute_dry_baseline(
2446 self: RenderHandle<Self>,
2447 app: &mut App,
2448 constraints: BoxConstraints,
2449 _baseline: TextBaseline,
2450 ) -> Option<f64> {
2451 let height = self.as_box().get_dry_layout(app, constraints).height();
2452 (self.get(app).baseline_placer)(height)
2453 }
2454
2455 fn compute_distance_to_actual_baseline(
2456 self: RenderHandle<Self>,
2457 app: &mut App,
2458 _baseline: TextBaseline,
2459 ) -> Option<f64> {
2460 let height = self.size(app).height();
2461 (self.get(app).baseline_placer)(height)
2462 }
2463 }
2464
2465 fn flow_baseline_box(
2466 app: &mut App,
2467 grid_count: usize,
2468 baseline_placer: fn(f64) -> Option<f64>,
2469 ) -> AnyRenderBox {
2470 RenderHandle::new_box(
2471 app,
2472 FlowBaselineTestBox {
2473 render_object: RenderObjectData::new(),
2474 render_box: RenderBoxData::new(),
2475 grid_count,
2476 baseline_placer,
2477 },
2478 )
2479 .as_box()
2480 }
2481
2482 fn baseline_row(app: &mut App, children: Vec<AnyRenderBox>) -> RenderHandle<RenderFlex> {
2484 let flex = row(app, children);
2485 flex.set_cross_axis_alignment(app, CrossAxisAlignment::Baseline);
2486 flex.set_text_baseline(app, Some(TextBaseline::Alphabetic));
2487 flex
2488 }
2489
2490 fn lay_out_root(
2493 app: &mut App,
2494 root: AnyRenderBox,
2495 constraints: BoxConstraints,
2496 ) -> Handle<PipelineOwner> {
2497 let owner = PipelineOwner::new(app, None, None);
2498 owner.set_root_node(app, Some(root.as_object()));
2499 root.as_object().schedule_initial_layout(app);
2500 root.layout(app, constraints, false);
2501 owner
2502 }
2503
2504 fn set_flex(app: &mut App, child: AnyRenderBox, flex: i32, fit: FlexFit) {
2506 let parent_data = child.parent_data_of_mut::<FlexParentData>(app);
2507 parent_data.flex = Some(flex);
2508 parent_data.fit = Some(fit);
2509 }
2510
2511 #[test]
2514 fn children_with_no_baselines_are_top_aligned() {
2515 let cell = AppCell::new();
2516 let mut app = cell.borrow_mut();
2517 let first = sized_box(&mut app, Size::new(100.0, 100.0));
2518 let second = sized_box(&mut app, Size::new(100.0, 100.0));
2519 let flex = baseline_row(&mut app, vec![first, second]);
2520 flex.set_vertical_direction(&mut app, VerticalDirection::Up);
2521 let owner = lay_out_root(
2522 &mut app,
2523 flex.as_box(),
2524 BoxConstraints::tight(Size::new(800.0, 600.0)),
2525 );
2526
2527 assert_eq!(offset_of(&app, first).dy(), 0.0);
2529 assert_eq!(offset_of(&app, second).dy(), 0.0);
2530
2531 flex.set_vertical_direction(&mut app, VerticalDirection::Down);
2532 owner.flush_layout(&mut app);
2533 assert_eq!(offset_of(&app, first).dy(), 0.0);
2534 assert_eq!(offset_of(&app, second).dy(), 0.0);
2535 }
2536
2537 #[test]
2541 fn a_column_s_baseline_is_its_first_child_s_baseline() {
2542 let cell = AppCell::new();
2543 let mut app = cell.borrow_mut();
2544 let square = BoxConstraints::tight_for(Some(100.0), Some(100.0));
2545 let boxed = |app: &mut App, baseline: Option<AnyRenderBox>| {
2546 crate::proxy_box::RenderConstrainedBox::new(app, square, baseline).as_box()
2547 };
2548 let first_inner = flow_baseline_box(&mut app, 1, |_| Some(10.0));
2549 let second_inner = flow_baseline_box(&mut app, 1, |_| Some(10.0));
2550 let children = vec![
2551 boxed(&mut app, None),
2552 boxed(&mut app, Some(first_inner)),
2553 boxed(&mut app, None),
2554 boxed(&mut app, Some(second_inner)),
2555 boxed(&mut app, None),
2556 ];
2557 let flex = RenderFlex::new(&mut app);
2558 flex.set_text_direction(&mut app, Some(TextDirection::Ltr));
2559 flex.set_direction(&mut app, Axis::Vertical);
2560 flex.add_all(&mut app, Some(children));
2561 let constraints = BoxConstraints::tight(Size::new(800.0, 600.0));
2562 let owner = lay_out_root(&mut app, flex.as_box(), constraints);
2563 let flex_height = flex.size(&app).height();
2564
2565 let expect_baseline = |app: &mut App, expected: f64| {
2566 let column = flex.as_box();
2567 assert_eq!(
2568 column.get_dry_baseline(app, constraints, TextBaseline::Alphabetic),
2569 Some(expected)
2570 );
2571 assert_eq!(
2572 column.get_distance_to_baseline(app, TextBaseline::Alphabetic, true),
2573 Some(expected)
2574 );
2575 };
2576 expect_baseline(&mut app, 100.0 + 10.0);
2577
2578 flex.set_main_axis_alignment(&mut app, MainAxisAlignment::End);
2579 owner.flush_layout(&mut app);
2580 expect_baseline(&mut app, flex_height - 400.0 + 10.0);
2581
2582 flex.set_vertical_direction(&mut app, VerticalDirection::Up);
2583 owner.flush_layout(&mut app);
2584 expect_baseline(&mut app, 300.0 + 10.0);
2585
2586 flex.set_main_axis_alignment(&mut app, MainAxisAlignment::Start);
2587 owner.flush_layout(&mut app);
2588 expect_baseline(&mut app, flex_height - 200.0 + 10.0);
2589 }
2590
2591 #[test]
2594 fn baseline_aligned_dry_layout_adds_the_largest_ascent_and_descent() {
2595 let cell = AppCell::new();
2596 let mut app = cell.borrow_mut();
2597 let first = flow_baseline_box(&mut app, 10, |_| Some(0.0));
2599 let second = flow_baseline_box(&mut app, 10, Some);
2600 let flex = baseline_row(&mut app, vec![first, second]);
2601 set_flex(&mut app, first, 2, FlexFit::Tight);
2602 set_flex(&mut app, second, 1, FlexFit::Loose);
2603 let flex = flex.as_box();
2604
2605 let constraints = BoxConstraints::loose(Size::new(200.0, 100.0));
2607 assert_eq!(
2608 flex.get_dry_layout(&mut app, constraints),
2609 Size::new(200.0, 30.0)
2610 );
2611 assert_eq!(
2612 flex.get_dry_baseline(&mut app, constraints, TextBaseline::Alphabetic),
2613 Some(20.0)
2614 );
2615 let constraints = BoxConstraints::loose(Size::new(300.0, 100.0));
2617 assert_eq!(
2618 flex.get_dry_layout(&mut app, constraints),
2619 Size::new(300.0, 20.0)
2620 );
2621 assert_eq!(
2622 flex.get_dry_baseline(&mut app, constraints, TextBaseline::Alphabetic),
2623 Some(10.0)
2624 );
2625 }
2626
2627 #[test]
2629 fn baseline_aligned_cross_intrinsics_add_the_largest_ascent_and_descent() {
2630 let cell = AppCell::new();
2631 let mut app = cell.borrow_mut();
2632 let first = flow_baseline_box(&mut app, 10, |_| Some(0.0));
2633 let second = flow_baseline_box(&mut app, 10, Some);
2634 let flex = baseline_row(&mut app, vec![first, second]);
2635 set_flex(&mut app, first, 2, FlexFit::Tight);
2636 set_flex(&mut app, second, 1, FlexFit::Loose);
2637 let flex = flex.as_box();
2638
2639 assert_eq!(flex.get_max_intrinsic_height(&mut app, 200.0), 30.0);
2641 assert_eq!(flex.get_min_intrinsic_height(&mut app, 200.0), 30.0);
2642 assert_eq!(flex.get_max_intrinsic_height(&mut app, 300.0), 20.0);
2644 assert_eq!(flex.get_min_intrinsic_height(&mut app, 300.0), 20.0);
2645 }
2646
2647 #[test]
2650 fn children_with_no_baselines_do_not_affect_the_baseline_location() {
2651 let cell = AppCell::new();
2652 let mut app = cell.borrow_mut();
2653 let first = flow_baseline_box(&mut app, 10, Some);
2654 let second = flow_baseline_box(&mut app, 10, |_| None);
2655 let flex = baseline_row(&mut app, vec![first, second]);
2656 set_flex(&mut app, first, 2, FlexFit::Tight);
2657 set_flex(&mut app, second, 1, FlexFit::Loose);
2658 let flex = flex.as_box();
2659
2660 let constraints = BoxConstraints::loose(Size::new(200.0, 100.0));
2662 assert_eq!(
2663 flex.get_dry_layout(&mut app, constraints),
2664 Size::new(200.0, 20.0)
2665 );
2666 assert_eq!(
2667 flex.get_dry_baseline(&mut app, constraints, TextBaseline::Alphabetic),
2668 Some(10.0)
2669 );
2670 let constraints = BoxConstraints::loose(Size::new(300.0, 100.0));
2672 assert_eq!(
2673 flex.get_dry_layout(&mut app, constraints),
2674 Size::new(300.0, 10.0)
2675 );
2676 assert_eq!(
2677 flex.get_dry_baseline(&mut app, constraints, TextBaseline::Alphabetic),
2678 Some(10.0)
2679 );
2680 }
2681
2682 #[test]
2686 fn layout_places_the_children_on_a_shared_baseline() {
2687 let cell = AppCell::new();
2688 let mut app = cell.borrow_mut();
2689 let tight_box = |app: &mut App, size: Size, child: Option<AnyRenderBox>| {
2690 crate::proxy_box::RenderConstrainedBox::new(app, BoxConstraints::tight(size), child)
2691 .as_box()
2692 };
2693 let deep_baseline = flow_baseline_box(&mut app, 1, |_| Some(30.0));
2695 let deep = tight_box(&mut app, Size::new(10.0, 40.0), Some(deep_baseline));
2696 let shallow_baseline = flow_baseline_box(&mut app, 1, |_| Some(10.0));
2697 let shallow = tight_box(&mut app, Size::new(10.0, 20.0), Some(shallow_baseline));
2698 let without = tight_box(&mut app, Size::new(10.0, 50.0), None);
2699 let flex = baseline_row(&mut app, vec![deep, shallow, without]);
2700 let row = flex.as_box();
2701 let loose = BoxConstraints::loose(Size::new(200.0, 100.0));
2702 let dry_size = row.get_dry_layout(&mut app, loose);
2703 let dry_baseline = row.get_dry_baseline(&mut app, loose, TextBaseline::Alphabetic);
2704 let _owner = lay_out_root(&mut app, row, loose);
2705
2706 assert_eq!(flex.size(&app), Size::new(200.0, 50.0));
2707 assert_eq!(offset_of(&app, deep).dy(), 0.0);
2708 assert_eq!(offset_of(&app, shallow).dy(), 20.0);
2709 assert_eq!(offset_of(&app, without).dy(), 0.0);
2710 let baseline = row.get_distance_to_baseline(&mut app, TextBaseline::Alphabetic, true);
2711 assert_eq!(baseline, Some(30.0));
2712 assert_eq!(
2713 (dry_size, dry_baseline),
2714 (flex.size(&app), baseline),
2715 "the dry layout is the real layout"
2716 );
2717
2718 let tall = BoxConstraints {
2719 min_height: 100.0,
2720 ..loose
2721 };
2722 row.layout(&mut app, tall, false);
2723 assert_eq!(flex.size(&app), Size::new(200.0, 100.0));
2724 assert_eq!(
2725 offset_of(&app, shallow).dy(),
2726 20.0,
2727 "the extra space is below the children"
2728 );
2729 }
2730}