1use 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#[derive(Debug, Clone, PartialEq)]
15#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
16pub struct GridTemplateArea<CustomIdent: CheapCloneStr> {
17    #[cfg_attr(feature = "serde", serde(deserialize_with = "crate::util::deserialize_from_str"))]
19    pub name: CustomIdent,
20    pub row_start: u16,
22    pub row_end: u16,
24    pub column_start: u16,
26    pub column_end: u16,
28}
29
30#[derive(Debug, Clone, PartialEq)]
32#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
33pub struct NamedGridLine<CustomIdent: CheapCloneStr> {
34    #[cfg_attr(feature = "serde", serde(deserialize_with = "crate::util::deserialize_from_str"))]
36    pub name: CustomIdent,
37    pub index: u16,
39}
40
41#[derive(Debug, Clone, Copy, PartialEq)]
43pub(crate) enum GridAreaAxis {
44    Row,
46    Column,
48}
49
50#[derive(Debug, Clone, Copy, PartialEq)]
52pub(crate) enum GridAreaEnd {
53    Start,
55    End,
57}
58
59pub trait GenericRepetition {
61    type CustomIdent: CheapCloneStr;
63    type RepetitionTrackList<'a>: Iterator<Item = TrackSizingFunction> + ExactSizeIterator + Clone
65    where
66        Self: 'a;
67
68    type TemplateLineNames<'a>: TemplateLineNames<'a, Self::CustomIdent>
70    where
71        Self: 'a;
72    fn count(&self) -> RepetitionCount;
74    fn tracks(&self) -> Self::RepetitionTrackList<'_>;
76    fn track_count(&self) -> u16 {
78        self.tracks().len() as u16
79    }
80    fn lines_names(&self) -> Self::TemplateLineNames<'_>;
82}
83
84#[rustfmt::skip]
87pub trait TemplateLineNames<'a, S: CheapCloneStr> : Iterator<Item = Self::LineNameSet<'a>> + ExactSizeIterator + Clone where Self: 'a {
88    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)]
103pub enum GenericGridTemplateComponent<S, Repetition>
106where
107    S: CheapCloneStr,
108    Repetition: GenericRepetition<CustomIdent = S>,
109{
110    Single(TrackSizingFunction),
112    Repeat(Repetition),
114}
115
116impl<S, Repetition> GenericGridTemplateComponent<S, Repetition>
117where
118    S: CheapCloneStr,
119    Repetition: GenericRepetition<CustomIdent = S>,
120{
121    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
130pub trait GridContainerStyle: CoreStyle {
132    type Repetition<'a>: GenericRepetition<CustomIdent = Self::CustomIdent>
134    where
135        Self: 'a;
136
137    type TemplateTrackList<'a>: Iterator<Item = GenericGridTemplateComponent<Self::CustomIdent, Self::Repetition<'a>>>
139        + ExactSizeIterator
140        + Clone
141    where
142        Self: 'a;
143
144    type AutoTrackList<'a>: Iterator<Item = TrackSizingFunction> + ExactSizeIterator + Clone
146    where
147        Self: 'a;
148
149    type TemplateLineNames<'a>: TemplateLineNames<'a, Self::CustomIdent>
152    where
153        Self: 'a;
154
155    type GridTemplateAreas<'a>: IntoIterator<Item = GridTemplateArea<Self::CustomIdent>>
157    where
158        Self: 'a;
159
160    fn grid_template_rows(&self) -> Option<Self::TemplateTrackList<'_>>;
165    fn grid_template_columns(&self) -> Option<Self::TemplateTrackList<'_>>;
167    fn grid_auto_rows(&self) -> Self::AutoTrackList<'_>;
169    fn grid_auto_columns(&self) -> Self::AutoTrackList<'_>;
171
172    fn grid_template_areas(&self) -> Option<Self::GridTemplateAreas<'_>>;
174    fn grid_template_column_names(&self) -> Option<Self::TemplateLineNames<'_>>;
176    fn grid_template_row_names(&self) -> Option<Self::TemplateLineNames<'_>>;
178
179    #[inline(always)]
181    fn grid_auto_flow(&self) -> GridAutoFlow {
182        Style::<Self::CustomIdent>::DEFAULT.grid_auto_flow
183    }
184
185    #[inline(always)]
187    fn gap(&self) -> Size<LengthPercentage> {
188        Style::<Self::CustomIdent>::DEFAULT.gap
189    }
190
191    #[inline(always)]
195    fn align_content(&self) -> Option<AlignContent> {
196        Style::<Self::CustomIdent>::DEFAULT.align_content
197    }
198    #[inline(always)]
200    fn justify_content(&self) -> Option<JustifyContent> {
201        Style::<Self::CustomIdent>::DEFAULT.justify_content
202    }
203    #[inline(always)]
205    fn align_items(&self) -> Option<AlignItems> {
206        Style::<Self::CustomIdent>::DEFAULT.align_items
207    }
208    #[inline(always)]
210    fn justify_items(&self) -> Option<AlignItems> {
211        Style::<Self::CustomIdent>::DEFAULT.justify_items
212    }
213
214    #[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    #[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
233pub trait GridItemStyle: CoreStyle {
235    #[inline(always)]
237    fn grid_row(&self) -> Line<GridPlacement<Self::CustomIdent>> {
238        Default::default()
239    }
240    #[inline(always)]
242    fn grid_column(&self) -> Line<GridPlacement<Self::CustomIdent>> {
243        Default::default()
244    }
245
246    #[inline(always)]
249    fn align_self(&self) -> Option<AlignSelf> {
250        Style::<Self::CustomIdent>::DEFAULT.align_self
251    }
252    #[inline(always)]
255    fn justify_self(&self) -> Option<AlignSelf> {
256        Style::<Self::CustomIdent>::DEFAULT.justify_self
257    }
258
259    #[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#[derive(Copy, Clone, PartialEq, Eq, Debug)]
277#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
278pub enum GridAutoFlow {
279    Row,
281    Column,
283    RowDense,
285    ColumnDense,
287}
288
289impl Default for GridAutoFlow {
290    fn default() -> Self {
291        Self::Row
292    }
293}
294
295impl GridAutoFlow {
296    pub fn is_dense(&self) -> bool {
299        match self {
300            Self::Row | Self::Column => false,
301            Self::RowDense | Self::ColumnDense => true,
302        }
303    }
304
305    pub fn primary_axis(&self) -> AbsoluteAxis {
308        match self {
309            Self::Row | Self::RowDense => AbsoluteAxis::Horizontal,
310            Self::Column | Self::ColumnDense => AbsoluteAxis::Vertical,
311        }
312    }
313}
314
315#[derive(Copy, Clone, PartialEq, Eq, Debug)]
323#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
324pub enum GenericGridPlacement<LineType: GridCoordinate> {
325    Auto,
327    Line(LineType),
329    Span(u16),
331}
332
333pub(crate) type OriginZeroGridPlacement = GenericGridPlacement<OriginZeroLine>;
335
336pub(crate) type NonNamedGridPlacement = GenericGridPlacement<GridLine>;
340
341#[derive(Clone, PartialEq, Debug)]
347#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
348pub enum GridPlacement<S: CheapCloneStr = DefaultCheapStr> {
349    Auto,
351    Line(GridLine),
353    NamedLine(S, i16),
355    Span(u16),
357    NamedSpan(S, u16),
362}
363impl<S: CheapCloneStr> TaffyAuto for GridPlacement<S> {
364    const AUTO: Self = Self::Auto;
365}
366impl<S: CheapCloneStr> TaffyGridLine for GridPlacement<S> {
367    fn from_line_index(index: i16) -> Self {
368        GridPlacement::<S>::Line(GridLine::from(index))
369    }
370}
371impl<S: CheapCloneStr> TaffyGridLine for Line<GridPlacement<S>> {
372    fn from_line_index(index: i16) -> Self {
373        Line { start: GridPlacement::<S>::from_line_index(index), end: GridPlacement::<S>::Auto }
374    }
375}
376impl<S: CheapCloneStr> TaffyGridSpan for GridPlacement<S> {
377    fn from_span(span: u16) -> Self {
378        GridPlacement::<S>::Span(span)
379    }
380}
381impl<S: CheapCloneStr> TaffyGridSpan for Line<GridPlacement<S>> {
382    fn from_span(span: u16) -> Self {
383        Line { start: GridPlacement::<S>::from_span(span), end: GridPlacement::<S>::Auto }
384    }
385}
386
387impl<S: CheapCloneStr> Default for GridPlacement<S> {
388    fn default() -> Self {
389        Self::Auto
390    }
391}
392
393impl<S: CheapCloneStr> GridPlacement<S> {
394    pub fn into_origin_zero_placement_ignoring_named(&self, explicit_track_count: u16) -> OriginZeroGridPlacement {
396        match self {
397            Self::Auto => OriginZeroGridPlacement::Auto,
398            Self::Span(span) => OriginZeroGridPlacement::Span(*span),
399            Self::Line(line) => match line.as_i16() {
402                0 => OriginZeroGridPlacement::Auto,
403                _ => OriginZeroGridPlacement::Line(line.into_origin_zero_line(explicit_track_count)),
404            },
405            Self::NamedLine(_, _) => OriginZeroGridPlacement::Auto,
406            Self::NamedSpan(_, _) => OriginZeroGridPlacement::Auto,
407        }
408    }
409}
410
411impl<S: CheapCloneStr> Line<GridPlacement<S>> {
412    pub fn into_origin_zero_ignoring_named(&self, explicit_track_count: u16) -> Line<OriginZeroGridPlacement> {
414        Line {
415            start: self.start.into_origin_zero_placement_ignoring_named(explicit_track_count),
416            end: self.end.into_origin_zero_placement_ignoring_named(explicit_track_count),
417        }
418    }
419}
420
421impl NonNamedGridPlacement {
422    pub fn into_origin_zero_placement(
424        &self,
425        explicit_track_count: u16,
426        ) -> OriginZeroGridPlacement {
428        match self {
429            Self::Auto => OriginZeroGridPlacement::Auto,
430            Self::Span(span) => OriginZeroGridPlacement::Span(*span),
431            Self::Line(line) => match line.as_i16() {
434                0 => OriginZeroGridPlacement::Auto,
435                _ => OriginZeroGridPlacement::Line(line.into_origin_zero_line(explicit_track_count)),
436            },
437        }
438    }
439}
440
441impl<T: GridCoordinate> Line<GenericGridPlacement<T>> {
442    pub fn indefinite_span(&self) -> u16 {
445        use GenericGridPlacement as GP;
446        match (self.start, self.end) {
447            (GP::Line(_), GP::Auto) => 1,
448            (GP::Auto, GP::Line(_)) => 1,
449            (GP::Auto, GP::Auto) => 1,
450            (GP::Line(_), GP::Span(span)) => span,
451            (GP::Span(span), GP::Line(_)) => span,
452            (GP::Span(span), GP::Auto) => span,
453            (GP::Auto, GP::Span(span)) => span,
454            (GP::Span(span), GP::Span(_)) => span,
455            (GP::Line(_), GP::Line(_)) => panic!("indefinite_span should only be called on indefinite grid tracks"),
456        }
457    }
458}
459
460impl<S: CheapCloneStr> Line<GridPlacement<S>> {
461    #[inline]
462    pub fn is_definite(&self) -> bool {
466        match (&self.start, &self.end) {
467            (GridPlacement::Line(line), _) if line.as_i16() != 0 => true,
468            (_, GridPlacement::Line(line)) if line.as_i16() != 0 => true,
469            (GridPlacement::NamedLine(_, _), _) => true,
470            (_, GridPlacement::NamedLine(_, _)) => true,
471            _ => false,
472        }
473    }
474}
475
476impl Line<NonNamedGridPlacement> {
477    #[inline]
478    pub fn is_definite(&self) -> bool {
482        match (&self.start, &self.end) {
483            (GenericGridPlacement::Line(line), _) if line.as_i16() != 0 => true,
484            (_, GenericGridPlacement::Line(line)) if line.as_i16() != 0 => true,
485            _ => false,
486        }
487    }
488
489    pub fn into_origin_zero(&self, explicit_track_count: u16) -> Line<OriginZeroGridPlacement> {
491        Line {
492            start: self.start.into_origin_zero_placement(explicit_track_count),
493            end: self.end.into_origin_zero_placement(explicit_track_count),
494        }
495    }
496}
497
498impl Line<OriginZeroGridPlacement> {
499    #[inline]
500    pub fn is_definite(&self) -> bool {
503        matches!((self.start, self.end), (GenericGridPlacement::Line(_), _) | (_, GenericGridPlacement::Line(_)))
504    }
505
506    pub fn resolve_definite_grid_lines(&self) -> Line<OriginZeroLine> {
509        use OriginZeroGridPlacement as GP;
510        match (self.start, self.end) {
511            (GP::Line(line1), GP::Line(line2)) => {
512                if line1 == line2 {
513                    Line { start: line1, end: line1 + 1 }
514                } else {
515                    Line { start: min(line1, line2), end: max(line1, line2) }
516                }
517            }
518            (GP::Line(line), GP::Span(span)) => Line { start: line, end: line + span },
519            (GP::Line(line), GP::Auto) => Line { start: line, end: line + 1 },
520            (GP::Span(span), GP::Line(line)) => Line { start: line - span, end: line },
521            (GP::Auto, GP::Line(line)) => Line { start: line - 1, end: line },
522            _ => panic!("resolve_definite_grid_tracks should only be called on definite grid tracks"),
523        }
524    }
525
526    pub fn resolve_absolutely_positioned_grid_tracks(&self) -> Line<Option<OriginZeroLine>> {
536        use OriginZeroGridPlacement as GP;
537        match (self.start, self.end) {
538            (GP::Line(track1), GP::Line(track2)) => {
539                if track1 == track2 {
540                    Line { start: Some(track1), end: Some(track1 + 1) }
541                } else {
542                    Line { start: Some(min(track1, track2)), end: Some(max(track1, track2)) }
543                }
544            }
545            (GP::Line(track), GP::Span(span)) => Line { start: Some(track), end: Some(track + span) },
546            (GP::Line(track), GP::Auto) => Line { start: Some(track), end: None },
547            (GP::Span(span), GP::Line(track)) => Line { start: Some(track - span), end: Some(track) },
548            (GP::Auto, GP::Line(track)) => Line { start: None, end: Some(track) },
549            _ => Line { start: None, end: None },
550        }
551    }
552
553    pub fn resolve_indefinite_grid_tracks(&self, start: OriginZeroLine) -> Line<OriginZeroLine> {
556        use OriginZeroGridPlacement as GP;
557        match (self.start, self.end) {
558            (GP::Auto, GP::Auto) => Line { start, end: start + 1 },
559            (GP::Span(span), GP::Auto) => Line { start, end: start + span },
560            (GP::Auto, GP::Span(span)) => Line { start, end: start + span },
561            (GP::Span(span), GP::Span(_)) => Line { start, end: start + span },
562            _ => panic!("resolve_indefinite_grid_tracks should only be called on indefinite grid tracks"),
563        }
564    }
565}
566
567impl<S: CheapCloneStr> Default for Line<GridPlacement<S>> {
569    fn default() -> Self {
570        Line { start: GridPlacement::<S>::Auto, end: GridPlacement::<S>::Auto }
571    }
572}
573
574#[derive(Copy, Clone, PartialEq, Debug)]
580#[cfg_attr(feature = "serde", derive(Serialize))]
581pub struct MaxTrackSizingFunction(pub(crate) CompactLength);
582impl TaffyZero for MaxTrackSizingFunction {
583    const ZERO: Self = Self(CompactLength::ZERO);
584}
585impl TaffyAuto for MaxTrackSizingFunction {
586    const AUTO: Self = Self(CompactLength::AUTO);
587}
588impl TaffyMinContent for MaxTrackSizingFunction {
589    const MIN_CONTENT: Self = Self(CompactLength::MIN_CONTENT);
590}
591impl TaffyMaxContent for MaxTrackSizingFunction {
592    const MAX_CONTENT: Self = Self(CompactLength::MAX_CONTENT);
593}
594impl FromLength for MaxTrackSizingFunction {
595    fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
596        Self::length(value.into())
597    }
598}
599impl FromPercent for MaxTrackSizingFunction {
600    fn from_percent<Input: Into<f32> + Copy>(value: Input) -> Self {
601        Self::percent(value.into())
602    }
603}
604impl TaffyFitContent for MaxTrackSizingFunction {
605    fn fit_content(argument: LengthPercentage) -> Self {
606        Self(CompactLength::fit_content(argument))
607    }
608}
609impl FromFr for MaxTrackSizingFunction {
610    fn from_fr<Input: Into<f32> + Copy>(value: Input) -> Self {
611        Self::fr(value.into())
612    }
613}
614impl From<LengthPercentage> for MaxTrackSizingFunction {
615    fn from(input: LengthPercentage) -> Self {
616        Self(input.0)
617    }
618}
619impl From<LengthPercentageAuto> for MaxTrackSizingFunction {
620    fn from(input: LengthPercentageAuto) -> Self {
621        Self(input.0)
622    }
623}
624impl From<Dimension> for MaxTrackSizingFunction {
625    fn from(input: Dimension) -> Self {
626        Self(input.0)
627    }
628}
629impl From<MinTrackSizingFunction> for MaxTrackSizingFunction {
630    fn from(input: MinTrackSizingFunction) -> Self {
631        Self(input.0)
632    }
633}
634#[cfg(feature = "serde")]
635impl<'de> serde::Deserialize<'de> for MaxTrackSizingFunction {
636    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
637    where
638        D: serde::Deserializer<'de>,
639    {
640        let inner = CompactLength::deserialize(deserializer)?;
641        if matches!(
643            inner.tag(),
644            CompactLength::LENGTH_TAG
645                | CompactLength::PERCENT_TAG
646                | CompactLength::AUTO_TAG
647                | CompactLength::MIN_CONTENT_TAG
648                | CompactLength::MAX_CONTENT_TAG
649                | CompactLength::FIT_CONTENT_PX_TAG
650                | CompactLength::FIT_CONTENT_PERCENT_TAG
651                | CompactLength::FR_TAG
652        ) {
653            Ok(Self(inner))
654        } else {
655            Err(serde::de::Error::custom("Invalid tag"))
656        }
657    }
658}
659
660impl MaxTrackSizingFunction {
661    #[inline(always)]
664    pub const fn length(val: f32) -> Self {
665        Self(CompactLength::length(val))
666    }
667
668    #[inline(always)]
672    pub const fn percent(val: f32) -> Self {
673        Self(CompactLength::percent(val))
674    }
675
676    #[inline(always)]
679    pub const fn auto() -> Self {
680        Self(CompactLength::auto())
681    }
682
683    #[inline(always)]
686    pub const fn min_content() -> Self {
687        Self(CompactLength::min_content())
688    }
689
690    #[inline(always)]
693    pub const fn max_content() -> Self {
694        Self(CompactLength::max_content())
695    }
696
697    #[inline(always)]
707    pub const fn fit_content_px(limit: f32) -> Self {
708        Self(CompactLength::fit_content_px(limit))
709    }
710
711    #[inline(always)]
721    pub const fn fit_content_percent(limit: f32) -> Self {
722        Self(CompactLength::fit_content_percent(limit))
723    }
724
725    #[inline(always)]
729    pub const fn fr(val: f32) -> Self {
730        Self(CompactLength::fr(val))
731    }
732
733    #[inline]
738    #[cfg(feature = "calc")]
739    pub fn calc(ptr: *const ()) -> Self {
740        Self(CompactLength::calc(ptr))
741    }
742
743    #[allow(unsafe_code)]
747    pub unsafe fn from_raw(val: CompactLength) -> Self {
748        Self(val)
749    }
750
751    pub fn into_raw(self) -> CompactLength {
753        self.0
754    }
755
756    #[inline(always)]
758    pub fn is_intrinsic(&self) -> bool {
759        self.0.is_intrinsic()
760    }
761
762    #[inline(always)]
766    pub fn is_max_content_alike(&self) -> bool {
767        self.0.is_max_content_alike()
768    }
769
770    #[inline(always)]
772    pub fn is_fr(&self) -> bool {
773        self.0.is_fr()
774    }
775
776    #[inline(always)]
778    pub fn is_auto(&self) -> bool {
779        self.0.is_auto()
780    }
781
782    #[inline(always)]
784    pub fn is_min_content(&self) -> bool {
785        self.0.is_min_content()
786    }
787
788    #[inline(always)]
790    pub fn is_max_content(&self) -> bool {
791        self.0.is_max_content()
792    }
793
794    #[inline(always)]
796    pub fn is_fit_content(&self) -> bool {
797        self.0.is_fit_content()
798    }
799
800    #[inline(always)]
802    pub fn is_max_or_fit_content(&self) -> bool {
803        self.0.is_max_or_fit_content()
804    }
805
806    #[inline(always)]
808    pub fn has_definite_value(self, parent_size: Option<f32>) -> bool {
809        match self.0.tag() {
810            CompactLength::LENGTH_TAG => true,
811            CompactLength::PERCENT_TAG => parent_size.is_some(),
812            #[cfg(feature = "calc")]
813            _ if self.0.is_calc() => parent_size.is_some(),
814            _ => false,
815        }
816    }
817
818    #[inline(always)]
822    pub fn definite_value(
823        self,
824        parent_size: Option<f32>,
825        calc_resolver: impl Fn(*const (), f32) -> f32,
826    ) -> Option<f32> {
827        match self.0.tag() {
828            CompactLength::LENGTH_TAG => Some(self.0.value()),
829            CompactLength::PERCENT_TAG => parent_size.map(|size| self.0.value() * size),
830            #[cfg(feature = "calc")]
831            _ if self.0.is_calc() => parent_size.map(|size| calc_resolver(self.0.calc_value(), size)),
832            _ => None,
833        }
834    }
835
836    #[inline(always)]
843    pub fn definite_limit(
844        self,
845        parent_size: Option<f32>,
846        calc_resolver: impl Fn(*const (), f32) -> f32,
847    ) -> Option<f32> {
848        match self.0.tag() {
849            CompactLength::FIT_CONTENT_PX_TAG => Some(self.0.value()),
850            CompactLength::FIT_CONTENT_PERCENT_TAG => parent_size.map(|size| self.0.value() * size),
851            _ => self.definite_value(parent_size, calc_resolver),
852        }
853    }
854
855    #[inline(always)]
858    pub fn resolved_percentage_size(
859        self,
860        parent_size: f32,
861        calc_resolver: impl Fn(*const (), f32) -> f32,
862    ) -> Option<f32> {
863        self.0.resolved_percentage_size(parent_size, calc_resolver)
864    }
865
866    #[inline(always)]
868    pub fn uses_percentage(self) -> bool {
869        self.0.uses_percentage()
870    }
871}
872
873#[derive(Copy, Clone, PartialEq, Debug)]
879#[cfg_attr(feature = "serde", derive(Serialize))]
880pub struct MinTrackSizingFunction(pub(crate) CompactLength);
881impl TaffyZero for MinTrackSizingFunction {
882    const ZERO: Self = Self(CompactLength::ZERO);
883}
884impl TaffyAuto for MinTrackSizingFunction {
885    const AUTO: Self = Self(CompactLength::AUTO);
886}
887impl TaffyMinContent for MinTrackSizingFunction {
888    const MIN_CONTENT: Self = Self(CompactLength::MIN_CONTENT);
889}
890impl TaffyMaxContent for MinTrackSizingFunction {
891    const MAX_CONTENT: Self = Self(CompactLength::MAX_CONTENT);
892}
893impl FromLength for MinTrackSizingFunction {
894    fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
895        Self::length(value.into())
896    }
897}
898impl FromPercent for MinTrackSizingFunction {
899    fn from_percent<Input: Into<f32> + Copy>(value: Input) -> Self {
900        Self::percent(value.into())
901    }
902}
903impl From<LengthPercentage> for MinTrackSizingFunction {
904    fn from(input: LengthPercentage) -> Self {
905        Self(input.0)
906    }
907}
908impl From<LengthPercentageAuto> for MinTrackSizingFunction {
909    fn from(input: LengthPercentageAuto) -> Self {
910        Self(input.0)
911    }
912}
913impl From<Dimension> for MinTrackSizingFunction {
914    fn from(input: Dimension) -> Self {
915        Self(input.0)
916    }
917}
918#[cfg(feature = "serde")]
919impl<'de> serde::Deserialize<'de> for MinTrackSizingFunction {
920    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
921    where
922        D: serde::Deserializer<'de>,
923    {
924        let inner = CompactLength::deserialize(deserializer)?;
925        if matches!(
927            inner.tag(),
928            CompactLength::LENGTH_TAG
929                | CompactLength::PERCENT_TAG
930                | CompactLength::AUTO_TAG
931                | CompactLength::MIN_CONTENT_TAG
932                | CompactLength::MAX_CONTENT_TAG
933                | CompactLength::FIT_CONTENT_PX_TAG
934                | CompactLength::FIT_CONTENT_PERCENT_TAG
935        ) {
936            Ok(Self(inner))
937        } else {
938            Err(serde::de::Error::custom("Invalid tag"))
939        }
940    }
941}
942
943impl MinTrackSizingFunction {
944    #[inline(always)]
947    pub const fn length(val: f32) -> Self {
948        Self(CompactLength::length(val))
949    }
950
951    #[inline(always)]
955    pub const fn percent(val: f32) -> Self {
956        Self(CompactLength::percent(val))
957    }
958
959    #[inline(always)]
962    pub const fn auto() -> Self {
963        Self(CompactLength::auto())
964    }
965
966    #[inline(always)]
969    pub const fn min_content() -> Self {
970        Self(CompactLength::min_content())
971    }
972
973    #[inline(always)]
976    pub const fn max_content() -> Self {
977        Self(CompactLength::max_content())
978    }
979
980    #[inline]
985    #[cfg(feature = "calc")]
986    pub fn calc(ptr: *const ()) -> Self {
987        Self(CompactLength::calc(ptr))
988    }
989
990    #[allow(unsafe_code)]
994    pub unsafe fn from_raw(val: CompactLength) -> Self {
995        Self(val)
996    }
997
998    pub fn into_raw(self) -> CompactLength {
1000        self.0
1001    }
1002
1003    #[inline(always)]
1005    pub fn is_intrinsic(&self) -> bool {
1006        self.0.is_intrinsic()
1007    }
1008
1009    #[inline(always)]
1011    pub fn is_min_or_max_content(&self) -> bool {
1012        self.0.is_min_or_max_content()
1013    }
1014
1015    #[inline(always)]
1017    pub fn is_fr(&self) -> bool {
1018        self.0.is_fr()
1019    }
1020
1021    #[inline(always)]
1023    pub fn is_auto(&self) -> bool {
1024        self.0.is_auto()
1025    }
1026
1027    #[inline(always)]
1029    pub fn is_min_content(&self) -> bool {
1030        self.0.is_min_content()
1031    }
1032
1033    #[inline(always)]
1035    pub fn is_max_content(&self) -> bool {
1036        self.0.is_max_content()
1037    }
1038
1039    #[inline(always)]
1043    pub fn definite_value(
1044        self,
1045        parent_size: Option<f32>,
1046        calc_resolver: impl Fn(*const (), f32) -> f32,
1047    ) -> Option<f32> {
1048        match self.0.tag() {
1049            CompactLength::LENGTH_TAG => Some(self.0.value()),
1050            CompactLength::PERCENT_TAG => parent_size.map(|size| self.0.value() * size),
1051            #[cfg(feature = "calc")]
1052            _ if self.0.is_calc() => parent_size.map(|size| calc_resolver(self.0.calc_value(), size)),
1053            _ => None,
1054        }
1055    }
1056
1057    #[inline(always)]
1060    pub fn resolved_percentage_size(
1061        self,
1062        parent_size: f32,
1063        calc_resolver: impl Fn(*const (), f32) -> f32,
1064    ) -> Option<f32> {
1065        self.0.resolved_percentage_size(parent_size, calc_resolver)
1066    }
1067
1068    #[inline(always)]
1070    pub fn uses_percentage(self) -> bool {
1071        #[cfg(feature = "calc")]
1072        {
1073            matches!(self.0.tag(), CompactLength::PERCENT_TAG) || self.0.is_calc()
1074        }
1075        #[cfg(not(feature = "calc"))]
1076        {
1077            matches!(self.0.tag(), CompactLength::PERCENT_TAG)
1078        }
1079    }
1080}
1081
1082pub type TrackSizingFunction = MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>;
1087impl TrackSizingFunction {
1088    pub fn min_sizing_function(&self) -> MinTrackSizingFunction {
1090        self.min
1091    }
1092    pub fn max_sizing_function(&self) -> MaxTrackSizingFunction {
1094        self.max
1095    }
1096    pub fn has_fixed_component(&self) -> bool {
1098        self.min.0.is_length_or_percentage() || self.max.0.is_length_or_percentage()
1099    }
1100}
1101impl TaffyAuto for TrackSizingFunction {
1102    const AUTO: Self = Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::AUTO };
1103}
1104impl TaffyMinContent for TrackSizingFunction {
1105    const MIN_CONTENT: Self =
1106        Self { min: MinTrackSizingFunction::MIN_CONTENT, max: MaxTrackSizingFunction::MIN_CONTENT };
1107}
1108impl TaffyMaxContent for TrackSizingFunction {
1109    const MAX_CONTENT: Self =
1110        Self { min: MinTrackSizingFunction::MAX_CONTENT, max: MaxTrackSizingFunction::MAX_CONTENT };
1111}
1112impl TaffyFitContent for TrackSizingFunction {
1113    fn fit_content(argument: LengthPercentage) -> Self {
1114        Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::fit_content(argument) }
1115    }
1116}
1117impl TaffyZero for TrackSizingFunction {
1118    const ZERO: Self = Self { min: MinTrackSizingFunction::ZERO, max: MaxTrackSizingFunction::ZERO };
1119}
1120impl FromLength for TrackSizingFunction {
1121    fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
1122        Self { min: MinTrackSizingFunction::from_length(value), max: MaxTrackSizingFunction::from_length(value) }
1123    }
1124}
1125impl FromPercent for TrackSizingFunction {
1126    fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
1127        Self { min: MinTrackSizingFunction::from_percent(percent), max: MaxTrackSizingFunction::from_percent(percent) }
1128    }
1129}
1130impl FromFr for TrackSizingFunction {
1131    fn from_fr<Input: Into<f32> + Copy>(flex: Input) -> Self {
1132        Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::from_fr(flex) }
1133    }
1134}
1135impl From<LengthPercentage> for TrackSizingFunction {
1136    fn from(input: LengthPercentage) -> Self {
1137        Self { min: input.into(), max: input.into() }
1138    }
1139}
1140impl From<LengthPercentageAuto> for TrackSizingFunction {
1141    fn from(input: LengthPercentageAuto) -> Self {
1142        Self { min: input.into(), max: input.into() }
1143    }
1144}
1145impl From<Dimension> for TrackSizingFunction {
1146    fn from(input: Dimension) -> Self {
1147        Self { min: input.into(), max: input.into() }
1148    }
1149}
1150
1151#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1156#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1157pub enum RepetitionCount {
1158    AutoFill,
1161    AutoFit,
1164    Count(u16),
1166}
1167impl From<u16> for RepetitionCount {
1168    fn from(value: u16) -> Self {
1169        Self::Count(value)
1170    }
1171}
1172
1173#[derive(Debug)]
1176pub struct InvalidStringRepetitionValue;
1177#[cfg(feature = "std")]
1178impl std::error::Error for InvalidStringRepetitionValue {}
1179impl core::fmt::Display for InvalidStringRepetitionValue {
1180    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1181        f.write_str("&str can only be converted to GridTrackRepetition if it's value is 'auto-fit' or 'auto-fill'")
1182    }
1183}
1184impl TryFrom<&str> for RepetitionCount {
1185    type Error = InvalidStringRepetitionValue;
1186    fn try_from(value: &str) -> Result<Self, InvalidStringRepetitionValue> {
1187        match value {
1188            "auto-fit" => Ok(Self::AutoFit),
1189            "auto-fill" => Ok(Self::AutoFill),
1190            _ => Err(InvalidStringRepetitionValue),
1191        }
1192    }
1193}
1194
1195#[derive(Clone, PartialEq, Debug)]
1197#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1198pub struct GridTemplateRepetition<S: CheapCloneStr> {
1199    pub count: RepetitionCount,
1201    pub tracks: Vec<TrackSizingFunction>,
1203    pub line_names: Vec<Vec<S>>,
1205}
1206
1207#[rustfmt::skip]
1208impl<S: CheapCloneStr> GenericRepetition for &'_ GridTemplateRepetition<S> {
1209    type CustomIdent = S;
1210    type RepetitionTrackList<'a> = core::iter::Copied<core::slice::Iter<'a, TrackSizingFunction>> where Self: 'a;
1211    type TemplateLineNames<'a> = core::iter::Map<core::slice::Iter<'a, Vec<S>>, fn(&Vec<S>) -> core::slice::Iter<'_, S>> where Self: 'a;
1212    #[inline(always)]
1213    fn count(&self) -> RepetitionCount {
1214        self.count
1215    }
1216    #[inline(always)]
1217    fn track_count(&self) -> u16 {
1218        self.tracks.len() as u16
1219    }
1220    #[inline(always)]
1221    fn tracks(&self) -> Self::RepetitionTrackList<'_> {
1222        self.tracks.iter().copied()
1223    }
1224    #[inline(always)]
1225    fn lines_names(&self) -> Self::TemplateLineNames<'_> {
1226        self.line_names.iter().map(|names| names.iter())
1227    }
1228}
1229
1230#[derive(Clone, PartialEq, Debug)]
1235#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1236pub enum GridTemplateComponent<S: CheapCloneStr> {
1237    Single(TrackSizingFunction),
1239    Repeat(GridTemplateRepetition<S>),
1242}
1243
1244impl<S: CheapCloneStr> GridTemplateComponent<S> {
1245    pub fn as_component_ref(&self) -> GenericGridTemplateComponent<S, &GridTemplateRepetition<S>> {
1247        match self {
1248            GridTemplateComponent::Single(size) => GenericGridTemplateComponent::Single(*size),
1249            GridTemplateComponent::Repeat(repetition) => GenericGridTemplateComponent::Repeat(repetition),
1250        }
1251    }
1252}
1253
1254impl<S: CheapCloneStr> GridTemplateComponent<S> {
1255    pub fn is_auto_repetition(&self) -> bool {
1257        matches!(
1258            self,
1259            Self::Repeat(GridTemplateRepetition { count: RepetitionCount::AutoFit | RepetitionCount::AutoFill, .. })
1260        )
1261    }
1262}
1263impl<S: CheapCloneStr> TaffyAuto for GridTemplateComponent<S> {
1264    const AUTO: Self = Self::Single(TrackSizingFunction::AUTO);
1265}
1266impl<S: CheapCloneStr> TaffyMinContent for GridTemplateComponent<S> {
1267    const MIN_CONTENT: Self = Self::Single(TrackSizingFunction::MIN_CONTENT);
1268}
1269impl<S: CheapCloneStr> TaffyMaxContent for GridTemplateComponent<S> {
1270    const MAX_CONTENT: Self = Self::Single(TrackSizingFunction::MAX_CONTENT);
1271}
1272impl<S: CheapCloneStr> TaffyFitContent for GridTemplateComponent<S> {
1273    fn fit_content(argument: LengthPercentage) -> Self {
1274        Self::Single(TrackSizingFunction::fit_content(argument))
1275    }
1276}
1277impl<S: CheapCloneStr> TaffyZero for GridTemplateComponent<S> {
1278    const ZERO: Self = Self::Single(TrackSizingFunction::ZERO);
1279}
1280impl<S: CheapCloneStr> FromLength for GridTemplateComponent<S> {
1281    fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
1282        Self::Single(TrackSizingFunction::from_length(value))
1283    }
1284}
1285impl<S: CheapCloneStr> FromPercent for GridTemplateComponent<S> {
1286    fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
1287        Self::Single(TrackSizingFunction::from_percent(percent))
1288    }
1289}
1290impl<S: CheapCloneStr> FromFr for GridTemplateComponent<S> {
1291    fn from_fr<Input: Into<f32> + Copy>(flex: Input) -> Self {
1292        Self::Single(TrackSizingFunction::from_fr(flex))
1293    }
1294}
1295impl<S: CheapCloneStr> From<MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>> for GridTemplateComponent<S> {
1296    fn from(input: MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>) -> Self {
1297        Self::Single(input)
1298    }
1299}