1use ratatui::layout::{Direction, Rect};
7
8use a2ui_base::protocol::common_types::{Align, Justify};
9
10pub fn padded_content(area: Rect) -> Rect {
17 let h = if area.height > 2 { area.height - 2 } else { area.height };
18 let w = if area.width > 2 { area.width - 2 } else { area.width };
19 let y = if area.height > 2 { area.y + 1 } else { area.y };
20 let x = if area.width > 2 { area.x + 1 } else { area.x };
21 Rect { x, y, width: w, height: h }
22}
23
24pub fn weighted_split(
34 direction: Direction,
35 area: Rect,
36 weights: &[Option<f64>],
37) -> Vec<Rect> {
38 let n = weights.len();
39 if n == 0 {
40 return vec![];
41 }
42
43 let total_size = match direction {
44 Direction::Horizontal => area.width as u16,
45 Direction::Vertical => area.height as u16,
46 } as f64;
47
48 let effective: Vec<f64> = weights.iter().map(|w| w.unwrap_or(1.0)).collect();
50 let total_weight: f64 = effective.iter().sum();
51
52 if total_weight <= 0.0 {
53 return equal_split(direction, area, n);
55 }
56
57 let mut rects = Vec::with_capacity(n);
58 let mut offset: u16 = 0;
59
60 for (i, &w) in effective.iter().enumerate() {
61 let fraction = w / total_weight;
62 let raw = total_size * fraction;
63 let mut size = raw.floor() as u16;
64
65 if i == n - 1 {
67 let used: u16 = rects.iter().map(|r: &Rect| size_axis(r, direction)).sum();
68 size = total_size as u16 - used;
69 }
70
71 let rect = make_rect(direction, area, offset, size);
72 rects.push(rect);
73 offset += size;
74 }
75
76 rects
77}
78
79pub fn apply_justify(
85 justify: Justify,
86 items: &[(Rect, u16)],
87 total_area: Rect,
88 direction: Direction,
89) -> Vec<Rect> {
90 let container_size = size_from_direction(total_area, direction);
91 let total_item_size: u16 = items.iter().map(|(_, s)| *s).sum();
92
93 match justify {
94 Justify::Start => {
95 items.iter().map(|(rect, _)| *rect).collect()
97 }
98 Justify::Center => {
99 let gap = container_size.saturating_sub(total_item_size);
100 let offset = gap / 2;
101 shift_items(items, total_area, direction, offset)
102 }
103 Justify::End => {
104 let gap = container_size.saturating_sub(total_item_size);
105 shift_items(items, total_area, direction, gap)
106 }
107 Justify::SpaceBetween => {
108 let count = items.len();
109 if count <= 1 {
110 return items.iter().map(|(rect, _)| *rect).collect();
111 }
112 let gap = container_size.saturating_sub(total_item_size);
113 let spacing = gap / (count as u16 - 1);
114 let mut result = Vec::with_capacity(count);
115 let mut offset: u16 = 0;
116 for (rect, size) in items {
117 result.push(set_offset(*rect, total_area, direction, offset));
118 offset += size + spacing;
119 }
120 result
121 }
122 Justify::SpaceAround => {
123 let count = items.len();
124 if count == 0 {
125 return vec![];
126 }
127 let gap = container_size.saturating_sub(total_item_size);
128 let spacing = gap / count as u16;
129 let start_offset = spacing / 2;
130 let mut result = Vec::with_capacity(count);
131 let mut offset: u16 = 0;
132 for (rect, size) in items {
133 offset += if result.is_empty() { start_offset } else { spacing };
134 result.push(set_offset(*rect, total_area, direction, offset));
135 offset += size;
136 }
137 result
138 }
139 Justify::SpaceEvenly => {
140 let count = items.len();
141 if count == 0 {
142 return vec![];
143 }
144 let gap = container_size.saturating_sub(total_item_size);
145 let spacing = gap / (count as u16 + 1);
146 let mut result = Vec::with_capacity(count);
147 let mut offset: u16 = spacing;
148 for (rect, size) in items {
149 result.push(set_offset(*rect, total_area, direction, offset));
150 offset += size + spacing;
151 }
152 result
153 }
154 Justify::Stretch => {
155 let count = items.len();
156 if count == 0 {
157 return vec![];
158 }
159 let each_size = container_size / count as u16;
160 let mut result = Vec::with_capacity(count);
161 let mut offset: u16 = 0;
162 for (i, (_rect, _size)) in items.iter().enumerate() {
163 let size = if i == count - 1 {
164 container_size - offset
165 } else {
166 each_size
167 };
168 result.push(make_rect(direction, total_area, offset, size));
169 offset += size;
170 }
171 result
172 }
173 }
174}
175
176pub fn apply_align(align: Align, item: Rect, container: Rect, direction: Direction) -> Rect {
180 let (cross_size, container_cross) = match direction {
181 Direction::Horizontal => (item.height, container.height),
182 Direction::Vertical => (item.width, container.width),
183 };
184
185 match align {
186 Align::Start => item,
187 Align::Center => {
188 let offset = container_cross.saturating_sub(cross_size) / 2;
189 set_cross_offset(item, container, direction, offset)
190 }
191 Align::End => {
192 let offset = container_cross.saturating_sub(cross_size);
193 set_cross_offset(item, container, direction, offset)
194 }
195 Align::Stretch => {
196 match direction {
198 Direction::Horizontal => Rect {
199 x: item.x,
200 y: container.y,
201 width: item.width,
202 height: container.height,
203 },
204 Direction::Vertical => Rect {
205 x: container.x,
206 y: item.y,
207 width: container.width,
208 height: item.height,
209 },
210 }
211 }
212 }
213}
214
215pub fn flex_layout(
236 direction: Direction,
237 area: Rect,
238 items: &[(Option<u16>, Option<f64>)],
239 justify: Justify,
240) -> Vec<Rect> {
241 let n = items.len();
242 if n == 0 {
243 return vec![];
244 }
245
246 let total = size_from_direction(area, direction) as i64;
247
248 let bases: Vec<i64> = items.iter().map(|(nat, _)| nat.unwrap_or(0) as i64).collect();
250 let weights: Vec<f64> = items
251 .iter()
252 .map(|(nat, w)| w.unwrap_or(if nat.is_none() { 1.0 } else { 0.0 }))
253 .collect();
254
255 let sum_base: i64 = bases.iter().sum();
256 let sum_weight: f64 = weights.iter().sum();
257 let free = total - sum_base; let mut finals: Vec<i64> = vec![0; n];
260
261 if free > 0 && sum_weight > 0.0 {
262 for i in 0..n {
264 finals[i] = bases[i];
265 if weights[i] > 0.0 {
266 finals[i] += (free as f64 * weights[i] / sum_weight).round() as i64;
267 }
268 }
269 let used: i64 = finals.iter().sum();
271 if let Some(pos) = weights.iter().rposition(|&w| w > 0.0) {
272 finals[pos] += total - used;
273 }
274 } else if free > 0 {
275 if matches!(justify, Justify::Stretch) {
277 let each = free / n as i64;
278 let mut rem = free - each * n as i64;
279 for i in 0..n {
280 finals[i] = bases[i] + each + (if rem > 0 { rem -= 1; 1 } else { 0 });
281 }
282 } else {
283 for i in 0..n {
284 finals[i] = bases[i];
285 }
286 }
287 } else if free < 0 {
288 if sum_weight > 0.0 {
291 for i in 0..n {
292 finals[i] = (total as f64 * weights[i] / sum_weight).round() as i64;
293 }
294 let used: i64 = finals.iter().sum();
295 if let Some(pos) = weights.iter().rposition(|&w| w > 0.0) {
296 finals[pos] += total - used;
297 }
298 } else if sum_base > 0 {
299 for i in 0..n {
300 finals[i] =
301 ((total as f64 * bases[i] as f64 / sum_base as f64).round() as i64).max(1);
302 }
303 let used: i64 = finals.iter().sum();
304 finals[n - 1] += total - used;
305 } else {
306 let each = total / n as i64;
307 let mut rem = total - each * n as i64;
308 for i in 0..n {
309 finals[i] = each + (if rem > 0 { rem -= 1; 1 } else { 0 });
310 }
311 }
312 } else {
313 for i in 0..n {
315 finals[i] = bases[i];
316 }
317 }
318
319 for f in finals.iter_mut() {
321 if *f < 0 {
322 *f = 0;
323 }
324 if *f > total {
325 *f = total;
326 }
327 }
328
329 let total_size: i64 = finals.iter().sum();
331 let sizes = finals;
332
333 let pack_from = |start: i64| -> Vec<i64> {
334 let mut acc = start;
335 let mut out = vec![0i64; n];
336 for i in 0..n {
337 out[i] = acc;
338 acc += sizes[i];
339 }
340 out
341 };
342
343 let offsets: Vec<i64> = match justify {
344 Justify::Start | Justify::Stretch => pack_from(0),
346 Justify::Center => {
347 let gap = (total - total_size).max(0);
348 pack_from(gap / 2)
349 }
350 Justify::End => pack_from((total - total_size).max(0)),
351 Justify::SpaceBetween => {
352 if n <= 1 {
353 pack_from(0)
354 } else {
355 let gap = (total - total_size).max(0);
356 let spacing = gap / (n as i64 - 1);
357 let mut out = vec![0i64; n];
358 let mut acc = 0;
359 for i in 0..n {
360 out[i] = acc;
361 acc += sizes[i] + spacing;
362 }
363 out
364 }
365 }
366 Justify::SpaceAround => {
367 let gap = (total - total_size).max(0);
368 let spacing = gap / n as i64;
369 pack_from(spacing / 2)
370 .iter()
371 .enumerate()
372 .map(|(i, &o)| o + spacing * i as i64)
373 .collect()
374 }
375 Justify::SpaceEvenly => {
376 let gap = (total - total_size).max(0);
377 let spacing = gap / (n as i64 + 1);
378 pack_from(spacing)
379 }
380 };
381
382 sizes
383 .iter()
384 .zip(offsets.iter())
385 .map(|(&size, &offset)| {
386 make_rect(direction, area, offset.max(0) as u16, size.max(0) as u16)
387 })
388 .collect()
389}
390
391fn equal_split(direction: Direction, area: Rect, n: usize) -> Vec<Rect> {
397 if n == 0 {
398 return vec![];
399 }
400 let total = match direction {
401 Direction::Horizontal => area.width,
402 Direction::Vertical => area.height,
403 };
404 let each = total / n as u16;
405 let mut rects = Vec::with_capacity(n);
406 let base = match direction {
407 Direction::Horizontal => area.x,
408 Direction::Vertical => area.y,
409 };
410 for i in 0..n {
411 let offset = base + (each * i as u16);
412 let size = if i == n - 1 {
414 total - each * (n as u16 - 1)
415 } else {
416 each
417 };
418 rects.push(make_rect(direction, area, offset - base, size));
419 }
420 rects
421}
422
423fn size_axis(rect: &Rect, direction: Direction) -> u16 {
424 match direction {
425 Direction::Horizontal => rect.width,
426 Direction::Vertical => rect.height,
427 }
428}
429
430fn size_from_direction(area: Rect, direction: Direction) -> u16 {
431 match direction {
432 Direction::Horizontal => area.width,
433 Direction::Vertical => area.height,
434 }
435}
436
437fn make_rect(direction: Direction, area: Rect, offset: u16, size: u16) -> Rect {
438 match direction {
439 Direction::Horizontal => Rect {
440 x: area.x + offset,
441 y: area.y,
442 width: size,
443 height: area.height,
444 },
445 Direction::Vertical => Rect {
446 x: area.x,
447 y: area.y + offset,
448 width: area.width,
449 height: size,
450 },
451 }
452}
453
454fn shift_items(
456 items: &[(Rect, u16)],
457 total_area: Rect,
458 direction: Direction,
459 start_offset: u16,
460) -> Vec<Rect> {
461 let base = match direction {
462 Direction::Horizontal => total_area.x,
463 Direction::Vertical => total_area.y,
464 };
465 let mut result = Vec::with_capacity(items.len());
466 let mut pos = base + start_offset;
467 for (rect, size) in items {
468 result.push(set_offset(*rect, total_area, direction, pos - base));
469 pos += size;
470 }
471 result
472}
473
474fn set_offset(rect: Rect, _total_area: Rect, direction: Direction, offset: u16) -> Rect {
475 match direction {
476 Direction::Horizontal => Rect {
477 x: _total_area.x + offset,
478 ..rect
479 },
480 Direction::Vertical => Rect {
481 y: _total_area.y + offset,
482 ..rect
483 },
484 }
485}
486
487fn set_cross_offset(item: Rect, container: Rect, direction: Direction, offset: u16) -> Rect {
488 match direction {
489 Direction::Horizontal => Rect {
490 y: container.y + offset,
491 ..item
492 },
493 Direction::Vertical => Rect {
494 x: container.x + offset,
495 ..item
496 },
497 }
498}
499
500#[cfg(test)]
501mod tests {
502 use super::*;
503
504 fn test_area() -> Rect {
505 Rect::new(0, 0, 100, 30)
506 }
507
508 #[test]
509 fn weighted_split_equal_when_no_weights() {
510 let area = test_area();
511 let result = weighted_split(Direction::Horizontal, area, &[None, None, None]);
512 assert_eq!(result.len(), 3);
513 let total_width: u16 = result.iter().map(|r| r.width).sum();
515 assert_eq!(total_width, 100);
516 }
517
518 #[test]
519 fn weighted_split_respects_weights() {
520 let area = test_area();
521 let result = weighted_split(Direction::Vertical, area, &[Some(3.0), Some(1.0)]);
522 assert_eq!(result.len(), 2);
523 assert_eq!(result[0].height, 22); assert_eq!(result[1].height, 8); assert_eq!(result[0].height + result[1].height, 30);
526 }
527
528 #[test]
529 fn weighted_split_mixed_weights() {
530 let area = test_area();
531 let result = weighted_split(Direction::Horizontal, area, &[None, Some(2.0)]);
533 assert_eq!(result.len(), 2);
534 let total: u16 = result.iter().map(|r| r.width).sum();
535 assert_eq!(total, 100);
536 assert!(result[0].width < result[1].width);
538 }
539
540 #[test]
541 fn weighted_split_empty() {
542 let area = test_area();
543 let result = weighted_split(Direction::Horizontal, area, &[]);
544 assert!(result.is_empty());
545 }
546
547 #[test]
548 fn apply_align_stretch_horizontal() {
549 let container = Rect::new(0, 0, 100, 30);
550 let item = Rect::new(10, 5, 50, 10);
551 let result = apply_align(Align::Stretch, item, container, Direction::Horizontal);
552 assert_eq!(result.y, 0);
553 assert_eq!(result.height, 30);
554 assert_eq!(result.width, 50);
555 }
556
557 #[test]
558 fn apply_align_center_vertical() {
559 let container = Rect::new(0, 0, 100, 30);
560 let item = Rect::new(0, 0, 10, 10);
561 let result = apply_align(Align::Center, item, container, Direction::Vertical);
562 assert_eq!(result.x, 45); }
564
565 #[test]
566 fn apply_justify_space_between() {
567 let container = Rect::new(0, 0, 100, 30);
568 let items: Vec<(Rect, u16)> = vec![
569 (Rect::new(0, 0, 20, 30), 20),
570 (Rect::new(20, 0, 20, 30), 20),
571 (Rect::new(40, 0, 20, 30), 20),
572 ];
573 let result = apply_justify(Justify::SpaceBetween, &items, container, Direction::Horizontal);
574 assert_eq!(result.len(), 3);
575 assert_eq!(result[0].x, 0);
577 assert_eq!(result[1].x, 40);
578 assert_eq!(result[2].x, 80);
579 }
580
581 #[test]
582 fn apply_justify_center() {
583 let container = Rect::new(0, 0, 100, 30);
584 let items: Vec<(Rect, u16)> = vec![
585 (Rect::new(0, 0, 20, 30), 20),
586 ];
587 let result = apply_justify(Justify::Center, &items, container, Direction::Horizontal);
588 assert_eq!(result[0].x, 40); }
590
591 #[test]
592 fn apply_justify_end_vertical() {
593 let container = Rect::new(0, 0, 100, 30);
594 let items: Vec<(Rect, u16)> = vec![
595 (Rect::new(0, 0, 100, 10), 10),
596 ];
597 let result = apply_justify(Justify::End, &items, container, Direction::Vertical);
598 assert_eq!(result[0].y, 20); }
600
601 #[test]
604 fn apply_justify_space_around_three_items() {
605 let container = Rect::new(0, 0, 100, 30);
606 let items: Vec<(Rect, u16)> = vec![
607 (Rect::new(0, 0, 20, 30), 20),
608 (Rect::new(20, 0, 20, 30), 20),
609 (Rect::new(40, 0, 20, 30), 20),
610 ];
611 let result = apply_justify(Justify::SpaceAround, &items, container, Direction::Horizontal);
612 assert_eq!(result.len(), 3);
613 assert_eq!(result[0].x, 6);
618 assert_eq!(result[1].x, 39);
619 assert_eq!(result[2].x, 72);
620 }
621
622 #[test]
623 fn apply_justify_space_around_single_item() {
624 let container = Rect::new(0, 0, 100, 30);
625 let items: Vec<(Rect, u16)> = vec![
626 (Rect::new(0, 0, 20, 30), 20),
627 ];
628 let result = apply_justify(Justify::SpaceAround, &items, container, Direction::Horizontal);
629 assert_eq!(result.len(), 1);
630 assert_eq!(result[0].x, 40);
632 }
633
634 #[test]
635 fn apply_justify_space_around_empty() {
636 let container = Rect::new(0, 0, 100, 30);
637 let items: Vec<(Rect, u16)> = vec![];
638 let result = apply_justify(Justify::SpaceAround, &items, container, Direction::Horizontal);
639 assert!(result.is_empty());
640 }
641
642 #[test]
645 fn apply_justify_space_evenly_three_items() {
646 let container = Rect::new(0, 0, 100, 30);
647 let items: Vec<(Rect, u16)> = vec![
648 (Rect::new(0, 0, 20, 30), 20),
649 (Rect::new(20, 0, 20, 30), 20),
650 (Rect::new(40, 0, 20, 30), 20),
651 ];
652 let result = apply_justify(Justify::SpaceEvenly, &items, container, Direction::Horizontal);
653 assert_eq!(result.len(), 3);
654 assert_eq!(result[0].x, 10);
659 assert_eq!(result[1].x, 40);
660 assert_eq!(result[2].x, 70);
661 }
662
663 #[test]
664 fn apply_justify_space_evenly_single_item() {
665 let container = Rect::new(0, 0, 100, 30);
666 let items: Vec<(Rect, u16)> = vec![
667 (Rect::new(0, 0, 20, 30), 20),
668 ];
669 let result = apply_justify(Justify::SpaceEvenly, &items, container, Direction::Horizontal);
670 assert_eq!(result.len(), 1);
671 assert_eq!(result[0].x, 40);
673 }
674
675 #[test]
676 fn apply_justify_space_evenly_empty() {
677 let container = Rect::new(0, 0, 100, 30);
678 let items: Vec<(Rect, u16)> = vec![];
679 let result = apply_justify(Justify::SpaceEvenly, &items, container, Direction::Horizontal);
680 assert!(result.is_empty());
681 }
682
683 #[test]
686 fn apply_justify_stretch_three_items() {
687 let container = Rect::new(0, 0, 99, 30);
688 let items: Vec<(Rect, u16)> = vec![
689 (Rect::new(0, 0, 20, 30), 20),
690 (Rect::new(20, 0, 20, 30), 20),
691 (Rect::new(40, 0, 20, 30), 20),
692 ];
693 let result = apply_justify(Justify::Stretch, &items, container, Direction::Horizontal);
694 assert_eq!(result.len(), 3);
695 assert_eq!(result[0].x, 0);
697 assert_eq!(result[0].width, 33);
698 assert_eq!(result[1].x, 33);
699 assert_eq!(result[1].width, 33);
700 assert_eq!(result[2].x, 66);
701 assert_eq!(result[2].width, 33);
702 let total: u16 = result.iter().map(|r| r.width).sum();
704 assert_eq!(total, 99);
705 }
706
707 #[test]
708 fn apply_justify_stretch_with_remainder() {
709 let container = Rect::new(0, 0, 100, 30);
710 let items: Vec<(Rect, u16)> = vec![
711 (Rect::new(0, 0, 10, 30), 10),
712 (Rect::new(10, 0, 10, 30), 10),
713 (Rect::new(20, 0, 10, 30), 10),
714 ];
715 let result = apply_justify(Justify::Stretch, &items, container, Direction::Horizontal);
716 assert_eq!(result.len(), 3);
717 assert_eq!(result[0].width, 33);
719 assert_eq!(result[1].width, 33);
720 assert_eq!(result[2].width, 34);
721 let total: u16 = result.iter().map(|r| r.width).sum();
722 assert_eq!(total, 100);
723 }
724
725 #[test]
726 fn apply_justify_stretch_vertical() {
727 let container = Rect::new(0, 0, 100, 30);
728 let items: Vec<(Rect, u16)> = vec![
729 (Rect::new(0, 0, 100, 5), 5),
730 (Rect::new(0, 5, 100, 5), 5),
731 ];
732 let result = apply_justify(Justify::Stretch, &items, container, Direction::Vertical);
733 assert_eq!(result.len(), 2);
734 assert_eq!(result[0].y, 0);
736 assert_eq!(result[0].height, 15);
737 assert_eq!(result[1].y, 15);
738 assert_eq!(result[1].height, 15);
739 let total: u16 = result.iter().map(|r| r.height).sum();
740 assert_eq!(total, 30);
741 }
742
743 #[test]
744 fn apply_justify_stretch_empty() {
745 let container = Rect::new(0, 0, 100, 30);
746 let items: Vec<(Rect, u16)> = vec![];
747 let result = apply_justify(Justify::Stretch, &items, container, Direction::Horizontal);
748 assert!(result.is_empty());
749 }
750
751 #[test]
754 fn flex_layout_all_none_matches_weighted_split() {
755 let area = Rect::new(0, 0, 100, 30);
758 let items = vec![(None, None), (None, None)];
759 let result = flex_layout(Direction::Vertical, area, &items, Justify::Start);
760 assert_eq!(result.len(), 2);
761 assert_eq!(result[0].height, 15);
762 assert_eq!(result[1].height, 15);
763 let total: u16 = result.iter().map(|r| r.height).sum();
764 assert_eq!(total, 30);
765 }
766
767 #[test]
768 fn flex_layout_measured_children_pack_to_natural() {
769 let area = Rect::new(0, 0, 100, 30);
772 let items = vec![(Some(3u16), None), (Some(3u16), None)];
773 let result = flex_layout(Direction::Vertical, area, &items, Justify::Start);
774 assert_eq!(result[0].y, 0);
775 assert_eq!(result[0].height, 3);
776 assert_eq!(result[1].y, 3);
777 assert_eq!(result[1].height, 3);
778 }
779
780 #[test]
781 fn flex_layout_unmeasured_fills_leftover() {
782 let area = Rect::new(0, 0, 100, 30);
785 let items = vec![(Some(3u16), None), (None, None)];
786 let result = flex_layout(Direction::Vertical, area, &items, Justify::Start);
787 assert_eq!(result[0].height, 3);
788 assert_eq!(result[1].height, 27);
789 assert_eq!(result[1].y, 3);
790 }
791
792 #[test]
793 fn flex_layout_weight_grows_measured_child() {
794 let area = Rect::new(0, 0, 100, 30);
796 let items = vec![(Some(3u16), Some(2.0)), (None, None)];
800 let result = flex_layout(Direction::Vertical, area, &items, Justify::Start);
801 assert_eq!(result[0].height, 21);
802 assert_eq!(result[1].height, 9);
803 let total: u16 = result.iter().map(|r| r.height).sum();
804 assert_eq!(total, 30);
805 }
806
807 #[test]
808 fn flex_layout_stretch_fills_axis() {
809 let area = Rect::new(0, 0, 100, 30);
811 let items = vec![(Some(3u16), None), (Some(3u16), None)];
812 let result = flex_layout(Direction::Vertical, area, &items, Justify::Stretch);
813 assert_eq!(result.len(), 2);
814 let total: u16 = result.iter().map(|r| r.height).sum();
815 assert_eq!(total, 30);
816 assert_eq!(result[0].height, 15);
817 assert_eq!(result[1].height, 15);
818 }
819
820 #[test]
821 fn flex_layout_center_offsets_packed_items() {
822 let area = Rect::new(0, 0, 100, 30);
824 let items = vec![(Some(3u16), None), (Some(3u16), None)];
825 let result = flex_layout(Direction::Vertical, area, &items, Justify::Center);
826 assert_eq!(result[0].y, 12);
827 assert_eq!(result[1].y, 15);
828 assert_eq!(result[0].height, 3);
829 }
830
831 #[test]
832 fn flex_layout_overflow_shrinks_proportionally() {
833 let area = Rect::new(0, 0, 100, 5);
836 let items = vec![(Some(3u16), None), (Some(3u16), None), (Some(3u16), None)];
837 let result = flex_layout(Direction::Vertical, area, &items, Justify::Start);
838 let total: u16 = result.iter().map(|r| r.height).sum();
839 assert_eq!(total, 5);
840 assert!(result.iter().all(|r| r.height >= 1));
841 }
842
843 #[test]
844 fn flex_layout_horizontal_uses_width() {
845 let area = Rect::new(0, 0, 100, 30);
847 let items = vec![(Some(10u16), None), (None, None)];
848 let result = flex_layout(Direction::Horizontal, area, &items, Justify::Start);
849 assert_eq!(result[0].width, 10);
850 assert_eq!(result[1].width, 90);
851 assert_eq!(result[1].x, 10);
852 }
853}