Implement #[animate(fallback)] for #[derive(Animate)]

This allows us to derive the Animate trait, providing a fallback function
for when the 2 values aren't similar.
This commit is contained in:
Anthony Ramine 2017-08-26 16:25:28 +02:00
parent 3751fe9fdc
commit 4a4bf89575
7 changed files with 99 additions and 121 deletions

View file

@ -812,73 +812,6 @@ impl Animate for CalcLengthOrPercentage {
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
impl Animate for LengthOrPercentage {
#[inline]
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
match (self, other) {
(
&LengthOrPercentage::Length(ref this),
&LengthOrPercentage::Length(ref other),
) => {
Ok(LengthOrPercentage::Length(this.animate(other, procedure)?))
},
(
&LengthOrPercentage::Percentage(ref this),
&LengthOrPercentage::Percentage(ref other),
) => {
Ok(LengthOrPercentage::Percentage(this.animate(other, procedure)?))
},
(this, other) => {
// Special handling for zero values since these should not require calc().
if this.is_definitely_zero() {
return other.to_animated_zero()?.animate(other, procedure);
}
if other.is_definitely_zero() {
return this.animate(&this.to_animated_zero()?, procedure);
}
let this = CalcLengthOrPercentage::from(*this);
let other = CalcLengthOrPercentage::from(*other);
Ok(LengthOrPercentage::Calc(this.animate(&other, procedure)?))
}
}
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
impl Animate for LengthOrPercentageOrAuto {
#[inline]
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
match (self, other) {
(
&LengthOrPercentageOrAuto::Length(ref this),
&LengthOrPercentageOrAuto::Length(ref other),
) => {
Ok(LengthOrPercentageOrAuto::Length(this.animate(other, procedure)?))
},
(
&LengthOrPercentageOrAuto::Percentage(ref this),
&LengthOrPercentageOrAuto::Percentage(ref other),
) => {
Ok(LengthOrPercentageOrAuto::Percentage(
this.animate(other, procedure)?,
))
},
(&LengthOrPercentageOrAuto::Auto, &LengthOrPercentageOrAuto::Auto) => {
Ok(LengthOrPercentageOrAuto::Auto)
},
(this, other) => {
let this: Option<CalcLengthOrPercentage> = From::from(*this);
let other: Option<CalcLengthOrPercentage> = From::from(*other);
Ok(LengthOrPercentageOrAuto::Calc(
this.animate(&other, procedure)?.ok_or(())?,
))
},
}
}
}
impl ToAnimatedZero for LengthOrPercentageOrAuto {
#[inline]
fn to_animated_zero(&self) -> Result<Self, ()> {
@ -893,39 +826,6 @@ impl ToAnimatedZero for LengthOrPercentageOrAuto {
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
impl Animate for LengthOrPercentageOrNone {
#[inline]
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
match (self, other) {
(
&LengthOrPercentageOrNone::Length(ref this),
&LengthOrPercentageOrNone::Length(ref other),
) => {
Ok(LengthOrPercentageOrNone::Length(this.animate(other, procedure)?))
},
(
&LengthOrPercentageOrNone::Percentage(ref this),
&LengthOrPercentageOrNone::Percentage(ref other),
) => {
Ok(LengthOrPercentageOrNone::Percentage(
this.animate(other, procedure)?,
))
}
(&LengthOrPercentageOrNone::None, &LengthOrPercentageOrNone::None) => {
Ok(LengthOrPercentageOrNone::None)
},
(this, other) => {
let this = <Option<CalcLengthOrPercentage>>::from(*this);
let other = <Option<CalcLengthOrPercentage>>::from(*other);
Ok(LengthOrPercentageOrNone::Calc(
this.animate(&other, procedure)?.ok_or(())?,
))
},
}
}
}
impl ToAnimatedZero for LengthOrPercentageOrNone {
#[inline]
fn to_animated_zero(&self) -> Result<Self, ()> {

View file

@ -11,6 +11,7 @@ use style_traits::ToCss;
use style_traits::values::specified::AllowedLengthType;
use super::{Number, ToComputedValue, Context, Percentage};
use values::{Auto, CSSFloat, Either, ExtremumLength, None_, Normal, specified};
use values::animated::{Animate, Procedure, ToAnimatedZero};
use values::computed::{NonNegativeAu, NonNegativeNumber};
use values::distance::{ComputeSquaredDistance, SquaredDistance};
use values::generics::NonNegative;
@ -274,14 +275,36 @@ impl ToComputedValue for specified::CalcLengthOrPercentage {
}
#[allow(missing_docs)]
#[animate(fallback = "Self::animate_fallback")]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Copy, PartialEq, ToAnimatedZero, ToCss)]
#[derive(Animate, Clone, Copy, PartialEq, ToAnimatedZero, ToCss)]
pub enum LengthOrPercentage {
Length(Au),
Percentage(Percentage),
Calc(CalcLengthOrPercentage),
}
impl LengthOrPercentage {
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
fn animate_fallback(
&self,
other: &Self,
procedure: Procedure,
) -> Result<Self, ()> {
// Special handling for zero values since these should not require calc().
if self.is_definitely_zero() {
return other.to_animated_zero()?.animate(other, procedure);
}
if other.is_definitely_zero() {
return self.animate(&self.to_animated_zero()?, procedure);
}
let this = CalcLengthOrPercentage::from(*self);
let other = CalcLengthOrPercentage::from(*other);
Ok(LengthOrPercentage::Calc(this.animate(&other, procedure)?))
}
}
impl ComputeSquaredDistance for LengthOrPercentage {
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
@ -415,8 +438,9 @@ impl ToComputedValue for specified::LengthOrPercentage {
}
#[allow(missing_docs)]
#[animate(fallback = "Self::animate_fallback")]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Copy, PartialEq, ToCss)]
#[derive(Animate, Clone, Copy, PartialEq, ToCss)]
pub enum LengthOrPercentageOrAuto {
Length(Au),
Percentage(Percentage),
@ -424,6 +448,21 @@ pub enum LengthOrPercentageOrAuto {
Calc(CalcLengthOrPercentage),
}
impl LengthOrPercentageOrAuto {
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
fn animate_fallback(
&self,
other: &Self,
procedure: Procedure,
) -> Result<Self, ()> {
let this = <Option<CalcLengthOrPercentage>>::from(*self);
let other = <Option<CalcLengthOrPercentage>>::from(*other);
Ok(LengthOrPercentageOrAuto::Calc(
this.animate(&other, procedure)?.ok_or(())?,
))
}
}
impl ComputeSquaredDistance for LengthOrPercentageOrAuto {
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
@ -510,8 +549,9 @@ impl ToComputedValue for specified::LengthOrPercentageOrAuto {
}
#[allow(missing_docs)]
#[animate(fallback = "Self::animate_fallback")]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Copy, PartialEq, ToCss)]
#[derive(Animate, Clone, Copy, PartialEq, ToCss)]
pub enum LengthOrPercentageOrNone {
Length(Au),
Percentage(Percentage),
@ -519,6 +559,21 @@ pub enum LengthOrPercentageOrNone {
None,
}
impl LengthOrPercentageOrNone {
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
fn animate_fallback(
&self,
other: &Self,
procedure: Procedure,
) -> Result<Self, ()> {
let this = <Option<CalcLengthOrPercentage>>::from(*self);
let other = <Option<CalcLengthOrPercentage>>::from(*other);
Ok(LengthOrPercentageOrNone::Calc(
this.animate(&other, procedure)?.ok_or(())?,
))
}
}
impl ComputeSquaredDistance for LengthOrPercentageOrNone {
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {

View file

@ -3,21 +3,22 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use cg;
use quote;
use syn;
use quote::Tokens;
use syn::{DeriveInput, Path};
pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
pub fn derive(input: DeriveInput) -> Tokens {
let name = &input.ident;
let trait_path = &["values", "animated", "Animate"];
let (impl_generics, ty_generics, mut where_clause) =
cg::trait_parts(&input, trait_path);
let input_attrs = cg::parse_input_attrs::<AnimateInputAttrs>(&input);
let variants = cg::variants(&input);
let mut match_body = quote!();
let mut append_error_clause = variants.len() > 1;
match_body.append_all(variants.iter().flat_map(|variant| {
let attrs = cg::parse_variant_attrs::<AnimateAttrs>(variant);
if attrs.error {
let variant_attrs = cg::parse_variant_attrs::<AnimationVariantAttrs>(variant);
if variant_attrs.error {
append_error_clause = true;
return None;
}
@ -28,7 +29,7 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
let mut computations = quote!();
let iter = result_info.iter().zip(this_info.iter().zip(&other_info));
computations.append_all(iter.map(|(result, (this, other))| {
let field_attrs = cg::parse_field_attrs::<AnimateFieldAttrs>(&result.field);
let field_attrs = cg::parse_field_attrs::<AnimationFieldAttrs>(&result.field);
if field_attrs.constant {
if cg::is_parameterized(&result.field.ty, where_clause.params, None) {
where_clause.inner.predicates.push(cg::where_predicate(
@ -65,7 +66,13 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
}));
if append_error_clause {
match_body = quote! { #match_body, _ => Err(()), };
if let Some(fallback) = input_attrs.fallback {
match_body.append(quote! {
(this, other) => #fallback(this, other, procedure)
});
} else {
match_body.append(quote! { _ => Err(()) });
}
}
quote! {
@ -85,14 +92,20 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
}
}
#[derive(Default, FromVariant)]
#[darling(attributes(animate), default)]
#[derive(Default, FromDeriveInput)]
struct AnimateInputAttrs {
fallback: Option<Path>,
}
#[darling(attributes(animation), default)]
pub struct AnimateAttrs {
#[derive(Default, FromVariant)]
pub struct AnimationVariantAttrs {
pub error: bool,
}
#[derive(Default, FromField)]
#[darling(attributes(animation), default)]
pub struct AnimateFieldAttrs {
#[derive(Default, FromField)]
pub struct AnimationFieldAttrs {
pub constant: bool,
}

View file

@ -2,7 +2,7 @@
* 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/. */
use darling::{FromField, FromVariant};
use darling::{FromDeriveInput, FromField, FromVariant};
use quote::{ToTokens, Tokens};
use std::borrow::Cow;
use std::collections::HashSet;
@ -276,6 +276,16 @@ where
}
}
pub fn parse_input_attrs<A>(input: &DeriveInput) -> A
where
A: FromDeriveInput,
{
match A::from_derive_input(input) {
Ok(attrs) => attrs,
Err(e) => panic!("failed to parse input attributes: {}", e),
}
}
pub fn parse_variant_attrs<A>(variant: &Variant) -> A
where
A: FromVariant,

View file

@ -2,7 +2,7 @@
* 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/. */
use animate::AnimateAttrs;
use animate::AnimationVariantAttrs;
use cg;
use quote;
use syn;
@ -17,7 +17,7 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
let mut match_body = quote!();
let mut append_error_clause = variants.len() > 1;
match_body.append_all(variants.iter().map(|variant| {
let attrs = cg::parse_variant_attrs::<AnimateAttrs>(variant);
let attrs = cg::parse_variant_attrs::<AnimationVariantAttrs>(variant);
if attrs.error {
append_error_clause = true;
return None;

View file

@ -19,7 +19,7 @@ mod to_animated_zero;
mod to_computed_value;
mod to_css;
#[proc_macro_derive(Animate, attributes(animation))]
#[proc_macro_derive(Animate, attributes(animate, animation))]
pub fn derive_animate(stream: TokenStream) -> TokenStream {
let input = syn::parse_derive_input(&stream.to_string()).unwrap();
animate::derive(input).to_string().parse().unwrap()

View file

@ -2,7 +2,7 @@
* 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/. */
use animate::{AnimateAttrs, AnimateFieldAttrs};
use animate::{AnimationVariantAttrs, AnimationFieldAttrs};
use cg;
use quote;
use syn;
@ -16,7 +16,7 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
let bind_opts = BindStyle::Ref.into();
let to_body = synstructure::each_variant(&input, &bind_opts, |bindings, variant| {
let attrs = cg::parse_variant_attrs::<AnimateAttrs>(variant);
let attrs = cg::parse_variant_attrs::<AnimationVariantAttrs>(variant);
if attrs.error {
return Some(quote! { Err(()) });
}
@ -25,7 +25,7 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
let bindings_pairs = bindings.into_iter().zip(mapped_bindings);
let mut computations = quote!();
computations.append_all(bindings_pairs.map(|(binding, mapped_binding)| {
let field_attrs = cg::parse_field_attrs::<AnimateFieldAttrs>(&binding.field);
let field_attrs = cg::parse_field_attrs::<AnimationFieldAttrs>(&binding.field);
if field_attrs.constant {
if cg::is_parameterized(&binding.field.ty, where_clause.params, None) {
where_clause.inner.predicates.push(cg::where_predicate(