mirror of
https://github.com/servo/servo.git
synced 2025-08-05 21:50:18 +01:00
Auto merge of #18072 - servo:we-are-leaving-babylon, r=emilio
Move some code out of animated_properties <!-- 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/18072) <!-- Reviewable:end -->
This commit is contained in:
commit
4d22af6136
21 changed files with 598 additions and 496 deletions
|
@ -7,7 +7,7 @@
|
||||||
<% from data import to_idl_name, SYSTEM_FONT_LONGHANDS %>
|
<% from data import to_idl_name, SYSTEM_FONT_LONGHANDS %>
|
||||||
|
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use cssparser::{Parser, RGBA};
|
use cssparser::Parser;
|
||||||
use euclid::{Point2D, Size2D};
|
use euclid::{Point2D, Size2D};
|
||||||
#[cfg(feature = "gecko")] use gecko_bindings::bindings::RawServoAnimationValueMap;
|
#[cfg(feature = "gecko")] use gecko_bindings::bindings::RawServoAnimationValueMap;
|
||||||
#[cfg(feature = "gecko")] use gecko_bindings::structs::RawGeckoGfxMatrix4x4;
|
#[cfg(feature = "gecko")] use gecko_bindings::structs::RawGeckoGfxMatrix4x4;
|
||||||
|
@ -38,13 +38,14 @@ use super::ComputedValues;
|
||||||
use values::Auto;
|
use values::Auto;
|
||||||
use values::{CSSFloat, CustomIdent, Either};
|
use values::{CSSFloat, CustomIdent, Either};
|
||||||
use values::animated::{ToAnimatedValue, ToAnimatedZero};
|
use values::animated::{ToAnimatedValue, ToAnimatedZero};
|
||||||
|
use values::animated::color::{Color as AnimatedColor, RGBA as AnimatedRGBA};
|
||||||
use values::animated::effects::BoxShadowList as AnimatedBoxShadowList;
|
use values::animated::effects::BoxShadowList as AnimatedBoxShadowList;
|
||||||
use values::animated::effects::Filter as AnimatedFilter;
|
use values::animated::effects::Filter as AnimatedFilter;
|
||||||
use values::animated::effects::FilterList as AnimatedFilterList;
|
use values::animated::effects::FilterList as AnimatedFilterList;
|
||||||
use values::animated::effects::TextShadowList as AnimatedTextShadowList;
|
use values::animated::effects::TextShadowList as AnimatedTextShadowList;
|
||||||
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, Color, Context, ComputedValueAsSpecified, ComputedUrl};
|
use values::computed::{CalcLengthOrPercentage, Context, ComputedValueAsSpecified, ComputedUrl};
|
||||||
use values::computed::{LengthOrPercentage, MaxLength, MozLength, Percentage, ToComputedValue};
|
use values::computed::{LengthOrPercentage, MaxLength, MozLength, Percentage, ToComputedValue};
|
||||||
use values::computed::{NonNegativeAu, NonNegativeNumber, PositiveIntegerOrAuto};
|
use values::computed::{NonNegativeAu, NonNegativeNumber, PositiveIntegerOrAuto};
|
||||||
use values::computed::length::{NonNegativeLengthOrAuto, NonNegativeLengthOrNormal};
|
use values::computed::length::{NonNegativeLengthOrAuto, NonNegativeLengthOrNormal};
|
||||||
|
@ -887,21 +888,6 @@ impl Animatable for Angle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-transitions/#animtype-percentage
|
|
||||||
impl Animatable for Percentage {
|
|
||||||
#[inline]
|
|
||||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
||||||
Ok(Percentage((self.0 as f64 * self_portion + other.0 as f64 * other_portion) as f32))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToAnimatedZero for Percentage {
|
|
||||||
#[inline]
|
|
||||||
fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
||||||
Ok(Percentage(0.))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-transitions/#animtype-visibility
|
/// https://drafts.csswg.org/css-transitions/#animtype-visibility
|
||||||
impl Animatable for Visibility {
|
impl Animatable for Visibility {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -952,11 +938,6 @@ impl<T: Animatable + Copy> Animatable for Point2D<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToAnimatedZero for BorderCornerRadius {
|
|
||||||
#[inline]
|
|
||||||
fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-transitions/#animtype-length
|
/// https://drafts.csswg.org/css-transitions/#animtype-length
|
||||||
impl Animatable for VerticalAlign {
|
impl Animatable for VerticalAlign {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -2455,253 +2436,11 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
||||||
/// Unlike RGBA, each component value may exceed the range [0.0, 1.0].
|
|
||||||
pub struct IntermediateRGBA {
|
|
||||||
/// The red component.
|
|
||||||
pub red: f32,
|
|
||||||
/// The green component.
|
|
||||||
pub green: f32,
|
|
||||||
/// The blue component.
|
|
||||||
pub blue: f32,
|
|
||||||
/// The alpha component.
|
|
||||||
pub alpha: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntermediateRGBA {
|
|
||||||
/// Returns a transparent color.
|
|
||||||
#[inline]
|
|
||||||
pub fn transparent() -> Self {
|
|
||||||
Self::new(0., 0., 0., 0.)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a new color.
|
|
||||||
#[inline]
|
|
||||||
pub fn new(red: f32, green: f32, blue: f32, alpha: f32) -> Self {
|
|
||||||
IntermediateRGBA { red: red, green: green, blue: blue, alpha: alpha }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToAnimatedValue for RGBA {
|
|
||||||
type AnimatedValue = IntermediateRGBA;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn to_animated_value(self) -> Self::AnimatedValue {
|
|
||||||
IntermediateRGBA::new(
|
|
||||||
self.red_f32(),
|
|
||||||
self.green_f32(),
|
|
||||||
self.blue_f32(),
|
|
||||||
self.alpha_f32(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
|
|
||||||
// RGBA::from_floats clamps each component values.
|
|
||||||
RGBA::from_floats(
|
|
||||||
animated.red,
|
|
||||||
animated.green,
|
|
||||||
animated.blue,
|
|
||||||
animated.alpha,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unlike Animatable for RGBA we don't clamp any component values.
|
|
||||||
impl Animatable for IntermediateRGBA {
|
|
||||||
#[inline]
|
|
||||||
fn add_weighted(&self, other: &IntermediateRGBA, self_portion: f64, other_portion: f64)
|
|
||||||
-> Result<Self, ()> {
|
|
||||||
let mut alpha = self.alpha.add_weighted(&other.alpha, self_portion, other_portion)?;
|
|
||||||
if alpha <= 0. {
|
|
||||||
// Ideally we should return color value that only alpha component is
|
|
||||||
// 0, but this is what current gecko does.
|
|
||||||
Ok(IntermediateRGBA::transparent())
|
|
||||||
} else {
|
|
||||||
alpha = alpha.min(1.);
|
|
||||||
let red = (self.red * self.alpha).add_weighted(
|
|
||||||
&(other.red * other.alpha), self_portion, other_portion
|
|
||||||
)? * 1. / alpha;
|
|
||||||
let green = (self.green * self.alpha).add_weighted(
|
|
||||||
&(other.green * other.alpha), self_portion, other_portion
|
|
||||||
)? * 1. / alpha;
|
|
||||||
let blue = (self.blue * self.alpha).add_weighted(
|
|
||||||
&(other.blue * other.alpha), self_portion, other_portion
|
|
||||||
)? * 1. / alpha;
|
|
||||||
Ok(IntermediateRGBA::new(red, green, blue, alpha))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ComputeSquaredDistance for IntermediateRGBA {
|
|
||||||
#[inline]
|
|
||||||
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
|
||||||
let start = [ self.alpha, self.red * self.alpha, self.green * self.alpha, self.blue * self.alpha ];
|
|
||||||
let end = [ other.alpha, other.red * other.alpha, other.green * other.alpha, other.blue * other.alpha ];
|
|
||||||
start.iter().zip(&end).map(|(this, other)| this.compute_squared_distance(other)).sum()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToAnimatedZero for IntermediateRGBA {
|
|
||||||
#[inline]
|
|
||||||
fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
||||||
Ok(IntermediateRGBA::transparent())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub struct IntermediateColor {
|
|
||||||
color: IntermediateRGBA,
|
|
||||||
foreground_ratio: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntermediateColor {
|
|
||||||
fn currentcolor() -> Self {
|
|
||||||
IntermediateColor {
|
|
||||||
color: IntermediateRGBA::transparent(),
|
|
||||||
foreground_ratio: 1.,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a transparent intermediate color.
|
|
||||||
pub fn transparent() -> Self {
|
|
||||||
IntermediateColor {
|
|
||||||
color: IntermediateRGBA::transparent(),
|
|
||||||
foreground_ratio: 0.,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_currentcolor(&self) -> bool {
|
|
||||||
self.foreground_ratio >= 1.
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_numeric(&self) -> bool {
|
|
||||||
self.foreground_ratio <= 0.
|
|
||||||
}
|
|
||||||
|
|
||||||
fn effective_intermediate_rgba(&self) -> IntermediateRGBA {
|
|
||||||
IntermediateRGBA {
|
|
||||||
alpha: self.color.alpha * (1. - self.foreground_ratio),
|
|
||||||
.. self.color
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToAnimatedValue for Color {
|
|
||||||
type AnimatedValue = IntermediateColor;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn to_animated_value(self) -> Self::AnimatedValue {
|
|
||||||
IntermediateColor {
|
|
||||||
color: self.color.to_animated_value(),
|
|
||||||
foreground_ratio: self.foreground_ratio as f32 * (1. / 255.),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
|
|
||||||
Color {
|
|
||||||
color: RGBA::from_animated_value(animated.color),
|
|
||||||
foreground_ratio: (animated.foreground_ratio * 255.).round() as u8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Animatable for IntermediateColor {
|
|
||||||
#[inline]
|
|
||||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
||||||
// Common cases are interpolating between two numeric colors,
|
|
||||||
// two currentcolors, and a numeric color and a currentcolor.
|
|
||||||
//
|
|
||||||
// Note: this algorithm assumes self_portion + other_portion
|
|
||||||
// equals to one, so it may be broken for additive operation.
|
|
||||||
// To properly support additive color interpolation, we would
|
|
||||||
// need two ratio fields in computed color types.
|
|
||||||
if self.foreground_ratio == other.foreground_ratio {
|
|
||||||
if self.is_currentcolor() {
|
|
||||||
Ok(IntermediateColor::currentcolor())
|
|
||||||
} else {
|
|
||||||
Ok(IntermediateColor {
|
|
||||||
color: self.color.add_weighted(&other.color, self_portion, other_portion)?,
|
|
||||||
foreground_ratio: self.foreground_ratio,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else if self.is_currentcolor() && other.is_numeric() {
|
|
||||||
Ok(IntermediateColor {
|
|
||||||
color: other.color,
|
|
||||||
foreground_ratio: self_portion as f32,
|
|
||||||
})
|
|
||||||
} else if self.is_numeric() && other.is_currentcolor() {
|
|
||||||
Ok(IntermediateColor {
|
|
||||||
color: self.color,
|
|
||||||
foreground_ratio: other_portion as f32,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
// For interpolating between two complex colors, we need to
|
|
||||||
// generate colors with effective alpha value.
|
|
||||||
let self_color = self.effective_intermediate_rgba();
|
|
||||||
let other_color = other.effective_intermediate_rgba();
|
|
||||||
let color = self_color.add_weighted(&other_color, self_portion, other_portion)?;
|
|
||||||
// Then we compute the final foreground ratio, and derive
|
|
||||||
// the final alpha value from the effective alpha value.
|
|
||||||
let foreground_ratio = self.foreground_ratio
|
|
||||||
.add_weighted(&other.foreground_ratio, self_portion, other_portion)?;
|
|
||||||
let alpha = color.alpha / (1. - foreground_ratio);
|
|
||||||
Ok(IntermediateColor {
|
|
||||||
color: IntermediateRGBA {
|
|
||||||
alpha: alpha,
|
|
||||||
.. color
|
|
||||||
},
|
|
||||||
foreground_ratio: foreground_ratio,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ComputeSquaredDistance for IntermediateColor {
|
|
||||||
#[inline]
|
|
||||||
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
|
||||||
// All comments in add_weighted also applies here.
|
|
||||||
if self.foreground_ratio == other.foreground_ratio {
|
|
||||||
if self.is_currentcolor() {
|
|
||||||
Ok(SquaredDistance::Value(0.))
|
|
||||||
} else {
|
|
||||||
self.color.compute_squared_distance(&other.color)
|
|
||||||
}
|
|
||||||
} else if self.is_currentcolor() && other.is_numeric() {
|
|
||||||
Ok(
|
|
||||||
IntermediateRGBA::transparent().compute_squared_distance(&other.color)? +
|
|
||||||
SquaredDistance::Value(1.),
|
|
||||||
)
|
|
||||||
} else if self.is_numeric() && other.is_currentcolor() {
|
|
||||||
Ok(
|
|
||||||
self.color.compute_squared_distance(&IntermediateRGBA::transparent())? +
|
|
||||||
SquaredDistance::Value(1.),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
let self_color = self.effective_intermediate_rgba();
|
|
||||||
let other_color = other.effective_intermediate_rgba();
|
|
||||||
Ok(
|
|
||||||
self_color.compute_squared_distance(&other_color)? +
|
|
||||||
self.foreground_ratio.compute_squared_distance(&other.foreground_ratio)?,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToAnimatedZero for IntermediateColor {
|
|
||||||
#[inline]
|
|
||||||
fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Animatable SVGPaint
|
/// Animatable SVGPaint
|
||||||
pub type IntermediateSVGPaint = SVGPaint<IntermediateRGBA, ComputedUrl>;
|
pub type IntermediateSVGPaint = SVGPaint<AnimatedRGBA, ComputedUrl>;
|
||||||
|
|
||||||
/// Animatable SVGPaintKind
|
/// Animatable SVGPaintKind
|
||||||
pub type IntermediateSVGPaintKind = SVGPaintKind<IntermediateRGBA, ComputedUrl>;
|
pub type IntermediateSVGPaintKind = SVGPaintKind<AnimatedRGBA, ComputedUrl>;
|
||||||
|
|
||||||
impl Animatable for IntermediateSVGPaint {
|
impl Animatable for IntermediateSVGPaint {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -6,14 +6,17 @@
|
||||||
|
|
||||||
<% data.new_style_struct("Background", inherited=False) %>
|
<% data.new_style_struct("Background", inherited=False) %>
|
||||||
|
|
||||||
${helpers.predefined_type("background-color", "Color",
|
${helpers.predefined_type(
|
||||||
|
"background-color",
|
||||||
|
"Color",
|
||||||
"computed_value::T::transparent()",
|
"computed_value::T::transparent()",
|
||||||
initial_specified_value="SpecifiedValue::transparent()",
|
initial_specified_value="SpecifiedValue::transparent()",
|
||||||
spec="https://drafts.csswg.org/css-backgrounds/#background-color",
|
spec="https://drafts.csswg.org/css-backgrounds/#background-color",
|
||||||
animation_value_type="IntermediateColor",
|
animation_value_type="AnimatedColor",
|
||||||
ignored_when_colors_disabled=True,
|
ignored_when_colors_disabled=True,
|
||||||
allow_quirks=True,
|
allow_quirks=True,
|
||||||
flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER")}
|
flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
|
||||||
|
)}
|
||||||
|
|
||||||
${helpers.predefined_type("background-image", "ImageLayer",
|
${helpers.predefined_type("background-image", "ImageLayer",
|
||||||
initial_value="Either::First(None_)",
|
initial_value="Either::First(None_)",
|
||||||
|
|
|
@ -20,15 +20,17 @@
|
||||||
side_name = side[0]
|
side_name = side[0]
|
||||||
is_logical = side[1]
|
is_logical = side[1]
|
||||||
%>
|
%>
|
||||||
${helpers.predefined_type("border-%s-color" % side_name, "Color",
|
${helpers.predefined_type(
|
||||||
"computed_value::T::currentcolor()",
|
"border-%s-color" % side_name, "Color",
|
||||||
alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-color"),
|
"computed_value::T::currentcolor()",
|
||||||
spec=maybe_logical_spec(side, "color"),
|
alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-color"),
|
||||||
animation_value_type="IntermediateColor",
|
spec=maybe_logical_spec(side, "color"),
|
||||||
logical=is_logical,
|
animation_value_type="AnimatedColor",
|
||||||
allow_quirks=not is_logical,
|
logical=is_logical,
|
||||||
flags="APPLIES_TO_FIRST_LETTER",
|
allow_quirks=not is_logical,
|
||||||
ignored_when_colors_disabled=True)}
|
flags="APPLIES_TO_FIRST_LETTER",
|
||||||
|
ignored_when_colors_disabled=True,
|
||||||
|
)}
|
||||||
|
|
||||||
${helpers.predefined_type("border-%s-style" % side_name, "BorderStyle",
|
${helpers.predefined_type("border-%s-style" % side_name, "BorderStyle",
|
||||||
"specified::BorderStyle::none",
|
"specified::BorderStyle::none",
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
<% from data import to_rust_ident %>
|
<% from data import to_rust_ident %>
|
||||||
|
|
||||||
<%helpers:longhand name="color" need_clone="True"
|
<%helpers:longhand name="color" need_clone="True"
|
||||||
animation_value_type="IntermediateRGBA"
|
animation_value_type="AnimatedRGBA"
|
||||||
ignored_when_colors_disabled="True"
|
ignored_when_colors_disabled="True"
|
||||||
flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER"
|
flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER"
|
||||||
spec="https://drafts.csswg.org/css-color/#color">
|
spec="https://drafts.csswg.org/css-color/#color">
|
||||||
|
|
|
@ -48,12 +48,18 @@ ${helpers.predefined_type("column-rule-width",
|
||||||
extra_prefixes="moz")}
|
extra_prefixes="moz")}
|
||||||
|
|
||||||
// https://drafts.csswg.org/css-multicol-1/#crc
|
// https://drafts.csswg.org/css-multicol-1/#crc
|
||||||
${helpers.predefined_type("column-rule-color", "Color",
|
${helpers.predefined_type(
|
||||||
"computed_value::T::currentcolor()",
|
"column-rule-color",
|
||||||
initial_specified_value="specified::Color::currentcolor()",
|
"Color",
|
||||||
products="gecko", animation_value_type="IntermediateColor", extra_prefixes="moz",
|
"computed_value::T::currentcolor()",
|
||||||
need_clone=True, ignored_when_colors_disabled=True,
|
initial_specified_value="specified::Color::currentcolor()",
|
||||||
spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-color")}
|
products="gecko",
|
||||||
|
animation_value_type="AnimatedColor",
|
||||||
|
extra_prefixes="moz",
|
||||||
|
need_clone=True,
|
||||||
|
ignored_when_colors_disabled=True,
|
||||||
|
spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-color",
|
||||||
|
)}
|
||||||
|
|
||||||
${helpers.single_keyword("column-span", "none all",
|
${helpers.single_keyword("column-span", "none all",
|
||||||
products="gecko", animation_value_type="discrete",
|
products="gecko", animation_value_type="discrete",
|
||||||
|
|
|
@ -705,13 +705,17 @@ ${helpers.predefined_type(
|
||||||
% endif
|
% endif
|
||||||
</%helpers:longhand>
|
</%helpers:longhand>
|
||||||
|
|
||||||
${helpers.predefined_type("text-emphasis-color", "Color",
|
${helpers.predefined_type(
|
||||||
"computed_value::T::currentcolor()",
|
"text-emphasis-color",
|
||||||
initial_specified_value="specified::Color::currentcolor()",
|
"Color",
|
||||||
products="gecko", animation_value_type="IntermediateColor",
|
"computed_value::T::currentcolor()",
|
||||||
need_clone=True, ignored_when_colors_disabled=True,
|
initial_specified_value="specified::Color::currentcolor()",
|
||||||
spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-color")}
|
products="gecko",
|
||||||
|
animation_value_type="AnimatedColor",
|
||||||
|
need_clone=True,
|
||||||
|
ignored_when_colors_disabled=True,
|
||||||
|
spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-color",
|
||||||
|
)}
|
||||||
|
|
||||||
${helpers.predefined_type(
|
${helpers.predefined_type(
|
||||||
"-moz-tab-size", "length::NonNegativeLengthOrNumber",
|
"-moz-tab-size", "length::NonNegativeLengthOrNumber",
|
||||||
|
@ -723,21 +727,28 @@ ${helpers.predefined_type(
|
||||||
// CSS Compatibility
|
// CSS Compatibility
|
||||||
// https://compat.spec.whatwg.org
|
// https://compat.spec.whatwg.org
|
||||||
${helpers.predefined_type(
|
${helpers.predefined_type(
|
||||||
"-webkit-text-fill-color", "Color",
|
"-webkit-text-fill-color",
|
||||||
|
"Color",
|
||||||
"computed_value::T::currentcolor()",
|
"computed_value::T::currentcolor()",
|
||||||
products="gecko", animation_value_type="IntermediateColor",
|
products="gecko",
|
||||||
need_clone=True, ignored_when_colors_disabled=True,
|
animation_value_type="AnimatedColor",
|
||||||
|
need_clone=True,
|
||||||
|
ignored_when_colors_disabled=True,
|
||||||
flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
|
flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
|
||||||
spec="https://compat.spec.whatwg.org/#the-webkit-text-fill-color")}
|
spec="https://compat.spec.whatwg.org/#the-webkit-text-fill-color",
|
||||||
|
)}
|
||||||
|
|
||||||
${helpers.predefined_type(
|
${helpers.predefined_type(
|
||||||
"-webkit-text-stroke-color", "Color",
|
"-webkit-text-stroke-color",
|
||||||
|
"Color",
|
||||||
"computed_value::T::currentcolor()",
|
"computed_value::T::currentcolor()",
|
||||||
initial_specified_value="specified::Color::currentcolor()",
|
initial_specified_value="specified::Color::currentcolor()",
|
||||||
products="gecko", animation_value_type="IntermediateColor",
|
products="gecko",
|
||||||
|
animation_value_type="AnimatedColor",
|
||||||
need_clone=True, ignored_when_colors_disabled=True,
|
need_clone=True, ignored_when_colors_disabled=True,
|
||||||
flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
|
flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
|
||||||
spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke-color")}
|
spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke-color",
|
||||||
|
)}
|
||||||
|
|
||||||
${helpers.predefined_type("-webkit-text-stroke-width",
|
${helpers.predefined_type("-webkit-text-stroke-width",
|
||||||
"BorderSideWidth",
|
"BorderSideWidth",
|
||||||
|
|
|
@ -10,11 +10,16 @@
|
||||||
additional_methods=[Method("outline_has_nonzero_width", "bool")]) %>
|
additional_methods=[Method("outline_has_nonzero_width", "bool")]) %>
|
||||||
|
|
||||||
// TODO(pcwalton): `invert`
|
// TODO(pcwalton): `invert`
|
||||||
${helpers.predefined_type("outline-color", "Color", "computed_value::T::currentcolor()",
|
${helpers.predefined_type(
|
||||||
initial_specified_value="specified::Color::currentcolor()",
|
"outline-color",
|
||||||
animation_value_type="IntermediateColor", need_clone=True,
|
"Color",
|
||||||
ignored_when_colors_disabled=True,
|
"computed_value::T::currentcolor()",
|
||||||
spec="https://drafts.csswg.org/css-ui/#propdef-outline-color")}
|
initial_specified_value="specified::Color::currentcolor()",
|
||||||
|
animation_value_type="AnimatedColor",
|
||||||
|
need_clone=True,
|
||||||
|
ignored_when_colors_disabled=True,
|
||||||
|
spec="https://drafts.csswg.org/css-ui/#propdef-outline-color",
|
||||||
|
)}
|
||||||
|
|
||||||
<%helpers:longhand name="outline-style" animation_value_type="discrete"
|
<%helpers:longhand name="outline-style" animation_value_type="discrete"
|
||||||
spec="https://drafts.csswg.org/css-ui/#propdef-outline-style">
|
spec="https://drafts.csswg.org/css-ui/#propdef-outline-style">
|
||||||
|
|
|
@ -178,11 +178,13 @@ ${helpers.single_keyword("-moz-user-focus",
|
||||||
animation_value_type="discrete",
|
animation_value_type="discrete",
|
||||||
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-user-focus)")}
|
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-user-focus)")}
|
||||||
|
|
||||||
${helpers.predefined_type("caret-color",
|
${helpers.predefined_type(
|
||||||
"ColorOrAuto",
|
"caret-color",
|
||||||
"Either::Second(Auto)",
|
"ColorOrAuto",
|
||||||
spec="https://drafts.csswg.org/css-ui/#caret-color",
|
"Either::Second(Auto)",
|
||||||
animation_value_type="Either<IntermediateColor, Auto>",
|
spec="https://drafts.csswg.org/css-ui/#caret-color",
|
||||||
boxed=True,
|
animation_value_type="Either<AnimatedColor, Auto>",
|
||||||
ignored_when_colors_disabled=True,
|
boxed=True,
|
||||||
products="gecko")}
|
ignored_when_colors_disabled=True,
|
||||||
|
products="gecko",
|
||||||
|
)}
|
||||||
|
|
|
@ -20,11 +20,13 @@ ${helpers.single_keyword("vector-effect", "none non-scaling-stroke",
|
||||||
// Section 13 - Gradients and Patterns
|
// Section 13 - Gradients and Patterns
|
||||||
|
|
||||||
${helpers.predefined_type(
|
${helpers.predefined_type(
|
||||||
"stop-color", "RGBAColor",
|
"stop-color",
|
||||||
|
"RGBAColor",
|
||||||
"RGBA::new(0, 0, 0, 255)",
|
"RGBA::new(0, 0, 0, 255)",
|
||||||
products="gecko",
|
products="gecko",
|
||||||
animation_value_type="IntermediateRGBA",
|
animation_value_type="AnimatedRGBA",
|
||||||
spec="https://www.w3.org/TR/SVGTiny12/painting.html#StopColorProperty")}
|
spec="https://www.w3.org/TR/SVGTiny12/painting.html#StopColorProperty",
|
||||||
|
)}
|
||||||
|
|
||||||
${helpers.predefined_type("stop-opacity", "Opacity", "1.0",
|
${helpers.predefined_type("stop-opacity", "Opacity", "1.0",
|
||||||
products="gecko",
|
products="gecko",
|
||||||
|
@ -34,22 +36,26 @@ ${helpers.predefined_type("stop-opacity", "Opacity", "1.0",
|
||||||
// Section 15 - Filter Effects
|
// Section 15 - Filter Effects
|
||||||
|
|
||||||
${helpers.predefined_type(
|
${helpers.predefined_type(
|
||||||
"flood-color", "RGBAColor",
|
"flood-color",
|
||||||
|
"RGBAColor",
|
||||||
"RGBA::new(0, 0, 0, 255)",
|
"RGBA::new(0, 0, 0, 255)",
|
||||||
products="gecko",
|
products="gecko",
|
||||||
animation_value_type="IntermediateRGBA",
|
animation_value_type="AnimatedRGBA",
|
||||||
spec="https://www.w3.org/TR/SVG/filters.html#FloodColorProperty")}
|
spec="https://www.w3.org/TR/SVG/filters.html#FloodColorProperty",
|
||||||
|
)}
|
||||||
|
|
||||||
${helpers.predefined_type("flood-opacity", "Opacity",
|
${helpers.predefined_type("flood-opacity", "Opacity",
|
||||||
"1.0", products="gecko", animation_value_type="ComputedValue",
|
"1.0", products="gecko", animation_value_type="ComputedValue",
|
||||||
spec="https://www.w3.org/TR/SVG/filters.html#FloodOpacityProperty")}
|
spec="https://www.w3.org/TR/SVG/filters.html#FloodOpacityProperty")}
|
||||||
|
|
||||||
${helpers.predefined_type(
|
${helpers.predefined_type(
|
||||||
"lighting-color", "RGBAColor",
|
"lighting-color",
|
||||||
|
"RGBAColor",
|
||||||
"RGBA::new(255, 255, 255, 255)",
|
"RGBA::new(255, 255, 255, 255)",
|
||||||
products="gecko",
|
products="gecko",
|
||||||
animation_value_type="IntermediateRGBA",
|
animation_value_type="AnimatedRGBA",
|
||||||
spec="https://www.w3.org/TR/SVG/filters.html#LightingColorProperty")}
|
spec="https://www.w3.org/TR/SVG/filters.html#LightingColorProperty",
|
||||||
|
)}
|
||||||
|
|
||||||
// CSS Masking Module Level 1
|
// CSS Masking Module Level 1
|
||||||
// https://drafts.fxtf.org/css-masking
|
// https://drafts.fxtf.org/css-masking
|
||||||
|
|
|
@ -268,14 +268,16 @@ ${helpers.single_keyword("text-decoration-style",
|
||||||
spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-style")}
|
spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-style")}
|
||||||
|
|
||||||
${helpers.predefined_type(
|
${helpers.predefined_type(
|
||||||
"text-decoration-color", "Color",
|
"text-decoration-color",
|
||||||
|
"Color",
|
||||||
"computed_value::T::currentcolor()",
|
"computed_value::T::currentcolor()",
|
||||||
initial_specified_value="specified::Color::currentcolor()",
|
initial_specified_value="specified::Color::currentcolor()",
|
||||||
products="gecko",
|
products="gecko",
|
||||||
animation_value_type="IntermediateColor",
|
animation_value_type="AnimatedColor",
|
||||||
ignored_when_colors_disabled=True,
|
ignored_when_colors_disabled=True,
|
||||||
flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
|
flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
|
||||||
spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-color")}
|
spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-color",
|
||||||
|
)}
|
||||||
|
|
||||||
${helpers.predefined_type(
|
${helpers.predefined_type(
|
||||||
"initial-letter",
|
"initial-letter",
|
||||||
|
|
214
components/style/values/animated/color.rs
Normal file
214
components/style/values/animated/color.rs
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
//! Animated types for CSS colors.
|
||||||
|
|
||||||
|
use properties::animated_properties::Animatable;
|
||||||
|
use values::animated::ToAnimatedZero;
|
||||||
|
use values::distance::{ComputeSquaredDistance, SquaredDistance};
|
||||||
|
|
||||||
|
/// An animated RGBA color.
|
||||||
|
///
|
||||||
|
/// Unlike in computed values, each component value may exceed the
|
||||||
|
/// range `[0.0, 1.0]`.
|
||||||
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub struct RGBA {
|
||||||
|
/// The red component.
|
||||||
|
pub red: f32,
|
||||||
|
/// The green component.
|
||||||
|
pub green: f32,
|
||||||
|
/// The blue component.
|
||||||
|
pub blue: f32,
|
||||||
|
/// The alpha component.
|
||||||
|
pub alpha: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RGBA {
|
||||||
|
/// Returns a transparent color.
|
||||||
|
#[inline]
|
||||||
|
pub fn transparent() -> Self {
|
||||||
|
Self::new(0., 0., 0., 0.)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new color.
|
||||||
|
#[inline]
|
||||||
|
pub fn new(red: f32, green: f32, blue: f32, alpha: f32) -> Self {
|
||||||
|
RGBA { red: red, green: green, blue: blue, alpha: alpha }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unlike Animatable for computed colors, we don't clamp any component values.
|
||||||
|
///
|
||||||
|
/// FIXME(nox): Why do computed colors even implement Animatable?
|
||||||
|
impl Animatable for RGBA {
|
||||||
|
#[inline]
|
||||||
|
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||||
|
let mut alpha = self.alpha.add_weighted(&other.alpha, self_portion, other_portion)?;
|
||||||
|
if alpha <= 0. {
|
||||||
|
// Ideally we should return color value that only alpha component is
|
||||||
|
// 0, but this is what current gecko does.
|
||||||
|
return Ok(RGBA::transparent());
|
||||||
|
}
|
||||||
|
|
||||||
|
alpha = alpha.min(1.);
|
||||||
|
let red = (self.red * self.alpha).add_weighted(
|
||||||
|
&(other.red * other.alpha), self_portion, other_portion
|
||||||
|
)? * 1. / alpha;
|
||||||
|
let green = (self.green * self.alpha).add_weighted(
|
||||||
|
&(other.green * other.alpha), self_portion, other_portion
|
||||||
|
)? * 1. / alpha;
|
||||||
|
let blue = (self.blue * self.alpha).add_weighted(
|
||||||
|
&(other.blue * other.alpha), self_portion, other_portion
|
||||||
|
)? * 1. / alpha;
|
||||||
|
|
||||||
|
Ok(RGBA::new(red, green, blue, alpha))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComputeSquaredDistance for RGBA {
|
||||||
|
#[inline]
|
||||||
|
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
||||||
|
let start = [ self.alpha, self.red * self.alpha, self.green * self.alpha, self.blue * self.alpha ];
|
||||||
|
let end = [ other.alpha, other.red * other.alpha, other.green * other.alpha, other.blue * other.alpha ];
|
||||||
|
start.iter().zip(&end).map(|(this, other)| this.compute_squared_distance(other)).sum()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToAnimatedZero for RGBA {
|
||||||
|
#[inline]
|
||||||
|
fn to_animated_zero(&self) -> Result<Self, ()> {
|
||||||
|
Ok(RGBA::transparent())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub struct Color {
|
||||||
|
pub color: RGBA,
|
||||||
|
pub foreground_ratio: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Color {
|
||||||
|
fn currentcolor() -> Self {
|
||||||
|
Color {
|
||||||
|
color: RGBA::transparent(),
|
||||||
|
foreground_ratio: 1.,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a transparent intermediate color.
|
||||||
|
pub fn transparent() -> Self {
|
||||||
|
Color {
|
||||||
|
color: RGBA::transparent(),
|
||||||
|
foreground_ratio: 0.,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_currentcolor(&self) -> bool {
|
||||||
|
self.foreground_ratio >= 1.
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_numeric(&self) -> bool {
|
||||||
|
self.foreground_ratio <= 0.
|
||||||
|
}
|
||||||
|
|
||||||
|
fn effective_intermediate_rgba(&self) -> RGBA {
|
||||||
|
RGBA {
|
||||||
|
alpha: self.color.alpha * (1. - self.foreground_ratio),
|
||||||
|
.. self.color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Animatable for Color {
|
||||||
|
#[inline]
|
||||||
|
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||||
|
// Common cases are interpolating between two numeric colors,
|
||||||
|
// two currentcolors, and a numeric color and a currentcolor.
|
||||||
|
//
|
||||||
|
// Note: this algorithm assumes self_portion + other_portion
|
||||||
|
// equals to one, so it may be broken for additive operation.
|
||||||
|
// To properly support additive color interpolation, we would
|
||||||
|
// need two ratio fields in computed color types.
|
||||||
|
if self.foreground_ratio == other.foreground_ratio {
|
||||||
|
if self.is_currentcolor() {
|
||||||
|
Ok(Color::currentcolor())
|
||||||
|
} else {
|
||||||
|
Ok(Color {
|
||||||
|
color: self.color.add_weighted(&other.color, self_portion, other_portion)?,
|
||||||
|
foreground_ratio: self.foreground_ratio,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if self.is_currentcolor() && other.is_numeric() {
|
||||||
|
Ok(Color {
|
||||||
|
color: other.color,
|
||||||
|
foreground_ratio: self_portion as f32,
|
||||||
|
})
|
||||||
|
} else if self.is_numeric() && other.is_currentcolor() {
|
||||||
|
Ok(Color {
|
||||||
|
color: self.color,
|
||||||
|
foreground_ratio: other_portion as f32,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// For interpolating between two complex colors, we need to
|
||||||
|
// generate colors with effective alpha value.
|
||||||
|
let self_color = self.effective_intermediate_rgba();
|
||||||
|
let other_color = other.effective_intermediate_rgba();
|
||||||
|
let color = self_color.add_weighted(&other_color, self_portion, other_portion)?;
|
||||||
|
// Then we compute the final foreground ratio, and derive
|
||||||
|
// the final alpha value from the effective alpha value.
|
||||||
|
let foreground_ratio = self.foreground_ratio
|
||||||
|
.add_weighted(&other.foreground_ratio, self_portion, other_portion)?;
|
||||||
|
let alpha = color.alpha / (1. - foreground_ratio);
|
||||||
|
Ok(Color {
|
||||||
|
color: RGBA {
|
||||||
|
alpha: alpha,
|
||||||
|
.. color
|
||||||
|
},
|
||||||
|
foreground_ratio: foreground_ratio,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComputeSquaredDistance for Color {
|
||||||
|
#[inline]
|
||||||
|
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
||||||
|
// All comments in add_weighted also applies here.
|
||||||
|
if self.foreground_ratio == other.foreground_ratio {
|
||||||
|
if self.is_currentcolor() {
|
||||||
|
Ok(SquaredDistance::Value(0.))
|
||||||
|
} else {
|
||||||
|
self.color.compute_squared_distance(&other.color)
|
||||||
|
}
|
||||||
|
} else if self.is_currentcolor() && other.is_numeric() {
|
||||||
|
Ok(
|
||||||
|
RGBA::transparent().compute_squared_distance(&other.color)? +
|
||||||
|
SquaredDistance::Value(1.),
|
||||||
|
)
|
||||||
|
} else if self.is_numeric() && other.is_currentcolor() {
|
||||||
|
Ok(
|
||||||
|
self.color.compute_squared_distance(&RGBA::transparent())? +
|
||||||
|
SquaredDistance::Value(1.),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let self_color = self.effective_intermediate_rgba();
|
||||||
|
let other_color = other.effective_intermediate_rgba();
|
||||||
|
Ok(
|
||||||
|
self_color.compute_squared_distance(&other_color)? +
|
||||||
|
self.foreground_ratio.compute_squared_distance(&other.foreground_ratio)?,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToAnimatedZero for Color {
|
||||||
|
#[inline]
|
||||||
|
fn to_animated_zero(&self) -> Result<Self, ()> {
|
||||||
|
/// FIXME(nox): This does not look correct to me.
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
//! Animated types for CSS values related to effects.
|
//! Animated types for CSS values related to effects.
|
||||||
|
|
||||||
use properties::animated_properties::{Animatable, IntermediateColor};
|
use properties::animated_properties::Animatable;
|
||||||
use properties::longhands::box_shadow::computed_value::T as ComputedBoxShadowList;
|
use properties::longhands::box_shadow::computed_value::T as ComputedBoxShadowList;
|
||||||
use properties::longhands::filter::computed_value::T as ComputedFilterList;
|
use properties::longhands::filter::computed_value::T as ComputedFilterList;
|
||||||
use properties::longhands::text_shadow::computed_value::T as ComputedTextShadowList;
|
use properties::longhands::text_shadow::computed_value::T as ComputedTextShadowList;
|
||||||
|
@ -12,6 +12,7 @@ use std::cmp;
|
||||||
#[cfg(not(feature = "gecko"))]
|
#[cfg(not(feature = "gecko"))]
|
||||||
use values::Impossible;
|
use values::Impossible;
|
||||||
use values::animated::{ToAnimatedValue, ToAnimatedZero};
|
use values::animated::{ToAnimatedValue, ToAnimatedZero};
|
||||||
|
use values::animated::color::Color;
|
||||||
use values::computed::{Angle, NonNegativeNumber};
|
use values::computed::{Angle, NonNegativeNumber};
|
||||||
use values::computed::length::{Length, NonNegativeLength};
|
use values::computed::length::{Length, NonNegativeLength};
|
||||||
use values::distance::{ComputeSquaredDistance, SquaredDistance};
|
use values::distance::{ComputeSquaredDistance, SquaredDistance};
|
||||||
|
@ -33,7 +34,7 @@ pub type TextShadowList = ShadowList<SimpleShadow>;
|
||||||
pub struct ShadowList<Shadow>(Vec<Shadow>);
|
pub struct ShadowList<Shadow>(Vec<Shadow>);
|
||||||
|
|
||||||
/// An animated value for a single `box-shadow`.
|
/// An animated value for a single `box-shadow`.
|
||||||
pub type BoxShadow = GenericBoxShadow<IntermediateColor, Length, NonNegativeLength, Length>;
|
pub type BoxShadow = GenericBoxShadow<Color, Length, NonNegativeLength, Length>;
|
||||||
|
|
||||||
/// An animated value for the `filter` property.
|
/// An animated value for the `filter` property.
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
|
@ -49,7 +50,7 @@ pub type Filter = GenericFilter<Angle, NonNegativeNumber, NonNegativeLength, Sim
|
||||||
pub type Filter = GenericFilter<Angle, NonNegativeNumber, NonNegativeLength, Impossible>;
|
pub type Filter = GenericFilter<Angle, NonNegativeNumber, NonNegativeLength, Impossible>;
|
||||||
|
|
||||||
/// An animated value for the `drop-shadow()` filter.
|
/// An animated value for the `drop-shadow()` filter.
|
||||||
pub type SimpleShadow = GenericSimpleShadow<IntermediateColor, Length, NonNegativeLength>;
|
pub type SimpleShadow = GenericSimpleShadow<Color, Length, NonNegativeLength>;
|
||||||
|
|
||||||
impl ToAnimatedValue for ComputedBoxShadowList {
|
impl ToAnimatedValue for ComputedBoxShadowList {
|
||||||
type AnimatedValue = BoxShadowList;
|
type AnimatedValue = BoxShadowList;
|
||||||
|
@ -231,7 +232,7 @@ impl ToAnimatedZero for SimpleShadow {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn to_animated_zero(&self) -> Result<Self, ()> {
|
fn to_animated_zero(&self) -> Result<Self, ()> {
|
||||||
Ok(SimpleShadow {
|
Ok(SimpleShadow {
|
||||||
color: IntermediateColor::transparent(),
|
color: Color::transparent(),
|
||||||
horizontal: self.horizontal.to_animated_zero()?,
|
horizontal: self.horizontal.to_animated_zero()?,
|
||||||
vertical: self.vertical.to_animated_zero()?,
|
vertical: self.vertical.to_animated_zero()?,
|
||||||
blur: self.blur.to_animated_zero()?,
|
blur: self.blur.to_animated_zero()?,
|
||||||
|
|
|
@ -24,6 +24,7 @@ use values::computed::NonNegativeNumber as ComputedNonNegativeNumber;
|
||||||
use values::computed::PositiveInteger as ComputedPositiveInteger;
|
use values::computed::PositiveInteger as ComputedPositiveInteger;
|
||||||
use values::specified::url::SpecifiedUrl;
|
use values::specified::url::SpecifiedUrl;
|
||||||
|
|
||||||
|
pub mod color;
|
||||||
pub mod effects;
|
pub mod effects;
|
||||||
|
|
||||||
/// Conversion between computed values and intermediate values for animations.
|
/// Conversion between computed values and intermediate values for animations.
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
//! Computed types for CSS values related to borders.
|
//! Computed types for CSS values related to borders.
|
||||||
|
|
||||||
|
use values::animated::ToAnimatedZero;
|
||||||
use values::computed::{Number, NumberOrPercentage};
|
use values::computed::{Number, NumberOrPercentage};
|
||||||
use values::computed::length::LengthOrPercentage;
|
use values::computed::length::LengthOrPercentage;
|
||||||
use values::generics::border::BorderCornerRadius as GenericBorderCornerRadius;
|
use values::generics::border::BorderCornerRadius as GenericBorderCornerRadius;
|
||||||
|
@ -34,3 +35,11 @@ impl BorderImageSideWidth {
|
||||||
GenericBorderImageSideWidth::Number(1.)
|
GenericBorderImageSideWidth::Number(1.)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToAnimatedZero for BorderCornerRadius {
|
||||||
|
#[inline]
|
||||||
|
fn to_animated_zero(&self) -> Result<Self, ()> {
|
||||||
|
/// FIXME(nox): Why?
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
use cssparser::{Color as CSSParserColor, RGBA};
|
use cssparser::{Color as CSSParserColor, RGBA};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use style_traits::ToCss;
|
use style_traits::ToCss;
|
||||||
|
use values::animated::ToAnimatedValue;
|
||||||
|
use values::animated::color::{Color as AnimatedColor, RGBA as AnimatedRGBA};
|
||||||
|
|
||||||
/// This struct represents a combined color from a numeric color and
|
/// This struct represents a combined color from a numeric color and
|
||||||
/// the current foreground color (currentcolor keyword).
|
/// the current foreground color (currentcolor keyword).
|
||||||
|
@ -21,13 +23,8 @@ pub struct Color {
|
||||||
pub foreground_ratio: u8,
|
pub foreground_ratio: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn blend_color_component(bg: u8, fg: u8, fg_alpha: u8) -> u8 {
|
/// Computed value type for the specified RGBAColor.
|
||||||
let bg_ratio = (u8::max_value() - fg_alpha) as u32;
|
pub type RGBAColor = RGBA;
|
||||||
let fg_ratio = fg_alpha as u32;
|
|
||||||
let color = bg as u32 * bg_ratio + fg as u32 * fg_ratio;
|
|
||||||
// Rounding divide the number by 255
|
|
||||||
((color + 127) / 255) as u8
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Color {
|
impl Color {
|
||||||
/// Returns a numeric color representing the given RGBA value.
|
/// Returns a numeric color representing the given RGBA value.
|
||||||
|
@ -72,6 +69,15 @@ impl Color {
|
||||||
if self.is_currentcolor() {
|
if self.is_currentcolor() {
|
||||||
return fg_color.clone();
|
return fg_color.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn blend_color_component(bg: u8, fg: u8, fg_alpha: u8) -> u8 {
|
||||||
|
let bg_ratio = (u8::max_value() - fg_alpha) as u32;
|
||||||
|
let fg_ratio = fg_alpha as u32;
|
||||||
|
let color = bg as u32 * bg_ratio + fg as u32 * fg_ratio;
|
||||||
|
// Rounding divide the number by 255
|
||||||
|
((color + 127) / 255) as u8
|
||||||
|
}
|
||||||
|
|
||||||
// Common case that alpha channel is equal (usually both are opaque).
|
// Common case that alpha channel is equal (usually both are opaque).
|
||||||
let fg_ratio = self.foreground_ratio;
|
let fg_ratio = self.foreground_ratio;
|
||||||
if self.color.alpha == fg_color.alpha {
|
if self.color.alpha == fg_color.alpha {
|
||||||
|
@ -140,5 +146,47 @@ impl ToCss for Color {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computed value type for the specified RGBAColor.
|
impl ToAnimatedValue for Color {
|
||||||
pub type RGBAColor = RGBA;
|
type AnimatedValue = AnimatedColor;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn to_animated_value(self) -> Self::AnimatedValue {
|
||||||
|
AnimatedColor {
|
||||||
|
color: self.color.to_animated_value(),
|
||||||
|
foreground_ratio: self.foreground_ratio as f32 * (1. / 255.),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
|
||||||
|
Color {
|
||||||
|
color: RGBA::from_animated_value(animated.color),
|
||||||
|
foreground_ratio: (animated.foreground_ratio * 255.).round() as u8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToAnimatedValue for RGBA {
|
||||||
|
type AnimatedValue = AnimatedRGBA;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn to_animated_value(self) -> Self::AnimatedValue {
|
||||||
|
AnimatedRGBA::new(
|
||||||
|
self.red_f32(),
|
||||||
|
self.green_f32(),
|
||||||
|
self.blue_f32(),
|
||||||
|
self.alpha_f32(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
|
||||||
|
// RGBA::from_floats clamps each component values.
|
||||||
|
RGBA::from_floats(
|
||||||
|
animated.red,
|
||||||
|
animated.green,
|
||||||
|
animated.blue,
|
||||||
|
animated.alpha,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -49,6 +49,7 @@ pub use super::generics::grid::GridLine;
|
||||||
pub use self::length::{CalcLengthOrPercentage, Length, LengthOrNone, LengthOrNumber, LengthOrPercentage};
|
pub use self::length::{CalcLengthOrPercentage, Length, LengthOrNone, LengthOrNumber, LengthOrPercentage};
|
||||||
pub use self::length::{LengthOrPercentageOrAuto, LengthOrPercentageOrNone, MaxLength, MozLength};
|
pub use self::length::{LengthOrPercentageOrAuto, LengthOrPercentageOrNone, MaxLength, MozLength};
|
||||||
pub use self::length::NonNegativeLengthOrPercentage;
|
pub use self::length::NonNegativeLengthOrPercentage;
|
||||||
|
pub use self::percentage::Percentage;
|
||||||
pub use self::position::Position;
|
pub use self::position::Position;
|
||||||
pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind, SVGStrokeDashArray, SVGWidth};
|
pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind, SVGStrokeDashArray, SVGWidth};
|
||||||
pub use self::text::{InitialLetter, LetterSpacing, LineHeight, WordSpacing};
|
pub use self::text::{InitialLetter, LetterSpacing, LineHeight, WordSpacing};
|
||||||
|
@ -66,6 +67,7 @@ pub mod image;
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub mod gecko;
|
pub mod gecko;
|
||||||
pub mod length;
|
pub mod length;
|
||||||
|
pub mod percentage;
|
||||||
pub mod position;
|
pub mod position;
|
||||||
pub mod rect;
|
pub mod rect;
|
||||||
pub mod svg;
|
pub mod svg;
|
||||||
|
@ -658,45 +660,6 @@ impl From<Au> for NonNegativeAu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A computed `<percentage>` value.
|
|
||||||
#[derive(Clone, ComputeSquaredDistance, Copy, Debug, Default, HasViewportPercentage, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "servo", derive(Deserialize, HeapSizeOf, Serialize))]
|
|
||||||
pub struct Percentage(pub CSSFloat);
|
|
||||||
|
|
||||||
impl Percentage {
|
|
||||||
/// 0%
|
|
||||||
#[inline]
|
|
||||||
pub fn zero() -> Self {
|
|
||||||
Percentage(0.)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 100%
|
|
||||||
#[inline]
|
|
||||||
pub fn hundred() -> Self {
|
|
||||||
Percentage(1.)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToCss for Percentage {
|
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
||||||
write!(dest, "{}%", self.0 * 100.)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToComputedValue for specified::Percentage {
|
|
||||||
type ComputedValue = Percentage;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn to_computed_value(&self, _: &Context) -> Percentage {
|
|
||||||
Percentage(self.get())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn from_computed_value(computed: &Percentage) -> Self {
|
|
||||||
specified::Percentage::new(computed.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The computed value of a CSS `url()`, resolved relative to the stylesheet URL.
|
/// The computed value of a CSS `url()`, resolved relative to the stylesheet URL.
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
#[derive(Clone, Debug, HeapSizeOf, Serialize, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, HeapSizeOf, Serialize, Deserialize, PartialEq)]
|
||||||
|
|
54
components/style/values/computed/percentage.rs
Normal file
54
components/style/values/computed/percentage.rs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
//! Computed percentages.
|
||||||
|
|
||||||
|
use properties::animated_properties::Animatable;
|
||||||
|
use std::fmt;
|
||||||
|
use style_traits::ToCss;
|
||||||
|
use values::CSSFloat;
|
||||||
|
use values::animated::ToAnimatedZero;
|
||||||
|
|
||||||
|
/// A computed percentage.
|
||||||
|
#[derive(Clone, ComputeSquaredDistance, Copy, Debug, Default, HasViewportPercentage, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "servo", derive(Deserialize, HeapSizeOf, Serialize))]
|
||||||
|
pub struct Percentage(pub CSSFloat);
|
||||||
|
|
||||||
|
impl Percentage {
|
||||||
|
/// 0%
|
||||||
|
#[inline]
|
||||||
|
pub fn zero() -> Self {
|
||||||
|
Percentage(0.)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 100%
|
||||||
|
#[inline]
|
||||||
|
pub fn hundred() -> Self {
|
||||||
|
Percentage(1.)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css-transitions/#animtype-percentage
|
||||||
|
impl Animatable for Percentage {
|
||||||
|
#[inline]
|
||||||
|
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||||
|
Ok(Percentage((self.0 as f64 * self_portion + other.0 as f64 * other_portion) as f32))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToAnimatedZero for Percentage {
|
||||||
|
#[inline]
|
||||||
|
fn to_animated_zero(&self) -> Result<Self, ()> {
|
||||||
|
Ok(Percentage(0.))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCss for Percentage {
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
||||||
|
where
|
||||||
|
W: fmt::Write,
|
||||||
|
{
|
||||||
|
write!(dest, "{}%", self.0 * 100.)
|
||||||
|
}
|
||||||
|
}
|
|
@ -785,7 +785,7 @@ impl From<NoCalcLength> for LengthOrPercentage {
|
||||||
impl From<Percentage> for LengthOrPercentage {
|
impl From<Percentage> for LengthOrPercentage {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(pc: Percentage) -> Self {
|
fn from(pc: Percentage) -> Self {
|
||||||
if pc.calc_clamping_mode.is_some() {
|
if pc.is_calc() {
|
||||||
LengthOrPercentage::Calc(Box::new(CalcLengthOrPercentage {
|
LengthOrPercentage::Calc(Box::new(CalcLengthOrPercentage {
|
||||||
percentage: Some(computed::Percentage(pc.get())),
|
percentage: Some(computed::Percentage(pc.get())),
|
||||||
.. Default::default()
|
.. Default::default()
|
||||||
|
|
|
@ -44,6 +44,7 @@ pub use self::length::{LengthOrPercentageOrNone, MaxLength, MozLength};
|
||||||
pub use self::length::{NoCalcLength, ViewportPercentageLength};
|
pub use self::length::{NoCalcLength, ViewportPercentageLength};
|
||||||
pub use self::length::NonNegativeLengthOrPercentage;
|
pub use self::length::NonNegativeLengthOrPercentage;
|
||||||
pub use self::rect::LengthOrNumberRect;
|
pub use self::rect::LengthOrNumberRect;
|
||||||
|
pub use self::percentage::Percentage;
|
||||||
pub use self::position::{Position, PositionComponent};
|
pub use self::position::{Position, PositionComponent};
|
||||||
pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind, SVGStrokeDashArray, SVGWidth};
|
pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind, SVGStrokeDashArray, SVGWidth};
|
||||||
pub use self::text::{InitialLetter, LetterSpacing, LineHeight, WordSpacing};
|
pub use self::text::{InitialLetter, LetterSpacing, LineHeight, WordSpacing};
|
||||||
|
@ -65,6 +66,7 @@ pub mod gecko;
|
||||||
pub mod grid;
|
pub mod grid;
|
||||||
pub mod image;
|
pub mod image;
|
||||||
pub mod length;
|
pub mod length;
|
||||||
|
pub mod percentage;
|
||||||
pub mod position;
|
pub mod position;
|
||||||
pub mod rect;
|
pub mod rect;
|
||||||
pub mod svg;
|
pub mod svg;
|
||||||
|
@ -1047,121 +1049,3 @@ impl ToCss for Attr {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ComputedValueAsSpecified for Attr {}
|
impl ComputedValueAsSpecified for Attr {}
|
||||||
|
|
||||||
/// A percentage value.
|
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
||||||
pub struct Percentage {
|
|
||||||
/// The percentage value as a float.
|
|
||||||
///
|
|
||||||
/// [0 .. 100%] maps to [0.0 .. 1.0]
|
|
||||||
value: CSSFloat,
|
|
||||||
/// If this percentage came from a calc() expression, this tells how
|
|
||||||
/// clamping should be done on the value.
|
|
||||||
calc_clamping_mode: Option<AllowedNumericType>,
|
|
||||||
}
|
|
||||||
|
|
||||||
no_viewport_percentage!(Percentage);
|
|
||||||
|
|
||||||
impl ToCss for Percentage {
|
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
|
||||||
where W: fmt::Write,
|
|
||||||
{
|
|
||||||
if self.calc_clamping_mode.is_some() {
|
|
||||||
dest.write_str("calc(")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(dest, "{}%", self.value * 100.)?;
|
|
||||||
|
|
||||||
if self.calc_clamping_mode.is_some() {
|
|
||||||
dest.write_str(")")?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Percentage {
|
|
||||||
/// Create a percentage from a numeric value.
|
|
||||||
pub fn new(value: CSSFloat) -> Self {
|
|
||||||
Self {
|
|
||||||
value,
|
|
||||||
calc_clamping_mode: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the underlying value for this float.
|
|
||||||
pub fn get(&self) -> CSSFloat {
|
|
||||||
self.calc_clamping_mode.map_or(self.value, |mode| mode.clamp(self.value))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reverse this percentage, preserving calc-ness.
|
|
||||||
///
|
|
||||||
/// For example: If it was 20%, convert it into 80%.
|
|
||||||
pub fn reverse(&mut self) {
|
|
||||||
let new_value = 1. - self.value;
|
|
||||||
self.value = new_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Parse a specific kind of percentage.
|
|
||||||
pub fn parse_with_clamping_mode<'i, 't>(
|
|
||||||
context: &ParserContext,
|
|
||||||
input: &mut Parser<'i, 't>,
|
|
||||||
num_context: AllowedNumericType,
|
|
||||||
) -> Result<Self, ParseError<'i>> {
|
|
||||||
// FIXME: remove early returns when lifetimes are non-lexical
|
|
||||||
match *input.next()? {
|
|
||||||
Token::Percentage { unit_value, .. } if num_context.is_ok(context.parsing_mode, unit_value) => {
|
|
||||||
return Ok(Percentage::new(unit_value))
|
|
||||||
}
|
|
||||||
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {}
|
|
||||||
ref t => return Err(BasicParseError::UnexpectedToken(t.clone()).into())
|
|
||||||
}
|
|
||||||
|
|
||||||
let result = input.parse_nested_block(|i| {
|
|
||||||
CalcNode::parse_percentage(context, i)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// TODO(emilio): -moz-image-rect is the only thing that uses
|
|
||||||
// the clamping mode... I guess we could disallow it...
|
|
||||||
Ok(Percentage {
|
|
||||||
value: result,
|
|
||||||
calc_clamping_mode: Some(num_context),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parses a percentage token, but rejects it if it's negative.
|
|
||||||
pub fn parse_non_negative<'i, 't>(context: &ParserContext,
|
|
||||||
input: &mut Parser<'i, 't>)
|
|
||||||
-> Result<Self, ParseError<'i>> {
|
|
||||||
Self::parse_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 0%
|
|
||||||
#[inline]
|
|
||||||
pub fn zero() -> Self {
|
|
||||||
Percentage {
|
|
||||||
value: 0.,
|
|
||||||
calc_clamping_mode: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 100%
|
|
||||||
#[inline]
|
|
||||||
pub fn hundred() -> Self {
|
|
||||||
Percentage {
|
|
||||||
value: 1.,
|
|
||||||
calc_clamping_mode: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parse for Percentage {
|
|
||||||
#[inline]
|
|
||||||
fn parse<'i, 't>(
|
|
||||||
context: &ParserContext,
|
|
||||||
input: &mut Parser<'i, 't>
|
|
||||||
) -> Result<Self, ParseError<'i>> {
|
|
||||||
Self::parse_with_clamping_mode(context, input, AllowedNumericType::All)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
152
components/style/values/specified/percentage.rs
Normal file
152
components/style/values/specified/percentage.rs
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
//! Specified percentages.
|
||||||
|
|
||||||
|
use cssparser::{BasicParseError, Parser, Token};
|
||||||
|
use parser::{Parse, ParserContext};
|
||||||
|
use std::ascii::AsciiExt;
|
||||||
|
use std::fmt;
|
||||||
|
use style_traits::{ParseError, ToCss};
|
||||||
|
use style_traits::values::specified::AllowedNumericType;
|
||||||
|
use values::CSSFloat;
|
||||||
|
use values::computed::{Context, ToComputedValue};
|
||||||
|
use values::computed::percentage::Percentage as ComputedPercentage;
|
||||||
|
use values::specified::calc::CalcNode;
|
||||||
|
|
||||||
|
/// A percentage value.
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
|
pub struct Percentage {
|
||||||
|
/// The percentage value as a float.
|
||||||
|
///
|
||||||
|
/// [0 .. 100%] maps to [0.0 .. 1.0]
|
||||||
|
value: CSSFloat,
|
||||||
|
/// If this percentage came from a calc() expression, this tells how
|
||||||
|
/// clamping should be done on the value.
|
||||||
|
calc_clamping_mode: Option<AllowedNumericType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
no_viewport_percentage!(Percentage);
|
||||||
|
|
||||||
|
impl ToCss for Percentage {
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
||||||
|
where W: fmt::Write,
|
||||||
|
{
|
||||||
|
if self.calc_clamping_mode.is_some() {
|
||||||
|
dest.write_str("calc(")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(dest, "{}%", self.value * 100.)?;
|
||||||
|
|
||||||
|
if self.calc_clamping_mode.is_some() {
|
||||||
|
dest.write_str(")")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Percentage {
|
||||||
|
/// Creates a percentage from a numeric value.
|
||||||
|
pub fn new(value: CSSFloat) -> Self {
|
||||||
|
Self {
|
||||||
|
value,
|
||||||
|
calc_clamping_mode: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `0%`
|
||||||
|
#[inline]
|
||||||
|
pub fn zero() -> Self {
|
||||||
|
Percentage {
|
||||||
|
value: 0.,
|
||||||
|
calc_clamping_mode: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `100%`
|
||||||
|
#[inline]
|
||||||
|
pub fn hundred() -> Self {
|
||||||
|
Percentage {
|
||||||
|
value: 1.,
|
||||||
|
calc_clamping_mode: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Gets the underlying value for this float.
|
||||||
|
pub fn get(&self) -> CSSFloat {
|
||||||
|
self.calc_clamping_mode.map_or(self.value, |mode| mode.clamp(self.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether this percentage is a `calc()` value.
|
||||||
|
pub fn is_calc(&self) -> bool {
|
||||||
|
self.calc_clamping_mode.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reverses this percentage, preserving calc-ness.
|
||||||
|
///
|
||||||
|
/// For example: If it was 20%, convert it into 80%.
|
||||||
|
pub fn reverse(&mut self) {
|
||||||
|
let new_value = 1. - self.value;
|
||||||
|
self.value = new_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses a specific kind of percentage.
|
||||||
|
pub fn parse_with_clamping_mode<'i, 't>(
|
||||||
|
context: &ParserContext,
|
||||||
|
input: &mut Parser<'i, 't>,
|
||||||
|
num_context: AllowedNumericType,
|
||||||
|
) -> Result<Self, ParseError<'i>> {
|
||||||
|
// FIXME: remove early returns when lifetimes are non-lexical
|
||||||
|
match *input.next()? {
|
||||||
|
Token::Percentage { unit_value, .. } if num_context.is_ok(context.parsing_mode, unit_value) => {
|
||||||
|
return Ok(Percentage::new(unit_value));
|
||||||
|
}
|
||||||
|
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {},
|
||||||
|
ref t => return Err(BasicParseError::UnexpectedToken(t.clone()).into()),
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = input.parse_nested_block(|i| {
|
||||||
|
CalcNode::parse_percentage(context, i)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// TODO(emilio): -moz-image-rect is the only thing that uses
|
||||||
|
// the clamping mode... I guess we could disallow it...
|
||||||
|
Ok(Percentage {
|
||||||
|
value: result,
|
||||||
|
calc_clamping_mode: Some(num_context),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses a percentage token, but rejects it if it's negative.
|
||||||
|
pub fn parse_non_negative<'i, 't>(
|
||||||
|
context: &ParserContext,
|
||||||
|
input: &mut Parser<'i, 't>,
|
||||||
|
) -> Result<Self, ParseError<'i>> {
|
||||||
|
Self::parse_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for Percentage {
|
||||||
|
#[inline]
|
||||||
|
fn parse<'i, 't>(
|
||||||
|
context: &ParserContext,
|
||||||
|
input: &mut Parser<'i, 't>
|
||||||
|
) -> Result<Self, ParseError<'i>> {
|
||||||
|
Self::parse_with_clamping_mode(context, input, AllowedNumericType::All)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToComputedValue for Percentage {
|
||||||
|
type ComputedValue = ComputedPercentage;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
|
||||||
|
ComputedPercentage(self.get())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||||
|
Percentage::new(computed.0)
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
//! Computed types for CSS borders.
|
//! Specified types for CSS borders.
|
||||||
|
|
||||||
use cssparser::Parser;
|
use cssparser::Parser;
|
||||||
use parser::ParserContext;
|
use parser::ParserContext;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue