gioser

tawasuyu/taffy

A high performance rust-powered UI layout library

HTML 227Rust 7TOML 3CSS 1307 Definitions

⎇ 0.2.x src/compute/mod.rs Rust · 203 lines · 8.5 KB History Who touched what raw

Change signature of mark_dirty method to match taffy 0.1 (#253) dfaec22a1 · Nico Burns ·

1//! The layout algorithms themselves
2
3pub(crate) mod flexbox;
4pub(crate) mod leaf;
5
6use crate::error::TaffyError;
7use crate::geometry::{Point, Size};
8use crate::layout::{AvailableSpace, Cache, Layout, RunMode, SizingMode};
9use crate::node::Node;
10use crate::style::Display;
11use crate::sys::round;
12use crate::tree::LayoutTree;
13
14#[cfg(feature = "debug")]
15use crate::debug::NODE_LOGGER;
16
17/// Updates the stored layout of the provided `node` and its children
18pub fn compute_layout(
19    tree: &mut impl LayoutTree,
20    root: Node,
21    available_space: Size<AvailableSpace>,
22) -> Result<(), TaffyError> {
23    // Recursively compute node layout
24    let size =
25        compute_node_layout(tree, root, Size::NONE, available_space, RunMode::PeformLayout, SizingMode::InherentSize);
26
27    let layout = Layout { order: 0, size, location: Point::ZERO };
28    *tree.layout_mut(root) = layout;
29
30    // Recursively round the layout's of this node and all children
31    round_layout(tree, root, 0.0, 0.0);
32
33    Ok(())
34}
35
36/// Updates the stored layout of the provided `node` and its children
37fn compute_node_layout(
38    tree: &mut impl LayoutTree,
39    node: Node,
40    known_dimensions: Size<Option<f32>>,
41    available_space: Size<AvailableSpace>,
42    run_mode: RunMode,
43    sizing_mode: SizingMode,
44) -> Size<f32> {
45    #[cfg(feature = "debug")]
46    NODE_LOGGER.push_node(node);
47    #[cfg(feature = "debug")]
48    println!();
49
50    // First we check if we have a cached result for the given input
51    let cache_run_mode = if tree.children(node).is_empty() { RunMode::PeformLayout } else { run_mode };
52    if let Some(cached_size) =
53        compute_from_cache(tree, node, known_dimensions, available_space, cache_run_mode, sizing_mode)
54    {
55        #[cfg(feature = "debug")]
56        NODE_LOGGER.labelled_debug_log("CACHE", cached_size);
57        #[cfg(feature = "debug")]
58        NODE_LOGGER.labelled_debug_log("run_mode", run_mode);
59        #[cfg(feature = "debug")]
60        NODE_LOGGER.labelled_debug_log("sizing_mode", sizing_mode);
61        #[cfg(feature = "debug")]
62        NODE_LOGGER.labelled_debug_log("known_dimensions", known_dimensions);
63        #[cfg(feature = "debug")]
64        NODE_LOGGER.labelled_debug_log("available_space", available_space);
65        #[cfg(feature = "debug")]
66        NODE_LOGGER.pop_node();
67        return cached_size;
68    }
69
70    #[cfg(feature = "debug")]
71    NODE_LOGGER.log("COMPUTE");
72    #[cfg(feature = "debug")]
73    NODE_LOGGER.labelled_debug_log("run_mode", run_mode);
74    #[cfg(feature = "debug")]
75    NODE_LOGGER.labelled_debug_log("sizing_mode", sizing_mode);
76    #[cfg(feature = "debug")]
77    NODE_LOGGER.labelled_debug_log("known_dimensions", known_dimensions);
78    #[cfg(feature = "debug")]
79    NODE_LOGGER.labelled_debug_log("available_space", available_space);
80
81    // Attempt to shortcut size computation based on
82    //  - KnownSize sizing constraints
83    //  - The node's preferred sizes (width/heights) styles and AvailableSpace sizing constraints
84    // (percentages resolve to pixel values if there is a definite AvailableSpace sizing constraint)
85    // let style = tree.style(node);
86    // // let known_node_size = available_space.
87    // //     .zip_map(style.size, |constraint, style_size| style_size.maybe_resolve(constraint.as_option()));
88    // let known_node_size = style
89    //     .size
90    //     .maybe_resolve(available_space.as_options())
91    //     .zip_map(known_dimensions, |style_size, known_dimensions| known_dimensions.or(style_size));
92    // if run_mode == RunMode::ComputeSize && known_node_size.width.is_some() && known_node_size.height.is_some() {
93    //     let node_min_size = style.min_size.maybe_resolve(available_space.as_options());
94    //     let node_max_size = style.max_size.maybe_resolve(available_space.as_options());
95    //     return Size {
96    //         width: known_node_size.width.maybe_max(node_min_size.width).maybe_min(node_max_size.width).unwrap_or(0.0),
97    //         height: known_node_size
98    //             .height
99    //             .maybe_max(node_min_size.height)
100    //             .maybe_min(node_max_size.height)
101    //             .unwrap_or(0.0),
102    //     };
103    // }
104
105    // If this is a leaf node we can skip a lot of this function in some cases
106    let computed_size = if tree.children(node).is_empty() {
107        #[cfg(feature = "debug")]
108        NODE_LOGGER.log("Algo: leaf");
109        self::leaf::compute(tree, node, known_dimensions, available_space, run_mode, sizing_mode)
110    } else {
111        // println!("match {:?}", tree.style(node).display);
112        match tree.style(node).display {
113            Display::Flex => {
114                #[cfg(feature = "debug")]
115                NODE_LOGGER.log("Algo: flexbox");
116                self::flexbox::compute(tree, node, known_dimensions, available_space, run_mode)
117            }
118            Display::None => {
119                #[cfg(feature = "debug")]
120                NODE_LOGGER.log("Algo: none");
121                Size { width: 0.0, height: 0.0 }
122            }
123        }
124    };
125
126    // Cache result
127    let cache_slot = (known_dimensions.width.is_some() as usize) + (known_dimensions.height.is_some() as usize * 2);
128    *tree.cache_mut(node, cache_slot) =
129        Some(Cache { known_dimensions, available_space, run_mode: cache_run_mode, cached_size: computed_size });
130
131    #[cfg(feature = "debug")]
132    NODE_LOGGER.labelled_debug_log("RESULT", computed_size);
133    #[cfg(feature = "debug")]
134    NODE_LOGGER.pop_node();
135
136    computed_size
137}
138
139/// Try to get the computation result from the cache.
140#[inline]
141fn compute_from_cache(
142    tree: &mut impl LayoutTree,
143    node: Node,
144    known_dimensions: Size<Option<f32>>,
145    available_space: Size<AvailableSpace>,
146    run_mode: RunMode,
147    sizing_mode: SizingMode,
148) -> Option<Size<f32>> {
149    for idx in 0..4 {
150        let entry = tree.cache_mut(node, idx);
151        #[cfg(feature = "debug")]
152        NODE_LOGGER.labelled_debug_log("cache_entry", &entry);
153        if let Some(entry) = entry {
154            // Cached ComputeSize results are not valid if we are running in PerformLayout mode
155            if entry.run_mode == RunMode::ComputeSize && run_mode == RunMode::PeformLayout {
156                return None;
157            }
158
159            // if known_dimensions.width == entry.known_dimensions.width
160            // && known_dimensions.height == entry.known_dimensions.height
161            if (known_dimensions.width == entry.known_dimensions.width || known_dimensions.width == Some(entry.cached_size.width))
162                && (known_dimensions.height == entry.known_dimensions.height || known_dimensions.height == Some(entry.cached_size.height))
163                // && entry.available_space.width.is_roughly_equal(available_space.width)
164                // && entry.available_space.height.is_roughly_equal(available_space.height)
165                && (
166                  known_dimensions.width.is_some()
167                  || entry.available_space.width.is_roughly_equal(available_space.width)
168                  || (sizing_mode == SizingMode::ContentSize && available_space.width.is_definite() && available_space.width.unwrap() >= entry.cached_size.width)
169                )
170                && (
171                  known_dimensions.height.is_some()
172                  || entry.available_space.height.is_roughly_equal(available_space.height)
173                  || (sizing_mode == SizingMode::ContentSize && available_space.height.is_definite() && available_space.height.unwrap() >= entry.cached_size.height)
174                )
175            // && (entry.available_space.width.is_roughly_equal(available_space.width) || (available_space.width.is_definite() && available_space.width.unwrap() >= entry.cached_size.width))
176            // && (entry.available_space.height.is_roughly_equal(available_space.height) || (available_space.height.is_definite() && available_space.height.unwrap() >= entry.cached_size.height))
177            {
178                return Some(entry.cached_size);
179            }
180        }
181    }
182
183    None
184}
185
186/// Rounds the calculated [`NodeData`] according to the spec
187fn round_layout(tree: &mut impl LayoutTree, root: Node, abs_x: f32, abs_y: f32) {
188    let layout = tree.layout_mut(root);
189    let abs_x = abs_x + layout.location.x;
190    let abs_y = abs_y + layout.location.y;
191
192    layout.location.x = round(layout.location.x);
193    layout.location.y = round(layout.location.y);
194
195    layout.size.width = round(layout.size.width);
196    layout.size.height = round(layout.size.height);
197
198    // Satisfy the borrow checker here by re-indexing to shorten the lifetime to the loop scope
199    for x in 0..tree.children(root).len() {
200        let child = tree.child(root, x);
201        round_layout(tree, child, abs_x, abs_y);
202    }
203}