mirror of
https://github.com/servo/servo.git
synced 2025-08-07 22:45:34 +01:00
Auto merge of #17167 - servo:derive-all-the-things, r=emilio
Use generics for transition-timing-function 📈 <!-- 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/17167) <!-- Reviewable:end -->
This commit is contained in:
commit
621c1a4f96
8 changed files with 300 additions and 412 deletions
|
@ -16,14 +16,14 @@ use properties::animated_properties::{AnimatedProperty, TransitionProperty};
|
||||||
use properties::longhands::animation_direction::computed_value::single_value::T as AnimationDirection;
|
use properties::longhands::animation_direction::computed_value::single_value::T as AnimationDirection;
|
||||||
use properties::longhands::animation_iteration_count::single_value::computed_value::T as AnimationIterationCount;
|
use properties::longhands::animation_iteration_count::single_value::computed_value::T as AnimationIterationCount;
|
||||||
use properties::longhands::animation_play_state::computed_value::single_value::T as AnimationPlayState;
|
use properties::longhands::animation_play_state::computed_value::single_value::T as AnimationPlayState;
|
||||||
use properties::longhands::transition_timing_function::single_value::computed_value::StartEnd;
|
|
||||||
use properties::longhands::transition_timing_function::single_value::computed_value::T as TransitionTimingFunction;
|
|
||||||
use rule_tree::CascadeLevel;
|
use rule_tree::CascadeLevel;
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
use stylearc::Arc;
|
use stylearc::Arc;
|
||||||
use stylesheets::keyframes_rule::{KeyframesStep, KeyframesStepValue};
|
use stylesheets::keyframes_rule::{KeyframesStep, KeyframesStepValue};
|
||||||
use timer::Timer;
|
use timer::Timer;
|
||||||
use values::computed::Time;
|
use values::computed::Time;
|
||||||
|
use values::computed::transform::TimingFunction;
|
||||||
|
use values::generics::transform::{StepPosition, TimingFunction as GenericTimingFunction};
|
||||||
|
|
||||||
/// This structure represents a keyframes animation current iteration state.
|
/// This structure represents a keyframes animation current iteration state.
|
||||||
///
|
///
|
||||||
|
@ -258,7 +258,7 @@ pub struct AnimationFrame {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PropertyAnimation {
|
pub struct PropertyAnimation {
|
||||||
property: AnimatedProperty,
|
property: AnimatedProperty,
|
||||||
timing_function: TransitionTimingFunction,
|
timing_function: TimingFunction,
|
||||||
duration: Time, // TODO: isn't this just repeated?
|
duration: Time, // TODO: isn't this just repeated?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,7 +323,7 @@ impl PropertyAnimation {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_transition_property(transition_property: &TransitionProperty,
|
fn from_transition_property(transition_property: &TransitionProperty,
|
||||||
timing_function: TransitionTimingFunction,
|
timing_function: TimingFunction,
|
||||||
duration: Time,
|
duration: Time,
|
||||||
old_style: &ComputedValues,
|
old_style: &ComputedValues,
|
||||||
new_style: &ComputedValues)
|
new_style: &ComputedValues)
|
||||||
|
@ -349,25 +349,26 @@ impl PropertyAnimation {
|
||||||
|
|
||||||
/// Update the given animation at a given point of progress.
|
/// Update the given animation at a given point of progress.
|
||||||
pub fn update(&self, style: &mut ComputedValues, time: f64) {
|
pub fn update(&self, style: &mut ComputedValues, time: f64) {
|
||||||
let timing_function = match self.timing_function {
|
let solve_bezier = |(p1, p2): (Point2D<_>, Point2D<_>)| {
|
||||||
TransitionTimingFunction::Keyword(keyword) =>
|
let epsilon = 1. / (200. * (self.duration.seconds() as f64));
|
||||||
keyword.to_non_keyword_value(),
|
let bezier = Bezier::new(
|
||||||
other => other,
|
Point2D::new(p1.x as f64, p1.y as f64),
|
||||||
|
Point2D::new(p2.x as f64, p2.y as f64),
|
||||||
|
);
|
||||||
|
bezier.solve(time, epsilon)
|
||||||
};
|
};
|
||||||
let progress = match timing_function {
|
|
||||||
TransitionTimingFunction::CubicBezier(p1, p2) => {
|
let progress = match self.timing_function {
|
||||||
// See `WebCore::AnimationBase::solveEpsilon(double)` in WebKit.
|
GenericTimingFunction::CubicBezier(p1, p2) => {
|
||||||
let epsilon = 1.0 / (200.0 * (self.duration.seconds() as f64));
|
solve_bezier((p1, p2))
|
||||||
Bezier::new(Point2D::new(p1.x as f64, p1.y as f64),
|
|
||||||
Point2D::new(p2.x as f64, p2.y as f64)).solve(time, epsilon)
|
|
||||||
},
|
},
|
||||||
TransitionTimingFunction::Steps(steps, StartEnd::Start) => {
|
GenericTimingFunction::Steps(steps, StepPosition::Start) => {
|
||||||
(time * (steps as f64)).ceil() / (steps as f64)
|
(time * (steps as f64)).ceil() / (steps as f64)
|
||||||
},
|
},
|
||||||
TransitionTimingFunction::Steps(steps, StartEnd::End) => {
|
GenericTimingFunction::Steps(steps, StepPosition::End) => {
|
||||||
(time * (steps as f64)).floor() / (steps as f64)
|
(time * (steps as f64)).floor() / (steps as f64)
|
||||||
},
|
},
|
||||||
TransitionTimingFunction::Frames(frames) => {
|
GenericTimingFunction::Frames(frames) => {
|
||||||
// https://drafts.csswg.org/css-timing/#frames-timing-functions
|
// https://drafts.csswg.org/css-timing/#frames-timing-functions
|
||||||
let mut out = (time * (frames as f64)).floor() / ((frames - 1) as f64);
|
let mut out = (time * (frames as f64)).floor() / ((frames - 1) as f64);
|
||||||
if out > 1.0 {
|
if out > 1.0 {
|
||||||
|
@ -383,8 +384,8 @@ impl PropertyAnimation {
|
||||||
}
|
}
|
||||||
out
|
out
|
||||||
},
|
},
|
||||||
TransitionTimingFunction::Keyword(_) => {
|
GenericTimingFunction::Keyword(keyword) => {
|
||||||
panic!("Keyword function should not appear")
|
solve_bezier(keyword.to_bezier_points())
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,12 +4,11 @@
|
||||||
|
|
||||||
use euclid::point::{Point2D, TypedPoint2D};
|
use euclid::point::{Point2D, TypedPoint2D};
|
||||||
use gecko_bindings::structs::{nsTimingFunction, nsTimingFunction_Type};
|
use gecko_bindings::structs::{nsTimingFunction, nsTimingFunction_Type};
|
||||||
use properties::longhands::transition_timing_function::single_value::FunctionKeyword;
|
|
||||||
use properties::longhands::transition_timing_function::single_value::SpecifiedValue as SpecifiedTimingFunction;
|
|
||||||
use properties::longhands::transition_timing_function::single_value::computed_value::StartEnd;
|
|
||||||
use properties::longhands::transition_timing_function::single_value::computed_value::T as ComputedTimingFunction;
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use values::computed::ToComputedValue;
|
use values::computed::ToComputedValue;
|
||||||
|
use values::computed::transform::TimingFunction as ComputedTimingFunction;
|
||||||
|
use values::generics::transform::{StepPosition, TimingFunction as GenericTimingFunction, TimingKeyword};
|
||||||
|
use values::specified::transform::TimingFunction;
|
||||||
|
|
||||||
impl nsTimingFunction {
|
impl nsTimingFunction {
|
||||||
fn set_as_step(&mut self, function_type: nsTimingFunction_Type, steps: u32) {
|
fn set_as_step(&mut self, function_type: nsTimingFunction_Type, steps: u32) {
|
||||||
|
@ -47,69 +46,35 @@ impl nsTimingFunction {
|
||||||
|
|
||||||
impl From<ComputedTimingFunction> for nsTimingFunction {
|
impl From<ComputedTimingFunction> for nsTimingFunction {
|
||||||
fn from(function: ComputedTimingFunction) -> nsTimingFunction {
|
fn from(function: ComputedTimingFunction) -> nsTimingFunction {
|
||||||
SpecifiedTimingFunction::from_computed_value(&function).into()
|
TimingFunction::from_computed_value(&function).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<SpecifiedTimingFunction> for nsTimingFunction {
|
impl From<TimingFunction> for nsTimingFunction {
|
||||||
fn from(function: SpecifiedTimingFunction) -> nsTimingFunction {
|
fn from(function: TimingFunction) -> nsTimingFunction {
|
||||||
let mut tf: nsTimingFunction = unsafe { mem::zeroed() };
|
let mut tf: nsTimingFunction = unsafe { mem::zeroed() };
|
||||||
|
|
||||||
match function {
|
match function {
|
||||||
SpecifiedTimingFunction::Steps(steps, StartEnd::Start) => {
|
GenericTimingFunction::Steps(steps, StepPosition::Start) => {
|
||||||
debug_assert!(steps.value() >= 0);
|
debug_assert!(steps.value() >= 0);
|
||||||
tf.set_as_step(nsTimingFunction_Type::StepStart, steps.value() as u32);
|
tf.set_as_step(nsTimingFunction_Type::StepStart, steps.value() as u32);
|
||||||
},
|
},
|
||||||
SpecifiedTimingFunction::Steps(steps, StartEnd::End) => {
|
GenericTimingFunction::Steps(steps, StepPosition::End) => {
|
||||||
debug_assert!(steps.value() >= 0);
|
debug_assert!(steps.value() >= 0);
|
||||||
tf.set_as_step(nsTimingFunction_Type::StepEnd, steps.value() as u32);
|
tf.set_as_step(nsTimingFunction_Type::StepEnd, steps.value() as u32);
|
||||||
},
|
},
|
||||||
SpecifiedTimingFunction::Frames(frames) => {
|
GenericTimingFunction::Frames(frames) => {
|
||||||
debug_assert!(frames.value() >= 2);
|
debug_assert!(frames.value() >= 2);
|
||||||
tf.set_as_frames(frames.value() as u32);
|
tf.set_as_frames(frames.value() as u32);
|
||||||
},
|
},
|
||||||
SpecifiedTimingFunction::CubicBezier(p1, p2) => {
|
GenericTimingFunction::CubicBezier(p1, p2) => {
|
||||||
tf.set_as_bezier(nsTimingFunction_Type::CubicBezier,
|
tf.set_as_bezier(nsTimingFunction_Type::CubicBezier,
|
||||||
Point2D::new(p1.x.get(), p1.y.get()),
|
Point2D::new(p1.x.get(), p1.y.get()),
|
||||||
Point2D::new(p2.x.get(), p2.y.get()));
|
Point2D::new(p2.x.get(), p2.y.get()));
|
||||||
},
|
},
|
||||||
SpecifiedTimingFunction::Keyword(keyword) => {
|
GenericTimingFunction::Keyword(keyword) => {
|
||||||
match keyword.to_non_keyword_value() {
|
let (p1, p2) = keyword.to_bezier_points();
|
||||||
ComputedTimingFunction::CubicBezier(p1, p2) => {
|
tf.set_as_bezier(keyword.into(), p1, p2)
|
||||||
match keyword {
|
|
||||||
FunctionKeyword::Ease => {
|
|
||||||
tf.set_as_bezier(nsTimingFunction_Type::Ease, p1, p2);
|
|
||||||
},
|
|
||||||
FunctionKeyword::Linear => {
|
|
||||||
tf.set_as_bezier(nsTimingFunction_Type::Linear, p1, p2);
|
|
||||||
},
|
|
||||||
FunctionKeyword::EaseIn => {
|
|
||||||
tf.set_as_bezier(nsTimingFunction_Type::EaseIn, p1, p2);
|
|
||||||
},
|
|
||||||
FunctionKeyword::EaseOut => {
|
|
||||||
tf.set_as_bezier(nsTimingFunction_Type::EaseOut, p1, p2);
|
|
||||||
},
|
|
||||||
FunctionKeyword::EaseInOut => {
|
|
||||||
tf.set_as_bezier(nsTimingFunction_Type::EaseInOut, p1, p2);
|
|
||||||
},
|
|
||||||
_ => unreachable!("Unexpected bezier function type"),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ComputedTimingFunction::Steps(steps, StartEnd::Start) => {
|
|
||||||
debug_assert!(keyword == FunctionKeyword::StepStart && steps == 1);
|
|
||||||
tf.set_as_step(nsTimingFunction_Type::StepStart, steps);
|
|
||||||
},
|
|
||||||
ComputedTimingFunction::Steps(steps, StartEnd::End) => {
|
|
||||||
debug_assert!(keyword == FunctionKeyword::StepEnd && steps == 1);
|
|
||||||
tf.set_as_step(nsTimingFunction_Type::StepEnd, steps);
|
|
||||||
},
|
|
||||||
ComputedTimingFunction::Frames(frames) => {
|
|
||||||
tf.set_as_frames(frames)
|
|
||||||
},
|
|
||||||
ComputedTimingFunction::Keyword(_) => {
|
|
||||||
panic!("Keyword function should not appear")
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
tf
|
tf
|
||||||
|
@ -120,36 +85,36 @@ impl From<nsTimingFunction> for ComputedTimingFunction {
|
||||||
fn from(function: nsTimingFunction) -> ComputedTimingFunction {
|
fn from(function: nsTimingFunction) -> ComputedTimingFunction {
|
||||||
match function.mType {
|
match function.mType {
|
||||||
nsTimingFunction_Type::StepStart => {
|
nsTimingFunction_Type::StepStart => {
|
||||||
ComputedTimingFunction::Steps(
|
GenericTimingFunction::Steps(
|
||||||
unsafe { function.__bindgen_anon_1.__bindgen_anon_1.as_ref().mStepsOrFrames },
|
unsafe { function.__bindgen_anon_1.__bindgen_anon_1.as_ref().mStepsOrFrames },
|
||||||
StartEnd::Start)
|
StepPosition::Start)
|
||||||
},
|
},
|
||||||
nsTimingFunction_Type::StepEnd => {
|
nsTimingFunction_Type::StepEnd => {
|
||||||
ComputedTimingFunction::Steps(
|
GenericTimingFunction::Steps(
|
||||||
unsafe { function.__bindgen_anon_1.__bindgen_anon_1.as_ref().mStepsOrFrames },
|
unsafe { function.__bindgen_anon_1.__bindgen_anon_1.as_ref().mStepsOrFrames },
|
||||||
StartEnd::End)
|
StepPosition::End)
|
||||||
},
|
},
|
||||||
nsTimingFunction_Type::Frames => {
|
nsTimingFunction_Type::Frames => {
|
||||||
ComputedTimingFunction::Frames(
|
GenericTimingFunction::Frames(
|
||||||
unsafe { function.__bindgen_anon_1.__bindgen_anon_1.as_ref().mStepsOrFrames })
|
unsafe { function.__bindgen_anon_1.__bindgen_anon_1.as_ref().mStepsOrFrames })
|
||||||
}
|
}
|
||||||
nsTimingFunction_Type::Ease => {
|
nsTimingFunction_Type::Ease => {
|
||||||
ComputedTimingFunction::Keyword(FunctionKeyword::Ease)
|
GenericTimingFunction::Keyword(TimingKeyword::Ease)
|
||||||
},
|
},
|
||||||
nsTimingFunction_Type::Linear => {
|
nsTimingFunction_Type::Linear => {
|
||||||
ComputedTimingFunction::Keyword(FunctionKeyword::Linear)
|
GenericTimingFunction::Keyword(TimingKeyword::Linear)
|
||||||
},
|
},
|
||||||
nsTimingFunction_Type::EaseIn => {
|
nsTimingFunction_Type::EaseIn => {
|
||||||
ComputedTimingFunction::Keyword(FunctionKeyword::EaseIn)
|
GenericTimingFunction::Keyword(TimingKeyword::EaseIn)
|
||||||
},
|
},
|
||||||
nsTimingFunction_Type::EaseOut => {
|
nsTimingFunction_Type::EaseOut => {
|
||||||
ComputedTimingFunction::Keyword(FunctionKeyword::EaseOut)
|
GenericTimingFunction::Keyword(TimingKeyword::EaseOut)
|
||||||
},
|
},
|
||||||
nsTimingFunction_Type::EaseInOut => {
|
nsTimingFunction_Type::EaseInOut => {
|
||||||
ComputedTimingFunction::Keyword(FunctionKeyword::EaseInOut)
|
GenericTimingFunction::Keyword(TimingKeyword::EaseInOut)
|
||||||
},
|
},
|
||||||
nsTimingFunction_Type::CubicBezier => {
|
nsTimingFunction_Type::CubicBezier => {
|
||||||
ComputedTimingFunction::CubicBezier(
|
GenericTimingFunction::CubicBezier(
|
||||||
TypedPoint2D::new(unsafe { function.__bindgen_anon_1.mFunc.as_ref().mX1 },
|
TypedPoint2D::new(unsafe { function.__bindgen_anon_1.mFunc.as_ref().mX1 },
|
||||||
unsafe { function.__bindgen_anon_1.mFunc.as_ref().mY1 }),
|
unsafe { function.__bindgen_anon_1.mFunc.as_ref().mY1 }),
|
||||||
TypedPoint2D::new(unsafe { function.__bindgen_anon_1.mFunc.as_ref().mX2 },
|
TypedPoint2D::new(unsafe { function.__bindgen_anon_1.mFunc.as_ref().mX2 },
|
||||||
|
@ -158,3 +123,15 @@ impl From<nsTimingFunction> for ComputedTimingFunction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<TimingKeyword> for nsTimingFunction_Type {
|
||||||
|
fn from(keyword: TimingKeyword) -> Self {
|
||||||
|
match keyword {
|
||||||
|
TimingKeyword::Linear => nsTimingFunction_Type::Linear,
|
||||||
|
TimingKeyword::Ease => nsTimingFunction_Type::Ease,
|
||||||
|
TimingKeyword::EaseIn => nsTimingFunction_Type::EaseIn,
|
||||||
|
TimingKeyword::EaseOut => nsTimingFunction_Type::EaseOut,
|
||||||
|
TimingKeyword::EaseInOut => nsTimingFunction_Type::EaseInOut,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -414,336 +414,15 @@ ${helpers.predefined_type("transition-duration",
|
||||||
extra_prefixes="moz webkit",
|
extra_prefixes="moz webkit",
|
||||||
spec="https://drafts.csswg.org/css-transitions/#propdef-transition-duration")}
|
spec="https://drafts.csswg.org/css-transitions/#propdef-transition-duration")}
|
||||||
|
|
||||||
// TODO(pcwalton): Lots more timing functions.
|
${helpers.predefined_type("transition-timing-function",
|
||||||
<%helpers:vector_longhand name="transition-timing-function"
|
"TimingFunction",
|
||||||
need_index="True"
|
"computed::TimingFunction::ease()",
|
||||||
animation_value_type="none"
|
initial_specified_value="specified::TimingFunction::ease()",
|
||||||
extra_prefixes="moz webkit"
|
vector=True,
|
||||||
spec="https://drafts.csswg.org/css-transitions/#propdef-transition-timing-function">
|
need_index=True,
|
||||||
use self::computed_value::StartEnd;
|
animation_value_type="none",
|
||||||
use values::specified::Number;
|
extra_prefixes="moz webkit",
|
||||||
use euclid::point::{Point2D, TypedPoint2D};
|
spec="https://drafts.csswg.org/css-transitions/#propdef-transition-timing-function")}
|
||||||
use std::fmt;
|
|
||||||
use style_traits::ToCss;
|
|
||||||
|
|
||||||
// FIXME: This could use static variables and const functions when they are available.
|
|
||||||
#[inline(always)]
|
|
||||||
fn ease() -> computed_value::T {
|
|
||||||
computed_value::T::CubicBezier(TypedPoint2D::new(0.25, 0.1),
|
|
||||||
TypedPoint2D::new(0.25, 1.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn linear() -> computed_value::T {
|
|
||||||
computed_value::T::CubicBezier(TypedPoint2D::new(0.0, 0.0),
|
|
||||||
TypedPoint2D::new(1.0, 1.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn ease_in() -> computed_value::T {
|
|
||||||
computed_value::T::CubicBezier(TypedPoint2D::new(0.42, 0.0),
|
|
||||||
TypedPoint2D::new(1.0, 1.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn ease_out() -> computed_value::T {
|
|
||||||
computed_value::T::CubicBezier(TypedPoint2D::new(0.0, 0.0),
|
|
||||||
TypedPoint2D::new(0.58, 1.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn ease_in_out() -> computed_value::T {
|
|
||||||
computed_value::T::CubicBezier(TypedPoint2D::new(0.42, 0.0),
|
|
||||||
TypedPoint2D::new(0.58, 1.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
static STEP_START: computed_value::T =
|
|
||||||
computed_value::T::Steps(1, StartEnd::Start);
|
|
||||||
static STEP_END: computed_value::T =
|
|
||||||
computed_value::T::Steps(1, StartEnd::End);
|
|
||||||
|
|
||||||
pub mod computed_value {
|
|
||||||
use euclid::point::Point2D;
|
|
||||||
use std::fmt;
|
|
||||||
use style_traits::ToCss;
|
|
||||||
use super::FunctionKeyword;
|
|
||||||
use values::specified;
|
|
||||||
|
|
||||||
pub use super::parse;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
||||||
pub enum T {
|
|
||||||
CubicBezier(Point2D<f32>, Point2D<f32>),
|
|
||||||
Steps(u32, StartEnd),
|
|
||||||
Frames(u32),
|
|
||||||
Keyword(FunctionKeyword),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToCss for T {
|
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
|
||||||
where W: fmt::Write,
|
|
||||||
{
|
|
||||||
match *self {
|
|
||||||
T::CubicBezier(p1, p2) => {
|
|
||||||
try!(dest.write_str("cubic-bezier("));
|
|
||||||
try!(p1.x.to_css(dest));
|
|
||||||
try!(dest.write_str(", "));
|
|
||||||
try!(p1.y.to_css(dest));
|
|
||||||
try!(dest.write_str(", "));
|
|
||||||
try!(p2.x.to_css(dest));
|
|
||||||
try!(dest.write_str(", "));
|
|
||||||
try!(p2.y.to_css(dest));
|
|
||||||
dest.write_str(")")
|
|
||||||
},
|
|
||||||
T::Steps(steps, start_end) => {
|
|
||||||
super::serialize_steps(dest, specified::Integer::new(steps as i32), start_end)
|
|
||||||
},
|
|
||||||
T::Frames(frames) => {
|
|
||||||
try!(dest.write_str("frames("));
|
|
||||||
try!(frames.to_css(dest));
|
|
||||||
dest.write_str(")")
|
|
||||||
},
|
|
||||||
T::Keyword(keyword) => {
|
|
||||||
super::serialize_keyword(dest, keyword)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
||||||
pub enum StartEnd {
|
|
||||||
Start,
|
|
||||||
End,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToCss for StartEnd {
|
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
|
||||||
where W: fmt::Write,
|
|
||||||
{
|
|
||||||
match *self {
|
|
||||||
StartEnd::Start => dest.write_str("start"),
|
|
||||||
StartEnd::End => dest.write_str("end"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
define_css_keyword_enum!(FunctionKeyword:
|
|
||||||
"ease" => Ease,
|
|
||||||
"linear" => Linear,
|
|
||||||
"ease-in" => EaseIn,
|
|
||||||
"ease-out" => EaseOut,
|
|
||||||
"ease-in-out" => EaseInOut,
|
|
||||||
"step-start" => StepStart,
|
|
||||||
"step-end" => StepEnd);
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
||||||
pub enum SpecifiedValue {
|
|
||||||
CubicBezier(Point2D<Number>, Point2D<Number>),
|
|
||||||
Steps(specified::Integer, StartEnd),
|
|
||||||
Frames(specified::Integer),
|
|
||||||
Keyword(FunctionKeyword),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parse for SpecifiedValue {
|
|
||||||
fn parse(context: &ParserContext, input: &mut ::cssparser::Parser) -> Result<Self, ()> {
|
|
||||||
if let Ok(function_name) = input.try(|input| input.expect_function()) {
|
|
||||||
return match_ignore_ascii_case! { &function_name,
|
|
||||||
"cubic-bezier" => {
|
|
||||||
let (mut p1x, mut p1y, mut p2x, mut p2y) =
|
|
||||||
(Number::new(0.0), Number::new(0.0), Number::new(0.0), Number::new(0.0));
|
|
||||||
try!(input.parse_nested_block(|input| {
|
|
||||||
p1x = try!(specified::parse_number(context, input));
|
|
||||||
try!(input.expect_comma());
|
|
||||||
p1y = try!(specified::parse_number(context, input));
|
|
||||||
try!(input.expect_comma());
|
|
||||||
p2x = try!(specified::parse_number(context, input));
|
|
||||||
try!(input.expect_comma());
|
|
||||||
p2y = try!(specified::parse_number(context, input));
|
|
||||||
Ok(())
|
|
||||||
}));
|
|
||||||
if p1x.get() < 0.0 || p1x.get() > 1.0 ||
|
|
||||||
p2x.get() < 0.0 || p2x.get() > 1.0 {
|
|
||||||
return Err(())
|
|
||||||
}
|
|
||||||
|
|
||||||
let (p1, p2) = (Point2D::new(p1x, p1y), Point2D::new(p2x, p2y));
|
|
||||||
Ok(SpecifiedValue::CubicBezier(p1, p2))
|
|
||||||
},
|
|
||||||
"steps" => {
|
|
||||||
let (mut step_count, mut start_end) = (specified::Integer::new(0), StartEnd::End);
|
|
||||||
try!(input.parse_nested_block(|input| {
|
|
||||||
step_count = try!(specified::parse_integer(context, input));
|
|
||||||
if step_count.value() < 1 {
|
|
||||||
return Err(())
|
|
||||||
}
|
|
||||||
|
|
||||||
if input.try(|input| input.expect_comma()).is_ok() {
|
|
||||||
start_end = try!(match_ignore_ascii_case! {
|
|
||||||
&try!(input.expect_ident()),
|
|
||||||
"start" => Ok(StartEnd::Start),
|
|
||||||
"end" => Ok(StartEnd::End),
|
|
||||||
_ => Err(())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}));
|
|
||||||
Ok(SpecifiedValue::Steps(step_count, start_end))
|
|
||||||
},
|
|
||||||
"frames" => {
|
|
||||||
// https://drafts.csswg.org/css-timing/#frames-timing-functions
|
|
||||||
let frames = try!(input.parse_nested_block(|input| {
|
|
||||||
specified::Integer::parse_with_minimum(context, input, 2)
|
|
||||||
}));
|
|
||||||
Ok(SpecifiedValue::Frames(frames))
|
|
||||||
},
|
|
||||||
_ => Err(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(SpecifiedValue::Keyword(try!(FunctionKeyword::parse(input))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_steps<W>(dest: &mut W,
|
|
||||||
steps: specified::Integer,
|
|
||||||
start_end: StartEnd) -> fmt::Result
|
|
||||||
where W: fmt::Write,
|
|
||||||
{
|
|
||||||
try!(dest.write_str("steps("));
|
|
||||||
try!(steps.to_css(dest));
|
|
||||||
if let StartEnd::Start = start_end {
|
|
||||||
try!(dest.write_str(", start"));
|
|
||||||
}
|
|
||||||
dest.write_str(")")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_keyword<W>(dest: &mut W, keyword: FunctionKeyword) -> fmt::Result
|
|
||||||
where W: fmt::Write,
|
|
||||||
{
|
|
||||||
match keyword {
|
|
||||||
FunctionKeyword::StepStart => {
|
|
||||||
serialize_steps(dest, specified::Integer::new(1), StartEnd::Start)
|
|
||||||
},
|
|
||||||
FunctionKeyword::StepEnd => {
|
|
||||||
serialize_steps(dest, specified::Integer::new(1), StartEnd::End)
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
keyword.to_css(dest)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://drafts.csswg.org/css-transitions/#serializing-a-timing-function
|
|
||||||
impl ToCss for SpecifiedValue {
|
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
||||||
match *self {
|
|
||||||
SpecifiedValue::CubicBezier(p1, p2) => {
|
|
||||||
try!(dest.write_str("cubic-bezier("));
|
|
||||||
try!(p1.x.to_css(dest));
|
|
||||||
try!(dest.write_str(", "));
|
|
||||||
try!(p1.y.to_css(dest));
|
|
||||||
try!(dest.write_str(", "));
|
|
||||||
try!(p2.x.to_css(dest));
|
|
||||||
try!(dest.write_str(", "));
|
|
||||||
try!(p2.y.to_css(dest));
|
|
||||||
dest.write_str(")")
|
|
||||||
},
|
|
||||||
SpecifiedValue::Steps(steps, start_end) => {
|
|
||||||
serialize_steps(dest, steps, start_end)
|
|
||||||
},
|
|
||||||
SpecifiedValue::Frames(frames) => {
|
|
||||||
try!(dest.write_str("frames("));
|
|
||||||
try!(frames.to_css(dest));
|
|
||||||
dest.write_str(")")
|
|
||||||
},
|
|
||||||
SpecifiedValue::Keyword(keyword) => {
|
|
||||||
serialize_keyword(dest, keyword)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToComputedValue for SpecifiedValue {
|
|
||||||
type ComputedValue = computed_value::T;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn to_computed_value(&self, context: &Context) -> computed_value::T {
|
|
||||||
match *self {
|
|
||||||
SpecifiedValue::CubicBezier(p1, p2) => {
|
|
||||||
computed_value::T::CubicBezier(
|
|
||||||
Point2D::new(p1.x.to_computed_value(context), p1.y.to_computed_value(context)),
|
|
||||||
Point2D::new(p2.x.to_computed_value(context), p2.y.to_computed_value(context)))
|
|
||||||
},
|
|
||||||
SpecifiedValue::Steps(count, start_end) => {
|
|
||||||
computed_value::T::Steps(count.to_computed_value(context) as u32, start_end)
|
|
||||||
},
|
|
||||||
SpecifiedValue::Frames(frames) => {
|
|
||||||
computed_value::T::Frames(frames.to_computed_value(context) as u32)
|
|
||||||
},
|
|
||||||
SpecifiedValue::Keyword(keyword) => {
|
|
||||||
computed_value::T::Keyword(keyword)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn from_computed_value(computed: &computed_value::T) -> Self {
|
|
||||||
match *computed {
|
|
||||||
computed_value::T::CubicBezier(p1, p2) => {
|
|
||||||
SpecifiedValue::CubicBezier(
|
|
||||||
Point2D::new(Number::from_computed_value(&p1.x),
|
|
||||||
Number::from_computed_value(&p1.y)),
|
|
||||||
Point2D::new(Number::from_computed_value(&p2.x),
|
|
||||||
Number::from_computed_value(&p2.y)))
|
|
||||||
},
|
|
||||||
computed_value::T::Steps(count, start_end) => {
|
|
||||||
let int_count = count as i32;
|
|
||||||
SpecifiedValue::Steps(specified::Integer::from_computed_value(&int_count), start_end)
|
|
||||||
},
|
|
||||||
computed_value::T::Frames(frames) => {
|
|
||||||
let frames = frames as i32;
|
|
||||||
SpecifiedValue::Frames(specified::Integer::from_computed_value(&frames))
|
|
||||||
},
|
|
||||||
computed_value::T::Keyword(keyword) => {
|
|
||||||
SpecifiedValue::Keyword(keyword)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FunctionKeyword {
|
|
||||||
#[inline]
|
|
||||||
pub fn to_non_keyword_value(&self) -> computed_value::T {
|
|
||||||
match *self {
|
|
||||||
FunctionKeyword::Ease => ease(),
|
|
||||||
FunctionKeyword::Linear => linear(),
|
|
||||||
FunctionKeyword::EaseIn => ease_in(),
|
|
||||||
FunctionKeyword::EaseOut => ease_out(),
|
|
||||||
FunctionKeyword::EaseInOut => ease_in_out(),
|
|
||||||
FunctionKeyword::StepStart => STEP_START,
|
|
||||||
FunctionKeyword::StepEnd => STEP_END,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
no_viewport_percentage!(SpecifiedValue);
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn get_initial_value() -> computed_value::T {
|
|
||||||
computed_value::T::Keyword(FunctionKeyword::Ease)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn get_initial_specified_value() -> SpecifiedValue {
|
|
||||||
SpecifiedValue::Keyword(FunctionKeyword::Ease)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
|
|
||||||
SpecifiedValue::parse(context, input)
|
|
||||||
}
|
|
||||||
</%helpers:vector_longhand>
|
|
||||||
|
|
||||||
<%helpers:vector_longhand name="transition-property"
|
<%helpers:vector_longhand name="transition-property"
|
||||||
allow_empty="True"
|
allow_empty="True"
|
||||||
|
|
|
@ -39,7 +39,7 @@ pub use self::length::{LengthOrPercentageOrAutoOrContent, LengthOrPercentageOrNo
|
||||||
pub use self::length::{MaxLength, MozLength};
|
pub use self::length::{MaxLength, MozLength};
|
||||||
pub use self::position::Position;
|
pub use self::position::Position;
|
||||||
pub use self::text::{LetterSpacing, LineHeight, WordSpacing};
|
pub use self::text::{LetterSpacing, LineHeight, WordSpacing};
|
||||||
pub use self::transform::TransformOrigin;
|
pub use self::transform::{TimingFunction, TransformOrigin};
|
||||||
|
|
||||||
pub mod background;
|
pub mod background;
|
||||||
pub mod basic_shape;
|
pub mod basic_shape;
|
||||||
|
|
|
@ -5,12 +5,16 @@
|
||||||
//! Computed types for CSS values that are related to transformations.
|
//! Computed types for CSS values that are related to transformations.
|
||||||
|
|
||||||
use properties::animated_properties::Animatable;
|
use properties::animated_properties::Animatable;
|
||||||
use values::computed::{Length, LengthOrPercentage};
|
use values::computed::{Length, LengthOrPercentage, Number};
|
||||||
|
use values::generics::transform::TimingFunction as GenericTimingFunction;
|
||||||
use values::generics::transform::TransformOrigin as GenericTransformOrigin;
|
use values::generics::transform::TransformOrigin as GenericTransformOrigin;
|
||||||
|
|
||||||
/// The computed value of a CSS `<transform-origin>`
|
/// The computed value of a CSS `<transform-origin>`
|
||||||
pub type TransformOrigin = GenericTransformOrigin<LengthOrPercentage, LengthOrPercentage, Length>;
|
pub type TransformOrigin = GenericTransformOrigin<LengthOrPercentage, LengthOrPercentage, Length>;
|
||||||
|
|
||||||
|
/// A computed timing function.
|
||||||
|
pub type TimingFunction = GenericTimingFunction<u32, Number>;
|
||||||
|
|
||||||
impl TransformOrigin {
|
impl TransformOrigin {
|
||||||
/// Returns the initial computed value for `transform-origin`.
|
/// Returns the initial computed value for `transform-origin`.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -4,6 +4,11 @@
|
||||||
|
|
||||||
//! Generic types for CSS values that are related to transformations.
|
//! Generic types for CSS values that are related to transformations.
|
||||||
|
|
||||||
|
use euclid::Point2D;
|
||||||
|
use std::fmt;
|
||||||
|
use style_traits::{HasViewportPercentage, ToCss};
|
||||||
|
use values::CSSFloat;
|
||||||
|
|
||||||
/// A generic transform origin.
|
/// A generic transform origin.
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue, ToCss)]
|
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue, ToCss)]
|
||||||
|
@ -16,6 +21,41 @@ pub struct TransformOrigin<H, V, Depth> {
|
||||||
pub depth: Depth,
|
pub depth: Depth,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A generic timing function.
|
||||||
|
///
|
||||||
|
/// https://drafts.csswg.org/css-timing-1/#single-timing-function-production
|
||||||
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub enum TimingFunction<Integer, Number> {
|
||||||
|
/// `linear | ease | ease-in | ease-out | ease-in-out`
|
||||||
|
Keyword(TimingKeyword),
|
||||||
|
/// `cubic-bezier(<number>, <number>, <number>, <number>)`
|
||||||
|
CubicBezier(Point2D<Number>, Point2D<Number>),
|
||||||
|
/// `step-start | step-end | steps(<integer>, [ start | end ]?)`
|
||||||
|
Steps(Integer, StepPosition),
|
||||||
|
/// `frames(<integer>)`
|
||||||
|
Frames(Integer),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, N> HasViewportPercentage for TimingFunction<I, N> {
|
||||||
|
fn has_viewport_percentage(&self) -> bool { false }
|
||||||
|
}
|
||||||
|
|
||||||
|
define_css_keyword_enum! { TimingKeyword:
|
||||||
|
"linear" => Linear,
|
||||||
|
"ease" => Ease,
|
||||||
|
"ease-in" => EaseIn,
|
||||||
|
"ease-out" => EaseOut,
|
||||||
|
"ease-in-out" => EaseInOut,
|
||||||
|
}
|
||||||
|
add_impls_for_keyword_enum!(TimingKeyword);
|
||||||
|
|
||||||
|
define_css_keyword_enum! { StepPosition:
|
||||||
|
"start" => Start,
|
||||||
|
"end" => End,
|
||||||
|
}
|
||||||
|
add_impls_for_keyword_enum!(StepPosition);
|
||||||
|
|
||||||
impl<H, V, D> TransformOrigin<H, V, D> {
|
impl<H, V, D> TransformOrigin<H, V, D> {
|
||||||
/// Returns a new transform origin.
|
/// Returns a new transform origin.
|
||||||
pub fn new(horizontal: H, vertical: V, depth: D) -> Self {
|
pub fn new(horizontal: H, vertical: V, depth: D) -> Self {
|
||||||
|
@ -26,3 +66,65 @@ impl<H, V, D> TransformOrigin<H, V, D> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<Integer, Number> TimingFunction<Integer, Number> {
|
||||||
|
/// `ease`
|
||||||
|
#[inline]
|
||||||
|
pub fn ease() -> Self {
|
||||||
|
TimingFunction::Keyword(TimingKeyword::Ease)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Integer, Number> ToCss for TimingFunction<Integer, Number>
|
||||||
|
where
|
||||||
|
Integer: ToCss,
|
||||||
|
Number: ToCss,
|
||||||
|
{
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
||||||
|
where
|
||||||
|
W: fmt::Write,
|
||||||
|
{
|
||||||
|
match *self {
|
||||||
|
TimingFunction::Keyword(keyword) => keyword.to_css(dest),
|
||||||
|
TimingFunction::CubicBezier(ref p1, ref p2) => {
|
||||||
|
dest.write_str("cubic-bezier(")?;
|
||||||
|
p1.x.to_css(dest)?;
|
||||||
|
dest.write_str(", ")?;
|
||||||
|
p1.y.to_css(dest)?;
|
||||||
|
dest.write_str(", ")?;
|
||||||
|
p2.x.to_css(dest)?;
|
||||||
|
dest.write_str(", ")?;
|
||||||
|
p2.y.to_css(dest)?;
|
||||||
|
dest.write_str(")")
|
||||||
|
},
|
||||||
|
TimingFunction::Steps(ref intervals, position) => {
|
||||||
|
dest.write_str("steps(")?;
|
||||||
|
intervals.to_css(dest)?;
|
||||||
|
if position != StepPosition::End {
|
||||||
|
dest.write_str(", ")?;
|
||||||
|
position.to_css(dest)?;
|
||||||
|
}
|
||||||
|
dest.write_str(")")
|
||||||
|
},
|
||||||
|
TimingFunction::Frames(ref frames) => {
|
||||||
|
dest.write_str("frames(")?;
|
||||||
|
frames.to_css(dest)?;
|
||||||
|
dest.write_str(")")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TimingKeyword {
|
||||||
|
/// Returns this timing keyword as a pair of `cubic-bezier()` points.
|
||||||
|
#[inline]
|
||||||
|
pub fn to_bezier_points(self) -> (Point2D<CSSFloat>, Point2D<CSSFloat>) {
|
||||||
|
match self {
|
||||||
|
TimingKeyword::Linear => (Point2D::new(0., 0.), Point2D::new(1., 1.)),
|
||||||
|
TimingKeyword::Ease => (Point2D::new(0.25, 0.1), Point2D::new(0.25, 1.)),
|
||||||
|
TimingKeyword::EaseIn => (Point2D::new(0.42, 0.), Point2D::new(1., 1.)),
|
||||||
|
TimingKeyword::EaseOut => (Point2D::new(0., 0.), Point2D::new(0.58, 1.)),
|
||||||
|
TimingKeyword::EaseInOut => (Point2D::new(0.42, 0.), Point2D::new(0.58, 1.)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ pub use self::length::{LengthOrPercentageOrNone, LengthOrPercentageOrAutoOrConte
|
||||||
pub use self::length::{MaxLength, MozLength};
|
pub use self::length::{MaxLength, MozLength};
|
||||||
pub use self::position::{Position, PositionComponent};
|
pub use self::position::{Position, PositionComponent};
|
||||||
pub use self::text::{LetterSpacing, LineHeight, WordSpacing};
|
pub use self::text::{LetterSpacing, LineHeight, WordSpacing};
|
||||||
pub use self::transform::TransformOrigin;
|
pub use self::transform::{TimingFunction, TransformOrigin};
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub mod align;
|
pub mod align;
|
||||||
|
|
|
@ -5,11 +5,15 @@
|
||||||
//! Specified types for CSS values that are related to transformations.
|
//! Specified types for CSS values that are related to transformations.
|
||||||
|
|
||||||
use cssparser::Parser;
|
use cssparser::Parser;
|
||||||
|
use euclid::Point2D;
|
||||||
use parser::{Parse, ParserContext};
|
use parser::{Parse, ParserContext};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use style_traits::ToCss;
|
use style_traits::ToCss;
|
||||||
use values::computed::{LengthOrPercentage as ComputedLengthOrPercentage, Context, ToComputedValue};
|
use values::computed::{LengthOrPercentage as ComputedLengthOrPercentage, Context, ToComputedValue};
|
||||||
use values::generics::transform::TransformOrigin as GenericTransformOrigin;
|
use values::computed::transform::TimingFunction as ComputedTimingFunction;
|
||||||
|
use values::generics::transform::{StepPosition, TimingFunction as GenericTimingFunction};
|
||||||
|
use values::generics::transform::{TimingKeyword, TransformOrigin as GenericTransformOrigin};
|
||||||
|
use values::specified::{Integer, Number};
|
||||||
use values::specified::length::{Length, LengthOrPercentage};
|
use values::specified::length::{Length, LengthOrPercentage};
|
||||||
use values::specified::position::{Side, X, Y};
|
use values::specified::position::{Side, X, Y};
|
||||||
|
|
||||||
|
@ -28,6 +32,9 @@ pub enum OriginComponent<S> {
|
||||||
Side(S),
|
Side(S),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A specified timing function.
|
||||||
|
pub type TimingFunction = GenericTimingFunction<Integer, Number>;
|
||||||
|
|
||||||
impl Parse for TransformOrigin {
|
impl Parse for TransformOrigin {
|
||||||
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||||
let parse_depth = |input: &mut Parser| {
|
let parse_depth = |input: &mut Parser| {
|
||||||
|
@ -130,3 +137,121 @@ impl<S> ToComputedValue for OriginComponent<S>
|
||||||
OriginComponent::Length(ToComputedValue::from_computed_value(computed))
|
OriginComponent::Length(ToComputedValue::from_computed_value(computed))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Parse for TimingFunction {
|
||||||
|
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||||
|
if let Ok(keyword) = input.try(TimingKeyword::parse) {
|
||||||
|
return Ok(GenericTimingFunction::Keyword(keyword));
|
||||||
|
}
|
||||||
|
if let Ok(ident) = input.try(|i| i.expect_ident()) {
|
||||||
|
let position = match_ignore_ascii_case! { &ident,
|
||||||
|
"step-start" => StepPosition::Start,
|
||||||
|
"step-end" => StepPosition::End,
|
||||||
|
_ => return Err(()),
|
||||||
|
};
|
||||||
|
return Ok(GenericTimingFunction::Steps(Integer::new(1), position));
|
||||||
|
}
|
||||||
|
let function = input.expect_function()?;
|
||||||
|
input.parse_nested_block(|i| {
|
||||||
|
match_ignore_ascii_case! { &function,
|
||||||
|
"cubic-bezier" => {
|
||||||
|
let p1x = Number::parse(context, i)?;
|
||||||
|
i.expect_comma()?;
|
||||||
|
let p1y = Number::parse(context, i)?;
|
||||||
|
i.expect_comma()?;
|
||||||
|
let p2x = Number::parse(context, i)?;
|
||||||
|
i.expect_comma()?;
|
||||||
|
let p2y = Number::parse(context, i)?;
|
||||||
|
|
||||||
|
if p1x.get() < 0.0 || p1x.get() > 1.0 || p2x.get() < 0.0 || p2x.get() > 1.0 {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let (p1, p2) = (Point2D::new(p1x, p1y), Point2D::new(p2x, p2y));
|
||||||
|
Ok(GenericTimingFunction::CubicBezier(p1, p2))
|
||||||
|
},
|
||||||
|
"steps" => {
|
||||||
|
let steps = Integer::parse_positive(context, i)?;
|
||||||
|
let position = i.try(|i| {
|
||||||
|
i.expect_comma()?;
|
||||||
|
StepPosition::parse(i)
|
||||||
|
}).unwrap_or(StepPosition::End);
|
||||||
|
Ok(GenericTimingFunction::Steps(steps, position))
|
||||||
|
},
|
||||||
|
"frames" => {
|
||||||
|
let frames = Integer::parse_with_minimum(context, i, 2)?;
|
||||||
|
Ok(GenericTimingFunction::Frames(frames))
|
||||||
|
},
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToComputedValue for TimingFunction {
|
||||||
|
type ComputedValue = ComputedTimingFunction;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
||||||
|
match *self {
|
||||||
|
GenericTimingFunction::Keyword(keyword) => {
|
||||||
|
GenericTimingFunction::Keyword(keyword)
|
||||||
|
},
|
||||||
|
GenericTimingFunction::CubicBezier(p1, p2) => {
|
||||||
|
GenericTimingFunction::CubicBezier(
|
||||||
|
Point2D::new(
|
||||||
|
p1.x.to_computed_value(context),
|
||||||
|
p1.y.to_computed_value(context),
|
||||||
|
),
|
||||||
|
Point2D::new(
|
||||||
|
p2.x.to_computed_value(context),
|
||||||
|
p2.y.to_computed_value(context),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
GenericTimingFunction::Steps(steps, position) => {
|
||||||
|
GenericTimingFunction::Steps(
|
||||||
|
steps.to_computed_value(context) as u32,
|
||||||
|
position,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
GenericTimingFunction::Frames(frames) => {
|
||||||
|
GenericTimingFunction::Frames(
|
||||||
|
frames.to_computed_value(context) as u32,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||||
|
match *computed {
|
||||||
|
GenericTimingFunction::Keyword(keyword) => {
|
||||||
|
GenericTimingFunction::Keyword(keyword)
|
||||||
|
},
|
||||||
|
GenericTimingFunction::CubicBezier(p1, p2) => {
|
||||||
|
GenericTimingFunction::CubicBezier(
|
||||||
|
Point2D::new(
|
||||||
|
Number::from_computed_value(&p1.x),
|
||||||
|
Number::from_computed_value(&p1.y),
|
||||||
|
),
|
||||||
|
Point2D::new(
|
||||||
|
Number::from_computed_value(&p2.x),
|
||||||
|
Number::from_computed_value(&p2.y),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
GenericTimingFunction::Steps(steps, position) => {
|
||||||
|
GenericTimingFunction::Steps(
|
||||||
|
Integer::from_computed_value(&(steps as i32)),
|
||||||
|
position,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
GenericTimingFunction::Frames(frames) => {
|
||||||
|
GenericTimingFunction::Frames(
|
||||||
|
Integer::from_computed_value(&(frames as i32)),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue