1#![allow(dead_code)]
7
8use alloc::vec::Vec;
9
10use super::events::NodeId;
11
12pub type FixedPoint = i32;
18
19pub const FP_SHIFT: u32 = 6;
21
22#[inline]
24pub const fn fp(v: i32) -> FixedPoint {
25 v << FP_SHIFT
26}
27
28#[inline]
30pub const fn fp_int(v: FixedPoint) -> i32 {
31 v >> FP_SHIFT
32}
33
34#[inline]
36pub const fn fp_mul(a: FixedPoint, b: FixedPoint) -> FixedPoint {
37 ((a as i64 * b as i64) >> FP_SHIFT) as i32
38}
39
40#[inline]
42pub fn fp_div(a: FixedPoint, b: FixedPoint) -> FixedPoint {
43 if b == 0 {
44 return 0;
45 }
46 (((a as i64) << FP_SHIFT) / (b as i64)) as i32
47}
48
49pub const FP_ZERO: FixedPoint = 0;
51
52pub const FP_ONE: FixedPoint = 1 << FP_SHIFT;
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
61pub enum FlexDirection {
62 #[default]
63 Row,
64 RowReverse,
65 Column,
66 ColumnReverse,
67}
68
69impl FlexDirection {
70 pub fn is_row(self) -> bool {
72 matches!(self, Self::Row | Self::RowReverse)
73 }
74
75 pub fn is_reverse(self) -> bool {
77 matches!(self, Self::RowReverse | Self::ColumnReverse)
78 }
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
83pub enum FlexWrap {
84 #[default]
85 NoWrap,
86 Wrap,
87 WrapReverse,
88}
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
92pub enum AlignItems {
93 FlexStart,
94 FlexEnd,
95 Center,
96 #[default]
97 Stretch,
98 Baseline,
99}
100
101#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
103pub enum JustifyContent {
104 #[default]
105 FlexStart,
106 FlexEnd,
107 Center,
108 SpaceBetween,
109 SpaceAround,
110 SpaceEvenly,
111}
112
113#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
115pub enum AlignContent {
116 FlexStart,
117 FlexEnd,
118 Center,
119 #[default]
120 Stretch,
121 SpaceBetween,
122 SpaceAround,
123}
124
125#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
127pub enum AlignSelf {
128 #[default]
129 Auto,
130 FlexStart,
131 FlexEnd,
132 Center,
133 Stretch,
134 Baseline,
135}
136
137#[derive(Debug, Clone)]
143pub struct FlexItem {
144 pub node_id: NodeId,
146 pub order: i32,
148 pub flex_grow: FixedPoint,
150 pub flex_shrink: FixedPoint,
152 pub flex_basis: FixedPoint,
154 pub min_main: FixedPoint,
156 pub max_main: FixedPoint,
158 pub min_cross: FixedPoint,
160 pub max_cross: FixedPoint,
162 pub hyp_main: FixedPoint,
164 pub hyp_cross: FixedPoint,
166 pub align_self: AlignSelf,
168
169 pub main_offset: FixedPoint,
172 pub cross_offset: FixedPoint,
174 pub main_size: FixedPoint,
176 pub cross_size: FixedPoint,
178 frozen: bool,
180 target_main: FixedPoint,
182}
183
184impl Default for FlexItem {
185 fn default() -> Self {
186 Self {
187 node_id: 0,
188 order: 0,
189 flex_grow: FP_ZERO,
190 flex_shrink: FP_ONE,
191 flex_basis: FP_ZERO,
192 min_main: FP_ZERO,
193 max_main: 0,
194 min_cross: FP_ZERO,
195 max_cross: 0,
196 hyp_main: FP_ZERO,
197 hyp_cross: FP_ZERO,
198 align_self: AlignSelf::Auto,
199 main_offset: FP_ZERO,
200 cross_offset: FP_ZERO,
201 main_size: FP_ZERO,
202 cross_size: FP_ZERO,
203 frozen: false,
204 target_main: FP_ZERO,
205 }
206 }
207}
208
209impl FlexItem {
210 pub fn new(node_id: NodeId) -> Self {
211 Self {
212 node_id,
213 ..Default::default()
214 }
215 }
216
217 fn clamp_main(&self, size: FixedPoint) -> FixedPoint {
219 let clamped = if self.min_main > 0 {
220 size.max(self.min_main)
221 } else {
222 size
223 };
224 if self.max_main > 0 {
225 clamped.min(self.max_main)
226 } else {
227 clamped
228 }
229 }
230}
231
232#[derive(Debug, Clone, Default)]
238struct FlexLine {
239 item_indices: Vec<usize>,
241 cross_size: FixedPoint,
243 cross_offset: FixedPoint,
245}
246
247#[derive(Debug, Clone, Default)]
253pub struct FlexContainerStyle {
254 pub direction: FlexDirection,
255 pub wrap: FlexWrap,
256 pub justify_content: JustifyContent,
257 pub align_items: AlignItems,
258 pub align_content: AlignContent,
259}
260
261pub struct FlexLayout {
263 pub style: FlexContainerStyle,
265 pub available_main: FixedPoint,
267 pub available_cross: FixedPoint,
269}
270
271impl FlexLayout {
272 pub fn new(
273 style: FlexContainerStyle,
274 available_main: FixedPoint,
275 available_cross: FixedPoint,
276 ) -> Self {
277 Self {
278 style,
279 available_main,
280 available_cross,
281 }
282 }
283
284 pub fn layout(&self, items: &mut [FlexItem]) {
287 if items.is_empty() {
288 return;
289 }
290
291 items.sort_by_key(|item| item.order);
293
294 if self.style.direction.is_reverse() {
296 items.reverse();
297 }
298
299 let lines = self.collect_into_lines(items);
301
302 for line in &lines {
304 self.resolve_flexible_lengths(items, &line.item_indices);
305 }
306
307 let mut computed_lines = self.compute_cross_sizes(items, &lines);
309
310 self.distribute_cross_space(&mut computed_lines, items);
312
313 for line in &computed_lines {
315 self.position_main_axis(items, &line.item_indices);
316 }
317
318 for line in &computed_lines {
320 self.position_cross_axis(items, line);
321 }
322 }
323
324 fn collect_into_lines(&self, items: &[FlexItem]) -> Vec<FlexLine> {
326 let mut lines = Vec::new();
327 let mut current_line = FlexLine::default();
328 let mut line_main_size: FixedPoint = FP_ZERO;
329
330 for (i, item) in items.iter().enumerate() {
331 let item_main = if item.flex_basis > 0 {
332 item.flex_basis
333 } else {
334 item.hyp_main
335 };
336
337 if self.style.wrap != FlexWrap::NoWrap
338 && !current_line.item_indices.is_empty()
339 && line_main_size + item_main > self.available_main
340 {
341 lines.push(current_line);
342 current_line = FlexLine::default();
343 line_main_size = FP_ZERO;
344 }
345
346 current_line.item_indices.push(i);
347 line_main_size += item_main;
348 }
349
350 if !current_line.item_indices.is_empty() {
351 lines.push(current_line);
352 }
353
354 if self.style.wrap == FlexWrap::WrapReverse {
355 lines.reverse();
356 }
357
358 lines
359 }
360
361 fn resolve_flexible_lengths(&self, items: &mut [FlexItem], indices: &[usize]) {
363 for &idx in indices {
365 items[idx].frozen = false;
366 items[idx].target_main = if items[idx].flex_basis > 0 {
367 items[idx].flex_basis
368 } else {
369 items[idx].hyp_main
370 };
371 }
372
373 let used: FixedPoint = indices.iter().map(|&i| items[i].target_main).sum();
375
376 let free_space = self.available_main - used;
377 let growing = free_space > 0;
378
379 for &idx in indices {
381 let item = &mut items[idx];
382 if (growing && item.flex_grow == 0) || (!growing && item.flex_shrink == 0) {
383 item.frozen = true;
384 item.main_size = item.target_main;
385 }
386 }
387
388 for _ in 0..10 {
390 let unfrozen: Vec<usize> = indices
391 .iter()
392 .copied()
393 .filter(|&i| !items[i].frozen)
394 .collect();
395
396 if unfrozen.is_empty() {
397 break;
398 }
399
400 let frozen_size: FixedPoint = indices
402 .iter()
403 .filter(|&&i| items[i].frozen)
404 .map(|&i| items[i].main_size)
405 .sum();
406 let unfrozen_basis: FixedPoint = unfrozen.iter().map(|&i| items[i].target_main).sum();
407 let remaining = self.available_main - frozen_size - unfrozen_basis;
408
409 if growing {
410 let total_grow: FixedPoint = unfrozen.iter().map(|&i| items[i].flex_grow).sum();
411 if total_grow > 0 {
412 for &idx in &unfrozen {
413 let addition = ((remaining as i64 * items[idx].flex_grow as i64)
415 / total_grow as i64) as i32;
416 items[idx].target_main += addition;
417 }
418 }
419 } else {
420 let total_shrink: FixedPoint = unfrozen
421 .iter()
422 .map(|&i| fp_mul(items[i].flex_shrink, items[i].target_main))
423 .sum();
424 if total_shrink > 0 {
425 for &idx in &unfrozen {
426 let scaled = fp_mul(items[idx].flex_shrink, items[idx].target_main);
427 let ratio = fp_div(scaled, total_shrink);
428 let reduction = fp_mul(remaining.abs(), ratio);
429 items[idx].target_main -= reduction;
430 }
431 }
432 }
433
434 let mut all_ok = true;
436 for &idx in &unfrozen {
437 let clamped = items[idx].clamp_main(items[idx].target_main);
438 if clamped != items[idx].target_main {
439 items[idx].target_main = clamped;
440 items[idx].frozen = true;
441 all_ok = false;
442 }
443 }
444
445 if all_ok {
447 for &idx in &unfrozen {
448 items[idx].frozen = true;
449 }
450 break;
451 }
452 }
453
454 for &idx in indices {
456 items[idx].main_size = items[idx].clamp_main(items[idx].target_main);
457 }
458 }
459
460 fn compute_cross_sizes(&self, items: &[FlexItem], lines: &[FlexLine]) -> Vec<FlexLine> {
462 let mut result = Vec::with_capacity(lines.len());
463 for line in lines {
464 let mut max_cross = FP_ZERO;
465 for &idx in &line.item_indices {
466 let cross = if items[idx].hyp_cross > 0 {
467 items[idx].hyp_cross
468 } else {
469 items[idx].main_size };
471 if cross > max_cross {
472 max_cross = cross;
473 }
474 }
475 result.push(FlexLine {
476 item_indices: line.item_indices.clone(),
477 cross_size: max_cross,
478 cross_offset: FP_ZERO,
479 });
480 }
481 result
482 }
483
484 fn distribute_cross_space(&self, lines: &mut [FlexLine], _items: &[FlexItem]) {
486 let total_cross: FixedPoint = lines.iter().map(|l| l.cross_size).sum();
487 let free_cross = self.available_cross - total_cross;
488
489 match self.style.align_content {
490 AlignContent::FlexStart => {
491 let mut offset = FP_ZERO;
492 for line in lines.iter_mut() {
493 line.cross_offset = offset;
494 offset += line.cross_size;
495 }
496 }
497 AlignContent::FlexEnd => {
498 let mut offset = free_cross.max(FP_ZERO);
499 for line in lines.iter_mut() {
500 line.cross_offset = offset;
501 offset += line.cross_size;
502 }
503 }
504 AlignContent::Center => {
505 let mut offset = (free_cross / 2).max(FP_ZERO);
506 for line in lines.iter_mut() {
507 line.cross_offset = offset;
508 offset += line.cross_size;
509 }
510 }
511 AlignContent::Stretch => {
512 let extra_per_line = if !lines.is_empty() && free_cross > 0 {
513 free_cross / lines.len() as i32
514 } else {
515 FP_ZERO
516 };
517 let mut offset = FP_ZERO;
518 for line in lines.iter_mut() {
519 line.cross_offset = offset;
520 line.cross_size += extra_per_line;
521 offset += line.cross_size;
522 }
523 }
524 AlignContent::SpaceBetween => {
525 let gap = if lines.len() > 1 && free_cross > 0 {
526 free_cross / (lines.len() as i32 - 1)
527 } else {
528 FP_ZERO
529 };
530 let mut offset = FP_ZERO;
531 for line in lines.iter_mut() {
532 line.cross_offset = offset;
533 offset += line.cross_size + gap;
534 }
535 }
536 AlignContent::SpaceAround => {
537 let gap = if !lines.is_empty() && free_cross > 0 {
538 free_cross / lines.len() as i32
539 } else {
540 FP_ZERO
541 };
542 let mut offset = gap / 2;
543 for line in lines.iter_mut() {
544 line.cross_offset = offset;
545 offset += line.cross_size + gap;
546 }
547 }
548 }
549 }
550
551 fn position_main_axis(&self, items: &mut [FlexItem], indices: &[usize]) {
553 let total_main: FixedPoint = indices.iter().map(|&i| items[i].main_size).sum();
554 let free = self.available_main - total_main;
555 let count = indices.len() as i32;
556
557 let (mut offset, gap) = match self.style.justify_content {
558 JustifyContent::FlexStart => (FP_ZERO, FP_ZERO),
559 JustifyContent::FlexEnd => (free.max(FP_ZERO), FP_ZERO),
560 JustifyContent::Center => ((free / 2).max(FP_ZERO), FP_ZERO),
561 JustifyContent::SpaceBetween => {
562 let g = if count > 1 && free > 0 {
563 free / (count - 1)
564 } else {
565 FP_ZERO
566 };
567 (FP_ZERO, g)
568 }
569 JustifyContent::SpaceAround => {
570 let g = if count > 0 && free > 0 {
571 free / count
572 } else {
573 FP_ZERO
574 };
575 (g / 2, g)
576 }
577 JustifyContent::SpaceEvenly => {
578 let g = if count > 0 && free > 0 {
579 free / (count + 1)
580 } else {
581 FP_ZERO
582 };
583 (g, g)
584 }
585 };
586
587 for &idx in indices {
588 items[idx].main_offset = offset;
589 offset += items[idx].main_size + gap;
590 }
591 }
592
593 fn position_cross_axis(&self, items: &mut [FlexItem], line: &FlexLine) {
595 for &idx in &line.item_indices {
596 let align = match items[idx].align_self {
597 AlignSelf::Auto => self.style.align_items,
598 AlignSelf::FlexStart => AlignItems::FlexStart,
599 AlignSelf::FlexEnd => AlignItems::FlexEnd,
600 AlignSelf::Center => AlignItems::Center,
601 AlignSelf::Stretch => AlignItems::Stretch,
602 AlignSelf::Baseline => AlignItems::Baseline,
603 };
604
605 let item_cross = if items[idx].hyp_cross > 0 {
606 items[idx].hyp_cross
607 } else {
608 line.cross_size
609 };
610
611 match align {
612 AlignItems::FlexStart => {
613 items[idx].cross_offset = line.cross_offset;
614 items[idx].cross_size = item_cross;
615 }
616 AlignItems::FlexEnd => {
617 items[idx].cross_offset = line.cross_offset + line.cross_size - item_cross;
618 items[idx].cross_size = item_cross;
619 }
620 AlignItems::Center => {
621 items[idx].cross_offset =
622 line.cross_offset + (line.cross_size - item_cross) / 2;
623 items[idx].cross_size = item_cross;
624 }
625 AlignItems::Stretch => {
626 items[idx].cross_offset = line.cross_offset;
627 items[idx].cross_size = line.cross_size;
628 }
629 AlignItems::Baseline => {
630 items[idx].cross_offset = line.cross_offset;
632 items[idx].cross_size = item_cross;
633 }
634 }
635 }
636 }
637
638 pub fn item_rect(
641 item: &FlexItem,
642 direction: FlexDirection,
643 ) -> (FixedPoint, FixedPoint, FixedPoint, FixedPoint) {
644 if direction.is_row() {
645 (
646 item.main_offset,
647 item.cross_offset,
648 item.main_size,
649 item.cross_size,
650 )
651 } else {
652 (
653 item.cross_offset,
654 item.main_offset,
655 item.cross_size,
656 item.main_size,
657 )
658 }
659 }
660}
661
662#[cfg(test)]
667mod tests {
668 #[allow(unused_imports)]
669 use alloc::vec;
670
671 use super::*;
672
673 fn make_item(node_id: NodeId, basis: i32, grow: i32, shrink: i32) -> FlexItem {
674 FlexItem {
675 node_id,
676 flex_basis: fp(basis),
677 flex_grow: fp(grow),
678 flex_shrink: fp(shrink),
679 hyp_main: fp(basis),
680 hyp_cross: fp(30),
681 ..Default::default()
682 }
683 }
684
685 #[test]
686 fn test_fp_helpers() {
687 assert_eq!(fp(10), 640);
688 assert_eq!(fp_int(fp(10)), 10);
689 assert_eq!(fp_int(fp_mul(fp(3), fp(4))), 12);
690 }
691
692 #[test]
693 fn test_fp_div() {
694 assert_eq!(fp_int(fp_div(fp(10), fp(2))), 5);
695 assert_eq!(fp_div(fp(1), fp(0)), 0);
696 }
697
698 #[test]
699 fn test_direction_is_row() {
700 assert!(FlexDirection::Row.is_row());
701 assert!(FlexDirection::RowReverse.is_row());
702 assert!(!FlexDirection::Column.is_row());
703 }
704
705 #[test]
706 fn test_direction_is_reverse() {
707 assert!(!FlexDirection::Row.is_reverse());
708 assert!(FlexDirection::RowReverse.is_reverse());
709 assert!(FlexDirection::ColumnReverse.is_reverse());
710 }
711
712 #[test]
713 fn test_single_item_fills_container() {
714 let style = FlexContainerStyle {
715 direction: FlexDirection::Row,
716 justify_content: JustifyContent::FlexStart,
717 align_items: AlignItems::Stretch,
718 ..Default::default()
719 };
720 let layout = FlexLayout::new(style, fp(300), fp(100));
721 let mut items = vec![make_item(0, 100, 1, 0)];
722 layout.layout(&mut items);
723 assert_eq!(fp_int(items[0].main_size), 300);
725 }
726
727 #[test]
728 fn test_two_items_equal_grow() {
729 let style = FlexContainerStyle::default();
730 let layout = FlexLayout::new(style, fp(200), fp(100));
731 let mut items = vec![make_item(0, 50, 1, 0), make_item(1, 50, 1, 0)];
732 layout.layout(&mut items);
733 assert_eq!(fp_int(items[0].main_size), 100);
735 assert_eq!(fp_int(items[1].main_size), 100);
736 }
737
738 #[test]
739 fn test_unequal_grow() {
740 let style = FlexContainerStyle::default();
741 let layout = FlexLayout::new(style, fp(300), fp(100));
742 let mut items = vec![make_item(0, 0, 1, 0), make_item(1, 0, 2, 0)];
743 layout.layout(&mut items);
744 assert_eq!(fp_int(items[0].main_size), 100);
746 assert_eq!(fp_int(items[1].main_size), 200);
747 }
748
749 #[test]
750 fn test_no_grow() {
751 let style = FlexContainerStyle::default();
752 let layout = FlexLayout::new(style, fp(300), fp(100));
753 let mut items = vec![make_item(0, 80, 0, 0), make_item(1, 60, 0, 0)];
754 layout.layout(&mut items);
755 assert_eq!(fp_int(items[0].main_size), 80);
756 assert_eq!(fp_int(items[1].main_size), 60);
757 }
758
759 #[test]
760 fn test_justify_center() {
761 let style = FlexContainerStyle {
762 justify_content: JustifyContent::Center,
763 ..Default::default()
764 };
765 let layout = FlexLayout::new(style, fp(300), fp(100));
766 let mut items = vec![make_item(0, 100, 0, 0)];
767 layout.layout(&mut items);
768 assert_eq!(fp_int(items[0].main_offset), 100);
770 }
771
772 #[test]
773 fn test_justify_flex_end() {
774 let style = FlexContainerStyle {
775 justify_content: JustifyContent::FlexEnd,
776 ..Default::default()
777 };
778 let layout = FlexLayout::new(style, fp(300), fp(100));
779 let mut items = vec![make_item(0, 100, 0, 0)];
780 layout.layout(&mut items);
781 assert_eq!(fp_int(items[0].main_offset), 200);
782 }
783
784 #[test]
785 fn test_justify_space_between() {
786 let style = FlexContainerStyle {
787 justify_content: JustifyContent::SpaceBetween,
788 ..Default::default()
789 };
790 let layout = FlexLayout::new(style, fp(300), fp(100));
791 let mut items = vec![
792 make_item(0, 50, 0, 0),
793 make_item(1, 50, 0, 0),
794 make_item(2, 50, 0, 0),
795 ];
796 layout.layout(&mut items);
797 assert_eq!(fp_int(items[0].main_offset), 0);
799 assert_eq!(fp_int(items[1].main_offset), 125); assert_eq!(fp_int(items[2].main_offset), 250); }
802
803 #[test]
804 fn test_justify_space_evenly() {
805 let style = FlexContainerStyle {
806 justify_content: JustifyContent::SpaceEvenly,
807 ..Default::default()
808 };
809 let layout = FlexLayout::new(style, fp(400), fp(100));
810 let mut items = vec![make_item(0, 50, 0, 0), make_item(1, 50, 0, 0)];
811 layout.layout(&mut items);
812 assert_eq!(fp_int(items[0].main_offset), 100);
814 assert_eq!(fp_int(items[1].main_offset), 250);
815 }
816
817 #[test]
818 fn test_align_items_center() {
819 let style = FlexContainerStyle {
820 align_items: AlignItems::Center,
821 ..Default::default()
822 };
823 let layout = FlexLayout::new(style, fp(300), fp(100));
824 let mut items = vec![make_item(0, 100, 0, 0)];
825 items[0].hyp_cross = fp(40);
826 layout.layout(&mut items);
827 assert_eq!(fp_int(items[0].cross_offset), 30);
833 }
834
835 #[test]
836 fn test_align_items_stretch() {
837 let style = FlexContainerStyle {
838 align_items: AlignItems::Stretch,
839 ..Default::default()
840 };
841 let layout = FlexLayout::new(style, fp(300), fp(100));
842 let mut items = vec![make_item(0, 100, 0, 0)];
843 items[0].hyp_cross = fp(40);
844 layout.layout(&mut items);
845 assert_eq!(fp_int(items[0].cross_size), 100);
847 }
848
849 #[test]
850 fn test_wrap_basic() {
851 let style = FlexContainerStyle {
852 wrap: FlexWrap::Wrap,
853 ..Default::default()
854 };
855 let layout = FlexLayout::new(style, fp(200), fp(200));
856 let mut items = vec![make_item(0, 120, 0, 0), make_item(1, 120, 0, 0)];
857 layout.layout(&mut items);
858 assert_eq!(fp_int(items[0].main_offset), 0);
861 assert_eq!(fp_int(items[1].main_offset), 0);
862 assert!(items[1].cross_offset > items[0].cross_offset);
864 }
865
866 #[test]
867 fn test_nowrap() {
868 let style = FlexContainerStyle {
869 wrap: FlexWrap::NoWrap,
870 ..Default::default()
871 };
872 let layout = FlexLayout::new(style, fp(200), fp(100));
873 let mut items = vec![make_item(0, 120, 0, 1), make_item(1, 120, 0, 1)];
874 layout.layout(&mut items);
875 }
878
879 #[test]
880 fn test_order() {
881 let style = FlexContainerStyle::default();
882 let layout = FlexLayout::new(style, fp(300), fp(100));
883 let mut items = vec![
884 {
885 let mut item = make_item(0, 50, 0, 0);
886 item.order = 2;
887 item
888 },
889 {
890 let mut item = make_item(1, 50, 0, 0);
891 item.order = 1;
892 item
893 },
894 ];
895 layout.layout(&mut items);
896 assert!(items[0].order <= items[1].order);
898 }
899
900 #[test]
901 fn test_reverse() {
902 let style = FlexContainerStyle {
903 direction: FlexDirection::RowReverse,
904 ..Default::default()
905 };
906 let layout = FlexLayout::new(style, fp(300), fp(100));
907 let mut items = vec![make_item(0, 100, 0, 0), make_item(1, 100, 0, 0)];
908 layout.layout(&mut items);
909 }
911
912 #[test]
913 fn test_column_direction() {
914 let style = FlexContainerStyle {
915 direction: FlexDirection::Column,
916 ..Default::default()
917 };
918 let layout = FlexLayout::new(style, fp(400), fp(300));
919 let mut items = vec![make_item(0, 100, 1, 0), make_item(1, 100, 1, 0)];
920 layout.layout(&mut items);
921 assert_eq!(fp_int(items[0].main_size), 200);
923 assert_eq!(fp_int(items[1].main_size), 200);
924 }
925
926 #[test]
927 fn test_item_rect_row() {
928 let mut item = FlexItem::new(0);
929 item.main_offset = fp(10);
930 item.cross_offset = fp(20);
931 item.main_size = fp(100);
932 item.cross_size = fp(50);
933 let (x, y, w, h) = FlexLayout::item_rect(&item, FlexDirection::Row);
934 assert_eq!(fp_int(x), 10);
935 assert_eq!(fp_int(y), 20);
936 assert_eq!(fp_int(w), 100);
937 assert_eq!(fp_int(h), 50);
938 }
939
940 #[test]
941 fn test_item_rect_column() {
942 let mut item = FlexItem::new(0);
943 item.main_offset = fp(10);
944 item.cross_offset = fp(20);
945 item.main_size = fp(100);
946 item.cross_size = fp(50);
947 let (x, y, w, h) = FlexLayout::item_rect(&item, FlexDirection::Column);
948 assert_eq!(fp_int(x), 20);
950 assert_eq!(fp_int(y), 10);
951 assert_eq!(fp_int(w), 50);
952 assert_eq!(fp_int(h), 100);
953 }
954
955 #[test]
956 fn test_shrink() {
957 let style = FlexContainerStyle::default();
958 let layout = FlexLayout::new(style, fp(100), fp(50));
959 let mut items = vec![make_item(0, 80, 0, 1), make_item(1, 80, 0, 1)];
960 layout.layout(&mut items);
961 assert_eq!(fp_int(items[0].main_size), 50);
964 assert_eq!(fp_int(items[1].main_size), 50);
965 }
966
967 #[test]
968 fn test_min_max_clamp() {
969 let style = FlexContainerStyle::default();
970 let layout = FlexLayout::new(style, fp(300), fp(100));
971 let mut items = vec![{
972 let mut item = make_item(0, 50, 1, 0);
973 item.max_main = fp(200);
974 item
975 }];
976 layout.layout(&mut items);
977 assert_eq!(fp_int(items[0].main_size), 200);
979 }
980
981 #[test]
982 fn test_align_self_override() {
983 let style = FlexContainerStyle {
984 align_items: AlignItems::FlexStart,
985 ..Default::default()
986 };
987 let layout = FlexLayout::new(style, fp(300), fp(100));
988 let mut items = vec![{
989 let mut item = make_item(0, 100, 0, 0);
990 item.align_self = AlignSelf::FlexEnd;
991 item.hyp_cross = fp(30);
992 item
993 }];
994 layout.layout(&mut items);
995 assert_eq!(fp_int(items[0].cross_offset), 70);
999 }
1000
1001 #[test]
1002 fn test_empty_items() {
1003 let style = FlexContainerStyle::default();
1004 let layout = FlexLayout::new(style, fp(300), fp(100));
1005 let mut items: Vec<FlexItem> = Vec::new();
1006 layout.layout(&mut items);
1007 }
1009
1010 #[test]
1011 fn test_flex_defaults() {
1012 let item = FlexItem::default();
1013 assert_eq!(item.flex_grow, FP_ZERO);
1014 assert_eq!(item.flex_shrink, FP_ONE);
1015 assert_eq!(item.order, 0);
1016 }
1017
1018 #[test]
1019 fn test_justify_space_around() {
1020 let style = FlexContainerStyle {
1021 justify_content: JustifyContent::SpaceAround,
1022 ..Default::default()
1023 };
1024 let layout = FlexLayout::new(style, fp(300), fp(100));
1025 let mut items = vec![make_item(0, 50, 0, 0), make_item(1, 50, 0, 0)];
1026 layout.layout(&mut items);
1027 assert_eq!(fp_int(items[0].main_offset), 50);
1029 assert_eq!(fp_int(items[1].main_offset), 200);
1030 }
1031}