--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -42,22 +42,28 @@ use values::animated::effects::BoxShadow
use values::animated::effects::Filter as AnimatedFilter;
use values::animated::effects::FilterList as AnimatedFilterList;
use values::animated::effects::TextShadowList as AnimatedTextShadowList;
use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
use values::computed::{BorderCornerRadius, ClipRect};
use values::computed::{CalcLengthOrPercentage, Color, Context, ComputedValueAsSpecified};
use values::computed::{LengthOrPercentage, MaxLength, MozLength, Percentage, ToComputedValue};
use values::computed::{NonNegativeAu, NonNegativeNumber, PositiveIntegerOrAuto};
+use values::computed::basic_shape::{BasicShape, Circle, Ellipse, InsetRect, ShapeRadius};
+use values::computed::border::BorderRadius;
use values::computed::length::{NonNegativeLengthOrAuto, NonNegativeLengthOrNormal};
use values::computed::length::NonNegativeLengthOrPercentage;
use values::generics::{GreaterThanOrEqualToOne, NonNegative};
+use values::generics::basic_shape::{Polygon, ShapeSource};
+use values::generics::basic_shape::BasicShape as GenericBasicShape;
+use values::generics::basic_shape::ShapeRadius as GenericShapeRadius;
use values::generics::border::BorderCornerRadius as GenericBorderCornerRadius;
use values::generics::effects::Filter;
use values::generics::position as generic_position;
+use values::generics::rect::Rect;
use values::generics::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind, SVGStrokeDashArray};
/// A trait used to implement various procedures used during animation.
pub trait Animatable: Sized {
/// Performs a weighted sum of this value and |other|. This is used for
/// interpolation and addition of animation values.
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
-> Result<Self, ()>;
@@ -3324,16 +3330,264 @@ impl Animatable for AnimatedFilterList {
compute_filter_square_distance(&none, &list)?
},
};
}
Ok(square_distance)
}
}
+impl<RefBox: PartialEq> ShapeSource<BasicShape, RefBox> {
+ fn get_animatable_values<'a>(source1: &'a Self, source2: &'a Self)
+ -> Result<(&'a BasicShape,
+ &'a BasicShape, &'a Option<RefBox>), ()> {
+ if let (&ShapeSource::Shape(ref shape1, ref refbox1),
+ &ShapeSource::Shape(ref shape2, ref refbox2)) = (source1, source2) {
+ if refbox1 == refbox2 {
+ return Ok((shape1, shape2, refbox1));
+ };
+ };
+ Err(())
+ }
+}
+
+impl<RefBox: PartialEq + Clone> Animatable for ShapeSource<BasicShape, RefBox> {
+ #[inline]
+ fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+ if let Ok((self_shape, other_shape, refbox)) =
+ Self::get_animatable_values(self, other) {
+ if let Ok(shape) = self_shape.add_weighted(other_shape, self_portion, other_portion) {
+ return Ok(ShapeSource::Shape(shape, refbox.clone()));
+ };
+ };
+ Err(())
+ }
+
+ #[inline]
+ fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
+ if let Ok((self_shape, other_shape, _)) =
+ Self::get_animatable_values(self, other) {
+ return self_shape.compute_distance(other_shape);
+ };
+ Err(())
+ }
+}
+
+impl Animatable for BasicShape {
+ <% basic_shapes = ["Inset", "Circle", "Ellipse", "Polygon"] %>
+ #[inline]
+ fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+ match (self, other) {
+ % for basic_shape in basic_shapes:
+ (&GenericBasicShape::${basic_shape}(ref self_shape),
+ &GenericBasicShape::${basic_shape}(ref other_shape)) => {
+ if let Ok(shape) =
+ self_shape.add_weighted(&other_shape, self_portion, other_portion) {
+ Ok(GenericBasicShape::${basic_shape}(shape))
+ } else {
+ Err(())
+ }
+ },
+ % endfor
+ _ => Err(())
+ }
+ }
+
+ #[inline]
+ fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
+ match (self, other) {
+ % for basic_shape in basic_shapes:
+ (&GenericBasicShape::${basic_shape}(ref self_shape),
+ &GenericBasicShape::${basic_shape}(ref other_shape)) => {
+ self_shape.compute_distance(&other_shape)
+ },
+ % endfor
+ _ => Err(())
+ }
+ }
+}
+
+impl Animatable for ShapeRadius {
+ #[inline]
+ fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+ match (self, other) {
+ (&GenericShapeRadius::Length(self_length), &GenericShapeRadius::Length(other_length)) => {
+ let value =
+ self_length.add_weighted(&other_length, self_portion, other_portion).unwrap();
+ Ok(GenericShapeRadius::Length(value))
+ },
+ _ => Err(())
+ }
+ }
+
+ #[inline]
+ fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
+ match (self, other) {
+ (&GenericShapeRadius::Length(self_length), &GenericShapeRadius::Length(other_length)) =>
+ self_length.compute_distance(&other_length),
+ _ => Err(())
+ }
+ }
+}
+
+impl Animatable for InsetRect {
+ #[inline]
+ fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+ let round = match (self.round, other.round) {
+ (Some(self_round), Some(other_round)) => {
+ if let Ok(round) =
+ self_round.add_weighted(&other_round, self_portion, other_portion) {
+ Some(round)
+ } else {
+ return Err(());
+ }
+ },
+ (None, None) => None,
+ _ => {
+ return Err(());
+ }
+ };
+ let rect = self.rect.add_weighted(&other.rect, self_portion, other_portion).unwrap();
+ Ok(InsetRect{rect, round})
+ }
+
+ #[inline]
+ fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
+ let mut distance = self.rect.compute_distance(&other.rect).unwrap();
+ if let (Some(self_round), Some(other_round)) = (self.round, other.round) {
+ distance += self_round.compute_distance(&other_round).unwrap();
+ }
+ Ok(distance)
+ }
+}
+
+impl Animatable for Circle {
+ #[inline]
+ fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+ if let Ok(radius) =
+ self.radius.add_weighted(&other.radius, self_portion, other_portion) {
+ let position =
+ self.position.add_weighted(&other.position, self_portion, other_portion).unwrap();
+ Ok(Circle{position, radius})
+ } else {
+ Err(())
+ }
+ }
+
+ #[inline]
+ fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
+ self.radius.compute_distance(&other.radius)
+ }
+}
+
+impl Animatable for Ellipse {
+ #[inline]
+ fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+ if let (Ok(semiaxis_x), Ok(semiaxis_y)) =
+ (self.semiaxis_x.add_weighted(&other.semiaxis_x, self_portion, other_portion),
+ self.semiaxis_y.add_weighted(&other.semiaxis_y, self_portion, other_portion)) {
+ let position =
+ self.position.add_weighted(&other.position, self_portion, other_portion).unwrap();
+ Ok(Ellipse{position, semiaxis_x, semiaxis_y})
+ } else {
+ Err(())
+ }
+ }
+
+ #[inline]
+ fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
+ if let (Ok(semiaxis_x), Ok(semiaxis_y)) =
+ (self.semiaxis_x.compute_distance(&other.semiaxis_x),
+ self.semiaxis_y.compute_distance(&other.semiaxis_y)) {
+ Ok(semiaxis_x + semiaxis_y)
+ } else {
+ Err(())
+ }
+
+ }
+}
+
+impl Animatable for Polygon<LengthOrPercentage> {
+ #[inline]
+ fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+ if self.fill == other.fill && self.coordinates.len() == other.coordinates.len() {
+ let fill = self.fill;
+ let coordinates: Vec<(LengthOrPercentage, LengthOrPercentage)> =
+ self.coordinates.iter().zip(other.coordinates.iter())
+ .map(|(&(self_x, self_y), &(other_x, other_y))| {
+ let x = self_x.add_weighted(&other_x, self_portion, other_portion).unwrap();
+ let y = self_y.add_weighted(&other_y, self_portion, other_portion).unwrap();
+ (x, y)
+ }).collect();
+ Ok(Polygon{fill, coordinates})
+ } else {
+ Err(())
+ }
+ }
+
+ #[inline]
+ fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
+ if self.fill == other.fill && self.coordinates.len() == other.coordinates.len() {
+ let mut distance = 0_f64;
+ for (&(self_x, self_y), &(other_x, other_y)) in
+ self.coordinates.iter().zip(other.coordinates.iter()) {
+ distance += self_x.compute_distance(&other_x).unwrap();
+ distance += self_y.compute_distance(&other_y).unwrap();
+ }
+ Ok(distance)
+ } else {
+ Err(())
+ }
+ }
+}
+
+impl Animatable for BorderRadius {
+ #[inline]
+ fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+ let top_left =
+ self.top_left.add_weighted(&other.top_left, self_portion, other_portion).unwrap();
+ let top_right =
+ self.top_right.add_weighted(&other.top_right, self_portion, other_portion).unwrap();
+ let bottom_right =
+ self.bottom_right.add_weighted(&other.bottom_right, self_portion, other_portion).unwrap();
+ let bottom_left =
+ self.bottom_left.add_weighted(&other.bottom_left, self_portion, other_portion).unwrap();
+ Ok(BorderRadius{top_left, top_right, bottom_right, bottom_left})
+ }
+
+ #[inline]
+ fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
+ let mut distance = self.top_left.compute_distance(&other.top_left).unwrap();
+ distance += self.top_right.compute_distance(&other.top_right).unwrap();
+ distance += self.bottom_right.compute_distance(&other.bottom_right).unwrap();
+ distance += self.bottom_left.compute_distance(&other.bottom_left).unwrap();
+ Ok(distance)
+ }
+}
+
+impl Animatable for Rect<LengthOrPercentage> {
+ #[inline]
+ fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+ let x = self.0.add_weighted(&other.0, self_portion, other_portion).unwrap();
+ let y = self.1.add_weighted(&other.1, self_portion, other_portion).unwrap();
+ let w = self.2.add_weighted(&other.2, self_portion, other_portion).unwrap();
+ let h = self.3.add_weighted(&other.3, self_portion, other_portion).unwrap();
+ Ok(Rect(x, y, w, h))
+ }
+
+ #[inline]
+ fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
+ let mut distance = self.0.compute_distance(&other.0).unwrap();
+ distance += self.1.compute_distance(&other.1).unwrap();
+ distance += self.2.compute_distance(&other.2).unwrap();
+ distance += self.3.compute_distance(&other.3).unwrap();
+ Ok(distance)
+ }
+}
+
/// A comparator to sort PropertyIds such that longhands are sorted before shorthands,
/// shorthands with fewer components are sorted before shorthands with more components,
/// and otherwise shorthands are sorted by IDL name as defined by [Web Animations][property-order].
///
/// Using this allows us to prioritize values specified by longhands (or smaller
/// shorthand subsets) when longhands and shorthands are both specified on the one keyframe.
///
/// Example orderings that result from this: