1use crate::geometry::{Point, Size};
4use crate::style::{AvailableSpace, Display, Overflow, Position, Style};
5use crate::tree::{CollapsibleMarginSet, RunMode};
6use crate::tree::{LayoutInput, LayoutOutput, SizingMode};
7use crate::util::debug::debug_log;
8use crate::util::sys::f32_max;
9use crate::util::MaybeMath;
10use crate::util::{MaybeResolve, ResolveOrZero};
11use core::unreachable;
12
13pub fn compute_leaf_layout<MeasureFunction>(
15 inputs: LayoutInput,
16 style: &Style,
17 measure_function: Option<MeasureFunction>,
18) -> LayoutOutput
19where
20 MeasureFunction: FnOnce(Size<Option<f32>>, Size<AvailableSpace>) -> Size<f32>,
21{
22 let LayoutInput { known_dimensions, parent_size, available_space, sizing_mode, run_mode, .. } = inputs;
23
24 let (node_size, node_min_size, node_max_size, aspect_ratio) = match sizing_mode {
27 SizingMode::ContentSize => {
28 let node_size = known_dimensions;
29 let node_min_size = Size::NONE;
30 let node_max_size = Size::NONE;
31 (node_size, node_min_size, node_max_size, None)
32 }
33 SizingMode::InherentSize => {
34 let aspect_ratio = style.aspect_ratio;
35 let style_size = style.size.maybe_resolve(parent_size).maybe_apply_aspect_ratio(aspect_ratio);
36 let style_min_size = style.min_size.maybe_resolve(parent_size).maybe_apply_aspect_ratio(aspect_ratio);
37 let style_max_size = style.max_size.maybe_resolve(parent_size);
38
39 let node_size = known_dimensions.or(style_size);
40 (node_size, style_min_size, style_max_size, aspect_ratio)
41 }
42 };
43
44 let margin = style.margin.resolve_or_zero(parent_size.width);
47 let padding = style.padding.resolve_or_zero(parent_size.width);
48 let border = style.border.resolve_or_zero(parent_size.width);
49 let padding_border = padding + border;
50
51 let scrollbar_gutter = style.overflow.transpose().map(|overflow| match overflow {
55 Overflow::Scroll => style.scrollbar_width,
56 _ => 0.0,
57 });
58 let mut content_box_inset = padding_border;
60 content_box_inset.right += scrollbar_gutter.x;
61 content_box_inset.bottom += scrollbar_gutter.y;
62
63 #[cfg(feature = "block_layout")]
64 let is_block = style.display == Display::Block;
65 #[cfg(not(feature = "block_layout"))]
66 let is_block = false;
67
68 let has_styles_preventing_being_collapsed_through = !is_block
69 || style.overflow.x.is_scroll_container()
70 || style.overflow.y.is_scroll_container()
71 || style.position == Position::Absolute
72 || padding.top > 0.0
73 || padding.bottom > 0.0
74 || border.top > 0.0
75 || border.bottom > 0.0;
76
77 debug_log!("LEAF");
78 debug_log!("node_size", dbg:node_size);
79 debug_log!("min_size ", dbg:node_min_size);
80 debug_log!("max_size ", dbg:node_max_size);
81
82 if run_mode == RunMode::ComputeSize {
84 if let Size { width: Some(width), height: Some(height) } = node_size {
85 let size = Size { width, height }
86 .maybe_clamp(node_min_size, node_max_size)
87 .maybe_max(padding_border.sum_axes().map(Some));
88 return LayoutOutput {
89 size,
90 #[cfg(feature = "content_size")]
91 content_size: Size::ZERO,
92 first_baselines: Point::NONE,
93 top_margin: CollapsibleMarginSet::ZERO,
94 bottom_margin: CollapsibleMarginSet::ZERO,
95 margins_can_collapse_through: !has_styles_preventing_being_collapsed_through
96 && size.height == 0.0
97 && measure_function.is_none(),
98 };
99 };
100 }
101
102 if let Some(measure_function) = measure_function {
103 let available_space = Size {
105 width: known_dimensions
106 .width
107 .map(AvailableSpace::from)
108 .unwrap_or(available_space.width)
109 .maybe_sub(margin.horizontal_axis_sum())
110 .maybe_set(known_dimensions.width)
111 .maybe_set(node_size.width)
112 .maybe_set(node_max_size.width)
113 .map_definite_value(|size| {
114 size.maybe_clamp(node_min_size.width, node_max_size.width) - content_box_inset.horizontal_axis_sum()
115 }),
116 height: known_dimensions
117 .height
118 .map(AvailableSpace::from)
119 .unwrap_or(available_space.height)
120 .maybe_sub(margin.vertical_axis_sum())
121 .maybe_set(known_dimensions.height)
122 .maybe_set(node_size.height)
123 .maybe_set(node_max_size.height)
124 .map_definite_value(|size| {
125 size.maybe_clamp(node_min_size.height, node_max_size.height) - content_box_inset.vertical_axis_sum()
126 }),
127 };
128
129 let measured_size = measure_function(
131 match run_mode {
132 RunMode::ComputeSize => known_dimensions,
133 RunMode::PerformLayout => Size::NONE,
134 RunMode::PerformHiddenLayout => unreachable!(),
135 },
136 available_space,
137 );
138 let clamped_size = known_dimensions
139 .or(node_size)
140 .unwrap_or(measured_size + content_box_inset.sum_axes())
141 .maybe_clamp(node_min_size, node_max_size);
142 let size = Size {
143 width: clamped_size.width,
144 height: f32_max(clamped_size.height, aspect_ratio.map(|ratio| clamped_size.width / ratio).unwrap_or(0.0)),
145 };
146 let size = size.maybe_max(padding_border.sum_axes().map(Some));
147
148 return LayoutOutput {
149 size,
150 #[cfg(feature = "content_size")]
151 content_size: measured_size + padding.sum_axes(),
152 first_baselines: Point::NONE,
153 top_margin: CollapsibleMarginSet::ZERO,
154 bottom_margin: CollapsibleMarginSet::ZERO,
155 margins_can_collapse_through: !has_styles_preventing_being_collapsed_through
156 && size.height == 0.0
157 && measured_size.height == 0.0,
158 };
159 }
160
161 let size = Size {
162 width: node_size
163 .width
164 .unwrap_or(content_box_inset.horizontal_axis_sum()) .maybe_clamp(node_min_size.width, node_max_size.width)
167 .maybe_max(padding_border.horizontal_axis_sum().into()),
168 height: node_size
169 .height
170 .unwrap_or(content_box_inset.vertical_axis_sum()) .maybe_clamp(node_min_size.height, node_max_size.height)
173 .maybe_max(padding_border.vertical_axis_sum().into()),
174 };
175
176 let size = Size {
177 width: f32_max(size.width, aspect_ratio.map(|ratio| size.height * ratio).unwrap_or(0.0)),
178 height: f32_max(size.height, aspect_ratio.map(|ratio| size.width / ratio).unwrap_or(0.0)),
179 };
180
181 LayoutOutput {
182 size,
183 #[cfg(feature = "content_size")]
184 content_size: padding.sum_axes(),
185 first_baselines: Point::NONE,
186 top_margin: CollapsibleMarginSet::ZERO,
187 bottom_margin: CollapsibleMarginSet::ZERO,
188 margins_can_collapse_through: !has_styles_preventing_being_collapsed_through && size.height == 0.0,
189 }
190}