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