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