1use alloc::rc::Rc;
2use alloc::vec::Vec;
3use core::array::TryFromSliceError;
4use core::iter;
5#[cfg(feature = "layout-cache")]
6use core::num::NonZeroUsize;
7
8use hashbrown::HashMap;
9use itertools::Itertools;
10use kasuari::WeightedRelation::{EQ, GE, LE};
11use kasuari::{AddConstraintError, Expression, Solver, Strength, Variable};
12#[cfg(feature = "layout-cache")]
13use lru::LruCache;
14
15use self::strengths::{
16 ALL_SEGMENT_GROW, FILL_GROW, GROW, LENGTH_SIZE_EQ, MAX_SIZE_EQ, MAX_SIZE_LE, MIN_SIZE_EQ,
17 MIN_SIZE_GE, PERCENTAGE_SIZE_EQ, RATIO_SIZE_EQ, SPACE_GROW, SPACER_SIZE_EQ,
18};
19use crate::layout::{Constraint, Direction, Flex, Margin, Rect};
20
21type Rects = Rc<[Rect]>;
22type Segments = Rects;
23type Spacers = Rects;
24#[cfg(feature = "layout-cache")]
36type Cache = LruCache<(Rect, Layout), (Segments, Spacers)>;
37
38const FLOAT_PRECISION_MULTIPLIER: f64 = 100.0;
42
43#[cfg(feature = "layout-cache")]
44std::thread_local! {
45 static LAYOUT_CACHE: core::cell::RefCell<Cache> = core::cell::RefCell::new(Cache::new(
46 NonZeroUsize::new(Layout::DEFAULT_CACHE_SIZE).unwrap(),
47 ));
48}
49
50#[derive(Debug, Clone, Eq, PartialEq, Hash)]
79#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
80pub enum Spacing {
81 Space(u16),
82 Overlap(u16),
83}
84
85impl Default for Spacing {
86 fn default() -> Self {
87 Self::Space(0)
88 }
89}
90
91impl From<i32> for Spacing {
92 fn from(value: i32) -> Self {
93 Self::from(value.clamp(i32::from(i16::MIN), i32::from(i16::MAX)) as i16)
94 }
95}
96
97impl From<u16> for Spacing {
98 fn from(value: u16) -> Self {
99 Self::Space(value)
100 }
101}
102
103impl From<i16> for Spacing {
104 fn from(value: i16) -> Self {
105 if value < 0 {
106 Self::Overlap(value.unsigned_abs())
107 } else {
108 Self::Space(value.unsigned_abs())
109 }
110 }
111}
112
113#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
192#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
193pub struct Layout {
194 direction: Direction,
195 constraints: Vec<Constraint>,
196 margin: Margin,
197 flex: Flex,
198 spacing: Spacing,
199}
200
201impl Layout {
202 #[cfg(feature = "layout-cache")]
209 pub const DEFAULT_CACHE_SIZE: usize = 500;
210
211 pub fn new<I>(direction: Direction, constraints: I) -> Self
242 where
243 I: IntoIterator,
244 I::Item: Into<Constraint>,
245 {
246 Self {
247 direction,
248 constraints: constraints.into_iter().map(Into::into).collect(),
249 ..Self::default()
250 }
251 }
252
253 pub fn vertical<I>(constraints: I) -> Self
266 where
267 I: IntoIterator,
268 I::Item: Into<Constraint>,
269 {
270 Self::new(Direction::Vertical, constraints.into_iter().map(Into::into))
271 }
272
273 pub fn horizontal<I>(constraints: I) -> Self
286 where
287 I: IntoIterator,
288 I::Item: Into<Constraint>,
289 {
290 Self::new(
291 Direction::Horizontal,
292 constraints.into_iter().map(Into::into),
293 )
294 }
295
296 #[cfg(feature = "layout-cache")]
302 pub fn init_cache(cache_size: NonZeroUsize) {
303 LAYOUT_CACHE.with_borrow_mut(|cache| cache.resize(cache_size));
304 }
305
306 #[must_use = "method moves the value of self and returns the modified value"]
326 pub const fn direction(mut self, direction: Direction) -> Self {
327 self.direction = direction;
328 self
329 }
330
331 #[must_use = "method moves the value of self and returns the modified value"]
377 pub fn constraints<I>(mut self, constraints: I) -> Self
378 where
379 I: IntoIterator,
380 I::Item: Into<Constraint>,
381 {
382 self.constraints = constraints.into_iter().map(Into::into).collect();
383 self
384 }
385
386 #[must_use = "method moves the value of self and returns the modified value"]
400 pub const fn margin(mut self, margin: u16) -> Self {
401 self.margin = Margin {
402 horizontal: margin,
403 vertical: margin,
404 };
405 self
406 }
407
408 #[must_use = "method moves the value of self and returns the modified value"]
422 pub const fn horizontal_margin(mut self, horizontal: u16) -> Self {
423 self.margin.horizontal = horizontal;
424 self
425 }
426
427 #[must_use = "method moves the value of self and returns the modified value"]
441 pub const fn vertical_margin(mut self, vertical: u16) -> Self {
442 self.margin.vertical = vertical;
443 self
444 }
445
446 #[must_use = "method moves the value of self and returns the modified value"]
482 pub const fn flex(mut self, flex: Flex) -> Self {
483 self.flex = flex;
484 self
485 }
486
487 #[must_use = "method moves the value of self and returns the modified value"]
521 pub fn spacing<T>(mut self, spacing: T) -> Self
522 where
523 T: Into<Spacing>,
524 {
525 self.spacing = spacing.into();
526 self
527 }
528
529 pub fn areas<const N: usize>(&self, area: Rect) -> [Rect; N] {
554 let areas = self.split(area);
555 areas.as_ref().try_into().unwrap_or_else(|_| {
556 panic!(
557 "invalid number of rects: expected {N}, found {}",
558 areas.len()
559 )
560 })
561 }
562
563 pub fn try_areas<const N: usize>(&self, area: Rect) -> Result<[Rect; N], TryFromSliceError> {
590 self.split(area).as_ref().try_into()
591 }
592
593 pub fn spacers<const N: usize>(&self, area: Rect) -> [Rect; N] {
622 let (_, spacers) = self.split_with_spacers(area);
623 spacers
624 .as_ref()
625 .try_into()
626 .expect("invalid number of rects")
627 }
628
629 pub fn split(&self, area: Rect) -> Rects {
664 self.split_with_spacers(area).0
665 }
666
667 pub fn split_with_spacers(&self, area: Rect) -> (Segments, Spacers) {
714 let split = || self.try_split(area).expect("failed to split");
715
716 #[cfg(feature = "layout-cache")]
717 {
718 LAYOUT_CACHE.with_borrow_mut(|cache| {
719 let key = (area, self.clone());
720 cache.get_or_insert(key, split).clone()
721 })
722 }
723
724 #[cfg(not(feature = "layout-cache"))]
725 split()
726 }
727
728 fn try_split(&self, area: Rect) -> Result<(Segments, Spacers), AddConstraintError> {
729 let mut solver = Solver::new();
750
751 let inner_area = area.inner(self.margin);
752 let (area_start, area_end) = match self.direction {
753 Direction::Horizontal => (
754 f64::from(inner_area.x) * FLOAT_PRECISION_MULTIPLIER,
755 f64::from(inner_area.right()) * FLOAT_PRECISION_MULTIPLIER,
756 ),
757 Direction::Vertical => (
758 f64::from(inner_area.y) * FLOAT_PRECISION_MULTIPLIER,
759 f64::from(inner_area.bottom()) * FLOAT_PRECISION_MULTIPLIER,
760 ),
761 };
762
763 let variable_count = self.constraints.len() * 2 + 2;
783 let variables = iter::repeat_with(Variable::new)
784 .take(variable_count)
785 .collect_vec();
786 let spacers = variables
787 .iter()
788 .tuples()
789 .map(|(a, b)| Element::from((*a, *b)))
790 .collect_vec();
791 let segments = variables
792 .iter()
793 .skip(1)
794 .tuples()
795 .map(|(a, b)| Element::from((*a, *b)))
796 .collect_vec();
797
798 let flex = self.flex;
799
800 let spacing = match self.spacing {
801 Spacing::Space(x) => x as i16,
802 Spacing::Overlap(x) => -(x as i16),
803 };
804
805 let constraints = &self.constraints;
806
807 let area_size = Element::from((*variables.first().unwrap(), *variables.last().unwrap()));
808 configure_area(&mut solver, area_size, area_start, area_end)?;
809 configure_variable_in_area_constraints(&mut solver, &variables, area_size)?;
810 configure_variable_constraints(&mut solver, &variables)?;
811 configure_flex_constraints(&mut solver, area_size, &spacers, flex, spacing)?;
812 configure_constraints(&mut solver, area_size, &segments, constraints, flex)?;
813 configure_fill_constraints(&mut solver, &segments, constraints, flex)?;
814
815 if !flex.is_legacy() {
816 for (left, right) in segments.iter().tuple_windows() {
817 solver.add_constraint(left.has_size(right, ALL_SEGMENT_GROW))?;
818 }
819 }
820
821 let changes: HashMap<Variable, f64> = solver.fetch_changes().iter().copied().collect();
823 let segment_rects = changes_to_rects(&changes, &segments, inner_area, self.direction);
827 let spacer_rects = changes_to_rects(&changes, &spacers, inner_area, self.direction);
828
829 Ok((segment_rects, spacer_rects))
830 }
831}
832
833fn configure_area(
834 solver: &mut Solver,
835 area: Element,
836 area_start: f64,
837 area_end: f64,
838) -> Result<(), AddConstraintError> {
839 solver.add_constraint(area.start | EQ(Strength::REQUIRED) | area_start)?;
840 solver.add_constraint(area.end | EQ(Strength::REQUIRED) | area_end)?;
841 Ok(())
842}
843
844fn configure_variable_in_area_constraints(
845 solver: &mut Solver,
846 variables: &[Variable],
847 area: Element,
848) -> Result<(), AddConstraintError> {
849 for &variable in variables {
851 solver.add_constraint(variable | GE(Strength::REQUIRED) | area.start)?;
852 solver.add_constraint(variable | LE(Strength::REQUIRED) | area.end)?;
853 }
854
855 Ok(())
856}
857
858fn configure_variable_constraints(
859 solver: &mut Solver,
860 variables: &[Variable],
861) -> Result<(), AddConstraintError> {
862 for (&left, &right) in variables.iter().skip(1).tuples() {
872 solver.add_constraint(left | LE(Strength::REQUIRED) | right)?;
873 }
874 Ok(())
875}
876
877fn configure_constraints(
878 solver: &mut Solver,
879 area: Element,
880 segments: &[Element],
881 constraints: &[Constraint],
882 flex: Flex,
883) -> Result<(), AddConstraintError> {
884 for (&constraint, &segment) in constraints.iter().zip(segments.iter()) {
885 match constraint {
886 Constraint::Max(max) => {
887 solver.add_constraint(segment.has_max_size(max, MAX_SIZE_LE))?;
888 solver.add_constraint(segment.has_int_size(max, MAX_SIZE_EQ))?;
889 }
890 Constraint::Min(min) => {
891 solver.add_constraint(segment.has_min_size(min as i16, MIN_SIZE_GE))?;
892 if flex.is_legacy() {
893 solver.add_constraint(segment.has_int_size(min, MIN_SIZE_EQ))?;
894 } else {
895 solver.add_constraint(segment.has_size(area, FILL_GROW))?;
896 }
897 }
898 Constraint::Length(length) => {
899 solver.add_constraint(segment.has_int_size(length, LENGTH_SIZE_EQ))?;
900 }
901 Constraint::Percentage(p) => {
902 let size = area.size() * f64::from(p) / 100.00;
903 solver.add_constraint(segment.has_size(size, PERCENTAGE_SIZE_EQ))?;
904 }
905 Constraint::Ratio(num, den) => {
906 let size = area.size() * f64::from(num) / f64::from(den.max(1));
908 solver.add_constraint(segment.has_size(size, RATIO_SIZE_EQ))?;
909 }
910 Constraint::Fill(_) => {
911 solver.add_constraint(segment.has_size(area, FILL_GROW))?;
913 }
914 }
915 }
916 Ok(())
917}
918
919fn configure_flex_constraints(
920 solver: &mut Solver,
921 area: Element,
922 spacers: &[Element],
923 flex: Flex,
924 spacing: i16,
925) -> Result<(), AddConstraintError> {
926 let spacers_except_first_and_last = spacers.get(1..spacers.len() - 1).unwrap_or(&[]);
927 let spacing_f64 = f64::from(spacing) * FLOAT_PRECISION_MULTIPLIER;
928 match flex {
929 Flex::Legacy => {
930 for spacer in spacers_except_first_and_last {
931 solver.add_constraint(spacer.has_size(spacing_f64, SPACER_SIZE_EQ))?;
932 }
933 if let (Some(first), Some(last)) = (spacers.first(), spacers.last()) {
934 solver.add_constraint(first.is_empty())?;
935 solver.add_constraint(last.is_empty())?;
936 }
937 }
938
939 Flex::SpaceAround => {
944 if spacers.len() <= 2 {
945 for (left, right) in spacers.iter().tuple_combinations() {
947 solver.add_constraint(left.has_size(right, SPACER_SIZE_EQ))?;
948 }
949 for spacer in spacers {
950 solver.add_constraint(spacer.has_min_size(spacing, SPACER_SIZE_EQ))?;
951 solver.add_constraint(spacer.has_size(area, SPACE_GROW))?;
952 }
953 } else {
954 let (first, rest) = spacers.split_first().unwrap();
956 let (last, middle) = rest.split_last().unwrap();
957
958 for (left, right) in middle.iter().tuple_combinations() {
960 solver.add_constraint(left.has_size(right, SPACER_SIZE_EQ))?;
961 }
962
963 if let Some(first_middle) = middle.first() {
965 solver.add_constraint(first_middle.has_double_size(first, SPACER_SIZE_EQ))?;
966 solver.add_constraint(first_middle.has_double_size(last, SPACER_SIZE_EQ))?;
967 }
968
969 for spacer in spacers {
971 solver.add_constraint(spacer.has_min_size(spacing, SPACER_SIZE_EQ))?;
972 solver.add_constraint(spacer.has_size(area, SPACE_GROW))?;
973 }
974 }
975 }
976
977 Flex::SpaceEvenly => {
980 for (left, right) in spacers.iter().tuple_combinations() {
981 solver.add_constraint(left.has_size(right, SPACER_SIZE_EQ))?;
982 }
983 for spacer in spacers {
984 solver.add_constraint(spacer.has_min_size(spacing, SPACER_SIZE_EQ))?;
985 solver.add_constraint(spacer.has_size(area, SPACE_GROW))?;
986 }
987 }
988
989 Flex::SpaceBetween => {
993 for (left, right) in spacers_except_first_and_last.iter().tuple_combinations() {
994 solver.add_constraint(left.has_size(right.size(), SPACER_SIZE_EQ))?;
995 }
996 for spacer in spacers_except_first_and_last {
997 solver.add_constraint(spacer.has_min_size(spacing, SPACER_SIZE_EQ))?;
998 solver.add_constraint(spacer.has_size(area, SPACE_GROW))?;
999 }
1000 if let (Some(first), Some(last)) = (spacers.first(), spacers.last()) {
1001 solver.add_constraint(first.is_empty())?;
1002 solver.add_constraint(last.is_empty())?;
1003 }
1004 }
1005
1006 Flex::Start => {
1007 for spacer in spacers_except_first_and_last {
1008 solver.add_constraint(spacer.has_size(spacing_f64, SPACER_SIZE_EQ))?;
1009 }
1010 if let (Some(first), Some(last)) = (spacers.first(), spacers.last()) {
1011 solver.add_constraint(first.is_empty())?;
1012 solver.add_constraint(last.has_size(area, GROW))?;
1013 }
1014 }
1015 Flex::Center => {
1016 for spacer in spacers_except_first_and_last {
1017 solver.add_constraint(spacer.has_size(spacing_f64, SPACER_SIZE_EQ))?;
1018 }
1019 if let (Some(first), Some(last)) = (spacers.first(), spacers.last()) {
1020 solver.add_constraint(first.has_size(area, GROW))?;
1021 solver.add_constraint(last.has_size(area, GROW))?;
1022 solver.add_constraint(first.has_size(last, SPACER_SIZE_EQ))?;
1023 }
1024 }
1025 Flex::End => {
1026 for spacer in spacers_except_first_and_last {
1027 solver.add_constraint(spacer.has_size(spacing_f64, SPACER_SIZE_EQ))?;
1028 }
1029 if let (Some(first), Some(last)) = (spacers.first(), spacers.last()) {
1030 solver.add_constraint(last.is_empty())?;
1031 solver.add_constraint(first.has_size(area, GROW))?;
1032 }
1033 }
1034 }
1035 Ok(())
1036}
1037
1038fn configure_fill_constraints(
1053 solver: &mut Solver,
1054 segments: &[Element],
1055 constraints: &[Constraint],
1056 flex: Flex,
1057) -> Result<(), AddConstraintError> {
1058 for ((&left_constraint, &left_segment), (&right_constraint, &right_segment)) in constraints
1059 .iter()
1060 .zip(segments.iter())
1061 .filter(|(c, _)| c.is_fill() || (!flex.is_legacy() && c.is_min()))
1062 .tuple_combinations()
1063 {
1064 let left_scaling_factor = match left_constraint {
1065 Constraint::Fill(scale) => f64::from(scale).max(1e-6),
1066 Constraint::Min(_) => 1.0,
1067 _ => unreachable!(),
1068 };
1069 let right_scaling_factor = match right_constraint {
1070 Constraint::Fill(scale) => f64::from(scale).max(1e-6),
1071 Constraint::Min(_) => 1.0,
1072 _ => unreachable!(),
1073 };
1074 solver.add_constraint(
1075 (right_scaling_factor * left_segment.size())
1076 | EQ(GROW)
1077 | (left_scaling_factor * right_segment.size()),
1078 )?;
1079 }
1080 Ok(())
1081}
1082
1083#[cfg(feature = "std")]
1085#[inline]
1086fn round(value: f64) -> f64 {
1087 value.round()
1088}
1089
1090#[cfg(not(feature = "std"))]
1092#[inline]
1093fn round(value: f64) -> f64 {
1094 (value + 0.5f64.copysign(value)) as i64 as f64
1095}
1096
1097fn changes_to_rects(
1098 changes: &HashMap<Variable, f64>,
1099 elements: &[Element],
1100 area: Rect,
1101 direction: Direction,
1102) -> Rects {
1103 elements
1105 .iter()
1106 .map(|element| {
1107 let start = changes.get(&element.start).unwrap_or(&0.0);
1108 let end = changes.get(&element.end).unwrap_or(&0.0);
1109 let start = round(round(*start) / FLOAT_PRECISION_MULTIPLIER) as u16;
1110 let end = round(round(*end) / FLOAT_PRECISION_MULTIPLIER) as u16;
1111 let size = end.saturating_sub(start);
1112 match direction {
1113 Direction::Horizontal => Rect {
1114 x: start,
1115 y: area.y,
1116 width: size,
1117 height: area.height,
1118 },
1119 Direction::Vertical => Rect {
1120 x: area.x,
1121 y: start,
1122 width: area.width,
1123 height: size,
1124 },
1125 }
1126 })
1127 .collect::<Rects>()
1128}
1129
1130#[expect(dead_code)]
1133#[cfg(feature = "std")]
1134fn debug_elements(elements: &[Element], changes: &HashMap<Variable, f64>) {
1135 let variables = alloc::format!(
1136 "{:?}",
1137 elements
1138 .iter()
1139 .map(|e| (
1140 changes.get(&e.start).unwrap_or(&0.0) / FLOAT_PRECISION_MULTIPLIER,
1141 changes.get(&e.end).unwrap_or(&0.0) / FLOAT_PRECISION_MULTIPLIER,
1142 ))
1143 .collect::<Vec<(f64, f64)>>()
1144 );
1145 std::dbg!(variables);
1146}
1147
1148#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
1150struct Element {
1151 start: Variable,
1152 end: Variable,
1153}
1154
1155impl From<(Variable, Variable)> for Element {
1156 fn from((start, end): (Variable, Variable)) -> Self {
1157 Self { start, end }
1158 }
1159}
1160
1161impl Element {
1162 #[expect(dead_code)]
1163 fn new() -> Self {
1164 Self {
1165 start: Variable::new(),
1166 end: Variable::new(),
1167 }
1168 }
1169
1170 fn size(&self) -> Expression {
1171 self.end - self.start
1172 }
1173
1174 fn has_max_size(&self, size: u16, strength: Strength) -> kasuari::Constraint {
1175 self.size() | LE(strength) | (f64::from(size) * FLOAT_PRECISION_MULTIPLIER)
1176 }
1177
1178 fn has_min_size(&self, size: i16, strength: Strength) -> kasuari::Constraint {
1179 self.size() | GE(strength) | (f64::from(size) * FLOAT_PRECISION_MULTIPLIER)
1180 }
1181
1182 fn has_int_size(&self, size: u16, strength: Strength) -> kasuari::Constraint {
1183 self.size() | EQ(strength) | (f64::from(size) * FLOAT_PRECISION_MULTIPLIER)
1184 }
1185
1186 fn has_size<E: Into<Expression>>(&self, size: E, strength: Strength) -> kasuari::Constraint {
1187 self.size() | EQ(strength) | size.into()
1188 }
1189
1190 fn has_double_size<E: Into<Expression>>(
1191 &self,
1192 size: E,
1193 strength: Strength,
1194 ) -> kasuari::Constraint {
1195 self.size() | EQ(strength) | (size.into() * 2.0)
1196 }
1197
1198 fn is_empty(&self) -> kasuari::Constraint {
1199 self.size() | EQ(Strength::REQUIRED - Strength::WEAK) | 0.0
1200 }
1201}
1202
1203impl From<Element> for Expression {
1205 fn from(element: Element) -> Self {
1206 element.size()
1207 }
1208}
1209
1210impl From<&Element> for Expression {
1212 fn from(element: &Element) -> Self {
1213 element.size()
1214 }
1215}
1216
1217mod strengths {
1218 use kasuari::Strength;
1219
1220 pub const SPACER_SIZE_EQ: Strength = Strength::REQUIRED.div_f64(10.0);
1226
1227 pub const MIN_SIZE_GE: Strength = Strength::STRONG.mul_f64(100.0);
1233
1234 pub const MAX_SIZE_LE: Strength = Strength::STRONG.mul_f64(100.0);
1240
1241 pub const LENGTH_SIZE_EQ: Strength = Strength::STRONG.mul_f64(10.0);
1247
1248 pub const PERCENTAGE_SIZE_EQ: Strength = Strength::STRONG;
1254
1255 pub const RATIO_SIZE_EQ: Strength = Strength::STRONG.div_f64(10.0);
1261
1262 pub const MIN_SIZE_EQ: Strength = Strength::MEDIUM.mul_f64(10.0);
1268
1269 pub const MAX_SIZE_EQ: Strength = Strength::MEDIUM.mul_f64(10.0);
1275
1276 pub const FILL_GROW: Strength = Strength::MEDIUM;
1282
1283 pub const GROW: Strength = Strength::MEDIUM.div_f64(10.0);
1289
1290 pub const SPACE_GROW: Strength = Strength::WEAK.mul_f64(10.0);
1296
1297 pub const ALL_SEGMENT_GROW: Strength = Strength::WEAK;
1303}
1304
1305#[cfg(test)]
1306mod tests {
1307 use alloc::borrow::ToOwned;
1308 use alloc::vec;
1309 use alloc::vec::Vec;
1310
1311 use super::*;
1312
1313 #[test]
1314 pub fn strength_is_valid() {
1317 use strengths::*;
1318 assert!(SPACER_SIZE_EQ > MAX_SIZE_LE);
1319 assert!(MAX_SIZE_LE > MAX_SIZE_EQ);
1320 assert!(MIN_SIZE_GE == MAX_SIZE_LE);
1321 assert!(MAX_SIZE_LE > LENGTH_SIZE_EQ);
1322 assert!(LENGTH_SIZE_EQ > PERCENTAGE_SIZE_EQ);
1323 assert!(PERCENTAGE_SIZE_EQ > RATIO_SIZE_EQ);
1324 assert!(RATIO_SIZE_EQ > MAX_SIZE_EQ);
1325 assert!(MIN_SIZE_GE > FILL_GROW);
1326 assert!(FILL_GROW > GROW);
1327 assert!(GROW > SPACE_GROW);
1328 assert!(SPACE_GROW > ALL_SEGMENT_GROW);
1329 }
1330
1331 #[test]
1332 #[cfg(feature = "layout-cache")]
1333 fn cache_size() {
1334 LAYOUT_CACHE.with_borrow(|cache| {
1335 assert_eq!(cache.cap().get(), Layout::DEFAULT_CACHE_SIZE);
1336 });
1337
1338 Layout::init_cache(NonZeroUsize::new(10).unwrap());
1339 LAYOUT_CACHE.with_borrow(|cache| {
1340 assert_eq!(cache.cap().get(), 10);
1341 });
1342 }
1343
1344 #[test]
1345 fn default() {
1346 assert_eq!(
1347 Layout::default(),
1348 Layout {
1349 direction: Direction::Vertical,
1350 margin: Margin::new(0, 0),
1351 constraints: vec![],
1352 flex: Flex::default(),
1353 spacing: Spacing::default(),
1354 }
1355 );
1356 }
1357
1358 #[test]
1359 fn new() {
1360 let fixed_size_array = [Constraint::Min(0)];
1362 let layout = Layout::new(Direction::Horizontal, fixed_size_array);
1363 assert_eq!(layout.direction, Direction::Horizontal);
1364 assert_eq!(layout.constraints, [Constraint::Min(0)]);
1365
1366 #[expect(clippy::needless_borrows_for_generic_args)] let layout = Layout::new(Direction::Horizontal, &[Constraint::Min(0)]);
1369 assert_eq!(layout.direction, Direction::Horizontal);
1370 assert_eq!(layout.constraints, [Constraint::Min(0)]);
1371
1372 let layout = Layout::new(Direction::Horizontal, vec![Constraint::Min(0)]);
1374 assert_eq!(layout.direction, Direction::Horizontal);
1375 assert_eq!(layout.constraints, [Constraint::Min(0)]);
1376
1377 #[expect(clippy::needless_borrows_for_generic_args)] let layout = Layout::new(Direction::Horizontal, &(vec![Constraint::Min(0)]));
1380 assert_eq!(layout.direction, Direction::Horizontal);
1381 assert_eq!(layout.constraints, [Constraint::Min(0)]);
1382
1383 let layout = Layout::new(Direction::Horizontal, iter::once(Constraint::Min(0)));
1385 assert_eq!(layout.direction, Direction::Horizontal);
1386 assert_eq!(layout.constraints, [Constraint::Min(0)]);
1387 }
1388
1389 #[test]
1390 fn vertical() {
1391 assert_eq!(
1392 Layout::vertical([Constraint::Min(0)]),
1393 Layout {
1394 direction: Direction::Vertical,
1395 margin: Margin::new(0, 0),
1396 constraints: vec![Constraint::Min(0)],
1397 flex: Flex::default(),
1398 spacing: Spacing::default(),
1399 }
1400 );
1401 }
1402
1403 #[test]
1404 fn horizontal() {
1405 assert_eq!(
1406 Layout::horizontal([Constraint::Min(0)]),
1407 Layout {
1408 direction: Direction::Horizontal,
1409 margin: Margin::new(0, 0),
1410 constraints: vec![Constraint::Min(0)],
1411 flex: Flex::default(),
1412 spacing: Spacing::default(),
1413 }
1414 );
1415 }
1416
1417 #[test]
1420 fn constraints() {
1421 const CONSTRAINTS: [Constraint; 2] = [Constraint::Min(0), Constraint::Max(10)];
1422 let fixed_size_array = CONSTRAINTS;
1423 assert_eq!(
1424 Layout::default().constraints(fixed_size_array).constraints,
1425 CONSTRAINTS,
1426 "constraints should be settable with an array"
1427 );
1428
1429 let slice_of_fixed_size_array = &CONSTRAINTS;
1430 assert_eq!(
1431 Layout::default()
1432 .constraints(slice_of_fixed_size_array)
1433 .constraints,
1434 CONSTRAINTS,
1435 "constraints should be settable with a slice"
1436 );
1437
1438 let vec = CONSTRAINTS.to_vec();
1439 let slice_of_vec = vec.as_slice();
1440 assert_eq!(
1441 Layout::default().constraints(slice_of_vec).constraints,
1442 CONSTRAINTS,
1443 "constraints should be settable with a slice"
1444 );
1445
1446 assert_eq!(
1447 Layout::default().constraints(vec).constraints,
1448 CONSTRAINTS,
1449 "constraints should be settable with a Vec"
1450 );
1451
1452 let iter = CONSTRAINTS.iter();
1453 assert_eq!(
1454 Layout::default().constraints(iter).constraints,
1455 CONSTRAINTS,
1456 "constraints should be settable with an iter"
1457 );
1458
1459 let iterator = CONSTRAINTS.iter().map(ToOwned::to_owned);
1460 assert_eq!(
1461 Layout::default().constraints(iterator).constraints,
1462 CONSTRAINTS,
1463 "constraints should be settable with an iterator"
1464 );
1465
1466 let iterator_ref = CONSTRAINTS.iter().map(AsRef::as_ref);
1467 assert_eq!(
1468 Layout::default().constraints(iterator_ref).constraints,
1469 CONSTRAINTS,
1470 "constraints should be settable with an iterator of refs"
1471 );
1472 }
1473
1474 #[test]
1475 fn direction() {
1476 assert_eq!(
1477 Layout::default().direction(Direction::Horizontal).direction,
1478 Direction::Horizontal
1479 );
1480 assert_eq!(
1481 Layout::default().direction(Direction::Vertical).direction,
1482 Direction::Vertical
1483 );
1484 }
1485
1486 #[test]
1487 fn margins() {
1488 assert_eq!(Layout::default().margin(10).margin, Margin::new(10, 10));
1489 assert_eq!(
1490 Layout::default().horizontal_margin(10).margin,
1491 Margin::new(10, 0)
1492 );
1493 assert_eq!(
1494 Layout::default().vertical_margin(10).margin,
1495 Margin::new(0, 10)
1496 );
1497 assert_eq!(
1498 Layout::default()
1499 .horizontal_margin(10)
1500 .vertical_margin(20)
1501 .margin,
1502 Margin::new(10, 20)
1503 );
1504 }
1505
1506 #[test]
1507 fn flex() {
1508 assert_eq!(Layout::default().flex, Flex::Start);
1509 assert_eq!(Layout::default().flex(Flex::Center).flex, Flex::Center);
1510 }
1511
1512 #[test]
1513 fn spacing() {
1514 assert_eq!(Layout::default().spacing(10).spacing, Spacing::Space(10));
1515 assert_eq!(Layout::default().spacing(0).spacing, Spacing::Space(0));
1516 assert_eq!(Layout::default().spacing(-10).spacing, Spacing::Overlap(10));
1517 }
1518
1519 mod split {
1537 use alloc::string::ToString;
1538 use core::ops::Range;
1539
1540 use itertools::Itertools;
1541 use pretty_assertions::assert_eq;
1542 use rstest::rstest;
1543
1544 use super::*;
1545 use crate::buffer::Buffer;
1546 use crate::layout::Constraint::{self, *};
1547 use crate::layout::{Direction, Flex, Layout, Rect};
1548 use crate::text::Text;
1549 use crate::widgets::Widget;
1550
1551 #[track_caller]
1560 fn letters(flex: Flex, constraints: &[Constraint], width: u16, expected: &str) {
1561 let area = Rect::new(0, 0, width, 1);
1562 let layout = Layout::default()
1563 .direction(Direction::Horizontal)
1564 .constraints(constraints)
1565 .flex(flex)
1566 .split(area);
1567 let mut buffer = Buffer::empty(area);
1568 for (c, &area) in ('a'..='z').take(constraints.len()).zip(layout.iter()) {
1569 let s = c.to_string().repeat(area.width as usize);
1570 Text::from(s).render(area, &mut buffer);
1571 }
1572 assert_eq!(buffer, Buffer::with_lines([expected]));
1573 }
1574
1575 #[rstest]
1576 #[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(
1611 #[case] flex: Flex,
1612 #[case] width: u16,
1613 #[case] constraints: &[Constraint],
1614 #[case] expected: &str,
1615 ) {
1616 letters(flex, constraints, width, expected);
1617 }
1618
1619 #[rstest]
1620 #[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")]
1653 fn max(
1654 #[case] flex: Flex,
1655 #[case] width: u16,
1656 #[case] constraints: &[Constraint],
1657 #[case] expected: &str,
1658 ) {
1659 letters(flex, constraints, width, expected);
1660 }
1661
1662 #[rstest]
1663 #[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")]
1689 fn min(
1690 #[case] flex: Flex,
1691 #[case] width: u16,
1692 #[case] constraints: &[Constraint],
1693 #[case] expected: &str,
1694 ) {
1695 letters(flex, constraints, width, expected);
1696 }
1697
1698 #[rstest] #[case(Flex::Legacy, 1, &[Percentage(0)], "a")]
1701 #[case(Flex::Legacy, 1, &[Percentage(25)], "a")]
1702 #[case(Flex::Legacy, 1, &[Percentage(50)], "a")]
1703 #[case(Flex::Legacy, 1, &[Percentage(90)], "a")]
1704 #[case(Flex::Legacy, 1, &[Percentage(100)], "a")]
1705 #[case(Flex::Legacy, 1, &[Percentage(200)], "a")]
1706 #[case(Flex::Legacy, 2, &[Percentage(0)], "aa")]
1708 #[case(Flex::Legacy, 2, &[Percentage(10)], "aa")]
1709 #[case(Flex::Legacy, 2, &[Percentage(25)], "aa")]
1710 #[case(Flex::Legacy, 2, &[Percentage(50)], "aa")]
1711 #[case(Flex::Legacy, 2, &[Percentage(66)], "aa")]
1712 #[case(Flex::Legacy, 2, &[Percentage(100)], "aa")]
1713 #[case(Flex::Legacy, 2, &[Percentage(200)], "aa")]
1714 #[case(Flex::Legacy, 10, &[Percentage(0)], "aaaaaaaaaa")]
1716 #[case(Flex::Legacy, 10, &[Percentage(10)], "aaaaaaaaaa")]
1717 #[case(Flex::Legacy, 10, &[Percentage(25)], "aaaaaaaaaa")]
1718 #[case(Flex::Legacy, 10, &[Percentage(50)], "aaaaaaaaaa")]
1719 #[case(Flex::Legacy, 10, &[Percentage(66)], "aaaaaaaaaa")]
1720 #[case(Flex::Legacy, 10, &[Percentage(100)], "aaaaaaaaaa")]
1721 #[case(Flex::Legacy, 10, &[Percentage(200)], "aaaaaaaaaa")]
1722 #[case(Flex::Legacy, 1, &[Percentage(0), Percentage(0)], "b")]
1724 #[case(Flex::Legacy, 1, &[Percentage(0), Percentage(10)], "b")]
1725 #[case(Flex::Legacy, 1, &[Percentage(0), Percentage(50)], "b")]
1726 #[case(Flex::Legacy, 1, &[Percentage(0), Percentage(90)], "b")]
1727 #[case(Flex::Legacy, 1, &[Percentage(0), Percentage(100)], "b")]
1728 #[case(Flex::Legacy, 1, &[Percentage(0), Percentage(200)], "b")]
1729 #[case(Flex::Legacy, 1, &[Percentage(10), Percentage(0)], "b")]
1731 #[case(Flex::Legacy, 1, &[Percentage(10), Percentage(10)], "b")]
1732 #[case(Flex::Legacy, 1, &[Percentage(10), Percentage(50)], "b")]
1733 #[case(Flex::Legacy, 1, &[Percentage(10), Percentage(90)], "b")]
1734 #[case(Flex::Legacy, 1, &[Percentage(10), Percentage(100)], "b")]
1735 #[case(Flex::Legacy, 1, &[Percentage(10), Percentage(200)], "b")]
1736 #[case(Flex::Legacy, 1, &[Percentage(50), Percentage(0)], "a")]
1738 #[case(Flex::Legacy, 1, &[Percentage(50), Percentage(50)], "a")]
1739 #[case(Flex::Legacy, 1, &[Percentage(50), Percentage(100)], "a")]
1740 #[case(Flex::Legacy, 1, &[Percentage(50), Percentage(200)], "a")]
1741 #[case(Flex::Legacy, 1, &[Percentage(90), Percentage(0)], "a")]
1743 #[case(Flex::Legacy, 1, &[Percentage(90), Percentage(50)], "a")]
1744 #[case(Flex::Legacy, 1, &[Percentage(90), Percentage(100)], "a")]
1745 #[case(Flex::Legacy, 1, &[Percentage(90), Percentage(200)], "a")]
1746 #[case(Flex::Legacy, 1, &[Percentage(100), Percentage(0)], "a")]
1748 #[case(Flex::Legacy, 1, &[Percentage(100), Percentage(50)], "a")]
1749 #[case(Flex::Legacy, 1, &[Percentage(100), Percentage(100)], "a")]
1750 #[case(Flex::Legacy, 1, &[Percentage(100), Percentage(200)], "a")]
1751 #[case(Flex::Legacy, 2, &[Percentage(0), Percentage(0)], "bb")]
1753 #[case(Flex::Legacy, 2, &[Percentage(0), Percentage(25)], "bb")]
1754 #[case(Flex::Legacy, 2, &[Percentage(0), Percentage(50)], "bb")]
1755 #[case(Flex::Legacy, 2, &[Percentage(0), Percentage(100)], "bb")]
1756 #[case(Flex::Legacy, 2, &[Percentage(0), Percentage(200)], "bb")]
1757 #[case(Flex::Legacy, 2, &[Percentage(10), Percentage(0)], "bb")]
1759 #[case(Flex::Legacy, 2, &[Percentage(10), Percentage(25)], "bb")]
1760 #[case(Flex::Legacy, 2, &[Percentage(10), Percentage(50)], "bb")]
1761 #[case(Flex::Legacy, 2, &[Percentage(10), Percentage(100)], "bb")]
1762 #[case(Flex::Legacy, 2, &[Percentage(10), Percentage(200)], "bb")]
1763 #[case(Flex::Legacy, 2, &[Percentage(25), Percentage(0)], "ab")]
1765 #[case(Flex::Legacy, 2, &[Percentage(25), Percentage(25)], "ab")]
1766 #[case(Flex::Legacy, 2, &[Percentage(25), Percentage(50)], "ab")]
1767 #[case(Flex::Legacy, 2, &[Percentage(25), Percentage(100)], "ab")]
1768 #[case(Flex::Legacy, 2, &[Percentage(25), Percentage(200)], "ab")]
1769 #[case(Flex::Legacy, 2, &[Percentage(33), Percentage(0)], "ab")]
1771 #[case(Flex::Legacy, 2, &[Percentage(33), Percentage(25)], "ab")]
1772 #[case(Flex::Legacy, 2, &[Percentage(33), Percentage(50)], "ab")]
1773 #[case(Flex::Legacy, 2, &[Percentage(33), Percentage(100)], "ab")]
1774 #[case(Flex::Legacy, 2, &[Percentage(33), Percentage(200)], "ab")]
1775 #[case(Flex::Legacy, 2, &[Percentage(50), Percentage(0)], "ab")]
1777 #[case(Flex::Legacy, 2, &[Percentage(50), Percentage(50)], "ab")]
1778 #[case(Flex::Legacy, 2, &[Percentage(50), Percentage(100)], "ab")]
1779 #[case(Flex::Legacy, 2, &[Percentage(100), Percentage(0)], "aa")]
1782 #[case(Flex::Legacy, 2, &[Percentage(100), Percentage(50)], "aa")]
1783 #[case(Flex::Legacy, 2, &[Percentage(100), Percentage(100)], "aa")]
1784 #[case(Flex::Legacy, 3, &[Percentage(33), Percentage(33)], "abb")]
1786 #[case(Flex::Legacy, 3, &[Percentage(33), Percentage(66)], "abb")]
1787 #[case(Flex::Legacy, 4, &[Percentage(33), Percentage(33)], "abbb")]
1789 #[case(Flex::Legacy, 4, &[Percentage(33), Percentage(66)], "abbb")]
1790 #[case(Flex::Legacy, 10, &[Percentage(0), Percentage(0)], "bbbbbbbbbb" )]
1792 #[case(Flex::Legacy, 10, &[Percentage(0), Percentage(25)], "bbbbbbbbbb" )]
1793 #[case(Flex::Legacy, 10, &[Percentage(0), Percentage(50)], "bbbbbbbbbb" )]
1794 #[case(Flex::Legacy, 10, &[Percentage(0), Percentage(100)], "bbbbbbbbbb" )]
1795 #[case(Flex::Legacy, 10, &[Percentage(0), Percentage(200)], "bbbbbbbbbb" )]
1796 #[case(Flex::Legacy, 10, &[Percentage(10), Percentage(0)], "abbbbbbbbb" )]
1798 #[case(Flex::Legacy, 10, &[Percentage(10), Percentage(25)], "abbbbbbbbb" )]
1799 #[case(Flex::Legacy, 10, &[Percentage(10), Percentage(50)], "abbbbbbbbb" )]
1800 #[case(Flex::Legacy, 10, &[Percentage(10), Percentage(100)], "abbbbbbbbb" )]
1801 #[case(Flex::Legacy, 10, &[Percentage(10), Percentage(200)], "abbbbbbbbb" )]
1802 #[case(Flex::Legacy, 10, &[Percentage(25), Percentage(0)], "aaabbbbbbb" )]
1804 #[case(Flex::Legacy, 10, &[Percentage(25), Percentage(25)], "aaabbbbbbb" )]
1805 #[case(Flex::Legacy, 10, &[Percentage(25), Percentage(50)], "aaabbbbbbb" )]
1806 #[case(Flex::Legacy, 10, &[Percentage(25), Percentage(100)], "aaabbbbbbb" )]
1807 #[case(Flex::Legacy, 10, &[Percentage(25), Percentage(200)], "aaabbbbbbb" )]
1808 #[case(Flex::Legacy, 10, &[Percentage(33), Percentage(0)], "aaabbbbbbb" )]
1810 #[case(Flex::Legacy, 10, &[Percentage(33), Percentage(25)], "aaabbbbbbb" )]
1811 #[case(Flex::Legacy, 10, &[Percentage(33), Percentage(50)], "aaabbbbbbb" )]
1812 #[case(Flex::Legacy, 10, &[Percentage(33), Percentage(100)], "aaabbbbbbb" )]
1813 #[case(Flex::Legacy, 10, &[Percentage(33), Percentage(200)], "aaabbbbbbb" )]
1814 #[case(Flex::Legacy, 10, &[Percentage(50), Percentage(0)], "aaaaabbbbb" )]
1816 #[case(Flex::Legacy, 10, &[Percentage(50), Percentage(50)], "aaaaabbbbb" )]
1817 #[case(Flex::Legacy, 10, &[Percentage(50), Percentage(100)], "aaaaabbbbb" )]
1818 #[case(Flex::Legacy, 10, &[Percentage(100), Percentage(0)], "aaaaaaaaaa" )]
1820 #[case(Flex::Legacy, 10, &[Percentage(100), Percentage(50)], "aaaaaaaaaa" )]
1821 #[case(Flex::Legacy, 10, &[Percentage(100), Percentage(100)], "aaaaaaaaaa" )]
1822 fn percentage(
1823 #[case] flex: Flex,
1824 #[case] width: u16,
1825 #[case] constraints: &[Constraint],
1826 #[case] expected: &str,
1827 ) {
1828 letters(flex, constraints, width, expected);
1829 }
1830
1831 #[rstest]
1832 #[case(Flex::Start, 10, &[Percentage(0), Percentage(0)], " " )]
1833 #[case(Flex::Start, 10, &[Percentage(0), Percentage(25)], "bbb " )]
1834 #[case(Flex::Start, 10, &[Percentage(0), Percentage(50)], "bbbbb " )]
1835 #[case(Flex::Start, 10, &[Percentage(0), Percentage(100)], "bbbbbbbbbb" )]
1836 #[case(Flex::Start, 10, &[Percentage(0), Percentage(200)], "bbbbbbbbbb" )]
1837 #[case(Flex::Start, 10, &[Percentage(10), Percentage(0)], "a " )]
1838 #[case(Flex::Start, 10, &[Percentage(10), Percentage(25)], "abbb " )]
1839 #[case(Flex::Start, 10, &[Percentage(10), Percentage(50)], "abbbbb " )]
1840 #[case(Flex::Start, 10, &[Percentage(10), Percentage(100)], "abbbbbbbbb" )]
1841 #[case(Flex::Start, 10, &[Percentage(10), Percentage(200)], "abbbbbbbbb" )]
1842 #[case(Flex::Start, 10, &[Percentage(25), Percentage(0)], "aaa " )]
1843 #[case(Flex::Start, 10, &[Percentage(25), Percentage(25)], "aaabb " )]
1844 #[case(Flex::Start, 10, &[Percentage(25), Percentage(50)], "aaabbbbb " )]
1845 #[case(Flex::Start, 10, &[Percentage(25), Percentage(100)], "aaabbbbbbb" )]
1846 #[case(Flex::Start, 10, &[Percentage(25), Percentage(200)], "aaabbbbbbb" )]
1847 #[case(Flex::Start, 10, &[Percentage(33), Percentage(0)], "aaa " )]
1848 #[case(Flex::Start, 10, &[Percentage(33), Percentage(25)], "aaabbb " )]
1849 #[case(Flex::Start, 10, &[Percentage(33), Percentage(50)], "aaabbbbb " )]
1850 #[case(Flex::Start, 10, &[Percentage(33), Percentage(100)], "aaabbbbbbb" )]
1851 #[case(Flex::Start, 10, &[Percentage(33), Percentage(200)], "aaabbbbbbb" )]
1852 #[case(Flex::Start, 10, &[Percentage(50), Percentage(0)], "aaaaa " )]
1853 #[case(Flex::Start, 10, &[Percentage(50), Percentage(50)], "aaaaabbbbb" )]
1854 #[case(Flex::Start, 10, &[Percentage(50), Percentage(100)], "aaaaabbbbb" )]
1855 #[case(Flex::Start, 10, &[Percentage(100), Percentage(0)], "aaaaaaaaaa" )]
1856 #[case(Flex::Start, 10, &[Percentage(100), Percentage(50)], "aaaaabbbbb" )]
1857 #[case(Flex::Start, 10, &[Percentage(100), Percentage(100)], "aaaaabbbbb" )]
1858 #[case(Flex::Start, 10, &[Percentage(100), Percentage(200)], "aaaaabbbbb" )]
1859 fn percentage_start(
1860 #[case] flex: Flex,
1861 #[case] width: u16,
1862 #[case] constraints: &[Constraint],
1863 #[case] expected: &str,
1864 ) {
1865 letters(flex, constraints, width, expected);
1866 }
1867
1868 #[rstest]
1869 #[case(Flex::SpaceBetween, 10, &[Percentage(0), Percentage(0)], " " )]
1870 #[case(Flex::SpaceBetween, 10, &[Percentage(0), Percentage(25)], " bb" )]
1871 #[case(Flex::SpaceBetween, 10, &[Percentage(0), Percentage(50)], " bbbbb" )]
1872 #[case(Flex::SpaceBetween, 10, &[Percentage(0), Percentage(100)], "bbbbbbbbbb" )]
1873 #[case(Flex::SpaceBetween, 10, &[Percentage(0), Percentage(200)], "bbbbbbbbbb" )]
1874 #[case(Flex::SpaceBetween, 10, &[Percentage(10), Percentage(0)], "a " )]
1875 #[case(Flex::SpaceBetween, 10, &[Percentage(10), Percentage(25)], "a bb" )]
1876 #[case(Flex::SpaceBetween, 10, &[Percentage(10), Percentage(50)], "a bbbbb" )]
1877 #[case(Flex::SpaceBetween, 10, &[Percentage(10), Percentage(100)], "abbbbbbbbb" )]
1878 #[case(Flex::SpaceBetween, 10, &[Percentage(10), Percentage(200)], "abbbbbbbbb" )]
1879 #[case(Flex::SpaceBetween, 10, &[Percentage(25), Percentage(0)], "aaa " )]
1880 #[case(Flex::SpaceBetween, 10, &[Percentage(25), Percentage(25)], "aaa bb" )]
1881 #[case(Flex::SpaceBetween, 10, &[Percentage(25), Percentage(50)], "aaa bbbbb" )]
1882 #[case(Flex::SpaceBetween, 10, &[Percentage(25), Percentage(100)], "aaabbbbbbb" )]
1883 #[case(Flex::SpaceBetween, 10, &[Percentage(25), Percentage(200)], "aaabbbbbbb" )]
1884 #[case(Flex::SpaceBetween, 10, &[Percentage(33), Percentage(0)], "aaa " )]
1885 #[case(Flex::SpaceBetween, 10, &[Percentage(33), Percentage(25)], "aaa bb" )]
1886 #[case(Flex::SpaceBetween, 10, &[Percentage(33), Percentage(50)], "aaa bbbbb" )]
1887 #[case(Flex::SpaceBetween, 10, &[Percentage(33), Percentage(100)], "aaabbbbbbb" )]
1888 #[case(Flex::SpaceBetween, 10, &[Percentage(33), Percentage(200)], "aaabbbbbbb" )]
1889 #[case(Flex::SpaceBetween, 10, &[Percentage(50), Percentage(0)], "aaaaa " )]
1890 #[case(Flex::SpaceBetween, 10, &[Percentage(50), Percentage(50)], "aaaaabbbbb" )]
1891 #[case(Flex::SpaceBetween, 10, &[Percentage(50), Percentage(100)], "aaaaabbbbb" )]
1892 #[case(Flex::SpaceBetween, 10, &[Percentage(100), Percentage(0)], "aaaaaaaaaa" )]
1893 #[case(Flex::SpaceBetween, 10, &[Percentage(100), Percentage(50)], "aaaaabbbbb" )]
1894 #[case(Flex::SpaceBetween, 10, &[Percentage(100), Percentage(100)], "aaaaabbbbb" )]
1895 #[case(Flex::SpaceBetween, 10, &[Percentage(100), Percentage(200)], "aaaaabbbbb" )]
1896 fn percentage_spacebetween(
1897 #[case] flex: Flex,
1898 #[case] width: u16,
1899 #[case] constraints: &[Constraint],
1900 #[case] expected: &str,
1901 ) {
1902 letters(flex, constraints, width, expected);
1903 }
1904
1905 #[rstest]
1906 #[case(Flex::Legacy, 1, &[Ratio(0, 1)], "a")]
1909 #[case(Flex::Legacy, 1, &[Ratio(1, 4)], "a")]
1910 #[case(Flex::Legacy, 1, &[Ratio(1, 2)], "a")]
1911 #[case(Flex::Legacy, 1, &[Ratio(9, 10)], "a")]
1912 #[case(Flex::Legacy, 1, &[Ratio(1, 1)], "a")]
1913 #[case(Flex::Legacy, 1, &[Ratio(2, 1)], "a")]
1914 #[case(Flex::Legacy, 2, &[Ratio(0, 1)], "aa")]
1915 #[case(Flex::Legacy, 2, &[Ratio(1, 10)], "aa")]
1916 #[case(Flex::Legacy, 2, &[Ratio(1, 4)], "aa")]
1917 #[case(Flex::Legacy, 2, &[Ratio(1, 2)], "aa")]
1918 #[case(Flex::Legacy, 2, &[Ratio(2, 3)], "aa")]
1919 #[case(Flex::Legacy, 2, &[Ratio(1, 1)], "aa")]
1920 #[case(Flex::Legacy, 2, &[Ratio(2, 1)], "aa")]
1921 #[case(Flex::Legacy, 1, &[Ratio(0, 1), Ratio(0, 1)], "b")]
1922 #[case(Flex::Legacy, 1, &[Ratio(0, 1), Ratio(1, 10)], "b")]
1923 #[case(Flex::Legacy, 1, &[Ratio(0, 1), Ratio(1, 2)], "b")]
1924 #[case(Flex::Legacy, 1, &[Ratio(0, 1), Ratio(9, 10)], "b")]
1925 #[case(Flex::Legacy, 1, &[Ratio(0, 1), Ratio(1, 1)], "b")]
1926 #[case(Flex::Legacy, 1, &[Ratio(0, 1), Ratio(2, 1)], "b")]
1927 #[case(Flex::Legacy, 1, &[Ratio(1, 10), Ratio(0, 1)], "b")]
1928 #[case(Flex::Legacy, 1, &[Ratio(1, 10), Ratio(1, 10)], "b")]
1929 #[case(Flex::Legacy, 1, &[Ratio(1, 10), Ratio(1, 2)], "b")]
1930 #[case(Flex::Legacy, 1, &[Ratio(1, 10), Ratio(9, 10)], "b")]
1931 #[case(Flex::Legacy, 1, &[Ratio(1, 10), Ratio(1, 1)], "b")]
1932 #[case(Flex::Legacy, 1, &[Ratio(1, 10), Ratio(2, 1)], "b")]
1933 #[case(Flex::Legacy, 1, &[Ratio(1, 2), Ratio(0, 1)], "a")]
1934 #[case(Flex::Legacy, 1, &[Ratio(1, 2), Ratio(1, 2)], "a")]
1935 #[case(Flex::Legacy, 1, &[Ratio(1, 2), Ratio(1, 1)], "a")]
1936 #[case(Flex::Legacy, 1, &[Ratio(1, 2), Ratio(2, 1)], "a")]
1937 #[case(Flex::Legacy, 1, &[Ratio(9, 10), Ratio(0, 1)], "a")]
1938 #[case(Flex::Legacy, 1, &[Ratio(9, 10), Ratio(1, 2)], "a")]
1939 #[case(Flex::Legacy, 1, &[Ratio(9, 10), Ratio(1, 1)], "a")]
1940 #[case(Flex::Legacy, 1, &[Ratio(9, 10), Ratio(2, 1)], "a")]
1941 #[case(Flex::Legacy, 1, &[Ratio(1, 1), Ratio(0, 1)], "a")]
1942 #[case(Flex::Legacy, 1, &[Ratio(1, 1), Ratio(1, 2)], "a")]
1943 #[case(Flex::Legacy, 1, &[Ratio(1, 1), Ratio(1, 1)], "a")]
1944 #[case(Flex::Legacy, 1, &[Ratio(1, 1), Ratio(2, 1)], "a")]
1945 #[case(Flex::Legacy, 2, &[Ratio(0, 1), Ratio(0, 1)], "bb")]
1946 #[case(Flex::Legacy, 2, &[Ratio(0, 1), Ratio(1, 4)], "bb")]
1947 #[case(Flex::Legacy, 2, &[Ratio(0, 1), Ratio(1, 2)], "bb")]
1948 #[case(Flex::Legacy, 2, &[Ratio(0, 1), Ratio(1, 1)], "bb")]
1949 #[case(Flex::Legacy, 2, &[Ratio(0, 1), Ratio(2, 1)], "bb")]
1950 #[case(Flex::Legacy, 2, &[Ratio(1, 10), Ratio(0, 1)], "bb")]
1951 #[case(Flex::Legacy, 2, &[Ratio(1, 10), Ratio(1, 4)], "bb")]
1952 #[case(Flex::Legacy, 2, &[Ratio(1, 10), Ratio(1, 2)], "bb")]
1953 #[case(Flex::Legacy, 2, &[Ratio(1, 10), Ratio(1, 1)], "bb")]
1954 #[case(Flex::Legacy, 2, &[Ratio(1, 10), Ratio(2, 1)], "bb")]
1955 #[case(Flex::Legacy, 2, &[Ratio(1, 4), Ratio(0, 1)], "ab")]
1956 #[case(Flex::Legacy, 2, &[Ratio(1, 4), Ratio(1, 4)], "ab")]
1957 #[case(Flex::Legacy, 2, &[Ratio(1, 4), Ratio(1, 2)], "ab")]
1958 #[case(Flex::Legacy, 2, &[Ratio(1, 4), Ratio(1, 1)], "ab")]
1959 #[case(Flex::Legacy, 2, &[Ratio(1, 4), Ratio(2, 1)], "ab")]
1960 #[case(Flex::Legacy, 2, &[Ratio(1, 3), Ratio(0, 1)], "ab")]
1961 #[case(Flex::Legacy, 2, &[Ratio(1, 3), Ratio(1, 4)], "ab")]
1962 #[case(Flex::Legacy, 2, &[Ratio(1, 3), Ratio(1, 2)], "ab")]
1963 #[case(Flex::Legacy, 2, &[Ratio(1, 3), Ratio(1, 1)], "ab")]
1964 #[case(Flex::Legacy, 2, &[Ratio(1, 3), Ratio(2, 1)], "ab")]
1965 #[case(Flex::Legacy, 2, &[Ratio(1, 2), Ratio(0, 1)], "ab")]
1966 #[case(Flex::Legacy, 2, &[Ratio(1, 2), Ratio(1, 2)], "ab")]
1967 #[case(Flex::Legacy, 2, &[Ratio(1, 2), Ratio(1, 1)], "ab")]
1968 #[case(Flex::Legacy, 2, &[Ratio(1, 1), Ratio(0, 1)], "aa")]
1969 #[case(Flex::Legacy, 2, &[Ratio(1, 1), Ratio(1, 2)], "aa")]
1970 #[case(Flex::Legacy, 2, &[Ratio(1, 1), Ratio(1, 1)], "aa")]
1971 #[case(Flex::Legacy, 3, &[Ratio(1, 3), Ratio(1, 3)], "abb")]
1972 #[case(Flex::Legacy, 3, &[Ratio(1, 3), Ratio(2,3)], "abb")]
1973 #[case(Flex::Legacy, 10, &[Ratio(0, 1), Ratio(0, 1)], "bbbbbbbbbb" )]
1974 #[case(Flex::Legacy, 10, &[Ratio(0, 1), Ratio(1, 4)], "bbbbbbbbbb" )]
1975 #[case(Flex::Legacy, 10, &[Ratio(0, 1), Ratio(1, 2)], "bbbbbbbbbb" )]
1976 #[case(Flex::Legacy, 10, &[Ratio(0, 1), Ratio(1, 1)], "bbbbbbbbbb" )]
1977 #[case(Flex::Legacy, 10, &[Ratio(0, 1), Ratio(2, 1)], "bbbbbbbbbb" )]
1978 #[case(Flex::Legacy, 10, &[Ratio(1, 10), Ratio(0, 1)], "abbbbbbbbb" )]
1979 #[case(Flex::Legacy, 10, &[Ratio(1, 10), Ratio(1, 4)], "abbbbbbbbb" )]
1980 #[case(Flex::Legacy, 10, &[Ratio(1, 10), Ratio(1, 2)], "abbbbbbbbb" )]
1981 #[case(Flex::Legacy, 10, &[Ratio(1, 10), Ratio(1, 1)], "abbbbbbbbb" )]
1982 #[case(Flex::Legacy, 10, &[Ratio(1, 10), Ratio(2, 1)], "abbbbbbbbb" )]
1983 #[case(Flex::Legacy, 10, &[Ratio(1, 4), Ratio(0, 1)], "aaabbbbbbb" )]
1984 #[case(Flex::Legacy, 10, &[Ratio(1, 4), Ratio(1, 4)], "aaabbbbbbb" )]
1985 #[case(Flex::Legacy, 10, &[Ratio(1, 4), Ratio(1, 2)], "aaabbbbbbb" )]
1986 #[case(Flex::Legacy, 10, &[Ratio(1, 4), Ratio(1, 1)], "aaabbbbbbb" )]
1987 #[case(Flex::Legacy, 10, &[Ratio(1, 4), Ratio(2, 1)], "aaabbbbbbb" )]
1988 #[case(Flex::Legacy, 10, &[Ratio(1, 3), Ratio(0, 1)], "aaabbbbbbb" )]
1989 #[case(Flex::Legacy, 10, &[Ratio(1, 3), Ratio(1, 4)], "aaabbbbbbb" )]
1990 #[case(Flex::Legacy, 10, &[Ratio(1, 3), Ratio(1, 2)], "aaabbbbbbb" )]
1991 #[case(Flex::Legacy, 10, &[Ratio(1, 3), Ratio(1, 1)], "aaabbbbbbb" )]
1992 #[case(Flex::Legacy, 10, &[Ratio(1, 3), Ratio(2, 1)], "aaabbbbbbb" )]
1993 #[case(Flex::Legacy, 10, &[Ratio(1, 2), Ratio(0, 1)], "aaaaabbbbb" )]
1994 #[case(Flex::Legacy, 10, &[Ratio(1, 2), Ratio(1, 2)], "aaaaabbbbb" )]
1995 #[case(Flex::Legacy, 10, &[Ratio(1, 2), Ratio(1, 1)], "aaaaabbbbb" )]
1996 #[case(Flex::Legacy, 10, &[Ratio(1, 1), Ratio(0, 1)], "aaaaaaaaaa" )]
1997 #[case(Flex::Legacy, 10, &[Ratio(1, 1), Ratio(1, 2)], "aaaaaaaaaa" )]
1998 #[case(Flex::Legacy, 10, &[Ratio(1, 1), Ratio(1, 1)], "aaaaaaaaaa" )]
1999 fn ratio(
2000 #[case] flex: Flex,
2001 #[case] width: u16,
2002 #[case] constraints: &[Constraint],
2003 #[case] expected: &str,
2004 ) {
2005 letters(flex, constraints, width, expected);
2006 }
2007
2008 #[rstest]
2009 #[case(Flex::Start, 10, &[Ratio(0, 1), Ratio(0, 1)], " " )]
2010 #[case(Flex::Start, 10, &[Ratio(0, 1), Ratio(1, 4)], "bbb " )]
2011 #[case(Flex::Start, 10, &[Ratio(0, 1), Ratio(1, 2)], "bbbbb " )]
2012 #[case(Flex::Start, 10, &[Ratio(0, 1), Ratio(1, 1)], "bbbbbbbbbb" )]
2013 #[case(Flex::Start, 10, &[Ratio(0, 1), Ratio(2, 1)], "bbbbbbbbbb" )]
2014 #[case(Flex::Start, 10, &[Ratio(1, 10), Ratio(0, 1)], "a " )]
2015 #[case(Flex::Start, 10, &[Ratio(1, 10), Ratio(1, 4)], "abbb " )]
2016 #[case(Flex::Start, 10, &[Ratio(1, 10), Ratio(1, 2)], "abbbbb " )]
2017 #[case(Flex::Start, 10, &[Ratio(1, 10), Ratio(1, 1)], "abbbbbbbbb" )]
2018 #[case(Flex::Start, 10, &[Ratio(1, 10), Ratio(2, 1)], "abbbbbbbbb" )]
2019 #[case(Flex::Start, 10, &[Ratio(1, 4), Ratio(0, 1)], "aaa " )]
2020 #[case(Flex::Start, 10, &[Ratio(1, 4), Ratio(1, 4)], "aaabb " )]
2021 #[case(Flex::Start, 10, &[Ratio(1, 4), Ratio(1, 2)], "aaabbbbb " )]
2022 #[case(Flex::Start, 10, &[Ratio(1, 4), Ratio(1, 1)], "aaabbbbbbb" )]
2023 #[case(Flex::Start, 10, &[Ratio(1, 4), Ratio(2, 1)], "aaabbbbbbb" )]
2024 #[case(Flex::Start, 10, &[Ratio(1, 3), Ratio(0, 1)], "aaa " )]
2025 #[case(Flex::Start, 10, &[Ratio(1, 3), Ratio(1, 4)], "aaabbb " )]
2026 #[case(Flex::Start, 10, &[Ratio(1, 3), Ratio(1, 2)], "aaabbbbb " )]
2027 #[case(Flex::Start, 10, &[Ratio(1, 3), Ratio(1, 1)], "aaabbbbbbb" )]
2028 #[case(Flex::Start, 10, &[Ratio(1, 3), Ratio(2, 1)], "aaabbbbbbb" )]
2029 #[case(Flex::Start, 10, &[Ratio(1, 2), Ratio(0, 1)], "aaaaa " )]
2030 #[case(Flex::Start, 10, &[Ratio(1, 2), Ratio(1, 2)], "aaaaabbbbb" )]
2031 #[case(Flex::Start, 10, &[Ratio(1, 2), Ratio(1, 1)], "aaaaabbbbb" )]
2032 #[case(Flex::Start, 10, &[Ratio(1, 1), Ratio(0, 1)], "aaaaaaaaaa" )]
2033 #[case(Flex::Start, 10, &[Ratio(1, 1), Ratio(1, 2)], "aaaaabbbbb" )]
2034 #[case(Flex::Start, 10, &[Ratio(1, 1), Ratio(1, 1)], "aaaaabbbbb" )]
2035 #[case(Flex::Start, 10, &[Ratio(1, 1), Ratio(2, 1)], "aaaaabbbbb" )]
2036 fn ratio_start(
2037 #[case] flex: Flex,
2038 #[case] width: u16,
2039 #[case] constraints: &[Constraint],
2040 #[case] expected: &str,
2041 ) {
2042 letters(flex, constraints, width, expected);
2043 }
2044
2045 #[rstest]
2046 #[case(Flex::SpaceBetween, 10, &[Ratio(0, 1), Ratio(0, 1)], " " )]
2047 #[case(Flex::SpaceBetween, 10, &[Ratio(0, 1), Ratio(1, 4)], " bb" )]
2048 #[case(Flex::SpaceBetween, 10, &[Ratio(0, 1), Ratio(1, 2)], " bbbbb" )]
2049 #[case(Flex::SpaceBetween, 10, &[Ratio(0, 1), Ratio(1, 1)], "bbbbbbbbbb" )]
2050 #[case(Flex::SpaceBetween, 10, &[Ratio(0, 1), Ratio(2, 1)], "bbbbbbbbbb" )]
2051 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 10), Ratio(0, 1)], "a " )]
2052 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 10), Ratio(1, 4)], "a bb" )]
2053 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 10), Ratio(1, 2)], "a bbbbb" )]
2054 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 10), Ratio(1, 1)], "abbbbbbbbb" )]
2055 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 10), Ratio(2, 1)], "abbbbbbbbb" )]
2056 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 4), Ratio(0, 1)], "aaa " )]
2057 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 4), Ratio(1, 4)], "aaa bb" )]
2058 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 4), Ratio(1, 2)], "aaa bbbbb" )]
2059 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 4), Ratio(1, 1)], "aaabbbbbbb" )]
2060 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 4), Ratio(2, 1)], "aaabbbbbbb" )]
2061 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 3), Ratio(0, 1)], "aaa " )]
2062 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 3), Ratio(1, 4)], "aaa bb" )]
2063 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 3), Ratio(1, 2)], "aaa bbbbb" )]
2064 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 3), Ratio(1, 1)], "aaabbbbbbb" )]
2065 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 3), Ratio(2, 1)], "aaabbbbbbb" )]
2066 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 2), Ratio(0, 1)], "aaaaa " )]
2067 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 2), Ratio(1, 2)], "aaaaabbbbb" )]
2068 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 2), Ratio(1, 1)], "aaaaabbbbb" )]
2069 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 1), Ratio(0, 1)], "aaaaaaaaaa" )]
2070 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 1), Ratio(1, 2)], "aaaaabbbbb" )]
2071 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 1), Ratio(1, 1)], "aaaaabbbbb" )]
2072 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 1), Ratio(2, 1)], "aaaaabbbbb" )]
2073 fn ratio_spacebetween(
2074 #[case] flex: Flex,
2075 #[case] width: u16,
2076 #[case] constraints: &[Constraint],
2077 #[case] expected: &str,
2078 ) {
2079 letters(flex, constraints, width, expected);
2080 }
2081
2082 #[test]
2083 fn vertical_split_by_height() {
2084 let target = Rect {
2085 x: 2,
2086 y: 2,
2087 width: 10,
2088 height: 10,
2089 };
2090
2091 let chunks = Layout::default()
2092 .direction(Direction::Vertical)
2093 .constraints([
2094 Constraint::Percentage(10),
2095 Constraint::Max(5),
2096 Constraint::Min(1),
2097 ])
2098 .split(target);
2099
2100 assert_eq!(chunks.iter().map(|r| r.height).sum::<u16>(), target.height);
2101 chunks.windows(2).for_each(|w| assert!(w[0].y <= w[1].y));
2102 }
2103
2104 #[test]
2105 fn edge_cases() {
2106 let layout = Layout::default()
2108 .constraints([
2109 Constraint::Percentage(50),
2110 Constraint::Percentage(50),
2111 Constraint::Min(0),
2112 ])
2113 .split(Rect::new(0, 0, 1, 1));
2114 assert_eq!(
2115 layout[..],
2116 [
2117 Rect::new(0, 0, 1, 1),
2118 Rect::new(0, 1, 1, 0),
2119 Rect::new(0, 1, 1, 0)
2120 ]
2121 );
2122
2123 let layout = Layout::default()
2125 .constraints([
2126 Constraint::Max(1),
2127 Constraint::Percentage(99),
2128 Constraint::Min(0),
2129 ])
2130 .split(Rect::new(0, 0, 1, 1));
2131 assert_eq!(
2132 layout[..],
2133 [
2134 Rect::new(0, 0, 1, 0),
2135 Rect::new(0, 0, 1, 1),
2136 Rect::new(0, 1, 1, 0)
2137 ]
2138 );
2139
2140 let layout = Layout::default()
2144 .constraints([Min(1), Length(0), Min(1)])
2145 .direction(Direction::Horizontal)
2146 .split(Rect::new(0, 0, 1, 1));
2147 assert_eq!(
2148 layout[..],
2149 [
2150 Rect::new(0, 0, 1, 1),
2151 Rect::new(1, 0, 0, 1),
2152 Rect::new(1, 0, 0, 1),
2153 ]
2154 );
2155
2156 let layout = Layout::default()
2158 .constraints([Length(3), Min(4), Length(1), Min(4)])
2159 .direction(Direction::Horizontal)
2160 .split(Rect::new(0, 0, 7, 1));
2161 assert_eq!(
2162 layout[..],
2163 [
2164 Rect::new(0, 0, 0, 1),
2165 Rect::new(0, 0, 4, 1),
2166 Rect::new(4, 0, 0, 1),
2167 Rect::new(4, 0, 3, 1),
2168 ]
2169 );
2170 }
2171
2172 #[rstest]
2173 #[case::len_min1(vec![Length(25), Min(100)], vec![0..0, 0..100])]
2174 #[case::len_min2(vec![Length(25), Min(0)], vec![0..25, 25..100])]
2175 #[case::len_max1(vec![Length(25), Max(0)], vec![0..100, 100..100])]
2176 #[case::len_max2(vec![Length(25), Max(100)], vec![0..25, 25..100])]
2177 #[case::len_perc(vec![Length(25), Percentage(25)], vec![0..25, 25..100])]
2178 #[case::perc_len(vec![Percentage(25), Length(25)], vec![0..75, 75..100])]
2179 #[case::len_ratio(vec![Length(25), Ratio(1, 4)], vec![0..25, 25..100])]
2180 #[case::ratio_len(vec![Ratio(1, 4), Length(25)], vec![0..75, 75..100])]
2181 #[case::len_len(vec![Length(25), Length(25)], vec![0..25, 25..100])]
2182 #[case::len1(vec![Length(25), Length(25), Length(25)], vec![0..25, 25..50, 50..100])]
2183 #[case::len2(vec![Length(15), Length(35), Length(25)], vec![0..15, 15..50, 50..100])]
2184 #[case::len3(vec![Length(25), Length(25), Length(25)], vec![0..25, 25..50, 50..100])]
2185 fn constraint_length(
2186 #[case] constraints: Vec<Constraint>,
2187 #[case] expected: Vec<Range<u16>>,
2188 ) {
2189 let rect = Rect::new(0, 0, 100, 1);
2190 let ranges = Layout::horizontal(constraints)
2191 .flex(Flex::Legacy)
2192 .split(rect)
2193 .iter()
2194 .map(|r| r.left()..r.right())
2195 .collect_vec();
2196 assert_eq!(ranges, expected);
2197 }
2198
2199 #[rstest]
2200 #[case(7, vec![Length(4), Length(4)], vec![0..3, 4..7])]
2201 #[case(4, vec![Length(4), Length(4)], vec![0..2, 3..4])]
2202 fn table_length(
2203 #[case] width: u16,
2204 #[case] constraints: Vec<Constraint>,
2205 #[case] expected: Vec<Range<u16>>,
2206 ) {
2207 let rect = Rect::new(0, 0, width, 1);
2208 let ranges = Layout::horizontal(constraints)
2209 .spacing(1)
2210 .flex(Flex::Start)
2211 .split(rect)
2212 .iter()
2213 .map(|r| r.left()..r.right())
2214 .collect::<Vec<Range<u16>>>();
2215 assert_eq!(ranges, expected);
2216 }
2217
2218 #[rstest]
2219 #[case::min_len_max(vec![Min(25), Length(25), Max(25)], vec![0..50, 50..75, 75..100])]
2220 #[case::max_len_min(vec![Max(25), Length(25), Min(25)], vec![0..25, 25..50, 50..100])]
2221 #[case::len_len_len(vec![Length(33), Length(33), Length(33)], vec![0..33, 33..66, 66..100])]
2222 #[case::len_len_len_25(vec![Length(25), Length(25), Length(25)], vec![0..25, 25..50, 50..100])]
2223 #[case::perc_len_ratio(vec![Percentage(25), Length(25), Ratio(1, 4)], vec![0..25, 25..50, 50..100])]
2224 #[case::len_ratio_perc(vec![Length(25), Ratio(1, 4), Percentage(25)], vec![0..25, 25..75, 75..100])]
2225 #[case::ratio_len_perc(vec![Ratio(1, 4), Length(25), Percentage(25)], vec![0..50, 50..75, 75..100])]
2226 #[case::ratio_perc_len(vec![Ratio(1, 4), Percentage(25), Length(25)], vec![0..50, 50..75, 75..100])]
2227 #[case::len_len_min(vec![Length(100), Length(1), Min(20)], vec![0..80, 80..80, 80..100])]
2228 #[case::min_len_len(vec![Min(20), Length(1), Length(100)], vec![0..20, 20..21, 21..100])]
2229 #[case::fill_len_fill(vec![Fill(1), Length(10), Fill(1)], vec![0..45, 45..55, 55..100])]
2230 #[case::fill_len_fill_2(vec![Fill(1), Length(10), Fill(2)], vec![0..30, 30..40, 40..100])]
2231 #[case::fill_len_fill_4(vec![Fill(1), Length(10), Fill(4)], vec![0..18, 18..28, 28..100])]
2232 #[case::fill_len_fill_5(vec![Fill(1), Length(10), Fill(5)], vec![0..15, 15..25, 25..100])]
2233 #[case::len_len_len_25(vec![Length(25), Length(25), Length(25)], vec![0..25, 25..50, 50..100])]
2234 #[case::unstable_test(vec![Length(25), Length(25), Length(25)], vec![0..25, 25..50, 50..100])]
2235 fn length_is_higher_priority(
2236 #[case] constraints: Vec<Constraint>,
2237 #[case] expected: Vec<Range<u16>>,
2238 ) {
2239 let rect = Rect::new(0, 0, 100, 1);
2240 let ranges = Layout::horizontal(constraints)
2241 .flex(Flex::Legacy)
2242 .split(rect)
2243 .iter()
2244 .map(|r| r.left()..r.right())
2245 .collect_vec();
2246 assert_eq!(ranges, expected);
2247 }
2248
2249 #[rstest]
2250 #[case::min_len_max(vec![Min(25), Length(25), Max(25)], vec![50, 25, 25])]
2251 #[case::max_len_min(vec![Max(25), Length(25), Min(25)], vec![25, 25, 50])]
2252 #[case::len_len_len1(vec![Length(33), Length(33), Length(33)], vec![33, 33, 33])]
2253 #[case::len_len_len2(vec![Length(25), Length(25), Length(25)], vec![25, 25, 25])]
2254 #[case::perc_len_ratio(vec![Percentage(25), Length(25), Ratio(1, 4)], vec![25, 25, 25])]
2255 #[case::len_ratio_perc(vec![Length(25), Ratio(1, 4), Percentage(25)], vec![25, 25, 25])]
2256 #[case::ratio_len_perc(vec![Ratio(1, 4), Length(25), Percentage(25)], vec![25, 25, 25])]
2257 #[case::ratio_perc_len(vec![Ratio(1, 4), Percentage(25), Length(25)], vec![25, 25, 25])]
2258 #[case::len_len_min(vec![Length(100), Length(1), Min(20)], vec![79, 1, 20])]
2259 #[case::min_len_len(vec![Min(20), Length(1), Length(100)], vec![20, 1, 79])]
2260 #[case::fill_len_fill1(vec![Fill(1), Length(10), Fill(1)], vec![45, 10, 45])]
2261 #[case::fill_len_fill2(vec![Fill(1), Length(10), Fill(2)], vec![30, 10, 60])]
2262 #[case::fill_len_fill4(vec![Fill(1), Length(10), Fill(4)], vec![18, 10, 72])]
2263 #[case::fill_len_fill5(vec![Fill(1), Length(10), Fill(5)], vec![15, 10, 75])]
2264 #[case::len_len_len3(vec![Length(25), Length(25), Length(25)], vec![25, 25, 25])]
2265 fn length_is_higher_priority_in_flex(
2266 #[case] constraints: Vec<Constraint>,
2267 #[case] expected: Vec<u16>,
2268 ) {
2269 let rect = Rect::new(0, 0, 100, 1);
2270 for flex in [
2271 Flex::Start,
2272 Flex::End,
2273 Flex::Center,
2274 Flex::SpaceAround,
2275 Flex::SpaceEvenly,
2276 Flex::SpaceBetween,
2277 ] {
2278 let widths = Layout::horizontal(&constraints)
2279 .flex(flex)
2280 .split(rect)
2281 .iter()
2282 .map(|r| r.width)
2283 .collect_vec();
2284 assert_eq!(widths, expected);
2285 }
2286 }
2287
2288 #[rstest]
2289 #[case::fill_len_fill(vec![Fill(1), Length(10), Fill(2)], vec![0..13, 13..23, 23..50])]
2290 #[case::len_fill_fill(vec![Length(10), Fill(2), Fill(1)], vec![0..10, 10..37, 37..50])] fn fixed_with_50_width(
2292 #[case] constraints: Vec<Constraint>,
2293 #[case] expected: Vec<Range<u16>>,
2294 ) {
2295 let rect = Rect::new(0, 0, 50, 1);
2296 let ranges = Layout::horizontal(constraints)
2297 .flex(Flex::Legacy)
2298 .split(rect)
2299 .iter()
2300 .map(|r| r.left()..r.right())
2301 .collect_vec();
2302 assert_eq!(ranges, expected);
2303 }
2304
2305 #[rstest]
2306 #[case::same_fill(vec![Fill(1), Fill(2), Fill(1), Fill(1)], vec![0..20, 20..60, 60..80, 80..100])]
2307 #[case::inc_fill(vec![Fill(1), Fill(2), Fill(3), Fill(4)], vec![0..10, 10..30, 30..60, 60..100])]
2308 #[case::dec_fill(vec![Fill(4), Fill(3), Fill(2), Fill(1)], vec![0..40, 40..70, 70..90, 90..100])]
2309 #[case::rand_fill1(vec![Fill(1), Fill(3), Fill(2), Fill(4)], vec![0..10, 10..40, 40..60, 60..100])]
2310 #[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])]
2311 #[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])]
2312 #[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])]
2313 #[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])]
2314 #[case::zero_fill1(vec![Fill(0), Fill(1), Fill(0)], vec![0..0, 0..100, 100..100])]
2315 #[case::zero_fill2(vec![Fill(0), Length(1), Fill(0)], vec![0..50, 50..51, 51..100])]
2316 #[case::zero_fill3(vec![Fill(0), Percentage(1), Fill(0)], vec![0..50, 50..51, 51..100])]
2317 #[case::zero_fill4(vec![Fill(0), Min(1), Fill(0)], vec![0..50, 50..51, 51..100])]
2318 #[case::zero_fill5(vec![Fill(0), Max(1), Fill(0)], vec![0..50, 50..51, 51..100])]
2319 #[case::zero_fill6(vec![Fill(0), Fill(2), Fill(0), Fill(1)], vec![0..0, 0..67, 67..67, 67..100])]
2320 #[case::space_fill1(vec![Fill(0), Fill(2), Percentage(20)], vec![0..0, 0..80, 80..100])]
2321 #[case::space_fill2(vec![Fill(0), Fill(0), Percentage(20)], vec![0..40, 40..80, 80..100])]
2322 #[case::space_fill3(vec![Fill(0), Ratio(1, 5)], vec![0..80, 80..100])]
2323 #[case::space_fill4(vec![Fill(0), Fill(u16::MAX)], vec![0..0, 0..100])]
2324 #[case::space_fill5(vec![Fill(u16::MAX), Fill(0)], vec![0..100, 100..100])]
2325 #[case::space_fill6(vec![Fill(0), Percentage(20)], vec![0..80, 80..100])]
2326 #[case::space_fill7(vec![Fill(1), Percentage(20)], vec![0..80, 80..100])]
2327 #[case::space_fill8(vec![Fill(u16::MAX), Percentage(20)], vec![0..80, 80..100])]
2328 #[case::space_fill9(vec![Fill(u16::MAX), Fill(0), Percentage(20)], vec![0..80, 80..80, 80..100])]
2329 #[case::space_fill10(vec![Fill(0), Length(20)], vec![0..80, 80..100])]
2330 #[case::space_fill11(vec![Fill(0), Min(20)], vec![0..80, 80..100])]
2331 #[case::space_fill12(vec![Fill(0), Max(20)], vec![0..80, 80..100])]
2332 #[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])]
2333 #[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])]
2334 #[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])]
2335 #[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])]
2336 #[case::fill_collapse5(vec![Fill(1), Fill(1), Fill(1), Ratio(1, 1)], vec![0..0, 0..0, 0..0, 0..100])]
2337 #[case::fill_collapse6(vec![Fill(1), Fill(1), Fill(1), Percentage(100)], vec![0..0, 0..0, 0..0, 0..100])]
2338 fn fill(#[case] constraints: Vec<Constraint>, #[case] expected: Vec<Range<u16>>) {
2339 let rect = Rect::new(0, 0, 100, 1);
2340 let ranges = Layout::horizontal(constraints)
2341 .flex(Flex::Legacy)
2342 .split(rect)
2343 .iter()
2344 .map(|r| r.left()..r.right())
2345 .collect_vec();
2346 assert_eq!(ranges, expected);
2347 }
2348
2349 #[rstest]
2350 #[case::min_percentage(vec![Min(0), Percentage(20)], vec![0..80, 80..100])]
2351 #[case::max_percentage(vec![Max(0), Percentage(20)], vec![0..0, 0..100])]
2352 fn percentage_parameterized(
2353 #[case] constraints: Vec<Constraint>,
2354 #[case] expected: Vec<Range<u16>>,
2355 ) {
2356 let rect = Rect::new(0, 0, 100, 1);
2357 let ranges = Layout::horizontal(constraints)
2358 .flex(Flex::Legacy)
2359 .split(rect)
2360 .iter()
2361 .map(|r| r.left()..r.right())
2362 .collect_vec();
2363 assert_eq!(ranges, expected);
2364 }
2365
2366 #[rstest]
2367 #[case::max_min(vec![Max(100), Min(0)], vec![0..100, 100..100])]
2368 #[case::min_max(vec![Min(0), Max(100)], vec![0..0, 0..100])]
2369 #[case::length_min(vec![Length(u16::MAX), Min(10)], vec![0..90, 90..100])]
2370 #[case::min_length(vec![Min(10), Length(u16::MAX)], vec![0..10, 10..100])]
2371 #[case::length_max(vec![Length(0), Max(10)], vec![0..90, 90..100])]
2372 #[case::max_length(vec![Max(10), Length(0)], vec![0..10, 10..100])]
2373 fn min_max(#[case] constraints: Vec<Constraint>, #[case] expected: Vec<Range<u16>>) {
2374 let rect = Rect::new(0, 0, 100, 1);
2375 let ranges = Layout::horizontal(constraints)
2376 .flex(Flex::Legacy)
2377 .split(rect)
2378 .iter()
2379 .map(|r| r.left()..r.right())
2380 .collect_vec();
2381 assert_eq!(ranges, expected);
2382 }
2383
2384 #[rstest]
2385 #[case::length_legacy(vec![Length(50)], vec![0..100], Flex::Legacy)]
2386 #[case::length_start(vec![Length(50)], vec![0..50], Flex::Start)]
2387 #[case::length_end(vec![Length(50)], vec![50..100], Flex::End)]
2388 #[case::length_center(vec![Length(50)], vec![25..75], Flex::Center)]
2389 #[case::ratio_legacy(vec![Ratio(1, 2)], vec![0..100], Flex::Legacy)]
2390 #[case::ratio_start(vec![Ratio(1, 2)], vec![0..50], Flex::Start)]
2391 #[case::ratio_end(vec![Ratio(1, 2)], vec![50..100], Flex::End)]
2392 #[case::ratio_center(vec![Ratio(1, 2)], vec![25..75], Flex::Center)]
2393 #[case::percent_legacy(vec![Percentage(50)], vec![0..100], Flex::Legacy)]
2394 #[case::percent_start(vec![Percentage(50)], vec![0..50], Flex::Start)]
2395 #[case::percent_end(vec![Percentage(50)], vec![50..100], Flex::End)]
2396 #[case::percent_center(vec![Percentage(50)], vec![25..75], Flex::Center)]
2397 #[case::min_legacy(vec![Min(50)], vec![0..100], Flex::Legacy)]
2398 #[case::min_start(vec![Min(50)], vec![0..100], Flex::Start)]
2399 #[case::min_end(vec![Min(50)], vec![0..100], Flex::End)]
2400 #[case::min_center(vec![Min(50)], vec![0..100], Flex::Center)]
2401 #[case::max_legacy(vec![Max(50)], vec![0..100], Flex::Legacy)]
2402 #[case::max_start(vec![Max(50)], vec![0..50], Flex::Start)]
2403 #[case::max_end(vec![Max(50)], vec![50..100], Flex::End)]
2404 #[case::max_center(vec![Max(50)], vec![25..75], Flex::Center)]
2405 #[case::spacebetween_becomes_stretch1(vec![Min(1)], vec![0..100], Flex::SpaceBetween)]
2406 #[case::spacebetween_becomes_stretch2(vec![Max(20)], vec![0..100], Flex::SpaceBetween)]
2407 #[case::spacebetween_becomes_stretch3(vec![Length(20)], vec![0..100], Flex::SpaceBetween)]
2408 #[case::length_legacy2(vec![Length(25), Length(25)], vec![0..25, 25..100], Flex::Legacy)]
2409 #[case::length_start2(vec![Length(25), Length(25)], vec![0..25, 25..50], Flex::Start)]
2410 #[case::length_center2(vec![Length(25), Length(25)], vec![25..50, 50..75], Flex::Center)]
2411 #[case::length_end2(vec![Length(25), Length(25)], vec![50..75, 75..100], Flex::End)]
2412 #[case::length_spacebetween(vec![Length(25), Length(25)], vec![0..25, 75..100], Flex::SpaceBetween)]
2413 #[case::length_spaceevenly(vec![Length(25), Length(25)], vec![17..42, 58..83], Flex::SpaceEvenly)]
2414 #[case::length_spacearound(vec![Length(25), Length(25)], vec![13..38, 63..88], Flex::SpaceAround)]
2415 #[case::percentage_legacy(vec![Percentage(25), Percentage(25)], vec![0..25, 25..100], Flex::Legacy)]
2416 #[case::percentage_start(vec![Percentage(25), Percentage(25)], vec![0..25, 25..50], Flex::Start)]
2417 #[case::percentage_center(vec![Percentage(25), Percentage(25)], vec![25..50, 50..75], Flex::Center)]
2418 #[case::percentage_end(vec![Percentage(25), Percentage(25)], vec![50..75, 75..100], Flex::End)]
2419 #[case::percentage_spacebetween(vec![Percentage(25), Percentage(25)], vec![0..25, 75..100], Flex::SpaceBetween)]
2420 #[case::percentage_spaceevenly(vec![Percentage(25), Percentage(25)], vec![17..42, 58..83], Flex::SpaceEvenly)]
2421 #[case::percentage_spacearound(vec![Percentage(25), Percentage(25)], vec![13..38, 63..88], Flex::SpaceAround)]
2422 #[case::min_legacy2(vec![Min(25), Min(25)], vec![0..25, 25..100], Flex::Legacy)]
2423 #[case::min_start2(vec![Min(25), Min(25)], vec![0..50, 50..100], Flex::Start)]
2424 #[case::min_center2(vec![Min(25), Min(25)], vec![0..50, 50..100], Flex::Center)]
2425 #[case::min_end2(vec![Min(25), Min(25)], vec![0..50, 50..100], Flex::End)]
2426 #[case::min_spacebetween(vec![Min(25), Min(25)], vec![0..50, 50..100], Flex::SpaceBetween)]
2427 #[case::min_spaceevenly(vec![Min(25), Min(25)], vec![0..50, 50..100], Flex::SpaceEvenly)]
2428 #[case::min_spacearound(vec![Min(25), Min(25)], vec![0..50, 50..100], Flex::SpaceAround)]
2429 #[case::max_legacy2(vec![Max(25), Max(25)], vec![0..25, 25..100], Flex::Legacy)]
2430 #[case::max_start2(vec![Max(25), Max(25)], vec![0..25, 25..50], Flex::Start)]
2431 #[case::max_center2(vec![Max(25), Max(25)], vec![25..50, 50..75], Flex::Center)]
2432 #[case::max_end2(vec![Max(25), Max(25)], vec![50..75, 75..100], Flex::End)]
2433 #[case::max_spacebetween(vec![Max(25), Max(25)], vec![0..25, 75..100], Flex::SpaceBetween)]
2434 #[case::max_spaceevenly(vec![Max(25), Max(25)], vec![17..42, 58..83], Flex::SpaceEvenly)]
2435 #[case::max_spacearound(vec![Max(25), Max(25)], vec![13..38, 63..88], Flex::SpaceAround)]
2436 #[case::length_spaced_around(vec![Length(25), Length(25), Length(25)], vec![0..25, 38..63, 75..100], Flex::SpaceBetween)]
2437 #[case::one_segment_legacy(vec![Length(50)], vec![0..100], Flex::Legacy)]
2438 #[case::one_segment_start(vec![Length(50)], vec![0..50], Flex::Start)]
2439 #[case::one_segment_end(vec![Length(50)], vec![50..100], Flex::End)]
2440 #[case::one_segment_center(vec![Length(50)], vec![25..75], Flex::Center)]
2441 #[case::one_segment_spacebetween(vec![Length(50)], vec![0..100], Flex::SpaceBetween)]
2442 #[case::one_segment_spaceevenly(vec![Length(50)], vec![25..75], Flex::SpaceEvenly)]
2443 #[case::one_segment_spacearound(vec![Length(50)], vec![25..75], Flex::SpaceAround)]
2444 fn flex_constraint(
2445 #[case] constraints: Vec<Constraint>,
2446 #[case] expected: Vec<Range<u16>>,
2447 #[case] flex: Flex,
2448 ) {
2449 let rect = Rect::new(0, 0, 100, 1);
2450 let ranges = Layout::horizontal(constraints)
2451 .flex(flex)
2452 .split(rect)
2453 .iter()
2454 .map(|r| r.left()..r.right())
2455 .collect_vec();
2456 assert_eq!(ranges, expected);
2457 }
2458
2459 #[rstest]
2460 #[case::length_overlap1(vec![(0 , 20) , (20 , 20) , (40 , 20)] , vec![Length(20) , Length(20) , Length(20)] , Flex::Start , 0)]
2461 #[case::length_overlap2(vec![(0 , 20) , (19 , 20) , (38 , 20)] , vec![Length(20) , Length(20) , Length(20)] , Flex::Start , -1)]
2462 #[case::length_overlap3(vec![(21 , 20) , (40 , 20) , (59 , 20)] , vec![Length(20) , Length(20) , Length(20)] , Flex::Center , -1)]
2463 #[case::length_overlap4(vec![(42 , 20) , (61 , 20) , (80 , 20)] , vec![Length(20) , Length(20) , Length(20)] , Flex::End , -1)]
2464 #[case::length_overlap5(vec![(0 , 20) , (19 , 20) , (38 , 62)] , vec![Length(20) , Length(20) , Length(20)] , Flex::Legacy , -1)]
2465 #[case::length_overlap6(vec![(0 , 20) , (40 , 20) , (80 , 20)] , vec![Length(20) , Length(20) , Length(20)] , Flex::SpaceBetween , -1)]
2466 #[case::length_overlap7(vec![(10 , 20) , (40 , 20) , (70 , 20)] , vec![Length(20) , Length(20) , Length(20)] , Flex::SpaceEvenly , -1)]
2467 #[case::length_overlap7(vec![(7 , 20) , (40 , 20) , (73 , 20)] , vec![Length(20) , Length(20) , Length(20)] , Flex::SpaceAround , -1)]
2468 fn flex_overlap(
2469 #[case] expected: Vec<(u16, u16)>,
2470 #[case] constraints: Vec<Constraint>,
2471 #[case] flex: Flex,
2472 #[case] spacing: i16,
2473 ) {
2474 let rect = Rect::new(0, 0, 100, 1);
2475 let r = Layout::horizontal(constraints)
2476 .flex(flex)
2477 .spacing(spacing)
2478 .split(rect);
2479 let result = r
2480 .iter()
2481 .map(|r| (r.x, r.width))
2482 .collect::<Vec<(u16, u16)>>();
2483
2484 assert_eq!(result, expected);
2485 }
2486
2487 #[rstest]
2488 #[case::length_spacing(vec![(0 , 20), (20, 20) , (40, 20)], vec![Length(20), Length(20), Length(20)], Flex::Start , 0)]
2489 #[case::length_spacing(vec![(0 , 20), (22, 20) , (44, 20)], vec![Length(20), Length(20), Length(20)], Flex::Start , 2)]
2490 #[case::length_spacing(vec![(18, 20), (40, 20) , (62, 20)], vec![Length(20), Length(20), Length(20)], Flex::Center , 2)]
2491 #[case::length_spacing(vec![(36, 20), (58, 20) , (80, 20)], vec![Length(20), Length(20), Length(20)], Flex::End , 2)]
2492 #[case::length_spacing(vec![(0 , 20), (22, 20) , (44, 56)], vec![Length(20), Length(20), Length(20)], Flex::Legacy , 2)]
2493 #[case::length_spacing(vec![(0 , 20), (40, 20) , (80, 20)], vec![Length(20), Length(20), Length(20)], Flex::SpaceBetween, 2)]
2494 #[case::length_spacing(vec![(10, 20), (40, 20) , (70, 20)], vec![Length(20), Length(20), Length(20)], Flex::SpaceEvenly, 2)]
2495 #[case::length_spacing(vec![(7, 20), (40, 20) , (73, 20)], vec![Length(20), Length(20), Length(20)], Flex::SpaceAround, 2)]
2496 fn flex_spacing(
2497 #[case] expected: Vec<(u16, u16)>,
2498 #[case] constraints: Vec<Constraint>,
2499 #[case] flex: Flex,
2500 #[case] spacing: i16,
2501 ) {
2502 let rect = Rect::new(0, 0, 100, 1);
2503 let r = Layout::horizontal(constraints)
2504 .flex(flex)
2505 .spacing(spacing)
2506 .split(rect);
2507 let result = r
2508 .iter()
2509 .map(|r| (r.x, r.width))
2510 .collect::<Vec<(u16, u16)>>();
2511 assert_eq!(result, expected);
2512 }
2513
2514 #[rstest]
2515 #[case::a(vec![(0, 25), (25, 75)], vec![Length(25), Length(25)])]
2516 #[case::b(vec![(0, 25), (25, 75)], vec![Length(25), Percentage(25)])]
2517 #[case::c(vec![(0, 75), (75, 25)], vec![Percentage(25), Length(25)])]
2518 #[case::d(vec![(0, 75), (75, 25)], vec![Min(25), Percentage(25)])]
2519 #[case::e(vec![(0, 25), (25, 75)], vec![Percentage(25), Min(25)])]
2520 #[case::f(vec![(0, 25), (25, 75)], vec![Min(25), Percentage(100)])]
2521 #[case::g(vec![(0, 75), (75, 25)], vec![Percentage(100), Min(25)])]
2522 #[case::h(vec![(0, 25), (25, 75)], vec![Max(75), Percentage(75)])]
2523 #[case::i(vec![(0, 75), (75, 25)], vec![Percentage(75), Max(75)])]
2524 #[case::j(vec![(0, 25), (25, 75)], vec![Max(25), Percentage(25)])]
2525 #[case::k(vec![(0, 75), (75, 25)], vec![Percentage(25), Max(25)])]
2526 #[case::l(vec![(0, 25), (25, 75)], vec![Length(25), Ratio(1, 4)])]
2527 #[case::m(vec![(0, 75), (75, 25)], vec![Ratio(1, 4), Length(25)])]
2528 #[case::n(vec![(0, 25), (25, 75)], vec![Percentage(25), Ratio(1, 4)])]
2529 #[case::o(vec![(0, 75), (75, 25)], vec![Ratio(1, 4), Percentage(25)])]
2530 #[case::p(vec![(0, 25), (25, 75)], vec![Ratio(1, 4), Fill(25)])]
2531 #[case::q(vec![(0, 75), (75, 25)], vec![Fill(25), Ratio(1, 4)])]
2532 fn constraint_specification_tests_for_priority(
2533 #[case] expected: Vec<(u16, u16)>,
2534 #[case] constraints: Vec<Constraint>,
2535 ) {
2536 let rect = Rect::new(0, 0, 100, 1);
2537 let r = Layout::horizontal(constraints)
2538 .flex(Flex::Legacy)
2539 .split(rect)
2540 .iter()
2541 .map(|r| (r.x, r.width))
2542 .collect::<Vec<(u16, u16)>>();
2543 assert_eq!(r, expected);
2544 }
2545
2546 #[rstest]
2547 #[case::a(vec![(0, 20), (20, 20), (40, 20)], vec![Length(20), Length(20), Length(20)], Flex::Start, 0)]
2548 #[case::b(vec![(18, 20), (40, 20), (62, 20)], vec![Length(20), Length(20), Length(20)], Flex::Center, 2)]
2549 #[case::c(vec![(36, 20), (58, 20), (80, 20)], vec![Length(20), Length(20), Length(20)], Flex::End, 2)]
2550 #[case::d(vec![(0, 20), (22, 20), (44, 56)], vec![Length(20), Length(20), Length(20)], Flex::Legacy, 2)]
2551 #[case::e(vec![(0, 20), (22, 20), (44, 56)], vec![Length(20), Length(20), Length(20)], Flex::Legacy, 2)]
2552 #[case::f(vec![(10, 20), (40, 20), (70, 20)], vec![Length(20), Length(20), Length(20)], Flex::SpaceEvenly, 2)]
2553 #[case::f(vec![(7, 20), (40, 20), (73, 20)], vec![Length(20), Length(20), Length(20)], Flex::SpaceAround, 2)]
2554 fn constraint_specification_tests_for_priority_with_spacing(
2555 #[case] expected: Vec<(u16, u16)>,
2556 #[case] constraints: Vec<Constraint>,
2557 #[case] flex: Flex,
2558 #[case] spacing: i16,
2559 ) {
2560 let rect = Rect::new(0, 0, 100, 1);
2561 let r = Layout::horizontal(constraints)
2562 .spacing(spacing)
2563 .flex(flex)
2564 .split(rect)
2565 .iter()
2566 .map(|r| (r.x, r.width))
2567 .collect::<Vec<(u16, u16)>>();
2568 assert_eq!(r, expected);
2569 }
2570
2571 #[rstest]
2572 #[case::prop(vec![(0 , 10), (10, 80), (90 , 10)] , vec![Length(10), Fill(1), Length(10)], Flex::Legacy)]
2573 #[case::flex(vec![(0 , 10), (90 , 10)] , vec![Length(10), Length(10)], Flex::SpaceBetween)]
2574 #[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)]
2575 #[case::flex(vec![(27 , 10), (63, 10)] , vec![Length(10), Length(10)], Flex::SpaceEvenly)]
2576 #[case::prop(vec![(0 , 10), (10, 10), (20 , 80)] , vec![Length(10), Length(10), Fill(1)], Flex::Legacy)]
2577 #[case::flex(vec![(0 , 10), (10, 10)] , vec![Length(10), Length(10)], Flex::Start)]
2578 #[case::prop(vec![(0 , 80), (80 , 10), (90, 10)] , vec![Fill(1), Length(10), Length(10)], Flex::Legacy)]
2579 #[case::flex(vec![(80 , 10), (90, 10)] , vec![Length(10), Length(10)], Flex::End)]
2580 #[case::prop(vec![(0 , 40), (40, 10), (50, 10), (60, 40)] , vec![Fill(1), Length(10), Length(10), Fill(1)], Flex::Legacy)]
2581 #[case::flex(vec![(40 , 10), (50, 10)] , vec![Length(10), Length(10)], Flex::Center)]
2582 #[case::flex(vec![(20 , 10), (70, 10)] , vec![Length(10), Length(10)], Flex::SpaceAround)]
2583 fn fill_vs_flex(
2584 #[case] expected: Vec<(u16, u16)>,
2585 #[case] constraints: Vec<Constraint>,
2586 #[case] flex: Flex,
2587 ) {
2588 let rect = Rect::new(0, 0, 100, 1);
2589 let r = Layout::horizontal(constraints).flex(flex).split(rect);
2590 let result = r
2591 .iter()
2592 .map(|r| (r.x, r.width))
2593 .collect::<Vec<(u16, u16)>>();
2594 assert_eq!(result, expected);
2595 }
2596
2597 #[rstest]
2598 #[case::flex0(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::Legacy , 0)]
2599 #[case::flex0(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::SpaceEvenly , 0)]
2600 #[case::flex0(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::SpaceBetween , 0)]
2601 #[case::flex0(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::SpaceAround , 0)]
2602 #[case::flex0(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::Start , 0)]
2603 #[case::flex0(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::Center , 0)]
2604 #[case::flex0(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::End , 0)]
2605 #[case::flex10(vec![(0 , 45), (55 , 45)] , vec![Fill(1), Fill(1)], Flex::Legacy , 10)]
2606 #[case::flex10(vec![(0 , 45), (55 , 45)] , vec![Fill(1), Fill(1)], Flex::Start , 10)]
2607 #[case::flex10(vec![(0 , 45), (55 , 45)] , vec![Fill(1), Fill(1)], Flex::Center , 10)]
2608 #[case::flex10(vec![(0 , 45), (55 , 45)] , vec![Fill(1), Fill(1)], Flex::End , 10)]
2609 #[case::flex10(vec![(10 , 35), (55 , 35)] , vec![Fill(1), Fill(1)], Flex::SpaceEvenly , 10)]
2610 #[case::flex10(vec![(0 , 45), (55 , 45)] , vec![Fill(1), Fill(1)], Flex::SpaceBetween , 10)]
2611 #[case::flex10(vec![(10 , 30), (60 , 30)] , vec![Fill(1), Fill(1)], Flex::SpaceAround , 10)]
2612 #[case::flex_length0(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::Legacy , 0)]
2613 #[case::flex_length0(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceEvenly , 0)]
2614 #[case::flex_length0(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceBetween , 0)]
2615 #[case::flex_length0(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceAround , 0)]
2616 #[case::flex_length0(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::Start , 0)]
2617 #[case::flex_length0(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::Center , 0)]
2618 #[case::flex_length0(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::End , 0)]
2619 #[case::flex_length10(vec![(0 , 35), (45, 10), (65 , 35)] , vec![Fill(1), Length(10), Fill(1)], Flex::Legacy , 10)]
2620 #[case::flex_length10(vec![(0 , 35), (45, 10), (65 , 35)] , vec![Fill(1), Length(10), Fill(1)], Flex::Start , 10)]
2621 #[case::flex_length10(vec![(0 , 35), (45, 10), (65 , 35)] , vec![Fill(1), Length(10), Fill(1)], Flex::Center , 10)]
2622 #[case::flex_length10(vec![(0 , 35), (45, 10), (65 , 35)] , vec![Fill(1), Length(10), Fill(1)], Flex::End , 10)]
2623 #[case::flex_length10(vec![(10 , 25), (45, 10), (65 , 25)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceEvenly , 10)]
2624 #[case::flex_length10(vec![(0 , 35), (45, 10), (65 , 35)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceBetween , 10)]
2625 #[case::flex_length10(vec![(10 , 15), (45, 10), (75 , 15)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceAround , 10)]
2626 fn fill_spacing(
2627 #[case] expected: Vec<(u16, u16)>,
2628 #[case] constraints: Vec<Constraint>,
2629 #[case] flex: Flex,
2630 #[case] spacing: i16,
2631 ) {
2632 let rect = Rect::new(0, 0, 100, 1);
2633 let r = Layout::horizontal(constraints)
2634 .flex(flex)
2635 .spacing(spacing)
2636 .split(rect);
2637 let result = r
2638 .iter()
2639 .map(|r| (r.x, r.width))
2640 .collect::<Vec<(u16, u16)>>();
2641 assert_eq!(expected, result);
2642 }
2643
2644 #[rstest]
2645 #[case::flex0_1(vec![(0 , 55), (45 , 55)] , vec![Fill(1), Fill(1)], Flex::Legacy , -10)]
2646 #[case::flex0_2(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::SpaceAround , -10)]
2647 #[case::flex0_3(vec![(0 , 55), (45 , 55)] , vec![Fill(1), Fill(1)], Flex::SpaceBetween , -10)]
2648 #[case::flex0_4(vec![(0 , 55), (45 , 55)] , vec![Fill(1), Fill(1)], Flex::Start , -10)]
2649 #[case::flex0_5(vec![(0 , 55), (45 , 55)] , vec![Fill(1), Fill(1)], Flex::Center , -10)]
2650 #[case::flex0_6(vec![(0 , 55), (45 , 55)] , vec![Fill(1), Fill(1)], Flex::End , -10)]
2651 #[case::flex0_7(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::SpaceEvenly , -10)]
2652 #[case::flex10_1(vec![(0 , 51), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::Legacy , -1)]
2653 #[case::flex10_2(vec![(0 , 51), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::Start , -1)]
2654 #[case::flex10_3(vec![(0 , 51), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::Center , -1)]
2655 #[case::flex10_4(vec![(0 , 51), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::End , -1)]
2656 #[case::flex10_5(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::SpaceAround , -1)]
2657 #[case::flex10_6(vec![(0 , 51), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::SpaceBetween , -1)]
2658 #[case::flex10_7(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::SpaceEvenly , -1)]
2659 #[case::flex_length0_1(vec![(0 , 55), (45, 10), (45 , 55)] , vec![Fill(1), Length(10), Fill(1)], Flex::Legacy , -10)]
2660 #[case::flex_length0_2(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceAround , -10)]
2661 #[case::flex_length0_3(vec![(0 , 55), (45, 10), (45 , 55)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceBetween , -10)]
2662 #[case::flex_length0_4(vec![(0 , 55), (45, 10), (45 , 55)] , vec![Fill(1), Length(10), Fill(1)], Flex::Start , -10)]
2663 #[case::flex_length0_5(vec![(0 , 55), (45, 10), (45 , 55)] , vec![Fill(1), Length(10), Fill(1)], Flex::Center , -10)]
2664 #[case::flex_length0_6(vec![(0 , 55), (45, 10), (45 , 55)] , vec![Fill(1), Length(10), Fill(1)], Flex::End , -10)]
2665 #[case::flex_length0_7(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceEvenly , -10)]
2666 #[case::flex_length10_1(vec![(0 , 46), (45, 10), (54 , 46)] , vec![Fill(1), Length(10), Fill(1)], Flex::Legacy , -1)]
2667 #[case::flex_length10_2(vec![(0 , 46), (45, 10), (54 , 46)] , vec![Fill(1), Length(10), Fill(1)], Flex::Start , -1)]
2668 #[case::flex_length10_3(vec![(0 , 46), (45, 10), (54 , 46)] , vec![Fill(1), Length(10), Fill(1)], Flex::Center , -1)]
2669 #[case::flex_length10_4(vec![(0 , 46), (45, 10), (54 , 46)] , vec![Fill(1), Length(10), Fill(1)], Flex::End , -1)]
2670 #[case::flex_length10_5(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceAround , -1)]
2671 #[case::flex_length10_6(vec![(0 , 46), (45, 10), (54 , 46)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceBetween , -1)]
2672 #[case::flex_length10_7(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceEvenly , -1)]
2673 fn fill_overlap(
2674 #[case] expected: Vec<(u16, u16)>,
2675 #[case] constraints: Vec<Constraint>,
2676 #[case] flex: Flex,
2677 #[case] spacing: i16,
2678 ) {
2679 let rect = Rect::new(0, 0, 100, 1);
2680 let r = Layout::horizontal(constraints)
2681 .flex(flex)
2682 .spacing(spacing)
2683 .split(rect);
2684 let result = r
2685 .iter()
2686 .map(|r| (r.x, r.width))
2687 .collect::<Vec<(u16, u16)>>();
2688 assert_eq!(result, expected);
2689 }
2690
2691 #[rstest]
2692 #[case::flex_length10(vec![(0, 10), (90, 10)], vec![Length(10), Length(10)], Flex::Center, 80)]
2693 fn flex_spacing_lower_priority_than_user_spacing(
2694 #[case] expected: Vec<(u16, u16)>,
2695 #[case] constraints: Vec<Constraint>,
2696 #[case] flex: Flex,
2697 #[case] spacing: i16,
2698 ) {
2699 let rect = Rect::new(0, 0, 100, 1);
2700 let r = Layout::horizontal(constraints)
2701 .flex(flex)
2702 .spacing(spacing)
2703 .split(rect);
2704 let result = r
2705 .iter()
2706 .map(|r| (r.x, r.width))
2707 .collect::<Vec<(u16, u16)>>();
2708 assert_eq!(result, expected);
2709 }
2710
2711 #[rstest]
2712 #[case::spacers(vec![(0, 0), (10, 0), (100, 0)], vec![Length(10), Length(10)], Flex::Legacy)]
2713 #[case::spacers(vec![(0, 0), (10, 80), (100, 0)], vec![Length(10), Length(10)], Flex::SpaceBetween)]
2714 #[case::spacers(vec![(0, 27), (37, 26), (73, 27)], vec![Length(10), Length(10)], Flex::SpaceEvenly)]
2715 #[case::spacers(vec![(0, 20), (30, 40), (80, 20)], vec![Length(10), Length(10)], Flex::SpaceAround)]
2716 #[case::spacers(vec![(0, 0), (10, 0), (20, 80)], vec![Length(10), Length(10)], Flex::Start)]
2717 #[case::spacers(vec![(0, 40), (50, 0), (60, 40)], vec![Length(10), Length(10)], Flex::Center)]
2718 #[case::spacers(vec![(0, 80), (90, 0), (100, 0)], vec![Length(10), Length(10)], Flex::End)]
2719 fn split_with_spacers_no_spacing(
2720 #[case] expected: Vec<(u16, u16)>,
2721 #[case] constraints: Vec<Constraint>,
2722 #[case] flex: Flex,
2723 ) {
2724 let rect = Rect::new(0, 0, 100, 1);
2725 let (_, s) = Layout::horizontal(&constraints)
2726 .flex(flex)
2727 .split_with_spacers(rect);
2728 assert_eq!(s.len(), constraints.len() + 1);
2729 let result = s
2730 .iter()
2731 .map(|r| (r.x, r.width))
2732 .collect::<Vec<(u16, u16)>>();
2733 assert_eq!(result, expected);
2734 }
2735
2736 #[rstest]
2737 #[case::spacers(vec![(0, 0), (10, 5), (100, 0)], vec![Length(10), Length(10)], Flex::Legacy, 5)]
2738 #[case::spacers(vec![(0, 0), (10, 80), (100, 0)], vec![Length(10), Length(10)], Flex::SpaceBetween, 5)]
2739 #[case::spacers(vec![(0, 27), (37, 26), (73, 27)], vec![Length(10), Length(10)], Flex::SpaceEvenly, 5)]
2740 #[case::spacers(vec![(0, 20), (30, 40), (80, 20)], vec![Length(10), Length(10)], Flex::SpaceAround, 5)]
2741 #[case::spacers(vec![(0, 0), (10, 5), (25, 75)], vec![Length(10), Length(10)], Flex::Start, 5)]
2742 #[case::spacers(vec![(0, 38), (48, 5), (63, 37)], vec![Length(10), Length(10)], Flex::Center, 5)]
2743 #[case::spacers(vec![(0, 75), (85, 5), (100, 0)], vec![Length(10), Length(10)], Flex::End, 5)]
2744 fn split_with_spacers_and_spacing(
2745 #[case] expected: Vec<(u16, u16)>,
2746 #[case] constraints: Vec<Constraint>,
2747 #[case] flex: Flex,
2748 #[case] spacing: i16,
2749 ) {
2750 let rect = Rect::new(0, 0, 100, 1);
2751 let (_, s) = Layout::horizontal(&constraints)
2752 .flex(flex)
2753 .spacing(spacing)
2754 .split_with_spacers(rect);
2755 assert_eq!(s.len(), constraints.len() + 1);
2756 let result = s
2757 .iter()
2758 .map(|r| (r.x, r.width))
2759 .collect::<Vec<(u16, u16)>>();
2760 assert_eq!(expected, result);
2761 }
2762
2763 #[rstest]
2764 #[case::spacers_1(vec![(0, 0), (10, 0), (100, 0)], vec![Length(10), Length(10)], Flex::Legacy, -1)]
2765 #[case::spacers_2(vec![(0, 0), (10, 80), (100, 0)], vec![Length(10), Length(10)], Flex::SpaceBetween, -1)]
2766 #[case::spacers_3(vec![(0, 27), (37, 26), (73, 27)], vec![Length(10), Length(10)], Flex::SpaceEvenly, -1)]
2767 #[case::spacers_3(vec![(0, 20), (30, 40), (80, 20)], vec![Length(10), Length(10)], Flex::SpaceAround, -1)]
2768 #[case::spacers_4(vec![(0, 0), (10, 0), (19, 81)], vec![Length(10), Length(10)], Flex::Start, -1)]
2769 #[case::spacers_5(vec![(0, 41), (51, 0), (60, 40)], vec![Length(10), Length(10)], Flex::Center, -1)]
2770 #[case::spacers_6(vec![(0, 81), (91, 0), (100, 0)], vec![Length(10), Length(10)], Flex::End, -1)]
2771 fn split_with_spacers_and_overlap(
2772 #[case] expected: Vec<(u16, u16)>,
2773 #[case] constraints: Vec<Constraint>,
2774 #[case] flex: Flex,
2775 #[case] spacing: i16,
2776 ) {
2777 let rect = Rect::new(0, 0, 100, 1);
2778 let (_, s) = Layout::horizontal(&constraints)
2779 .flex(flex)
2780 .spacing(spacing)
2781 .split_with_spacers(rect);
2782 assert_eq!(s.len(), constraints.len() + 1);
2783 let result = s
2784 .iter()
2785 .map(|r| (r.x, r.width))
2786 .collect::<Vec<(u16, u16)>>();
2787 assert_eq!(result, expected);
2788 }
2789
2790 #[rstest]
2791 #[case::spacers(vec![(0, 0), (0, 100), (100, 0)], vec![Length(10), Length(10)], Flex::Legacy, 200)]
2792 #[case::spacers(vec![(0, 0), (0, 100), (100, 0)], vec![Length(10), Length(10)], Flex::SpaceBetween, 200)]
2793 #[case::spacers(vec![(0, 33), (33, 34), (67, 33)], vec![Length(10), Length(10)], Flex::SpaceEvenly, 200)]
2794 #[case::spacers(vec![(0, 25), (25, 50), (75, 25)], vec![Length(10), Length(10)], Flex::SpaceAround, 200)]
2795 #[case::spacers(vec![(0, 0), (0, 100), (100, 0)], vec![Length(10), Length(10)], Flex::Start, 200)]
2796 #[case::spacers(vec![(0, 0), (0, 100), (100, 0)], vec![Length(10), Length(10)], Flex::Center, 200)]
2797 #[case::spacers(vec![(0, 0), (0, 100), (100, 0)], vec![Length(10), Length(10)], Flex::End, 200)]
2798 fn split_with_spacers_and_too_much_spacing(
2799 #[case] expected: Vec<(u16, u16)>,
2800 #[case] constraints: Vec<Constraint>,
2801 #[case] flex: Flex,
2802 #[case] spacing: i16,
2803 ) {
2804 let rect = Rect::new(0, 0, 100, 1);
2805 let (_, s) = Layout::horizontal(&constraints)
2806 .flex(flex)
2807 .spacing(spacing)
2808 .split_with_spacers(rect);
2809 assert_eq!(s.len(), constraints.len() + 1);
2810 let result = s
2811 .iter()
2812 .map(|r| (r.x, r.width))
2813 .collect::<Vec<(u16, u16)>>();
2814 assert_eq!(result, expected);
2815 }
2816
2817 #[rstest]
2818 #[case::compare(vec![(0, 90), (90, 10)], vec![Min(10), Length(10)], Flex::Legacy)]
2819 #[case::compare(vec![(0, 90), (90, 10)], vec![Min(10), Length(10)], Flex::Start)]
2820 #[case::compare(vec![(0, 10), (10, 90)], vec![Min(10), Percentage(100)], Flex::Legacy)]
2821 #[case::compare(vec![(0, 10), (10, 90)], vec![Min(10), Percentage(100)], Flex::Start)]
2822 #[case::compare(vec![(0, 50), (50, 50)], vec![Percentage(50), Percentage(50)], Flex::Legacy)]
2823 #[case::compare(vec![(0, 50), (50, 50)], vec![Percentage(50), Percentage(50)], Flex::Start)]
2824 fn legacy_vs_default(
2825 #[case] expected: Vec<(u16, u16)>,
2826 #[case] constraints: Vec<Constraint>,
2827 #[case] flex: Flex,
2828 ) {
2829 let rect = Rect::new(0, 0, 100, 1);
2830 let r = Layout::horizontal(constraints).flex(flex).split(rect);
2831 let result = r
2832 .iter()
2833 .map(|r| (r.x, r.width))
2834 .collect::<Vec<(u16, u16)>>();
2835 assert_eq!(result, expected);
2836 }
2837 }
2838}