1use super::{AlignContent, LengthPercentage, Style};
3use crate::compute::grid::{GridCoordinate, GridLine, OriginZeroLine};
4use crate::geometry::{AbsoluteAxis, AbstractAxis};
5use crate::geometry::{Line, MinMax};
6use crate::style_helpers::*;
7use crate::util::sys::GridTrackVec;
8use core::cmp::{max, min};
9use core::convert::Infallible;
10
11#[derive(Copy, Clone, PartialEq, Eq, Debug)]
19#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
20pub enum GridAutoFlow {
21 Row,
23 Column,
25 RowDense,
27 ColumnDense,
29}
30
31impl Default for GridAutoFlow {
32 fn default() -> Self {
33 Self::Row
34 }
35}
36
37impl GridAutoFlow {
38 pub fn is_dense(&self) -> bool {
41 match self {
42 Self::Row | Self::Column => false,
43 Self::RowDense | Self::ColumnDense => true,
44 }
45 }
46
47 pub fn primary_axis(&self) -> AbsoluteAxis {
50 match self {
51 Self::Row | Self::RowDense => AbsoluteAxis::Horizontal,
52 Self::Column | Self::ColumnDense => AbsoluteAxis::Vertical,
53 }
54 }
55}
56
57#[derive(Copy, Clone, PartialEq, Eq, Debug)]
65#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
66pub enum GenericGridPlacement<LineType: GridCoordinate> {
67 Auto,
69 Line(LineType),
71 Span(u16),
73}
74
75pub(crate) type OriginZeroGridPlacement = GenericGridPlacement<OriginZeroLine>;
77
78pub type GridPlacement = GenericGridPlacement<GridLine>;
84impl TaffyAuto for GridPlacement {
85 const AUTO: Self = Self::Auto;
86}
87impl TaffyGridLine for GridPlacement {
88 fn from_line_index(index: i16) -> Self {
89 GridPlacement::Line(GridLine::from(index))
90 }
91}
92impl TaffyGridLine for Line<GridPlacement> {
93 fn from_line_index(index: i16) -> Self {
94 Line { start: GridPlacement::from_line_index(index), end: GridPlacement::Auto }
95 }
96}
97impl TaffyGridSpan for GridPlacement {
98 fn from_span(span: u16) -> Self {
99 GridPlacement::Span(span)
100 }
101}
102impl TaffyGridSpan for Line<GridPlacement> {
103 fn from_span(span: u16) -> Self {
104 Line { start: GridPlacement::from_span(span), end: GridPlacement::Auto }
105 }
106}
107
108impl Default for GridPlacement {
109 fn default() -> Self {
110 Self::Auto
111 }
112}
113
114impl GridPlacement {
115 pub fn into_origin_zero_placement(self, explicit_track_count: u16) -> OriginZeroGridPlacement {
117 match self {
118 Self::Auto => OriginZeroGridPlacement::Auto,
119 Self::Span(span) => OriginZeroGridPlacement::Span(span),
120 Self::Line(line) => match line.as_i16() {
123 0 => OriginZeroGridPlacement::Auto,
124 _ => OriginZeroGridPlacement::Line(line.into_origin_zero_line(explicit_track_count)),
125 },
126 }
127 }
128}
129
130impl<T: GridCoordinate> Line<GenericGridPlacement<T>> {
131 #[inline]
132 pub fn is_definite(&self) -> bool {
135 matches!((self.start, self.end), (GenericGridPlacement::Line(_), _) | (_, GenericGridPlacement::Line(_)))
136 }
137
138 pub fn indefinite_span(&self) -> u16 {
141 use GenericGridPlacement as GP;
142 match (self.start, self.end) {
143 (GP::Line(_), GP::Auto) => 1,
144 (GP::Auto, GP::Line(_)) => 1,
145 (GP::Auto, GP::Auto) => 1,
146 (GP::Line(_), GP::Span(span)) => span,
147 (GP::Span(span), GP::Line(_)) => span,
148 (GP::Span(span), GP::Auto) => span,
149 (GP::Auto, GP::Span(span)) => span,
150 (GP::Span(span), GP::Span(_)) => span,
151 (GP::Line(_), GP::Line(_)) => panic!("indefinite_span should only be called on indefinite grid tracks"),
152 }
153 }
154}
155
156impl Line<GridPlacement> {
157 pub fn into_origin_zero(&self, explicit_track_count: u16) -> Line<OriginZeroGridPlacement> {
159 Line {
160 start: self.start.into_origin_zero_placement(explicit_track_count),
161 end: self.end.into_origin_zero_placement(explicit_track_count),
162 }
163 }
164}
165
166impl Line<OriginZeroGridPlacement> {
167 pub fn resolve_definite_grid_lines(&self) -> Line<OriginZeroLine> {
170 use OriginZeroGridPlacement as GP;
171 match (self.start, self.end) {
172 (GP::Line(line1), GP::Line(line2)) => {
173 if line1 == line2 {
174 Line { start: line1, end: line1 + 1 }
175 } else {
176 Line { start: min(line1, line2), end: max(line1, line2) }
177 }
178 }
179 (GP::Line(line), GP::Span(span)) => Line { start: line, end: line + span },
180 (GP::Line(line), GP::Auto) => Line { start: line, end: line + 1 },
181 (GP::Span(span), GP::Line(line)) => Line { start: line - span, end: line },
182 (GP::Auto, GP::Line(line)) => Line { start: line - 1, end: line },
183 _ => panic!("resolve_definite_grid_tracks should only be called on definite grid tracks"),
184 }
185 }
186
187 pub fn resolve_absolutely_positioned_grid_tracks(&self) -> Line<Option<OriginZeroLine>> {
197 use OriginZeroGridPlacement as GP;
198 match (self.start, self.end) {
199 (GP::Line(track1), GP::Line(track2)) => {
200 if track1 == track2 {
201 Line { start: Some(track1), end: Some(track1 + 1) }
202 } else {
203 Line { start: Some(min(track1, track2)), end: Some(max(track1, track2)) }
204 }
205 }
206 (GP::Line(track), GP::Span(span)) => Line { start: Some(track), end: Some(track + span) },
207 (GP::Line(track), GP::Auto) => Line { start: Some(track), end: None },
208 (GP::Span(span), GP::Line(track)) => Line { start: Some(track - span), end: Some(track) },
209 (GP::Auto, GP::Line(track)) => Line { start: None, end: Some(track) },
210 _ => Line { start: None, end: None },
211 }
212 }
213
214 pub fn resolve_indefinite_grid_tracks(&self, start: OriginZeroLine) -> Line<OriginZeroLine> {
217 use OriginZeroGridPlacement as GP;
218 match (self.start, self.end) {
219 (GP::Auto, GP::Auto) => Line { start, end: start + 1 },
220 (GP::Span(span), GP::Auto) => Line { start, end: start + span },
221 (GP::Auto, GP::Span(span)) => Line { start, end: start + span },
222 (GP::Span(span), GP::Span(_)) => Line { start, end: start + span },
223 _ => panic!("resolve_indefinite_grid_tracks should only be called on indefinite grid tracks"),
224 }
225 }
226}
227
228impl Default for Line<GridPlacement> {
230 fn default() -> Self {
231 Line { start: GridPlacement::Auto, end: GridPlacement::Auto }
232 }
233}
234
235#[derive(Copy, Clone, PartialEq, Debug)]
241#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
242pub enum MaxTrackSizingFunction {
243 Fixed(LengthPercentage),
245 MinContent,
247 MaxContent,
249 FitContent(LengthPercentage),
251 Auto,
253 Fraction(f32),
257}
258impl TaffyAuto for MaxTrackSizingFunction {
259 const AUTO: Self = Self::Auto;
260}
261impl TaffyMinContent for MaxTrackSizingFunction {
262 const MIN_CONTENT: Self = Self::MinContent;
263}
264impl TaffyMaxContent for MaxTrackSizingFunction {
265 const MAX_CONTENT: Self = Self::MaxContent;
266}
267impl TaffyFitContent for MaxTrackSizingFunction {
268 fn fit_content(argument: LengthPercentage) -> Self {
269 Self::FitContent(argument)
270 }
271}
272impl TaffyZero for MaxTrackSizingFunction {
273 const ZERO: Self = Self::Fixed(LengthPercentage::ZERO);
274}
275impl FromLength for MaxTrackSizingFunction {
276 fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
277 Self::Fixed(LengthPercentage::from_length(value))
278 }
279}
280impl FromPercent for MaxTrackSizingFunction {
281 fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
282 Self::Fixed(LengthPercentage::from_percent(percent))
283 }
284}
285impl FromFlex for MaxTrackSizingFunction {
286 fn from_flex<Input: Into<f32> + Copy>(flex: Input) -> Self {
287 Self::Fraction(flex.into())
288 }
289}
290
291impl MaxTrackSizingFunction {
292 #[inline(always)]
294 pub fn is_intrinsic(&self) -> bool {
295 matches!(self, Self::MinContent | Self::MaxContent | Self::FitContent(_) | Self::Auto)
296 }
297
298 #[inline(always)]
302 pub fn is_max_content_alike(&self) -> bool {
303 matches!(self, Self::MaxContent | Self::FitContent(_) | Self::Auto)
304 }
305
306 #[inline(always)]
308 pub fn is_flexible(&self) -> bool {
309 matches!(self, Self::Fraction(_))
310 }
311
312 #[inline(always)]
316 pub fn definite_value(self, parent_size: Option<f32>) -> Option<f32> {
317 use MaxTrackSizingFunction::*;
318 match self {
319 Fixed(LengthPercentage::Length(size)) => Some(size),
320 Fixed(LengthPercentage::Percent(fraction)) => parent_size.map(|size| fraction * size),
321 MinContent | MaxContent | FitContent(_) | Auto | Fraction(_) => None,
322 }
323 }
324
325 #[inline(always)]
332 pub fn definite_limit(self, parent_size: Option<f32>) -> Option<f32> {
333 use MaxTrackSizingFunction::FitContent;
334 match self {
335 FitContent(LengthPercentage::Length(size)) => Some(size),
336 FitContent(LengthPercentage::Percent(fraction)) => parent_size.map(|size| fraction * size),
337 _ => self.definite_value(parent_size),
338 }
339 }
340
341 #[inline(always)]
344 pub fn resolved_percentage_size(self, parent_size: f32) -> Option<f32> {
345 use MaxTrackSizingFunction::*;
346 match self {
347 Fixed(LengthPercentage::Percent(fraction)) => Some(fraction * parent_size),
348 Fixed(LengthPercentage::Length(_)) | MinContent | MaxContent | FitContent(_) | Auto | Fraction(_) => None,
349 }
350 }
351
352 #[inline(always)]
354 pub fn uses_percentage(self) -> bool {
355 use MaxTrackSizingFunction::*;
356 matches!(self, Fixed(LengthPercentage::Percent(_)) | FitContent(LengthPercentage::Percent(_)))
357 }
358}
359
360#[derive(Copy, Clone, PartialEq, Debug)]
366#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
367pub enum MinTrackSizingFunction {
368 Fixed(LengthPercentage),
370 MinContent,
372 MaxContent,
374 Auto,
376}
377impl TaffyAuto for MinTrackSizingFunction {
378 const AUTO: Self = Self::Auto;
379}
380impl TaffyMinContent for MinTrackSizingFunction {
381 const MIN_CONTENT: Self = Self::MinContent;
382}
383impl TaffyMaxContent for MinTrackSizingFunction {
384 const MAX_CONTENT: Self = Self::MaxContent;
385}
386impl TaffyZero for MinTrackSizingFunction {
387 const ZERO: Self = Self::Fixed(LengthPercentage::ZERO);
388}
389impl FromLength for MinTrackSizingFunction {
390 fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
391 Self::Fixed(LengthPercentage::from_length(value))
392 }
393}
394impl FromPercent for MinTrackSizingFunction {
395 fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
396 Self::Fixed(LengthPercentage::from_percent(percent))
397 }
398}
399
400impl MinTrackSizingFunction {
401 #[inline(always)]
403 pub fn is_intrinsic(&self) -> bool {
404 matches!(self, Self::MinContent | Self::MaxContent | Self::Auto)
405 }
406
407 #[inline(always)]
411 pub fn definite_value(self, parent_size: Option<f32>) -> Option<f32> {
412 use MinTrackSizingFunction::*;
413 match self {
414 Fixed(LengthPercentage::Length(size)) => Some(size),
415 Fixed(LengthPercentage::Percent(fraction)) => parent_size.map(|size| fraction * size),
416 MinContent | MaxContent | Auto => None,
417 }
418 }
419
420 #[inline(always)]
423 pub fn resolved_percentage_size(self, parent_size: f32) -> Option<f32> {
424 use MinTrackSizingFunction::*;
425 match self {
426 Fixed(LengthPercentage::Percent(fraction)) => Some(fraction * parent_size),
427 Fixed(LengthPercentage::Length(_)) | MinContent | MaxContent | Auto => None,
428 }
429 }
430
431 #[inline(always)]
433 pub fn uses_percentage(self) -> bool {
434 use MinTrackSizingFunction::*;
435 matches!(self, Fixed(LengthPercentage::Percent(_)))
436 }
437}
438
439pub type NonRepeatedTrackSizingFunction = MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>;
443impl NonRepeatedTrackSizingFunction {
444 pub fn min_sizing_function(&self) -> MinTrackSizingFunction {
446 self.min
447 }
448 pub fn max_sizing_function(&self) -> MaxTrackSizingFunction {
450 self.max
451 }
452 pub fn has_fixed_component(&self) -> bool {
454 matches!(self.min, MinTrackSizingFunction::Fixed(_)) || matches!(self.max, MaxTrackSizingFunction::Fixed(_))
455 }
456}
457impl TaffyAuto for NonRepeatedTrackSizingFunction {
458 const AUTO: Self = Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::AUTO };
459}
460impl TaffyMinContent for NonRepeatedTrackSizingFunction {
461 const MIN_CONTENT: Self =
462 Self { min: MinTrackSizingFunction::MIN_CONTENT, max: MaxTrackSizingFunction::MIN_CONTENT };
463}
464impl TaffyMaxContent for NonRepeatedTrackSizingFunction {
465 const MAX_CONTENT: Self =
466 Self { min: MinTrackSizingFunction::MAX_CONTENT, max: MaxTrackSizingFunction::MAX_CONTENT };
467}
468impl TaffyFitContent for NonRepeatedTrackSizingFunction {
469 fn fit_content(argument: LengthPercentage) -> Self {
470 Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::FitContent(argument) }
471 }
472}
473impl TaffyZero for NonRepeatedTrackSizingFunction {
474 const ZERO: Self = Self { min: MinTrackSizingFunction::ZERO, max: MaxTrackSizingFunction::ZERO };
475}
476impl FromLength for NonRepeatedTrackSizingFunction {
477 fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
478 Self { min: MinTrackSizingFunction::from_length(value), max: MaxTrackSizingFunction::from_length(value) }
479 }
480}
481impl FromPercent for NonRepeatedTrackSizingFunction {
482 fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
483 Self { min: MinTrackSizingFunction::from_percent(percent), max: MaxTrackSizingFunction::from_percent(percent) }
484 }
485}
486impl FromFlex for NonRepeatedTrackSizingFunction {
487 fn from_flex<Input: Into<f32> + Copy>(flex: Input) -> Self {
488 Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::from_flex(flex) }
489 }
490}
491
492#[derive(Clone, Copy, Debug, PartialEq, Eq)]
497#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
498pub enum GridTrackRepetition {
499 AutoFill,
502 AutoFit,
505 Count(u16),
507}
508impl TryFrom<u16> for GridTrackRepetition {
509 type Error = Infallible;
510 fn try_from(value: u16) -> Result<Self, Infallible> {
511 Ok(Self::Count(value))
512 }
513}
514
515#[derive(Debug)]
518pub struct InvalidStringRepetitionValue;
519#[cfg(feature = "std")]
520impl std::error::Error for InvalidStringRepetitionValue {}
521impl core::fmt::Display for InvalidStringRepetitionValue {
522 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
523 f.write_str("&str can only be converted to GridTrackRepetition if it's value is 'auto-fit' or 'auto-fill'")
524 }
525}
526impl<'a> TryFrom<&'a str> for GridTrackRepetition {
527 type Error = InvalidStringRepetitionValue;
528 fn try_from(value: &str) -> Result<Self, InvalidStringRepetitionValue> {
529 match value {
530 "auto-fit" => Ok(Self::AutoFit),
531 "auto-fill" => Ok(Self::AutoFill),
532 _ => Err(InvalidStringRepetitionValue),
533 }
534 }
535}
536
537#[derive(Clone, PartialEq, Debug)]
540#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
541pub enum TrackSizingFunction {
542 Single(NonRepeatedTrackSizingFunction),
544 Repeat(GridTrackRepetition, GridTrackVec<NonRepeatedTrackSizingFunction>),
547}
548impl TrackSizingFunction {
549 pub fn is_auto_repetition(&self) -> bool {
551 matches!(self, Self::Repeat(GridTrackRepetition::AutoFit | GridTrackRepetition::AutoFill, _))
552 }
553}
554impl TaffyAuto for TrackSizingFunction {
555 const AUTO: Self = Self::Single(NonRepeatedTrackSizingFunction::AUTO);
556}
557impl TaffyMinContent for TrackSizingFunction {
558 const MIN_CONTENT: Self = Self::Single(NonRepeatedTrackSizingFunction::MIN_CONTENT);
559}
560impl TaffyMaxContent for TrackSizingFunction {
561 const MAX_CONTENT: Self = Self::Single(NonRepeatedTrackSizingFunction::MAX_CONTENT);
562}
563impl TaffyFitContent for TrackSizingFunction {
564 fn fit_content(argument: LengthPercentage) -> Self {
565 Self::Single(NonRepeatedTrackSizingFunction::fit_content(argument))
566 }
567}
568impl TaffyZero for TrackSizingFunction {
569 const ZERO: Self = Self::Single(NonRepeatedTrackSizingFunction::ZERO);
570}
571impl FromLength for TrackSizingFunction {
572 fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
573 Self::Single(NonRepeatedTrackSizingFunction::from_length(value))
574 }
575}
576impl FromPercent for TrackSizingFunction {
577 fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
578 Self::Single(NonRepeatedTrackSizingFunction::from_percent(percent))
579 }
580}
581impl FromFlex for TrackSizingFunction {
582 fn from_flex<Input: Into<f32> + Copy>(flex: Input) -> Self {
583 Self::Single(NonRepeatedTrackSizingFunction::from_flex(flex))
584 }
585}
586impl From<MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>> for TrackSizingFunction {
587 fn from(input: MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>) -> Self {
588 Self::Single(input)
589 }
590}
591
592impl Style {
594 pub(crate) fn grid_template_tracks(&self, axis: AbsoluteAxis) -> &GridTrackVec<TrackSizingFunction> {
596 match axis {
597 AbsoluteAxis::Horizontal => &self.grid_template_columns,
598 AbsoluteAxis::Vertical => &self.grid_template_rows,
599 }
600 }
601
602 pub(crate) fn grid_placement(&self, axis: AbsoluteAxis) -> Line<GridPlacement> {
604 match axis {
605 AbsoluteAxis::Horizontal => self.grid_column,
606 AbsoluteAxis::Vertical => self.grid_row,
607 }
608 }
609
610 pub(crate) fn grid_align_content(&self, axis: AbstractAxis) -> AlignContent {
612 match axis {
613 AbstractAxis::Inline => self.justify_content.unwrap_or(AlignContent::Stretch),
614 AbstractAxis::Block => self.align_content.unwrap_or(AlignContent::Stretch),
615 }
616 }
617}