1//! Contains [TaffyTree](crate::tree::TaffyTree): the default implementation of [LayoutTree](crate::tree::LayoutTree), and the error type for Taffy.
2#[cfg(not(feature = "std"))]
3use slotmap::SecondaryMap;
4#[cfg(feature = "std")]
5use slotmap::SparseSecondaryMap as SecondaryMap;
6use slotmap::{DefaultKey, SlotMap};
7
8use crate::geometry::Size;
9use crate::style::{AvailableSpace, Display, Style};
10use crate::sys::DefaultCheapStr;
11use crate::tree::{
12 Cache, ClearState, Layout, LayoutInput, LayoutOutput, LayoutPartialTree, NodeId, PrintTree, RoundTree, RunMode,
13 TraversePartialTree, TraverseTree,
14};
15use crate::util::debug::{debug_log, debug_log_node};
16use crate::util::sys::{new_vec_with_capacity, ChildrenVec, Vec};
17
18use crate::compute::{
19 compute_cached_layout, compute_hidden_layout, compute_leaf_layout, compute_root_layout, round_layout,
20};
21use crate::BlockFormattingContext;
22use crate::CacheTree;
23
24#[cfg(feature = "block_layout")]
25use crate::{compute::compute_block_layout, LayoutBlockContainer};
26#[cfg(feature = "flexbox")]
27use crate::{compute::compute_flexbox_layout, LayoutFlexboxContainer};
28#[cfg(feature = "grid")]
29use crate::{compute::compute_grid_layout, LayoutGridContainer};
30
31#[cfg(all(feature = "detailed_layout_info", feature = "grid"))]
32use crate::compute::grid::DetailedGridInfo;
33#[cfg(feature = "detailed_layout_info")]
34use crate::tree::layout::DetailedLayoutInfo;
35
36/// The error Taffy generates on invalid operations
37pub type TaffyResult<T> = Result<T, TaffyError>;
38
39/// An error that occurs while trying to access or modify a node's children by index.
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub enum TaffyError {
42 /// The parent node does not have a child at `child_index`. It only has `child_count` children
43 ChildIndexOutOfBounds {
44 /// The parent node whose child was being looked up
45 parent: NodeId,
46 /// The index that was looked up
47 child_index: usize,
48 /// The total number of children the parent has
49 child_count: usize,
50 },
51 /// The parent node was not found in the [`TaffyTree`](crate::TaffyTree) instance.
52 InvalidParentNode(NodeId),
53 /// The child node was not found in the [`TaffyTree`](crate::TaffyTree) instance.
54 InvalidChildNode(NodeId),
55 /// The supplied node was not found in the [`TaffyTree`](crate::TaffyTree) instance.
56 InvalidInputNode(NodeId),
57}
58
59impl core::fmt::Display for TaffyError {
60 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
61 match self {
62 TaffyError::ChildIndexOutOfBounds { parent, child_index, child_count } => {
63 write!(f, "Index (is {child_index}) should be < child_count ({child_count}) for parent node {parent:?}")
64 }
65 TaffyError::InvalidParentNode(parent) => {
66 write!(f, "Parent Node {parent:?} is not in the TaffyTree instance")
67 }
68 TaffyError::InvalidChildNode(child) => write!(f, "Child Node {child:?} is not in the TaffyTree instance"),
69 TaffyError::InvalidInputNode(node) => write!(f, "Supplied Node {node:?} is not in the TaffyTree instance"),
70 }
71 }
72}
73
74#[cfg(feature = "std")]
75impl std::error::Error for TaffyError {}
76
77/// Global configuration values for a TaffyTree instance
78#[derive(Debug, Clone, Copy)]
79pub(crate) struct TaffyConfig {
80 /// Whether to round layout values
81 pub(crate) use_rounding: bool,
82}
83
84impl Default for TaffyConfig {
85 fn default() -> Self {
86 Self { use_rounding: true }
87 }
88}
89
90/// Layout information for a given [`Node`](crate::node::Node)
91///
92/// Stored in a [`TaffyTree`].
93#[derive(Debug, Clone, PartialEq)]
94struct NodeData {
95 /// The layout strategy used by this node
96 pub(crate) style: Style,
97
98 /// The always unrounded results of the layout computation. We must store this separately from the rounded
99 /// layout to avoid errors from rounding already-rounded values. See <https://github.com/DioxusLabs/taffy/issues/501>.
100 pub(crate) unrounded_layout: Layout,
101
102 /// The final results of the layout computation.
103 /// These may be rounded or unrounded depending on what the `use_rounding` config setting is set to.
104 pub(crate) final_layout: Layout,
105
106 /// Whether the node has context data associated with it or not
107 pub(crate) has_context: bool,
108
109 /// The cached results of the layout computation
110 pub(crate) cache: Cache,
111
112 /// The computation result from layout algorithm
113 #[cfg(feature = "detailed_layout_info")]
114 pub(crate) detailed_layout_info: DetailedLayoutInfo,
115}
116
117impl NodeData {
118 /// Create the data for a new node
119 #[must_use]
120 pub const fn new(style: Style) -> Self {
121 Self {
122 style,
123 cache: Cache::new(),
124 unrounded_layout: Layout::new(),
125 final_layout: Layout::new(),
126 has_context: false,
127 #[cfg(feature = "detailed_layout_info")]
128 detailed_layout_info: DetailedLayoutInfo::None,
129 }
130 }
131
132 /// Marks a node and all of its ancestors as requiring relayout
133 ///
134 /// This clears any cached data and signals that the data must be recomputed.
135 /// If the node was already marked as dirty, returns true
136 #[inline]
137 pub fn mark_dirty(&mut self) -> ClearState {
138 self.cache.clear()
139 }
140}
141
142/// An entire tree of UI nodes. The entry point to Taffy's high-level API.
143///
144/// Allows you to build a tree of UI nodes, run Taffy's layout algorithms over that tree, and then access the resultant layout.]
145#[derive(Debug, Clone)]
146pub struct TaffyTree<NodeContext = ()> {
147 /// The [`NodeData`] for each node stored in this tree
148 nodes: SlotMap<DefaultKey, NodeData>,
149
150 /// Functions/closures that compute the intrinsic size of leaf nodes
151 node_context_data: SecondaryMap<DefaultKey, NodeContext>,
152
153 /// The children of each node
154 ///
155 /// The indexes in the outer vector correspond to the position of the parent [`NodeData`]
156 children: SlotMap<DefaultKey, ChildrenVec<NodeId>>,
157
158 /// The parents of each node
159 ///
160 /// The indexes in the outer vector correspond to the position of the child [`NodeData`]
161 parents: SlotMap<DefaultKey, Option<NodeId>>,
162
163 /// Layout mode configuration
164 config: TaffyConfig,
165}
166
167impl Default for TaffyTree {
168 fn default() -> TaffyTree<()> {
169 TaffyTree::new()
170 }
171}
172
173/// Iterator that wraps a slice of nodes, lazily converting them to u64
174pub struct TaffyTreeChildIter<'a>(core::slice::Iter<'a, NodeId>);
175impl Iterator for TaffyTreeChildIter<'_> {
176 type Item = NodeId;
177
178 #[inline]
179 fn next(&mut self) -> Option<Self::Item> {
180 self.0.next().copied()
181 }
182}
183
184// TraversePartialTree impl for TaffyTree
185impl<NodeContext> TraversePartialTree for TaffyTree<NodeContext> {
186 type ChildIter<'a>
187 = TaffyTreeChildIter<'a>
188 where
189 Self: 'a;
190
191 #[inline(always)]
192 fn child_ids(&self, parent_node_id: NodeId) -> Self::ChildIter<'_> {
193 TaffyTreeChildIter(self.children[parent_node_id.into()].iter())
194 }
195
196 #[inline(always)]
197 fn child_count(&self, parent_node_id: NodeId) -> usize {
198 self.children[parent_node_id.into()].len()
199 }
200
201 #[inline(always)]
202 fn get_child_id(&self, parent_node_id: NodeId, id: usize) -> NodeId {
203 self.children[parent_node_id.into()][id]
204 }
205}
206
207// TraverseTree impl for TaffyTree
208impl<NodeContext> TraverseTree for TaffyTree<NodeContext> {}
209
210// CacheTree impl for TaffyTree
211impl<NodeContext> CacheTree for TaffyTree<NodeContext> {
212 fn cache_get(
213 &self,
214 node_id: NodeId,
215 known_dimensions: Size<Option<f32>>,
216 available_space: Size<AvailableSpace>,
217 run_mode: RunMode,
218 ) -> Option<LayoutOutput> {
219 self.nodes[node_id.into()].cache.get(known_dimensions, available_space, run_mode)
220 }
221
222 fn cache_store(
223 &mut self,
224 node_id: NodeId,
225 known_dimensions: Size<Option<f32>>,
226 available_space: Size<AvailableSpace>,
227 run_mode: RunMode,
228 layout_output: LayoutOutput,
229 ) {
230 self.nodes[node_id.into()].cache.store(known_dimensions, available_space, run_mode, layout_output)
231 }
232
233 fn cache_clear(&mut self, node_id: NodeId) {
234 self.nodes[node_id.into()].cache.clear();
235 }
236}
237
238// PrintTree impl for TaffyTree
239impl<NodeContext> PrintTree for TaffyTree<NodeContext> {
240 #[inline(always)]
241 fn get_debug_label(&self, node_id: NodeId) -> &'static str {
242 let node = &self.nodes[node_id.into()];
243 let display = node.style.display;
244 let num_children = self.child_count(node_id);
245
246 match (num_children, display) {
247 (_, Display::None) => "NONE",
248 (0, _) => "LEAF",
249 #[cfg(feature = "block_layout")]
250 (_, Display::Block) => "BLOCK",
251 #[cfg(feature = "flexbox")]
252 (_, Display::Flex) => {
253 use crate::FlexDirection;
254 match node.style.flex_direction {
255 FlexDirection::Row | FlexDirection::RowReverse => "FLEX ROW",
256 FlexDirection::Column | FlexDirection::ColumnReverse => "FLEX COL",
257 }
258 }
259 #[cfg(feature = "grid")]
260 (_, Display::Grid) => "GRID",
261 }
262 }
263
264 #[inline(always)]
265 fn get_final_layout(&self, node_id: NodeId) -> Layout {
266 if self.config.use_rounding {
267 self.nodes[node_id.into()].final_layout
268 } else {
269 self.nodes[node_id.into()].unrounded_layout
270 }
271 }
272}
273
274/// View over the Taffy tree that holds the tree itself along with a reference to the context
275/// and implements LayoutTree. This allows the context to be stored outside of the TaffyTree struct
276/// which makes the lifetimes of the context much more flexible.
277pub(crate) struct TaffyView<'t, NodeContext, MeasureFunction>
278where
279 MeasureFunction:
280 FnMut(Size<Option<f32>>, Size<AvailableSpace>, NodeId, Option<&mut NodeContext>, &Style) -> Size<f32>,
281{
282 /// A reference to the TaffyTree
283 pub(crate) taffy: &'t mut TaffyTree<NodeContext>,
284 /// The context provided for passing to measure functions if layout is run over this struct
285 pub(crate) measure_function: MeasureFunction,
286}
287
288impl<NodeContext, MeasureFunction> TaffyView<'_, NodeContext, MeasureFunction>
289where
290 MeasureFunction:
291 FnMut(Size<Option<f32>>, Size<AvailableSpace>, NodeId, Option<&mut NodeContext>, &Style) -> Size<f32>,
292{
293 #[inline(always)]
294 fn compute_child_layout(
295 &mut self,
296 node_id: NodeId,
297 inputs: LayoutInput,
298 #[cfg(feature = "block_layout")] bfc: Option<&mut BlockFormattingContext>,
299 ) -> LayoutOutput {
300 // If RunMode is PerformHiddenLayout then this indicates that an ancestor node is `Display::None`
301 // and thus that we should lay out this node using hidden layout regardless of it's own display style.
302 if inputs.run_mode == RunMode::PerformHiddenLayout {
303 debug_log!("HIDDEN");
304 return compute_hidden_layout(self, node_id);
305 }
306
307 // We run the following wrapped in "compute_cached_layout", which will check the cache for an entry matching the node and inputs and:
308 // - Return that entry if exists
309 // - Else call the passed closure (below) to compute the result
310 //
311 // If there was no cache match and a new result needs to be computed then that result will be added to the cache
312 compute_cached_layout(self, node_id, inputs, |tree, node_id, inputs| {
313 let display_mode = tree.taffy.nodes[node_id.into()].style.display;
314 let has_children = tree.child_count(node_id) > 0;
315
316 debug_log!(display_mode);
317 debug_log_node!(
318 inputs.known_dimensions,
319 inputs.parent_size,
320 inputs.available_space,
321 inputs.run_mode,
322 inputs.sizing_mode
323 );
324
325 // Dispatch to a layout algorithm based on the node's display style and whether the node has children or not.
326 match (display_mode, has_children) {
327 (Display::None, _) => compute_hidden_layout(tree, node_id),
328 #[cfg(feature = "block_layout")]
329 (Display::Block, true) => compute_block_layout(tree, node_id, inputs, bfc),
330 #[cfg(feature = "flexbox")]
331 (Display::Flex, true) => compute_flexbox_layout(tree, node_id, inputs),
332 #[cfg(feature = "grid")]
333 (Display::Grid, true) => compute_grid_layout(tree, node_id, inputs),
334 (_, false) => {
335 let node_key = node_id.into();
336 let style = &tree.taffy.nodes[node_key].style;
337 let has_context = tree.taffy.nodes[node_key].has_context;
338 let node_context = has_context.then(|| tree.taffy.node_context_data.get_mut(node_key)).flatten();
339 let measure_function = |known_dimensions, available_space| {
340 (tree.measure_function)(known_dimensions, available_space, node_id, node_context, style)
341 };
342 compute_leaf_layout(inputs, style, |_, _| 0.0, measure_function)
343 }
344 }
345 })
346 }
347}
348
349// TraversePartialTree impl for TaffyView
350impl<NodeContext, MeasureFunction> TraversePartialTree for TaffyView<'_, NodeContext, MeasureFunction>
351where
352 MeasureFunction:
353 FnMut(Size<Option<f32>>, Size<AvailableSpace>, NodeId, Option<&mut NodeContext>, &Style) -> Size<f32>,
354{
355 type ChildIter<'a>
356 = TaffyTreeChildIter<'a>
357 where
358 Self: 'a;
359
360 #[inline(always)]
361 fn child_ids(&self, parent_node_id: NodeId) -> Self::ChildIter<'_> {
362 self.taffy.child_ids(parent_node_id)
363 }
364
365 #[inline(always)]
366 fn child_count(&self, parent_node_id: NodeId) -> usize {
367 self.taffy.child_count(parent_node_id)
368 }
369
370 #[inline(always)]
371 fn get_child_id(&self, parent_node_id: NodeId, child_index: usize) -> NodeId {
372 self.taffy.get_child_id(parent_node_id, child_index)
373 }
374}
375
376// TraverseTree impl for TaffyView
377impl<NodeContext, MeasureFunction> TraverseTree for TaffyView<'_, NodeContext, MeasureFunction> where
378 MeasureFunction:
379 FnMut(Size<Option<f32>>, Size<AvailableSpace>, NodeId, Option<&mut NodeContext>, &Style) -> Size<f32>
380{
381}
382
383// LayoutPartialTree impl for TaffyView
384impl<NodeContext, MeasureFunction> LayoutPartialTree for TaffyView<'_, NodeContext, MeasureFunction>
385where
386 MeasureFunction:
387 FnMut(Size<Option<f32>>, Size<AvailableSpace>, NodeId, Option<&mut NodeContext>, &Style) -> Size<f32>,
388{
389 type CoreContainerStyle<'a>
390 = &'a Style
391 where
392 Self: 'a;
393
394 type CustomIdent = DefaultCheapStr;
395
396 #[inline(always)]
397 fn get_core_container_style(&self, node_id: NodeId) -> Self::CoreContainerStyle<'_> {
398 &self.taffy.nodes[node_id.into()].style
399 }
400
401 #[inline(always)]
402 fn set_unrounded_layout(&mut self, node_id: NodeId, layout: &Layout) {
403 self.taffy.nodes[node_id.into()].unrounded_layout = *layout;
404 }
405
406 #[inline(always)]
407 fn resolve_calc_value(&self, _val: *const (), _basis: f32) -> f32 {
408 0.0
409 }
410
411 #[inline(always)]
412 fn compute_child_layout(&mut self, node_id: NodeId, inputs: LayoutInput) -> LayoutOutput {
413 self.compute_child_layout(node_id, inputs, None)
414 }
415}
416
417impl<NodeContext, MeasureFunction> CacheTree for TaffyView<'_, NodeContext, MeasureFunction>
418where
419 MeasureFunction:
420 FnMut(Size<Option<f32>>, Size<AvailableSpace>, NodeId, Option<&mut NodeContext>, &Style) -> Size<f32>,
421{
422 fn cache_get(
423 &self,
424 node_id: NodeId,
425 known_dimensions: Size<Option<f32>>,
426 available_space: Size<AvailableSpace>,
427 run_mode: RunMode,
428 ) -> Option<LayoutOutput> {
429 self.taffy.nodes[node_id.into()].cache.get(known_dimensions, available_space, run_mode)
430 }
431
432 fn cache_store(
433 &mut self,
434 node_id: NodeId,
435 known_dimensions: Size<Option<f32>>,
436 available_space: Size<AvailableSpace>,
437 run_mode: RunMode,
438 layout_output: LayoutOutput,
439 ) {
440 self.taffy.nodes[node_id.into()].cache.store(known_dimensions, available_space, run_mode, layout_output)
441 }
442
443 fn cache_clear(&mut self, node_id: NodeId) {
444 self.taffy.nodes[node_id.into()].cache.clear();
445 }
446}
447
448#[cfg(feature = "block_layout")]
449impl<NodeContext, MeasureFunction> LayoutBlockContainer for TaffyView<'_, NodeContext, MeasureFunction>
450where
451 MeasureFunction:
452 FnMut(Size<Option<f32>>, Size<AvailableSpace>, NodeId, Option<&mut NodeContext>, &Style) -> Size<f32>,
453{
454 type BlockContainerStyle<'a>
455 = &'a Style
456 where
457 Self: 'a;
458 type BlockItemStyle<'a>
459 = &'a Style
460 where
461 Self: 'a;
462
463 #[inline(always)]
464 fn get_block_container_style(&self, node_id: NodeId) -> Self::BlockContainerStyle<'_> {
465 self.get_core_container_style(node_id)
466 }
467
468 #[inline(always)]
469 fn get_block_child_style(&self, child_node_id: NodeId) -> Self::BlockItemStyle<'_> {
470 self.get_core_container_style(child_node_id)
471 }
472
473 #[inline(always)]
474 fn compute_block_child_layout(
475 &mut self,
476 node_id: NodeId,
477 inputs: LayoutInput,
478 bfc: Option<&mut BlockFormattingContext>,
479 ) -> LayoutOutput {
480 self.compute_child_layout(node_id, inputs, bfc)
481 }
482}
483
484#[cfg(feature = "flexbox")]
485impl<NodeContext, MeasureFunction> LayoutFlexboxContainer for TaffyView<'_, NodeContext, MeasureFunction>
486where
487 MeasureFunction:
488 FnMut(Size<Option<f32>>, Size<AvailableSpace>, NodeId, Option<&mut NodeContext>, &Style) -> Size<f32>,
489{
490 type FlexboxContainerStyle<'a>
491 = &'a Style
492 where
493 Self: 'a;
494 type FlexboxItemStyle<'a>
495 = &'a Style
496 where
497 Self: 'a;
498
499 #[inline(always)]
500 fn get_flexbox_container_style(&self, node_id: NodeId) -> Self::FlexboxContainerStyle<'_> {
501 &self.taffy.nodes[node_id.into()].style
502 }
503
504 #[inline(always)]
505 fn get_flexbox_child_style(&self, child_node_id: NodeId) -> Self::FlexboxItemStyle<'_> {
506 &self.taffy.nodes[child_node_id.into()].style
507 }
508}
509
510#[cfg(feature = "grid")]
511impl<NodeContext, MeasureFunction> LayoutGridContainer for TaffyView<'_, NodeContext, MeasureFunction>
512where
513 MeasureFunction:
514 FnMut(Size<Option<f32>>, Size<AvailableSpace>, NodeId, Option<&mut NodeContext>, &Style) -> Size<f32>,
515{
516 type GridContainerStyle<'a>
517 = &'a Style
518 where
519 Self: 'a;
520 type GridItemStyle<'a>
521 = &'a Style
522 where
523 Self: 'a;
524
525 #[inline(always)]
526 fn get_grid_container_style(&self, node_id: NodeId) -> Self::GridContainerStyle<'_> {
527 &self.taffy.nodes[node_id.into()].style
528 }
529
530 #[inline(always)]
531 fn get_grid_child_style(&self, child_node_id: NodeId) -> Self::GridItemStyle<'_> {
532 &self.taffy.nodes[child_node_id.into()].style
533 }
534
535 #[inline(always)]
536 #[cfg(feature = "detailed_layout_info")]
537 fn set_detailed_grid_info(&mut self, node_id: NodeId, detailed_grid_info: DetailedGridInfo) {
538 self.taffy.nodes[node_id.into()].detailed_layout_info = DetailedLayoutInfo::Grid(Box::new(detailed_grid_info));
539 }
540}
541
542// RoundTree impl for TaffyView
543impl<NodeContext, MeasureFunction> RoundTree for TaffyView<'_, NodeContext, MeasureFunction>
544where
545 MeasureFunction:
546 FnMut(Size<Option<f32>>, Size<AvailableSpace>, NodeId, Option<&mut NodeContext>, &Style) -> Size<f32>,
547{
548 #[inline(always)]
549 fn get_unrounded_layout(&self, node: NodeId) -> Layout {
550 self.taffy.nodes[node.into()].unrounded_layout
551 }
552
553 #[inline(always)]
554 fn set_final_layout(&mut self, node_id: NodeId, layout: &Layout) {
555 self.taffy.nodes[node_id.into()].final_layout = *layout;
556 }
557}
558
559#[allow(clippy::iter_cloned_collect)] // due to no-std support, we need to use `iter_cloned` instead of `collect`
560impl<NodeContext> TaffyTree<NodeContext> {
561 /// Creates a new [`TaffyTree`]
562 ///
563 /// The default capacity of a [`TaffyTree`] is 16 nodes.
564 #[must_use]
565 pub fn new() -> Self {
566 Self::with_capacity(16)
567 }
568
569 /// Creates a new [`TaffyTree`] that can store `capacity` nodes before reallocation
570 #[must_use]
571 pub fn with_capacity(capacity: usize) -> Self {
572 TaffyTree {
573 // TODO: make this method const upstream,
574 // so constructors here can be const
575 nodes: SlotMap::with_capacity(capacity),
576 children: SlotMap::with_capacity(capacity),
577 parents: SlotMap::with_capacity(capacity),
578 node_context_data: SecondaryMap::with_capacity(capacity),
579 config: TaffyConfig::default(),
580 }
581 }
582
583 /// Enable rounding of layout values. Rounding is enabled by default.
584 pub fn enable_rounding(&mut self) {
585 self.config.use_rounding = true;
586 }
587
588 /// Disable rounding of layout values. Rounding is enabled by default.
589 pub fn disable_rounding(&mut self) {
590 self.config.use_rounding = false;
591 }
592
593 /// Creates and adds a new unattached leaf node to the tree, and returns the node of the new node
594 pub fn new_leaf(&mut self, layout: Style) -> TaffyResult<NodeId> {
595 let id = self.nodes.insert(NodeData::new(layout));
596 let _ = self.children.insert(new_vec_with_capacity(0));
597 let _ = self.parents.insert(None);
598
599 Ok(id.into())
600 }
601
602 /// Creates and adds a new unattached leaf node to the tree, and returns the [`NodeId`] of the new node
603 ///
604 /// Creates and adds a new leaf node with a supplied context
605 pub fn new_leaf_with_context(&mut self, layout: Style, context: NodeContext) -> TaffyResult<NodeId> {
606 let mut data = NodeData::new(layout);
607 data.has_context = true;
608
609 let id = self.nodes.insert(data);
610 self.node_context_data.insert(id, context);
611
612 let _ = self.children.insert(new_vec_with_capacity(0));
613 let _ = self.parents.insert(None);
614
615 Ok(id.into())
616 }
617
618 /// Creates and adds a new node, which may have any number of `children`
619 pub fn new_with_children(&mut self, layout: Style, children: &[NodeId]) -> TaffyResult<NodeId> {
620 let id = NodeId::from(self.nodes.insert(NodeData::new(layout)));
621
622 for child in children {
623 self.parents[(*child).into()] = Some(id);
624 }
625
626 let _ = self.children.insert(children.iter().copied().collect::<_>());
627 let _ = self.parents.insert(None);
628
629 Ok(id)
630 }
631
632 /// Drops all nodes in the tree
633 pub fn clear(&mut self) {
634 self.nodes.clear();
635 self.children.clear();
636 self.parents.clear();
637 }
638
639 /// Remove a specific node from the tree and drop it
640 ///
641 /// Returns the id of the node removed.
642 pub fn remove(&mut self, node: NodeId) -> TaffyResult<NodeId> {
643 let key = node.into();
644 if let Some(parent) = self.parents[key] {
645 if let Some(children) = self.children.get_mut(parent.into()) {
646 children.retain(|f| *f != node);
647 }
648 }
649
650 // Remove "parent" references to a node when removing that node
651 if let Some(children) = self.children.get(key) {
652 for child in children.iter().copied() {
653 self.parents[child.into()] = None;
654 }
655 }
656
657 let _ = self.children.remove(key);
658 let _ = self.parents.remove(key);
659 let _ = self.nodes.remove(key);
660
661 Ok(node)
662 }
663
664 /// Sets the context data associated with the node
665 #[inline]
666 pub fn set_node_context(&mut self, node: NodeId, measure: Option<NodeContext>) -> TaffyResult<()> {
667 let key = node.into();
668 if let Some(measure) = measure {
669 self.nodes[key].has_context = true;
670 self.node_context_data.insert(key, measure);
671 } else {
672 self.nodes[key].has_context = false;
673 self.node_context_data.remove(key);
674 }
675
676 self.mark_dirty(node)?;
677
678 Ok(())
679 }
680
681 /// Gets a reference to the the context data associated with the node
682 #[inline]
683 pub fn get_node_context(&self, node: NodeId) -> Option<&NodeContext> {
684 self.node_context_data.get(node.into())
685 }
686
687 /// Gets a mutable reference to the the context data associated with the node
688 #[inline]
689 pub fn get_node_context_mut(&mut self, node: NodeId) -> Option<&mut NodeContext> {
690 self.node_context_data.get_mut(node.into())
691 }
692
693 /// Gets mutable references to the the context data associated with the nodes. All keys must be valid and disjoint, otherwise None is returned.
694 pub fn get_disjoint_node_context_mut<const N: usize>(
695 &mut self,
696 keys: [NodeId; N],
697 ) -> Option<[&mut NodeContext; N]> {
698 self.node_context_data.get_disjoint_mut(keys.map(|k| k.into()))
699 }
700
701 /// Adds a `child` node under the supplied `parent`
702 pub fn add_child(&mut self, parent: NodeId, child: NodeId) -> TaffyResult<()> {
703 let parent_key = parent.into();
704 let child_key = child.into();
705 self.parents[child_key] = Some(parent);
706 self.children[parent_key].push(child);
707 self.mark_dirty(parent)?;
708
709 Ok(())
710 }
711
712 /// Inserts a `child` node at the given `child_index` under the supplied `parent`, shifting all children after it to the right.
713 pub fn insert_child_at_index(&mut self, parent: NodeId, child_index: usize, child: NodeId) -> TaffyResult<()> {
714 let parent_key = parent.into();
715
716 let child_count = self.children[parent_key].len();
717 if child_index > child_count {
718 return Err(TaffyError::ChildIndexOutOfBounds { parent, child_index, child_count });
719 }
720
721 self.parents[child.into()] = Some(parent);
722 self.children[parent_key].insert(child_index, child);
723 self.mark_dirty(parent)?;
724
725 Ok(())
726 }
727
728 /// Directly sets the `children` of the supplied `parent`
729 pub fn set_children(&mut self, parent: NodeId, children: &[NodeId]) -> TaffyResult<()> {
730 let parent_key = parent.into();
731
732 // Remove node as parent from all its current children.
733 for child in &self.children[parent_key] {
734 self.parents[(*child).into()] = None;
735 }
736
737 // Build up relation node <-> child
738 for &child in children {
739 // Remove child from previous parent
740 if let Some(previous_parent) = self.parents[child.into()] {
741 self.remove_child(previous_parent, child).unwrap();
742 }
743 self.parents[child.into()] = Some(parent);
744 }
745
746 let parent_children = &mut self.children[parent_key];
747 parent_children.clear();
748 children.iter().for_each(|child| parent_children.push(*child));
749
750 self.mark_dirty(parent)?;
751
752 Ok(())
753 }
754
755 /// Removes the `child` of the parent `node`
756 ///
757 /// The child is not removed from the tree entirely, it is simply no longer attached to its previous parent.
758 pub fn remove_child(&mut self, parent: NodeId, child: NodeId) -> TaffyResult<NodeId> {
759 let index = self.children[parent.into()].iter().position(|n| *n == child).unwrap();
760 self.remove_child_at_index(parent, index)
761 }
762
763 /// Removes the child at the given `index` from the `parent`
764 ///
765 /// The child is not removed from the tree entirely, it is simply no longer attached to its previous parent.
766 pub fn remove_child_at_index(&mut self, parent: NodeId, child_index: usize) -> TaffyResult<NodeId> {
767 let parent_key = parent.into();
768 let child_count = self.children[parent_key].len();
769 if child_index >= child_count {
770 return Err(TaffyError::ChildIndexOutOfBounds { parent, child_index, child_count });
771 }
772
773 let child = self.children[parent_key].remove(child_index);
774 self.parents[child.into()] = None;
775
776 self.mark_dirty(parent)?;
777
778 Ok(child)
779 }
780
781 /// Removes children at the given range from the `parent`
782 ///
783 /// Children are not removed from the tree entirely, they are simply no longer attached to their previous parent.
784 ///
785 /// Function will panic if given range is invalid. See [`core::slice::range`]
786 pub fn remove_children_range<R>(&mut self, parent: NodeId, range: R) -> TaffyResult<()>
787 where
788 R: core::ops::RangeBounds<usize>,
789 {
790 let parent_key = parent.into();
791 for child in self.children[parent_key].drain(range) {
792 self.parents[child.into()] = None;
793 }
794
795 self.mark_dirty(parent)?;
796 Ok(())
797 }
798
799 /// Replaces the child at the given `child_index` from the `parent` node with the new `child` node
800 ///
801 /// The child is not removed from the tree entirely, it is simply no longer attached to its previous parent.
802 pub fn replace_child_at_index(
803 &mut self,
804 parent: NodeId,
805 child_index: usize,
806 new_child: NodeId,
807 ) -> TaffyResult<NodeId> {
808 let parent_key = parent.into();
809
810 let child_count = self.children[parent_key].len();
811 if child_index >= child_count {
812 return Err(TaffyError::ChildIndexOutOfBounds { parent, child_index, child_count });
813 }
814
815 self.parents[new_child.into()] = Some(parent);
816 let old_child = core::mem::replace(&mut self.children[parent_key][child_index], new_child);
817 self.parents[old_child.into()] = None;
818
819 self.mark_dirty(parent)?;
820
821 Ok(old_child)
822 }
823
824 /// Returns the child node of the parent `node` at the provided `child_index`
825 #[inline]
826 pub fn child_at_index(&self, parent: NodeId, child_index: usize) -> TaffyResult<NodeId> {
827 let parent_key = parent.into();
828 let child_count = self.children[parent_key].len();
829 if child_index >= child_count {
830 return Err(TaffyError::ChildIndexOutOfBounds { parent, child_index, child_count });
831 }
832
833 Ok(self.children[parent_key][child_index])
834 }
835
836 /// Returns the total number of nodes in the tree
837 #[inline]
838 pub fn total_node_count(&self) -> usize {
839 self.nodes.len()
840 }
841
842 /// Returns the `NodeId` of the parent node of the specified node (if it exists)
843 ///
844 /// - Return None if the specified node has no parent
845 /// - Panics if the specified node does not exist
846 #[inline]
847 pub fn parent(&self, child_id: NodeId) -> Option<NodeId> {
848 self.parents[child_id.into()]
849 }
850
851 /// Returns a list of children that belong to the parent node
852 pub fn children(&self, parent: NodeId) -> TaffyResult<Vec<NodeId>> {
853 Ok(self.children[parent.into()].clone())
854 }
855
856 /// Sets the [`Style`] of the provided `node`
857 #[inline]
858 pub fn set_style(&mut self, node: NodeId, style: Style) -> TaffyResult<()> {
859 self.nodes[node.into()].style = style;
860 self.mark_dirty(node)?;
861 Ok(())
862 }
863
864 /// Gets the [`Style`] of the provided `node`
865 #[inline]
866 pub fn style(&self, node: NodeId) -> TaffyResult<&Style> {
867 Ok(&self.nodes[node.into()].style)
868 }
869
870 /// Return this node layout relative to its parent
871 #[inline]
872 pub fn layout(&self, node: NodeId) -> TaffyResult<&Layout> {
873 if self.config.use_rounding {
874 Ok(&self.nodes[node.into()].final_layout)
875 } else {
876 Ok(&self.nodes[node.into()].unrounded_layout)
877 }
878 }
879
880 /// Returns this node layout with unrounded values relative to its parent.
881 #[inline]
882 pub fn unrounded_layout(&self, node: NodeId) -> &Layout {
883 &self.nodes[node.into()].unrounded_layout
884 }
885
886 /// Get the "detailed layout info" for a node.
887 ///
888 /// Currently this is only implemented for CSS Grid containers where it contains
889 /// the computed size of each grid track and the computed placement of each grid item
890 #[cfg(feature = "detailed_layout_info")]
891 #[inline]
892 pub fn detailed_layout_info(&self, node_id: NodeId) -> &DetailedLayoutInfo {
893 &self.nodes[node_id.into()].detailed_layout_info
894 }
895
896 /// Marks the layout of this node and its ancestors as outdated
897 pub fn mark_dirty(&mut self, node: NodeId) -> TaffyResult<()> {
898 fn mark_dirty_recursive(
899 nodes: &mut SlotMap<DefaultKey, NodeData>,
900 parents: &SlotMap<DefaultKey, Option<NodeId>>,
901 node_key: DefaultKey,
902 ) {
903 match nodes[node_key].mark_dirty() {
904 ClearState::AlreadyEmpty => {
905 // Node was already marked as dirty.
906 // No need to visit ancestors
907 // as they should be marked as dirty already.
908 }
909 ClearState::Cleared => {
910 if let Some(Some(node)) = parents.get(node_key) {
911 mark_dirty_recursive(nodes, parents, (*node).into());
912 }
913 }
914 }
915 }
916
917 mark_dirty_recursive(&mut self.nodes, &self.parents, node.into());
918
919 Ok(())
920 }
921
922 /// Indicates whether the layout of this node needs to be recomputed
923 #[inline]
924 pub fn dirty(&self, node: NodeId) -> TaffyResult<bool> {
925 Ok(self.nodes[node.into()].cache.is_empty())
926 }
927
928 /// Updates the stored layout of the provided `node` and its children
929 pub fn compute_layout_with_measure<MeasureFunction>(
930 &mut self,
931 node_id: NodeId,
932 available_space: Size<AvailableSpace>,
933 measure_function: MeasureFunction,
934 ) -> Result<(), TaffyError>
935 where
936 MeasureFunction:
937 FnMut(Size<Option<f32>>, Size<AvailableSpace>, NodeId, Option<&mut NodeContext>, &Style) -> Size<f32>,
938 {
939 let use_rounding = self.config.use_rounding;
940 let mut taffy_view = TaffyView { taffy: self, measure_function };
941 compute_root_layout(&mut taffy_view, node_id, available_space);
942 if use_rounding {
943 round_layout(&mut taffy_view, node_id);
944 }
945 Ok(())
946 }
947
948 /// Updates the stored layout of the provided `node` and its children
949 pub fn compute_layout(&mut self, node: NodeId, available_space: Size<AvailableSpace>) -> Result<(), TaffyError> {
950 self.compute_layout_with_measure(node, available_space, |_, _, _, _, _| Size::ZERO)
951 }
952
953 /// Prints a debug representation of the tree's layout
954 #[cfg(feature = "std")]
955 pub fn print_tree(&mut self, root: NodeId) {
956 crate::util::print_tree(self, root)
957 }
958
959 /// Returns an instance of LayoutTree representing the TaffyTree
960 #[cfg(test)]
961 pub(crate) fn as_layout_tree(&mut self) -> impl LayoutPartialTree + CacheTree + '_ {
962 TaffyView { taffy: self, measure_function: |_, _, _, _, _| Size::ZERO }
963 }
964}
965
966#[cfg(test)]
967mod tests {
968
969 use super::*;
970 use crate::style::{Dimension, Display, FlexDirection};
971 use crate::style_helpers::*;
972 use crate::util::sys;
973
974 fn size_measure_function(
975 known_dimensions: Size<Option<f32>>,
976 _available_space: Size<AvailableSpace>,
977 _node_id: NodeId,
978 node_context: Option<&mut Size<f32>>,
979 _style: &Style,
980 ) -> Size<f32> {
981 known_dimensions.unwrap_or(node_context.cloned().unwrap_or(Size::ZERO))
982 }
983
984 #[test]
985 fn new_should_allocate_default_capacity() {
986 const DEFAULT_CAPACITY: usize = 16; // This is the capacity defined in the `impl Default`
987 let taffy: TaffyTree<()> = TaffyTree::new();
988
989 assert!(taffy.children.capacity() >= DEFAULT_CAPACITY);
990 assert!(taffy.parents.capacity() >= DEFAULT_CAPACITY);
991 assert!(taffy.nodes.capacity() >= DEFAULT_CAPACITY);
992 }
993
994 #[test]
995 fn test_with_capacity() {
996 const CAPACITY: usize = 8;
997 let taffy: TaffyTree<()> = TaffyTree::with_capacity(CAPACITY);
998
999 assert!(taffy.children.capacity() >= CAPACITY);
1000 assert!(taffy.parents.capacity() >= CAPACITY);
1001 assert!(taffy.nodes.capacity() >= CAPACITY);
1002 }
1003
1004 #[test]
1005 fn test_new_leaf() {
1006 let mut taffy: TaffyTree<()> = TaffyTree::new();
1007
1008 let res = taffy.new_leaf(Style::default());
1009 assert!(res.is_ok());
1010 let node = res.unwrap();
1011
1012 // node should be in the taffy tree and have no children
1013 assert!(taffy.child_count(node) == 0);
1014 }
1015
1016 #[test]
1017 fn new_leaf_with_context() {
1018 let mut taffy: TaffyTree<Size<f32>> = TaffyTree::new();
1019
1020 let res = taffy.new_leaf_with_context(Style::default(), Size::ZERO);
1021 assert!(res.is_ok());
1022 let node = res.unwrap();
1023
1024 // node should be in the taffy tree and have no children
1025 assert!(taffy.child_count(node) == 0);
1026 }
1027
1028 /// Test that new_with_children works as expected
1029 #[test]
1030 fn test_new_with_children() {
1031 let mut taffy: TaffyTree<()> = TaffyTree::new();
1032 let child0 = taffy.new_leaf(Style::default()).unwrap();
1033 let child1 = taffy.new_leaf(Style::default()).unwrap();
1034 let node = taffy.new_with_children(Style::default(), &[child0, child1]).unwrap();
1035
1036 // node should have two children
1037 assert_eq!(taffy.child_count(node), 2);
1038 assert_eq!(taffy.children(node).unwrap()[0], child0);
1039 assert_eq!(taffy.children(node).unwrap()[1], child1);
1040 }
1041
1042 #[test]
1043 fn remove_node_should_remove() {
1044 let mut taffy: TaffyTree<()> = TaffyTree::new();
1045
1046 let node = taffy.new_leaf(Style::default()).unwrap();
1047
1048 let _ = taffy.remove(node).unwrap();
1049 }
1050
1051 #[test]
1052 fn remove_node_should_detach_hierarchy() {
1053 let mut taffy: TaffyTree<()> = TaffyTree::new();
1054
1055 // Build a linear tree layout: <0> <- <1> <- <2>
1056 let node2 = taffy.new_leaf(Style::default()).unwrap();
1057 let node1 = taffy.new_with_children(Style::default(), &[node2]).unwrap();
1058 let node0 = taffy.new_with_children(Style::default(), &[node1]).unwrap();
1059
1060 // Both node0 and node1 should have 1 child nodes
1061 assert_eq!(taffy.children(node0).unwrap().as_slice(), &[node1]);
1062 assert_eq!(taffy.children(node1).unwrap().as_slice(), &[node2]);
1063
1064 // Disconnect the tree: <0> <2>
1065 let _ = taffy.remove(node1).unwrap();
1066
1067 // Both remaining nodes should have no child nodes
1068 assert!(taffy.children(node0).unwrap().is_empty());
1069 assert!(taffy.children(node2).unwrap().is_empty());
1070 }
1071
1072 #[test]
1073 fn remove_last_node() {
1074 let mut taffy: TaffyTree<()> = TaffyTree::new();
1075
1076 let parent = taffy.new_leaf(Style::default()).unwrap();
1077 let child = taffy.new_leaf(Style::default()).unwrap();
1078 taffy.add_child(parent, child).unwrap();
1079
1080 taffy.remove(child).unwrap();
1081 taffy.remove(parent).unwrap();
1082 }
1083
1084 #[test]
1085 fn set_measure() {
1086 let mut taffy: TaffyTree<Size<f32>> = TaffyTree::new();
1087 let node = taffy.new_leaf_with_context(Style::default(), Size { width: 200.0, height: 200.0 }).unwrap();
1088 taffy.compute_layout_with_measure(node, Size::MAX_CONTENT, size_measure_function).unwrap();
1089 assert_eq!(taffy.layout(node).unwrap().size.width, 200.0);
1090
1091 taffy.set_node_context(node, Some(Size { width: 100.0, height: 100.0 })).unwrap();
1092 taffy.compute_layout_with_measure(node, Size::MAX_CONTENT, size_measure_function).unwrap();
1093 assert_eq!(taffy.layout(node).unwrap().size.width, 100.0);
1094 }
1095
1096 #[test]
1097 fn set_measure_of_previously_unmeasured_node() {
1098 let mut taffy: TaffyTree<Size<f32>> = TaffyTree::new();
1099 let node = taffy.new_leaf(Style::default()).unwrap();
1100 taffy.compute_layout_with_measure(node, Size::MAX_CONTENT, size_measure_function).unwrap();
1101 assert_eq!(taffy.layout(node).unwrap().size.width, 0.0);
1102
1103 taffy.set_node_context(node, Some(Size { width: 100.0, height: 100.0 })).unwrap();
1104 taffy.compute_layout_with_measure(node, Size::MAX_CONTENT, size_measure_function).unwrap();
1105 assert_eq!(taffy.layout(node).unwrap().size.width, 100.0);
1106 }
1107
1108 /// Test that adding `add_child()` works
1109 #[test]
1110 fn add_child() {
1111 let mut taffy: TaffyTree<()> = TaffyTree::new();
1112 let node = taffy.new_leaf(Style::default()).unwrap();
1113 assert_eq!(taffy.child_count(node), 0);
1114
1115 let child0 = taffy.new_leaf(Style::default()).unwrap();
1116 taffy.add_child(node, child0).unwrap();
1117 assert_eq!(taffy.child_count(node), 1);
1118
1119 let child1 = taffy.new_leaf(Style::default()).unwrap();
1120 taffy.add_child(node, child1).unwrap();
1121 assert_eq!(taffy.child_count(node), 2);
1122 }
1123
1124 #[test]
1125 fn insert_child_at_index() {
1126 let mut taffy: TaffyTree<()> = TaffyTree::new();
1127
1128 let child0 = taffy.new_leaf(Style::default()).unwrap();
1129 let child1 = taffy.new_leaf(Style::default()).unwrap();
1130 let child2 = taffy.new_leaf(Style::default()).unwrap();
1131
1132 let node = taffy.new_leaf(Style::default()).unwrap();
1133 assert_eq!(taffy.child_count(node), 0);
1134
1135 taffy.insert_child_at_index(node, 0, child0).unwrap();
1136 assert_eq!(taffy.child_count(node), 1);
1137 assert_eq!(taffy.children(node).unwrap()[0], child0);
1138
1139 taffy.insert_child_at_index(node, 0, child1).unwrap();
1140 assert_eq!(taffy.child_count(node), 2);
1141 assert_eq!(taffy.children(node).unwrap()[0], child1);
1142 assert_eq!(taffy.children(node).unwrap()[1], child0);
1143
1144 taffy.insert_child_at_index(node, 1, child2).unwrap();
1145 assert_eq!(taffy.child_count(node), 3);
1146 assert_eq!(taffy.children(node).unwrap()[0], child1);
1147 assert_eq!(taffy.children(node).unwrap()[1], child2);
1148 assert_eq!(taffy.children(node).unwrap()[2], child0);
1149 }
1150
1151 #[test]
1152 fn set_children() {
1153 let mut taffy: TaffyTree<()> = TaffyTree::new();
1154
1155 let child0 = taffy.new_leaf(Style::default()).unwrap();
1156 let child1 = taffy.new_leaf(Style::default()).unwrap();
1157 let node = taffy.new_with_children(Style::default(), &[child0, child1]).unwrap();
1158
1159 assert_eq!(taffy.child_count(node), 2);
1160 assert_eq!(taffy.children(node).unwrap()[0], child0);
1161 assert_eq!(taffy.children(node).unwrap()[1], child1);
1162
1163 let child2 = taffy.new_leaf(Style::default()).unwrap();
1164 let child3 = taffy.new_leaf(Style::default()).unwrap();
1165 taffy.set_children(node, &[child2, child3]).unwrap();
1166
1167 assert_eq!(taffy.child_count(node), 2);
1168 assert_eq!(taffy.children(node).unwrap()[0], child2);
1169 assert_eq!(taffy.children(node).unwrap()[1], child3);
1170 }
1171
1172 /// Test that removing a child works
1173 #[test]
1174 fn remove_child() {
1175 let mut taffy: TaffyTree<()> = TaffyTree::new();
1176 let child0 = taffy.new_leaf(Style::default()).unwrap();
1177 let child1 = taffy.new_leaf(Style::default()).unwrap();
1178 let node = taffy.new_with_children(Style::default(), &[child0, child1]).unwrap();
1179
1180 assert_eq!(taffy.child_count(node), 2);
1181
1182 taffy.remove_child(node, child0).unwrap();
1183 assert_eq!(taffy.child_count(node), 1);
1184 assert_eq!(taffy.children(node).unwrap()[0], child1);
1185
1186 taffy.remove_child(node, child1).unwrap();
1187 assert_eq!(taffy.child_count(node), 0);
1188 }
1189
1190 #[test]
1191 fn remove_child_at_index() {
1192 let mut taffy: TaffyTree<()> = TaffyTree::new();
1193 let child0 = taffy.new_leaf(Style::default()).unwrap();
1194 let child1 = taffy.new_leaf(Style::default()).unwrap();
1195 let node = taffy.new_with_children(Style::default(), &[child0, child1]).unwrap();
1196
1197 assert_eq!(taffy.child_count(node), 2);
1198
1199 taffy.remove_child_at_index(node, 0).unwrap();
1200 assert_eq!(taffy.child_count(node), 1);
1201 assert_eq!(taffy.children(node).unwrap()[0], child1);
1202
1203 taffy.remove_child_at_index(node, 0).unwrap();
1204 assert_eq!(taffy.child_count(node), 0);
1205 }
1206
1207 #[test]
1208 fn remove_children_range() {
1209 let mut taffy: TaffyTree<()> = TaffyTree::new();
1210 let child0 = taffy.new_leaf(Style::default()).unwrap();
1211 let child1 = taffy.new_leaf(Style::default()).unwrap();
1212 let child2 = taffy.new_leaf(Style::default()).unwrap();
1213 let child3 = taffy.new_leaf(Style::default()).unwrap();
1214 let node = taffy.new_with_children(Style::default(), &[child0, child1, child2, child3]).unwrap();
1215
1216 assert_eq!(taffy.child_count(node), 4);
1217
1218 taffy.remove_children_range(node, 1..=2).unwrap();
1219 assert_eq!(taffy.child_count(node), 2);
1220 assert_eq!(taffy.children(node).unwrap(), [child0, child3]);
1221 for child in [child0, child3] {
1222 assert_eq!(taffy.parent(child), Some(node));
1223 }
1224 for child in [child1, child2] {
1225 assert_eq!(taffy.parent(child), None);
1226 }
1227 }
1228
1229 // Related to: https://github.com/DioxusLabs/taffy/issues/510
1230 #[test]
1231 fn remove_child_updates_parents() {
1232 let mut taffy: TaffyTree<()> = TaffyTree::new();
1233
1234 let parent = taffy.new_leaf(Style::default()).unwrap();
1235 let child = taffy.new_leaf(Style::default()).unwrap();
1236
1237 taffy.add_child(parent, child).unwrap();
1238
1239 taffy.remove(parent).unwrap();
1240
1241 // Once the parent is removed this shouldn't panic.
1242 assert!(taffy.set_children(child, &[]).is_ok());
1243 }
1244
1245 #[test]
1246 fn replace_child_at_index() {
1247 let mut taffy: TaffyTree<()> = TaffyTree::new();
1248
1249 let child0 = taffy.new_leaf(Style::default()).unwrap();
1250 let child1 = taffy.new_leaf(Style::default()).unwrap();
1251
1252 let node = taffy.new_with_children(Style::default(), &[child0]).unwrap();
1253 assert_eq!(taffy.child_count(node), 1);
1254 assert_eq!(taffy.children(node).unwrap()[0], child0);
1255
1256 taffy.replace_child_at_index(node, 0, child1).unwrap();
1257 assert_eq!(taffy.child_count(node), 1);
1258 assert_eq!(taffy.children(node).unwrap()[0], child1);
1259 }
1260 #[test]
1261 fn test_child_at_index() {
1262 let mut taffy: TaffyTree<()> = TaffyTree::new();
1263 let child0 = taffy.new_leaf(Style::default()).unwrap();
1264 let child1 = taffy.new_leaf(Style::default()).unwrap();
1265 let child2 = taffy.new_leaf(Style::default()).unwrap();
1266 let node = taffy.new_with_children(Style::default(), &[child0, child1, child2]).unwrap();
1267
1268 assert!(if let Ok(result) = taffy.child_at_index(node, 0) { result == child0 } else { false });
1269 assert!(if let Ok(result) = taffy.child_at_index(node, 1) { result == child1 } else { false });
1270 assert!(if let Ok(result) = taffy.child_at_index(node, 2) { result == child2 } else { false });
1271 }
1272 #[test]
1273 fn test_child_count() {
1274 let mut taffy: TaffyTree<()> = TaffyTree::new();
1275 let child0 = taffy.new_leaf(Style::default()).unwrap();
1276 let child1 = taffy.new_leaf(Style::default()).unwrap();
1277 let node = taffy.new_with_children(Style::default(), &[child0, child1]).unwrap();
1278
1279 assert!(taffy.child_count(node) == 2);
1280 assert!(taffy.child_count(child0) == 0);
1281 assert!(taffy.child_count(child1) == 0);
1282 }
1283
1284 #[allow(clippy::vec_init_then_push)]
1285 #[test]
1286 fn test_children() {
1287 let mut taffy: TaffyTree<()> = TaffyTree::new();
1288 let child0 = taffy.new_leaf(Style::default()).unwrap();
1289 let child1 = taffy.new_leaf(Style::default()).unwrap();
1290 let node = taffy.new_with_children(Style::default(), &[child0, child1]).unwrap();
1291
1292 let mut children = sys::Vec::new();
1293 children.push(child0);
1294 children.push(child1);
1295
1296 let children_result = taffy.children(node).unwrap();
1297 assert_eq!(children_result, children);
1298
1299 assert!(taffy.children(child0).unwrap().is_empty());
1300 }
1301 #[test]
1302 fn test_set_style() {
1303 let mut taffy: TaffyTree<()> = TaffyTree::new();
1304
1305 let node = taffy.new_leaf(Style::default()).unwrap();
1306 assert_eq!(taffy.style(node).unwrap().display, Display::Flex);
1307
1308 taffy.set_style(node, Style { display: Display::None, ..Style::default() }).unwrap();
1309 assert_eq!(taffy.style(node).unwrap().display, Display::None);
1310 }
1311 #[test]
1312 fn test_style() {
1313 let mut taffy: TaffyTree<()> = TaffyTree::new();
1314
1315 let style = Style { display: Display::None, flex_direction: FlexDirection::RowReverse, ..Default::default() };
1316
1317 let node = taffy.new_leaf(style.clone()).unwrap();
1318
1319 let res = taffy.style(node);
1320 assert!(res.is_ok());
1321 assert!(res.unwrap() == &style);
1322 }
1323 #[test]
1324 fn test_layout() {
1325 let mut taffy: TaffyTree<()> = TaffyTree::new();
1326 let node = taffy.new_leaf(Style::default()).unwrap();
1327
1328 // TODO: Improve this test?
1329 let res = taffy.layout(node);
1330 assert!(res.is_ok());
1331 }
1332
1333 #[test]
1334 fn test_mark_dirty() {
1335 let mut taffy: TaffyTree<()> = TaffyTree::new();
1336 let child0 = taffy.new_leaf(Style::default()).unwrap();
1337 let child1 = taffy.new_leaf(Style::default()).unwrap();
1338 let node = taffy.new_with_children(Style::default(), &[child0, child1]).unwrap();
1339
1340 taffy.compute_layout(node, Size::MAX_CONTENT).unwrap();
1341
1342 assert_eq!(taffy.dirty(child0), Ok(false));
1343 assert_eq!(taffy.dirty(child1), Ok(false));
1344 assert_eq!(taffy.dirty(node), Ok(false));
1345
1346 taffy.mark_dirty(node).unwrap();
1347 assert_eq!(taffy.dirty(child0), Ok(false));
1348 assert_eq!(taffy.dirty(child1), Ok(false));
1349 assert_eq!(taffy.dirty(node), Ok(true));
1350
1351 taffy.compute_layout(node, Size::MAX_CONTENT).unwrap();
1352 taffy.mark_dirty(child0).unwrap();
1353 assert_eq!(taffy.dirty(child0), Ok(true));
1354 assert_eq!(taffy.dirty(child1), Ok(false));
1355 assert_eq!(taffy.dirty(node), Ok(true));
1356 }
1357
1358 #[test]
1359 fn compute_layout_should_produce_valid_result() {
1360 let mut taffy: TaffyTree<()> = TaffyTree::new();
1361 let node_result = taffy.new_leaf(Style {
1362 size: Size { width: Dimension::from_length(10f32), height: Dimension::from_length(10f32) },
1363 ..Default::default()
1364 });
1365 assert!(node_result.is_ok());
1366 let node = node_result.unwrap();
1367 let layout_result = taffy.compute_layout(
1368 node,
1369 Size { width: AvailableSpace::Definite(100.), height: AvailableSpace::Definite(100.) },
1370 );
1371 assert!(layout_result.is_ok());
1372 }
1373
1374 #[test]
1375 fn make_sure_layout_location_is_top_left() {
1376 use crate::prelude::*;
1377
1378 let mut taffy: TaffyTree<()> = TaffyTree::new();
1379
1380 let node = taffy
1381 .new_leaf(Style {
1382 size: Size { width: Dimension::from_percent(1f32), height: Dimension::from_percent(1f32) },
1383 ..Default::default()
1384 })
1385 .unwrap();
1386
1387 let root = taffy
1388 .new_with_children(
1389 Style {
1390 size: Size { width: Dimension::from_length(100f32), height: Dimension::from_length(100f32) },
1391 padding: Rect {
1392 left: length(10f32),
1393 right: length(20f32),
1394 top: length(30f32),
1395 bottom: length(40f32),
1396 },
1397 ..Default::default()
1398 },
1399 &[node],
1400 )
1401 .unwrap();
1402
1403 taffy.compute_layout(root, Size::MAX_CONTENT).unwrap();
1404
1405 // If Layout::location represents top-left coord, 'node' location
1406 // must be (due applied 'root' padding): {x: 10, y: 30}.
1407 //
1408 // It's important, since result will be different for each other
1409 // coordinate space:
1410 // - bottom-left: {x: 10, y: 40}
1411 // - top-right: {x: 20, y: 30}
1412 // - bottom-right: {x: 20, y: 40}
1413 let layout = taffy.layout(node).unwrap();
1414 assert_eq!(layout.location.x, 10f32);
1415 assert_eq!(layout.location.y, 30f32);
1416 }
1417
1418 #[test]
1419 fn set_children_reparents() {
1420 let mut taffy: TaffyTree<()> = TaffyTree::new();
1421 let child = taffy.new_leaf(Style::default()).unwrap();
1422 let old_parent = taffy.new_with_children(Style::default(), &[child]).unwrap();
1423
1424 let new_parent = taffy.new_leaf(Style::default()).unwrap();
1425 taffy.set_children(new_parent, &[child]).unwrap();
1426
1427 assert!(taffy.children(old_parent).unwrap().is_empty());
1428 }
1429}