mirror of
https://github.com/servo/servo.git
synced 2025-06-21 07:38:59 +01:00
Auto merge of #17543 - servo:derive-all-the-things, r=emilio
Make text-shadow and box-shadow use SimpleShadow <!-- 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/17543) <!-- Reviewable:end -->
This commit is contained in:
commit
522d24d126
14 changed files with 414 additions and 434 deletions
|
@ -57,7 +57,8 @@ use style::properties::style_structs;
|
|||
use style::servo::restyle_damage::REPAINT;
|
||||
use style::values::{Either, RGBA};
|
||||
use style::values::computed::{Gradient, GradientItem, LengthOrPercentage};
|
||||
use style::values::computed::{LengthOrPercentageOrAuto, NumberOrPercentage, Position, Shadow};
|
||||
use style::values::computed::{LengthOrPercentageOrAuto, NumberOrPercentage, Position};
|
||||
use style::values::computed::effects::SimpleShadow;
|
||||
use style::values::computed::image::{EndingShape, LineDirection};
|
||||
use style::values::generics::background::BackgroundSize;
|
||||
use style::values::generics::effects::Filter;
|
||||
|
@ -525,7 +526,7 @@ pub trait FragmentDisplayListBuilding {
|
|||
state: &mut DisplayListBuildState,
|
||||
text_fragment: &ScannedTextFragmentInfo,
|
||||
stacking_relative_content_box: &Rect<Au>,
|
||||
text_shadow: Option<&Shadow>,
|
||||
text_shadow: Option<&SimpleShadow>,
|
||||
clip: &Rect<Au>);
|
||||
|
||||
/// Creates the display item for a text decoration: underline, overline, or line-through.
|
||||
|
@ -534,7 +535,7 @@ pub trait FragmentDisplayListBuilding {
|
|||
color: &RGBA,
|
||||
stacking_relative_box: &LogicalRect<Au>,
|
||||
clip: &Rect<Au>,
|
||||
blur_radius: Au);
|
||||
blur: Au);
|
||||
|
||||
/// A helper method that `build_display_list` calls to create per-fragment-type display items.
|
||||
fn build_fragment_type_specific_display_items(&mut self,
|
||||
|
@ -1351,11 +1352,14 @@ impl FragmentDisplayListBuilding for Fragment {
|
|||
clip: &Rect<Au>) {
|
||||
// NB: According to CSS-BACKGROUNDS, box shadows render in *reverse* order (front to back).
|
||||
for box_shadow in style.get_effects().box_shadow.0.iter().rev() {
|
||||
let bounds =
|
||||
shadow_bounds(&absolute_bounds.translate(&Vector2D::new(box_shadow.offset_x,
|
||||
box_shadow.offset_y)),
|
||||
box_shadow.blur_radius,
|
||||
box_shadow.spread_radius);
|
||||
let bounds = shadow_bounds(
|
||||
&absolute_bounds.translate(&Vector2D::new(
|
||||
box_shadow.base.horizontal,
|
||||
box_shadow.base.vertical,
|
||||
)),
|
||||
box_shadow.base.blur,
|
||||
box_shadow.spread,
|
||||
);
|
||||
|
||||
// TODO(pcwalton): Multiple border radii; elliptical border radii.
|
||||
let base = state.create_base_display_item(&bounds,
|
||||
|
@ -1366,10 +1370,10 @@ impl FragmentDisplayListBuilding for Fragment {
|
|||
state.add_display_item(DisplayItem::BoxShadow(box BoxShadowDisplayItem {
|
||||
base: base,
|
||||
box_bounds: *absolute_bounds,
|
||||
color: style.resolve_color(box_shadow.color).to_gfx_color(),
|
||||
offset: Vector2D::new(box_shadow.offset_x, box_shadow.offset_y),
|
||||
blur_radius: box_shadow.blur_radius,
|
||||
spread_radius: box_shadow.spread_radius,
|
||||
color: style.resolve_color(box_shadow.base.color).to_gfx_color(),
|
||||
offset: Vector2D::new(box_shadow.base.horizontal, box_shadow.base.vertical),
|
||||
blur_radius: box_shadow.base.blur,
|
||||
spread_radius: box_shadow.spread,
|
||||
border_radius: model::specified_border_radius(style.get_border()
|
||||
.border_top_left_radius,
|
||||
absolute_bounds.size).width,
|
||||
|
@ -2039,7 +2043,7 @@ impl FragmentDisplayListBuilding for Fragment {
|
|||
state: &mut DisplayListBuildState,
|
||||
text_fragment: &ScannedTextFragmentInfo,
|
||||
stacking_relative_content_box: &Rect<Au>,
|
||||
text_shadow: Option<&Shadow>,
|
||||
text_shadow: Option<&SimpleShadow>,
|
||||
clip: &Rect<Au>) {
|
||||
// TODO(emilio): Allow changing more properties by ::selection
|
||||
let text_color = if let Some(shadow) = text_shadow {
|
||||
|
@ -2051,8 +2055,10 @@ impl FragmentDisplayListBuilding for Fragment {
|
|||
} else {
|
||||
self.style().get_color().color
|
||||
};
|
||||
let offset = text_shadow.map(|s| Vector2D::new(s.offset_x, s.offset_y)).unwrap_or_else(Vector2D::zero);
|
||||
let shadow_blur_radius = text_shadow.map(|s| s.blur_radius).unwrap_or(Au(0));
|
||||
let offset = text_shadow.map_or(Vector2D::zero(), |s| {
|
||||
Vector2D::new(s.horizontal, s.vertical)
|
||||
});
|
||||
let shadow_blur_radius = text_shadow.map(|s| s.blur).unwrap_or(Au(0));
|
||||
|
||||
// Determine the orientation and cursor to use.
|
||||
let (orientation, cursor) = if self.style.writing_mode.is_vertical() {
|
||||
|
@ -2885,8 +2891,8 @@ fn position_to_offset(position: LengthOrPercentage, total_length: Au) -> f32 {
|
|||
|
||||
/// Adjusts `content_rect` as necessary for the given spread, and blur so that the resulting
|
||||
/// bounding rect contains all of a shadow's ink.
|
||||
fn shadow_bounds(content_rect: &Rect<Au>, blur_radius: Au, spread_radius: Au) -> Rect<Au> {
|
||||
let inflation = spread_radius + blur_radius * BLUR_INFLATION_FACTOR;
|
||||
fn shadow_bounds(content_rect: &Rect<Au>, blur: Au, spread: Au) -> Rect<Au> {
|
||||
let inflation = spread + blur * BLUR_INFLATION_FACTOR;
|
||||
content_rect.inflate(inflation, inflation)
|
||||
}
|
||||
|
||||
|
|
|
@ -2551,9 +2551,8 @@ impl Fragment {
|
|||
|
||||
// Box shadows cause us to draw outside our border box.
|
||||
for box_shadow in &self.style().get_effects().box_shadow.0 {
|
||||
let offset = Vector2D::new(box_shadow.offset_x, box_shadow.offset_y);
|
||||
let inflation = box_shadow.spread_radius + box_shadow.blur_radius *
|
||||
BLUR_INFLATION_FACTOR;
|
||||
let offset = Vector2D::new(box_shadow.base.horizontal, box_shadow.base.vertical);
|
||||
let inflation = box_shadow.spread + box_shadow.base.blur * BLUR_INFLATION_FACTOR;
|
||||
overflow.paint = overflow.paint.union(&border_box.translate(&offset)
|
||||
.inflate(inflation, inflation))
|
||||
}
|
||||
|
|
|
@ -7,37 +7,30 @@
|
|||
use app_units::Au;
|
||||
use gecko::values::{convert_rgba_to_nscolor, convert_nscolor_to_rgba};
|
||||
use gecko_bindings::structs::nsCSSShadowItem;
|
||||
use values::computed::{Color, Shadow};
|
||||
use values::computed::effects::SimpleShadow;
|
||||
use values::computed::Color;
|
||||
use values::computed::effects::{BoxShadow, SimpleShadow};
|
||||
|
||||
impl nsCSSShadowItem {
|
||||
/// Set this item to the given shadow value.
|
||||
pub fn set_from_shadow(&mut self, other: Shadow) {
|
||||
self.mXOffset = other.offset_x.0;
|
||||
self.mYOffset = other.offset_y.0;
|
||||
self.mRadius = other.blur_radius.0;
|
||||
self.mSpread = other.spread_radius.0;
|
||||
self.mInset = other.inset;
|
||||
if other.color.is_currentcolor() {
|
||||
// TODO handle currentColor
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=760345
|
||||
self.mHasColor = false;
|
||||
self.mColor = 0;
|
||||
} else {
|
||||
self.mHasColor = true;
|
||||
self.mColor = convert_rgba_to_nscolor(&other.color.color);
|
||||
}
|
||||
/// Sets this item from the given box shadow.
|
||||
#[inline]
|
||||
pub fn set_from_box_shadow(&mut self, shadow: BoxShadow) {
|
||||
self.set_from_simple_shadow(shadow.base);
|
||||
self.mSpread = shadow.spread.0;
|
||||
self.mInset = shadow.inset;
|
||||
}
|
||||
|
||||
/// Generate shadow value from this shadow item.
|
||||
pub fn to_shadow(&self) -> Shadow {
|
||||
Shadow {
|
||||
offset_x: Au(self.mXOffset),
|
||||
offset_y: Au(self.mYOffset),
|
||||
blur_radius: Au(self.mRadius),
|
||||
spread_radius: Au(self.mSpread),
|
||||
inset: self.mInset,
|
||||
/// Returns this item as a box shadow.
|
||||
#[inline]
|
||||
pub fn to_box_shadow(&self) -> BoxShadow {
|
||||
BoxShadow {
|
||||
base: SimpleShadow {
|
||||
color: Color::rgba(convert_nscolor_to_rgba(self.mColor)),
|
||||
horizontal: Au(self.mXOffset),
|
||||
vertical: Au(self.mYOffset),
|
||||
blur: Au(self.mRadius),
|
||||
},
|
||||
spread: Au(self.mSpread),
|
||||
inset: self.mInset,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ use std::ptr;
|
|||
use stylearc::Arc;
|
||||
use std::cmp;
|
||||
use values::{Auto, CustomIdent, Either, KeyframesName};
|
||||
use values::computed::{Filter, Shadow};
|
||||
use values::computed::effects::{BoxShadow, Filter, SimpleShadow};
|
||||
use values::specified::length::Percentage;
|
||||
use computed_values::border_style;
|
||||
|
||||
|
@ -3173,13 +3173,13 @@ fn static_assert() {
|
|||
<%self:impl_trait style_struct_name="Effects"
|
||||
skip_longhands="box-shadow clip filter">
|
||||
pub fn set_box_shadow<I>(&mut self, v: I)
|
||||
where I: IntoIterator<Item = Shadow>,
|
||||
where I: IntoIterator<Item = BoxShadow>,
|
||||
I::IntoIter: ExactSizeIterator
|
||||
{
|
||||
let v = v.into_iter();
|
||||
self.gecko.mBoxShadow.replace_with_new(v.len() as u32);
|
||||
for (servo, gecko_shadow) in v.zip(self.gecko.mBoxShadow.iter_mut()) {
|
||||
gecko_shadow.set_from_shadow(servo);
|
||||
gecko_shadow.set_from_box_shadow(servo);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3188,7 +3188,7 @@ fn static_assert() {
|
|||
}
|
||||
|
||||
pub fn clone_box_shadow(&self) -> longhands::box_shadow::computed_value::T {
|
||||
let buf = self.gecko.mBoxShadow.iter().map(|v| v.to_shadow()).collect();
|
||||
let buf = self.gecko.mBoxShadow.iter().map(|v| v.to_box_shadow()).collect();
|
||||
longhands::box_shadow::computed_value::T(buf)
|
||||
}
|
||||
|
||||
|
@ -3494,13 +3494,13 @@ fn static_assert() {
|
|||
${impl_keyword_clone('text_align', 'mTextAlign', text_align_keyword)}
|
||||
|
||||
pub fn set_text_shadow<I>(&mut self, v: I)
|
||||
where I: IntoIterator<Item = Shadow>,
|
||||
where I: IntoIterator<Item = SimpleShadow>,
|
||||
I::IntoIter: ExactSizeIterator
|
||||
{
|
||||
let v = v.into_iter();
|
||||
self.gecko.mTextShadow.replace_with_new(v.len() as u32);
|
||||
for (servo, gecko_shadow) in v.zip(self.gecko.mTextShadow.iter_mut()) {
|
||||
gecko_shadow.set_from_shadow(servo);
|
||||
gecko_shadow.set_from_simple_shadow(servo);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3509,7 +3509,7 @@ fn static_assert() {
|
|||
}
|
||||
|
||||
pub fn clone_text_shadow(&self) -> longhands::text_shadow::computed_value::T {
|
||||
let buf = self.gecko.mTextShadow.iter().map(|v| v.to_shadow()).collect();
|
||||
let buf = self.gecko.mTextShadow.iter().map(|v| v.to_simple_shadow()).collect();
|
||||
longhands::text_shadow::computed_value::T(buf)
|
||||
}
|
||||
|
||||
|
|
|
@ -19,8 +19,6 @@ use properties::longhands;
|
|||
use properties::longhands::background_size::computed_value::T as BackgroundSizeList;
|
||||
use properties::longhands::font_weight::computed_value::T as FontWeight;
|
||||
use properties::longhands::font_stretch::computed_value::T as FontStretch;
|
||||
use properties::longhands::text_shadow::computed_value::T as TextShadowList;
|
||||
use properties::longhands::box_shadow::computed_value::T as BoxShadowList;
|
||||
use properties::longhands::transform::computed_value::ComputedMatrix;
|
||||
use properties::longhands::transform::computed_value::ComputedOperation as TransformOperation;
|
||||
use properties::longhands::transform::computed_value::T as TransformList;
|
||||
|
@ -34,11 +32,14 @@ use std::cmp;
|
|||
use style_traits::ParseError;
|
||||
use super::ComputedValues;
|
||||
use values::{Auto, CSSFloat, CustomIdent, Either};
|
||||
use values::animated::effects::{Filter as AnimatedFilter, FilterList as AnimatedFilterList};
|
||||
use values::animated::effects::BoxShadowList as AnimatedBoxShadowList;
|
||||
use values::animated::effects::Filter as AnimatedFilter;
|
||||
use values::animated::effects::FilterList as AnimatedFilterList;
|
||||
use values::animated::effects::TextShadowList as AnimatedTextShadowList;
|
||||
use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
|
||||
use values::computed::{BorderCornerRadius, ClipRect};
|
||||
use values::computed::{CalcLengthOrPercentage, Color, Context, ComputedValueAsSpecified};
|
||||
use values::computed::{LengthOrPercentage, MaxLength, MozLength, Shadow, ToComputedValue};
|
||||
use values::computed::{LengthOrPercentage, MaxLength, MozLength, ToComputedValue};
|
||||
use values::generics::{SVGPaint, SVGPaintKind};
|
||||
use values::generics::border::BorderCornerRadius as GenericBorderCornerRadius;
|
||||
use values::generics::effects::Filter;
|
||||
|
@ -2832,7 +2833,8 @@ impl IntermediateColor {
|
|||
}
|
||||
}
|
||||
|
||||
fn transparent() -> Self {
|
||||
/// Returns a transparent intermediate color.
|
||||
pub fn transparent() -> Self {
|
||||
IntermediateColor {
|
||||
color: IntermediateRGBA::transparent(),
|
||||
foreground_ratio: 0.,
|
||||
|
@ -3040,179 +3042,6 @@ impl Animatable for IntermediateSVGPaintKind {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[allow(missing_docs)]
|
||||
/// Intermediate type for box-shadow and text-shadow.
|
||||
/// The difference from normal shadow type is that this type uses
|
||||
/// IntermediateColor instead of ParserColor.
|
||||
pub struct IntermediateShadow {
|
||||
pub offset_x: Au,
|
||||
pub offset_y: Au,
|
||||
pub blur_radius: Au,
|
||||
pub spread_radius: Au,
|
||||
pub color: IntermediateColor,
|
||||
pub inset: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[allow(missing_docs)]
|
||||
/// Intermediate type for box-shadow list and text-shadow list.
|
||||
pub struct IntermediateShadowList(pub Vec<IntermediateShadow>);
|
||||
|
||||
type ShadowList = Vec<Shadow>;
|
||||
|
||||
impl From<IntermediateShadowList> for ShadowList {
|
||||
fn from(shadow_list: IntermediateShadowList) -> Self {
|
||||
shadow_list.0.into_iter().map(|s| s.into()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ShadowList> for IntermediateShadowList {
|
||||
fn from(shadow_list: ShadowList) -> IntermediateShadowList {
|
||||
IntermediateShadowList(shadow_list.into_iter().map(|s| s.into()).collect())
|
||||
}
|
||||
}
|
||||
|
||||
% for ty in "Box Text".split():
|
||||
impl From<IntermediateShadowList> for ${ty}ShadowList {
|
||||
#[inline]
|
||||
fn from(shadow_list: IntermediateShadowList) -> Self {
|
||||
${ty}ShadowList(shadow_list.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<${ty}ShadowList> for IntermediateShadowList {
|
||||
#[inline]
|
||||
fn from(shadow_list: ${ty}ShadowList) -> IntermediateShadowList {
|
||||
shadow_list.0.into()
|
||||
}
|
||||
}
|
||||
% endfor
|
||||
|
||||
impl From<IntermediateShadow> for Shadow {
|
||||
fn from(shadow: IntermediateShadow) -> Shadow {
|
||||
Shadow {
|
||||
offset_x: shadow.offset_x,
|
||||
offset_y: shadow.offset_y,
|
||||
blur_radius: shadow.blur_radius,
|
||||
spread_radius: shadow.spread_radius,
|
||||
color: shadow.color.into(),
|
||||
inset: shadow.inset,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Shadow> for IntermediateShadow {
|
||||
fn from(shadow: Shadow) -> IntermediateShadow {
|
||||
IntermediateShadow {
|
||||
offset_x: shadow.offset_x,
|
||||
offset_y: shadow.offset_y,
|
||||
blur_radius: shadow.blur_radius,
|
||||
spread_radius: shadow.spread_radius,
|
||||
color: shadow.color.into(),
|
||||
inset: shadow.inset,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Animatable for IntermediateShadow {
|
||||
#[inline]
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
// It can't be interpolated if inset does not match.
|
||||
if self.inset != other.inset {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let x = self.offset_x.add_weighted(&other.offset_x, self_portion, other_portion)?;
|
||||
let y = self.offset_y.add_weighted(&other.offset_y, self_portion, other_portion)?;
|
||||
let color = self.color.add_weighted(&other.color, self_portion, other_portion)?;
|
||||
let blur = self.blur_radius.add_weighted(&other.blur_radius, self_portion, other_portion)?;
|
||||
let spread = self.spread_radius.add_weighted(&other.spread_radius, self_portion, other_portion)?;
|
||||
|
||||
Ok(IntermediateShadow {
|
||||
offset_x: x,
|
||||
offset_y: y,
|
||||
blur_radius: blur,
|
||||
spread_radius: spread,
|
||||
color: color,
|
||||
inset: self.inset,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
|
||||
self.compute_squared_distance(other).map(|sd| sd.sqrt())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
|
||||
if self.inset != other.inset {
|
||||
return Err(());
|
||||
}
|
||||
let list = [
|
||||
self.offset_x.compute_distance(&other.offset_x)?,
|
||||
self.offset_y.compute_distance(&other.offset_y)?,
|
||||
self.blur_radius.compute_distance(&other.blur_radius)?,
|
||||
self.color.compute_distance(&other.color)?,
|
||||
self.spread_radius.compute_distance(&other.spread_radius)?,
|
||||
];
|
||||
Ok(list.iter().fold(0.0f64, |sum, diff| sum + diff * diff))
|
||||
}
|
||||
}
|
||||
|
||||
/// https://drafts.csswg.org/css-transitions/#animtype-shadow-list
|
||||
impl Animatable for IntermediateShadowList {
|
||||
#[inline]
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
// The inset value must change
|
||||
let mut zero = IntermediateShadow {
|
||||
offset_x: Au(0),
|
||||
offset_y: Au(0),
|
||||
blur_radius: Au(0),
|
||||
spread_radius: Au(0),
|
||||
color: IntermediateColor::transparent(),
|
||||
inset: false,
|
||||
};
|
||||
|
||||
let max_len = cmp::max(self.0.len(), other.0.len());
|
||||
|
||||
let mut result = Vec::with_capacity(max_len);
|
||||
|
||||
for i in 0..max_len {
|
||||
let shadow = match (self.0.get(i), other.0.get(i)) {
|
||||
(Some(shadow), Some(other)) => {
|
||||
shadow.add_weighted(other, self_portion, other_portion)?
|
||||
}
|
||||
(Some(shadow), None) => {
|
||||
zero.inset = shadow.inset;
|
||||
shadow.add_weighted(&zero, self_portion, other_portion).unwrap()
|
||||
}
|
||||
(None, Some(shadow)) => {
|
||||
zero.inset = shadow.inset;
|
||||
zero.add_weighted(&shadow, self_portion, other_portion).unwrap()
|
||||
}
|
||||
(None, None) => unreachable!(),
|
||||
};
|
||||
result.push(shadow);
|
||||
}
|
||||
|
||||
Ok(IntermediateShadowList(result))
|
||||
}
|
||||
|
||||
fn add(&self, other: &Self) -> Result<Self, ()> {
|
||||
let len = self.0.len() + other.0.len();
|
||||
|
||||
let mut result = Vec::with_capacity(len);
|
||||
|
||||
result.extend(self.0.iter().cloned());
|
||||
result.extend(other.0.iter().cloned());
|
||||
|
||||
Ok(IntermediateShadowList(result))
|
||||
}
|
||||
}
|
||||
|
||||
<%
|
||||
FILTER_FUNCTIONS = [ 'Blur', 'Brightness', 'Contrast', 'Grayscale',
|
||||
'HueRotate', 'Invert', 'Opacity', 'Saturate',
|
||||
|
|
|
@ -14,24 +14,16 @@ ${helpers.predefined_type("opacity",
|
|||
flags="CREATES_STACKING_CONTEXT",
|
||||
spec="https://drafts.csswg.org/css-color/#opacity")}
|
||||
|
||||
<%helpers:vector_longhand name="box-shadow" allow_empty="True"
|
||||
animation_value_type="IntermediateShadowList"
|
||||
extra_prefixes="webkit"
|
||||
ignored_when_colors_disabled="True"
|
||||
spec="https://drafts.csswg.org/css-backgrounds/#box-shadow">
|
||||
pub type SpecifiedValue = specified::Shadow;
|
||||
|
||||
pub mod computed_value {
|
||||
use values::computed::Shadow;
|
||||
|
||||
pub type T = Shadow;
|
||||
}
|
||||
|
||||
pub fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
|
||||
-> Result<specified::Shadow, ParseError<'i>> {
|
||||
specified::Shadow::parse(context, input, false)
|
||||
}
|
||||
</%helpers:vector_longhand>
|
||||
${helpers.predefined_type(
|
||||
"box-shadow",
|
||||
"BoxShadow",
|
||||
None,
|
||||
vector=True,
|
||||
animation_value_type="AnimatedBoxShadowList",
|
||||
extra_prefixes="webkit",
|
||||
ignored_when_colors_disabled=True,
|
||||
spec="https://drafts.csswg.org/css-backgrounds/#box-shadow",
|
||||
)}
|
||||
|
||||
${helpers.predefined_type("clip",
|
||||
"ClipRectOrAuto",
|
||||
|
|
|
@ -407,21 +407,15 @@ ${helpers.predefined_type("word-spacing",
|
|||
% endif
|
||||
</%helpers:single_keyword_computed>
|
||||
|
||||
<%helpers:vector_longhand name="text-shadow" allow_empty="True"
|
||||
animation_value_type="IntermediateShadowList"
|
||||
ignored_when_colors_disabled="True"
|
||||
spec="https://drafts.csswg.org/css-backgrounds/#box-shadow">
|
||||
pub type SpecifiedValue = specified::Shadow;
|
||||
pub mod computed_value {
|
||||
use values::computed::Shadow;
|
||||
pub type T = Shadow;
|
||||
}
|
||||
|
||||
pub fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
|
||||
-> Result<specified::Shadow, ParseError<'i>> {
|
||||
specified::Shadow::parse(context, input, true)
|
||||
}
|
||||
</%helpers:vector_longhand>
|
||||
${helpers.predefined_type(
|
||||
"text-shadow",
|
||||
"SimpleShadow",
|
||||
None,
|
||||
vector=True,
|
||||
animation_value_type="AnimatedTextShadowList",
|
||||
ignored_when_colors_disabled=True,
|
||||
spec="https://drafts.csswg.org/css-text-decor-3/#text-shadow-property",
|
||||
)}
|
||||
|
||||
<%helpers:longhand name="text-emphasis-style" products="gecko" need_clone="True" boxed="True"
|
||||
animation_value_type="none"
|
||||
|
|
|
@ -4,18 +4,32 @@
|
|||
|
||||
//! Animated types for CSS values related to effects.
|
||||
|
||||
use app_units::Au;
|
||||
use properties::animated_properties::{Animatable, IntermediateColor};
|
||||
use properties::longhands::box_shadow::computed_value::T as ComputedBoxShadowList;
|
||||
use properties::longhands::filter::computed_value::T as ComputedFilterList;
|
||||
use properties::longhands::text_shadow::computed_value::T as ComputedTextShadowList;
|
||||
use std::cmp;
|
||||
#[cfg(not(feature = "gecko"))]
|
||||
use values::Impossible;
|
||||
use values::computed::{Angle, Number};
|
||||
use values::computed::effects::BoxShadow as ComputedBoxShadow;
|
||||
#[cfg(feature = "gecko")]
|
||||
use values::computed::effects::Filter as ComputedFilter;
|
||||
use values::computed::effects::SimpleShadow as ComputedSimpleShadow;
|
||||
use values::computed::length::Length;
|
||||
use values::generics::effects::BoxShadow as GenericBoxShadow;
|
||||
use values::generics::effects::Filter as GenericFilter;
|
||||
use values::generics::effects::SimpleShadow as GenericSimpleShadow;
|
||||
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
/// An animated value for the `box-shadow` property.
|
||||
pub struct BoxShadowList(pub Vec<BoxShadow>);
|
||||
|
||||
/// An animated value for a single `box-shadow`.
|
||||
pub type BoxShadow = GenericBoxShadow<IntermediateColor, Length, Length>;
|
||||
|
||||
/// An animated value for the `filter` property.
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
@ -29,9 +43,190 @@ pub type Filter = GenericFilter<Angle, Number, Length, SimpleShadow>;
|
|||
#[cfg(not(feature = "gecko"))]
|
||||
pub type Filter = GenericFilter<Angle, Number, Length, Impossible>;
|
||||
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
/// An animated value for the `box-shadow` property.
|
||||
pub struct TextShadowList(pub Vec<SimpleShadow>);
|
||||
|
||||
/// An animated value for the `drop-shadow()` filter.
|
||||
pub type SimpleShadow = GenericSimpleShadow<IntermediateColor, Length, Length>;
|
||||
|
||||
impl From<BoxShadowList> for ComputedBoxShadowList {
|
||||
fn from(list: BoxShadowList) -> Self {
|
||||
ComputedBoxShadowList(list.0.into_iter().map(|s| s.into()).collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ComputedBoxShadowList> for BoxShadowList {
|
||||
fn from(list: ComputedBoxShadowList) -> Self {
|
||||
BoxShadowList(list.0.into_iter().map(|s| s.into()).collect())
|
||||
}
|
||||
}
|
||||
|
||||
/// https://drafts.csswg.org/css-transitions/#animtype-shadow-list
|
||||
impl Animatable for BoxShadowList {
|
||||
#[inline]
|
||||
fn add_weighted(
|
||||
&self,
|
||||
other: &Self,
|
||||
self_portion: f64,
|
||||
other_portion: f64,
|
||||
) -> Result<Self, ()> {
|
||||
// The inset value must change
|
||||
let mut zero = BoxShadow {
|
||||
base: SimpleShadow {
|
||||
color: IntermediateColor::transparent(),
|
||||
horizontal: Au(0),
|
||||
vertical: Au(0),
|
||||
blur: Au(0),
|
||||
},
|
||||
spread: Au(0),
|
||||
inset: false,
|
||||
};
|
||||
|
||||
let max_len = cmp::max(self.0.len(), other.0.len());
|
||||
let mut shadows = Vec::with_capacity(max_len);
|
||||
for i in 0..max_len {
|
||||
shadows.push(match (self.0.get(i), other.0.get(i)) {
|
||||
(Some(shadow), Some(other)) => {
|
||||
shadow.add_weighted(other, self_portion, other_portion)?
|
||||
},
|
||||
(Some(shadow), None) => {
|
||||
zero.inset = shadow.inset;
|
||||
shadow.add_weighted(&zero, self_portion, other_portion)?
|
||||
},
|
||||
(None, Some(shadow)) => {
|
||||
zero.inset = shadow.inset;
|
||||
zero.add_weighted(&shadow, self_portion, other_portion)?
|
||||
},
|
||||
(None, None) => unreachable!(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(BoxShadowList(shadows))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn add(&self, other: &Self) -> Result<Self, ()> {
|
||||
Ok(BoxShadowList(
|
||||
self.0.iter().cloned().chain(other.0.iter().cloned()).collect(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TextShadowList> for ComputedTextShadowList {
|
||||
fn from(list: TextShadowList) -> Self {
|
||||
ComputedTextShadowList(list.0.into_iter().map(|s| s.into()).collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ComputedTextShadowList> for TextShadowList {
|
||||
fn from(list: ComputedTextShadowList) -> Self {
|
||||
TextShadowList(list.0.into_iter().map(|s| s.into()).collect())
|
||||
}
|
||||
}
|
||||
|
||||
/// https://drafts.csswg.org/css-transitions/#animtype-shadow-list
|
||||
impl Animatable for TextShadowList {
|
||||
#[inline]
|
||||
fn add_weighted(
|
||||
&self,
|
||||
other: &Self,
|
||||
self_portion: f64,
|
||||
other_portion: f64,
|
||||
) -> Result<Self, ()> {
|
||||
let zero = SimpleShadow {
|
||||
color: IntermediateColor::transparent(),
|
||||
horizontal: Au(0),
|
||||
vertical: Au(0),
|
||||
blur: Au(0),
|
||||
};
|
||||
|
||||
let max_len = cmp::max(self.0.len(), other.0.len());
|
||||
let mut shadows = Vec::with_capacity(max_len);
|
||||
for i in 0..max_len {
|
||||
shadows.push(match (self.0.get(i), other.0.get(i)) {
|
||||
(Some(shadow), Some(other)) => {
|
||||
shadow.add_weighted(other, self_portion, other_portion)?
|
||||
},
|
||||
(Some(shadow), None) => {
|
||||
shadow.add_weighted(&zero, self_portion, other_portion)?
|
||||
},
|
||||
(None, Some(shadow)) => {
|
||||
zero.add_weighted(&shadow, self_portion, other_portion)?
|
||||
},
|
||||
(None, None) => unreachable!(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(TextShadowList(shadows))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn add(&self, other: &Self) -> Result<Self, ()> {
|
||||
Ok(TextShadowList(
|
||||
self.0.iter().cloned().chain(other.0.iter().cloned()).collect(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ComputedBoxShadow> for BoxShadow {
|
||||
#[inline]
|
||||
fn from(shadow: ComputedBoxShadow) -> Self {
|
||||
BoxShadow {
|
||||
base: shadow.base.into(),
|
||||
spread: shadow.spread,
|
||||
inset: shadow.inset,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BoxShadow> for ComputedBoxShadow {
|
||||
#[inline]
|
||||
fn from(shadow: BoxShadow) -> Self {
|
||||
ComputedBoxShadow {
|
||||
base: shadow.base.into(),
|
||||
spread: shadow.spread,
|
||||
inset: shadow.inset,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Animatable for BoxShadow {
|
||||
#[inline]
|
||||
fn add_weighted(
|
||||
&self,
|
||||
other: &Self,
|
||||
self_portion: f64,
|
||||
other_portion: f64,
|
||||
) -> Result<Self, ()> {
|
||||
if self.inset != other.inset {
|
||||
return Err(());
|
||||
}
|
||||
Ok(BoxShadow {
|
||||
base: self.base.add_weighted(&other.base, self_portion, other_portion)?,
|
||||
spread: self.spread.add_weighted(&other.spread, self_portion, other_portion)?,
|
||||
inset: self.inset,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
|
||||
self.compute_squared_distance(other).map(|sd| sd.sqrt())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
|
||||
if self.inset != other.inset {
|
||||
return Err(());
|
||||
}
|
||||
Ok(
|
||||
self.base.compute_squared_distance(&other.base)? +
|
||||
self.spread.compute_squared_distance(&other.spread)?,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ComputedFilterList> for FilterList {
|
||||
#[cfg(not(feature = "gecko"))]
|
||||
#[inline]
|
||||
|
@ -77,7 +272,6 @@ impl From<ComputedFilter> for Filter {
|
|||
GenericFilter::DropShadow(shadow) => {
|
||||
GenericFilter::DropShadow(shadow.into())
|
||||
},
|
||||
#[cfg(feature = "gecko")]
|
||||
GenericFilter::Url(url) => GenericFilter::Url(url),
|
||||
}
|
||||
}
|
||||
|
@ -100,7 +294,6 @@ impl From<Filter> for ComputedFilter {
|
|||
GenericFilter::DropShadow(shadow) => {
|
||||
GenericFilter::DropShadow(shadow.into())
|
||||
},
|
||||
#[cfg(feature = "gecko")]
|
||||
GenericFilter::Url(url) => GenericFilter::Url(url.clone())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,9 +9,13 @@ use values::Impossible;
|
|||
use values::computed::{Angle, Number};
|
||||
use values::computed::color::Color;
|
||||
use values::computed::length::Length;
|
||||
use values::generics::effects::BoxShadow as GenericBoxShadow;
|
||||
use values::generics::effects::Filter as GenericFilter;
|
||||
use values::generics::effects::SimpleShadow as GenericSimpleShadow;
|
||||
|
||||
/// A computed value for a single shadow of the `box-shadow` property.
|
||||
pub type BoxShadow = GenericBoxShadow<Color, Length, Length>;
|
||||
|
||||
/// A computed value for a single `filter`.
|
||||
#[cfg(feature = "gecko")]
|
||||
pub type Filter = GenericFilter<Angle, Number, Length, SimpleShadow>;
|
||||
|
|
|
@ -9,7 +9,6 @@ use context::QuirksMode;
|
|||
use euclid::Size2D;
|
||||
use font_metrics::FontMetricsProvider;
|
||||
use media_queries::Device;
|
||||
use num_traits::Zero;
|
||||
#[cfg(feature = "gecko")]
|
||||
use properties;
|
||||
use properties::{ComputedValues, StyleBuilder};
|
||||
|
@ -28,7 +27,7 @@ pub use self::background::BackgroundSize;
|
|||
pub use self::border::{BorderImageSlice, BorderImageWidth, BorderImageSideWidth};
|
||||
pub use self::border::{BorderRadius, BorderCornerRadius};
|
||||
pub use self::color::{Color, RGBAColor};
|
||||
pub use self::effects::Filter;
|
||||
pub use self::effects::{BoxShadow, Filter, SimpleShadow};
|
||||
pub use self::flex::FlexBasis;
|
||||
pub use self::image::{Gradient, GradientItem, ImageLayer, LineDirection, Image, ImageRect};
|
||||
#[cfg(feature = "gecko")]
|
||||
|
@ -412,38 +411,6 @@ impl ComputedValueAsSpecified for specified::AlignJustifyContent {}
|
|||
impl ComputedValueAsSpecified for specified::AlignJustifySelf {}
|
||||
impl ComputedValueAsSpecified for specified::BorderStyle {}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[allow(missing_docs)]
|
||||
pub struct Shadow {
|
||||
pub offset_x: Au,
|
||||
pub offset_y: Au,
|
||||
pub blur_radius: Au,
|
||||
pub spread_radius: Au,
|
||||
pub color: Color,
|
||||
pub inset: bool,
|
||||
}
|
||||
|
||||
impl ToCss for Shadow {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
if self.inset {
|
||||
dest.write_str("inset ")?;
|
||||
}
|
||||
self.offset_x.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
self.offset_y.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
self.blur_radius.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
if self.spread_radius != Au::zero() {
|
||||
self.spread_radius.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
}
|
||||
self.color.to_css(dest)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// A `<number>` value.
|
||||
pub type Number = CSSFloat;
|
||||
|
||||
|
|
|
@ -4,9 +4,23 @@
|
|||
|
||||
//! Generic types for CSS values related to effects.
|
||||
|
||||
use std::fmt;
|
||||
use style_traits::values::{SequenceWriter, ToCss};
|
||||
#[cfg(feature = "gecko")]
|
||||
use values::specified::url::SpecifiedUrl;
|
||||
|
||||
/// A generic value for a single `box-shadow`.
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
|
||||
pub struct BoxShadow<Color, SizeLength, ShapeLength> {
|
||||
/// The base shadow.
|
||||
pub base: SimpleShadow<Color, SizeLength, ShapeLength>,
|
||||
/// The spread radius.
|
||||
pub spread: ShapeLength,
|
||||
/// Whether this is an inset box shadow.
|
||||
pub inset: bool,
|
||||
}
|
||||
|
||||
/// A generic value for a single `filter`.
|
||||
#[cfg_attr(feature = "servo", derive(Deserialize, HeapSizeOf, Serialize))]
|
||||
#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToComputedValue, ToCss)]
|
||||
|
@ -62,3 +76,25 @@ pub struct SimpleShadow<Color, SizeLength, ShapeLength> {
|
|||
/// Blur radius.
|
||||
pub blur: ShapeLength,
|
||||
}
|
||||
|
||||
impl<Color, SizeLength, ShapeLength> ToCss for BoxShadow<Color, SizeLength, ShapeLength>
|
||||
where
|
||||
Color: ToCss,
|
||||
SizeLength: ToCss,
|
||||
ShapeLength: ToCss,
|
||||
{
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
||||
where
|
||||
W: fmt::Write,
|
||||
{
|
||||
{
|
||||
let mut writer = SequenceWriter::new(&mut *dest, " ");
|
||||
writer.item(&self.base)?;
|
||||
writer.item(&self.spread)?;
|
||||
}
|
||||
if self.inset {
|
||||
dest.write_str(" inset")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,11 +6,13 @@
|
|||
|
||||
use cssparser::{BasicParseError, Parser, Token};
|
||||
use parser::{Parse, ParserContext};
|
||||
use style_traits::ParseError;
|
||||
use style_traits::{ParseError, StyleParseError};
|
||||
#[cfg(not(feature = "gecko"))]
|
||||
use values::Impossible;
|
||||
use values::computed::{Context, Number as ComputedNumber, ToComputedValue};
|
||||
use values::computed::effects::BoxShadow as ComputedBoxShadow;
|
||||
use values::computed::effects::SimpleShadow as ComputedSimpleShadow;
|
||||
use values::generics::effects::BoxShadow as GenericBoxShadow;
|
||||
use values::generics::effects::Filter as GenericFilter;
|
||||
use values::generics::effects::SimpleShadow as GenericSimpleShadow;
|
||||
use values::specified::{Angle, Percentage};
|
||||
|
@ -19,6 +21,9 @@ use values::specified::length::Length;
|
|||
#[cfg(feature = "gecko")]
|
||||
use values::specified::url::SpecifiedUrl;
|
||||
|
||||
/// A specified value for a single shadow of the `box-shadow` property.
|
||||
pub type BoxShadow = GenericBoxShadow<Option<Color>, Length, Option<Length>>;
|
||||
|
||||
/// A specified value for a single `filter`.
|
||||
#[cfg(feature = "gecko")]
|
||||
pub type Filter = GenericFilter<Angle, Factor, Length, SimpleShadow>;
|
||||
|
@ -42,6 +47,85 @@ pub enum Factor {
|
|||
/// A specified value for the `drop-shadow()` filter.
|
||||
pub type SimpleShadow = GenericSimpleShadow<Option<Color>, Length, Option<Length>>;
|
||||
|
||||
impl Parse for BoxShadow {
|
||||
fn parse<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
let mut lengths = None;
|
||||
let mut color = None;
|
||||
let mut inset = false;
|
||||
|
||||
loop {
|
||||
if !inset {
|
||||
if input.try(|input| input.expect_ident_matching("inset")).is_ok() {
|
||||
inset = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if lengths.is_none() {
|
||||
let value = input.try::<_, _, ParseError>(|i| {
|
||||
let horizontal = Length::parse(context, i)?;
|
||||
let vertical = Length::parse(context, i)?;
|
||||
let (blur, spread) = match i.try::<_, _, ParseError>(|i| Length::parse_non_negative(context, i)) {
|
||||
Ok(blur) => {
|
||||
let spread = i.try(|i| Length::parse(context, i)).ok();
|
||||
(Some(blur), spread)
|
||||
},
|
||||
Err(_) => (None, None),
|
||||
};
|
||||
Ok((horizontal, vertical, blur, spread))
|
||||
});
|
||||
if let Ok(value) = value {
|
||||
lengths = Some(value);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if color.is_none() {
|
||||
if let Ok(value) = input.try(|i| Color::parse(context, i)) {
|
||||
color = Some(value);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
let lengths = lengths.ok_or(StyleParseError::UnspecifiedError)?;
|
||||
Ok(BoxShadow {
|
||||
base: SimpleShadow {
|
||||
color: color,
|
||||
horizontal: lengths.0,
|
||||
vertical: lengths.1,
|
||||
blur: lengths.2,
|
||||
},
|
||||
spread: lengths.3,
|
||||
inset: inset,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToComputedValue for BoxShadow {
|
||||
type ComputedValue = ComputedBoxShadow;
|
||||
|
||||
#[inline]
|
||||
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
||||
ComputedBoxShadow {
|
||||
base: self.base.to_computed_value(context),
|
||||
spread: self.spread.as_ref().unwrap_or(&Length::zero()).to_computed_value(context),
|
||||
inset: self.inset,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_computed_value(computed: &ComputedBoxShadow) -> Self {
|
||||
BoxShadow {
|
||||
base: ToComputedValue::from_computed_value(&computed.base),
|
||||
spread: Some(ToComputedValue::from_computed_value(&computed.spread)),
|
||||
inset: computed.inset,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for Filter {
|
||||
#[inline]
|
||||
fn parse<'i, 't>(
|
||||
|
|
|
@ -19,8 +19,7 @@ use std::fmt;
|
|||
use style_traits::{ToCss, ParseError, StyleParseError};
|
||||
use style_traits::values::specified::AllowedNumericType;
|
||||
use super::{Auto, CSSFloat, CSSInteger, Either, None_};
|
||||
use super::computed::{self, Context};
|
||||
use super::computed::{Shadow as ComputedShadow, ToComputedValue};
|
||||
use super::computed::{self, Context, ToComputedValue};
|
||||
use super::generics::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize};
|
||||
use super::generics::grid::TrackList as GenericTrackList;
|
||||
use values::computed::ComputedValueAsSpecified;
|
||||
|
@ -33,7 +32,7 @@ pub use self::background::BackgroundSize;
|
|||
pub use self::border::{BorderCornerRadius, BorderImageSlice, BorderImageWidth};
|
||||
pub use self::border::{BorderImageSideWidth, BorderRadius, BorderSideWidth};
|
||||
pub use self::color::{Color, RGBAColor};
|
||||
pub use self::effects::Filter;
|
||||
pub use self::effects::{BoxShadow, Filter, SimpleShadow};
|
||||
pub use self::flex::FlexBasis;
|
||||
#[cfg(feature = "gecko")]
|
||||
pub use self::gecko::ScrollSnapPoint;
|
||||
|
@ -690,130 +689,6 @@ pub type TrackList = GenericTrackList<TrackSizeOrRepeat>;
|
|||
/// `<track-list> | none`
|
||||
pub type TrackListOrNone = Either<TrackList, None_>;
|
||||
|
||||
#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[allow(missing_docs)]
|
||||
pub struct Shadow {
|
||||
pub offset_x: Length,
|
||||
pub offset_y: Length,
|
||||
pub blur_radius: Length,
|
||||
pub spread_radius: Length,
|
||||
pub color: Option<Color>,
|
||||
pub inset: bool,
|
||||
}
|
||||
|
||||
impl ToComputedValue for Shadow {
|
||||
type ComputedValue = ComputedShadow;
|
||||
|
||||
#[inline]
|
||||
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
||||
ComputedShadow {
|
||||
offset_x: self.offset_x.to_computed_value(context),
|
||||
offset_y: self.offset_y.to_computed_value(context),
|
||||
blur_radius: self.blur_radius.to_computed_value(context),
|
||||
spread_radius: self.spread_radius.to_computed_value(context),
|
||||
color: self.color.as_ref().unwrap_or(&Color::CurrentColor)
|
||||
.to_computed_value(context),
|
||||
inset: self.inset,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_computed_value(computed: &ComputedShadow) -> Self {
|
||||
Shadow {
|
||||
offset_x: ToComputedValue::from_computed_value(&computed.offset_x),
|
||||
offset_y: ToComputedValue::from_computed_value(&computed.offset_y),
|
||||
blur_radius: ToComputedValue::from_computed_value(&computed.blur_radius),
|
||||
spread_radius: ToComputedValue::from_computed_value(&computed.spread_radius),
|
||||
color: Some(ToComputedValue::from_computed_value(&computed.color)),
|
||||
inset: computed.inset,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCss for Shadow {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
if self.inset {
|
||||
dest.write_str("inset ")?;
|
||||
}
|
||||
self.offset_x.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
self.offset_y.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
self.blur_radius.to_css(dest)?;
|
||||
if self.spread_radius != Length::zero() {
|
||||
dest.write_str(" ")?;
|
||||
self.spread_radius.to_css(dest)?;
|
||||
}
|
||||
if let Some(ref color) = self.color {
|
||||
dest.write_str(" ")?;
|
||||
color.to_css(dest)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Shadow {
|
||||
// disable_spread_and_inset is for filter: drop-shadow(...)
|
||||
#[allow(missing_docs)]
|
||||
pub fn parse<'i, 't>(context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
disable_spread_and_inset: bool)
|
||||
-> Result<Shadow, ParseError<'i>> {
|
||||
let mut lengths = [Length::zero(), Length::zero(), Length::zero(), Length::zero()];
|
||||
let mut lengths_parsed = false;
|
||||
let mut color = None;
|
||||
let mut inset = false;
|
||||
|
||||
loop {
|
||||
if !inset && !disable_spread_and_inset {
|
||||
if input.try(|input| input.expect_ident_matching("inset")).is_ok() {
|
||||
inset = true;
|
||||
continue
|
||||
}
|
||||
}
|
||||
if !lengths_parsed {
|
||||
if let Ok(value) = input.try(|i| Length::parse(context, i)) {
|
||||
lengths[0] = value;
|
||||
lengths[1] = Length::parse(context, input)?;
|
||||
if let Ok(value) = input.try(|i| Length::parse_non_negative(context, i)) {
|
||||
lengths[2] = value;
|
||||
if !disable_spread_and_inset {
|
||||
if let Ok(value) = input.try(|i| Length::parse(context, i)) {
|
||||
lengths[3] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
lengths_parsed = true;
|
||||
continue
|
||||
}
|
||||
}
|
||||
if color.is_none() {
|
||||
if let Ok(value) = input.try(|i| Color::parse(context, i)) {
|
||||
color = Some(value);
|
||||
continue
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// Lengths must be specified.
|
||||
if !lengths_parsed {
|
||||
return Err(StyleParseError::UnspecifiedError.into())
|
||||
}
|
||||
|
||||
debug_assert!(!disable_spread_and_inset || lengths[3] == Length::zero());
|
||||
Ok(Shadow {
|
||||
offset_x: lengths[0].take(),
|
||||
offset_y: lengths[1].take(),
|
||||
blur_radius: lengths[2].take(),
|
||||
spread_radius: lengths[3].take(),
|
||||
color: color,
|
||||
inset: inset,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
no_viewport_percentage!(SVGPaint);
|
||||
|
||||
/// Specified SVG Paint value
|
||||
|
|
|
@ -1234,15 +1234,23 @@ mod shorthand_serialization {
|
|||
|
||||
mod effects {
|
||||
pub use super::*;
|
||||
pub use style::properties::longhands::box_shadow::SpecifiedValue as BoxShadow;
|
||||
pub use style::values::specified::Shadow;
|
||||
pub use style::properties::longhands::box_shadow::SpecifiedValue as BoxShadowList;
|
||||
pub use style::values::specified::effects::{BoxShadow, SimpleShadow};
|
||||
|
||||
#[test]
|
||||
fn box_shadow_should_serialize_correctly() {
|
||||
let mut properties = Vec::new();
|
||||
let shadow_val = Shadow { offset_x: Length::from_px(1f32), offset_y: Length::from_px(2f32),
|
||||
blur_radius: Length::from_px(3f32), spread_radius: Length::from_px(4f32), color: None, inset: false };
|
||||
let shadow_decl = BoxShadow(vec![shadow_val]);
|
||||
let shadow_val = BoxShadow {
|
||||
base: SimpleShadow {
|
||||
color: None,
|
||||
horizontal: Length::from_px(1f32),
|
||||
vertical: Length::from_px(2f32),
|
||||
blur: Some(Length::from_px(3f32)),
|
||||
},
|
||||
spread: Some(Length::from_px(4f32)),
|
||||
inset: false,
|
||||
};
|
||||
let shadow_decl = BoxShadowList(vec![shadow_val]);
|
||||
properties.push(PropertyDeclaration::BoxShadow(shadow_decl));
|
||||
let shadow_css = "box-shadow: 1px 2px 3px 4px;";
|
||||
let shadow = parse(|c, i| Ok(parse_property_declaration_list(c, i)), shadow_css).unwrap();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue