tawasuyu

tawasuyu/taffy

A high performance rust-powered UI layout library

Rust 520HTML 244Markdown 7TOML 41339 Definitions

⎇ 7b6efafe42c49ebdf1d84a7afe8ff4e62fda94ed src/style/grid.rs Rust · 713 lines · 30.1 KB History Who touched what raw

Convert algorithms to traitified styles 7b6efafe4 · Nico Burns ·

1//! Style types for CSS Grid layout
2use super::{AlignContent, AlignItems, AlignSelf, CoreStyle, JustifyContent, LengthPercentage, Style};
3use crate::compute::grid::{GridCoordinate, GridLine, OriginZeroLine};
4use crate::geometry::{AbsoluteAxis, AbstractAxis, Line, MinMax, Size};
5use crate::style_helpers::*;
6use crate::util::sys::GridTrackVec;
7use core::cmp::{max, min};
8use core::convert::Infallible;
9
10/// The set of styles required for a CSS Grid container
11pub trait GridContainerStyle: CoreStyle {
12    /// Defines the track sizing functions (heights) of the grid rows
13    #[inline(always)]
14    fn grid_template_rows(&self) -> &[TrackSizingFunction] {
15        &[]
16    }
17    /// Defines the track sizing functions (widths) of the grid columns
18    #[inline(always)]
19    fn grid_template_columns(&self) -> &[TrackSizingFunction] {
20        &[]
21    }
22    /// Defines the size of implicitly created rows
23    #[inline(always)]
24    fn grid_auto_rows(&self) -> &[NonRepeatedTrackSizingFunction] {
25        &[]
26    }
27    /// Defined the size of implicitly created columns
28    #[inline(always)]
29    fn grid_auto_columns(&self) -> &[NonRepeatedTrackSizingFunction] {
30        &[]
31    }
32    /// Controls how items get placed into the grid for auto-placed items
33    #[inline(always)]
34    fn grid_auto_flow(&self) -> GridAutoFlow {
35        Style::DEFAULT.grid_auto_flow
36    }
37
38    /// How large should the gaps between items in a grid or flex container be?
39    #[inline(always)]
40    fn gap(&self) -> Size<LengthPercentage> {
41        Style::DEFAULT.gap
42    }
43
44    // Alignment properties
45
46    /// How should content contained within this item be aligned in the cross/block axis
47    #[inline(always)]
48    fn align_content(&self) -> Option<AlignContent> {
49        Style::DEFAULT.align_content
50    }
51    /// How should contained within this item be aligned in the main/inline axis
52    #[inline(always)]
53    fn justify_content(&self) -> Option<JustifyContent> {
54        Style::DEFAULT.justify_content
55    }
56    /// How this node's children aligned in the cross/block axis?
57    #[inline(always)]
58    fn align_items(&self) -> Option<AlignItems> {
59        Style::DEFAULT.align_items
60    }
61    /// How this node's children should be aligned in the inline axis
62    #[inline(always)]
63    fn justify_items(&self) -> Option<AlignItems> {
64        Style::DEFAULT.justify_items
65    }
66
67    /// Get a grid item's row or column placement depending on the axis passed
68    #[inline(always)]
69    fn grid_template_tracks(&self, axis: AbsoluteAxis) -> &[TrackSizingFunction] {
70        match axis {
71            AbsoluteAxis::Horizontal => self.grid_template_columns(),
72            AbsoluteAxis::Vertical => self.grid_template_rows(),
73        }
74    }
75
76    /// Get a grid container's align-content or justify-content alignment depending on the axis passed
77    #[inline(always)]
78    fn grid_align_content(&self, axis: AbstractAxis) -> AlignContent {
79        match axis {
80            AbstractAxis::Inline => self.justify_content().unwrap_or(AlignContent::Stretch),
81            AbstractAxis::Block => self.align_content().unwrap_or(AlignContent::Stretch),
82        }
83    }
84}
85
86/// The set of styles required for a CSS Grid item (child of a CSS Grid container)
87pub trait GridItemStyle: CoreStyle {
88    /// Defines which row in the grid the item should start and end at
89    #[inline(always)]
90    fn grid_row(&self) -> Line<GridPlacement> {
91        Style::DEFAULT.grid_row
92    }
93    /// Defines which column in the grid the item should start and end at
94    #[inline(always)]
95    fn grid_column(&self) -> Line<GridPlacement> {
96        Style::DEFAULT.grid_column
97    }
98
99    /// How this node should be aligned in the cross/block axis
100    /// Falls back to the parents [`AlignItems`] if not set
101    #[inline(always)]
102    fn align_self(&self) -> Option<AlignSelf> {
103        Style::DEFAULT.align_self
104    }
105    /// How this node should be aligned in the inline axis
106    /// Falls back to the parents [`super::JustifyItems`] if not set
107    #[inline(always)]
108    fn justify_self(&self) -> Option<AlignSelf> {
109        Style::DEFAULT.justify_self
110    }
111
112    /// Get a grid item's row or column placement depending on the axis passed
113    #[inline(always)]
114    fn grid_placement(&self, axis: AbsoluteAxis) -> Line<GridPlacement> {
115        match axis {
116            AbsoluteAxis::Horizontal => self.grid_column(),
117            AbsoluteAxis::Vertical => self.grid_row(),
118        }
119    }
120}
121
122/// Controls whether grid items are placed row-wise or column-wise. And whether the sparse or dense packing algorithm is used.
123///
124/// The "dense" packing algorithm attempts to fill in holes earlier in the grid, if smaller items come up later. This may cause items to appear out-of-order, when doing so would fill in holes left by larger items.
125///
126/// Defaults to [`GridAutoFlow::Row`]
127///
128/// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-flow)
129#[derive(Copy, Clone, PartialEq, Eq, Debug)]
130#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
131pub enum GridAutoFlow {
132    /// Items are placed by filling each row in turn, adding new rows as necessary
133    Row,
134    /// Items are placed by filling each column in turn, adding new columns as necessary.
135    Column,
136    /// Combines `Row` with the dense packing algorithm.
137    RowDense,
138    /// Combines `Column` with the dense packing algorithm.
139    ColumnDense,
140}
141
142impl Default for GridAutoFlow {
143    fn default() -> Self {
144        Self::Row
145    }
146}
147
148impl GridAutoFlow {
149    /// Whether grid auto placement uses the sparse placement algorithm or the dense placement algorithm
150    /// See: <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-flow#values>
151    pub fn is_dense(&self) -> bool {
152        match self {
153            Self::Row | Self::Column => false,
154            Self::RowDense | Self::ColumnDense => true,
155        }
156    }
157
158    /// Whether grid auto placement fills areas row-wise or column-wise
159    /// See: <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-flow#values>
160    pub fn primary_axis(&self) -> AbsoluteAxis {
161        match self {
162            Self::Row | Self::RowDense => AbsoluteAxis::Horizontal,
163            Self::Column | Self::ColumnDense => AbsoluteAxis::Vertical,
164        }
165    }
166}
167
168/// A grid line placement specification which is generic over the coordinate system that it uses to define
169/// grid line positions.
170///
171/// GenericGridPlacement<GridLine> is aliased as GridPlacement and is exposed to users of Taffy to define styles.
172/// GenericGridPlacement<OriginZeroLine> is aliased as OriginZeroGridPlacement and is used internally for placement computations.
173///
174/// See [`crate::compute::grid::type::coordinates`] for documentation on the different coordinate systems.
175#[derive(Copy, Clone, PartialEq, Eq, Debug)]
176#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
177pub enum GenericGridPlacement<LineType: GridCoordinate> {
178    /// Place item according to the auto-placement algorithm, and the parent's grid_auto_flow property
179    Auto,
180    /// Place item at specified line (column or row) index
181    Line(LineType),
182    /// Item should span specified number of tracks (columns or rows)
183    Span(u16),
184}
185
186/// A grid line placement using the normalized OriginZero coordinates to specify line positions.
187pub(crate) type OriginZeroGridPlacement = GenericGridPlacement<OriginZeroLine>;
188
189/// A grid line placement specification. Used for grid-[row/column]-[start/end]. Named tracks are not implemented.
190///
191/// Defaults to `GridPlacement::Auto`
192///
193/// [Specification](https://www.w3.org/TR/css3-grid-layout/#typedef-grid-row-start-grid-line)
194pub type GridPlacement = GenericGridPlacement<GridLine>;
195impl TaffyAuto for GridPlacement {
196    const AUTO: Self = Self::Auto;
197}
198impl TaffyGridLine for GridPlacement {
199    fn from_line_index(index: i16) -> Self {
200        GridPlacement::Line(GridLine::from(index))
201    }
202}
203impl TaffyGridLine for Line<GridPlacement> {
204    fn from_line_index(index: i16) -> Self {
205        Line { start: GridPlacement::from_line_index(index), end: GridPlacement::Auto }
206    }
207}
208impl TaffyGridSpan for GridPlacement {
209    fn from_span(span: u16) -> Self {
210        GridPlacement::Span(span)
211    }
212}
213impl TaffyGridSpan for Line<GridPlacement> {
214    fn from_span(span: u16) -> Self {
215        Line { start: GridPlacement::from_span(span), end: GridPlacement::Auto }
216    }
217}
218
219impl Default for GridPlacement {
220    fn default() -> Self {
221        Self::Auto
222    }
223}
224
225impl GridPlacement {
226    /// Apply a mapping function if the [`GridPlacement`] is a `Track`. Otherwise return `self` unmodified.
227    pub fn into_origin_zero_placement(self, explicit_track_count: u16) -> OriginZeroGridPlacement {
228        match self {
229            Self::Auto => OriginZeroGridPlacement::Auto,
230            Self::Span(span) => OriginZeroGridPlacement::Span(span),
231            // Grid line zero is an invalid index, so it gets treated as Auto
232            // See: https://developer.mozilla.org/en-US/docs/Web/CSS/grid-row-start#values
233            Self::Line(line) => match line.as_i16() {
234                0 => OriginZeroGridPlacement::Auto,
235                _ => OriginZeroGridPlacement::Line(line.into_origin_zero_line(explicit_track_count)),
236            },
237        }
238    }
239}
240
241impl<T: GridCoordinate> Line<GenericGridPlacement<T>> {
242    /// Resolves the span for an indefinite placement (a placement that does not consist of two `Track`s).
243    /// Panics if called on a definite placement
244    pub fn indefinite_span(&self) -> u16 {
245        use GenericGridPlacement as GP;
246        match (self.start, self.end) {
247            (GP::Line(_), GP::Auto) => 1,
248            (GP::Auto, GP::Line(_)) => 1,
249            (GP::Auto, GP::Auto) => 1,
250            (GP::Line(_), GP::Span(span)) => span,
251            (GP::Span(span), GP::Line(_)) => span,
252            (GP::Span(span), GP::Auto) => span,
253            (GP::Auto, GP::Span(span)) => span,
254            (GP::Span(span), GP::Span(_)) => span,
255            (GP::Line(_), GP::Line(_)) => panic!("indefinite_span should only be called on indefinite grid tracks"),
256        }
257    }
258}
259
260impl Line<GridPlacement> {
261    #[inline]
262    /// Whether the track position is definite in this axis (or the item will need auto placement)
263    /// The track position is definite if least one of the start and end positions is a NON-ZERO track index
264    /// (0 is an invalid line in GridLine coordinates, and falls back to "auto" which is indefinite)
265    pub fn is_definite(&self) -> bool {
266        match (self.start, self.end) {
267            (GenericGridPlacement::Line(line), _) if line.as_i16() != 0 => true,
268            (_, GenericGridPlacement::Line(line)) if line.as_i16() != 0 => true,
269            _ => false,
270        }
271    }
272
273    /// Apply a mapping function if the [`GridPlacement`] is a `Track`. Otherwise return `self` unmodified.
274    pub fn into_origin_zero(&self, explicit_track_count: u16) -> Line<OriginZeroGridPlacement> {
275        Line {
276            start: self.start.into_origin_zero_placement(explicit_track_count),
277            end: self.end.into_origin_zero_placement(explicit_track_count),
278        }
279    }
280}
281
282impl Line<OriginZeroGridPlacement> {
283    #[inline]
284    /// Whether the track position is definite in this axis (or the item will need auto placement)
285    /// The track position is definite if least one of the start and end positions is a track index
286    pub fn is_definite(&self) -> bool {
287        matches!((self.start, self.end), (GenericGridPlacement::Line(_), _) | (_, GenericGridPlacement::Line(_)))
288    }
289
290    /// If at least one of the of the start and end positions is a track index then the other end can be resolved
291    /// into a track index purely based on the information contained with the placement specification
292    pub fn resolve_definite_grid_lines(&self) -> Line<OriginZeroLine> {
293        use OriginZeroGridPlacement as GP;
294        match (self.start, self.end) {
295            (GP::Line(line1), GP::Line(line2)) => {
296                if line1 == line2 {
297                    Line { start: line1, end: line1 + 1 }
298                } else {
299                    Line { start: min(line1, line2), end: max(line1, line2) }
300                }
301            }
302            (GP::Line(line), GP::Span(span)) => Line { start: line, end: line + span },
303            (GP::Line(line), GP::Auto) => Line { start: line, end: line + 1 },
304            (GP::Span(span), GP::Line(line)) => Line { start: line - span, end: line },
305            (GP::Auto, GP::Line(line)) => Line { start: line - 1, end: line },
306            _ => panic!("resolve_definite_grid_tracks should only be called on definite grid tracks"),
307        }
308    }
309
310    /// For absolutely positioned items:
311    ///   - Tracks resolve to definite tracks
312    ///   - For Spans:
313    ///      - If the other position is a Track, they resolve to a definite track relative to the other track
314    ///      - Else resolve to None
315    ///   - Auto resolves to None
316    ///
317    /// When finally positioning the item, a value of None means that the item's grid area is bounded by the grid
318    /// container's border box on that side.
319    pub fn resolve_absolutely_positioned_grid_tracks(&self) -> Line<Option<OriginZeroLine>> {
320        use OriginZeroGridPlacement as GP;
321        match (self.start, self.end) {
322            (GP::Line(track1), GP::Line(track2)) => {
323                if track1 == track2 {
324                    Line { start: Some(track1), end: Some(track1 + 1) }
325                } else {
326                    Line { start: Some(min(track1, track2)), end: Some(max(track1, track2)) }
327                }
328            }
329            (GP::Line(track), GP::Span(span)) => Line { start: Some(track), end: Some(track + span) },
330            (GP::Line(track), GP::Auto) => Line { start: Some(track), end: None },
331            (GP::Span(span), GP::Line(track)) => Line { start: Some(track - span), end: Some(track) },
332            (GP::Auto, GP::Line(track)) => Line { start: None, end: Some(track) },
333            _ => Line { start: None, end: None },
334        }
335    }
336
337    /// If neither of the start and end positions is a track index then the other end can be resolved
338    /// into a track index if a definite start position is supplied externally
339    pub fn resolve_indefinite_grid_tracks(&self, start: OriginZeroLine) -> Line<OriginZeroLine> {
340        use OriginZeroGridPlacement as GP;
341        match (self.start, self.end) {
342            (GP::Auto, GP::Auto) => Line { start, end: start + 1 },
343            (GP::Span(span), GP::Auto) => Line { start, end: start + span },
344            (GP::Auto, GP::Span(span)) => Line { start, end: start + span },
345            (GP::Span(span), GP::Span(_)) => Line { start, end: start + span },
346            _ => panic!("resolve_indefinite_grid_tracks should only be called on indefinite grid tracks"),
347        }
348    }
349}
350
351/// Represents the start and end points of a GridItem within a given axis
352impl Default for Line<GridPlacement> {
353    fn default() -> Self {
354        Line { start: GridPlacement::Auto, end: GridPlacement::Auto }
355    }
356}
357
358/// Maximum track sizing function
359///
360/// Specifies the maximum size of a grid track. A grid track will automatically size between it's minimum and maximum size based
361/// on the size of it's contents, the amount of available space, and the sizing constraint the grid is being size under.
362/// See <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns>
363#[derive(Copy, Clone, PartialEq, Debug)]
364#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
365pub enum MaxTrackSizingFunction {
366    /// Track maximum size should be a fixed length or percentage value
367    Fixed(LengthPercentage),
368    /// Track maximum size should be content sized under a min-content constraint
369    MinContent,
370    /// Track maximum size should be content sized under a max-content constraint
371    MaxContent,
372    /// Track maximum size should be sized according to the fit-content formula
373    FitContent(LengthPercentage),
374    /// Track maximum size should be automatically sized
375    Auto,
376    /// The dimension as a fraction of the total available grid space (`fr` units in CSS)
377    /// Specified value is the numerator of the fraction. Denominator is the sum of all fraction specified in that grid dimension
378    /// Spec: <https://www.w3.org/TR/css3-grid-layout/#fr-unit>
379    Fraction(f32),
380}
381impl TaffyAuto for MaxTrackSizingFunction {
382    const AUTO: Self = Self::Auto;
383}
384impl TaffyMinContent for MaxTrackSizingFunction {
385    const MIN_CONTENT: Self = Self::MinContent;
386}
387impl TaffyMaxContent for MaxTrackSizingFunction {
388    const MAX_CONTENT: Self = Self::MaxContent;
389}
390impl TaffyFitContent for MaxTrackSizingFunction {
391    fn fit_content(argument: LengthPercentage) -> Self {
392        Self::FitContent(argument)
393    }
394}
395impl TaffyZero for MaxTrackSizingFunction {
396    const ZERO: Self = Self::Fixed(LengthPercentage::ZERO);
397}
398impl FromLength for MaxTrackSizingFunction {
399    fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
400        Self::Fixed(LengthPercentage::from_length(value))
401    }
402}
403impl FromPercent for MaxTrackSizingFunction {
404    fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
405        Self::Fixed(LengthPercentage::from_percent(percent))
406    }
407}
408impl FromFlex for MaxTrackSizingFunction {
409    fn from_flex<Input: Into<f32> + Copy>(flex: Input) -> Self {
410        Self::Fraction(flex.into())
411    }
412}
413
414impl MaxTrackSizingFunction {
415    /// Returns true if the max track sizing function is `MinContent`, `MaxContent`, `FitContent` or `Auto`, else false.
416    #[inline(always)]
417    pub fn is_intrinsic(&self) -> bool {
418        matches!(self, Self::MinContent | Self::MaxContent | Self::FitContent(_) | Self::Auto)
419    }
420
421    /// Returns true if the max track sizing function is `MaxContent`, `FitContent` or `Auto` else false.
422    /// "In all cases, treat auto and fit-content() as max-content, except where specified otherwise for fit-content()."
423    /// See: <https://www.w3.org/TR/css-grid-1/#algo-terms>
424    #[inline(always)]
425    pub fn is_max_content_alike(&self) -> bool {
426        matches!(self, Self::MaxContent | Self::FitContent(_) | Self::Auto)
427    }
428
429    /// Returns true if the max track sizing function is `Flex`, else false.
430    #[inline(always)]
431    pub fn is_flexible(&self) -> bool {
432        matches!(self, Self::Fraction(_))
433    }
434
435    /// Returns fixed point values directly. Attempts to resolve percentage values against
436    /// the passed available_space and returns if this results in a concrete value (which it
437    /// will if the available_space is `Some`). Otherwise returns None.
438    #[inline(always)]
439    pub fn definite_value(self, parent_size: Option<f32>) -> Option<f32> {
440        use MaxTrackSizingFunction::*;
441        match self {
442            Fixed(LengthPercentage::Length(size)) => Some(size),
443            Fixed(LengthPercentage::Percent(fraction)) => parent_size.map(|size| fraction * size),
444            MinContent | MaxContent | FitContent(_) | Auto | Fraction(_) => None,
445        }
446    }
447
448    /// Resolve the maximum size of the track as defined by either:
449    ///     - A fixed track sizing function
450    ///     - A percentage track sizing function (with definite available space)
451    ///     - A fit-content sizing function with fixed argument
452    ///     - A fit-content sizing function with percentage argument (with definite available space)
453    /// All other kinds of track sizing function return None.
454    #[inline(always)]
455    pub fn definite_limit(self, parent_size: Option<f32>) -> Option<f32> {
456        use MaxTrackSizingFunction::FitContent;
457        match self {
458            FitContent(LengthPercentage::Length(size)) => Some(size),
459            FitContent(LengthPercentage::Percent(fraction)) => parent_size.map(|size| fraction * size),
460            _ => self.definite_value(parent_size),
461        }
462    }
463
464    /// Resolve percentage values against the passed parent_size, returning Some(value)
465    /// Non-percentage values always return None.
466    #[inline(always)]
467    pub fn resolved_percentage_size(self, parent_size: f32) -> Option<f32> {
468        use MaxTrackSizingFunction::*;
469        match self {
470            Fixed(LengthPercentage::Percent(fraction)) => Some(fraction * parent_size),
471            Fixed(LengthPercentage::Length(_)) | MinContent | MaxContent | FitContent(_) | Auto | Fraction(_) => None,
472        }
473    }
474
475    /// Whether the track sizing functions depends on the size of the parent node
476    #[inline(always)]
477    pub fn uses_percentage(self) -> bool {
478        use MaxTrackSizingFunction::*;
479        matches!(self, Fixed(LengthPercentage::Percent(_)) | FitContent(LengthPercentage::Percent(_)))
480    }
481}
482
483/// Minimum track sizing function
484///
485/// Specifies the minimum size of a grid track. A grid track will automatically size between it's minimum and maximum size based
486/// on the size of it's contents, the amount of available space, and the sizing constraint the grid is being size under.
487/// See <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns>
488#[derive(Copy, Clone, PartialEq, Debug)]
489#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
490pub enum MinTrackSizingFunction {
491    /// Track minimum size should be a fixed length or percentage value
492    Fixed(LengthPercentage),
493    /// Track minimum size should be content sized under a min-content constraint
494    MinContent,
495    /// Track minimum size should be content sized under a max-content constraint
496    MaxContent,
497    /// Track minimum size should be automatically sized
498    Auto,
499}
500impl TaffyAuto for MinTrackSizingFunction {
501    const AUTO: Self = Self::Auto;
502}
503impl TaffyMinContent for MinTrackSizingFunction {
504    const MIN_CONTENT: Self = Self::MinContent;
505}
506impl TaffyMaxContent for MinTrackSizingFunction {
507    const MAX_CONTENT: Self = Self::MaxContent;
508}
509impl TaffyZero for MinTrackSizingFunction {
510    const ZERO: Self = Self::Fixed(LengthPercentage::ZERO);
511}
512impl FromLength for MinTrackSizingFunction {
513    fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
514        Self::Fixed(LengthPercentage::from_length(value))
515    }
516}
517impl FromPercent for MinTrackSizingFunction {
518    fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
519        Self::Fixed(LengthPercentage::from_percent(percent))
520    }
521}
522
523impl MinTrackSizingFunction {
524    /// Returns true if the min track sizing function is `MinContent`, `MaxContent` or `Auto`, else false.
525    #[inline(always)]
526    pub fn is_intrinsic(&self) -> bool {
527        matches!(self, Self::MinContent | Self::MaxContent | Self::Auto)
528    }
529
530    /// Returns fixed point values directly. Attempts to resolve percentage values against
531    /// the passed available_space and returns if this results in a concrete value (which it
532    /// will if the available_space is `Some`). Otherwise returns `None`.
533    #[inline(always)]
534    pub fn definite_value(self, parent_size: Option<f32>) -> Option<f32> {
535        use MinTrackSizingFunction::*;
536        match self {
537            Fixed(LengthPercentage::Length(size)) => Some(size),
538            Fixed(LengthPercentage::Percent(fraction)) => parent_size.map(|size| fraction * size),
539            MinContent | MaxContent | Auto => None,
540        }
541    }
542
543    /// Resolve percentage values against the passed parent_size, returning Some(value)
544    /// Non-percentage values always return None.
545    #[inline(always)]
546    pub fn resolved_percentage_size(self, parent_size: f32) -> Option<f32> {
547        use MinTrackSizingFunction::*;
548        match self {
549            Fixed(LengthPercentage::Percent(fraction)) => Some(fraction * parent_size),
550            Fixed(LengthPercentage::Length(_)) | MinContent | MaxContent | Auto => None,
551        }
552    }
553
554    /// Whether the track sizing functions depends on the size of the parent node
555    #[inline(always)]
556    pub fn uses_percentage(self) -> bool {
557        use MinTrackSizingFunction::*;
558        matches!(self, Fixed(LengthPercentage::Percent(_)))
559    }
560}
561
562/// The sizing function for a grid track (row/column) (either auto-track or template track)
563/// May either be a MinMax variant which specifies separate values for the min-/max- track sizing functions
564/// or a scalar value which applies to both track sizing functions.
565pub type NonRepeatedTrackSizingFunction = MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>;
566impl NonRepeatedTrackSizingFunction {
567    /// Extract the min track sizing function
568    pub fn min_sizing_function(&self) -> MinTrackSizingFunction {
569        self.min
570    }
571    /// Extract the max track sizing function
572    pub fn max_sizing_function(&self) -> MaxTrackSizingFunction {
573        self.max
574    }
575    /// Determine whether at least one of the components ("min" and "max") are fixed sizing function
576    pub fn has_fixed_component(&self) -> bool {
577        matches!(self.min, MinTrackSizingFunction::Fixed(_)) || matches!(self.max, MaxTrackSizingFunction::Fixed(_))
578    }
579}
580impl TaffyAuto for NonRepeatedTrackSizingFunction {
581    const AUTO: Self = Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::AUTO };
582}
583impl TaffyMinContent for NonRepeatedTrackSizingFunction {
584    const MIN_CONTENT: Self =
585        Self { min: MinTrackSizingFunction::MIN_CONTENT, max: MaxTrackSizingFunction::MIN_CONTENT };
586}
587impl TaffyMaxContent for NonRepeatedTrackSizingFunction {
588    const MAX_CONTENT: Self =
589        Self { min: MinTrackSizingFunction::MAX_CONTENT, max: MaxTrackSizingFunction::MAX_CONTENT };
590}
591impl TaffyFitContent for NonRepeatedTrackSizingFunction {
592    fn fit_content(argument: LengthPercentage) -> Self {
593        Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::FitContent(argument) }
594    }
595}
596impl TaffyZero for NonRepeatedTrackSizingFunction {
597    const ZERO: Self = Self { min: MinTrackSizingFunction::ZERO, max: MaxTrackSizingFunction::ZERO };
598}
599impl FromLength for NonRepeatedTrackSizingFunction {
600    fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
601        Self { min: MinTrackSizingFunction::from_length(value), max: MaxTrackSizingFunction::from_length(value) }
602    }
603}
604impl FromPercent for NonRepeatedTrackSizingFunction {
605    fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
606        Self { min: MinTrackSizingFunction::from_percent(percent), max: MaxTrackSizingFunction::from_percent(percent) }
607    }
608}
609impl FromFlex for NonRepeatedTrackSizingFunction {
610    fn from_flex<Input: Into<f32> + Copy>(flex: Input) -> Self {
611        Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::from_flex(flex) }
612    }
613}
614
615/// The first argument to a repeated track definition. This type represents the type of automatic repetition to perform.
616///
617/// See <https://www.w3.org/TR/css-grid-1/#auto-repeat> for an explanation of how auto-repeated track definitions work
618/// and the difference between AutoFit and AutoFill.
619#[derive(Clone, Copy, Debug, PartialEq, Eq)]
620#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
621pub enum GridTrackRepetition {
622    /// Auto-repeating tracks should be generated to fit the container
623    /// See: <https://developer.mozilla.org/en-US/docs/Web/CSS/repeat#auto-fill>
624    AutoFill,
625    /// Auto-repeating tracks should be generated to fit the container
626    /// See: <https://developer.mozilla.org/en-US/docs/Web/CSS/repeat#auto-fit>
627    AutoFit,
628    /// The specified tracks should be repeated exacts N times
629    Count(u16),
630}
631impl TryFrom<u16> for GridTrackRepetition {
632    type Error = Infallible;
633    fn try_from(value: u16) -> Result<Self, Infallible> {
634        Ok(Self::Count(value))
635    }
636}
637
638/// Error returned when trying to convert a string to a GridTrackRepetition and that string is not
639/// either "auto-fit" or "auto-fill"
640#[derive(Debug)]
641pub struct InvalidStringRepetitionValue;
642#[cfg(feature = "std")]
643impl std::error::Error for InvalidStringRepetitionValue {}
644impl core::fmt::Display for InvalidStringRepetitionValue {
645    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
646        f.write_str("&str can only be converted to GridTrackRepetition if it's value is 'auto-fit' or 'auto-fill'")
647    }
648}
649impl<'a> TryFrom<&'a str> for GridTrackRepetition {
650    type Error = InvalidStringRepetitionValue;
651    fn try_from(value: &str) -> Result<Self, InvalidStringRepetitionValue> {
652        match value {
653            "auto-fit" => Ok(Self::AutoFit),
654            "auto-fill" => Ok(Self::AutoFill),
655            _ => Err(InvalidStringRepetitionValue),
656        }
657    }
658}
659
660/// The sizing function for a grid track (row/column)
661/// See <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns>
662#[derive(Clone, PartialEq, Debug)]
663#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
664pub enum TrackSizingFunction {
665    /// A single non-repeated track
666    Single(NonRepeatedTrackSizingFunction),
667    /// Automatically generate grid tracks to fit the available space using the specified definite track lengths
668    /// Only valid if every track in template (not just the repetition) has a fixed size.
669    Repeat(GridTrackRepetition, GridTrackVec<NonRepeatedTrackSizingFunction>),
670}
671impl TrackSizingFunction {
672    /// Whether the track definition is a auto-repeated fragment
673    pub fn is_auto_repetition(&self) -> bool {
674        matches!(self, Self::Repeat(GridTrackRepetition::AutoFit | GridTrackRepetition::AutoFill, _))
675    }
676}
677impl TaffyAuto for TrackSizingFunction {
678    const AUTO: Self = Self::Single(NonRepeatedTrackSizingFunction::AUTO);
679}
680impl TaffyMinContent for TrackSizingFunction {
681    const MIN_CONTENT: Self = Self::Single(NonRepeatedTrackSizingFunction::MIN_CONTENT);
682}
683impl TaffyMaxContent for TrackSizingFunction {
684    const MAX_CONTENT: Self = Self::Single(NonRepeatedTrackSizingFunction::MAX_CONTENT);
685}
686impl TaffyFitContent for TrackSizingFunction {
687    fn fit_content(argument: LengthPercentage) -> Self {
688        Self::Single(NonRepeatedTrackSizingFunction::fit_content(argument))
689    }
690}
691impl TaffyZero for TrackSizingFunction {
692    const ZERO: Self = Self::Single(NonRepeatedTrackSizingFunction::ZERO);
693}
694impl FromLength for TrackSizingFunction {
695    fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
696        Self::Single(NonRepeatedTrackSizingFunction::from_length(value))
697    }
698}
699impl FromPercent for TrackSizingFunction {
700    fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
701        Self::Single(NonRepeatedTrackSizingFunction::from_percent(percent))
702    }
703}
704impl FromFlex for TrackSizingFunction {
705    fn from_flex<Input: Into<f32> + Copy>(flex: Input) -> Self {
706        Self::Single(NonRepeatedTrackSizingFunction::from_flex(flex))
707    }
708}
709impl From<MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>> for TrackSizingFunction {
710    fn from(input: MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>) -> Self {
711        Self::Single(input)
712    }
713}