style: Implement style system support for Masonry layout.

This implements support for this CSS Masonry layout proposal:
https://github.com/w3c/csswg-drafts/issues/4650

I've intentionally left out a shorthand (place-tracks?) for now until
we have a draft CSS spec for this.

Differential Revision: https://phabricator.services.mozilla.com/D67061
This commit is contained in:
Mats Palmgren 2020-04-28 01:18:44 +00:00 committed by Emilio Cobos Álvarez
parent 21d48e00cc
commit 6f58c66589
11 changed files with 295 additions and 30 deletions

View file

@ -356,6 +356,7 @@ class Longhand(object):
"JustifyItems",
"JustifySelf",
"LineBreak",
"MasonryAutoFlow",
"MozForceBrokenImageIcon",
"MozListReversed",
"MozScriptLevel",

View file

@ -802,7 +802,8 @@ fn static_assert() {
<% skip_position_longhands = " ".join(x.ident for x in SIDES) %>
<%self:impl_trait style_struct_name="Position"
skip_longhands="${skip_position_longhands}">
skip_longhands="${skip_position_longhands}
masonry-auto-flow">
% for side in SIDES:
<% impl_split_style_coord(side.ident, "mOffset", side.index) %>
% endfor
@ -811,6 +812,7 @@ fn static_assert() {
self.gecko.mJustifyItems.computed = v;
}
${impl_simple_type_with_conversion("masonry_auto_flow", "mMasonryAutoFlow")}
</%self:impl_trait>
<% skip_outline_longhands = " ".join("outline-style outline-width".split() +

View file

@ -114,6 +114,17 @@ ${helpers.single_keyword(
animation_value_type="discrete",
servo_restyle_damage="reflow",
)}
${helpers.predefined_type(
"justify-tracks",
"JustifyTracks",
"specified::JustifyTracks::default()",
engines="gecko",
gecko_pref="layout.css.grid-template-masonry-value.enabled",
animation_value_type="discrete",
servo_restyle_damage="reflow",
spec="https://github.com/w3c/csswg-drafts/issues/4650",
)}
% endif
% if engine in ["servo-2013", "servo-2020"]:
@ -151,6 +162,17 @@ ${helpers.single_keyword(
servo_restyle_damage="reflow",
)}
${helpers.predefined_type(
"align-tracks",
"AlignTracks",
"specified::AlignTracks::default()",
engines="gecko",
gecko_pref="layout.css.grid-template-masonry-value.enabled",
animation_value_type="discrete",
servo_restyle_damage="reflow",
spec="https://github.com/w3c/csswg-drafts/issues/4650",
)}
${helpers.predefined_type(
"align-items",
"AlignItems",
@ -372,6 +394,16 @@ ${helpers.predefined_type(
% endfor
${helpers.predefined_type(
"masonry-auto-flow",
"MasonryAutoFlow",
"computed::MasonryAutoFlow::initial()",
engines="gecko",
gecko_pref="layout.css.grid-template-masonry-value.enabled",
animation_value_type="discrete",
spec="https://github.com/w3c/csswg-drafts/issues/4650",
)}
${helpers.predefined_type(
"grid-auto-flow",
"GridAutoFlow",

View file

@ -10,7 +10,7 @@ use crate::values::computed::{Context, ToComputedValue};
use crate::values::specified;
pub use super::specified::{
AlignContent, AlignItems, ContentDistribution, JustifyContent, SelfAlignment,
AlignContent, AlignItems, AlignTracks, ContentDistribution, JustifyContent, JustifyTracks, SelfAlignment,
};
pub use super::specified::{AlignSelf, JustifySelf};

View file

@ -29,7 +29,7 @@ use std::cmp;
use std::f32;
#[cfg(feature = "gecko")]
pub use self::align::{AlignContent, AlignItems, JustifyContent, JustifyItems, SelfAlignment};
pub use self::align::{AlignContent, AlignItems, AlignTracks, JustifyContent, JustifyItems, JustifyTracks, SelfAlignment};
#[cfg(feature = "gecko")]
pub use self::align::{AlignSelf, JustifySelf};
pub use self::angle::Angle;
@ -68,7 +68,7 @@ pub use self::list::Quotes;
pub use self::motion::{OffsetPath, OffsetRotate};
pub use self::outline::OutlineStyle;
pub use self::percentage::{NonNegativePercentage, Percentage};
pub use self::position::{GridAutoFlow, GridTemplateAreas, Position, PositionOrAuto, ZIndex};
pub use self::position::{GridAutoFlow, GridTemplateAreas, MasonryAutoFlow, Position, PositionOrAuto, ZIndex};
pub use self::rect::NonNegativeLengthOrNumberRect;
pub use self::resolution::Resolution;
pub use self::svg::MozContextProperties;

View file

@ -12,7 +12,7 @@ use crate::values::generics::position::Position as GenericPosition;
use crate::values::generics::position::PositionComponent as GenericPositionComponent;
use crate::values::generics::position::PositionOrAuto as GenericPositionOrAuto;
use crate::values::generics::position::ZIndex as GenericZIndex;
pub use crate::values::specified::position::{GridAutoFlow, GridTemplateAreas};
pub use crate::values::specified::position::{GridAutoFlow, GridTemplateAreas, MasonryAutoFlow};
use crate::Zero;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss};

View file

@ -786,6 +786,9 @@ pub enum GenericGridTemplateComponent<L, I> {
/// TODO: Support animations for this after subgrid is addressed in [grid-2] spec.
#[animation(error)]
Subgrid(Box<LineNameList>),
/// `masonry` value.
/// https://github.com/w3c/csswg-drafts/issues/4650
Masonry,
}
pub use self::GenericGridTemplateComponent as GridTemplateComponent;

View file

@ -171,16 +171,6 @@ impl ContentDistribution {
Self { primary }
}
fn from_bits(bits: u16) -> Self {
Self {
primary: AlignFlags::from_bits_truncate(bits as u8),
}
}
fn as_bits(&self) -> u16 {
self.primary.bits() as u16
}
/// Returns whether this value is a <baseline-position>.
pub fn is_baseline_position(&self) -> bool {
matches!(
@ -292,6 +282,41 @@ impl SpecifiedValueInfo for AlignContent {
}
}
/// Value for the `align-tracks` property.
///
/// <https://github.com/w3c/csswg-drafts/issues/4650>
#[derive(
Clone,
Debug,
Default,
Eq,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(transparent)]
#[css(comma)]
pub struct AlignTracks(
#[css(iterable, if_empty = "normal")]
pub crate::OwnedSlice<AlignContent>
);
impl Parse for AlignTracks {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
let values = input.parse_comma_separated(|input| {
AlignContent::parse(context, input)
})?;
Ok(AlignTracks(values.into()))
}
}
/// Value for the `justify-content` property.
///
/// <https://drafts.csswg.org/css-align/#propdef-justify-content>
@ -329,18 +354,38 @@ impl SpecifiedValueInfo for JustifyContent {
ContentDistribution::list_keywords(f, AxisDirection::Inline);
}
}
/// Value for the `justify-tracks` property.
///
/// <https://github.com/w3c/csswg-drafts/issues/4650>
#[derive(
Clone,
Debug,
Default,
Eq,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(transparent)]
#[css(comma)]
pub struct JustifyTracks(
#[css(iterable, if_empty = "normal")]
pub crate::OwnedSlice<JustifyContent>
);
#[cfg(feature = "gecko")]
impl From<u16> for JustifyContent {
fn from(bits: u16) -> Self {
JustifyContent(ContentDistribution::from_bits(bits))
}
}
#[cfg(feature = "gecko")]
impl From<JustifyContent> for u16 {
fn from(v: JustifyContent) -> u16 {
v.0.as_bits()
impl Parse for JustifyTracks {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
let values = input.parse_comma_separated(|input| {
JustifyContent::parse(context, input)
})?;
Ok(JustifyTracks(values.into()))
}
}

View file

@ -295,6 +295,18 @@ fn allow_grid_template_subgrids() -> bool {
false
}
#[cfg(feature = "gecko")]
#[inline]
fn allow_grid_template_masonry() -> bool {
static_prefs::pref!("layout.css.grid-template-masonry-value.enabled")
}
#[cfg(feature = "servo")]
#[inline]
fn allow_grid_template_masonry() -> bool {
false
}
impl Parse for GridTemplateComponent<LengthPercentage, Integer> {
fn parse<'i, 't>(
context: &ParserContext,
@ -319,7 +331,11 @@ impl GridTemplateComponent<LengthPercentage, Integer> {
return Ok(GridTemplateComponent::Subgrid(Box::new(t)));
}
}
if allow_grid_template_masonry() {
if input.try(|i| i.expect_ident_matching("masonry")).is_ok() {
return Ok(GridTemplateComponent::Masonry);
}
}
let track_list = TrackList::parse(context, input)?;
Ok(GridTemplateComponent::TrackList(Box::new(track_list)))
}

View file

@ -28,9 +28,9 @@ use style_traits::values::specified::AllowedNumericType;
use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss};
#[cfg(feature = "gecko")]
pub use self::align::{AlignContent, AlignItems, AlignSelf, ContentDistribution};
pub use self::align::{AlignContent, AlignItems, AlignSelf, AlignTracks, ContentDistribution};
#[cfg(feature = "gecko")]
pub use self::align::{JustifyContent, JustifyItems, JustifySelf, SelfAlignment};
pub use self::align::{JustifyContent, JustifyItems, JustifySelf, JustifyTracks, SelfAlignment};
pub use self::angle::{AllowUnitlessZeroAngle, Angle};
pub use self::background::{BackgroundRepeat, BackgroundSize};
pub use self::basic_shape::FillRule;
@ -72,7 +72,7 @@ pub use self::list::Quotes;
pub use self::motion::{OffsetPath, OffsetRotate};
pub use self::outline::OutlineStyle;
pub use self::percentage::Percentage;
pub use self::position::{GridAutoFlow, GridTemplateAreas, Position, PositionOrAuto};
pub use self::position::{GridAutoFlow, GridTemplateAreas, MasonryAutoFlow, Position, PositionOrAuto};
pub use self::position::{PositionComponent, ZIndex};
pub use self::rect::NonNegativeLengthOrNumberRect;
pub use self::resolution::Resolution;

View file

@ -383,6 +383,172 @@ bitflags! {
}
}
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
/// Masonry auto-placement algorithm packing.
pub enum MasonryPlacement {
/// Place the item in the track(s) with the smallest extent so far.
Pack,
/// Place the item after the last item, from start to end.
Next,
}
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
/// Masonry auto-placement algorithm item sorting option.
pub enum MasonryItemOrder {
/// Place all items with a definite placement before auto-placed items.
DefiniteFirst,
/// Place items in `order-modified document order`.
Ordered,
}
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
/// Controls how the Masonry layout algorithm works
/// specifying exactly how auto-placed items get flowed in the masonry axis.
pub struct MasonryAutoFlow {
/// Specify how to pick a auto-placement track.
#[css(contextual_skip_if = "is_pack_with_non_default_order")]
pub placement: MasonryPlacement,
/// Specify how to pick an item to place.
#[css(skip_if = "is_item_order_definite_first")]
pub order: MasonryItemOrder,
}
#[inline]
fn is_pack_with_non_default_order(placement: &MasonryPlacement, order: &MasonryItemOrder) -> bool {
*placement == MasonryPlacement::Pack &&
*order != MasonryItemOrder::DefiniteFirst
}
#[inline]
fn is_item_order_definite_first(order: &MasonryItemOrder) -> bool {
*order == MasonryItemOrder::DefiniteFirst
}
impl MasonryAutoFlow {
#[inline]
/// Get initial `masonry-auto-flow` value.
pub fn initial() -> MasonryAutoFlow {
MasonryAutoFlow {
placement: MasonryPlacement::Pack,
order: MasonryItemOrder::DefiniteFirst,
}
}
}
impl Parse for MasonryAutoFlow {
/// [ definite-first | ordered ] || [ pack | next ]
fn parse<'i, 't>(
_context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<MasonryAutoFlow, ParseError<'i>> {
let mut value = MasonryAutoFlow::initial();
let mut got_placement = false;
let mut got_order = false;
while !input.is_exhausted() {
let location = input.current_source_location();
let ident = input.expect_ident()?;
let success = match_ignore_ascii_case! { &ident,
"pack" if !got_placement => {
got_placement = true;
true
},
"next" if !got_placement => {
value.placement = MasonryPlacement::Next;
got_placement = true;
true
},
"definite-first" if !got_order => {
got_order = true;
true
},
"ordered" if !got_order => {
value.order = MasonryItemOrder::Ordered;
got_order = true;
true
},
_ => false
};
if !success {
return Err(location
.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())));
}
}
if got_placement || got_order {
Ok(value)
} else {
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
}
}
}
#[cfg(feature = "gecko")]
impl From<u8> for MasonryAutoFlow {
fn from(bits: u8) -> MasonryAutoFlow {
use crate::gecko_bindings::structs;
let mut value = MasonryAutoFlow::initial();
if bits & structs::NS_STYLE_MASONRY_PLACEMENT_PACK as u8 == 0 {
value.placement = MasonryPlacement::Next;
}
if bits & structs::NS_STYLE_MASONRY_ORDER_DEFINITE_FIRST as u8 == 0 {
value.order = MasonryItemOrder::Ordered;
}
value
}
}
#[cfg(feature = "gecko")]
impl From<MasonryAutoFlow> for u8 {
fn from(v: MasonryAutoFlow) -> u8 {
use crate::gecko_bindings::structs;
let mut result: u8 = 0;
if v.placement == MasonryPlacement::Pack {
result |= structs::NS_STYLE_MASONRY_PLACEMENT_PACK as u8;
}
if v.order == MasonryItemOrder::DefiniteFirst {
result |= structs::NS_STYLE_MASONRY_ORDER_DEFINITE_FIRST as u8;
}
result
}
}
impl Parse for GridAutoFlow {
/// [ row | column ] || dense
fn parse<'i, 't>(