/* 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}; /// #[allow(missing_docs)] #[derive( Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, Parse, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] #[repr(u8)] pub enum ShapeGeometryBox { /// Depending on which kind of element this style value applied on, the /// default value of the reference-box can be different. For an HTML /// element, the default value of reference-box is border-box; for an SVG /// element, the default value is fill-box. Since we can not determine the /// default value at parsing time, we keep this value to make a decision on /// it. #[css(skip)] ElementDependent, FillBox, StrokeBox, ViewBox, ShapeBox(ShapeBox), } impl Default for ShapeGeometryBox { fn default() -> Self { Self::ElementDependent } } /// https://drafts.csswg.org/css-shapes-1/#typedef-shape-box #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] #[derive( Animate, Clone, Copy, ComputeSquaredDistance, Debug, Eq, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] #[repr(u8)] pub enum ShapeBox { MarginBox, BorderBox, PaddingBox, ContentBox, } impl Default for ShapeBox { fn default() -> Self { ShapeBox::MarginBox } } /// A value for the `clip-path` property. #[allow(missing_docs)] #[derive( Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] #[animation(no_bound(U))] #[repr(u8)] pub enum GenericClipPath { #[animation(error)] None, #[animation(error)] Url(U), #[css(function)] Path(Path), Shape( Box, #[css(skip_if = "is_default")] ShapeGeometryBox, ), #[animation(error)] Box(ShapeGeometryBox), } pub use self::GenericClipPath as ClipPath; /// A value for the `shape-outside` property. #[allow(missing_docs)] #[derive( Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] #[animation(no_bound(I))] #[repr(u8)] pub enum GenericShapeOutside { #[animation(error)] None, #[animation(error)] Image(I), Shape(Box, #[css(skip_if = "is_default")] ShapeBox), #[animation(error)] Box(ShapeBox), } pub use self::GenericShapeOutside as ShapeOutside; #[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)] #[derive( Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToResolvedValue, ToShmem, )] #[css(function = "inset")] #[repr(C)] pub struct InsetRect { pub rect: Rect, #[shmem(field_bound)] pub round: GenericBorderRadius, } /// #[allow(missing_docs)] #[derive( Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToResolvedValue, ToShmem, )] #[css(function)] #[repr(C)] pub struct Circle { pub position: GenericPosition, pub radius: GenericShapeRadius, } /// #[allow(missing_docs)] #[derive( Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToResolvedValue, ToShmem, )] #[css(function)] #[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, Parse, 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 /// /// #[derive( Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] #[css(comma, function = "polygon")] #[repr(C)] pub struct GenericPolygon { /// The filling rule for a polygon. #[css(skip_if = "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 #[derive( Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] #[css(comma)] #[repr(C)] pub struct Path { /// The filling rule for the svg path. #[css(skip_if = "is_default")] #[animation(constant)] pub fill: FillRule, /// The svg path data. pub path: SVGPathData, } impl ToAnimatedZero for ClipPath { fn to_animated_zero(&self) -> Result { Err(()) } } impl ToAnimatedZero for ShapeOutside { 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 is_default(fill: &T) -> bool { *fill == Default::default() }