/* 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::{self, Write}; use style_traits::{CssWriter, ToCss}; use values::animated::{Animate, Procedure, ToAnimatedZero}; 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; /// #[allow(missing_docs)] #[derive(Animate, Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)] pub enum GeometryBox { FillBox, StrokeBox, ViewBox, ShapeBox(ShapeBox), } /// A float area shape, for `shape-outside`. pub type FloatAreaShape = ShapeSource; /// https://drafts.csswg.org/css-shapes-1/#typedef-shape-box #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] #[derive(Animate, Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)] #[derive(ToComputedValue, ToCss)] pub enum ShapeBox { MarginBox, BorderBox, PaddingBox, ContentBox, } /// A shape source, for some reference box. #[allow(missing_docs)] #[animation(no_bound(ImageOrUrl))] #[derive(Animate, Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)] pub enum ShapeSource { #[animation(error)] ImageOrUrl(ImageOrUrl), Shape( BasicShape, Option, ), #[animation(error)] Box(ReferenceBox), #[animation(error)] None, } #[allow(missing_docs)] #[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq)] #[derive(ToComputedValue, ToCss)] pub enum BasicShape { Inset(#[css(field_bound)] InsetRect), Circle(#[css(field_bound)] Circle), Ellipse(#[css(field_bound)] Ellipse), Polygon(Polygon), } /// #[allow(missing_docs)] #[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq, ToComputedValue)] pub struct InsetRect { pub rect: Rect, pub round: Option>, } /// #[allow(missing_docs)] #[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue)] pub struct Circle { pub position: Position, pub radius: ShapeRadius, } /// #[allow(missing_docs)] #[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue)] pub struct Ellipse { pub position: Position, pub semiaxis_x: ShapeRadius, pub semiaxis_y: ShapeRadius, } /// #[allow(missing_docs)] #[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq)] #[derive(ToComputedValue, ToCss)] pub enum ShapeRadius { Length(LengthOrPercentage), #[animation(error)] ClosestSide, #[animation(error)] FarthestSide, } /// A generic type for representing the `polygon()` function /// /// #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue)] 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` #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)] #[derive(ToComputedValue, ToCss)] pub enum FillRule { Nonzero, Evenodd, } // 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 CssWriter) -> fmt::Result where W: 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 CssWriter) -> fmt::Result where W: 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 } }