gioser

tawasuyu/taffy

A high performance rust-powered UI layout library

HTML 5364Rust 86Markdown 12TOML 91841 Definiciones

⎇ fda1f88edd2bb6de8a7eb2611be94fc81485726d src/compute/taffy_tree.rs Rust · 212 líneas · 7.4 KB Historia Quién tocó qué crudo

Add `taffy_tree` feature to opt-out of default Taffy tree (#448) fda1f88ed · Martijn Arts ·

1//! Computation specific for the default `Taffy` tree implementation
2
3use crate::{
4    compute::{
5        leaf::{measure_size, perform_layout},
6        HiddenAlgorithm,
7    },
8    geometry::Point,
9    prelude::{Layout, NodeId, Size},
10    style::{AvailableSpace, Display},
11    tree::{RunMode, SizeAndBaselines, SizingMode},
12    util::sys::round,
13    CssGridAlgorithm, FlexboxAlgorithm, LayoutAlgorithm, LayoutTree, Taffy, TaffyError,
14};
15
16/// Updates the stored layout of the provided `node` and its children
17pub(crate) fn compute_layout(
18    taffy: &mut Taffy,
19    root: NodeId,
20    available_space: Size<AvailableSpace>,
21) -> Result<(), TaffyError> {
22    // Recursively compute node layout
23    let size_and_baselines = perform_node_layout(
24        taffy,
25        root,
26        Size::NONE,
27        available_space.into_options(),
28        available_space,
29        SizingMode::InherentSize,
30    );
31
32    let layout = Layout { order: 0, size: size_and_baselines.size, location: Point::ZERO };
33    *taffy.layout_mut(root) = layout;
34
35    // If rounding is enabled, recursively round the layout's of this node and all children
36    if taffy.config.use_rounding {
37        round_layout(taffy, root, 0.0, 0.0);
38    }
39
40    Ok(())
41}
42
43/// Perform full layout on a node. Chooses which algorithm to use based on the `display` property.
44pub(crate) fn perform_node_layout(
45    tree: &mut Taffy,
46    node: NodeId,
47    known_dimensions: Size<Option<f32>>,
48    parent_size: Size<Option<f32>>,
49    available_space: Size<AvailableSpace>,
50    sizing_mode: SizingMode,
51) -> SizeAndBaselines {
52    compute_node_layout(tree, node, known_dimensions, parent_size, available_space, RunMode::PeformLayout, sizing_mode)
53}
54
55/// Measure a node's size. Chooses which algorithm to use based on the `display` property.
56pub(crate) fn measure_node_size(
57    tree: &mut Taffy,
58    node: NodeId,
59    known_dimensions: Size<Option<f32>>,
60    parent_size: Size<Option<f32>>,
61    available_space: Size<AvailableSpace>,
62    sizing_mode: SizingMode,
63) -> Size<f32> {
64    compute_node_layout(tree, node, known_dimensions, parent_size, available_space, RunMode::ComputeSize, sizing_mode)
65        .size
66}
67
68/// Updates the stored layout of the provided `node` and its children
69fn compute_node_layout(
70    tree: &mut Taffy,
71    node: NodeId,
72    known_dimensions: Size<Option<f32>>,
73    parent_size: Size<Option<f32>>,
74    available_space: Size<AvailableSpace>,
75    run_mode: RunMode,
76    sizing_mode: SizingMode,
77) -> SizeAndBaselines {
78    #[cfg(any(feature = "debug", feature = "profile"))]
79    NODE_LOGGER.push_node(node);
80    #[cfg(feature = "debug")]
81    println!();
82
83    let node_key = node.into();
84    let has_children = !tree.children[node_key].is_empty();
85
86    // First we check if we have a cached result for the given input
87    let cache_run_mode = if !has_children { RunMode::PeformLayout } else { run_mode };
88    if let Some(cached_size_and_baselines) =
89        tree.nodes[node_key].cache.get(known_dimensions, available_space, cache_run_mode)
90    {
91        #[cfg(feature = "debug")]
92        NODE_LOGGER.labelled_debug_log("CACHE", cached_size_and_baselines.size);
93        #[cfg(feature = "debug")]
94        debug_log_node(known_dimensions, parent_size, available_space, run_mode, sizing_mode);
95        #[cfg(any(feature = "debug", feature = "profile"))]
96        NODE_LOGGER.pop_node();
97        return cached_size_and_baselines;
98    }
99
100    #[cfg(feature = "debug")]
101    debug_log_node(known_dimensions, parent_size, available_space, run_mode, sizing_mode);
102
103    /// Inlined function generic over the LayoutAlgorithm to reduce code duplication
104    #[inline(always)]
105    fn perform_computations<Algorithm: LayoutAlgorithm>(
106        tree: &mut impl LayoutTree,
107        node: NodeId,
108        known_dimensions: Size<Option<f32>>,
109        parent_size: Size<Option<f32>>,
110        available_space: Size<AvailableSpace>,
111        run_mode: RunMode,
112        sizing_mode: SizingMode,
113    ) -> SizeAndBaselines {
114        #[cfg(feature = "debug")]
115        NODE_LOGGER.log(Algorithm::NAME);
116
117        match run_mode {
118            RunMode::PeformLayout => {
119                Algorithm::perform_layout(tree, node, known_dimensions, parent_size, available_space, sizing_mode)
120            }
121            RunMode::ComputeSize => {
122                Algorithm::measure_size(tree, node, known_dimensions, parent_size, available_space, sizing_mode).into()
123            }
124        }
125    }
126
127    let display_mode = tree.nodes[node_key].style.display;
128    let computed_size_and_baselines = match (display_mode, has_children) {
129        (Display::None, _) => perform_computations::<HiddenAlgorithm>(
130            tree,
131            node,
132            known_dimensions,
133            parent_size,
134            available_space,
135            run_mode,
136            sizing_mode,
137        ),
138        #[cfg(feature = "flexbox")]
139        (Display::Flex, true) => perform_computations::<FlexboxAlgorithm>(
140            tree,
141            node,
142            known_dimensions,
143            parent_size,
144            available_space,
145            run_mode,
146            sizing_mode,
147        ),
148        #[cfg(feature = "grid")]
149        (Display::Grid, true) => perform_computations::<CssGridAlgorithm>(
150            tree,
151            node,
152            known_dimensions,
153            parent_size,
154            available_space,
155            run_mode,
156            sizing_mode,
157        ),
158        (_, false) => match run_mode {
159            RunMode::PeformLayout => perform_layout(
160                &tree.nodes[node_key].style,
161                tree.nodes[node_key].needs_measure.then(|| &tree.measure_funcs[node_key]),
162                known_dimensions,
163                parent_size,
164                available_space,
165                sizing_mode,
166            ),
167            RunMode::ComputeSize => measure_size(
168                &tree.nodes[node_key].style,
169                tree.nodes[node_key].needs_measure.then(|| &tree.measure_funcs[node_key]),
170                known_dimensions,
171                parent_size,
172                available_space,
173                sizing_mode,
174            )
175            .into(),
176        },
177    };
178
179    // Cache result
180    tree.nodes[node_key].cache.store(known_dimensions, available_space, cache_run_mode, computed_size_and_baselines);
181
182    #[cfg(feature = "debug")]
183    NODE_LOGGER.labelled_debug_log("RESULT", computed_size_and_baselines.size);
184    #[cfg(any(feature = "debug", feature = "profile"))]
185    NODE_LOGGER.pop_node();
186
187    computed_size_and_baselines
188}
189
190/// Rounds the calculated [`Layout`] to exact pixel values
191/// In order to ensure that no gaps in the layout are introduced we:
192///   - Always round based on the absolute coordinates rather than parent-relative coordinates
193///   - Compute width/height by first rounding the top/bottom/left/right and then computing the difference
194///     rather than rounding the width/height directly
195///
196/// See <https://github.com/facebook/yoga/commit/aa5b296ac78f7a22e1aeaf4891243c6bb76488e2> for more context
197fn round_layout(tree: &mut impl LayoutTree, node: NodeId, abs_x: f32, abs_y: f32) {
198    let layout = tree.layout_mut(node);
199    let abs_x = abs_x + layout.location.x;
200    let abs_y = abs_y + layout.location.y;
201
202    layout.location.x = round(layout.location.x);
203    layout.location.y = round(layout.location.y);
204    layout.size.width = round(abs_x + layout.size.width) - round(abs_x);
205    layout.size.height = round(abs_y + layout.size.height) - round(abs_y);
206
207    let child_count = tree.child_count(node);
208    for index in 0..child_count {
209        let child = tree.child(node, index);
210        round_layout(tree, child, abs_x, abs_y);
211    }
212}