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 logical: FxHashMap<NodeId, LayoutStyle>,
21 directional_rows: FxHashSet<NodeId>,
25 leading_margins: FxHashMap<NodeId, (bool, f32)>,
28}
29
30impl LayoutEngine {
31 pub fn new() -> Self {
32 Self {
33 tree: TaffyTree::new(),
34 direction: Direction::default(),
35 logical: FxHashMap::default(),
36 directional_rows: FxHashSet::default(),
37 leading_margins: FxHashMap::default(),
38 }
39 }
40
41 pub fn direction(&self) -> Direction {
43 self.direction
44 }
45
46 pub fn set_direction(&mut self, direction: Direction) -> bool {
52 if self.direction == direction {
53 return false;
54 }
55 self.direction = direction;
56 let rows = std::mem::take(&mut self.directional_rows);
57 for &node in &rows {
58 if let Ok(current) = self.tree.style(node) {
59 let mut style = current.clone();
60 style.flex_direction = if direction.is_rtl() {
61 taffy::FlexDirection::RowReverse
62 } else {
63 taffy::FlexDirection::Row
64 };
65 let _ = self.tree.set_style(node, style);
66 }
67 }
68 self.directional_rows = rows;
69 let logical = std::mem::take(&mut self.logical);
70 for (&node, style) in &logical {
71 let _ = self.tree.set_style(node, style.resolve(direction));
72 }
73 self.logical = logical;
74 let margins = std::mem::take(&mut self.leading_margins);
75 for (&node, &(is_row, px)) in &margins {
76 self.apply_leading_margin(node, is_row, px, true);
77 }
78 self.leading_margins = margins;
79 true
80 }
81
82 fn track(&mut self, node: NodeId, style: LayoutStyle) {
85 if style.logical.has_edges() {
86 self.directional_rows.remove(&node);
87 self.logical.insert(node, style);
88 return;
89 }
90 self.logical.remove(&node);
91 if style.logical.row_follows_direction {
92 self.directional_rows.insert(node);
93 } else {
94 self.directional_rows.remove(&node);
95 }
96 }
97
98 fn forget(&mut self, node: NodeId) {
99 self.logical.remove(&node);
100 self.directional_rows.remove(&node);
101 self.leading_margins.remove(&node);
102 }
103
104 pub fn new_leaf(&mut self, style: LayoutStyle) -> Result<NodeId, LayoutError> {
105 let node = self.tree.new_leaf(style.resolve(self.direction))?;
106 self.track(node, style);
107 Ok(node)
108 }
109
110 pub fn new_measured_leaf(
111 &mut self,
112 style: LayoutStyle,
113 measure: MeasureFn,
114 ) -> Result<NodeId, LayoutError> {
115 let node = self
116 .tree
117 .new_leaf_with_context(style.resolve(self.direction), measure)?;
118 self.track(node, style);
119 Ok(node)
120 }
121
122 pub fn new_container(
123 &mut self,
124 style: LayoutStyle,
125 children: &[NodeId],
126 ) -> Result<NodeId, LayoutError> {
127 let node = self
128 .tree
129 .new_with_children(style.resolve(self.direction), children)?;
130 self.track(node, style);
131 Ok(node)
132 }
133
134 pub fn set_style(&mut self, node: NodeId, style: LayoutStyle) -> Result<(), LayoutError> {
135 self.tree.set_style(node, style.resolve(self.direction))?;
136 self.track(node, style);
137 Ok(())
138 }
139
140 pub fn set_children(&mut self, parent: NodeId, children: &[NodeId]) -> Result<(), LayoutError> {
143 self.tree
144 .set_children(parent, children)
145 .map_err(LayoutError::from)
146 }
147
148 pub fn add_child(&mut self, parent: NodeId, child: NodeId) -> Result<(), LayoutError> {
151 self.tree
152 .add_child(parent, child)
153 .map_err(LayoutError::from)
154 }
155
156 pub fn remove_child(&mut self, parent: NodeId, child: NodeId) -> Result<(), LayoutError> {
158 self.tree
159 .remove_child(parent, child)
160 .map(|_| ())
161 .map_err(LayoutError::from)
162 }
163
164 pub fn remove(&mut self, node: NodeId) {
167 self.forget(node);
168 let _ = self.tree.remove(node);
169 }
170
171 pub fn mark_dirty(&mut self, node: NodeId) -> Result<(), LayoutError> {
172 self.tree.mark_dirty(node).map_err(LayoutError::from)
173 }
174
175 pub fn is_size_auto(&self, node: NodeId) -> (bool, bool) {
177 match self.tree.style(node) {
178 Ok(s) => (s.size.width.is_auto(), s.size.height.is_auto()),
179 Err(_) => (false, false),
180 }
181 }
182
183 pub fn set_width(&mut self, node: NodeId, width: Option<f32>) {
185 if let Ok(s) = self.tree.style(node) {
186 let mut style = s.clone();
187 style.size.width = width.map_or(taffy::Dimension::auto(), taffy::Dimension::length);
188 let _ = self.tree.set_style(node, style);
189 }
190 }
191
192 pub fn set_height(&mut self, node: NodeId, height: Option<f32>) {
194 if let Ok(s) = self.tree.style(node) {
195 let mut style = s.clone();
196 style.size.height = height.map_or(taffy::Dimension::auto(), taffy::Dimension::length);
197 let _ = self.tree.set_style(node, style);
198 }
199 }
200
201 pub fn set_min_height(&mut self, node: NodeId, height: Option<f32>) {
204 if let Ok(s) = self.tree.style(node) {
205 let mut style = s.clone();
206 style.min_size.height =
207 height.map_or(taffy::Dimension::auto(), taffy::Dimension::length);
208 let _ = self.tree.set_style(node, style);
209 }
210 }
211
212 pub fn is_row(&self, node: NodeId) -> bool {
216 self.tree
217 .style(node)
218 .map(|s| {
219 matches!(
220 s.flex_direction,
221 taffy::FlexDirection::Row | taffy::FlexDirection::RowReverse
222 )
223 })
224 .unwrap_or(false)
225 }
226
227 pub fn set_leading_margin(&mut self, node: NodeId, is_row: bool, px: f32) {
232 self.leading_margins.insert(node, (is_row, px));
233 self.apply_leading_margin(node, is_row, px, false);
234 }
235
236 fn leads_from_right(&self, node: NodeId) -> bool {
238 self.tree
239 .parent(node)
240 .and_then(|parent| self.tree.style(parent).ok())
241 .map(|s| s.flex_direction == taffy::FlexDirection::RowReverse)
242 .unwrap_or(false)
243 }
244
245 fn apply_leading_margin(&mut self, node: NodeId, is_row: bool, px: f32, clear_opposite: bool) {
248 let leading_right = is_row && self.leads_from_right(node);
249 let Ok(current) = self.tree.style(node) else {
250 return;
251 };
252 let mut style = current.clone();
253 let m = taffy::LengthPercentageAuto::length(px);
254 if is_row {
255 if leading_right {
256 style.margin.right = m;
257 } else {
258 style.margin.left = m;
259 }
260 if clear_opposite {
261 let zero = taffy::LengthPercentageAuto::length(0.0);
262 if leading_right {
263 style.margin.left = zero;
264 } else {
265 style.margin.right = zero;
266 }
267 }
268 } else {
269 style.margin.top = m;
270 }
271 let _ = self.tree.set_style(node, style);
272 }
273
274 pub fn set_display(&mut self, node: NodeId, visible: bool) {
276 if let Ok(s) = self.tree.style(node) {
277 let mut style = s.clone();
278 style.display = if visible {
279 taffy::Display::Flex
280 } else {
281 taffy::Display::None
282 };
283 let _ = self.tree.set_style(node, style);
284 }
285 }
286
287 pub fn compute_layout(
288 &mut self,
289 root: NodeId,
290 available_width: AvailableSpace,
291 available_height: AvailableSpace,
292 ) -> Result<(), LayoutError> {
293 self.tree
294 .compute_layout_with_measure(
295 root,
296 taffy::geometry::Size {
297 width: available_width.into(),
298 height: available_height.into(),
299 },
300 |known, available, _node, context, _style| {
301 let Some(measure) = context else {
302 return taffy::geometry::Size::ZERO;
303 };
304 let width = known.width.unwrap_or(match available.width {
306 taffy::AvailableSpace::Definite(w) => w,
307 taffy::AvailableSpace::MaxContent => 1.0e6,
308 taffy::AvailableSpace::MinContent => 0.0,
309 });
310 let (mw, mh) = measure(width);
311 taffy::geometry::Size {
312 width: known.width.unwrap_or(mw),
313 height: known.height.unwrap_or(mh),
314 }
315 },
316 )
317 .map_err(LayoutError::from)
318 }
319
320 pub fn is_dirty(&self, node: NodeId) -> bool {
321 self.tree.dirty(node).unwrap_or(true)
322 }
323
324 pub fn layout(&self, node: NodeId) -> Result<geometry_core::Rect, LayoutError> {
325 let layout = self.tree.layout(node).map_err(LayoutError::from)?;
326 Ok(geometry_core::Rect::new(
327 layout.location.x,
328 layout.location.y,
329 layout.size.width,
330 layout.size.height,
331 ))
332 }
333
334 pub fn is_fixed_size(&self, node: NodeId) -> Option<(f32, f32)> {
335 let style = self.tree.style(node).ok()?;
336 let w = style.size.width.into_option()?;
337 let h = style.size.height.into_option()?;
338 if style.flex_grow > 0.0 {
339 return None;
340 }
341 Some((w, h))
342 }
343
344 pub fn collect_dirty_nodes(&self, root: NodeId, out: &mut Vec<NodeId>) {
345 let mut stack = vec![root];
346 while let Some(node) = stack.pop() {
347 if self.is_dirty(node) {
348 out.push(node);
349 }
350 for child in self.tree.child_ids(node) {
351 stack.push(child);
352 }
353 }
354 }
355
356 pub fn walk<F>(&self, root: NodeId, f: &mut F) -> Result<(), LayoutError>
357 where
358 F: FnMut(NodeId, geometry_core::Rect) -> bool,
359 {
360 struct StackEntry {
361 node: NodeId,
362 offset_x: f32,
363 offset_y: f32,
364 hidden: bool,
368 }
369
370 let mut stack = Vec::with_capacity(64);
371 stack.push(StackEntry {
372 node: root,
373 offset_x: 0.0,
374 offset_y: 0.0,
375 hidden: false,
376 });
377
378 while let Some(entry) = stack.pop() {
379 let layout = self.tree.layout(entry.node).map_err(LayoutError::from)?;
380 let abs_x = entry.offset_x + layout.location.x;
381 let abs_y = entry.offset_y + layout.location.y;
382 let hidden = entry.hidden
383 || self
384 .tree
385 .style(entry.node)
386 .map(|s| s.display == taffy::Display::None)
387 .unwrap_or(false);
388 let (w, h) = if hidden {
389 (0.0, 0.0)
390 } else {
391 (layout.size.width, layout.size.height)
392 };
393
394 let descend = f(entry.node, geometry_core::Rect::new(abs_x, abs_y, w, h));
395
396 if descend {
397 let base = stack.len();
398 for child in self.tree.child_ids(entry.node) {
399 stack.push(StackEntry {
400 node: child,
401 offset_x: abs_x,
402 offset_y: abs_y,
403 hidden,
404 });
405 }
406 stack[base..].reverse();
407 }
408 }
409 Ok(())
410 }
411}
412
413impl Default for LayoutEngine {
414 fn default() -> Self {
415 Self::new()
416 }
417}
418
419#[cfg(test)]
420mod tests {
421 use super::*;
422
423 fn lay_out(engine: &mut LayoutEngine, root: NodeId) {
424 engine
425 .compute_layout(
426 root,
427 AvailableSpace::Definite(300.0),
428 AvailableSpace::Definite(100.0),
429 )
430 .unwrap();
431 }
432
433 #[test]
434 fn flipping_direction_relays_an_existing_row_without_rebuilding_it() {
435 let mut engine = LayoutEngine::new();
437 let first = engine
438 .new_leaf(LayoutStyle::new().width(50.0).height(10.0))
439 .unwrap();
440 let second = engine
441 .new_leaf(LayoutStyle::new().width(50.0).height(10.0))
442 .unwrap();
443 let row = engine
444 .new_container(
445 LayoutStyle::new().flex_row().width(300.0).height(100.0),
446 &[first, second],
447 )
448 .unwrap();
449 lay_out(&mut engine, row);
450 assert_eq!(engine.layout(first).unwrap().x, 0.0);
451 assert_eq!(engine.layout(second).unwrap().x, 50.0);
452
453 assert!(engine.set_direction(Direction::Rtl));
454 engine.mark_dirty(row).unwrap();
455 lay_out(&mut engine, row);
456 assert_eq!(
457 engine.layout(first).unwrap().x,
458 250.0,
459 "the first item now starts at the right edge"
460 );
461 assert_eq!(engine.layout(second).unwrap().x, 200.0);
462 }
463
464 #[test]
465 fn flipping_direction_moves_logical_padding_to_the_other_edge() {
466 let mut engine = LayoutEngine::new();
467 let child = engine
468 .new_leaf(LayoutStyle::new().width(50.0).height(10.0))
469 .unwrap();
470 let box_ = engine
471 .new_container(
472 LayoutStyle::new()
473 .flex_column()
474 .width(300.0)
475 .height(100.0)
476 .padding_start(20.0),
477 &[child],
478 )
479 .unwrap();
480 lay_out(&mut engine, box_);
481 assert_eq!(engine.layout(child).unwrap().x, 20.0);
482
483 engine.set_direction(Direction::Rtl);
484 engine.mark_dirty(box_).unwrap();
485 lay_out(&mut engine, box_);
486 assert_eq!(
487 engine.layout(child).unwrap().x,
488 0.0,
489 "padding moved to the right edge, so the child starts flush left"
490 );
491 }
492
493 #[test]
494 fn setting_the_same_direction_reports_no_change() {
495 let mut engine = LayoutEngine::new();
496 assert!(!engine.set_direction(Direction::Ltr));
497 assert!(engine.set_direction(Direction::Rtl));
498 assert!(!engine.set_direction(Direction::Rtl));
499 }
500
501 #[test]
502 fn restyling_a_node_drops_the_logical_edges_it_no_longer_has() {
503 let mut engine = LayoutEngine::new();
505 let node = engine
506 .new_leaf(LayoutStyle::new().padding_start(20.0).width(50.0))
507 .unwrap();
508 engine
509 .set_style(node, LayoutStyle::new().width(50.0))
510 .unwrap();
511 engine.set_direction(Direction::Rtl);
512 let style = engine.tree.style(node).unwrap();
513 assert_eq!(style.padding.left, taffy::LengthPercentage::length(0.0));
514 assert_eq!(style.padding.right, taffy::LengthPercentage::length(0.0));
515 }
516
517 #[test]
518 fn a_gap_margin_follows_the_edge_its_row_leads_from() {
519 let mut engine = LayoutEngine::new();
520 let first = engine.new_leaf(LayoutStyle::new().width(50.0)).unwrap();
521 let second = engine.new_leaf(LayoutStyle::new().width(50.0)).unwrap();
522 let row = engine
523 .new_container(LayoutStyle::new().flex_row().width(300.0), &[first, second])
524 .unwrap();
525 engine.set_leading_margin(second, true, 8.0);
526 assert_eq!(
527 engine.tree.style(second).unwrap().margin.left,
528 taffy::LengthPercentageAuto::length(8.0)
529 );
530
531 engine.set_direction(Direction::Rtl);
532 engine.mark_dirty(row).unwrap();
533 let margin = engine.tree.style(second).unwrap().margin;
534 assert_eq!(
535 margin.right,
536 taffy::LengthPercentageAuto::length(8.0),
537 "the gap moved to the edge the reversed row leads from"
538 );
539 assert_eq!(
540 margin.left,
541 taffy::LengthPercentageAuto::length(0.0),
542 "and does not linger on the old one"
543 );
544 }
545
546 #[test]
547 fn engine_leaf_layout() {
548 let mut engine = LayoutEngine::new();
549 let leaf = engine
550 .new_leaf(LayoutStyle::new().width(50.0).height(40.0))
551 .unwrap();
552 engine
553 .compute_layout(
554 leaf,
555 AvailableSpace::Definite(200.0),
556 AvailableSpace::Definite(200.0),
557 )
558 .unwrap();
559 let rect = engine.layout(leaf).unwrap();
560 assert_eq!(rect.width, 50.0_f32);
561 assert_eq!(rect.height, 40.0_f32);
562 }
563
564 #[test]
565 fn engine_flex_row_positions() {
566 let mut engine = LayoutEngine::new();
567 let child1 = engine
568 .new_leaf(LayoutStyle::new().width(100.0).height(100.0))
569 .unwrap();
570 let child2 = engine
571 .new_leaf(LayoutStyle::new().width(100.0).height(100.0))
572 .unwrap();
573 let root = engine
574 .new_container(
575 LayoutStyle::new().flex_row().width(200.0).height(100.0),
576 &[child1, child2],
577 )
578 .unwrap();
579 engine
580 .compute_layout(
581 root,
582 AvailableSpace::Definite(200.0),
583 AvailableSpace::Definite(100.0),
584 )
585 .unwrap();
586
587 let r1 = engine.layout(child1).unwrap();
588 let r2 = engine.layout(child2).unwrap();
589 assert_eq!(r1.x, 0.0_f32);
590 assert_eq!(r1.y, 0.0_f32);
591 assert_eq!(r2.x, 100.0_f32);
592 assert_eq!(r2.y, 0.0_f32);
593 }
594
595 #[test]
596 fn engine_flex_column_positions() {
597 let mut engine = LayoutEngine::new();
598 let child1 = engine
599 .new_leaf(LayoutStyle::new().width(100.0).height(100.0))
600 .unwrap();
601 let child2 = engine
602 .new_leaf(LayoutStyle::new().width(100.0).height(100.0))
603 .unwrap();
604 let root = engine
605 .new_container(
606 LayoutStyle::new().flex_column().width(100.0).height(200.0),
607 &[child1, child2],
608 )
609 .unwrap();
610 engine
611 .compute_layout(
612 root,
613 AvailableSpace::Definite(100.0),
614 AvailableSpace::Definite(200.0),
615 )
616 .unwrap();
617
618 let r1 = engine.layout(child1).unwrap();
619 let r2 = engine.layout(child2).unwrap();
620 assert_eq!(r1.x, 0.0_f32);
621 assert_eq!(r1.y, 0.0_f32);
622 assert_eq!(r2.x, 0.0_f32);
623 assert_eq!(r2.y, 100.0_f32);
624 }
625
626 #[test]
627 fn engine_walk_absolute() {
628 let mut engine = LayoutEngine::new();
629 let inner_child = engine
630 .new_leaf(LayoutStyle::new().width(50.0).height(50.0))
631 .unwrap();
632 let inner = engine
633 .new_container(
634 LayoutStyle::new().flex_row().width(50.0).height(50.0),
635 &[inner_child],
636 )
637 .unwrap();
638 let outer_first = engine
639 .new_leaf(LayoutStyle::new().width(100.0).height(50.0))
640 .unwrap();
641 let root = engine
642 .new_container(
643 LayoutStyle::new().flex_row().width(150.0).height(50.0),
644 &[outer_first, inner],
645 )
646 .unwrap();
647 engine
648 .compute_layout(
649 root,
650 AvailableSpace::Definite(150.0),
651 AvailableSpace::Definite(50.0),
652 )
653 .unwrap();
654
655 let mut hits: Vec<(NodeId, geometry_core::Rect)> = Vec::new();
656 engine
657 .walk(root, &mut |node, rect| {
658 hits.push((node, rect));
659 true
660 })
661 .unwrap();
662
663 let inner_child_rect = hits
664 .iter()
665 .find(|(n, _)| *n == inner_child)
666 .map(|(_, r)| *r)
667 .unwrap();
668 assert_eq!(inner_child_rect.x, 100.0_f32);
669 assert_eq!(inner_child_rect.y, 0.0_f32);
670 assert_eq!(inner_child_rect.width, 50.0_f32);
671 assert_eq!(inner_child_rect.height, 50.0_f32);
672 }
673
674 #[test]
675 fn engine_set_style() {
676 let mut engine = LayoutEngine::new();
677 let leaf = engine
678 .new_leaf(LayoutStyle::new().width(10.0).height(10.0))
679 .unwrap();
680 engine
681 .set_style(leaf, LayoutStyle::new().width(80.0).height(60.0))
682 .unwrap();
683 engine
684 .compute_layout(
685 leaf,
686 AvailableSpace::Definite(200.0),
687 AvailableSpace::Definite(200.0),
688 )
689 .unwrap();
690 let rect = engine.layout(leaf).unwrap();
691 assert_eq!(rect.width, 80.0_f32);
692 assert_eq!(rect.height, 60.0_f32);
693 }
694}