tawasuyu

tawasuyu/taffy

A high performance rust-powered UI layout library

HTML 5544Rust 88Markdown 11TOML 91926 Definitions

⎇ 89f50ccb7d37e4d4b78a8250eca82eeaf63ad3de src/tree/taffy_tree.rs Rust · 1409 lines · 50.9 KB History Who touched what raw

Add support for display: flow-root 89f50ccb7 · nico.burns ·

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