⎇ 2ca609ecf19c2e34bca2ce1fe1f4e73b09d62a97
src/style/grid.rs
Ñawpa willay
Pi imata llamkarqan
ch'uya
1//! Style types for CSS Grid layout
2use super::{
3 AlignContent, AlignItems, AlignSelf, CheapCloneStr, CompactLength, CoreStyle, Dimension, JustifyContent,
4 LengthPercentage, LengthPercentageAuto, Style,
5};
6use crate::compute::grid::{GridCoordinate, GridLine, OriginZeroLine};
7use crate::geometry::{AbsoluteAxis, AbstractAxis, Line, MinMax, Size};
8use crate::style_helpers::*;
9use crate::sys::{DefaultCheapStr, Vec};
10use core::cmp::{max, min};
11use core::fmt::Debug;
12
13/// Defines a grid area
14#[derive(Debug, Clone, PartialEq)]
15#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
16pub struct GridTemplateArea<CustomIdent: CheapCloneStr> {
17 /// The name of the grid area which
18 #[cfg_attr(feature = "serde", serde(deserialize_with = "crate::util::deserialize_from_str"))]
19 pub name: CustomIdent,
20 /// The index of the row at which the grid area starts in grid coordinates.
21 pub row_start: u16,
22 /// The index of the row at which the grid area ends in grid coordinates.
23 pub row_end: u16,
24 /// The index of the column at which the grid area starts in grid coordinates.
25 pub column_start: u16,
26 /// The index of the column at which the grid area end in grid coordinates.
27 pub column_end: u16,
28}
29
30/// Defines a named grid line
31#[derive(Debug, Clone, PartialEq)]
32#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
33pub struct NamedGridLine<CustomIdent: CheapCloneStr> {
34 /// The name of the grid area which
35 #[cfg_attr(feature = "serde", serde(deserialize_with = "crate::util::deserialize_from_str"))]
36 pub name: CustomIdent,
37 /// The index of the row at which the grid area starts in grid coordinates.
38 pub index: u16,
39}
40
41/// Axis as `Row` or `Column`
42#[derive(Debug, Clone, Copy, PartialEq)]
43pub(crate) enum GridAreaAxis {
44 /// The `Row` axis
45 Row,
46 /// The `Column` axis
47 Column,
48}
49
50/// Logical end (`Start` or `End`)
51#[derive(Debug, Clone, Copy, PartialEq)]
52pub(crate) enum GridAreaEnd {
53 /// The `Start` end
54 Start,
55 /// The `End` end
56 End,
57}
58
59/// A trait to represent a `repeat()` clause in a `grid-template-*` definition
60pub trait GenericRepetition {
61 /// The type that represents `<custom-ident>`s (for named lines)
62 type CustomIdent: CheapCloneStr;
63 /// The type which represents an iterator over the list of repeated tracks
64 type RepetitionTrackList<'a>: Iterator<Item = TrackSizingFunction> + ExactSizeIterator + Clone
65 where
66 Self: 'a;
67
68 /// A nested iterator of line names (nested because each line may have multiple associated names)
69 type TemplateLineNames<'a>: TemplateLineNames<'a, Self::CustomIdent>
70 where
71 Self: 'a;
72 /// The repetition count (integer, auto-fill, or auto-fit)
73 fn count(&self) -> RepetitionCount;
74 /// Get an iterator over the repeated tracks
75 fn tracks(&self) -> Self::RepetitionTrackList<'_>;
76 /// Returns the number of repeated tracks
77 fn track_count(&self) -> u16 {
78 self.tracks().len() as u16
79 }
80 /// Returns an iterator over the lines names
81 fn lines_names(&self) -> Self::TemplateLineNames<'_>;
82}
83
84/// A nested list of line names. This is effectively a generic representation of `Vec<Vec<String>>` that allows
85/// both the collection and string type to be customised.
86#[rustfmt::skip]
87pub trait TemplateLineNames<'a, S: CheapCloneStr> : Iterator<Item = Self::LineNameSet<'a>> + ExactSizeIterator + Clone where Self: 'a {
88 /// A simple list line names. This is effectively a generic representation of `VecString>` that allows
89 /// both the collection and string type to be customised.
90 type LineNameSet<'b>: Iterator<Item = &'b S> + ExactSizeIterator + Clone where Self: 'b;
91}
92
93impl<'a, S: CheapCloneStr> TemplateLineNames<'a, S>
94 for core::iter::Map<core::slice::Iter<'a, Vec<S>>, fn(&Vec<S>) -> core::slice::Iter<'_, S>>
95{
96 type LineNameSet<'b>
97 = core::slice::Iter<'b, S>
98 where
99 Self: 'b;
100}
101
102#[derive(Copy, Clone)]
103/// A type representing a component in a `grid-template-*` defintion where the type
104/// representing `repeat()`s is generic
105pub enum GenericGridTemplateComponent<S, Repetition>
106where
107 S: CheapCloneStr,
108 Repetition: GenericRepetition<CustomIdent = S>,
109{
110 /// A single track sizing function
111 Single(TrackSizingFunction),
112 /// A `repeat()`
113 Repeat(Repetition),
114}
115
116impl<S, Repetition> GenericGridTemplateComponent<S, Repetition>
117where
118 S: CheapCloneStr,
119 Repetition: GenericRepetition<CustomIdent = S>,
120{
121 /// Whether the track definition is a auto-repeated fragment
122 pub fn is_auto_repetition(&self) -> bool {
123 match self {
124 Self::Single(_) => false,
125 Self::Repeat(repeat) => matches!(repeat.count(), RepetitionCount::AutoFit | RepetitionCount::AutoFill),
126 }
127 }
128}
129
130/// The set of styles required for a CSS Grid container
131pub trait GridContainerStyle: CoreStyle {
132 /// The type for a `repeat()` within a grid_template_rows or grid_template_columns
133 type Repetition<'a>: GenericRepetition<CustomIdent = Self::CustomIdent>
134 where
135 Self: 'a;
136
137 /// The type returned by grid_template_rows and grid_template_columns
138 type TemplateTrackList<'a>: Iterator<Item = GenericGridTemplateComponent<Self::CustomIdent, Self::Repetition<'a>>>
139 + ExactSizeIterator
140 + Clone
141 where
142 Self: 'a;
143
144 /// The type returned by grid_auto_rows and grid_auto_columns
145 type AutoTrackList<'a>: Iterator<Item = TrackSizingFunction> + ExactSizeIterator + Clone
146 where
147 Self: 'a;
148
149 /// The type returned by grid_template_row_names and grid_template_column_names
150 //IntoIterator<Item = &'a Self::LineNameSet<'a>>
151 type TemplateLineNames<'a>: TemplateLineNames<'a, Self::CustomIdent>
152 where
153 Self: 'a;
154
155 /// The type of custom identifiers used to identify named grid lines and areas
156 type GridTemplateAreas<'a>: IntoIterator<Item = GridTemplateArea<Self::CustomIdent>>
157 where
158 Self: 'a;
159
160 // FIXME: re-add default implemenations for grid_{template,auto}_{rows,columns} once the
161 // associated_type_defaults feature (https://github.com/rust-lang/rust/issues/29661) is stabilised.
162
163 /// Defines the track sizing functions (heights) of the grid rows
164 fn grid_template_rows(&self) -> Option<Self::TemplateTrackList<'_>>;
165 /// Defines the track sizing functions (widths) of the grid columns
166 fn grid_template_columns(&self) -> Option<Self::TemplateTrackList<'_>>;
167 /// Defines the size of implicitly created rows
168 fn grid_auto_rows(&self) -> Self::AutoTrackList<'_>;
169 /// Defined the size of implicitly created columns
170 fn grid_auto_columns(&self) -> Self::AutoTrackList<'_>;
171
172 /// Named grid areas
173 fn grid_template_areas(&self) -> Option<Self::GridTemplateAreas<'_>>;
174 /// Defines the line names for row lines
175 fn grid_template_column_names(&self) -> Option<Self::TemplateLineNames<'_>>;
176 /// Defines the size of implicitly created rows
177 fn grid_template_row_names(&self) -> Option<Self::TemplateLineNames<'_>>;
178
179 /// Controls how items get placed into the grid for auto-placed items
180 #[inline(always)]
181 fn grid_auto_flow(&self) -> GridAutoFlow {
182 Style::<Self::CustomIdent>::DEFAULT.grid_auto_flow
183 }
184
185 /// How large should the gaps between items in a grid or flex container be?
186 #[inline(always)]
187 fn gap(&self) -> Size<LengthPercentage> {
188 Style::<Self::CustomIdent>::DEFAULT.gap
189 }
190
191 // Alignment properties
192
193 /// How should content contained within this item be aligned in the cross/block axis
194 #[inline(always)]
195 fn align_content(&self) -> Option<AlignContent> {
196 Style::<Self::CustomIdent>::DEFAULT.align_content
197 }
198 /// How should contained within this item be aligned in the main/inline axis
199 #[inline(always)]
200 fn justify_content(&self) -> Option<JustifyContent> {
201 Style::<Self::CustomIdent>::DEFAULT.justify_content
202 }
203 /// How this node's children aligned in the cross/block axis?
204 #[inline(always)]
205 fn align_items(&self) -> Option<AlignItems> {
206 Style::<Self::CustomIdent>::DEFAULT.align_items
207 }
208 /// How this node's children should be aligned in the inline axis
209 #[inline(always)]
210 fn justify_items(&self) -> Option<AlignItems> {
211 Style::<Self::CustomIdent>::DEFAULT.justify_items
212 }
213
214 /// Get a grid item's row or column placement depending on the axis passed
215 #[inline(always)]
216 fn grid_template_tracks(&self, axis: AbsoluteAxis) -> Option<Self::TemplateTrackList<'_>> {
217 match axis {
218 AbsoluteAxis::Horizontal => self.grid_template_columns(),
219 AbsoluteAxis::Vertical => self.grid_template_rows(),
220 }
221 }
222
223 /// Get a grid container's align-content or justify-content alignment depending on the axis passed
224 #[inline(always)]
225 fn grid_align_content(&self, axis: AbstractAxis) -> AlignContent {
226 match axis {
227 AbstractAxis::Inline => self.justify_content().unwrap_or(AlignContent::Stretch),
228 AbstractAxis::Block => self.align_content().unwrap_or(AlignContent::Stretch),
229 }
230 }
231}
232
233/// The set of styles required for a CSS Grid item (child of a CSS Grid container)
234pub trait GridItemStyle: CoreStyle {
235 /// Defines which row in the grid the item should start and end at
236 #[inline(always)]
237 fn grid_row(&self) -> Line<GridPlacement<Self::CustomIdent>> {
238 Default::default()
239 }
240 /// Defines which column in the grid the item should start and end at
241 #[inline(always)]
242 fn grid_column(&self) -> Line<GridPlacement<Self::CustomIdent>> {
243 Default::default()
244 }
245
246 /// How this node should be aligned in the cross/block axis
247 /// Falls back to the parents [`AlignItems`] if not set
248 #[inline(always)]
249 fn align_self(&self) -> Option<AlignSelf> {
250 Style::<Self::CustomIdent>::DEFAULT.align_self
251 }
252 /// How this node should be aligned in the inline axis
253 /// Falls back to the parents [`super::JustifyItems`] if not set
254 #[inline(always)]
255 fn justify_self(&self) -> Option<AlignSelf> {
256 Style::<Self::CustomIdent>::DEFAULT.justify_self
257 }
258
259 /// Get a grid item's row or column placement depending on the axis passed
260 #[inline(always)]
261 fn grid_placement(&self, axis: AbsoluteAxis) -> Line<GridPlacement<Self::CustomIdent>> {
262 match axis {
263 AbsoluteAxis::Horizontal => self.grid_column(),
264 AbsoluteAxis::Vertical => self.grid_row(),
265 }
266 }
267}
268
269/// Controls whether grid items are placed row-wise or column-wise. And whether the sparse or dense packing algorithm is used.
270///
271/// 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.
272///
273/// Defaults to [`GridAutoFlow::Row`]
274///
275/// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-flow)
276#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
277#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
278pub enum GridAutoFlow {
279 /// Items are placed by filling each row in turn, adding new rows as necessary
280 #[default]
281 Row,
282 /// Items are placed by filling each column in turn, adding new columns as necessary.
283 Column,
284 /// Combines `Row` with the dense packing algorithm.
285 RowDense,
286 /// Combines `Column` with the dense packing algorithm.
287 ColumnDense,
288}
289
290impl GridAutoFlow {
291 /// Whether grid auto placement uses the sparse placement algorithm or the dense placement algorithm
292 /// See: <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-flow#values>
293 pub const fn is_dense(&self) -> bool {
294 match self {
295 Self::Row | Self::Column => false,
296 Self::RowDense | Self::ColumnDense => true,
297 }
298 }
299
300 /// Whether grid auto placement fills areas row-wise or column-wise
301 /// See: <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-flow#values>
302 pub const fn primary_axis(&self) -> AbsoluteAxis {
303 match self {
304 Self::Row | Self::RowDense => AbsoluteAxis::Horizontal,
305 Self::Column | Self::ColumnDense => AbsoluteAxis::Vertical,
306 }
307 }
308}
309
310/// A grid line placement specification which is generic over the coordinate system that it uses to define
311/// grid line positions.
312///
313/// `GenericGridPlacement<GridLine>` is aliased as GridPlacement and is exposed to users of Taffy to define styles.
314/// `GenericGridPlacement<OriginZeroLine>` is aliased as OriginZeroGridPlacement and is used internally for placement computations.
315#[derive(Copy, Clone, PartialEq, Eq, Debug)]
316#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
317pub enum GenericGridPlacement<LineType: GridCoordinate> {
318 /// Place item according to the auto-placement algorithm, and the parent's grid_auto_flow property
319 Auto,
320 /// Place item at specified line (column or row) index
321 Line(LineType),
322 /// Item should span specified number of tracks (columns or rows)
323 Span(u16),
324}
325
326/// A grid line placement using the normalized OriginZero coordinates to specify line positions.
327pub(crate) type OriginZeroGridPlacement = GenericGridPlacement<OriginZeroLine>;
328
329/// A grid line placement using CSS grid line coordinates to specify line positions. This uses the same coordinate
330/// system as the public `GridPlacement` type but doesn't support named lines (these are expected to have already
331/// been resolved by the time values of this type are constructed).
332pub(crate) type NonNamedGridPlacement = GenericGridPlacement<GridLine>;
333
334/// A grid line placement specification. Used for grid-[row/column]-[start/end]. Named tracks are not implemented.
335///
336/// Defaults to `GridPlacement::Auto`
337///
338/// [Specification](https://www.w3.org/TR/css3-grid-layout/#typedef-grid-row-start-grid-line)
339#[derive(Clone, PartialEq, Debug, Default)]
340#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
341pub enum GridPlacement<S: CheapCloneStr = DefaultCheapStr> {
342 /// Place item according to the auto-placement algorithm, and the parent's grid_auto_flow property
343 #[default]
344 Auto,
345 /// Place item at specified line (column or row) index
346 Line(GridLine),
347 /// Place item at specified named line (column or row)
348 NamedLine(S, i16),
349 /// Item should span specified number of tracks (columns or rows)
350 Span(u16),
351 /// Item should span until the nth line named `<name>`.
352 ///
353 /// If there are less than n lines named `<name>` in the specified direction then
354 /// all implicit lines will be counted.
355 NamedSpan(S, u16),
356}
357impl<S: CheapCloneStr> TaffyAuto for GridPlacement<S> {
358 const AUTO: Self = Self::Auto;
359}
360impl<S: CheapCloneStr> TaffyGridLine for GridPlacement<S> {
361 fn from_line_index(index: i16) -> Self {
362 GridPlacement::<S>::Line(GridLine::from(index))
363 }
364}
365impl<S: CheapCloneStr> TaffyGridLine for Line<GridPlacement<S>> {
366 fn from_line_index(index: i16) -> Self {
367 Line { start: GridPlacement::<S>::from_line_index(index), end: GridPlacement::<S>::Auto }
368 }
369}
370impl<S: CheapCloneStr> TaffyGridSpan for GridPlacement<S> {
371 fn from_span(span: u16) -> Self {
372 GridPlacement::<S>::Span(span)
373 }
374}
375impl<S: CheapCloneStr> TaffyGridSpan for Line<GridPlacement<S>> {
376 fn from_span(span: u16) -> Self {
377 Line { start: GridPlacement::<S>::from_span(span), end: GridPlacement::<S>::Auto }
378 }
379}
380
381impl<S: CheapCloneStr> GridPlacement<S> {
382 /// Apply a mapping function if the [`GridPlacement`] is a `Line`. Otherwise return `self` unmodified.
383 pub fn into_origin_zero_placement_ignoring_named(&self, explicit_track_count: u16) -> OriginZeroGridPlacement {
384 match self {
385 Self::Auto => OriginZeroGridPlacement::Auto,
386 Self::Span(span) => OriginZeroGridPlacement::Span(*span),
387 // Grid line zero is an invalid index, so it gets treated as Auto
388 // See: https://developer.mozilla.org/en-US/docs/Web/CSS/grid-row-start#values
389 Self::Line(line) => match line.as_i16() {
390 0 => OriginZeroGridPlacement::Auto,
391 _ => OriginZeroGridPlacement::Line(line.into_origin_zero_line(explicit_track_count)),
392 },
393 Self::NamedLine(_, _) => OriginZeroGridPlacement::Auto,
394 Self::NamedSpan(_, _) => OriginZeroGridPlacement::Auto,
395 }
396 }
397}
398
399impl<S: CheapCloneStr> Line<GridPlacement<S>> {
400 /// Apply a mapping function if the [`GridPlacement`] is a `Line`. Otherwise return `self` unmodified.
401 pub fn into_origin_zero_ignoring_named(&self, explicit_track_count: u16) -> Line<OriginZeroGridPlacement> {
402 Line {
403 start: self.start.into_origin_zero_placement_ignoring_named(explicit_track_count),
404 end: self.end.into_origin_zero_placement_ignoring_named(explicit_track_count),
405 }
406 }
407}
408
409impl NonNamedGridPlacement {
410 /// Apply a mapping function if the [`GridPlacement`] is a `Track`. Otherwise return `self` unmodified.
411 pub fn into_origin_zero_placement(
412 &self,
413 explicit_track_count: u16,
414 // resolve_named: impl Fn(&str) -> Option<GridLine>
415 ) -> OriginZeroGridPlacement {
416 match self {
417 Self::Auto => OriginZeroGridPlacement::Auto,
418 Self::Span(span) => OriginZeroGridPlacement::Span(*span),
419 // Grid line zero is an invalid index, so it gets treated as Auto
420 // See: https://developer.mozilla.org/en-US/docs/Web/CSS/grid-row-start#values
421 Self::Line(line) => match line.as_i16() {
422 0 => OriginZeroGridPlacement::Auto,
423 _ => OriginZeroGridPlacement::Line(line.into_origin_zero_line(explicit_track_count)),
424 },
425 }
426 }
427}
428
429impl<T: GridCoordinate> Line<GenericGridPlacement<T>> {
430 /// Resolves the span for an indefinite placement (a placement that does not consist of two `Track`s).
431 /// Panics if called on a definite placement
432 pub const fn indefinite_span(&self) -> u16 {
433 use GenericGridPlacement as GP;
434 match (self.start, self.end) {
435 (GP::Line(_), GP::Auto) => 1,
436 (GP::Auto, GP::Line(_)) => 1,
437 (GP::Auto, GP::Auto) => 1,
438 (GP::Line(_), GP::Span(span)) => span,
439 (GP::Span(span), GP::Line(_)) => span,
440 (GP::Span(span), GP::Auto) => span,
441 (GP::Auto, GP::Span(span)) => span,
442 (GP::Span(span), GP::Span(_)) => span,
443 (GP::Line(_), GP::Line(_)) => panic!("indefinite_span should only be called on indefinite grid tracks"),
444 }
445 }
446}
447
448impl<S: CheapCloneStr> Line<GridPlacement<S>> {
449 #[inline]
450 /// Whether the track position is definite in this axis (or the item will need auto placement)
451 /// The track position is definite if least one of the start and end positions is a NON-ZERO track index
452 /// (0 is an invalid line in GridLine coordinates, and falls back to "auto" which is indefinite)
453 pub fn is_definite(&self) -> bool {
454 match (&self.start, &self.end) {
455 (GridPlacement::Line(line), _) if line.as_i16() != 0 => true,
456 (_, GridPlacement::Line(line)) if line.as_i16() != 0 => true,
457 (GridPlacement::NamedLine(_, _), _) => true,
458 (_, GridPlacement::NamedLine(_, _)) => true,
459 _ => false,
460 }
461 }
462}
463
464impl Line<NonNamedGridPlacement> {
465 #[inline]
466 /// Whether the track position is definite in this axis (or the item will need auto placement)
467 /// The track position is definite if least one of the start and end positions is a NON-ZERO track index
468 /// (0 is an invalid line in GridLine coordinates, and falls back to "auto" which is indefinite)
469 pub fn is_definite(&self) -> bool {
470 match (&self.start, &self.end) {
471 (GenericGridPlacement::Line(line), _) if line.as_i16() != 0 => true,
472 (_, GenericGridPlacement::Line(line)) if line.as_i16() != 0 => true,
473 _ => false,
474 }
475 }
476
477 /// Apply a mapping function if the [`GridPlacement`] is a `Track`. Otherwise return `self` unmodified.
478 pub fn into_origin_zero(&self, explicit_track_count: u16) -> Line<OriginZeroGridPlacement> {
479 Line {
480 start: self.start.into_origin_zero_placement(explicit_track_count),
481 end: self.end.into_origin_zero_placement(explicit_track_count),
482 }
483 }
484}
485
486impl Line<OriginZeroGridPlacement> {
487 #[inline]
488 /// Whether the track position is definite in this axis (or the item will need auto placement)
489 /// The track position is definite if least one of the start and end positions is a track index
490 pub const fn is_definite(&self) -> bool {
491 matches!((self.start, self.end), (GenericGridPlacement::Line(_), _) | (_, GenericGridPlacement::Line(_)))
492 }
493
494 /// If at least one of the of the start and end positions is a track index then the other end can be resolved
495 /// into a track index purely based on the information contained with the placement specification
496 pub fn resolve_definite_grid_lines(&self) -> Line<OriginZeroLine> {
497 use OriginZeroGridPlacement as GP;
498 match (self.start, self.end) {
499 (GP::Line(line1), GP::Line(line2)) => {
500 if line1 == line2 {
501 Line { start: line1, end: line1 + 1 }
502 } else {
503 Line { start: min(line1, line2), end: max(line1, line2) }
504 }
505 }
506 (GP::Line(line), GP::Span(span)) => Line { start: line, end: line + span },
507 (GP::Line(line), GP::Auto) => Line { start: line, end: line + 1 },
508 (GP::Span(span), GP::Line(line)) => Line { start: line - span, end: line },
509 (GP::Auto, GP::Line(line)) => Line { start: line - 1, end: line },
510 _ => panic!("resolve_definite_grid_tracks should only be called on definite grid tracks"),
511 }
512 }
513
514 /// For absolutely positioned items:
515 /// - Tracks resolve to definite tracks
516 /// - For Spans:
517 /// - If the other position is a Track, they resolve to a definite track relative to the other track
518 /// - Else resolve to None
519 /// - Auto resolves to None
520 ///
521 /// When finally positioning the item, a value of None means that the item's grid area is bounded by the grid
522 /// container's border box on that side.
523 pub fn resolve_absolutely_positioned_grid_tracks(&self) -> Line<Option<OriginZeroLine>> {
524 use OriginZeroGridPlacement as GP;
525 match (self.start, self.end) {
526 (GP::Line(track1), GP::Line(track2)) => {
527 if track1 == track2 {
528 Line { start: Some(track1), end: Some(track1 + 1) }
529 } else {
530 Line { start: Some(min(track1, track2)), end: Some(max(track1, track2)) }
531 }
532 }
533 (GP::Line(track), GP::Span(span)) => Line { start: Some(track), end: Some(track + span) },
534 (GP::Line(track), GP::Auto) => Line { start: Some(track), end: None },
535 (GP::Span(span), GP::Line(track)) => Line { start: Some(track - span), end: Some(track) },
536 (GP::Auto, GP::Line(track)) => Line { start: None, end: Some(track) },
537 _ => Line { start: None, end: None },
538 }
539 }
540
541 /// If neither of the start and end positions is a track index then the other end can be resolved
542 /// into a track index if a definite start position is supplied externally
543 pub fn resolve_indefinite_grid_tracks(&self, start: OriginZeroLine) -> Line<OriginZeroLine> {
544 use OriginZeroGridPlacement as GP;
545 match (self.start, self.end) {
546 (GP::Auto, GP::Auto) => Line { start, end: start + 1 },
547 (GP::Span(span), GP::Auto) => Line { start, end: start + span },
548 (GP::Auto, GP::Span(span)) => Line { start, end: start + span },
549 (GP::Span(span), GP::Span(_)) => Line { start, end: start + span },
550 _ => panic!("resolve_indefinite_grid_tracks should only be called on indefinite grid tracks"),
551 }
552 }
553}
554
555/// Represents the start and end points of a GridItem within a given axis
556impl<S: CheapCloneStr> Default for Line<GridPlacement<S>> {
557 fn default() -> Self {
558 Line { start: GridPlacement::<S>::Auto, end: GridPlacement::<S>::Auto }
559 }
560}
561
562/// Maximum track sizing function
563///
564/// Specifies the maximum size of a grid track. A grid track will automatically size between it's minimum and maximum size based
565/// on the size of it's contents, the amount of available space, and the sizing constraint the grid is being size under.
566/// See <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns>
567#[derive(Copy, Clone, PartialEq, Debug)]
568#[cfg_attr(feature = "serde", derive(Serialize))]
569pub struct MaxTrackSizingFunction(pub(crate) CompactLength);
570impl TaffyZero for MaxTrackSizingFunction {
571 const ZERO: Self = Self(CompactLength::ZERO);
572}
573impl TaffyAuto for MaxTrackSizingFunction {
574 const AUTO: Self = Self(CompactLength::AUTO);
575}
576impl TaffyMinContent for MaxTrackSizingFunction {
577 const MIN_CONTENT: Self = Self(CompactLength::MIN_CONTENT);
578}
579impl TaffyMaxContent for MaxTrackSizingFunction {
580 const MAX_CONTENT: Self = Self(CompactLength::MAX_CONTENT);
581}
582impl FromLength for MaxTrackSizingFunction {
583 fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
584 Self::length(value.into())
585 }
586}
587impl FromPercent for MaxTrackSizingFunction {
588 fn from_percent<Input: Into<f32> + Copy>(value: Input) -> Self {
589 Self::percent(value.into())
590 }
591}
592impl TaffyFitContent for MaxTrackSizingFunction {
593 fn fit_content(argument: LengthPercentage) -> Self {
594 Self(CompactLength::fit_content(argument))
595 }
596}
597impl FromFr for MaxTrackSizingFunction {
598 fn from_fr<Input: Into<f32> + Copy>(value: Input) -> Self {
599 Self::fr(value.into())
600 }
601}
602impl From<LengthPercentage> for MaxTrackSizingFunction {
603 fn from(input: LengthPercentage) -> Self {
604 Self(input.0)
605 }
606}
607impl From<LengthPercentageAuto> for MaxTrackSizingFunction {
608 fn from(input: LengthPercentageAuto) -> Self {
609 Self(input.0)
610 }
611}
612impl From<Dimension> for MaxTrackSizingFunction {
613 fn from(input: Dimension) -> Self {
614 Self(input.0)
615 }
616}
617impl From<MinTrackSizingFunction> for MaxTrackSizingFunction {
618 fn from(input: MinTrackSizingFunction) -> Self {
619 Self(input.0)
620 }
621}
622#[cfg(feature = "serde")]
623impl<'de> serde::Deserialize<'de> for MaxTrackSizingFunction {
624 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
625 where
626 D: serde::Deserializer<'de>,
627 {
628 let inner = CompactLength::deserialize(deserializer)?;
629 // Note: validation intentionally excludes the CALC_TAG as deserializing calc() values is not supported
630 if matches!(
631 inner.tag(),
632 CompactLength::LENGTH_TAG
633 | CompactLength::PERCENT_TAG
634 | CompactLength::AUTO_TAG
635 | CompactLength::MIN_CONTENT_TAG
636 | CompactLength::MAX_CONTENT_TAG
637 | CompactLength::FIT_CONTENT_PX_TAG
638 | CompactLength::FIT_CONTENT_PERCENT_TAG
639 | CompactLength::FR_TAG
640 ) {
641 Ok(Self(inner))
642 } else {
643 Err(serde::de::Error::custom("Invalid tag"))
644 }
645 }
646}
647
648impl MaxTrackSizingFunction {
649 /// An absolute length in some abstract units. Users of Taffy may define what they correspond
650 /// to in their application (pixels, logical pixels, mm, etc) as they see fit.
651 #[inline(always)]
652 pub const fn length(val: f32) -> Self {
653 Self(CompactLength::length(val))
654 }
655
656 /// A percentage length relative to the size of the containing block.
657 ///
658 /// **NOTE: percentages are represented as a f32 value in the range [0.0, 1.0] NOT the range [0.0, 100.0]**
659 #[inline(always)]
660 pub const fn percent(val: f32) -> Self {
661 Self(CompactLength::percent(val))
662 }
663
664 /// The dimension should be automatically computed according to algorithm-specific rules
665 /// regarding the default size of boxes.
666 #[inline(always)]
667 pub const fn auto() -> Self {
668 Self(CompactLength::auto())
669 }
670
671 /// The size should be the "min-content" size.
672 /// This is the smallest size that can fit the item's contents with ALL soft line-wrapping opportunities taken
673 #[inline(always)]
674 pub const fn min_content() -> Self {
675 Self(CompactLength::min_content())
676 }
677
678 /// The size should be the "max-content" size.
679 /// This is the smallest size that can fit the item's contents with NO soft line-wrapping opportunities taken
680 #[inline(always)]
681 pub const fn max_content() -> Self {
682 Self(CompactLength::max_content())
683 }
684
685 /// The size should be computed according to the "fit content" formula:
686 /// `max(min_content, min(max_content, limit))`
687 /// where:
688 /// - `min_content` is the [min-content](Self::min_content) size
689 /// - `max_content` is the [max-content](Self::max_content) size
690 /// - `limit` is a LENGTH value passed to this function
691 ///
692 /// The effect of this is that the item takes the size of `limit` clamped
693 /// by the min-content and max-content sizes.
694 #[inline(always)]
695 pub const fn fit_content_px(limit: f32) -> Self {
696 Self(CompactLength::fit_content_px(limit))
697 }
698
699 /// The size should be computed according to the "fit content" formula:
700 /// `max(min_content, min(max_content, limit))`
701 /// where:
702 /// - `min_content` is the [min-content](Self::min_content) size
703 /// - `max_content` is the [max-content](Self::max_content) size
704 /// - `limit` is a PERCENTAGE value passed to this function
705 ///
706 /// The effect of this is that the item takes the size of `limit` clamped
707 /// by the min-content and max-content sizes.
708 #[inline(always)]
709 pub const fn fit_content_percent(limit: f32) -> Self {
710 Self(CompactLength::fit_content_percent(limit))
711 }
712
713 /// The dimension as a fraction of the total available grid space (`fr` units in CSS)
714 /// Specified value is the numerator of the fraction. Denominator is the sum of all fraction specified in that grid dimension
715 /// Spec: <https://www.w3.org/TR/css3-grid-layout/#fr-unit>
716 #[inline(always)]
717 pub const fn fr(val: f32) -> Self {
718 Self(CompactLength::fr(val))
719 }
720
721 /// A `calc()` value. The value passed here is treated as an opaque handle to
722 /// the actual calc representation and may be a pointer, index, etc.
723 ///
724 /// The low 3 bits are used as a tag value and will be returned as 0.
725 #[inline]
726 #[cfg(feature = "calc")]
727 pub fn calc(ptr: *const ()) -> Self {
728 Self(CompactLength::calc(ptr))
729 }
730
731 /// Create a LengthPercentageAuto from a raw `CompactLength`.
732 /// # Safety
733 /// CompactLength must represent a valid variant for LengthPercentageAuto
734 #[allow(unsafe_code)]
735 pub unsafe fn from_raw(val: CompactLength) -> Self {
736 Self(val)
737 }
738
739 /// Get the underlying `CompactLength` representation of the value
740 pub fn into_raw(self) -> CompactLength {
741 self.0
742 }
743
744 /// Returns true if the max track sizing function is `MinContent`, `MaxContent`, `FitContent` or `Auto`, else false.
745 #[inline(always)]
746 pub fn is_intrinsic(&self) -> bool {
747 self.0.is_intrinsic()
748 }
749
750 /// Returns true if the max track sizing function is `MaxContent`, `FitContent` or `Auto` else false.
751 /// "In all cases, treat auto and fit-content() as max-content, except where specified otherwise for fit-content()."
752 /// See: <https://www.w3.org/TR/css-grid-1/#algo-terms>
753 #[inline(always)]
754 pub fn is_max_content_alike(&self) -> bool {
755 self.0.is_max_content_alike()
756 }
757
758 /// Returns true if the an Fr value, else false.
759 #[inline(always)]
760 pub fn is_fr(&self) -> bool {
761 self.0.is_fr()
762 }
763
764 /// Returns true if the is `Auto`, else false.
765 #[inline(always)]
766 pub fn is_auto(&self) -> bool {
767 self.0.is_auto()
768 }
769
770 /// Returns true if value is MinContent
771 #[inline(always)]
772 pub fn is_min_content(&self) -> bool {
773 self.0.is_min_content()
774 }
775
776 /// Returns true if value is MaxContent
777 #[inline(always)]
778 pub fn is_max_content(&self) -> bool {
779 self.0.is_max_content()
780 }
781
782 /// Returns true if value is FitContent(...)
783 #[inline(always)]
784 pub fn is_fit_content(&self) -> bool {
785 self.0.is_fit_content()
786 }
787
788 /// Returns true if value is MaxContent or FitContent(...)
789 #[inline(always)]
790 pub fn is_max_or_fit_content(&self) -> bool {
791 self.0.is_max_or_fit_content()
792 }
793
794 /// Returns whether the value can be resolved using `Self::definite_value`
795 #[inline(always)]
796 pub fn has_definite_value(self, parent_size: Option<f32>) -> bool {
797 match self.0.tag() {
798 CompactLength::LENGTH_TAG => true,
799 CompactLength::PERCENT_TAG => parent_size.is_some(),
800 #[cfg(feature = "calc")]
801 _ if self.0.is_calc() => parent_size.is_some(),
802 _ => false,
803 }
804 }
805
806 /// Returns fixed point values directly. Attempts to resolve percentage values against
807 /// the passed available_space and returns if this results in a concrete value (which it
808 /// will if the available_space is `Some`). Otherwise returns None.
809 #[inline(always)]
810 pub fn definite_value(
811 self,
812 parent_size: Option<f32>,
813 calc_resolver: impl Fn(*const (), f32) -> f32,
814 ) -> Option<f32> {
815 match self.0.tag() {
816 CompactLength::LENGTH_TAG => Some(self.0.value()),
817 CompactLength::PERCENT_TAG => parent_size.map(|size| self.0.value() * size),
818 #[cfg(feature = "calc")]
819 _ if self.0.is_calc() => parent_size.map(|size| calc_resolver(self.0.calc_value(), size)),
820 _ => None,
821 }
822 }
823
824 /// Resolve the maximum size of the track as defined by either:
825 /// - A fixed track sizing function
826 /// - A percentage track sizing function (with definite available space)
827 /// - A fit-content sizing function with fixed argument
828 /// - A fit-content sizing function with percentage argument (with definite available space)
829 /// All other kinds of track sizing function return None.
830 #[inline(always)]
831 pub fn definite_limit(
832 self,
833 parent_size: Option<f32>,
834 calc_resolver: impl Fn(*const (), f32) -> f32,
835 ) -> Option<f32> {
836 match self.0.tag() {
837 CompactLength::FIT_CONTENT_PX_TAG => Some(self.0.value()),
838 CompactLength::FIT_CONTENT_PERCENT_TAG => parent_size.map(|size| self.0.value() * size),
839 _ => self.definite_value(parent_size, calc_resolver),
840 }
841 }
842
843 /// Resolve percentage values against the passed parent_size, returning Some(value)
844 /// Non-percentage values always return None.
845 #[inline(always)]
846 pub fn resolved_percentage_size(
847 self,
848 parent_size: f32,
849 calc_resolver: impl Fn(*const (), f32) -> f32,
850 ) -> Option<f32> {
851 self.0.resolved_percentage_size(parent_size, calc_resolver)
852 }
853
854 /// Whether the track sizing functions depends on the size of the parent node
855 #[inline(always)]
856 pub fn uses_percentage(self) -> bool {
857 self.0.uses_percentage()
858 }
859}
860
861/// Minimum track sizing function
862///
863/// Specifies the minimum size of a grid track. A grid track will automatically size between it's minimum and maximum size based
864/// on the size of it's contents, the amount of available space, and the sizing constraint the grid is being size under.
865/// See <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns>
866#[derive(Copy, Clone, PartialEq, Debug)]
867#[cfg_attr(feature = "serde", derive(Serialize))]
868pub struct MinTrackSizingFunction(pub(crate) CompactLength);
869impl TaffyZero for MinTrackSizingFunction {
870 const ZERO: Self = Self(CompactLength::ZERO);
871}
872impl TaffyAuto for MinTrackSizingFunction {
873 const AUTO: Self = Self(CompactLength::AUTO);
874}
875impl TaffyMinContent for MinTrackSizingFunction {
876 const MIN_CONTENT: Self = Self(CompactLength::MIN_CONTENT);
877}
878impl TaffyMaxContent for MinTrackSizingFunction {
879 const MAX_CONTENT: Self = Self(CompactLength::MAX_CONTENT);
880}
881impl FromLength for MinTrackSizingFunction {
882 fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
883 Self::length(value.into())
884 }
885}
886impl FromPercent for MinTrackSizingFunction {
887 fn from_percent<Input: Into<f32> + Copy>(value: Input) -> Self {
888 Self::percent(value.into())
889 }
890}
891impl From<LengthPercentage> for MinTrackSizingFunction {
892 fn from(input: LengthPercentage) -> Self {
893 Self(input.0)
894 }
895}
896impl From<LengthPercentageAuto> for MinTrackSizingFunction {
897 fn from(input: LengthPercentageAuto) -> Self {
898 Self(input.0)
899 }
900}
901impl From<Dimension> for MinTrackSizingFunction {
902 fn from(input: Dimension) -> Self {
903 Self(input.0)
904 }
905}
906#[cfg(feature = "serde")]
907impl<'de> serde::Deserialize<'de> for MinTrackSizingFunction {
908 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
909 where
910 D: serde::Deserializer<'de>,
911 {
912 let inner = CompactLength::deserialize(deserializer)?;
913 // Note: validation intentionally excludes the CALC_TAG as deserializing calc() values is not supported
914 if matches!(
915 inner.tag(),
916 CompactLength::LENGTH_TAG
917 | CompactLength::PERCENT_TAG
918 | CompactLength::AUTO_TAG
919 | CompactLength::MIN_CONTENT_TAG
920 | CompactLength::MAX_CONTENT_TAG
921 | CompactLength::FIT_CONTENT_PX_TAG
922 | CompactLength::FIT_CONTENT_PERCENT_TAG
923 ) {
924 Ok(Self(inner))
925 } else {
926 Err(serde::de::Error::custom("Invalid tag"))
927 }
928 }
929}
930
931impl MinTrackSizingFunction {
932 /// An absolute length in some abstract units. Users of Taffy may define what they correspond
933 /// to in their application (pixels, logical pixels, mm, etc) as they see fit.
934 #[inline(always)]
935 pub const fn length(val: f32) -> Self {
936 Self(CompactLength::length(val))
937 }
938
939 /// A percentage length relative to the size of the containing block.
940 ///
941 /// **NOTE: percentages are represented as a f32 value in the range [0.0, 1.0] NOT the range [0.0, 100.0]**
942 #[inline(always)]
943 pub const fn percent(val: f32) -> Self {
944 Self(CompactLength::percent(val))
945 }
946
947 /// The dimension should be automatically computed according to algorithm-specific rules
948 /// regarding the default size of boxes.
949 #[inline(always)]
950 pub const fn auto() -> Self {
951 Self(CompactLength::auto())
952 }
953
954 /// The size should be the "min-content" size.
955 /// This is the smallest size that can fit the item's contents with ALL soft line-wrapping opportunities taken
956 #[inline(always)]
957 pub const fn min_content() -> Self {
958 Self(CompactLength::min_content())
959 }
960
961 /// The size should be the "max-content" size.
962 /// This is the smallest size that can fit the item's contents with NO soft line-wrapping opportunities taken
963 #[inline(always)]
964 pub const fn max_content() -> Self {
965 Self(CompactLength::max_content())
966 }
967
968 /// A `calc()` value. The value passed here is treated as an opaque handle to
969 /// the actual calc representation and may be a pointer, index, etc.
970 ///
971 /// The low 3 bits are used as a tag value and will be returned as 0.
972 #[inline]
973 #[cfg(feature = "calc")]
974 pub fn calc(ptr: *const ()) -> Self {
975 Self(CompactLength::calc(ptr))
976 }
977
978 /// Create a LengthPercentageAuto from a raw `CompactLength`.
979 /// # Safety
980 /// CompactLength must represent a valid variant for LengthPercentageAuto
981 #[allow(unsafe_code)]
982 pub unsafe fn from_raw(val: CompactLength) -> Self {
983 Self(val)
984 }
985
986 /// Get the underlying `CompactLength` representation of the value
987 pub fn into_raw(self) -> CompactLength {
988 self.0
989 }
990
991 /// Returns true if the min track sizing function is `MinContent`, `MaxContent` or `Auto`, else false.
992 #[inline(always)]
993 pub fn is_intrinsic(&self) -> bool {
994 self.0.is_intrinsic()
995 }
996
997 /// Returns true if the min track sizing function is `MinContent` or `MaxContent`, else false.
998 #[inline(always)]
999 pub fn is_min_or_max_content(&self) -> bool {
1000 self.0.is_min_or_max_content()
1001 }
1002
1003 /// Returns true if the value is an fr value
1004 #[inline(always)]
1005 pub fn is_fr(&self) -> bool {
1006 self.0.is_fr()
1007 }
1008
1009 /// Returns true if the is `Auto`, else false.
1010 #[inline(always)]
1011 pub fn is_auto(&self) -> bool {
1012 self.0.is_auto()
1013 }
1014
1015 /// Returns true if value is MinContent
1016 #[inline(always)]
1017 pub fn is_min_content(&self) -> bool {
1018 self.0.is_min_content()
1019 }
1020
1021 /// Returns true if value is MaxContent
1022 #[inline(always)]
1023 pub fn is_max_content(&self) -> bool {
1024 self.0.is_max_content()
1025 }
1026
1027 /// Returns fixed point values directly. Attempts to resolve percentage values against
1028 /// the passed available_space and returns if this results in a concrete value (which it
1029 /// will if the available_space is `Some`). Otherwise returns `None`.
1030 #[inline(always)]
1031 pub fn definite_value(
1032 self,
1033 parent_size: Option<f32>,
1034 calc_resolver: impl Fn(*const (), f32) -> f32,
1035 ) -> Option<f32> {
1036 match self.0.tag() {
1037 CompactLength::LENGTH_TAG => Some(self.0.value()),
1038 CompactLength::PERCENT_TAG => parent_size.map(|size| self.0.value() * size),
1039 #[cfg(feature = "calc")]
1040 _ if self.0.is_calc() => parent_size.map(|size| calc_resolver(self.0.calc_value(), size)),
1041 _ => None,
1042 }
1043 }
1044
1045 /// Resolve percentage values against the passed parent_size, returning Some(value)
1046 /// Non-percentage values always return None.
1047 #[inline(always)]
1048 pub fn resolved_percentage_size(
1049 self,
1050 parent_size: f32,
1051 calc_resolver: impl Fn(*const (), f32) -> f32,
1052 ) -> Option<f32> {
1053 self.0.resolved_percentage_size(parent_size, calc_resolver)
1054 }
1055
1056 /// Whether the track sizing functions depends on the size of the parent node
1057 #[inline(always)]
1058 pub fn uses_percentage(self) -> bool {
1059 #[cfg(feature = "calc")]
1060 {
1061 matches!(self.0.tag(), CompactLength::PERCENT_TAG) || self.0.is_calc()
1062 }
1063 #[cfg(not(feature = "calc"))]
1064 {
1065 matches!(self.0.tag(), CompactLength::PERCENT_TAG)
1066 }
1067 }
1068}
1069
1070/// The sizing function for a grid track (row/column)
1071///
1072/// May either be a MinMax variant which specifies separate values for the min-/max- track sizing functions
1073/// or a scalar value which applies to both track sizing functions.
1074pub type TrackSizingFunction = MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>;
1075impl TrackSizingFunction {
1076 /// Extract the min track sizing function
1077 pub fn min_sizing_function(&self) -> MinTrackSizingFunction {
1078 self.min
1079 }
1080 /// Extract the max track sizing function
1081 pub fn max_sizing_function(&self) -> MaxTrackSizingFunction {
1082 self.max
1083 }
1084 /// Determine whether at least one of the components ("min" and "max") are fixed sizing function
1085 pub fn has_fixed_component(&self) -> bool {
1086 self.min.0.is_length_or_percentage() || self.max.0.is_length_or_percentage()
1087 }
1088}
1089impl TaffyAuto for TrackSizingFunction {
1090 const AUTO: Self = Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::AUTO };
1091}
1092impl TaffyMinContent for TrackSizingFunction {
1093 const MIN_CONTENT: Self =
1094 Self { min: MinTrackSizingFunction::MIN_CONTENT, max: MaxTrackSizingFunction::MIN_CONTENT };
1095}
1096impl TaffyMaxContent for TrackSizingFunction {
1097 const MAX_CONTENT: Self =
1098 Self { min: MinTrackSizingFunction::MAX_CONTENT, max: MaxTrackSizingFunction::MAX_CONTENT };
1099}
1100impl TaffyFitContent for TrackSizingFunction {
1101 fn fit_content(argument: LengthPercentage) -> Self {
1102 Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::fit_content(argument) }
1103 }
1104}
1105impl TaffyZero for TrackSizingFunction {
1106 const ZERO: Self = Self { min: MinTrackSizingFunction::ZERO, max: MaxTrackSizingFunction::ZERO };
1107}
1108impl FromLength for TrackSizingFunction {
1109 fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
1110 Self { min: MinTrackSizingFunction::from_length(value), max: MaxTrackSizingFunction::from_length(value) }
1111 }
1112}
1113impl FromPercent for TrackSizingFunction {
1114 fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
1115 Self { min: MinTrackSizingFunction::from_percent(percent), max: MaxTrackSizingFunction::from_percent(percent) }
1116 }
1117}
1118impl FromFr for TrackSizingFunction {
1119 fn from_fr<Input: Into<f32> + Copy>(flex: Input) -> Self {
1120 Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::from_fr(flex) }
1121 }
1122}
1123impl From<LengthPercentage> for TrackSizingFunction {
1124 fn from(input: LengthPercentage) -> Self {
1125 Self { min: input.into(), max: input.into() }
1126 }
1127}
1128impl From<LengthPercentageAuto> for TrackSizingFunction {
1129 fn from(input: LengthPercentageAuto) -> Self {
1130 Self { min: input.into(), max: input.into() }
1131 }
1132}
1133impl From<Dimension> for TrackSizingFunction {
1134 fn from(input: Dimension) -> Self {
1135 Self { min: input.into(), max: input.into() }
1136 }
1137}
1138
1139/// The first argument to a repeated track definition. This type represents the type of automatic repetition to perform.
1140///
1141/// See <https://www.w3.org/TR/css-grid-1/#auto-repeat> for an explanation of how auto-repeated track definitions work
1142/// and the difference between AutoFit and AutoFill.
1143#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1144#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1145pub enum RepetitionCount {
1146 /// Auto-repeating tracks should be generated to fit the container
1147 /// See: <https://developer.mozilla.org/en-US/docs/Web/CSS/repeat#auto-fill>
1148 AutoFill,
1149 /// Auto-repeating tracks should be generated to fit the container
1150 /// See: <https://developer.mozilla.org/en-US/docs/Web/CSS/repeat#auto-fit>
1151 AutoFit,
1152 /// The specified tracks should be repeated exacts N times
1153 Count(u16),
1154}
1155impl From<u16> for RepetitionCount {
1156 fn from(value: u16) -> Self {
1157 Self::Count(value)
1158 }
1159}
1160
1161/// Error returned when trying to convert a string to a GridTrackRepetition and that string is not
1162/// either "auto-fit" or "auto-fill"
1163#[derive(Debug)]
1164pub struct InvalidStringRepetitionValue;
1165#[cfg(feature = "std")]
1166impl std::error::Error for InvalidStringRepetitionValue {}
1167impl core::fmt::Display for InvalidStringRepetitionValue {
1168 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1169 f.write_str("&str can only be converted to GridTrackRepetition if it's value is 'auto-fit' or 'auto-fill'")
1170 }
1171}
1172impl TryFrom<&str> for RepetitionCount {
1173 type Error = InvalidStringRepetitionValue;
1174 fn try_from(value: &str) -> Result<Self, InvalidStringRepetitionValue> {
1175 match value {
1176 "auto-fit" => Ok(Self::AutoFit),
1177 "auto-fill" => Ok(Self::AutoFill),
1178 _ => Err(InvalidStringRepetitionValue),
1179 }
1180 }
1181}
1182
1183/// A typed representation of a `repeat(..)` in `grid-template-*` value
1184#[derive(Clone, PartialEq, Debug)]
1185#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1186pub struct GridTemplateRepetition<S: CheapCloneStr> {
1187 /// The number of the times the repeat is repeated
1188 pub count: RepetitionCount,
1189 /// The tracks to repeat
1190 pub tracks: Vec<TrackSizingFunction>,
1191 /// The line names for the repeated tracks
1192 pub line_names: Vec<Vec<S>>,
1193}
1194
1195#[rustfmt::skip]
1196impl<S: CheapCloneStr> GenericRepetition for &'_ GridTemplateRepetition<S> {
1197 type CustomIdent = S;
1198 type RepetitionTrackList<'a> = core::iter::Copied<core::slice::Iter<'a, TrackSizingFunction>> where Self: 'a;
1199 type TemplateLineNames<'a> = core::iter::Map<core::slice::Iter<'a, Vec<S>>, fn(&Vec<S>) -> core::slice::Iter<'_, S>> where Self: 'a;
1200 #[inline(always)]
1201 fn count(&self) -> RepetitionCount {
1202 self.count
1203 }
1204 #[inline(always)]
1205 fn track_count(&self) -> u16 {
1206 self.tracks.len() as u16
1207 }
1208 #[inline(always)]
1209 fn tracks(&self) -> Self::RepetitionTrackList<'_> {
1210 self.tracks.iter().copied()
1211 }
1212 #[inline(always)]
1213 fn lines_names(&self) -> Self::TemplateLineNames<'_> {
1214 self.line_names.iter().map(|names| names.iter())
1215 }
1216}
1217
1218/// An element in a `grid-template-columns` or `grid-template-rows` definition.
1219/// Either a track sizing function or a repeat().
1220///
1221/// See <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns>
1222#[derive(Clone, PartialEq, Debug)]
1223#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1224pub enum GridTemplateComponent<S: CheapCloneStr> {
1225 /// A single non-repeated track
1226 Single(TrackSizingFunction),
1227 /// Automatically generate grid tracks to fit the available space using the specified definite track lengths
1228 /// Only valid if every track in template (not just the repetition) has a fixed size.
1229 Repeat(GridTemplateRepetition<S>),
1230}
1231
1232impl<S: CheapCloneStr> GridTemplateComponent<S> {
1233 /// Convert a `GridTemplateComponent` into a `GridTemplateComponentRef`
1234 pub fn as_component_ref(&self) -> GenericGridTemplateComponent<S, &GridTemplateRepetition<S>> {
1235 match self {
1236 GridTemplateComponent::Single(size) => GenericGridTemplateComponent::Single(*size),
1237 GridTemplateComponent::Repeat(repetition) => GenericGridTemplateComponent::Repeat(repetition),
1238 }
1239 }
1240}
1241
1242impl<S: CheapCloneStr> GridTemplateComponent<S> {
1243 /// Whether the track definition is a auto-repeated fragment
1244 pub fn is_auto_repetition(&self) -> bool {
1245 matches!(
1246 self,
1247 Self::Repeat(GridTemplateRepetition { count: RepetitionCount::AutoFit | RepetitionCount::AutoFill, .. })
1248 )
1249 }
1250}
1251impl<S: CheapCloneStr> TaffyAuto for GridTemplateComponent<S> {
1252 const AUTO: Self = Self::Single(TrackSizingFunction::AUTO);
1253}
1254impl<S: CheapCloneStr> TaffyMinContent for GridTemplateComponent<S> {
1255 const MIN_CONTENT: Self = Self::Single(TrackSizingFunction::MIN_CONTENT);
1256}
1257impl<S: CheapCloneStr> TaffyMaxContent for GridTemplateComponent<S> {
1258 const MAX_CONTENT: Self = Self::Single(TrackSizingFunction::MAX_CONTENT);
1259}
1260impl<S: CheapCloneStr> TaffyFitContent for GridTemplateComponent<S> {
1261 fn fit_content(argument: LengthPercentage) -> Self {
1262 Self::Single(TrackSizingFunction::fit_content(argument))
1263 }
1264}
1265impl<S: CheapCloneStr> TaffyZero for GridTemplateComponent<S> {
1266 const ZERO: Self = Self::Single(TrackSizingFunction::ZERO);
1267}
1268impl<S: CheapCloneStr> FromLength for GridTemplateComponent<S> {
1269 fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
1270 Self::Single(TrackSizingFunction::from_length(value))
1271 }
1272}
1273impl<S: CheapCloneStr> FromPercent for GridTemplateComponent<S> {
1274 fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
1275 Self::Single(TrackSizingFunction::from_percent(percent))
1276 }
1277}
1278impl<S: CheapCloneStr> FromFr for GridTemplateComponent<S> {
1279 fn from_fr<Input: Into<f32> + Copy>(flex: Input) -> Self {
1280 Self::Single(TrackSizingFunction::from_fr(flex))
1281 }
1282}
1283impl<S: CheapCloneStr> From<MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>> for GridTemplateComponent<S> {
1284 fn from(input: MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>) -> Self {
1285 Self::Single(input)
1286 }
1287}