1//! A typed representation of [CSS style properties](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) in Rust. Used as input to layout computation.
2mod alignment;
3mod dimension;
4
5#[cfg(feature = "flexbox")]
6mod flex;
7
8pub use self::alignment::{AlignContent, AlignItems, AlignSelf, JustifyContent, JustifyItems, JustifySelf};
9pub use self::dimension::{AvailableSpace, Dimension, LengthPercentage, LengthPercentageAuto};
10
11#[cfg(feature = "flexbox")]
12pub use self::flex::{FlexDirection, FlexWrap};
13
14#[cfg(feature = "grid")]
15mod grid;
16#[cfg(feature = "grid")]
17pub(crate) use self::grid::{GenericGridPlacement, OriginZeroGridPlacement};
18#[cfg(feature = "grid")]
19pub use self::grid::{
20 GridAutoFlow, GridPlacement, GridTrackRepetition, MaxTrackSizingFunction, MinTrackSizingFunction,
21 NonRepeatedTrackSizingFunction, TrackSizingFunction,
22};
23use crate::geometry::{Point, Rect, Size};
24
25#[cfg(feature = "grid")]
26use crate::geometry::Line;
27#[cfg(feature = "serde")]
28use crate::style_helpers;
29#[cfg(feature = "grid")]
30use crate::util::sys::GridTrackVec;
31
32/// Sets the layout used for the children of this node
33///
34/// The default values depends on on which feature flags are enabled. The order of precedence is: Flex, Grid, Block, None.
35#[derive(Copy, Clone, PartialEq, Eq, Debug)]
36#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
37pub enum Display {
38 /// The children will follow the block layout algorithm
39 #[cfg(feature = "block_layout")]
40 Block,
41 /// The children will follow the flexbox layout algorithm
42 #[cfg(feature = "flexbox")]
43 Flex,
44 /// The children will follow the CSS Grid layout algorithm
45 #[cfg(feature = "grid")]
46 Grid,
47 /// The children will not be laid out, and will follow absolute positioning
48 None,
49}
50
51impl Display {
52 /// The default of Display.
53 #[cfg(feature = "flexbox")]
54 pub const DEFAULT: Display = Display::Flex;
55
56 /// The default of Display.
57 #[cfg(all(feature = "grid", not(feature = "flexbox")))]
58 pub const DEFAULT: Display = Display::Grid;
59
60 /// The default of Display.
61 #[cfg(all(feature = "block_layout", not(feature = "flexbox"), not(feature = "grid")))]
62 pub const DEFAULT: Display = Display::Block;
63
64 /// The default of Display.
65 #[cfg(all(not(feature = "flexbox"), not(feature = "grid"), not(feature = "block_layout")))]
66 pub const DEFAULT: Display = Display::None;
67}
68
69impl Default for Display {
70 fn default() -> Self {
71 Self::DEFAULT
72 }
73}
74
75/// The positioning strategy for this item.
76///
77/// This controls both how the origin is determined for the [`Style::position`] field,
78/// and whether or not the item will be controlled by flexbox's layout algorithm.
79///
80/// WARNING: this enum follows the behavior of [CSS's `position` property](https://developer.mozilla.org/en-US/docs/Web/CSS/position),
81/// which can be unintuitive.
82///
83/// [`Position::Relative`] is the default value, in contrast to the default behavior in CSS.
84#[derive(Copy, Clone, PartialEq, Eq, Debug)]
85#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
86pub enum Position {
87 /// The offset is computed relative to the final position given by the layout algorithm.
88 /// Offsets do not affect the position of any other items; they are effectively a correction factor applied at the end.
89 Relative,
90 /// The offset is computed relative to this item's closest positioned ancestor, if any.
91 /// Otherwise, it is placed relative to the origin.
92 /// No space is created for the item in the page layout, and its size will not be altered.
93 ///
94 /// WARNING: to opt-out of layouting entirely, you must use [`Display::None`] instead on your [`Style`] object.
95 Absolute,
96}
97
98impl Default for Position {
99 fn default() -> Self {
100 Self::Relative
101 }
102}
103
104/// How children overflowing their container should affect layout
105///
106/// In CSS the primary effect of this property is to control whether contents of a parent container that overflow that container should
107/// be displayed anyway, be clipped, or trigger the container to become a scroll container. However it also has secondary effects on layout,
108/// the main ones being:
109///
110/// - The automatic minimum size Flexbox/CSS Grid items with non-`Visible` overflow is `0` rather than being content based
111/// - `Overflow::Scroll` nodes have space in the layout reserved for a scrollbar (width controlled by the `scrollbar_width` property)
112///
113/// In Taffy, we only implement the layout related secondary effects as we are not concerned with drawing/painting. The amount of space reserved for
114/// a scrollbar is controlled by the `scrollbar_width` property. If this is `0` then `Scroll` behaves identically to `Hidden`.
115///
116/// <https://developer.mozilla.org/en-US/docs/Web/CSS/overflow>
117#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
118#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
119pub enum Overflow {
120 /// The automatic minimum size of this node as a flexbox/grid item should be based on the size of it's content.
121 #[default]
122 Visible,
123 /// The automatic minimum size of this node as a flexbox/grid item should be `0`.
124 Hidden,
125 /// The automatic minimum size of this node as a flexbox/grid item should be `0`. Additionally, space should be reserved
126 /// for a scrollbar. The amount of space reserved is controlled by the `scrollbar_width` property.
127 Scroll,
128}
129
130impl Overflow {
131 /// Returns true for overflow modes that contain their contents (`Overflow::Hidden`, `Overflow::Scroll`, `Overflow::Auto`)
132 /// or else false for overflow modes that allow their contains to spill (`Overflow::Visible`).
133 #[inline(always)]
134 pub(crate) fn is_scroll_container(self) -> bool {
135 self != Overflow::Visible
136 }
137
138 /// Returns `Some(0.0)` if the overflow mode would cause the automatic minimum size of a Flexbox or CSS Grid item
139 /// to be `0`. Else returns None.
140 #[inline(always)]
141 pub(crate) fn maybe_into_automatic_min_size(self) -> Option<f32> {
142 match self.is_scroll_container() {
143 true => Some(0.0),
144 false => None,
145 }
146 }
147}
148
149/// The flexbox layout information for a single node.
150///
151/// The most important idea in flexbox is the notion of a "main" and "cross" axis, which are always perpendicular to each other.
152/// The orientation of these axes are controlled via the [`FlexDirection`] field of this struct.
153///
154/// This struct follows the [CSS equivalent](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox) directly;
155/// information about the behavior on the web should transfer directly.
156///
157/// Detailed information about the exact behavior of each of these fields
158/// can be found on [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS) by searching for the field name.
159/// The distinction between margin, padding and border is explained well in
160/// this [introduction to the box model](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Introduction_to_the_CSS_box_model).
161///
162/// If the behavior does not match the flexbox layout algorithm on the web, please file a bug!
163#[derive(Clone, PartialEq, Debug)]
164#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
165#[cfg_attr(feature = "serde", serde(default))]
166pub struct Style {
167 /// What layout strategy should be used?
168 pub display: Display,
169
170 // Overflow properties
171 /// How children overflowing their container should affect layout
172 pub overflow: Point<Overflow>,
173 /// How much space (in points) should be reserved for the scrollbars of `Overflow::Scroll` and `Overflow::Auto` nodes.
174 pub scrollbar_width: f32,
175
176 // Position properties
177 /// What should the `position` value of this struct use as a base offset?
178 pub position: Position,
179 /// How should the position of this element be tweaked relative to the layout defined?
180 #[cfg_attr(feature = "serde", serde(default = "style_helpers::auto"))]
181 pub inset: Rect<LengthPercentageAuto>,
182
183 // Size properies
184 /// Sets the initial size of the item
185 #[cfg_attr(feature = "serde", serde(default = "style_helpers::auto"))]
186 pub size: Size<Dimension>,
187 /// Controls the minimum size of the item
188 #[cfg_attr(feature = "serde", serde(default = "style_helpers::auto"))]
189 pub min_size: Size<Dimension>,
190 /// Controls the maximum size of the item
191 #[cfg_attr(feature = "serde", serde(default = "style_helpers::auto"))]
192 pub max_size: Size<Dimension>,
193 /// Sets the preferred aspect ratio for the item
194 ///
195 /// The ratio is calculated as width divided by height.
196 pub aspect_ratio: Option<f32>,
197
198 // Spacing Properties
199 /// How large should the margin be on each side?
200 #[cfg_attr(feature = "serde", serde(default = "style_helpers::zero"))]
201 pub margin: Rect<LengthPercentageAuto>,
202 /// How large should the padding be on each side?
203 #[cfg_attr(feature = "serde", serde(default = "style_helpers::zero"))]
204 pub padding: Rect<LengthPercentage>,
205 /// How large should the border be on each side?
206 #[cfg_attr(feature = "serde", serde(default = "style_helpers::zero"))]
207 pub border: Rect<LengthPercentage>,
208
209 // Alignment properties
210 /// How this node's children aligned in the cross/block axis?
211 #[cfg(any(feature = "flexbox", feature = "grid"))]
212 pub align_items: Option<AlignItems>,
213 /// How this node should be aligned in the cross/block axis
214 /// Falls back to the parents [`AlignItems`] if not set
215 #[cfg(any(feature = "flexbox", feature = "grid"))]
216 pub align_self: Option<AlignSelf>,
217 /// How this node's children should be aligned in the inline axis
218 #[cfg(feature = "grid")]
219 pub justify_items: Option<AlignItems>,
220 /// How this node should be aligned in the inline axis
221 /// Falls back to the parents [`JustifyItems`] if not set
222 #[cfg(feature = "grid")]
223 pub justify_self: Option<AlignSelf>,
224 /// How should content contained within this item be aligned in the cross/block axis
225 #[cfg(any(feature = "flexbox", feature = "grid"))]
226 pub align_content: Option<AlignContent>,
227 /// How should contained within this item be aligned in the main/inline axis
228 #[cfg(any(feature = "flexbox", feature = "grid"))]
229 pub justify_content: Option<JustifyContent>,
230 /// How large should the gaps between items in a grid or flex container be?
231 #[cfg(any(feature = "flexbox", feature = "grid"))]
232 #[cfg_attr(feature = "serde", serde(default = "style_helpers::zero"))]
233 pub gap: Size<LengthPercentage>,
234
235 // Flexbox properies
236 /// Which direction does the main axis flow in?
237 #[cfg(feature = "flexbox")]
238 pub flex_direction: FlexDirection,
239 /// Should elements wrap, or stay in a single line?
240 #[cfg(feature = "flexbox")]
241 pub flex_wrap: FlexWrap,
242 /// Sets the initial main axis size of the item
243 #[cfg(feature = "flexbox")]
244 pub flex_basis: Dimension,
245 /// The relative rate at which this item grows when it is expanding to fill space
246 ///
247 /// 0.0 is the default value, and this value must be positive.
248 #[cfg(feature = "flexbox")]
249 pub flex_grow: f32,
250 /// The relative rate at which this item shrinks when it is contracting to fit into space
251 ///
252 /// 1.0 is the default value, and this value must be positive.
253 #[cfg(feature = "flexbox")]
254 pub flex_shrink: f32,
255
256 // Grid container properies
257 /// Defines the track sizing functions (widths) of the grid rows
258 #[cfg(feature = "grid")]
259 pub grid_template_rows: GridTrackVec<TrackSizingFunction>,
260 /// Defines the track sizing functions (heights) of the grid columns
261 #[cfg(feature = "grid")]
262 pub grid_template_columns: GridTrackVec<TrackSizingFunction>,
263 /// Defines the size of implicitly created rows
264 #[cfg(feature = "grid")]
265 pub grid_auto_rows: GridTrackVec<NonRepeatedTrackSizingFunction>,
266 /// Defined the size of implicitly created columns
267 #[cfg(feature = "grid")]
268 pub grid_auto_columns: GridTrackVec<NonRepeatedTrackSizingFunction>,
269 /// Controls how items get placed into the grid for auto-placed items
270 #[cfg(feature = "grid")]
271 pub grid_auto_flow: GridAutoFlow,
272
273 // Grid child properties
274 /// Defines which row in the grid the item should start and end at
275 #[cfg(feature = "grid")]
276 pub grid_row: Line<GridPlacement>,
277 /// Defines which column in the grid the item should start and end at
278 #[cfg(feature = "grid")]
279 pub grid_column: Line<GridPlacement>,
280
281 // Morphorm container properties
282 /// Sets the default spacing *around* the group of all children of a morphorm container
283 #[cfg(feature = "morphorm")]
284 pub child_spacing: Rect<LengthPercentageAuto>,
285
286 // Morphorm child properties
287 /// Sets the minimum margin for child
288 #[cfg(feature = "morphorm")]
289 pub min_margin: Rect<LengthPercentage>,
290 /// Sets the maximum margin for child
291 #[cfg(feature = "morphorm")]
292 pub max_margin: Rect<LengthPercentage>,
293}
294
295impl Style {
296 /// The [`Default`] layout, in a form that can be used in const functions
297 pub const DEFAULT: Style = Style {
298 display: Display::DEFAULT,
299 overflow: Point { x: Overflow::Visible, y: Overflow::Visible },
300 scrollbar_width: 0.0,
301 position: Position::Relative,
302 inset: Rect::auto(),
303 margin: Rect::zero(),
304 padding: Rect::zero(),
305 border: Rect::zero(),
306 size: Size::auto(),
307 min_size: Size::auto(),
308 max_size: Size::auto(),
309 aspect_ratio: None,
310 #[cfg(any(feature = "flexbox", feature = "grid", feature = "morphorm"))]
311 gap: Size::zero(),
312 // Aligment
313 #[cfg(any(feature = "flexbox", feature = "grid"))]
314 align_items: None,
315 #[cfg(any(feature = "flexbox", feature = "grid"))]
316 align_self: None,
317 #[cfg(feature = "grid")]
318 justify_items: None,
319 #[cfg(feature = "grid")]
320 justify_self: None,
321 #[cfg(any(feature = "flexbox", feature = "grid"))]
322 align_content: None,
323 #[cfg(any(feature = "flexbox", feature = "grid"))]
324 justify_content: None,
325 // Flexbox
326 #[cfg(any(feature = "flexbox", feature = "morphorm"))]
327 flex_direction: FlexDirection::Row,
328 #[cfg(any(feature = "flexbox", feature = "morphorm"))]
329 flex_wrap: FlexWrap::NoWrap,
330 #[cfg(feature = "flexbox")]
331 flex_grow: 0.0,
332 #[cfg(feature = "flexbox")]
333 flex_shrink: 1.0,
334 #[cfg(feature = "flexbox")]
335 flex_basis: Dimension::Auto,
336 // Grid
337 #[cfg(feature = "grid")]
338 grid_template_rows: GridTrackVec::new(),
339 #[cfg(feature = "grid")]
340 grid_template_columns: GridTrackVec::new(),
341 #[cfg(feature = "grid")]
342 grid_auto_rows: GridTrackVec::new(),
343 #[cfg(feature = "grid")]
344 grid_auto_columns: GridTrackVec::new(),
345 #[cfg(feature = "grid")]
346 grid_auto_flow: GridAutoFlow::Row,
347 #[cfg(feature = "grid")]
348 grid_row: Line { start: GridPlacement::Auto, end: GridPlacement::Auto },
349 #[cfg(feature = "grid")]
350 grid_column: Line { start: GridPlacement::Auto, end: GridPlacement::Auto },
351
352 #[cfg(feature = "morphorm")]
353 child_spacing: Rect::zero(),
354 #[cfg(feature = "morphorm")]
355 min_margin: Rect::zero(),
356 #[cfg(feature = "morphorm")]
357 max_margin: Rect {
358 left: LengthPercentage::Length(f32::INFINITY),
359 right: LengthPercentage::Length(f32::INFINITY),
360 top: LengthPercentage::Length(f32::INFINITY),
361 bottom: LengthPercentage::Length(f32::INFINITY),
362 },
363 };
364}
365
366impl Default for Style {
367 fn default() -> Self {
368 Style::DEFAULT
369 }
370}
371
372#[cfg(test)]
373mod tests {
374 use super::Style;
375 use crate::geometry::*;
376
377 #[test]
378 fn defaults_match() {
379 #[cfg(feature = "grid")]
380 use super::GridPlacement;
381 use super::LengthPercentage;
382
383 let old_defaults = Style {
384 display: Default::default(),
385 overflow: Default::default(),
386 scrollbar_width: 0.0,
387 position: Default::default(),
388 #[cfg(feature = "flexbox")]
389 flex_direction: Default::default(),
390 #[cfg(feature = "flexbox")]
391 flex_wrap: Default::default(),
392 #[cfg(any(feature = "flexbox", feature = "grid"))]
393 align_items: Default::default(),
394 #[cfg(any(feature = "flexbox", feature = "grid"))]
395 align_self: Default::default(),
396 #[cfg(feature = "grid")]
397 justify_items: Default::default(),
398 #[cfg(feature = "grid")]
399 justify_self: Default::default(),
400 #[cfg(any(feature = "flexbox", feature = "grid"))]
401 align_content: Default::default(),
402 #[cfg(any(feature = "flexbox", feature = "grid"))]
403 justify_content: Default::default(),
404 inset: Rect::auto(),
405 margin: Rect::zero(),
406 padding: Rect::zero(),
407 border: Rect::zero(),
408 gap: Size::zero(),
409 #[cfg(feature = "flexbox")]
410 flex_grow: 0.0,
411 #[cfg(feature = "flexbox")]
412 flex_shrink: 1.0,
413 #[cfg(feature = "flexbox")]
414 flex_basis: super::Dimension::Auto,
415 size: Size::auto(),
416 min_size: Size::auto(),
417 max_size: Size::auto(),
418 aspect_ratio: Default::default(),
419
420 #[cfg(feature = "grid")]
421 grid_template_rows: Default::default(),
422 #[cfg(feature = "grid")]
423 grid_template_columns: Default::default(),
424 #[cfg(feature = "grid")]
425 grid_auto_rows: Default::default(),
426 #[cfg(feature = "grid")]
427 grid_auto_columns: Default::default(),
428 #[cfg(feature = "grid")]
429 grid_auto_flow: Default::default(),
430 #[cfg(feature = "grid")]
431 grid_row: Line { start: GridPlacement::Auto, end: GridPlacement::Auto },
432 #[cfg(feature = "grid")]
433 grid_column: Line { start: GridPlacement::Auto, end: GridPlacement::Auto },
434
435 #[cfg(feature = "morphorm")]
436 child_spacing: Rect::zero(),
437 #[cfg(feature = "morphorm")]
438 min_margin: Rect::zero(),
439 #[cfg(feature = "morphorm")]
440 max_margin: Rect {
441 left: LengthPercentage::Length(f32::INFINITY),
442 right: LengthPercentage::Length(f32::INFINITY),
443 top: LengthPercentage::Length(f32::INFINITY),
444 bottom: LengthPercentage::Length(f32::INFINITY),
445 },
446 };
447
448 assert_eq!(Style::DEFAULT, Style::default());
449 assert_eq!(Style::DEFAULT, old_defaults);
450 }
451
452 // NOTE: Please feel free the update the sizes in this test as required. This test is here to prevent unintentional size changes
453 // and to serve as accurate up-to-date documentation on the sizes.
454 #[test]
455 fn style_sizes() {
456 use super::*;
457
458 fn assert_type_size<T>(expected_size: usize) {
459 let name = ::core::any::type_name::<T>();
460 let name = name.replace("taffy::geometry::", "");
461 let name = name.replace("taffy::style::dimension::", "");
462 let name = name.replace("taffy::style::alignment::", "");
463 let name = name.replace("taffy::style::flex::", "");
464 let name = name.replace("taffy::style::grid::", "");
465
466 assert_eq!(
467 ::core::mem::size_of::<T>(),
468 expected_size,
469 "Expected {} for be {} byte(s) but it was {} byte(s)",
470 name,
471 expected_size,
472 ::core::mem::size_of::<T>(),
473 );
474 }
475
476 // Display and Position
477 assert_type_size::<Display>(1);
478 assert_type_size::<Position>(1);
479 assert_type_size::<Overflow>(1);
480
481 // Dimensions and aggregations of Dimensions
482 assert_type_size::<f32>(4);
483 assert_type_size::<LengthPercentage>(8);
484 assert_type_size::<LengthPercentageAuto>(8);
485 assert_type_size::<Dimension>(8);
486 assert_type_size::<Size<LengthPercentage>>(16);
487 assert_type_size::<Size<LengthPercentageAuto>>(16);
488 assert_type_size::<Size<Dimension>>(16);
489 assert_type_size::<Rect<LengthPercentage>>(32);
490 assert_type_size::<Rect<LengthPercentageAuto>>(32);
491 assert_type_size::<Rect<Dimension>>(32);
492
493 // Alignment
494 assert_type_size::<AlignContent>(1);
495 assert_type_size::<AlignItems>(1);
496 assert_type_size::<Option<AlignItems>>(1);
497
498 // Flexbox Container
499 assert_type_size::<FlexDirection>(1);
500 assert_type_size::<FlexWrap>(1);
501
502 // CSS Grid Container
503 assert_type_size::<GridAutoFlow>(1);
504 assert_type_size::<MinTrackSizingFunction>(8);
505 assert_type_size::<MaxTrackSizingFunction>(12);
506 assert_type_size::<NonRepeatedTrackSizingFunction>(20);
507 assert_type_size::<TrackSizingFunction>(32);
508 assert_type_size::<Vec<NonRepeatedTrackSizingFunction>>(24);
509 assert_type_size::<Vec<TrackSizingFunction>>(24);
510
511 // CSS Grid Item
512 assert_type_size::<GridPlacement>(4);
513 assert_type_size::<Line<GridPlacement>>(8);
514
515 // Overall
516 assert_type_size::<Style>(448);
517 }
518}