Introduce style::values::generics::rect ▭

This defines a single type Rect<T> which allows us to abstract over
CSS values made of four sides top, right, bottom and left.
This commit is contained in:
Anthony Ramine 2017-05-23 02:40:12 +02:00
parent e9c5860808
commit 7a214831f0
10 changed files with 169 additions and 105 deletions

View file

@ -959,8 +959,7 @@ fn static_assert() {
pub fn set_border_image_outset(&mut self, v: longhands::border_image_outset::computed_value::T) { pub fn set_border_image_outset(&mut self, v: longhands::border_image_outset::computed_value::T) {
% for side in SIDES: % for side in SIDES:
v.${side.index}.to_gecko_style_coord(&mut self.gecko.mBorderImageOutset v.${side.ident}.to_gecko_style_coord(&mut self.gecko.mBorderImageOutset.data_at_mut(${side.index}));
.data_at_mut(${side.index}));
% endfor % endfor
} }

View file

@ -200,109 +200,13 @@ ${helpers.predefined_type("border-image-source", "ImageLayer",
has_uncacheable_values=False, has_uncacheable_values=False,
boxed="True")} boxed="True")}
<%helpers:longhand name="border-image-outset" animation_value_type="none" ${helpers.predefined_type("border-image-outset", "LengthOrNumberRect",
spec="https://drafts.csswg.org/css-backgrounds/#border-image-outset"> parse_method="parse_non_negative",
use std::fmt; initial_value="computed::LengthOrNumber::zero().into()",
use style_traits::ToCss; initial_specified_value="specified::LengthOrNumber::zero().into()",
use values::specified::{LengthOrNumber, Number}; spec="https://drafts.csswg.org/css-backgrounds/#border-image-outset",
animation_value_type="none",
pub mod computed_value { boxed=True)}
use values::computed::LengthOrNumber;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct T(pub LengthOrNumber, pub LengthOrNumber,
pub LengthOrNumber, pub LengthOrNumber);
}
#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct SpecifiedValue(pub Vec<LengthOrNumber>);
impl ToCss for computed_value::T {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
try!(self.0.to_css(dest));
try!(dest.write_str(" "));
try!(self.1.to_css(dest));
try!(dest.write_str(" "));
try!(self.2.to_css(dest));
try!(dest.write_str(" "));
self.3.to_css(dest)
}
}
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
try!(self.0[0].to_css(dest));
for value in self.0.iter().skip(1) {
try!(dest.write_str(" "));
try!(value.to_css(dest));
}
Ok(())
}
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T(Either::Second(0.0), Either::Second(0.0),
Either::Second(0.0), Either::Second(0.0))
}
#[inline]
pub fn get_initial_specified_value() -> SpecifiedValue {
SpecifiedValue(vec![Either::Second(Number::new(0.0))])
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
let length = self.0.len();
match length {
4 => computed_value::T(self.0[0].to_computed_value(context),
self.0[1].to_computed_value(context),
self.0[2].to_computed_value(context),
self.0[3].to_computed_value(context)),
3 => computed_value::T(self.0[0].to_computed_value(context),
self.0[1].to_computed_value(context),
self.0[2].to_computed_value(context),
self.0[1].to_computed_value(context)),
2 => computed_value::T(self.0[0].to_computed_value(context),
self.0[1].to_computed_value(context),
self.0[0].to_computed_value(context),
self.0[1].to_computed_value(context)),
1 => computed_value::T(self.0[0].to_computed_value(context),
self.0[0].to_computed_value(context),
self.0[0].to_computed_value(context),
self.0[0].to_computed_value(context)),
_ => unreachable!(),
}
}
#[inline]
fn from_computed_value(computed: &computed_value::T) -> Self {
SpecifiedValue(vec![ToComputedValue::from_computed_value(&computed.0),
ToComputedValue::from_computed_value(&computed.1),
ToComputedValue::from_computed_value(&computed.2),
ToComputedValue::from_computed_value(&computed.3)])
}
}
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
let mut values = vec![];
for _ in 0..4 {
let value = input.try(|input| LengthOrNumber::parse_non_negative(context, input));
match value {
Ok(val) => values.push(val),
Err(_) => break,
}
}
if values.len() > 0 {
Ok(SpecifiedValue(values))
} else {
Err(())
}
}
</%helpers:longhand>
<%helpers:longhand name="border-image-repeat" animation_value_type="none" <%helpers:longhand name="border-image-repeat" animation_value_type="none"
spec="https://drafts.csswg.org/css-backgrounds/#border-image-repeat"> spec="https://drafts.csswg.org/css-backgrounds/#border-image-repeat">

View file

@ -603,6 +603,14 @@ pub type LengthOrAuto = Either<Length, Auto>;
/// Either a computed `<length>` or a `<number>` value. /// Either a computed `<length>` or a `<number>` value.
pub type LengthOrNumber = Either<Length, Number>; pub type LengthOrNumber = Either<Length, Number>;
impl LengthOrNumber {
/// Returns `0`.
#[inline]
pub fn zero() -> Self {
Either::Second(0.)
}
}
/// Either a computed `<length>` or the `normal` keyword. /// Either a computed `<length>` or the `normal` keyword.
pub type LengthOrNormal = Either<Length, Normal>; pub type LengthOrNormal = Either<Length, Normal>;

View file

@ -25,6 +25,7 @@ use super::specified;
pub use app_units::Au; pub use app_units::Au;
pub use cssparser::Color as CSSColor; pub use cssparser::Color as CSSColor;
pub use self::image::{Gradient, GradientItem, ImageLayer, LineDirection, Image, ImageRect}; pub use self::image::{Gradient, GradientItem, ImageLayer, LineDirection, Image, ImageRect};
pub use self::rect::LengthOrNumberRect;
pub use super::{Auto, Either, None_}; pub use super::{Auto, Either, None_};
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub use super::specified::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems}; pub use super::specified::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
@ -40,6 +41,7 @@ pub mod basic_shape;
pub mod image; pub mod image;
pub mod length; pub mod length;
pub mod position; pub mod position;
pub mod rect;
/// A `Context` is all the data a specified value could ever need to compute /// A `Context` is all the data a specified value could ever need to compute
/// itself and be transformed to a computed value. /// itself and be transformed to a computed value.

View file

@ -0,0 +1,11 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! Computed types for CSS borders.
use values::computed::length::LengthOrNumber;
use values::generics::rect::Rect;
/// A specified rectangle made of four `<length-or-number>` values.
pub type LengthOrNumberRect = Rect<LengthOrNumber>;

View file

@ -19,6 +19,7 @@ pub mod basic_shape;
pub mod grid; pub mod grid;
pub mod image; pub mod image;
pub mod position; pub mod position;
pub mod rect;
#[derive(Clone, Debug, PartialEq, ToComputedValue)] #[derive(Clone, Debug, PartialEq, ToComputedValue)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]

View file

@ -0,0 +1,110 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! Generic types for CSS values that are composed of four sides.
use cssparser::Parser;
use parser::{Parse, ParserContext};
use std::fmt;
use style_traits::ToCss;
/// A CSS value made of four sides: top, right, bottom, and left.
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct Rect<T> {
/// Top
pub top: T,
/// Right.
pub right: T,
/// Bottom.
pub bottom: T,
/// Left.
pub left: T,
}
impl<T> Rect<T> {
/// Returns a new `Rect<T>` value.
pub fn new(top: T, right: T, bottom: T, left: T) -> Self {
Rect {
top: top,
right: right,
bottom: bottom,
left: left,
}
}
}
impl<T> Rect<T>
where T: Clone
{
/// Parses a new `Rect<T>` value with the given parse function.
pub fn parse_with<Parse>(
context: &ParserContext,
input: &mut Parser,
parse: Parse)
-> Result<Self, ()>
where Parse: Fn(&ParserContext, &mut Parser) -> Result<T, ()>
{
let top = parse(context, input)?;
let right = if let Ok(right) = input.try(|i| parse(context, i)) { right } else {
// <top>
return Ok(Self::new(top.clone(), top.clone(), top.clone(), top));
};
let bottom = if let Ok(bottom) = input.try(|i| parse(context, i)) { bottom } else {
// <top> <right>
return Ok(Self::new(top.clone(), right.clone(), top, right));
};
let left = if let Ok(left) = input.try(|i| parse(context, i)) { left } else {
// <top> <right> <bottom>
return Ok(Self::new(top, right.clone(), bottom, right));
};
// <top> <right> <bottom> <left>
Ok(Self::new(top, right, bottom, left))
}
}
impl<T> From<T> for Rect<T>
where T: Clone
{
#[inline]
fn from(value: T) -> Self {
Self::new(value.clone(), value.clone(), value.clone(), value)
}
}
impl<T> Parse for Rect<T>
where T: Clone + Parse
{
#[inline]
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
Self::parse_with(context, input, T::parse)
}
}
impl<T> ToCss for Rect<T>
where T: PartialEq + ToCss
{
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
where W: fmt::Write,
{
self.top.to_css(dest)?;
let same_vertical = self.top == self.bottom;
let same_horizontal = self.right == self.left;
if same_vertical && same_horizontal && self.top == self.right {
return Ok(());
}
dest.write_str(" ")?;
self.right.to_css(dest)?;
if same_vertical && same_horizontal {
return Ok(());
}
dest.write_str(" ")?;
self.bottom.to_css(dest)?;
if same_horizontal {
return Ok(());
}
dest.write_str(" ")?;
self.left.to_css(dest)
}
}

View file

@ -1180,6 +1180,12 @@ impl LengthOrNumber {
Length::parse_non_negative(context, input).map(Either::First) Length::parse_non_negative(context, input).map(Either::First)
} }
/// Returns `0`.
#[inline]
pub fn zero() -> Self {
Either::Second(Number::new(0.))
}
} }
/// A value suitable for a `min-width` or `min-height` property. /// A value suitable for a `min-width` or `min-height` property.

View file

@ -30,6 +30,7 @@ use values::specified::calc::CalcNode;
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub use self::align::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems}; pub use self::align::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
pub use self::rect::LengthOrNumberRect;
pub use self::color::Color; pub use self::color::Color;
pub use super::generics::grid::GridLine; pub use super::generics::grid::GridLine;
pub use self::image::{ColorStop, EndingShape as GradientEndingShape, Gradient}; pub use self::image::{ColorStop, EndingShape as GradientEndingShape, Gradient};
@ -50,6 +51,7 @@ pub mod grid;
pub mod image; pub mod image;
pub mod length; pub mod length;
pub mod position; pub mod position;
pub mod rect;
/// Common handling for the specified value CSS url() values. /// Common handling for the specified value CSS url() values.
pub mod url { pub mod url {

View file

@ -0,0 +1,21 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! Computed types for CSS borders.
use cssparser::Parser;
use parser::ParserContext;
use values::generics::rect::Rect;
use values::specified::length::LengthOrNumber;
/// A specified rectangle made of four `<length-or-number>` values.
pub type LengthOrNumberRect = Rect<LengthOrNumber>;
impl LengthOrNumberRect {
/// Parses a `LengthOrNumberRect`, rejecting negative values.
#[inline]
pub fn parse_non_negative(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
Rect::parse_with(context, input, LengthOrNumber::parse_non_negative)
}
}