1use std::{cell::RefCell, collections::HashMap, iter, num::NonZeroUsize, rc::Rc};
2
3use cassowary::{
4 strength::REQUIRED,
5 AddConstraintError, Expression, Solver, Variable,
6 WeightedRelation::{EQ, GE, LE},
7};
8use itertools::Itertools;
9use lru::LruCache;
10
11use self::strengths::{
12 ALL_SEGMENT_GROW, FILL_GROW, GROW, LENGTH_SIZE_EQ, MAX_SIZE_EQ, MAX_SIZE_LE, MIN_SIZE_EQ,
13 MIN_SIZE_GE, PERCENTAGE_SIZE_EQ, RATIO_SIZE_EQ, SPACER_SIZE_EQ, SPACE_GROW,
14};
15use crate::layout::{Constraint, Direction, Flex, Margin, Rect};
16
17type Rects = Rc<[Rect]>;
18type Segments = Rects;
19type Spacers = Rects;
20type Cache = LruCache<(Rect, Layout), (Segments, Spacers)>;
32
33const FLOAT_PRECISION_MULTIPLIER: f64 = 100.0;
37
38thread_local! {
39 static LAYOUT_CACHE: RefCell<Cache> = RefCell::new(Cache::new(
40 NonZeroUsize::new(Layout::DEFAULT_CACHE_SIZE).unwrap(),
41 ));
42}
43
44#[derive(Debug, Clone, Eq, PartialEq, Hash)]
73pub enum Spacing {
74 Space(u16),
75 Overlap(u16),
76}
77
78impl Default for Spacing {
79 fn default() -> Self {
80 Self::Space(0)
81 }
82}
83
84impl From<i32> for Spacing {
85 fn from(value: i32) -> Self {
86 Self::from(value.clamp(i32::from(i16::MIN), i32::from(i16::MAX)) as i16)
87 }
88}
89
90impl From<u16> for Spacing {
91 fn from(value: u16) -> Self {
92 Self::Space(value)
93 }
94}
95
96impl From<i16> for Spacing {
97 fn from(value: i16) -> Self {
98 if value < 0 {
99 Self::Overlap(value.unsigned_abs())
100 } else {
101 Self::Space(value.unsigned_abs())
102 }
103 }
104}
105
106#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
177pub struct Layout {
178 direction: Direction,
179 constraints: Vec<Constraint>,
180 margin: Margin,
181 flex: Flex,
182 spacing: Spacing,
183}
184
185impl Layout {
186 pub const DEFAULT_CACHE_SIZE: usize = 500;
192
193 pub fn new<I>(direction: Direction, constraints: I) -> Self
224 where
225 I: IntoIterator,
226 I::Item: Into<Constraint>,
227 {
228 Self {
229 direction,
230 constraints: constraints.into_iter().map(Into::into).collect(),
231 ..Self::default()
232 }
233 }
234
235 pub fn vertical<I>(constraints: I) -> Self
248 where
249 I: IntoIterator,
250 I::Item: Into<Constraint>,
251 {
252 Self::new(Direction::Vertical, constraints.into_iter().map(Into::into))
253 }
254
255 pub fn horizontal<I>(constraints: I) -> Self
268 where
269 I: IntoIterator,
270 I::Item: Into<Constraint>,
271 {
272 Self::new(
273 Direction::Horizontal,
274 constraints.into_iter().map(Into::into),
275 )
276 }
277
278 pub fn init_cache(cache_size: NonZeroUsize) {
284 LAYOUT_CACHE.with_borrow_mut(|c| c.resize(cache_size));
285 }
286
287 #[must_use = "method moves the value of self and returns the modified value"]
307 pub const fn direction(mut self, direction: Direction) -> Self {
308 self.direction = direction;
309 self
310 }
311
312 #[must_use = "method moves the value of self and returns the modified value"]
358 pub fn constraints<I>(mut self, constraints: I) -> Self
359 where
360 I: IntoIterator,
361 I::Item: Into<Constraint>,
362 {
363 self.constraints = constraints.into_iter().map(Into::into).collect();
364 self
365 }
366
367 #[must_use = "method moves the value of self and returns the modified value"]
381 pub const fn margin(mut self, margin: u16) -> Self {
382 self.margin = Margin {
383 horizontal: margin,
384 vertical: margin,
385 };
386 self
387 }
388
389 #[must_use = "method moves the value of self and returns the modified value"]
403 pub const fn horizontal_margin(mut self, horizontal: u16) -> Self {
404 self.margin.horizontal = horizontal;
405 self
406 }
407
408 #[must_use = "method moves the value of self and returns the modified value"]
422 pub const fn vertical_margin(mut self, vertical: u16) -> Self {
423 self.margin.vertical = vertical;
424 self
425 }
426
427 #[must_use = "method moves the value of self and returns the modified value"]
459 pub const fn flex(mut self, flex: Flex) -> Self {
460 self.flex = flex;
461 self
462 }
463
464 #[must_use = "method moves the value of self and returns the modified value"]
495 pub fn spacing<T>(mut self, spacing: T) -> Self
496 where
497 T: Into<Spacing>,
498 {
499 self.spacing = spacing.into();
500 self
501 }
502
503 pub fn areas<const N: usize>(&self, area: Rect) -> [Rect; N] {
529 let (areas, _) = self.split_with_spacers(area);
530 areas.as_ref().try_into().expect("invalid number of rects")
531 }
532
533 pub fn spacers<const N: usize>(&self, area: Rect) -> [Rect; N] {
563 let (_, spacers) = self.split_with_spacers(area);
564 spacers
565 .as_ref()
566 .try_into()
567 .expect("invalid number of rects")
568 }
569
570 pub fn split(&self, area: Rect) -> Rects {
605 self.split_with_spacers(area).0
606 }
607
608 pub fn split_with_spacers(&self, area: Rect) -> (Segments, Spacers) {
655 LAYOUT_CACHE.with_borrow_mut(|c| {
656 let key = (area, self.clone());
657 c.get_or_insert(key, || self.try_split(area).expect("failed to split"))
658 .clone()
659 })
660 }
661
662 fn try_split(&self, area: Rect) -> Result<(Segments, Spacers), AddConstraintError> {
663 let mut solver = Solver::new();
684
685 let inner_area = area.inner(self.margin);
686 let (area_start, area_end) = match self.direction {
687 Direction::Horizontal => (
688 f64::from(inner_area.x) * FLOAT_PRECISION_MULTIPLIER,
689 f64::from(inner_area.right()) * FLOAT_PRECISION_MULTIPLIER,
690 ),
691 Direction::Vertical => (
692 f64::from(inner_area.y) * FLOAT_PRECISION_MULTIPLIER,
693 f64::from(inner_area.bottom()) * FLOAT_PRECISION_MULTIPLIER,
694 ),
695 };
696
697 let variable_count = self.constraints.len() * 2 + 2;
717 let variables = iter::repeat_with(Variable::new)
718 .take(variable_count)
719 .collect_vec();
720 let spacers = variables
721 .iter()
722 .tuples()
723 .map(|(a, b)| Element::from((*a, *b)))
724 .collect_vec();
725 let segments = variables
726 .iter()
727 .skip(1)
728 .tuples()
729 .map(|(a, b)| Element::from((*a, *b)))
730 .collect_vec();
731
732 let flex = self.flex;
733
734 let spacing = match self.spacing {
735 Spacing::Space(x) => x as i16,
736 Spacing::Overlap(x) => -(x as i16),
737 };
738
739 let constraints = &self.constraints;
740
741 let area_size = Element::from((*variables.first().unwrap(), *variables.last().unwrap()));
742 configure_area(&mut solver, area_size, area_start, area_end)?;
743 configure_variable_in_area_constraints(&mut solver, &variables, area_size)?;
744 configure_variable_constraints(&mut solver, &variables)?;
745 configure_flex_constraints(&mut solver, area_size, &spacers, flex, spacing)?;
746 configure_constraints(&mut solver, area_size, &segments, constraints, flex)?;
747 configure_fill_constraints(&mut solver, &segments, constraints, flex)?;
748
749 if !flex.is_legacy() {
750 for (left, right) in segments.iter().tuple_windows() {
751 solver.add_constraint(left.has_size(right, ALL_SEGMENT_GROW))?;
752 }
753 }
754
755 let changes: HashMap<Variable, f64> = solver.fetch_changes().iter().copied().collect();
757 let segment_rects = changes_to_rects(&changes, &segments, inner_area, self.direction);
761 let spacer_rects = changes_to_rects(&changes, &spacers, inner_area, self.direction);
762
763 Ok((segment_rects, spacer_rects))
764 }
765}
766
767fn configure_area(
768 solver: &mut Solver,
769 area: Element,
770 area_start: f64,
771 area_end: f64,
772) -> Result<(), AddConstraintError> {
773 solver.add_constraint(area.start | EQ(REQUIRED) | area_start)?;
774 solver.add_constraint(area.end | EQ(REQUIRED) | area_end)?;
775 Ok(())
776}
777
778fn configure_variable_in_area_constraints(
779 solver: &mut Solver,
780 variables: &[Variable],
781 area: Element,
782) -> Result<(), AddConstraintError> {
783 for &variable in variables {
785 solver.add_constraint(variable | GE(REQUIRED) | area.start)?;
786 solver.add_constraint(variable | LE(REQUIRED) | area.end)?;
787 }
788
789 Ok(())
790}
791
792fn configure_variable_constraints(
793 solver: &mut Solver,
794 variables: &[Variable],
795) -> Result<(), AddConstraintError> {
796 for (&left, &right) in variables.iter().skip(1).tuples() {
806 solver.add_constraint(left | LE(REQUIRED) | right)?;
807 }
808 Ok(())
809}
810
811fn configure_constraints(
812 solver: &mut Solver,
813 area: Element,
814 segments: &[Element],
815 constraints: &[Constraint],
816 flex: Flex,
817) -> Result<(), AddConstraintError> {
818 for (&constraint, &segment) in constraints.iter().zip(segments.iter()) {
819 match constraint {
820 Constraint::Max(max) => {
821 solver.add_constraint(segment.has_max_size(max, MAX_SIZE_LE))?;
822 solver.add_constraint(segment.has_int_size(max, MAX_SIZE_EQ))?;
823 }
824 Constraint::Min(min) => {
825 solver.add_constraint(segment.has_min_size(min as i16, MIN_SIZE_GE))?;
826 if flex.is_legacy() {
827 solver.add_constraint(segment.has_int_size(min, MIN_SIZE_EQ))?;
828 } else {
829 solver.add_constraint(segment.has_size(area, FILL_GROW))?;
830 }
831 }
832 Constraint::Length(length) => {
833 solver.add_constraint(segment.has_int_size(length, LENGTH_SIZE_EQ))?;
834 }
835 Constraint::Percentage(p) => {
836 let size = area.size() * f64::from(p) / 100.00;
837 solver.add_constraint(segment.has_size(size, PERCENTAGE_SIZE_EQ))?;
838 }
839 Constraint::Ratio(num, den) => {
840 let size = area.size() * f64::from(num) / f64::from(den.max(1));
842 solver.add_constraint(segment.has_size(size, RATIO_SIZE_EQ))?;
843 }
844 Constraint::Fill(_) => {
845 solver.add_constraint(segment.has_size(area, FILL_GROW))?;
847 }
848 }
849 }
850 Ok(())
851}
852
853fn configure_flex_constraints(
854 solver: &mut Solver,
855 area: Element,
856 spacers: &[Element],
857 flex: Flex,
858 spacing: i16,
859) -> Result<(), AddConstraintError> {
860 let spacers_except_first_and_last = spacers.get(1..spacers.len() - 1).unwrap_or(&[]);
861 let spacing_f64 = f64::from(spacing) * FLOAT_PRECISION_MULTIPLIER;
862 match flex {
863 Flex::Legacy => {
864 for spacer in spacers_except_first_and_last {
865 solver.add_constraint(spacer.has_size(spacing_f64, SPACER_SIZE_EQ))?;
866 }
867 if let (Some(first), Some(last)) = (spacers.first(), spacers.last()) {
868 solver.add_constraint(first.is_empty())?;
869 solver.add_constraint(last.is_empty())?;
870 }
871 }
872 Flex::SpaceAround => {
875 for (left, right) in spacers.iter().tuple_combinations() {
876 solver.add_constraint(left.has_size(right, SPACER_SIZE_EQ))?;
877 }
878 for spacer in spacers {
879 solver.add_constraint(spacer.has_min_size(spacing, SPACER_SIZE_EQ))?;
880 solver.add_constraint(spacer.has_size(area, SPACE_GROW))?;
881 }
882 }
883
884 Flex::SpaceBetween => {
887 for (left, right) in spacers_except_first_and_last.iter().tuple_combinations() {
888 solver.add_constraint(left.has_size(right.size(), SPACER_SIZE_EQ))?;
889 }
890 for spacer in spacers_except_first_and_last {
891 solver.add_constraint(spacer.has_min_size(spacing, SPACER_SIZE_EQ))?;
892 solver.add_constraint(spacer.has_size(area, SPACE_GROW))?;
893 }
894 if let (Some(first), Some(last)) = (spacers.first(), spacers.last()) {
895 solver.add_constraint(first.is_empty())?;
896 solver.add_constraint(last.is_empty())?;
897 }
898 }
899 Flex::Start => {
900 for spacer in spacers_except_first_and_last {
901 solver.add_constraint(spacer.has_size(spacing_f64, SPACER_SIZE_EQ))?;
902 }
903 if let (Some(first), Some(last)) = (spacers.first(), spacers.last()) {
904 solver.add_constraint(first.is_empty())?;
905 solver.add_constraint(last.has_size(area, GROW))?;
906 }
907 }
908 Flex::Center => {
909 for spacer in spacers_except_first_and_last {
910 solver.add_constraint(spacer.has_size(spacing_f64, SPACER_SIZE_EQ))?;
911 }
912 if let (Some(first), Some(last)) = (spacers.first(), spacers.last()) {
913 solver.add_constraint(first.has_size(area, GROW))?;
914 solver.add_constraint(last.has_size(area, GROW))?;
915 solver.add_constraint(first.has_size(last, SPACER_SIZE_EQ))?;
916 }
917 }
918 Flex::End => {
919 for spacer in spacers_except_first_and_last {
920 solver.add_constraint(spacer.has_size(spacing_f64, SPACER_SIZE_EQ))?;
921 }
922 if let (Some(first), Some(last)) = (spacers.first(), spacers.last()) {
923 solver.add_constraint(last.is_empty())?;
924 solver.add_constraint(first.has_size(area, GROW))?;
925 }
926 }
927 }
928 Ok(())
929}
930
931fn configure_fill_constraints(
946 solver: &mut Solver,
947 segments: &[Element],
948 constraints: &[Constraint],
949 flex: Flex,
950) -> Result<(), AddConstraintError> {
951 for ((&left_constraint, &left_segment), (&right_constraint, &right_segment)) in constraints
952 .iter()
953 .zip(segments.iter())
954 .filter(|(c, _)| c.is_fill() || (!flex.is_legacy() && c.is_min()))
955 .tuple_combinations()
956 {
957 let left_scaling_factor = match left_constraint {
958 Constraint::Fill(scale) => f64::from(scale).max(1e-6),
959 Constraint::Min(_) => 1.0,
960 _ => unreachable!(),
961 };
962 let right_scaling_factor = match right_constraint {
963 Constraint::Fill(scale) => f64::from(scale).max(1e-6),
964 Constraint::Min(_) => 1.0,
965 _ => unreachable!(),
966 };
967 solver.add_constraint(
968 (right_scaling_factor * left_segment.size())
969 | EQ(GROW)
970 | (left_scaling_factor * right_segment.size()),
971 )?;
972 }
973 Ok(())
974}
975
976fn changes_to_rects(
977 changes: &HashMap<Variable, f64>,
978 elements: &[Element],
979 area: Rect,
980 direction: Direction,
981) -> Rects {
982 elements
984 .iter()
985 .map(|element| {
986 let start = changes.get(&element.start).unwrap_or(&0.0);
987 let end = changes.get(&element.end).unwrap_or(&0.0);
988 let start = (start.round() / FLOAT_PRECISION_MULTIPLIER).round() as u16;
989 let end = (end.round() / FLOAT_PRECISION_MULTIPLIER).round() as u16;
990 let size = end.saturating_sub(start);
991 match direction {
992 Direction::Horizontal => Rect {
993 x: start,
994 y: area.y,
995 width: size,
996 height: area.height,
997 },
998 Direction::Vertical => Rect {
999 x: area.x,
1000 y: start,
1001 width: area.width,
1002 height: size,
1003 },
1004 }
1005 })
1006 .collect::<Rects>()
1007}
1008
1009#[allow(dead_code)]
1012fn debug_elements(elements: &[Element], changes: &HashMap<Variable, f64>) {
1013 let variables = format!(
1014 "{:?}",
1015 elements
1016 .iter()
1017 .map(|e| (
1018 changes.get(&e.start).unwrap_or(&0.0) / FLOAT_PRECISION_MULTIPLIER,
1019 changes.get(&e.end).unwrap_or(&0.0) / FLOAT_PRECISION_MULTIPLIER,
1020 ))
1021 .collect::<Vec<(f64, f64)>>()
1022 );
1023 dbg!(variables);
1024}
1025
1026#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
1028struct Element {
1029 start: Variable,
1030 end: Variable,
1031}
1032
1033impl From<(Variable, Variable)> for Element {
1034 fn from((start, end): (Variable, Variable)) -> Self {
1035 Self { start, end }
1036 }
1037}
1038
1039impl Element {
1040 #[allow(dead_code)]
1041 fn new() -> Self {
1042 Self {
1043 start: Variable::new(),
1044 end: Variable::new(),
1045 }
1046 }
1047
1048 fn size(&self) -> Expression {
1049 self.end - self.start
1050 }
1051
1052 fn has_max_size(&self, size: u16, strength: f64) -> cassowary::Constraint {
1053 self.size() | LE(strength) | (f64::from(size) * FLOAT_PRECISION_MULTIPLIER)
1054 }
1055
1056 fn has_min_size(&self, size: i16, strength: f64) -> cassowary::Constraint {
1057 self.size() | GE(strength) | (f64::from(size) * FLOAT_PRECISION_MULTIPLIER)
1058 }
1059
1060 fn has_int_size(&self, size: u16, strength: f64) -> cassowary::Constraint {
1061 self.size() | EQ(strength) | (f64::from(size) * FLOAT_PRECISION_MULTIPLIER)
1062 }
1063
1064 fn has_size<E: Into<Expression>>(&self, size: E, strength: f64) -> cassowary::Constraint {
1065 self.size() | EQ(strength) | size.into()
1066 }
1067
1068 fn is_empty(&self) -> cassowary::Constraint {
1069 self.size() | EQ(REQUIRED - 1.0) | 0.0
1070 }
1071}
1072
1073impl From<Element> for Expression {
1075 fn from(element: Element) -> Self {
1076 element.size()
1077 }
1078}
1079
1080impl From<&Element> for Expression {
1082 fn from(element: &Element) -> Self {
1083 element.size()
1084 }
1085}
1086
1087mod strengths {
1088 use cassowary::strength::{MEDIUM, REQUIRED, STRONG, WEAK};
1089
1090 pub const SPACER_SIZE_EQ: f64 = REQUIRED / 10.0;
1096
1097 pub const MIN_SIZE_GE: f64 = STRONG * 100.0;
1103
1104 pub const MAX_SIZE_LE: f64 = STRONG * 100.0;
1110
1111 pub const LENGTH_SIZE_EQ: f64 = STRONG * 10.0;
1117
1118 pub const PERCENTAGE_SIZE_EQ: f64 = STRONG;
1124
1125 pub const RATIO_SIZE_EQ: f64 = STRONG / 10.0;
1131
1132 pub const MIN_SIZE_EQ: f64 = MEDIUM * 10.0;
1138
1139 pub const MAX_SIZE_EQ: f64 = MEDIUM * 10.0;
1145
1146 pub const FILL_GROW: f64 = MEDIUM;
1152
1153 pub const GROW: f64 = MEDIUM / 10.0;
1159
1160 pub const SPACE_GROW: f64 = WEAK * 10.0;
1166
1167 pub const ALL_SEGMENT_GROW: f64 = WEAK;
1173}
1174
1175#[cfg(test)]
1176mod tests {
1177 use super::*;
1178
1179 #[test]
1180 #[allow(clippy::assertions_on_constants)]
1183 pub fn strength_is_valid() {
1184 use strengths::*;
1185 assert!(SPACER_SIZE_EQ > MAX_SIZE_LE);
1186 assert!(MAX_SIZE_LE > MAX_SIZE_EQ);
1187 assert!(MIN_SIZE_GE == MAX_SIZE_LE);
1188 assert!(MAX_SIZE_LE > LENGTH_SIZE_EQ);
1189 assert!(LENGTH_SIZE_EQ > PERCENTAGE_SIZE_EQ);
1190 assert!(PERCENTAGE_SIZE_EQ > RATIO_SIZE_EQ);
1191 assert!(RATIO_SIZE_EQ > MAX_SIZE_EQ);
1192 assert!(MIN_SIZE_GE > FILL_GROW);
1193 assert!(FILL_GROW > GROW);
1194 assert!(GROW > SPACE_GROW);
1195 assert!(SPACE_GROW > ALL_SEGMENT_GROW);
1196 }
1197
1198 #[test]
1199 fn cache_size() {
1200 LAYOUT_CACHE.with_borrow(|c| {
1201 assert_eq!(c.cap().get(), Layout::DEFAULT_CACHE_SIZE);
1202 });
1203
1204 Layout::init_cache(NonZeroUsize::new(10).unwrap());
1205 LAYOUT_CACHE.with_borrow(|c| {
1206 assert_eq!(c.cap().get(), 10);
1207 });
1208 }
1209
1210 #[test]
1211 fn default() {
1212 assert_eq!(
1213 Layout::default(),
1214 Layout {
1215 direction: Direction::Vertical,
1216 margin: Margin::new(0, 0),
1217 constraints: vec![],
1218 flex: Flex::default(),
1219 spacing: Spacing::default(),
1220 }
1221 );
1222 }
1223
1224 #[test]
1225 fn new() {
1226 let fixed_size_array = [Constraint::Min(0)];
1228 let layout = Layout::new(Direction::Horizontal, fixed_size_array);
1229 assert_eq!(layout.direction, Direction::Horizontal);
1230 assert_eq!(layout.constraints, [Constraint::Min(0)]);
1231
1232 #[allow(clippy::needless_borrows_for_generic_args)] let layout = Layout::new(Direction::Horizontal, &[Constraint::Min(0)]);
1235 assert_eq!(layout.direction, Direction::Horizontal);
1236 assert_eq!(layout.constraints, [Constraint::Min(0)]);
1237
1238 let layout = Layout::new(Direction::Horizontal, vec![Constraint::Min(0)]);
1240 assert_eq!(layout.direction, Direction::Horizontal);
1241 assert_eq!(layout.constraints, [Constraint::Min(0)]);
1242
1243 #[allow(clippy::needless_borrows_for_generic_args)] let layout = Layout::new(Direction::Horizontal, &(vec![Constraint::Min(0)]));
1246 assert_eq!(layout.direction, Direction::Horizontal);
1247 assert_eq!(layout.constraints, [Constraint::Min(0)]);
1248
1249 let layout = Layout::new(Direction::Horizontal, iter::once(Constraint::Min(0)));
1251 assert_eq!(layout.direction, Direction::Horizontal);
1252 assert_eq!(layout.constraints, [Constraint::Min(0)]);
1253 }
1254
1255 #[test]
1256 fn vertical() {
1257 assert_eq!(
1258 Layout::vertical([Constraint::Min(0)]),
1259 Layout {
1260 direction: Direction::Vertical,
1261 margin: Margin::new(0, 0),
1262 constraints: vec![Constraint::Min(0)],
1263 flex: Flex::default(),
1264 spacing: Spacing::default(),
1265 }
1266 );
1267 }
1268
1269 #[test]
1270 fn horizontal() {
1271 assert_eq!(
1272 Layout::horizontal([Constraint::Min(0)]),
1273 Layout {
1274 direction: Direction::Horizontal,
1275 margin: Margin::new(0, 0),
1276 constraints: vec![Constraint::Min(0)],
1277 flex: Flex::default(),
1278 spacing: Spacing::default(),
1279 }
1280 );
1281 }
1282
1283 #[test]
1286 #[allow(
1287 clippy::needless_borrow,
1288 clippy::unnecessary_to_owned,
1289 clippy::useless_asref
1290 )]
1291 fn constraints() {
1292 const CONSTRAINTS: [Constraint; 2] = [Constraint::Min(0), Constraint::Max(10)];
1293 let fixed_size_array = CONSTRAINTS;
1294 assert_eq!(
1295 Layout::default().constraints(fixed_size_array).constraints,
1296 CONSTRAINTS,
1297 "constraints should be settable with an array"
1298 );
1299
1300 let slice_of_fixed_size_array = &CONSTRAINTS;
1301 assert_eq!(
1302 Layout::default()
1303 .constraints(slice_of_fixed_size_array)
1304 .constraints,
1305 CONSTRAINTS,
1306 "constraints should be settable with a slice"
1307 );
1308
1309 let vec = CONSTRAINTS.to_vec();
1310 let slice_of_vec = vec.as_slice();
1311 assert_eq!(
1312 Layout::default().constraints(slice_of_vec).constraints,
1313 CONSTRAINTS,
1314 "constraints should be settable with a slice"
1315 );
1316
1317 assert_eq!(
1318 Layout::default().constraints(vec).constraints,
1319 CONSTRAINTS,
1320 "constraints should be settable with a Vec"
1321 );
1322
1323 let iter = CONSTRAINTS.iter();
1324 assert_eq!(
1325 Layout::default().constraints(iter).constraints,
1326 CONSTRAINTS,
1327 "constraints should be settable with an iter"
1328 );
1329
1330 let iterator = CONSTRAINTS.iter().map(ToOwned::to_owned);
1331 assert_eq!(
1332 Layout::default().constraints(iterator).constraints,
1333 CONSTRAINTS,
1334 "constraints should be settable with an iterator"
1335 );
1336
1337 let iterator_ref = CONSTRAINTS.iter().map(AsRef::as_ref);
1338 assert_eq!(
1339 Layout::default().constraints(iterator_ref).constraints,
1340 CONSTRAINTS,
1341 "constraints should be settable with an iterator of refs"
1342 );
1343 }
1344
1345 #[test]
1346 fn direction() {
1347 assert_eq!(
1348 Layout::default().direction(Direction::Horizontal).direction,
1349 Direction::Horizontal
1350 );
1351 assert_eq!(
1352 Layout::default().direction(Direction::Vertical).direction,
1353 Direction::Vertical
1354 );
1355 }
1356
1357 #[test]
1358 fn margins() {
1359 assert_eq!(Layout::default().margin(10).margin, Margin::new(10, 10));
1360 assert_eq!(
1361 Layout::default().horizontal_margin(10).margin,
1362 Margin::new(10, 0)
1363 );
1364 assert_eq!(
1365 Layout::default().vertical_margin(10).margin,
1366 Margin::new(0, 10)
1367 );
1368 assert_eq!(
1369 Layout::default()
1370 .horizontal_margin(10)
1371 .vertical_margin(20)
1372 .margin,
1373 Margin::new(10, 20)
1374 );
1375 }
1376
1377 #[test]
1378 fn flex() {
1379 assert_eq!(Layout::default().flex, Flex::Start);
1380 assert_eq!(Layout::default().flex(Flex::Center).flex, Flex::Center);
1381 }
1382
1383 #[test]
1384 fn spacing() {
1385 assert_eq!(Layout::default().spacing(10).spacing, Spacing::Space(10));
1386 assert_eq!(Layout::default().spacing(0).spacing, Spacing::Space(0));
1387 assert_eq!(Layout::default().spacing(-10).spacing, Spacing::Overlap(10));
1388 }
1389
1390 mod split {
1408 use std::ops::Range;
1409
1410 use itertools::Itertools;
1411 use pretty_assertions::assert_eq;
1412 use rstest::rstest;
1413
1414 use crate::{
1415 buffer::Buffer,
1416 layout::{Constraint, Constraint::*, Direction, Flex, Layout, Rect},
1417 widgets::{Paragraph, Widget},
1418 };
1419
1420 #[track_caller]
1429 fn letters(flex: Flex, constraints: &[Constraint], width: u16, expected: &str) {
1430 let area = Rect::new(0, 0, width, 1);
1431 let layout = Layout::default()
1432 .direction(Direction::Horizontal)
1433 .constraints(constraints)
1434 .flex(flex)
1435 .split(area);
1436 let mut buffer = Buffer::empty(area);
1437 for (c, &area) in ('a'..='z').take(constraints.len()).zip(layout.iter()) {
1438 let s = c.to_string().repeat(area.width as usize);
1439 Paragraph::new(s).render(area, &mut buffer);
1440 }
1441 assert_eq!(buffer, Buffer::with_lines([expected]));
1442 }
1443
1444 #[rstest]
1445 #[case(Flex::Legacy, 1, &[Length(0)], "a")] #[case(Flex::Legacy, 1, &[Length(1)], "a")] #[case(Flex::Legacy, 1, &[Length(2)], "a")] #[case(Flex::Legacy, 2, &[Length(0)], "aa")] #[case(Flex::Legacy, 2, &[Length(1)], "aa")] #[case(Flex::Legacy, 2, &[Length(2)], "aa")] #[case(Flex::Legacy, 2, &[Length(3)], "aa")] #[case(Flex::Legacy, 1, &[Length(0), Length(0)], "b")] #[case(Flex::Legacy, 1, &[Length(0), Length(1)], "b")] #[case(Flex::Legacy, 1, &[Length(0), Length(2)], "b")] #[case(Flex::Legacy, 1, &[Length(1), Length(0)], "a")] #[case(Flex::Legacy, 1, &[Length(1), Length(1)], "a")] #[case(Flex::Legacy, 1, &[Length(1), Length(2)], "a")] #[case(Flex::Legacy, 1, &[Length(2), Length(0)], "a")] #[case(Flex::Legacy, 1, &[Length(2), Length(1)], "a")] #[case(Flex::Legacy, 1, &[Length(2), Length(2)], "a")] #[case(Flex::Legacy, 2, &[Length(0), Length(0)], "bb")] #[case(Flex::Legacy, 2, &[Length(0), Length(1)], "bb")] #[case(Flex::Legacy, 2, &[Length(0), Length(2)], "bb")] #[case(Flex::Legacy, 2, &[Length(0), Length(3)], "bb")] #[case(Flex::Legacy, 2, &[Length(1), Length(0)], "ab")] #[case(Flex::Legacy, 2, &[Length(1), Length(1)], "ab")] #[case(Flex::Legacy, 2, &[Length(1), Length(2)], "ab")] #[case(Flex::Legacy, 2, &[Length(1), Length(3)], "ab")] #[case(Flex::Legacy, 2, &[Length(2), Length(0)], "aa")] #[case(Flex::Legacy, 2, &[Length(2), Length(1)], "aa")] #[case(Flex::Legacy, 2, &[Length(2), Length(2)], "aa")] #[case(Flex::Legacy, 2, &[Length(2), Length(3)], "aa")] #[case(Flex::Legacy, 2, &[Length(3), Length(0)], "aa")] #[case(Flex::Legacy, 2, &[Length(3), Length(1)], "aa")] #[case(Flex::Legacy, 2, &[Length(3), Length(2)], "aa")] #[case(Flex::Legacy, 2, &[Length(3), Length(3)], "aa")] #[case(Flex::Legacy, 3, &[Length(2), Length(2)], "aab")] fn length(
1480 #[case] flex: Flex,
1481 #[case] width: u16,
1482 #[case] constraints: &[Constraint],
1483 #[case] expected: &str,
1484 ) {
1485 letters(flex, constraints, width, expected);
1486 }
1487
1488 #[rstest]
1489 #[case(Flex::Legacy, 1, &[Max(0)], "a")] #[case(Flex::Legacy, 1, &[Max(1)], "a")] #[case(Flex::Legacy, 1, &[Max(2)], "a")] #[case(Flex::Legacy, 2, &[Max(0)], "aa")] #[case(Flex::Legacy, 2, &[Max(1)], "aa")] #[case(Flex::Legacy, 2, &[Max(2)], "aa")] #[case(Flex::Legacy, 2, &[Max(3)], "aa")] #[case(Flex::Legacy, 1, &[Max(0), Max(0)], "b")] #[case(Flex::Legacy, 1, &[Max(0), Max(1)], "b")] #[case(Flex::Legacy, 1, &[Max(0), Max(2)], "b")] #[case(Flex::Legacy, 1, &[Max(1), Max(0)], "a")] #[case(Flex::Legacy, 1, &[Max(1), Max(1)], "a")] #[case(Flex::Legacy, 1, &[Max(1), Max(2)], "a")] #[case(Flex::Legacy, 1, &[Max(2), Max(0)], "a")] #[case(Flex::Legacy, 1, &[Max(2), Max(1)], "a")] #[case(Flex::Legacy, 1, &[Max(2), Max(2)], "a")] #[case(Flex::Legacy, 2, &[Max(0), Max(0)], "bb")] #[case(Flex::Legacy, 2, &[Max(0), Max(1)], "bb")] #[case(Flex::Legacy, 2, &[Max(0), Max(2)], "bb")] #[case(Flex::Legacy, 2, &[Max(0), Max(3)], "bb")] #[case(Flex::Legacy, 2, &[Max(1), Max(0)], "ab")] #[case(Flex::Legacy, 2, &[Max(1), Max(1)], "ab")] #[case(Flex::Legacy, 2, &[Max(1), Max(2)], "ab")] #[case(Flex::Legacy, 2, &[Max(1), Max(3)], "ab")] #[case(Flex::Legacy, 2, &[Max(2), Max(0)], "aa")] #[case(Flex::Legacy, 2, &[Max(2), Max(1)], "aa")] #[case(Flex::Legacy, 2, &[Max(2), Max(2)], "aa")] #[case(Flex::Legacy, 2, &[Max(2), Max(3)], "aa")] #[case(Flex::Legacy, 2, &[Max(3), Max(0)], "aa")] #[case(Flex::Legacy, 2, &[Max(3), Max(1)], "aa")] #[case(Flex::Legacy, 2, &[Max(3), Max(2)], "aa")] #[case(Flex::Legacy, 2, &[Max(3), Max(3)], "aa")] #[case(Flex::Legacy, 3, &[Max(2), Max(2)], "aab")]
1522 fn max(
1523 #[case] flex: Flex,
1524 #[case] width: u16,
1525 #[case] constraints: &[Constraint],
1526 #[case] expected: &str,
1527 ) {
1528 letters(flex, constraints, width, expected);
1529 }
1530
1531 #[rstest]
1532 #[case(Flex::Legacy, 1, &[Min(0), Min(0)], "b")] #[case(Flex::Legacy, 1, &[Min(0), Min(1)], "b")] #[case(Flex::Legacy, 1, &[Min(0), Min(2)], "b")] #[case(Flex::Legacy, 1, &[Min(1), Min(0)], "a")] #[case(Flex::Legacy, 1, &[Min(1), Min(1)], "a")] #[case(Flex::Legacy, 1, &[Min(1), Min(2)], "a")] #[case(Flex::Legacy, 1, &[Min(2), Min(0)], "a")] #[case(Flex::Legacy, 1, &[Min(2), Min(1)], "a")] #[case(Flex::Legacy, 1, &[Min(2), Min(2)], "a")] #[case(Flex::Legacy, 2, &[Min(0), Min(0)], "bb")] #[case(Flex::Legacy, 2, &[Min(0), Min(1)], "bb")] #[case(Flex::Legacy, 2, &[Min(0), Min(2)], "bb")] #[case(Flex::Legacy, 2, &[Min(0), Min(3)], "bb")] #[case(Flex::Legacy, 2, &[Min(1), Min(0)], "ab")] #[case(Flex::Legacy, 2, &[Min(1), Min(1)], "ab")] #[case(Flex::Legacy, 2, &[Min(1), Min(2)], "ab")] #[case(Flex::Legacy, 2, &[Min(1), Min(3)], "ab")] #[case(Flex::Legacy, 2, &[Min(2), Min(0)], "aa")] #[case(Flex::Legacy, 2, &[Min(2), Min(1)], "aa")] #[case(Flex::Legacy, 2, &[Min(2), Min(2)], "aa")] #[case(Flex::Legacy, 2, &[Min(2), Min(3)], "aa")] #[case(Flex::Legacy, 2, &[Min(3), Min(0)], "aa")] #[case(Flex::Legacy, 2, &[Min(3), Min(1)], "aa")] #[case(Flex::Legacy, 2, &[Min(3), Min(2)], "aa")] #[case(Flex::Legacy, 2, &[Min(3), Min(3)], "aa")] #[case(Flex::Legacy, 3, &[Min(2), Min(2)], "aab")]
1558 fn min(
1559 #[case] flex: Flex,
1560 #[case] width: u16,
1561 #[case] constraints: &[Constraint],
1562 #[case] expected: &str,
1563 ) {
1564 letters(flex, constraints, width, expected);
1565 }
1566
1567 #[rstest] #[case(Flex::Legacy, 1, &[Percentage(0)], "a")]
1570 #[case(Flex::Legacy, 1, &[Percentage(25)], "a")]
1571 #[case(Flex::Legacy, 1, &[Percentage(50)], "a")]
1572 #[case(Flex::Legacy, 1, &[Percentage(90)], "a")]
1573 #[case(Flex::Legacy, 1, &[Percentage(100)], "a")]
1574 #[case(Flex::Legacy, 1, &[Percentage(200)], "a")]
1575 #[case(Flex::Legacy, 2, &[Percentage(0)], "aa")]
1577 #[case(Flex::Legacy, 2, &[Percentage(10)], "aa")]
1578 #[case(Flex::Legacy, 2, &[Percentage(25)], "aa")]
1579 #[case(Flex::Legacy, 2, &[Percentage(50)], "aa")]
1580 #[case(Flex::Legacy, 2, &[Percentage(66)], "aa")]
1581 #[case(Flex::Legacy, 2, &[Percentage(100)], "aa")]
1582 #[case(Flex::Legacy, 2, &[Percentage(200)], "aa")]
1583 #[case(Flex::Legacy, 10, &[Percentage(0)], "aaaaaaaaaa")]
1585 #[case(Flex::Legacy, 10, &[Percentage(10)], "aaaaaaaaaa")]
1586 #[case(Flex::Legacy, 10, &[Percentage(25)], "aaaaaaaaaa")]
1587 #[case(Flex::Legacy, 10, &[Percentage(50)], "aaaaaaaaaa")]
1588 #[case(Flex::Legacy, 10, &[Percentage(66)], "aaaaaaaaaa")]
1589 #[case(Flex::Legacy, 10, &[Percentage(100)], "aaaaaaaaaa")]
1590 #[case(Flex::Legacy, 10, &[Percentage(200)], "aaaaaaaaaa")]
1591 #[case(Flex::Legacy, 1, &[Percentage(0), Percentage(0)], "b")]
1593 #[case(Flex::Legacy, 1, &[Percentage(0), Percentage(10)], "b")]
1594 #[case(Flex::Legacy, 1, &[Percentage(0), Percentage(50)], "b")]
1595 #[case(Flex::Legacy, 1, &[Percentage(0), Percentage(90)], "b")]
1596 #[case(Flex::Legacy, 1, &[Percentage(0), Percentage(100)], "b")]
1597 #[case(Flex::Legacy, 1, &[Percentage(0), Percentage(200)], "b")]
1598 #[case(Flex::Legacy, 1, &[Percentage(10), Percentage(0)], "b")]
1600 #[case(Flex::Legacy, 1, &[Percentage(10), Percentage(10)], "b")]
1601 #[case(Flex::Legacy, 1, &[Percentage(10), Percentage(50)], "b")]
1602 #[case(Flex::Legacy, 1, &[Percentage(10), Percentage(90)], "b")]
1603 #[case(Flex::Legacy, 1, &[Percentage(10), Percentage(100)], "b")]
1604 #[case(Flex::Legacy, 1, &[Percentage(10), Percentage(200)], "b")]
1605 #[case(Flex::Legacy, 1, &[Percentage(50), Percentage(0)], "a")]
1607 #[case(Flex::Legacy, 1, &[Percentage(50), Percentage(50)], "a")]
1608 #[case(Flex::Legacy, 1, &[Percentage(50), Percentage(100)], "a")]
1609 #[case(Flex::Legacy, 1, &[Percentage(50), Percentage(200)], "a")]
1610 #[case(Flex::Legacy, 1, &[Percentage(90), Percentage(0)], "a")]
1612 #[case(Flex::Legacy, 1, &[Percentage(90), Percentage(50)], "a")]
1613 #[case(Flex::Legacy, 1, &[Percentage(90), Percentage(100)], "a")]
1614 #[case(Flex::Legacy, 1, &[Percentage(90), Percentage(200)], "a")]
1615 #[case(Flex::Legacy, 1, &[Percentage(100), Percentage(0)], "a")]
1617 #[case(Flex::Legacy, 1, &[Percentage(100), Percentage(50)], "a")]
1618 #[case(Flex::Legacy, 1, &[Percentage(100), Percentage(100)], "a")]
1619 #[case(Flex::Legacy, 1, &[Percentage(100), Percentage(200)], "a")]
1620 #[case(Flex::Legacy, 2, &[Percentage(0), Percentage(0)], "bb")]
1622 #[case(Flex::Legacy, 2, &[Percentage(0), Percentage(25)], "bb")]
1623 #[case(Flex::Legacy, 2, &[Percentage(0), Percentage(50)], "bb")]
1624 #[case(Flex::Legacy, 2, &[Percentage(0), Percentage(100)], "bb")]
1625 #[case(Flex::Legacy, 2, &[Percentage(0), Percentage(200)], "bb")]
1626 #[case(Flex::Legacy, 2, &[Percentage(10), Percentage(0)], "bb")]
1628 #[case(Flex::Legacy, 2, &[Percentage(10), Percentage(25)], "bb")]
1629 #[case(Flex::Legacy, 2, &[Percentage(10), Percentage(50)], "bb")]
1630 #[case(Flex::Legacy, 2, &[Percentage(10), Percentage(100)], "bb")]
1631 #[case(Flex::Legacy, 2, &[Percentage(10), Percentage(200)], "bb")]
1632 #[case(Flex::Legacy, 2, &[Percentage(25), Percentage(0)], "ab")]
1634 #[case(Flex::Legacy, 2, &[Percentage(25), Percentage(25)], "ab")]
1635 #[case(Flex::Legacy, 2, &[Percentage(25), Percentage(50)], "ab")]
1636 #[case(Flex::Legacy, 2, &[Percentage(25), Percentage(100)], "ab")]
1637 #[case(Flex::Legacy, 2, &[Percentage(25), Percentage(200)], "ab")]
1638 #[case(Flex::Legacy, 2, &[Percentage(33), Percentage(0)], "ab")]
1640 #[case(Flex::Legacy, 2, &[Percentage(33), Percentage(25)], "ab")]
1641 #[case(Flex::Legacy, 2, &[Percentage(33), Percentage(50)], "ab")]
1642 #[case(Flex::Legacy, 2, &[Percentage(33), Percentage(100)], "ab")]
1643 #[case(Flex::Legacy, 2, &[Percentage(33), Percentage(200)], "ab")]
1644 #[case(Flex::Legacy, 2, &[Percentage(50), Percentage(0)], "ab")]
1646 #[case(Flex::Legacy, 2, &[Percentage(50), Percentage(50)], "ab")]
1647 #[case(Flex::Legacy, 2, &[Percentage(50), Percentage(100)], "ab")]
1648 #[case(Flex::Legacy, 2, &[Percentage(100), Percentage(0)], "aa")]
1651 #[case(Flex::Legacy, 2, &[Percentage(100), Percentage(50)], "aa")]
1652 #[case(Flex::Legacy, 2, &[Percentage(100), Percentage(100)], "aa")]
1653 #[case(Flex::Legacy, 3, &[Percentage(33), Percentage(33)], "abb")]
1655 #[case(Flex::Legacy, 3, &[Percentage(33), Percentage(66)], "abb")]
1656 #[case(Flex::Legacy, 4, &[Percentage(33), Percentage(33)], "abbb")]
1658 #[case(Flex::Legacy, 4, &[Percentage(33), Percentage(66)], "abbb")]
1659 #[case(Flex::Legacy, 10, &[Percentage(0), Percentage(0)], "bbbbbbbbbb" )]
1661 #[case(Flex::Legacy, 10, &[Percentage(0), Percentage(25)], "bbbbbbbbbb" )]
1662 #[case(Flex::Legacy, 10, &[Percentage(0), Percentage(50)], "bbbbbbbbbb" )]
1663 #[case(Flex::Legacy, 10, &[Percentage(0), Percentage(100)], "bbbbbbbbbb" )]
1664 #[case(Flex::Legacy, 10, &[Percentage(0), Percentage(200)], "bbbbbbbbbb" )]
1665 #[case(Flex::Legacy, 10, &[Percentage(10), Percentage(0)], "abbbbbbbbb" )]
1667 #[case(Flex::Legacy, 10, &[Percentage(10), Percentage(25)], "abbbbbbbbb" )]
1668 #[case(Flex::Legacy, 10, &[Percentage(10), Percentage(50)], "abbbbbbbbb" )]
1669 #[case(Flex::Legacy, 10, &[Percentage(10), Percentage(100)], "abbbbbbbbb" )]
1670 #[case(Flex::Legacy, 10, &[Percentage(10), Percentage(200)], "abbbbbbbbb" )]
1671 #[case(Flex::Legacy, 10, &[Percentage(25), Percentage(0)], "aaabbbbbbb" )]
1673 #[case(Flex::Legacy, 10, &[Percentage(25), Percentage(25)], "aaabbbbbbb" )]
1674 #[case(Flex::Legacy, 10, &[Percentage(25), Percentage(50)], "aaabbbbbbb" )]
1675 #[case(Flex::Legacy, 10, &[Percentage(25), Percentage(100)], "aaabbbbbbb" )]
1676 #[case(Flex::Legacy, 10, &[Percentage(25), Percentage(200)], "aaabbbbbbb" )]
1677 #[case(Flex::Legacy, 10, &[Percentage(33), Percentage(0)], "aaabbbbbbb" )]
1679 #[case(Flex::Legacy, 10, &[Percentage(33), Percentage(25)], "aaabbbbbbb" )]
1680 #[case(Flex::Legacy, 10, &[Percentage(33), Percentage(50)], "aaabbbbbbb" )]
1681 #[case(Flex::Legacy, 10, &[Percentage(33), Percentage(100)], "aaabbbbbbb" )]
1682 #[case(Flex::Legacy, 10, &[Percentage(33), Percentage(200)], "aaabbbbbbb" )]
1683 #[case(Flex::Legacy, 10, &[Percentage(50), Percentage(0)], "aaaaabbbbb" )]
1685 #[case(Flex::Legacy, 10, &[Percentage(50), Percentage(50)], "aaaaabbbbb" )]
1686 #[case(Flex::Legacy, 10, &[Percentage(50), Percentage(100)], "aaaaabbbbb" )]
1687 #[case(Flex::Legacy, 10, &[Percentage(100), Percentage(0)], "aaaaaaaaaa" )]
1689 #[case(Flex::Legacy, 10, &[Percentage(100), Percentage(50)], "aaaaaaaaaa" )]
1690 #[case(Flex::Legacy, 10, &[Percentage(100), Percentage(100)], "aaaaaaaaaa" )]
1691 fn percentage(
1692 #[case] flex: Flex,
1693 #[case] width: u16,
1694 #[case] constraints: &[Constraint],
1695 #[case] expected: &str,
1696 ) {
1697 letters(flex, constraints, width, expected);
1698 }
1699
1700 #[rstest]
1701 #[case(Flex::Start, 10, &[Percentage(0), Percentage(0)], " " )]
1702 #[case(Flex::Start, 10, &[Percentage(0), Percentage(25)], "bbb " )]
1703 #[case(Flex::Start, 10, &[Percentage(0), Percentage(50)], "bbbbb " )]
1704 #[case(Flex::Start, 10, &[Percentage(0), Percentage(100)], "bbbbbbbbbb" )]
1705 #[case(Flex::Start, 10, &[Percentage(0), Percentage(200)], "bbbbbbbbbb" )]
1706 #[case(Flex::Start, 10, &[Percentage(10), Percentage(0)], "a " )]
1707 #[case(Flex::Start, 10, &[Percentage(10), Percentage(25)], "abbb " )]
1708 #[case(Flex::Start, 10, &[Percentage(10), Percentage(50)], "abbbbb " )]
1709 #[case(Flex::Start, 10, &[Percentage(10), Percentage(100)], "abbbbbbbbb" )]
1710 #[case(Flex::Start, 10, &[Percentage(10), Percentage(200)], "abbbbbbbbb" )]
1711 #[case(Flex::Start, 10, &[Percentage(25), Percentage(0)], "aaa " )]
1712 #[case(Flex::Start, 10, &[Percentage(25), Percentage(25)], "aaabb " )]
1713 #[case(Flex::Start, 10, &[Percentage(25), Percentage(50)], "aaabbbbb " )]
1714 #[case(Flex::Start, 10, &[Percentage(25), Percentage(100)], "aaabbbbbbb" )]
1715 #[case(Flex::Start, 10, &[Percentage(25), Percentage(200)], "aaabbbbbbb" )]
1716 #[case(Flex::Start, 10, &[Percentage(33), Percentage(0)], "aaa " )]
1717 #[case(Flex::Start, 10, &[Percentage(33), Percentage(25)], "aaabbb " )]
1718 #[case(Flex::Start, 10, &[Percentage(33), Percentage(50)], "aaabbbbb " )]
1719 #[case(Flex::Start, 10, &[Percentage(33), Percentage(100)], "aaabbbbbbb" )]
1720 #[case(Flex::Start, 10, &[Percentage(33), Percentage(200)], "aaabbbbbbb" )]
1721 #[case(Flex::Start, 10, &[Percentage(50), Percentage(0)], "aaaaa " )]
1722 #[case(Flex::Start, 10, &[Percentage(50), Percentage(50)], "aaaaabbbbb" )]
1723 #[case(Flex::Start, 10, &[Percentage(50), Percentage(100)], "aaaaabbbbb" )]
1724 #[case(Flex::Start, 10, &[Percentage(100), Percentage(0)], "aaaaaaaaaa" )]
1725 #[case(Flex::Start, 10, &[Percentage(100), Percentage(50)], "aaaaabbbbb" )]
1726 #[case(Flex::Start, 10, &[Percentage(100), Percentage(100)], "aaaaabbbbb" )]
1727 #[case(Flex::Start, 10, &[Percentage(100), Percentage(200)], "aaaaabbbbb" )]
1728 fn percentage_start(
1729 #[case] flex: Flex,
1730 #[case] width: u16,
1731 #[case] constraints: &[Constraint],
1732 #[case] expected: &str,
1733 ) {
1734 letters(flex, constraints, width, expected);
1735 }
1736
1737 #[rstest]
1738 #[case(Flex::SpaceBetween, 10, &[Percentage(0), Percentage(0)], " " )]
1739 #[case(Flex::SpaceBetween, 10, &[Percentage(0), Percentage(25)], " bb" )]
1740 #[case(Flex::SpaceBetween, 10, &[Percentage(0), Percentage(50)], " bbbbb" )]
1741 #[case(Flex::SpaceBetween, 10, &[Percentage(0), Percentage(100)], "bbbbbbbbbb" )]
1742 #[case(Flex::SpaceBetween, 10, &[Percentage(0), Percentage(200)], "bbbbbbbbbb" )]
1743 #[case(Flex::SpaceBetween, 10, &[Percentage(10), Percentage(0)], "a " )]
1744 #[case(Flex::SpaceBetween, 10, &[Percentage(10), Percentage(25)], "a bb" )]
1745 #[case(Flex::SpaceBetween, 10, &[Percentage(10), Percentage(50)], "a bbbbb" )]
1746 #[case(Flex::SpaceBetween, 10, &[Percentage(10), Percentage(100)], "abbbbbbbbb" )]
1747 #[case(Flex::SpaceBetween, 10, &[Percentage(10), Percentage(200)], "abbbbbbbbb" )]
1748 #[case(Flex::SpaceBetween, 10, &[Percentage(25), Percentage(0)], "aaa " )]
1749 #[case(Flex::SpaceBetween, 10, &[Percentage(25), Percentage(25)], "aaa bb" )]
1750 #[case(Flex::SpaceBetween, 10, &[Percentage(25), Percentage(50)], "aaa bbbbb" )]
1751 #[case(Flex::SpaceBetween, 10, &[Percentage(25), Percentage(100)], "aaabbbbbbb" )]
1752 #[case(Flex::SpaceBetween, 10, &[Percentage(25), Percentage(200)], "aaabbbbbbb" )]
1753 #[case(Flex::SpaceBetween, 10, &[Percentage(33), Percentage(0)], "aaa " )]
1754 #[case(Flex::SpaceBetween, 10, &[Percentage(33), Percentage(25)], "aaa bb" )]
1755 #[case(Flex::SpaceBetween, 10, &[Percentage(33), Percentage(50)], "aaa bbbbb" )]
1756 #[case(Flex::SpaceBetween, 10, &[Percentage(33), Percentage(100)], "aaabbbbbbb" )]
1757 #[case(Flex::SpaceBetween, 10, &[Percentage(33), Percentage(200)], "aaabbbbbbb" )]
1758 #[case(Flex::SpaceBetween, 10, &[Percentage(50), Percentage(0)], "aaaaa " )]
1759 #[case(Flex::SpaceBetween, 10, &[Percentage(50), Percentage(50)], "aaaaabbbbb" )]
1760 #[case(Flex::SpaceBetween, 10, &[Percentage(50), Percentage(100)], "aaaaabbbbb" )]
1761 #[case(Flex::SpaceBetween, 10, &[Percentage(100), Percentage(0)], "aaaaaaaaaa" )]
1762 #[case(Flex::SpaceBetween, 10, &[Percentage(100), Percentage(50)], "aaaaabbbbb" )]
1763 #[case(Flex::SpaceBetween, 10, &[Percentage(100), Percentage(100)], "aaaaabbbbb" )]
1764 #[case(Flex::SpaceBetween, 10, &[Percentage(100), Percentage(200)], "aaaaabbbbb" )]
1765 fn percentage_spacebetween(
1766 #[case] flex: Flex,
1767 #[case] width: u16,
1768 #[case] constraints: &[Constraint],
1769 #[case] expected: &str,
1770 ) {
1771 letters(flex, constraints, width, expected);
1772 }
1773
1774 #[rstest]
1775 #[case(Flex::Legacy, 1, &[Ratio(0, 1)], "a")]
1778 #[case(Flex::Legacy, 1, &[Ratio(1, 4)], "a")]
1779 #[case(Flex::Legacy, 1, &[Ratio(1, 2)], "a")]
1780 #[case(Flex::Legacy, 1, &[Ratio(9, 10)], "a")]
1781 #[case(Flex::Legacy, 1, &[Ratio(1, 1)], "a")]
1782 #[case(Flex::Legacy, 1, &[Ratio(2, 1)], "a")]
1783 #[case(Flex::Legacy, 2, &[Ratio(0, 1)], "aa")]
1784 #[case(Flex::Legacy, 2, &[Ratio(1, 10)], "aa")]
1785 #[case(Flex::Legacy, 2, &[Ratio(1, 4)], "aa")]
1786 #[case(Flex::Legacy, 2, &[Ratio(1, 2)], "aa")]
1787 #[case(Flex::Legacy, 2, &[Ratio(2, 3)], "aa")]
1788 #[case(Flex::Legacy, 2, &[Ratio(1, 1)], "aa")]
1789 #[case(Flex::Legacy, 2, &[Ratio(2, 1)], "aa")]
1790 #[case(Flex::Legacy, 1, &[Ratio(0, 1), Ratio(0, 1)], "b")]
1791 #[case(Flex::Legacy, 1, &[Ratio(0, 1), Ratio(1, 10)], "b")]
1792 #[case(Flex::Legacy, 1, &[Ratio(0, 1), Ratio(1, 2)], "b")]
1793 #[case(Flex::Legacy, 1, &[Ratio(0, 1), Ratio(9, 10)], "b")]
1794 #[case(Flex::Legacy, 1, &[Ratio(0, 1), Ratio(1, 1)], "b")]
1795 #[case(Flex::Legacy, 1, &[Ratio(0, 1), Ratio(2, 1)], "b")]
1796 #[case(Flex::Legacy, 1, &[Ratio(1, 10), Ratio(0, 1)], "b")]
1797 #[case(Flex::Legacy, 1, &[Ratio(1, 10), Ratio(1, 10)], "b")]
1798 #[case(Flex::Legacy, 1, &[Ratio(1, 10), Ratio(1, 2)], "b")]
1799 #[case(Flex::Legacy, 1, &[Ratio(1, 10), Ratio(9, 10)], "b")]
1800 #[case(Flex::Legacy, 1, &[Ratio(1, 10), Ratio(1, 1)], "b")]
1801 #[case(Flex::Legacy, 1, &[Ratio(1, 10), Ratio(2, 1)], "b")]
1802 #[case(Flex::Legacy, 1, &[Ratio(1, 2), Ratio(0, 1)], "a")]
1803 #[case(Flex::Legacy, 1, &[Ratio(1, 2), Ratio(1, 2)], "a")]
1804 #[case(Flex::Legacy, 1, &[Ratio(1, 2), Ratio(1, 1)], "a")]
1805 #[case(Flex::Legacy, 1, &[Ratio(1, 2), Ratio(2, 1)], "a")]
1806 #[case(Flex::Legacy, 1, &[Ratio(9, 10), Ratio(0, 1)], "a")]
1807 #[case(Flex::Legacy, 1, &[Ratio(9, 10), Ratio(1, 2)], "a")]
1808 #[case(Flex::Legacy, 1, &[Ratio(9, 10), Ratio(1, 1)], "a")]
1809 #[case(Flex::Legacy, 1, &[Ratio(9, 10), Ratio(2, 1)], "a")]
1810 #[case(Flex::Legacy, 1, &[Ratio(1, 1), Ratio(0, 1)], "a")]
1811 #[case(Flex::Legacy, 1, &[Ratio(1, 1), Ratio(1, 2)], "a")]
1812 #[case(Flex::Legacy, 1, &[Ratio(1, 1), Ratio(1, 1)], "a")]
1813 #[case(Flex::Legacy, 1, &[Ratio(1, 1), Ratio(2, 1)], "a")]
1814 #[case(Flex::Legacy, 2, &[Ratio(0, 1), Ratio(0, 1)], "bb")]
1815 #[case(Flex::Legacy, 2, &[Ratio(0, 1), Ratio(1, 4)], "bb")]
1816 #[case(Flex::Legacy, 2, &[Ratio(0, 1), Ratio(1, 2)], "bb")]
1817 #[case(Flex::Legacy, 2, &[Ratio(0, 1), Ratio(1, 1)], "bb")]
1818 #[case(Flex::Legacy, 2, &[Ratio(0, 1), Ratio(2, 1)], "bb")]
1819 #[case(Flex::Legacy, 2, &[Ratio(1, 10), Ratio(0, 1)], "bb")]
1820 #[case(Flex::Legacy, 2, &[Ratio(1, 10), Ratio(1, 4)], "bb")]
1821 #[case(Flex::Legacy, 2, &[Ratio(1, 10), Ratio(1, 2)], "bb")]
1822 #[case(Flex::Legacy, 2, &[Ratio(1, 10), Ratio(1, 1)], "bb")]
1823 #[case(Flex::Legacy, 2, &[Ratio(1, 10), Ratio(2, 1)], "bb")]
1824 #[case(Flex::Legacy, 2, &[Ratio(1, 4), Ratio(0, 1)], "ab")]
1825 #[case(Flex::Legacy, 2, &[Ratio(1, 4), Ratio(1, 4)], "ab")]
1826 #[case(Flex::Legacy, 2, &[Ratio(1, 4), Ratio(1, 2)], "ab")]
1827 #[case(Flex::Legacy, 2, &[Ratio(1, 4), Ratio(1, 1)], "ab")]
1828 #[case(Flex::Legacy, 2, &[Ratio(1, 4), Ratio(2, 1)], "ab")]
1829 #[case(Flex::Legacy, 2, &[Ratio(1, 3), Ratio(0, 1)], "ab")]
1830 #[case(Flex::Legacy, 2, &[Ratio(1, 3), Ratio(1, 4)], "ab")]
1831 #[case(Flex::Legacy, 2, &[Ratio(1, 3), Ratio(1, 2)], "ab")]
1832 #[case(Flex::Legacy, 2, &[Ratio(1, 3), Ratio(1, 1)], "ab")]
1833 #[case(Flex::Legacy, 2, &[Ratio(1, 3), Ratio(2, 1)], "ab")]
1834 #[case(Flex::Legacy, 2, &[Ratio(1, 2), Ratio(0, 1)], "ab")]
1835 #[case(Flex::Legacy, 2, &[Ratio(1, 2), Ratio(1, 2)], "ab")]
1836 #[case(Flex::Legacy, 2, &[Ratio(1, 2), Ratio(1, 1)], "ab")]
1837 #[case(Flex::Legacy, 2, &[Ratio(1, 1), Ratio(0, 1)], "aa")]
1838 #[case(Flex::Legacy, 2, &[Ratio(1, 1), Ratio(1, 2)], "aa")]
1839 #[case(Flex::Legacy, 2, &[Ratio(1, 1), Ratio(1, 1)], "aa")]
1840 #[case(Flex::Legacy, 3, &[Ratio(1, 3), Ratio(1, 3)], "abb")]
1841 #[case(Flex::Legacy, 3, &[Ratio(1, 3), Ratio(2,3)], "abb")]
1842 #[case(Flex::Legacy, 10, &[Ratio(0, 1), Ratio(0, 1)], "bbbbbbbbbb" )]
1843 #[case(Flex::Legacy, 10, &[Ratio(0, 1), Ratio(1, 4)], "bbbbbbbbbb" )]
1844 #[case(Flex::Legacy, 10, &[Ratio(0, 1), Ratio(1, 2)], "bbbbbbbbbb" )]
1845 #[case(Flex::Legacy, 10, &[Ratio(0, 1), Ratio(1, 1)], "bbbbbbbbbb" )]
1846 #[case(Flex::Legacy, 10, &[Ratio(0, 1), Ratio(2, 1)], "bbbbbbbbbb" )]
1847 #[case(Flex::Legacy, 10, &[Ratio(1, 10), Ratio(0, 1)], "abbbbbbbbb" )]
1848 #[case(Flex::Legacy, 10, &[Ratio(1, 10), Ratio(1, 4)], "abbbbbbbbb" )]
1849 #[case(Flex::Legacy, 10, &[Ratio(1, 10), Ratio(1, 2)], "abbbbbbbbb" )]
1850 #[case(Flex::Legacy, 10, &[Ratio(1, 10), Ratio(1, 1)], "abbbbbbbbb" )]
1851 #[case(Flex::Legacy, 10, &[Ratio(1, 10), Ratio(2, 1)], "abbbbbbbbb" )]
1852 #[case(Flex::Legacy, 10, &[Ratio(1, 4), Ratio(0, 1)], "aaabbbbbbb" )]
1853 #[case(Flex::Legacy, 10, &[Ratio(1, 4), Ratio(1, 4)], "aaabbbbbbb" )]
1854 #[case(Flex::Legacy, 10, &[Ratio(1, 4), Ratio(1, 2)], "aaabbbbbbb" )]
1855 #[case(Flex::Legacy, 10, &[Ratio(1, 4), Ratio(1, 1)], "aaabbbbbbb" )]
1856 #[case(Flex::Legacy, 10, &[Ratio(1, 4), Ratio(2, 1)], "aaabbbbbbb" )]
1857 #[case(Flex::Legacy, 10, &[Ratio(1, 3), Ratio(0, 1)], "aaabbbbbbb" )]
1858 #[case(Flex::Legacy, 10, &[Ratio(1, 3), Ratio(1, 4)], "aaabbbbbbb" )]
1859 #[case(Flex::Legacy, 10, &[Ratio(1, 3), Ratio(1, 2)], "aaabbbbbbb" )]
1860 #[case(Flex::Legacy, 10, &[Ratio(1, 3), Ratio(1, 1)], "aaabbbbbbb" )]
1861 #[case(Flex::Legacy, 10, &[Ratio(1, 3), Ratio(2, 1)], "aaabbbbbbb" )]
1862 #[case(Flex::Legacy, 10, &[Ratio(1, 2), Ratio(0, 1)], "aaaaabbbbb" )]
1863 #[case(Flex::Legacy, 10, &[Ratio(1, 2), Ratio(1, 2)], "aaaaabbbbb" )]
1864 #[case(Flex::Legacy, 10, &[Ratio(1, 2), Ratio(1, 1)], "aaaaabbbbb" )]
1865 #[case(Flex::Legacy, 10, &[Ratio(1, 1), Ratio(0, 1)], "aaaaaaaaaa" )]
1866 #[case(Flex::Legacy, 10, &[Ratio(1, 1), Ratio(1, 2)], "aaaaaaaaaa" )]
1867 #[case(Flex::Legacy, 10, &[Ratio(1, 1), Ratio(1, 1)], "aaaaaaaaaa" )]
1868 fn ratio(
1869 #[case] flex: Flex,
1870 #[case] width: u16,
1871 #[case] constraints: &[Constraint],
1872 #[case] expected: &str,
1873 ) {
1874 letters(flex, constraints, width, expected);
1875 }
1876
1877 #[rstest]
1878 #[case(Flex::Start, 10, &[Ratio(0, 1), Ratio(0, 1)], " " )]
1879 #[case(Flex::Start, 10, &[Ratio(0, 1), Ratio(1, 4)], "bbb " )]
1880 #[case(Flex::Start, 10, &[Ratio(0, 1), Ratio(1, 2)], "bbbbb " )]
1881 #[case(Flex::Start, 10, &[Ratio(0, 1), Ratio(1, 1)], "bbbbbbbbbb" )]
1882 #[case(Flex::Start, 10, &[Ratio(0, 1), Ratio(2, 1)], "bbbbbbbbbb" )]
1883 #[case(Flex::Start, 10, &[Ratio(1, 10), Ratio(0, 1)], "a " )]
1884 #[case(Flex::Start, 10, &[Ratio(1, 10), Ratio(1, 4)], "abbb " )]
1885 #[case(Flex::Start, 10, &[Ratio(1, 10), Ratio(1, 2)], "abbbbb " )]
1886 #[case(Flex::Start, 10, &[Ratio(1, 10), Ratio(1, 1)], "abbbbbbbbb" )]
1887 #[case(Flex::Start, 10, &[Ratio(1, 10), Ratio(2, 1)], "abbbbbbbbb" )]
1888 #[case(Flex::Start, 10, &[Ratio(1, 4), Ratio(0, 1)], "aaa " )]
1889 #[case(Flex::Start, 10, &[Ratio(1, 4), Ratio(1, 4)], "aaabb " )]
1890 #[case(Flex::Start, 10, &[Ratio(1, 4), Ratio(1, 2)], "aaabbbbb " )]
1891 #[case(Flex::Start, 10, &[Ratio(1, 4), Ratio(1, 1)], "aaabbbbbbb" )]
1892 #[case(Flex::Start, 10, &[Ratio(1, 4), Ratio(2, 1)], "aaabbbbbbb" )]
1893 #[case(Flex::Start, 10, &[Ratio(1, 3), Ratio(0, 1)], "aaa " )]
1894 #[case(Flex::Start, 10, &[Ratio(1, 3), Ratio(1, 4)], "aaabbb " )]
1895 #[case(Flex::Start, 10, &[Ratio(1, 3), Ratio(1, 2)], "aaabbbbb " )]
1896 #[case(Flex::Start, 10, &[Ratio(1, 3), Ratio(1, 1)], "aaabbbbbbb" )]
1897 #[case(Flex::Start, 10, &[Ratio(1, 3), Ratio(2, 1)], "aaabbbbbbb" )]
1898 #[case(Flex::Start, 10, &[Ratio(1, 2), Ratio(0, 1)], "aaaaa " )]
1899 #[case(Flex::Start, 10, &[Ratio(1, 2), Ratio(1, 2)], "aaaaabbbbb" )]
1900 #[case(Flex::Start, 10, &[Ratio(1, 2), Ratio(1, 1)], "aaaaabbbbb" )]
1901 #[case(Flex::Start, 10, &[Ratio(1, 1), Ratio(0, 1)], "aaaaaaaaaa" )]
1902 #[case(Flex::Start, 10, &[Ratio(1, 1), Ratio(1, 2)], "aaaaabbbbb" )]
1903 #[case(Flex::Start, 10, &[Ratio(1, 1), Ratio(1, 1)], "aaaaabbbbb" )]
1904 #[case(Flex::Start, 10, &[Ratio(1, 1), Ratio(2, 1)], "aaaaabbbbb" )]
1905 fn ratio_start(
1906 #[case] flex: Flex,
1907 #[case] width: u16,
1908 #[case] constraints: &[Constraint],
1909 #[case] expected: &str,
1910 ) {
1911 letters(flex, constraints, width, expected);
1912 }
1913
1914 #[rstest]
1915 #[case(Flex::SpaceBetween, 10, &[Ratio(0, 1), Ratio(0, 1)], " " )]
1916 #[case(Flex::SpaceBetween, 10, &[Ratio(0, 1), Ratio(1, 4)], " bb" )]
1917 #[case(Flex::SpaceBetween, 10, &[Ratio(0, 1), Ratio(1, 2)], " bbbbb" )]
1918 #[case(Flex::SpaceBetween, 10, &[Ratio(0, 1), Ratio(1, 1)], "bbbbbbbbbb" )]
1919 #[case(Flex::SpaceBetween, 10, &[Ratio(0, 1), Ratio(2, 1)], "bbbbbbbbbb" )]
1920 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 10), Ratio(0, 1)], "a " )]
1921 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 10), Ratio(1, 4)], "a bb" )]
1922 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 10), Ratio(1, 2)], "a bbbbb" )]
1923 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 10), Ratio(1, 1)], "abbbbbbbbb" )]
1924 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 10), Ratio(2, 1)], "abbbbbbbbb" )]
1925 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 4), Ratio(0, 1)], "aaa " )]
1926 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 4), Ratio(1, 4)], "aaa bb" )]
1927 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 4), Ratio(1, 2)], "aaa bbbbb" )]
1928 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 4), Ratio(1, 1)], "aaabbbbbbb" )]
1929 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 4), Ratio(2, 1)], "aaabbbbbbb" )]
1930 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 3), Ratio(0, 1)], "aaa " )]
1931 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 3), Ratio(1, 4)], "aaa bb" )]
1932 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 3), Ratio(1, 2)], "aaa bbbbb" )]
1933 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 3), Ratio(1, 1)], "aaabbbbbbb" )]
1934 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 3), Ratio(2, 1)], "aaabbbbbbb" )]
1935 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 2), Ratio(0, 1)], "aaaaa " )]
1936 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 2), Ratio(1, 2)], "aaaaabbbbb" )]
1937 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 2), Ratio(1, 1)], "aaaaabbbbb" )]
1938 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 1), Ratio(0, 1)], "aaaaaaaaaa" )]
1939 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 1), Ratio(1, 2)], "aaaaabbbbb" )]
1940 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 1), Ratio(1, 1)], "aaaaabbbbb" )]
1941 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 1), Ratio(2, 1)], "aaaaabbbbb" )]
1942 fn ratio_spacebetween(
1943 #[case] flex: Flex,
1944 #[case] width: u16,
1945 #[case] constraints: &[Constraint],
1946 #[case] expected: &str,
1947 ) {
1948 letters(flex, constraints, width, expected);
1949 }
1950
1951 #[test]
1952 fn vertical_split_by_height() {
1953 let target = Rect {
1954 x: 2,
1955 y: 2,
1956 width: 10,
1957 height: 10,
1958 };
1959
1960 let chunks = Layout::default()
1961 .direction(Direction::Vertical)
1962 .constraints([
1963 Constraint::Percentage(10),
1964 Constraint::Max(5),
1965 Constraint::Min(1),
1966 ])
1967 .split(target);
1968
1969 assert_eq!(chunks.iter().map(|r| r.height).sum::<u16>(), target.height);
1970 chunks.windows(2).for_each(|w| assert!(w[0].y <= w[1].y));
1971 }
1972
1973 #[test]
1974 fn edge_cases() {
1975 let layout = Layout::default()
1977 .constraints([
1978 Constraint::Percentage(50),
1979 Constraint::Percentage(50),
1980 Constraint::Min(0),
1981 ])
1982 .split(Rect::new(0, 0, 1, 1));
1983 assert_eq!(
1984 layout[..],
1985 [
1986 Rect::new(0, 0, 1, 1),
1987 Rect::new(0, 1, 1, 0),
1988 Rect::new(0, 1, 1, 0)
1989 ]
1990 );
1991
1992 let layout = Layout::default()
1994 .constraints([
1995 Constraint::Max(1),
1996 Constraint::Percentage(99),
1997 Constraint::Min(0),
1998 ])
1999 .split(Rect::new(0, 0, 1, 1));
2000 assert_eq!(
2001 layout[..],
2002 [
2003 Rect::new(0, 0, 1, 0),
2004 Rect::new(0, 0, 1, 1),
2005 Rect::new(0, 1, 1, 0)
2006 ]
2007 );
2008
2009 let layout = Layout::default()
2013 .constraints([Min(1), Length(0), Min(1)])
2014 .direction(Direction::Horizontal)
2015 .split(Rect::new(0, 0, 1, 1));
2016 assert_eq!(
2017 layout[..],
2018 [
2019 Rect::new(0, 0, 1, 1),
2020 Rect::new(1, 0, 0, 1),
2021 Rect::new(1, 0, 0, 1),
2022 ]
2023 );
2024
2025 let layout = Layout::default()
2027 .constraints([Length(3), Min(4), Length(1), Min(4)])
2028 .direction(Direction::Horizontal)
2029 .split(Rect::new(0, 0, 7, 1));
2030 assert_eq!(
2031 layout[..],
2032 [
2033 Rect::new(0, 0, 0, 1),
2034 Rect::new(0, 0, 4, 1),
2035 Rect::new(4, 0, 0, 1),
2036 Rect::new(4, 0, 3, 1),
2037 ]
2038 );
2039 }
2040
2041 #[rstest]
2042 #[case::len_min1(vec![Length(25), Min(100)], vec![0..0, 0..100])]
2043 #[case::len_min2(vec![Length(25), Min(0)], vec![0..25, 25..100])]
2044 #[case::len_max1(vec![Length(25), Max(0)], vec![0..100, 100..100])]
2045 #[case::len_max2(vec![Length(25), Max(100)], vec![0..25, 25..100])]
2046 #[case::len_perc(vec![Length(25), Percentage(25)], vec![0..25, 25..100])]
2047 #[case::perc_len(vec![Percentage(25), Length(25)], vec![0..75, 75..100])]
2048 #[case::len_ratio(vec![Length(25), Ratio(1, 4)], vec![0..25, 25..100])]
2049 #[case::ratio_len(vec![Ratio(1, 4), Length(25)], vec![0..75, 75..100])]
2050 #[case::len_len(vec![Length(25), Length(25)], vec![0..25, 25..100])]
2051 #[case::len1(vec![Length(25), Length(25), Length(25)], vec![0..25, 25..50, 50..100])]
2052 #[case::len2(vec![Length(15), Length(35), Length(25)], vec![0..15, 15..50, 50..100])]
2053 #[case::len3(vec![Length(25), Length(25), Length(25)], vec![0..25, 25..50, 50..100])]
2054 fn constraint_length(
2055 #[case] constraints: Vec<Constraint>,
2056 #[case] expected: Vec<Range<u16>>,
2057 ) {
2058 let rect = Rect::new(0, 0, 100, 1);
2059 let ranges = Layout::horizontal(constraints)
2060 .flex(Flex::Legacy)
2061 .split(rect)
2062 .iter()
2063 .map(|r| r.left()..r.right())
2064 .collect_vec();
2065 assert_eq!(ranges, expected);
2066 }
2067
2068 #[rstest]
2069 #[case(7, vec![Length(4), Length(4)], vec![0..3, 4..7])]
2070 #[case(4, vec![Length(4), Length(4)], vec![0..2, 3..4])]
2071 fn table_length(
2072 #[case] width: u16,
2073 #[case] constraints: Vec<Constraint>,
2074 #[case] expected: Vec<Range<u16>>,
2075 ) {
2076 let rect = Rect::new(0, 0, width, 1);
2077 let ranges = Layout::horizontal(constraints)
2078 .spacing(1)
2079 .flex(Flex::Start)
2080 .split(rect)
2081 .iter()
2082 .map(|r| r.left()..r.right())
2083 .collect::<Vec<Range<u16>>>();
2084 assert_eq!(ranges, expected);
2085 }
2086
2087 #[rstest]
2088 #[case::min_len_max(vec![Min(25), Length(25), Max(25)], vec![0..50, 50..75, 75..100])]
2089 #[case::max_len_min(vec![Max(25), Length(25), Min(25)], vec![0..25, 25..50, 50..100])]
2090 #[case::len_len_len(vec![Length(33), Length(33), Length(33)], vec![0..33, 33..66, 66..100])]
2091 #[case::len_len_len_25(vec![Length(25), Length(25), Length(25)], vec![0..25, 25..50, 50..100])]
2092 #[case::perc_len_ratio(vec![Percentage(25), Length(25), Ratio(1, 4)], vec![0..25, 25..50, 50..100])]
2093 #[case::len_ratio_perc(vec![Length(25), Ratio(1, 4), Percentage(25)], vec![0..25, 25..75, 75..100])]
2094 #[case::ratio_len_perc(vec![Ratio(1, 4), Length(25), Percentage(25)], vec![0..50, 50..75, 75..100])]
2095 #[case::ratio_perc_len(vec![Ratio(1, 4), Percentage(25), Length(25)], vec![0..50, 50..75, 75..100])]
2096 #[case::len_len_min(vec![Length(100), Length(1), Min(20)], vec![0..80, 80..80, 80..100])]
2097 #[case::min_len_len(vec![Min(20), Length(1), Length(100)], vec![0..20, 20..21, 21..100])]
2098 #[case::fill_len_fill(vec![Fill(1), Length(10), Fill(1)], vec![0..45, 45..55, 55..100])]
2099 #[case::fill_len_fill_2(vec![Fill(1), Length(10), Fill(2)], vec![0..30, 30..40, 40..100])]
2100 #[case::fill_len_fill_4(vec![Fill(1), Length(10), Fill(4)], vec![0..18, 18..28, 28..100])]
2101 #[case::fill_len_fill_5(vec![Fill(1), Length(10), Fill(5)], vec![0..15, 15..25, 25..100])]
2102 #[case::len_len_len_25(vec![Length(25), Length(25), Length(25)], vec![0..25, 25..50, 50..100])]
2103 #[case::unstable_test(vec![Length(25), Length(25), Length(25)], vec![0..25, 25..50, 50..100])]
2104 fn length_is_higher_priority(
2105 #[case] constraints: Vec<Constraint>,
2106 #[case] expected: Vec<Range<u16>>,
2107 ) {
2108 let rect = Rect::new(0, 0, 100, 1);
2109 let ranges = Layout::horizontal(constraints)
2110 .flex(Flex::Legacy)
2111 .split(rect)
2112 .iter()
2113 .map(|r| r.left()..r.right())
2114 .collect_vec();
2115 assert_eq!(ranges, expected);
2116 }
2117
2118 #[rstest]
2119 #[case::min_len_max(vec![Min(25), Length(25), Max(25)], vec![50, 25, 25])]
2120 #[case::max_len_min(vec![Max(25), Length(25), Min(25)], vec![25, 25, 50])]
2121 #[case::len_len_len1(vec![Length(33), Length(33), Length(33)], vec![33, 33, 33])]
2122 #[case::len_len_len2(vec![Length(25), Length(25), Length(25)], vec![25, 25, 25])]
2123 #[case::perc_len_ratio(vec![Percentage(25), Length(25), Ratio(1, 4)], vec![25, 25, 25])]
2124 #[case::len_ratio_perc(vec![Length(25), Ratio(1, 4), Percentage(25)], vec![25, 25, 25])]
2125 #[case::ratio_len_perc(vec![Ratio(1, 4), Length(25), Percentage(25)], vec![25, 25, 25])]
2126 #[case::ratio_perc_len(vec![Ratio(1, 4), Percentage(25), Length(25)], vec![25, 25, 25])]
2127 #[case::len_len_min(vec![Length(100), Length(1), Min(20)], vec![79, 1, 20])]
2128 #[case::min_len_len(vec![Min(20), Length(1), Length(100)], vec![20, 1, 79])]
2129 #[case::fill_len_fill1(vec![Fill(1), Length(10), Fill(1)], vec![45, 10, 45])]
2130 #[case::fill_len_fill2(vec![Fill(1), Length(10), Fill(2)], vec![30, 10, 60])]
2131 #[case::fill_len_fill4(vec![Fill(1), Length(10), Fill(4)], vec![18, 10, 72])]
2132 #[case::fill_len_fill5(vec![Fill(1), Length(10), Fill(5)], vec![15, 10, 75])]
2133 #[case::len_len_len3(vec![Length(25), Length(25), Length(25)], vec![25, 25, 25])]
2134 fn length_is_higher_priority_in_flex(
2135 #[case] constraints: Vec<Constraint>,
2136 #[case] expected: Vec<u16>,
2137 ) {
2138 let rect = Rect::new(0, 0, 100, 1);
2139 for flex in [
2140 Flex::Start,
2141 Flex::End,
2142 Flex::Center,
2143 Flex::SpaceAround,
2144 Flex::SpaceBetween,
2145 ] {
2146 let widths = Layout::horizontal(&constraints)
2147 .flex(flex)
2148 .split(rect)
2149 .iter()
2150 .map(|r| r.width)
2151 .collect_vec();
2152 assert_eq!(widths, expected);
2153 }
2154 }
2155
2156 #[rstest]
2157 #[case::fill_len_fill(vec![Fill(1), Length(10), Fill(2)], vec![0..13, 13..23, 23..50])]
2158 #[case::len_fill_fill(vec![Length(10), Fill(2), Fill(1)], vec![0..10, 10..37, 37..50])] fn fixed_with_50_width(
2160 #[case] constraints: Vec<Constraint>,
2161 #[case] expected: Vec<Range<u16>>,
2162 ) {
2163 let rect = Rect::new(0, 0, 50, 1);
2164 let ranges = Layout::horizontal(constraints)
2165 .flex(Flex::Legacy)
2166 .split(rect)
2167 .iter()
2168 .map(|r| r.left()..r.right())
2169 .collect_vec();
2170 assert_eq!(ranges, expected);
2171 }
2172
2173 #[rstest]
2174 #[case::same_fill(vec![Fill(1), Fill(2), Fill(1), Fill(1)], vec![0..20, 20..60, 60..80, 80..100])]
2175 #[case::inc_fill(vec![Fill(1), Fill(2), Fill(3), Fill(4)], vec![0..10, 10..30, 30..60, 60..100])]
2176 #[case::dec_fill(vec![Fill(4), Fill(3), Fill(2), Fill(1)], vec![0..40, 40..70, 70..90, 90..100])]
2177 #[case::rand_fill1(vec![Fill(1), Fill(3), Fill(2), Fill(4)], vec![0..10, 10..40, 40..60, 60..100])]
2178 #[case::rand_fill2(vec![Fill(1), Fill(3), Length(50), Fill(2), Fill(4)], vec![0..5, 5..20, 20..70, 70..80, 80..100])]
2179 #[case::rand_fill3(vec![Fill(1), Fill(3), Percentage(50), Fill(2), Fill(4)], vec![0..5, 5..20, 20..70, 70..80, 80..100])]
2180 #[case::rand_fill4(vec![Fill(1), Fill(3), Min(50), Fill(2), Fill(4)], vec![0..5, 5..20, 20..70, 70..80, 80..100])]
2181 #[case::rand_fill5(vec![Fill(1), Fill(3), Max(50), Fill(2), Fill(4)], vec![0..5, 5..20, 20..70, 70..80, 80..100])]
2182 #[case::zero_fill1(vec![Fill(0), Fill(1), Fill(0)], vec![0..0, 0..100, 100..100])]
2183 #[case::zero_fill2(vec![Fill(0), Length(1), Fill(0)], vec![0..50, 50..51, 51..100])]
2184 #[case::zero_fill3(vec![Fill(0), Percentage(1), Fill(0)], vec![0..50, 50..51, 51..100])]
2185 #[case::zero_fill4(vec![Fill(0), Min(1), Fill(0)], vec![0..50, 50..51, 51..100])]
2186 #[case::zero_fill5(vec![Fill(0), Max(1), Fill(0)], vec![0..50, 50..51, 51..100])]
2187 #[case::zero_fill6(vec![Fill(0), Fill(2), Fill(0), Fill(1)], vec![0..0, 0..67, 67..67, 67..100])]
2188 #[case::space_fill1(vec![Fill(0), Fill(2), Percentage(20)], vec![0..0, 0..80, 80..100])]
2189 #[case::space_fill2(vec![Fill(0), Fill(0), Percentage(20)], vec![0..40, 40..80, 80..100])]
2190 #[case::space_fill3(vec![Fill(0), Ratio(1, 5)], vec![0..80, 80..100])]
2191 #[case::space_fill4(vec![Fill(0), Fill(u16::MAX)], vec![0..0, 0..100])]
2192 #[case::space_fill5(vec![Fill(u16::MAX), Fill(0)], vec![0..100, 100..100])]
2193 #[case::space_fill6(vec![Fill(0), Percentage(20)], vec![0..80, 80..100])]
2194 #[case::space_fill7(vec![Fill(1), Percentage(20)], vec![0..80, 80..100])]
2195 #[case::space_fill8(vec![Fill(u16::MAX), Percentage(20)], vec![0..80, 80..100])]
2196 #[case::space_fill9(vec![Fill(u16::MAX), Fill(0), Percentage(20)], vec![0..80, 80..80, 80..100])]
2197 #[case::space_fill10(vec![Fill(0), Length(20)], vec![0..80, 80..100])]
2198 #[case::space_fill11(vec![Fill(0), Min(20)], vec![0..80, 80..100])]
2199 #[case::space_fill12(vec![Fill(0), Max(20)], vec![0..80, 80..100])]
2200 #[case::fill_collapse1(vec![Fill(1), Fill(1), Fill(1), Min(30), Length(50)], vec![0..7, 7..13, 13..20, 20..50, 50..100])]
2201 #[case::fill_collapse2(vec![Fill(1), Fill(1), Fill(1), Length(50), Length(50)], vec![0..0, 0..0, 0..0, 0..50, 50..100])]
2202 #[case::fill_collapse3(vec![Fill(1), Fill(1), Fill(1), Length(75), Length(50)], vec![0..0, 0..0, 0..0, 0..75, 75..100])]
2203 #[case::fill_collapse4(vec![Fill(1), Fill(1), Fill(1), Min(50), Max(50)], vec![0..0, 0..0, 0..0, 0..50, 50..100])]
2204 #[case::fill_collapse5(vec![Fill(1), Fill(1), Fill(1), Ratio(1, 1)], vec![0..0, 0..0, 0..0, 0..100])]
2205 #[case::fill_collapse6(vec![Fill(1), Fill(1), Fill(1), Percentage(100)], vec![0..0, 0..0, 0..0, 0..100])]
2206 fn fill(#[case] constraints: Vec<Constraint>, #[case] expected: Vec<Range<u16>>) {
2207 let rect = Rect::new(0, 0, 100, 1);
2208 let ranges = Layout::horizontal(constraints)
2209 .flex(Flex::Legacy)
2210 .split(rect)
2211 .iter()
2212 .map(|r| r.left()..r.right())
2213 .collect_vec();
2214 assert_eq!(ranges, expected);
2215 }
2216
2217 #[rstest]
2218 #[case::min_percentage(vec![Min(0), Percentage(20)], vec![0..80, 80..100])]
2219 #[case::max_percentage(vec![Max(0), Percentage(20)], vec![0..0, 0..100])]
2220 fn percentage_parameterized(
2221 #[case] constraints: Vec<Constraint>,
2222 #[case] expected: Vec<Range<u16>>,
2223 ) {
2224 let rect = Rect::new(0, 0, 100, 1);
2225 let ranges = Layout::horizontal(constraints)
2226 .flex(Flex::Legacy)
2227 .split(rect)
2228 .iter()
2229 .map(|r| r.left()..r.right())
2230 .collect_vec();
2231 assert_eq!(ranges, expected);
2232 }
2233
2234 #[rstest]
2235 #[case::max_min(vec![Max(100), Min(0)], vec![0..100, 100..100])]
2236 #[case::min_max(vec![Min(0), Max(100)], vec![0..0, 0..100])]
2237 #[case::length_min(vec![Length(u16::MAX), Min(10)], vec![0..90, 90..100])]
2238 #[case::min_length(vec![Min(10), Length(u16::MAX)], vec![0..10, 10..100])]
2239 #[case::length_max(vec![Length(0), Max(10)], vec![0..90, 90..100])]
2240 #[case::max_length(vec![Max(10), Length(0)], vec![0..10, 10..100])]
2241 fn min_max(#[case] constraints: Vec<Constraint>, #[case] expected: Vec<Range<u16>>) {
2242 let rect = Rect::new(0, 0, 100, 1);
2243 let ranges = Layout::horizontal(constraints)
2244 .flex(Flex::Legacy)
2245 .split(rect)
2246 .iter()
2247 .map(|r| r.left()..r.right())
2248 .collect_vec();
2249 assert_eq!(ranges, expected);
2250 }
2251
2252 #[rstest]
2253 #[case::length_legacy(vec![Length(50)], vec![0..100], Flex::Legacy)]
2254 #[case::length_start(vec![Length(50)], vec![0..50], Flex::Start)]
2255 #[case::length_end(vec![Length(50)], vec![50..100], Flex::End)]
2256 #[case::length_center(vec![Length(50)], vec![25..75], Flex::Center)]
2257 #[case::ratio_legacy(vec![Ratio(1, 2)], vec![0..100], Flex::Legacy)]
2258 #[case::ratio_start(vec![Ratio(1, 2)], vec![0..50], Flex::Start)]
2259 #[case::ratio_end(vec![Ratio(1, 2)], vec![50..100], Flex::End)]
2260 #[case::ratio_center(vec![Ratio(1, 2)], vec![25..75], Flex::Center)]
2261 #[case::percent_legacy(vec![Percentage(50)], vec![0..100], Flex::Legacy)]
2262 #[case::percent_start(vec![Percentage(50)], vec![0..50], Flex::Start)]
2263 #[case::percent_end(vec![Percentage(50)], vec![50..100], Flex::End)]
2264 #[case::percent_center(vec![Percentage(50)], vec![25..75], Flex::Center)]
2265 #[case::min_legacy(vec![Min(50)], vec![0..100], Flex::Legacy)]
2266 #[case::min_start(vec![Min(50)], vec![0..100], Flex::Start)]
2267 #[case::min_end(vec![Min(50)], vec![0..100], Flex::End)]
2268 #[case::min_center(vec![Min(50)], vec![0..100], Flex::Center)]
2269 #[case::max_legacy(vec![Max(50)], vec![0..100], Flex::Legacy)]
2270 #[case::max_start(vec![Max(50)], vec![0..50], Flex::Start)]
2271 #[case::max_end(vec![Max(50)], vec![50..100], Flex::End)]
2272 #[case::max_center(vec![Max(50)], vec![25..75], Flex::Center)]
2273 #[case::spacebetween_becomes_stretch1(vec![Min(1)], vec![0..100], Flex::SpaceBetween)]
2274 #[case::spacebetween_becomes_stretch2(vec![Max(20)], vec![0..100], Flex::SpaceBetween)]
2275 #[case::spacebetween_becomes_stretch3(vec![Length(20)], vec![0..100], Flex::SpaceBetween)]
2276 #[case::length_legacy2(vec![Length(25), Length(25)], vec![0..25, 25..100], Flex::Legacy)]
2277 #[case::length_start2(vec![Length(25), Length(25)], vec![0..25, 25..50], Flex::Start)]
2278 #[case::length_center2(vec![Length(25), Length(25)], vec![25..50, 50..75], Flex::Center)]
2279 #[case::length_end2(vec![Length(25), Length(25)], vec![50..75, 75..100], Flex::End)]
2280 #[case::length_spacebetween(vec![Length(25), Length(25)], vec![0..25, 75..100], Flex::SpaceBetween)]
2281 #[case::length_spacearound(vec![Length(25), Length(25)], vec![17..42, 58..83], Flex::SpaceAround)]
2282 #[case::percentage_legacy(vec![Percentage(25), Percentage(25)], vec![0..25, 25..100], Flex::Legacy)]
2283 #[case::percentage_start(vec![Percentage(25), Percentage(25)], vec![0..25, 25..50], Flex::Start)]
2284 #[case::percentage_center(vec![Percentage(25), Percentage(25)], vec![25..50, 50..75], Flex::Center)]
2285 #[case::percentage_end(vec![Percentage(25), Percentage(25)], vec![50..75, 75..100], Flex::End)]
2286 #[case::percentage_spacebetween(vec![Percentage(25), Percentage(25)], vec![0..25, 75..100], Flex::SpaceBetween)]
2287 #[case::percentage_spacearound(vec![Percentage(25), Percentage(25)], vec![17..42, 58..83], Flex::SpaceAround)]
2288 #[case::min_legacy2(vec![Min(25), Min(25)], vec![0..25, 25..100], Flex::Legacy)]
2289 #[case::min_start2(vec![Min(25), Min(25)], vec![0..50, 50..100], Flex::Start)]
2290 #[case::min_center2(vec![Min(25), Min(25)], vec![0..50, 50..100], Flex::Center)]
2291 #[case::min_end2(vec![Min(25), Min(25)], vec![0..50, 50..100], Flex::End)]
2292 #[case::min_spacebetween(vec![Min(25), Min(25)], vec![0..50, 50..100], Flex::SpaceBetween)]
2293 #[case::min_spacearound(vec![Min(25), Min(25)], vec![0..50, 50..100], Flex::SpaceAround)]
2294 #[case::max_legacy2(vec![Max(25), Max(25)], vec![0..25, 25..100], Flex::Legacy)]
2295 #[case::max_start2(vec![Max(25), Max(25)], vec![0..25, 25..50], Flex::Start)]
2296 #[case::max_center2(vec![Max(25), Max(25)], vec![25..50, 50..75], Flex::Center)]
2297 #[case::max_end2(vec![Max(25), Max(25)], vec![50..75, 75..100], Flex::End)]
2298 #[case::max_spacebetween(vec![Max(25), Max(25)], vec![0..25, 75..100], Flex::SpaceBetween)]
2299 #[case::max_spacearound(vec![Max(25), Max(25)], vec![17..42, 58..83], Flex::SpaceAround)]
2300 #[case::length_spaced_around(vec![Length(25), Length(25), Length(25)], vec![0..25, 38..63, 75..100], Flex::SpaceBetween)]
2301 fn flex_constraint(
2302 #[case] constraints: Vec<Constraint>,
2303 #[case] expected: Vec<Range<u16>>,
2304 #[case] flex: Flex,
2305 ) {
2306 let rect = Rect::new(0, 0, 100, 1);
2307 let ranges = Layout::horizontal(constraints)
2308 .flex(flex)
2309 .split(rect)
2310 .iter()
2311 .map(|r| r.left()..r.right())
2312 .collect_vec();
2313 assert_eq!(ranges, expected);
2314 }
2315
2316 #[rstest]
2317 #[case::length_overlap1(vec![(0 , 20) , (20 , 20) , (40 , 20)] , vec![Length(20) , Length(20) , Length(20)] , Flex::Start , 0)]
2318 #[case::length_overlap2(vec![(0 , 20) , (19 , 20) , (38 , 20)] , vec![Length(20) , Length(20) , Length(20)] , Flex::Start , -1)]
2319 #[case::length_overlap3(vec![(21 , 20) , (40 , 20) , (59 , 20)] , vec![Length(20) , Length(20) , Length(20)] , Flex::Center , -1)]
2320 #[case::length_overlap4(vec![(42 , 20) , (61 , 20) , (80 , 20)] , vec![Length(20) , Length(20) , Length(20)] , Flex::End , -1)]
2321 #[case::length_overlap5(vec![(0 , 20) , (19 , 20) , (38 , 62)] , vec![Length(20) , Length(20) , Length(20)] , Flex::Legacy , -1)]
2322 #[case::length_overlap6(vec![(0 , 20) , (40 , 20) , (80 , 20)] , vec![Length(20) , Length(20) , Length(20)] , Flex::SpaceBetween , -1)]
2323 #[case::length_overlap7(vec![(10 , 20) , (40 , 20) , (70 , 20)] , vec![Length(20) , Length(20) , Length(20)] , Flex::SpaceAround , -1)]
2324 fn flex_overlap(
2325 #[case] expected: Vec<(u16, u16)>,
2326 #[case] constraints: Vec<Constraint>,
2327 #[case] flex: Flex,
2328 #[case] spacing: i16,
2329 ) {
2330 let rect = Rect::new(0, 0, 100, 1);
2331 let r = Layout::horizontal(constraints)
2332 .flex(flex)
2333 .spacing(spacing)
2334 .split(rect);
2335 let result = r
2336 .iter()
2337 .map(|r| (r.x, r.width))
2338 .collect::<Vec<(u16, u16)>>();
2339
2340 assert_eq!(result, expected);
2341 }
2342
2343 #[rstest]
2344 #[case::length_spacing(vec![(0 , 20), (20, 20) , (40, 20)], vec![Length(20), Length(20), Length(20)], Flex::Start , 0)]
2345 #[case::length_spacing(vec![(0 , 20), (22, 20) , (44, 20)], vec![Length(20), Length(20), Length(20)], Flex::Start , 2)]
2346 #[case::length_spacing(vec![(18, 20), (40, 20) , (62, 20)], vec![Length(20), Length(20), Length(20)], Flex::Center , 2)]
2347 #[case::length_spacing(vec![(36, 20), (58, 20) , (80, 20)], vec![Length(20), Length(20), Length(20)], Flex::End , 2)]
2348 #[case::length_spacing(vec![(0 , 20), (22, 20) , (44, 56)], vec![Length(20), Length(20), Length(20)], Flex::Legacy , 2)]
2349 #[case::length_spacing(vec![(0 , 20), (40, 20) , (80, 20)], vec![Length(20), Length(20), Length(20)], Flex::SpaceBetween, 2)]
2350 #[case::length_spacing(vec![(10, 20), (40, 20) , (70, 20)], vec![Length(20), Length(20), Length(20)], Flex::SpaceAround, 2)]
2351 fn flex_spacing(
2352 #[case] expected: Vec<(u16, u16)>,
2353 #[case] constraints: Vec<Constraint>,
2354 #[case] flex: Flex,
2355 #[case] spacing: i16,
2356 ) {
2357 let rect = Rect::new(0, 0, 100, 1);
2358 let r = Layout::horizontal(constraints)
2359 .flex(flex)
2360 .spacing(spacing)
2361 .split(rect);
2362 let result = r
2363 .iter()
2364 .map(|r| (r.x, r.width))
2365 .collect::<Vec<(u16, u16)>>();
2366 assert_eq!(result, expected);
2367 }
2368
2369 #[rstest]
2370 #[case::a(vec![(0, 25), (25, 75)], vec![Length(25), Length(25)])]
2371 #[case::b(vec![(0, 25), (25, 75)], vec![Length(25), Percentage(25)])]
2372 #[case::c(vec![(0, 75), (75, 25)], vec![Percentage(25), Length(25)])]
2373 #[case::d(vec![(0, 75), (75, 25)], vec![Min(25), Percentage(25)])]
2374 #[case::e(vec![(0, 25), (25, 75)], vec![Percentage(25), Min(25)])]
2375 #[case::f(vec![(0, 25), (25, 75)], vec![Min(25), Percentage(100)])]
2376 #[case::g(vec![(0, 75), (75, 25)], vec![Percentage(100), Min(25)])]
2377 #[case::h(vec![(0, 25), (25, 75)], vec![Max(75), Percentage(75)])]
2378 #[case::i(vec![(0, 75), (75, 25)], vec![Percentage(75), Max(75)])]
2379 #[case::j(vec![(0, 25), (25, 75)], vec![Max(25), Percentage(25)])]
2380 #[case::k(vec![(0, 75), (75, 25)], vec![Percentage(25), Max(25)])]
2381 #[case::l(vec![(0, 25), (25, 75)], vec![Length(25), Ratio(1, 4)])]
2382 #[case::m(vec![(0, 75), (75, 25)], vec![Ratio(1, 4), Length(25)])]
2383 #[case::n(vec![(0, 25), (25, 75)], vec![Percentage(25), Ratio(1, 4)])]
2384 #[case::o(vec![(0, 75), (75, 25)], vec![Ratio(1, 4), Percentage(25)])]
2385 #[case::p(vec![(0, 25), (25, 75)], vec![Ratio(1, 4), Fill(25)])]
2386 #[case::q(vec![(0, 75), (75, 25)], vec![Fill(25), Ratio(1, 4)])]
2387 fn constraint_specification_tests_for_priority(
2388 #[case] expected: Vec<(u16, u16)>,
2389 #[case] constraints: Vec<Constraint>,
2390 ) {
2391 let rect = Rect::new(0, 0, 100, 1);
2392 let r = Layout::horizontal(constraints)
2393 .flex(Flex::Legacy)
2394 .split(rect)
2395 .iter()
2396 .map(|r| (r.x, r.width))
2397 .collect::<Vec<(u16, u16)>>();
2398 assert_eq!(r, expected);
2399 }
2400
2401 #[rstest]
2402 #[case::a(vec![(0, 20), (20, 20), (40, 20)], vec![Length(20), Length(20), Length(20)], Flex::Start, 0)]
2403 #[case::b(vec![(18, 20), (40, 20), (62, 20)], vec![Length(20), Length(20), Length(20)], Flex::Center, 2)]
2404 #[case::c(vec![(36, 20), (58, 20), (80, 20)], vec![Length(20), Length(20), Length(20)], Flex::End, 2)]
2405 #[case::d(vec![(0, 20), (22, 20), (44, 56)], vec![Length(20), Length(20), Length(20)], Flex::Legacy, 2)]
2406 #[case::e(vec![(0, 20), (22, 20), (44, 56)], vec![Length(20), Length(20), Length(20)], Flex::Legacy, 2)]
2407 #[case::f(vec![(10, 20), (40, 20), (70, 20)], vec![Length(20), Length(20), Length(20)], Flex::SpaceAround, 2)]
2408 fn constraint_specification_tests_for_priority_with_spacing(
2409 #[case] expected: Vec<(u16, u16)>,
2410 #[case] constraints: Vec<Constraint>,
2411 #[case] flex: Flex,
2412 #[case] spacing: i16,
2413 ) {
2414 let rect = Rect::new(0, 0, 100, 1);
2415 let r = Layout::horizontal(constraints)
2416 .spacing(spacing)
2417 .flex(flex)
2418 .split(rect)
2419 .iter()
2420 .map(|r| (r.x, r.width))
2421 .collect::<Vec<(u16, u16)>>();
2422 assert_eq!(r, expected);
2423 }
2424
2425 #[rstest]
2426 #[case::prop(vec![(0 , 10), (10, 80), (90 , 10)] , vec![Length(10), Fill(1), Length(10)], Flex::Legacy)]
2427 #[case::flex(vec![(0 , 10), (90 , 10)] , vec![Length(10), Length(10)], Flex::SpaceBetween)]
2428 #[case::prop(vec![(0 , 27), (27, 10), (37, 26), (63, 10), (73, 27)] , vec![Fill(1), Length(10), Fill(1), Length(10), Fill(1)], Flex::Legacy)]
2429 #[case::flex(vec![(27 , 10), (63, 10)] , vec![Length(10), Length(10)], Flex::SpaceAround)]
2430 #[case::prop(vec![(0 , 10), (10, 10), (20 , 80)] , vec![Length(10), Length(10), Fill(1)], Flex::Legacy)]
2431 #[case::flex(vec![(0 , 10), (10, 10)] , vec![Length(10), Length(10)], Flex::Start)]
2432 #[case::prop(vec![(0 , 80), (80 , 10), (90, 10)] , vec![Fill(1), Length(10), Length(10)], Flex::Legacy)]
2433 #[case::flex(vec![(80 , 10), (90, 10)] , vec![Length(10), Length(10)], Flex::End)]
2434 #[case::prop(vec![(0 , 40), (40, 10), (50, 10), (60, 40)] , vec![Fill(1), Length(10), Length(10), Fill(1)], Flex::Legacy)]
2435 #[case::flex(vec![(40 , 10), (50, 10)] , vec![Length(10), Length(10)], Flex::Center)]
2436 fn fill_vs_flex(
2437 #[case] expected: Vec<(u16, u16)>,
2438 #[case] constraints: Vec<Constraint>,
2439 #[case] flex: Flex,
2440 ) {
2441 let rect = Rect::new(0, 0, 100, 1);
2442 let r = Layout::horizontal(constraints).flex(flex).split(rect);
2443 let result = r
2444 .iter()
2445 .map(|r| (r.x, r.width))
2446 .collect::<Vec<(u16, u16)>>();
2447 assert_eq!(result, expected);
2448 }
2449
2450 #[rstest]
2451 #[case::flex0(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::Legacy , 0)]
2452 #[case::flex0(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::SpaceAround , 0)]
2453 #[case::flex0(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::SpaceBetween , 0)]
2454 #[case::flex0(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::Start , 0)]
2455 #[case::flex0(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::Center , 0)]
2456 #[case::flex0(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::End , 0)]
2457 #[case::flex10(vec![(0 , 45), (55 , 45)] , vec![Fill(1), Fill(1)], Flex::Legacy , 10)]
2458 #[case::flex10(vec![(0 , 45), (55 , 45)] , vec![Fill(1), Fill(1)], Flex::Start , 10)]
2459 #[case::flex10(vec![(0 , 45), (55 , 45)] , vec![Fill(1), Fill(1)], Flex::Center , 10)]
2460 #[case::flex10(vec![(0 , 45), (55 , 45)] , vec![Fill(1), Fill(1)], Flex::End , 10)]
2461 #[case::flex10(vec![(10 , 35), (55 , 35)] , vec![Fill(1), Fill(1)], Flex::SpaceAround , 10)]
2462 #[case::flex10(vec![(0 , 45), (55 , 45)] , vec![Fill(1), Fill(1)], Flex::SpaceBetween , 10)]
2463 #[case::flex_length0(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::Legacy , 0)]
2464 #[case::flex_length0(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceAround , 0)]
2465 #[case::flex_length0(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceBetween , 0)]
2466 #[case::flex_length0(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::Start , 0)]
2467 #[case::flex_length0(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::Center , 0)]
2468 #[case::flex_length0(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::End , 0)]
2469 #[case::flex_length10(vec![(0 , 35), (45, 10), (65 , 35)] , vec![Fill(1), Length(10), Fill(1)], Flex::Legacy , 10)]
2470 #[case::flex_length10(vec![(0 , 35), (45, 10), (65 , 35)] , vec![Fill(1), Length(10), Fill(1)], Flex::Start , 10)]
2471 #[case::flex_length10(vec![(0 , 35), (45, 10), (65 , 35)] , vec![Fill(1), Length(10), Fill(1)], Flex::Center , 10)]
2472 #[case::flex_length10(vec![(0 , 35), (45, 10), (65 , 35)] , vec![Fill(1), Length(10), Fill(1)], Flex::End , 10)]
2473 #[case::flex_length10(vec![(10 , 25), (45, 10), (65 , 25)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceAround , 10)]
2474 #[case::flex_length10(vec![(0 , 35), (45, 10), (65 , 35)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceBetween , 10)]
2475 fn fill_spacing(
2476 #[case] expected: Vec<(u16, u16)>,
2477 #[case] constraints: Vec<Constraint>,
2478 #[case] flex: Flex,
2479 #[case] spacing: i16,
2480 ) {
2481 let rect = Rect::new(0, 0, 100, 1);
2482 let r = Layout::horizontal(constraints)
2483 .flex(flex)
2484 .spacing(spacing)
2485 .split(rect);
2486 let result = r
2487 .iter()
2488 .map(|r| (r.x, r.width))
2489 .collect::<Vec<(u16, u16)>>();
2490 assert_eq!(expected, result);
2491 }
2492
2493 #[rstest]
2494 #[case::flex0_1(vec![(0 , 55), (45 , 55)] , vec![Fill(1), Fill(1)], Flex::Legacy , -10)]
2495 #[case::flex0_2(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::SpaceAround , -10)]
2496 #[case::flex0_3(vec![(0 , 55), (45 , 55)] , vec![Fill(1), Fill(1)], Flex::SpaceBetween , -10)]
2497 #[case::flex0_4(vec![(0 , 55), (45 , 55)] , vec![Fill(1), Fill(1)], Flex::Start , -10)]
2498 #[case::flex0_5(vec![(0 , 55), (45 , 55)] , vec![Fill(1), Fill(1)], Flex::Center , -10)]
2499 #[case::flex0_6(vec![(0 , 55), (45 , 55)] , vec![Fill(1), Fill(1)], Flex::End , -10)]
2500 #[case::flex10_1(vec![(0 , 51), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::Legacy , -1)]
2501 #[case::flex10_2(vec![(0 , 51), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::Start , -1)]
2502 #[case::flex10_3(vec![(0 , 51), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::Center , -1)]
2503 #[case::flex10_4(vec![(0 , 51), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::End , -1)]
2504 #[case::flex10_5(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::SpaceAround , -1)]
2505 #[case::flex10_6(vec![(0 , 51), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::SpaceBetween , -1)]
2506 #[case::flex_length0_1(vec![(0 , 55), (45, 10), (45 , 55)] , vec![Fill(1), Length(10), Fill(1)], Flex::Legacy , -10)]
2507 #[case::flex_length0_2(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceAround , -10)]
2508 #[case::flex_length0_3(vec![(0 , 55), (45, 10), (45 , 55)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceBetween , -10)]
2509 #[case::flex_length0_4(vec![(0 , 55), (45, 10), (45 , 55)] , vec![Fill(1), Length(10), Fill(1)], Flex::Start , -10)]
2510 #[case::flex_length0_5(vec![(0 , 55), (45, 10), (45 , 55)] , vec![Fill(1), Length(10), Fill(1)], Flex::Center , -10)]
2511 #[case::flex_length0_6(vec![(0 , 55), (45, 10), (45 , 55)] , vec![Fill(1), Length(10), Fill(1)], Flex::End , -10)]
2512 #[case::flex_length10_1(vec![(0 , 46), (45, 10), (54 , 46)] , vec![Fill(1), Length(10), Fill(1)], Flex::Legacy , -1)]
2513 #[case::flex_length10_2(vec![(0 , 46), (45, 10), (54 , 46)] , vec![Fill(1), Length(10), Fill(1)], Flex::Start , -1)]
2514 #[case::flex_length10_3(vec![(0 , 46), (45, 10), (54 , 46)] , vec![Fill(1), Length(10), Fill(1)], Flex::Center , -1)]
2515 #[case::flex_length10_4(vec![(0 , 46), (45, 10), (54 , 46)] , vec![Fill(1), Length(10), Fill(1)], Flex::End , -1)]
2516 #[case::flex_length10_5(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceAround , -1)]
2517 #[case::flex_length10_6(vec![(0 , 46), (45, 10), (54 , 46)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceBetween , -1)]
2518 fn fill_overlap(
2519 #[case] expected: Vec<(u16, u16)>,
2520 #[case] constraints: Vec<Constraint>,
2521 #[case] flex: Flex,
2522 #[case] spacing: i16,
2523 ) {
2524 let rect = Rect::new(0, 0, 100, 1);
2525 let r = Layout::horizontal(constraints)
2526 .flex(flex)
2527 .spacing(spacing)
2528 .split(rect);
2529 let result = r
2530 .iter()
2531 .map(|r| (r.x, r.width))
2532 .collect::<Vec<(u16, u16)>>();
2533 assert_eq!(result, expected);
2534 }
2535
2536 #[rstest]
2537 #[case::flex_length10(vec![(0, 10), (90, 10)], vec![Length(10), Length(10)], Flex::Center, 80)]
2538 fn flex_spacing_lower_priority_than_user_spacing(
2539 #[case] expected: Vec<(u16, u16)>,
2540 #[case] constraints: Vec<Constraint>,
2541 #[case] flex: Flex,
2542 #[case] spacing: i16,
2543 ) {
2544 let rect = Rect::new(0, 0, 100, 1);
2545 let r = Layout::horizontal(constraints)
2546 .flex(flex)
2547 .spacing(spacing)
2548 .split(rect);
2549 let result = r
2550 .iter()
2551 .map(|r| (r.x, r.width))
2552 .collect::<Vec<(u16, u16)>>();
2553 assert_eq!(result, expected);
2554 }
2555
2556 #[rstest]
2557 #[case::spacers(vec![(0, 0), (10, 0), (100, 0)], vec![Length(10), Length(10)], Flex::Legacy)]
2558 #[case::spacers(vec![(0, 0), (10, 80), (100, 0)], vec![Length(10), Length(10)], Flex::SpaceBetween)]
2559 #[case::spacers(vec![(0, 27), (37, 26), (73, 27)], vec![Length(10), Length(10)], Flex::SpaceAround)]
2560 #[case::spacers(vec![(0, 0), (10, 0), (20, 80)], vec![Length(10), Length(10)], Flex::Start)]
2561 #[case::spacers(vec![(0, 40), (50, 0), (60, 40)], vec![Length(10), Length(10)], Flex::Center)]
2562 #[case::spacers(vec![(0, 80), (90, 0), (100, 0)], vec![Length(10), Length(10)], Flex::End)]
2563 fn split_with_spacers_no_spacing(
2564 #[case] expected: Vec<(u16, u16)>,
2565 #[case] constraints: Vec<Constraint>,
2566 #[case] flex: Flex,
2567 ) {
2568 let rect = Rect::new(0, 0, 100, 1);
2569 let (_, s) = Layout::horizontal(&constraints)
2570 .flex(flex)
2571 .split_with_spacers(rect);
2572 assert_eq!(s.len(), constraints.len() + 1);
2573 let result = s
2574 .iter()
2575 .map(|r| (r.x, r.width))
2576 .collect::<Vec<(u16, u16)>>();
2577 assert_eq!(result, expected);
2578 }
2579
2580 #[rstest]
2581 #[case::spacers(vec![(0, 0), (10, 5), (100, 0)], vec![Length(10), Length(10)], Flex::Legacy, 5)]
2582 #[case::spacers(vec![(0, 0), (10, 80), (100, 0)], vec![Length(10), Length(10)], Flex::SpaceBetween, 5)]
2583 #[case::spacers(vec![(0, 27), (37, 26), (73, 27)], vec![Length(10), Length(10)], Flex::SpaceAround, 5)]
2584 #[case::spacers(vec![(0, 0), (10, 5), (25, 75)], vec![Length(10), Length(10)], Flex::Start, 5)]
2585 #[case::spacers(vec![(0, 38), (48, 5), (63, 37)], vec![Length(10), Length(10)], Flex::Center, 5)]
2586 #[case::spacers(vec![(0, 75), (85, 5), (100, 0)], vec![Length(10), Length(10)], Flex::End, 5)]
2587 fn split_with_spacers_and_spacing(
2588 #[case] expected: Vec<(u16, u16)>,
2589 #[case] constraints: Vec<Constraint>,
2590 #[case] flex: Flex,
2591 #[case] spacing: i16,
2592 ) {
2593 let rect = Rect::new(0, 0, 100, 1);
2594 let (_, s) = Layout::horizontal(&constraints)
2595 .flex(flex)
2596 .spacing(spacing)
2597 .split_with_spacers(rect);
2598 assert_eq!(s.len(), constraints.len() + 1);
2599 let result = s
2600 .iter()
2601 .map(|r| (r.x, r.width))
2602 .collect::<Vec<(u16, u16)>>();
2603 assert_eq!(expected, result);
2604 }
2605
2606 #[rstest]
2607 #[case::spacers_1(vec![(0, 0), (10, 0), (100, 0)], vec![Length(10), Length(10)], Flex::Legacy, -1)]
2608 #[case::spacers_2(vec![(0, 0), (10, 80), (100, 0)], vec![Length(10), Length(10)], Flex::SpaceBetween, -1)]
2609 #[case::spacers_3(vec![(0, 27), (37, 26), (73, 27)], vec![Length(10), Length(10)], Flex::SpaceAround, -1)]
2610 #[case::spacers_4(vec![(0, 0), (10, 0), (19, 81)], vec![Length(10), Length(10)], Flex::Start, -1)]
2611 #[case::spacers_5(vec![(0, 41), (51, 0), (60, 40)], vec![Length(10), Length(10)], Flex::Center, -1)]
2612 #[case::spacers_6(vec![(0, 81), (91, 0), (100, 0)], vec![Length(10), Length(10)], Flex::End, -1)]
2613 fn split_with_spacers_and_overlap(
2614 #[case] expected: Vec<(u16, u16)>,
2615 #[case] constraints: Vec<Constraint>,
2616 #[case] flex: Flex,
2617 #[case] spacing: i16,
2618 ) {
2619 let rect = Rect::new(0, 0, 100, 1);
2620 let (_, s) = Layout::horizontal(&constraints)
2621 .flex(flex)
2622 .spacing(spacing)
2623 .split_with_spacers(rect);
2624 assert_eq!(s.len(), constraints.len() + 1);
2625 let result = s
2626 .iter()
2627 .map(|r| (r.x, r.width))
2628 .collect::<Vec<(u16, u16)>>();
2629 assert_eq!(result, expected);
2630 }
2631
2632 #[rstest]
2633 #[case::spacers(vec![(0, 0), (0, 100), (100, 0)], vec![Length(10), Length(10)], Flex::Legacy, 200)]
2634 #[case::spacers(vec![(0, 0), (0, 100), (100, 0)], vec![Length(10), Length(10)], Flex::SpaceBetween, 200)]
2635 #[case::spacers(vec![(0, 33), (33, 34), (67, 33)], vec![Length(10), Length(10)], Flex::SpaceAround, 200)]
2636 #[case::spacers(vec![(0, 0), (0, 100), (100, 0)], vec![Length(10), Length(10)], Flex::Start, 200)]
2637 #[case::spacers(vec![(0, 0), (0, 100), (100, 0)], vec![Length(10), Length(10)], Flex::Center, 200)]
2638 #[case::spacers(vec![(0, 0), (0, 100), (100, 0)], vec![Length(10), Length(10)], Flex::End, 200)]
2639 fn split_with_spacers_and_too_much_spacing(
2640 #[case] expected: Vec<(u16, u16)>,
2641 #[case] constraints: Vec<Constraint>,
2642 #[case] flex: Flex,
2643 #[case] spacing: i16,
2644 ) {
2645 let rect = Rect::new(0, 0, 100, 1);
2646 let (_, s) = Layout::horizontal(&constraints)
2647 .flex(flex)
2648 .spacing(spacing)
2649 .split_with_spacers(rect);
2650 assert_eq!(s.len(), constraints.len() + 1);
2651 let result = s
2652 .iter()
2653 .map(|r| (r.x, r.width))
2654 .collect::<Vec<(u16, u16)>>();
2655 assert_eq!(result, expected);
2656 }
2657
2658 #[rstest]
2659 #[case::compare(vec![(0, 90), (90, 10)], vec![Min(10), Length(10)], Flex::Legacy)]
2660 #[case::compare(vec![(0, 90), (90, 10)], vec![Min(10), Length(10)], Flex::Start)]
2661 #[case::compare(vec![(0, 10), (10, 90)], vec![Min(10), Percentage(100)], Flex::Legacy)]
2662 #[case::compare(vec![(0, 10), (10, 90)], vec![Min(10), Percentage(100)], Flex::Start)]
2663 #[case::compare(vec![(0, 50), (50, 50)], vec![Percentage(50), Percentage(50)], Flex::Legacy)]
2664 #[case::compare(vec![(0, 50), (50, 50)], vec![Percentage(50), Percentage(50)], Flex::Start)]
2665 fn legacy_vs_default(
2666 #[case] expected: Vec<(u16, u16)>,
2667 #[case] constraints: Vec<Constraint>,
2668 #[case] flex: Flex,
2669 ) {
2670 let rect = Rect::new(0, 0, 100, 1);
2671 let r = Layout::horizontal(constraints).flex(flex).split(rect);
2672 let result = r
2673 .iter()
2674 .map(|r| (r.x, r.width))
2675 .collect::<Vec<(u16, u16)>>();
2676 assert_eq!(result, expected);
2677 }
2678 }
2679
2680 #[test]
2681 fn test_solver() {
2682 use super::*;
2683
2684 let mut solver = Solver::new();
2685 let x = Variable::new();
2686 let y = Variable::new();
2687
2688 solver.add_constraint((x + y) | EQ(4.0) | 5.0).unwrap();
2689 solver.add_constraint(x | EQ(1.0) | 2.0).unwrap();
2690 for _ in 0..5 {
2691 solver.add_constraint(y | EQ(1.0) | 2.0).unwrap();
2692 }
2693
2694 let changes: HashMap<Variable, f64> = solver.fetch_changes().iter().copied().collect();
2695 let x = changes.get(&x).unwrap_or(&0.0).round() as u16;
2696 let y = changes.get(&y).unwrap_or(&0.0).round() as u16;
2697 assert_eq!(x, 3);
2698 assert_eq!(y, 2);
2699
2700 let mut solver = Solver::new();
2701 let x = Variable::new();
2702 let y = Variable::new();
2703
2704 solver.add_constraint((x + y) | EQ(4.0) | 5.0).unwrap();
2705 solver.add_constraint(y | EQ(1.0) | 2.0).unwrap();
2706 for _ in 0..5 {
2707 solver.add_constraint(x | EQ(1.0) | 2.0).unwrap();
2708 }
2709
2710 let changes: HashMap<Variable, f64> = solver.fetch_changes().iter().copied().collect();
2711 let x = changes.get(&x).unwrap_or(&0.0).round() as u16;
2712 let y = changes.get(&y).unwrap_or(&0.0).round() as u16;
2713 assert_eq!(x, 2);
2714 assert_eq!(y, 3);
2715 }
2716}