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