mirror of
https://github.com/servo/servo.git
synced 2025-08-05 21:50:18 +01:00
style: Refactor GenericGradient for conic-gradient support.
Differential Revision: https://phabricator.services.mozilla.com/D62923
This commit is contained in:
parent
fc88e908d5
commit
25b265a10f
4 changed files with 120 additions and 105 deletions
|
@ -51,7 +51,7 @@ pub use self::GenericImage as Image;
|
||||||
/// <https://drafts.csswg.org/css-images/#gradients>
|
/// <https://drafts.csswg.org/css-images/#gradients>
|
||||||
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
|
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct GenericGradient<
|
pub enum GenericGradient<
|
||||||
LineDirection,
|
LineDirection,
|
||||||
LengthPercentage,
|
LengthPercentage,
|
||||||
NonNegativeLength,
|
NonNegativeLength,
|
||||||
|
@ -59,19 +59,30 @@ pub struct GenericGradient<
|
||||||
Position,
|
Position,
|
||||||
Color,
|
Color,
|
||||||
> {
|
> {
|
||||||
/// Gradients can be linear or radial.
|
/// A linear gradient.
|
||||||
pub kind: GenericGradientKind<
|
Linear {
|
||||||
LineDirection,
|
/// Line direction
|
||||||
NonNegativeLength,
|
direction: LineDirection,
|
||||||
NonNegativeLengthPercentage,
|
/// The color stops and interpolation hints.
|
||||||
Position,
|
items: crate::OwnedSlice<GenericGradientItem<Color, LengthPercentage>>,
|
||||||
>,
|
/// True if this is a repeating gradient.
|
||||||
/// The color stops and interpolation hints.
|
repeating: bool,
|
||||||
pub items: crate::OwnedSlice<GenericGradientItem<Color, LengthPercentage>>,
|
/// Compatibility mode.
|
||||||
/// True if this is a repeating gradient.
|
compat_mode: GradientCompatMode,
|
||||||
pub repeating: bool,
|
},
|
||||||
/// Compatibility mode.
|
/// A radial gradient.
|
||||||
pub compat_mode: GradientCompatMode,
|
Radial {
|
||||||
|
/// Shape of gradient
|
||||||
|
shape: GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage>,
|
||||||
|
/// Center of gradient
|
||||||
|
position: Position,
|
||||||
|
/// The color stops and interpolation hints.
|
||||||
|
items: crate::OwnedSlice<GenericGradientItem<Color, LengthPercentage>>,
|
||||||
|
/// True if this is a repeating gradient.
|
||||||
|
repeating: bool,
|
||||||
|
/// Compatibility mode.
|
||||||
|
compat_mode: GradientCompatMode,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use self::GenericGradient as Gradient;
|
pub use self::GenericGradient as Gradient;
|
||||||
|
@ -88,26 +99,6 @@ pub enum GradientCompatMode {
|
||||||
Moz,
|
Moz,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A gradient kind.
|
|
||||||
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
|
|
||||||
#[repr(C, u8)]
|
|
||||||
pub enum GenericGradientKind<
|
|
||||||
LineDirection,
|
|
||||||
NonNegativeLength,
|
|
||||||
NonNegativeLengthPercentage,
|
|
||||||
Position,
|
|
||||||
> {
|
|
||||||
/// A linear gradient.
|
|
||||||
Linear(LineDirection),
|
|
||||||
/// A radial gradient.
|
|
||||||
Radial(
|
|
||||||
GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage>,
|
|
||||||
Position,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub use self::GenericGradientKind as GradientKind;
|
|
||||||
|
|
||||||
/// A radial gradient's ending shape.
|
/// A radial gradient's ending shape.
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
|
Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
|
||||||
|
@ -330,32 +321,39 @@ where
|
||||||
where
|
where
|
||||||
W: Write,
|
W: Write,
|
||||||
{
|
{
|
||||||
match self.compat_mode {
|
let (compat_mode, repeating) = match *self {
|
||||||
|
Gradient::Linear { compat_mode, repeating, .. } => (compat_mode, repeating),
|
||||||
|
Gradient::Radial { compat_mode, repeating, .. } => (compat_mode, repeating),
|
||||||
|
};
|
||||||
|
|
||||||
|
match compat_mode {
|
||||||
GradientCompatMode::WebKit => dest.write_str("-webkit-")?,
|
GradientCompatMode::WebKit => dest.write_str("-webkit-")?,
|
||||||
GradientCompatMode::Moz => dest.write_str("-moz-")?,
|
GradientCompatMode::Moz => dest.write_str("-moz-")?,
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.repeating {
|
if repeating {
|
||||||
dest.write_str("repeating-")?;
|
dest.write_str("repeating-")?;
|
||||||
}
|
}
|
||||||
dest.write_str(self.kind.label())?;
|
|
||||||
dest.write_str("-gradient(")?;
|
let (items, mut skip_comma) = match *self {
|
||||||
let mut skip_comma = match self.kind {
|
Gradient::Linear { ref direction, compat_mode, ref items, .. } => {
|
||||||
GradientKind::Linear(ref direction) if direction.points_downwards(self.compat_mode) => {
|
dest.write_str("linear-gradient(")?;
|
||||||
true
|
if !direction.points_downwards(compat_mode) {
|
||||||
|
direction.to_css(dest, compat_mode)?;
|
||||||
|
(items, false)
|
||||||
|
} else {
|
||||||
|
(items, true)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
GradientKind::Linear(ref direction) => {
|
Gradient::Radial { ref shape, ref position, compat_mode, ref items, .. } => {
|
||||||
direction.to_css(dest, self.compat_mode)?;
|
dest.write_str("radial-gradient(")?;
|
||||||
false
|
|
||||||
},
|
|
||||||
GradientKind::Radial(ref shape, ref position) => {
|
|
||||||
let omit_shape = match *shape {
|
let omit_shape = match *shape {
|
||||||
EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::Cover)) |
|
EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::Cover)) |
|
||||||
EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) => true,
|
EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
if self.compat_mode == GradientCompatMode::Modern {
|
if compat_mode == GradientCompatMode::Modern {
|
||||||
if !omit_shape {
|
if !omit_shape {
|
||||||
shape.to_css(dest)?;
|
shape.to_css(dest)?;
|
||||||
dest.write_str(" ")?;
|
dest.write_str(" ")?;
|
||||||
|
@ -369,10 +367,10 @@ where
|
||||||
shape.to_css(dest)?;
|
shape.to_css(dest)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
false
|
(items, false)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
for item in &*self.items {
|
for item in &**items {
|
||||||
if !skip_comma {
|
if !skip_comma {
|
||||||
dest.write_str(", ")?;
|
dest.write_str(", ")?;
|
||||||
}
|
}
|
||||||
|
@ -383,15 +381,6 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D, L, LoP, P> GradientKind<D, L, LoP, P> {
|
|
||||||
fn label(&self) -> &str {
|
|
||||||
match *self {
|
|
||||||
GradientKind::Linear(..) => "linear",
|
|
||||||
GradientKind::Radial(..) => "radial",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The direction of a linear gradient.
|
/// The direction of a linear gradient.
|
||||||
pub trait LineDirection {
|
pub trait LineDirection {
|
||||||
/// Whether this direction points towards, and thus can be omitted.
|
/// Whether this direction points towards, and thus can be omitted.
|
||||||
|
|
|
@ -70,10 +70,6 @@ impl SpecifiedValueInfo for Gradient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A specified gradient kind.
|
|
||||||
pub type GradientKind =
|
|
||||||
generic::GradientKind<LineDirection, NonNegativeLength, NonNegativeLengthPercentage, Position>;
|
|
||||||
|
|
||||||
/// A specified gradient line direction.
|
/// A specified gradient line direction.
|
||||||
///
|
///
|
||||||
/// FIXME(emilio): This should be generic over Angle.
|
/// FIXME(emilio): This should be generic over Angle.
|
||||||
|
@ -245,25 +241,12 @@ impl Parse for Gradient {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let (kind, items) = input.parse_nested_block(|i| {
|
Ok(input.parse_nested_block(|i| {
|
||||||
let shape = match shape {
|
Ok(match shape {
|
||||||
Shape::Linear => GradientKind::parse_linear(context, i, &mut compat_mode)?,
|
Shape::Linear => Self::parse_linear(context, i, repeating, &mut compat_mode)?,
|
||||||
Shape::Radial => GradientKind::parse_radial(context, i, &mut compat_mode)?,
|
Shape::Radial => Self::parse_radial(context, i, repeating, compat_mode)?,
|
||||||
};
|
})
|
||||||
let items = generic::GradientItem::parse_comma_separated(context, i)?;
|
})?)
|
||||||
Ok((shape, items))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
if items.len() < 2 {
|
|
||||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Gradient {
|
|
||||||
items,
|
|
||||||
repeating,
|
|
||||||
kind,
|
|
||||||
compat_mode,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,16 +366,21 @@ impl Gradient {
|
||||||
let ident = input.expect_ident_cloned()?;
|
let ident = input.expect_ident_cloned()?;
|
||||||
input.expect_comma()?;
|
input.expect_comma()?;
|
||||||
|
|
||||||
let (kind, reverse_stops) = match_ignore_ascii_case! { &ident,
|
Ok(match_ignore_ascii_case! { &ident,
|
||||||
"linear" => {
|
"linear" => {
|
||||||
let first = Point::parse(context, input)?;
|
let first = Point::parse(context, input)?;
|
||||||
input.expect_comma()?;
|
input.expect_comma()?;
|
||||||
let second = Point::parse(context, input)?;
|
let second = Point::parse(context, input)?;
|
||||||
|
|
||||||
let direction = LineDirection::from_points(first, second);
|
let direction = LineDirection::from_points(first, second);
|
||||||
let kind = generic::GradientKind::Linear(direction);
|
let items = Gradient::parse_webkit_gradient_stops(context, input, false)?;
|
||||||
|
|
||||||
(kind, false)
|
generic::Gradient::Linear {
|
||||||
|
direction,
|
||||||
|
items,
|
||||||
|
repeating: false,
|
||||||
|
compat_mode: GradientCompatMode::Modern,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"radial" => {
|
"radial" => {
|
||||||
let first_point = Point::parse(context, input)?;
|
let first_point = Point::parse(context, input)?;
|
||||||
|
@ -412,16 +400,28 @@ impl Gradient {
|
||||||
let rad = Circle::Radius(NonNegative(Length::from_px(radius.value)));
|
let rad = Circle::Radius(NonNegative(Length::from_px(radius.value)));
|
||||||
let shape = generic::EndingShape::Circle(rad);
|
let shape = generic::EndingShape::Circle(rad);
|
||||||
let position: Position = point.into();
|
let position: Position = point.into();
|
||||||
|
let items = Gradient::parse_webkit_gradient_stops(context, input, reverse_stops)?;
|
||||||
|
|
||||||
let kind = generic::GradientKind::Radial(shape, position);
|
generic::Gradient::Radial {
|
||||||
(kind, reverse_stops)
|
shape,
|
||||||
|
position,
|
||||||
|
items,
|
||||||
|
repeating: false,
|
||||||
|
compat_mode: GradientCompatMode::Modern,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
let e = SelectorParseErrorKind::UnexpectedIdent(ident.clone());
|
let e = SelectorParseErrorKind::UnexpectedIdent(ident.clone());
|
||||||
return Err(input.new_custom_error(e));
|
return Err(input.new_custom_error(e));
|
||||||
},
|
},
|
||||||
};
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_webkit_gradient_stops<'i, 't>(
|
||||||
|
context: &ParserContext,
|
||||||
|
input: &mut Parser<'i, 't>,
|
||||||
|
reverse_stops: bool,
|
||||||
|
) -> Result<crate::OwnedSlice<generic::GradientItem<Color, LengthPercentage>>, ParseError<'i>> {
|
||||||
let mut items = input
|
let mut items = input
|
||||||
.try(|i| {
|
.try(|i| {
|
||||||
i.expect_comma()?;
|
i.expect_comma()?;
|
||||||
|
@ -500,22 +500,29 @@ impl Gradient {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Ok(items.into())
|
||||||
Ok(generic::Gradient {
|
}
|
||||||
kind,
|
|
||||||
items: items.into(),
|
/// Not used for -webkit-gradient syntax.
|
||||||
repeating: false,
|
fn parse_stops<'i, 't>(
|
||||||
compat_mode: GradientCompatMode::Modern,
|
context: &ParserContext,
|
||||||
})
|
input: &mut Parser<'i, 't>,
|
||||||
|
) -> Result<crate::OwnedSlice<generic::GradientItem<Color, LengthPercentage>>, ParseError<'i>> {
|
||||||
|
let items = generic::GradientItem::parse_comma_separated(context, input)?;
|
||||||
|
|
||||||
|
if items.len() < 2 {
|
||||||
|
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(items)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl GradientKind {
|
|
||||||
/// Parses a linear gradient.
|
/// Parses a linear gradient.
|
||||||
/// GradientCompatMode can change during `-moz-` prefixed gradient parsing if it come across a `to` keyword.
|
/// GradientCompatMode can change during `-moz-` prefixed gradient parsing if it come across a `to` keyword.
|
||||||
fn parse_linear<'i, 't>(
|
fn parse_linear<'i, 't>(
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
|
repeating: bool,
|
||||||
compat_mode: &mut GradientCompatMode,
|
compat_mode: &mut GradientCompatMode,
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> Result<Self, ParseError<'i>> {
|
||||||
let direction = if let Ok(d) = input.try(|i| LineDirection::parse(context, i, compat_mode))
|
let direction = if let Ok(d) = input.try(|i| LineDirection::parse(context, i, compat_mode))
|
||||||
|
@ -523,23 +530,33 @@ impl GradientKind {
|
||||||
input.expect_comma()?;
|
input.expect_comma()?;
|
||||||
d
|
d
|
||||||
} else {
|
} else {
|
||||||
match *compat_mode {
|
match compat_mode {
|
||||||
GradientCompatMode::Modern => {
|
GradientCompatMode::Modern => {
|
||||||
LineDirection::Vertical(VerticalPositionKeyword::Bottom)
|
LineDirection::Vertical(VerticalPositionKeyword::Bottom)
|
||||||
},
|
},
|
||||||
_ => LineDirection::Vertical(VerticalPositionKeyword::Top),
|
_ => LineDirection::Vertical(VerticalPositionKeyword::Top),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(generic::GradientKind::Linear(direction))
|
let items = Gradient::parse_stops(context, input)?;
|
||||||
|
|
||||||
|
Ok(Gradient::Linear {
|
||||||
|
direction,
|
||||||
|
items,
|
||||||
|
repeating,
|
||||||
|
compat_mode: *compat_mode,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parses a radial gradient.
|
||||||
fn parse_radial<'i, 't>(
|
fn parse_radial<'i, 't>(
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
compat_mode: &mut GradientCompatMode,
|
repeating: bool,
|
||||||
|
compat_mode: GradientCompatMode,
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> Result<Self, ParseError<'i>> {
|
||||||
let (shape, position) = match *compat_mode {
|
let (shape, position) = match compat_mode {
|
||||||
GradientCompatMode::Modern => {
|
GradientCompatMode::Modern => {
|
||||||
let shape = input.try(|i| EndingShape::parse(context, i, *compat_mode));
|
let shape = input.try(|i| EndingShape::parse(context, i, compat_mode));
|
||||||
let position = input.try(|i| {
|
let position = input.try(|i| {
|
||||||
i.expect_ident_matching("at")?;
|
i.expect_ident_matching("at")?;
|
||||||
Position::parse(context, i)
|
Position::parse(context, i)
|
||||||
|
@ -552,7 +569,7 @@ impl GradientKind {
|
||||||
if position.is_ok() {
|
if position.is_ok() {
|
||||||
i.expect_comma()?;
|
i.expect_comma()?;
|
||||||
}
|
}
|
||||||
EndingShape::parse(context, i, *compat_mode)
|
EndingShape::parse(context, i, compat_mode)
|
||||||
});
|
});
|
||||||
(shape, position.ok())
|
(shape, position.ok())
|
||||||
},
|
},
|
||||||
|
@ -567,7 +584,16 @@ impl GradientKind {
|
||||||
});
|
});
|
||||||
|
|
||||||
let position = position.unwrap_or(Position::center());
|
let position = position.unwrap_or(Position::center());
|
||||||
Ok(generic::GradientKind::Radial(shape, position))
|
|
||||||
|
let items = Gradient::parse_stops(context, input)?;
|
||||||
|
|
||||||
|
Ok(Gradient::Radial {
|
||||||
|
shape,
|
||||||
|
position,
|
||||||
|
items,
|
||||||
|
repeating,
|
||||||
|
compat_mode,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ use style_traits::values::specified::AllowedNumericType;
|
||||||
use style_traits::{ParseError, SpecifiedValueInfo, StyleParseErrorKind};
|
use style_traits::{ParseError, SpecifiedValueInfo, StyleParseErrorKind};
|
||||||
|
|
||||||
pub use super::image::{EndingShape as GradientEndingShape, Gradient};
|
pub use super::image::{EndingShape as GradientEndingShape, Gradient};
|
||||||
pub use super::image::{GradientKind, Image};
|
pub use super::image::Image;
|
||||||
pub use crate::values::specified::calc::CalcLengthPercentage;
|
pub use crate::values::specified::calc::CalcLengthPercentage;
|
||||||
|
|
||||||
/// Number of app units per pixel
|
/// Number of app units per pixel
|
||||||
|
|
|
@ -56,7 +56,7 @@ pub use self::font::{FontVariantAlternates, FontWeight};
|
||||||
pub use self::font::{FontVariantEastAsian, FontVariationSettings};
|
pub use self::font::{FontVariantEastAsian, FontVariationSettings};
|
||||||
pub use self::font::{MozScriptLevel, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextZoom};
|
pub use self::font::{MozScriptLevel, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextZoom};
|
||||||
pub use self::image::{EndingShape as GradientEndingShape, Gradient};
|
pub use self::image::{EndingShape as GradientEndingShape, Gradient};
|
||||||
pub use self::image::{GradientKind, Image, MozImageRect};
|
pub use self::image::{Image, MozImageRect};
|
||||||
pub use self::length::{AbsoluteLength, CalcLengthPercentage, CharacterWidth};
|
pub use self::length::{AbsoluteLength, CalcLengthPercentage, CharacterWidth};
|
||||||
pub use self::length::{FontRelativeLength, Length, LengthOrNumber, NonNegativeLengthOrNumber};
|
pub use self::length::{FontRelativeLength, Length, LengthOrNumber, NonNegativeLengthOrNumber};
|
||||||
pub use self::length::{LengthOrAuto, LengthPercentage, LengthPercentageOrAuto};
|
pub use self::length::{LengthOrAuto, LengthPercentage, LengthPercentageOrAuto};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue