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