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(all(feature = "layout-cache", feature = "std"))]
37type Cache = LruCache<(Rect, Layout), (Segments, Spacers)>;
38
39#[cfg(all(feature = "layout-cache", not(feature = "std")))]
41type Cache = LruCache<(Rect, Layout), (Vec<Rect>, Vec<Rect>)>;
42
43const FLOAT_PRECISION_MULTIPLIER: f64 = 100.0;
47
48#[cfg(all(feature = "layout-cache", feature = "std"))]
49std::thread_local! {
50 static LAYOUT_CACHE: core::cell::RefCell<Cache> = core::cell::RefCell::new(Cache::new(
51 NonZeroUsize::new(Layout::DEFAULT_CACHE_SIZE).unwrap(),
52 ));
53}
54
55#[cfg(all(feature = "layout-cache", not(feature = "std")))]
56static LAYOUT_CACHE: critical_section::Mutex<core::cell::RefCell<Option<Cache>>> =
57 critical_section::Mutex::new(core::cell::RefCell::new(None));
58
59#[derive(Debug, Clone, Eq, PartialEq, Hash)]
88#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
89pub enum Spacing {
90 Space(u16),
91 Overlap(u16),
92}
93
94impl Default for Spacing {
95 fn default() -> Self {
96 Self::Space(0)
97 }
98}
99
100impl From<i32> for Spacing {
101 fn from(value: i32) -> Self {
102 Self::from(value.clamp(i32::from(i16::MIN), i32::from(i16::MAX)) as i16)
103 }
104}
105
106impl From<u16> for Spacing {
107 fn from(value: u16) -> Self {
108 Self::Space(value)
109 }
110}
111
112impl From<i16> for Spacing {
113 fn from(value: i16) -> Self {
114 if value < 0 {
115 Self::Overlap(value.unsigned_abs())
116 } else {
117 Self::Space(value.unsigned_abs())
118 }
119 }
120}
121
122#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
201#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
202pub struct Layout {
203 direction: Direction,
204 constraints: Vec<Constraint>,
205 margin: Margin,
206 flex: Flex,
207 spacing: Spacing,
208}
209
210impl Layout {
211 #[cfg(feature = "layout-cache")]
218 pub const DEFAULT_CACHE_SIZE: usize = 500;
219
220 pub fn new<I>(direction: Direction, constraints: I) -> Self
251 where
252 I: IntoIterator,
253 I::Item: Into<Constraint>,
254 {
255 Self {
256 direction,
257 constraints: constraints.into_iter().map(Into::into).collect(),
258 ..Self::default()
259 }
260 }
261
262 pub fn vertical<I>(constraints: I) -> Self
275 where
276 I: IntoIterator,
277 I::Item: Into<Constraint>,
278 {
279 Self::new(Direction::Vertical, constraints.into_iter().map(Into::into))
280 }
281
282 pub fn horizontal<I>(constraints: I) -> Self
295 where
296 I: IntoIterator,
297 I::Item: Into<Constraint>,
298 {
299 Self::new(
300 Direction::Horizontal,
301 constraints.into_iter().map(Into::into),
302 )
303 }
304
305 #[cfg(feature = "layout-cache")]
311 pub fn init_cache(cache_size: NonZeroUsize) {
312 Self::resize_cache(cache_size);
313 }
314
315 #[cfg(all(feature = "layout-cache", feature = "std"))]
316 fn resize_cache(cache_size: NonZeroUsize) {
317 LAYOUT_CACHE.with_borrow_mut(|cache| cache.resize(cache_size));
318 }
319
320 #[cfg(all(feature = "layout-cache", not(feature = "std")))]
321 fn resize_cache(cache_size: NonZeroUsize) {
322 critical_section::with(|cs| {
323 let mut cache = LAYOUT_CACHE.borrow(cs).borrow_mut();
324 match cache.as_mut() {
325 Some(c) => c.resize(cache_size),
326 None => *cache = Some(Cache::new(cache_size)),
327 }
328 });
329 }
330
331 #[must_use = "method moves the value of self and returns the modified value"]
351 pub const fn direction(mut self, direction: Direction) -> Self {
352 self.direction = direction;
353 self
354 }
355
356 #[must_use = "method moves the value of self and returns the modified value"]
402 pub fn constraints<I>(mut self, constraints: I) -> Self
403 where
404 I: IntoIterator,
405 I::Item: Into<Constraint>,
406 {
407 self.constraints = constraints.into_iter().map(Into::into).collect();
408 self
409 }
410
411 #[must_use = "method moves the value of self and returns the modified value"]
425 pub const fn margin(mut self, margin: u16) -> Self {
426 self.margin = Margin {
427 horizontal: margin,
428 vertical: margin,
429 };
430 self
431 }
432
433 #[must_use = "method moves the value of self and returns the modified value"]
447 pub const fn horizontal_margin(mut self, horizontal: u16) -> Self {
448 self.margin.horizontal = horizontal;
449 self
450 }
451
452 #[must_use = "method moves the value of self and returns the modified value"]
466 pub const fn vertical_margin(mut self, vertical: u16) -> Self {
467 self.margin.vertical = vertical;
468 self
469 }
470
471 #[must_use = "method moves the value of self and returns the modified value"]
507 pub const fn flex(mut self, flex: Flex) -> Self {
508 self.flex = flex;
509 self
510 }
511
512 #[must_use = "method moves the value of self and returns the modified value"]
546 pub fn spacing<T>(mut self, spacing: T) -> Self
547 where
548 T: Into<Spacing>,
549 {
550 self.spacing = spacing.into();
551 self
552 }
553
554 pub fn areas<const N: usize>(&self, area: Rect) -> [Rect; N] {
579 let areas = self.split(area);
580 areas.as_ref().try_into().unwrap_or_else(|_| {
581 panic!(
582 "invalid number of rects: expected {N}, found {}",
583 areas.len()
584 )
585 })
586 }
587
588 pub fn try_areas<const N: usize>(&self, area: Rect) -> Result<[Rect; N], TryFromSliceError> {
615 self.split(area).as_ref().try_into()
616 }
617
618 pub fn spacers<const N: usize>(&self, area: Rect) -> [Rect; N] {
647 let (_, spacers) = self.split_with_spacers(area);
648 spacers
649 .as_ref()
650 .try_into()
651 .expect("invalid number of rects")
652 }
653
654 pub fn split(&self, area: Rect) -> Rects {
689 self.split_with_spacers(area).0
690 }
691
692 pub fn split_with_spacers(&self, area: Rect) -> (Segments, Spacers) {
739 #[cfg(feature = "layout-cache")]
740 {
741 self.cached_split(area)
742 }
743
744 #[cfg(not(feature = "layout-cache"))]
745 {
746 self.split_layout(area)
747 }
748 }
749
750 #[cfg(all(feature = "layout-cache", feature = "std"))]
752 fn cached_split(&self, area: Rect) -> (Segments, Spacers) {
753 LAYOUT_CACHE.with_borrow_mut(|cache| {
754 let key = (area, self.clone());
755 cache.get_or_insert(key, || self.split_layout(area)).clone()
756 })
757 }
758
759 #[cfg(all(feature = "layout-cache", not(feature = "std")))]
761 fn cached_split(&self, area: Rect) -> (Segments, Spacers) {
762 let cached = critical_section::with(|cs| {
765 let mut cache = LAYOUT_CACHE.borrow(cs).borrow_mut();
766 let cache = cache.get_or_insert_with(|| {
767 Cache::new(NonZeroUsize::new(Self::DEFAULT_CACHE_SIZE).unwrap())
768 });
769 let key = (area, self.clone());
770 cache
771 .get(&key)
772 .map(|(s, sp)| (Rc::from(s.as_slice()), Rc::from(sp.as_slice())))
773 });
774
775 match cached {
776 Some(result) => result,
777 None => {
778 let result = self.split_layout(area);
779
780 critical_section::with(|cs| {
781 let mut cache = LAYOUT_CACHE.borrow(cs).borrow_mut();
782 if let Some(cache) = cache.as_mut() {
783 let key = (area, self.clone());
784 cache.put(key, (result.0.to_vec(), result.1.to_vec()));
785 }
786 });
787
788 result
789 }
790 }
791 }
792
793 fn split_layout(&self, area: Rect) -> (Segments, Spacers) {
794 self.try_split(area).expect("failed to split")
795 }
796
797 fn try_split(&self, area: Rect) -> Result<(Segments, Spacers), AddConstraintError> {
798 let mut solver = Solver::new();
819
820 let inner_area = area.inner(self.margin);
821 let (area_start, area_end) = match self.direction {
822 Direction::Horizontal => (
823 f64::from(inner_area.x) * FLOAT_PRECISION_MULTIPLIER,
824 f64::from(inner_area.right()) * FLOAT_PRECISION_MULTIPLIER,
825 ),
826 Direction::Vertical => (
827 f64::from(inner_area.y) * FLOAT_PRECISION_MULTIPLIER,
828 f64::from(inner_area.bottom()) * FLOAT_PRECISION_MULTIPLIER,
829 ),
830 };
831
832 let variable_count = self.constraints.len() * 2 + 2;
852 let variables = iter::repeat_with(Variable::new)
853 .take(variable_count)
854 .collect_vec();
855 let spacers = variables
856 .iter()
857 .tuples()
858 .map(|(a, b)| Element::from((*a, *b)))
859 .collect_vec();
860 let segments = variables
861 .iter()
862 .skip(1)
863 .tuples()
864 .map(|(a, b)| Element::from((*a, *b)))
865 .collect_vec();
866
867 let flex = self.flex;
868
869 let spacing = match self.spacing {
870 Spacing::Space(x) => x as i16,
871 Spacing::Overlap(x) => -(x as i16),
872 };
873
874 let constraints = &self.constraints;
875
876 let area_size = Element::from((*variables.first().unwrap(), *variables.last().unwrap()));
877 configure_area(&mut solver, area_size, area_start, area_end)?;
878 configure_variable_in_area_constraints(&mut solver, &variables, area_size)?;
879 configure_variable_constraints(&mut solver, &variables)?;
880 configure_flex_constraints(&mut solver, area_size, &spacers, flex, spacing)?;
881 configure_constraints(&mut solver, area_size, &segments, constraints, flex)?;
882 configure_fill_constraints(&mut solver, &segments, constraints, flex)?;
883
884 if !flex.is_legacy() {
885 for (left, right) in segments.iter().tuple_windows() {
886 solver.add_constraint(left.has_size(right, ALL_SEGMENT_GROW))?;
887 }
888 }
889
890 let changes: HashMap<Variable, f64> = solver.fetch_changes().iter().copied().collect();
892 let segment_rects = changes_to_rects(&changes, &segments, inner_area, self.direction);
896 let spacer_rects = changes_to_rects(&changes, &spacers, inner_area, self.direction);
897
898 Ok((segment_rects, spacer_rects))
899 }
900}
901
902fn configure_area(
903 solver: &mut Solver,
904 area: Element,
905 area_start: f64,
906 area_end: f64,
907) -> Result<(), AddConstraintError> {
908 solver.add_constraint(area.start | EQ(Strength::REQUIRED) | area_start)?;
909 solver.add_constraint(area.end | EQ(Strength::REQUIRED) | area_end)?;
910 Ok(())
911}
912
913fn configure_variable_in_area_constraints(
914 solver: &mut Solver,
915 variables: &[Variable],
916 area: Element,
917) -> Result<(), AddConstraintError> {
918 for &variable in variables {
920 solver.add_constraint(variable | GE(Strength::REQUIRED) | area.start)?;
921 solver.add_constraint(variable | LE(Strength::REQUIRED) | area.end)?;
922 }
923
924 Ok(())
925}
926
927fn configure_variable_constraints(
928 solver: &mut Solver,
929 variables: &[Variable],
930) -> Result<(), AddConstraintError> {
931 for (&left, &right) in variables.iter().skip(1).tuples() {
941 solver.add_constraint(left | LE(Strength::REQUIRED) | right)?;
942 }
943 Ok(())
944}
945
946fn configure_constraints(
947 solver: &mut Solver,
948 area: Element,
949 segments: &[Element],
950 constraints: &[Constraint],
951 flex: Flex,
952) -> Result<(), AddConstraintError> {
953 for (&constraint, &segment) in constraints.iter().zip(segments.iter()) {
954 match constraint {
955 Constraint::Max(max) => {
956 solver.add_constraint(segment.has_max_size(max, MAX_SIZE_LE))?;
957 solver.add_constraint(segment.has_int_size(max, MAX_SIZE_EQ))?;
958 }
959 Constraint::Min(min) => {
960 solver.add_constraint(segment.has_min_size(min as i16, MIN_SIZE_GE))?;
961 if flex.is_legacy() {
962 solver.add_constraint(segment.has_int_size(min, MIN_SIZE_EQ))?;
963 } else {
964 solver.add_constraint(segment.has_size(area, FILL_GROW))?;
965 }
966 }
967 Constraint::Length(length) => {
968 solver.add_constraint(segment.has_int_size(length, LENGTH_SIZE_EQ))?;
969 }
970 Constraint::Percentage(p) => {
971 let size = area.size() * f64::from(p) / 100.00;
972 solver.add_constraint(segment.has_size(size, PERCENTAGE_SIZE_EQ))?;
973 }
974 Constraint::Ratio(num, den) => {
975 let size = area.size() * f64::from(num) / f64::from(den.max(1));
977 solver.add_constraint(segment.has_size(size, RATIO_SIZE_EQ))?;
978 }
979 Constraint::Fill(_) => {
980 solver.add_constraint(segment.has_size(area, FILL_GROW))?;
982 }
983 }
984 }
985 Ok(())
986}
987
988fn configure_flex_constraints(
989 solver: &mut Solver,
990 area: Element,
991 spacers: &[Element],
992 flex: Flex,
993 spacing: i16,
994) -> Result<(), AddConstraintError> {
995 let spacers_except_first_and_last = spacers.get(1..spacers.len() - 1).unwrap_or(&[]);
996 let spacing_f64 = f64::from(spacing) * FLOAT_PRECISION_MULTIPLIER;
997 match flex {
998 Flex::Legacy => {
999 for spacer in spacers_except_first_and_last {
1000 solver.add_constraint(spacer.has_size(spacing_f64, SPACER_SIZE_EQ))?;
1001 }
1002 if let (Some(first), Some(last)) = (spacers.first(), spacers.last()) {
1003 solver.add_constraint(first.is_empty())?;
1004 solver.add_constraint(last.is_empty())?;
1005 }
1006 }
1007
1008 Flex::SpaceAround => {
1013 if spacers.len() <= 2 {
1014 for (left, right) in spacers.iter().tuple_combinations() {
1016 solver.add_constraint(left.has_size(right, SPACER_SIZE_EQ))?;
1017 }
1018 for spacer in spacers {
1019 solver.add_constraint(spacer.has_min_size(spacing, SPACER_SIZE_EQ))?;
1020 solver.add_constraint(spacer.has_size(area, SPACE_GROW))?;
1021 }
1022 } else {
1023 let (first, rest) = spacers.split_first().unwrap();
1025 let (last, middle) = rest.split_last().unwrap();
1026
1027 for (left, right) in middle.iter().tuple_combinations() {
1029 solver.add_constraint(left.has_size(right, SPACER_SIZE_EQ))?;
1030 }
1031
1032 if let Some(first_middle) = middle.first() {
1034 solver.add_constraint(first_middle.has_double_size(first, SPACER_SIZE_EQ))?;
1035 solver.add_constraint(first_middle.has_double_size(last, SPACER_SIZE_EQ))?;
1036 }
1037
1038 for spacer in spacers {
1040 solver.add_constraint(spacer.has_min_size(spacing, SPACER_SIZE_EQ))?;
1041 solver.add_constraint(spacer.has_size(area, SPACE_GROW))?;
1042 }
1043 }
1044 }
1045
1046 Flex::SpaceEvenly => {
1049 for (left, right) in spacers.iter().tuple_combinations() {
1050 solver.add_constraint(left.has_size(right, SPACER_SIZE_EQ))?;
1051 }
1052 for spacer in spacers {
1053 solver.add_constraint(spacer.has_min_size(spacing, SPACER_SIZE_EQ))?;
1054 solver.add_constraint(spacer.has_size(area, SPACE_GROW))?;
1055 }
1056 }
1057
1058 Flex::SpaceBetween => {
1062 for (left, right) in spacers_except_first_and_last.iter().tuple_combinations() {
1063 solver.add_constraint(left.has_size(right.size(), SPACER_SIZE_EQ))?;
1064 }
1065 for spacer in spacers_except_first_and_last {
1066 solver.add_constraint(spacer.has_min_size(spacing, SPACER_SIZE_EQ))?;
1067 solver.add_constraint(spacer.has_size(area, SPACE_GROW))?;
1068 }
1069 if let (Some(first), Some(last)) = (spacers.first(), spacers.last()) {
1070 solver.add_constraint(first.is_empty())?;
1071 solver.add_constraint(last.is_empty())?;
1072 }
1073 }
1074
1075 Flex::Start => {
1076 for spacer in spacers_except_first_and_last {
1077 solver.add_constraint(spacer.has_size(spacing_f64, SPACER_SIZE_EQ))?;
1078 }
1079 if let (Some(first), Some(last)) = (spacers.first(), spacers.last()) {
1080 solver.add_constraint(first.is_empty())?;
1081 solver.add_constraint(last.has_size(area, GROW))?;
1082 }
1083 }
1084 Flex::Center => {
1085 for spacer in spacers_except_first_and_last {
1086 solver.add_constraint(spacer.has_size(spacing_f64, SPACER_SIZE_EQ))?;
1087 }
1088 if let (Some(first), Some(last)) = (spacers.first(), spacers.last()) {
1089 solver.add_constraint(first.has_size(area, GROW))?;
1090 solver.add_constraint(last.has_size(area, GROW))?;
1091 solver.add_constraint(first.has_size(last, SPACER_SIZE_EQ))?;
1092 }
1093 }
1094 Flex::End => {
1095 for spacer in spacers_except_first_and_last {
1096 solver.add_constraint(spacer.has_size(spacing_f64, SPACER_SIZE_EQ))?;
1097 }
1098 if let (Some(first), Some(last)) = (spacers.first(), spacers.last()) {
1099 solver.add_constraint(last.is_empty())?;
1100 solver.add_constraint(first.has_size(area, GROW))?;
1101 }
1102 }
1103 }
1104 Ok(())
1105}
1106
1107fn configure_fill_constraints(
1122 solver: &mut Solver,
1123 segments: &[Element],
1124 constraints: &[Constraint],
1125 flex: Flex,
1126) -> Result<(), AddConstraintError> {
1127 for ((&left_constraint, &left_segment), (&right_constraint, &right_segment)) in constraints
1128 .iter()
1129 .zip(segments.iter())
1130 .filter(|(c, _)| c.is_fill() || (!flex.is_legacy() && c.is_min()))
1131 .tuple_combinations()
1132 {
1133 let left_scaling_factor = match left_constraint {
1134 Constraint::Fill(scale) => f64::from(scale).max(1e-6),
1135 Constraint::Min(_) => 1.0,
1136 _ => unreachable!(),
1137 };
1138 let right_scaling_factor = match right_constraint {
1139 Constraint::Fill(scale) => f64::from(scale).max(1e-6),
1140 Constraint::Min(_) => 1.0,
1141 _ => unreachable!(),
1142 };
1143 solver.add_constraint(
1144 (right_scaling_factor * left_segment.size())
1145 | EQ(GROW)
1146 | (left_scaling_factor * right_segment.size()),
1147 )?;
1148 }
1149 Ok(())
1150}
1151
1152#[cfg(feature = "std")]
1154#[inline]
1155fn round(value: f64) -> f64 {
1156 value.round()
1157}
1158
1159#[cfg(not(feature = "std"))]
1161#[inline]
1162fn round(value: f64) -> f64 {
1163 (value + 0.5f64.copysign(value)) as i64 as f64
1164}
1165
1166fn changes_to_rects(
1167 changes: &HashMap<Variable, f64>,
1168 elements: &[Element],
1169 area: Rect,
1170 direction: Direction,
1171) -> Rects {
1172 elements
1174 .iter()
1175 .map(|element| {
1176 let start = changes.get(&element.start).unwrap_or(&0.0);
1177 let end = changes.get(&element.end).unwrap_or(&0.0);
1178 let start = round(round(*start) / FLOAT_PRECISION_MULTIPLIER) as u16;
1179 let end = round(round(*end) / FLOAT_PRECISION_MULTIPLIER) as u16;
1180 let size = end.saturating_sub(start);
1181 match direction {
1182 Direction::Horizontal => Rect {
1183 x: start,
1184 y: area.y,
1185 width: size,
1186 height: area.height,
1187 },
1188 Direction::Vertical => Rect {
1189 x: area.x,
1190 y: start,
1191 width: area.width,
1192 height: size,
1193 },
1194 }
1195 })
1196 .collect::<Rects>()
1197}
1198
1199#[expect(dead_code)]
1202#[cfg(feature = "std")]
1203fn debug_elements(elements: &[Element], changes: &HashMap<Variable, f64>) {
1204 let variables = alloc::format!(
1205 "{:?}",
1206 elements
1207 .iter()
1208 .map(|e| (
1209 changes.get(&e.start).unwrap_or(&0.0) / FLOAT_PRECISION_MULTIPLIER,
1210 changes.get(&e.end).unwrap_or(&0.0) / FLOAT_PRECISION_MULTIPLIER,
1211 ))
1212 .collect::<Vec<(f64, f64)>>()
1213 );
1214 std::dbg!(variables);
1215}
1216
1217#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
1219struct Element {
1220 start: Variable,
1221 end: Variable,
1222}
1223
1224impl From<(Variable, Variable)> for Element {
1225 fn from((start, end): (Variable, Variable)) -> Self {
1226 Self { start, end }
1227 }
1228}
1229
1230impl Element {
1231 #[expect(dead_code)]
1232 fn new() -> Self {
1233 Self {
1234 start: Variable::new(),
1235 end: Variable::new(),
1236 }
1237 }
1238
1239 fn size(&self) -> Expression {
1240 self.end - self.start
1241 }
1242
1243 fn has_max_size(&self, size: u16, strength: Strength) -> kasuari::Constraint {
1244 self.size() | LE(strength) | (f64::from(size) * FLOAT_PRECISION_MULTIPLIER)
1245 }
1246
1247 fn has_min_size(&self, size: i16, strength: Strength) -> kasuari::Constraint {
1248 self.size() | GE(strength) | (f64::from(size) * FLOAT_PRECISION_MULTIPLIER)
1249 }
1250
1251 fn has_int_size(&self, size: u16, strength: Strength) -> kasuari::Constraint {
1252 self.size() | EQ(strength) | (f64::from(size) * FLOAT_PRECISION_MULTIPLIER)
1253 }
1254
1255 fn has_size<E: Into<Expression>>(&self, size: E, strength: Strength) -> kasuari::Constraint {
1256 self.size() | EQ(strength) | size.into()
1257 }
1258
1259 fn has_double_size<E: Into<Expression>>(
1260 &self,
1261 size: E,
1262 strength: Strength,
1263 ) -> kasuari::Constraint {
1264 self.size() | EQ(strength) | (size.into() * 2.0)
1265 }
1266
1267 fn is_empty(&self) -> kasuari::Constraint {
1268 self.size() | EQ(Strength::REQUIRED - Strength::WEAK) | 0.0
1269 }
1270}
1271
1272impl From<Element> for Expression {
1274 fn from(element: Element) -> Self {
1275 element.size()
1276 }
1277}
1278
1279impl From<&Element> for Expression {
1281 fn from(element: &Element) -> Self {
1282 element.size()
1283 }
1284}
1285
1286mod strengths {
1287 use kasuari::Strength;
1288
1289 pub const SPACER_SIZE_EQ: Strength = Strength::REQUIRED.div_f64(10.0);
1295
1296 pub const MIN_SIZE_GE: Strength = Strength::STRONG.mul_f64(100.0);
1302
1303 pub const MAX_SIZE_LE: Strength = Strength::STRONG.mul_f64(100.0);
1309
1310 pub const LENGTH_SIZE_EQ: Strength = Strength::STRONG.mul_f64(10.0);
1316
1317 pub const PERCENTAGE_SIZE_EQ: Strength = Strength::STRONG;
1323
1324 pub const RATIO_SIZE_EQ: Strength = Strength::STRONG.div_f64(10.0);
1330
1331 pub const MIN_SIZE_EQ: Strength = Strength::MEDIUM.mul_f64(10.0);
1337
1338 pub const MAX_SIZE_EQ: Strength = Strength::MEDIUM.mul_f64(10.0);
1344
1345 pub const FILL_GROW: Strength = Strength::MEDIUM;
1351
1352 pub const GROW: Strength = Strength::MEDIUM.div_f64(10.0);
1358
1359 pub const SPACE_GROW: Strength = Strength::WEAK.mul_f64(10.0);
1365
1366 pub const ALL_SEGMENT_GROW: Strength = Strength::WEAK;
1372}
1373
1374#[cfg(test)]
1375mod tests {
1376 use alloc::borrow::ToOwned;
1377 use alloc::vec;
1378 use alloc::vec::Vec;
1379
1380 use super::*;
1381
1382 #[test]
1383 pub fn strength_is_valid() {
1386 use strengths::*;
1387 assert!(SPACER_SIZE_EQ > MAX_SIZE_LE);
1388 assert!(MAX_SIZE_LE > MAX_SIZE_EQ);
1389 assert_eq!(MIN_SIZE_GE, MAX_SIZE_LE);
1390 assert!(MAX_SIZE_LE > LENGTH_SIZE_EQ);
1391 assert!(LENGTH_SIZE_EQ > PERCENTAGE_SIZE_EQ);
1392 assert!(PERCENTAGE_SIZE_EQ > RATIO_SIZE_EQ);
1393 assert!(RATIO_SIZE_EQ > MAX_SIZE_EQ);
1394 assert!(MIN_SIZE_GE > FILL_GROW);
1395 assert!(FILL_GROW > GROW);
1396 assert!(GROW > SPACE_GROW);
1397 assert!(SPACE_GROW > ALL_SEGMENT_GROW);
1398 }
1399
1400 #[test]
1401 #[cfg(all(feature = "layout-cache", feature = "std"))]
1402 fn cache_size() {
1403 LAYOUT_CACHE.with_borrow(|cache| {
1404 assert_eq!(cache.cap().get(), Layout::DEFAULT_CACHE_SIZE);
1405 });
1406
1407 Layout::init_cache(NonZeroUsize::new(10).unwrap());
1408 LAYOUT_CACHE.with_borrow(|cache| {
1409 assert_eq!(cache.cap().get(), 10);
1410 });
1411 }
1412
1413 #[test]
1414 #[cfg(all(feature = "layout-cache", not(feature = "std")))]
1415 fn cache_size() {
1416 Layout::init_cache(NonZeroUsize::new(Layout::DEFAULT_CACHE_SIZE).unwrap());
1417 critical_section::with(|cs| {
1418 let cache = LAYOUT_CACHE.borrow(cs).borrow();
1419 assert_eq!(
1420 cache.as_ref().unwrap().cap().get(),
1421 Layout::DEFAULT_CACHE_SIZE
1422 );
1423 });
1424
1425 Layout::init_cache(NonZeroUsize::new(10).unwrap());
1426 critical_section::with(|cs| {
1427 let cache = LAYOUT_CACHE.borrow(cs).borrow();
1428 assert_eq!(cache.as_ref().unwrap().cap().get(), 10);
1429 });
1430 }
1431
1432 #[test]
1433 fn default() {
1434 assert_eq!(
1435 Layout::default(),
1436 Layout {
1437 direction: Direction::Vertical,
1438 margin: Margin::new(0, 0),
1439 constraints: vec![],
1440 flex: Flex::default(),
1441 spacing: Spacing::default(),
1442 }
1443 );
1444 }
1445
1446 #[test]
1447 fn new() {
1448 let fixed_size_array = [Constraint::Min(0)];
1450 let layout = Layout::new(Direction::Horizontal, fixed_size_array);
1451 assert_eq!(layout.direction, Direction::Horizontal);
1452 assert_eq!(layout.constraints, [Constraint::Min(0)]);
1453
1454 #[expect(clippy::needless_borrows_for_generic_args)] let layout = Layout::new(Direction::Horizontal, &[Constraint::Min(0)]);
1457 assert_eq!(layout.direction, Direction::Horizontal);
1458 assert_eq!(layout.constraints, [Constraint::Min(0)]);
1459
1460 let layout = Layout::new(Direction::Horizontal, vec![Constraint::Min(0)]);
1462 assert_eq!(layout.direction, Direction::Horizontal);
1463 assert_eq!(layout.constraints, [Constraint::Min(0)]);
1464
1465 #[expect(clippy::needless_borrows_for_generic_args)] let layout = Layout::new(Direction::Horizontal, &(vec![Constraint::Min(0)]));
1468 assert_eq!(layout.direction, Direction::Horizontal);
1469 assert_eq!(layout.constraints, [Constraint::Min(0)]);
1470
1471 let layout = Layout::new(Direction::Horizontal, iter::once(Constraint::Min(0)));
1473 assert_eq!(layout.direction, Direction::Horizontal);
1474 assert_eq!(layout.constraints, [Constraint::Min(0)]);
1475 }
1476
1477 #[test]
1478 fn vertical() {
1479 assert_eq!(
1480 Layout::vertical([Constraint::Min(0)]),
1481 Layout {
1482 direction: Direction::Vertical,
1483 margin: Margin::new(0, 0),
1484 constraints: vec![Constraint::Min(0)],
1485 flex: Flex::default(),
1486 spacing: Spacing::default(),
1487 }
1488 );
1489 }
1490
1491 #[test]
1492 fn horizontal() {
1493 assert_eq!(
1494 Layout::horizontal([Constraint::Min(0)]),
1495 Layout {
1496 direction: Direction::Horizontal,
1497 margin: Margin::new(0, 0),
1498 constraints: vec![Constraint::Min(0)],
1499 flex: Flex::default(),
1500 spacing: Spacing::default(),
1501 }
1502 );
1503 }
1504
1505 #[test]
1508 fn constraints() {
1509 const CONSTRAINTS: [Constraint; 2] = [Constraint::Min(0), Constraint::Max(10)];
1510 let fixed_size_array = CONSTRAINTS;
1511 assert_eq!(
1512 Layout::default().constraints(fixed_size_array).constraints,
1513 CONSTRAINTS,
1514 "constraints should be settable with an array"
1515 );
1516
1517 let slice_of_fixed_size_array = &CONSTRAINTS;
1518 assert_eq!(
1519 Layout::default()
1520 .constraints(slice_of_fixed_size_array)
1521 .constraints,
1522 CONSTRAINTS,
1523 "constraints should be settable with a slice"
1524 );
1525
1526 let vec = CONSTRAINTS.to_vec();
1527 let slice_of_vec = vec.as_slice();
1528 assert_eq!(
1529 Layout::default().constraints(slice_of_vec).constraints,
1530 CONSTRAINTS,
1531 "constraints should be settable with a slice"
1532 );
1533
1534 assert_eq!(
1535 Layout::default().constraints(vec).constraints,
1536 CONSTRAINTS,
1537 "constraints should be settable with a Vec"
1538 );
1539
1540 let iter = CONSTRAINTS.iter();
1541 assert_eq!(
1542 Layout::default().constraints(iter).constraints,
1543 CONSTRAINTS,
1544 "constraints should be settable with an iter"
1545 );
1546
1547 let iterator = CONSTRAINTS.iter().map(ToOwned::to_owned);
1548 assert_eq!(
1549 Layout::default().constraints(iterator).constraints,
1550 CONSTRAINTS,
1551 "constraints should be settable with an iterator"
1552 );
1553
1554 let iterator_ref = CONSTRAINTS.iter().map(AsRef::as_ref);
1555 assert_eq!(
1556 Layout::default().constraints(iterator_ref).constraints,
1557 CONSTRAINTS,
1558 "constraints should be settable with an iterator of refs"
1559 );
1560 }
1561
1562 #[test]
1563 fn direction() {
1564 assert_eq!(
1565 Layout::default().direction(Direction::Horizontal).direction,
1566 Direction::Horizontal
1567 );
1568 assert_eq!(
1569 Layout::default().direction(Direction::Vertical).direction,
1570 Direction::Vertical
1571 );
1572 }
1573
1574 #[test]
1575 fn margins() {
1576 assert_eq!(Layout::default().margin(10).margin, Margin::new(10, 10));
1577 assert_eq!(
1578 Layout::default().horizontal_margin(10).margin,
1579 Margin::new(10, 0)
1580 );
1581 assert_eq!(
1582 Layout::default().vertical_margin(10).margin,
1583 Margin::new(0, 10)
1584 );
1585 assert_eq!(
1586 Layout::default()
1587 .horizontal_margin(10)
1588 .vertical_margin(20)
1589 .margin,
1590 Margin::new(10, 20)
1591 );
1592 }
1593
1594 #[test]
1595 fn flex() {
1596 assert_eq!(Layout::default().flex, Flex::Start);
1597 assert_eq!(Layout::default().flex(Flex::Center).flex, Flex::Center);
1598 }
1599
1600 #[test]
1601 fn spacing() {
1602 assert_eq!(Layout::default().spacing(10).spacing, Spacing::Space(10));
1603 assert_eq!(Layout::default().spacing(0).spacing, Spacing::Space(0));
1604 assert_eq!(Layout::default().spacing(-10).spacing, Spacing::Overlap(10));
1605 }
1606
1607 mod split {
1625 use alloc::string::ToString;
1626 use core::ops::Range;
1627
1628 use itertools::Itertools;
1629 use pretty_assertions::assert_eq;
1630 use rstest::rstest;
1631
1632 use super::*;
1633 use crate::buffer::Buffer;
1634 use crate::layout::Constraint::{self, *};
1635 use crate::layout::{Direction, Flex, Layout, Rect};
1636 use crate::text::Text;
1637 use crate::widgets::Widget;
1638
1639 #[track_caller]
1648 fn letters(flex: Flex, constraints: &[Constraint], width: u16, expected: &str) {
1649 let area = Rect::new(0, 0, width, 1);
1650 let layout = Layout::default()
1651 .direction(Direction::Horizontal)
1652 .constraints(constraints)
1653 .flex(flex)
1654 .split(area);
1655 let mut buffer = Buffer::empty(area);
1656 for (c, &area) in ('a'..='z').take(constraints.len()).zip(layout.iter()) {
1657 let s = c.to_string().repeat(area.width as usize);
1658 Text::from(s).render(area, &mut buffer);
1659 }
1660 assert_eq!(buffer, Buffer::with_lines([expected]));
1661 }
1662
1663 #[rstest]
1664 #[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(
1699 #[case] flex: Flex,
1700 #[case] width: u16,
1701 #[case] constraints: &[Constraint],
1702 #[case] expected: &str,
1703 ) {
1704 letters(flex, constraints, width, expected);
1705 }
1706
1707 #[rstest]
1708 #[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")]
1741 fn max(
1742 #[case] flex: Flex,
1743 #[case] width: u16,
1744 #[case] constraints: &[Constraint],
1745 #[case] expected: &str,
1746 ) {
1747 letters(flex, constraints, width, expected);
1748 }
1749
1750 #[rstest]
1751 #[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")]
1777 fn min(
1778 #[case] flex: Flex,
1779 #[case] width: u16,
1780 #[case] constraints: &[Constraint],
1781 #[case] expected: &str,
1782 ) {
1783 letters(flex, constraints, width, expected);
1784 }
1785
1786 #[rstest] #[case(Flex::Legacy, 1, &[Percentage(0)], "a")]
1789 #[case(Flex::Legacy, 1, &[Percentage(25)], "a")]
1790 #[case(Flex::Legacy, 1, &[Percentage(50)], "a")]
1791 #[case(Flex::Legacy, 1, &[Percentage(90)], "a")]
1792 #[case(Flex::Legacy, 1, &[Percentage(100)], "a")]
1793 #[case(Flex::Legacy, 1, &[Percentage(200)], "a")]
1794 #[case(Flex::Legacy, 2, &[Percentage(0)], "aa")]
1796 #[case(Flex::Legacy, 2, &[Percentage(10)], "aa")]
1797 #[case(Flex::Legacy, 2, &[Percentage(25)], "aa")]
1798 #[case(Flex::Legacy, 2, &[Percentage(50)], "aa")]
1799 #[case(Flex::Legacy, 2, &[Percentage(66)], "aa")]
1800 #[case(Flex::Legacy, 2, &[Percentage(100)], "aa")]
1801 #[case(Flex::Legacy, 2, &[Percentage(200)], "aa")]
1802 #[case(Flex::Legacy, 10, &[Percentage(0)], "aaaaaaaaaa")]
1804 #[case(Flex::Legacy, 10, &[Percentage(10)], "aaaaaaaaaa")]
1805 #[case(Flex::Legacy, 10, &[Percentage(25)], "aaaaaaaaaa")]
1806 #[case(Flex::Legacy, 10, &[Percentage(50)], "aaaaaaaaaa")]
1807 #[case(Flex::Legacy, 10, &[Percentage(66)], "aaaaaaaaaa")]
1808 #[case(Flex::Legacy, 10, &[Percentage(100)], "aaaaaaaaaa")]
1809 #[case(Flex::Legacy, 10, &[Percentage(200)], "aaaaaaaaaa")]
1810 #[case(Flex::Legacy, 1, &[Percentage(0), Percentage(0)], "b")]
1812 #[case(Flex::Legacy, 1, &[Percentage(0), Percentage(10)], "b")]
1813 #[case(Flex::Legacy, 1, &[Percentage(0), Percentage(50)], "b")]
1814 #[case(Flex::Legacy, 1, &[Percentage(0), Percentage(90)], "b")]
1815 #[case(Flex::Legacy, 1, &[Percentage(0), Percentage(100)], "b")]
1816 #[case(Flex::Legacy, 1, &[Percentage(0), Percentage(200)], "b")]
1817 #[case(Flex::Legacy, 1, &[Percentage(10), Percentage(0)], "b")]
1819 #[case(Flex::Legacy, 1, &[Percentage(10), Percentage(10)], "b")]
1820 #[case(Flex::Legacy, 1, &[Percentage(10), Percentage(50)], "b")]
1821 #[case(Flex::Legacy, 1, &[Percentage(10), Percentage(90)], "b")]
1822 #[case(Flex::Legacy, 1, &[Percentage(10), Percentage(100)], "b")]
1823 #[case(Flex::Legacy, 1, &[Percentage(10), Percentage(200)], "b")]
1824 #[case(Flex::Legacy, 1, &[Percentage(50), Percentage(0)], "a")]
1826 #[case(Flex::Legacy, 1, &[Percentage(50), Percentage(50)], "a")]
1827 #[case(Flex::Legacy, 1, &[Percentage(50), Percentage(100)], "a")]
1828 #[case(Flex::Legacy, 1, &[Percentage(50), Percentage(200)], "a")]
1829 #[case(Flex::Legacy, 1, &[Percentage(90), Percentage(0)], "a")]
1831 #[case(Flex::Legacy, 1, &[Percentage(90), Percentage(50)], "a")]
1832 #[case(Flex::Legacy, 1, &[Percentage(90), Percentage(100)], "a")]
1833 #[case(Flex::Legacy, 1, &[Percentage(90), Percentage(200)], "a")]
1834 #[case(Flex::Legacy, 1, &[Percentage(100), Percentage(0)], "a")]
1836 #[case(Flex::Legacy, 1, &[Percentage(100), Percentage(50)], "a")]
1837 #[case(Flex::Legacy, 1, &[Percentage(100), Percentage(100)], "a")]
1838 #[case(Flex::Legacy, 1, &[Percentage(100), Percentage(200)], "a")]
1839 #[case(Flex::Legacy, 2, &[Percentage(0), Percentage(0)], "bb")]
1841 #[case(Flex::Legacy, 2, &[Percentage(0), Percentage(25)], "bb")]
1842 #[case(Flex::Legacy, 2, &[Percentage(0), Percentage(50)], "bb")]
1843 #[case(Flex::Legacy, 2, &[Percentage(0), Percentage(100)], "bb")]
1844 #[case(Flex::Legacy, 2, &[Percentage(0), Percentage(200)], "bb")]
1845 #[case(Flex::Legacy, 2, &[Percentage(10), Percentage(0)], "bb")]
1847 #[case(Flex::Legacy, 2, &[Percentage(10), Percentage(25)], "bb")]
1848 #[case(Flex::Legacy, 2, &[Percentage(10), Percentage(50)], "bb")]
1849 #[case(Flex::Legacy, 2, &[Percentage(10), Percentage(100)], "bb")]
1850 #[case(Flex::Legacy, 2, &[Percentage(10), Percentage(200)], "bb")]
1851 #[case(Flex::Legacy, 2, &[Percentage(25), Percentage(0)], "ab")]
1853 #[case(Flex::Legacy, 2, &[Percentage(25), Percentage(25)], "ab")]
1854 #[case(Flex::Legacy, 2, &[Percentage(25), Percentage(50)], "ab")]
1855 #[case(Flex::Legacy, 2, &[Percentage(25), Percentage(100)], "ab")]
1856 #[case(Flex::Legacy, 2, &[Percentage(25), Percentage(200)], "ab")]
1857 #[case(Flex::Legacy, 2, &[Percentage(33), Percentage(0)], "ab")]
1859 #[case(Flex::Legacy, 2, &[Percentage(33), Percentage(25)], "ab")]
1860 #[case(Flex::Legacy, 2, &[Percentage(33), Percentage(50)], "ab")]
1861 #[case(Flex::Legacy, 2, &[Percentage(33), Percentage(100)], "ab")]
1862 #[case(Flex::Legacy, 2, &[Percentage(33), Percentage(200)], "ab")]
1863 #[case(Flex::Legacy, 2, &[Percentage(50), Percentage(0)], "ab")]
1865 #[case(Flex::Legacy, 2, &[Percentage(50), Percentage(50)], "ab")]
1866 #[case(Flex::Legacy, 2, &[Percentage(50), Percentage(100)], "ab")]
1867 #[case(Flex::Legacy, 2, &[Percentage(100), Percentage(0)], "aa")]
1870 #[case(Flex::Legacy, 2, &[Percentage(100), Percentage(50)], "aa")]
1871 #[case(Flex::Legacy, 2, &[Percentage(100), Percentage(100)], "aa")]
1872 #[case(Flex::Legacy, 3, &[Percentage(33), Percentage(33)], "abb")]
1874 #[case(Flex::Legacy, 3, &[Percentage(33), Percentage(66)], "abb")]
1875 #[case(Flex::Legacy, 4, &[Percentage(33), Percentage(33)], "abbb")]
1877 #[case(Flex::Legacy, 4, &[Percentage(33), Percentage(66)], "abbb")]
1878 #[case(Flex::Legacy, 10, &[Percentage(0), Percentage(0)], "bbbbbbbbbb" )]
1880 #[case(Flex::Legacy, 10, &[Percentage(0), Percentage(25)], "bbbbbbbbbb" )]
1881 #[case(Flex::Legacy, 10, &[Percentage(0), Percentage(50)], "bbbbbbbbbb" )]
1882 #[case(Flex::Legacy, 10, &[Percentage(0), Percentage(100)], "bbbbbbbbbb" )]
1883 #[case(Flex::Legacy, 10, &[Percentage(0), Percentage(200)], "bbbbbbbbbb" )]
1884 #[case(Flex::Legacy, 10, &[Percentage(10), Percentage(0)], "abbbbbbbbb" )]
1886 #[case(Flex::Legacy, 10, &[Percentage(10), Percentage(25)], "abbbbbbbbb" )]
1887 #[case(Flex::Legacy, 10, &[Percentage(10), Percentage(50)], "abbbbbbbbb" )]
1888 #[case(Flex::Legacy, 10, &[Percentage(10), Percentage(100)], "abbbbbbbbb" )]
1889 #[case(Flex::Legacy, 10, &[Percentage(10), Percentage(200)], "abbbbbbbbb" )]
1890 #[case(Flex::Legacy, 10, &[Percentage(25), Percentage(0)], "aaabbbbbbb" )]
1892 #[case(Flex::Legacy, 10, &[Percentage(25), Percentage(25)], "aaabbbbbbb" )]
1893 #[case(Flex::Legacy, 10, &[Percentage(25), Percentage(50)], "aaabbbbbbb" )]
1894 #[case(Flex::Legacy, 10, &[Percentage(25), Percentage(100)], "aaabbbbbbb" )]
1895 #[case(Flex::Legacy, 10, &[Percentage(25), Percentage(200)], "aaabbbbbbb" )]
1896 #[case(Flex::Legacy, 10, &[Percentage(33), Percentage(0)], "aaabbbbbbb" )]
1898 #[case(Flex::Legacy, 10, &[Percentage(33), Percentage(25)], "aaabbbbbbb" )]
1899 #[case(Flex::Legacy, 10, &[Percentage(33), Percentage(50)], "aaabbbbbbb" )]
1900 #[case(Flex::Legacy, 10, &[Percentage(33), Percentage(100)], "aaabbbbbbb" )]
1901 #[case(Flex::Legacy, 10, &[Percentage(33), Percentage(200)], "aaabbbbbbb" )]
1902 #[case(Flex::Legacy, 10, &[Percentage(50), Percentage(0)], "aaaaabbbbb" )]
1904 #[case(Flex::Legacy, 10, &[Percentage(50), Percentage(50)], "aaaaabbbbb" )]
1905 #[case(Flex::Legacy, 10, &[Percentage(50), Percentage(100)], "aaaaabbbbb" )]
1906 #[case(Flex::Legacy, 10, &[Percentage(100), Percentage(0)], "aaaaaaaaaa" )]
1908 #[case(Flex::Legacy, 10, &[Percentage(100), Percentage(50)], "aaaaaaaaaa" )]
1909 #[case(Flex::Legacy, 10, &[Percentage(100), Percentage(100)], "aaaaaaaaaa" )]
1910 fn percentage(
1911 #[case] flex: Flex,
1912 #[case] width: u16,
1913 #[case] constraints: &[Constraint],
1914 #[case] expected: &str,
1915 ) {
1916 letters(flex, constraints, width, expected);
1917 }
1918
1919 #[rstest]
1920 #[case(Flex::Start, 10, &[Percentage(0), Percentage(0)], " " )]
1921 #[case(Flex::Start, 10, &[Percentage(0), Percentage(25)], "bbb " )]
1922 #[case(Flex::Start, 10, &[Percentage(0), Percentage(50)], "bbbbb " )]
1923 #[case(Flex::Start, 10, &[Percentage(0), Percentage(100)], "bbbbbbbbbb" )]
1924 #[case(Flex::Start, 10, &[Percentage(0), Percentage(200)], "bbbbbbbbbb" )]
1925 #[case(Flex::Start, 10, &[Percentage(10), Percentage(0)], "a " )]
1926 #[case(Flex::Start, 10, &[Percentage(10), Percentage(25)], "abbb " )]
1927 #[case(Flex::Start, 10, &[Percentage(10), Percentage(50)], "abbbbb " )]
1928 #[case(Flex::Start, 10, &[Percentage(10), Percentage(100)], "abbbbbbbbb" )]
1929 #[case(Flex::Start, 10, &[Percentage(10), Percentage(200)], "abbbbbbbbb" )]
1930 #[case(Flex::Start, 10, &[Percentage(25), Percentage(0)], "aaa " )]
1931 #[case(Flex::Start, 10, &[Percentage(25), Percentage(25)], "aaabb " )]
1932 #[case(Flex::Start, 10, &[Percentage(25), Percentage(50)], "aaabbbbb " )]
1933 #[case(Flex::Start, 10, &[Percentage(25), Percentage(100)], "aaabbbbbbb" )]
1934 #[case(Flex::Start, 10, &[Percentage(25), Percentage(200)], "aaabbbbbbb" )]
1935 #[case(Flex::Start, 10, &[Percentage(33), Percentage(0)], "aaa " )]
1936 #[case(Flex::Start, 10, &[Percentage(33), Percentage(25)], "aaabbb " )]
1937 #[case(Flex::Start, 10, &[Percentage(33), Percentage(50)], "aaabbbbb " )]
1938 #[case(Flex::Start, 10, &[Percentage(33), Percentage(100)], "aaabbbbbbb" )]
1939 #[case(Flex::Start, 10, &[Percentage(33), Percentage(200)], "aaabbbbbbb" )]
1940 #[case(Flex::Start, 10, &[Percentage(50), Percentage(0)], "aaaaa " )]
1941 #[case(Flex::Start, 10, &[Percentage(50), Percentage(50)], "aaaaabbbbb" )]
1942 #[case(Flex::Start, 10, &[Percentage(50), Percentage(100)], "aaaaabbbbb" )]
1943 #[case(Flex::Start, 10, &[Percentage(100), Percentage(0)], "aaaaaaaaaa" )]
1944 #[case(Flex::Start, 10, &[Percentage(100), Percentage(50)], "aaaaabbbbb" )]
1945 #[case(Flex::Start, 10, &[Percentage(100), Percentage(100)], "aaaaabbbbb" )]
1946 #[case(Flex::Start, 10, &[Percentage(100), Percentage(200)], "aaaaabbbbb" )]
1947 fn percentage_start(
1948 #[case] flex: Flex,
1949 #[case] width: u16,
1950 #[case] constraints: &[Constraint],
1951 #[case] expected: &str,
1952 ) {
1953 letters(flex, constraints, width, expected);
1954 }
1955
1956 #[rstest]
1957 #[case(Flex::SpaceBetween, 10, &[Percentage(0), Percentage(0)], " " )]
1958 #[case(Flex::SpaceBetween, 10, &[Percentage(0), Percentage(25)], " bb" )]
1959 #[case(Flex::SpaceBetween, 10, &[Percentage(0), Percentage(50)], " bbbbb" )]
1960 #[case(Flex::SpaceBetween, 10, &[Percentage(0), Percentage(100)], "bbbbbbbbbb" )]
1961 #[case(Flex::SpaceBetween, 10, &[Percentage(0), Percentage(200)], "bbbbbbbbbb" )]
1962 #[case(Flex::SpaceBetween, 10, &[Percentage(10), Percentage(0)], "a " )]
1963 #[case(Flex::SpaceBetween, 10, &[Percentage(10), Percentage(25)], "a bb" )]
1964 #[case(Flex::SpaceBetween, 10, &[Percentage(10), Percentage(50)], "a bbbbb" )]
1965 #[case(Flex::SpaceBetween, 10, &[Percentage(10), Percentage(100)], "abbbbbbbbb" )]
1966 #[case(Flex::SpaceBetween, 10, &[Percentage(10), Percentage(200)], "abbbbbbbbb" )]
1967 #[case(Flex::SpaceBetween, 10, &[Percentage(25), Percentage(0)], "aaa " )]
1968 #[case(Flex::SpaceBetween, 10, &[Percentage(25), Percentage(25)], "aaa bb" )]
1969 #[case(Flex::SpaceBetween, 10, &[Percentage(25), Percentage(50)], "aaa bbbbb" )]
1970 #[case(Flex::SpaceBetween, 10, &[Percentage(25), Percentage(100)], "aaabbbbbbb" )]
1971 #[case(Flex::SpaceBetween, 10, &[Percentage(25), Percentage(200)], "aaabbbbbbb" )]
1972 #[case(Flex::SpaceBetween, 10, &[Percentage(33), Percentage(0)], "aaa " )]
1973 #[case(Flex::SpaceBetween, 10, &[Percentage(33), Percentage(25)], "aaa bb" )]
1974 #[case(Flex::SpaceBetween, 10, &[Percentage(33), Percentage(50)], "aaa bbbbb" )]
1975 #[case(Flex::SpaceBetween, 10, &[Percentage(33), Percentage(100)], "aaabbbbbbb" )]
1976 #[case(Flex::SpaceBetween, 10, &[Percentage(33), Percentage(200)], "aaabbbbbbb" )]
1977 #[case(Flex::SpaceBetween, 10, &[Percentage(50), Percentage(0)], "aaaaa " )]
1978 #[case(Flex::SpaceBetween, 10, &[Percentage(50), Percentage(50)], "aaaaabbbbb" )]
1979 #[case(Flex::SpaceBetween, 10, &[Percentage(50), Percentage(100)], "aaaaabbbbb" )]
1980 #[case(Flex::SpaceBetween, 10, &[Percentage(100), Percentage(0)], "aaaaaaaaaa" )]
1981 #[case(Flex::SpaceBetween, 10, &[Percentage(100), Percentage(50)], "aaaaabbbbb" )]
1982 #[case(Flex::SpaceBetween, 10, &[Percentage(100), Percentage(100)], "aaaaabbbbb" )]
1983 #[case(Flex::SpaceBetween, 10, &[Percentage(100), Percentage(200)], "aaaaabbbbb" )]
1984 fn percentage_spacebetween(
1985 #[case] flex: Flex,
1986 #[case] width: u16,
1987 #[case] constraints: &[Constraint],
1988 #[case] expected: &str,
1989 ) {
1990 letters(flex, constraints, width, expected);
1991 }
1992
1993 #[rstest]
1994 #[case(Flex::Legacy, 1, &[Ratio(0, 1)], "a")]
1997 #[case(Flex::Legacy, 1, &[Ratio(1, 4)], "a")]
1998 #[case(Flex::Legacy, 1, &[Ratio(1, 2)], "a")]
1999 #[case(Flex::Legacy, 1, &[Ratio(9, 10)], "a")]
2000 #[case(Flex::Legacy, 1, &[Ratio(1, 1)], "a")]
2001 #[case(Flex::Legacy, 1, &[Ratio(2, 1)], "a")]
2002 #[case(Flex::Legacy, 2, &[Ratio(0, 1)], "aa")]
2003 #[case(Flex::Legacy, 2, &[Ratio(1, 10)], "aa")]
2004 #[case(Flex::Legacy, 2, &[Ratio(1, 4)], "aa")]
2005 #[case(Flex::Legacy, 2, &[Ratio(1, 2)], "aa")]
2006 #[case(Flex::Legacy, 2, &[Ratio(2, 3)], "aa")]
2007 #[case(Flex::Legacy, 2, &[Ratio(1, 1)], "aa")]
2008 #[case(Flex::Legacy, 2, &[Ratio(2, 1)], "aa")]
2009 #[case(Flex::Legacy, 1, &[Ratio(0, 1), Ratio(0, 1)], "b")]
2010 #[case(Flex::Legacy, 1, &[Ratio(0, 1), Ratio(1, 10)], "b")]
2011 #[case(Flex::Legacy, 1, &[Ratio(0, 1), Ratio(1, 2)], "b")]
2012 #[case(Flex::Legacy, 1, &[Ratio(0, 1), Ratio(9, 10)], "b")]
2013 #[case(Flex::Legacy, 1, &[Ratio(0, 1), Ratio(1, 1)], "b")]
2014 #[case(Flex::Legacy, 1, &[Ratio(0, 1), Ratio(2, 1)], "b")]
2015 #[case(Flex::Legacy, 1, &[Ratio(1, 10), Ratio(0, 1)], "b")]
2016 #[case(Flex::Legacy, 1, &[Ratio(1, 10), Ratio(1, 10)], "b")]
2017 #[case(Flex::Legacy, 1, &[Ratio(1, 10), Ratio(1, 2)], "b")]
2018 #[case(Flex::Legacy, 1, &[Ratio(1, 10), Ratio(9, 10)], "b")]
2019 #[case(Flex::Legacy, 1, &[Ratio(1, 10), Ratio(1, 1)], "b")]
2020 #[case(Flex::Legacy, 1, &[Ratio(1, 10), Ratio(2, 1)], "b")]
2021 #[case(Flex::Legacy, 1, &[Ratio(1, 2), Ratio(0, 1)], "a")]
2022 #[case(Flex::Legacy, 1, &[Ratio(1, 2), Ratio(1, 2)], "a")]
2023 #[case(Flex::Legacy, 1, &[Ratio(1, 2), Ratio(1, 1)], "a")]
2024 #[case(Flex::Legacy, 1, &[Ratio(1, 2), Ratio(2, 1)], "a")]
2025 #[case(Flex::Legacy, 1, &[Ratio(9, 10), Ratio(0, 1)], "a")]
2026 #[case(Flex::Legacy, 1, &[Ratio(9, 10), Ratio(1, 2)], "a")]
2027 #[case(Flex::Legacy, 1, &[Ratio(9, 10), Ratio(1, 1)], "a")]
2028 #[case(Flex::Legacy, 1, &[Ratio(9, 10), Ratio(2, 1)], "a")]
2029 #[case(Flex::Legacy, 1, &[Ratio(1, 1), Ratio(0, 1)], "a")]
2030 #[case(Flex::Legacy, 1, &[Ratio(1, 1), Ratio(1, 2)], "a")]
2031 #[case(Flex::Legacy, 1, &[Ratio(1, 1), Ratio(1, 1)], "a")]
2032 #[case(Flex::Legacy, 1, &[Ratio(1, 1), Ratio(2, 1)], "a")]
2033 #[case(Flex::Legacy, 2, &[Ratio(0, 1), Ratio(0, 1)], "bb")]
2034 #[case(Flex::Legacy, 2, &[Ratio(0, 1), Ratio(1, 4)], "bb")]
2035 #[case(Flex::Legacy, 2, &[Ratio(0, 1), Ratio(1, 2)], "bb")]
2036 #[case(Flex::Legacy, 2, &[Ratio(0, 1), Ratio(1, 1)], "bb")]
2037 #[case(Flex::Legacy, 2, &[Ratio(0, 1), Ratio(2, 1)], "bb")]
2038 #[case(Flex::Legacy, 2, &[Ratio(1, 10), Ratio(0, 1)], "bb")]
2039 #[case(Flex::Legacy, 2, &[Ratio(1, 10), Ratio(1, 4)], "bb")]
2040 #[case(Flex::Legacy, 2, &[Ratio(1, 10), Ratio(1, 2)], "bb")]
2041 #[case(Flex::Legacy, 2, &[Ratio(1, 10), Ratio(1, 1)], "bb")]
2042 #[case(Flex::Legacy, 2, &[Ratio(1, 10), Ratio(2, 1)], "bb")]
2043 #[case(Flex::Legacy, 2, &[Ratio(1, 4), Ratio(0, 1)], "ab")]
2044 #[case(Flex::Legacy, 2, &[Ratio(1, 4), Ratio(1, 4)], "ab")]
2045 #[case(Flex::Legacy, 2, &[Ratio(1, 4), Ratio(1, 2)], "ab")]
2046 #[case(Flex::Legacy, 2, &[Ratio(1, 4), Ratio(1, 1)], "ab")]
2047 #[case(Flex::Legacy, 2, &[Ratio(1, 4), Ratio(2, 1)], "ab")]
2048 #[case(Flex::Legacy, 2, &[Ratio(1, 3), Ratio(0, 1)], "ab")]
2049 #[case(Flex::Legacy, 2, &[Ratio(1, 3), Ratio(1, 4)], "ab")]
2050 #[case(Flex::Legacy, 2, &[Ratio(1, 3), Ratio(1, 2)], "ab")]
2051 #[case(Flex::Legacy, 2, &[Ratio(1, 3), Ratio(1, 1)], "ab")]
2052 #[case(Flex::Legacy, 2, &[Ratio(1, 3), Ratio(2, 1)], "ab")]
2053 #[case(Flex::Legacy, 2, &[Ratio(1, 2), Ratio(0, 1)], "ab")]
2054 #[case(Flex::Legacy, 2, &[Ratio(1, 2), Ratio(1, 2)], "ab")]
2055 #[case(Flex::Legacy, 2, &[Ratio(1, 2), Ratio(1, 1)], "ab")]
2056 #[case(Flex::Legacy, 2, &[Ratio(1, 1), Ratio(0, 1)], "aa")]
2057 #[case(Flex::Legacy, 2, &[Ratio(1, 1), Ratio(1, 2)], "aa")]
2058 #[case(Flex::Legacy, 2, &[Ratio(1, 1), Ratio(1, 1)], "aa")]
2059 #[case(Flex::Legacy, 3, &[Ratio(1, 3), Ratio(1, 3)], "abb")]
2060 #[case(Flex::Legacy, 3, &[Ratio(1, 3), Ratio(2,3)], "abb")]
2061 #[case(Flex::Legacy, 10, &[Ratio(0, 1), Ratio(0, 1)], "bbbbbbbbbb" )]
2062 #[case(Flex::Legacy, 10, &[Ratio(0, 1), Ratio(1, 4)], "bbbbbbbbbb" )]
2063 #[case(Flex::Legacy, 10, &[Ratio(0, 1), Ratio(1, 2)], "bbbbbbbbbb" )]
2064 #[case(Flex::Legacy, 10, &[Ratio(0, 1), Ratio(1, 1)], "bbbbbbbbbb" )]
2065 #[case(Flex::Legacy, 10, &[Ratio(0, 1), Ratio(2, 1)], "bbbbbbbbbb" )]
2066 #[case(Flex::Legacy, 10, &[Ratio(1, 10), Ratio(0, 1)], "abbbbbbbbb" )]
2067 #[case(Flex::Legacy, 10, &[Ratio(1, 10), Ratio(1, 4)], "abbbbbbbbb" )]
2068 #[case(Flex::Legacy, 10, &[Ratio(1, 10), Ratio(1, 2)], "abbbbbbbbb" )]
2069 #[case(Flex::Legacy, 10, &[Ratio(1, 10), Ratio(1, 1)], "abbbbbbbbb" )]
2070 #[case(Flex::Legacy, 10, &[Ratio(1, 10), Ratio(2, 1)], "abbbbbbbbb" )]
2071 #[case(Flex::Legacy, 10, &[Ratio(1, 4), Ratio(0, 1)], "aaabbbbbbb" )]
2072 #[case(Flex::Legacy, 10, &[Ratio(1, 4), Ratio(1, 4)], "aaabbbbbbb" )]
2073 #[case(Flex::Legacy, 10, &[Ratio(1, 4), Ratio(1, 2)], "aaabbbbbbb" )]
2074 #[case(Flex::Legacy, 10, &[Ratio(1, 4), Ratio(1, 1)], "aaabbbbbbb" )]
2075 #[case(Flex::Legacy, 10, &[Ratio(1, 4), Ratio(2, 1)], "aaabbbbbbb" )]
2076 #[case(Flex::Legacy, 10, &[Ratio(1, 3), Ratio(0, 1)], "aaabbbbbbb" )]
2077 #[case(Flex::Legacy, 10, &[Ratio(1, 3), Ratio(1, 4)], "aaabbbbbbb" )]
2078 #[case(Flex::Legacy, 10, &[Ratio(1, 3), Ratio(1, 2)], "aaabbbbbbb" )]
2079 #[case(Flex::Legacy, 10, &[Ratio(1, 3), Ratio(1, 1)], "aaabbbbbbb" )]
2080 #[case(Flex::Legacy, 10, &[Ratio(1, 3), Ratio(2, 1)], "aaabbbbbbb" )]
2081 #[case(Flex::Legacy, 10, &[Ratio(1, 2), Ratio(0, 1)], "aaaaabbbbb" )]
2082 #[case(Flex::Legacy, 10, &[Ratio(1, 2), Ratio(1, 2)], "aaaaabbbbb" )]
2083 #[case(Flex::Legacy, 10, &[Ratio(1, 2), Ratio(1, 1)], "aaaaabbbbb" )]
2084 #[case(Flex::Legacy, 10, &[Ratio(1, 1), Ratio(0, 1)], "aaaaaaaaaa" )]
2085 #[case(Flex::Legacy, 10, &[Ratio(1, 1), Ratio(1, 2)], "aaaaaaaaaa" )]
2086 #[case(Flex::Legacy, 10, &[Ratio(1, 1), Ratio(1, 1)], "aaaaaaaaaa" )]
2087 fn ratio(
2088 #[case] flex: Flex,
2089 #[case] width: u16,
2090 #[case] constraints: &[Constraint],
2091 #[case] expected: &str,
2092 ) {
2093 letters(flex, constraints, width, expected);
2094 }
2095
2096 #[rstest]
2097 #[case(Flex::Start, 10, &[Ratio(0, 1), Ratio(0, 1)], " " )]
2098 #[case(Flex::Start, 10, &[Ratio(0, 1), Ratio(1, 4)], "bbb " )]
2099 #[case(Flex::Start, 10, &[Ratio(0, 1), Ratio(1, 2)], "bbbbb " )]
2100 #[case(Flex::Start, 10, &[Ratio(0, 1), Ratio(1, 1)], "bbbbbbbbbb" )]
2101 #[case(Flex::Start, 10, &[Ratio(0, 1), Ratio(2, 1)], "bbbbbbbbbb" )]
2102 #[case(Flex::Start, 10, &[Ratio(1, 10), Ratio(0, 1)], "a " )]
2103 #[case(Flex::Start, 10, &[Ratio(1, 10), Ratio(1, 4)], "abbb " )]
2104 #[case(Flex::Start, 10, &[Ratio(1, 10), Ratio(1, 2)], "abbbbb " )]
2105 #[case(Flex::Start, 10, &[Ratio(1, 10), Ratio(1, 1)], "abbbbbbbbb" )]
2106 #[case(Flex::Start, 10, &[Ratio(1, 10), Ratio(2, 1)], "abbbbbbbbb" )]
2107 #[case(Flex::Start, 10, &[Ratio(1, 4), Ratio(0, 1)], "aaa " )]
2108 #[case(Flex::Start, 10, &[Ratio(1, 4), Ratio(1, 4)], "aaabb " )]
2109 #[case(Flex::Start, 10, &[Ratio(1, 4), Ratio(1, 2)], "aaabbbbb " )]
2110 #[case(Flex::Start, 10, &[Ratio(1, 4), Ratio(1, 1)], "aaabbbbbbb" )]
2111 #[case(Flex::Start, 10, &[Ratio(1, 4), Ratio(2, 1)], "aaabbbbbbb" )]
2112 #[case(Flex::Start, 10, &[Ratio(1, 3), Ratio(0, 1)], "aaa " )]
2113 #[case(Flex::Start, 10, &[Ratio(1, 3), Ratio(1, 4)], "aaabbb " )]
2114 #[case(Flex::Start, 10, &[Ratio(1, 3), Ratio(1, 2)], "aaabbbbb " )]
2115 #[case(Flex::Start, 10, &[Ratio(1, 3), Ratio(1, 1)], "aaabbbbbbb" )]
2116 #[case(Flex::Start, 10, &[Ratio(1, 3), Ratio(2, 1)], "aaabbbbbbb" )]
2117 #[case(Flex::Start, 10, &[Ratio(1, 2), Ratio(0, 1)], "aaaaa " )]
2118 #[case(Flex::Start, 10, &[Ratio(1, 2), Ratio(1, 2)], "aaaaabbbbb" )]
2119 #[case(Flex::Start, 10, &[Ratio(1, 2), Ratio(1, 1)], "aaaaabbbbb" )]
2120 #[case(Flex::Start, 10, &[Ratio(1, 1), Ratio(0, 1)], "aaaaaaaaaa" )]
2121 #[case(Flex::Start, 10, &[Ratio(1, 1), Ratio(1, 2)], "aaaaabbbbb" )]
2122 #[case(Flex::Start, 10, &[Ratio(1, 1), Ratio(1, 1)], "aaaaabbbbb" )]
2123 #[case(Flex::Start, 10, &[Ratio(1, 1), Ratio(2, 1)], "aaaaabbbbb" )]
2124 fn ratio_start(
2125 #[case] flex: Flex,
2126 #[case] width: u16,
2127 #[case] constraints: &[Constraint],
2128 #[case] expected: &str,
2129 ) {
2130 letters(flex, constraints, width, expected);
2131 }
2132
2133 #[rstest]
2134 #[case(Flex::SpaceBetween, 10, &[Ratio(0, 1), Ratio(0, 1)], " " )]
2135 #[case(Flex::SpaceBetween, 10, &[Ratio(0, 1), Ratio(1, 4)], " bb" )]
2136 #[case(Flex::SpaceBetween, 10, &[Ratio(0, 1), Ratio(1, 2)], " bbbbb" )]
2137 #[case(Flex::SpaceBetween, 10, &[Ratio(0, 1), Ratio(1, 1)], "bbbbbbbbbb" )]
2138 #[case(Flex::SpaceBetween, 10, &[Ratio(0, 1), Ratio(2, 1)], "bbbbbbbbbb" )]
2139 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 10), Ratio(0, 1)], "a " )]
2140 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 10), Ratio(1, 4)], "a bb" )]
2141 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 10), Ratio(1, 2)], "a bbbbb" )]
2142 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 10), Ratio(1, 1)], "abbbbbbbbb" )]
2143 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 10), Ratio(2, 1)], "abbbbbbbbb" )]
2144 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 4), Ratio(0, 1)], "aaa " )]
2145 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 4), Ratio(1, 4)], "aaa bb" )]
2146 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 4), Ratio(1, 2)], "aaa bbbbb" )]
2147 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 4), Ratio(1, 1)], "aaabbbbbbb" )]
2148 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 4), Ratio(2, 1)], "aaabbbbbbb" )]
2149 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 3), Ratio(0, 1)], "aaa " )]
2150 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 3), Ratio(1, 4)], "aaa bb" )]
2151 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 3), Ratio(1, 2)], "aaa bbbbb" )]
2152 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 3), Ratio(1, 1)], "aaabbbbbbb" )]
2153 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 3), Ratio(2, 1)], "aaabbbbbbb" )]
2154 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 2), Ratio(0, 1)], "aaaaa " )]
2155 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 2), Ratio(1, 2)], "aaaaabbbbb" )]
2156 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 2), Ratio(1, 1)], "aaaaabbbbb" )]
2157 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 1), Ratio(0, 1)], "aaaaaaaaaa" )]
2158 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 1), Ratio(1, 2)], "aaaaabbbbb" )]
2159 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 1), Ratio(1, 1)], "aaaaabbbbb" )]
2160 #[case(Flex::SpaceBetween, 10, &[Ratio(1, 1), Ratio(2, 1)], "aaaaabbbbb" )]
2161 fn ratio_spacebetween(
2162 #[case] flex: Flex,
2163 #[case] width: u16,
2164 #[case] constraints: &[Constraint],
2165 #[case] expected: &str,
2166 ) {
2167 letters(flex, constraints, width, expected);
2168 }
2169
2170 #[test]
2171 fn vertical_split_by_height() {
2172 let target = Rect {
2173 x: 2,
2174 y: 2,
2175 width: 10,
2176 height: 10,
2177 };
2178
2179 let chunks = Layout::default()
2180 .direction(Direction::Vertical)
2181 .constraints([
2182 Constraint::Percentage(10),
2183 Constraint::Max(5),
2184 Constraint::Min(1),
2185 ])
2186 .split(target);
2187
2188 assert_eq!(chunks.iter().map(|r| r.height).sum::<u16>(), target.height);
2189 chunks.windows(2).for_each(|w| assert!(w[0].y <= w[1].y));
2190 }
2191
2192 #[test]
2193 fn edge_cases() {
2194 let layout = Layout::default()
2196 .constraints([
2197 Constraint::Percentage(50),
2198 Constraint::Percentage(50),
2199 Constraint::Min(0),
2200 ])
2201 .split(Rect::new(0, 0, 1, 1));
2202 assert_eq!(
2203 layout[..],
2204 [
2205 Rect::new(0, 0, 1, 1),
2206 Rect::new(0, 1, 1, 0),
2207 Rect::new(0, 1, 1, 0)
2208 ]
2209 );
2210
2211 let layout = Layout::default()
2213 .constraints([
2214 Constraint::Max(1),
2215 Constraint::Percentage(99),
2216 Constraint::Min(0),
2217 ])
2218 .split(Rect::new(0, 0, 1, 1));
2219 assert_eq!(
2220 layout[..],
2221 [
2222 Rect::new(0, 0, 1, 0),
2223 Rect::new(0, 0, 1, 1),
2224 Rect::new(0, 1, 1, 0)
2225 ]
2226 );
2227
2228 let layout = Layout::default()
2232 .constraints([Min(1), Length(0), Min(1)])
2233 .direction(Direction::Horizontal)
2234 .split(Rect::new(0, 0, 1, 1));
2235 assert_eq!(
2236 layout[..],
2237 [
2238 Rect::new(0, 0, 1, 1),
2239 Rect::new(1, 0, 0, 1),
2240 Rect::new(1, 0, 0, 1),
2241 ]
2242 );
2243
2244 let layout = Layout::default()
2246 .constraints([Length(3), Min(4), Length(1), Min(4)])
2247 .direction(Direction::Horizontal)
2248 .split(Rect::new(0, 0, 7, 1));
2249 assert_eq!(
2250 layout[..],
2251 [
2252 Rect::new(0, 0, 0, 1),
2253 Rect::new(0, 0, 4, 1),
2254 Rect::new(4, 0, 0, 1),
2255 Rect::new(4, 0, 3, 1),
2256 ]
2257 );
2258 }
2259
2260 #[rstest]
2261 #[case::len_min1(vec![Length(25), Min(100)], vec![0..0, 0..100])]
2262 #[case::len_min2(vec![Length(25), Min(0)], vec![0..25, 25..100])]
2263 #[case::len_max1(vec![Length(25), Max(0)], vec![0..100, 100..100])]
2264 #[case::len_max2(vec![Length(25), Max(100)], vec![0..25, 25..100])]
2265 #[case::len_perc(vec![Length(25), Percentage(25)], vec![0..25, 25..100])]
2266 #[case::perc_len(vec![Percentage(25), Length(25)], vec![0..75, 75..100])]
2267 #[case::len_ratio(vec![Length(25), Ratio(1, 4)], vec![0..25, 25..100])]
2268 #[case::ratio_len(vec![Ratio(1, 4), Length(25)], vec![0..75, 75..100])]
2269 #[case::len_len(vec![Length(25), Length(25)], vec![0..25, 25..100])]
2270 #[case::len1(vec![Length(25), Length(25), Length(25)], vec![0..25, 25..50, 50..100])]
2271 #[case::len2(vec![Length(15), Length(35), Length(25)], vec![0..15, 15..50, 50..100])]
2272 #[case::len3(vec![Length(25), Length(25), Length(25)], vec![0..25, 25..50, 50..100])]
2273 fn constraint_length(
2274 #[case] constraints: Vec<Constraint>,
2275 #[case] expected: Vec<Range<u16>>,
2276 ) {
2277 let rect = Rect::new(0, 0, 100, 1);
2278 let ranges = Layout::horizontal(constraints)
2279 .flex(Flex::Legacy)
2280 .split(rect)
2281 .iter()
2282 .map(|r| r.left()..r.right())
2283 .collect_vec();
2284 assert_eq!(ranges, expected);
2285 }
2286
2287 #[rstest]
2288 #[case(7, vec![Length(4), Length(4)], vec![0..3, 4..7])]
2289 #[case(4, vec![Length(4), Length(4)], vec![0..2, 3..4])]
2290 fn table_length(
2291 #[case] width: u16,
2292 #[case] constraints: Vec<Constraint>,
2293 #[case] expected: Vec<Range<u16>>,
2294 ) {
2295 let rect = Rect::new(0, 0, width, 1);
2296 let ranges = Layout::horizontal(constraints)
2297 .spacing(1)
2298 .flex(Flex::Start)
2299 .split(rect)
2300 .iter()
2301 .map(|r| r.left()..r.right())
2302 .collect::<Vec<Range<u16>>>();
2303 assert_eq!(ranges, expected);
2304 }
2305
2306 #[rstest]
2307 #[case::min_len_max(vec![Min(25), Length(25), Max(25)], vec![0..50, 50..75, 75..100])]
2308 #[case::max_len_min(vec![Max(25), Length(25), Min(25)], vec![0..25, 25..50, 50..100])]
2309 #[case::len_len_len(vec![Length(33), Length(33), Length(33)], vec![0..33, 33..66, 66..100])]
2310 #[case::len_len_len_25(vec![Length(25), Length(25), Length(25)], vec![0..25, 25..50, 50..100])]
2311 #[case::perc_len_ratio(vec![Percentage(25), Length(25), Ratio(1, 4)], vec![0..25, 25..50, 50..100])]
2312 #[case::len_ratio_perc(vec![Length(25), Ratio(1, 4), Percentage(25)], vec![0..25, 25..75, 75..100])]
2313 #[case::ratio_len_perc(vec![Ratio(1, 4), Length(25), Percentage(25)], vec![0..50, 50..75, 75..100])]
2314 #[case::ratio_perc_len(vec![Ratio(1, 4), Percentage(25), Length(25)], vec![0..50, 50..75, 75..100])]
2315 #[case::len_len_min(vec![Length(100), Length(1), Min(20)], vec![0..80, 80..80, 80..100])]
2316 #[case::min_len_len(vec![Min(20), Length(1), Length(100)], vec![0..20, 20..21, 21..100])]
2317 #[case::fill_len_fill(vec![Fill(1), Length(10), Fill(1)], vec![0..45, 45..55, 55..100])]
2318 #[case::fill_len_fill_2(vec![Fill(1), Length(10), Fill(2)], vec![0..30, 30..40, 40..100])]
2319 #[case::fill_len_fill_4(vec![Fill(1), Length(10), Fill(4)], vec![0..18, 18..28, 28..100])]
2320 #[case::fill_len_fill_5(vec![Fill(1), Length(10), Fill(5)], vec![0..15, 15..25, 25..100])]
2321 #[case::len_len_len_25(vec![Length(25), Length(25), Length(25)], vec![0..25, 25..50, 50..100])]
2322 #[case::unstable_test(vec![Length(25), Length(25), Length(25)], vec![0..25, 25..50, 50..100])]
2323 fn length_is_higher_priority(
2324 #[case] constraints: Vec<Constraint>,
2325 #[case] expected: Vec<Range<u16>>,
2326 ) {
2327 let rect = Rect::new(0, 0, 100, 1);
2328 let ranges = Layout::horizontal(constraints)
2329 .flex(Flex::Legacy)
2330 .split(rect)
2331 .iter()
2332 .map(|r| r.left()..r.right())
2333 .collect_vec();
2334 assert_eq!(ranges, expected);
2335 }
2336
2337 #[rstest]
2338 #[case::min_len_max(vec![Min(25), Length(25), Max(25)], vec![50, 25, 25])]
2339 #[case::max_len_min(vec![Max(25), Length(25), Min(25)], vec![25, 25, 50])]
2340 #[case::len_len_len1(vec![Length(33), Length(33), Length(33)], vec![33, 33, 33])]
2341 #[case::len_len_len2(vec![Length(25), Length(25), Length(25)], vec![25, 25, 25])]
2342 #[case::perc_len_ratio(vec![Percentage(25), Length(25), Ratio(1, 4)], vec![25, 25, 25])]
2343 #[case::len_ratio_perc(vec![Length(25), Ratio(1, 4), Percentage(25)], vec![25, 25, 25])]
2344 #[case::ratio_len_perc(vec![Ratio(1, 4), Length(25), Percentage(25)], vec![25, 25, 25])]
2345 #[case::ratio_perc_len(vec![Ratio(1, 4), Percentage(25), Length(25)], vec![25, 25, 25])]
2346 #[case::len_len_min(vec![Length(100), Length(1), Min(20)], vec![79, 1, 20])]
2347 #[case::min_len_len(vec![Min(20), Length(1), Length(100)], vec![20, 1, 79])]
2348 #[case::fill_len_fill1(vec![Fill(1), Length(10), Fill(1)], vec![45, 10, 45])]
2349 #[case::fill_len_fill2(vec![Fill(1), Length(10), Fill(2)], vec![30, 10, 60])]
2350 #[case::fill_len_fill4(vec![Fill(1), Length(10), Fill(4)], vec![18, 10, 72])]
2351 #[case::fill_len_fill5(vec![Fill(1), Length(10), Fill(5)], vec![15, 10, 75])]
2352 #[case::len_len_len3(vec![Length(25), Length(25), Length(25)], vec![25, 25, 25])]
2353 fn length_is_higher_priority_in_flex(
2354 #[case] constraints: Vec<Constraint>,
2355 #[case] expected: Vec<u16>,
2356 ) {
2357 let rect = Rect::new(0, 0, 100, 1);
2358 for flex in [
2359 Flex::Start,
2360 Flex::End,
2361 Flex::Center,
2362 Flex::SpaceAround,
2363 Flex::SpaceEvenly,
2364 Flex::SpaceBetween,
2365 ] {
2366 let widths = Layout::horizontal(&constraints)
2367 .flex(flex)
2368 .split(rect)
2369 .iter()
2370 .map(|r| r.width)
2371 .collect_vec();
2372 assert_eq!(widths, expected);
2373 }
2374 }
2375
2376 #[rstest]
2377 #[case::fill_len_fill(vec![Fill(1), Length(10), Fill(2)], vec![0..13, 13..23, 23..50])]
2378 #[case::len_fill_fill(vec![Length(10), Fill(2), Fill(1)], vec![0..10, 10..37, 37..50])] fn fixed_with_50_width(
2380 #[case] constraints: Vec<Constraint>,
2381 #[case] expected: Vec<Range<u16>>,
2382 ) {
2383 let rect = Rect::new(0, 0, 50, 1);
2384 let ranges = Layout::horizontal(constraints)
2385 .flex(Flex::Legacy)
2386 .split(rect)
2387 .iter()
2388 .map(|r| r.left()..r.right())
2389 .collect_vec();
2390 assert_eq!(ranges, expected);
2391 }
2392
2393 #[rstest]
2394 #[case::same_fill(vec![Fill(1), Fill(2), Fill(1), Fill(1)], vec![0..20, 20..60, 60..80, 80..100])]
2395 #[case::inc_fill(vec![Fill(1), Fill(2), Fill(3), Fill(4)], vec![0..10, 10..30, 30..60, 60..100])]
2396 #[case::dec_fill(vec![Fill(4), Fill(3), Fill(2), Fill(1)], vec![0..40, 40..70, 70..90, 90..100])]
2397 #[case::rand_fill1(vec![Fill(1), Fill(3), Fill(2), Fill(4)], vec![0..10, 10..40, 40..60, 60..100])]
2398 #[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])]
2399 #[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])]
2400 #[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])]
2401 #[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])]
2402 #[case::zero_fill1(vec![Fill(0), Fill(1), Fill(0)], vec![0..0, 0..100, 100..100])]
2403 #[case::zero_fill2(vec![Fill(0), Length(1), Fill(0)], vec![0..50, 50..51, 51..100])]
2404 #[case::zero_fill3(vec![Fill(0), Percentage(1), Fill(0)], vec![0..50, 50..51, 51..100])]
2405 #[case::zero_fill4(vec![Fill(0), Min(1), Fill(0)], vec![0..50, 50..51, 51..100])]
2406 #[case::zero_fill5(vec![Fill(0), Max(1), Fill(0)], vec![0..50, 50..51, 51..100])]
2407 #[case::zero_fill6(vec![Fill(0), Fill(2), Fill(0), Fill(1)], vec![0..0, 0..67, 67..67, 67..100])]
2408 #[case::space_fill1(vec![Fill(0), Fill(2), Percentage(20)], vec![0..0, 0..80, 80..100])]
2409 #[case::space_fill2(vec![Fill(0), Fill(0), Percentage(20)], vec![0..40, 40..80, 80..100])]
2410 #[case::space_fill3(vec![Fill(0), Ratio(1, 5)], vec![0..80, 80..100])]
2411 #[case::space_fill4(vec![Fill(0), Fill(u16::MAX)], vec![0..0, 0..100])]
2412 #[case::space_fill5(vec![Fill(u16::MAX), Fill(0)], vec![0..100, 100..100])]
2413 #[case::space_fill6(vec![Fill(0), Percentage(20)], vec![0..80, 80..100])]
2414 #[case::space_fill7(vec![Fill(1), Percentage(20)], vec![0..80, 80..100])]
2415 #[case::space_fill8(vec![Fill(u16::MAX), Percentage(20)], vec![0..80, 80..100])]
2416 #[case::space_fill9(vec![Fill(u16::MAX), Fill(0), Percentage(20)], vec![0..80, 80..80, 80..100])]
2417 #[case::space_fill10(vec![Fill(0), Length(20)], vec![0..80, 80..100])]
2418 #[case::space_fill11(vec![Fill(0), Min(20)], vec![0..80, 80..100])]
2419 #[case::space_fill12(vec![Fill(0), Max(20)], vec![0..80, 80..100])]
2420 #[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])]
2421 #[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])]
2422 #[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])]
2423 #[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])]
2424 #[case::fill_collapse5(vec![Fill(1), Fill(1), Fill(1), Ratio(1, 1)], vec![0..0, 0..0, 0..0, 0..100])]
2425 #[case::fill_collapse6(vec![Fill(1), Fill(1), Fill(1), Percentage(100)], vec![0..0, 0..0, 0..0, 0..100])]
2426 fn fill(#[case] constraints: Vec<Constraint>, #[case] expected: Vec<Range<u16>>) {
2427 let rect = Rect::new(0, 0, 100, 1);
2428 let ranges = Layout::horizontal(constraints)
2429 .flex(Flex::Legacy)
2430 .split(rect)
2431 .iter()
2432 .map(|r| r.left()..r.right())
2433 .collect_vec();
2434 assert_eq!(ranges, expected);
2435 }
2436
2437 #[rstest]
2438 #[case::min_percentage(vec![Min(0), Percentage(20)], vec![0..80, 80..100])]
2439 #[case::max_percentage(vec![Max(0), Percentage(20)], vec![0..0, 0..100])]
2440 fn percentage_parameterized(
2441 #[case] constraints: Vec<Constraint>,
2442 #[case] expected: Vec<Range<u16>>,
2443 ) {
2444 let rect = Rect::new(0, 0, 100, 1);
2445 let ranges = Layout::horizontal(constraints)
2446 .flex(Flex::Legacy)
2447 .split(rect)
2448 .iter()
2449 .map(|r| r.left()..r.right())
2450 .collect_vec();
2451 assert_eq!(ranges, expected);
2452 }
2453
2454 #[rstest]
2455 #[case::max_min(vec![Max(100), Min(0)], vec![0..100, 100..100])]
2456 #[case::min_max(vec![Min(0), Max(100)], vec![0..0, 0..100])]
2457 #[case::length_min(vec![Length(u16::MAX), Min(10)], vec![0..90, 90..100])]
2458 #[case::min_length(vec![Min(10), Length(u16::MAX)], vec![0..10, 10..100])]
2459 #[case::length_max(vec![Length(0), Max(10)], vec![0..90, 90..100])]
2460 #[case::max_length(vec![Max(10), Length(0)], vec![0..10, 10..100])]
2461 fn min_max(#[case] constraints: Vec<Constraint>, #[case] expected: Vec<Range<u16>>) {
2462 let rect = Rect::new(0, 0, 100, 1);
2463 let ranges = Layout::horizontal(constraints)
2464 .flex(Flex::Legacy)
2465 .split(rect)
2466 .iter()
2467 .map(|r| r.left()..r.right())
2468 .collect_vec();
2469 assert_eq!(ranges, expected);
2470 }
2471
2472 #[rstest]
2473 #[case::length_legacy(vec![Length(50)], vec![0..100], Flex::Legacy)]
2474 #[case::length_start(vec![Length(50)], vec![0..50], Flex::Start)]
2475 #[case::length_end(vec![Length(50)], vec![50..100], Flex::End)]
2476 #[case::length_center(vec![Length(50)], vec![25..75], Flex::Center)]
2477 #[case::ratio_legacy(vec![Ratio(1, 2)], vec![0..100], Flex::Legacy)]
2478 #[case::ratio_start(vec![Ratio(1, 2)], vec![0..50], Flex::Start)]
2479 #[case::ratio_end(vec![Ratio(1, 2)], vec![50..100], Flex::End)]
2480 #[case::ratio_center(vec![Ratio(1, 2)], vec![25..75], Flex::Center)]
2481 #[case::percent_legacy(vec![Percentage(50)], vec![0..100], Flex::Legacy)]
2482 #[case::percent_start(vec![Percentage(50)], vec![0..50], Flex::Start)]
2483 #[case::percent_end(vec![Percentage(50)], vec![50..100], Flex::End)]
2484 #[case::percent_center(vec![Percentage(50)], vec![25..75], Flex::Center)]
2485 #[case::min_legacy(vec![Min(50)], vec![0..100], Flex::Legacy)]
2486 #[case::min_start(vec![Min(50)], vec![0..100], Flex::Start)]
2487 #[case::min_end(vec![Min(50)], vec![0..100], Flex::End)]
2488 #[case::min_center(vec![Min(50)], vec![0..100], Flex::Center)]
2489 #[case::max_legacy(vec![Max(50)], vec![0..100], Flex::Legacy)]
2490 #[case::max_start(vec![Max(50)], vec![0..50], Flex::Start)]
2491 #[case::max_end(vec![Max(50)], vec![50..100], Flex::End)]
2492 #[case::max_center(vec![Max(50)], vec![25..75], Flex::Center)]
2493 #[case::spacebetween_becomes_stretch1(vec![Min(1)], vec![0..100], Flex::SpaceBetween)]
2494 #[case::spacebetween_becomes_stretch2(vec![Max(20)], vec![0..100], Flex::SpaceBetween)]
2495 #[case::spacebetween_becomes_stretch3(vec![Length(20)], vec![0..100], Flex::SpaceBetween)]
2496 #[case::length_legacy2(vec![Length(25), Length(25)], vec![0..25, 25..100], Flex::Legacy)]
2497 #[case::length_start2(vec![Length(25), Length(25)], vec![0..25, 25..50], Flex::Start)]
2498 #[case::length_center2(vec![Length(25), Length(25)], vec![25..50, 50..75], Flex::Center)]
2499 #[case::length_end2(vec![Length(25), Length(25)], vec![50..75, 75..100], Flex::End)]
2500 #[case::length_spacebetween(vec![Length(25), Length(25)], vec![0..25, 75..100], Flex::SpaceBetween)]
2501 #[case::length_spaceevenly(vec![Length(25), Length(25)], vec![17..42, 58..83], Flex::SpaceEvenly)]
2502 #[case::length_spacearound(vec![Length(25), Length(25)], vec![13..38, 63..88], Flex::SpaceAround)]
2503 #[case::percentage_legacy(vec![Percentage(25), Percentage(25)], vec![0..25, 25..100], Flex::Legacy)]
2504 #[case::percentage_start(vec![Percentage(25), Percentage(25)], vec![0..25, 25..50], Flex::Start)]
2505 #[case::percentage_center(vec![Percentage(25), Percentage(25)], vec![25..50, 50..75], Flex::Center)]
2506 #[case::percentage_end(vec![Percentage(25), Percentage(25)], vec![50..75, 75..100], Flex::End)]
2507 #[case::percentage_spacebetween(vec![Percentage(25), Percentage(25)], vec![0..25, 75..100], Flex::SpaceBetween)]
2508 #[case::percentage_spaceevenly(vec![Percentage(25), Percentage(25)], vec![17..42, 58..83], Flex::SpaceEvenly)]
2509 #[case::percentage_spacearound(vec![Percentage(25), Percentage(25)], vec![13..38, 63..88], Flex::SpaceAround)]
2510 #[case::min_legacy2(vec![Min(25), Min(25)], vec![0..25, 25..100], Flex::Legacy)]
2511 #[case::min_start2(vec![Min(25), Min(25)], vec![0..50, 50..100], Flex::Start)]
2512 #[case::min_center2(vec![Min(25), Min(25)], vec![0..50, 50..100], Flex::Center)]
2513 #[case::min_end2(vec![Min(25), Min(25)], vec![0..50, 50..100], Flex::End)]
2514 #[case::min_spacebetween(vec![Min(25), Min(25)], vec![0..50, 50..100], Flex::SpaceBetween)]
2515 #[case::min_spaceevenly(vec![Min(25), Min(25)], vec![0..50, 50..100], Flex::SpaceEvenly)]
2516 #[case::min_spacearound(vec![Min(25), Min(25)], vec![0..50, 50..100], Flex::SpaceAround)]
2517 #[case::max_legacy2(vec![Max(25), Max(25)], vec![0..25, 25..100], Flex::Legacy)]
2518 #[case::max_start2(vec![Max(25), Max(25)], vec![0..25, 25..50], Flex::Start)]
2519 #[case::max_center2(vec![Max(25), Max(25)], vec![25..50, 50..75], Flex::Center)]
2520 #[case::max_end2(vec![Max(25), Max(25)], vec![50..75, 75..100], Flex::End)]
2521 #[case::max_spacebetween(vec![Max(25), Max(25)], vec![0..25, 75..100], Flex::SpaceBetween)]
2522 #[case::max_spaceevenly(vec![Max(25), Max(25)], vec![17..42, 58..83], Flex::SpaceEvenly)]
2523 #[case::max_spacearound(vec![Max(25), Max(25)], vec![13..38, 63..88], Flex::SpaceAround)]
2524 #[case::length_spaced_around(vec![Length(25), Length(25), Length(25)], vec![0..25, 38..63, 75..100], Flex::SpaceBetween)]
2525 #[case::one_segment_legacy(vec![Length(50)], vec![0..100], Flex::Legacy)]
2526 #[case::one_segment_start(vec![Length(50)], vec![0..50], Flex::Start)]
2527 #[case::one_segment_end(vec![Length(50)], vec![50..100], Flex::End)]
2528 #[case::one_segment_center(vec![Length(50)], vec![25..75], Flex::Center)]
2529 #[case::one_segment_spacebetween(vec![Length(50)], vec![0..100], Flex::SpaceBetween)]
2530 #[case::one_segment_spaceevenly(vec![Length(50)], vec![25..75], Flex::SpaceEvenly)]
2531 #[case::one_segment_spacearound(vec![Length(50)], vec![25..75], Flex::SpaceAround)]
2532 fn flex_constraint(
2533 #[case] constraints: Vec<Constraint>,
2534 #[case] expected: Vec<Range<u16>>,
2535 #[case] flex: Flex,
2536 ) {
2537 let rect = Rect::new(0, 0, 100, 1);
2538 let ranges = Layout::horizontal(constraints)
2539 .flex(flex)
2540 .split(rect)
2541 .iter()
2542 .map(|r| r.left()..r.right())
2543 .collect_vec();
2544 assert_eq!(ranges, expected);
2545 }
2546
2547 #[rstest]
2548 #[case::length_overlap1(vec![(0 , 20) , (20 , 20) , (40 , 20)] , vec![Length(20) , Length(20) , Length(20)] , Flex::Start , 0)]
2549 #[case::length_overlap2(vec![(0 , 20) , (19 , 20) , (38 , 20)] , vec![Length(20) , Length(20) , Length(20)] , Flex::Start , -1)]
2550 #[case::length_overlap3(vec![(21 , 20) , (40 , 20) , (59 , 20)] , vec![Length(20) , Length(20) , Length(20)] , Flex::Center , -1)]
2551 #[case::length_overlap4(vec![(42 , 20) , (61 , 20) , (80 , 20)] , vec![Length(20) , Length(20) , Length(20)] , Flex::End , -1)]
2552 #[case::length_overlap5(vec![(0 , 20) , (19 , 20) , (38 , 62)] , vec![Length(20) , Length(20) , Length(20)] , Flex::Legacy , -1)]
2553 #[case::length_overlap6(vec![(0 , 20) , (40 , 20) , (80 , 20)] , vec![Length(20) , Length(20) , Length(20)] , Flex::SpaceBetween , -1)]
2554 #[case::length_overlap7(vec![(10 , 20) , (40 , 20) , (70 , 20)] , vec![Length(20) , Length(20) , Length(20)] , Flex::SpaceEvenly , -1)]
2555 #[case::length_overlap7(vec![(7 , 20) , (40 , 20) , (73 , 20)] , vec![Length(20) , Length(20) , Length(20)] , Flex::SpaceAround , -1)]
2556 fn flex_overlap(
2557 #[case] expected: Vec<(u16, u16)>,
2558 #[case] constraints: Vec<Constraint>,
2559 #[case] flex: Flex,
2560 #[case] spacing: i16,
2561 ) {
2562 let rect = Rect::new(0, 0, 100, 1);
2563 let r = Layout::horizontal(constraints)
2564 .flex(flex)
2565 .spacing(spacing)
2566 .split(rect);
2567 let result = r
2568 .iter()
2569 .map(|r| (r.x, r.width))
2570 .collect::<Vec<(u16, u16)>>();
2571
2572 assert_eq!(result, expected);
2573 }
2574
2575 #[rstest]
2576 #[case::length_spacing(vec![(0 , 20), (20, 20) , (40, 20)], vec![Length(20), Length(20), Length(20)], Flex::Start , 0)]
2577 #[case::length_spacing(vec![(0 , 20), (22, 20) , (44, 20)], vec![Length(20), Length(20), Length(20)], Flex::Start , 2)]
2578 #[case::length_spacing(vec![(18, 20), (40, 20) , (62, 20)], vec![Length(20), Length(20), Length(20)], Flex::Center , 2)]
2579 #[case::length_spacing(vec![(36, 20), (58, 20) , (80, 20)], vec![Length(20), Length(20), Length(20)], Flex::End , 2)]
2580 #[case::length_spacing(vec![(0 , 20), (22, 20) , (44, 56)], vec![Length(20), Length(20), Length(20)], Flex::Legacy , 2)]
2581 #[case::length_spacing(vec![(0 , 20), (40, 20) , (80, 20)], vec![Length(20), Length(20), Length(20)], Flex::SpaceBetween, 2)]
2582 #[case::length_spacing(vec![(10, 20), (40, 20) , (70, 20)], vec![Length(20), Length(20), Length(20)], Flex::SpaceEvenly, 2)]
2583 #[case::length_spacing(vec![(7, 20), (40, 20) , (73, 20)], vec![Length(20), Length(20), Length(20)], Flex::SpaceAround, 2)]
2584 fn flex_spacing(
2585 #[case] expected: Vec<(u16, u16)>,
2586 #[case] constraints: Vec<Constraint>,
2587 #[case] flex: Flex,
2588 #[case] spacing: i16,
2589 ) {
2590 let rect = Rect::new(0, 0, 100, 1);
2591 let r = Layout::horizontal(constraints)
2592 .flex(flex)
2593 .spacing(spacing)
2594 .split(rect);
2595 let result = r
2596 .iter()
2597 .map(|r| (r.x, r.width))
2598 .collect::<Vec<(u16, u16)>>();
2599 assert_eq!(result, expected);
2600 }
2601
2602 #[rstest]
2603 #[case::a(vec![(0, 25), (25, 75)], vec![Length(25), Length(25)])]
2604 #[case::b(vec![(0, 25), (25, 75)], vec![Length(25), Percentage(25)])]
2605 #[case::c(vec![(0, 75), (75, 25)], vec![Percentage(25), Length(25)])]
2606 #[case::d(vec![(0, 75), (75, 25)], vec![Min(25), Percentage(25)])]
2607 #[case::e(vec![(0, 25), (25, 75)], vec![Percentage(25), Min(25)])]
2608 #[case::f(vec![(0, 25), (25, 75)], vec![Min(25), Percentage(100)])]
2609 #[case::g(vec![(0, 75), (75, 25)], vec![Percentage(100), Min(25)])]
2610 #[case::h(vec![(0, 25), (25, 75)], vec![Max(75), Percentage(75)])]
2611 #[case::i(vec![(0, 75), (75, 25)], vec![Percentage(75), Max(75)])]
2612 #[case::j(vec![(0, 25), (25, 75)], vec![Max(25), Percentage(25)])]
2613 #[case::k(vec![(0, 75), (75, 25)], vec![Percentage(25), Max(25)])]
2614 #[case::l(vec![(0, 25), (25, 75)], vec![Length(25), Ratio(1, 4)])]
2615 #[case::m(vec![(0, 75), (75, 25)], vec![Ratio(1, 4), Length(25)])]
2616 #[case::n(vec![(0, 25), (25, 75)], vec![Percentage(25), Ratio(1, 4)])]
2617 #[case::o(vec![(0, 75), (75, 25)], vec![Ratio(1, 4), Percentage(25)])]
2618 #[case::p(vec![(0, 25), (25, 75)], vec![Ratio(1, 4), Fill(25)])]
2619 #[case::q(vec![(0, 75), (75, 25)], vec![Fill(25), Ratio(1, 4)])]
2620 fn constraint_specification_tests_for_priority(
2621 #[case] expected: Vec<(u16, u16)>,
2622 #[case] constraints: Vec<Constraint>,
2623 ) {
2624 let rect = Rect::new(0, 0, 100, 1);
2625 let r = Layout::horizontal(constraints)
2626 .flex(Flex::Legacy)
2627 .split(rect)
2628 .iter()
2629 .map(|r| (r.x, r.width))
2630 .collect::<Vec<(u16, u16)>>();
2631 assert_eq!(r, expected);
2632 }
2633
2634 #[rstest]
2635 #[case::a(vec![(0, 20), (20, 20), (40, 20)], vec![Length(20), Length(20), Length(20)], Flex::Start, 0)]
2636 #[case::b(vec![(18, 20), (40, 20), (62, 20)], vec![Length(20), Length(20), Length(20)], Flex::Center, 2)]
2637 #[case::c(vec![(36, 20), (58, 20), (80, 20)], vec![Length(20), Length(20), Length(20)], Flex::End, 2)]
2638 #[case::d(vec![(0, 20), (22, 20), (44, 56)], vec![Length(20), Length(20), Length(20)], Flex::Legacy, 2)]
2639 #[case::e(vec![(0, 20), (22, 20), (44, 56)], vec![Length(20), Length(20), Length(20)], Flex::Legacy, 2)]
2640 #[case::f(vec![(10, 20), (40, 20), (70, 20)], vec![Length(20), Length(20), Length(20)], Flex::SpaceEvenly, 2)]
2641 #[case::f(vec![(7, 20), (40, 20), (73, 20)], vec![Length(20), Length(20), Length(20)], Flex::SpaceAround, 2)]
2642 fn constraint_specification_tests_for_priority_with_spacing(
2643 #[case] expected: Vec<(u16, u16)>,
2644 #[case] constraints: Vec<Constraint>,
2645 #[case] flex: Flex,
2646 #[case] spacing: i16,
2647 ) {
2648 let rect = Rect::new(0, 0, 100, 1);
2649 let r = Layout::horizontal(constraints)
2650 .spacing(spacing)
2651 .flex(flex)
2652 .split(rect)
2653 .iter()
2654 .map(|r| (r.x, r.width))
2655 .collect::<Vec<(u16, u16)>>();
2656 assert_eq!(r, expected);
2657 }
2658
2659 #[rstest]
2660 #[case::prop(vec![(0 , 10), (10, 80), (90 , 10)] , vec![Length(10), Fill(1), Length(10)], Flex::Legacy)]
2661 #[case::flex(vec![(0 , 10), (90 , 10)] , vec![Length(10), Length(10)], Flex::SpaceBetween)]
2662 #[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)]
2663 #[case::flex(vec![(27 , 10), (63, 10)] , vec![Length(10), Length(10)], Flex::SpaceEvenly)]
2664 #[case::prop(vec![(0 , 10), (10, 10), (20 , 80)] , vec![Length(10), Length(10), Fill(1)], Flex::Legacy)]
2665 #[case::flex(vec![(0 , 10), (10, 10)] , vec![Length(10), Length(10)], Flex::Start)]
2666 #[case::prop(vec![(0 , 80), (80 , 10), (90, 10)] , vec![Fill(1), Length(10), Length(10)], Flex::Legacy)]
2667 #[case::flex(vec![(80 , 10), (90, 10)] , vec![Length(10), Length(10)], Flex::End)]
2668 #[case::prop(vec![(0 , 40), (40, 10), (50, 10), (60, 40)] , vec![Fill(1), Length(10), Length(10), Fill(1)], Flex::Legacy)]
2669 #[case::flex(vec![(40 , 10), (50, 10)] , vec![Length(10), Length(10)], Flex::Center)]
2670 #[case::flex(vec![(20 , 10), (70, 10)] , vec![Length(10), Length(10)], Flex::SpaceAround)]
2671 fn fill_vs_flex(
2672 #[case] expected: Vec<(u16, u16)>,
2673 #[case] constraints: Vec<Constraint>,
2674 #[case] flex: Flex,
2675 ) {
2676 let rect = Rect::new(0, 0, 100, 1);
2677 let r = Layout::horizontal(constraints).flex(flex).split(rect);
2678 let result = r
2679 .iter()
2680 .map(|r| (r.x, r.width))
2681 .collect::<Vec<(u16, u16)>>();
2682 assert_eq!(result, expected);
2683 }
2684
2685 #[rstest]
2686 #[case::flex0(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::Legacy , 0)]
2687 #[case::flex0(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::SpaceEvenly , 0)]
2688 #[case::flex0(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::SpaceBetween , 0)]
2689 #[case::flex0(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::SpaceAround , 0)]
2690 #[case::flex0(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::Start , 0)]
2691 #[case::flex0(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::Center , 0)]
2692 #[case::flex0(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::End , 0)]
2693 #[case::flex10(vec![(0 , 45), (55 , 45)] , vec![Fill(1), Fill(1)], Flex::Legacy , 10)]
2694 #[case::flex10(vec![(0 , 45), (55 , 45)] , vec![Fill(1), Fill(1)], Flex::Start , 10)]
2695 #[case::flex10(vec![(0 , 45), (55 , 45)] , vec![Fill(1), Fill(1)], Flex::Center , 10)]
2696 #[case::flex10(vec![(0 , 45), (55 , 45)] , vec![Fill(1), Fill(1)], Flex::End , 10)]
2697 #[case::flex10(vec![(10 , 35), (55 , 35)] , vec![Fill(1), Fill(1)], Flex::SpaceEvenly , 10)]
2698 #[case::flex10(vec![(0 , 45), (55 , 45)] , vec![Fill(1), Fill(1)], Flex::SpaceBetween , 10)]
2699 #[case::flex10(vec![(10 , 30), (60 , 30)] , vec![Fill(1), Fill(1)], Flex::SpaceAround , 10)]
2700 #[case::flex_length0(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::Legacy , 0)]
2701 #[case::flex_length0(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceEvenly , 0)]
2702 #[case::flex_length0(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceBetween , 0)]
2703 #[case::flex_length0(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceAround , 0)]
2704 #[case::flex_length0(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::Start , 0)]
2705 #[case::flex_length0(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::Center , 0)]
2706 #[case::flex_length0(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::End , 0)]
2707 #[case::flex_length10(vec![(0 , 35), (45, 10), (65 , 35)] , vec![Fill(1), Length(10), Fill(1)], Flex::Legacy , 10)]
2708 #[case::flex_length10(vec![(0 , 35), (45, 10), (65 , 35)] , vec![Fill(1), Length(10), Fill(1)], Flex::Start , 10)]
2709 #[case::flex_length10(vec![(0 , 35), (45, 10), (65 , 35)] , vec![Fill(1), Length(10), Fill(1)], Flex::Center , 10)]
2710 #[case::flex_length10(vec![(0 , 35), (45, 10), (65 , 35)] , vec![Fill(1), Length(10), Fill(1)], Flex::End , 10)]
2711 #[case::flex_length10(vec![(10 , 25), (45, 10), (65 , 25)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceEvenly , 10)]
2712 #[case::flex_length10(vec![(0 , 35), (45, 10), (65 , 35)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceBetween , 10)]
2713 #[case::flex_length10(vec![(10 , 15), (45, 10), (75 , 15)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceAround , 10)]
2714 fn fill_spacing(
2715 #[case] expected: Vec<(u16, u16)>,
2716 #[case] constraints: Vec<Constraint>,
2717 #[case] flex: Flex,
2718 #[case] spacing: i16,
2719 ) {
2720 let rect = Rect::new(0, 0, 100, 1);
2721 let r = Layout::horizontal(constraints)
2722 .flex(flex)
2723 .spacing(spacing)
2724 .split(rect);
2725 let result = r
2726 .iter()
2727 .map(|r| (r.x, r.width))
2728 .collect::<Vec<(u16, u16)>>();
2729 assert_eq!(expected, result);
2730 }
2731
2732 #[rstest]
2733 #[case::flex0_1(vec![(0 , 55), (45 , 55)] , vec![Fill(1), Fill(1)], Flex::Legacy , -10)]
2734 #[case::flex0_2(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::SpaceAround , -10)]
2735 #[case::flex0_3(vec![(0 , 55), (45 , 55)] , vec![Fill(1), Fill(1)], Flex::SpaceBetween , -10)]
2736 #[case::flex0_4(vec![(0 , 55), (45 , 55)] , vec![Fill(1), Fill(1)], Flex::Start , -10)]
2737 #[case::flex0_5(vec![(0 , 55), (45 , 55)] , vec![Fill(1), Fill(1)], Flex::Center , -10)]
2738 #[case::flex0_6(vec![(0 , 55), (45 , 55)] , vec![Fill(1), Fill(1)], Flex::End , -10)]
2739 #[case::flex0_7(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::SpaceEvenly , -10)]
2740 #[case::flex10_1(vec![(0 , 51), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::Legacy , -1)]
2741 #[case::flex10_2(vec![(0 , 51), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::Start , -1)]
2742 #[case::flex10_3(vec![(0 , 51), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::Center , -1)]
2743 #[case::flex10_4(vec![(0 , 51), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::End , -1)]
2744 #[case::flex10_5(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::SpaceAround , -1)]
2745 #[case::flex10_6(vec![(0 , 51), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::SpaceBetween , -1)]
2746 #[case::flex10_7(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::SpaceEvenly , -1)]
2747 #[case::flex_length0_1(vec![(0 , 55), (45, 10), (45 , 55)] , vec![Fill(1), Length(10), Fill(1)], Flex::Legacy , -10)]
2748 #[case::flex_length0_2(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceAround , -10)]
2749 #[case::flex_length0_3(vec![(0 , 55), (45, 10), (45 , 55)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceBetween , -10)]
2750 #[case::flex_length0_4(vec![(0 , 55), (45, 10), (45 , 55)] , vec![Fill(1), Length(10), Fill(1)], Flex::Start , -10)]
2751 #[case::flex_length0_5(vec![(0 , 55), (45, 10), (45 , 55)] , vec![Fill(1), Length(10), Fill(1)], Flex::Center , -10)]
2752 #[case::flex_length0_6(vec![(0 , 55), (45, 10), (45 , 55)] , vec![Fill(1), Length(10), Fill(1)], Flex::End , -10)]
2753 #[case::flex_length0_7(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceEvenly , -10)]
2754 #[case::flex_length10_1(vec![(0 , 46), (45, 10), (54 , 46)] , vec![Fill(1), Length(10), Fill(1)], Flex::Legacy , -1)]
2755 #[case::flex_length10_2(vec![(0 , 46), (45, 10), (54 , 46)] , vec![Fill(1), Length(10), Fill(1)], Flex::Start , -1)]
2756 #[case::flex_length10_3(vec![(0 , 46), (45, 10), (54 , 46)] , vec![Fill(1), Length(10), Fill(1)], Flex::Center , -1)]
2757 #[case::flex_length10_4(vec![(0 , 46), (45, 10), (54 , 46)] , vec![Fill(1), Length(10), Fill(1)], Flex::End , -1)]
2758 #[case::flex_length10_5(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceAround , -1)]
2759 #[case::flex_length10_6(vec![(0 , 46), (45, 10), (54 , 46)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceBetween , -1)]
2760 #[case::flex_length10_7(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceEvenly , -1)]
2761 fn fill_overlap(
2762 #[case] expected: Vec<(u16, u16)>,
2763 #[case] constraints: Vec<Constraint>,
2764 #[case] flex: Flex,
2765 #[case] spacing: i16,
2766 ) {
2767 let rect = Rect::new(0, 0, 100, 1);
2768 let r = Layout::horizontal(constraints)
2769 .flex(flex)
2770 .spacing(spacing)
2771 .split(rect);
2772 let result = r
2773 .iter()
2774 .map(|r| (r.x, r.width))
2775 .collect::<Vec<(u16, u16)>>();
2776 assert_eq!(result, expected);
2777 }
2778
2779 #[rstest]
2780 #[case::flex_length10(vec![(0, 10), (90, 10)], vec![Length(10), Length(10)], Flex::Center, 80)]
2781 fn flex_spacing_lower_priority_than_user_spacing(
2782 #[case] expected: Vec<(u16, u16)>,
2783 #[case] constraints: Vec<Constraint>,
2784 #[case] flex: Flex,
2785 #[case] spacing: i16,
2786 ) {
2787 let rect = Rect::new(0, 0, 100, 1);
2788 let r = Layout::horizontal(constraints)
2789 .flex(flex)
2790 .spacing(spacing)
2791 .split(rect);
2792 let result = r
2793 .iter()
2794 .map(|r| (r.x, r.width))
2795 .collect::<Vec<(u16, u16)>>();
2796 assert_eq!(result, expected);
2797 }
2798
2799 #[rstest]
2800 #[case::spacers(vec![(0, 0), (10, 0), (100, 0)], vec![Length(10), Length(10)], Flex::Legacy)]
2801 #[case::spacers(vec![(0, 0), (10, 80), (100, 0)], vec![Length(10), Length(10)], Flex::SpaceBetween)]
2802 #[case::spacers(vec![(0, 27), (37, 26), (73, 27)], vec![Length(10), Length(10)], Flex::SpaceEvenly)]
2803 #[case::spacers(vec![(0, 20), (30, 40), (80, 20)], vec![Length(10), Length(10)], Flex::SpaceAround)]
2804 #[case::spacers(vec![(0, 0), (10, 0), (20, 80)], vec![Length(10), Length(10)], Flex::Start)]
2805 #[case::spacers(vec![(0, 40), (50, 0), (60, 40)], vec![Length(10), Length(10)], Flex::Center)]
2806 #[case::spacers(vec![(0, 80), (90, 0), (100, 0)], vec![Length(10), Length(10)], Flex::End)]
2807 fn split_with_spacers_no_spacing(
2808 #[case] expected: Vec<(u16, u16)>,
2809 #[case] constraints: Vec<Constraint>,
2810 #[case] flex: Flex,
2811 ) {
2812 let rect = Rect::new(0, 0, 100, 1);
2813 let (_, s) = Layout::horizontal(&constraints)
2814 .flex(flex)
2815 .split_with_spacers(rect);
2816 assert_eq!(s.len(), constraints.len() + 1);
2817 let result = s
2818 .iter()
2819 .map(|r| (r.x, r.width))
2820 .collect::<Vec<(u16, u16)>>();
2821 assert_eq!(result, expected);
2822 }
2823
2824 #[rstest]
2825 #[case::spacers(vec![(0, 0), (10, 5), (100, 0)], vec![Length(10), Length(10)], Flex::Legacy, 5)]
2826 #[case::spacers(vec![(0, 0), (10, 80), (100, 0)], vec![Length(10), Length(10)], Flex::SpaceBetween, 5)]
2827 #[case::spacers(vec![(0, 27), (37, 26), (73, 27)], vec![Length(10), Length(10)], Flex::SpaceEvenly, 5)]
2828 #[case::spacers(vec![(0, 20), (30, 40), (80, 20)], vec![Length(10), Length(10)], Flex::SpaceAround, 5)]
2829 #[case::spacers(vec![(0, 0), (10, 5), (25, 75)], vec![Length(10), Length(10)], Flex::Start, 5)]
2830 #[case::spacers(vec![(0, 38), (48, 5), (63, 37)], vec![Length(10), Length(10)], Flex::Center, 5)]
2831 #[case::spacers(vec![(0, 75), (85, 5), (100, 0)], vec![Length(10), Length(10)], Flex::End, 5)]
2832 fn split_with_spacers_and_spacing(
2833 #[case] expected: Vec<(u16, u16)>,
2834 #[case] constraints: Vec<Constraint>,
2835 #[case] flex: Flex,
2836 #[case] spacing: i16,
2837 ) {
2838 let rect = Rect::new(0, 0, 100, 1);
2839 let (_, s) = Layout::horizontal(&constraints)
2840 .flex(flex)
2841 .spacing(spacing)
2842 .split_with_spacers(rect);
2843 assert_eq!(s.len(), constraints.len() + 1);
2844 let result = s
2845 .iter()
2846 .map(|r| (r.x, r.width))
2847 .collect::<Vec<(u16, u16)>>();
2848 assert_eq!(expected, result);
2849 }
2850
2851 #[rstest]
2852 #[case::spacers_1(vec![(0, 0), (10, 0), (100, 0)], vec![Length(10), Length(10)], Flex::Legacy, -1)]
2853 #[case::spacers_2(vec![(0, 0), (10, 80), (100, 0)], vec![Length(10), Length(10)], Flex::SpaceBetween, -1)]
2854 #[case::spacers_3(vec![(0, 27), (37, 26), (73, 27)], vec![Length(10), Length(10)], Flex::SpaceEvenly, -1)]
2855 #[case::spacers_3(vec![(0, 20), (30, 40), (80, 20)], vec![Length(10), Length(10)], Flex::SpaceAround, -1)]
2856 #[case::spacers_4(vec![(0, 0), (10, 0), (19, 81)], vec![Length(10), Length(10)], Flex::Start, -1)]
2857 #[case::spacers_5(vec![(0, 41), (51, 0), (60, 40)], vec![Length(10), Length(10)], Flex::Center, -1)]
2858 #[case::spacers_6(vec![(0, 81), (91, 0), (100, 0)], vec![Length(10), Length(10)], Flex::End, -1)]
2859 fn split_with_spacers_and_overlap(
2860 #[case] expected: Vec<(u16, u16)>,
2861 #[case] constraints: Vec<Constraint>,
2862 #[case] flex: Flex,
2863 #[case] spacing: i16,
2864 ) {
2865 let rect = Rect::new(0, 0, 100, 1);
2866 let (_, s) = Layout::horizontal(&constraints)
2867 .flex(flex)
2868 .spacing(spacing)
2869 .split_with_spacers(rect);
2870 assert_eq!(s.len(), constraints.len() + 1);
2871 let result = s
2872 .iter()
2873 .map(|r| (r.x, r.width))
2874 .collect::<Vec<(u16, u16)>>();
2875 assert_eq!(result, expected);
2876 }
2877
2878 #[rstest]
2879 #[case::spacers(vec![(0, 0), (0, 100), (100, 0)], vec![Length(10), Length(10)], Flex::Legacy, 200)]
2880 #[case::spacers(vec![(0, 0), (0, 100), (100, 0)], vec![Length(10), Length(10)], Flex::SpaceBetween, 200)]
2881 #[case::spacers(vec![(0, 33), (33, 34), (67, 33)], vec![Length(10), Length(10)], Flex::SpaceEvenly, 200)]
2882 #[case::spacers(vec![(0, 25), (25, 50), (75, 25)], vec![Length(10), Length(10)], Flex::SpaceAround, 200)]
2883 #[case::spacers(vec![(0, 0), (0, 100), (100, 0)], vec![Length(10), Length(10)], Flex::Start, 200)]
2884 #[case::spacers(vec![(0, 0), (0, 100), (100, 0)], vec![Length(10), Length(10)], Flex::Center, 200)]
2885 #[case::spacers(vec![(0, 0), (0, 100), (100, 0)], vec![Length(10), Length(10)], Flex::End, 200)]
2886 fn split_with_spacers_and_too_much_spacing(
2887 #[case] expected: Vec<(u16, u16)>,
2888 #[case] constraints: Vec<Constraint>,
2889 #[case] flex: Flex,
2890 #[case] spacing: i16,
2891 ) {
2892 let rect = Rect::new(0, 0, 100, 1);
2893 let (_, s) = Layout::horizontal(&constraints)
2894 .flex(flex)
2895 .spacing(spacing)
2896 .split_with_spacers(rect);
2897 assert_eq!(s.len(), constraints.len() + 1);
2898 let result = s
2899 .iter()
2900 .map(|r| (r.x, r.width))
2901 .collect::<Vec<(u16, u16)>>();
2902 assert_eq!(result, expected);
2903 }
2904
2905 #[rstest]
2906 #[case::compare(vec![(0, 90), (90, 10)], vec![Min(10), Length(10)], Flex::Legacy)]
2907 #[case::compare(vec![(0, 90), (90, 10)], vec![Min(10), Length(10)], Flex::Start)]
2908 #[case::compare(vec![(0, 10), (10, 90)], vec![Min(10), Percentage(100)], Flex::Legacy)]
2909 #[case::compare(vec![(0, 10), (10, 90)], vec![Min(10), Percentage(100)], Flex::Start)]
2910 #[case::compare(vec![(0, 50), (50, 50)], vec![Percentage(50), Percentage(50)], Flex::Legacy)]
2911 #[case::compare(vec![(0, 50), (50, 50)], vec![Percentage(50), Percentage(50)], Flex::Start)]
2912 fn legacy_vs_default(
2913 #[case] expected: Vec<(u16, u16)>,
2914 #[case] constraints: Vec<Constraint>,
2915 #[case] flex: Flex,
2916 ) {
2917 let rect = Rect::new(0, 0, 100, 1);
2918 let r = Layout::horizontal(constraints).flex(flex).split(rect);
2919 let result = r
2920 .iter()
2921 .map(|r| (r.x, r.width))
2922 .collect::<Vec<(u16, u16)>>();
2923 assert_eq!(result, expected);
2924 }
2925 }
2926}