gioser

tawasuyu/taffy

A high performance rust-powered UI layout library

HTML 5364Rust 86Markdown 12TOML 91841 Definiciones

⎇ 385a9b80396244728e191d94a931a4d69a5003ea src/style/grid.rs Rust · 629 líneas · 27.2 KB Historia Quién tocó qué crudo

Fix panic when GridLine 0 is specified (#671) 385a9b803 · Nico Burns ·

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