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::{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 use crate::BoxSizing;
64
65 let parent_size = available_space.into_options();
66 let style = tree.get_core_container_style(root);
67
68 if style.is_block() {
69 let aspect_ratio = style.aspect_ratio();
71 let margin = style.margin().resolve_or_zero(parent_size.width, |val, basis| tree.calc(val, basis));
72 let padding = style.padding().resolve_or_zero(parent_size.width, |val, basis| tree.calc(val, basis));
73 let border = style.border().resolve_or_zero(parent_size.width, |val, basis| tree.calc(val, basis));
74 let padding_border_size = (padding + border).sum_axes();
75 let box_sizing_adjustment =
76 if style.box_sizing() == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO };
77
78 let min_size = style
79 .min_size()
80 .maybe_resolve(parent_size, |val, basis| tree.calc(val, basis))
81 .maybe_apply_aspect_ratio(aspect_ratio)
82 .maybe_add(box_sizing_adjustment);
83 let max_size = style
84 .max_size()
85 .maybe_resolve(parent_size, |val, basis| tree.calc(val, basis))
86 .maybe_apply_aspect_ratio(aspect_ratio)
87 .maybe_add(box_sizing_adjustment);
88 let clamped_style_size = style
89 .size()
90 .maybe_resolve(parent_size, |val, basis| tree.calc(val, basis))
91 .maybe_apply_aspect_ratio(aspect_ratio)
92 .maybe_add(box_sizing_adjustment)
93 .maybe_clamp(min_size, max_size);
94
95 let min_max_definite_size = min_size.zip_map(max_size, |min, max| match (min, max) {
97 (Some(min), Some(max)) if max <= min => Some(min),
98 _ => None,
99 });
100
101 let available_space_based_size = Size {
103 width: available_space.width.into_option().maybe_sub(margin.horizontal_axis_sum()),
104 height: None,
105 };
106
107 let styled_based_known_dimensions = known_dimensions
108 .or(min_max_definite_size)
109 .or(clamped_style_size)
110 .or(available_space_based_size)
111 .maybe_max(padding_border_size);
112
113 known_dimensions = styled_based_known_dimensions;
114 }
115 }
116
117 let output = tree.perform_child_layout(
119 root,
120 known_dimensions,
121 available_space.into_options(),
122 available_space,
123 SizingMode::InherentSize,
124 Line::FALSE,
125 );
126
127 let style = tree.get_core_container_style(root);
128 let padding =
129 style.padding().resolve_or_zero(available_space.width.into_option(), |val, basis| tree.calc(val, basis));
130 let border =
131 style.border().resolve_or_zero(available_space.width.into_option(), |val, basis| tree.calc(val, basis));
132 let margin =
133 style.margin().resolve_or_zero(available_space.width.into_option(), |val, basis| tree.calc(val, basis));
134 let scrollbar_size = Size {
135 width: if style.overflow().y == Overflow::Scroll { style.scrollbar_width() } else { 0.0 },
136 height: if style.overflow().x == Overflow::Scroll { style.scrollbar_width() } else { 0.0 },
137 };
138 drop(style);
139
140 tree.set_unrounded_layout(
141 root,
142 &Layout {
143 order: 0,
144 location: Point::ZERO,
145 size: output.size,
146 #[cfg(feature = "content_size")]
147 content_size: output.content_size,
148 scrollbar_size,
149 padding,
150 border,
151 margin,
153 },
154 );
155}
156
157#[inline(always)]
161pub fn compute_cached_layout<Tree: CacheTree + ?Sized, ComputeFunction>(
162 tree: &mut Tree,
163 node: NodeId,
164 inputs: LayoutInput,
165 mut compute_uncached: ComputeFunction,
166) -> LayoutOutput
167where
168 ComputeFunction: FnMut(&mut Tree, NodeId, LayoutInput) -> LayoutOutput,
169{
170 debug_push_node!(node);
171 let LayoutInput { known_dimensions, available_space, run_mode, .. } = inputs;
172
173 let cache_entry = tree.cache_get(node, known_dimensions, available_space, run_mode);
175 if let Some(cached_size_and_baselines) = cache_entry {
176 debug_log_node!(known_dimensions, inputs.parent_size, available_space, run_mode, inputs.sizing_mode);
177 debug_log!("RESULT (CACHED)", dbg:cached_size_and_baselines.size);
178 debug_pop_node!();
179 return cached_size_and_baselines;
180 }
181
182 debug_log_node!(known_dimensions, inputs.parent_size, available_space, run_mode, inputs.sizing_mode);
183
184 let computed_size_and_baselines = compute_uncached(tree, node, inputs);
185
186 tree.cache_store(node, known_dimensions, available_space, run_mode, computed_size_and_baselines);
188
189 debug_log!("RESULT", dbg:computed_size_and_baselines.size);
190 debug_pop_node!();
191
192 computed_size_and_baselines
193}
194
195pub fn round_layout(tree: &mut impl RoundTree, node_id: NodeId) {
208 return round_layout_inner(tree, node_id, 0.0, 0.0);
209
210 fn round_layout_inner(tree: &mut impl RoundTree, node_id: NodeId, cumulative_x: f32, cumulative_y: f32) {
212 let unrounded_layout = tree.get_unrounded_layout(node_id);
213 let mut layout = unrounded_layout;
214
215 let cumulative_x = cumulative_x + unrounded_layout.location.x;
216 let cumulative_y = cumulative_y + unrounded_layout.location.y;
217
218 layout.location.x = round(unrounded_layout.location.x);
219 layout.location.y = round(unrounded_layout.location.y);
220 layout.size.width = round(cumulative_x + unrounded_layout.size.width) - round(cumulative_x);
221 layout.size.height = round(cumulative_y + unrounded_layout.size.height) - round(cumulative_y);
222 layout.scrollbar_size.width = round(unrounded_layout.scrollbar_size.width);
223 layout.scrollbar_size.height = round(unrounded_layout.scrollbar_size.height);
224 layout.border.left = round(cumulative_x + unrounded_layout.border.left) - round(cumulative_x);
225 layout.border.right = round(cumulative_x + unrounded_layout.size.width)
226 - round(cumulative_x + unrounded_layout.size.width - unrounded_layout.border.right);
227 layout.border.top = round(cumulative_y + unrounded_layout.border.top) - round(cumulative_y);
228 layout.border.bottom = round(cumulative_y + unrounded_layout.size.height)
229 - round(cumulative_y + unrounded_layout.size.height - unrounded_layout.border.bottom);
230 layout.padding.left = round(cumulative_x + unrounded_layout.padding.left) - round(cumulative_x);
231 layout.padding.right = round(cumulative_x + unrounded_layout.size.width)
232 - round(cumulative_x + unrounded_layout.size.width - unrounded_layout.padding.right);
233 layout.padding.top = round(cumulative_y + unrounded_layout.padding.top) - round(cumulative_y);
234 layout.padding.bottom = round(cumulative_y + unrounded_layout.size.height)
235 - round(cumulative_y + unrounded_layout.size.height - unrounded_layout.padding.bottom);
236
237 #[cfg(feature = "content_size")]
238 round_content_size(&mut layout, unrounded_layout.content_size, cumulative_x, cumulative_y);
239
240 tree.set_final_layout(node_id, &layout);
241
242 let child_count = tree.child_count(node_id);
243 for index in 0..child_count {
244 let child = tree.get_child_id(node_id, index);
245 round_layout_inner(tree, child, cumulative_x, cumulative_y);
246 }
247 }
248
249 #[cfg(feature = "content_size")]
250 #[inline(always)]
251 fn round_content_size(
254 layout: &mut Layout,
255 unrounded_content_size: Size<f32>,
256 cumulative_x: f32,
257 cumulative_y: f32,
258 ) {
259 layout.content_size.width = round(cumulative_x + unrounded_content_size.width) - round(cumulative_x);
260 layout.content_size.height = round(cumulative_y + unrounded_content_size.height) - round(cumulative_y);
261 }
262}
263
264pub fn compute_hidden_layout(tree: &mut (impl LayoutPartialTree + CacheTree), node: NodeId) -> LayoutOutput {
267 tree.cache_clear(node);
269 tree.set_unrounded_layout(node, &Layout::with_order(0));
270
271 for index in 0..tree.child_count(node) {
273 let child_id = tree.get_child_id(node, index);
274 tree.compute_child_layout(child_id, LayoutInput::HIDDEN);
275 }
276
277 LayoutOutput::HIDDEN
278}
279
280#[cfg(feature = "detailed_layout_info")]
282pub mod detailed_info {
283 #[cfg(feature = "grid")]
284 pub use super::grid::{DetailedGridInfo, DetailedGridTracksInfo};
285}
286
287#[cfg(test)]
288mod tests {
289 use super::compute_hidden_layout;
290 use crate::geometry::{Point, Size};
291 use crate::style::{Display, Style};
292 use crate::TaffyTree;
293
294 #[test]
295 fn hidden_layout_should_hide_recursively() {
296 let mut taffy: TaffyTree<()> = TaffyTree::new();
297
298 let style: Style = Style { display: Display::Flex, size: Size::from_lengths(50.0, 50.0), ..Default::default() };
299
300 let grandchild_00 = taffy.new_leaf(style.clone()).unwrap();
301 let grandchild_01 = taffy.new_leaf(style.clone()).unwrap();
302 let child_00 = taffy.new_with_children(style.clone(), &[grandchild_00, grandchild_01]).unwrap();
303
304 let grandchild_02 = taffy.new_leaf(style.clone()).unwrap();
305 let child_01 = taffy.new_with_children(style.clone(), &[grandchild_02]).unwrap();
306
307 let root = taffy
308 .new_with_children(
309 Style { display: Display::None, size: Size::from_lengths(50.0, 50.0), ..Default::default() },
310 &[child_00, child_01],
311 )
312 .unwrap();
313
314 compute_hidden_layout(&mut taffy.as_layout_tree(), root);
315
316 for node in [root, child_00, child_01, grandchild_00, grandchild_01, grandchild_02] {
320 let layout = taffy.layout(node).unwrap();
321 assert_eq!(layout.size, Size::zero());
322 assert_eq!(layout.location, Point::zero());
323 }
324 }
325}