use std::mem::take;
use cssparser::Parser;
use crate::layout::style::{
CssDescriptorKind, CssSyntaxKind, CssToken, FromCss, GridRepeatTrack, GridRepetitionCount,
GridTrackSize, MakeComputed, ParseResult, ToCss,
};
use crate::rendering::Sizing;
pub type GridTemplateComponents = Vec<GridTemplateComponent>;
pub(crate) trait GridTemplateComponentsExt {
fn collect_components_and_names(
&self,
sizing: &Sizing,
) -> (Vec<taffy::GridTemplateComponent<String>>, Vec<Vec<String>>);
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum GridTemplateComponent {
LineNames(Vec<String>),
Single(GridTrackSize),
Repeat(GridRepetitionCount, Vec<GridRepeatTrack>),
}
impl MakeComputed for GridTemplateComponent {
fn make_computed(&mut self, sizing: &Sizing) {
match self {
GridTemplateComponent::Single(size) => size.make_computed(sizing),
GridTemplateComponent::Repeat(_, tracks) => {
for track in tracks.iter_mut() {
track.make_computed(sizing);
}
}
_ => {}
}
}
}
impl<'i> FromCss<'i> for GridTemplateComponent {
fn from_css(input: &mut Parser<'i, '_>) -> ParseResult<'i, Self> {
if input.try_parse(Parser::expect_square_bracket_block).is_ok() {
let mut names: Vec<String> = Vec::new();
input.parse_nested_block(|i| {
while let Ok(name) = i.try_parse(Parser::expect_ident_cloned) {
names.push(name.as_ref().to_owned());
}
Ok(())
})?;
return Ok(GridTemplateComponent::LineNames(names));
}
if input
.try_parse(|i| i.expect_function_matching("repeat"))
.is_ok()
{
return input.parse_nested_block(|input| {
let repetition = GridRepetitionCount::from_css(input)?;
input.expect_comma()?;
let mut tracks: Vec<GridRepeatTrack> = Vec::new();
let mut pending_leading_names: Vec<String> = Vec::new();
loop {
let mut names: Vec<String> = take(&mut pending_leading_names);
while input.try_parse(Parser::expect_square_bracket_block).is_ok() {
input.parse_nested_block(|i| {
while let Ok(name) = i.try_parse(Parser::expect_ident_cloned) {
names.push(name.as_ref().to_owned());
}
Ok(())
})?;
}
let size = if let Ok(size) = input.try_parse(GridTrackSize::from_css) {
size
} else {
break;
};
while input.try_parse(Parser::expect_square_bracket_block).is_ok() {
input.parse_nested_block(|i| {
while let Ok(name) = i.try_parse(Parser::expect_ident_cloned) {
pending_leading_names.push(name.as_ref().to_owned());
}
Ok(())
})?;
}
tracks.push(GridRepeatTrack {
size,
names,
end_names: None,
});
}
if !pending_leading_names.is_empty()
&& let Some(last) = tracks.last_mut()
{
last.end_names = Some(take(&mut pending_leading_names));
}
Ok(GridTemplateComponent::Repeat(repetition, tracks))
});
}
let size = GridTrackSize::from_css(input)?;
Ok(GridTemplateComponent::Single(size))
}
const VALID_TOKENS: &'static [CssToken] = &[
CssToken::Syntax(CssSyntaxKind::LineNames),
CssToken::Descriptor(CssDescriptorKind::RepeatFn),
CssToken::Descriptor(CssDescriptorKind::MinmaxFn),
CssToken::Syntax(CssSyntaxKind::Length),
];
}
impl<'i> FromCss<'i> for GridTemplateComponents {
fn from_css(input: &mut Parser<'i, '_>) -> ParseResult<'i, Self> {
let mut components = Vec::new();
while let Ok(component) = GridTemplateComponent::from_css(input) {
components.push(component);
}
Ok(components)
}
const VALID_TOKENS: &'static [CssToken] = GridTemplateComponent::VALID_TOKENS;
}
impl GridTemplateComponentsExt for [GridTemplateComponent] {
fn collect_components_and_names(
&self,
sizing: &Sizing,
) -> (Vec<taffy::GridTemplateComponent<String>>, Vec<Vec<String>>) {
let mut track_components = Vec::new();
let mut line_name_sets = Vec::new();
let mut pending_line_names = Vec::new();
for component in self {
match component {
GridTemplateComponent::LineNames(names) => {
if !names.is_empty() {
pending_line_names.extend_from_slice(names);
}
}
GridTemplateComponent::Single(track_size) => {
line_name_sets.push(take(&mut pending_line_names));
track_components.push(taffy::GridTemplateComponent::Single(
track_size.to_min_max(sizing),
));
}
GridTemplateComponent::Repeat(repetition, tracks) => {
line_name_sets.push(take(&mut pending_line_names));
let track_sizes = tracks
.iter()
.map(|track| track.size.to_min_max(sizing))
.collect();
let mut inner_line_names = tracks
.iter()
.map(|track| track.names.to_owned())
.collect::<Vec<_>>();
inner_line_names.push(
tracks
.last()
.and_then(|track| track.end_names.clone())
.unwrap_or_default(),
);
track_components.push(taffy::GridTemplateComponent::Repeat(
taffy::GridTemplateRepetition {
count: (*repetition).into(),
tracks: track_sizes,
line_names: inner_line_names,
},
));
}
}
}
line_name_sets.push(pending_line_names);
(track_components, line_name_sets)
}
}
impl ToCss for GridTemplateComponent {
fn to_css<W: std::fmt::Write>(&self, dest: &mut W) -> std::fmt::Result {
match self {
Self::LineNames(names) => {
dest.write_str("[")?;
let mut first = true;
for name in names {
if !first {
dest.write_str(" ")?;
}
first = false;
dest.write_str(name)?;
}
dest.write_str("]")
}
Self::Single(size) => size.to_css(dest),
Self::Repeat(count, tracks) => {
dest.write_str("repeat(")?;
count.to_css(dest)?;
dest.write_str(", ")?;
let mut first = true;
for track in tracks {
if !first {
dest.write_str(" ")?;
}
first = false;
track.to_css(dest)?;
}
dest.write_str(")")
}
}
}
}
#[cfg(test)]
mod tests {
use crate::layout::style::{GridLength, GridRepetitionKeyword};
use super::*;
#[test]
fn test_parse_template_component_repeat() {
assert_eq!(
GridTemplateComponent::from_str("repeat(auto-fill, [a] 1fr [b] 2fr)"),
Ok(GridTemplateComponent::Repeat(
GridRepetitionCount::Keyword(GridRepetitionKeyword::AutoFill),
vec![
GridRepeatTrack {
names: vec!["a".to_string()],
size: GridTrackSize::Fixed(GridLength::Fr(1.0)),
end_names: None
},
GridRepeatTrack {
names: vec!["b".to_string()],
size: GridTrackSize::Fixed(GridLength::Fr(2.0)),
end_names: None
}
]
))
);
}
}