1use super::{
3 AlignContent, AlignContentKeyword, AlignItems, AlignSelf, CheapCloneStr, CompactLength, CoreStyle, Dimension,
4 JustifyContent, LengthPercentage, LengthPercentageAuto, Style,
5};
6use crate::compute::grid::{GridCoordinate, GridLine, OriginZeroLine, MAX_GRID_TRACKS};
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#[cfg(feature = "parse")]
14use crate::util::parse::{
15 from_str_from_css, parse_css_str_entirely, CssParseResult, FromCss, ParseError, Parser, Token,
16};
17
18#[derive(Debug, Clone, PartialEq)]
24#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
25pub struct GridTemplateAreas<CustomIdent: CheapCloneStr> {
26 pub areas: crate::util::sys::GridTrackVec<GridTemplateArea<CustomIdent>>,
28 pub row_count: u16,
30 pub column_count: u16,
32}
33
34#[derive(Debug, Clone, PartialEq)]
36#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
37pub struct GridTemplateArea<CustomIdent: CheapCloneStr> {
38 #[cfg_attr(feature = "serde", serde(deserialize_with = "crate::util::deserialize_from_str"))]
40 pub name: CustomIdent,
41 pub row_start: u16,
43 pub row_end: u16,
45 pub column_start: u16,
47 pub column_end: u16,
49}
50
51#[derive(Debug, Clone, PartialEq)]
53#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
54pub struct NamedGridLine<CustomIdent: CheapCloneStr> {
55 #[cfg_attr(feature = "serde", serde(deserialize_with = "crate::util::deserialize_from_str"))]
57 pub name: CustomIdent,
58 pub index: u16,
60}
61
62#[derive(Debug, Clone, Copy, PartialEq)]
64pub(crate) enum GridAreaAxis {
65 Row,
67 Column,
69}
70
71#[derive(Debug, Clone, Copy, PartialEq)]
73pub(crate) enum GridAreaEnd {
74 Start,
76 End,
78}
79
80pub trait GenericRepetition {
82 type CustomIdent: CheapCloneStr;
84 type RepetitionTrackList<'a>: Iterator<Item = TrackSizingFunction> + ExactSizeIterator + Clone
86 where
87 Self: 'a;
88
89 type TemplateLineNames<'a>: TemplateLineNames<'a, Self::CustomIdent>
91 where
92 Self: 'a;
93 fn count(&self) -> RepetitionCount;
95 fn tracks(&self) -> Self::RepetitionTrackList<'_>;
97 fn track_count(&self) -> u16 {
99 self.tracks().len().min(u16::MAX as usize) as u16
100 }
101 fn lines_names(&self) -> Self::TemplateLineNames<'_>;
107}
108
109#[rustfmt::skip]
112pub trait TemplateLineNames<'a, S: CheapCloneStr> : Iterator<Item = Self::LineNameSet<'a>> + ExactSizeIterator + Clone where Self: 'a {
113 type LineNameSet<'b>: Iterator<Item = &'b S> + ExactSizeIterator + Clone where Self: 'b;
116}
117
118impl<'a, S: CheapCloneStr> TemplateLineNames<'a, S>
119 for core::iter::Map<core::slice::Iter<'a, Vec<S>>, fn(&Vec<S>) -> core::slice::Iter<'_, S>>
120{
121 type LineNameSet<'b>
122 = core::slice::Iter<'b, S>
123 where
124 Self: 'b;
125}
126
127#[derive(Copy, Clone)]
128pub enum GenericGridTemplateComponent<S, Repetition>
131where
132 S: CheapCloneStr,
133 Repetition: GenericRepetition<CustomIdent = S>,
134{
135 Single(TrackSizingFunction),
137 Repeat(Repetition),
139}
140
141impl<S, Repetition> GenericGridTemplateComponent<S, Repetition>
142where
143 S: CheapCloneStr,
144 Repetition: GenericRepetition<CustomIdent = S>,
145{
146 pub fn is_auto_repetition(&self) -> bool {
148 match self {
149 Self::Single(_) => false,
150 Self::Repeat(repeat) => matches!(repeat.count(), RepetitionCount::AutoFit | RepetitionCount::AutoFill),
151 }
152 }
153}
154
155pub trait GridContainerStyle: CoreStyle {
157 type Repetition<'a>: GenericRepetition<CustomIdent = Self::CustomIdent>
159 where
160 Self: 'a;
161
162 type TemplateTrackList<'a>: Iterator<Item = GenericGridTemplateComponent<Self::CustomIdent, Self::Repetition<'a>>>
164 + ExactSizeIterator
165 + Clone
166 where
167 Self: 'a;
168
169 type AutoTrackList<'a>: Iterator<Item = TrackSizingFunction> + ExactSizeIterator + Clone
171 where
172 Self: 'a;
173
174 type TemplateLineNames<'a>: TemplateLineNames<'a, Self::CustomIdent>
177 where
178 Self: 'a;
179
180 type GridTemplateAreas<'a>: IntoIterator<Item = GridTemplateArea<Self::CustomIdent>>
182 where
183 Self: 'a;
184
185 fn grid_template_rows(&self) -> Option<Self::TemplateTrackList<'_>>;
190 fn grid_template_columns(&self) -> Option<Self::TemplateTrackList<'_>>;
192 fn grid_auto_rows(&self) -> Self::AutoTrackList<'_>;
194 fn grid_auto_columns(&self) -> Self::AutoTrackList<'_>;
196
197 fn grid_template_areas(&self) -> Option<Self::GridTemplateAreas<'_>>;
199 fn grid_template_area_row_count(&self) -> u16 {
202 self.grid_template_areas()
203 .map(|areas| areas.into_iter().map(|area| area.row_end.max(1) - 1).max().unwrap_or(0))
204 .unwrap_or(0)
205 }
206 fn grid_template_area_column_count(&self) -> u16 {
209 self.grid_template_areas()
210 .map(|areas| areas.into_iter().map(|area| area.column_end.max(1) - 1).max().unwrap_or(0))
211 .unwrap_or(0)
212 }
213 fn grid_template_column_names(&self) -> Option<Self::TemplateLineNames<'_>>;
215 fn grid_template_row_names(&self) -> Option<Self::TemplateLineNames<'_>>;
217
218 #[inline(always)]
220 fn grid_auto_flow(&self) -> GridAutoFlow {
221 Style::<Self::CustomIdent>::DEFAULT.grid_auto_flow
222 }
223
224 #[inline(always)]
226 fn gap(&self) -> Size<LengthPercentage> {
227 Style::<Self::CustomIdent>::DEFAULT.gap
228 }
229
230 #[inline(always)]
234 fn align_content(&self) -> AlignContent {
235 Style::<Self::CustomIdent>::DEFAULT.align_content
236 }
237 #[inline(always)]
239 fn justify_content(&self) -> JustifyContent {
240 Style::<Self::CustomIdent>::DEFAULT.justify_content
241 }
242 #[inline(always)]
244 fn align_items(&self) -> AlignItems {
245 Style::<Self::CustomIdent>::DEFAULT.align_items
246 }
247 #[inline(always)]
249 fn justify_items(&self) -> AlignItems {
250 Style::<Self::CustomIdent>::DEFAULT.justify_items
251 }
252
253 #[inline(always)]
255 fn grid_template_tracks(&self, axis: AbsoluteAxis) -> Option<Self::TemplateTrackList<'_>> {
256 match axis {
257 AbsoluteAxis::Horizontal => self.grid_template_columns(),
258 AbsoluteAxis::Vertical => self.grid_template_rows(),
259 }
260 }
261
262 #[inline(always)]
265 fn grid_align_content(&self, axis: AbstractAxis) -> AlignContent {
266 let alignment = match axis {
267 AbstractAxis::Inline => self.justify_content(),
268 AbstractAxis::Block => self.align_content(),
269 };
270 match alignment.keyword {
271 AlignContentKeyword::Normal => AlignContent::STRETCH,
272 _ => alignment,
273 }
274 }
275}
276
277pub trait GridItemStyle: CoreStyle {
279 #[inline(always)]
281 fn grid_row(&self) -> Line<GridPlacement<Self::CustomIdent>> {
282 Default::default()
283 }
284 #[inline(always)]
286 fn grid_column(&self) -> Line<GridPlacement<Self::CustomIdent>> {
287 Default::default()
288 }
289
290 #[inline(always)]
293 fn align_self(&self) -> Option<AlignSelf> {
294 Style::<Self::CustomIdent>::DEFAULT.align_self
295 }
296 #[inline(always)]
299 fn justify_self(&self) -> Option<AlignSelf> {
300 Style::<Self::CustomIdent>::DEFAULT.justify_self
301 }
302
303 #[inline(always)]
305 fn grid_placement(&self, axis: AbsoluteAxis) -> Line<GridPlacement<Self::CustomIdent>> {
306 match axis {
307 AbsoluteAxis::Horizontal => self.grid_column(),
308 AbsoluteAxis::Vertical => self.grid_row(),
309 }
310 }
311}
312
313#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
321#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
322pub enum GridAutoFlow {
323 #[default]
325 Row,
326 Column,
328 RowDense,
330 ColumnDense,
332}
333
334#[cfg(feature = "parse")]
335impl FromCss for GridAutoFlow {
336 fn from_css<'i>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
337 let mut axis: Option<&'static str> = None;
338 let mut dense = false;
339
340 for _ in 0..2 {
341 if let Ok(ident) = parser.try_parse(|parser| parser.expect_ident_cloned()) {
342 match &*ident {
343 "row" => {
344 axis = Some("row");
345 }
346 "column" => {
347 axis = Some("column");
348 }
349 "dense" => dense = true,
350 _ => {
351 return Err(parser.new_unexpected_token_error(Token::Ident(ident)));
352 }
353 }
354 } else {
355 break;
356 }
357 }
358
359 match (axis, dense) {
360 (Some("row"), false) => Ok(Self::Row),
361 (Some("row") | None, true) => Ok(Self::RowDense),
362 (Some("column"), false) => Ok(Self::Column),
363 (Some("column"), true) => Ok(Self::ColumnDense),
364 (None, false) => {
365 let token = parser.next().cloned()?;
366 Err(parser.new_unexpected_token_error(token))
367 }
368 _ => unreachable!(),
369 }
370 }
371}
372#[cfg(feature = "parse")]
373from_str_from_css!(GridAutoFlow);
374
375impl GridAutoFlow {
376 pub const fn is_dense(&self) -> bool {
379 match self {
380 Self::Row | Self::Column => false,
381 Self::RowDense | Self::ColumnDense => true,
382 }
383 }
384
385 pub const fn primary_axis(&self) -> AbsoluteAxis {
388 match self {
389 Self::Row | Self::RowDense => AbsoluteAxis::Horizontal,
390 Self::Column | Self::ColumnDense => AbsoluteAxis::Vertical,
391 }
392 }
393}
394
395#[derive(Copy, Clone, PartialEq, Eq, Debug)]
401#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
402pub enum GenericGridPlacement<LineType: GridCoordinate> {
403 Auto,
405 Line(LineType),
407 Span(u16),
409}
410
411pub(crate) type OriginZeroGridPlacement = GenericGridPlacement<OriginZeroLine>;
413
414pub(crate) type NonNamedGridPlacement = GenericGridPlacement<GridLine>;
418
419#[derive(Clone, PartialEq, Debug, Default)]
425#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
426pub enum GridPlacement<S: CheapCloneStr = DefaultCheapStr> {
427 #[default]
429 Auto,
430 Line(GridLine),
432 NamedLine(S, i16),
434 Span(u16),
436 NamedSpan(S, u16),
441}
442impl<S: CheapCloneStr> TaffyAuto for GridPlacement<S> {
443 const AUTO: Self = Self::Auto;
444}
445impl<S: CheapCloneStr> TaffyGridLine for GridPlacement<S> {
446 fn from_line_index(index: i16) -> Self {
447 GridPlacement::<S>::Line(GridLine::from(index))
448 }
449}
450impl<S: CheapCloneStr> TaffyGridLine for Line<GridPlacement<S>> {
451 fn from_line_index(index: i16) -> Self {
452 Line { start: GridPlacement::<S>::from_line_index(index), end: GridPlacement::<S>::Auto }
453 }
454}
455impl<S: CheapCloneStr> TaffyGridSpan for GridPlacement<S> {
456 fn from_span(span: u16) -> Self {
457 GridPlacement::<S>::Span(span)
458 }
459}
460impl<S: CheapCloneStr> TaffyGridSpan for Line<GridPlacement<S>> {
461 fn from_span(span: u16) -> Self {
462 Line { start: GridPlacement::<S>::from_span(span), end: GridPlacement::<S>::Auto }
463 }
464}
465
466#[cfg(feature = "parse")]
467fn saturating_i16(value: i32) -> i16 {
469 value.clamp(i16::MIN as i32, i16::MAX as i32) as i16
470}
471
472#[cfg(feature = "parse")]
473fn saturating_u16(value: i32) -> u16 {
475 value.clamp(u16::MIN as i32, u16::MAX as i32) as u16
476}
477
478#[cfg(feature = "parse")]
479impl<S: CheapCloneStr> FromCss for GridPlacement<S> {
480 fn from_css<'i>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
481 let mut span = false;
482 let mut number = None;
483 let mut ident = None;
484
485 while !parser.is_exhausted() {
486 let token = parser.next()?.clone();
487 match &token {
488 Token::Ident(s) => match s.as_ref() {
489 "auto" => {
490 if span || number.is_some() || ident.is_some() {
491 return Err(parser.new_unexpected_token_error(token));
492 }
493 parser.expect_exhausted()?;
494 return Ok(Self::Auto);
495 }
496 "span" => {
497 if span {
498 return Err(parser.new_unexpected_token_error(token));
499 }
500 span = true;
501 }
502 other => {
503 if ident.is_some() {
504 return Err(parser.new_unexpected_token_error(token));
505 }
506 ident = Some(S::from(other));
507 }
508 },
509 Token::Number { int_value: Some(value), .. } if *value != 0 => {
510 if number.is_some() {
511 return Err(parser.new_unexpected_token_error(token));
512 }
513 number = Some(*value);
514 }
515 _ => return Err(parser.new_unexpected_token_error(token)),
516 };
517 }
518
519 match (span, number, ident) {
520 (true, None, None) => Ok(Self::Span(0)),
521 (true, Some(number), None) => Ok(Self::Span(saturating_u16(number))),
522 (true, None, Some(ident)) => Ok(Self::NamedSpan(ident, 0)),
523 (true, Some(number), Some(ident)) => Ok(Self::NamedSpan(ident, saturating_u16(number))),
524 (false, Some(number), None) => Ok(Self::Line(GridLine::from(saturating_i16(number)))),
525 (false, Some(number), Some(ident)) => Ok(Self::NamedLine(ident, saturating_i16(number))),
526 (false, None, Some(ident)) => Ok(Self::NamedLine(ident, 0)),
527 (false, None, None) => Err(parser.new_error(cssparser::BasicParseErrorKind::EndOfInput)),
528 }
529 }
530}
531
532#[cfg(feature = "parse")]
533impl<S: CheapCloneStr> core::str::FromStr for GridPlacement<S> {
534 type Err = ParseError;
535 fn from_str(input: &str) -> Result<Self, Self::Err> {
536 parse_css_str_entirely(input)
537 }
538}
539
540impl<S: CheapCloneStr> GridPlacement<S> {
541 pub fn into_origin_zero_placement_ignoring_named(&self, explicit_track_count: u16) -> OriginZeroGridPlacement {
543 match self {
544 Self::Auto => OriginZeroGridPlacement::Auto,
545 Self::Span(span) => OriginZeroGridPlacement::Span((*span).clamp(1, MAX_GRID_TRACKS)),
548 Self::Line(line) => match line.as_i16() {
551 0 => OriginZeroGridPlacement::Auto,
552 _ => OriginZeroGridPlacement::Line(line.into_origin_zero_line(explicit_track_count)),
553 },
554 Self::NamedLine(_, _) => OriginZeroGridPlacement::Auto,
555 Self::NamedSpan(_, _) => OriginZeroGridPlacement::Auto,
556 }
557 }
558}
559
560impl<S: CheapCloneStr> Line<GridPlacement<S>> {
561 pub fn into_origin_zero_ignoring_named(&self, explicit_track_count: u16) -> Line<OriginZeroGridPlacement> {
563 Line {
564 start: self.start.into_origin_zero_placement_ignoring_named(explicit_track_count),
565 end: self.end.into_origin_zero_placement_ignoring_named(explicit_track_count),
566 }
567 }
568}
569
570impl NonNamedGridPlacement {
571 pub fn into_origin_zero_placement(
573 &self,
574 explicit_track_count: u16,
575 ) -> OriginZeroGridPlacement {
577 match self {
578 Self::Auto => OriginZeroGridPlacement::Auto,
579 Self::Span(span) => OriginZeroGridPlacement::Span((*span).clamp(1, MAX_GRID_TRACKS)),
582 Self::Line(line) => match line.as_i16() {
585 0 => OriginZeroGridPlacement::Auto,
586 _ => OriginZeroGridPlacement::Line(line.into_origin_zero_line(explicit_track_count)),
587 },
588 }
589 }
590}
591
592impl<T: GridCoordinate> Line<GenericGridPlacement<T>> {
593 pub const fn indefinite_span(&self) -> u16 {
596 use GenericGridPlacement as GP;
597 match (self.start, self.end) {
598 (GP::Line(_), GP::Auto) => 1,
599 (GP::Auto, GP::Line(_)) => 1,
600 (GP::Auto, GP::Auto) => 1,
601 (GP::Line(_), GP::Span(span)) => span,
602 (GP::Span(span), GP::Line(_)) => span,
603 (GP::Span(span), GP::Auto) => span,
604 (GP::Auto, GP::Span(span)) => span,
605 (GP::Span(span), GP::Span(_)) => span,
606 (GP::Line(_), GP::Line(_)) => panic!("indefinite_span should only be called on indefinite grid tracks"),
607 }
608 }
609}
610
611impl<S: CheapCloneStr> Line<GridPlacement<S>> {
612 #[inline]
613 pub fn is_definite(&self) -> bool {
617 match (&self.start, &self.end) {
618 (GridPlacement::Line(line), _) if line.as_i16() != 0 => true,
619 (_, GridPlacement::Line(line)) if line.as_i16() != 0 => true,
620 (GridPlacement::NamedLine(_, _), _) => true,
621 (_, GridPlacement::NamedLine(_, _)) => true,
622 _ => false,
623 }
624 }
625}
626
627impl Line<NonNamedGridPlacement> {
628 #[inline]
629 pub fn is_definite(&self) -> bool {
633 match (&self.start, &self.end) {
634 (GenericGridPlacement::Line(line), _) if line.as_i16() != 0 => true,
635 (_, GenericGridPlacement::Line(line)) if line.as_i16() != 0 => true,
636 _ => false,
637 }
638 }
639
640 pub fn into_origin_zero(&self, explicit_track_count: u16) -> Line<OriginZeroGridPlacement> {
642 Line {
643 start: self.start.into_origin_zero_placement(explicit_track_count),
644 end: self.end.into_origin_zero_placement(explicit_track_count),
645 }
646 }
647}
648
649impl Line<OriginZeroGridPlacement> {
650 #[inline]
651 pub const fn is_definite(&self) -> bool {
654 matches!((self.start, self.end), (GenericGridPlacement::Line(_), _) | (_, GenericGridPlacement::Line(_)))
655 }
656
657 pub fn resolve_definite_grid_lines(&self) -> Line<OriginZeroLine> {
660 use OriginZeroGridPlacement as GP;
661 match (self.start, self.end) {
662 (GP::Line(line1), GP::Line(line2)) => {
663 if line1 == line2 {
664 Line { start: line1, end: line1 + 1 }
665 } else {
666 Line { start: min(line1, line2), end: max(line1, line2) }
667 }
668 }
669 (GP::Line(line), GP::Span(span)) => Line { start: line, end: line + span },
670 (GP::Line(line), GP::Auto) => Line { start: line, end: line + 1 },
671 (GP::Span(span), GP::Line(line)) => Line { start: line - span, end: line },
672 (GP::Auto, GP::Line(line)) => Line { start: line - 1, end: line },
673 _ => panic!("resolve_definite_grid_tracks should only be called on definite grid tracks"),
674 }
675 }
676
677 pub fn resolve_absolutely_positioned_grid_tracks(&self) -> Line<Option<OriginZeroLine>> {
687 use OriginZeroGridPlacement as GP;
688 match (self.start, self.end) {
689 (GP::Line(track1), GP::Line(track2)) => {
690 if track1 == track2 {
691 Line { start: Some(track1), end: Some(track1 + 1) }
692 } else {
693 Line { start: Some(min(track1, track2)), end: Some(max(track1, track2)) }
694 }
695 }
696 (GP::Line(track), GP::Span(span)) => Line { start: Some(track), end: Some(track + span) },
697 (GP::Line(track), GP::Auto) => Line { start: Some(track), end: None },
698 (GP::Span(span), GP::Line(track)) => Line { start: Some(track - span), end: Some(track) },
699 (GP::Auto, GP::Line(track)) => Line { start: None, end: Some(track) },
700 _ => Line { start: None, end: None },
701 }
702 }
703
704 pub fn resolve_indefinite_grid_tracks(&self, start: OriginZeroLine) -> Line<OriginZeroLine> {
707 use OriginZeroGridPlacement as GP;
708 match (self.start, self.end) {
709 (GP::Auto, GP::Auto) => Line { start, end: start + 1 },
710 (GP::Span(span), GP::Auto) => Line { start, end: start + span },
711 (GP::Auto, GP::Span(span)) => Line { start, end: start + span },
712 (GP::Span(span), GP::Span(_)) => Line { start, end: start + span },
713 _ => panic!("resolve_indefinite_grid_tracks should only be called on indefinite grid tracks"),
714 }
715 }
716}
717
718impl<S: CheapCloneStr> Default for Line<GridPlacement<S>> {
720 fn default() -> Self {
721 Line { start: GridPlacement::<S>::Auto, end: GridPlacement::<S>::Auto }
722 }
723}
724
725#[derive(Copy, Clone, PartialEq, Debug)]
731#[cfg_attr(feature = "serde", derive(Serialize))]
732pub struct MaxTrackSizingFunction(pub(crate) CompactLength);
733impl TaffyZero for MaxTrackSizingFunction {
734 const ZERO: Self = Self(CompactLength::ZERO);
735}
736impl TaffyAuto for MaxTrackSizingFunction {
737 const AUTO: Self = Self(CompactLength::AUTO);
738}
739impl TaffyMinContent for MaxTrackSizingFunction {
740 const MIN_CONTENT: Self = Self(CompactLength::MIN_CONTENT);
741}
742impl TaffyMaxContent for MaxTrackSizingFunction {
743 const MAX_CONTENT: Self = Self(CompactLength::MAX_CONTENT);
744}
745impl FromLength for MaxTrackSizingFunction {
746 fn from_length<Input: Into<f64> + Copy>(value: Input) -> Self {
747 Self::length(value.into() as f32)
748 }
749}
750impl FromPercent for MaxTrackSizingFunction {
751 fn from_percent<Input: Into<f64> + Copy>(value: Input) -> Self {
752 Self::percent(value.into() as f32)
753 }
754}
755impl TaffyFitContent for MaxTrackSizingFunction {
756 fn fit_content(argument: LengthPercentage) -> Self {
757 Self(CompactLength::fit_content(argument))
758 }
759}
760impl FromFr for MaxTrackSizingFunction {
761 fn from_fr<Input: Into<f64> + Copy>(value: Input) -> Self {
762 Self::fr(value.into() as f32)
763 }
764}
765impl From<LengthPercentage> for MaxTrackSizingFunction {
766 fn from(input: LengthPercentage) -> Self {
767 Self(input.0)
768 }
769}
770impl From<LengthPercentageAuto> for MaxTrackSizingFunction {
771 fn from(input: LengthPercentageAuto) -> Self {
772 Self(input.0)
773 }
774}
775impl From<Dimension> for MaxTrackSizingFunction {
776 fn from(input: Dimension) -> Self {
777 match input.0.tag() {
780 CompactLength::FIT_CONTENT_KEYWORD_TAG | CompactLength::STRETCH_TAG | CompactLength::CONTENT_TAG => {
781 Self::auto()
782 }
783 _ => Self(input.0),
784 }
785 }
786}
787impl From<MinTrackSizingFunction> for MaxTrackSizingFunction {
788 fn from(input: MinTrackSizingFunction) -> Self {
789 Self(input.0)
790 }
791}
792
793#[cfg(feature = "parse")]
794impl FromCss for MaxTrackSizingFunction {
795 fn from_css<'i>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
796 let token = parser.next()?.clone();
797 match token {
798 Token::Percentage { unit_value, .. } => Ok(Self::percent(unit_value)),
799 Token::Dimension { unit, value, .. } if unit == "px" => Ok(Self::length(value)),
800 Token::Dimension { unit, value, .. } if unit == "fr" && value.is_sign_positive() => Ok(Self::fr(value)),
801 Token::Ident(ref ident) => match ident.as_ref() {
802 "auto" => Ok(Self::auto()),
803 "min-content" => Ok(Self::min_content()),
804 "max-content" => Ok(Self::max_content()),
805 _ => Err(parser.new_unexpected_token_error(token))?,
806 },
807 Token::Function(ref name) if name.as_ref() == "fit-content" => parser.parse_nested_block(|parser| {
808 let token = parser.next()?.clone();
809 match token {
810 Token::Percentage { unit_value, .. } => Ok(Self::fit_content_percent(unit_value)),
811 Token::Dimension { unit, value, .. } if unit == "px" => Ok(Self::fit_content_px(value)),
812 token => Err(parser.new_unexpected_token_error(token))?,
813 }
814 }),
815 token => Err(parser.new_unexpected_token_error(token))?,
816 }
817 }
818}
819
820#[cfg(feature = "parse")]
821from_str_from_css!(MaxTrackSizingFunction);
822
823#[cfg(feature = "serde")]
824impl<'de> serde::Deserialize<'de> for MaxTrackSizingFunction {
825 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
826 where
827 D: serde::Deserializer<'de>,
828 {
829 let inner = CompactLength::deserialize(deserializer)?;
830 if matches!(
832 inner.tag(),
833 CompactLength::LENGTH_TAG
834 | CompactLength::PERCENT_TAG
835 | CompactLength::AUTO_TAG
836 | CompactLength::MIN_CONTENT_TAG
837 | CompactLength::MAX_CONTENT_TAG
838 | CompactLength::FIT_CONTENT_PX_TAG
839 | CompactLength::FIT_CONTENT_PERCENT_TAG
840 | CompactLength::FR_TAG
841 ) {
842 Ok(Self(inner))
843 } else {
844 Err(serde::de::Error::custom("Invalid tag"))
845 }
846 }
847}
848
849impl MaxTrackSizingFunction {
850 #[inline(always)]
853 pub const fn length(val: f32) -> Self {
854 Self(CompactLength::length(val))
855 }
856
857 #[inline(always)]
861 pub const fn percent(val: f32) -> Self {
862 Self(CompactLength::percent(val))
863 }
864
865 #[inline(always)]
868 pub const fn auto() -> Self {
869 Self(CompactLength::auto())
870 }
871
872 #[inline(always)]
875 pub const fn min_content() -> Self {
876 Self(CompactLength::min_content())
877 }
878
879 #[inline(always)]
882 pub const fn max_content() -> Self {
883 Self(CompactLength::max_content())
884 }
885
886 #[inline(always)]
896 pub const fn fit_content_px(limit: f32) -> Self {
897 Self(CompactLength::fit_content_px(limit))
898 }
899
900 #[inline(always)]
910 pub const fn fit_content_percent(limit: f32) -> Self {
911 Self(CompactLength::fit_content_percent(limit))
912 }
913
914 #[inline(always)]
918 pub const fn fr(val: f32) -> Self {
919 Self(CompactLength::fr(val))
920 }
921
922 #[inline]
927 #[cfg(feature = "calc")]
928 pub fn calc(ptr: *const ()) -> Self {
929 Self(CompactLength::calc(ptr))
930 }
931
932 #[inline]
937 #[cfg(feature = "calc")]
938 pub fn fit_content_calc(ptr: *const ()) -> Self {
939 Self(CompactLength::fit_content_calc(ptr))
940 }
941
942 #[allow(unsafe_code)]
946 pub unsafe fn from_raw(val: CompactLength) -> Self {
947 Self(val)
948 }
949
950 pub fn into_raw(self) -> CompactLength {
952 self.0
953 }
954
955 #[inline(always)]
957 pub fn is_intrinsic(&self) -> bool {
958 self.0.is_intrinsic()
959 }
960
961 #[inline(always)]
965 pub fn is_max_content_alike(&self) -> bool {
966 self.0.is_max_content_alike()
967 }
968
969 #[inline(always)]
971 pub fn is_fr(&self) -> bool {
972 self.0.is_fr()
973 }
974
975 #[inline(always)]
977 pub fn is_auto(&self) -> bool {
978 self.0.is_auto()
979 }
980
981 #[inline(always)]
983 pub fn is_min_content(&self) -> bool {
984 self.0.is_min_content()
985 }
986
987 #[inline(always)]
989 pub fn is_max_content(&self) -> bool {
990 self.0.is_max_content()
991 }
992
993 #[inline(always)]
995 pub fn is_fit_content(&self) -> bool {
996 self.0.is_fit_content()
997 }
998
999 #[inline(always)]
1001 pub fn is_max_or_fit_content(&self) -> bool {
1002 self.0.is_max_or_fit_content()
1003 }
1004
1005 #[inline(always)]
1007 pub fn has_definite_value(self, parent_size: Option<f32>) -> bool {
1008 match self.0.tag() {
1009 CompactLength::LENGTH_TAG => true,
1010 CompactLength::PERCENT_TAG => parent_size.is_some(),
1011 #[cfg(feature = "calc")]
1012 _ if self.0.is_plain_calc() => parent_size.is_some(),
1013 _ => false,
1014 }
1015 }
1016
1017 #[inline(always)]
1021 pub fn definite_value(
1022 self,
1023 parent_size: Option<f32>,
1024 calc_resolver: impl Fn(*const (), f32) -> f32,
1025 ) -> Option<f32> {
1026 match self.0.tag() {
1027 CompactLength::LENGTH_TAG => Some(self.0.value()),
1028 CompactLength::PERCENT_TAG => parent_size.map(|size| self.0.value() * size),
1029 #[cfg(feature = "calc")]
1030 _ if self.0.is_plain_calc() => parent_size.map(|size| calc_resolver(self.0.calc_value(), size)),
1031 _ => None,
1032 }
1033 }
1034
1035 #[inline(always)]
1039 pub fn fit_content_limit(
1040 self,
1041 parent_size: Option<f32>,
1042 calc_resolver: impl Fn(*const (), f32) -> f32,
1043 ) -> Option<f32> {
1044 #[cfg(not(feature = "calc"))]
1045 let _ = &calc_resolver;
1046 match self.0.tag() {
1047 CompactLength::FIT_CONTENT_PX_TAG => Some(self.0.value()),
1048 CompactLength::FIT_CONTENT_PERCENT_TAG => parent_size.map(|size| self.0.value() * size),
1049 #[cfg(feature = "calc")]
1050 _ if self.0.is_fit_content_calc() => parent_size.map(|size| calc_resolver(self.0.calc_value(), size)),
1051 _ => None,
1052 }
1053 }
1054
1055 #[inline(always)]
1062 pub fn definite_limit(
1063 self,
1064 parent_size: Option<f32>,
1065 calc_resolver: impl Fn(*const (), f32) -> f32,
1066 ) -> Option<f32> {
1067 match self.0.tag() {
1068 CompactLength::FIT_CONTENT_PX_TAG => Some(self.0.value()),
1069 CompactLength::FIT_CONTENT_PERCENT_TAG => parent_size.map(|size| self.0.value() * size),
1070 #[cfg(feature = "calc")]
1071 _ if self.0.is_fit_content_calc() => parent_size.map(|size| calc_resolver(self.0.calc_value(), size)),
1072 _ => self.definite_value(parent_size, calc_resolver),
1073 }
1074 }
1075
1076 #[inline(always)]
1079 pub fn resolved_percentage_size(
1080 self,
1081 parent_size: f32,
1082 calc_resolver: impl Fn(*const (), f32) -> f32,
1083 ) -> Option<f32> {
1084 self.0.resolved_percentage_size(parent_size, calc_resolver)
1085 }
1086
1087 #[inline(always)]
1089 pub fn uses_percentage(self) -> bool {
1090 self.0.uses_percentage()
1091 }
1092
1093 pub fn expand(self) -> ExpandedMaxTrackSizingFunction {
1099 match self.0.tag() {
1100 CompactLength::LENGTH_TAG => ExpandedMaxTrackSizingFunction::Length(self.0.value()),
1101 CompactLength::PERCENT_TAG => ExpandedMaxTrackSizingFunction::Percent(self.0.value()),
1102 CompactLength::AUTO_TAG => ExpandedMaxTrackSizingFunction::Auto,
1103 CompactLength::MIN_CONTENT_TAG => ExpandedMaxTrackSizingFunction::MinContent,
1104 CompactLength::MAX_CONTENT_TAG => ExpandedMaxTrackSizingFunction::MaxContent,
1105 CompactLength::FIT_CONTENT_PX_TAG => ExpandedMaxTrackSizingFunction::FitContentPx(self.0.value()),
1106 CompactLength::FIT_CONTENT_PERCENT_TAG => ExpandedMaxTrackSizingFunction::FitContentPercent(self.0.value()),
1107 #[cfg(feature = "calc")]
1108 _ if self.0.is_fit_content_calc() => ExpandedMaxTrackSizingFunction::FitContentCalc(self.0.calc_value()),
1109 CompactLength::FR_TAG => ExpandedMaxTrackSizingFunction::Fr(self.0.value()),
1110 #[cfg(feature = "calc")]
1111 _ if self.0.is_plain_calc() => ExpandedMaxTrackSizingFunction::Calc(self.0.calc_value()),
1112 _ => unreachable!("MaxTrackSizingFunction contains a value with an invalid tag"),
1113 }
1114 }
1115}
1116
1117#[derive(Copy, Clone, PartialEq, Debug)]
1122pub enum ExpandedMaxTrackSizingFunction {
1123 Length(f32),
1125 Percent(f32),
1127 Auto,
1129 MinContent,
1131 MaxContent,
1133 FitContentPx(f32),
1135 FitContentPercent(f32),
1137 #[cfg(feature = "calc")]
1140 FitContentCalc(*const ()),
1141 Fr(f32),
1143 #[cfg(feature = "calc")]
1146 Calc(*const ()),
1147}
1148
1149impl From<MaxTrackSizingFunction> for ExpandedMaxTrackSizingFunction {
1150 fn from(value: MaxTrackSizingFunction) -> Self {
1151 value.expand()
1152 }
1153}
1154
1155impl From<ExpandedMaxTrackSizingFunction> for MaxTrackSizingFunction {
1156 fn from(value: ExpandedMaxTrackSizingFunction) -> Self {
1157 match value {
1158 ExpandedMaxTrackSizingFunction::Length(val) => Self::length(val),
1159 ExpandedMaxTrackSizingFunction::Percent(val) => Self::percent(val),
1160 ExpandedMaxTrackSizingFunction::Auto => Self::auto(),
1161 ExpandedMaxTrackSizingFunction::MinContent => Self::min_content(),
1162 ExpandedMaxTrackSizingFunction::MaxContent => Self::max_content(),
1163 ExpandedMaxTrackSizingFunction::FitContentPx(val) => Self::fit_content_px(val),
1164 ExpandedMaxTrackSizingFunction::FitContentPercent(val) => Self::fit_content_percent(val),
1165 #[cfg(feature = "calc")]
1166 ExpandedMaxTrackSizingFunction::FitContentCalc(ptr) => Self::fit_content_calc(ptr),
1167 ExpandedMaxTrackSizingFunction::Fr(val) => Self::fr(val),
1168 #[cfg(feature = "calc")]
1169 ExpandedMaxTrackSizingFunction::Calc(ptr) => Self::calc(ptr),
1170 }
1171 }
1172}
1173
1174#[derive(Copy, Clone, PartialEq, Debug)]
1180#[cfg_attr(feature = "serde", derive(Serialize))]
1181pub struct MinTrackSizingFunction(pub(crate) CompactLength);
1182impl TaffyZero for MinTrackSizingFunction {
1183 const ZERO: Self = Self(CompactLength::ZERO);
1184}
1185impl TaffyAuto for MinTrackSizingFunction {
1186 const AUTO: Self = Self(CompactLength::AUTO);
1187}
1188impl TaffyMinContent for MinTrackSizingFunction {
1189 const MIN_CONTENT: Self = Self(CompactLength::MIN_CONTENT);
1190}
1191impl TaffyMaxContent for MinTrackSizingFunction {
1192 const MAX_CONTENT: Self = Self(CompactLength::MAX_CONTENT);
1193}
1194impl FromLength for MinTrackSizingFunction {
1195 fn from_length<Input: Into<f64> + Copy>(value: Input) -> Self {
1196 Self::length(value.into() as f32)
1197 }
1198}
1199impl FromPercent for MinTrackSizingFunction {
1200 fn from_percent<Input: Into<f64> + Copy>(value: Input) -> Self {
1201 Self::percent(value.into() as f32)
1202 }
1203}
1204impl From<LengthPercentage> for MinTrackSizingFunction {
1205 fn from(input: LengthPercentage) -> Self {
1206 Self(input.0)
1207 }
1208}
1209impl From<LengthPercentageAuto> for MinTrackSizingFunction {
1210 fn from(input: LengthPercentageAuto) -> Self {
1211 Self(input.0)
1212 }
1213}
1214impl From<Dimension> for MinTrackSizingFunction {
1215 fn from(input: Dimension) -> Self {
1216 match input.0.tag() {
1219 CompactLength::FIT_CONTENT_PX_TAG
1220 | CompactLength::FIT_CONTENT_PERCENT_TAG
1221 | CompactLength::FIT_CONTENT_KEYWORD_TAG
1222 | CompactLength::STRETCH_TAG
1223 | CompactLength::CONTENT_TAG => Self::auto(),
1224 _ if input.0.is_fit_content_calc() => Self::auto(),
1225 _ => Self(input.0),
1226 }
1227 }
1228}
1229
1230impl From<MaxTrackSizingFunction> for MinTrackSizingFunction {
1231 fn from(input: MaxTrackSizingFunction) -> Self {
1232 if input.is_fr() || input.is_fit_content() {
1233 return Self::auto();
1234 }
1235 Self(input.0)
1236 }
1237}
1238
1239#[cfg(feature = "parse")]
1240impl FromCss for MinTrackSizingFunction {
1241 fn from_css<'i>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
1242 let token = parser.next()?.clone();
1243 match token {
1244 Token::Percentage { unit_value, .. } => Ok(Self::percent(unit_value)),
1245 Token::Dimension { unit, value, .. } if unit == "px" => Ok(Self::length(value)),
1246 Token::Ident(ref ident) => match ident.as_ref() {
1247 "auto" => Ok(Self::auto()),
1248 "min-content" => Ok(Self::min_content()),
1249 "max-content" => Ok(Self::max_content()),
1250 _ => Err(parser.new_unexpected_token_error(token))?,
1251 },
1252 token => Err(parser.new_unexpected_token_error(token))?,
1253 }
1254 }
1255}
1256
1257#[cfg(feature = "parse")]
1258from_str_from_css!(MinTrackSizingFunction);
1259
1260#[cfg(feature = "serde")]
1261impl<'de> serde::Deserialize<'de> for MinTrackSizingFunction {
1262 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1263 where
1264 D: serde::Deserializer<'de>,
1265 {
1266 let inner = CompactLength::deserialize(deserializer)?;
1267 if matches!(
1269 inner.tag(),
1270 CompactLength::LENGTH_TAG
1271 | CompactLength::PERCENT_TAG
1272 | CompactLength::AUTO_TAG
1273 | CompactLength::MIN_CONTENT_TAG
1274 | CompactLength::MAX_CONTENT_TAG
1275 | CompactLength::FIT_CONTENT_PX_TAG
1276 | CompactLength::FIT_CONTENT_PERCENT_TAG
1277 ) {
1278 Ok(Self(inner))
1279 } else {
1280 Err(serde::de::Error::custom("Invalid tag"))
1281 }
1282 }
1283}
1284
1285impl MinTrackSizingFunction {
1286 #[inline(always)]
1289 pub const fn length(val: f32) -> Self {
1290 Self(CompactLength::length(val))
1291 }
1292
1293 #[inline(always)]
1297 pub const fn percent(val: f32) -> Self {
1298 Self(CompactLength::percent(val))
1299 }
1300
1301 #[inline(always)]
1304 pub const fn auto() -> Self {
1305 Self(CompactLength::auto())
1306 }
1307
1308 #[inline(always)]
1311 pub const fn min_content() -> Self {
1312 Self(CompactLength::min_content())
1313 }
1314
1315 #[inline(always)]
1318 pub const fn max_content() -> Self {
1319 Self(CompactLength::max_content())
1320 }
1321
1322 #[inline]
1327 #[cfg(feature = "calc")]
1328 pub fn calc(ptr: *const ()) -> Self {
1329 Self(CompactLength::calc(ptr))
1330 }
1331
1332 #[allow(unsafe_code)]
1336 pub unsafe fn from_raw(val: CompactLength) -> Self {
1337 Self(val)
1338 }
1339
1340 pub fn into_raw(self) -> CompactLength {
1342 self.0
1343 }
1344
1345 #[inline(always)]
1347 pub fn is_intrinsic(&self) -> bool {
1348 self.0.is_intrinsic()
1349 }
1350
1351 #[inline(always)]
1353 pub fn is_min_or_max_content(&self) -> bool {
1354 self.0.is_min_or_max_content()
1355 }
1356
1357 #[inline(always)]
1359 pub fn is_fr(&self) -> bool {
1360 self.0.is_fr()
1361 }
1362
1363 #[inline(always)]
1365 pub fn is_auto(&self) -> bool {
1366 self.0.is_auto()
1367 }
1368
1369 #[inline(always)]
1371 pub fn behaves_as_auto(&self, parent_size: Option<f32>) -> bool {
1372 self.0.is_auto() || (parent_size.is_none() && self.0.uses_percentage())
1373 }
1374
1375 #[inline(always)]
1377 pub fn is_min_content(&self) -> bool {
1378 self.0.is_min_content()
1379 }
1380
1381 #[inline(always)]
1383 pub fn is_max_content(&self) -> bool {
1384 self.0.is_max_content()
1385 }
1386
1387 #[inline(always)]
1391 pub fn definite_value(
1392 self,
1393 parent_size: Option<f32>,
1394 calc_resolver: impl Fn(*const (), f32) -> f32,
1395 ) -> Option<f32> {
1396 match self.0.tag() {
1397 CompactLength::LENGTH_TAG => Some(self.0.value()),
1398 CompactLength::PERCENT_TAG => parent_size.map(|size| self.0.value() * size),
1399 #[cfg(feature = "calc")]
1400 _ if self.0.is_plain_calc() => parent_size.map(|size| calc_resolver(self.0.calc_value(), size)),
1401 _ => None,
1402 }
1403 }
1404
1405 #[inline(always)]
1408 pub fn resolved_percentage_size(
1409 self,
1410 parent_size: f32,
1411 calc_resolver: impl Fn(*const (), f32) -> f32,
1412 ) -> Option<f32> {
1413 self.0.resolved_percentage_size(parent_size, calc_resolver)
1414 }
1415
1416 #[inline(always)]
1418 pub fn uses_percentage(self) -> bool {
1419 self.0.uses_percentage()
1420 }
1421
1422 pub fn expand(self) -> ExpandedMinTrackSizingFunction {
1428 match self.0.tag() {
1429 CompactLength::LENGTH_TAG => ExpandedMinTrackSizingFunction::Length(self.0.value()),
1430 CompactLength::PERCENT_TAG => ExpandedMinTrackSizingFunction::Percent(self.0.value()),
1431 CompactLength::AUTO_TAG => ExpandedMinTrackSizingFunction::Auto,
1432 CompactLength::MIN_CONTENT_TAG => ExpandedMinTrackSizingFunction::MinContent,
1433 CompactLength::MAX_CONTENT_TAG => ExpandedMinTrackSizingFunction::MaxContent,
1434 #[cfg(feature = "calc")]
1435 _ if self.0.is_plain_calc() => ExpandedMinTrackSizingFunction::Calc(self.0.calc_value()),
1436 _ => unreachable!("MinTrackSizingFunction contains a value with an invalid tag"),
1437 }
1438 }
1439}
1440
1441#[derive(Copy, Clone, PartialEq, Debug)]
1446pub enum ExpandedMinTrackSizingFunction {
1447 Length(f32),
1449 Percent(f32),
1451 Auto,
1453 MinContent,
1455 MaxContent,
1457 #[cfg(feature = "calc")]
1460 Calc(*const ()),
1461}
1462
1463impl From<MinTrackSizingFunction> for ExpandedMinTrackSizingFunction {
1464 fn from(value: MinTrackSizingFunction) -> Self {
1465 value.expand()
1466 }
1467}
1468
1469impl From<ExpandedMinTrackSizingFunction> for MinTrackSizingFunction {
1470 fn from(value: ExpandedMinTrackSizingFunction) -> Self {
1471 match value {
1472 ExpandedMinTrackSizingFunction::Length(val) => Self::length(val),
1473 ExpandedMinTrackSizingFunction::Percent(val) => Self::percent(val),
1474 ExpandedMinTrackSizingFunction::Auto => Self::auto(),
1475 ExpandedMinTrackSizingFunction::MinContent => Self::min_content(),
1476 ExpandedMinTrackSizingFunction::MaxContent => Self::max_content(),
1477 #[cfg(feature = "calc")]
1478 ExpandedMinTrackSizingFunction::Calc(ptr) => Self::calc(ptr),
1479 }
1480 }
1481}
1482
1483pub type TrackSizingFunction = MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>;
1488impl TrackSizingFunction {
1489 pub fn min_sizing_function(&self) -> MinTrackSizingFunction {
1491 self.min
1492 }
1493 pub fn max_sizing_function(&self) -> MaxTrackSizingFunction {
1495 self.max
1496 }
1497 pub fn has_fixed_component(&self) -> bool {
1503 self.min.0.is_length_percentage_or_calc() || self.max.0.is_length_percentage_or_calc()
1504 }
1505}
1506impl TaffyAuto for TrackSizingFunction {
1507 const AUTO: Self = Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::AUTO };
1508}
1509impl TaffyMinContent for TrackSizingFunction {
1510 const MIN_CONTENT: Self =
1511 Self { min: MinTrackSizingFunction::MIN_CONTENT, max: MaxTrackSizingFunction::MIN_CONTENT };
1512}
1513impl TaffyMaxContent for TrackSizingFunction {
1514 const MAX_CONTENT: Self =
1515 Self { min: MinTrackSizingFunction::MAX_CONTENT, max: MaxTrackSizingFunction::MAX_CONTENT };
1516}
1517impl TaffyFitContent for TrackSizingFunction {
1518 fn fit_content(argument: LengthPercentage) -> Self {
1519 Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::fit_content(argument) }
1520 }
1521}
1522impl TaffyZero for TrackSizingFunction {
1523 const ZERO: Self = Self { min: MinTrackSizingFunction::ZERO, max: MaxTrackSizingFunction::ZERO };
1524}
1525impl FromLength for TrackSizingFunction {
1526 fn from_length<Input: Into<f64> + Copy>(value: Input) -> Self {
1527 Self { min: MinTrackSizingFunction::from_length(value), max: MaxTrackSizingFunction::from_length(value) }
1528 }
1529}
1530impl FromPercent for TrackSizingFunction {
1531 fn from_percent<Input: Into<f64> + Copy>(percent: Input) -> Self {
1532 Self { min: MinTrackSizingFunction::from_percent(percent), max: MaxTrackSizingFunction::from_percent(percent) }
1533 }
1534}
1535impl FromFr for TrackSizingFunction {
1536 fn from_fr<Input: Into<f64> + Copy>(flex: Input) -> Self {
1537 Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::from_fr(flex) }
1538 }
1539}
1540impl From<LengthPercentage> for TrackSizingFunction {
1541 fn from(input: LengthPercentage) -> Self {
1542 Self { min: input.into(), max: input.into() }
1543 }
1544}
1545impl From<LengthPercentageAuto> for TrackSizingFunction {
1546 fn from(input: LengthPercentageAuto) -> Self {
1547 Self { min: input.into(), max: input.into() }
1548 }
1549}
1550impl From<Dimension> for TrackSizingFunction {
1551 fn from(input: Dimension) -> Self {
1552 Self { min: input.into(), max: input.into() }
1553 }
1554}
1555
1556#[cfg(feature = "parse")]
1557impl FromCss for TrackSizingFunction {
1558 fn from_css<'i>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
1559 if let Ok(value) = parser.try_parse(|parser| {
1561 parser.expect_function_matching("minmax")?;
1562 parser.parse_nested_block(|parser| {
1563 let min = MinTrackSizingFunction::from_css(parser)?;
1564 parser.expect_comma()?;
1565 let max = MaxTrackSizingFunction::from_css(parser)?;
1566
1567 Ok(Self { min, max })
1568 })
1569 }) {
1570 return Ok(value);
1571 }
1572
1573 let max = MaxTrackSizingFunction::from_css(parser)?;
1575 let min = max.into();
1576 Ok(Self { min, max })
1577 }
1578}
1579
1580#[cfg(feature = "parse")]
1581from_str_from_css!(TrackSizingFunction);
1582
1583#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1588#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1589pub enum RepetitionCount {
1590 AutoFill,
1593 AutoFit,
1596 Count(u16),
1598}
1599impl From<u16> for RepetitionCount {
1600 fn from(value: u16) -> Self {
1601 Self::Count(value)
1602 }
1603}
1604
1605#[derive(Debug)]
1608pub struct InvalidStringRepetitionValue;
1609#[cfg(feature = "std")]
1610impl std::error::Error for InvalidStringRepetitionValue {}
1611impl core::fmt::Display for InvalidStringRepetitionValue {
1612 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1613 f.write_str("&str can only be converted to GridTrackRepetition if it's value is 'auto-fit' or 'auto-fill'")
1614 }
1615}
1616impl TryFrom<&str> for RepetitionCount {
1617 type Error = InvalidStringRepetitionValue;
1618 fn try_from(value: &str) -> Result<Self, InvalidStringRepetitionValue> {
1619 match value {
1620 "auto-fit" => Ok(Self::AutoFit),
1621 "auto-fill" => Ok(Self::AutoFill),
1622 _ => Err(InvalidStringRepetitionValue),
1623 }
1624 }
1625}
1626
1627#[cfg(feature = "parse")]
1628impl FromCss for RepetitionCount {
1629 fn from_css<'i>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
1630 match parser.next()?.clone() {
1631 Token::Number { int_value: Some(value), .. } if value.is_positive() => {
1632 Ok(Self::Count(saturating_u16(value)))
1633 }
1634 Token::Ident(ident) if ident == "auto-fit" => Ok(Self::AutoFit),
1635 Token::Ident(ident) if ident == "auto-fill" => Ok(Self::AutoFill),
1636 token => Err(parser.new_unexpected_token_error(token))?,
1637 }
1638 }
1639}
1640#[cfg(feature = "parse")]
1641from_str_from_css!(RepetitionCount);
1642
1643#[derive(Clone, PartialEq, Debug)]
1645#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1646pub struct GridTemplateRepetition<S: CheapCloneStr> {
1647 pub count: RepetitionCount,
1649 pub tracks: Vec<TrackSizingFunction>,
1651 pub line_names: Vec<Vec<S>>,
1657}
1658
1659#[rustfmt::skip]
1660impl<S: CheapCloneStr> GenericRepetition for &'_ GridTemplateRepetition<S> {
1661 type CustomIdent = S;
1662 type RepetitionTrackList<'a> = core::iter::Copied<core::slice::Iter<'a, TrackSizingFunction>> where Self: 'a;
1663 type TemplateLineNames<'a> = core::iter::Map<core::slice::Iter<'a, Vec<S>>, fn(&Vec<S>) -> core::slice::Iter<'_, S>> where Self: 'a;
1664 #[inline(always)]
1665 fn count(&self) -> RepetitionCount {
1666 self.count
1667 }
1668 #[inline(always)]
1669 fn track_count(&self) -> u16 {
1670 self.tracks.len().min(u16::MAX as usize) as u16
1671 }
1672 #[inline(always)]
1673 fn tracks(&self) -> Self::RepetitionTrackList<'_> {
1674 self.tracks.iter().copied()
1675 }
1676 #[inline(always)]
1677 fn lines_names(&self) -> Self::TemplateLineNames<'_> {
1678 self.line_names.iter().map(|names| names.iter())
1679 }
1680}
1681
1682#[derive(Clone, PartialEq, Debug)]
1687#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1688pub enum GridTemplateComponent<S: CheapCloneStr> {
1689 Single(TrackSizingFunction),
1691 Repeat(GridTemplateRepetition<S>),
1694}
1695
1696impl<S: CheapCloneStr> GridTemplateComponent<S> {
1697 pub fn as_component_ref(&self) -> GenericGridTemplateComponent<S, &GridTemplateRepetition<S>> {
1699 match self {
1700 GridTemplateComponent::Single(size) => GenericGridTemplateComponent::Single(*size),
1701 GridTemplateComponent::Repeat(repetition) => GenericGridTemplateComponent::Repeat(repetition),
1702 }
1703 }
1704}
1705
1706impl<S: CheapCloneStr> GridTemplateComponent<S> {
1707 pub fn is_auto_repetition(&self) -> bool {
1709 matches!(
1710 self,
1711 Self::Repeat(GridTemplateRepetition { count: RepetitionCount::AutoFit | RepetitionCount::AutoFill, .. })
1712 )
1713 }
1714}
1715impl<S: CheapCloneStr> TaffyAuto for GridTemplateComponent<S> {
1716 const AUTO: Self = Self::Single(TrackSizingFunction::AUTO);
1717}
1718impl<S: CheapCloneStr> TaffyMinContent for GridTemplateComponent<S> {
1719 const MIN_CONTENT: Self = Self::Single(TrackSizingFunction::MIN_CONTENT);
1720}
1721impl<S: CheapCloneStr> TaffyMaxContent for GridTemplateComponent<S> {
1722 const MAX_CONTENT: Self = Self::Single(TrackSizingFunction::MAX_CONTENT);
1723}
1724impl<S: CheapCloneStr> TaffyFitContent for GridTemplateComponent<S> {
1725 fn fit_content(argument: LengthPercentage) -> Self {
1726 Self::Single(TrackSizingFunction::fit_content(argument))
1727 }
1728}
1729impl<S: CheapCloneStr> TaffyZero for GridTemplateComponent<S> {
1730 const ZERO: Self = Self::Single(TrackSizingFunction::ZERO);
1731}
1732impl<S: CheapCloneStr> FromLength for GridTemplateComponent<S> {
1733 fn from_length<Input: Into<f64> + Copy>(value: Input) -> Self {
1734 Self::Single(TrackSizingFunction::from_length(value))
1735 }
1736}
1737impl<S: CheapCloneStr> FromPercent for GridTemplateComponent<S> {
1738 fn from_percent<Input: Into<f64> + Copy>(percent: Input) -> Self {
1739 Self::Single(TrackSizingFunction::from_percent(percent))
1740 }
1741}
1742impl<S: CheapCloneStr> FromFr for GridTemplateComponent<S> {
1743 fn from_fr<Input: Into<f64> + Copy>(flex: Input) -> Self {
1744 Self::Single(TrackSizingFunction::from_fr(flex))
1745 }
1746}
1747impl<S: CheapCloneStr> From<MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>> for GridTemplateComponent<S> {
1748 fn from(input: MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>) -> Self {
1749 Self::Single(input)
1750 }
1751}
1752
1753#[cfg(feature = "parse")]
1754impl<S: CheapCloneStr> FromCss for GridTemplateComponent<S> {
1755 fn from_css<'i>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
1756 if let Ok(value) = parser.try_parse(|parser| {
1758 parser.expect_function_matching("repeat")?;
1759 parser.parse_nested_block(|parser| {
1760 let count = RepetitionCount::from_css(parser)?;
1761 parser.expect_comma()?;
1762 let tracks = GridTemplateTracks::<S, TrackSizingFunction>::from_css(parser)?;
1763
1764 Ok(Self::Repeat(GridTemplateRepetition { count, tracks: tracks.tracks, line_names: tracks.line_names }))
1765 })
1766 }) {
1767 return Ok(value);
1768 }
1769
1770 let track_sizing_function = TrackSizingFunction::from_css(parser)?;
1772 Ok(Self::Single(track_sizing_function))
1773 }
1774}
1775#[cfg(feature = "parse")]
1776impl<S: CheapCloneStr> core::str::FromStr for GridTemplateComponent<S> {
1777 type Err = ParseError;
1778 fn from_str(input: &str) -> Result<Self, Self::Err> {
1779 parse_css_str_entirely(input)
1780 }
1781}
1782
1783#[derive(Clone, PartialEq, Debug)]
1784#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1785#[doc(hidden)]
1786pub struct GridTemplateTracks<S: CheapCloneStr, Track> {
1787 pub tracks: Vec<Track>,
1789 pub line_names: Vec<Vec<S>>,
1791}
1792
1793impl<S: CheapCloneStr, Track> Default for GridTemplateTracks<S, Track> {
1794 fn default() -> Self {
1795 Self { tracks: Vec::new(), line_names: Vec::new() }
1796 }
1797}
1798
1799#[cfg(feature = "parse")]
1800impl<S: CheapCloneStr, Track: FromCss + Debug> FromCss for GridTemplateTracks<S, Track> {
1801 fn from_css<'i>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
1802 fn try_parse_line_names<'i, S: CheapCloneStr>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Vec<S>> {
1803 parser.try_parse(|parser| {
1804 parser.expect_square_bracket_block()?;
1805 parser.parse_nested_block(|parser| {
1806 let mut line_names = Vec::new();
1807 while !parser.is_exhausted() {
1808 line_names.push(S::from(parser.expect_ident_cloned()?.as_ref()));
1809 }
1810 Ok(line_names)
1811 })
1812 })
1813 }
1814
1815 let mut tracks = Self::default();
1818 tracks.line_names.push(try_parse_line_names(parser).unwrap_or_default());
1819
1820 while !parser.is_exhausted() {
1821 tracks.tracks.push(Track::from_css(parser)?);
1822 tracks.line_names.push(try_parse_line_names(parser).unwrap_or_default());
1823 }
1824
1825 if tracks.tracks.is_empty() {
1826 return Err(parser.new_error(cssparser::BasicParseErrorKind::EndOfInput));
1827 }
1828
1829 Ok(tracks)
1830 }
1831}
1832#[cfg(feature = "parse")]
1833impl<S: CheapCloneStr, Track: FromCss + Debug> core::str::FromStr for GridTemplateTracks<S, Track> {
1834 type Err = ParseError;
1835 fn from_str(input: &str) -> Result<Self, Self::Err> {
1836 parse_css_str_entirely(input)
1837 }
1838}
1839
1840#[derive(Default)]
1841#[doc(hidden)]
1842pub struct GridAutoTracks(pub Vec<TrackSizingFunction>);
1843
1844#[cfg(feature = "parse")]
1845impl FromCss for GridAutoTracks {
1846 fn from_css<'i>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
1847 let mut tracks = Self::default();
1848 while !parser.is_exhausted() {
1849 tracks.0.push(TrackSizingFunction::from_css(parser)?);
1850 }
1851 if tracks.0.is_empty() {
1852 return Err(parser.new_error(cssparser::BasicParseErrorKind::EndOfInput));
1853 }
1854 Ok(tracks)
1855 }
1856}
1857#[cfg(feature = "parse")]
1858from_str_from_css!(GridAutoTracks);
1859
1860#[cfg(all(test, feature = "parse"))]
1861mod tests {
1862 use super::*;
1863 use crate::sys::DefaultCheapStr;
1864
1865 #[test]
1866 fn grid_placement_parser_saturates_numeric_values() {
1867 assert_eq!(
1868 "32768".parse::<GridPlacement<DefaultCheapStr>>().unwrap(),
1869 GridPlacement::Line(GridLine::from(i16::MAX))
1870 );
1871 assert_eq!(
1872 "-32769".parse::<GridPlacement<DefaultCheapStr>>().unwrap(),
1873 GridPlacement::Line(GridLine::from(i16::MIN))
1874 );
1875 assert_eq!("span 65536".parse::<GridPlacement<DefaultCheapStr>>().unwrap(), GridPlacement::Span(u16::MAX));
1876
1877 let named_line = "32768 line".parse::<GridPlacement<DefaultCheapStr>>().unwrap();
1878 assert!(matches!(named_line, GridPlacement::NamedLine(_, i16::MAX)));
1879
1880 let named_span = "span 65536 line".parse::<GridPlacement<DefaultCheapStr>>().unwrap();
1881 assert!(matches!(named_span, GridPlacement::NamedSpan(_, u16::MAX)));
1882 }
1883
1884 #[test]
1885 fn repetition_parser_saturates_numeric_values() {
1886 assert_eq!("65536".parse::<RepetitionCount>().unwrap(), RepetitionCount::Count(u16::MAX));
1887
1888 let component = "repeat(65536, 1px)".parse::<GridTemplateComponent<DefaultCheapStr>>().unwrap();
1889 assert!(matches!(
1890 component,
1891 GridTemplateComponent::Repeat(GridTemplateRepetition { count: RepetitionCount::Count(u16::MAX), .. })
1892 ));
1893 }
1894
1895 #[test]
1896 fn repetition_track_count_saturates() {
1897 let repetition = GridTemplateRepetition::<DefaultCheapStr> {
1898 count: RepetitionCount::Count(1),
1899 tracks: vec![TrackSizingFunction::AUTO; u16::MAX as usize + 1],
1900 line_names: Vec::new(),
1901 };
1902 assert_eq!((&repetition).track_count(), u16::MAX);
1903 }
1904}
1905
1906#[cfg(test)]
1907mod expand_tests {
1908 use super::*;
1909
1910 #[test]
1911 fn max_track_sizing_function_round_trips() {
1912 let cases = [
1913 MaxTrackSizingFunction::length(12.0),
1914 MaxTrackSizingFunction::percent(0.5),
1915 MaxTrackSizingFunction::auto(),
1916 MaxTrackSizingFunction::min_content(),
1917 MaxTrackSizingFunction::max_content(),
1918 MaxTrackSizingFunction::fit_content_px(30.0),
1919 MaxTrackSizingFunction::fit_content_percent(0.75),
1920 MaxTrackSizingFunction::fr(2.0),
1921 ];
1922 for value in cases {
1923 assert_eq!(MaxTrackSizingFunction::from(value.expand()), value);
1924 assert_eq!(ExpandedMaxTrackSizingFunction::from(value), value.expand());
1925 }
1926 assert_eq!(MaxTrackSizingFunction::fr(2.0).expand(), ExpandedMaxTrackSizingFunction::Fr(2.0));
1927 assert_eq!(
1928 MaxTrackSizingFunction::fit_content_px(30.0).expand(),
1929 ExpandedMaxTrackSizingFunction::FitContentPx(30.0)
1930 );
1931 }
1932
1933 #[test]
1934 fn min_track_sizing_function_round_trips() {
1935 let cases = [
1936 MinTrackSizingFunction::length(12.0),
1937 MinTrackSizingFunction::percent(0.5),
1938 MinTrackSizingFunction::auto(),
1939 MinTrackSizingFunction::min_content(),
1940 MinTrackSizingFunction::max_content(),
1941 ];
1942 for value in cases {
1943 assert_eq!(MinTrackSizingFunction::from(value.expand()), value);
1944 assert_eq!(ExpandedMinTrackSizingFunction::from(value), value.expand());
1945 }
1946 assert_eq!(MinTrackSizingFunction::max_content().expand(), ExpandedMinTrackSizingFunction::MaxContent);
1947 }
1948
1949 #[cfg(feature = "calc")]
1950 #[test]
1951 fn track_sizing_function_calc_round_trips() {
1952 #[allow(dead_code)]
1953 #[repr(align(8))]
1954 struct Aligned(u64);
1955 static HANDLE: Aligned = Aligned(0);
1956 let handle = &HANDLE as *const Aligned as *const ();
1957
1958 assert_eq!(MaxTrackSizingFunction::calc(handle).expand(), ExpandedMaxTrackSizingFunction::Calc(handle));
1959 assert_eq!(
1960 MaxTrackSizingFunction::fit_content_calc(handle).expand(),
1961 ExpandedMaxTrackSizingFunction::FitContentCalc(handle)
1962 );
1963 assert_eq!(
1964 MaxTrackSizingFunction::from(ExpandedMaxTrackSizingFunction::FitContentCalc(handle)),
1965 MaxTrackSizingFunction::fit_content_calc(handle)
1966 );
1967 assert_eq!(
1968 MaxTrackSizingFunction::fit_content(LengthPercentage::calc(handle)),
1969 MaxTrackSizingFunction::fit_content_calc(handle)
1970 );
1971 assert_eq!(
1972 MinTrackSizingFunction::from(MaxTrackSizingFunction::fit_content_calc(handle)),
1973 MinTrackSizingFunction::AUTO
1974 );
1975 assert_eq!(MinTrackSizingFunction::from(Dimension::fit_content_calc(handle)), MinTrackSizingFunction::AUTO);
1976 assert_eq!(
1977 MaxTrackSizingFunction::from(ExpandedMaxTrackSizingFunction::Calc(handle)),
1978 MaxTrackSizingFunction::calc(handle)
1979 );
1980 assert_eq!(MinTrackSizingFunction::calc(handle).expand(), ExpandedMinTrackSizingFunction::Calc(handle));
1981 }
1982}