/* 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 https://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 crate::values::animated::{Animate, Procedure, ToAnimatedZero}; use crate::values::distance::{ComputeSquaredDistance, SquaredDistance}; use crate::values::generics::border::BorderRadius; use crate::values::generics::position::Position; use crate::values::generics::rect::Rect; use crate::values::specified::SVGPathData; use crate::Zero; use std::fmt::{self, Write}; use style_traits::{CssWriter, ToCss}; /// A clipping shape, for `clip-path`. pub type ClippingShape = ShapeSource; /// #[allow(missing_docs)] #[derive( Animate, Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] 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, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] 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, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] pub enum ShapeSource { #[animation(error)] ImageOrUrl(ImageOrUrl), Shape(BasicShape, Option), #[animation(error)] Box(ReferenceBox), #[css(function)] Path(Path), #[animation(error)] None, } #[allow(missing_docs)] #[derive( Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] pub enum BasicShape { Inset( #[css(field_bound)] #[shmem(field_bound)] InsetRect, ), Circle( #[css(field_bound)] #[shmem(field_bound)] Circle, ), Ellipse( #[css(field_bound)] #[shmem(field_bound)] Ellipse, ), Polygon(Polygon), } /// #[allow(missing_docs)] #[css(function = "inset")] #[derive( Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToResolvedValue, ToShmem, )] pub struct InsetRect { pub rect: Rect, #[shmem(field_bound)] pub round: BorderRadius, } /// #[allow(missing_docs)] #[css(function)] #[derive( Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToResolvedValue, ToShmem, )] pub struct Circle { pub position: Position, pub radius: ShapeRadius, } /// #[allow(missing_docs)] #[css(function)] #[derive( Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToResolvedValue, ToShmem, )] 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, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] pub enum ShapeRadius { Length(NonNegativeLengthPercentage), #[animation(error)] ClosestSide, #[animation(error)] FarthestSide, } /// A generic type for representing the `polygon()` function /// /// #[css(comma, function)] #[derive( Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] pub struct Polygon { /// The filling rule for a polygon. #[css(skip_if = "fill_is_default")] pub fill: FillRule, /// A collection of (x, y) coordinates to draw the polygon. #[css(iterable)] pub coordinates: Vec>, } /// Coordinates for Polygon. #[derive( Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] pub struct PolygonCoord(pub LengthPercentage, pub LengthPercentage); // 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, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] #[repr(u8)] pub enum FillRule { Nonzero, Evenodd, } /// The path function defined in css-shape-2. /// /// https://drafts.csswg.org/css-shapes-2/#funcdef-path #[css(comma)] #[derive( Animate, Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] pub struct Path { /// The filling rule for the svg path. #[css(skip_if = "fill_is_default")] #[animation(constant)] pub fill: FillRule, /// The svg path data. pub path: SVGPathData, } // 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), (&ShapeSource::Path(ref this), &ShapeSource::Path(ref other)) if this.fill == other.fill => { this.path.compute_squared_distance(&other.path) }, _ => Err(()), } } } impl ToAnimatedZero for ShapeSource { fn to_animated_zero(&self) -> Result { Err(()) } } impl ToCss for InsetRect where Length: ToCss + PartialEq, NonNegativeLength: ToCss + PartialEq + Zero, { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: Write, { dest.write_str("inset(")?; self.rect.to_css(dest)?; if !self.round.is_zero() { dest.write_str(" round ")?; self.round.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(PolygonCoord( 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)| { let d1 = this.0.compute_squared_distance(&other.0)?; let d2 = this.1.compute_squared_distance(&other.1)?; Ok(d1 + d2) }) .sum() } } impl Default for FillRule { #[inline] fn default() -> Self { FillRule::Nonzero } } #[inline] fn fill_is_default(fill: &FillRule) -> bool { *fill == FillRule::default() }