1use rustc_hash::{FxHashMap, FxHashSet};
2use taffy::{TaffyTree, TraversePartialTree};
3
4use crate::direction::Direction;
5use crate::error::LayoutError;
6use crate::style::{AvailableSpace, LayoutStyle};
7
8pub type NodeId = taffy::NodeId;
9
10pub type MeasureFn = Box<dyn FnMut(f32) -> (f32, f32)>;
14
15pub struct LayoutEngine {
16 tree: TaffyTree<MeasureFn>,
17 direction: Direction,
18 styles: FxHashMap<NodeId, LayoutStyle>,
20 directional_rows: FxHashSet<NodeId>,
22 live: FxHashSet<NodeId>,
26}
27
28impl LayoutEngine {
29 pub fn new() -> Self {
30 Self {
31 tree: TaffyTree::new(),
32 direction: Direction::default(),
33 styles: FxHashMap::default(),
34 directional_rows: FxHashSet::default(),
35 live: FxHashSet::default(),
36 }
37 }
38
39 pub fn direction(&self) -> Direction {
41 self.direction
42 }
43
44 pub fn set_direction(&mut self, direction: Direction) -> bool {
50 if self.direction == direction {
51 return false;
52 }
53 self.direction = direction;
54 let rows = std::mem::take(&mut self.directional_rows);
55 for &node in &rows {
56 if let Some(current) = self.style_of(node) {
57 let mut style = current.clone();
58 style.flex_direction = if direction.is_rtl() {
59 taffy::FlexDirection::RowReverse
60 } else {
61 taffy::FlexDirection::Row
62 };
63 let _ = self.tree.set_style(node, style);
64 }
65 }
66 self.directional_rows = rows;
67 let styles = std::mem::take(&mut self.styles);
68 for (&node, style) in &styles {
69 self.push_style(node, style);
70 }
71 self.styles = styles;
72 true
73 }
74
75 fn track(&mut self, node: NodeId, style: LayoutStyle) {
77 self.live.insert(node);
78 if style.logical.needs_tracking() {
79 self.directional_rows.remove(&node);
80 self.styles.insert(node, style);
81 return;
82 }
83 self.styles.remove(&node);
84 if style.logical.row_follows_direction {
85 self.directional_rows.insert(node);
86 } else {
87 self.directional_rows.remove(&node);
88 }
89 }
90
91 fn current_style(&self, node: NodeId) -> LayoutStyle {
93 if let Some(style) = self.styles.get(&node) {
94 return style.clone();
95 }
96 LayoutStyle {
97 inner: self.tree.style(node).cloned().unwrap_or_default(),
98 logical: crate::style::LogicalStyle {
99 row_follows_direction: self.directional_rows.contains(&node),
100 ..Default::default()
101 },
102 }
103 }
104
105 fn push_style(&mut self, node: NodeId, style: &LayoutStyle) {
107 let mut resolved = style.resolve(self.direction);
108 if let Some((is_row, px)) = style.logical.leading_margin {
109 let leading_right = is_row && self.leads_from_right(node);
110 let m = taffy::LengthPercentageAuto::length(px);
111 if is_row {
112 if leading_right {
113 resolved.margin.right = m;
114 } else {
115 resolved.margin.left = m;
116 }
117 } else {
118 resolved.margin.top = m;
119 }
120 }
121 let _ = self.tree.set_style(node, resolved);
122 }
123
124 fn mutate_style(&mut self, node: NodeId, f: impl FnOnce(&mut LayoutStyle)) {
126 if !self.live.contains(&node) {
127 return;
128 }
129 let mut style = self.current_style(node);
130 f(&mut style);
131 self.push_style(node, &style);
132 self.track(node, style);
133 }
134
135 fn forget(&mut self, node: NodeId) {
136 self.live.remove(&node);
137 self.styles.remove(&node);
138 self.directional_rows.remove(&node);
139 }
140
141 pub fn new_leaf(&mut self, style: LayoutStyle) -> Result<NodeId, LayoutError> {
142 let node = self.tree.new_leaf(style.resolve(self.direction))?;
143 self.track(node, style);
144 Ok(node)
145 }
146
147 pub fn new_measured_leaf(
148 &mut self,
149 style: LayoutStyle,
150 measure: MeasureFn,
151 ) -> Result<NodeId, LayoutError> {
152 let node = self
153 .tree
154 .new_leaf_with_context(style.resolve(self.direction), measure)?;
155 self.track(node, style);
156 Ok(node)
157 }
158
159 pub fn new_container(
160 &mut self,
161 style: LayoutStyle,
162 children: &[NodeId],
163 ) -> Result<NodeId, LayoutError> {
164 let node = self
165 .tree
166 .new_with_children(style.resolve(self.direction), children)?;
167 self.track(node, style);
168 Ok(node)
169 }
170
171 pub fn set_style(&mut self, node: NodeId, mut style: LayoutStyle) -> Result<(), LayoutError> {
173 self.alive(node)?;
174 if let Some(previous) = self.styles.get(&node) {
175 style.logical.hidden |= previous.logical.hidden;
176 style.logical.row_forced |= previous.logical.row_forced;
177 style.logical.min_height_override = style
178 .logical
179 .min_height_override
180 .or(previous.logical.min_height_override);
181 style.logical.leading_margin = style
182 .logical
183 .leading_margin
184 .or(previous.logical.leading_margin);
185 }
186 self.push_style(node, &style);
187 self.track(node, style);
188 Ok(())
189 }
190
191 pub fn set_children(&mut self, parent: NodeId, children: &[NodeId]) -> Result<(), LayoutError> {
194 self.alive(parent)?;
195 for &child in children {
196 self.alive(child)?;
197 }
198 self.tree
199 .set_children(parent, children)
200 .map_err(LayoutError::from)
201 }
202
203 pub fn add_child(&mut self, parent: NodeId, child: NodeId) -> Result<(), LayoutError> {
206 self.alive(parent)?;
207 self.alive(child)?;
208 self.tree
209 .add_child(parent, child)
210 .map_err(LayoutError::from)
211 }
212
213 pub fn remove_child(&mut self, parent: NodeId, child: NodeId) -> Result<(), LayoutError> {
215 self.alive(parent)?;
216 self.alive(child)?;
217 self.tree
218 .remove_child(parent, child)
219 .map(|_| ())
220 .map_err(LayoutError::from)
221 }
222
223 fn style_of(&self, node: NodeId) -> Option<&taffy::Style> {
225 if !self.live.contains(&node) {
226 return None;
227 }
228 self.tree.style(node).ok()
229 }
230
231 fn alive(&self, node: NodeId) -> Result<(), LayoutError> {
240 if self.live.contains(&node) {
241 Ok(())
242 } else {
243 Err(LayoutError::Engine(format!(
244 "node {node:?} no longer exists"
245 )))
246 }
247 }
248
249 pub fn remove(&mut self, node: NodeId) {
252 self.forget(node);
253 let _ = self.tree.remove(node);
254 }
255
256 pub fn mark_dirty(&mut self, node: NodeId) -> Result<(), LayoutError> {
257 self.alive(node)?;
258 self.tree.mark_dirty(node).map_err(LayoutError::from)
259 }
260
261 pub fn is_size_auto(&self, node: NodeId) -> (bool, bool) {
263 match self.style_of(node) {
264 Some(s) => (s.size.width.is_auto(), s.size.height.is_auto()),
265 None => (false, false),
266 }
267 }
268
269 pub fn set_width(&mut self, node: NodeId, width: Option<f32>) {
271 self.mutate_style(node, |style| {
272 style.inner.size.width =
273 width.map_or(taffy::Dimension::auto(), taffy::Dimension::length);
274 });
275 }
276
277 pub fn set_height(&mut self, node: NodeId, height: Option<f32>) {
279 self.mutate_style(node, |style| {
280 style.inner.size.height =
281 height.map_or(taffy::Dimension::auto(), taffy::Dimension::length);
282 });
283 }
284
285 pub fn set_min_height(&mut self, node: NodeId, height: Option<f32>) {
288 self.mutate_style(node, |style| {
289 style.logical.min_height_override = height;
290 });
291 }
292
293 pub fn make_flex_row(&mut self, node: NodeId) {
297 self.mutate_style(node, |style| {
298 style.logical.row_forced = true;
299 });
300 }
301
302 pub fn is_row(&self, node: NodeId) -> bool {
306 if self.alive(node).is_err() {
309 return false;
310 }
311 self.tree
312 .style(node)
313 .map(|s| {
314 matches!(
315 s.flex_direction,
316 taffy::FlexDirection::Row | taffy::FlexDirection::RowReverse
317 )
318 })
319 .unwrap_or(false)
320 }
321
322 pub fn set_leading_margin(&mut self, node: NodeId, is_row: bool, px: f32) {
327 self.mutate_style(node, |style| {
328 style.logical.leading_margin = Some((is_row, px));
329 });
330 }
331
332 fn leads_from_right(&self, node: NodeId) -> bool {
334 if !self.live.contains(&node) {
335 return false;
336 }
337 self.tree
338 .parent(node)
339 .and_then(|parent| self.style_of(parent))
340 .map(|s| s.flex_direction == taffy::FlexDirection::RowReverse)
341 .unwrap_or(false)
342 }
343
344 pub fn is_display_none(&self, node: NodeId) -> bool {
347 self.style_of(node)
348 .map(|s| s.display == taffy::Display::None)
349 .unwrap_or(false)
350 }
351
352 pub fn set_display(&mut self, node: NodeId, visible: bool) {
354 self.mutate_style(node, |style| {
355 style.logical.hidden = !visible;
356 });
357 }
358
359 pub fn compute_layout(
360 &mut self,
361 root: NodeId,
362 available_width: AvailableSpace,
363 available_height: AvailableSpace,
364 ) -> Result<(), LayoutError> {
365 self.alive(root)?;
366 self.tree
367 .compute_layout_with_measure(
368 root,
369 taffy::geometry::Size {
370 width: available_width.into(),
371 height: available_height.into(),
372 },
373 |known, available, _node, context, _style| {
374 let Some(measure) = context else {
375 return taffy::geometry::Size::ZERO;
376 };
377 let width = known.width.unwrap_or(match available.width {
379 taffy::AvailableSpace::Definite(w) => w,
380 taffy::AvailableSpace::MaxContent => 1.0e6,
381 taffy::AvailableSpace::MinContent => 0.0,
382 });
383 let (mw, mh) = measure(width);
384 taffy::geometry::Size {
385 width: known.width.unwrap_or(mw),
386 height: known.height.unwrap_or(mh),
387 }
388 },
389 )
390 .map_err(LayoutError::from)
391 }
392
393 pub fn is_dirty(&self, node: NodeId) -> bool {
394 self.live.contains(&node) && self.tree.dirty(node).unwrap_or(true)
395 }
396
397 pub fn layout(&self, node: NodeId) -> Result<geometry_core::Rect, LayoutError> {
398 self.alive(node)?;
399 let layout = self.tree.layout(node).map_err(LayoutError::from)?;
400 Ok(geometry_core::Rect::new(
401 layout.location.x,
402 layout.location.y,
403 layout.size.width,
404 layout.size.height,
405 ))
406 }
407
408 pub fn is_fixed_size(&self, node: NodeId) -> Option<(f32, f32)> {
409 let style = self.style_of(node)?;
410 let w = style.size.width.into_option()?;
411 let h = style.size.height.into_option()?;
412 if style.flex_grow > 0.0 {
413 return None;
414 }
415 Some((w, h))
416 }
417
418 pub fn collect_dirty_nodes(&self, root: NodeId, out: &mut Vec<NodeId>) {
419 let mut stack = vec![root];
420 while let Some(node) = stack.pop() {
421 if self.is_dirty(node) {
422 out.push(node);
423 }
424 for child in self.tree.child_ids(node) {
425 stack.push(child);
426 }
427 }
428 }
429
430 pub fn walk<F>(&self, root: NodeId, f: &mut F) -> Result<(), LayoutError>
431 where
432 F: FnMut(NodeId, geometry_core::Rect) -> bool,
433 {
434 self.alive(root)?;
435 struct StackEntry {
436 node: NodeId,
437 offset_x: f32,
438 offset_y: f32,
439 hidden: bool,
443 }
444
445 let mut stack = Vec::with_capacity(64);
446 stack.push(StackEntry {
447 node: root,
448 offset_x: 0.0,
449 offset_y: 0.0,
450 hidden: false,
451 });
452
453 while let Some(entry) = stack.pop() {
454 let layout = self.tree.layout(entry.node).map_err(LayoutError::from)?;
455 let abs_x = entry.offset_x + layout.location.x;
456 let abs_y = entry.offset_y + layout.location.y;
457 let hidden = entry.hidden
458 || self
459 .tree
460 .style(entry.node)
461 .map(|s| s.display == taffy::Display::None)
462 .unwrap_or(false);
463 let (w, h) = if hidden {
464 (0.0, 0.0)
465 } else {
466 (layout.size.width, layout.size.height)
467 };
468
469 let descend = f(entry.node, geometry_core::Rect::new(abs_x, abs_y, w, h));
470
471 if descend {
472 let base = stack.len();
473 for child in self.tree.child_ids(entry.node) {
474 stack.push(StackEntry {
475 node: child,
476 offset_x: abs_x,
477 offset_y: abs_y,
478 hidden,
479 });
480 }
481 stack[base..].reverse();
482 }
483 }
484 Ok(())
485 }
486}
487
488impl Default for LayoutEngine {
489 fn default() -> Self {
490 Self::new()
491 }
492}
493
494#[cfg(test)]
495mod tests {
496 use super::*;
497
498 fn lay_out(engine: &mut LayoutEngine, root: NodeId) {
499 engine
500 .compute_layout(
501 root,
502 AvailableSpace::Definite(300.0),
503 AvailableSpace::Definite(100.0),
504 )
505 .unwrap();
506 }
507
508 #[test]
515 fn a_freed_node_is_an_error_and_never_a_panic() {
516 let mut engine = LayoutEngine::new();
517 let parent = engine.new_container(LayoutStyle::new(), &[]).unwrap();
518 let child = engine.new_leaf(LayoutStyle::new()).unwrap();
519 let ghost = engine.new_leaf(LayoutStyle::new()).unwrap();
520 engine.remove(ghost);
521
522 assert!(engine.set_children(ghost, &[]).is_err());
523 assert!(engine.set_children(parent, &[ghost]).is_err());
524 assert!(engine.set_style(ghost, LayoutStyle::new()).is_err());
525 assert!(engine.mark_dirty(ghost).is_err());
526 assert!(engine.add_child(parent, ghost).is_err());
527 assert!(engine.remove_child(parent, ghost).is_err());
528 assert!(engine.layout(ghost).is_err());
529 assert!(
530 engine
531 .compute_layout(
532 ghost,
533 AvailableSpace::MaxContent,
534 AvailableSpace::MaxContent
535 )
536 .is_err()
537 );
538 assert_eq!(engine.is_size_auto(ghost), (false, false));
539 assert!(engine.is_fixed_size(ghost).is_none());
540 engine.set_width(ghost, Some(10.0));
542 engine.set_height(ghost, None);
543 engine.set_leading_margin(ghost, true, 4.0);
544
545 assert!(engine.set_children(parent, &[child]).is_ok());
547 }
548
549 #[test]
550 fn flipping_direction_relays_an_existing_row_without_rebuilding_it() {
551 let mut engine = LayoutEngine::new();
553 let first = engine
554 .new_leaf(LayoutStyle::new().width(50.0).height(10.0))
555 .unwrap();
556 let second = engine
557 .new_leaf(LayoutStyle::new().width(50.0).height(10.0))
558 .unwrap();
559 let row = engine
560 .new_container(
561 LayoutStyle::new().flex_row().width(300.0).height(100.0),
562 &[first, second],
563 )
564 .unwrap();
565 lay_out(&mut engine, row);
566 assert_eq!(engine.layout(first).unwrap().x, 0.0);
567 assert_eq!(engine.layout(second).unwrap().x, 50.0);
568
569 assert!(engine.set_direction(Direction::Rtl));
570 engine.mark_dirty(row).unwrap();
571 lay_out(&mut engine, row);
572 assert_eq!(
573 engine.layout(first).unwrap().x,
574 250.0,
575 "the first item now starts at the right edge"
576 );
577 assert_eq!(engine.layout(second).unwrap().x, 200.0);
578 }
579
580 #[test]
581 fn flipping_direction_moves_logical_padding_to_the_other_edge() {
582 let mut engine = LayoutEngine::new();
583 let child = engine
584 .new_leaf(LayoutStyle::new().width(50.0).height(10.0))
585 .unwrap();
586 let box_ = engine
587 .new_container(
588 LayoutStyle::new()
589 .flex_column()
590 .width(300.0)
591 .height(100.0)
592 .padding_start(20.0),
593 &[child],
594 )
595 .unwrap();
596 lay_out(&mut engine, box_);
597 assert_eq!(engine.layout(child).unwrap().x, 20.0);
598
599 engine.set_direction(Direction::Rtl);
600 engine.mark_dirty(box_).unwrap();
601 lay_out(&mut engine, box_);
602 assert_eq!(
603 engine.layout(child).unwrap().x,
604 0.0,
605 "padding moved to the right edge, so the child starts flush left"
606 );
607 }
608
609 #[test]
610 fn setting_the_same_direction_reports_no_change() {
611 let mut engine = LayoutEngine::new();
612 assert!(!engine.set_direction(Direction::Ltr));
613 assert!(engine.set_direction(Direction::Rtl));
614 assert!(!engine.set_direction(Direction::Rtl));
615 }
616
617 #[test]
618 fn restyling_a_node_drops_the_logical_edges_it_no_longer_has() {
619 let mut engine = LayoutEngine::new();
621 let node = engine
622 .new_leaf(LayoutStyle::new().padding_start(20.0).width(50.0))
623 .unwrap();
624 engine
625 .set_style(node, LayoutStyle::new().width(50.0))
626 .unwrap();
627 engine.set_direction(Direction::Rtl);
628 let style = engine.tree.style(node).unwrap();
629 assert_eq!(style.padding.left, taffy::LengthPercentage::length(0.0));
630 assert_eq!(style.padding.right, taffy::LengthPercentage::length(0.0));
631 }
632
633 #[test]
634 fn a_gap_margin_follows_the_edge_its_row_leads_from() {
635 let mut engine = LayoutEngine::new();
636 let first = engine.new_leaf(LayoutStyle::new().width(50.0)).unwrap();
637 let second = engine.new_leaf(LayoutStyle::new().width(50.0)).unwrap();
638 let row = engine
639 .new_container(LayoutStyle::new().flex_row().width(300.0), &[first, second])
640 .unwrap();
641 engine.set_leading_margin(second, true, 8.0);
642 assert_eq!(
643 engine.tree.style(second).unwrap().margin.left,
644 taffy::LengthPercentageAuto::length(8.0)
645 );
646
647 engine.set_direction(Direction::Rtl);
648 engine.mark_dirty(row).unwrap();
649 let margin = engine.tree.style(second).unwrap().margin;
650 assert_eq!(
651 margin.right,
652 taffy::LengthPercentageAuto::length(8.0),
653 "the gap moved to the edge the reversed row leads from"
654 );
655 assert_eq!(
656 margin.left,
657 taffy::LengthPercentageAuto::length(0.0),
658 "and does not linger on the old one"
659 );
660 }
661
662 #[test]
663 fn engine_leaf_layout() {
664 let mut engine = LayoutEngine::new();
665 let leaf = engine
666 .new_leaf(LayoutStyle::new().width(50.0).height(40.0))
667 .unwrap();
668 engine
669 .compute_layout(
670 leaf,
671 AvailableSpace::Definite(200.0),
672 AvailableSpace::Definite(200.0),
673 )
674 .unwrap();
675 let rect = engine.layout(leaf).unwrap();
676 assert_eq!(rect.width, 50.0_f32);
677 assert_eq!(rect.height, 40.0_f32);
678 }
679
680 #[test]
681 fn engine_flex_row_positions() {
682 let mut engine = LayoutEngine::new();
683 let child1 = engine
684 .new_leaf(LayoutStyle::new().width(100.0).height(100.0))
685 .unwrap();
686 let child2 = engine
687 .new_leaf(LayoutStyle::new().width(100.0).height(100.0))
688 .unwrap();
689 let root = engine
690 .new_container(
691 LayoutStyle::new().flex_row().width(200.0).height(100.0),
692 &[child1, child2],
693 )
694 .unwrap();
695 engine
696 .compute_layout(
697 root,
698 AvailableSpace::Definite(200.0),
699 AvailableSpace::Definite(100.0),
700 )
701 .unwrap();
702
703 let r1 = engine.layout(child1).unwrap();
704 let r2 = engine.layout(child2).unwrap();
705 assert_eq!(r1.x, 0.0_f32);
706 assert_eq!(r1.y, 0.0_f32);
707 assert_eq!(r2.x, 100.0_f32);
708 assert_eq!(r2.y, 0.0_f32);
709 }
710
711 #[test]
712 fn engine_flex_column_positions() {
713 let mut engine = LayoutEngine::new();
714 let child1 = engine
715 .new_leaf(LayoutStyle::new().width(100.0).height(100.0))
716 .unwrap();
717 let child2 = engine
718 .new_leaf(LayoutStyle::new().width(100.0).height(100.0))
719 .unwrap();
720 let root = engine
721 .new_container(
722 LayoutStyle::new().flex_column().width(100.0).height(200.0),
723 &[child1, child2],
724 )
725 .unwrap();
726 engine
727 .compute_layout(
728 root,
729 AvailableSpace::Definite(100.0),
730 AvailableSpace::Definite(200.0),
731 )
732 .unwrap();
733
734 let r1 = engine.layout(child1).unwrap();
735 let r2 = engine.layout(child2).unwrap();
736 assert_eq!(r1.x, 0.0_f32);
737 assert_eq!(r1.y, 0.0_f32);
738 assert_eq!(r2.x, 0.0_f32);
739 assert_eq!(r2.y, 100.0_f32);
740 }
741
742 #[test]
743 fn engine_walk_absolute() {
744 let mut engine = LayoutEngine::new();
745 let inner_child = engine
746 .new_leaf(LayoutStyle::new().width(50.0).height(50.0))
747 .unwrap();
748 let inner = engine
749 .new_container(
750 LayoutStyle::new().flex_row().width(50.0).height(50.0),
751 &[inner_child],
752 )
753 .unwrap();
754 let outer_first = engine
755 .new_leaf(LayoutStyle::new().width(100.0).height(50.0))
756 .unwrap();
757 let root = engine
758 .new_container(
759 LayoutStyle::new().flex_row().width(150.0).height(50.0),
760 &[outer_first, inner],
761 )
762 .unwrap();
763 engine
764 .compute_layout(
765 root,
766 AvailableSpace::Definite(150.0),
767 AvailableSpace::Definite(50.0),
768 )
769 .unwrap();
770
771 let mut hits: Vec<(NodeId, geometry_core::Rect)> = Vec::new();
772 engine
773 .walk(root, &mut |node, rect| {
774 hits.push((node, rect));
775 true
776 })
777 .unwrap();
778
779 let inner_child_rect = hits
780 .iter()
781 .find(|(n, _)| *n == inner_child)
782 .map(|(_, r)| *r)
783 .unwrap();
784 assert_eq!(inner_child_rect.x, 100.0_f32);
785 assert_eq!(inner_child_rect.y, 0.0_f32);
786 assert_eq!(inner_child_rect.width, 50.0_f32);
787 assert_eq!(inner_child_rect.height, 50.0_f32);
788 }
789
790 #[test]
791 fn engine_set_style() {
792 let mut engine = LayoutEngine::new();
793 let leaf = engine
794 .new_leaf(LayoutStyle::new().width(10.0).height(10.0))
795 .unwrap();
796 engine
797 .set_style(leaf, LayoutStyle::new().width(80.0).height(60.0))
798 .unwrap();
799 engine
800 .compute_layout(
801 leaf,
802 AvailableSpace::Definite(200.0),
803 AvailableSpace::Definite(200.0),
804 )
805 .unwrap();
806 let rect = engine.layout(leaf).unwrap();
807 assert_eq!(rect.width, 80.0_f32);
808 assert_eq!(rect.height, 60.0_f32);
809 }
810}