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