Skip to main content

taffy/tree/
traits.rs

1//! The abstractions that make up the core of Taffy's low-level API
2//!
3//! ## Examples
4//!
5//! The following examples demonstrate end-to-end implementation of Taffy's traits and usage of the low-level compute APIs:
6//!
7//!   - [custom_tree_vec](https://github.com/DioxusLabs/taffy/blob/main/examples/custom_tree_vec.rs) which implements a custom Taffy tree using a `Vec` as an arena with NodeId's being index's into the Vec.
8//!   - [custom_tree_owned_partial](https://github.com/DioxusLabs/taffy/blob/main/examples/custom_tree_owned_partial.rs) which implements a custom Taffy tree using directly owned children with NodeId's being index's into vec on parent node.
9//!   - [custom_tree_owned_unsafe](https://github.com/DioxusLabs/taffy/blob/main/examples/custom_tree_owned_unsafe.rs) which implements a custom Taffy tree using directly owned children with NodeId's being pointers.
10//!
11//! ## Overview
12//!
13//! ### Trait dependency tree
14//!
15//! The tree below illustrates which traits depend on which other traits.
16//!
17//! ```text
18//! TraversePartialTree     - Access a node's children
19//! ├──  LayoutPartialTree  - Run layout algorithms on a node and it's direct children
20//! └──  TraverseTree       - Recursively access a node's descendants
21//!     ├──  RoundTree      - Round a float-valued`  layout to integer pixels
22//!     └──  PrintTree      - Print a debug representation of a node tree
23//! ```
24//!
25//! ### A table of traits
26//!
27//! | Trait                 | Requires                | Enables                                                                                                                                                                                                                                                                                                                                                                                                                   |
28//! | ---                   | ---                     | ---                                                                                                                                                                                                                                                                                                                                                                                                                       |
29//! | [`LayoutPartialTree`] | [`TraversePartialTree`] | [`compute_flexbox_layout`](crate::compute_flexbox_layout)<br />[`compute_grid_layout`](crate::compute_grid_layout)<br />[`compute_block_layout`](crate::compute_block_layout)<br />[`compute_root_layout`](crate::compute_root_layout)<br />[`compute_leaf_layout`](crate::compute_leaf_layout)<br />[`compute_hidden_layout`](crate::compute_hidden_layout)<br />[`compute_cached_layout`](crate::compute_cached_layout) |
30//! | [`RoundTree`]         | [`TraverseTree`]        | [`round_layout`](crate::round_layout)                                                                                                                                                                                                                                                                                                                                                                                     |
31//! | [`PrintTree`]         | [`TraverseTree`]        | [`print_tree`](crate::print_tree)                                                                                                                                                                                                                                                                                                                                                                                         |
32//!
33//! ## All of the traits on one page
34//!
35//! ### TraversePartialTree and TraverseTree
36//! These traits are Taffy's abstraction for downward tree traversal:
37//!  - [`TraversePartialTree`] allows access to a single container node, and it's immediate children. This is the only "traverse" trait that is required
38//!    for use of Taffy's core layout algorithms (flexbox, grid, etc).
39//!  - [`TraverseTree`] is a marker trait which uses the same API signature as `TraversePartialTree`, but extends it with a guarantee that the child/children methods can be used to recurse
40//!    infinitely down the tree. It is required by the `RoundTree` and
41//!    the `PrintTree` traits.
42//! ```rust
43//! # use taffy::*;
44//! pub trait TraversePartialTree {
45//!     /// Type representing an iterator of the children of a node
46//!     type ChildIter<'a>: Iterator<Item = NodeId>
47//!     where
48//!         Self: 'a;
49//!
50//!     /// Get the list of children IDs for the given node
51//!     fn child_ids(&self, parent_node_id: NodeId) -> Self::ChildIter<'_>;
52//!
53//!     /// Get the number of children for the given node
54//!     fn child_count(&self, parent_node_id: NodeId) -> usize;
55//!
56//!     /// Get a specific child of a node, where the index represents the nth child
57//!     fn get_child_id(&self, parent_node_id: NodeId, child_index: usize) -> NodeId;
58//! }
59//!
60//! pub trait TraverseTree: TraversePartialTree {}
61//! ```
62//!
63//! You must implement [`TraversePartialTree`] to access any of Taffy's low-level API. If your tree implementation allows you to implement [`TraverseTree`] with
64//! the correct semantics (full recursive traversal is available) then you should.
65//!
66//! ### LayoutPartialTree
67//!
68//! **Requires:** `TraversePartialTree`<br />
69//! **Enables:** Flexbox, Grid, Block and Leaf layout algorithms from the [`crate::compute`] module
70//!
71//! Any type that implements [`LayoutPartialTree`] can be laid out using [Taffy's algorithms](crate::compute)
72//!
73//! Note that this trait extends [`TraversePartialTree`] (not [`TraverseTree`]). Taffy's algorithm implementations have been designed such that they can be used for a laying out a single
74//! node that only has access to it's immediate children.
75//!
76//! ```rust
77//! # use taffy::*;
78//! pub trait LayoutPartialTree: TraversePartialTree {
79//!     /// Get a reference to the [`Style`] for this node.
80//!     fn get_style(&self, node_id: NodeId) -> &Style;
81//!
82//!     /// Set the node's unrounded layout
83//!     fn set_unrounded_layout(&mut self, node_id: NodeId, layout: &Layout);
84//!
85//!     /// Get a mutable reference to the [`Cache`] for this node.
86//!     fn get_cache_mut(&mut self, node_id: NodeId) -> &mut Cache;
87//!
88//!     /// Compute the specified node's size or full layout given the specified constraints
89//!     fn compute_child_layout(&mut self, node_id: NodeId, inputs: LayoutInput) -> LayoutOutput;
90//! }
91//! ```
92//!
93//! ### RoundTree
94//!
95//! **Requires:** `TraverseTree`
96//!
97//! Trait used by the `round_layout` method which takes a tree of unrounded float-valued layouts and performs
98//! rounding to snap the values to the pixel grid.
99//!
100//! As indicated by it's dependence on `TraverseTree`, it required full recursive access to the tree.
101//!
102//! ```rust
103//! # use taffy::*;
104//! pub trait RoundTree: TraverseTree {
105//!     /// Get the node's unrounded layout
106//!     fn get_unrounded_layout(&self, node_id: NodeId) -> Layout;
107//!     /// Get a reference to the node's final layout
108//!     fn set_final_layout(&mut self, node_id: NodeId, layout: &Layout);
109//! }
110//! ```
111//!
112//! ### PrintTree
113//!
114//! **Requires:** `TraverseTree`
115//!
116//! ```rust
117//! /// Trait used by the `print_tree` method which prints a debug representation
118//! ///
119//! /// As indicated by it's dependence on `TraverseTree`, it required full recursive access to the tree.
120//! # use taffy::*;
121//! pub trait PrintTree: TraverseTree {
122//!     /// Get a debug label for the node (typically the type of node: flexbox, grid, text, image, etc)
123//!     fn get_debug_label(&self, node_id: NodeId) -> &'static str;
124//!     /// Get a reference to the node's final layout
125//!     fn get_final_layout(&self, node_id: NodeId) -> Layout;
126//! }
127//! ```
128//!
129use super::{DetailedLayoutInfo, Layout, LayoutInput, LayoutOutput, NodeId, RequestedAxis, RunMode, SizingMode};
130use crate::debug::debug_log;
131use crate::geometry::{AbsoluteAxis, Line, Size};
132use crate::style::{AvailableSpace, CoreStyle, OofItemStyle};
133#[cfg(feature = "flexbox")]
134use crate::style::{FlexboxContainerStyle, FlexboxItemStyle};
135#[cfg(feature = "grid")]
136use crate::style::{GridContainerStyle, GridItemStyle};
137use crate::CheapCloneStr;
138#[cfg(feature = "block_layout")]
139use crate::{BlockContainerStyle, BlockContext, BlockItemStyle};
140
141#[cfg(feature = "grid")]
142use crate::compute::grid::DetailedGridInfo;
143
144/// Taffy's abstraction for downward tree traversal.
145///
146/// However, this trait does *not* require access to any node's other than a single container node's immediate children unless you also intend to implement `TraverseTree`.
147pub trait TraversePartialTree {
148    /// Type representing an iterator of the children of a node
149    type ChildIter<'a>: Iterator<Item = NodeId>
150    where
151        Self: 'a;
152
153    /// Get the list of children IDs for the given node
154    fn child_ids(&self, parent_node_id: NodeId) -> Self::ChildIter<'_>;
155
156    /// Get the number of children for the given node
157    fn child_count(&self, parent_node_id: NodeId) -> usize;
158
159    /// Get a specific child of a node, where the index represents the nth child
160    fn get_child_id(&self, parent_node_id: NodeId, child_index: usize) -> NodeId;
161}
162
163/// A marker trait which extends `TraversePartialTree`
164///
165/// Implementing this trait implies the additional guarantee that the child/children methods can be used to recurse
166/// infinitely down the tree. Is required by the `RoundTree` and the `PrintTree` traits.
167pub trait TraverseTree: TraversePartialTree {}
168
169/// Any type that implements [`LayoutPartialTree`] can be laid out using [Taffy's algorithms](crate::compute)
170///
171/// Note that this trait extends [`TraversePartialTree`] (not [`TraverseTree`]). Taffy's algorithm implementations have been designed such that they can be used for a laying out a single
172/// node that only has access to it's immediate children.
173pub trait LayoutPartialTree: TraversePartialTree {
174    /// The style type representing the core container styles that all containers should have
175    /// Used when laying out the root node of a tree
176    type CoreContainerStyle<'a>: CoreStyle<CustomIdent = Self::CustomIdent>
177    where
178        Self: 'a;
179
180    /// String type for representing "custom identifiers" (for example, named grid lines or areas)
181    /// If you are unsure what to use here then consider `Arc<str>`.
182    type CustomIdent: CheapCloneStr;
183
184    /// Get core style
185    ///
186    /// Only called for the node being laid out, never for its children (those go through the
187    /// `get_*_child_style` methods), so it may be expressed in the node's own axes while child
188    /// styles are expressed in the axes of the node laying them out.
189    fn get_core_container_style(&self, node_id: NodeId) -> Self::CoreContainerStyle<'_>;
190
191    /// Resolve calc value
192    #[inline(always)]
193    fn resolve_calc_value(&self, val: *const (), basis: f32) -> f32 {
194        let _ = val;
195        let _ = basis;
196        0.0
197    }
198
199    /// Set the node's unrounded layout
200    fn set_unrounded_layout(&mut self, node_id: NodeId, layout: &Layout);
201
202    /// Compute the specified node's size or full layout given the specified constraints
203    fn compute_child_layout(&mut self, node_id: NodeId, inputs: LayoutInput) -> LayoutOutput;
204}
205
206/// Extends [`LayoutPartialTree`] with the operations needed by the out-of-flow positioning pass
207/// ([`compute_oof_layout`](crate::compute_oof_layout)), which lays out the absolute/fixed boxes
208/// for which a node acts as the containing block.
209///
210/// This trait is required by [`compute_oof_layout`](crate::compute_oof_layout) and
211/// [`compute_root_layout`](crate::compute_root_layout).
212pub trait LayoutContainingBlock: LayoutPartialTree {
213    /// The style type representing the styles of an out-of-flow (absolute/fixed) box being
214    /// positioned by its containing block
215    type OofItemStyle<'a>: OofItemStyle<CustomIdent = Self::CustomIdent>
216    where
217        Self: 'a;
218
219    /// Get the style of an out-of-flow box being positioned by its containing block
220    ///
221    /// Interpreted in the containing block's axes (see [`LayoutPartialTree::get_core_container_style`]).
222    fn get_oof_item_style(&self, node_id: NodeId) -> Self::OofItemStyle<'_>;
223
224    /// Clear the list of out-of-flow (absolute/fixed) boxes whose containing block is `node_id`.
225    ///
226    /// This is called (exactly once) for each node laid out with `RunMode::PerformLayout`, before
227    /// the boxes it lays out are recorded with [`add_hoisted_children`](Self::add_hoisted_children),
228    /// so that lists recorded by previous layout runs do not persist.
229    fn clear_hoisted_children(&mut self, node_id: NodeId);
230
231    /// Append to the list of out-of-flow boxes whose containing block is `node_id`.
232    ///
233    /// This is called by each containing block for the boxes it lays out, and by
234    /// [`compute_root_layout`](crate::compute_root_layout) to record boxes (e.g. `position: fixed`
235    /// boxes) which are positioned by the final root positioning pass, which runs after the root
236    /// node's own layout algorithm has already recorded its list. The recorded lists are consumed
237    /// by [`round_layout`](crate::round_layout) (out-of-flow boxes are rounded via their
238    /// containing block rather than via their parent), and are also useful for consumers
239    /// implementing paint/hit-testing traversals.
240    fn add_hoisted_children(&mut self, node_id: NodeId, hoisted: &[NodeId]);
241
242    /// Read back the detailed layout information most recently recorded for `node_id`
243    /// (as stored by [`LayoutGridContainer::set_detailed_grid_info`]).
244    ///
245    /// This is used by the out-of-flow positioning pass to resolve the grid area of absolutely
246    /// positioned boxes whose containing block is a grid container. Implementing this method is
247    /// optional: the default implementation returns [`DetailedLayoutInfo::None`], in which case
248    /// such boxes are positioned relative to the grid container's padding box instead of their
249    /// grid area.
250    #[inline(always)]
251    fn get_detailed_layout_info(&self, node_id: NodeId) -> &DetailedLayoutInfo<Self::CustomIdent> {
252        let _ = node_id;
253        &DetailedLayoutInfo::None
254    }
255}
256
257/// Trait used by the `compute_cached_layout` method which allows cached layout results to be stored and retrieved.
258///
259/// The `Cache` struct implements a per-node cache that is compatible with this trait.
260pub trait CacheTree {
261    /// Try to retrieve a cached result from the cache
262    fn cache_get(&mut self, node_id: NodeId, input: &LayoutInput) -> Option<LayoutOutput>;
263
264    /// Store a computed size in the cache
265    fn cache_store(&mut self, node_id: NodeId, input: &LayoutInput, layout_output: LayoutOutput);
266
267    /// Clear all cache entries for the node
268    fn cache_clear(&mut self, node_id: NodeId);
269}
270
271/// Trait used by the `round_layout` method which takes a tree of unrounded float-valued layouts and performs
272/// rounding to snap the values to the pixel grid.
273///
274/// As indicated by it's dependence on `TraverseTree`, it required full recursive access to the tree.
275pub trait RoundTree: TraverseTree {
276    /// Get the node's unrounded layout
277    fn get_unrounded_layout(&self, node_id: NodeId) -> Layout;
278    /// Get a reference to the node's final layout
279    fn set_final_layout(&mut self, node_id: NodeId, layout: &Layout);
280    /// Whether the node is an out-of-flow (absolute/fixed) box. Out-of-flow boxes are hoisted to
281    /// their containing block, so [`round_layout`](crate::round_layout) skips them when visiting a
282    /// node's children and instead visits them via their containing block's hoisted child list.
283    ///
284    /// This should return `true` for box-generating nodes whose position style is `absolute` or
285    /// `fixed`, and `false` otherwise (including for `display: none` nodes).
286    fn is_out_of_flow(&self, node_id: NodeId) -> bool;
287    /// The number of out-of-flow boxes whose containing block is `node_id`
288    /// (as recorded by [`LayoutContainingBlock::add_hoisted_children`])
289    fn hoisted_child_count(&self, node_id: NodeId) -> usize;
290    /// Get the nth out-of-flow box whose containing block is `node_id`
291    /// (as recorded by [`LayoutContainingBlock::add_hoisted_children`])
292    fn get_hoisted_child_id(&self, node_id: NodeId, index: usize) -> NodeId;
293}
294
295/// Trait used by the `print_tree` method which prints a debug representation
296///
297/// As indicated by it's dependence on `TraverseTree`, it required full recursive access to the tree.
298pub trait PrintTree: TraverseTree {
299    /// Get a debug label for the node (typically the type of node: flexbox, grid, text, image, etc)
300    fn get_debug_label(&self, node_id: NodeId) -> &'static str;
301    /// Get a reference to the node's final layout
302    fn get_final_layout(&self, node_id: NodeId) -> Layout;
303}
304
305#[cfg(feature = "flexbox")]
306/// Extends [`LayoutPartialTree`] with getters for the styles required for Flexbox layout
307pub trait LayoutFlexboxContainer: LayoutPartialTree {
308    /// The style type representing the Flexbox container's styles
309    type FlexboxContainerStyle<'a>: FlexboxContainerStyle
310    where
311        Self: 'a;
312    /// The style type representing each Flexbox item's styles
313    type FlexboxItemStyle<'a>: FlexboxItemStyle
314    where
315        Self: 'a;
316
317    /// Get the container's styles
318    fn get_flexbox_container_style(&self, node_id: NodeId) -> Self::FlexboxContainerStyle<'_>;
319
320    /// Get the child's styles
321    ///
322    /// Interpreted in the container's axes (see [`LayoutPartialTree::get_core_container_style`]).
323    fn get_flexbox_child_style(&self, child_node_id: NodeId) -> Self::FlexboxItemStyle<'_>;
324}
325
326#[cfg(feature = "grid")]
327/// Extends [`LayoutPartialTree`] with getters for the styles required for CSS Grid layout
328pub trait LayoutGridContainer: LayoutPartialTree {
329    /// The style type representing the CSS Grid container's styles
330    type GridContainerStyle<'a>: GridContainerStyle<CustomIdent = Self::CustomIdent>
331    where
332        Self: 'a;
333
334    /// The style type representing each CSS Grid item's styles
335    type GridItemStyle<'a>: GridItemStyle<CustomIdent = Self::CustomIdent>
336    where
337        Self: 'a;
338
339    /// Get the container's styles
340    fn get_grid_container_style(&self, node_id: NodeId) -> Self::GridContainerStyle<'_>;
341
342    /// Get the child's styles
343    ///
344    /// Interpreted in the container's axes (see [`LayoutPartialTree::get_core_container_style`]).
345    fn get_grid_child_style(&self, child_node_id: NodeId) -> Self::GridItemStyle<'_>;
346
347    /// Set the node's detailed grid information
348    ///
349    /// Implementing this method is optional. Doing so allows you to access details about the the grid such as
350    /// the computed size of each grid track and the computed placement of each grid item.
351    fn set_detailed_grid_info(&mut self, _node_id: NodeId, _detailed_grid_info: DetailedGridInfo<Self::CustomIdent>) {
352        debug_log!("LayoutGridContainer::set_detailed_grid_info called");
353    }
354}
355
356#[cfg(feature = "block_layout")]
357/// Extends [`LayoutPartialTree`] with getters for the styles required for CSS Block layout
358pub trait LayoutBlockContainer: LayoutPartialTree {
359    /// The style type representing the CSS Block container's styles
360    type BlockContainerStyle<'a>: BlockContainerStyle
361    where
362        Self: 'a;
363    /// The style type representing each CSS Block item's styles
364    type BlockItemStyle<'a>: BlockItemStyle
365    where
366        Self: 'a;
367
368    /// Get the container's styles
369    fn get_block_container_style(&self, node_id: NodeId) -> Self::BlockContainerStyle<'_>;
370
371    /// Get the child's styles
372    ///
373    /// Interpreted in the container's axes (see [`LayoutPartialTree::get_core_container_style`]).
374    fn get_block_child_style(&self, child_node_id: NodeId) -> Self::BlockItemStyle<'_>;
375
376    /// Compute the specified node's size or full layout given the specified constraints
377    #[cfg(feature = "block_layout")]
378    fn compute_block_child_layout(
379        &mut self,
380        node_id: NodeId,
381        inputs: LayoutInput,
382        block_ctx: Option<&mut BlockContext<'_>>,
383    ) -> LayoutOutput {
384        let _ = block_ctx;
385        self.compute_child_layout(node_id, inputs)
386    }
387}
388
389// --- PRIVATE TRAITS
390
391/// A private trait which allows us to add extra convenience methods to types which implement
392/// LayoutTree without making those methods public.
393pub(crate) trait LayoutPartialTreeExt: LayoutPartialTree {
394    /// Compute the size of the node given the specified constraints
395    #[inline(always)]
396    #[allow(clippy::too_many_arguments)]
397    fn measure_child_size(
398        &mut self,
399        node_id: NodeId,
400        known_dimensions: Size<Option<f32>>,
401        parent_size: Size<Option<f32>>,
402        available_space: Size<AvailableSpace>,
403        sizing_mode: SizingMode,
404        axis: AbsoluteAxis,
405        vertical_margins_are_collapsible: Line<bool>,
406    ) -> f32 {
407        self.compute_child_layout(
408            node_id,
409            LayoutInput {
410                known_dimensions,
411                known_dimensions_are_definite: Size { width: true, height: true },
412                parent_size,
413                available_space,
414                sizing_mode,
415                axis: axis.into(),
416                run_mode: RunMode::ComputeSize,
417                vertical_margins_are_collapsible,
418            },
419        )
420        .size
421        .get_abs(axis)
422    }
423
424    /// Compute the size of the node given the specified constraints
425    #[inline(always)]
426    #[allow(clippy::too_many_arguments)]
427    fn measure_child_size_both(
428        &mut self,
429        node_id: NodeId,
430        known_dimensions: Size<Option<f32>>,
431        parent_size: Size<Option<f32>>,
432        available_space: Size<AvailableSpace>,
433        sizing_mode: SizingMode,
434        vertical_margins_are_collapsible: Line<bool>,
435    ) -> Size<f32> {
436        self.compute_child_layout(
437            node_id,
438            LayoutInput {
439                known_dimensions,
440                known_dimensions_are_definite: Size { width: true, height: true },
441                parent_size,
442                available_space,
443                sizing_mode,
444                axis: RequestedAxis::Both,
445                run_mode: RunMode::ComputeSize,
446                vertical_margins_are_collapsible,
447            },
448        )
449        .size
450    }
451
452    /// Perform a full layout on the node given the specified constraints
453    #[inline(always)]
454    fn perform_child_layout(
455        &mut self,
456        node_id: NodeId,
457        known_dimensions: Size<Option<f32>>,
458        parent_size: Size<Option<f32>>,
459        available_space: Size<AvailableSpace>,
460        sizing_mode: SizingMode,
461        vertical_margins_are_collapsible: Line<bool>,
462    ) -> LayoutOutput {
463        self.compute_child_layout(
464            node_id,
465            LayoutInput {
466                known_dimensions,
467                known_dimensions_are_definite: Size { width: true, height: true },
468                parent_size,
469                available_space,
470                sizing_mode,
471                axis: RequestedAxis::Both,
472                run_mode: RunMode::PerformLayout,
473                vertical_margins_are_collapsible,
474            },
475        )
476    }
477
478    /// Alias to `resolve_calc_value` with a shorter function name
479    #[inline(always)]
480    #[cfg(feature = "calc")]
481    fn calc(&self, val: *const (), basis: f32) -> f32 {
482        self.resolve_calc_value(val, basis)
483    }
484
485    /// Alias to `resolve_calc_value` with a shorter function name
486    #[inline(always)]
487    #[cfg(not(feature = "calc"))]
488    fn calc(&self, _val: *const (), _basis: f32) -> f32 {
489        0.0
490    }
491}
492
493impl<T: LayoutPartialTree> LayoutPartialTreeExt for T {}