Change signature of mark_dirty method to match taffy 0.1 (#253)
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}