1use std::collections::HashMap;
8
9use taffy::prelude::*;
10
11use crate::focus::WidgetId;
12use crate::geometry::Rect;
13
14#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
16pub struct LayoutRect {
17 pub x: u16,
19 pub y: u16,
21 pub width: u16,
23 pub height: u16,
25}
26
27impl LayoutRect {
28 pub const fn new(x: u16, y: u16, width: u16, height: u16) -> Self {
30 Self {
31 x,
32 y,
33 width,
34 height,
35 }
36 }
37
38 pub const fn to_rect(self) -> Rect {
40 Rect::new(self.x, self.y, self.width, self.height)
41 }
42}
43
44#[derive(Clone, Debug, PartialEq, Eq)]
46pub enum LayoutError {
47 WidgetNotFound(WidgetId),
49 TaffyError(String),
51 NoRoot,
53}
54
55impl std::fmt::Display for LayoutError {
56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 match self {
58 Self::WidgetNotFound(id) => write!(f, "widget not found: {id}"),
59 Self::TaffyError(e) => write!(f, "taffy error: {e}"),
60 Self::NoRoot => write!(f, "no root node set"),
61 }
62 }
63}
64
65impl std::error::Error for LayoutError {}
66
67pub struct LayoutEngine {
72 taffy: TaffyTree<()>,
73 widget_to_node: HashMap<WidgetId, NodeId>,
74 node_to_widget: HashMap<NodeId, WidgetId>,
75 root: Option<NodeId>,
76}
77
78impl LayoutEngine {
79 pub fn new() -> Self {
81 Self {
82 taffy: TaffyTree::new(),
83 widget_to_node: HashMap::new(),
84 node_to_widget: HashMap::new(),
85 root: None,
86 }
87 }
88
89 pub fn add_node(&mut self, widget_id: WidgetId, style: Style) -> Result<(), LayoutError> {
91 let node = self
92 .taffy
93 .new_leaf(style)
94 .map_err(|e| LayoutError::TaffyError(format!("{e}")))?;
95 self.widget_to_node.insert(widget_id, node);
96 self.node_to_widget.insert(node, widget_id);
97 Ok(())
98 }
99
100 pub fn add_node_with_children(
102 &mut self,
103 widget_id: WidgetId,
104 style: Style,
105 children: &[WidgetId],
106 ) -> Result<(), LayoutError> {
107 let child_nodes: Vec<NodeId> = children
108 .iter()
109 .map(|id| {
110 self.widget_to_node
111 .get(id)
112 .copied()
113 .ok_or(LayoutError::WidgetNotFound(*id))
114 })
115 .collect::<Result<Vec<_>, _>>()?;
116
117 let node = self
118 .taffy
119 .new_with_children(style, &child_nodes)
120 .map_err(|e| LayoutError::TaffyError(format!("{e}")))?;
121 self.widget_to_node.insert(widget_id, node);
122 self.node_to_widget.insert(node, widget_id);
123 Ok(())
124 }
125
126 pub fn set_root(&mut self, widget_id: WidgetId) -> Result<(), LayoutError> {
128 let node = self
129 .widget_to_node
130 .get(&widget_id)
131 .copied()
132 .ok_or(LayoutError::WidgetNotFound(widget_id))?;
133 self.root = Some(node);
134 Ok(())
135 }
136
137 pub fn update_style(&mut self, widget_id: WidgetId, style: Style) -> Result<(), LayoutError> {
139 let node = self
140 .widget_to_node
141 .get(&widget_id)
142 .copied()
143 .ok_or(LayoutError::WidgetNotFound(widget_id))?;
144 self.taffy
145 .set_style(node, style)
146 .map_err(|e| LayoutError::TaffyError(format!("{e}")))?;
147 Ok(())
148 }
149
150 pub fn set_children(
152 &mut self,
153 widget_id: WidgetId,
154 children: &[WidgetId],
155 ) -> Result<(), LayoutError> {
156 let node = self
157 .widget_to_node
158 .get(&widget_id)
159 .copied()
160 .ok_or(LayoutError::WidgetNotFound(widget_id))?;
161 let child_nodes: Vec<NodeId> = children
162 .iter()
163 .map(|id| {
164 self.widget_to_node
165 .get(id)
166 .copied()
167 .ok_or(LayoutError::WidgetNotFound(*id))
168 })
169 .collect::<Result<Vec<_>, _>>()?;
170 self.taffy
171 .set_children(node, &child_nodes)
172 .map_err(|e| LayoutError::TaffyError(format!("{e}")))?;
173 Ok(())
174 }
175
176 pub fn remove_node(&mut self, widget_id: WidgetId) -> Result<(), LayoutError> {
178 let node = self
179 .widget_to_node
180 .remove(&widget_id)
181 .ok_or(LayoutError::WidgetNotFound(widget_id))?;
182 self.node_to_widget.remove(&node);
183 self.taffy
184 .remove(node)
185 .map_err(|e| LayoutError::TaffyError(format!("{e}")))?;
186 if self.root == Some(node) {
187 self.root = None;
188 }
189 Ok(())
190 }
191
192 pub fn compute(
194 &mut self,
195 available_width: u16,
196 available_height: u16,
197 ) -> Result<(), LayoutError> {
198 let root = self.root.ok_or(LayoutError::NoRoot)?;
199 let available = taffy::Size {
200 width: AvailableSpace::Definite(f32::from(available_width)),
201 height: AvailableSpace::Definite(f32::from(available_height)),
202 };
203 self.taffy
204 .compute_layout(root, available)
205 .map_err(|e| LayoutError::TaffyError(format!("{e}")))?;
206 Ok(())
207 }
208
209 pub fn layout(&self, widget_id: WidgetId) -> Result<LayoutRect, LayoutError> {
211 let node = self
212 .widget_to_node
213 .get(&widget_id)
214 .copied()
215 .ok_or(LayoutError::WidgetNotFound(widget_id))?;
216 let layout = self
217 .taffy
218 .layout(node)
219 .map_err(|e| LayoutError::TaffyError(format!("{e}")))?;
220
221 Ok(LayoutRect {
222 x: round_position(layout.location.x),
223 y: round_position(layout.location.y),
224 width: round_size(layout.size.width),
225 height: round_size(layout.size.height),
226 })
227 }
228
229 pub fn layout_rect(&self, widget_id: WidgetId) -> Result<Rect, LayoutError> {
231 self.layout(widget_id).map(|lr| lr.to_rect())
232 }
233
234 pub fn has_node(&self, widget_id: WidgetId) -> bool {
236 self.widget_to_node.contains_key(&widget_id)
237 }
238
239 pub fn node_count(&self) -> usize {
241 self.widget_to_node.len()
242 }
243}
244
245impl Default for LayoutEngine {
246 fn default() -> Self {
247 Self::new()
248 }
249}
250
251pub fn round_position(value: f32) -> u16 {
253 if value < 0.0 {
254 0
255 } else if value > f32::from(u16::MAX) {
256 u16::MAX
257 } else {
258 value.floor() as u16
259 }
260}
261
262pub fn round_size(value: f32) -> u16 {
264 if value < 0.0 {
265 0
266 } else if value > f32::from(u16::MAX) {
267 u16::MAX
268 } else {
269 value.round() as u16
270 }
271}
272
273#[cfg(test)]
274mod tests {
275 use super::*;
276 use crate::geometry::Rect;
277 use taffy::prelude::{
278 AlignItems, Dimension, Display, FlexDirection, GridPlacement, JustifyContent,
279 LengthPercentage, LengthPercentageAuto, Line, Style, auto, fr, length,
280 };
281
282 fn wid(n: u64) -> WidgetId {
283 n
284 }
285
286 #[test]
287 fn empty_engine() {
288 let engine = LayoutEngine::new();
289 assert_eq!(engine.node_count(), 0);
290 assert!(!engine.has_node(wid(1)));
291 }
292
293 #[test]
294 fn add_leaf_node() {
295 let mut engine = LayoutEngine::new();
296 let result = engine.add_node(wid(1), Style::default());
297 assert!(result.is_ok());
298 assert!(engine.has_node(wid(1)));
299 assert_eq!(engine.node_count(), 1);
300 }
301
302 #[test]
303 fn add_with_children() {
304 let mut engine = LayoutEngine::new();
305 engine.add_node(wid(1), Style::default()).ok();
306 engine.add_node(wid(2), Style::default()).ok();
307 let result = engine.add_node_with_children(wid(3), Style::default(), &[wid(1), wid(2)]);
308 assert!(result.is_ok());
309 assert_eq!(engine.node_count(), 3);
310 }
311
312 #[test]
313 fn set_root() {
314 let mut engine = LayoutEngine::new();
315 engine.add_node(wid(1), Style::default()).ok();
316 let result = engine.set_root(wid(1));
317 assert!(result.is_ok());
318 }
319
320 #[test]
321 fn remove_node() {
322 let mut engine = LayoutEngine::new();
323 engine.add_node(wid(1), Style::default()).ok();
324 assert!(engine.has_node(wid(1)));
325 let result = engine.remove_node(wid(1));
326 assert!(result.is_ok());
327 assert!(!engine.has_node(wid(1)));
328 assert_eq!(engine.node_count(), 0);
329 }
330
331 #[test]
332 fn update_style() {
333 let mut engine = LayoutEngine::new();
334 engine.add_node(wid(1), Style::default()).ok();
335 let new_style = Style {
336 size: taffy::Size {
337 width: Dimension::Length(50.0),
338 height: Dimension::Length(25.0),
339 },
340 ..Default::default()
341 };
342 let result = engine.update_style(wid(1), new_style);
343 assert!(result.is_ok());
344 }
345
346 #[test]
347 fn compute_single_node() {
348 let mut engine = LayoutEngine::new();
349 engine
350 .add_node(
351 wid(1),
352 Style {
353 size: taffy::Size {
354 width: Dimension::Length(80.0),
355 height: Dimension::Length(24.0),
356 },
357 ..Default::default()
358 },
359 )
360 .ok();
361 engine.set_root(wid(1)).ok();
362 let result = engine.compute(80, 24);
363 assert!(result.is_ok());
364
365 let layout = engine.layout(wid(1));
366 assert!(layout.is_ok());
367 let rect = layout.ok();
368 assert!(rect.is_some());
369 let rect = rect.unwrap_or_default();
370 assert_eq!(rect.width, 80);
371 assert_eq!(rect.height, 24);
372 }
373
374 #[test]
375 fn compute_two_children_row() {
376 let mut engine = LayoutEngine::new();
377 engine
378 .add_node(
379 wid(1),
380 Style {
381 flex_grow: 1.0,
382 ..Default::default()
383 },
384 )
385 .ok();
386 engine
387 .add_node(
388 wid(2),
389 Style {
390 flex_grow: 1.0,
391 ..Default::default()
392 },
393 )
394 .ok();
395 engine
396 .add_node_with_children(
397 wid(3),
398 Style {
399 size: taffy::Size {
400 width: Dimension::Length(80.0),
401 height: Dimension::Length(24.0),
402 },
403 ..Default::default()
404 },
405 &[wid(1), wid(2)],
406 )
407 .ok();
408 engine.set_root(wid(3)).ok();
409 engine.compute(80, 24).ok();
410
411 let l1 = engine.layout(wid(1)).unwrap_or_default();
412 let l2 = engine.layout(wid(2)).unwrap_or_default();
413 assert_eq!(l1.width, 40);
414 assert_eq!(l2.width, 40);
415 assert_eq!(l1.height, 24);
416 }
417
418 #[test]
419 fn compute_two_children_column() {
420 let mut engine = LayoutEngine::new();
421 engine
422 .add_node(
423 wid(1),
424 Style {
425 flex_grow: 1.0,
426 ..Default::default()
427 },
428 )
429 .ok();
430 engine
431 .add_node(
432 wid(2),
433 Style {
434 flex_grow: 1.0,
435 ..Default::default()
436 },
437 )
438 .ok();
439 engine
440 .add_node_with_children(
441 wid(3),
442 Style {
443 flex_direction: FlexDirection::Column,
444 size: taffy::Size {
445 width: Dimension::Length(80.0),
446 height: Dimension::Length(24.0),
447 },
448 ..Default::default()
449 },
450 &[wid(1), wid(2)],
451 )
452 .ok();
453 engine.set_root(wid(3)).ok();
454 engine.compute(80, 24).ok();
455
456 let l1 = engine.layout(wid(1)).unwrap_or_default();
457 let l2 = engine.layout(wid(2)).unwrap_or_default();
458 assert_eq!(l1.height, 12);
459 assert_eq!(l2.height, 12);
460 assert_eq!(l1.width, 80);
461 }
462
463 #[test]
464 fn layout_rect_conversion() {
465 let lr = LayoutRect::new(5, 10, 40, 20);
466 let rect = lr.to_rect();
467 assert_eq!(rect, Rect::new(5, 10, 40, 20));
468 }
469
470 #[test]
471 fn widget_not_found_error() {
472 let engine = LayoutEngine::new();
473 let result = engine.layout(wid(999));
474 assert!(result.is_err());
475 match result {
476 Err(LayoutError::WidgetNotFound(id)) => assert_eq!(id, wid(999)),
477 _ => unreachable!(),
478 }
479 }
480
481 #[test]
482 fn no_root_error() {
483 let mut engine = LayoutEngine::new();
484 let result = engine.compute(80, 24);
485 assert!(result.is_err());
486 match result {
487 Err(LayoutError::NoRoot) => {}
488 _ => unreachable!(),
489 }
490 }
491
492 #[test]
493 fn round_position_values() {
494 assert_eq!(round_position(0.0), 0);
495 assert_eq!(round_position(5.7), 5); assert_eq!(round_position(10.99), 10); assert_eq!(round_position(-1.0), 0); }
499
500 #[test]
501 fn round_size_values() {
502 assert_eq!(round_size(0.0), 0);
503 assert_eq!(round_size(5.4), 5); assert_eq!(round_size(5.5), 6); assert_eq!(round_size(-1.0), 0); }
507
508 #[test]
509 fn children_not_found_error() {
510 let mut engine = LayoutEngine::new();
511 let result = engine.add_node_with_children(wid(1), Style::default(), &[wid(999)]);
512 assert!(result.is_err());
513 match result {
514 Err(LayoutError::WidgetNotFound(id)) => assert_eq!(id, wid(999)),
515 _ => unreachable!(),
516 }
517 }
518
519 #[test]
520 fn remove_root_clears_root() {
521 let mut engine = LayoutEngine::new();
522 engine.add_node(wid(1), Style::default()).ok();
523 engine.set_root(wid(1)).ok();
524 engine.remove_node(wid(1)).ok();
525 let result = engine.compute(80, 24);
526 assert!(matches!(result, Err(LayoutError::NoRoot)));
527 }
528
529 #[test]
532 fn flex_row_equal_grow() {
533 let mut engine = LayoutEngine::new();
534 for i in 1..=3 {
535 engine
536 .add_node(
537 wid(i),
538 Style {
539 flex_grow: 1.0,
540 ..Default::default()
541 },
542 )
543 .ok();
544 }
545 engine
546 .add_node_with_children(
547 wid(10),
548 Style {
549 size: taffy::Size {
550 width: Dimension::Length(90.0),
551 height: Dimension::Length(30.0),
552 },
553 ..Default::default()
554 },
555 &[wid(1), wid(2), wid(3)],
556 )
557 .ok();
558 engine.set_root(wid(10)).ok();
559 engine.compute(90, 30).ok();
560
561 for i in 1..=3 {
562 let l = engine.layout(wid(i)).unwrap_or_default();
563 assert_eq!(l.width, 30, "child {i} width should be 30");
564 }
565 }
566
567 #[test]
568 fn flex_column_equal_grow() {
569 let mut engine = LayoutEngine::new();
570 for i in 1..=3 {
571 engine
572 .add_node(
573 wid(i),
574 Style {
575 flex_grow: 1.0,
576 ..Default::default()
577 },
578 )
579 .ok();
580 }
581 engine
582 .add_node_with_children(
583 wid(10),
584 Style {
585 flex_direction: FlexDirection::Column,
586 size: taffy::Size {
587 width: Dimension::Length(60.0),
588 height: Dimension::Length(30.0),
589 },
590 ..Default::default()
591 },
592 &[wid(1), wid(2), wid(3)],
593 )
594 .ok();
595 engine.set_root(wid(10)).ok();
596 engine.compute(60, 30).ok();
597
598 for i in 1..=3 {
599 let l = engine.layout(wid(i)).unwrap_or_default();
600 assert_eq!(l.height, 10, "child {i} height should be 10");
601 }
602 }
603
604 #[test]
605 fn flex_row_unequal_grow() {
606 let mut engine = LayoutEngine::new();
607 engine
608 .add_node(
609 wid(1),
610 Style {
611 flex_grow: 1.0,
612 ..Default::default()
613 },
614 )
615 .ok();
616 engine
617 .add_node(
618 wid(2),
619 Style {
620 flex_grow: 2.0,
621 ..Default::default()
622 },
623 )
624 .ok();
625 engine
626 .add_node(
627 wid(3),
628 Style {
629 flex_grow: 1.0,
630 ..Default::default()
631 },
632 )
633 .ok();
634 engine
635 .add_node_with_children(
636 wid(10),
637 Style {
638 size: taffy::Size {
639 width: Dimension::Length(80.0),
640 height: Dimension::Length(20.0),
641 },
642 ..Default::default()
643 },
644 &[wid(1), wid(2), wid(3)],
645 )
646 .ok();
647 engine.set_root(wid(10)).ok();
648 engine.compute(80, 20).ok();
649
650 let l1 = engine.layout(wid(1)).unwrap_or_default();
651 let l2 = engine.layout(wid(2)).unwrap_or_default();
652 let l3 = engine.layout(wid(3)).unwrap_or_default();
653 assert_eq!(l1.width, 20);
654 assert_eq!(l2.width, 40);
655 assert_eq!(l3.width, 20);
656 }
657
658 #[test]
659 fn flex_column_fixed_and_grow() {
660 let mut engine = LayoutEngine::new();
661 engine
662 .add_node(
663 wid(1),
664 Style {
665 size: taffy::Size {
666 width: auto(),
667 height: Dimension::Length(5.0),
668 },
669 ..Default::default()
670 },
671 )
672 .ok();
673 engine
674 .add_node(
675 wid(2),
676 Style {
677 flex_grow: 1.0,
678 ..Default::default()
679 },
680 )
681 .ok();
682 engine
683 .add_node_with_children(
684 wid(10),
685 Style {
686 flex_direction: FlexDirection::Column,
687 size: taffy::Size {
688 width: Dimension::Length(80.0),
689 height: Dimension::Length(25.0),
690 },
691 ..Default::default()
692 },
693 &[wid(1), wid(2)],
694 )
695 .ok();
696 engine.set_root(wid(10)).ok();
697 engine.compute(80, 25).ok();
698
699 let l1 = engine.layout(wid(1)).unwrap_or_default();
700 let l2 = engine.layout(wid(2)).unwrap_or_default();
701 assert_eq!(l1.height, 5);
702 assert_eq!(l2.height, 20);
703 }
704
705 #[test]
706 fn flex_justify_center() {
707 let mut engine = LayoutEngine::new();
708 engine
709 .add_node(
710 wid(1),
711 Style {
712 size: taffy::Size {
713 width: Dimension::Length(20.0),
714 height: Dimension::Length(10.0),
715 },
716 ..Default::default()
717 },
718 )
719 .ok();
720 engine
721 .add_node_with_children(
722 wid(10),
723 Style {
724 justify_content: Some(JustifyContent::Center),
725 size: taffy::Size {
726 width: Dimension::Length(80.0),
727 height: Dimension::Length(10.0),
728 },
729 ..Default::default()
730 },
731 &[wid(1)],
732 )
733 .ok();
734 engine.set_root(wid(10)).ok();
735 engine.compute(80, 10).ok();
736
737 let l = engine.layout(wid(1)).unwrap_or_default();
738 assert_eq!(l.x, 30); }
740
741 #[test]
742 fn flex_justify_space_between() {
743 let mut engine = LayoutEngine::new();
744 engine
745 .add_node(
746 wid(1),
747 Style {
748 size: taffy::Size {
749 width: Dimension::Length(10.0),
750 height: Dimension::Length(10.0),
751 },
752 ..Default::default()
753 },
754 )
755 .ok();
756 engine
757 .add_node(
758 wid(2),
759 Style {
760 size: taffy::Size {
761 width: Dimension::Length(10.0),
762 height: Dimension::Length(10.0),
763 },
764 ..Default::default()
765 },
766 )
767 .ok();
768 engine
769 .add_node_with_children(
770 wid(10),
771 Style {
772 justify_content: Some(JustifyContent::SpaceBetween),
773 size: taffy::Size {
774 width: Dimension::Length(80.0),
775 height: Dimension::Length(10.0),
776 },
777 ..Default::default()
778 },
779 &[wid(1), wid(2)],
780 )
781 .ok();
782 engine.set_root(wid(10)).ok();
783 engine.compute(80, 10).ok();
784
785 let l1 = engine.layout(wid(1)).unwrap_or_default();
786 let l2 = engine.layout(wid(2)).unwrap_or_default();
787 assert_eq!(l1.x, 0);
788 assert_eq!(l2.x, 70); }
790
791 #[test]
792 fn flex_align_items_center() {
793 let mut engine = LayoutEngine::new();
794 engine
795 .add_node(
796 wid(1),
797 Style {
798 size: taffy::Size {
799 width: Dimension::Length(20.0),
800 height: Dimension::Length(10.0),
801 },
802 ..Default::default()
803 },
804 )
805 .ok();
806 engine
807 .add_node_with_children(
808 wid(10),
809 Style {
810 align_items: Some(AlignItems::Center),
811 size: taffy::Size {
812 width: Dimension::Length(80.0),
813 height: Dimension::Length(30.0),
814 },
815 ..Default::default()
816 },
817 &[wid(1)],
818 )
819 .ok();
820 engine.set_root(wid(10)).ok();
821 engine.compute(80, 30).ok();
822
823 let l = engine.layout(wid(1)).unwrap_or_default();
824 assert_eq!(l.y, 10); }
826
827 #[test]
828 fn flex_nested() {
829 let mut engine = LayoutEngine::new();
830 engine
832 .add_node(
833 wid(1),
834 Style {
835 flex_grow: 1.0,
836 ..Default::default()
837 },
838 )
839 .ok();
840 engine
841 .add_node(
842 wid(2),
843 Style {
844 flex_grow: 1.0,
845 ..Default::default()
846 },
847 )
848 .ok();
849 engine
851 .add_node_with_children(
852 wid(3),
853 Style {
854 flex_direction: FlexDirection::Column,
855 flex_grow: 1.0,
856 ..Default::default()
857 },
858 &[wid(1), wid(2)],
859 )
860 .ok();
861 engine
863 .add_node(
864 wid(4),
865 Style {
866 flex_grow: 1.0,
867 ..Default::default()
868 },
869 )
870 .ok();
871 engine
873 .add_node_with_children(
874 wid(10),
875 Style {
876 size: taffy::Size {
877 width: Dimension::Length(80.0),
878 height: Dimension::Length(20.0),
879 },
880 ..Default::default()
881 },
882 &[wid(3), wid(4)],
883 )
884 .ok();
885 engine.set_root(wid(10)).ok();
886 engine.compute(80, 20).ok();
887
888 let l3 = engine.layout(wid(3)).unwrap_or_default();
889 let l4 = engine.layout(wid(4)).unwrap_or_default();
890 assert_eq!(l3.width, 40);
891 assert_eq!(l4.width, 40);
892 let l1 = engine.layout(wid(1)).unwrap_or_default();
893 let l2 = engine.layout(wid(2)).unwrap_or_default();
894 assert_eq!(l1.height, 10);
895 assert_eq!(l2.height, 10);
896 }
897
898 #[test]
899 fn flex_with_gap() {
900 let mut engine = LayoutEngine::new();
901 engine
902 .add_node(
903 wid(1),
904 Style {
905 size: taffy::Size {
906 width: Dimension::Length(20.0),
907 height: Dimension::Length(10.0),
908 },
909 ..Default::default()
910 },
911 )
912 .ok();
913 engine
914 .add_node(
915 wid(2),
916 Style {
917 size: taffy::Size {
918 width: Dimension::Length(20.0),
919 height: Dimension::Length(10.0),
920 },
921 ..Default::default()
922 },
923 )
924 .ok();
925 engine
926 .add_node_with_children(
927 wid(10),
928 Style {
929 gap: taffy::Size {
930 width: LengthPercentage::Length(10.0),
931 height: LengthPercentage::Length(0.0),
932 },
933 size: taffy::Size {
934 width: Dimension::Length(80.0),
935 height: Dimension::Length(10.0),
936 },
937 ..Default::default()
938 },
939 &[wid(1), wid(2)],
940 )
941 .ok();
942 engine.set_root(wid(10)).ok();
943 engine.compute(80, 10).ok();
944
945 let l1 = engine.layout(wid(1)).unwrap_or_default();
946 let l2 = engine.layout(wid(2)).unwrap_or_default();
947 assert_eq!(l1.x, 0);
948 assert_eq!(l2.x, 30); }
950
951 #[test]
954 fn grid_two_columns_equal() {
955 let mut engine = LayoutEngine::new();
956 engine.add_node(wid(1), Style::default()).ok();
957 engine.add_node(wid(2), Style::default()).ok();
958 engine
959 .add_node_with_children(
960 wid(10),
961 Style {
962 display: Display::Grid,
963 grid_template_columns: vec![fr(1.0), fr(1.0)],
964 size: taffy::Size {
965 width: Dimension::Length(80.0),
966 height: Dimension::Length(20.0),
967 },
968 ..Default::default()
969 },
970 &[wid(1), wid(2)],
971 )
972 .ok();
973 engine.set_root(wid(10)).ok();
974 engine.compute(80, 20).ok();
975
976 let l1 = engine.layout(wid(1)).unwrap_or_default();
977 let l2 = engine.layout(wid(2)).unwrap_or_default();
978 assert_eq!(l1.width, 40);
979 assert_eq!(l2.width, 40);
980 }
981
982 #[test]
983 fn grid_three_columns_fr() {
984 let mut engine = LayoutEngine::new();
985 for i in 1..=3 {
986 engine.add_node(wid(i), Style::default()).ok();
987 }
988 engine
989 .add_node_with_children(
990 wid(10),
991 Style {
992 display: Display::Grid,
993 grid_template_columns: vec![fr(1.0), fr(2.0), fr(1.0)],
994 size: taffy::Size {
995 width: Dimension::Length(80.0),
996 height: Dimension::Length(20.0),
997 },
998 ..Default::default()
999 },
1000 &[wid(1), wid(2), wid(3)],
1001 )
1002 .ok();
1003 engine.set_root(wid(10)).ok();
1004 engine.compute(80, 20).ok();
1005
1006 let l1 = engine.layout(wid(1)).unwrap_or_default();
1007 let l2 = engine.layout(wid(2)).unwrap_or_default();
1008 let l3 = engine.layout(wid(3)).unwrap_or_default();
1009 assert_eq!(l1.width, 20);
1010 assert_eq!(l2.width, 40);
1011 assert_eq!(l3.width, 20);
1012 }
1013
1014 #[test]
1015 fn grid_columns_mixed_units() {
1016 let mut engine = LayoutEngine::new();
1017 engine.add_node(wid(1), Style::default()).ok();
1018 engine.add_node(wid(2), Style::default()).ok();
1019 engine
1020 .add_node_with_children(
1021 wid(10),
1022 Style {
1023 display: Display::Grid,
1024 grid_template_columns: vec![length(20.0), fr(1.0)],
1025 size: taffy::Size {
1026 width: Dimension::Length(80.0),
1027 height: Dimension::Length(20.0),
1028 },
1029 ..Default::default()
1030 },
1031 &[wid(1), wid(2)],
1032 )
1033 .ok();
1034 engine.set_root(wid(10)).ok();
1035 engine.compute(80, 20).ok();
1036
1037 let l1 = engine.layout(wid(1)).unwrap_or_default();
1038 let l2 = engine.layout(wid(2)).unwrap_or_default();
1039 assert_eq!(l1.width, 20);
1040 assert_eq!(l2.width, 60);
1041 }
1042
1043 #[test]
1044 fn grid_rows_and_columns() {
1045 let mut engine = LayoutEngine::new();
1046 for i in 1..=4 {
1047 engine.add_node(wid(i), Style::default()).ok();
1048 }
1049 engine
1050 .add_node_with_children(
1051 wid(10),
1052 Style {
1053 display: Display::Grid,
1054 grid_template_columns: vec![fr(1.0), fr(1.0)],
1055 grid_template_rows: vec![fr(1.0), fr(1.0)],
1056 size: taffy::Size {
1057 width: Dimension::Length(80.0),
1058 height: Dimension::Length(20.0),
1059 },
1060 ..Default::default()
1061 },
1062 &[wid(1), wid(2), wid(3), wid(4)],
1063 )
1064 .ok();
1065 engine.set_root(wid(10)).ok();
1066 engine.compute(80, 20).ok();
1067
1068 let l1 = engine.layout(wid(1)).unwrap_or_default();
1069 let l4 = engine.layout(wid(4)).unwrap_or_default();
1070 assert_eq!(l1.width, 40);
1071 assert_eq!(l1.height, 10);
1072 assert_eq!(l4.x, 40);
1073 assert_eq!(l4.y, 10);
1074 }
1075
1076 #[test]
1077 fn grid_placement_span() {
1078 let mut engine = LayoutEngine::new();
1079 engine
1081 .add_node(
1082 wid(1),
1083 Style {
1084 grid_column: Line {
1085 start: GridPlacement::from_span(2),
1086 end: GridPlacement::Auto,
1087 },
1088 ..Default::default()
1089 },
1090 )
1091 .ok();
1092 engine.add_node(wid(2), Style::default()).ok();
1093 engine.add_node(wid(3), Style::default()).ok();
1094 engine
1095 .add_node_with_children(
1096 wid(10),
1097 Style {
1098 display: Display::Grid,
1099 grid_template_columns: vec![fr(1.0), fr(1.0)],
1100 size: taffy::Size {
1101 width: Dimension::Length(80.0),
1102 height: Dimension::Length(30.0),
1103 },
1104 ..Default::default()
1105 },
1106 &[wid(1), wid(2), wid(3)],
1107 )
1108 .ok();
1109 engine.set_root(wid(10)).ok();
1110 engine.compute(80, 30).ok();
1111
1112 let l1 = engine.layout(wid(1)).unwrap_or_default();
1113 assert_eq!(l1.width, 80); }
1115
1116 #[test]
1117 fn box_model_padding_shrinks_content() {
1118 let mut engine = LayoutEngine::new();
1119 engine
1120 .add_node(
1121 wid(1),
1122 Style {
1123 flex_grow: 1.0,
1124 ..Default::default()
1125 },
1126 )
1127 .ok();
1128 engine
1129 .add_node_with_children(
1130 wid(10),
1131 Style {
1132 padding: taffy::Rect {
1133 left: LengthPercentage::Length(5.0),
1134 right: LengthPercentage::Length(5.0),
1135 top: LengthPercentage::Length(2.0),
1136 bottom: LengthPercentage::Length(2.0),
1137 },
1138 size: taffy::Size {
1139 width: Dimension::Length(80.0),
1140 height: Dimension::Length(24.0),
1141 },
1142 ..Default::default()
1143 },
1144 &[wid(1)],
1145 )
1146 .ok();
1147 engine.set_root(wid(10)).ok();
1148 engine.compute(80, 24).ok();
1149
1150 let l = engine.layout(wid(1)).unwrap_or_default();
1151 assert_eq!(l.x, 5);
1152 assert_eq!(l.y, 2);
1153 assert_eq!(l.width, 70); assert_eq!(l.height, 20); }
1156
1157 #[test]
1158 fn box_model_margin_creates_space() {
1159 let mut engine = LayoutEngine::new();
1160 engine
1161 .add_node(
1162 wid(1),
1163 Style {
1164 size: taffy::Size {
1165 width: Dimension::Length(30.0),
1166 height: Dimension::Length(10.0),
1167 },
1168 margin: taffy::Rect {
1169 left: LengthPercentageAuto::Length(0.0),
1170 right: LengthPercentageAuto::Length(5.0),
1171 top: LengthPercentageAuto::Length(0.0),
1172 bottom: LengthPercentageAuto::Length(0.0),
1173 },
1174 ..Default::default()
1175 },
1176 )
1177 .ok();
1178 engine
1179 .add_node(
1180 wid(2),
1181 Style {
1182 size: taffy::Size {
1183 width: Dimension::Length(30.0),
1184 height: Dimension::Length(10.0),
1185 },
1186 ..Default::default()
1187 },
1188 )
1189 .ok();
1190 engine
1191 .add_node_with_children(
1192 wid(10),
1193 Style {
1194 size: taffy::Size {
1195 width: Dimension::Length(80.0),
1196 height: Dimension::Length(10.0),
1197 },
1198 ..Default::default()
1199 },
1200 &[wid(1), wid(2)],
1201 )
1202 .ok();
1203 engine.set_root(wid(10)).ok();
1204 engine.compute(80, 10).ok();
1205
1206 let l2 = engine.layout(wid(2)).unwrap_or_default();
1207 assert_eq!(l2.x, 35); }
1209
1210 #[test]
1211 fn box_model_border_width() {
1212 let mut engine = LayoutEngine::new();
1213 engine
1214 .add_node(
1215 wid(1),
1216 Style {
1217 flex_grow: 1.0,
1218 ..Default::default()
1219 },
1220 )
1221 .ok();
1222 engine
1223 .add_node_with_children(
1224 wid(10),
1225 Style {
1226 border: taffy::Rect {
1227 left: LengthPercentage::Length(1.0),
1228 right: LengthPercentage::Length(1.0),
1229 top: LengthPercentage::Length(1.0),
1230 bottom: LengthPercentage::Length(1.0),
1231 },
1232 size: taffy::Size {
1233 width: Dimension::Length(80.0),
1234 height: Dimension::Length(24.0),
1235 },
1236 ..Default::default()
1237 },
1238 &[wid(1)],
1239 )
1240 .ok();
1241 engine.set_root(wid(10)).ok();
1242 engine.compute(80, 24).ok();
1243
1244 let l = engine.layout(wid(1)).unwrap_or_default();
1245 assert_eq!(l.x, 1);
1246 assert_eq!(l.y, 1);
1247 assert_eq!(l.width, 78); assert_eq!(l.height, 22); }
1250
1251 #[test]
1252 fn box_model_combined() {
1253 let mut engine = LayoutEngine::new();
1254 engine
1255 .add_node(
1256 wid(1),
1257 Style {
1258 flex_grow: 1.0,
1259 ..Default::default()
1260 },
1261 )
1262 .ok();
1263 engine
1264 .add_node_with_children(
1265 wid(10),
1266 Style {
1267 padding: taffy::Rect {
1268 left: LengthPercentage::Length(2.0),
1269 right: LengthPercentage::Length(2.0),
1270 top: LengthPercentage::Length(1.0),
1271 bottom: LengthPercentage::Length(1.0),
1272 },
1273 border: taffy::Rect {
1274 left: LengthPercentage::Length(1.0),
1275 right: LengthPercentage::Length(1.0),
1276 top: LengthPercentage::Length(1.0),
1277 bottom: LengthPercentage::Length(1.0),
1278 },
1279 size: taffy::Size {
1280 width: Dimension::Length(80.0),
1281 height: Dimension::Length(24.0),
1282 },
1283 ..Default::default()
1284 },
1285 &[wid(1)],
1286 )
1287 .ok();
1288 engine.set_root(wid(10)).ok();
1289 engine.compute(80, 24).ok();
1290
1291 let l = engine.layout(wid(1)).unwrap_or_default();
1292 assert_eq!(l.x, 3);
1294 assert_eq!(l.y, 2); assert_eq!(l.width, 74); assert_eq!(l.height, 20); }
1298
1299 #[test]
1300 fn layout_error_display() {
1301 let e1 = LayoutError::WidgetNotFound(wid(42));
1302 assert!(format!("{e1}").contains("42"));
1303
1304 let e2 = LayoutError::TaffyError("boom".into());
1305 assert!(format!("{e2}").contains("boom"));
1306
1307 let e3 = LayoutError::NoRoot;
1308 assert!(format!("{e3}").contains("no root"));
1309 }
1310}