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