Grid/refactor coordinate handling (#301)
1//! A representation of [CSS layout properties](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) in Rust, used for flexbox layout
2mod alignment;
3mod dimension;
4mod flex;
5
6pub use self::alignment::{AlignContent, AlignItems, AlignSelf, JustifyContent, JustifyItems, JustifySelf};
7pub use self::dimension::{AvailableSpace, Dimension, LengthPercentage, LengthPercentageAuto};
8pub use self::flex::{FlexDirection, FlexWrap};
9
10#[cfg(feature = "grid")]
11mod grid;
12#[cfg(feature = "grid")]
13pub(crate) use self::grid::{GenericGridPlacement, OriginZeroGridPlacement};
14#[cfg(feature = "grid")]
15pub use self::grid::{
16 GridAutoFlow, GridPlacement, GridTrackRepetition, MaxTrackSizingFunction, MinTrackSizingFunction,
17 NonRepeatedTrackSizingFunction, TrackSizingFunction,
18};
19use crate::geometry::{Rect, Size};
20
21#[cfg(feature = "grid")]
22use crate::geometry::Line;
23#[cfg(feature = "grid")]
24use crate::sys::GridTrackVec;
25
26/// Sets the layout used for the children of this node
27///
28/// [`Display::Flex`] is the default value.
29#[derive(Copy, Clone, PartialEq, Eq, Debug)]
30#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
31pub enum Display {
32 /// The children will follow the flexbox layout algorithm
33 Flex,
34 /// The children will follow the CSS Grid layout algorithm
35 #[cfg(feature = "grid")]
36 Grid,
37 /// The children will not be laid out, and will follow absolute positioning
38 None,
39}
40
41impl Default for Display {
42 fn default() -> Self {
43 Self::Flex
44 }
45}
46
47/// The positioning strategy for this item.
48///
49/// This controls both how the origin is determined for the [`Style::position`] field,
50/// and whether or not the item will be controlled by flexbox's layout algorithm.
51///
52/// WARNING: this enum follows the behavior of [CSS's `position` property](https://developer.mozilla.org/en-US/docs/Web/CSS/position),
53/// which can be unintuitive.
54///
55/// [`PositionType::Relative`] is the default value, in contrast to the default behavior in CSS.
56#[derive(Copy, Clone, PartialEq, Eq, Debug)]
57#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
58pub enum Position {
59 /// The offset is computed relative to the final position given by the layout algorithm.
60 /// Offsets do not affect the position of any other items; they are effectively a correction factor applied at the end.
61 Relative,
62 /// The offset is computed relative to this item's closest positioned ancestor, if any.
63 /// Otherwise, it is placed relative to the origin.
64 /// No space is created for the item in the page layout, and its size will not be altered.
65 ///
66 /// WARNING: to opt-out of layouting entirely, you must use [`Display::None`] instead on your [`Style`] object.
67 Absolute,
68}
69
70impl Default for Position {
71 fn default() -> Self {
72 Self::Relative
73 }
74}
75
76/// The flexbox layout information for a single [`Node`](crate::node::Node).
77///
78/// The most important idea in flexbox is the notion of a "main" and "cross" axis, which are always perpendicular to each other.
79/// The orientation of these axes are controlled via the [`FlexDirection`] field of this struct.
80///
81/// This struct follows the [CSS equivalent](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox) directly;
82/// information about the behavior on the web should transfer directly.
83///
84/// Detailed information about the exact behavior of each of these fields
85/// can be found on [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS) by searching for the field name.
86/// The distinction between margin, padding and border is explained well in
87/// this [introduction to the box model](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Introduction_to_the_CSS_box_model).
88///
89/// If the behavior does not match the flexbox layout algorithm on the web, please file a bug!
90#[derive(Clone, PartialEq, Debug)]
91#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
92#[cfg_attr(feature = "serde", serde(default))]
93pub struct Style {
94 /// What layout strategy should be used?
95 pub display: Display,
96
97 // Position properties
98 /// What should the `position` value of this struct use as a base offset?
99 pub position: Position,
100 /// How should the position of this element be tweaked relative to the layout defined?
101 pub inset: Rect<LengthPercentageAuto>,
102
103 // Size properies
104 /// Sets the initial size of the item
105 pub size: Size<Dimension>,
106 /// Controls the minimum size of the item
107 pub min_size: Size<Dimension>,
108 /// Controls the maximum size of the item
109 pub max_size: Size<Dimension>,
110 /// Sets the preferred aspect ratio for the item
111 ///
112 /// The ratio is calculated as width divided by height.
113 pub aspect_ratio: Option<f32>,
114
115 // Spacing Properties
116 /// How large should the margin be on each side?
117 pub margin: Rect<LengthPercentageAuto>,
118 /// How large should the padding be on each side?
119 pub padding: Rect<LengthPercentage>,
120 /// How large should the border be on each side?
121 pub border: Rect<LengthPercentage>,
122
123 // Alignment properties
124 /// How this node's children aligned in the cross/block axis?
125 pub align_items: Option<AlignItems>,
126 /// How this node should be aligned in the cross/block axis
127 /// Falls back to the parents [`AlignItems`] if not set
128 pub align_self: Option<AlignSelf>,
129 /// How this node's children should be aligned in the inline axis
130 #[cfg(feature = "grid")]
131 pub justify_items: Option<AlignItems>,
132 /// How this node should be aligned in the inline axis
133 /// Falls back to the parents [`JustifyItems`] if not set
134 pub justify_self: Option<AlignSelf>,
135 /// How should content contained within this item be aligned in the cross/block axis
136 pub align_content: Option<AlignContent>,
137 /// How should contained within this item be aligned in the main/inline axis
138 pub justify_content: Option<JustifyContent>,
139 /// How large should the gaps between items in a grid or flex container be?
140 pub gap: Size<LengthPercentage>,
141
142 // Flexbox properies
143 /// Which direction does the main axis flow in?
144 pub flex_direction: FlexDirection,
145 /// Should elements wrap, or stay in a single line?
146 pub flex_wrap: FlexWrap,
147 /// Sets the initial main axis size of the item
148 pub flex_basis: Dimension,
149 /// The relative rate at which this item grows when it is expanding to fill space
150 ///
151 /// 0.0 is the default value, and this value must be positive.
152 pub flex_grow: f32,
153 /// The relative rate at which this item shrinks when it is contracting to fit into space
154 ///
155 /// 1.0 is the default value, and this value must be positive.
156 pub flex_shrink: f32,
157
158 // Grid container properies
159 /// Defines the track sizing functions (widths) of the grid rows
160 #[cfg(feature = "grid")]
161 pub grid_template_rows: GridTrackVec<TrackSizingFunction>,
162 /// Defines the track sizing functions (heights) of the grid columns
163 #[cfg(feature = "grid")]
164 pub grid_template_columns: GridTrackVec<TrackSizingFunction>,
165 /// Defines the size of implicitly created rows
166 #[cfg(feature = "grid")]
167 pub grid_auto_rows: GridTrackVec<NonRepeatedTrackSizingFunction>,
168 /// Defined the size of implicitly created columns
169 #[cfg(feature = "grid")]
170 pub grid_auto_columns: GridTrackVec<NonRepeatedTrackSizingFunction>,
171 /// Controls how items get placed into the grid for auto-placed items
172 #[cfg(feature = "grid")]
173 pub grid_auto_flow: GridAutoFlow,
174
175 // Grid child properties
176 /// Defines which row in the grid the item should start and end at
177 #[cfg(feature = "grid")]
178 pub grid_row: Line<GridPlacement>,
179 /// Defines which column in the grid the item should start and end at
180 #[cfg(feature = "grid")]
181 pub grid_column: Line<GridPlacement>,
182}
183
184impl Style {
185 /// The [`Default`] layout, in a form that can be used in const functions
186 pub const DEFAULT: Style = Style {
187 display: Display::Flex,
188 position: Position::Relative,
189 flex_direction: FlexDirection::Row,
190 flex_wrap: FlexWrap::NoWrap,
191 align_items: None,
192 align_self: None,
193 #[cfg(feature = "grid")]
194 justify_items: None,
195 justify_self: None,
196 align_content: None,
197 justify_content: None,
198 inset: Rect::auto(),
199 margin: Rect::zero(),
200 padding: Rect::zero(),
201 border: Rect::zero(),
202 gap: Size::zero(),
203 flex_grow: 0.0,
204 flex_shrink: 1.0,
205 flex_basis: Dimension::Auto,
206 size: Size::auto(),
207 min_size: Size::auto(),
208 max_size: Size::auto(),
209 aspect_ratio: None,
210 #[cfg(feature = "grid")]
211 grid_template_rows: GridTrackVec::new(),
212 #[cfg(feature = "grid")]
213 grid_template_columns: GridTrackVec::new(),
214 #[cfg(feature = "grid")]
215 grid_auto_rows: GridTrackVec::new(),
216 #[cfg(feature = "grid")]
217 grid_auto_columns: GridTrackVec::new(),
218 #[cfg(feature = "grid")]
219 grid_auto_flow: GridAutoFlow::Row,
220 #[cfg(feature = "grid")]
221 grid_row: Line { start: GridPlacement::Auto, end: GridPlacement::Auto },
222 #[cfg(feature = "grid")]
223 grid_column: Line { start: GridPlacement::Auto, end: GridPlacement::Auto },
224 };
225}
226
227impl Default for Style {
228 fn default() -> Self {
229 Style::DEFAULT
230 }
231}
232
233#[cfg(test)]
234mod tests {
235 use super::Style;
236 use crate::geometry::*;
237
238 #[test]
239 fn defaults_match() {
240 #[cfg(feature = "grid")]
241 use super::GridPlacement;
242
243 let old_defaults = Style {
244 display: Default::default(),
245 position: Default::default(),
246 flex_direction: Default::default(),
247 flex_wrap: Default::default(),
248 align_items: Default::default(),
249 align_self: Default::default(),
250 #[cfg(feature = "grid")]
251 justify_items: Default::default(),
252 justify_self: Default::default(),
253 align_content: Default::default(),
254 justify_content: Default::default(),
255 inset: Rect::auto(),
256 margin: Rect::zero(),
257 padding: Rect::zero(),
258 border: Rect::zero(),
259 gap: Size::zero(),
260 flex_grow: 0.0,
261 flex_shrink: 1.0,
262 flex_basis: super::Dimension::Auto,
263 size: Size::auto(),
264 min_size: Size::auto(),
265 max_size: Size::auto(),
266 aspect_ratio: Default::default(),
267 #[cfg(feature = "grid")]
268 grid_template_rows: Default::default(),
269 #[cfg(feature = "grid")]
270 grid_template_columns: Default::default(),
271 #[cfg(feature = "grid")]
272 grid_auto_rows: Default::default(),
273 #[cfg(feature = "grid")]
274 grid_auto_columns: Default::default(),
275 #[cfg(feature = "grid")]
276 grid_auto_flow: Default::default(),
277 #[cfg(feature = "grid")]
278 grid_row: Line { start: GridPlacement::Auto, end: GridPlacement::Auto },
279 #[cfg(feature = "grid")]
280 grid_column: Line { start: GridPlacement::Auto, end: GridPlacement::Auto },
281 };
282
283 assert_eq!(Style::DEFAULT, Style::default());
284 assert_eq!(Style::DEFAULT, old_defaults);
285 }
286
287 // NOTE: Please feel free the update the sizes in this test as required. This test is here to prevent unintentional size changes
288 // and to serve as accurate up-to-date documentation on the sizes.
289 #[test]
290 fn style_sizes() {
291 use super::*;
292
293 fn assert_type_size<T>(expected_size: usize) {
294 let name = ::core::any::type_name::<T>();
295 let name = name.replace("taffy::geometry::", "");
296 let name = name.replace("taffy::style::dimension::", "");
297 let name = name.replace("taffy::style::alignment::", "");
298 let name = name.replace("taffy::style::flex::", "");
299 let name = name.replace("taffy::style::grid::", "");
300
301 assert_eq!(
302 ::core::mem::size_of::<T>(),
303 expected_size,
304 "Expected {} for be {} byte(s) but it was {} byte(s)",
305 name,
306 expected_size,
307 ::core::mem::size_of::<T>(),
308 );
309 }
310
311 // Display and Position
312 assert_type_size::<Display>(1);
313 assert_type_size::<Position>(1);
314
315 // Dimensions and aggregations of Dimensions
316 assert_type_size::<f32>(4);
317 assert_type_size::<LengthPercentage>(8);
318 assert_type_size::<LengthPercentageAuto>(8);
319 assert_type_size::<Dimension>(8);
320 assert_type_size::<Size<LengthPercentage>>(16);
321 assert_type_size::<Size<LengthPercentageAuto>>(16);
322 assert_type_size::<Size<Dimension>>(16);
323 assert_type_size::<Rect<LengthPercentage>>(32);
324 assert_type_size::<Rect<LengthPercentageAuto>>(32);
325 assert_type_size::<Rect<Dimension>>(32);
326
327 // Alignment
328 assert_type_size::<AlignContent>(1);
329 assert_type_size::<AlignItems>(1);
330 assert_type_size::<Option<AlignItems>>(1);
331
332 // Flexbox Container
333 assert_type_size::<FlexDirection>(1);
334 assert_type_size::<FlexWrap>(1);
335
336 // CSS Grid Container
337 assert_type_size::<GridAutoFlow>(1);
338 assert_type_size::<MinTrackSizingFunction>(8);
339 assert_type_size::<MaxTrackSizingFunction>(12);
340 assert_type_size::<NonRepeatedTrackSizingFunction>(20);
341 assert_type_size::<TrackSizingFunction>(32);
342 assert_type_size::<Vec<NonRepeatedTrackSizingFunction>>(24);
343 assert_type_size::<Vec<TrackSizingFunction>>(24);
344
345 // CSS Grid Item
346 assert_type_size::<GridPlacement>(4);
347 assert_type_size::<Line<GridPlacement>>(8);
348
349 // Overall
350 assert_type_size::<Style>(344);
351 }
352}