1pub(crate) mod common;
25pub(crate) mod leaf;
26
27#[cfg(feature = "block_layout")]
28pub(crate) mod block;
29
30#[cfg(feature = "flexbox")]
31pub(crate) mod flexbox;
32
33#[cfg(feature = "grid")]
34pub(crate) mod grid;
35
36pub use leaf::compute_leaf_layout;
37
38#[cfg(feature = "block_layout")]
39pub use self::block::compute_block_layout;
40
41#[cfg(feature = "flexbox")]
42pub use self::flexbox::compute_flexbox_layout;
43
44#[cfg(feature = "grid")]
45pub use self::grid::compute_grid_layout;
46
47use crate::geometry::{Line, Point, Size};
48use crate::style::{AvailableSpace, CoreStyle, Overflow};
49use crate::tree::{
50 Layout, LayoutInput, LayoutOutput, LayoutPartialTree, LayoutPartialTreeExt, NodeId, RoundTree, SizingMode,
51};
52use crate::util::debug::{debug_log, debug_log_node, debug_pop_node, debug_push_node};
53use crate::util::sys::round;
54use crate::util::ResolveOrZero;
55use crate::{BoxSizing, CacheTree, MaybeMath, MaybeResolve};
56
57pub fn compute_root_layout(tree: &mut impl LayoutPartialTree, root: NodeId, available_space: Size<AvailableSpace>) {
59 let mut known_dimensions = Size::NONE;
60
61 #[cfg(feature = "block_layout")]
62 {
63 let parent_size = available_space.into_options();
64 let style = tree.get_core_container_style(root);
65
66 if style.is_block() {
67 let aspect_ratio = style.aspect_ratio();
69 let margin = style.margin().resolve_or_zero(parent_size.width);
70 let padding = style.padding().resolve_or_zero(parent_size.width);
71 let border = style.border().resolve_or_zero(parent_size.width);
72 let padding_border_size = (padding + border).sum_axes();
73 let box_sizing_adjustment =
74 if style.box_sizing() == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO };
75
76 let min_size = style
77 .min_size()
78 .maybe_resolve(parent_size)
79 .maybe_apply_aspect_ratio(aspect_ratio)
80 .maybe_add(box_sizing_adjustment);
81 let max_size = style
82 .max_size()
83 .maybe_resolve(parent_size)
84 .maybe_apply_aspect_ratio(aspect_ratio)
85 .maybe_add(box_sizing_adjustment);
86 let clamped_style_size = style
87 .size()
88 .maybe_resolve(parent_size)
89 .maybe_apply_aspect_ratio(aspect_ratio)
90 .maybe_add(box_sizing_adjustment)
91 .maybe_clamp(min_size, max_size);
92
93 let min_max_definite_size = min_size.zip_map(max_size, |min, max| match (min, max) {
95 (Some(min), Some(max)) if max <= min => Some(min),
96 _ => None,
97 });
98
99 let available_space_based_size = Size {
101 width: available_space.width.into_option().maybe_sub(margin.horizontal_axis_sum()),
102 height: None,
103 };
104
105 let styled_based_known_dimensions = known_dimensions
106 .or(min_max_definite_size)
107 .or(clamped_style_size)
108 .or(available_space_based_size)
109 .maybe_max(padding_border_size);
110
111 known_dimensions = styled_based_known_dimensions;
112 }
113 }
114
115 let output = tree.perform_child_layout(
117 root,
118 known_dimensions,
119 available_space.into_options(),
120 available_space,
121 SizingMode::InherentSize,
122 Line::FALSE,
123 );
124
125 let style = tree.get_core_container_style(root);
126 let padding = style.padding().resolve_or_zero(available_space.width.into_option());
127 let border = style.border().resolve_or_zero(available_space.width.into_option());
128 let margin = style.margin().resolve_or_zero(available_space.width.into_option());
129 let scrollbar_size = Size {
130 width: if style.overflow().y == Overflow::Scroll { style.scrollbar_width() } else { 0.0 },
131 height: if style.overflow().x == Overflow::Scroll { style.scrollbar_width() } else { 0.0 },
132 };
133 drop(style);
134
135 tree.set_unrounded_layout(
136 root,
137 &Layout {
138 order: 0,
139 location: Point::ZERO,
140 size: output.size,
141 #[cfg(feature = "content_size")]
142 content_size: output.content_size,
143 scrollbar_size,
144 padding,
145 border,
146 margin,
148 },
149 );
150}
151
152#[inline(always)]
156pub fn compute_cached_layout<Tree: CacheTree + ?Sized, ComputeFunction>(
157 tree: &mut Tree,
158 node: NodeId,
159 inputs: LayoutInput,
160 mut compute_uncached: ComputeFunction,
161) -> LayoutOutput
162where
163 ComputeFunction: FnMut(&mut Tree, NodeId, LayoutInput) -> LayoutOutput,
164{
165 debug_push_node!(node);
166 let LayoutInput { known_dimensions, available_space, run_mode, .. } = inputs;
167
168 let cache_entry = tree.cache_get(node, known_dimensions, available_space, run_mode);
170 if let Some(cached_size_and_baselines) = cache_entry {
171 debug_log_node!(known_dimensions, inputs.parent_size, available_space, run_mode, inputs.sizing_mode);
172 debug_log!("RESULT (CACHED)", dbg:cached_size_and_baselines.size);
173 debug_pop_node!();
174 return cached_size_and_baselines;
175 }
176
177 debug_log_node!(known_dimensions, inputs.parent_size, available_space, run_mode, inputs.sizing_mode);
178
179 let computed_size_and_baselines = compute_uncached(tree, node, inputs);
180
181 tree.cache_store(node, known_dimensions, available_space, run_mode, computed_size_and_baselines);
183
184 debug_log!("RESULT", dbg:computed_size_and_baselines.size);
185 debug_pop_node!();
186
187 computed_size_and_baselines
188}
189
190pub fn round_layout(tree: &mut impl RoundTree, node_id: NodeId) {
203 return round_layout_inner(tree, node_id, 0.0, 0.0);
204
205 fn round_layout_inner(tree: &mut impl RoundTree, node_id: NodeId, cumulative_x: f32, cumulative_y: f32) {
207 let unrounded_layout = *tree.get_unrounded_layout(node_id);
208 let mut layout = unrounded_layout;
209
210 let cumulative_x = cumulative_x + unrounded_layout.location.x;
211 let cumulative_y = cumulative_y + unrounded_layout.location.y;
212
213 layout.location.x = round(unrounded_layout.location.x);
214 layout.location.y = round(unrounded_layout.location.y);
215 layout.size.width = round(cumulative_x + unrounded_layout.size.width) - round(cumulative_x);
216 layout.size.height = round(cumulative_y + unrounded_layout.size.height) - round(cumulative_y);
217 layout.scrollbar_size.width = round(unrounded_layout.scrollbar_size.width);
218 layout.scrollbar_size.height = round(unrounded_layout.scrollbar_size.height);
219 layout.border.left = round(cumulative_x + unrounded_layout.border.left) - round(cumulative_x);
220 layout.border.right = round(cumulative_x + unrounded_layout.size.width)
221 - round(cumulative_x + unrounded_layout.size.width - unrounded_layout.border.right);
222 layout.border.top = round(cumulative_y + unrounded_layout.border.top) - round(cumulative_y);
223 layout.border.bottom = round(cumulative_y + unrounded_layout.size.height)
224 - round(cumulative_y + unrounded_layout.size.height - unrounded_layout.border.bottom);
225 layout.padding.left = round(cumulative_x + unrounded_layout.padding.left) - round(cumulative_x);
226 layout.padding.right = round(cumulative_x + unrounded_layout.size.width)
227 - round(cumulative_x + unrounded_layout.size.width - unrounded_layout.padding.right);
228 layout.padding.top = round(cumulative_y + unrounded_layout.padding.top) - round(cumulative_y);
229 layout.padding.bottom = round(cumulative_y + unrounded_layout.size.height)
230 - round(cumulative_y + unrounded_layout.size.height - unrounded_layout.padding.bottom);
231
232 #[cfg(feature = "content_size")]
233 round_content_size(&mut layout, unrounded_layout.content_size, cumulative_x, cumulative_y);
234
235 tree.set_final_layout(node_id, &layout);
236
237 let child_count = tree.child_count(node_id);
238 for index in 0..child_count {
239 let child = tree.get_child_id(node_id, index);
240 round_layout_inner(tree, child, cumulative_x, cumulative_y);
241 }
242 }
243
244 #[cfg(feature = "content_size")]
245 #[inline(always)]
246 fn round_content_size(
249 layout: &mut Layout,
250 unrounded_content_size: Size<f32>,
251 cumulative_x: f32,
252 cumulative_y: f32,
253 ) {
254 layout.content_size.width = round(cumulative_x + unrounded_content_size.width) - round(cumulative_x);
255 layout.content_size.height = round(cumulative_y + unrounded_content_size.height) - round(cumulative_y);
256 }
257}
258
259pub fn compute_hidden_layout(tree: &mut (impl LayoutPartialTree + CacheTree), node: NodeId) -> LayoutOutput {
262 tree.cache_clear(node);
264 tree.set_unrounded_layout(node, &Layout::with_order(0));
265
266 for index in 0..tree.child_count(node) {
268 let child_id = tree.get_child_id(node, index);
269 tree.compute_child_layout(child_id, LayoutInput::HIDDEN);
270 }
271
272 LayoutOutput::HIDDEN
273}
274
275#[cfg(feature = "detailed_layout_info")]
277pub mod detailed_info {
278 #[cfg(feature = "grid")]
279 pub use super::grid::DetailedGridInfo;
280}
281
282#[cfg(test)]
283mod tests {
284 use super::compute_hidden_layout;
285 use crate::geometry::{Point, Size};
286 use crate::style::{Display, Style};
287 use crate::TaffyTree;
288
289 #[test]
290 fn hidden_layout_should_hide_recursively() {
291 let mut taffy: TaffyTree<()> = TaffyTree::new();
292
293 let style: Style = Style { display: Display::Flex, size: Size::from_lengths(50.0, 50.0), ..Default::default() };
294
295 let grandchild_00 = taffy.new_leaf(style.clone()).unwrap();
296 let grandchild_01 = taffy.new_leaf(style.clone()).unwrap();
297 let child_00 = taffy.new_with_children(style.clone(), &[grandchild_00, grandchild_01]).unwrap();
298
299 let grandchild_02 = taffy.new_leaf(style.clone()).unwrap();
300 let child_01 = taffy.new_with_children(style.clone(), &[grandchild_02]).unwrap();
301
302 let root = taffy
303 .new_with_children(
304 Style { display: Display::None, size: Size::from_lengths(50.0, 50.0), ..Default::default() },
305 &[child_00, child_01],
306 )
307 .unwrap();
308
309 compute_hidden_layout(&mut taffy.as_layout_tree(), root);
310
311 for node in [root, child_00, child_01, grandchild_00, grandchild_01, grandchild_02] {
315 let layout = taffy.layout(node).unwrap();
316 assert_eq!(layout.size, Size::zero());
317 assert_eq!(layout.location, Point::zero());
318 }
319 }
320}