Refactor `LayoutTree` + add examples of custom trees (#564)
1//! Final data structures that represent the high-level UI layout
2use crate::geometry::{Line, Point, Size};
3use crate::prelude::TaffyMaxContent;
4use crate::style::AvailableSpace;
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/// A struct containing the inputs constraints/hints for laying out a node, which are passed in by the parent
75#[derive(Debug, Copy, Clone)]
76pub struct LayoutInput {
77 /// Known dimensions represent dimensions (width/height) which should be taken as fixed when performing layout.
78 /// For example, if known_dimensions.width is set to Some(WIDTH) then this means something like:
79 ///
80 /// "What would the height of this node be, assuming the width is WIDTH"
81 ///
82 /// Layout functions will be called with both known_dimensions set for final layout. Where the meaning is:
83 ///
84 /// "The exact size of this node is WIDTHxHEIGHT. Please lay out your children"
85 ///
86 pub known_dimensions: Size<Option<f32>>,
87 /// Parent size dimensions are intended to be used for percentage resolution.
88 pub parent_size: Size<Option<f32>>,
89 /// Available space represents an amount of space to layout into, and is used as a soft constraint
90 /// for the purpose of wrapping.
91 pub available_space: Size<AvailableSpace>,
92 /// Whether a Node's style sizes should be taken into account or ignored
93 pub sizing_mode: SizingMode,
94 /// Whether we only need to know the Node's size, or whe
95 pub run_mode: RunMode,
96 /// Specific to CSS Block layout. Used for correctly computing margin collapsing. You probably want to set this to `Line::FALSE`.
97 pub vertical_margins_are_collapsible: Line<bool>,
98}
99
100impl LayoutInput {
101 /// A LayoutInput that can be used to request hidden layout
102 pub const HIDDEN: LayoutInput = LayoutInput {
103 // The important property for hidden layout
104 run_mode: RunMode::PerformHiddenLayout,
105 // The rest will be ignored
106 known_dimensions: Size::NONE,
107 parent_size: Size::NONE,
108 available_space: Size::MAX_CONTENT,
109 sizing_mode: SizingMode::InherentSize,
110 vertical_margins_are_collapsible: Line::FALSE,
111 };
112}
113
114/// A struct containing the result of laying a single node, which is returned up to the parent node
115///
116/// A baseline is the line on which text sits. Your node likely has a baseline if it is a text node, or contains
117/// children that may be text nodes. See <https://www.w3.org/TR/css-writing-modes-3/#intro-baselines> for details.
118/// If your node does not have a baseline (or you are unsure how to compute it), then simply return `Point::NONE`
119/// for the first_baselines field
120#[derive(Debug, Copy, Clone)]
121pub struct LayoutOutput {
122 /// The size of the node
123 pub size: Size<f32>,
124 /// The first baseline of the node in each dimension, if any
125 pub first_baselines: Point<Option<f32>>,
126 /// Top margin that can be collapsed with. This is used for CSS block layout and can be set to
127 /// `CollapsibleMarginSet::ZERO` for other layout modes that don't support margin collapsing
128 pub top_margin: CollapsibleMarginSet,
129 /// Bottom margin that can be collapsed with. This is used for CSS block layout and can be set to
130 /// `CollapsibleMarginSet::ZERO` for other layout modes that don't support margin collapsing
131 pub bottom_margin: CollapsibleMarginSet,
132 /// Whether margins can be collapsed through this node. This is used for CSS block layout and can
133 /// be set to `false` for other layout modes that don't support margin collapsing
134 pub margins_can_collapse_through: bool,
135}
136
137impl LayoutOutput {
138 /// An all-zero `LayoutOutput` for hidden nodes
139 pub const HIDDEN: Self = Self {
140 size: Size::ZERO,
141 first_baselines: Point::NONE,
142 top_margin: CollapsibleMarginSet::ZERO,
143 bottom_margin: CollapsibleMarginSet::ZERO,
144 margins_can_collapse_through: false,
145 };
146
147 /// Constructor to create a `LayoutOutput` from just the size and baselines
148 pub fn from_size_and_baselines(size: Size<f32>, first_baselines: Point<Option<f32>>) -> Self {
149 Self {
150 size,
151 first_baselines,
152 top_margin: CollapsibleMarginSet::ZERO,
153 bottom_margin: CollapsibleMarginSet::ZERO,
154 margins_can_collapse_through: false,
155 }
156 }
157}
158
159impl From<Size<f32>> for LayoutOutput {
160 fn from(size: Size<f32>) -> Self {
161 Self {
162 size,
163 first_baselines: Point::NONE,
164 top_margin: CollapsibleMarginSet::ZERO,
165 bottom_margin: CollapsibleMarginSet::ZERO,
166 margins_can_collapse_through: false,
167 }
168 }
169}
170
171/// The final result of a layout algorithm for a single node.
172#[derive(Debug, Copy, Clone)]
173pub struct Layout {
174 /// The relative ordering of the node
175 ///
176 /// Nodes with a higher order should be rendered on top of those with a lower order.
177 /// This is effectively a topological sort of each tree.
178 pub order: u32,
179 /// The width and height of the node
180 pub size: Size<f32>,
181 /// The top-left corner of the node
182 pub location: Point<f32>,
183}
184
185impl Layout {
186 /// Creates a new zero-[`Layout`].
187 ///
188 /// The Zero-layout has size and location set to ZERO.
189 /// The `order` value of this layout is set to the minimum value of 0.
190 /// This means it should be rendered below all other [`Layout`]s.
191 #[must_use]
192 pub const fn new() -> Self {
193 Self { order: 0, size: Size::zero(), location: Point::ZERO }
194 }
195
196 /// Creates a new zero-[`Layout`] with the supplied `order` value.
197 ///
198 /// Nodes with a higher order should be rendered on top of those with a lower order.
199 /// The Zero-layout has size and location set to ZERO.
200 #[must_use]
201 pub const fn with_order(order: u32) -> Self {
202 Self { order, size: Size::zero(), location: Point::ZERO }
203 }
204}