mirror of
https://github.com/servo/servo.git
synced 2025-08-08 15:05:35 +01:00
Auto merge of #17268 - servo:grid, r=wafflespeanut,canaltinova
Add support for subgrids and fix some other grid properties This covers the `subgrid <line-name-lists>?` for `grid-template` (for gecko, and hence, stylo). <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/17268) <!-- Reviewable:end -->
This commit is contained in:
commit
7a71035793
8 changed files with 384 additions and 113 deletions
|
@ -5,13 +5,14 @@
|
|||
//! Generic types for the handling of
|
||||
//! [grids](https://drafts.csswg.org/css-grid/).
|
||||
|
||||
use cssparser::{Parser, serialize_identifier};
|
||||
use cssparser::Parser;
|
||||
use parser::{Parse, ParserContext};
|
||||
use std::{fmt, mem, usize};
|
||||
use style_traits::{ToCss, ParseError, StyleParseError};
|
||||
use values::{CSSFloat, CustomIdent};
|
||||
use values::computed::{self, ComputedValueAsSpecified, Context, ToComputedValue};
|
||||
use values::specified::Integer;
|
||||
use values::specified::grid::parse_line_names;
|
||||
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
|
@ -251,6 +252,13 @@ impl<L> Default for TrackSize<L> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<L: PartialEq> TrackSize<L> {
|
||||
/// Returns true if current TrackSize is same as default.
|
||||
pub fn is_default(&self) -> bool {
|
||||
*self == TrackSize::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: ToCss> ToCss for TrackSize<L> {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
match *self {
|
||||
|
@ -307,15 +315,15 @@ impl<L: ToComputedValue> ToComputedValue for TrackSize<L> {
|
|||
/// Helper function for serializing identifiers with a prefix and suffix, used
|
||||
/// for serializing <line-names> (in grid).
|
||||
pub fn concat_serialize_idents<W>(prefix: &str, suffix: &str,
|
||||
slice: &[String], sep: &str, dest: &mut W) -> fmt::Result
|
||||
slice: &[CustomIdent], sep: &str, dest: &mut W) -> fmt::Result
|
||||
where W: fmt::Write
|
||||
{
|
||||
if let Some((ref first, rest)) = slice.split_first() {
|
||||
dest.write_str(prefix)?;
|
||||
serialize_identifier(first, dest)?;
|
||||
first.to_css(dest)?;
|
||||
for thing in rest {
|
||||
dest.write_str(sep)?;
|
||||
serialize_identifier(thing, dest)?;
|
||||
thing.to_css(dest)?;
|
||||
}
|
||||
|
||||
dest.write_str(suffix)?;
|
||||
|
@ -372,33 +380,52 @@ pub struct TrackRepeat<L> {
|
|||
/// If there's no `<line-names>`, then it's represented by an empty vector.
|
||||
/// For N `<track-size>` values, there will be N+1 `<line-names>`, and so this vector's
|
||||
/// length is always one value more than that of the `<track-size>`.
|
||||
pub line_names: Vec<Vec<String>>,
|
||||
pub line_names: Vec<Vec<CustomIdent>>,
|
||||
/// `<track-size>` values.
|
||||
pub track_sizes: Vec<TrackSize<L>>,
|
||||
}
|
||||
|
||||
impl<L: ToCss> ToCss for TrackRepeat<L> {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
dest.write_str("repeat(")?;
|
||||
self.count.to_css(dest)?;
|
||||
dest.write_str(", ")?;
|
||||
// If repeat count is an integer instead of a keyword, it should'n serialized
|
||||
// with `repeat` function. It should serialized with `N` repeated form.
|
||||
let repeat_count = match self.count {
|
||||
RepeatCount::Number(integer) => integer.value(),
|
||||
_ => {
|
||||
dest.write_str("repeat(")?;
|
||||
self.count.to_css(dest)?;
|
||||
dest.write_str(", ")?;
|
||||
1
|
||||
},
|
||||
};
|
||||
|
||||
let mut line_names_iter = self.line_names.iter();
|
||||
for (i, (ref size, ref names)) in self.track_sizes.iter()
|
||||
.zip(&mut line_names_iter).enumerate() {
|
||||
if i > 0 {
|
||||
for i in 0..repeat_count {
|
||||
if i != 0 {
|
||||
dest.write_str(" ")?;
|
||||
}
|
||||
|
||||
concat_serialize_idents("[", "] ", names, " ", dest)?;
|
||||
size.to_css(dest)?;
|
||||
let mut line_names_iter = self.line_names.iter();
|
||||
for (i, (ref size, ref names)) in self.track_sizes.iter()
|
||||
.zip(&mut line_names_iter).enumerate() {
|
||||
if i > 0 {
|
||||
dest.write_str(" ")?;
|
||||
}
|
||||
|
||||
concat_serialize_idents("[", "] ", names, " ", dest)?;
|
||||
size.to_css(dest)?;
|
||||
}
|
||||
|
||||
if let Some(line_names_last) = line_names_iter.next() {
|
||||
concat_serialize_idents(" [", "]", line_names_last, " ", dest)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(line_names_last) = line_names_iter.next() {
|
||||
concat_serialize_idents(" [", "]", line_names_last, " ", dest)?;
|
||||
match self.count {
|
||||
RepeatCount::AutoFill | RepeatCount::AutoFit => {
|
||||
dest.write_str(")")?;
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
dest.write_str(")")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -502,7 +529,7 @@ pub struct TrackList<T> {
|
|||
/// If there's no `<line-names>`, then it's represented by an empty vector.
|
||||
/// For N values, there will be N+1 `<line-names>`, and so this vector's
|
||||
/// length is always one value more than that of the `<track-size>`.
|
||||
pub line_names: Vec<Vec<String>>,
|
||||
pub line_names: Vec<Vec<CustomIdent>>,
|
||||
/// `<auto-repeat>` value after computation. This field is necessary, because
|
||||
/// the `values` field (after computation) will only contain `<track-size>` values, and
|
||||
/// we need something to represent this function.
|
||||
|
@ -551,3 +578,115 @@ impl<T: ToCss> ToCss for TrackList<T> {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// The `<line-name-list>` for subgrids.
|
||||
///
|
||||
/// `subgrid [ <line-names> | repeat(<positive-integer> | auto-fill, <line-names>+) ]+`
|
||||
/// Old spec: https://www.w3.org/TR/2015/WD-css-grid-1-20150917/#typedef-line-name-list
|
||||
#[derive(Clone, PartialEq, Debug, Default)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
pub struct LineNameList {
|
||||
/// The optional `<line-name-list>`
|
||||
pub names: Vec<Vec<CustomIdent>>,
|
||||
/// Indicates the line name that requires `auto-fill`
|
||||
pub fill_idx: Option<u32>,
|
||||
}
|
||||
|
||||
impl Parse for LineNameList {
|
||||
fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
|
||||
input.expect_ident_matching("subgrid")?;
|
||||
let mut line_names = vec![];
|
||||
let mut fill_idx = None;
|
||||
|
||||
loop {
|
||||
let repeat_parse_result = input.try(|input| {
|
||||
input.expect_function_matching("repeat")?;
|
||||
input.parse_nested_block(|input| {
|
||||
let count = RepeatCount::parse(context, input)?;
|
||||
input.expect_comma()?;
|
||||
let mut names_list = vec![];
|
||||
names_list.push(parse_line_names(input)?); // there should be at least one
|
||||
while let Ok(names) = input.try(parse_line_names) {
|
||||
names_list.push(names);
|
||||
}
|
||||
|
||||
Ok((names_list, count))
|
||||
})
|
||||
});
|
||||
|
||||
if let Ok((mut names_list, count)) = repeat_parse_result {
|
||||
match count {
|
||||
RepeatCount::Number(num) =>
|
||||
line_names.extend(names_list.iter().cloned().cycle()
|
||||
.take(num.value() as usize * names_list.len())),
|
||||
RepeatCount::AutoFill if fill_idx.is_none() => {
|
||||
// `repeat(autof-fill, ..)` should have just one line name.
|
||||
if names_list.len() > 1 {
|
||||
return Err(StyleParseError::UnspecifiedError.into());
|
||||
}
|
||||
let names = names_list.pop().expect("expected one name list for auto-fill");
|
||||
|
||||
line_names.push(names);
|
||||
fill_idx = Some(line_names.len() as u32 - 1);
|
||||
},
|
||||
_ => return Err(StyleParseError::UnspecifiedError.into()),
|
||||
}
|
||||
} else if let Ok(names) = input.try(parse_line_names) {
|
||||
line_names.push(names);
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
Ok(LineNameList {
|
||||
names: line_names,
|
||||
fill_idx: fill_idx,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCss for LineNameList {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
dest.write_str("subgrid")?;
|
||||
let fill_idx = self.fill_idx.map(|v| v as usize).unwrap_or(usize::MAX);
|
||||
for (i, names) in self.names.iter().enumerate() {
|
||||
if i == fill_idx {
|
||||
dest.write_str(" repeat(auto-fill,")?;
|
||||
}
|
||||
|
||||
dest.write_str(" [")?;
|
||||
|
||||
if let Some((ref first, rest)) = names.split_first() {
|
||||
first.to_css(dest)?;
|
||||
for name in rest {
|
||||
dest.write_str(" ")?;
|
||||
name.to_css(dest)?;
|
||||
}
|
||||
}
|
||||
|
||||
dest.write_str("]")?;
|
||||
if i == fill_idx {
|
||||
dest.write_str(")")?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ComputedValueAsSpecified for LineNameList {}
|
||||
no_viewport_percentage!(LineNameList);
|
||||
|
||||
/// Variants for `<grid-template-rows> | <grid-template-columns>`
|
||||
/// Subgrid deferred to Level 2 spec due to lack of implementation.
|
||||
/// But it's implemented in gecko, so we have to as well.
|
||||
#[derive(Clone, PartialEq, Debug, ToCss)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
pub enum GridTemplateComponent<L> {
|
||||
/// `none` value.
|
||||
None,
|
||||
/// The grid `<track-list>`
|
||||
TrackList(TrackList<L>),
|
||||
/// A `subgrid <line-name-list>?`
|
||||
Subgrid(LineNameList),
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue