⎇ d099aaf21adefee3abe5c617d162eb13d31d3a59
src/tree/layout.rs
Ñawpa willay
Pi imata llamkarqan
ch'uya
Add DEFAULT constant to LayoutOutput
1//! Final data structures that represent the high-level UI layout
2use crate::geometry::{AbsoluteAxis, Line, Point, Rect, Size};
3use crate::style::AvailableSpace;
4use crate::style_helpers::TaffyMaxContent;
5use crate::util::sys::{f32_max, f32_min};
6
7/// Whether we are performing a full layout, or we merely need to size the node
8#[derive(Copy, Clone, Debug, PartialEq, Eq)]
9pub enum RunMode {
10 /// A full layout for this node and all children should be computed
11 PerformLayout,
12 /// The layout algorithm should be executed such that an accurate container size for the node can be determined.
13 /// Layout steps that aren't necessary for determining the container size of the current node can be skipped.
14 ComputeSize,
15 /// This node should have a null layout set as it has been hidden (i.e. using `Display::None`)
16 PerformHiddenLayout,
17}
18
19/// Whether styles should be taken into account when computing size
20#[derive(Copy, Clone, Debug, PartialEq, Eq)]
21pub enum SizingMode {
22 /// Only content contributions should be taken into account
23 ContentSize,
24 /// Inherent size styles should be taken into account in addition to content contributions
25 InherentSize,
26}
27
28/// A set of margins that are available for collapsing with for block layout's margin collapsing
29#[derive(Copy, Clone, Debug, PartialEq)]
30pub struct CollapsibleMarginSet {
31 /// The largest positive margin
32 positive: f32,
33 /// The smallest negative margin (with largest absolute value)
34 negative: f32,
35}
36
37impl CollapsibleMarginSet {
38 /// A default margin set with no collapsible margins
39 pub const ZERO: Self = Self { positive: 0.0, negative: 0.0 };
40
41 /// Create a set from a single margin
42 pub fn from_margin(margin: f32) -> Self {
43 if margin >= 0.0 {
44 Self { positive: margin, negative: 0.0 }
45 } else {
46 Self { positive: 0.0, negative: margin }
47 }
48 }
49
50 /// Collapse a single margin with this set
51 pub fn collapse_with_margin(mut self, margin: f32) -> Self {
52 if margin >= 0.0 {
53 self.positive = f32_max(self.positive, margin);
54 } else {
55 self.negative = f32_min(self.negative, margin);
56 }
57 self
58 }
59
60 /// Collapse another margin set with this set
61 pub fn collapse_with_set(mut self, other: CollapsibleMarginSet) -> Self {
62 self.positive = f32_max(self.positive, other.positive);
63 self.negative = f32_min(self.negative, other.negative);
64 self
65 }
66
67 /// Resolve the resultant margin from this set once all collapsible margins
68 /// have been collapsed into it
69 pub fn resolve(&self) -> f32 {
70 self.positive + self.negative
71 }
72}
73
74/// An axis that layout algorithms can be requested to compute a size for
75#[derive(Debug, Copy, Clone)]
76pub enum RequestedAxis {
77 /// The horizontal axis
78 Horizontal,
79 /// The vertical axis
80 Vertical,
81 /// Both axes
82 Both,
83}
84
85impl From<AbsoluteAxis> for RequestedAxis {
86 fn from(value: AbsoluteAxis) -> Self {
87 match value {
88 AbsoluteAxis::Horizontal => RequestedAxis::Horizontal,
89 AbsoluteAxis::Vertical => RequestedAxis::Vertical,
90 }
91 }
92}
93impl TryFrom<RequestedAxis> for AbsoluteAxis {
94 type Error = ();
95 fn try_from(value: RequestedAxis) -> Result<Self, Self::Error> {
96 match value {
97 RequestedAxis::Horizontal => Ok(AbsoluteAxis::Horizontal),
98 RequestedAxis::Vertical => Ok(AbsoluteAxis::Vertical),
99 RequestedAxis::Both => Err(()),
100 }
101 }
102}
103
104/// A struct containing the inputs constraints/hints for laying out a node, which are passed in by the parent
105#[derive(Debug, Copy, Clone)]
106pub struct LayoutInput {
107 /// Whether we only need to know the Node's size, or whe
108 pub run_mode: RunMode,
109 /// Whether a Node's style sizes should be taken into account or ignored
110 pub sizing_mode: SizingMode,
111 /// Which axis we need the size of
112 pub axis: RequestedAxis,
113
114 /// Known dimensions represent dimensions (width/height) which should be taken as fixed when performing layout.
115 /// For example, if known_dimensions.width is set to Some(WIDTH) then this means something like:
116 ///
117 /// "What would the height of this node be, assuming the width is WIDTH"
118 ///
119 /// Layout functions will be called with both known_dimensions set for final layout. Where the meaning is:
120 ///
121 /// "The exact size of this node is WIDTHxHEIGHT. Please lay out your children"
122 ///
123 pub known_dimensions: Size<Option<f32>>,
124 /// Parent size dimensions are intended to be used for percentage resolution.
125 pub parent_size: Size<Option<f32>>,
126 /// Available space represents an amount of space to layout into, and is used as a soft constraint
127 /// for the purpose of wrapping.
128 pub available_space: Size<AvailableSpace>,
129 /// Specific to CSS Block layout. Used for correctly computing margin collapsing. You probably want to set this to `Line::FALSE`.
130 pub vertical_margins_are_collapsible: Line<bool>,
131}
132
133impl LayoutInput {
134 /// A LayoutInput that can be used to request hidden layout
135 pub const HIDDEN: LayoutInput = LayoutInput {
136 // The important property for hidden layout
137 run_mode: RunMode::PerformHiddenLayout,
138 // The rest will be ignored
139 known_dimensions: Size::NONE,
140 parent_size: Size::NONE,
141 available_space: Size::MAX_CONTENT,
142 sizing_mode: SizingMode::InherentSize,
143 axis: RequestedAxis::Both,
144 vertical_margins_are_collapsible: Line::FALSE,
145 };
146}
147
148/// A struct containing the result of laying a single node, which is returned up to the parent node
149///
150/// A baseline is the line on which text sits. Your node likely has a baseline if it is a text node, or contains
151/// children that may be text nodes. See <https://www.w3.org/TR/css-writing-modes-3/#intro-baselines> for details.
152/// If your node does not have a baseline (or you are unsure how to compute it), then simply return `Point::NONE`
153/// for the first_baselines field
154#[derive(Debug, Copy, Clone)]
155pub struct LayoutOutput {
156 /// The size of the node
157 pub size: Size<f32>,
158 #[cfg(feature = "content_size")]
159 /// The size of the content within the node
160 pub content_size: Size<f32>,
161 /// The first baseline of the node in each dimension, if any
162 pub first_baselines: Point<Option<f32>>,
163 /// Top margin that can be collapsed with. This is used for CSS block layout and can be set to
164 /// `CollapsibleMarginSet::ZERO` for other layout modes that don't support margin collapsing
165 pub top_margin: CollapsibleMarginSet,
166 /// Bottom margin that can be collapsed with. This is used for CSS block layout and can be set to
167 /// `CollapsibleMarginSet::ZERO` for other layout modes that don't support margin collapsing
168 pub bottom_margin: CollapsibleMarginSet,
169 /// Whether margins can be collapsed through this node. This is used for CSS block layout and can
170 /// be set to `false` for other layout modes that don't support margin collapsing
171 pub margins_can_collapse_through: bool,
172}
173
174impl LayoutOutput {
175 /// An all-zero `LayoutOutput` for hidden nodes
176 pub const HIDDEN: Self = Self {
177 size: Size::ZERO,
178 #[cfg(feature = "content_size")]
179 content_size: Size::ZERO,
180 first_baselines: Point::NONE,
181 top_margin: CollapsibleMarginSet::ZERO,
182 bottom_margin: CollapsibleMarginSet::ZERO,
183 margins_can_collapse_through: false,
184 };
185
186 /// A blank layout output
187 pub const DEFAULT: Self = Self::HIDDEN;
188
189 /// Constructor to create a `LayoutOutput` from just the size and baselines
190 pub fn from_sizes_and_baselines(
191 size: Size<f32>,
192 #[cfg_attr(not(feature = "content_size"), allow(unused_variables))] content_size: Size<f32>,
193 first_baselines: Point<Option<f32>>,
194 ) -> Self {
195 Self {
196 size,
197 #[cfg(feature = "content_size")]
198 content_size,
199 first_baselines,
200 top_margin: CollapsibleMarginSet::ZERO,
201 bottom_margin: CollapsibleMarginSet::ZERO,
202 margins_can_collapse_through: false,
203 }
204 }
205
206 /// Construct a SizeBaselinesAndMargins from just the container and content sizes
207 pub fn from_sizes(size: Size<f32>, content_size: Size<f32>) -> Self {
208 Self::from_sizes_and_baselines(size, content_size, Point::NONE)
209 }
210
211 /// Construct a SizeBaselinesAndMargins from just the container's size.
212 pub fn from_outer_size(size: Size<f32>) -> Self {
213 Self::from_sizes(size, Size::zero())
214 }
215}
216
217/// The final result of a layout algorithm for a single node.
218#[derive(Debug, Copy, Clone)]
219pub struct Layout {
220 /// The relative ordering of the node
221 ///
222 /// Nodes with a higher order should be rendered on top of those with a lower order.
223 /// This is effectively a topological sort of each tree.
224 pub order: u32,
225 /// The top-left corner of the node
226 pub location: Point<f32>,
227 /// The width and height of the node
228 pub size: Size<f32>,
229 #[cfg(feature = "content_size")]
230 /// The width and height of the content inside the node. This may be larger than the size of the node in the case of
231 /// overflowing content and is useful for computing a "scroll width/height" for scrollable nodes
232 pub content_size: Size<f32>,
233 /// The size of the scrollbars in each dimension. If there is no scrollbar then the size will be zero.
234 pub scrollbar_size: Size<f32>,
235 /// The size of the borders of the node
236 pub border: Rect<f32>,
237 /// The size of the padding of the node
238 pub padding: Rect<f32>,
239}
240
241impl Default for Layout {
242 fn default() -> Self {
243 Self::new()
244 }
245}
246
247impl Layout {
248 /// Creates a new zero-[`Layout`].
249 ///
250 /// The Zero-layout has size and location set to ZERO.
251 /// The `order` value of this layout is set to the minimum value of 0.
252 /// This means it should be rendered below all other [`Layout`]s.
253 #[must_use]
254 pub const fn new() -> Self {
255 Self {
256 order: 0,
257 location: Point::ZERO,
258 size: Size::zero(),
259 #[cfg(feature = "content_size")]
260 content_size: Size::zero(),
261 scrollbar_size: Size::zero(),
262 border: Rect::zero(),
263 padding: Rect::zero(),
264 }
265 }
266
267 /// Creates a new zero-[`Layout`] with the supplied `order` value.
268 ///
269 /// Nodes with a higher order should be rendered on top of those with a lower order.
270 /// The Zero-layout has size and location set to ZERO.
271 #[must_use]
272 pub const fn with_order(order: u32) -> Self {
273 Self {
274 order,
275 size: Size::zero(),
276 location: Point::ZERO,
277 #[cfg(feature = "content_size")]
278 content_size: Size::zero(),
279 scrollbar_size: Size::zero(),
280 border: Rect::zero(),
281 padding: Rect::zero(),
282 }
283 }
284}
285
286#[cfg(feature = "content_size")]
287impl Layout {
288 /// Return the scroll width of the node.
289 /// The scroll width is the difference between the width and the content width, floored at zero
290 pub fn scroll_width(&self) -> f32 {
291 f32_max(
292 0.0,
293 self.content_size.width + f32_min(self.scrollbar_size.width, self.size.width) - self.size.width
294 + self.border.right,
295 )
296 }
297
298 /// Return the scroll width of the node.
299 /// The scroll width is the difference between the width and the content width, floored at zero
300 pub fn scroll_height(&self) -> f32 {
301 f32_max(
302 0.0,
303 self.content_size.height + f32_min(self.scrollbar_size.height, self.size.height) - self.size.height
304 + self.border.bottom,
305 )
306 }
307}