/* 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/. */ //! CSS handling for the [`basic-shape`](https://drafts.csswg.org/css-shapes/#typedef-basic-shape) //! types that are generic over their `ToCss` implementations. use std::fmt; use style_traits::ToCss; use values::animated::{Animate, Procedure, ToAnimatedZero}; use values::computed::ComputedValueAsSpecified; use values::distance::{ComputeSquaredDistance, SquaredDistance}; use values::generics::border::BorderRadius; use values::generics::position::Position; use values::generics::rect::Rect; /// A clipping shape, for `clip-path`. pub type ClippingShape = ShapeSource; /// https://drafts.fxtf.org/css-masking-1/#typedef-geometry-box #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[derive(Clone, Copy, Debug, PartialEq, ToCss)] pub enum GeometryBox { FillBox, StrokeBox, ViewBox, ShapeBox(ShapeBox), } impl ComputedValueAsSpecified for GeometryBox {} /// A float area shape, for `shape-outside`. pub type FloatAreaShape = ShapeSource; // https://drafts.csswg.org/css-shapes-1/#typedef-shape-box define_css_keyword_enum!(ShapeBox: "margin-box" => MarginBox, "border-box" => BorderBox, "padding-box" => PaddingBox, "content-box" => ContentBox ); add_impls_for_keyword_enum!(ShapeBox); /// A shape source, for some reference box. #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[derive(Animate, Clone, Debug, PartialEq, ToComputedValue, ToCss)] pub enum ShapeSource { #[animation(error)] Url(Url), Shape( BasicShape, #[animation(constant)] Option, ), #[animation(error)] Box(ReferenceBox), #[animation(error)] None, } #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[derive(Animate, Clone, ComputeSquaredDistance, Debug, PartialEq)] #[derive(ToComputedValue, ToCss)] pub enum BasicShape { Inset(InsetRect), Circle(Circle), Ellipse(Ellipse), Polygon(Polygon), } /// https://drafts.csswg.org/css-shapes/#funcdef-inset #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[derive(Animate, Clone, ComputeSquaredDistance, Debug, PartialEq, ToComputedValue)] pub struct InsetRect { pub rect: Rect, pub round: Option>, } /// https://drafts.csswg.org/css-shapes/#funcdef-circle #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, PartialEq, ToComputedValue)] pub struct Circle { pub position: Position, pub radius: ShapeRadius, } /// https://drafts.csswg.org/css-shapes/#funcdef-ellipse #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, PartialEq, ToComputedValue)] pub struct Ellipse { pub position: Position, pub semiaxis_x: ShapeRadius, pub semiaxis_y: ShapeRadius, } /// https://drafts.csswg.org/css-shapes/#typedef-shape-radius #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, PartialEq)] #[derive(ToComputedValue, ToCss)] pub enum ShapeRadius { Length(LengthOrPercentage), #[animation(error)] ClosestSide, #[animation(error)] FarthestSide, } #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[derive(Clone, Debug, PartialEq, ToComputedValue)] /// A generic type for representing the `polygon()` function /// /// https://drafts.csswg.org/css-shapes/#funcdef-polygon pub struct Polygon { /// The filling rule for a polygon. pub fill: FillRule, /// A collection of (x, y) coordinates to draw the polygon. pub coordinates: Vec<(LengthOrPercentage, LengthOrPercentage)>, } // https://drafts.csswg.org/css-shapes/#typedef-fill-rule // NOTE: Basic shapes spec says that these are the only two values, however // https://www.w3.org/TR/SVG/painting.html#FillRuleProperty // says that it can also be `inherit` define_css_keyword_enum!(FillRule: "nonzero" => NonZero, "evenodd" => EvenOdd ); add_impls_for_keyword_enum!(FillRule); // FIXME(nox): Implement ComputeSquaredDistance for T types and stop // using PartialEq here, this will let us derive this impl. impl ComputeSquaredDistance for ShapeSource where B: ComputeSquaredDistance, T: PartialEq, { fn compute_squared_distance(&self, other: &Self) -> Result { match (self, other) { ( &ShapeSource::Shape(ref this, ref this_box), &ShapeSource::Shape(ref other, ref other_box), ) if this_box == other_box => { this.compute_squared_distance(other) }, _ => Err(()), } } } impl ToAnimatedZero for ShapeSource { fn to_animated_zero(&self) -> Result { Err(()) } } impl ToCss for InsetRect where L: ToCss + PartialEq { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { dest.write_str("inset(")?; self.rect.to_css(dest)?; if let Some(ref radius) = self.round { dest.write_str(" round ")?; radius.to_css(dest)?; } dest.write_str(")") } } impl Default for ShapeRadius { #[inline] fn default() -> Self { ShapeRadius::ClosestSide } } impl Animate for Polygon where L: Animate, { fn animate(&self, other: &Self, procedure: Procedure) -> Result { if self.fill != other.fill { return Err(()); } if self.coordinates.len() != other.coordinates.len() { return Err(()); } let coordinates = self.coordinates.iter().zip(other.coordinates.iter()).map(|(this, other)| { Ok(( this.0.animate(&other.0, procedure)?, this.1.animate(&other.1, procedure)?, )) }).collect::, _>>()?; Ok(Polygon { fill: self.fill, coordinates }) } } impl ComputeSquaredDistance for Polygon where L: ComputeSquaredDistance, { fn compute_squared_distance(&self, other: &Self) -> Result { if self.fill != other.fill { return Err(()); } if self.coordinates.len() != other.coordinates.len() { return Err(()); } self.coordinates.iter().zip(other.coordinates.iter()).map(|(this, other)| { Ok( this.0.compute_squared_distance(&other.0)? + this.1.compute_squared_distance(&other.1)?, ) }).sum() } } impl ToCss for Polygon { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { dest.write_str("polygon(")?; if self.fill != FillRule::default() { self.fill.to_css(dest)?; dest.write_str(", ")?; } for (i, coord) in self.coordinates.iter().enumerate() { if i > 0 { dest.write_str(", ")?; } coord.0.to_css(dest)?; dest.write_str(" ")?; coord.1.to_css(dest)?; } dest.write_str(")") } } impl Default for FillRule { #[inline] fn default() -> Self { FillRule::NonZero } }