mirror of
https://github.com/servo/servo.git
synced 2025-07-28 09:40:33 +01:00
Make text-shadow reuse Shadow directly.
This commit is contained in:
parent
ccd7022457
commit
3e47e6eab2
5 changed files with 194 additions and 340 deletions
|
@ -51,7 +51,6 @@ use style::computed_values::{background_attachment, background_clip, background_
|
||||||
use style::computed_values::{background_repeat, border_style, cursor};
|
use style::computed_values::{background_repeat, border_style, cursor};
|
||||||
use style::computed_values::{image_rendering, overflow_x, pointer_events, position, visibility};
|
use style::computed_values::{image_rendering, overflow_x, pointer_events, position, visibility};
|
||||||
use style::computed_values::filter::Filter;
|
use style::computed_values::filter::Filter;
|
||||||
use style::computed_values::text_shadow::TextShadow;
|
|
||||||
use style::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize, WritingMode};
|
use style::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize, WritingMode};
|
||||||
use style::properties::{self, ServoComputedValues};
|
use style::properties::{self, ServoComputedValues};
|
||||||
use style::properties::longhands::border_image_repeat::computed_value::RepeatKeyword;
|
use style::properties::longhands::border_image_repeat::computed_value::RepeatKeyword;
|
||||||
|
@ -59,7 +58,7 @@ use style::properties::style_structs;
|
||||||
use style::servo::restyle_damage::REPAINT;
|
use style::servo::restyle_damage::REPAINT;
|
||||||
use style::values::{Either, RGBA};
|
use style::values::{Either, RGBA};
|
||||||
use style::values::computed::{Gradient, GradientItem, LengthOrPercentage};
|
use style::values::computed::{Gradient, GradientItem, LengthOrPercentage};
|
||||||
use style::values::computed::{LengthOrPercentageOrAuto, NumberOrPercentage, Position};
|
use style::values::computed::{LengthOrPercentageOrAuto, NumberOrPercentage, Position, Shadow};
|
||||||
use style::values::computed::image::{EndingShape, LineDirection};
|
use style::values::computed::image::{EndingShape, LineDirection};
|
||||||
use style::values::generics::background::BackgroundSize;
|
use style::values::generics::background::BackgroundSize;
|
||||||
use style::values::generics::image::{Circle, Ellipse, EndingShape as GenericEndingShape};
|
use style::values::generics::image::{Circle, Ellipse, EndingShape as GenericEndingShape};
|
||||||
|
@ -504,7 +503,7 @@ pub trait FragmentDisplayListBuilding {
|
||||||
state: &mut DisplayListBuildState,
|
state: &mut DisplayListBuildState,
|
||||||
text_fragment: &ScannedTextFragmentInfo,
|
text_fragment: &ScannedTextFragmentInfo,
|
||||||
stacking_relative_content_box: &Rect<Au>,
|
stacking_relative_content_box: &Rect<Au>,
|
||||||
text_shadow: Option<&TextShadow>,
|
text_shadow: Option<&Shadow>,
|
||||||
clip: &Rect<Au>);
|
clip: &Rect<Au>);
|
||||||
|
|
||||||
/// Creates the display item for a text decoration: underline, overline, or line-through.
|
/// Creates the display item for a text decoration: underline, overline, or line-through.
|
||||||
|
@ -1949,7 +1948,7 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||||
state: &mut DisplayListBuildState,
|
state: &mut DisplayListBuildState,
|
||||||
text_fragment: &ScannedTextFragmentInfo,
|
text_fragment: &ScannedTextFragmentInfo,
|
||||||
stacking_relative_content_box: &Rect<Au>,
|
stacking_relative_content_box: &Rect<Au>,
|
||||||
text_shadow: Option<&TextShadow>,
|
text_shadow: Option<&Shadow>,
|
||||||
clip: &Rect<Au>) {
|
clip: &Rect<Au>) {
|
||||||
// TODO(emilio): Allow changing more properties by ::selection
|
// TODO(emilio): Allow changing more properties by ::selection
|
||||||
let text_color = if let Some(shadow) = text_shadow {
|
let text_color = if let Some(shadow) = text_shadow {
|
||||||
|
|
|
@ -64,7 +64,7 @@ use std::mem::{forget, transmute, zeroed};
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use stylearc::Arc;
|
use stylearc::Arc;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use values::computed::ToComputedValue;
|
use values::computed::{Shadow, ToComputedValue};
|
||||||
use values::{Either, Auto, KeyframesName};
|
use values::{Either, Auto, KeyframesName};
|
||||||
use computed_values::border_style;
|
use computed_values::border_style;
|
||||||
|
|
||||||
|
@ -3310,7 +3310,7 @@ fn static_assert() {
|
||||||
<%self:impl_trait style_struct_name="Effects"
|
<%self:impl_trait style_struct_name="Effects"
|
||||||
skip_longhands="box-shadow clip filter">
|
skip_longhands="box-shadow clip filter">
|
||||||
pub fn set_box_shadow<I>(&mut self, v: I)
|
pub fn set_box_shadow<I>(&mut self, v: I)
|
||||||
where I: IntoIterator<Item = longhands::box_shadow::computed_value::single_value::T>,
|
where I: IntoIterator<Item = Shadow>,
|
||||||
I::IntoIter: ExactSizeIterator
|
I::IntoIter: ExactSizeIterator
|
||||||
{
|
{
|
||||||
let v = v.into_iter();
|
let v = v.into_iter();
|
||||||
|
@ -3344,7 +3344,7 @@ fn static_assert() {
|
||||||
|
|
||||||
pub fn clone_box_shadow(&self) -> longhands::box_shadow::computed_value::T {
|
pub fn clone_box_shadow(&self) -> longhands::box_shadow::computed_value::T {
|
||||||
let buf = self.gecko.mBoxShadow.iter().map(|shadow| {
|
let buf = self.gecko.mBoxShadow.iter().map(|shadow| {
|
||||||
longhands::box_shadow::single_value::computed_value::T {
|
Shadow {
|
||||||
offset_x: Au(shadow.mXOffset),
|
offset_x: Au(shadow.mXOffset),
|
||||||
offset_y: Au(shadow.mYOffset),
|
offset_y: Au(shadow.mYOffset),
|
||||||
blur_radius: Au(shadow.mRadius),
|
blur_radius: Au(shadow.mRadius),
|
||||||
|
@ -3617,12 +3617,15 @@ fn static_assert() {
|
||||||
${impl_keyword('text_align', 'mTextAlign', text_align_keyword, need_clone=False)}
|
${impl_keyword('text_align', 'mTextAlign', text_align_keyword, need_clone=False)}
|
||||||
${impl_keyword_clone('text_align', 'mTextAlign', text_align_keyword)}
|
${impl_keyword_clone('text_align', 'mTextAlign', text_align_keyword)}
|
||||||
|
|
||||||
pub fn set_text_shadow(&mut self, v: longhands::text_shadow::computed_value::T) {
|
pub fn set_text_shadow<I>(&mut self, v: I)
|
||||||
self.gecko.mTextShadow.replace_with_new(v.0.len() as u32);
|
where I: IntoIterator<Item = Shadow>,
|
||||||
|
I::IntoIter: ExactSizeIterator
|
||||||
|
{
|
||||||
|
let v = v.into_iter();
|
||||||
|
|
||||||
for (servo, gecko_shadow) in v.0.into_iter()
|
self.gecko.mTextShadow.replace_with_new(v.len() as u32);
|
||||||
.zip(self.gecko.mTextShadow.iter_mut()) {
|
|
||||||
|
|
||||||
|
for (servo, gecko_shadow) in v.zip(self.gecko.mTextShadow.iter_mut()) {
|
||||||
gecko_shadow.mXOffset = servo.offset_x.0;
|
gecko_shadow.mXOffset = servo.offset_x.0;
|
||||||
gecko_shadow.mYOffset = servo.offset_y.0;
|
gecko_shadow.mYOffset = servo.offset_y.0;
|
||||||
gecko_shadow.mRadius = servo.blur_radius.0;
|
gecko_shadow.mRadius = servo.blur_radius.0;
|
||||||
|
@ -3645,15 +3648,15 @@ fn static_assert() {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clone_text_shadow(&self) -> longhands::text_shadow::computed_value::T {
|
pub fn clone_text_shadow(&self) -> longhands::text_shadow::computed_value::T {
|
||||||
|
|
||||||
let buf = self.gecko.mTextShadow.iter().map(|shadow| {
|
let buf = self.gecko.mTextShadow.iter().map(|shadow| {
|
||||||
longhands::text_shadow::computed_value::TextShadow {
|
Shadow {
|
||||||
offset_x: Au(shadow.mXOffset),
|
offset_x: Au(shadow.mXOffset),
|
||||||
offset_y: Au(shadow.mYOffset),
|
offset_y: Au(shadow.mYOffset),
|
||||||
blur_radius: Au(shadow.mRadius),
|
blur_radius: Au(shadow.mRadius),
|
||||||
|
spread_radius: Au(0),
|
||||||
color: Color::RGBA(convert_nscolor_to_rgba(shadow.mColor)),
|
color: Color::RGBA(convert_nscolor_to_rgba(shadow.mColor)),
|
||||||
|
inset: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
}).collect();
|
}).collect();
|
||||||
longhands::text_shadow::computed_value::T(buf)
|
longhands::text_shadow::computed_value::T(buf)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,7 @@ use properties::longhands::background_size::computed_value::T as BackgroundSizeL
|
||||||
use properties::longhands::font_weight::computed_value::T as FontWeight;
|
use properties::longhands::font_weight::computed_value::T as FontWeight;
|
||||||
use properties::longhands::font_stretch::computed_value::T as FontStretch;
|
use properties::longhands::font_stretch::computed_value::T as FontStretch;
|
||||||
use properties::longhands::text_shadow::computed_value::T as TextShadowList;
|
use properties::longhands::text_shadow::computed_value::T as TextShadowList;
|
||||||
use properties::longhands::text_shadow::computed_value::TextShadow;
|
|
||||||
use properties::longhands::box_shadow::computed_value::T as BoxShadowList;
|
use properties::longhands::box_shadow::computed_value::T as BoxShadowList;
|
||||||
use properties::longhands::box_shadow::single_value::computed_value::T as BoxShadow;
|
|
||||||
use properties::longhands::transform::computed_value::ComputedMatrix;
|
use properties::longhands::transform::computed_value::ComputedMatrix;
|
||||||
use properties::longhands::transform::computed_value::ComputedOperation as TransformOperation;
|
use properties::longhands::transform::computed_value::ComputedOperation as TransformOperation;
|
||||||
use properties::longhands::transform::computed_value::T as TransformList;
|
use properties::longhands::transform::computed_value::T as TransformList;
|
||||||
|
@ -40,7 +38,7 @@ use values::{Auto, Either};
|
||||||
use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
|
use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
|
||||||
use values::computed::{BorderCornerRadius, ClipRect};
|
use values::computed::{BorderCornerRadius, ClipRect};
|
||||||
use values::computed::{CalcLengthOrPercentage, Context, ComputedValueAsSpecified};
|
use values::computed::{CalcLengthOrPercentage, Context, ComputedValueAsSpecified};
|
||||||
use values::computed::{LengthOrPercentage, MaxLength, MozLength, ToComputedValue};
|
use values::computed::{LengthOrPercentage, MaxLength, MozLength, Shadow, ToComputedValue};
|
||||||
use values::generics::{SVGPaint, SVGPaintKind};
|
use values::generics::{SVGPaint, SVGPaintKind};
|
||||||
use values::generics::border::BorderCornerRadius as GenericBorderCornerRadius;
|
use values::generics::border::BorderCornerRadius as GenericBorderCornerRadius;
|
||||||
use values::generics::position as generic_position;
|
use values::generics::position as generic_position;
|
||||||
|
@ -1457,132 +1455,6 @@ impl Animatable for ClipRect {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
<%def name="impl_animatable_for_shadow(item, transparent_color)">
|
|
||||||
impl Animatable for ${item} {
|
|
||||||
#[inline]
|
|
||||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
||||||
% if "Box" in item:
|
|
||||||
// It can't be interpolated if inset does not match.
|
|
||||||
if self.inset != other.inset {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
% endif
|
|
||||||
|
|
||||||
let x = try!(self.offset_x.add_weighted(&other.offset_x, self_portion, other_portion));
|
|
||||||
let y = try!(self.offset_y.add_weighted(&other.offset_y, self_portion, other_portion));
|
|
||||||
let color = try!(self.color.add_weighted(&other.color, self_portion, other_portion));
|
|
||||||
let blur = try!(self.blur_radius.add_weighted(&other.blur_radius,
|
|
||||||
self_portion, other_portion));
|
|
||||||
% if "Box" in item:
|
|
||||||
let spread = try!(self.spread_radius.add_weighted(&other.spread_radius,
|
|
||||||
self_portion, other_portion));
|
|
||||||
% endif
|
|
||||||
|
|
||||||
Ok(${item} {
|
|
||||||
offset_x: x,
|
|
||||||
offset_y: y,
|
|
||||||
blur_radius: blur,
|
|
||||||
color: color,
|
|
||||||
% if "Box" in item:
|
|
||||||
spread_radius: spread,
|
|
||||||
inset: self.inset,
|
|
||||||
% endif
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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 "Box" in item:
|
|
||||||
if self.inset != other.inset {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
% endif
|
|
||||||
let list = [ try!(self.offset_x.compute_distance(&other.offset_x)),
|
|
||||||
try!(self.offset_y.compute_distance(&other.offset_y)),
|
|
||||||
try!(self.blur_radius.compute_distance(&other.blur_radius)),
|
|
||||||
try!(self.color.compute_distance(&other.color)),
|
|
||||||
% if "Box" in item:
|
|
||||||
try!(self.spread_radius.compute_distance(&other.spread_radius)),
|
|
||||||
% endif
|
|
||||||
];
|
|
||||||
Ok(list.iter().fold(0.0f64, |sum, diff| sum + diff * diff))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-transitions/#animtype-shadow-list
|
|
||||||
impl Animatable for ${item}List {
|
|
||||||
#[inline]
|
|
||||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
||||||
// The inset value must change
|
|
||||||
% if "Box" in item:
|
|
||||||
let mut zero = ${item} {
|
|
||||||
% else:
|
|
||||||
let zero = ${item} {
|
|
||||||
% endif
|
|
||||||
offset_x: Au(0),
|
|
||||||
offset_y: Au(0),
|
|
||||||
blur_radius: Au(0),
|
|
||||||
color: ${transparent_color},
|
|
||||||
% if "Box" in item:
|
|
||||||
spread_radius: Au(0),
|
|
||||||
inset: false,
|
|
||||||
% endif
|
|
||||||
};
|
|
||||||
|
|
||||||
let max_len = cmp::max(self.0.len(), other.0.len());
|
|
||||||
|
|
||||||
let mut result = if max_len > 1 {
|
|
||||||
SmallVec::from_vec(Vec::with_capacity(max_len))
|
|
||||||
} else {
|
|
||||||
SmallVec::new()
|
|
||||||
};
|
|
||||||
|
|
||||||
for i in 0..max_len {
|
|
||||||
let shadow = match (self.0.get(i), other.0.get(i)) {
|
|
||||||
(Some(shadow), Some(other))
|
|
||||||
=> try!(shadow.add_weighted(other, self_portion, other_portion)),
|
|
||||||
(Some(shadow), None) => {
|
|
||||||
% if "Box" in item:
|
|
||||||
zero.inset = shadow.inset;
|
|
||||||
% endif
|
|
||||||
shadow.add_weighted(&zero, self_portion, other_portion).unwrap()
|
|
||||||
}
|
|
||||||
(None, Some(shadow)) => {
|
|
||||||
% if "Box" in item:
|
|
||||||
zero.inset = shadow.inset;
|
|
||||||
% endif
|
|
||||||
zero.add_weighted(&shadow, self_portion, other_portion).unwrap()
|
|
||||||
}
|
|
||||||
(None, None) => unreachable!(),
|
|
||||||
};
|
|
||||||
result.push(shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(${item}List(result))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add(&self, other: &Self) -> Result<Self, ()> {
|
|
||||||
let len = self.0.len() + other.0.len();
|
|
||||||
|
|
||||||
let mut result = if len > 1 {
|
|
||||||
SmallVec::from_vec(Vec::with_capacity(len))
|
|
||||||
} else {
|
|
||||||
SmallVec::new()
|
|
||||||
};
|
|
||||||
|
|
||||||
result.extend(self.0.iter().cloned());
|
|
||||||
result.extend(other.0.iter().cloned());
|
|
||||||
|
|
||||||
Ok(${item}List(result))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</%def>
|
|
||||||
|
|
||||||
/// Check if it's possible to do a direct numerical interpolation
|
/// Check if it's possible to do a direct numerical interpolation
|
||||||
/// between these two transform lists.
|
/// between these two transform lists.
|
||||||
/// http://dev.w3.org/csswg/css-transforms/#transform-transform-animation
|
/// http://dev.w3.org/csswg/css-transforms/#transform-transform-animation
|
||||||
|
@ -2922,74 +2794,183 @@ impl Animatable for IntermediateSVGPaintKind {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
<%def name="impl_intermediate_type_for_shadow(type)">
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[allow(missing_docs)]
|
||||||
#[allow(missing_docs)]
|
/// Intermediate type for box-shadow and text-shadow.
|
||||||
/// Intermediate type for box-shadow and text-shadow.
|
/// The difference between normal shadow type is that this type uses
|
||||||
/// The difference between normal shadow type is that this type uses
|
/// IntermediateColor instead of ParserColor.
|
||||||
/// IntermediateColor instead of ParserColor.
|
pub struct IntermediateShadow {
|
||||||
pub struct Intermediate${type}Shadow {
|
|
||||||
pub offset_x: Au,
|
pub offset_x: Au,
|
||||||
pub offset_y: Au,
|
pub offset_y: Au,
|
||||||
pub blur_radius: Au,
|
pub blur_radius: Au,
|
||||||
pub color: IntermediateColor,
|
|
||||||
% if type == "Box":
|
|
||||||
pub spread_radius: Au,
|
pub spread_radius: Au,
|
||||||
|
pub color: IntermediateColor,
|
||||||
pub inset: bool,
|
pub inset: bool,
|
||||||
% endif
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
/// Intermediate type for box-shadow list and text-shadow list.
|
/// Intermediate type for box-shadow list and text-shadow list.
|
||||||
pub struct Intermediate${type}ShadowList(pub SmallVec<[Intermediate${type}Shadow; 1]>);
|
pub struct IntermediateShadowList(pub SmallVec<[IntermediateShadow; 1]>);
|
||||||
|
|
||||||
impl From<Intermediate${type}ShadowList> for ${type}ShadowList {
|
type ShadowList = SmallVec<[Shadow; 1]>;
|
||||||
fn from(shadow_list: Intermediate${type}ShadowList) -> ${type}ShadowList {
|
|
||||||
${type}ShadowList(shadow_list.0.into_iter().map(|s| s.into()).collect())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<${type}ShadowList> for Intermediate${type}ShadowList {
|
impl From<IntermediateShadowList> for ShadowList {
|
||||||
fn from(shadow_list: ${type}ShadowList) -> Intermediate${type}ShadowList {
|
fn from(shadow_list: IntermediateShadowList) -> Self {
|
||||||
Intermediate${type}ShadowList(shadow_list.0.into_iter().map(|s| s.into()).collect())
|
shadow_list.0.into_iter().map(|s| s.into()).collect()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Intermediate${type}Shadow> for ${type}Shadow {
|
impl From<ShadowList> for IntermediateShadowList {
|
||||||
fn from(shadow: Intermediate${type}Shadow) -> ${type}Shadow {
|
fn from(shadow_list: ShadowList) -> IntermediateShadowList {
|
||||||
${type}Shadow {
|
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_x: shadow.offset_x,
|
||||||
offset_y: shadow.offset_y,
|
offset_y: shadow.offset_y,
|
||||||
blur_radius: shadow.blur_radius,
|
blur_radius: shadow.blur_radius,
|
||||||
color: shadow.color.into(),
|
|
||||||
% if type == "Box":
|
|
||||||
spread_radius: shadow.spread_radius,
|
spread_radius: shadow.spread_radius,
|
||||||
|
color: shadow.color.into(),
|
||||||
inset: shadow.inset,
|
inset: shadow.inset,
|
||||||
% endif
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<${type}Shadow> for Intermediate${type}Shadow {
|
impl From<Shadow> for IntermediateShadow {
|
||||||
fn from(shadow: ${type}Shadow) -> Intermediate${type}Shadow {
|
fn from(shadow: Shadow) -> IntermediateShadow {
|
||||||
Intermediate${type}Shadow {
|
IntermediateShadow {
|
||||||
offset_x: shadow.offset_x,
|
offset_x: shadow.offset_x,
|
||||||
offset_y: shadow.offset_y,
|
offset_y: shadow.offset_y,
|
||||||
blur_radius: shadow.blur_radius,
|
blur_radius: shadow.blur_radius,
|
||||||
color: shadow.color.into(),
|
|
||||||
% if type == "Box":
|
|
||||||
spread_radius: shadow.spread_radius,
|
spread_radius: shadow.spread_radius,
|
||||||
|
color: shadow.color.into(),
|
||||||
inset: shadow.inset,
|
inset: shadow.inset,
|
||||||
% endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
${impl_animatable_for_shadow('Intermediate%sShadow' % type,
|
|
||||||
'IntermediateColor::IntermediateRGBA(IntermediateRGBA::transparent())')}
|
|
||||||
</%def>
|
|
||||||
|
|
||||||
${impl_intermediate_type_for_shadow('Box')}
|
impl Animatable for IntermediateShadow {
|
||||||
${impl_intermediate_type_for_shadow('Text')}
|
#[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 = try!(self.offset_x.add_weighted(&other.offset_x, self_portion, other_portion));
|
||||||
|
let y = try!(self.offset_y.add_weighted(&other.offset_y, self_portion, other_portion));
|
||||||
|
let color = try!(self.color.add_weighted(&other.color, self_portion, other_portion));
|
||||||
|
let blur = try!(self.blur_radius.add_weighted(&other.blur_radius,
|
||||||
|
self_portion, other_portion));
|
||||||
|
let spread = try!(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 = [ try!(self.offset_x.compute_distance(&other.offset_x)),
|
||||||
|
try!(self.offset_y.compute_distance(&other.offset_y)),
|
||||||
|
try!(self.blur_radius.compute_distance(&other.blur_radius)),
|
||||||
|
try!(self.color.compute_distance(&other.color)),
|
||||||
|
try!(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::IntermediateRGBA(IntermediateRGBA::transparent()),
|
||||||
|
inset: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let max_len = cmp::max(self.0.len(), other.0.len());
|
||||||
|
|
||||||
|
let mut result = if max_len > 1 {
|
||||||
|
SmallVec::from_vec(Vec::with_capacity(max_len))
|
||||||
|
} else {
|
||||||
|
SmallVec::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
for i in 0..max_len {
|
||||||
|
let shadow = match (self.0.get(i), other.0.get(i)) {
|
||||||
|
(Some(shadow), Some(other)) =>
|
||||||
|
try!(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 = if len > 1 {
|
||||||
|
SmallVec::from_vec(Vec::with_capacity(len))
|
||||||
|
} else {
|
||||||
|
SmallVec::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
result.extend(self.0.iter().cloned());
|
||||||
|
result.extend(other.0.iter().cloned());
|
||||||
|
|
||||||
|
Ok(IntermediateShadowList(result))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ ${helpers.predefined_type("opacity",
|
||||||
spec="https://drafts.csswg.org/css-color/#opacity")}
|
spec="https://drafts.csswg.org/css-color/#opacity")}
|
||||||
|
|
||||||
<%helpers:vector_longhand name="box-shadow" allow_empty="True"
|
<%helpers:vector_longhand name="box-shadow" allow_empty="True"
|
||||||
animation_value_type="IntermediateBoxShadowList"
|
animation_value_type="IntermediateShadowList"
|
||||||
extra_prefixes="webkit"
|
extra_prefixes="webkit"
|
||||||
ignored_when_colors_disabled="True"
|
ignored_when_colors_disabled="True"
|
||||||
spec="https://drafts.csswg.org/css-backgrounds/#box-shadow">
|
spec="https://drafts.csswg.org/css-backgrounds/#box-shadow">
|
||||||
|
|
|
@ -401,149 +401,20 @@ ${helpers.predefined_type("word-spacing",
|
||||||
% endif
|
% endif
|
||||||
</%helpers:single_keyword_computed>
|
</%helpers:single_keyword_computed>
|
||||||
|
|
||||||
<%helpers:longhand name="text-shadow"
|
<%helpers:vector_longhand name="text-shadow" allow_empty="True"
|
||||||
animation_value_type="IntermediateTextShadowList",
|
animation_value_type="IntermediateShadowList"
|
||||||
ignored_when_colors_disabled="True",
|
ignored_when_colors_disabled="True"
|
||||||
spec="https://drafts.csswg.org/css-text-decor/#propdef-text-shadow">
|
spec="https://drafts.csswg.org/css-backgrounds/#box-shadow">
|
||||||
use cssparser;
|
pub type SpecifiedValue = specified::Shadow;
|
||||||
use std::fmt;
|
|
||||||
use style_traits::ToCss;
|
|
||||||
use values::specified::Shadow;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
||||||
pub struct SpecifiedValue(Vec<SpecifiedTextShadow>);
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
||||||
pub struct SpecifiedTextShadow {
|
|
||||||
pub offset_x: specified::Length,
|
|
||||||
pub offset_y: specified::Length,
|
|
||||||
pub blur_radius: specified::Length,
|
|
||||||
pub color: Option<specified::CSSColor>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Shadow> for SpecifiedTextShadow {
|
|
||||||
fn from(shadow: Shadow) -> SpecifiedTextShadow {
|
|
||||||
SpecifiedTextShadow {
|
|
||||||
offset_x: shadow.offset_x,
|
|
||||||
offset_y: shadow.offset_y,
|
|
||||||
blur_radius: shadow.blur_radius,
|
|
||||||
color: shadow.color,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod computed_value {
|
pub mod computed_value {
|
||||||
use app_units::Au;
|
use values::computed::Shadow;
|
||||||
use cssparser::Color;
|
pub type T = Shadow;
|
||||||
use smallvec::SmallVec;
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
||||||
pub struct T(pub SmallVec<[TextShadow; 1]>);
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
||||||
#[derive(Clone, PartialEq, Debug, ToCss)]
|
|
||||||
pub struct TextShadow {
|
|
||||||
pub offset_x: Au,
|
|
||||||
pub offset_y: Au,
|
|
||||||
pub blur_radius: Au,
|
|
||||||
pub color: Color,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToCss for computed_value::T {
|
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<specified::Shadow, ()> {
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
specified::Shadow::parse(context, input, true)
|
||||||
let mut iter = self.0.iter();
|
|
||||||
match iter.next() {
|
|
||||||
Some(shadow) => shadow.to_css(dest)?,
|
|
||||||
None => return dest.write_str("none"),
|
|
||||||
}
|
}
|
||||||
for shadow in iter {
|
</%helpers:vector_longhand>
|
||||||
dest.write_str(", ")?;
|
|
||||||
shadow.to_css(dest)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToCss for SpecifiedValue {
|
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
||||||
let mut iter = self.0.iter();
|
|
||||||
match iter.next() {
|
|
||||||
Some(shadow) => shadow.to_css(dest)?,
|
|
||||||
None => return dest.write_str("none"),
|
|
||||||
}
|
|
||||||
for shadow in iter {
|
|
||||||
dest.write_str(", ")?;
|
|
||||||
shadow.to_css(dest)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToCss for SpecifiedTextShadow {
|
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
||||||
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 let Some(ref color) = self.color {
|
|
||||||
dest.write_str(" ")?;
|
|
||||||
color.to_css(dest)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn get_initial_value() -> computed_value::T {
|
|
||||||
use smallvec::SmallVec;
|
|
||||||
computed_value::T(SmallVec::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
|
|
||||||
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
|
|
||||||
Ok(SpecifiedValue(Vec::new()))
|
|
||||||
} else {
|
|
||||||
input.parse_comma_separated(|i| {
|
|
||||||
Ok(SpecifiedTextShadow::from(Shadow::parse(context, i, true)?))
|
|
||||||
}).map(SpecifiedValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToComputedValue for SpecifiedValue {
|
|
||||||
type ComputedValue = computed_value::T;
|
|
||||||
|
|
||||||
fn to_computed_value(&self, context: &Context) -> computed_value::T {
|
|
||||||
computed_value::T(self.0.iter().map(|value| {
|
|
||||||
computed_value::TextShadow {
|
|
||||||
offset_x: value.offset_x.to_computed_value(context),
|
|
||||||
offset_y: value.offset_y.to_computed_value(context),
|
|
||||||
blur_radius: value.blur_radius.to_computed_value(context),
|
|
||||||
color: value.color
|
|
||||||
.as_ref()
|
|
||||||
.map(|color| color.to_computed_value(context))
|
|
||||||
.unwrap_or(cssparser::Color::CurrentColor),
|
|
||||||
}
|
|
||||||
}).collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_computed_value(computed: &computed_value::T) -> Self {
|
|
||||||
SpecifiedValue(computed.0.iter().map(|value| {
|
|
||||||
SpecifiedTextShadow {
|
|
||||||
offset_x: ToComputedValue::from_computed_value(&value.offset_x),
|
|
||||||
offset_y: ToComputedValue::from_computed_value(&value.offset_y),
|
|
||||||
blur_radius: ToComputedValue::from_computed_value(&value.blur_radius),
|
|
||||||
color: Some(ToComputedValue::from_computed_value(&value.color)),
|
|
||||||
}
|
|
||||||
}).collect())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</%helpers:longhand>
|
|
||||||
|
|
||||||
<%helpers:longhand name="text-emphasis-style" products="gecko" need_clone="True" boxed="True"
|
<%helpers:longhand name="text-emphasis-style" products="gecko" need_clone="True" boxed="True"
|
||||||
animation_value_type="none"
|
animation_value_type="none"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue