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 //! 129 use super::{Layout, LayoutInput, LayoutOutput, NodeId, RequestedAxis, RunMode, SizingMode}; 130 #[cfg(feature = "detailed_layout_info")] 131 use crate::debug::debug_log; 132 use crate::geometry::{AbsoluteAxis, Line, Size}; 133 use crate::style::{AvailableSpace, CoreStyle}; 134 #[cfg(feature = "flexbox")] 135 use crate::style::{FlexboxContainerStyle, FlexboxItemStyle}; 136 #[cfg(feature = "grid")] 137 use crate::style::{GridContainerStyle, GridItemStyle}; 138 #[cfg(feature = "block_layout")] 139 use crate::{BlockContainerStyle, BlockItemStyle}; 140 141 #[cfg(all(feature = "grid", feature = "detailed_layout_info"))] 142 use 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`. 147 pub 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 child_ids(&self, parent_node_id: NodeId) -> Self::ChildIter<'_>154 fn child_ids(&self, parent_node_id: NodeId) -> Self::ChildIter<'_>; 155 156 /// Get the number of children for the given node child_count(&self, parent_node_id: NodeId) -> usize157 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 get_child_id(&self, parent_node_id: NodeId, child_index: usize) -> NodeId160 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. 167 pub 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. 173 pub 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 177 where 178 Self: 'a; 179 180 /// Get core style get_core_container_style(&self, node_id: NodeId) -> Self::CoreContainerStyle<'_>181 fn get_core_container_style(&self, node_id: NodeId) -> Self::CoreContainerStyle<'_>; 182 183 /// Set the node's unrounded layout set_unrounded_layout(&mut self, node_id: NodeId, layout: &Layout)184 fn set_unrounded_layout(&mut self, node_id: NodeId, layout: &Layout); 185 186 /// Compute the specified node's size or full layout given the specified constraints compute_child_layout(&mut self, node_id: NodeId, inputs: LayoutInput) -> LayoutOutput187 fn compute_child_layout(&mut self, node_id: NodeId, inputs: LayoutInput) -> LayoutOutput; 188 } 189 190 /// Trait used by the `compute_cached_layout` method which allows cached layout results to be stored and retrieved. 191 /// 192 /// The `Cache` struct implements a per-node cache that is compatible with this trait. 193 pub trait CacheTree { 194 /// Try to retrieve a cached result from the cache cache_get( &self, node_id: NodeId, known_dimensions: Size<Option<f32>>, available_space: Size<AvailableSpace>, run_mode: RunMode, ) -> Option<LayoutOutput>195 fn cache_get( 196 &self, 197 node_id: NodeId, 198 known_dimensions: Size<Option<f32>>, 199 available_space: Size<AvailableSpace>, 200 run_mode: RunMode, 201 ) -> Option<LayoutOutput>; 202 203 /// Store a computed size in the cache cache_store( &mut self, node_id: NodeId, known_dimensions: Size<Option<f32>>, available_space: Size<AvailableSpace>, run_mode: RunMode, layout_output: LayoutOutput, )204 fn cache_store( 205 &mut self, 206 node_id: NodeId, 207 known_dimensions: Size<Option<f32>>, 208 available_space: Size<AvailableSpace>, 209 run_mode: RunMode, 210 layout_output: LayoutOutput, 211 ); 212 213 /// Clear all cache entries for the node cache_clear(&mut self, node_id: NodeId)214 fn cache_clear(&mut self, node_id: NodeId); 215 } 216 217 /// Trait used by the `round_layout` method which takes a tree of unrounded float-valued layouts and performs 218 /// rounding to snap the values to the pixel grid. 219 /// 220 /// As indicated by it's dependence on `TraverseTree`, it required full recursive access to the tree. 221 pub trait RoundTree: TraverseTree { 222 /// Get the node's unrounded layout get_unrounded_layout(&self, node_id: NodeId) -> &Layout223 fn get_unrounded_layout(&self, node_id: NodeId) -> &Layout; 224 /// Get a reference to the node's final layout set_final_layout(&mut self, node_id: NodeId, layout: &Layout)225 fn set_final_layout(&mut self, node_id: NodeId, layout: &Layout); 226 } 227 228 /// Trait used by the `print_tree` method which prints a debug representation 229 /// 230 /// As indicated by it's dependence on `TraverseTree`, it required full recursive access to the tree. 231 pub trait PrintTree: TraverseTree { 232 /// Get a debug label for the node (typically the type of node: flexbox, grid, text, image, etc) get_debug_label(&self, node_id: NodeId) -> &'static str233 fn get_debug_label(&self, node_id: NodeId) -> &'static str; 234 /// Get a reference to the node's final layout get_final_layout(&self, node_id: NodeId) -> &Layout235 fn get_final_layout(&self, node_id: NodeId) -> &Layout; 236 } 237 238 #[cfg(feature = "flexbox")] 239 /// Extends [`LayoutPartialTree`] with getters for the styles required for Flexbox layout 240 pub trait LayoutFlexboxContainer: LayoutPartialTree { 241 /// The style type representing the Flexbox container's styles 242 type FlexboxContainerStyle<'a>: FlexboxContainerStyle 243 where 244 Self: 'a; 245 /// The style type representing each Flexbox item's styles 246 type FlexboxItemStyle<'a>: FlexboxItemStyle 247 where 248 Self: 'a; 249 250 /// Get the container's styles get_flexbox_container_style(&self, node_id: NodeId) -> Self::FlexboxContainerStyle<'_>251 fn get_flexbox_container_style(&self, node_id: NodeId) -> Self::FlexboxContainerStyle<'_>; 252 253 /// Get the child's styles get_flexbox_child_style(&self, child_node_id: NodeId) -> Self::FlexboxItemStyle<'_>254 fn get_flexbox_child_style(&self, child_node_id: NodeId) -> Self::FlexboxItemStyle<'_>; 255 } 256 257 #[cfg(feature = "grid")] 258 /// Extends [`LayoutPartialTree`] with getters for the styles required for CSS Grid layout 259 pub trait LayoutGridContainer: LayoutPartialTree { 260 /// The style type representing the CSS Grid container's styles 261 type GridContainerStyle<'a>: GridContainerStyle 262 where 263 Self: 'a; 264 265 /// The style type representing each CSS Grid item's styles 266 type GridItemStyle<'a>: GridItemStyle 267 where 268 Self: 'a; 269 270 /// Get the container's styles get_grid_container_style(&self, node_id: NodeId) -> Self::GridContainerStyle<'_>271 fn get_grid_container_style(&self, node_id: NodeId) -> Self::GridContainerStyle<'_>; 272 273 /// Get the child's styles get_grid_child_style(&self, child_node_id: NodeId) -> Self::GridItemStyle<'_>274 fn get_grid_child_style(&self, child_node_id: NodeId) -> Self::GridItemStyle<'_>; 275 276 /// Set the node's detailed grid information 277 /// 278 /// Implementing this method is optional. Doing so allows you to access details about the the grid such as 279 /// the computed size of each grid track and the computed placement of each grid item. 280 #[cfg(feature = "detailed_layout_info")] set_detailed_grid_info(&mut self, _node_id: NodeId, _detailed_grid_info: DetailedGridInfo)281 fn set_detailed_grid_info(&mut self, _node_id: NodeId, _detailed_grid_info: DetailedGridInfo) { 282 debug_log!("LayoutGridContainer::set_detailed_grid_info called"); 283 } 284 } 285 286 #[cfg(feature = "block_layout")] 287 /// Extends [`LayoutPartialTree`] with getters for the styles required for CSS Block layout 288 pub trait LayoutBlockContainer: LayoutPartialTree { 289 /// The style type representing the CSS Block container's styles 290 type BlockContainerStyle<'a>: BlockContainerStyle 291 where 292 Self: 'a; 293 /// The style type representing each CSS Block item's styles 294 type BlockItemStyle<'a>: BlockItemStyle 295 where 296 Self: 'a; 297 298 /// Get the container's styles get_block_container_style(&self, node_id: NodeId) -> Self::BlockContainerStyle<'_>299 fn get_block_container_style(&self, node_id: NodeId) -> Self::BlockContainerStyle<'_>; 300 301 /// Get the child's styles get_block_child_style(&self, child_node_id: NodeId) -> Self::BlockItemStyle<'_>302 fn get_block_child_style(&self, child_node_id: NodeId) -> Self::BlockItemStyle<'_>; 303 } 304 305 // --- PRIVATE TRAITS 306 307 /// A private trait which allows us to add extra convenience methods to types which implement 308 /// LayoutTree without making those methods public. 309 pub(crate) trait LayoutPartialTreeExt: LayoutPartialTree { 310 /// Compute the size of the node given the specified constraints 311 #[inline(always)] 312 #[allow(clippy::too_many_arguments)] measure_child_size( &mut self, node_id: NodeId, known_dimensions: Size<Option<f32>>, parent_size: Size<Option<f32>>, available_space: Size<AvailableSpace>, sizing_mode: SizingMode, axis: AbsoluteAxis, vertical_margins_are_collapsible: Line<bool>, ) -> f32313 fn measure_child_size( 314 &mut self, 315 node_id: NodeId, 316 known_dimensions: Size<Option<f32>>, 317 parent_size: Size<Option<f32>>, 318 available_space: Size<AvailableSpace>, 319 sizing_mode: SizingMode, 320 axis: AbsoluteAxis, 321 vertical_margins_are_collapsible: Line<bool>, 322 ) -> f32 { 323 self.compute_child_layout( 324 node_id, 325 LayoutInput { 326 known_dimensions, 327 parent_size, 328 available_space, 329 sizing_mode, 330 axis: axis.into(), 331 run_mode: RunMode::ComputeSize, 332 vertical_margins_are_collapsible, 333 }, 334 ) 335 .size 336 .get_abs(axis) 337 } 338 339 /// Perform a full layout on the node given the specified constraints 340 #[inline(always)] perform_child_layout( &mut self, node_id: NodeId, known_dimensions: Size<Option<f32>>, parent_size: Size<Option<f32>>, available_space: Size<AvailableSpace>, sizing_mode: SizingMode, vertical_margins_are_collapsible: Line<bool>, ) -> LayoutOutput341 fn perform_child_layout( 342 &mut self, 343 node_id: NodeId, 344 known_dimensions: Size<Option<f32>>, 345 parent_size: Size<Option<f32>>, 346 available_space: Size<AvailableSpace>, 347 sizing_mode: SizingMode, 348 vertical_margins_are_collapsible: Line<bool>, 349 ) -> LayoutOutput { 350 self.compute_child_layout( 351 node_id, 352 LayoutInput { 353 known_dimensions, 354 parent_size, 355 available_space, 356 sizing_mode, 357 axis: RequestedAxis::Both, 358 run_mode: RunMode::PerformLayout, 359 vertical_margins_are_collapsible, 360 }, 361 ) 362 } 363 } 364 365 impl<T: LayoutPartialTree> LayoutPartialTreeExt for T {} 366