blob: c5eecab5b653936a51d3fac9ba228d5032bd7359 [file] [log] [blame]
//! Style types for CSS Grid layout
use super::{AlignContent, AlignItems, AlignSelf, CoreStyle, JustifyContent, LengthPercentage, Style};
use crate::compute::grid::{GridCoordinate, GridLine, OriginZeroLine};
use crate::geometry::{AbsoluteAxis, AbstractAxis, Line, MinMax, Size};
use crate::style_helpers::*;
use crate::util::sys::GridTrackVec;
use core::borrow::Borrow;
use core::cmp::{max, min};
use core::convert::Infallible;
/// The set of styles required for a CSS Grid container
pub trait GridContainerStyle: CoreStyle {
/// The type returned by grid_template_rows and grid_template_columns
type TemplateTrackList<'a>: Borrow<[TrackSizingFunction]>
where
Self: 'a;
/// The type returned by grid_auto_rows and grid_auto_columns
type AutoTrackList<'a>: Borrow<[NonRepeatedTrackSizingFunction]>
where
Self: 'a;
// FIXME: re-add default implemenations for grid_{template,auto}_{rows,columns} once the
// associated_type_defaults feature (https://github.com/rust-lang/rust/issues/29661) is stabilised.
/// Defines the track sizing functions (heights) of the grid rows
fn grid_template_rows(&self) -> Self::TemplateTrackList<'_>;
/// Defines the track sizing functions (widths) of the grid columns
fn grid_template_columns(&self) -> Self::TemplateTrackList<'_>;
/// Defines the size of implicitly created rows
fn grid_auto_rows(&self) -> Self::AutoTrackList<'_>;
/// Defined the size of implicitly created columns
fn grid_auto_columns(&self) -> Self::AutoTrackList<'_>;
/// Controls how items get placed into the grid for auto-placed items
#[inline(always)]
fn grid_auto_flow(&self) -> GridAutoFlow {
Style::DEFAULT.grid_auto_flow
}
/// How large should the gaps between items in a grid or flex container be?
#[inline(always)]
fn gap(&self) -> Size<LengthPercentage> {
Style::DEFAULT.gap
}
// Alignment properties
/// How should content contained within this item be aligned in the cross/block axis
#[inline(always)]
fn align_content(&self) -> Option<AlignContent> {
Style::DEFAULT.align_content
}
/// How should contained within this item be aligned in the main/inline axis
#[inline(always)]
fn justify_content(&self) -> Option<JustifyContent> {
Style::DEFAULT.justify_content
}
/// How this node's children aligned in the cross/block axis?
#[inline(always)]
fn align_items(&self) -> Option<AlignItems> {
Style::DEFAULT.align_items
}
/// How this node's children should be aligned in the inline axis
#[inline(always)]
fn justify_items(&self) -> Option<AlignItems> {
Style::DEFAULT.justify_items
}
/// Get a grid item's row or column placement depending on the axis passed
#[inline(always)]
fn grid_template_tracks(&self, axis: AbsoluteAxis) -> Self::TemplateTrackList<'_> {
match axis {
AbsoluteAxis::Horizontal => self.grid_template_columns(),
AbsoluteAxis::Vertical => self.grid_template_rows(),
}
}
/// Get a grid container's align-content or justify-content alignment depending on the axis passed
#[inline(always)]
fn grid_align_content(&self, axis: AbstractAxis) -> AlignContent {
match axis {
AbstractAxis::Inline => self.justify_content().unwrap_or(AlignContent::Stretch),
AbstractAxis::Block => self.align_content().unwrap_or(AlignContent::Stretch),
}
}
}
/// The set of styles required for a CSS Grid item (child of a CSS Grid container)
pub trait GridItemStyle: CoreStyle {
/// Defines which row in the grid the item should start and end at
#[inline(always)]
fn grid_row(&self) -> Line<GridPlacement> {
Style::DEFAULT.grid_row
}
/// Defines which column in the grid the item should start and end at
#[inline(always)]
fn grid_column(&self) -> Line<GridPlacement> {
Style::DEFAULT.grid_column
}
/// How this node should be aligned in the cross/block axis
/// Falls back to the parents [`AlignItems`] if not set
#[inline(always)]
fn align_self(&self) -> Option<AlignSelf> {
Style::DEFAULT.align_self
}
/// How this node should be aligned in the inline axis
/// Falls back to the parents [`super::JustifyItems`] if not set
#[inline(always)]
fn justify_self(&self) -> Option<AlignSelf> {
Style::DEFAULT.justify_self
}
/// Get a grid item's row or column placement depending on the axis passed
#[inline(always)]
fn grid_placement(&self, axis: AbsoluteAxis) -> Line<GridPlacement> {
match axis {
AbsoluteAxis::Horizontal => self.grid_column(),
AbsoluteAxis::Vertical => self.grid_row(),
}
}
}
/// Controls whether grid items are placed row-wise or column-wise. And whether the sparse or dense packing algorithm is used.
///
/// 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.
///
/// Defaults to [`GridAutoFlow::Row`]
///
/// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-flow)
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum GridAutoFlow {
/// Items are placed by filling each row in turn, adding new rows as necessary
Row,
/// Items are placed by filling each column in turn, adding new columns as necessary.
Column,
/// Combines `Row` with the dense packing algorithm.
RowDense,
/// Combines `Column` with the dense packing algorithm.
ColumnDense,
}
impl Default for GridAutoFlow {
fn default() -> Self {
Self::Row
}
}
impl GridAutoFlow {
/// Whether grid auto placement uses the sparse placement algorithm or the dense placement algorithm
/// See: <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-flow#values>
pub fn is_dense(&self) -> bool {
match self {
Self::Row | Self::Column => false,
Self::RowDense | Self::ColumnDense => true,
}
}
/// Whether grid auto placement fills areas row-wise or column-wise
/// See: <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-flow#values>
pub fn primary_axis(&self) -> AbsoluteAxis {
match self {
Self::Row | Self::RowDense => AbsoluteAxis::Horizontal,
Self::Column | Self::ColumnDense => AbsoluteAxis::Vertical,
}
}
}
/// A grid line placement specification which is generic over the coordinate system that it uses to define
/// grid line positions.
///
/// GenericGridPlacement<GridLine> is aliased as GridPlacement and is exposed to users of Taffy to define styles.
/// GenericGridPlacement<OriginZeroLine> is aliased as OriginZeroGridPlacement and is used internally for placement computations.
///
/// See [`crate::compute::grid::type::coordinates`] for documentation on the different coordinate systems.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum GenericGridPlacement<LineType: GridCoordinate> {
/// Place item according to the auto-placement algorithm, and the parent's grid_auto_flow property
Auto,
/// Place item at specified line (column or row) index
Line(LineType),
/// Item should span specified number of tracks (columns or rows)
Span(u16),
}
/// A grid line placement using the normalized OriginZero coordinates to specify line positions.
pub(crate) type OriginZeroGridPlacement = GenericGridPlacement<OriginZeroLine>;
/// A grid line placement specification. Used for grid-[row/column]-[start/end]. Named tracks are not implemented.
///
/// Defaults to `GridPlacement::Auto`
///
/// [Specification](https://www.w3.org/TR/css3-grid-layout/#typedef-grid-row-start-grid-line)
pub type GridPlacement = GenericGridPlacement<GridLine>;
impl TaffyAuto for GridPlacement {
const AUTO: Self = Self::Auto;
}
impl TaffyGridLine for GridPlacement {
fn from_line_index(index: i16) -> Self {
GridPlacement::Line(GridLine::from(index))
}
}
impl TaffyGridLine for Line<GridPlacement> {
fn from_line_index(index: i16) -> Self {
Line { start: GridPlacement::from_line_index(index), end: GridPlacement::Auto }
}
}
impl TaffyGridSpan for GridPlacement {
fn from_span(span: u16) -> Self {
GridPlacement::Span(span)
}
}
impl TaffyGridSpan for Line<GridPlacement> {
fn from_span(span: u16) -> Self {
Line { start: GridPlacement::from_span(span), end: GridPlacement::Auto }
}
}
impl Default for GridPlacement {
fn default() -> Self {
Self::Auto
}
}
impl GridPlacement {
/// Apply a mapping function if the [`GridPlacement`] is a `Track`. Otherwise return `self` unmodified.
pub fn into_origin_zero_placement(self, explicit_track_count: u16) -> OriginZeroGridPlacement {
match self {
Self::Auto => OriginZeroGridPlacement::Auto,
Self::Span(span) => OriginZeroGridPlacement::Span(span),
// Grid line zero is an invalid index, so it gets treated as Auto
// See: https://developer.mozilla.org/en-US/docs/Web/CSS/grid-row-start#values
Self::Line(line) => match line.as_i16() {
0 => OriginZeroGridPlacement::Auto,
_ => OriginZeroGridPlacement::Line(line.into_origin_zero_line(explicit_track_count)),
},
}
}
}
impl<T: GridCoordinate> Line<GenericGridPlacement<T>> {
/// Resolves the span for an indefinite placement (a placement that does not consist of two `Track`s).
/// Panics if called on a definite placement
pub fn indefinite_span(&self) -> u16 {
use GenericGridPlacement as GP;
match (self.start, self.end) {
(GP::Line(_), GP::Auto) => 1,
(GP::Auto, GP::Line(_)) => 1,
(GP::Auto, GP::Auto) => 1,
(GP::Line(_), GP::Span(span)) => span,
(GP::Span(span), GP::Line(_)) => span,
(GP::Span(span), GP::Auto) => span,
(GP::Auto, GP::Span(span)) => span,
(GP::Span(span), GP::Span(_)) => span,
(GP::Line(_), GP::Line(_)) => panic!("indefinite_span should only be called on indefinite grid tracks"),
}
}
}
impl Line<GridPlacement> {
#[inline]
/// Whether the track position is definite in this axis (or the item will need auto placement)
/// The track position is definite if least one of the start and end positions is a NON-ZERO track index
/// (0 is an invalid line in GridLine coordinates, and falls back to "auto" which is indefinite)
pub fn is_definite(&self) -> bool {
match (self.start, self.end) {
(GenericGridPlacement::Line(line), _) if line.as_i16() != 0 => true,
(_, GenericGridPlacement::Line(line)) if line.as_i16() != 0 => true,
_ => false,
}
}
/// Apply a mapping function if the [`GridPlacement`] is a `Track`. Otherwise return `self` unmodified.
pub fn into_origin_zero(&self, explicit_track_count: u16) -> Line<OriginZeroGridPlacement> {
Line {
start: self.start.into_origin_zero_placement(explicit_track_count),
end: self.end.into_origin_zero_placement(explicit_track_count),
}
}
}
impl Line<OriginZeroGridPlacement> {
#[inline]
/// Whether the track position is definite in this axis (or the item will need auto placement)
/// The track position is definite if least one of the start and end positions is a track index
pub fn is_definite(&self) -> bool {
matches!((self.start, self.end), (GenericGridPlacement::Line(_), _) | (_, GenericGridPlacement::Line(_)))
}
/// If at least one of the of the start and end positions is a track index then the other end can be resolved
/// into a track index purely based on the information contained with the placement specification
pub fn resolve_definite_grid_lines(&self) -> Line<OriginZeroLine> {
use OriginZeroGridPlacement as GP;
match (self.start, self.end) {
(GP::Line(line1), GP::Line(line2)) => {
if line1 == line2 {
Line { start: line1, end: line1 + 1 }
} else {
Line { start: min(line1, line2), end: max(line1, line2) }
}
}
(GP::Line(line), GP::Span(span)) => Line { start: line, end: line + span },
(GP::Line(line), GP::Auto) => Line { start: line, end: line + 1 },
(GP::Span(span), GP::Line(line)) => Line { start: line - span, end: line },
(GP::Auto, GP::Line(line)) => Line { start: line - 1, end: line },
_ => panic!("resolve_definite_grid_tracks should only be called on definite grid tracks"),
}
}
/// For absolutely positioned items:
/// - Tracks resolve to definite tracks
/// - For Spans:
/// - If the other position is a Track, they resolve to a definite track relative to the other track
/// - Else resolve to None
/// - Auto resolves to None
///
/// When finally positioning the item, a value of None means that the item's grid area is bounded by the grid
/// container's border box on that side.
pub fn resolve_absolutely_positioned_grid_tracks(&self) -> Line<Option<OriginZeroLine>> {
use OriginZeroGridPlacement as GP;
match (self.start, self.end) {
(GP::Line(track1), GP::Line(track2)) => {
if track1 == track2 {
Line { start: Some(track1), end: Some(track1 + 1) }
} else {
Line { start: Some(min(track1, track2)), end: Some(max(track1, track2)) }
}
}
(GP::Line(track), GP::Span(span)) => Line { start: Some(track), end: Some(track + span) },
(GP::Line(track), GP::Auto) => Line { start: Some(track), end: None },
(GP::Span(span), GP::Line(track)) => Line { start: Some(track - span), end: Some(track) },
(GP::Auto, GP::Line(track)) => Line { start: None, end: Some(track) },
_ => Line { start: None, end: None },
}
}
/// If neither of the start and end positions is a track index then the other end can be resolved
/// into a track index if a definite start position is supplied externally
pub fn resolve_indefinite_grid_tracks(&self, start: OriginZeroLine) -> Line<OriginZeroLine> {
use OriginZeroGridPlacement as GP;
match (self.start, self.end) {
(GP::Auto, GP::Auto) => Line { start, end: start + 1 },
(GP::Span(span), GP::Auto) => Line { start, end: start + span },
(GP::Auto, GP::Span(span)) => Line { start, end: start + span },
(GP::Span(span), GP::Span(_)) => Line { start, end: start + span },
_ => panic!("resolve_indefinite_grid_tracks should only be called on indefinite grid tracks"),
}
}
}
/// Represents the start and end points of a GridItem within a given axis
impl Default for Line<GridPlacement> {
fn default() -> Self {
Line { start: GridPlacement::Auto, end: GridPlacement::Auto }
}
}
/// Maximum track sizing function
///
/// Specifies the maximum size of a grid track. A grid track will automatically size between it's minimum and maximum size based
/// on the size of it's contents, the amount of available space, and the sizing constraint the grid is being size under.
/// See <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns>
#[derive(Copy, Clone, PartialEq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum MaxTrackSizingFunction {
/// Track maximum size should be a fixed length or percentage value
Fixed(LengthPercentage),
/// Track maximum size should be content sized under a min-content constraint
MinContent,
/// Track maximum size should be content sized under a max-content constraint
MaxContent,
/// Track maximum size should be sized according to the fit-content formula
FitContent(LengthPercentage),
/// Track maximum size should be automatically sized
Auto,
/// The dimension as a fraction of the total available grid space (`fr` units in CSS)
/// Specified value is the numerator of the fraction. Denominator is the sum of all fraction specified in that grid dimension
/// Spec: <https://www.w3.org/TR/css3-grid-layout/#fr-unit>
Fraction(f32),
}
impl TaffyAuto for MaxTrackSizingFunction {
const AUTO: Self = Self::Auto;
}
impl TaffyMinContent for MaxTrackSizingFunction {
const MIN_CONTENT: Self = Self::MinContent;
}
impl TaffyMaxContent for MaxTrackSizingFunction {
const MAX_CONTENT: Self = Self::MaxContent;
}
impl TaffyFitContent for MaxTrackSizingFunction {
fn fit_content(argument: LengthPercentage) -> Self {
Self::FitContent(argument)
}
}
impl TaffyZero for MaxTrackSizingFunction {
const ZERO: Self = Self::Fixed(LengthPercentage::ZERO);
}
impl FromLength for MaxTrackSizingFunction {
fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
Self::Fixed(LengthPercentage::from_length(value))
}
}
impl FromPercent for MaxTrackSizingFunction {
fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
Self::Fixed(LengthPercentage::from_percent(percent))
}
}
impl FromFlex for MaxTrackSizingFunction {
fn from_flex<Input: Into<f32> + Copy>(flex: Input) -> Self {
Self::Fraction(flex.into())
}
}
impl MaxTrackSizingFunction {
/// Returns true if the max track sizing function is `MinContent`, `MaxContent`, `FitContent` or `Auto`, else false.
#[inline(always)]
pub fn is_intrinsic(&self) -> bool {
matches!(self, Self::MinContent | Self::MaxContent | Self::FitContent(_) | Self::Auto)
}
/// Returns true if the max track sizing function is `MaxContent`, `FitContent` or `Auto` else false.
/// "In all cases, treat auto and fit-content() as max-content, except where specified otherwise for fit-content()."
/// See: <https://www.w3.org/TR/css-grid-1/#algo-terms>
#[inline(always)]
pub fn is_max_content_alike(&self) -> bool {
matches!(self, Self::MaxContent | Self::FitContent(_) | Self::Auto)
}
/// Returns true if the max track sizing function is `Flex`, else false.
#[inline(always)]
pub fn is_flexible(&self) -> bool {
matches!(self, Self::Fraction(_))
}
/// Returns fixed point values directly. Attempts to resolve percentage values against
/// the passed available_space and returns if this results in a concrete value (which it
/// will if the available_space is `Some`). Otherwise returns None.
#[inline(always)]
pub fn definite_value(self, parent_size: Option<f32>) -> Option<f32> {
use MaxTrackSizingFunction::*;
match self {
Fixed(LengthPercentage::Length(size)) => Some(size),
Fixed(LengthPercentage::Percent(fraction)) => parent_size.map(|size| fraction * size),
MinContent | MaxContent | FitContent(_) | Auto | Fraction(_) => None,
}
}
/// Resolve the maximum size of the track as defined by either:
/// - A fixed track sizing function
/// - A percentage track sizing function (with definite available space)
/// - A fit-content sizing function with fixed argument
/// - A fit-content sizing function with percentage argument (with definite available space)
/// All other kinds of track sizing function return None.
#[inline(always)]
pub fn definite_limit(self, parent_size: Option<f32>) -> Option<f32> {
use MaxTrackSizingFunction::FitContent;
match self {
FitContent(LengthPercentage::Length(size)) => Some(size),
FitContent(LengthPercentage::Percent(fraction)) => parent_size.map(|size| fraction * size),
_ => self.definite_value(parent_size),
}
}
/// Resolve percentage values against the passed parent_size, returning Some(value)
/// Non-percentage values always return None.
#[inline(always)]
pub fn resolved_percentage_size(self, parent_size: f32) -> Option<f32> {
use MaxTrackSizingFunction::*;
match self {
Fixed(LengthPercentage::Percent(fraction)) => Some(fraction * parent_size),
Fixed(LengthPercentage::Length(_)) | MinContent | MaxContent | FitContent(_) | Auto | Fraction(_) => None,
}
}
/// Whether the track sizing functions depends on the size of the parent node
#[inline(always)]
pub fn uses_percentage(self) -> bool {
use MaxTrackSizingFunction::*;
matches!(self, Fixed(LengthPercentage::Percent(_)) | FitContent(LengthPercentage::Percent(_)))
}
}
/// Minimum track sizing function
///
/// Specifies the minimum size of a grid track. A grid track will automatically size between it's minimum and maximum size based
/// on the size of it's contents, the amount of available space, and the sizing constraint the grid is being size under.
/// See <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns>
#[derive(Copy, Clone, PartialEq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum MinTrackSizingFunction {
/// Track minimum size should be a fixed length or percentage value
Fixed(LengthPercentage),
/// Track minimum size should be content sized under a min-content constraint
MinContent,
/// Track minimum size should be content sized under a max-content constraint
MaxContent,
/// Track minimum size should be automatically sized
Auto,
}
impl TaffyAuto for MinTrackSizingFunction {
const AUTO: Self = Self::Auto;
}
impl TaffyMinContent for MinTrackSizingFunction {
const MIN_CONTENT: Self = Self::MinContent;
}
impl TaffyMaxContent for MinTrackSizingFunction {
const MAX_CONTENT: Self = Self::MaxContent;
}
impl TaffyZero for MinTrackSizingFunction {
const ZERO: Self = Self::Fixed(LengthPercentage::ZERO);
}
impl FromLength for MinTrackSizingFunction {
fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
Self::Fixed(LengthPercentage::from_length(value))
}
}
impl FromPercent for MinTrackSizingFunction {
fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
Self::Fixed(LengthPercentage::from_percent(percent))
}
}
impl MinTrackSizingFunction {
/// Returns true if the min track sizing function is `MinContent`, `MaxContent` or `Auto`, else false.
#[inline(always)]
pub fn is_intrinsic(&self) -> bool {
matches!(self, Self::MinContent | Self::MaxContent | Self::Auto)
}
/// Returns fixed point values directly. Attempts to resolve percentage values against
/// the passed available_space and returns if this results in a concrete value (which it
/// will if the available_space is `Some`). Otherwise returns `None`.
#[inline(always)]
pub fn definite_value(self, parent_size: Option<f32>) -> Option<f32> {
use MinTrackSizingFunction::*;
match self {
Fixed(LengthPercentage::Length(size)) => Some(size),
Fixed(LengthPercentage::Percent(fraction)) => parent_size.map(|size| fraction * size),
MinContent | MaxContent | Auto => None,
}
}
/// Resolve percentage values against the passed parent_size, returning Some(value)
/// Non-percentage values always return None.
#[inline(always)]
pub fn resolved_percentage_size(self, parent_size: f32) -> Option<f32> {
use MinTrackSizingFunction::*;
match self {
Fixed(LengthPercentage::Percent(fraction)) => Some(fraction * parent_size),
Fixed(LengthPercentage::Length(_)) | MinContent | MaxContent | Auto => None,
}
}
/// Whether the track sizing functions depends on the size of the parent node
#[inline(always)]
pub fn uses_percentage(self) -> bool {
use MinTrackSizingFunction::*;
matches!(self, Fixed(LengthPercentage::Percent(_)))
}
}
/// The sizing function for a grid track (row/column)
///
/// May either be a MinMax variant which specifies separate values for the min-/max- track sizing functions
/// or a scalar value which applies to both track sizing functions.
pub type NonRepeatedTrackSizingFunction = MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>;
impl NonRepeatedTrackSizingFunction {
/// Extract the min track sizing function
pub fn min_sizing_function(&self) -> MinTrackSizingFunction {
self.min
}
/// Extract the max track sizing function
pub fn max_sizing_function(&self) -> MaxTrackSizingFunction {
self.max
}
/// Determine whether at least one of the components ("min" and "max") are fixed sizing function
pub fn has_fixed_component(&self) -> bool {
matches!(self.min, MinTrackSizingFunction::Fixed(_)) || matches!(self.max, MaxTrackSizingFunction::Fixed(_))
}
}
impl TaffyAuto for NonRepeatedTrackSizingFunction {
const AUTO: Self = Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::AUTO };
}
impl TaffyMinContent for NonRepeatedTrackSizingFunction {
const MIN_CONTENT: Self =
Self { min: MinTrackSizingFunction::MIN_CONTENT, max: MaxTrackSizingFunction::MIN_CONTENT };
}
impl TaffyMaxContent for NonRepeatedTrackSizingFunction {
const MAX_CONTENT: Self =
Self { min: MinTrackSizingFunction::MAX_CONTENT, max: MaxTrackSizingFunction::MAX_CONTENT };
}
impl TaffyFitContent for NonRepeatedTrackSizingFunction {
fn fit_content(argument: LengthPercentage) -> Self {
Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::FitContent(argument) }
}
}
impl TaffyZero for NonRepeatedTrackSizingFunction {
const ZERO: Self = Self { min: MinTrackSizingFunction::ZERO, max: MaxTrackSizingFunction::ZERO };
}
impl FromLength for NonRepeatedTrackSizingFunction {
fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
Self { min: MinTrackSizingFunction::from_length(value), max: MaxTrackSizingFunction::from_length(value) }
}
}
impl FromPercent for NonRepeatedTrackSizingFunction {
fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
Self { min: MinTrackSizingFunction::from_percent(percent), max: MaxTrackSizingFunction::from_percent(percent) }
}
}
impl FromFlex for NonRepeatedTrackSizingFunction {
fn from_flex<Input: Into<f32> + Copy>(flex: Input) -> Self {
Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::from_flex(flex) }
}
}
/// The first argument to a repeated track definition. This type represents the type of automatic repetition to perform.
///
/// See <https://www.w3.org/TR/css-grid-1/#auto-repeat> for an explanation of how auto-repeated track definitions work
/// and the difference between AutoFit and AutoFill.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum GridTrackRepetition {
/// Auto-repeating tracks should be generated to fit the container
/// See: <https://developer.mozilla.org/en-US/docs/Web/CSS/repeat#auto-fill>
AutoFill,
/// Auto-repeating tracks should be generated to fit the container
/// See: <https://developer.mozilla.org/en-US/docs/Web/CSS/repeat#auto-fit>
AutoFit,
/// The specified tracks should be repeated exacts N times
Count(u16),
}
impl TryFrom<u16> for GridTrackRepetition {
type Error = Infallible;
fn try_from(value: u16) -> Result<Self, Infallible> {
Ok(Self::Count(value))
}
}
/// Error returned when trying to convert a string to a GridTrackRepetition and that string is not
/// either "auto-fit" or "auto-fill"
#[derive(Debug)]
pub struct InvalidStringRepetitionValue;
#[cfg(feature = "std")]
impl std::error::Error for InvalidStringRepetitionValue {}
impl core::fmt::Display for InvalidStringRepetitionValue {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("&str can only be converted to GridTrackRepetition if it's value is 'auto-fit' or 'auto-fill'")
}
}
impl TryFrom<&str> for GridTrackRepetition {
type Error = InvalidStringRepetitionValue;
fn try_from(value: &str) -> Result<Self, InvalidStringRepetitionValue> {
match value {
"auto-fit" => Ok(Self::AutoFit),
"auto-fill" => Ok(Self::AutoFill),
_ => Err(InvalidStringRepetitionValue),
}
}
}
/// The sizing function for a grid track (row/column)
/// See <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns>
#[derive(Clone, PartialEq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum TrackSizingFunction {
/// A single non-repeated track
Single(NonRepeatedTrackSizingFunction),
/// Automatically generate grid tracks to fit the available space using the specified definite track lengths
/// Only valid if every track in template (not just the repetition) has a fixed size.
Repeat(GridTrackRepetition, GridTrackVec<NonRepeatedTrackSizingFunction>),
}
impl TrackSizingFunction {
/// Whether the track definition is a auto-repeated fragment
pub fn is_auto_repetition(&self) -> bool {
matches!(self, Self::Repeat(GridTrackRepetition::AutoFit | GridTrackRepetition::AutoFill, _))
}
}
impl TaffyAuto for TrackSizingFunction {
const AUTO: Self = Self::Single(NonRepeatedTrackSizingFunction::AUTO);
}
impl TaffyMinContent for TrackSizingFunction {
const MIN_CONTENT: Self = Self::Single(NonRepeatedTrackSizingFunction::MIN_CONTENT);
}
impl TaffyMaxContent for TrackSizingFunction {
const MAX_CONTENT: Self = Self::Single(NonRepeatedTrackSizingFunction::MAX_CONTENT);
}
impl TaffyFitContent for TrackSizingFunction {
fn fit_content(argument: LengthPercentage) -> Self {
Self::Single(NonRepeatedTrackSizingFunction::fit_content(argument))
}
}
impl TaffyZero for TrackSizingFunction {
const ZERO: Self = Self::Single(NonRepeatedTrackSizingFunction::ZERO);
}
impl FromLength for TrackSizingFunction {
fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
Self::Single(NonRepeatedTrackSizingFunction::from_length(value))
}
}
impl FromPercent for TrackSizingFunction {
fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
Self::Single(NonRepeatedTrackSizingFunction::from_percent(percent))
}
}
impl FromFlex for TrackSizingFunction {
fn from_flex<Input: Into<f32> + Copy>(flex: Input) -> Self {
Self::Single(NonRepeatedTrackSizingFunction::from_flex(flex))
}
}
impl From<MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>> for TrackSizingFunction {
fn from(input: MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>) -> Self {
Self::Single(input)
}
}