mirror of
https://github.com/servo/servo.git
synced 2025-08-06 22:15:33 +01:00
layout: Implement 2D CSS transforms per CSS-TRANSFORMS § 5, 6, 7, and 8.
This commit is contained in:
parent
7bd6cb0091
commit
d10627a2b3
14 changed files with 964 additions and 35 deletions
|
@ -2687,6 +2687,7 @@ pub mod longhands {
|
|||
}
|
||||
}
|
||||
</%self:longhand>
|
||||
|
||||
<%self:longhand name="filter">
|
||||
use values::specified::Angle;
|
||||
pub use self::computed_value::T as SpecifiedValue;
|
||||
|
@ -2834,6 +2835,488 @@ pub mod longhands {
|
|||
}
|
||||
</%self:longhand>
|
||||
|
||||
<%self:longhand name="transform">
|
||||
use values::CSSFloat;
|
||||
use values::computed::{ToComputedValue, Context};
|
||||
|
||||
use cssparser::ToCss;
|
||||
use std::f64;
|
||||
use std::ops::Mul;
|
||||
use text_writer::{self, TextWriter};
|
||||
use util::geometry::Au;
|
||||
|
||||
pub mod computed_value {
|
||||
use values::CSSFloat;
|
||||
use values::computed;
|
||||
|
||||
use std::num::Float;
|
||||
use std::ops::Mul;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct ComputedMatrix {
|
||||
pub m11: CSSFloat, pub m12: CSSFloat,
|
||||
pub m21: CSSFloat, pub m22: CSSFloat,
|
||||
pub m31: computed::LengthAndPercentage, pub m32: computed::LengthAndPercentage,
|
||||
}
|
||||
|
||||
impl Mul<ComputedMatrix> for ComputedMatrix {
|
||||
type Output = ComputedMatrix;
|
||||
|
||||
fn mul(self, other: ComputedMatrix) -> ComputedMatrix {
|
||||
ComputedMatrix {
|
||||
m11: self.m11*other.m11 + self.m12*other.m21,
|
||||
m12: self.m11*other.m12 + self.m12*other.m22,
|
||||
m21: self.m21*other.m11 + self.m22*other.m21,
|
||||
m22: self.m21*other.m12 + self.m22*other.m22,
|
||||
m31: self.m31.clone()*other.m11 + self.m32.clone()*other.m21 + other.m31,
|
||||
m32: self.m31*other.m12 + self.m32*other.m22 + other.m32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ComputedMatrix {
|
||||
#[inline]
|
||||
fn new(m11: CSSFloat,
|
||||
m12: CSSFloat,
|
||||
m21: CSSFloat,
|
||||
m22: CSSFloat,
|
||||
m31: computed::LengthAndPercentage,
|
||||
m32: computed::LengthAndPercentage)
|
||||
-> ComputedMatrix {
|
||||
ComputedMatrix {
|
||||
m11: m11, m12: m12,
|
||||
m21: m21, m22: m22,
|
||||
m31: m31, m32: m32,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn identity() -> ComputedMatrix {
|
||||
ComputedMatrix {
|
||||
m11: 1.0, m12: 0.0,
|
||||
m21: 0.0, m22: 1.0,
|
||||
m31: computed::LengthAndPercentage::zero(),
|
||||
m32: computed::LengthAndPercentage::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scale(&mut self, sx: CSSFloat, sy: CSSFloat) {
|
||||
*self = ComputedMatrix::new(sx, 0.0,
|
||||
0.0, sy,
|
||||
computed::LengthAndPercentage::zero(),
|
||||
computed::LengthAndPercentage::zero()) *
|
||||
(*self).clone()
|
||||
}
|
||||
|
||||
pub fn skew(&mut self, sx: CSSFloat, sy: CSSFloat) {
|
||||
*self = ComputedMatrix::new(1.0, sx,
|
||||
sy, 1.0,
|
||||
computed::LengthAndPercentage::zero(),
|
||||
computed::LengthAndPercentage::zero()) *
|
||||
(*self).clone()
|
||||
}
|
||||
|
||||
pub fn translate(&mut self,
|
||||
tx: computed::LengthAndPercentage,
|
||||
ty: computed::LengthAndPercentage) {
|
||||
*self = ComputedMatrix::new(1.0, 0.0, 0.0, 1.0, tx, ty) * (*self).clone()
|
||||
}
|
||||
|
||||
pub fn rotate(&mut self, theta: CSSFloat) {
|
||||
*self = ComputedMatrix::new(theta.cos(), -theta.sin(),
|
||||
theta.sin(), theta.cos(),
|
||||
computed::LengthAndPercentage::zero(),
|
||||
computed::LengthAndPercentage::zero()) *
|
||||
(*self).clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub type T = Option<ComputedMatrix>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct SpecifiedMatrix {
|
||||
m11: CSSFloat, m12: CSSFloat,
|
||||
m21: CSSFloat, m22: CSSFloat,
|
||||
m31: specified::LengthAndPercentage, m32: specified::LengthAndPercentage,
|
||||
}
|
||||
|
||||
impl ToCss for Option<SpecifiedMatrix> {
|
||||
fn to_css<W>(&self, _: &mut W) -> text_writer::Result where W: TextWriter {
|
||||
// TODO(pcwalton)
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<SpecifiedMatrix> for SpecifiedMatrix {
|
||||
type Output = SpecifiedMatrix;
|
||||
|
||||
fn mul(self, other: SpecifiedMatrix) -> SpecifiedMatrix {
|
||||
SpecifiedMatrix {
|
||||
m11: self.m11*other.m11 + self.m12*other.m21,
|
||||
m12: self.m11*other.m12 + self.m12*other.m22,
|
||||
m21: self.m21*other.m11 + self.m22*other.m21,
|
||||
m22: self.m21*other.m12 + self.m22*other.m22,
|
||||
m31: self.m31.clone()*other.m11 + self.m32.clone()*other.m21 + other.m31,
|
||||
m32: self.m31*other.m12 + self.m32*other.m22 + other.m32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SpecifiedMatrix {
|
||||
#[inline]
|
||||
fn new(m11: CSSFloat,
|
||||
m12: CSSFloat,
|
||||
m21: CSSFloat,
|
||||
m22: CSSFloat,
|
||||
m31: specified::LengthAndPercentage,
|
||||
m32: specified::LengthAndPercentage)
|
||||
-> SpecifiedMatrix {
|
||||
SpecifiedMatrix {
|
||||
m11: m11, m12: m12,
|
||||
m21: m21, m22: m22,
|
||||
m31: m31, m32: m32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_two_lengths_or_percentages(input: &mut Parser)
|
||||
-> Result<(specified::LengthAndPercentage,
|
||||
specified::LengthAndPercentage),()> {
|
||||
let first = try!(specified::LengthAndPercentage::parse(input));
|
||||
let second = input.try(|input| {
|
||||
try!(input.expect_comma());
|
||||
specified::LengthAndPercentage::parse(input)
|
||||
}).unwrap_or(specified::LengthAndPercentage::zero());
|
||||
Ok((first, second))
|
||||
}
|
||||
|
||||
fn parse_two_floats(input: &mut Parser) -> Result<(CSSFloat,CSSFloat),()> {
|
||||
let first = try!(input.expect_number());
|
||||
let second = input.try(|input| {
|
||||
try!(input.expect_comma());
|
||||
input.expect_number()
|
||||
}).unwrap_or(first);
|
||||
Ok((first, second))
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
enum SpecifiedOperation {
|
||||
Matrix(SpecifiedMatrix),
|
||||
Translate(specified::LengthAndPercentage, specified::LengthAndPercentage),
|
||||
Scale(CSSFloat, CSSFloat),
|
||||
Rotate(specified::Angle),
|
||||
Skew(CSSFloat, CSSFloat),
|
||||
}
|
||||
|
||||
impl ToCss for SpecifiedOperation {
|
||||
fn to_css<W>(&self, _: &mut W) -> text_writer::Result where W: TextWriter {
|
||||
// TODO(pcwalton)
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct SpecifiedValue(Vec<SpecifiedOperation>);
|
||||
|
||||
impl ToCss for SpecifiedValue {
|
||||
fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter {
|
||||
let mut first = true;
|
||||
for operation in self.0.iter() {
|
||||
if !first {
|
||||
try!(dest.write_str(" "));
|
||||
}
|
||||
first = false;
|
||||
try!(operation.to_css(dest))
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_initial_value() -> computed_value::T {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
|
||||
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
|
||||
return Ok(SpecifiedValue(Vec::new()))
|
||||
}
|
||||
|
||||
let mut result = Vec::new();
|
||||
loop {
|
||||
let name = match input.expect_function() {
|
||||
Ok(name) => name,
|
||||
Err(_) => break,
|
||||
};
|
||||
match_ignore_ascii_case! {
|
||||
name,
|
||||
"matrix" => {
|
||||
try!(input.parse_nested_block(|input| {
|
||||
let values = try!(input.parse_comma_separated(|input| {
|
||||
input.expect_number()
|
||||
}));
|
||||
if values.len() != 6 {
|
||||
return Err(())
|
||||
}
|
||||
let (tx, ty) =
|
||||
(specified::Length::Absolute(Au::from_frac_px(values[4])),
|
||||
specified::Length::Absolute(Au::from_frac_px(values[5])));
|
||||
let (tx, ty) =
|
||||
(specified::LengthAndPercentage::from_length(tx),
|
||||
specified::LengthAndPercentage::from_length(ty));
|
||||
result.push(SpecifiedOperation::Matrix(
|
||||
SpecifiedMatrix::new(values[0], values[1],
|
||||
values[2], values[3],
|
||||
tx, ty)));
|
||||
Ok(())
|
||||
}))
|
||||
},
|
||||
"translate" => {
|
||||
try!(input.parse_nested_block(|input| {
|
||||
let (tx, ty) = try!(parse_two_lengths_or_percentages(input));
|
||||
result.push(SpecifiedOperation::Translate(tx, ty));
|
||||
Ok(())
|
||||
}))
|
||||
},
|
||||
"translatex" => {
|
||||
try!(input.parse_nested_block(|input| {
|
||||
let tx = try!(specified::LengthOrPercentage::parse(input));
|
||||
result.push(SpecifiedOperation::Translate(
|
||||
specified::LengthAndPercentage::from_length_or_percentage(
|
||||
&tx),
|
||||
specified::LengthAndPercentage::zero()));
|
||||
Ok(())
|
||||
}))
|
||||
},
|
||||
"translatey" => {
|
||||
try!(input.parse_nested_block(|input| {
|
||||
let ty = try!(specified::LengthOrPercentage::parse(input));
|
||||
result.push(SpecifiedOperation::Translate(
|
||||
specified::LengthAndPercentage::zero(),
|
||||
specified::LengthAndPercentage::from_length_or_percentage(
|
||||
&ty)));
|
||||
Ok(())
|
||||
}))
|
||||
},
|
||||
"scale" => {
|
||||
try!(input.parse_nested_block(|input| {
|
||||
let (sx, sy) = try!(parse_two_floats(input));
|
||||
result.push(SpecifiedOperation::Scale(sx, sy));
|
||||
Ok(())
|
||||
}))
|
||||
},
|
||||
"scalex" => {
|
||||
try!(input.parse_nested_block(|input| {
|
||||
let sx = try!(input.expect_number());
|
||||
result.push(SpecifiedOperation::Scale(sx, 1.0));
|
||||
Ok(())
|
||||
}))
|
||||
},
|
||||
"scaley" => {
|
||||
try!(input.parse_nested_block(|input| {
|
||||
let sy = try!(input.expect_number());
|
||||
result.push(SpecifiedOperation::Scale(1.0, sy));
|
||||
Ok(())
|
||||
}))
|
||||
},
|
||||
"rotate" => {
|
||||
try!(input.parse_nested_block(|input| {
|
||||
let theta = try!(specified::Angle::parse(input));
|
||||
result.push(SpecifiedOperation::Rotate(theta));
|
||||
Ok(())
|
||||
}))
|
||||
},
|
||||
"skew" => {
|
||||
try!(input.parse_nested_block(|input| {
|
||||
let (sx, sy) = try!(parse_two_floats(input));
|
||||
result.push(SpecifiedOperation::Skew(sx, sy));
|
||||
Ok(())
|
||||
}))
|
||||
},
|
||||
"skewx" => {
|
||||
try!(input.parse_nested_block(|input| {
|
||||
let sx = try!(input.expect_number());
|
||||
result.push(SpecifiedOperation::Skew(sx, 1.0));
|
||||
Ok(())
|
||||
}))
|
||||
},
|
||||
"skewy" => {
|
||||
try!(input.parse_nested_block(|input| {
|
||||
let sy = try!(input.expect_number());
|
||||
result.push(SpecifiedOperation::Skew(1.0, sy));
|
||||
Ok(())
|
||||
}))
|
||||
}
|
||||
_ => return Err(())
|
||||
}
|
||||
}
|
||||
|
||||
if !result.is_empty() {
|
||||
Ok(SpecifiedValue(result))
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToComputedValue for SpecifiedValue {
|
||||
type ComputedValue = computed_value::T;
|
||||
|
||||
#[inline]
|
||||
fn to_computed_value(&self, context: &Context) -> computed_value::T {
|
||||
if self.0.is_empty() {
|
||||
return None
|
||||
}
|
||||
|
||||
let mut result = computed_value::ComputedMatrix::identity();
|
||||
for operation in self.0.iter() {
|
||||
match *operation {
|
||||
SpecifiedOperation::Matrix(ref matrix) => {
|
||||
result = computed_value::ComputedMatrix {
|
||||
m11: matrix.m11, m12: matrix.m12,
|
||||
m21: matrix.m21, m22: matrix.m22,
|
||||
m31: matrix.m31.to_computed_value(context),
|
||||
m32: matrix.m32.to_computed_value(context),
|
||||
} * result
|
||||
}
|
||||
SpecifiedOperation::Translate(ref tx, ref ty) => {
|
||||
result.translate(tx.to_computed_value(context),
|
||||
ty.to_computed_value(context))
|
||||
}
|
||||
SpecifiedOperation::Scale(sx, sy) => {
|
||||
result.scale(sx, sy)
|
||||
}
|
||||
SpecifiedOperation::Rotate(ref theta) => {
|
||||
result.rotate(f64::consts::PI_2 - theta.radians());
|
||||
}
|
||||
SpecifiedOperation::Skew(sx, sy) => {
|
||||
result.skew(sx, sy)
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(result)
|
||||
}
|
||||
}
|
||||
</%self:longhand>
|
||||
|
||||
<%self:longhand name="transform-origin">
|
||||
use values::computed::{ToComputedValue, Context};
|
||||
use values::specified::LengthOrPercentage;
|
||||
|
||||
use cssparser::ToCss;
|
||||
use text_writer::{self, TextWriter};
|
||||
|
||||
pub mod computed_value {
|
||||
use values::computed::LengthOrPercentage;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct T {
|
||||
pub horizontal: LengthOrPercentage,
|
||||
pub vertical: LengthOrPercentage,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct SpecifiedValue {
|
||||
horizontal: LengthOrPercentage,
|
||||
vertical: LengthOrPercentage,
|
||||
}
|
||||
|
||||
impl ToCss for SpecifiedValue {
|
||||
fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter {
|
||||
try!(self.horizontal.to_css(dest));
|
||||
try!(dest.write_str(" "));
|
||||
self.vertical.to_css(dest)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_initial_value() -> computed_value::T {
|
||||
computed_value::T {
|
||||
horizontal: computed::LengthOrPercentage::Percentage(0.5),
|
||||
vertical: computed::LengthOrPercentage::Percentage(0.5),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
|
||||
let (mut horizontal, mut vertical) = (None, None);
|
||||
loop {
|
||||
if let Err(_) = input.try(|input| {
|
||||
let token = try!(input.expect_ident());
|
||||
match_ignore_ascii_case! {
|
||||
token,
|
||||
"left" => {
|
||||
if horizontal.is_none() {
|
||||
horizontal = Some(LengthOrPercentage::Percentage(0.0))
|
||||
} else {
|
||||
return Err(())
|
||||
}
|
||||
},
|
||||
"center" => {
|
||||
if horizontal.is_none() {
|
||||
horizontal = Some(LengthOrPercentage::Percentage(0.5))
|
||||
} else if vertical.is_none() {
|
||||
vertical = Some(LengthOrPercentage::Percentage(0.5))
|
||||
} else {
|
||||
return Err(())
|
||||
}
|
||||
},
|
||||
"right" => {
|
||||
if horizontal.is_none() {
|
||||
horizontal = Some(LengthOrPercentage::Percentage(1.0))
|
||||
} else {
|
||||
return Err(())
|
||||
}
|
||||
},
|
||||
"top" => {
|
||||
if vertical.is_none() {
|
||||
vertical = Some(LengthOrPercentage::Percentage(0.0))
|
||||
} else {
|
||||
return Err(())
|
||||
}
|
||||
},
|
||||
"bottom" => {
|
||||
if vertical.is_none() {
|
||||
vertical = Some(LengthOrPercentage::Percentage(1.0))
|
||||
} else {
|
||||
return Err(())
|
||||
}
|
||||
}
|
||||
_ => return Err(())
|
||||
}
|
||||
Ok(())
|
||||
}) {
|
||||
match LengthOrPercentage::parse(input) {
|
||||
Ok(value) if horizontal.is_none() => horizontal = Some(value),
|
||||
Ok(value) if vertical.is_none() => vertical = Some(value),
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if horizontal.is_some() || vertical.is_some() {
|
||||
Ok(SpecifiedValue {
|
||||
horizontal: horizontal.unwrap_or(LengthOrPercentage::Percentage(0.5)),
|
||||
vertical: vertical.unwrap_or(LengthOrPercentage::Percentage(0.5)),
|
||||
})
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToComputedValue for SpecifiedValue {
|
||||
type ComputedValue = computed_value::T;
|
||||
|
||||
#[inline]
|
||||
fn to_computed_value(&self, context: &Context) -> computed_value::T {
|
||||
computed_value::T {
|
||||
horizontal: self.horizontal.to_computed_value(context),
|
||||
vertical: self.vertical.to_computed_value(context),
|
||||
}
|
||||
}
|
||||
}
|
||||
</%self:longhand>
|
||||
|
||||
${single_keyword("mix-blend-mode",
|
||||
"""normal multiply screen overlay darken lighten color-dodge
|
||||
color-burn hard-light soft-light difference exclusion hue
|
||||
|
|
|
@ -57,6 +57,7 @@ pub mod specified {
|
|||
use std::fmt;
|
||||
use std::fmt::{Formatter, Debug};
|
||||
use std::num::{NumCast, ToPrimitive};
|
||||
use std::ops::{Add, Mul};
|
||||
use url::Url;
|
||||
use cssparser::{self, Token, Parser, ToCss, CssStringWriter};
|
||||
use geom::size::Size2D;
|
||||
|
@ -243,6 +244,47 @@ pub mod specified {
|
|||
}
|
||||
}
|
||||
|
||||
impl Mul<CSSFloat> for Length {
|
||||
type Output = Length;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, scalar: CSSFloat) -> Length {
|
||||
match self {
|
||||
Length::Absolute(Au(v)) => Length::Absolute(Au(((v as f64) * scalar) as i32)),
|
||||
Length::FontRelative(v) => Length::FontRelative(v * scalar),
|
||||
Length::ViewportPercentage(v) => Length::ViewportPercentage(v * scalar),
|
||||
Length::ServoCharacterWidth(_) => panic!("Can't multiply ServoCharacterWidth!"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<CSSFloat> for FontRelativeLength {
|
||||
type Output = FontRelativeLength;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, scalar: CSSFloat) -> FontRelativeLength {
|
||||
match self {
|
||||
FontRelativeLength::Em(v) => FontRelativeLength::Em(v * scalar),
|
||||
FontRelativeLength::Ex(v) => FontRelativeLength::Ex(v * scalar),
|
||||
FontRelativeLength::Rem(v) => FontRelativeLength::Rem(v * scalar),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<CSSFloat> for ViewportPercentageLength {
|
||||
type Output = ViewportPercentageLength;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, scalar: CSSFloat) -> ViewportPercentageLength {
|
||||
match self {
|
||||
ViewportPercentageLength::Vw(v) => ViewportPercentageLength::Vw(v * scalar),
|
||||
ViewportPercentageLength::Vh(v) => ViewportPercentageLength::Vh(v * scalar),
|
||||
ViewportPercentageLength::Vmin(v) => ViewportPercentageLength::Vmin(v * scalar),
|
||||
ViewportPercentageLength::Vmax(v) => ViewportPercentageLength::Vmax(v * scalar),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const AU_PER_PX: CSSFloat = 60.;
|
||||
const AU_PER_IN: CSSFloat = AU_PER_PX * 96.;
|
||||
const AU_PER_CM: CSSFloat = AU_PER_IN / 2.54;
|
||||
|
@ -444,6 +486,81 @@ pub mod specified {
|
|||
}
|
||||
}
|
||||
|
||||
/// The sum of a series of lengths and a percentage. This is used in `calc()` and other things
|
||||
/// that effectively work like it (e.g. transforms).
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct LengthAndPercentage {
|
||||
/// The length components.
|
||||
pub lengths: Vec<Length>,
|
||||
/// The percentage component.
|
||||
pub percentage: CSSFloat,
|
||||
}
|
||||
|
||||
impl LengthAndPercentage {
|
||||
pub fn zero() -> LengthAndPercentage {
|
||||
LengthAndPercentage {
|
||||
lengths: Vec::new(),
|
||||
percentage: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_length_or_percentage(length_or_percentage: &LengthOrPercentage)
|
||||
-> LengthAndPercentage {
|
||||
match *length_or_percentage {
|
||||
LengthOrPercentage::Length(ref length) => {
|
||||
LengthAndPercentage::from_length(*length)
|
||||
}
|
||||
LengthOrPercentage::Percentage(percentage) => {
|
||||
LengthAndPercentage::from_percentage(percentage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(input: &mut Parser) -> Result<LengthAndPercentage, ()> {
|
||||
LengthOrPercentage::parse(input).map(|value| {
|
||||
LengthAndPercentage::from_length_or_percentage(&value)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_length(length: Length) -> LengthAndPercentage {
|
||||
LengthAndPercentage {
|
||||
lengths: vec![length],
|
||||
percentage: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_percentage(percentage: CSSFloat) -> LengthAndPercentage {
|
||||
LengthAndPercentage {
|
||||
lengths: Vec::new(),
|
||||
percentage: percentage,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<LengthAndPercentage> for LengthAndPercentage {
|
||||
type Output = LengthAndPercentage;
|
||||
|
||||
fn add(self, other: LengthAndPercentage) -> LengthAndPercentage {
|
||||
let mut new_lengths = self.lengths.clone();
|
||||
new_lengths.push_all(other.lengths.as_slice());
|
||||
LengthAndPercentage {
|
||||
lengths: new_lengths,
|
||||
percentage: self.percentage + other.percentage,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<CSSFloat> for LengthAndPercentage {
|
||||
type Output = LengthAndPercentage;
|
||||
|
||||
fn mul(self, scalar: CSSFloat) -> LengthAndPercentage {
|
||||
LengthAndPercentage {
|
||||
lengths: self.lengths.iter().map(|length| *length * scalar).collect(),
|
||||
percentage: self.percentage * scalar,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// http://dev.w3.org/csswg/css2/colors.html#propdef-background-position
|
||||
#[derive(Clone, PartialEq, Copy)]
|
||||
pub enum PositionComponent {
|
||||
|
@ -756,6 +873,7 @@ pub mod computed {
|
|||
use geom::size::Size2D;
|
||||
use properties::longhands;
|
||||
use std::fmt;
|
||||
use std::ops::{Add, Mul};
|
||||
use url::Url;
|
||||
use util::geometry::Au;
|
||||
|
||||
|
@ -929,6 +1047,62 @@ pub mod computed {
|
|||
}
|
||||
}
|
||||
|
||||
/// The sum of a series of lengths and a percentage. This is used in `calc()` and other things
|
||||
/// that effectively work like it (e.g. transforms).
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct LengthAndPercentage {
|
||||
/// The length component.
|
||||
pub length: Au,
|
||||
/// The percentage component.
|
||||
pub percentage: CSSFloat,
|
||||
}
|
||||
|
||||
impl LengthAndPercentage {
|
||||
#[inline]
|
||||
pub fn zero() -> LengthAndPercentage {
|
||||
LengthAndPercentage {
|
||||
length: Au(0),
|
||||
percentage: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToComputedValue for specified::LengthAndPercentage {
|
||||
type ComputedValue = LengthAndPercentage;
|
||||
|
||||
fn to_computed_value(&self, context: &Context) -> LengthAndPercentage {
|
||||
let mut total_length = Au(0);
|
||||
for length in self.lengths.iter() {
|
||||
total_length = total_length + length.to_computed_value(context)
|
||||
}
|
||||
LengthAndPercentage {
|
||||
length: total_length,
|
||||
percentage: self.percentage,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<LengthAndPercentage> for LengthAndPercentage {
|
||||
type Output = LengthAndPercentage;
|
||||
|
||||
fn add(self, other: LengthAndPercentage) -> LengthAndPercentage {
|
||||
LengthAndPercentage {
|
||||
length: self.length + other.length,
|
||||
percentage: self.percentage + other.percentage,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<CSSFloat> for LengthAndPercentage {
|
||||
type Output = LengthAndPercentage;
|
||||
|
||||
fn mul(self, scalar: CSSFloat) -> LengthAndPercentage {
|
||||
LengthAndPercentage {
|
||||
length: Au::from_frac_px(self.length.to_subpx() * scalar),
|
||||
percentage: self.percentage * scalar,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToComputedValue for specified::Image {
|
||||
type ComputedValue = Image;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue