1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
7pub enum FlexDirection {
8 #[default]
10 Row,
11 RowReverse,
13 Column,
15 ColumnReverse,
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
21pub enum FlexJustify {
22 #[default]
24 Start,
25 End,
27 Center,
29 SpaceBetween,
31 SpaceAround,
33 SpaceEvenly,
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
39pub enum FlexAlign {
40 Start,
42 End,
44 #[default]
46 Center,
47 Stretch,
49 Baseline,
51}
52
53#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
55pub struct FlexItem {
56 pub grow: f32,
58 pub shrink: f32,
60 pub basis: Option<f32>,
62 pub align_self: Option<FlexAlign>,
64}
65
66impl FlexItem {
67 #[must_use]
69 pub fn new() -> Self {
70 Self::default()
71 }
72
73 #[must_use]
75 pub const fn grow(mut self, grow: f32) -> Self {
76 self.grow = grow;
77 self
78 }
79
80 #[must_use]
82 pub const fn shrink(mut self, shrink: f32) -> Self {
83 self.shrink = shrink;
84 self
85 }
86
87 #[must_use]
89 pub const fn basis(mut self, basis: f32) -> Self {
90 self.basis = Some(basis);
91 self
92 }
93
94 #[must_use]
96 pub const fn align_self(mut self, align: FlexAlign) -> Self {
97 self.align_self = Some(align);
98 self
99 }
100}
101
102#[must_use]
104#[allow(dead_code)]
105pub(crate) fn distribute_flex(items: &[FlexItem], sizes: &[f32], available: f32) -> Vec<f32> {
106 if items.is_empty() {
107 return Vec::new();
108 }
109
110 let total_size: f32 = sizes.iter().sum();
111 let remaining = available - total_size;
112
113 if remaining.abs() < 0.001 {
114 return sizes.to_vec();
115 }
116
117 if remaining > 0.0 {
118 let total_grow: f32 = items.iter().map(|i| i.grow).sum();
120 if total_grow > 0.0 {
121 return sizes
122 .iter()
123 .zip(items.iter())
124 .map(|(&size, item)| size + (remaining * item.grow / total_grow))
125 .collect();
126 }
127 } else {
128 let total_shrink: f32 = items.iter().map(|i| i.shrink).sum();
130 if total_shrink > 0.0 {
131 return sizes
132 .iter()
133 .zip(items.iter())
134 .map(|(&size, item)| (size + (remaining * item.shrink / total_shrink)).max(0.0))
135 .collect();
136 }
137 }
138
139 sizes.to_vec()
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145
146 #[test]
147 fn test_flex_direction_default() {
148 assert_eq!(FlexDirection::default(), FlexDirection::Row);
149 }
150
151 #[test]
152 fn test_flex_justify_default() {
153 assert_eq!(FlexJustify::default(), FlexJustify::Start);
154 }
155
156 #[test]
157 fn test_flex_align_default() {
158 assert_eq!(FlexAlign::default(), FlexAlign::Center);
159 }
160
161 #[test]
162 fn test_flex_item_builder() {
163 let item = FlexItem::new()
164 .grow(1.0)
165 .shrink(0.0)
166 .basis(100.0)
167 .align_self(FlexAlign::Start);
168
169 assert_eq!(item.grow, 1.0);
170 assert_eq!(item.shrink, 0.0);
171 assert_eq!(item.basis, Some(100.0));
172 assert_eq!(item.align_self, Some(FlexAlign::Start));
173 }
174
175 #[test]
176 fn test_distribute_flex_empty() {
177 let result = distribute_flex(&[], &[], 100.0);
178 assert!(result.is_empty());
179 }
180
181 #[test]
182 fn test_distribute_flex_exact_fit() {
183 let items = vec![FlexItem::new(), FlexItem::new()];
184 let sizes = vec![50.0, 50.0];
185 let result = distribute_flex(&items, &sizes, 100.0);
186 assert_eq!(result, vec![50.0, 50.0]);
187 }
188
189 #[test]
190 fn test_distribute_flex_grow() {
191 let items = vec![FlexItem::new().grow(1.0), FlexItem::new().grow(1.0)];
192 let sizes = vec![25.0, 25.0];
193 let result = distribute_flex(&items, &sizes, 100.0);
194 assert_eq!(result, vec![50.0, 50.0]);
195 }
196
197 #[test]
198 fn test_distribute_flex_grow_uneven() {
199 let items = vec![FlexItem::new().grow(1.0), FlexItem::new().grow(3.0)];
200 let sizes = vec![0.0, 0.0];
201 let result = distribute_flex(&items, &sizes, 100.0);
202 assert_eq!(result, vec![25.0, 75.0]);
203 }
204
205 #[test]
206 fn test_distribute_flex_shrink() {
207 let items = vec![FlexItem::new().shrink(1.0), FlexItem::new().shrink(1.0)];
208 let sizes = vec![75.0, 75.0];
209 let result = distribute_flex(&items, &sizes, 100.0);
210 assert_eq!(result, vec![50.0, 50.0]);
211 }
212
213 #[test]
218 fn test_flex_direction_clone() {
219 let dir = FlexDirection::Column;
220 let cloned = dir;
221 assert_eq!(dir, cloned);
222 }
223
224 #[test]
225 fn test_flex_direction_all_variants() {
226 assert_eq!(FlexDirection::Row, FlexDirection::Row);
227 assert_eq!(FlexDirection::RowReverse, FlexDirection::RowReverse);
228 assert_eq!(FlexDirection::Column, FlexDirection::Column);
229 assert_eq!(FlexDirection::ColumnReverse, FlexDirection::ColumnReverse);
230 }
231
232 #[test]
233 fn test_flex_direction_debug() {
234 let dir = FlexDirection::Row;
235 let debug = format!("{:?}", dir);
236 assert!(debug.contains("Row"));
237 }
238
239 #[test]
244 fn test_flex_justify_all_variants() {
245 assert_eq!(FlexJustify::Start, FlexJustify::Start);
246 assert_eq!(FlexJustify::End, FlexJustify::End);
247 assert_eq!(FlexJustify::Center, FlexJustify::Center);
248 assert_eq!(FlexJustify::SpaceBetween, FlexJustify::SpaceBetween);
249 assert_eq!(FlexJustify::SpaceAround, FlexJustify::SpaceAround);
250 assert_eq!(FlexJustify::SpaceEvenly, FlexJustify::SpaceEvenly);
251 }
252
253 #[test]
254 fn test_flex_justify_clone() {
255 let justify = FlexJustify::SpaceBetween;
256 let cloned = justify;
257 assert_eq!(justify, cloned);
258 }
259
260 #[test]
261 fn test_flex_justify_debug() {
262 let justify = FlexJustify::Center;
263 let debug = format!("{:?}", justify);
264 assert!(debug.contains("Center"));
265 }
266
267 #[test]
272 fn test_flex_align_all_variants() {
273 assert_eq!(FlexAlign::Start, FlexAlign::Start);
274 assert_eq!(FlexAlign::End, FlexAlign::End);
275 assert_eq!(FlexAlign::Center, FlexAlign::Center);
276 assert_eq!(FlexAlign::Stretch, FlexAlign::Stretch);
277 assert_eq!(FlexAlign::Baseline, FlexAlign::Baseline);
278 }
279
280 #[test]
281 fn test_flex_align_clone() {
282 let align = FlexAlign::Stretch;
283 let cloned = align;
284 assert_eq!(align, cloned);
285 }
286
287 #[test]
288 fn test_flex_align_debug() {
289 let align = FlexAlign::Baseline;
290 let debug = format!("{:?}", align);
291 assert!(debug.contains("Baseline"));
292 }
293
294 #[test]
299 fn test_flex_item_default() {
300 let item = FlexItem::default();
301 assert_eq!(item.grow, 0.0);
302 assert_eq!(item.shrink, 0.0);
303 assert_eq!(item.basis, None);
304 assert_eq!(item.align_self, None);
305 }
306
307 #[test]
308 fn test_flex_item_new() {
309 let item = FlexItem::new();
310 assert_eq!(item.grow, 0.0);
311 assert_eq!(item.shrink, 0.0);
312 }
313
314 #[test]
315 fn test_flex_item_grow_only() {
316 let item = FlexItem::new().grow(2.5);
317 assert_eq!(item.grow, 2.5);
318 assert_eq!(item.shrink, 0.0);
319 }
320
321 #[test]
322 fn test_flex_item_shrink_only() {
323 let item = FlexItem::new().shrink(0.5);
324 assert_eq!(item.shrink, 0.5);
325 assert_eq!(item.grow, 0.0);
326 }
327
328 #[test]
329 fn test_flex_item_basis_only() {
330 let item = FlexItem::new().basis(200.0);
331 assert_eq!(item.basis, Some(200.0));
332 }
333
334 #[test]
335 fn test_flex_item_align_self_only() {
336 let item = FlexItem::new().align_self(FlexAlign::End);
337 assert_eq!(item.align_self, Some(FlexAlign::End));
338 }
339
340 #[test]
341 fn test_flex_item_clone() {
342 let item = FlexItem::new().grow(1.0).shrink(0.5);
343 let cloned = item;
344 assert_eq!(item.grow, cloned.grow);
345 assert_eq!(item.shrink, cloned.shrink);
346 }
347
348 #[test]
349 fn test_flex_item_debug() {
350 let item = FlexItem::new().grow(1.0);
351 let debug = format!("{:?}", item);
352 assert!(debug.contains("FlexItem"));
353 }
354
355 #[test]
360 fn test_distribute_flex_no_grow_no_shrink() {
361 let items = vec![FlexItem::new(), FlexItem::new()];
362 let sizes = vec![30.0, 30.0];
363 let result = distribute_flex(&items, &sizes, 100.0);
364 assert_eq!(result, vec![30.0, 30.0]);
366 }
367
368 #[test]
369 fn test_distribute_flex_single_item_grow() {
370 let items = vec![FlexItem::new().grow(1.0)];
371 let sizes = vec![50.0];
372 let result = distribute_flex(&items, &sizes, 100.0);
373 assert_eq!(result, vec![100.0]);
374 }
375
376 #[test]
377 fn test_distribute_flex_single_item_shrink() {
378 let items = vec![FlexItem::new().shrink(1.0)];
379 let sizes = vec![150.0];
380 let result = distribute_flex(&items, &sizes, 100.0);
381 assert_eq!(result, vec![100.0]);
382 }
383
384 #[test]
385 fn test_distribute_flex_shrink_uneven() {
386 let items = vec![FlexItem::new().shrink(1.0), FlexItem::new().shrink(3.0)];
387 let sizes = vec![100.0, 100.0];
388 let result = distribute_flex(&items, &sizes, 100.0);
389 assert_eq!(result, vec![75.0, 25.0]);
393 }
394
395 #[test]
396 fn test_distribute_flex_shrink_to_zero() {
397 let items = vec![FlexItem::new().shrink(1.0)];
398 let sizes = vec![50.0];
399 let result = distribute_flex(&items, &sizes, 0.0);
401 assert_eq!(result, vec![0.0]); }
403
404 #[test]
405 fn test_distribute_flex_mixed_grow() {
406 let items = vec![
407 FlexItem::new().grow(0.0), FlexItem::new().grow(1.0), ];
410 let sizes = vec![50.0, 0.0];
411 let result = distribute_flex(&items, &sizes, 100.0);
412 assert_eq!(result, vec![50.0, 50.0]);
413 }
414
415 #[test]
416 fn test_distribute_flex_three_items() {
417 let items = vec![
418 FlexItem::new().grow(1.0),
419 FlexItem::new().grow(2.0),
420 FlexItem::new().grow(1.0),
421 ];
422 let sizes = vec![0.0, 0.0, 0.0];
423 let result = distribute_flex(&items, &sizes, 100.0);
424 assert_eq!(result, vec![25.0, 50.0, 25.0]);
425 }
426
427 #[test]
428 fn test_distribute_flex_near_exact_fit() {
429 let items = vec![FlexItem::new().grow(1.0), FlexItem::new().grow(1.0)];
430 let sizes = vec![49.9995, 50.0005];
431 let result = distribute_flex(&items, &sizes, 100.0);
432 assert_eq!(result, vec![49.9995, 50.0005]);
434 }
435}