Skip to main content

taffy/style/
mod.rs

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 available_space;
4mod compact_length;
5mod dimension;
6
7#[cfg(feature = "block_layout")]
8mod block;
9#[cfg(feature = "flexbox")]
10mod flex;
11#[cfg(feature = "float_layout")]
12mod float;
13#[cfg(feature = "grid")]
14mod grid;
15
16pub use self::alignment::{
17    AlignContent, AlignContentKeyword, AlignItems, AlignItemsKeyword, AlignSelf, AlignmentSafety, JustifyContent,
18    JustifyItems, JustifySelf,
19};
20pub use self::available_space::AvailableSpace;
21pub use self::compact_length::CompactLength;
22pub use self::dimension::{
23    Dimension, ExpandedDimension, ExpandedLengthPercentage, ExpandedLengthPercentageAuto, LengthPercentage,
24    LengthPercentageAuto,
25};
26use crate::sys::DefaultCheapStr;
27
28#[cfg(feature = "block_layout")]
29pub use self::block::{BlockContainerStyle, BlockItemStyle, TextAlign};
30#[cfg(feature = "flexbox")]
31pub use self::flex::{FlexDirection, FlexWrap, FlexboxContainerStyle, FlexboxItemStyle};
32#[cfg(feature = "float_layout")]
33pub use self::float::{Clear, Float, FloatDirection};
34#[cfg(feature = "grid")]
35pub use self::grid::{
36    ExpandedMaxTrackSizingFunction, ExpandedMinTrackSizingFunction, GenericGridPlacement, GenericGridTemplateComponent,
37    GenericRepetition, GridAutoFlow, GridAutoTracks, GridContainerStyle, GridItemStyle, GridPlacement,
38    GridTemplateComponent, GridTemplateRepetition, GridTemplateTracks, MaxTrackSizingFunction, MinTrackSizingFunction,
39    RepetitionCount, TrackSizingFunction,
40};
41#[cfg(feature = "grid")]
42pub(crate) use self::grid::{GridAreaAxis, GridAreaEnd};
43#[cfg(feature = "grid")]
44pub use self::grid::{GridTemplateArea, GridTemplateAreas, NamedGridLine, TemplateLineNames};
45#[cfg(feature = "grid")]
46pub(crate) use self::grid::{NonNamedGridPlacement, OriginZeroGridPlacement};
47
48use crate::geometry::{Point, Rect, Size};
49use crate::style_helpers::TaffyAuto as _;
50use core::fmt::Debug;
51
52#[cfg(feature = "grid")]
53use crate::geometry::Line;
54#[cfg(feature = "serde")]
55use crate::style_helpers;
56#[cfg(feature = "grid")]
57use crate::util::sys::GridTrackVec;
58
59use crate::sys::String;
60
61/// Trait that represents a cheaply clonable string. If you're unsure what to use here
62/// consider `Arc<str>` or `string_cache::Atom`.
63#[cfg(any(feature = "alloc", feature = "std"))]
64pub trait CheapCloneStr:
65    AsRef<str> + for<'a> From<&'a str> + From<String> + PartialEq + Eq + Clone + Default + Debug + 'static
66{
67}
68#[cfg(any(feature = "alloc", feature = "std"))]
69impl<T> CheapCloneStr for T where
70    T: AsRef<str> + for<'a> From<&'a str> + From<String> + PartialEq + Eq + Clone + Default + Debug + 'static
71{
72}
73
74/// Trait that represents a cheaply clonable string. If you're unsure what to use here
75/// consider `Arc<str>` or `string_cache::Atom`.
76#[cfg(not(any(feature = "alloc", feature = "std")))]
77pub trait CheapCloneStr {}
78#[cfg(not(any(feature = "alloc", feature = "std")))]
79impl<T> CheapCloneStr for T {}
80
81/// The core set of styles that are shared between all CSS layout nodes
82///
83/// Note that all methods come with a default implementation which simply returns the default value for that style property
84/// but this is a just a convenience to save on boilerplate for styles that your implementation doesn't support. You will need
85/// to override the default implementation for each style property that your style type actually supports.
86pub trait CoreStyle {
87    /// The type of custom identifiers used to identify named grid lines and areas
88    type CustomIdent: CheapCloneStr;
89
90    /// Which box generation mode should be used
91    #[inline(always)]
92    fn box_generation_mode(&self) -> BoxGenerationMode {
93        BoxGenerationMode::DEFAULT
94    }
95    /// Is block layout?
96    ///
97    /// This should only return `true` for `display: block`, and NOT for `display: flow-root`.
98    /// Flow-root boxes establish a new block formatting context and must not be treated as
99    /// being part of their parent's block formatting context (which is what this method controls).
100    #[inline(always)]
101    fn is_block(&self) -> bool {
102        false
103    }
104    /// Is it a compressible replaced element?
105    /// <https://drafts.csswg.org/css-sizing-3/#min-content-zero>
106    #[inline(always)]
107    fn is_compressible_replaced(&self) -> bool {
108        false
109    }
110    /// Which box do size styles apply to
111    #[inline(always)]
112    fn box_sizing(&self) -> BoxSizing {
113        BoxSizing::BorderBox
114    }
115
116    /// The direction of text, table and grid columns, and horizontal overflow.
117    #[inline(always)]
118    fn direction(&self) -> Direction {
119        Direction::Ltr
120    }
121
122    // Overflow properties
123    /// How children overflowing their container should affect layout
124    #[inline(always)]
125    fn overflow(&self) -> Point<Overflow> {
126        Style::<Self::CustomIdent>::DEFAULT.overflow
127    }
128    /// How much space (in points) should be reserved for the scrollbars of `Overflow::Scroll` and `Overflow::Auto` nodes.
129    #[inline(always)]
130    fn scrollbar_width(&self) -> f32 {
131        0.0
132    }
133
134    // Position properties
135    /// What should the `position` value of this struct use as a base offset?
136    #[inline(always)]
137    fn position(&self) -> Position {
138        Style::<Self::CustomIdent>::DEFAULT.position
139    }
140    /// How should the position of this element be tweaked relative to the layout defined?
141    #[inline(always)]
142    fn inset(&self) -> Rect<LengthPercentageAuto> {
143        Style::<Self::CustomIdent>::DEFAULT.inset
144    }
145
146    // Size properies
147    /// Sets the initial size of the item
148    #[inline(always)]
149    fn size(&self) -> Size<Dimension> {
150        Style::<Self::CustomIdent>::DEFAULT.size
151    }
152    /// Controls the minimum size of the item
153    #[inline(always)]
154    fn min_size(&self) -> Size<LengthPercentageAuto> {
155        Style::<Self::CustomIdent>::DEFAULT.min_size
156    }
157    /// Controls the maximum size of the item
158    #[inline(always)]
159    fn max_size(&self) -> Size<LengthPercentageAuto> {
160        Style::<Self::CustomIdent>::DEFAULT.max_size
161    }
162    /// Sets the preferred aspect ratio for the item
163    /// The ratio is calculated as width divided by height.
164    #[inline(always)]
165    fn aspect_ratio(&self) -> Option<f32> {
166        Style::<Self::CustomIdent>::DEFAULT.aspect_ratio
167    }
168
169    // Spacing Properties
170    /// How large should the margin be on each side?
171    #[inline(always)]
172    fn margin(&self) -> Rect<LengthPercentageAuto> {
173        Style::<Self::CustomIdent>::DEFAULT.margin
174    }
175    /// How large should the padding be on each side?
176    #[inline(always)]
177    fn padding(&self) -> Rect<LengthPercentage> {
178        Style::<Self::CustomIdent>::DEFAULT.padding
179    }
180    /// How large should the border be on each side?
181    #[inline(always)]
182    fn border(&self) -> Rect<LengthPercentage> {
183        Style::<Self::CustomIdent>::DEFAULT.border
184    }
185
186    /// The layout-affecting parts of the CSS `contain` property that apply to this node
187    #[inline(always)]
188    fn contain(&self) -> Contain {
189        Contain::NONE
190    }
191}
192
193/// Sets the layout used for the children of this node
194///
195/// The default values depends on on which feature flags are enabled. The order of precedence is: Flex, Grid, Block, None.
196#[derive(Copy, Clone, PartialEq, Eq, Debug)]
197#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
198pub enum Display {
199    /// The children will follow the block layout algorithm
200    #[cfg(feature = "block_layout")]
201    Block,
202    /// The children will follow the block layout algorithm and establish a new block formatting context
203    #[cfg(feature = "block_layout")]
204    FlowRoot,
205    /// The children will follow the flexbox layout algorithm
206    #[cfg(feature = "flexbox")]
207    Flex,
208    /// The children will follow the CSS Grid layout algorithm
209    #[cfg(feature = "grid")]
210    Grid,
211    /// The node is hidden, and it's children will also be hidden
212    None,
213}
214
215impl Display {
216    /// The default Display mode
217    #[cfg(feature = "flexbox")]
218    pub const DEFAULT: Display = Display::Flex;
219
220    /// The default Display mode
221    #[cfg(all(feature = "grid", not(feature = "flexbox")))]
222    pub const DEFAULT: Display = Display::Grid;
223
224    /// The default Display mode
225    #[cfg(all(feature = "block_layout", not(feature = "flexbox"), not(feature = "grid")))]
226    pub const DEFAULT: Display = Display::Block;
227
228    /// The default Display mode
229    #[cfg(all(not(feature = "flexbox"), not(feature = "grid"), not(feature = "block_layout")))]
230    pub const DEFAULT: Display = Display::None;
231}
232
233impl Default for Display {
234    fn default() -> Self {
235        Self::DEFAULT
236    }
237}
238
239#[cfg(feature = "parse")]
240crate::util::parse::impl_parse_for_keyword_enum!(Display,
241    "none" => None,
242    #[cfg(feature = "flexbox")]
243    "flex" => Flex,
244    #[cfg(feature = "grid")]
245    "grid" => Grid,
246    #[cfg(feature = "block_layout")]
247    "block" => Block,
248    #[cfg(feature = "block_layout")]
249    "flow-root" => FlowRoot,
250);
251
252impl core::fmt::Display for Display {
253    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
254        match self {
255            Display::None => write!(f, "NONE"),
256            #[cfg(feature = "block_layout")]
257            Display::Block => write!(f, "BLOCK"),
258            #[cfg(feature = "block_layout")]
259            Display::FlowRoot => write!(f, "FLOW-ROOT"),
260            #[cfg(feature = "flexbox")]
261            Display::Flex => write!(f, "FLEX"),
262            #[cfg(feature = "grid")]
263            Display::Grid => write!(f, "GRID"),
264        }
265    }
266}
267
268/// An abstracted version of the CSS `display` property where any value other than "none" is represented by "normal"
269/// See: <https://www.w3.org/TR/css-display-3/#box-generation>
270#[derive(Copy, Clone, PartialEq, Eq, Debug)]
271#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
272pub enum BoxGenerationMode {
273    /// The node generates a box in the regular way
274    Normal,
275    /// The node and it's descendants generate no boxes (they are hidden)
276    None,
277}
278
279impl BoxGenerationMode {
280    /// The default of BoxGenerationMode
281    pub const DEFAULT: BoxGenerationMode = BoxGenerationMode::Normal;
282}
283
284impl Default for BoxGenerationMode {
285    fn default() -> Self {
286        Self::DEFAULT
287    }
288}
289
290/// The positioning strategy for this item.
291///
292/// This controls both how the origin is determined for the [`Style::position`] field,
293/// and whether or not the item will be controlled by flexbox's layout algorithm.
294///
295/// WARNING: this enum follows the behavior of [CSS's `position` property](https://developer.mozilla.org/en-US/docs/Web/CSS/position),
296/// which can be unintuitive.
297///
298/// [`Position::Relative`] is the default value, in contrast to the default behavior in CSS.
299#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
300#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
301pub enum Position {
302    /// The offset is computed relative to the final position given by the layout algorithm.
303    /// Offsets do not affect the position of any other items; they are effectively a correction factor applied at the end.
304    #[default]
305    Relative,
306    /// The offset is computed relative to this item's closest positioned ancestor, if any.
307    /// Otherwise, it is placed relative to the origin.
308    /// No space is created for the item in the page layout, and its size will not be altered.
309    ///
310    /// WARNING: to opt-out of layouting entirely, you must use [`Display::None`] instead on your [`Style`] object.
311    Absolute,
312}
313
314#[cfg(feature = "parse")]
315crate::util::parse::impl_parse_for_keyword_enum!(Position,
316    "relative" => Relative,
317    "absolute" => Absolute,
318);
319
320/// Specifies whether size styles for this node are assigned to the node's "content box" or "border box"
321///
322/// - The "content box" is the node's inner size excluding padding, border and margin
323/// - The "border box" is the node's outer size including padding and border (but still excluding margin)
324///
325/// This property modifies the application of the following styles:
326///
327///   - `size`
328///   - `min_size`
329///   - `max_size`
330///   - `flex_basis`
331///
332/// See <https://developer.mozilla.org/en-US/docs/Web/CSS/box-sizing>
333#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
334#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
335pub enum BoxSizing {
336    /// Size styles such size, min_size, max_size specify the box's "border box" (the size excluding margin but including padding/border)
337    #[default]
338    BorderBox,
339    /// Size styles such size, min_size, max_size specify the box's "content box" (the size excluding padding/border/margin)
340    ContentBox,
341}
342
343#[cfg(feature = "parse")]
344crate::util::parse::impl_parse_for_keyword_enum!(BoxSizing,
345    "border-box" => BorderBox,
346    "content-box" => ContentBox,
347);
348
349/// How children overflowing their container should affect layout
350///
351/// In CSS the primary effect of this property is to control whether contents of a parent container that overflow that container should
352/// be displayed anyway, be clipped, or trigger the container to become a scroll container. However it also has secondary effects on layout,
353/// the main ones being:
354///
355///   - The automatic minimum size Flexbox/CSS Grid items with non-`Visible` overflow is `0` rather than being content based
356///   - `Overflow::Scroll` nodes have space in the layout reserved for a scrollbar (width controlled by the `scrollbar_width` property)
357///
358/// In Taffy, we only implement the layout related secondary effects as we are not concerned with drawing/painting. The amount of space reserved for
359/// a scrollbar is controlled by the `scrollbar_width` property. If this is `0` then `Scroll` behaves identically to `Hidden`.
360///
361/// <https://developer.mozilla.org/en-US/docs/Web/CSS/overflow>
362#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
363#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
364pub enum Overflow {
365    /// The automatic minimum size of this node as a flexbox/grid item should be based on the size of its content.
366    /// Content that overflows this node *should* contribute to the scroll region of its parent.
367    #[default]
368    Visible,
369    /// The automatic minimum size of this node as a flexbox/grid item should be based on the size of its content.
370    /// Content that overflows this node should *not* contribute to the scroll region of its parent.
371    Clip,
372    /// The automatic minimum size of this node as a flexbox/grid item should be `0`.
373    /// Content that overflows this node should *not* contribute to the scroll region of its parent.
374    Hidden,
375    /// The automatic minimum size of this node as a flexbox/grid item should be `0`. Additionally, space should be reserved
376    /// for a scrollbar. The amount of space reserved is controlled by the `scrollbar_width` property.
377    /// Content that overflows this node should *not* contribute to the scroll region of its parent.
378    Scroll,
379}
380
381impl Overflow {
382    /// Returns true for overflow modes that contain their contents (`Overflow::Hidden`, `Overflow::Scroll`, `Overflow::Auto`)
383    /// or else false for overflow modes that allow their contains to spill (`Overflow::Visible`).
384    #[inline(always)]
385    pub fn is_scroll_container(self) -> bool {
386        match self {
387            Self::Visible | Self::Clip => false,
388            Self::Hidden | Self::Scroll => true,
389        }
390    }
391
392    /// Returns `Some(0.0)` if the overflow mode would cause the automatic minimum size of a Flexbox or CSS Grid item
393    /// to be `0`. Else returns None.
394    #[inline(always)]
395    pub(crate) fn maybe_into_automatic_min_size(self) -> Option<f32> {
396        match self.is_scroll_container() {
397            true => Some(0.0),
398            false => None,
399        }
400    }
401}
402
403#[cfg(feature = "parse")]
404crate::util::parse::impl_parse_for_keyword_enum!(Overflow,
405    "visible" => Visible,
406    "hidden" => Hidden,
407    "clip" => Clip,
408    "scroll" => Scroll,
409);
410
411/// The layout-affecting parts of the CSS `contain` property.
412///
413/// Containment limits the ways in which a box's contents can affect layout outside of the box
414/// (and vice versa). Taffy implements the layout-relevant containment types:
415///
416///   - [`Contain::LAYOUT`]: the box establishes an independent formatting context, and is treated
417///     as having no baseline for baseline-alignment purposes (layout containment).
418///   - [`Contain::PAINT`]: the box establishes an independent formatting context. Paint
419///     containment's other effects (clipping, containing absolutely-positioned descendants,
420///     stacking context) are outside of Taffy's scope.
421///
422/// The `style` containment type has no effect on layout and is therefore not represented
423/// (it is accepted and ignored when parsing). Size and inline-size containment are not
424/// currently implemented.
425///
426/// <https://developer.mozilla.org/en-US/docs/Web/CSS/contain>
427#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
428#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
429pub struct Contain(u8);
430
431impl Contain {
432    /// No containment (the default)
433    pub const NONE: Contain = Contain(0);
434    /// Layout containment: the box establishes an independent formatting context and is treated
435    /// as having no baseline for baseline-alignment purposes.
436    /// <https://drafts.csswg.org/css-contain-2/#containment-layout>
437    pub const LAYOUT: Contain = Contain(1 << 0);
438    /// Paint containment: the box establishes an independent formatting context. Its other
439    /// effects don't affect layout.
440    /// <https://drafts.csswg.org/css-contain-2/#containment-paint>
441    pub const PAINT: Contain = Contain(1 << 1);
442    /// The containment implied by `contain: content` (`layout paint`, ignoring style containment)
443    pub const CONTENT: Contain = Contain(Contain::LAYOUT.0 | Contain::PAINT.0);
444
445    /// The default containment (no containment)
446    pub const DEFAULT: Contain = Contain::NONE;
447
448    /// Returns whether `self` contains all of the containment types in `other`
449    #[inline(always)]
450    pub const fn contains(self, other: Contain) -> bool {
451        self.0 & other.0 == other.0
452    }
453
454    /// Returns whether `self` contains any of the containment types in `other`
455    #[inline(always)]
456    pub const fn intersects(self, other: Contain) -> bool {
457        self.0 & other.0 != 0
458    }
459
460    /// Returns the union of the containment types in `self` and `other`
461    #[inline(always)]
462    pub const fn union(self, other: Contain) -> Contain {
463        Contain(self.0 | other.0)
464    }
465
466    /// Whether this containment causes the box to establish an independent formatting context
467    /// (both layout and paint containment do)
468    #[inline(always)]
469    pub const fn establishes_independent_formatting_context(self) -> bool {
470        self.intersects(Contain::LAYOUT.union(Contain::PAINT))
471    }
472
473    /// Whether this containment suppresses the box's baseline for baseline-alignment purposes
474    /// (layout containment does, paint containment does not)
475    #[inline(always)]
476    pub const fn suppresses_baseline(self) -> bool {
477        self.contains(Contain::LAYOUT)
478    }
479
480    /// Whether this containment prevents the box's overflowing content from contributing to an
481    /// ancestor's scrollable overflow region (layout containment treats such overflow as ink
482    /// overflow; paint containment clips it)
483    #[inline(always)]
484    pub const fn contains_scrollable_overflow(self) -> bool {
485        self.intersects(Contain::LAYOUT.union(Contain::PAINT))
486    }
487}
488
489impl core::ops::BitOr for Contain {
490    type Output = Contain;
491    #[inline(always)]
492    fn bitor(self, rhs: Contain) -> Contain {
493        self.union(rhs)
494    }
495}
496
497impl core::ops::BitOrAssign for Contain {
498    #[inline(always)]
499    fn bitor_assign(&mut self, rhs: Contain) {
500        *self = self.union(rhs);
501    }
502}
503
504#[cfg(feature = "parse")]
505impl crate::util::parse::FromCss for Contain {
506    fn from_css<'i>(input: &mut crate::util::parse::Parser<'i, '_>) -> crate::util::parse::CssParseResult<'i, Self> {
507        /// Duplicate-detection bit for the ignored `style` keyword, which does not map to a
508        /// `Contain` flag
509        const STYLE_BIT: u8 = 1 << 6;
510
511        let mut flags = Contain::NONE;
512        let mut seen: u8 = 0;
513
514        loop {
515            let ident = input.expect_ident()?.clone();
516            let (flag, seen_bit) = cssparser::match_ignore_ascii_case! { &*ident,
517                // Single-keyword values (only valid on their own; `parse_entirely` in the
518                // `FromStr` impl rejects trailing keywords, and a leading keyword before them
519                // is rejected by the `seen != 0` check below)
520                "none" | "content" => {
521                    if seen != 0 || !input.is_exhausted() {
522                        return Err(input.new_unexpected_token_error(crate::util::parse::Token::Ident(ident)));
523                    }
524                    return Ok(cssparser::match_ignore_ascii_case! { &*ident,
525                        "content" => Contain::CONTENT,
526                        _ => Contain::NONE,
527                    });
528                },
529                "layout" => (Contain::LAYOUT, Contain::LAYOUT.0),
530                "paint" => (Contain::PAINT, Contain::PAINT.0),
531                // `style` containment has no layout effect: accept and ignore it so that real
532                // CSS values round-trip
533                "style" => (Contain::NONE, STYLE_BIT),
534                _ => {
535                    return Err(input.new_unexpected_token_error(crate::util::parse::Token::Ident(ident)));
536                }
537            };
538
539            // Reject duplicate keywords
540            if seen & seen_bit != 0 {
541                return Err(input.new_unexpected_token_error(crate::util::parse::Token::Ident(ident)));
542            }
543            seen |= seen_bit;
544            flags |= flag;
545
546            if input.is_exhausted() {
547                return Ok(flags);
548            }
549        }
550    }
551}
552#[cfg(feature = "parse")]
553crate::util::parse::from_str_from_css!(Contain);
554
555/// Sets the direction of text, table and grid columns, and horizontal overflow.
556/// <https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/direction>
557#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
558#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
559pub enum Direction {
560    #[default]
561    /// Left-to-right
562    Ltr,
563    /// Right-to-left
564    Rtl,
565}
566
567impl Direction {
568    /// Returns true if the direction is right-to-left
569    #[inline]
570    pub(crate) fn is_rtl(&self) -> bool {
571        matches!(self, Direction::Rtl)
572    }
573}
574
575#[cfg(feature = "parse")]
576crate::util::parse::impl_parse_for_keyword_enum!(Direction,
577    "ltr" => Ltr,
578    "rtl" => Rtl,
579);
580
581/// A typed representation of the CSS style information for a single node.
582///
583/// The most important idea in flexbox is the notion of a "main" and "cross" axis, which are always perpendicular to each other.
584/// The orientation of these axes are controlled via the [`FlexDirection`] field of this struct.
585///
586/// This struct follows the [CSS equivalent](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox) directly;
587/// information about the behavior on the web should transfer directly.
588///
589/// Detailed information about the exact behavior of each of these fields
590/// can be found on [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS) by searching for the field name.
591/// The distinction between margin, padding and border is explained well in
592/// this [introduction to the box model](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Introduction_to_the_CSS_box_model).
593///
594/// If the behavior does not match the flexbox layout algorithm on the web, please file a bug!
595#[derive(Clone, PartialEq, Debug)]
596#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
597#[cfg_attr(feature = "serde", serde(default))]
598pub struct Style<S: CheapCloneStr = DefaultCheapStr> {
599    /// This is a dummy field which is necessary to make Taffy compile with the `grid` feature disabled
600    /// It should always be set to `core::marker::PhantomData`.
601    pub dummy: core::marker::PhantomData<S>,
602    /// What layout strategy should be used?
603    pub display: Display,
604    /// Whether a child is display:table or not. This affects children of block layouts.
605    /// This should really be part of `Display`, but it is currently seperate because table layout isn't implemented
606    pub item_is_table: bool,
607    /// Is it a replaced element like an image or form field?
608    /// <https://drafts.csswg.org/css-sizing-3/#min-content-zero>
609    pub item_is_replaced: bool,
610    /// Should size styles apply to the content box or the border box of the node
611    pub box_sizing: BoxSizing,
612    /// Sets the direction of text, table and grid columns, and horizontal overflow.
613    pub direction: Direction,
614
615    // Overflow properties
616    /// How children overflowing their container should affect layout
617    pub overflow: Point<Overflow>,
618    /// How much space (in points) should be reserved for the scrollbars of `Overflow::Scroll` and `Overflow::Auto` nodes.
619    pub scrollbar_width: f32,
620    /// The layout-affecting parts of the CSS `contain` property
621    pub contain: Contain,
622
623    #[cfg(feature = "float_layout")]
624    /// Should the box be floated
625    pub float: Float,
626    #[cfg(feature = "float_layout")]
627    /// Should the box clear floats
628    pub clear: Clear,
629
630    // Position properties
631    /// What should the `position` value of this struct use as a base offset?
632    pub position: Position,
633    /// How should the position of this element be tweaked relative to the layout defined?
634    #[cfg_attr(feature = "serde", serde(default = "style_helpers::auto"))]
635    pub inset: Rect<LengthPercentageAuto>,
636
637    // Size properties
638    /// Sets the initial size of the item
639    #[cfg_attr(feature = "serde", serde(default = "style_helpers::auto"))]
640    pub size: Size<Dimension>,
641    /// Controls the minimum size of the item
642    #[cfg_attr(feature = "serde", serde(default = "style_helpers::auto"))]
643    pub min_size: Size<LengthPercentageAuto>,
644    /// Controls the maximum size of the item
645    #[cfg_attr(feature = "serde", serde(default = "style_helpers::auto"))]
646    pub max_size: Size<LengthPercentageAuto>,
647    /// Sets the preferred aspect ratio for the item
648    ///
649    /// The ratio is calculated as width divided by height.
650    pub aspect_ratio: Option<f32>,
651
652    // Spacing Properties
653    /// How large should the margin be on each side?
654    #[cfg_attr(feature = "serde", serde(default = "style_helpers::zero"))]
655    pub margin: Rect<LengthPercentageAuto>,
656    /// How large should the padding be on each side?
657    #[cfg_attr(feature = "serde", serde(default = "style_helpers::zero"))]
658    pub padding: Rect<LengthPercentage>,
659    /// How large should the border be on each side?
660    #[cfg_attr(feature = "serde", serde(default = "style_helpers::zero"))]
661    pub border: Rect<LengthPercentage>,
662
663    // Alignment properties
664    /// How this node's children aligned in the cross/block axis?
665    #[cfg(any(feature = "flexbox", feature = "grid"))]
666    pub align_items: Option<AlignItems>,
667    /// How this node should be aligned in the cross/block axis
668    /// Falls back to the parents [`AlignItems`] if not set
669    #[cfg(any(feature = "flexbox", feature = "grid"))]
670    pub align_self: Option<AlignSelf>,
671    /// How this node's children should be aligned in the inline axis
672    #[cfg(feature = "grid")]
673    pub justify_items: Option<AlignItems>,
674    /// How this node should be aligned in the inline axis
675    /// Falls back to the parents [`JustifyItems`] if not set
676    #[cfg(feature = "grid")]
677    pub justify_self: Option<AlignSelf>,
678    /// How should content contained within this item be aligned in the cross/block axis
679    #[cfg(any(feature = "flexbox", feature = "grid", feature = "block_layout"))]
680    pub align_content: Option<AlignContent>,
681    /// How should content contained within this item be aligned in the main/inline axis
682    #[cfg(any(feature = "flexbox", feature = "grid"))]
683    pub justify_content: Option<JustifyContent>,
684    /// How large should the gaps between items in a grid or flex container be?
685    #[cfg(any(feature = "flexbox", feature = "grid"))]
686    #[cfg_attr(feature = "serde", serde(default = "style_helpers::zero"))]
687    pub gap: Size<LengthPercentage>,
688
689    // Block container properties
690    /// How items elements should aligned in the inline axis
691    #[cfg(feature = "block_layout")]
692    pub text_align: TextAlign,
693
694    // Flexbox container properties
695    /// Which direction does the main axis flow in?
696    #[cfg(feature = "flexbox")]
697    pub flex_direction: FlexDirection,
698    /// Should elements wrap, or stay in a single line?
699    #[cfg(feature = "flexbox")]
700    pub flex_wrap: FlexWrap,
701    /// The minimum number of flex lines requested for a multi-line container. When items are
702    /// balanced ([`FlexWrap::Balance`] or [`FlexWrap::BalanceReverse`]) they are balanced into
703    /// at least this many lines. For any multi-line container, definite cross-axis available
704    /// space for measuring items is divided between this many lines.
705    ///
706    /// 1 is the default value, and this value must be at least 1.
707    #[cfg(feature = "flexbox_balance")]
708    pub flex_line_count: u16,
709
710    // Flexbox item properties
711    /// Sets the initial main axis size of the item
712    #[cfg(feature = "flexbox")]
713    pub flex_basis: Dimension,
714    /// The relative rate at which this item grows when it is expanding to fill space
715    ///
716    /// 0.0 is the default value, and this value must be positive.
717    #[cfg(feature = "flexbox")]
718    pub flex_grow: f32,
719    /// The relative rate at which this item shrinks when it is contracting to fit into space
720    ///
721    /// 1.0 is the default value, and this value must be positive.
722    #[cfg(feature = "flexbox")]
723    pub flex_shrink: f32,
724
725    // Grid container properies
726    /// Defines the track sizing functions (heights) of the grid rows
727    #[cfg(feature = "grid")]
728    pub grid_template_rows: GridTrackVec<GridTemplateComponent<S>>,
729    /// Defines the track sizing functions (widths) of the grid columns
730    #[cfg(feature = "grid")]
731    pub grid_template_columns: GridTrackVec<GridTemplateComponent<S>>,
732    /// Defines the size of implicitly created rows
733    #[cfg(feature = "grid")]
734    pub grid_auto_rows: GridTrackVec<TrackSizingFunction>,
735    /// Defined the size of implicitly created columns
736    #[cfg(feature = "grid")]
737    pub grid_auto_columns: GridTrackVec<TrackSizingFunction>,
738    /// Controls how items get placed into the grid for auto-placed items
739    #[cfg(feature = "grid")]
740    pub grid_auto_flow: GridAutoFlow,
741
742    // Grid container named properties
743    /// Defines the rectangular grid areas
744    #[cfg(feature = "grid")]
745    pub grid_template_areas: Option<GridTemplateAreas<S>>,
746    /// The named lines between the columns
747    #[cfg(feature = "grid")]
748    pub grid_template_column_names: GridTrackVec<GridTrackVec<S>>,
749    /// The named lines between the rows
750    #[cfg(feature = "grid")]
751    pub grid_template_row_names: GridTrackVec<GridTrackVec<S>>,
752
753    // Grid child properties
754    /// Defines which row in the grid the item should start and end at
755    #[cfg(feature = "grid")]
756    pub grid_row: Line<GridPlacement<S>>,
757    /// Defines which column in the grid the item should start and end at
758    #[cfg(feature = "grid")]
759    pub grid_column: Line<GridPlacement<S>>,
760}
761
762impl<S: CheapCloneStr> Style<S> {
763    /// The [`Default`] layout, in a form that can be used in const functions
764    pub const DEFAULT: Style<S> = Style {
765        dummy: core::marker::PhantomData,
766        display: Display::DEFAULT,
767        item_is_table: false,
768        item_is_replaced: false,
769        box_sizing: BoxSizing::BorderBox,
770        direction: Direction::Ltr,
771        overflow: Point { x: Overflow::Visible, y: Overflow::Visible },
772        scrollbar_width: 0.0,
773        contain: Contain::NONE,
774        #[cfg(feature = "float_layout")]
775        float: Float::None,
776        #[cfg(feature = "float_layout")]
777        clear: Clear::None,
778        position: Position::Relative,
779        inset: Rect::auto(),
780        margin: Rect::zero(),
781        padding: Rect::zero(),
782        border: Rect::zero(),
783        size: Size::auto(),
784        min_size: Size::auto(),
785        max_size: Size::auto(),
786        aspect_ratio: None,
787        #[cfg(any(feature = "flexbox", feature = "grid"))]
788        gap: Size::zero(),
789        // Alignment
790        #[cfg(any(feature = "flexbox", feature = "grid"))]
791        align_items: None,
792        #[cfg(any(feature = "flexbox", feature = "grid"))]
793        align_self: None,
794        #[cfg(feature = "grid")]
795        justify_items: None,
796        #[cfg(feature = "grid")]
797        justify_self: None,
798        #[cfg(any(feature = "flexbox", feature = "grid", feature = "block_layout"))]
799        align_content: None,
800        #[cfg(any(feature = "flexbox", feature = "grid"))]
801        justify_content: None,
802        // Block
803        #[cfg(feature = "block_layout")]
804        text_align: TextAlign::Auto,
805        // Flexbox
806        #[cfg(feature = "flexbox")]
807        flex_direction: FlexDirection::Row,
808        #[cfg(feature = "flexbox")]
809        flex_wrap: FlexWrap::NoWrap,
810        #[cfg(feature = "flexbox_balance")]
811        flex_line_count: 1,
812        #[cfg(feature = "flexbox")]
813        flex_grow: 0.0,
814        #[cfg(feature = "flexbox")]
815        flex_shrink: 1.0,
816        #[cfg(feature = "flexbox")]
817        flex_basis: Dimension::AUTO,
818        // Grid
819        #[cfg(feature = "grid")]
820        grid_template_rows: GridTrackVec::new(),
821        #[cfg(feature = "grid")]
822        grid_template_columns: GridTrackVec::new(),
823        #[cfg(feature = "grid")]
824        grid_template_areas: None,
825        #[cfg(feature = "grid")]
826        grid_template_column_names: GridTrackVec::new(),
827        #[cfg(feature = "grid")]
828        grid_template_row_names: GridTrackVec::new(),
829        #[cfg(feature = "grid")]
830        grid_auto_rows: GridTrackVec::new(),
831        #[cfg(feature = "grid")]
832        grid_auto_columns: GridTrackVec::new(),
833        #[cfg(feature = "grid")]
834        grid_auto_flow: GridAutoFlow::Row,
835        #[cfg(feature = "grid")]
836        grid_row: Line { start: GridPlacement::<S>::Auto, end: GridPlacement::<S>::Auto },
837        #[cfg(feature = "grid")]
838        grid_column: Line { start: GridPlacement::<S>::Auto, end: GridPlacement::<S>::Auto },
839    };
840}
841
842impl<S: CheapCloneStr> Default for Style<S> {
843    fn default() -> Self {
844        Style::DEFAULT
845    }
846}
847
848impl<S: CheapCloneStr> CoreStyle for Style<S> {
849    type CustomIdent = S;
850
851    #[inline(always)]
852    fn box_generation_mode(&self) -> BoxGenerationMode {
853        match self.display {
854            Display::None => BoxGenerationMode::None,
855            _ => BoxGenerationMode::Normal,
856        }
857    }
858    #[inline(always)]
859    #[cfg(feature = "block_layout")]
860    fn is_block(&self) -> bool {
861        matches!(self.display, Display::Block)
862    }
863    #[inline(always)]
864    fn is_compressible_replaced(&self) -> bool {
865        self.item_is_replaced
866    }
867    #[inline(always)]
868    fn box_sizing(&self) -> BoxSizing {
869        self.box_sizing
870    }
871    #[inline(always)]
872    fn direction(&self) -> Direction {
873        self.direction
874    }
875    #[inline(always)]
876    fn overflow(&self) -> Point<Overflow> {
877        self.overflow
878    }
879    #[inline(always)]
880    fn scrollbar_width(&self) -> f32 {
881        self.scrollbar_width
882    }
883    #[inline(always)]
884    fn position(&self) -> Position {
885        self.position
886    }
887    #[inline(always)]
888    fn inset(&self) -> Rect<LengthPercentageAuto> {
889        self.inset
890    }
891    #[inline(always)]
892    fn size(&self) -> Size<Dimension> {
893        self.size
894    }
895    #[inline(always)]
896    fn min_size(&self) -> Size<LengthPercentageAuto> {
897        self.min_size
898    }
899    #[inline(always)]
900    fn max_size(&self) -> Size<LengthPercentageAuto> {
901        self.max_size
902    }
903    #[inline(always)]
904    fn aspect_ratio(&self) -> Option<f32> {
905        self.aspect_ratio
906    }
907    #[inline(always)]
908    fn margin(&self) -> Rect<LengthPercentageAuto> {
909        self.margin
910    }
911    #[inline(always)]
912    fn padding(&self) -> Rect<LengthPercentage> {
913        self.padding
914    }
915    #[inline(always)]
916    fn border(&self) -> Rect<LengthPercentage> {
917        self.border
918    }
919    #[inline(always)]
920    fn contain(&self) -> Contain {
921        self.contain
922    }
923}
924
925impl<T: CoreStyle> CoreStyle for &'_ T {
926    type CustomIdent = T::CustomIdent;
927
928    #[inline(always)]
929    fn box_generation_mode(&self) -> BoxGenerationMode {
930        (*self).box_generation_mode()
931    }
932    #[inline(always)]
933    fn is_block(&self) -> bool {
934        (*self).is_block()
935    }
936    #[inline(always)]
937    fn is_compressible_replaced(&self) -> bool {
938        (*self).is_compressible_replaced()
939    }
940    #[inline(always)]
941    fn box_sizing(&self) -> BoxSizing {
942        (*self).box_sizing()
943    }
944    #[inline(always)]
945    fn direction(&self) -> Direction {
946        (*self).direction()
947    }
948    #[inline(always)]
949    fn overflow(&self) -> Point<Overflow> {
950        (*self).overflow()
951    }
952    #[inline(always)]
953    fn scrollbar_width(&self) -> f32 {
954        (*self).scrollbar_width()
955    }
956    #[inline(always)]
957    fn position(&self) -> Position {
958        (*self).position()
959    }
960    #[inline(always)]
961    fn inset(&self) -> Rect<LengthPercentageAuto> {
962        (*self).inset()
963    }
964    #[inline(always)]
965    fn size(&self) -> Size<Dimension> {
966        (*self).size()
967    }
968    #[inline(always)]
969    fn min_size(&self) -> Size<LengthPercentageAuto> {
970        (*self).min_size()
971    }
972    #[inline(always)]
973    fn max_size(&self) -> Size<LengthPercentageAuto> {
974        (*self).max_size()
975    }
976    #[inline(always)]
977    fn aspect_ratio(&self) -> Option<f32> {
978        (*self).aspect_ratio()
979    }
980    #[inline(always)]
981    fn margin(&self) -> Rect<LengthPercentageAuto> {
982        (*self).margin()
983    }
984    #[inline(always)]
985    fn padding(&self) -> Rect<LengthPercentage> {
986        (*self).padding()
987    }
988    #[inline(always)]
989    fn border(&self) -> Rect<LengthPercentage> {
990        (*self).border()
991    }
992    #[inline(always)]
993    fn contain(&self) -> Contain {
994        (*self).contain()
995    }
996}
997
998#[cfg(feature = "block_layout")]
999impl<S: CheapCloneStr> BlockContainerStyle for Style<S> {
1000    #[inline(always)]
1001    fn text_align(&self) -> TextAlign {
1002        self.text_align
1003    }
1004
1005    #[inline(always)]
1006    fn align_content(&self) -> Option<AlignContent> {
1007        self.align_content
1008    }
1009}
1010
1011#[cfg(feature = "block_layout")]
1012impl<T: BlockContainerStyle> BlockContainerStyle for &'_ T {
1013    #[inline(always)]
1014    fn text_align(&self) -> TextAlign {
1015        (*self).text_align()
1016    }
1017
1018    #[inline(always)]
1019    fn align_content(&self) -> Option<AlignContent> {
1020        (*self).align_content()
1021    }
1022}
1023
1024#[cfg(feature = "block_layout")]
1025impl<S: CheapCloneStr> BlockItemStyle for Style<S> {
1026    #[inline(always)]
1027    fn is_table(&self) -> bool {
1028        self.item_is_table
1029    }
1030
1031    #[cfg(feature = "float_layout")]
1032    #[inline(always)]
1033    fn float(&self) -> Float {
1034        self.float
1035    }
1036
1037    #[cfg(feature = "float_layout")]
1038    #[inline(always)]
1039    fn clear(&self) -> Clear {
1040        self.clear
1041    }
1042}
1043
1044#[cfg(feature = "block_layout")]
1045impl<T: BlockItemStyle> BlockItemStyle for &'_ T {
1046    #[inline(always)]
1047    fn is_table(&self) -> bool {
1048        (*self).is_table()
1049    }
1050
1051    #[cfg(feature = "float_layout")]
1052    #[inline(always)]
1053    fn float(&self) -> Float {
1054        (*self).float()
1055    }
1056
1057    #[cfg(feature = "float_layout")]
1058    #[inline(always)]
1059    fn clear(&self) -> Clear {
1060        (*self).clear()
1061    }
1062}
1063
1064#[cfg(feature = "flexbox")]
1065impl<S: CheapCloneStr> FlexboxContainerStyle for Style<S> {
1066    #[inline(always)]
1067    fn flex_direction(&self) -> FlexDirection {
1068        self.flex_direction
1069    }
1070    #[inline(always)]
1071    fn flex_wrap(&self) -> FlexWrap {
1072        self.flex_wrap
1073    }
1074    #[cfg(feature = "flexbox_balance")]
1075    #[inline(always)]
1076    fn flex_line_count(&self) -> u16 {
1077        self.flex_line_count
1078    }
1079    #[inline(always)]
1080    fn gap(&self) -> Size<LengthPercentage> {
1081        self.gap
1082    }
1083    #[inline(always)]
1084    fn align_content(&self) -> Option<AlignContent> {
1085        self.align_content
1086    }
1087    #[inline(always)]
1088    fn align_items(&self) -> Option<AlignItems> {
1089        self.align_items
1090    }
1091    #[inline(always)]
1092    fn justify_content(&self) -> Option<JustifyContent> {
1093        self.justify_content
1094    }
1095}
1096
1097#[cfg(feature = "flexbox")]
1098impl<T: FlexboxContainerStyle> FlexboxContainerStyle for &'_ T {
1099    #[inline(always)]
1100    fn flex_direction(&self) -> FlexDirection {
1101        (*self).flex_direction()
1102    }
1103    #[inline(always)]
1104    fn flex_wrap(&self) -> FlexWrap {
1105        (*self).flex_wrap()
1106    }
1107    #[cfg(feature = "flexbox_balance")]
1108    #[inline(always)]
1109    fn flex_line_count(&self) -> u16 {
1110        (*self).flex_line_count()
1111    }
1112    #[inline(always)]
1113    fn gap(&self) -> Size<LengthPercentage> {
1114        (*self).gap()
1115    }
1116    #[inline(always)]
1117    fn align_content(&self) -> Option<AlignContent> {
1118        (*self).align_content()
1119    }
1120    #[inline(always)]
1121    fn align_items(&self) -> Option<AlignItems> {
1122        (*self).align_items()
1123    }
1124    #[inline(always)]
1125    fn justify_content(&self) -> Option<JustifyContent> {
1126        (*self).justify_content()
1127    }
1128}
1129
1130#[cfg(feature = "flexbox")]
1131impl<S: CheapCloneStr> FlexboxItemStyle for Style<S> {
1132    #[inline(always)]
1133    fn flex_basis(&self) -> Dimension {
1134        self.flex_basis
1135    }
1136    #[inline(always)]
1137    fn flex_grow(&self) -> f32 {
1138        self.flex_grow
1139    }
1140    #[inline(always)]
1141    fn flex_shrink(&self) -> f32 {
1142        self.flex_shrink
1143    }
1144    #[inline(always)]
1145    fn align_self(&self) -> Option<AlignSelf> {
1146        self.align_self
1147    }
1148}
1149
1150#[cfg(feature = "flexbox")]
1151impl<T: FlexboxItemStyle> FlexboxItemStyle for &'_ T {
1152    #[inline(always)]
1153    fn flex_basis(&self) -> Dimension {
1154        (*self).flex_basis()
1155    }
1156    #[inline(always)]
1157    fn flex_grow(&self) -> f32 {
1158        (*self).flex_grow()
1159    }
1160    #[inline(always)]
1161    fn flex_shrink(&self) -> f32 {
1162        (*self).flex_shrink()
1163    }
1164    #[inline(always)]
1165    fn align_self(&self) -> Option<AlignSelf> {
1166        (*self).align_self()
1167    }
1168}
1169
1170#[cfg(feature = "grid")]
1171impl<S: CheapCloneStr> GridContainerStyle for Style<S> {
1172    type Repetition<'a>
1173        = &'a GridTemplateRepetition<S>
1174    where
1175        Self: 'a;
1176
1177    type TemplateTrackList<'a>
1178        = core::iter::Map<
1179        core::slice::Iter<'a, GridTemplateComponent<S>>,
1180        fn(&'a GridTemplateComponent<S>) -> GenericGridTemplateComponent<S, &'a GridTemplateRepetition<S>>,
1181    >
1182    where
1183        Self: 'a;
1184
1185    type AutoTrackList<'a>
1186        = core::iter::Copied<core::slice::Iter<'a, TrackSizingFunction>>
1187    where
1188        Self: 'a;
1189
1190    #[cfg(feature = "grid")]
1191    type TemplateLineNames<'a>
1192        = core::iter::Map<core::slice::Iter<'a, GridTrackVec<S>>, fn(&GridTrackVec<S>) -> core::slice::Iter<'_, S>>
1193    where
1194        Self: 'a;
1195    #[cfg(feature = "grid")]
1196    type GridTemplateAreas<'a>
1197        = core::iter::Cloned<core::slice::Iter<'a, GridTemplateArea<S>>>
1198    where
1199        Self: 'a;
1200
1201    #[inline(always)]
1202    fn grid_template_rows(&self) -> Option<Self::TemplateTrackList<'_>> {
1203        Some(self.grid_template_rows.iter().map(|c| c.as_component_ref()))
1204    }
1205    #[inline(always)]
1206    fn grid_template_columns(&self) -> Option<Self::TemplateTrackList<'_>> {
1207        Some(self.grid_template_columns.iter().map(|c| c.as_component_ref()))
1208    }
1209    #[inline(always)]
1210    fn grid_auto_rows(&self) -> Self::AutoTrackList<'_> {
1211        self.grid_auto_rows.iter().copied()
1212    }
1213    #[inline(always)]
1214    fn grid_auto_columns(&self) -> Self::AutoTrackList<'_> {
1215        self.grid_auto_columns.iter().copied()
1216    }
1217    #[inline(always)]
1218    fn grid_auto_flow(&self) -> GridAutoFlow {
1219        self.grid_auto_flow
1220    }
1221    #[inline(always)]
1222    fn gap(&self) -> Size<LengthPercentage> {
1223        self.gap
1224    }
1225    #[inline(always)]
1226    fn align_content(&self) -> Option<AlignContent> {
1227        self.align_content
1228    }
1229    #[inline(always)]
1230    fn justify_content(&self) -> Option<JustifyContent> {
1231        self.justify_content
1232    }
1233    #[inline(always)]
1234    fn align_items(&self) -> Option<AlignItems> {
1235        self.align_items
1236    }
1237    #[inline(always)]
1238    fn justify_items(&self) -> Option<AlignItems> {
1239        self.justify_items
1240    }
1241
1242    #[inline(always)]
1243    #[cfg(feature = "grid")]
1244    fn grid_template_areas(&self) -> Option<Self::GridTemplateAreas<'_>> {
1245        self.grid_template_areas.as_ref().map(|template| template.areas.iter().cloned())
1246    }
1247    #[inline(always)]
1248    #[cfg(feature = "grid")]
1249    fn grid_template_area_row_count(&self) -> u16 {
1250        self.grid_template_areas.as_ref().map(|template| template.row_count).unwrap_or(0)
1251    }
1252    #[inline(always)]
1253    #[cfg(feature = "grid")]
1254    fn grid_template_area_column_count(&self) -> u16 {
1255        self.grid_template_areas.as_ref().map(|template| template.column_count).unwrap_or(0)
1256    }
1257
1258    #[inline(always)]
1259    #[cfg(feature = "grid")]
1260    fn grid_template_column_names(&self) -> Option<Self::TemplateLineNames<'_>> {
1261        Some(self.grid_template_column_names.iter().map(|names| names.iter()))
1262    }
1263
1264    #[inline(always)]
1265    #[cfg(feature = "grid")]
1266    fn grid_template_row_names(&self) -> Option<Self::TemplateLineNames<'_>> {
1267        Some(self.grid_template_row_names.iter().map(|names| names.iter()))
1268    }
1269}
1270
1271#[cfg(feature = "grid")]
1272impl<T: GridContainerStyle> GridContainerStyle for &'_ T {
1273    type Repetition<'a>
1274        = T::Repetition<'a>
1275    where
1276        Self: 'a;
1277
1278    type TemplateTrackList<'a>
1279        = T::TemplateTrackList<'a>
1280    where
1281        Self: 'a;
1282
1283    type AutoTrackList<'a>
1284        = T::AutoTrackList<'a>
1285    where
1286        Self: 'a;
1287
1288    /// The type returned by grid_template_row_names and grid_template_column_names
1289    #[cfg(feature = "grid")]
1290    type TemplateLineNames<'a>
1291        = T::TemplateLineNames<'a>
1292    where
1293        Self: 'a;
1294    #[cfg(feature = "grid")]
1295    type GridTemplateAreas<'a>
1296        = T::GridTemplateAreas<'a>
1297    where
1298        Self: 'a;
1299
1300    #[inline(always)]
1301    fn grid_template_rows(&self) -> Option<Self::TemplateTrackList<'_>> {
1302        (*self).grid_template_rows()
1303    }
1304    #[inline(always)]
1305    fn grid_template_columns(&self) -> Option<Self::TemplateTrackList<'_>> {
1306        (*self).grid_template_columns()
1307    }
1308    #[inline(always)]
1309    fn grid_auto_rows(&self) -> Self::AutoTrackList<'_> {
1310        (*self).grid_auto_rows()
1311    }
1312    #[inline(always)]
1313    fn grid_auto_columns(&self) -> Self::AutoTrackList<'_> {
1314        (*self).grid_auto_columns()
1315    }
1316    #[cfg(feature = "grid")]
1317    #[inline(always)]
1318    fn grid_template_areas(&self) -> Option<Self::GridTemplateAreas<'_>> {
1319        (*self).grid_template_areas()
1320    }
1321    #[inline(always)]
1322    fn grid_template_area_row_count(&self) -> u16 {
1323        (*self).grid_template_area_row_count()
1324    }
1325    #[inline(always)]
1326    fn grid_template_area_column_count(&self) -> u16 {
1327        (*self).grid_template_area_column_count()
1328    }
1329    #[cfg(feature = "grid")]
1330    #[inline(always)]
1331    fn grid_template_column_names(&self) -> Option<Self::TemplateLineNames<'_>> {
1332        (*self).grid_template_column_names()
1333    }
1334    #[cfg(feature = "grid")]
1335    #[inline(always)]
1336    fn grid_template_row_names(&self) -> Option<Self::TemplateLineNames<'_>> {
1337        (*self).grid_template_row_names()
1338    }
1339    #[inline(always)]
1340    fn grid_auto_flow(&self) -> GridAutoFlow {
1341        (*self).grid_auto_flow()
1342    }
1343    #[inline(always)]
1344    fn gap(&self) -> Size<LengthPercentage> {
1345        (*self).gap()
1346    }
1347    #[inline(always)]
1348    fn align_content(&self) -> Option<AlignContent> {
1349        (*self).align_content()
1350    }
1351    #[inline(always)]
1352    fn justify_content(&self) -> Option<JustifyContent> {
1353        (*self).justify_content()
1354    }
1355    #[inline(always)]
1356    fn align_items(&self) -> Option<AlignItems> {
1357        (*self).align_items()
1358    }
1359    #[inline(always)]
1360    fn justify_items(&self) -> Option<AlignItems> {
1361        (*self).justify_items()
1362    }
1363}
1364
1365#[cfg(feature = "grid")]
1366impl<S: CheapCloneStr> GridItemStyle for Style<S> {
1367    #[inline(always)]
1368    fn grid_row(&self) -> Line<GridPlacement<S>> {
1369        // TODO: Investigate eliminating clone
1370        self.grid_row.clone()
1371    }
1372    #[inline(always)]
1373    fn grid_column(&self) -> Line<GridPlacement<S>> {
1374        // TODO: Investigate eliminating clone
1375        self.grid_column.clone()
1376    }
1377    #[inline(always)]
1378    fn align_self(&self) -> Option<AlignSelf> {
1379        self.align_self
1380    }
1381    #[inline(always)]
1382    fn justify_self(&self) -> Option<AlignSelf> {
1383        self.justify_self
1384    }
1385}
1386
1387#[cfg(feature = "grid")]
1388impl<T: GridItemStyle> GridItemStyle for &'_ T {
1389    #[inline(always)]
1390    fn grid_row(&self) -> Line<GridPlacement<Self::CustomIdent>> {
1391        (*self).grid_row()
1392    }
1393    #[inline(always)]
1394    fn grid_column(&self) -> Line<GridPlacement<Self::CustomIdent>> {
1395        (*self).grid_column()
1396    }
1397    #[inline(always)]
1398    fn align_self(&self) -> Option<AlignSelf> {
1399        (*self).align_self()
1400    }
1401    #[inline(always)]
1402    fn justify_self(&self) -> Option<AlignSelf> {
1403        (*self).justify_self()
1404    }
1405}
1406
1407#[cfg(test)]
1408mod tests {
1409    use std::sync::Arc;
1410
1411    use super::Style;
1412    use crate::sys::DefaultCheapStr;
1413    use crate::{geometry::*, style_helpers::TaffyAuto as _};
1414
1415    #[test]
1416    fn defaults_match() {
1417        #[cfg(feature = "grid")]
1418        use super::GridPlacement;
1419
1420        let old_defaults: Style<DefaultCheapStr> = Style {
1421            dummy: core::marker::PhantomData,
1422            display: Default::default(),
1423            item_is_table: false,
1424            item_is_replaced: false,
1425            box_sizing: Default::default(),
1426            #[cfg(feature = "float_layout")]
1427            float: Default::default(),
1428            #[cfg(feature = "float_layout")]
1429            clear: Default::default(),
1430            direction: Default::default(),
1431            overflow: Default::default(),
1432            scrollbar_width: 0.0,
1433            contain: Default::default(),
1434            position: Default::default(),
1435            #[cfg(feature = "flexbox")]
1436            flex_direction: Default::default(),
1437            #[cfg(feature = "flexbox")]
1438            flex_wrap: Default::default(),
1439            #[cfg(feature = "flexbox_balance")]
1440            flex_line_count: 1,
1441            #[cfg(any(feature = "flexbox", feature = "grid"))]
1442            align_items: Default::default(),
1443            #[cfg(any(feature = "flexbox", feature = "grid"))]
1444            align_self: Default::default(),
1445            #[cfg(feature = "grid")]
1446            justify_items: Default::default(),
1447            #[cfg(feature = "grid")]
1448            justify_self: Default::default(),
1449            #[cfg(any(feature = "flexbox", feature = "grid", feature = "block_layout"))]
1450            align_content: Default::default(),
1451            #[cfg(any(feature = "flexbox", feature = "grid"))]
1452            justify_content: Default::default(),
1453            inset: Rect::auto(),
1454            margin: Rect::zero(),
1455            padding: Rect::zero(),
1456            border: Rect::zero(),
1457            gap: Size::zero(),
1458            #[cfg(feature = "block_layout")]
1459            text_align: Default::default(),
1460            #[cfg(feature = "flexbox")]
1461            flex_grow: 0.0,
1462            #[cfg(feature = "flexbox")]
1463            flex_shrink: 1.0,
1464            #[cfg(feature = "flexbox")]
1465            flex_basis: super::Dimension::AUTO,
1466            size: Size::auto(),
1467            min_size: Size::auto(),
1468            max_size: Size::auto(),
1469            aspect_ratio: Default::default(),
1470            #[cfg(feature = "grid")]
1471            grid_template_rows: Default::default(),
1472            #[cfg(feature = "grid")]
1473            grid_template_columns: Default::default(),
1474            #[cfg(feature = "grid")]
1475            grid_template_row_names: Default::default(),
1476            #[cfg(feature = "grid")]
1477            grid_template_column_names: Default::default(),
1478            #[cfg(feature = "grid")]
1479            grid_template_areas: Default::default(),
1480            #[cfg(feature = "grid")]
1481            grid_auto_rows: Default::default(),
1482            #[cfg(feature = "grid")]
1483            grid_auto_columns: Default::default(),
1484            #[cfg(feature = "grid")]
1485            grid_auto_flow: Default::default(),
1486            #[cfg(feature = "grid")]
1487            grid_row: Line { start: GridPlacement::Auto, end: GridPlacement::Auto },
1488            #[cfg(feature = "grid")]
1489            grid_column: Line { start: GridPlacement::Auto, end: GridPlacement::Auto },
1490        };
1491
1492        assert_eq!(Style::DEFAULT, Style::<DefaultCheapStr>::default());
1493        assert_eq!(Style::DEFAULT, old_defaults);
1494    }
1495
1496    #[test]
1497    #[cfg(feature = "parse")]
1498    fn parse_contain() {
1499        use super::Contain;
1500
1501        fn parse(input: &str) -> Contain {
1502            input.parse().unwrap()
1503        }
1504
1505        assert_eq!(parse("none"), Contain::NONE);
1506        assert_eq!(parse("content"), Contain::LAYOUT | Contain::PAINT);
1507        assert_eq!(parse("layout"), Contain::LAYOUT);
1508        assert_eq!(parse("style"), Contain::NONE);
1509        assert_eq!(parse("paint"), Contain::PAINT);
1510        assert_eq!(parse("layout paint"), Contain::LAYOUT | Contain::PAINT);
1511        assert_eq!(parse("paint layout"), Contain::LAYOUT | Contain::PAINT);
1512        assert_eq!(parse("layout paint style"), Contain::LAYOUT | Contain::PAINT);
1513        assert_eq!(parse("Paint LAYOUT"), Contain::LAYOUT | Contain::PAINT);
1514        assert!("paint paint".parse::<Contain>().is_err());
1515
1516        assert!("".parse::<Contain>().is_err());
1517        assert!("banana".parse::<Contain>().is_err());
1518        assert!("layout layout".parse::<Contain>().is_err());
1519        assert!("none layout".parse::<Contain>().is_err());
1520        assert!("layout none".parse::<Contain>().is_err());
1521        assert!("content layout".parse::<Contain>().is_err());
1522        assert!("layout content".parse::<Contain>().is_err());
1523    }
1524
1525    // NOTE: Please feel free the update the sizes in this test as required. This test is here to prevent unintentional size changes
1526    // and to serve as accurate up-to-date documentation on the sizes.
1527    #[test]
1528    fn style_sizes() {
1529        use super::*;
1530        type S = crate::sys::DefaultCheapStr;
1531
1532        fn assert_type_size<T>(expected_size: usize) {
1533            let name = ::core::any::type_name::<T>();
1534            let name = name.replace("taffy::geometry::", "");
1535            let name = name.replace("taffy::style::dimension::", "");
1536            let name = name.replace("taffy::style::alignment::", "");
1537            let name = name.replace("taffy::style::flex::", "");
1538            let name = name.replace("taffy::style::grid::", "");
1539
1540            assert_eq!(
1541                ::core::mem::size_of::<T>(),
1542                expected_size,
1543                "Expected {} for be {} byte(s) but it was {} byte(s)",
1544                name,
1545                expected_size,
1546                ::core::mem::size_of::<T>(),
1547            );
1548        }
1549
1550        // Display and Position
1551        assert_type_size::<Display>(1);
1552        assert_type_size::<BoxSizing>(1);
1553        assert_type_size::<Position>(1);
1554        assert_type_size::<Overflow>(1);
1555
1556        // Dimensions and aggregations of Dimensions
1557        assert_type_size::<f32>(4);
1558        assert_type_size::<LengthPercentage>(8);
1559        assert_type_size::<LengthPercentageAuto>(8);
1560        assert_type_size::<Dimension>(8);
1561        assert_type_size::<Size<LengthPercentage>>(16);
1562        assert_type_size::<Size<LengthPercentageAuto>>(16);
1563        assert_type_size::<Size<Dimension>>(16);
1564        assert_type_size::<Rect<LengthPercentage>>(32);
1565        assert_type_size::<Rect<LengthPercentageAuto>>(32);
1566        assert_type_size::<Rect<Dimension>>(32);
1567
1568        // Alignment — `AlignContent` and `AlignItems` are structs of two `#[repr(u8)]` enums
1569        // (position keyword + safety modifier). Niche-packing in the safety byte (only 2 of
1570        // 256 values used) lets `Option<_>` stay the same size as the bare struct.
1571        assert_type_size::<AlignContentKeyword>(1);
1572        assert_type_size::<AlignItemsKeyword>(1);
1573        assert_type_size::<AlignmentSafety>(1);
1574        assert_type_size::<AlignContent>(2);
1575        assert_type_size::<AlignItems>(2);
1576        assert_type_size::<Option<AlignItems>>(2);
1577        assert_type_size::<Option<AlignContent>>(2);
1578
1579        // Flexbox Container
1580        assert_type_size::<FlexDirection>(1);
1581        assert_type_size::<FlexWrap>(1);
1582
1583        // CSS Grid Container
1584        assert_type_size::<GridAutoFlow>(1);
1585        assert_type_size::<MinTrackSizingFunction>(8);
1586        assert_type_size::<MaxTrackSizingFunction>(8);
1587        assert_type_size::<TrackSizingFunction>(16);
1588        assert_type_size::<Vec<TrackSizingFunction>>(24);
1589        assert_type_size::<Vec<GridTemplateComponent<S>>>(24);
1590
1591        // String-type dependent (String)
1592        assert_type_size::<GridTemplateComponent<String>>(56);
1593        assert_type_size::<GridPlacement<String>>(32);
1594        assert_type_size::<Line<GridPlacement<String>>>(64);
1595        assert_type_size::<Style<String>>(560);
1596
1597        // String-type dependent (Arc<str>)
1598        assert_type_size::<GridTemplateComponent<Arc<str>>>(56);
1599        assert_type_size::<GridPlacement<Arc<str>>>(24);
1600        assert_type_size::<Line<GridPlacement<Arc<str>>>>(48);
1601        assert_type_size::<Style<Arc<str>>>(528);
1602    }
1603}