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