use super::{
AlignContent, AlignItems, AlignSelf, CheapCloneStr, CompactLength, CoreStyle, Dimension, JustifyContent,
LengthPercentage, LengthPercentageAuto, Style,
};
use crate::compute::grid::{GridCoordinate, GridLine, OriginZeroLine};
use crate::geometry::{AbsoluteAxis, AbstractAxis, Line, MinMax, Size};
use crate::style_helpers::*;
use crate::sys::{DefaultCheapStr, Vec};
use core::cmp::{max, min};
use core::fmt::Debug;
#[cfg(feature = "parse")]
use crate::util::parse::{
from_str_from_css, parse_css_str_entirely, CssParseResult, FromCss, ParseError, Parser, Token,
};
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct GridTemplateArea<CustomIdent: CheapCloneStr> {
#[cfg_attr(feature = "serde", serde(deserialize_with = "crate::util::deserialize_from_str"))]
pub name: CustomIdent,
pub row_start: u16,
pub row_end: u16,
pub column_start: u16,
pub column_end: u16,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct NamedGridLine<CustomIdent: CheapCloneStr> {
#[cfg_attr(feature = "serde", serde(deserialize_with = "crate::util::deserialize_from_str"))]
pub name: CustomIdent,
pub index: u16,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) enum GridAreaAxis {
Row,
Column,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) enum GridAreaEnd {
Start,
End,
}
pub trait GenericRepetition {
type CustomIdent: CheapCloneStr;
type RepetitionTrackList<'a>: Iterator<Item = TrackSizingFunction> + ExactSizeIterator + Clone
where
Self: 'a;
type TemplateLineNames<'a>: TemplateLineNames<'a, Self::CustomIdent>
where
Self: 'a;
fn count(&self) -> RepetitionCount;
fn tracks(&self) -> Self::RepetitionTrackList<'_>;
fn track_count(&self) -> u16 {
self.tracks().len() as u16
}
fn lines_names(&self) -> Self::TemplateLineNames<'_>;
}
#[rustfmt::skip]
pub trait TemplateLineNames<'a, S: CheapCloneStr> : Iterator<Item = Self::LineNameSet<'a>> + ExactSizeIterator + Clone where Self: 'a {
type LineNameSet<'b>: Iterator<Item = &'b S> + ExactSizeIterator + Clone where Self: 'b;
}
impl<'a, S: CheapCloneStr> TemplateLineNames<'a, S>
for core::iter::Map<core::slice::Iter<'a, Vec<S>>, fn(&Vec<S>) -> core::slice::Iter<'_, S>>
{
type LineNameSet<'b>
= core::slice::Iter<'b, S>
where
Self: 'b;
}
#[derive(Copy, Clone)]
pub enum GenericGridTemplateComponent<S, Repetition>
where
S: CheapCloneStr,
Repetition: GenericRepetition<CustomIdent = S>,
{
Single(TrackSizingFunction),
Repeat(Repetition),
}
impl<S, Repetition> GenericGridTemplateComponent<S, Repetition>
where
S: CheapCloneStr,
Repetition: GenericRepetition<CustomIdent = S>,
{
pub fn is_auto_repetition(&self) -> bool {
match self {
Self::Single(_) => false,
Self::Repeat(repeat) => matches!(repeat.count(), RepetitionCount::AutoFit | RepetitionCount::AutoFill),
}
}
}
pub trait GridContainerStyle: CoreStyle {
type Repetition<'a>: GenericRepetition<CustomIdent = Self::CustomIdent>
where
Self: 'a;
type TemplateTrackList<'a>: Iterator<Item = GenericGridTemplateComponent<Self::CustomIdent, Self::Repetition<'a>>>
+ ExactSizeIterator
+ Clone
where
Self: 'a;
type AutoTrackList<'a>: Iterator<Item = TrackSizingFunction> + ExactSizeIterator + Clone
where
Self: 'a;
type TemplateLineNames<'a>: TemplateLineNames<'a, Self::CustomIdent>
where
Self: 'a;
type GridTemplateAreas<'a>: IntoIterator<Item = GridTemplateArea<Self::CustomIdent>>
where
Self: 'a;
fn grid_template_rows(&self) -> Option<Self::TemplateTrackList<'_>>;
fn grid_template_columns(&self) -> Option<Self::TemplateTrackList<'_>>;
fn grid_auto_rows(&self) -> Self::AutoTrackList<'_>;
fn grid_auto_columns(&self) -> Self::AutoTrackList<'_>;
fn grid_template_areas(&self) -> Option<Self::GridTemplateAreas<'_>>;
fn grid_template_column_names(&self) -> Option<Self::TemplateLineNames<'_>>;
fn grid_template_row_names(&self) -> Option<Self::TemplateLineNames<'_>>;
#[inline(always)]
fn grid_auto_flow(&self) -> GridAutoFlow {
Style::<Self::CustomIdent>::DEFAULT.grid_auto_flow
}
#[inline(always)]
fn gap(&self) -> Size<LengthPercentage> {
Style::<Self::CustomIdent>::DEFAULT.gap
}
#[inline(always)]
fn align_content(&self) -> Option<AlignContent> {
Style::<Self::CustomIdent>::DEFAULT.align_content
}
#[inline(always)]
fn justify_content(&self) -> Option<JustifyContent> {
Style::<Self::CustomIdent>::DEFAULT.justify_content
}
#[inline(always)]
fn align_items(&self) -> Option<AlignItems> {
Style::<Self::CustomIdent>::DEFAULT.align_items
}
#[inline(always)]
fn justify_items(&self) -> Option<AlignItems> {
Style::<Self::CustomIdent>::DEFAULT.justify_items
}
#[inline(always)]
fn grid_template_tracks(&self, axis: AbsoluteAxis) -> Option<Self::TemplateTrackList<'_>> {
match axis {
AbsoluteAxis::Horizontal => self.grid_template_columns(),
AbsoluteAxis::Vertical => self.grid_template_rows(),
}
}
#[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),
}
}
}
pub trait GridItemStyle: CoreStyle {
#[inline(always)]
fn grid_row(&self) -> Line<GridPlacement<Self::CustomIdent>> {
Default::default()
}
#[inline(always)]
fn grid_column(&self) -> Line<GridPlacement<Self::CustomIdent>> {
Default::default()
}
#[inline(always)]
fn align_self(&self) -> Option<AlignSelf> {
Style::<Self::CustomIdent>::DEFAULT.align_self
}
#[inline(always)]
fn justify_self(&self) -> Option<AlignSelf> {
Style::<Self::CustomIdent>::DEFAULT.justify_self
}
#[inline(always)]
fn grid_placement(&self, axis: AbsoluteAxis) -> Line<GridPlacement<Self::CustomIdent>> {
match axis {
AbsoluteAxis::Horizontal => self.grid_column(),
AbsoluteAxis::Vertical => self.grid_row(),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum GridAutoFlow {
#[default]
Row,
Column,
RowDense,
ColumnDense,
}
#[cfg(feature = "parse")]
impl FromCss for GridAutoFlow {
fn from_css<'i>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
let mut axis: Option<&'static str> = None;
let mut dense = false;
for _ in 0..2 {
if let Ok(ident) = parser.try_parse(|parser| parser.expect_ident_cloned()) {
match &*ident {
"row" => {
axis = Some("row");
}
"column" => {
axis = Some("column");
}
"dense" => dense = true,
_ => {
return Err(parser.new_unexpected_token_error(Token::Ident(ident)));
}
}
} else {
break;
}
}
match (axis, dense) {
(Some("row"), false) => Ok(Self::Row),
(Some("row") | None, true) => Ok(Self::RowDense),
(Some("column"), false) => Ok(Self::Column),
(Some("column"), true) => Ok(Self::ColumnDense),
(None, false) => {
let token = parser.next().cloned()?;
Err(parser.new_unexpected_token_error(token))
}
_ => unreachable!(),
}
}
}
#[cfg(feature = "parse")]
from_str_from_css!(GridAutoFlow);
impl GridAutoFlow {
pub const fn is_dense(&self) -> bool {
match self {
Self::Row | Self::Column => false,
Self::RowDense | Self::ColumnDense => true,
}
}
pub const fn primary_axis(&self) -> AbsoluteAxis {
match self {
Self::Row | Self::RowDense => AbsoluteAxis::Horizontal,
Self::Column | Self::ColumnDense => AbsoluteAxis::Vertical,
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum GenericGridPlacement<LineType: GridCoordinate> {
Auto,
Line(LineType),
Span(u16),
}
pub(crate) type OriginZeroGridPlacement = GenericGridPlacement<OriginZeroLine>;
pub(crate) type NonNamedGridPlacement = GenericGridPlacement<GridLine>;
#[derive(Clone, PartialEq, Debug, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum GridPlacement<S: CheapCloneStr = DefaultCheapStr> {
#[default]
Auto,
Line(GridLine),
NamedLine(S, i16),
Span(u16),
NamedSpan(S, u16),
}
impl<S: CheapCloneStr> TaffyAuto for GridPlacement<S> {
const AUTO: Self = Self::Auto;
}
impl<S: CheapCloneStr> TaffyGridLine for GridPlacement<S> {
fn from_line_index(index: i16) -> Self {
GridPlacement::<S>::Line(GridLine::from(index))
}
}
impl<S: CheapCloneStr> TaffyGridLine for Line<GridPlacement<S>> {
fn from_line_index(index: i16) -> Self {
Line { start: GridPlacement::<S>::from_line_index(index), end: GridPlacement::<S>::Auto }
}
}
impl<S: CheapCloneStr> TaffyGridSpan for GridPlacement<S> {
fn from_span(span: u16) -> Self {
GridPlacement::<S>::Span(span)
}
}
impl<S: CheapCloneStr> TaffyGridSpan for Line<GridPlacement<S>> {
fn from_span(span: u16) -> Self {
Line { start: GridPlacement::<S>::from_span(span), end: GridPlacement::<S>::Auto }
}
}
#[cfg(feature = "parse")]
impl<S: CheapCloneStr> FromCss for GridPlacement<S> {
fn from_css<'i>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
let mut span = false;
let mut number = None;
let mut ident = None;
while !parser.is_exhausted() {
let token = parser.next()?.clone();
match &token {
Token::Ident(s) => match s.as_ref() {
"auto" => {
if span || number.is_some() || ident.is_some() {
return Err(parser.new_unexpected_token_error(token));
}
parser.expect_exhausted()?;
return Ok(Self::Auto);
}
"span" => {
if span {
return Err(parser.new_unexpected_token_error(token));
}
span = true;
}
other => {
if ident.is_some() {
return Err(parser.new_unexpected_token_error(token));
}
ident = Some(S::from(other));
}
},
Token::Number { int_value: Some(value), .. } if *value != 0 => {
if number.is_some() {
return Err(parser.new_unexpected_token_error(token));
}
number = Some(*value);
}
_ => return Err(parser.new_unexpected_token_error(token)),
};
}
match (span, number, ident) {
(true, None, None) => Ok(Self::Span(0)),
(true, Some(number), None) => Ok(Self::Span(number as u16)),
(true, None, Some(ident)) => Ok(Self::NamedSpan(ident, 0)),
(true, Some(number), Some(ident)) => Ok(Self::NamedSpan(ident, number as u16)),
(false, Some(number), None) => Ok(Self::Line(GridLine::from(number as i16))),
(false, Some(number), Some(ident)) => Ok(Self::NamedLine(ident, number as i16)),
(false, None, Some(ident)) => Ok(Self::NamedLine(ident, 0)),
(false, None, None) => Err(parser.new_error(cssparser::BasicParseErrorKind::EndOfInput)),
}
}
}
#[cfg(feature = "parse")]
impl<S: CheapCloneStr> core::str::FromStr for GridPlacement<S> {
type Err = ParseError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
parse_css_str_entirely(input)
}
}
impl<S: CheapCloneStr> GridPlacement<S> {
pub fn into_origin_zero_placement_ignoring_named(&self, explicit_track_count: u16) -> OriginZeroGridPlacement {
match self {
Self::Auto => OriginZeroGridPlacement::Auto,
Self::Span(span) => OriginZeroGridPlacement::Span(*span),
Self::Line(line) => match line.as_i16() {
0 => OriginZeroGridPlacement::Auto,
_ => OriginZeroGridPlacement::Line(line.into_origin_zero_line(explicit_track_count)),
},
Self::NamedLine(_, _) => OriginZeroGridPlacement::Auto,
Self::NamedSpan(_, _) => OriginZeroGridPlacement::Auto,
}
}
}
impl<S: CheapCloneStr> Line<GridPlacement<S>> {
pub fn into_origin_zero_ignoring_named(&self, explicit_track_count: u16) -> Line<OriginZeroGridPlacement> {
Line {
start: self.start.into_origin_zero_placement_ignoring_named(explicit_track_count),
end: self.end.into_origin_zero_placement_ignoring_named(explicit_track_count),
}
}
}
impl NonNamedGridPlacement {
pub fn into_origin_zero_placement(
&self,
explicit_track_count: u16,
) -> OriginZeroGridPlacement {
match self {
Self::Auto => OriginZeroGridPlacement::Auto,
Self::Span(span) => OriginZeroGridPlacement::Span(*span),
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>> {
pub const 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<S: CheapCloneStr> Line<GridPlacement<S>> {
#[inline]
pub fn is_definite(&self) -> bool {
match (&self.start, &self.end) {
(GridPlacement::Line(line), _) if line.as_i16() != 0 => true,
(_, GridPlacement::Line(line)) if line.as_i16() != 0 => true,
(GridPlacement::NamedLine(_, _), _) => true,
(_, GridPlacement::NamedLine(_, _)) => true,
_ => false,
}
}
}
impl Line<NonNamedGridPlacement> {
#[inline]
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,
}
}
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]
pub const fn is_definite(&self) -> bool {
matches!((self.start, self.end), (GenericGridPlacement::Line(_), _) | (_, GenericGridPlacement::Line(_)))
}
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"),
}
}
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 },
}
}
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"),
}
}
}
impl<S: CheapCloneStr> Default for Line<GridPlacement<S>> {
fn default() -> Self {
Line { start: GridPlacement::<S>::Auto, end: GridPlacement::<S>::Auto }
}
}
#[derive(Copy, Clone, PartialEq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct MaxTrackSizingFunction(pub(crate) CompactLength);
impl TaffyZero for MaxTrackSizingFunction {
const ZERO: Self = Self(CompactLength::ZERO);
}
impl TaffyAuto for MaxTrackSizingFunction {
const AUTO: Self = Self(CompactLength::AUTO);
}
impl TaffyMinContent for MaxTrackSizingFunction {
const MIN_CONTENT: Self = Self(CompactLength::MIN_CONTENT);
}
impl TaffyMaxContent for MaxTrackSizingFunction {
const MAX_CONTENT: Self = Self(CompactLength::MAX_CONTENT);
}
impl FromLength for MaxTrackSizingFunction {
fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
Self::length(value.into())
}
}
impl FromPercent for MaxTrackSizingFunction {
fn from_percent<Input: Into<f32> + Copy>(value: Input) -> Self {
Self::percent(value.into())
}
}
impl TaffyFitContent for MaxTrackSizingFunction {
fn fit_content(argument: LengthPercentage) -> Self {
Self(CompactLength::fit_content(argument))
}
}
impl FromFr for MaxTrackSizingFunction {
fn from_fr<Input: Into<f32> + Copy>(value: Input) -> Self {
Self::fr(value.into())
}
}
impl From<LengthPercentage> for MaxTrackSizingFunction {
fn from(input: LengthPercentage) -> Self {
Self(input.0)
}
}
impl From<LengthPercentageAuto> for MaxTrackSizingFunction {
fn from(input: LengthPercentageAuto) -> Self {
Self(input.0)
}
}
impl From<Dimension> for MaxTrackSizingFunction {
fn from(input: Dimension) -> Self {
Self(input.0)
}
}
impl From<MinTrackSizingFunction> for MaxTrackSizingFunction {
fn from(input: MinTrackSizingFunction) -> Self {
Self(input.0)
}
}
#[cfg(feature = "parse")]
impl FromCss for MaxTrackSizingFunction {
fn from_css<'i>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
let token = parser.next()?.clone();
match token {
Token::Percentage { unit_value, .. } => Ok(Self::percent(unit_value)),
Token::Dimension { unit, value, .. } if unit == "px" => Ok(Self::length(value)),
Token::Dimension { unit, value, .. } if unit == "fr" && value.is_sign_positive() => Ok(Self::fr(value)),
Token::Ident(ref ident) => match ident.as_ref() {
"auto" => Ok(Self::auto()),
"min-content" => Ok(Self::min_content()),
"max-content" => Ok(Self::max_content()),
_ => Err(parser.new_unexpected_token_error(token))?,
},
Token::Function(ref name) if name.as_ref() == "fit-content" => parser.parse_nested_block(|parser| {
let token = parser.next()?.clone();
match token {
Token::Percentage { unit_value, .. } => Ok(Self::fit_content_percent(unit_value)),
Token::Dimension { unit, value, .. } if unit == "px" => Ok(Self::fit_content_px(value)),
token => Err(parser.new_unexpected_token_error(token))?,
}
}),
token => Err(parser.new_unexpected_token_error(token))?,
}
}
}
#[cfg(feature = "parse")]
from_str_from_css!(MaxTrackSizingFunction);
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for MaxTrackSizingFunction {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let inner = CompactLength::deserialize(deserializer)?;
if matches!(
inner.tag(),
CompactLength::LENGTH_TAG
| CompactLength::PERCENT_TAG
| CompactLength::AUTO_TAG
| CompactLength::MIN_CONTENT_TAG
| CompactLength::MAX_CONTENT_TAG
| CompactLength::FIT_CONTENT_PX_TAG
| CompactLength::FIT_CONTENT_PERCENT_TAG
| CompactLength::FR_TAG
) {
Ok(Self(inner))
} else {
Err(serde::de::Error::custom("Invalid tag"))
}
}
}
impl MaxTrackSizingFunction {
#[inline(always)]
pub const fn length(val: f32) -> Self {
Self(CompactLength::length(val))
}
#[inline(always)]
pub const fn percent(val: f32) -> Self {
Self(CompactLength::percent(val))
}
#[inline(always)]
pub const fn auto() -> Self {
Self(CompactLength::auto())
}
#[inline(always)]
pub const fn min_content() -> Self {
Self(CompactLength::min_content())
}
#[inline(always)]
pub const fn max_content() -> Self {
Self(CompactLength::max_content())
}
#[inline(always)]
pub const fn fit_content_px(limit: f32) -> Self {
Self(CompactLength::fit_content_px(limit))
}
#[inline(always)]
pub const fn fit_content_percent(limit: f32) -> Self {
Self(CompactLength::fit_content_percent(limit))
}
#[inline(always)]
pub const fn fr(val: f32) -> Self {
Self(CompactLength::fr(val))
}
#[inline]
#[cfg(feature = "calc")]
pub fn calc(ptr: *const ()) -> Self {
Self(CompactLength::calc(ptr))
}
#[allow(unsafe_code)]
pub unsafe fn from_raw(val: CompactLength) -> Self {
Self(val)
}
pub fn into_raw(self) -> CompactLength {
self.0
}
#[inline(always)]
pub fn is_intrinsic(&self) -> bool {
self.0.is_intrinsic()
}
#[inline(always)]
pub fn is_max_content_alike(&self) -> bool {
self.0.is_max_content_alike()
}
#[inline(always)]
pub fn is_fr(&self) -> bool {
self.0.is_fr()
}
#[inline(always)]
pub fn is_auto(&self) -> bool {
self.0.is_auto()
}
#[inline(always)]
pub fn is_min_content(&self) -> bool {
self.0.is_min_content()
}
#[inline(always)]
pub fn is_max_content(&self) -> bool {
self.0.is_max_content()
}
#[inline(always)]
pub fn is_fit_content(&self) -> bool {
self.0.is_fit_content()
}
#[inline(always)]
pub fn is_max_or_fit_content(&self) -> bool {
self.0.is_max_or_fit_content()
}
#[inline(always)]
pub fn has_definite_value(self, parent_size: Option<f32>) -> bool {
match self.0.tag() {
CompactLength::LENGTH_TAG => true,
CompactLength::PERCENT_TAG => parent_size.is_some(),
#[cfg(feature = "calc")]
_ if self.0.is_calc() => parent_size.is_some(),
_ => false,
}
}
#[inline(always)]
pub fn definite_value(
self,
parent_size: Option<f32>,
calc_resolver: impl Fn(*const (), f32) -> f32,
) -> Option<f32> {
match self.0.tag() {
CompactLength::LENGTH_TAG => Some(self.0.value()),
CompactLength::PERCENT_TAG => parent_size.map(|size| self.0.value() * size),
#[cfg(feature = "calc")]
_ if self.0.is_calc() => parent_size.map(|size| calc_resolver(self.0.calc_value(), size)),
_ => None,
}
}
#[inline(always)]
pub fn definite_limit(
self,
parent_size: Option<f32>,
calc_resolver: impl Fn(*const (), f32) -> f32,
) -> Option<f32> {
match self.0.tag() {
CompactLength::FIT_CONTENT_PX_TAG => Some(self.0.value()),
CompactLength::FIT_CONTENT_PERCENT_TAG => parent_size.map(|size| self.0.value() * size),
_ => self.definite_value(parent_size, calc_resolver),
}
}
#[inline(always)]
pub fn resolved_percentage_size(
self,
parent_size: f32,
calc_resolver: impl Fn(*const (), f32) -> f32,
) -> Option<f32> {
self.0.resolved_percentage_size(parent_size, calc_resolver)
}
#[inline(always)]
pub fn uses_percentage(self) -> bool {
self.0.uses_percentage()
}
}
#[derive(Copy, Clone, PartialEq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct MinTrackSizingFunction(pub(crate) CompactLength);
impl TaffyZero for MinTrackSizingFunction {
const ZERO: Self = Self(CompactLength::ZERO);
}
impl TaffyAuto for MinTrackSizingFunction {
const AUTO: Self = Self(CompactLength::AUTO);
}
impl TaffyMinContent for MinTrackSizingFunction {
const MIN_CONTENT: Self = Self(CompactLength::MIN_CONTENT);
}
impl TaffyMaxContent for MinTrackSizingFunction {
const MAX_CONTENT: Self = Self(CompactLength::MAX_CONTENT);
}
impl FromLength for MinTrackSizingFunction {
fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
Self::length(value.into())
}
}
impl FromPercent for MinTrackSizingFunction {
fn from_percent<Input: Into<f32> + Copy>(value: Input) -> Self {
Self::percent(value.into())
}
}
impl From<LengthPercentage> for MinTrackSizingFunction {
fn from(input: LengthPercentage) -> Self {
Self(input.0)
}
}
impl From<LengthPercentageAuto> for MinTrackSizingFunction {
fn from(input: LengthPercentageAuto) -> Self {
Self(input.0)
}
}
impl From<Dimension> for MinTrackSizingFunction {
fn from(input: Dimension) -> Self {
Self(input.0)
}
}
impl From<MaxTrackSizingFunction> for MinTrackSizingFunction {
fn from(input: MaxTrackSizingFunction) -> Self {
if input.is_fr() || input.is_fit_content() {
return Self::auto();
}
Self(input.0)
}
}
#[cfg(feature = "parse")]
impl FromCss for MinTrackSizingFunction {
fn from_css<'i>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
let token = parser.next()?.clone();
match token {
Token::Percentage { unit_value, .. } => Ok(Self::percent(unit_value)),
Token::Dimension { unit, value, .. } if unit == "px" => Ok(Self::length(value)),
Token::Ident(ref ident) => match ident.as_ref() {
"auto" => Ok(Self::auto()),
"min-content" => Ok(Self::min_content()),
"max-content" => Ok(Self::max_content()),
_ => Err(parser.new_unexpected_token_error(token))?,
},
token => Err(parser.new_unexpected_token_error(token))?,
}
}
}
#[cfg(feature = "parse")]
from_str_from_css!(MinTrackSizingFunction);
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for MinTrackSizingFunction {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let inner = CompactLength::deserialize(deserializer)?;
if matches!(
inner.tag(),
CompactLength::LENGTH_TAG
| CompactLength::PERCENT_TAG
| CompactLength::AUTO_TAG
| CompactLength::MIN_CONTENT_TAG
| CompactLength::MAX_CONTENT_TAG
| CompactLength::FIT_CONTENT_PX_TAG
| CompactLength::FIT_CONTENT_PERCENT_TAG
) {
Ok(Self(inner))
} else {
Err(serde::de::Error::custom("Invalid tag"))
}
}
}
impl MinTrackSizingFunction {
#[inline(always)]
pub const fn length(val: f32) -> Self {
Self(CompactLength::length(val))
}
#[inline(always)]
pub const fn percent(val: f32) -> Self {
Self(CompactLength::percent(val))
}
#[inline(always)]
pub const fn auto() -> Self {
Self(CompactLength::auto())
}
#[inline(always)]
pub const fn min_content() -> Self {
Self(CompactLength::min_content())
}
#[inline(always)]
pub const fn max_content() -> Self {
Self(CompactLength::max_content())
}
#[inline]
#[cfg(feature = "calc")]
pub fn calc(ptr: *const ()) -> Self {
Self(CompactLength::calc(ptr))
}
#[allow(unsafe_code)]
pub unsafe fn from_raw(val: CompactLength) -> Self {
Self(val)
}
pub fn into_raw(self) -> CompactLength {
self.0
}
#[inline(always)]
pub fn is_intrinsic(&self) -> bool {
self.0.is_intrinsic()
}
#[inline(always)]
pub fn is_min_or_max_content(&self) -> bool {
self.0.is_min_or_max_content()
}
#[inline(always)]
pub fn is_fr(&self) -> bool {
self.0.is_fr()
}
#[inline(always)]
pub fn is_auto(&self) -> bool {
self.0.is_auto()
}
#[inline(always)]
pub fn is_min_content(&self) -> bool {
self.0.is_min_content()
}
#[inline(always)]
pub fn is_max_content(&self) -> bool {
self.0.is_max_content()
}
#[inline(always)]
pub fn definite_value(
self,
parent_size: Option<f32>,
calc_resolver: impl Fn(*const (), f32) -> f32,
) -> Option<f32> {
match self.0.tag() {
CompactLength::LENGTH_TAG => Some(self.0.value()),
CompactLength::PERCENT_TAG => parent_size.map(|size| self.0.value() * size),
#[cfg(feature = "calc")]
_ if self.0.is_calc() => parent_size.map(|size| calc_resolver(self.0.calc_value(), size)),
_ => None,
}
}
#[inline(always)]
pub fn resolved_percentage_size(
self,
parent_size: f32,
calc_resolver: impl Fn(*const (), f32) -> f32,
) -> Option<f32> {
self.0.resolved_percentage_size(parent_size, calc_resolver)
}
#[inline(always)]
pub fn uses_percentage(self) -> bool {
#[cfg(feature = "calc")]
{
matches!(self.0.tag(), CompactLength::PERCENT_TAG) || self.0.is_calc()
}
#[cfg(not(feature = "calc"))]
{
matches!(self.0.tag(), CompactLength::PERCENT_TAG)
}
}
}
pub type TrackSizingFunction = MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>;
impl TrackSizingFunction {
pub fn min_sizing_function(&self) -> MinTrackSizingFunction {
self.min
}
pub fn max_sizing_function(&self) -> MaxTrackSizingFunction {
self.max
}
pub fn has_fixed_component(&self) -> bool {
self.min.0.is_length_or_percentage() || self.max.0.is_length_or_percentage()
}
}
impl TaffyAuto for TrackSizingFunction {
const AUTO: Self = Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::AUTO };
}
impl TaffyMinContent for TrackSizingFunction {
const MIN_CONTENT: Self =
Self { min: MinTrackSizingFunction::MIN_CONTENT, max: MaxTrackSizingFunction::MIN_CONTENT };
}
impl TaffyMaxContent for TrackSizingFunction {
const MAX_CONTENT: Self =
Self { min: MinTrackSizingFunction::MAX_CONTENT, max: MaxTrackSizingFunction::MAX_CONTENT };
}
impl TaffyFitContent for TrackSizingFunction {
fn fit_content(argument: LengthPercentage) -> Self {
Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::fit_content(argument) }
}
}
impl TaffyZero for TrackSizingFunction {
const ZERO: Self = Self { min: MinTrackSizingFunction::ZERO, max: MaxTrackSizingFunction::ZERO };
}
impl FromLength for TrackSizingFunction {
fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
Self { min: MinTrackSizingFunction::from_length(value), max: MaxTrackSizingFunction::from_length(value) }
}
}
impl FromPercent for TrackSizingFunction {
fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
Self { min: MinTrackSizingFunction::from_percent(percent), max: MaxTrackSizingFunction::from_percent(percent) }
}
}
impl FromFr for TrackSizingFunction {
fn from_fr<Input: Into<f32> + Copy>(flex: Input) -> Self {
Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::from_fr(flex) }
}
}
impl From<LengthPercentage> for TrackSizingFunction {
fn from(input: LengthPercentage) -> Self {
Self { min: input.into(), max: input.into() }
}
}
impl From<LengthPercentageAuto> for TrackSizingFunction {
fn from(input: LengthPercentageAuto) -> Self {
Self { min: input.into(), max: input.into() }
}
}
impl From<Dimension> for TrackSizingFunction {
fn from(input: Dimension) -> Self {
Self { min: input.into(), max: input.into() }
}
}
#[cfg(feature = "parse")]
impl FromCss for TrackSizingFunction {
fn from_css<'i>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
if let Ok(value) = parser.try_parse(|parser| {
parser.expect_function_matching("minmax")?;
parser.parse_nested_block(|parser| {
let min = MinTrackSizingFunction::from_css(parser)?;
parser.expect_comma()?;
let max = MaxTrackSizingFunction::from_css(parser)?;
Ok(Self { min, max })
})
}) {
return Ok(value);
}
let max = MaxTrackSizingFunction::from_css(parser)?;
let min = max.into();
Ok(Self { min, max })
}
}
#[cfg(feature = "parse")]
from_str_from_css!(TrackSizingFunction);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum RepetitionCount {
AutoFill,
AutoFit,
Count(u16),
}
impl From<u16> for RepetitionCount {
fn from(value: u16) -> Self {
Self::Count(value)
}
}
#[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 RepetitionCount {
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),
}
}
}
#[cfg(feature = "parse")]
impl FromCss for RepetitionCount {
fn from_css<'i>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
match parser.next()?.clone() {
Token::Number { int_value: Some(value), .. } if value.is_positive() => Ok(Self::Count(value as _)),
Token::Ident(ident) if ident == "auto-fit" => Ok(Self::AutoFit),
Token::Ident(ident) if ident == "auto-fill" => Ok(Self::AutoFill),
token => Err(parser.new_unexpected_token_error(token))?,
}
}
}
#[cfg(feature = "parse")]
from_str_from_css!(RepetitionCount);
#[derive(Clone, PartialEq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct GridTemplateRepetition<S: CheapCloneStr> {
pub count: RepetitionCount,
pub tracks: Vec<TrackSizingFunction>,
pub line_names: Vec<Vec<S>>,
}
#[rustfmt::skip]
impl<S: CheapCloneStr> GenericRepetition for &'_ GridTemplateRepetition<S> {
type CustomIdent = S;
type RepetitionTrackList<'a> = core::iter::Copied<core::slice::Iter<'a, TrackSizingFunction>> where Self: 'a;
type TemplateLineNames<'a> = core::iter::Map<core::slice::Iter<'a, Vec<S>>, fn(&Vec<S>) -> core::slice::Iter<'_, S>> where Self: 'a;
#[inline(always)]
fn count(&self) -> RepetitionCount {
self.count
}
#[inline(always)]
fn track_count(&self) -> u16 {
self.tracks.len() as u16
}
#[inline(always)]
fn tracks(&self) -> Self::RepetitionTrackList<'_> {
self.tracks.iter().copied()
}
#[inline(always)]
fn lines_names(&self) -> Self::TemplateLineNames<'_> {
self.line_names.iter().map(|names| names.iter())
}
}
#[derive(Clone, PartialEq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum GridTemplateComponent<S: CheapCloneStr> {
Single(TrackSizingFunction),
Repeat(GridTemplateRepetition<S>),
}
impl<S: CheapCloneStr> GridTemplateComponent<S> {
pub fn as_component_ref(&self) -> GenericGridTemplateComponent<S, &GridTemplateRepetition<S>> {
match self {
GridTemplateComponent::Single(size) => GenericGridTemplateComponent::Single(*size),
GridTemplateComponent::Repeat(repetition) => GenericGridTemplateComponent::Repeat(repetition),
}
}
}
impl<S: CheapCloneStr> GridTemplateComponent<S> {
pub fn is_auto_repetition(&self) -> bool {
matches!(
self,
Self::Repeat(GridTemplateRepetition { count: RepetitionCount::AutoFit | RepetitionCount::AutoFill, .. })
)
}
}
impl<S: CheapCloneStr> TaffyAuto for GridTemplateComponent<S> {
const AUTO: Self = Self::Single(TrackSizingFunction::AUTO);
}
impl<S: CheapCloneStr> TaffyMinContent for GridTemplateComponent<S> {
const MIN_CONTENT: Self = Self::Single(TrackSizingFunction::MIN_CONTENT);
}
impl<S: CheapCloneStr> TaffyMaxContent for GridTemplateComponent<S> {
const MAX_CONTENT: Self = Self::Single(TrackSizingFunction::MAX_CONTENT);
}
impl<S: CheapCloneStr> TaffyFitContent for GridTemplateComponent<S> {
fn fit_content(argument: LengthPercentage) -> Self {
Self::Single(TrackSizingFunction::fit_content(argument))
}
}
impl<S: CheapCloneStr> TaffyZero for GridTemplateComponent<S> {
const ZERO: Self = Self::Single(TrackSizingFunction::ZERO);
}
impl<S: CheapCloneStr> FromLength for GridTemplateComponent<S> {
fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
Self::Single(TrackSizingFunction::from_length(value))
}
}
impl<S: CheapCloneStr> FromPercent for GridTemplateComponent<S> {
fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
Self::Single(TrackSizingFunction::from_percent(percent))
}
}
impl<S: CheapCloneStr> FromFr for GridTemplateComponent<S> {
fn from_fr<Input: Into<f32> + Copy>(flex: Input) -> Self {
Self::Single(TrackSizingFunction::from_fr(flex))
}
}
impl<S: CheapCloneStr> From<MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>> for GridTemplateComponent<S> {
fn from(input: MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>) -> Self {
Self::Single(input)
}
}
#[cfg(feature = "parse")]
impl<S: CheapCloneStr> FromCss for GridTemplateComponent<S> {
fn from_css<'i>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
if let Ok(value) = parser.try_parse(|parser| {
parser.expect_function_matching("repeat")?;
parser.parse_nested_block(|parser| {
let count = RepetitionCount::from_css(parser)?;
parser.expect_comma()?;
let tracks = GridTemplateTracks::<S, TrackSizingFunction>::from_css(parser)?;
Ok(Self::Repeat(GridTemplateRepetition { count, tracks: tracks.tracks, line_names: tracks.line_names }))
})
}) {
return Ok(value);
}
let track_sizing_function = TrackSizingFunction::from_css(parser)?;
Ok(Self::Single(track_sizing_function))
}
}
#[cfg(feature = "parse")]
impl<S: CheapCloneStr> core::str::FromStr for GridTemplateComponent<S> {
type Err = ParseError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
parse_css_str_entirely(input)
}
}
#[derive(Clone, PartialEq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[doc(hidden)]
pub struct GridTemplateTracks<S: CheapCloneStr, Track> {
pub tracks: Vec<Track>,
pub line_names: Vec<Vec<S>>,
}
impl<S: CheapCloneStr, Track> Default for GridTemplateTracks<S, Track> {
fn default() -> Self {
Self { tracks: Vec::new(), line_names: Vec::new() }
}
}
#[cfg(feature = "parse")]
impl<S: CheapCloneStr, Track: FromCss + Debug> FromCss for GridTemplateTracks<S, Track> {
fn from_css<'i>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
fn try_parse_line_names<'i, S: CheapCloneStr>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Vec<S>> {
parser.try_parse(|parser| {
parser.expect_square_bracket_block()?;
parser.parse_nested_block(|parser| {
let mut line_names = Vec::new();
while !parser.is_exhausted() {
line_names.push(S::from(parser.expect_ident_cloned()?.as_ref()));
}
Ok(line_names)
})
})
}
let mut tracks = Self::default();
if let Ok(line_names) = try_parse_line_names(parser) {
tracks.line_names.push(line_names);
}
while !parser.is_exhausted() {
tracks.tracks.push(Track::from_css(parser)?);
if let Ok(line_names) = try_parse_line_names(parser) {
tracks.line_names.push(line_names);
}
}
if tracks.tracks.is_empty() {
return Err(parser.new_error(cssparser::BasicParseErrorKind::EndOfInput));
}
Ok(tracks)
}
}
#[cfg(feature = "parse")]
impl<S: CheapCloneStr, Track: FromCss + Debug> core::str::FromStr for GridTemplateTracks<S, Track> {
type Err = ParseError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
parse_css_str_entirely(input)
}
}
#[derive(Default)]
#[doc(hidden)]
pub struct GridAutoTracks(pub Vec<TrackSizingFunction>);
#[cfg(feature = "parse")]
impl FromCss for GridAutoTracks {
fn from_css<'i>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
let mut tracks = Self::default();
while !parser.is_exhausted() {
tracks.0.push(TrackSizingFunction::from_css(parser)?);
}
if tracks.0.is_empty() {
return Err(parser.new_error(cssparser::BasicParseErrorKind::EndOfInput));
}
Ok(tracks)
}
}
#[cfg(feature = "parse")]
from_str_from_css!(GridAutoTracks);