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