1#[cfg(feature = "content_size")]
4use crate::geometry::Rect;
5use crate::geometry::Size;
6use crate::style::{AvailableSpace, Overflow, Position};
7use crate::tree::{Baselines, CollapsibleMarginSet, RunMode};
8use crate::tree::{LayoutInput, LayoutOutput, SizingMode};
9use crate::util::debug::debug_log;
10use crate::util::sys::f32_max;
11use crate::util::MaybeMath;
12use crate::util::{MaybeResolve, ResolveOrZero};
13use crate::{BoxSizing, CoreStyle};
14use core::unreachable;
15
16pub fn compute_leaf_layout<MeasureFunction>(
18 inputs: LayoutInput,
19 style: &impl CoreStyle,
20 resolve_calc_value: impl Fn(*const (), f32) -> f32,
21 measure_function: MeasureFunction,
22) -> LayoutOutput
23where
24 MeasureFunction: FnOnce(Size<Option<f32>>, Size<AvailableSpace>) -> Size<f32>,
25{
26 let LayoutInput { known_dimensions, parent_size, available_space, sizing_mode, run_mode, .. } = inputs;
27
28 let margin = style.margin().resolve_or_zero(parent_size.width, &resolve_calc_value);
31 let padding = style.padding().resolve_or_zero(parent_size.width, &resolve_calc_value);
32 let border = style.border().resolve_or_zero(parent_size.width, &resolve_calc_value);
33 let padding_border = padding + border;
34 let pb_sum = padding_border.sum_axes();
35 let box_sizing_adjustment = if style.box_sizing() == BoxSizing::ContentBox { pb_sum } else { Size::ZERO };
36
37 let (node_size, node_min_size, node_max_size, aspect_ratio) = match sizing_mode {
40 SizingMode::ContentSize => {
41 let node_size = known_dimensions;
42 let node_min_size = Size::NONE;
43 let node_max_size = Size::NONE;
44 (node_size, node_min_size, node_max_size, None)
45 }
46 SizingMode::InherentSize => {
47 let aspect_ratio = style.aspect_ratio();
48 let style_size = style
49 .size()
50 .maybe_resolve(parent_size, &resolve_calc_value)
51 .maybe_apply_aspect_ratio(aspect_ratio)
52 .maybe_add(box_sizing_adjustment);
53 let style_min_size = style
54 .min_size()
55 .maybe_resolve(parent_size, &resolve_calc_value)
56 .maybe_apply_aspect_ratio(aspect_ratio)
57 .maybe_add(box_sizing_adjustment);
58 let style_max_size =
59 style.max_size().maybe_resolve(parent_size, &resolve_calc_value).maybe_add(box_sizing_adjustment);
60
61 let node_size = known_dimensions.or(style_size);
62 (node_size, style_min_size, style_max_size, aspect_ratio)
63 }
64 };
65
66 let scrollbar_gutter = style.overflow().transpose().map(|overflow| match overflow {
70 Overflow::Scroll => style.scrollbar_width(),
71 _ => 0.0,
72 });
73 let mut content_box_inset = padding_border;
75 content_box_inset.right += scrollbar_gutter.x;
76 content_box_inset.bottom += scrollbar_gutter.y;
77
78 let has_styles_preventing_being_collapsed_through = !style.is_block()
79 || style.overflow().x.is_scroll_container()
80 || style.overflow().y.is_scroll_container()
81 || style.position() == Position::Absolute
82 || style.contain().establishes_independent_formatting_context()
83 || padding.top > 0.0
84 || padding.bottom > 0.0
85 || border.top > 0.0
86 || border.bottom > 0.0
87 || matches!(node_size.height, Some(h) if h > 0.0)
88 || matches!(node_min_size.height, Some(h) if h > 0.0);
89
90 debug_log!("LEAF");
91 debug_log!("node_size", dbg:node_size);
92 debug_log!("min_size ", dbg:node_min_size);
93 debug_log!("max_size ", dbg:node_max_size);
94
95 if run_mode == RunMode::ComputeSize && has_styles_preventing_being_collapsed_through {
97 if let Size { width: Some(width), height: Some(height) } = node_size {
98 let size = Size { width, height }
99 .maybe_clamp(node_min_size, node_max_size)
100 .maybe_max(padding_border.sum_axes().map(Some));
101 return LayoutOutput {
102 size,
103 #[cfg(feature = "content_size")]
104 scrollable_overflow_rect: Rect::ZERO,
105 baselines: Baselines::NONE,
106 top_margin: CollapsibleMarginSet::ZERO,
107 bottom_margin: CollapsibleMarginSet::ZERO,
108 margins_can_collapse_through: false,
109 };
110 };
111 }
112
113 let available_space = Size {
115 width: known_dimensions
116 .width
117 .map(AvailableSpace::from)
118 .unwrap_or(available_space.width)
119 .maybe_sub(margin.horizontal_axis_sum())
120 .maybe_set(known_dimensions.width)
121 .maybe_set(node_size.width)
122 .map_definite_value(|size| {
123 size.maybe_clamp(node_min_size.width, node_max_size.width) - content_box_inset.horizontal_axis_sum()
124 }),
125 height: known_dimensions
126 .height
127 .map(AvailableSpace::from)
128 .unwrap_or(available_space.height)
129 .maybe_sub(margin.vertical_axis_sum())
130 .maybe_set(known_dimensions.height)
131 .maybe_set(node_size.height)
132 .map_definite_value(|size| {
133 size.maybe_clamp(node_min_size.height, node_max_size.height) - content_box_inset.vertical_axis_sum()
134 }),
135 };
136
137 let measured_size = measure_function(
139 match run_mode {
140 RunMode::ComputeSize => known_dimensions,
141 RunMode::PerformLayout => Size::NONE,
142 RunMode::PerformHiddenLayout => unreachable!(),
143 },
144 available_space,
145 );
146 let clamped_size = known_dimensions
147 .or(node_size)
148 .unwrap_or(measured_size + content_box_inset.sum_axes())
149 .maybe_clamp(node_min_size, node_max_size);
150 let size = Size {
151 width: clamped_size.width,
152 height: f32_max(clamped_size.height, aspect_ratio.map(|ratio| clamped_size.width / ratio).unwrap_or(0.0)),
153 };
154 let size = size.maybe_max(padding_border.sum_axes().map(Some));
155
156 #[cfg(feature = "content_size")]
160 let scrollable_overflow_rect = {
161 let is_scroll_container = style.overflow().x.is_scroll_container() || style.overflow().y.is_scroll_container();
162 let is_rtl = style.direction().is_rtl();
163 let start_padding = if is_rtl { padding.right } else { padding.left };
164 let end_padding = if is_rtl { padding.left } else { padding.right };
165 Rect {
166 left: 0.0,
167 right: start_padding + measured_size.width + if is_scroll_container { end_padding } else { 0.0 },
168 top: 0.0,
169 bottom: padding.top + measured_size.height + if is_scroll_container { padding.bottom } else { 0.0 },
170 }
171 };
172
173 LayoutOutput {
174 size,
175 #[cfg(feature = "content_size")]
176 scrollable_overflow_rect,
177 baselines: Baselines::NONE,
178 top_margin: CollapsibleMarginSet::ZERO,
179 bottom_margin: CollapsibleMarginSet::ZERO,
180 margins_can_collapse_through: !has_styles_preventing_being_collapsed_through
181 && size.height == 0.0
182 && measured_size.height == 0.0,
183 }
184}