compose_taffy/
taffy_tree.rs

1use compose_rt::{Composer, NodeKey};
2#[cfg(feature = "block_layout")]
3use taffy::{compute::compute_block_layout, LayoutBlockContainer};
4#[cfg(feature = "flexbox")]
5use taffy::{compute::compute_flexbox_layout, LayoutFlexboxContainer};
6#[cfg(feature = "grid")]
7use taffy::{compute::compute_grid_layout, LayoutGridContainer};
8use taffy::{
9    compute_cached_layout, compute_hidden_layout, compute_leaf_layout, AvailableSpace, CacheTree,
10    Display, FlexDirection, FlexboxContainerStyle, Layout, LayoutPartialTree, NodeId, PrintTree,
11    RoundTree, RunMode, Size, TraversePartialTree, TraverseTree,
12};
13
14use crate::traits::{IntoNodeId, IntoNodeKey, TaffyConfig, TaffyNode};
15
16pub struct TaffyTreeChildIter<'a>(core::slice::Iter<'a, NodeKey>);
17impl Iterator for TaffyTreeChildIter<'_> {
18    type Item = NodeId;
19
20    fn next(&mut self) -> Option<Self::Item> {
21        self.0.next().copied().map(IntoNodeId::into_node_id)
22    }
23}
24
25pub struct TaffyTreeView<'a, T>
26where
27    T: TaffyNode,
28    T::Context: TaffyConfig,
29{
30    pub composer: &'a Composer<T>,
31}
32
33impl<'a, T> TaffyTreeView<'a, T>
34where
35    T: TaffyNode,
36    T::Context: TaffyConfig,
37{
38    pub fn new(composer: &'a Composer<T>) -> Self {
39        Self { composer }
40    }
41}
42
43impl<T> TraversePartialTree for TaffyTreeView<'_, T>
44where
45    T: TaffyNode,
46    T::Context: TaffyConfig,
47{
48    type ChildIter<'a>
49        = TaffyTreeChildIter<'a>
50    where
51        Self: 'a;
52
53    #[inline(always)]
54    fn child_ids(&self, node_id: NodeId) -> Self::ChildIter<'_> {
55        let node_key = node_id.into_node_key();
56        TaffyTreeChildIter(self.composer.nodes[node_key].children.iter())
57    }
58
59    #[inline(always)]
60    fn child_count(&self, node_id: NodeId) -> usize {
61        let node_key = node_id.into_node_key();
62        self.composer.nodes[node_key].children.len()
63    }
64
65    #[inline(always)]
66    fn get_child_id(&self, node_id: NodeId, child_index: usize) -> NodeId {
67        let node_key = node_id.into_node_key();
68        self.composer.nodes[node_key].children[child_index].into_node_id()
69    }
70}
71
72impl<T> TraverseTree for TaffyTreeView<'_, T>
73where
74    T: TaffyNode,
75    T::Context: TaffyConfig,
76{
77}
78
79impl<T> PrintTree for TaffyTreeView<'_, T>
80where
81    T: TaffyNode,
82    T::Context: TaffyConfig,
83{
84    #[inline(always)]
85    fn get_debug_label(&self, node_id: NodeId) -> &'static str {
86        let node = self.composer.nodes[node_id.into_node_key()]
87            .data
88            .as_ref()
89            .unwrap();
90        let display = node.get_display();
91        let num_children = self.child_count(node_id);
92        match (num_children, display) {
93            (_, Display::None) => "NONE",
94            (0, _) => "LEAF",
95            #[cfg(feature = "block_layout")]
96            (_, Display::Block) => "BLOCK",
97            #[cfg(feature = "flexbox")]
98            (_, Display::Flex) => match node.get_flexbox_container_style().flex_direction() {
99                FlexDirection::Row | FlexDirection::RowReverse => "FLEX ROW",
100                FlexDirection::Column | FlexDirection::ColumnReverse => "FLEX COL",
101            },
102            #[cfg(feature = "grid")]
103            (_, Display::Grid) => "GRID",
104        }
105    }
106
107    #[inline(always)]
108    fn get_final_layout(&self, node_id: NodeId) -> &Layout {
109        if self.composer.context.use_rounding() {
110            self.composer.nodes[node_id.into_node_key()]
111                .data
112                .as_ref()
113                .unwrap()
114                .get_final_layout()
115        } else {
116            self.composer.nodes[node_id.into_node_key()]
117                .data
118                .as_ref()
119                .unwrap()
120                .get_unrounded_layout()
121        }
122    }
123}
124
125pub struct TaffyTree<'a, T, M>
126where
127    T: TaffyNode,
128    T::Context: TaffyConfig,
129    M: FnMut(
130        Size<Option<f32>>,
131        Size<AvailableSpace>,
132        NodeId,
133        Option<&mut T::NodeContext>,
134        &T::CoreContainerStyle,
135    ) -> Size<f32>,
136{
137    pub composer: &'a mut Composer<T>,
138    pub measure_function: M,
139}
140
141impl<'a, T, M> TaffyTree<'a, T, M>
142where
143    T: TaffyNode,
144    T::Context: TaffyConfig,
145    M: FnMut(
146        Size<Option<f32>>,
147        Size<AvailableSpace>,
148        NodeId,
149        Option<&mut T::NodeContext>,
150        &T::CoreContainerStyle,
151    ) -> Size<f32>,
152{
153    pub fn new(composer: &'a mut Composer<T>, measure_function: M) -> Self {
154        Self {
155            composer,
156            measure_function,
157        }
158    }
159}
160
161impl<T, M> TraversePartialTree for TaffyTree<'_, T, M>
162where
163    T: TaffyNode,
164    T::Context: TaffyConfig,
165    M: FnMut(
166        Size<Option<f32>>,
167        Size<AvailableSpace>,
168        NodeId,
169        Option<&mut T::NodeContext>,
170        &T::CoreContainerStyle,
171    ) -> Size<f32>,
172{
173    type ChildIter<'a>
174        = TaffyTreeChildIter<'a>
175    where
176        Self: 'a;
177
178    #[inline(always)]
179    fn child_ids(&self, node_id: NodeId) -> Self::ChildIter<'_> {
180        let node_key = node_id.into_node_key();
181        TaffyTreeChildIter(self.composer.nodes[node_key].children.iter())
182    }
183
184    #[inline(always)]
185    fn child_count(&self, node_id: NodeId) -> usize {
186        let node_key = node_id.into_node_key();
187        self.composer.nodes[node_key].children.len()
188    }
189
190    #[inline(always)]
191    fn get_child_id(&self, node_id: NodeId, child_index: usize) -> NodeId {
192        let node_key = node_id.into_node_key();
193        self.composer.nodes[node_key].children[child_index].into_node_id()
194    }
195}
196
197impl<T, M> TraverseTree for TaffyTree<'_, T, M>
198where
199    T: TaffyNode,
200    T::Context: TaffyConfig,
201    M: FnMut(
202        Size<Option<f32>>,
203        Size<AvailableSpace>,
204        NodeId,
205        Option<&mut T::NodeContext>,
206        &T::CoreContainerStyle,
207    ) -> Size<f32>,
208{
209}
210
211impl<T, M> CacheTree for TaffyTree<'_, T, M>
212where
213    T: TaffyNode,
214    T::Context: TaffyConfig,
215    M: FnMut(
216        Size<Option<f32>>,
217        Size<AvailableSpace>,
218        NodeId,
219        Option<&mut T::NodeContext>,
220        &T::CoreContainerStyle,
221    ) -> Size<f32>,
222{
223    #[inline(always)]
224    fn cache_get(
225        &self,
226        node_id: NodeId,
227        known_dimensions: taffy::Size<Option<f32>>,
228        available_space: taffy::Size<taffy::AvailableSpace>,
229        run_mode: taffy::RunMode,
230    ) -> Option<taffy::LayoutOutput> {
231        self.composer.nodes[node_id.into_node_key()]
232            .data
233            .as_ref()
234            .unwrap()
235            .cache_get(known_dimensions, available_space, run_mode)
236    }
237
238    #[inline(always)]
239    fn cache_store(
240        &mut self,
241        node_id: NodeId,
242        known_dimensions: taffy::Size<Option<f32>>,
243        available_space: taffy::Size<taffy::AvailableSpace>,
244        run_mode: taffy::RunMode,
245        layout_output: taffy::LayoutOutput,
246    ) {
247        self.composer
248            .nodes
249            .get_mut(node_id.into_node_key())
250            .unwrap()
251            .data
252            .as_mut()
253            .unwrap()
254            .cache_store(known_dimensions, available_space, run_mode, layout_output)
255    }
256
257    #[inline(always)]
258    fn cache_clear(&mut self, node_id: NodeId) {
259        self.composer
260            .nodes
261            .get_mut(node_id.into_node_key())
262            .unwrap()
263            .data
264            .as_mut()
265            .unwrap()
266            .cache_clear();
267    }
268}
269
270impl<T, M> PrintTree for TaffyTree<'_, T, M>
271where
272    T: TaffyNode,
273    T::Context: TaffyConfig,
274    M: FnMut(
275        Size<Option<f32>>,
276        Size<AvailableSpace>,
277        NodeId,
278        Option<&mut T::NodeContext>,
279        &T::CoreContainerStyle,
280    ) -> Size<f32>,
281{
282    #[inline(always)]
283    fn get_debug_label(&self, node_id: NodeId) -> &'static str {
284        let node = self.composer.nodes[node_id.into_node_key()]
285            .data
286            .as_ref()
287            .unwrap();
288        let display = node.get_display();
289        let num_children = self.child_count(node_id);
290
291        match (num_children, display) {
292            (_, Display::None) => "NONE",
293            (0, _) => "LEAF",
294            #[cfg(feature = "block_layout")]
295            (_, Display::Block) => "BLOCK",
296            #[cfg(feature = "flexbox")]
297            (_, Display::Flex) => match node.get_flexbox_container_style().flex_direction() {
298                FlexDirection::Row | FlexDirection::RowReverse => "FLEX ROW",
299                FlexDirection::Column | FlexDirection::ColumnReverse => "FLEX COL",
300            },
301            #[cfg(feature = "grid")]
302            (_, Display::Grid) => "GRID",
303        }
304    }
305
306    #[inline(always)]
307    fn get_final_layout(&self, node_id: NodeId) -> &Layout {
308        if self.composer.context.use_rounding() {
309            self.composer.nodes[node_id.into_node_key()]
310                .data
311                .as_ref()
312                .unwrap()
313                .get_final_layout()
314        } else {
315            self.composer.nodes[node_id.into_node_key()]
316                .data
317                .as_ref()
318                .unwrap()
319                .get_unrounded_layout()
320        }
321    }
322}
323
324impl<T, M> LayoutPartialTree for TaffyTree<'_, T, M>
325where
326    T: TaffyNode,
327    T::Context: TaffyConfig,
328    M: FnMut(
329        Size<Option<f32>>,
330        Size<AvailableSpace>,
331        NodeId,
332        Option<&mut T::NodeContext>,
333        &T::CoreContainerStyle,
334    ) -> Size<f32>,
335{
336    type CoreContainerStyle<'a>
337        = &'a T::CoreContainerStyle
338    where
339        Self: 'a;
340
341    #[inline(always)]
342    fn get_core_container_style(&self, node_id: NodeId) -> Self::CoreContainerStyle<'_> {
343        self.composer.nodes[node_id.into_node_key()]
344            .data
345            .as_ref()
346            .unwrap()
347            .get_core_container_style()
348    }
349
350    #[inline(always)]
351    fn set_unrounded_layout(&mut self, node_id: NodeId, layout: &Layout) {
352        self.composer
353            .nodes
354            .get_mut(node_id.into_node_key())
355            .unwrap()
356            .data
357            .as_mut()
358            .unwrap()
359            .set_unrounded_layout(layout);
360    }
361
362    #[inline(always)]
363    fn compute_child_layout(
364        &mut self,
365        node_id: NodeId,
366        inputs: taffy::LayoutInput,
367    ) -> taffy::LayoutOutput {
368        // If RunMode is PerformHiddenLayout then this indicates that an ancestor node is `Display::None`
369        // and thus that we should lay out this node using hidden layout regardless of it's own display style.
370        if inputs.run_mode == RunMode::PerformHiddenLayout {
371            return compute_hidden_layout(self, node_id);
372        }
373
374        // We run the following wrapped in "compute_cached_layout", which will check the cache for an entry matching the node and inputs and:
375        //   - Return that entry if exists
376        //   - Else call the passed closure (below) to compute the result
377        //
378        // If there was no cache match and a new result needs to be computed then that result will be added to the cache
379        compute_cached_layout(self, node_id, inputs, |tree, node_id, inputs| {
380            let node_key = node_id.into_node_key();
381            let display_mode = tree.composer.nodes[node_key]
382                .data
383                .as_ref()
384                .unwrap()
385                .get_display();
386            let has_children = tree.child_count(node_id) > 0;
387
388            // Dispatch to a layout algorithm based on the node's display style and whether the node has children or not.
389            match (display_mode, has_children) {
390                (Display::None, _) => compute_hidden_layout(tree, node_id),
391                #[cfg(feature = "block_layout")]
392                (Display::Block, true) => compute_block_layout(tree, node_id, inputs),
393                #[cfg(feature = "flexbox")]
394                (Display::Flex, true) => compute_flexbox_layout(tree, node_id, inputs),
395                #[cfg(feature = "grid")]
396                (Display::Grid, true) => compute_grid_layout(tree, node_id, inputs),
397                (_, false) => {
398                    let data = tree
399                        .composer
400                        .nodes
401                        .get_mut(node_key)
402                        .unwrap()
403                        .data
404                        .as_mut()
405                        .unwrap();
406                    let (node_context, style) = data.get_node_context_mut_with_core_style();
407                    let measure_function = |known_dimensions, available_space| {
408                        (tree.measure_function)(
409                            known_dimensions,
410                            available_space,
411                            node_id,
412                            node_context,
413                            style,
414                        )
415                    };
416                    compute_leaf_layout(inputs, style, measure_function)
417                }
418            }
419        })
420    }
421}
422
423#[cfg(feature = "block_layout")]
424impl<T, M> LayoutBlockContainer for TaffyTree<'_, T, M>
425where
426    T: TaffyNode,
427    T::Context: TaffyConfig,
428    M: FnMut(
429        Size<Option<f32>>,
430        Size<AvailableSpace>,
431        NodeId,
432        Option<&mut T::NodeContext>,
433        &T::CoreContainerStyle,
434    ) -> Size<f32>,
435{
436    type BlockContainerStyle<'a>
437        = T::BlockContainerStyle<'a>
438    where
439        Self: 'a;
440
441    type BlockItemStyle<'a>
442        = T::BlockItemStyle<'a>
443    where
444        Self: 'a;
445
446    #[inline(always)]
447    fn get_block_container_style(&self, node_id: NodeId) -> Self::BlockContainerStyle<'_> {
448        self.composer.nodes[node_id.into_node_key()]
449            .data
450            .as_ref()
451            .unwrap()
452            .get_block_container_style()
453    }
454
455    #[inline(always)]
456    fn get_block_child_style(&self, node_id: NodeId) -> Self::BlockItemStyle<'_> {
457        self.composer.nodes[node_id.into_node_key()]
458            .data
459            .as_ref()
460            .unwrap()
461            .get_block_item_style()
462    }
463}
464
465#[cfg(feature = "flexbox")]
466impl<T, M> LayoutFlexboxContainer for TaffyTree<'_, T, M>
467where
468    T: TaffyNode,
469    T::Context: TaffyConfig,
470    M: FnMut(
471        Size<Option<f32>>,
472        Size<AvailableSpace>,
473        NodeId,
474        Option<&mut T::NodeContext>,
475        &T::CoreContainerStyle,
476    ) -> Size<f32>,
477{
478    type FlexboxContainerStyle<'a>
479        = T::FlexboxContainerStyle<'a>
480    where
481        Self: 'a;
482
483    type FlexboxItemStyle<'a>
484        = T::FlexboxItemStyle<'a>
485    where
486        Self: 'a;
487
488    #[inline(always)]
489    fn get_flexbox_container_style(&self, node_id: NodeId) -> Self::FlexboxContainerStyle<'_> {
490        self.composer.nodes[node_id.into_node_key()]
491            .data
492            .as_ref()
493            .unwrap()
494            .get_flexbox_container_style()
495    }
496
497    #[inline(always)]
498    fn get_flexbox_child_style(&self, node_id: NodeId) -> Self::FlexboxItemStyle<'_> {
499        self.composer.nodes[node_id.into_node_key()]
500            .data
501            .as_ref()
502            .unwrap()
503            .get_flexbox_item_style()
504    }
505}
506
507#[cfg(feature = "grid")]
508impl<T, M> LayoutGridContainer for TaffyTree<'_, T, M>
509where
510    T: TaffyNode,
511    T::Context: TaffyConfig,
512    M: FnMut(
513        Size<Option<f32>>,
514        Size<AvailableSpace>,
515        NodeId,
516        Option<&mut T::NodeContext>,
517        &T::CoreContainerStyle,
518    ) -> Size<f32>,
519{
520    type GridContainerStyle<'a>
521        = T::GridContainerStyle<'a>
522    where
523        Self: 'a;
524
525    type GridItemStyle<'a>
526        = T::GridItemStyle<'a>
527    where
528        Self: 'a;
529
530    #[inline(always)]
531    fn get_grid_container_style(&self, node_id: NodeId) -> Self::GridContainerStyle<'_> {
532        self.composer.nodes[node_id.into_node_key()]
533            .data
534            .as_ref()
535            .unwrap()
536            .get_grid_container_style()
537    }
538
539    #[inline(always)]
540    fn get_grid_child_style(&self, node_id: NodeId) -> Self::GridItemStyle<'_> {
541        self.composer.nodes[node_id.into_node_key()]
542            .data
543            .as_ref()
544            .unwrap()
545            .get_grid_item_style()
546    }
547}
548
549impl<T, M> RoundTree for TaffyTree<'_, T, M>
550where
551    T: TaffyNode,
552    T::Context: TaffyConfig,
553    M: FnMut(
554        Size<Option<f32>>,
555        Size<AvailableSpace>,
556        NodeId,
557        Option<&mut T::NodeContext>,
558        &T::CoreContainerStyle,
559    ) -> Size<f32>,
560{
561    #[inline(always)]
562    fn get_unrounded_layout(&self, node_id: NodeId) -> &Layout {
563        self.composer.nodes[node_id.into_node_key()]
564            .data
565            .as_ref()
566            .unwrap()
567            .get_unrounded_layout()
568    }
569
570    #[inline(always)]
571    fn set_final_layout(&mut self, node_id: NodeId, layout: &Layout) {
572        self.composer
573            .nodes
574            .get_mut(node_id.into_node_key())
575            .unwrap()
576            .data
577            .as_mut()
578            .unwrap()
579            .set_final_layout(layout);
580    }
581}