Remove `LayoutAlgorithm` trait (#562)
1//! Final data structures that represent the high-level UI layout
2use crate::geometry::{Line, Point, Size};
3use crate::style::AvailableSpace;
4use crate::util::sys::{f32_max, f32_min};
5
6/// Whether we are performing a full layout, or we merely need to size the node
7#[derive(Copy, Clone, Debug, PartialEq, Eq)]
8pub enum RunMode {
9 /// A full layout for this node and all children should be computed
10 PerformLayout,
11 /// The layout algorithm should be executed such that an accurate container size for the node can be determined.
12 /// Layout steps that aren't necessary for determining the container size of the current node can be skipped.
13 ComputeSize,
14}
15
16/// Whether styles should be taken into account when computing size
17#[derive(Copy, Clone, Debug, PartialEq, Eq)]
18pub enum SizingMode {
19 /// Only content contributions should be taken into account
20 ContentSize,
21 /// Inherent size styles should be taken into account in addition to content contributions
22 InherentSize,
23}
24
25/// A set of margins that are available for collapsing with for block layout's margin collapsing
26#[derive(Copy, Clone, Debug, PartialEq)]
27pub struct CollapsibleMarginSet {
28 /// The largest positive margin
29 positive: f32,
30 /// The smallest negative margin (with largest absolute value)
31 negative: f32,
32}
33
34impl CollapsibleMarginSet {
35 /// A default margin set with no collapsible margins
36 pub const ZERO: Self = Self { positive: 0.0, negative: 0.0 };
37
38 /// Create a set from a single margin
39 pub fn from_margin(margin: f32) -> Self {
40 if margin >= 0.0 {
41 Self { positive: margin, negative: 0.0 }
42 } else {
43 Self { positive: 0.0, negative: margin }
44 }
45 }
46
47 /// Collapse a single margin with this set
48 pub fn collapse_with_margin(mut self, margin: f32) -> Self {
49 if margin >= 0.0 {
50 self.positive = f32_max(self.positive, margin);
51 } else {
52 self.negative = f32_min(self.negative, margin);
53 }
54 self
55 }
56
57 /// Collapse another margin set with this set
58 pub fn collapse_with_set(mut self, other: CollapsibleMarginSet) -> Self {
59 self.positive = f32_max(self.positive, other.positive);
60 self.negative = f32_min(self.negative, other.negative);
61 self
62 }
63
64 /// Resolve the resultant margin from this set once all collapsible margins
65 /// have been collapsed into it
66 pub fn resolve(&self) -> f32 {
67 self.positive + self.negative
68 }
69}
70
71/// A struct containing the inputs constraints/hints for laying out a node, which are passed in by the parent
72#[derive(Debug, Copy, Clone)]
73pub struct LayoutInput {
74 /// Known dimensions represent dimensions (width/height) which should be taken as fixed when performing layout.
75 /// For example, if known_dimensions.width is set to Some(WIDTH) then this means something like:
76 ///
77 /// "What would the height of this node be, assuming the width is WIDTH"
78 ///
79 /// Layout functions will be called with both known_dimensions set for final layout. Where the meaning is:
80 ///
81 /// "The exact size of this node is WIDTHxHEIGHT. Please lay out your children"
82 ///
83 pub known_dimensions: Size<Option<f32>>,
84 /// Parent size dimensions are intended to be used for percentage resolution.
85 pub parent_size: Size<Option<f32>>,
86 /// Available space represents an amount of space to layout into, and is used as a soft constraint
87 /// for the purpose of wrapping.
88 pub available_space: Size<AvailableSpace>,
89 /// Whether a Node's style sizes should be taken into account or ignored
90 pub sizing_mode: SizingMode,
91 /// Whether we only need to know the Node's size, or whe
92 pub run_mode: RunMode,
93 /// Specific to CSS Block layout. Used for correctly computing margin collapsing. You probably want to set this to `Line::FALSE`.
94 pub vertical_margins_are_collapsible: Line<bool>,
95}
96
97/// A struct containing the result of laying a single node, which is returned up to the parent node
98///
99/// A baseline is the line on which text sits. Your node likely has a baseline if it is a text node, or contains
100/// children that may be text nodes. See <https://www.w3.org/TR/css-writing-modes-3/#intro-baselines> for details.
101/// If your node does not have a baseline (or you are unsure how to compute it), then simply return `Point::NONE`
102/// for the first_baselines field
103#[derive(Debug, Copy, Clone)]
104pub struct LayoutOutput {
105 /// The size of the node
106 pub size: Size<f32>,
107 /// The first baseline of the node in each dimension, if any
108 pub first_baselines: Point<Option<f32>>,
109 /// Top margin that can be collapsed with. This is used for CSS block layout and can be set to
110 /// `CollapsibleMarginSet::ZERO` for other layout modes that don't support margin collapsing
111 pub top_margin: CollapsibleMarginSet,
112 /// Bottom margin that can be collapsed with. This is used for CSS block layout and can be set to
113 /// `CollapsibleMarginSet::ZERO` for other layout modes that don't support margin collapsing
114 pub bottom_margin: CollapsibleMarginSet,
115 /// Whether margins can be collapsed through this node. This is used for CSS block layout and can
116 /// be set to `false` for other layout modes that don't support margin collapsing
117 pub margins_can_collapse_through: bool,
118}
119
120impl LayoutOutput {
121 /// An all-zero `LayoutOutput` for hidden nodes
122 pub const HIDDEN: Self = Self {
123 size: Size::ZERO,
124 first_baselines: Point::NONE,
125 top_margin: CollapsibleMarginSet::ZERO,
126 bottom_margin: CollapsibleMarginSet::ZERO,
127 margins_can_collapse_through: false,
128 };
129
130 /// Constructor to create a `LayoutOutput` from just the size and baselines
131 pub fn from_size_and_baselines(size: Size<f32>, first_baselines: Point<Option<f32>>) -> Self {
132 Self {
133 size,
134 first_baselines,
135 top_margin: CollapsibleMarginSet::ZERO,
136 bottom_margin: CollapsibleMarginSet::ZERO,
137 margins_can_collapse_through: false,
138 }
139 }
140}
141
142impl From<Size<f32>> for LayoutOutput {
143 fn from(size: Size<f32>) -> Self {
144 Self {
145 size,
146 first_baselines: Point::NONE,
147 top_margin: CollapsibleMarginSet::ZERO,
148 bottom_margin: CollapsibleMarginSet::ZERO,
149 margins_can_collapse_through: false,
150 }
151 }
152}
153
154/// The final result of a layout algorithm for a single node.
155#[derive(Debug, Copy, Clone)]
156pub struct Layout {
157 /// The relative ordering of the node
158 ///
159 /// Nodes with a higher order should be rendered on top of those with a lower order.
160 /// This is effectively a topological sort of each tree.
161 pub order: u32,
162 /// The width and height of the node
163 pub size: Size<f32>,
164 /// The top-left corner of the node
165 pub location: Point<f32>,
166}
167
168impl Layout {
169 /// Creates a new zero-[`Layout`].
170 ///
171 /// The Zero-layout has size and location set to ZERO.
172 /// The `order` value of this layout is set to the minimum value of 0.
173 /// This means it should be rendered below all other [`Layout`]s.
174 #[must_use]
175 pub const fn new() -> Self {
176 Self { order: 0, size: Size::zero(), location: Point::ZERO }
177 }
178
179 /// Creates a new zero-[`Layout`] with the supplied `order` value.
180 ///
181 /// Nodes with a higher order should be rendered on top of those with a lower order.
182 /// The Zero-layout has size and location set to ZERO.
183 #[must_use]
184 pub const fn with_order(order: u32) -> Self {
185 Self { order, size: Size::zero(), location: Point::ZERO }
186 }
187}