/* 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::GenericBorderRadius; use crate::values::generics::position::GenericPosition; 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(Box, 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, )] #[repr(C, u8)] pub enum GenericBasicShape { 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(GenericPolygon), } pub use self::GenericBasicShape as BasicShape; /// #[allow(missing_docs)] #[css(function = "inset")] #[derive( Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToResolvedValue, ToShmem, )] #[repr(C)] pub struct InsetRect { pub rect: Rect, #[shmem(field_bound)] pub round: GenericBorderRadius, } /// #[allow(missing_docs)] #[css(function)] #[derive( Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToResolvedValue, ToShmem, )] #[repr(C)] pub struct Circle { pub position: GenericPosition, pub radius: GenericShapeRadius, } /// #[allow(missing_docs)] #[css(function)] #[derive( Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToResolvedValue, ToShmem, )] #[repr(C)] pub struct Ellipse { pub position: GenericPosition, pub semiaxis_x: GenericShapeRadius, pub semiaxis_y: GenericShapeRadius, } /// #[allow(missing_docs)] #[derive( Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] #[repr(C, u8)] pub enum GenericShapeRadius { Length(NonNegativeLengthPercentage), #[animation(error)] ClosestSide, #[animation(error)] FarthestSide, } pub use self::GenericShapeRadius as ShapeRadius; /// A generic type for representing the `polygon()` function /// /// #[css(comma, function = "polygon")] #[derive( Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] #[repr(C)] pub struct GenericPolygon { /// 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: crate::OwnedSlice>, } pub use self::GenericPolygon as Polygon; /// Coordinates for Polygon. #[derive( Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] #[repr(C)] 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 ToCss for Circle where GenericPosition: ToCss, NonNegativeLengthPercentage: ToCss + PartialEq, { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: Write, { dest.write_str("circle(")?; if self.radius != Default::default() { self.radius.to_css(dest)?; dest.write_str(" ")?; } dest.write_str("at ")?; self.position.to_css(dest)?; dest.write_str(")") } } impl ToCss for Ellipse where GenericPosition: ToCss, NonNegativeLengthPercentage: ToCss + PartialEq, { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: Write, { dest.write_str("ellipse(")?; if self.semiaxis_x != Default::default() || self.semiaxis_y != Default::default() { self.semiaxis_x.to_css(dest)?; dest.write_str(" ")?; self.semiaxis_y.to_css(dest)?; dest.write_str(" ")?; } dest.write_str("at ")?; self.position.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::, _>>()? .into(); 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() }