Move `NodeData` struct into `taffy_tree` module (#584)
1//! Contains [TaffyTree](crate::tree::TaffyTree): the default implementation of [LayoutTree](crate::tree::LayoutTree), and the error type for Taffy.
2use slotmap::{DefaultKey, SlotMap, SparseSecondaryMap};
3
4use crate::geometry::Size;
5use crate::style::{AvailableSpace, Display, Style};
6use crate::tree::{Cache, Layout, LayoutInput, LayoutOutput, LayoutTree, NodeId, PartialLayoutTree, RunMode};
7use crate::util::debug::{debug_log, debug_log_node};
8use crate::util::sys::{new_vec_with_capacity, ChildrenVec, Vec};
9
10#[cfg(feature = "block_layout")]
11use crate::compute::compute_block_layout;
12#[cfg(feature = "flexbox")]
13use crate::compute::compute_flexbox_layout;
14#[cfg(feature = "grid")]
15use crate::compute::compute_grid_layout;
16use crate::compute::{compute_cached_layout, compute_hidden_layout, compute_layout, compute_leaf_layout, round_layout};
17
18/// The error Taffy generates on invalid operations
19pub type TaffyResult<T> = Result<T, TaffyError>;
20
21/// An error that occurs while trying to access or modify a node's children by index.
22#[derive(Debug)]
23pub enum TaffyError {
24 /// The parent node does not have a child at `child_index`. It only has `child_count` children
25 ChildIndexOutOfBounds {
26 /// The parent node whose child was being looked up
27 parent: NodeId,
28 /// The index that was looked up
29 child_index: usize,
30 /// The total number of children the parent has
31 child_count: usize,
32 },
33 /// The parent node was not found in the [`TaffyTree`](crate::TaffyTree) instance.
34 InvalidParentNode(NodeId),
35 /// The child node was not found in the [`TaffyTree`](crate::TaffyTree) instance.
36 InvalidChildNode(NodeId),
37 /// The supplied node was not found in the [`TaffyTree`](crate::TaffyTree) instance.
38 InvalidInputNode(NodeId),
39}
40
41impl core::fmt::Display for TaffyError {
42 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
43 match self {
44 TaffyError::ChildIndexOutOfBounds { parent, child_index, child_count } => {
45 write!(f, "Index (is {child_index}) should be < child_count ({child_count}) for parent node {parent:?}")
46 }
47 TaffyError::InvalidParentNode(parent) => {
48 write!(f, "Parent Node {parent:?} is not in the TaffyTree instance")
49 }
50 TaffyError::InvalidChildNode(child) => write!(f, "Child Node {child:?} is not in the TaffyTree instance"),
51 TaffyError::InvalidInputNode(node) => write!(f, "Supplied Node {node:?} is not in the TaffyTree instance"),
52 }
53 }
54}
55
56#[cfg(feature = "std")]
57impl std::error::Error for TaffyError {}
58
59/// Global configuration values for a TaffyTree instance
60pub(crate) struct TaffyConfig {
61 /// Whether to round layout values
62 pub(crate) use_rounding: bool,
63}
64
65impl Default for TaffyConfig {
66 fn default() -> Self {
67 Self { use_rounding: true }
68 }
69}
70
71/// Layout information for a given [`Node`](crate::node::Node)
72///
73/// Stored in a [`TaffyTree`].
74struct NodeData {
75 /// The layout strategy used by this node
76 pub(crate) style: Style,
77
78 /// The always unrounded results of the layout computation. We must store this separately from the rounded
79 /// layout to avoid errors from rounding already-rounded values. See <https://github.com/DioxusLabs/taffy/issues/501>.
80 pub(crate) unrounded_layout: Layout,
81
82 /// The final results of the layout computation.
83 /// These may be rounded or unrounded depending on what the `use_rounding` config setting is set to.
84 pub(crate) final_layout: Layout,
85
86 /// Should we try and measure this node?
87 pub(crate) needs_measure: bool,
88
89 /// The cached results of the layout computation
90 pub(crate) cache: Cache,
91}
92
93impl NodeData {
94 /// Create the data for a new node
95 #[must_use]
96 pub const fn new(style: Style) -> Self {
97 Self {
98 style,
99 cache: Cache::new(),
100 unrounded_layout: Layout::new(),
101 final_layout: Layout::new(),
102 needs_measure: false,
103 }
104 }
105
106 /// Marks a node and all of its parents (recursively) as dirty
107 ///
108 /// This clears any cached data and signals that the data must be recomputed.
109 #[inline]
110 pub fn mark_dirty(&mut self) {
111 self.cache.clear()
112 }
113}
114
115/// An entire tree of UI nodes. The entry point to Taffy's high-level API.
116///
117/// Allows you to build a tree of UI nodes, run Taffy's layout algorithms over that tree, and then access the resultant layout.
118pub struct TaffyTree<NodeContext = ()> {
119 /// The [`NodeData`] for each node stored in this tree
120 nodes: SlotMap<DefaultKey, NodeData>,
121
122 /// Functions/closures that compute the intrinsic size of leaf nodes
123 node_context_data: SparseSecondaryMap<DefaultKey, NodeContext>,
124
125 /// The children of each node
126 ///
127 /// The indexes in the outer vector correspond to the position of the parent [`NodeData`]
128 children: SlotMap<DefaultKey, ChildrenVec<NodeId>>,
129
130 /// The parents of each node
131 ///
132 /// The indexes in the outer vector correspond to the position of the child [`NodeData`]
133 parents: SlotMap<DefaultKey, Option<NodeId>>,
134
135 /// Layout mode configuration
136 config: TaffyConfig,
137}
138
139impl Default for TaffyTree {
140 fn default() -> TaffyTree<()> {
141 TaffyTree::new()
142 }
143}
144
145/// Iterator that wraps a slice of nodes, lazily converting them to u64
146pub(crate) struct TaffyChildIter<'a>(core::slice::Iter<'a, NodeId>);
147impl<'a> Iterator for TaffyChildIter<'a> {
148 type Item = NodeId;
149
150 fn next(&mut self) -> Option<Self::Item> {
151 self.0.next().copied()
152 }
153}
154
155/// View over the Taffy tree that holds the tree itself along with a reference to the context
156/// and implements LayoutTree. This allows the context to be stored outside of the TaffyTree struct
157/// which makes the lifetimes of the context much more flexible.
158pub(crate) struct TaffyView<'t, NodeContext, MeasureFunction>
159where
160 MeasureFunction: FnMut(Size<Option<f32>>, Size<AvailableSpace>, NodeId, Option<&mut NodeContext>) -> Size<f32>,
161{
162 /// A reference to the TaffyTree
163 pub(crate) taffy: &'t mut TaffyTree<NodeContext>,
164 /// The context provided for passing to measure functions if layout is run over this struct
165 pub(crate) measure_function: MeasureFunction,
166}
167
168impl<'t, NodeContext, MeasureFunction> PartialLayoutTree for TaffyView<'t, NodeContext, MeasureFunction>
169where
170 MeasureFunction: FnMut(Size<Option<f32>>, Size<AvailableSpace>, NodeId, Option<&mut NodeContext>) -> Size<f32>,
171{
172 type ChildIter<'a> = TaffyChildIter<'a> where Self: 'a;
173
174 #[inline(always)]
175 fn child_ids(&self, node: NodeId) -> Self::ChildIter<'_> {
176 TaffyChildIter(self.taffy.children[node.into()].iter())
177 }
178
179 #[inline(always)]
180 fn child_count(&self, node: NodeId) -> usize {
181 self.taffy.children[node.into()].len()
182 }
183
184 #[inline(always)]
185 fn get_child_id(&self, node: NodeId, id: usize) -> NodeId {
186 self.taffy.children[node.into()][id]
187 }
188
189 #[inline(always)]
190 fn get_style(&self, node: NodeId) -> &Style {
191 &self.taffy.nodes[node.into()].style
192 }
193
194 #[inline(always)]
195 fn get_cache_mut(&mut self, node: NodeId) -> &mut Cache {
196 &mut self.taffy.nodes[node.into()].cache
197 }
198
199 #[inline(always)]
200 fn set_unrounded_layout(&mut self, node_id: NodeId, layout: &Layout) {
201 self.taffy.nodes[node_id.into()].unrounded_layout = *layout;
202 }
203
204 #[inline(always)]
205 fn compute_child_layout(&mut self, node: NodeId, inputs: LayoutInput) -> LayoutOutput {
206 // If RunMode is PerformHiddenLayout then this indicates that an ancestor node is `Display::None`
207 // and thus that we should lay out this node using hidden layout regardless of it's own display style.
208 if inputs.run_mode == RunMode::PerformHiddenLayout {
209 debug_log!("HIDDEN");
210 return compute_hidden_layout(self, node);
211 }
212
213 // We run the following wrapped in "compute_cached_layout", which will check the cache for an entry matching the node and inputs and:
214 // - Return that entry if exists
215 // - Else call the passed closure (below) to compute the result
216 //
217 // If there was no cache match and a new result needs to be computed then that result will be added to the cache
218 compute_cached_layout(self, node, inputs, |tree, node, inputs| {
219 let display_mode = tree.get_style(node).display;
220 let has_children = tree.child_count(node) > 0;
221
222 debug_log!(display_mode);
223 debug_log_node!(
224 inputs.known_dimensions,
225 inputs.parent_size,
226 inputs.available_space,
227 inputs.run_mode,
228 inputs.sizing_mode
229 );
230
231 // Dispatch to a layout algorithm based on the node's display style and whether the node has children or not.
232 match (display_mode, has_children) {
233 (Display::None, _) => compute_hidden_layout(tree, node),
234 #[cfg(feature = "block_layout")]
235 (Display::Block, true) => compute_block_layout(tree, node, inputs),
236 #[cfg(feature = "flexbox")]
237 (Display::Flex, true) => compute_flexbox_layout(tree, node, inputs),
238 #[cfg(feature = "grid")]
239 (Display::Grid, true) => compute_grid_layout(tree, node, inputs),
240 (_, false) => {
241 let node_key = node.into();
242 let style = &tree.taffy.nodes[node_key].style;
243 let needs_measure = tree.taffy.nodes[node_key].needs_measure;
244 let measure_function = needs_measure.then_some(|known_dimensions, available_space| {
245 let node_context = tree.taffy.node_context_data.get_mut(node_key);
246 (tree.measure_function)(known_dimensions, available_space, node, node_context)
247 });
248 compute_leaf_layout(inputs, style, measure_function)
249 }
250 }
251 })
252 }
253}
254
255impl<'t, NodeContext, MeasureFunction> LayoutTree for TaffyView<'t, NodeContext, MeasureFunction>
256where
257 MeasureFunction: FnMut(Size<Option<f32>>, Size<AvailableSpace>, NodeId, Option<&mut NodeContext>) -> Size<f32>,
258{
259 #[inline(always)]
260 fn get_unrounded_layout(&self, node: NodeId) -> &Layout {
261 &self.taffy.nodes[node.into()].unrounded_layout
262 }
263
264 #[inline(always)]
265 fn get_final_layout(&self, node: NodeId) -> &Layout {
266 &self.taffy.nodes[node.into()].final_layout
267 }
268
269 #[inline(always)]
270 fn set_final_layout(&mut self, node_id: NodeId, layout: &Layout) {
271 self.taffy.nodes[node_id.into()].final_layout = *layout;
272 }
273}
274
275#[allow(clippy::iter_cloned_collect)] // due to no-std support, we need to use `iter_cloned` instead of `collect`
276impl<NodeContext> TaffyTree<NodeContext> {
277 /// Creates a new [`TaffyTree`]
278 ///
279 /// The default capacity of a [`TaffyTree`] is 16 nodes.
280 #[must_use]
281 pub fn new() -> Self {
282 Self::with_capacity(16)
283 }
284
285 /// Creates a new [`TaffyTree`] that can store `capacity` nodes before reallocation
286 #[must_use]
287 pub fn with_capacity(capacity: usize) -> Self {
288 TaffyTree {
289 // TODO: make this method const upstream,
290 // so constructors here can be const
291 nodes: SlotMap::with_capacity(capacity),
292 children: SlotMap::with_capacity(capacity),
293 parents: SlotMap::with_capacity(capacity),
294 node_context_data: SparseSecondaryMap::with_capacity(capacity),
295 config: TaffyConfig::default(),
296 }
297 }
298
299 /// Enable rounding of layout values. Rounding is enabled by default.
300 pub fn enable_rounding(&mut self) {
301 self.config.use_rounding = true;
302 }
303
304 /// Disable rounding of layout values. Rounding is enabled by default.
305 pub fn disable_rounding(&mut self) {
306 self.config.use_rounding = false;
307 }
308
309 /// Creates and adds a new unattached leaf node to the tree, and returns the node of the new node
310 pub fn new_leaf(&mut self, layout: Style) -> TaffyResult<NodeId> {
311 let id = self.nodes.insert(NodeData::new(layout));
312 let _ = self.children.insert(new_vec_with_capacity(0));
313 let _ = self.parents.insert(None);
314
315 Ok(id.into())
316 }
317
318 /// Creates and adds a new unattached leaf node to the tree, and returns the [`NodeId`] of the new node
319 ///
320 /// Creates and adds a new leaf node with a supplied context
321 pub fn new_leaf_with_context(&mut self, layout: Style, context: NodeContext) -> TaffyResult<NodeId> {
322 let mut data = NodeData::new(layout);
323 data.needs_measure = true;
324
325 let id = self.nodes.insert(data);
326 self.node_context_data.insert(id, context);
327
328 let _ = self.children.insert(new_vec_with_capacity(0));
329 let _ = self.parents.insert(None);
330
331 Ok(id.into())
332 }
333
334 /// Creates and adds a new node, which may have any number of `children`
335 pub fn new_with_children(&mut self, layout: Style, children: &[NodeId]) -> TaffyResult<NodeId> {
336 let id = NodeId::from(self.nodes.insert(NodeData::new(layout)));
337
338 for child in children {
339 self.parents[(*child).into()] = Some(id);
340 }
341
342 let _ = self.children.insert(children.iter().copied().collect::<_>());
343 let _ = self.parents.insert(None);
344
345 Ok(id)
346 }
347
348 /// Drops all nodes in the tree
349 pub fn clear(&mut self) {
350 self.nodes.clear();
351 self.children.clear();
352 self.parents.clear();
353 }
354
355 /// Remove a specific node from the tree and drop it
356 ///
357 /// Returns the id of the node removed.
358 pub fn remove(&mut self, node: NodeId) -> TaffyResult<NodeId> {
359 let key = node.into();
360 if let Some(parent) = self.parents[key] {
361 if let Some(children) = self.children.get_mut(parent.into()) {
362 children.retain(|f| *f != node);
363 }
364 }
365
366 // Remove "parent" references to a node when removing that node
367 if let Some(children) = self.children.get(key) {
368 for child in children.iter().copied() {
369 self.parents[child.into()] = None;
370 }
371 }
372
373 let _ = self.children.remove(key);
374 let _ = self.parents.remove(key);
375 let _ = self.nodes.remove(key);
376
377 Ok(node)
378 }
379
380 /// Sets the context data associated with the node
381 pub fn set_node_context(&mut self, node: NodeId, measure: Option<NodeContext>) -> TaffyResult<()> {
382 let key = node.into();
383 if let Some(measure) = measure {
384 self.nodes[key].needs_measure = true;
385 self.node_context_data.insert(key, measure);
386 } else {
387 self.nodes[key].needs_measure = false;
388 self.node_context_data.remove(key);
389 }
390
391 self.mark_dirty(node)?;
392
393 Ok(())
394 }
395
396 /// Gets a reference to the the context data associated with the node
397 pub fn get_node_context(&self, node: NodeId) -> Option<&NodeContext> {
398 self.node_context_data.get(node.into())
399 }
400
401 /// Gets a mutable reference to the the context data associated with the node
402 pub fn get_node_context_mut(&mut self, node: NodeId) -> Option<&mut NodeContext> {
403 self.node_context_data.get_mut(node.into())
404 }
405
406 /// Adds a `child` node under the supplied `parent`
407 pub fn add_child(&mut self, parent: NodeId, child: NodeId) -> TaffyResult<()> {
408 let parent_key = parent.into();
409 let child_key = child.into();
410 self.parents[child_key] = Some(parent);
411 self.children[parent_key].push(child);
412 self.mark_dirty(parent)?;
413
414 Ok(())
415 }
416
417 /// Inserts a `child` node at the given `child_index` under the supplied `parent`, shifting all children after it to the right.
418 pub fn insert_child_at_index(&mut self, parent: NodeId, child_index: usize, child: NodeId) -> TaffyResult<()> {
419 let parent_key = parent.into();
420
421 let child_count = self.children[parent_key].len();
422 if child_index > child_count {
423 return Err(TaffyError::ChildIndexOutOfBounds { parent, child_index, child_count });
424 }
425
426 self.parents[child.into()] = Some(parent);
427 self.children[parent_key].insert(child_index, child);
428 self.mark_dirty(parent)?;
429
430 Ok(())
431 }
432
433 /// Directly sets the `children` of the supplied `parent`
434 pub fn set_children(&mut self, parent: NodeId, children: &[NodeId]) -> TaffyResult<()> {
435 let parent_key = parent.into();
436
437 // Remove node as parent from all its current children.
438 for child in &self.children[parent_key] {
439 self.parents[(*child).into()] = None;
440 }
441
442 // Build up relation node <-> child
443 for child in children {
444 self.parents[(*child).into()] = Some(parent);
445 }
446
447 let parent_children = &mut self.children[parent_key];
448 parent_children.clear();
449 children.iter().for_each(|child| parent_children.push(*child));
450
451 self.mark_dirty(parent)?;
452
453 Ok(())
454 }
455
456 /// Removes the `child` of the parent `node`
457 ///
458 /// The child is not removed from the tree entirely, it is simply no longer attached to its previous parent.
459 pub fn remove_child(&mut self, parent: NodeId, child: NodeId) -> TaffyResult<NodeId> {
460 let index = self.children[parent.into()].iter().position(|n| *n == child).unwrap();
461 self.remove_child_at_index(parent, index)
462 }
463
464 /// Removes the child at the given `index` from the `parent`
465 ///
466 /// The child is not removed from the tree entirely, it is simply no longer attached to its previous parent.
467 pub fn remove_child_at_index(&mut self, parent: NodeId, child_index: usize) -> TaffyResult<NodeId> {
468 let parent_key = parent.into();
469 let child_count = self.children[parent_key].len();
470 if child_index >= child_count {
471 return Err(TaffyError::ChildIndexOutOfBounds { parent, child_index, child_count });
472 }
473
474 let child = self.children[parent_key].remove(child_index);
475 self.parents[child.into()] = None;
476
477 self.mark_dirty(parent)?;
478
479 Ok(child)
480 }
481
482 /// Replaces the child at the given `child_index` from the `parent` node with the new `child` node
483 ///
484 /// The child is not removed from the tree entirely, it is simply no longer attached to its previous parent.
485 pub fn replace_child_at_index(
486 &mut self,
487 parent: NodeId,
488 child_index: usize,
489 new_child: NodeId,
490 ) -> TaffyResult<NodeId> {
491 let parent_key = parent.into();
492
493 let child_count = self.children[parent_key].len();
494 if child_index >= child_count {
495 return Err(TaffyError::ChildIndexOutOfBounds { parent, child_index, child_count });
496 }
497
498 self.parents[new_child.into()] = Some(parent);
499 let old_child = core::mem::replace(&mut self.children[parent_key][child_index], new_child);
500 self.parents[old_child.into()] = None;
501
502 self.mark_dirty(parent)?;
503
504 Ok(old_child)
505 }
506
507 /// Returns the child node of the parent `node` at the provided `child_index`
508 pub fn child_at_index(&self, parent: NodeId, child_index: usize) -> TaffyResult<NodeId> {
509 let parent_key = parent.into();
510 let child_count = self.children[parent_key].len();
511 if child_index >= child_count {
512 return Err(TaffyError::ChildIndexOutOfBounds { parent, child_index, child_count });
513 }
514
515 Ok(self.children[parent_key][child_index])
516 }
517
518 /// Returns the total number of nodes in the tree
519 pub fn total_node_count(&self) -> usize {
520 self.nodes.len()
521 }
522
523 /// Returns the number of children of the `parent` node
524 pub fn child_count(&self, parent: NodeId) -> TaffyResult<usize> {
525 Ok(self.children[parent.into()].len())
526 }
527
528 /// Returns a list of children that belong to the parent node
529 pub fn children(&self, parent: NodeId) -> TaffyResult<Vec<NodeId>> {
530 Ok(self.children[parent.into()].iter().copied().collect::<_>())
531 }
532
533 /// Sets the [`Style`] of the provided `node`
534 pub fn set_style(&mut self, node: NodeId, style: Style) -> TaffyResult<()> {
535 self.nodes[node.into()].style = style;
536 self.mark_dirty(node)?;
537 Ok(())
538 }
539
540 /// Gets the [`Style`] of the provided `node`
541 pub fn style(&self, node: NodeId) -> TaffyResult<&Style> {
542 Ok(&self.nodes[node.into()].style)
543 }
544
545 /// Return this node layout relative to its parent
546 pub fn layout(&self, node: NodeId) -> TaffyResult<&Layout> {
547 if self.config.use_rounding {
548 Ok(&self.nodes[node.into()].final_layout)
549 } else {
550 Ok(&self.nodes[node.into()].unrounded_layout)
551 }
552 }
553
554 /// Marks the layout computation of this node and its children as outdated
555 ///
556 /// Performs a recursive depth-first search up the tree until the root node is reached
557 ///
558 /// WARNING: this will stack-overflow if the tree contains a cycle
559 pub fn mark_dirty(&mut self, node: NodeId) -> TaffyResult<()> {
560 /// WARNING: this will stack-overflow if the tree contains a cycle
561 fn mark_dirty_recursive(
562 nodes: &mut SlotMap<DefaultKey, NodeData>,
563 parents: &SlotMap<DefaultKey, Option<NodeId>>,
564 node_key: DefaultKey,
565 ) {
566 nodes[node_key].mark_dirty();
567
568 if let Some(Some(node)) = parents.get(node_key) {
569 mark_dirty_recursive(nodes, parents, (*node).into());
570 }
571 }
572
573 mark_dirty_recursive(&mut self.nodes, &self.parents, node.into());
574
575 Ok(())
576 }
577
578 /// Indicates whether the layout of this node (and its children) need to be recomputed
579 pub fn dirty(&self, node: NodeId) -> TaffyResult<bool> {
580 Ok(self.nodes[node.into()].cache.is_empty())
581 }
582
583 /// Updates the stored layout of the provided `node` and its children
584 pub fn compute_layout_with_measure<MeasureFunction>(
585 &mut self,
586 node_id: NodeId,
587 available_space: Size<AvailableSpace>,
588 measure_function: MeasureFunction,
589 ) -> Result<(), TaffyError>
590 where
591 MeasureFunction: FnMut(Size<Option<f32>>, Size<AvailableSpace>, NodeId, Option<&mut NodeContext>) -> Size<f32>,
592 {
593 let use_rounding = self.config.use_rounding;
594 let mut taffy_view = TaffyView { taffy: self, measure_function };
595 compute_layout(&mut taffy_view, node_id, available_space);
596 if use_rounding {
597 round_layout(&mut taffy_view, node_id);
598 }
599 Ok(())
600 }
601
602 /// Updates the stored layout of the provided `node` and its children
603 pub fn compute_layout(&mut self, node: NodeId, available_space: Size<AvailableSpace>) -> Result<(), TaffyError> {
604 self.compute_layout_with_measure(node, available_space, |_, _, _, _| Size::ZERO)
605 }
606
607 /// Prints a debug representation of the tree's layout
608 #[cfg(feature = "std")]
609 pub fn print_tree(&mut self, root: NodeId) {
610 let taffy_view = TaffyView { taffy: self, measure_function: |_, _, _, _| Size::ZERO };
611 crate::util::print_tree(&taffy_view, root)
612 }
613
614 /// Returns an instance of LayoutTree representing the TaffyTree
615 #[cfg(test)]
616 pub(crate) fn as_layout_tree(&mut self) -> impl LayoutTree + '_ {
617 TaffyView { taffy: self, measure_function: |_, _, _, _| Size::ZERO }
618 }
619}
620
621#[cfg(test)]
622mod tests {
623 #![allow(clippy::bool_assert_comparison)]
624
625 use super::*;
626 use crate::style::{Dimension, Display, FlexDirection};
627 use crate::style_helpers::*;
628 use crate::util::sys;
629
630 fn size_measure_function(
631 known_dimensions: Size<Option<f32>>,
632 _available_space: Size<AvailableSpace>,
633 _node_id: NodeId,
634 node_context: Option<&mut Size<f32>>,
635 ) -> Size<f32> {
636 known_dimensions.unwrap_or(node_context.cloned().unwrap_or(Size::ZERO))
637 }
638
639 #[test]
640 fn new_should_allocate_default_capacity() {
641 const DEFAULT_CAPACITY: usize = 16; // This is the capacity defined in the `impl Default`
642 let taffy: TaffyTree<()> = TaffyTree::new();
643
644 assert!(taffy.children.capacity() >= DEFAULT_CAPACITY);
645 assert!(taffy.parents.capacity() >= DEFAULT_CAPACITY);
646 assert!(taffy.nodes.capacity() >= DEFAULT_CAPACITY);
647 }
648
649 #[test]
650 fn test_with_capacity() {
651 const CAPACITY: usize = 8;
652 let taffy: TaffyTree<()> = TaffyTree::with_capacity(CAPACITY);
653
654 assert!(taffy.children.capacity() >= CAPACITY);
655 assert!(taffy.parents.capacity() >= CAPACITY);
656 assert!(taffy.nodes.capacity() >= CAPACITY);
657 }
658
659 #[test]
660 fn test_new_leaf() {
661 let mut taffy: TaffyTree<()> = TaffyTree::new();
662
663 let res = taffy.new_leaf(Style::default());
664 assert!(res.is_ok());
665 let node = res.unwrap();
666
667 // node should be in the taffy tree and have no children
668 assert!(taffy.child_count(node).unwrap() == 0);
669 }
670
671 #[test]
672 fn new_leaf_with_context() {
673 let mut taffy: TaffyTree<Size<f32>> = TaffyTree::new();
674
675 let res = taffy.new_leaf_with_context(Style::default(), Size::ZERO);
676 assert!(res.is_ok());
677 let node = res.unwrap();
678
679 // node should be in the taffy tree and have no children
680 assert!(taffy.child_count(node).unwrap() == 0);
681 }
682
683 /// Test that new_with_children works as expected
684 #[test]
685 fn test_new_with_children() {
686 let mut taffy: TaffyTree<()> = TaffyTree::new();
687 let child0 = taffy.new_leaf(Style::default()).unwrap();
688 let child1 = taffy.new_leaf(Style::default()).unwrap();
689 let node = taffy.new_with_children(Style::default(), &[child0, child1]).unwrap();
690
691 // node should have two children
692 assert_eq!(taffy.child_count(node).unwrap(), 2);
693 assert_eq!(taffy.children(node).unwrap()[0], child0);
694 assert_eq!(taffy.children(node).unwrap()[1], child1);
695 }
696
697 #[test]
698 fn remove_node_should_remove() {
699 let mut taffy: TaffyTree<()> = TaffyTree::new();
700
701 let node = taffy.new_leaf(Style::default()).unwrap();
702
703 let _ = taffy.remove(node).unwrap();
704 }
705
706 #[test]
707 fn remove_node_should_detach_herarchy() {
708 let mut taffy: TaffyTree<()> = TaffyTree::new();
709
710 // Build a linear tree layout: <0> <- <1> <- <2>
711 let node2 = taffy.new_leaf(Style::default()).unwrap();
712 let node1 = taffy.new_with_children(Style::default(), &[node2]).unwrap();
713 let node0 = taffy.new_with_children(Style::default(), &[node1]).unwrap();
714
715 // Both node0 and node1 should have 1 child nodes
716 assert_eq!(taffy.children(node0).unwrap().as_slice(), &[node1]);
717 assert_eq!(taffy.children(node1).unwrap().as_slice(), &[node2]);
718
719 // Disconnect the tree: <0> <2>
720 let _ = taffy.remove(node1).unwrap();
721
722 // Both remaining nodes should have no child nodes
723 assert!(taffy.children(node0).unwrap().is_empty());
724 assert!(taffy.children(node2).unwrap().is_empty());
725 }
726
727 #[test]
728 fn remove_last_node() {
729 let mut taffy: TaffyTree<()> = TaffyTree::new();
730
731 let parent = taffy.new_leaf(Style::default()).unwrap();
732 let child = taffy.new_leaf(Style::default()).unwrap();
733 taffy.add_child(parent, child).unwrap();
734
735 taffy.remove(child).unwrap();
736 taffy.remove(parent).unwrap();
737 }
738
739 #[test]
740 fn set_measure() {
741 let mut taffy: TaffyTree<Size<f32>> = TaffyTree::new();
742 let node = taffy.new_leaf_with_context(Style::default(), Size { width: 200.0, height: 200.0 }).unwrap();
743 taffy.compute_layout_with_measure(node, Size::MAX_CONTENT, size_measure_function).unwrap();
744 assert_eq!(taffy.layout(node).unwrap().size.width, 200.0);
745
746 taffy.set_node_context(node, Some(Size { width: 100.0, height: 100.0 })).unwrap();
747 taffy.compute_layout_with_measure(node, Size::MAX_CONTENT, size_measure_function).unwrap();
748 assert_eq!(taffy.layout(node).unwrap().size.width, 100.0);
749 }
750
751 #[test]
752 fn set_measure_of_previously_unmeasured_node() {
753 let mut taffy: TaffyTree<Size<f32>> = TaffyTree::new();
754 let node = taffy.new_leaf(Style::default()).unwrap();
755 taffy.compute_layout_with_measure(node, Size::MAX_CONTENT, size_measure_function).unwrap();
756 assert_eq!(taffy.layout(node).unwrap().size.width, 0.0);
757
758 taffy.set_node_context(node, Some(Size { width: 100.0, height: 100.0 })).unwrap();
759 taffy.compute_layout_with_measure(node, Size::MAX_CONTENT, size_measure_function).unwrap();
760 assert_eq!(taffy.layout(node).unwrap().size.width, 100.0);
761 }
762
763 /// Test that adding `add_child()` works
764 #[test]
765 fn add_child() {
766 let mut taffy: TaffyTree<()> = TaffyTree::new();
767 let node = taffy.new_leaf(Style::default()).unwrap();
768 assert_eq!(taffy.child_count(node).unwrap(), 0);
769
770 let child0 = taffy.new_leaf(Style::default()).unwrap();
771 taffy.add_child(node, child0).unwrap();
772 assert_eq!(taffy.child_count(node).unwrap(), 1);
773
774 let child1 = taffy.new_leaf(Style::default()).unwrap();
775 taffy.add_child(node, child1).unwrap();
776 assert_eq!(taffy.child_count(node).unwrap(), 2);
777 }
778
779 #[test]
780 fn insert_child_at_index() {
781 let mut taffy: TaffyTree<()> = TaffyTree::new();
782
783 let child0 = taffy.new_leaf(Style::default()).unwrap();
784 let child1 = taffy.new_leaf(Style::default()).unwrap();
785 let child2 = taffy.new_leaf(Style::default()).unwrap();
786
787 let node = taffy.new_leaf(Style::default()).unwrap();
788 assert_eq!(taffy.child_count(node).unwrap(), 0);
789
790 taffy.insert_child_at_index(node, 0, child0).unwrap();
791 assert_eq!(taffy.child_count(node).unwrap(), 1);
792 assert_eq!(taffy.children(node).unwrap()[0], child0);
793
794 taffy.insert_child_at_index(node, 0, child1).unwrap();
795 assert_eq!(taffy.child_count(node).unwrap(), 2);
796 assert_eq!(taffy.children(node).unwrap()[0], child1);
797 assert_eq!(taffy.children(node).unwrap()[1], child0);
798
799 taffy.insert_child_at_index(node, 1, child2).unwrap();
800 assert_eq!(taffy.child_count(node).unwrap(), 3);
801 assert_eq!(taffy.children(node).unwrap()[0], child1);
802 assert_eq!(taffy.children(node).unwrap()[1], child2);
803 assert_eq!(taffy.children(node).unwrap()[2], child0);
804 }
805
806 #[test]
807 fn set_children() {
808 let mut taffy: TaffyTree<()> = TaffyTree::new();
809
810 let child0 = taffy.new_leaf(Style::default()).unwrap();
811 let child1 = taffy.new_leaf(Style::default()).unwrap();
812 let node = taffy.new_with_children(Style::default(), &[child0, child1]).unwrap();
813
814 assert_eq!(taffy.child_count(node).unwrap(), 2);
815 assert_eq!(taffy.children(node).unwrap()[0], child0);
816 assert_eq!(taffy.children(node).unwrap()[1], child1);
817
818 let child2 = taffy.new_leaf(Style::default()).unwrap();
819 let child3 = taffy.new_leaf(Style::default()).unwrap();
820 taffy.set_children(node, &[child2, child3]).unwrap();
821
822 assert_eq!(taffy.child_count(node).unwrap(), 2);
823 assert_eq!(taffy.children(node).unwrap()[0], child2);
824 assert_eq!(taffy.children(node).unwrap()[1], child3);
825 }
826
827 /// Test that removing a child works
828 #[test]
829 fn remove_child() {
830 let mut taffy: TaffyTree<()> = TaffyTree::new();
831 let child0 = taffy.new_leaf(Style::default()).unwrap();
832 let child1 = taffy.new_leaf(Style::default()).unwrap();
833 let node = taffy.new_with_children(Style::default(), &[child0, child1]).unwrap();
834
835 assert_eq!(taffy.child_count(node).unwrap(), 2);
836
837 taffy.remove_child(node, child0).unwrap();
838 assert_eq!(taffy.child_count(node).unwrap(), 1);
839 assert_eq!(taffy.children(node).unwrap()[0], child1);
840
841 taffy.remove_child(node, child1).unwrap();
842 assert_eq!(taffy.child_count(node).unwrap(), 0);
843 }
844
845 #[test]
846 fn remove_child_at_index() {
847 let mut taffy: TaffyTree<()> = TaffyTree::new();
848 let child0 = taffy.new_leaf(Style::default()).unwrap();
849 let child1 = taffy.new_leaf(Style::default()).unwrap();
850 let node = taffy.new_with_children(Style::default(), &[child0, child1]).unwrap();
851
852 assert_eq!(taffy.child_count(node).unwrap(), 2);
853
854 taffy.remove_child_at_index(node, 0).unwrap();
855 assert_eq!(taffy.child_count(node).unwrap(), 1);
856 assert_eq!(taffy.children(node).unwrap()[0], child1);
857
858 taffy.remove_child_at_index(node, 0).unwrap();
859 assert_eq!(taffy.child_count(node).unwrap(), 0);
860 }
861
862 // Related to: https://github.com/DioxusLabs/taffy/issues/510
863 #[test]
864 fn remove_child_updates_parents() {
865 let mut taffy: TaffyTree<()> = TaffyTree::new();
866
867 let parent = taffy.new_leaf(Style::default()).unwrap();
868 let child = taffy.new_leaf(Style::default()).unwrap();
869
870 taffy.add_child(parent, child).unwrap();
871
872 taffy.remove(parent).unwrap();
873
874 // Once the parent is removed this shouldn't panic.
875 assert!(taffy.set_children(child, &[]).is_ok());
876 }
877
878 #[test]
879 fn replace_child_at_index() {
880 let mut taffy: TaffyTree<()> = TaffyTree::new();
881
882 let child0 = taffy.new_leaf(Style::default()).unwrap();
883 let child1 = taffy.new_leaf(Style::default()).unwrap();
884
885 let node = taffy.new_with_children(Style::default(), &[child0]).unwrap();
886 assert_eq!(taffy.child_count(node).unwrap(), 1);
887 assert_eq!(taffy.children(node).unwrap()[0], child0);
888
889 taffy.replace_child_at_index(node, 0, child1).unwrap();
890 assert_eq!(taffy.child_count(node).unwrap(), 1);
891 assert_eq!(taffy.children(node).unwrap()[0], child1);
892 }
893 #[test]
894 fn test_child_at_index() {
895 let mut taffy: TaffyTree<()> = TaffyTree::new();
896 let child0 = taffy.new_leaf(Style::default()).unwrap();
897 let child1 = taffy.new_leaf(Style::default()).unwrap();
898 let child2 = taffy.new_leaf(Style::default()).unwrap();
899 let node = taffy.new_with_children(Style::default(), &[child0, child1, child2]).unwrap();
900
901 assert!(if let Ok(result) = taffy.child_at_index(node, 0) { result == child0 } else { false });
902 assert!(if let Ok(result) = taffy.child_at_index(node, 1) { result == child1 } else { false });
903 assert!(if let Ok(result) = taffy.child_at_index(node, 2) { result == child2 } else { false });
904 }
905 #[test]
906 fn test_child_count() {
907 let mut taffy: TaffyTree<()> = TaffyTree::new();
908 let child0 = taffy.new_leaf(Style::default()).unwrap();
909 let child1 = taffy.new_leaf(Style::default()).unwrap();
910 let node = taffy.new_with_children(Style::default(), &[child0, child1]).unwrap();
911
912 assert!(if let Ok(count) = taffy.child_count(node) { count == 2 } else { false });
913 assert!(if let Ok(count) = taffy.child_count(child0) { count == 0 } else { false });
914 assert!(if let Ok(count) = taffy.child_count(child1) { count == 0 } else { false });
915 }
916
917 #[allow(clippy::vec_init_then_push)]
918 #[test]
919 fn test_children() {
920 let mut taffy: TaffyTree<()> = TaffyTree::new();
921 let child0 = taffy.new_leaf(Style::default()).unwrap();
922 let child1 = taffy.new_leaf(Style::default()).unwrap();
923 let node = taffy.new_with_children(Style::default(), &[child0, child1]).unwrap();
924
925 let mut children = sys::Vec::new();
926 children.push(child0);
927 children.push(child1);
928
929 let children_result = taffy.children(node).unwrap();
930 assert_eq!(children_result, children);
931
932 assert!(taffy.children(child0).unwrap().is_empty());
933 }
934 #[test]
935 fn test_set_style() {
936 let mut taffy: TaffyTree<()> = TaffyTree::new();
937
938 let node = taffy.new_leaf(Style::default()).unwrap();
939 assert_eq!(taffy.style(node).unwrap().display, Display::Flex);
940
941 taffy.set_style(node, Style { display: Display::None, ..Style::default() }).unwrap();
942 assert_eq!(taffy.style(node).unwrap().display, Display::None);
943 }
944 #[test]
945 fn test_style() {
946 let mut taffy: TaffyTree<()> = TaffyTree::new();
947
948 let style = Style { display: Display::None, flex_direction: FlexDirection::RowReverse, ..Default::default() };
949
950 let node = taffy.new_leaf(style.clone()).unwrap();
951
952 let res = taffy.style(node);
953 assert!(res.is_ok());
954 assert!(res.unwrap() == &style);
955 }
956 #[test]
957 fn test_layout() {
958 let mut taffy: TaffyTree<()> = TaffyTree::new();
959 let node = taffy.new_leaf(Style::default()).unwrap();
960
961 // TODO: Improve this test?
962 let res = taffy.layout(node);
963 assert!(res.is_ok());
964 }
965
966 #[test]
967 fn test_mark_dirty() {
968 let mut taffy: TaffyTree<()> = TaffyTree::new();
969 let child0 = taffy.new_leaf(Style::default()).unwrap();
970 let child1 = taffy.new_leaf(Style::default()).unwrap();
971 let node = taffy.new_with_children(Style::default(), &[child0, child1]).unwrap();
972
973 taffy.compute_layout(node, Size::MAX_CONTENT).unwrap();
974
975 assert_eq!(taffy.dirty(child0).unwrap(), false);
976 assert_eq!(taffy.dirty(child1).unwrap(), false);
977 assert_eq!(taffy.dirty(node).unwrap(), false);
978
979 taffy.mark_dirty(node).unwrap();
980 assert_eq!(taffy.dirty(child0).unwrap(), false);
981 assert_eq!(taffy.dirty(child1).unwrap(), false);
982 assert_eq!(taffy.dirty(node).unwrap(), true);
983
984 taffy.compute_layout(node, Size::MAX_CONTENT).unwrap();
985 taffy.mark_dirty(child0).unwrap();
986 assert_eq!(taffy.dirty(child0).unwrap(), true);
987 assert_eq!(taffy.dirty(child1).unwrap(), false);
988 assert_eq!(taffy.dirty(node).unwrap(), true);
989 }
990
991 #[test]
992 fn compute_layout_should_produce_valid_result() {
993 let mut taffy: TaffyTree<()> = TaffyTree::new();
994 let node_result = taffy.new_leaf(Style {
995 size: Size { width: Dimension::Length(10f32), height: Dimension::Length(10f32) },
996 ..Default::default()
997 });
998 assert!(node_result.is_ok());
999 let node = node_result.unwrap();
1000 let layout_result = taffy.compute_layout(
1001 node,
1002 Size { width: AvailableSpace::Definite(100.), height: AvailableSpace::Definite(100.) },
1003 );
1004 assert!(layout_result.is_ok());
1005 }
1006
1007 #[test]
1008 fn make_sure_layout_location_is_top_left() {
1009 use crate::prelude::Rect;
1010
1011 let mut taffy: TaffyTree<()> = TaffyTree::new();
1012
1013 let node = taffy
1014 .new_leaf(Style {
1015 size: Size { width: Dimension::Percent(1f32), height: Dimension::Percent(1f32) },
1016 ..Default::default()
1017 })
1018 .unwrap();
1019
1020 let root = taffy
1021 .new_with_children(
1022 Style {
1023 size: Size { width: Dimension::Length(100f32), height: Dimension::Length(100f32) },
1024 padding: Rect {
1025 left: length(10f32),
1026 right: length(20f32),
1027 top: length(30f32),
1028 bottom: length(40f32),
1029 },
1030 ..Default::default()
1031 },
1032 &[node],
1033 )
1034 .unwrap();
1035
1036 taffy.compute_layout(root, Size::MAX_CONTENT).unwrap();
1037
1038 // If Layout::location represents top-left coord, 'node' location
1039 // must be (due applied 'root' padding): {x: 10, y: 30}.
1040 //
1041 // It's important, since result will be different for each other
1042 // coordinate space:
1043 // - bottom-left: {x: 10, y: 40}
1044 // - top-right: {x: 20, y: 30}
1045 // - bottom-right: {x: 20, y: 40}
1046 let layout = taffy.layout(node).unwrap();
1047 assert_eq!(layout.location.x, 10f32);
1048 assert_eq!(layout.location.y, 30f32);
1049 }
1050}