- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 83 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 83 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 83 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 86 %
- : 85 %
- : 85 %
- : 85 %
- : 85 %
- : 85 %
- : 85 %
- : 85 %
- : 85 %
- : 85 %
- : 85 %
- : 85 %
- : 85 %
- : 85 %
- : 84 %
- : 84 %
- : 84 %
- : 84 %
- : 84 %
- : 84 %
- : 84 %
- : 84 %
- : 84 %
- : 84 %
- : 84 %
- : 84 %
- : 84 %
- : 84 %
- : 84 %
- : 84 %
- : 84 %
- : 84 %
- : 84 %
- : 84 %
- : 84 %
- : 84 %
- : 84 %
- : 84 %
- : 84 %
- : 84 %
- : 84 %
- : 84 %
- : 84 %
- : 84 %
- : 84 %
- : 84 %
- : 84 %
- : 75 %
- : 84 %
- : 83 %
- : 83 %
- : 83 %
- : 83 %
- : 82 %
- : 82 %
- : 82 %
- : 82 %
- : 82 %
- : 82 %
- : 82 %
- : 82 %
- : 82 %
- : 82 %
- : 82 %
- : 82 %
- : 82 %
- : 83 %
- : 83 %
- : 83 %
- : 83 %
- : 83 %
- : 83 %
- : 83 %
- : 83 %
- : 83 %
- : 83 %
- : 70 %
- : 70 %
- : 71 %
- : 64 %
- : 64 %
- : 70 %
- : 64 %
- : 71 %
- : 71 %
- : 71 %
- : 64 %
- : 64 %
- : 64 %
- : 64 %
- : 64 %
- : 71 %
- : 64 %
- : 71 %
- : 71 %
- : 64 %
- : 64 %
- : 63 %
- : 71 %
- : 71 %
- : 64 %
- : 64 %
- : 71 %
- : 71 %
- : 71 %
- : 71 %
- : 71 %
- : 71 %
- : 71 %
- : 71 %
- : 71 %
- : 70 %
- : 69 %
- : 71 %
- : 71 %
- : 70 %
- : 82 %
- : 82 %
- : 82 %
- : 82 %
- : 82 %
- : 82 %
- : 82 %
- : 82 %
- : 82 %
- : 82 %
- : 82 %
- : 82 %
- : 82 %
- : 82 %
- : 82 %
- : 80 %
- : 75 %
- : 77 %
- : 81 %
- : 82 %
- : 82 %
Source code
Revision control
Copy as Markdown
Other Tools
/* 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::derives::*;
use crate::values::animated::{lists, Animate, Procedure, ToAnimatedZero};
use crate::values::computed::Percentage;
use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
use crate::values::generics::{
border::GenericBorderRadius,
position::{GenericPosition, GenericPositionOrAuto},
rect::Rect,
NonNegative, Optional,
};
use crate::values::specified::svg_path::{PathCommand, SVGPathData};
use crate::Zero;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss};
/// A generic value for `<position>` in circle(), ellipse(), and shape().
pub type ShapePosition<LengthPercentage> = GenericPosition<LengthPercentage, LengthPercentage>;
#[allow(missing_docs)]
#[derive(
Animate,
Clone,
ComputeSquaredDistance,
Copy,
Debug,
MallocSizeOf,
PartialEq,
Parse,
SpecifiedValueInfo,
ToAnimatedValue,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
ToTyped,
)]
#[repr(u8)]
#[typed_value(derive_fields)]
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
}
}
/// Skip the serialization if the author omits the box or specifies border-box.
#[inline]
fn is_default_box_for_clip_path(b: &ShapeGeometryBox) -> bool {
// Note: for clip-path, ElementDependent is always border-box, so we have to check both of them
// for serialization.
matches!(b, ShapeGeometryBox::ElementDependent)
|| matches!(b, ShapeGeometryBox::ShapeBox(ShapeBox::BorderBox))
}
#[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,
ToTyped,
)]
#[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,
ToTyped,
)]
#[animation(no_bound(U))]
#[repr(u8)]
#[typed_value(derive_fields)]
pub enum GenericClipPath<BasicShape, U> {
#[animation(error)]
None,
#[animation(error)]
// XXX This will likely change to skip since it seems Typed OM Level 1
// won't be updated to cover this case even though there's some preparation
// in WPT tests for this.
#[typed_value(todo)]
Url(U),
#[typed_value(skip)]
Shape(
#[animation(field_bound)] Box<BasicShape>,
#[css(skip_if = "is_default_box_for_clip_path")] 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,
ToTyped,
)]
#[animation(no_bound(I))]
#[repr(u8)]
pub enum GenericShapeOutside<BasicShape, I> {
#[animation(error)]
None,
#[animation(error)]
Image(I),
Shape(Box<BasicShape>, #[css(skip_if = "is_default")] ShapeBox),
#[animation(error)]
Box(ShapeBox),
}
pub use self::GenericShapeOutside as ShapeOutside;
/// The <basic-shape>.
///
#[derive(
Animate,
Clone,
ComputeSquaredDistance,
Debug,
Deserialize,
MallocSizeOf,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(C, u8)]
pub enum GenericBasicShape<Angle, Position, LengthPercentage, BasicShapeRect> {
/// The <basic-shape-rect>.
Rect(BasicShapeRect),
/// Defines a circle with a center and a radius.
Circle(
#[animation(field_bound)]
#[css(field_bound)]
#[shmem(field_bound)]
Circle<LengthPercentage>,
),
/// Defines an ellipse with a center and x-axis/y-axis radii.
Ellipse(
#[animation(field_bound)]
#[css(field_bound)]
#[shmem(field_bound)]
Ellipse<LengthPercentage>,
),
/// Defines a polygon with pair arguments.
Polygon(GenericPolygon<LengthPercentage>),
/// Defines a path() or shape().
PathOrShape(
#[animation(field_bound)]
#[css(field_bound)]
#[compute(field_bound)]
GenericPathOrShapeFunction<Angle, Position, LengthPercentage>,
),
}
pub use self::GenericBasicShape as BasicShape;
#[allow(missing_docs)]
#[derive(
Animate,
Clone,
ComputeSquaredDistance,
Debug,
Deserialize,
MallocSizeOf,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
#[css(function = "inset")]
#[repr(C)]
pub struct GenericInsetRect<LengthPercentage> {
pub rect: Rect<LengthPercentage>,
#[shmem(field_bound)]
#[animation(field_bound)]
pub round: GenericBorderRadius<NonNegative<LengthPercentage>>,
}
pub use self::GenericInsetRect as InsetRect;
#[allow(missing_docs)]
#[derive(
Animate,
Clone,
ComputeSquaredDistance,
Copy,
Debug,
Deserialize,
MallocSizeOf,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
#[css(function)]
#[repr(C)]
pub struct Circle<LengthPercentage> {
pub position: GenericPositionOrAuto<ShapePosition<LengthPercentage>>,
#[animation(field_bound)]
pub radius: GenericShapeRadius<LengthPercentage>,
}
#[allow(missing_docs)]
#[derive(
Animate,
Clone,
ComputeSquaredDistance,
Copy,
Debug,
Deserialize,
MallocSizeOf,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
#[css(function)]
#[repr(C)]
pub struct Ellipse<LengthPercentage> {
pub position: GenericPositionOrAuto<ShapePosition<LengthPercentage>>,
#[animation(field_bound)]
pub semiaxis_x: GenericShapeRadius<LengthPercentage>,
#[animation(field_bound)]
pub semiaxis_y: GenericShapeRadius<LengthPercentage>,
}
#[allow(missing_docs)]
#[derive(
Animate,
Clone,
ComputeSquaredDistance,
Copy,
Debug,
Deserialize,
MallocSizeOf,
Parse,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(C, u8)]
pub enum GenericShapeRadius<LengthPercentage> {
Length(
#[animation(field_bound)]
#[parse(field_bound)]
NonNegative<LengthPercentage>,
),
#[animation(error)]
ClosestSide,
#[animation(error)]
FarthestSide,
}
pub use self::GenericShapeRadius as ShapeRadius;
/// A generic type for representing the `polygon()` function
///
#[derive(
Clone,
Debug,
Deserialize,
MallocSizeOf,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[css(comma, function = "polygon")]
#[repr(C)]
pub struct GenericPolygon<LengthPercentage> {
/// 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<PolygonCoord<LengthPercentage>>,
}
pub use self::GenericPolygon as Polygon;
/// Coordinates for Polygon.
#[derive(
Animate,
Clone,
ComputeSquaredDistance,
Debug,
Deserialize,
MallocSizeOf,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(C)]
pub struct PolygonCoord<LengthPercentage>(pub LengthPercentage, pub LengthPercentage);
/// path() function or shape() function.
#[derive(
Clone,
ComputeSquaredDistance,
Debug,
Deserialize,
MallocSizeOf,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(C, u8)]
pub enum GenericPathOrShapeFunction<Angle, Position, LengthPercentage> {
/// Defines a path with SVG path syntax.
Path(Path),
/// Defines a shape function, which is identical to path() but it uses the CSS syntax.
Shape(
#[css(field_bound)]
#[compute(field_bound)]
Shape<Angle, Position, LengthPercentage>,
),
}
// NOTE: Basic shapes spec says that these are the only two values, however
// says that it can also be `inherit`
#[allow(missing_docs)]
#[derive(
Animate,
Clone,
ComputeSquaredDistance,
Copy,
Debug,
Deserialize,
Eq,
MallocSizeOf,
Parse,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
ToTyped,
)]
#[repr(u8)]
pub enum FillRule {
Nonzero,
Evenodd,
}
/// The path function.
///
#[derive(
Animate,
Clone,
ComputeSquaredDistance,
Debug,
Deserialize,
MallocSizeOf,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[css(comma, function = "path")]
#[repr(C)]
pub struct Path {
/// The filling rule for the svg path.
#[css(skip_if = "is_default")]
pub fill: FillRule,
/// The svg path data.
pub path: SVGPathData,
}
impl Path {
/// Returns the slice of PathCommand.
#[inline]
pub fn commands(&self) -> &[PathCommand] {
self.path.commands()
}
}
impl<B, U> ToAnimatedZero for ClipPath<B, U> {
fn to_animated_zero(&self) -> Result<Self, ()> {
Err(())
}
}
impl<B, U> ToAnimatedZero for ShapeOutside<B, U> {
fn to_animated_zero(&self) -> Result<Self, ()> {
Err(())
}
}
impl<Length> ToCss for InsetRect<Length>
where
Length: ToCss + PartialEq + Zero,
{
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> 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_char(')')
}
}
impl<LengthPercentage> ToCss for Circle<LengthPercentage>
where
LengthPercentage: ToCss + PartialEq,
ShapePosition<LengthPercentage>: ToCss,
{
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
let has_radius = self.radius != Default::default();
dest.write_str("circle(")?;
if has_radius {
self.radius.to_css(dest)?;
}
// Preserve the `at <position>` even if it specified the default value.
if !matches!(self.position, GenericPositionOrAuto::Auto) {
if has_radius {
dest.write_char(' ')?;
}
dest.write_str("at ")?;
self.position.to_css(dest)?;
}
dest.write_char(')')
}
}
impl<LengthPercentage> ToCss for Ellipse<LengthPercentage>
where
LengthPercentage: ToCss + PartialEq,
ShapePosition<LengthPercentage>: ToCss,
{
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
let has_radii =
self.semiaxis_x != Default::default() || self.semiaxis_y != Default::default();
dest.write_str("ellipse(")?;
if has_radii {
self.semiaxis_x.to_css(dest)?;
dest.write_char(' ')?;
self.semiaxis_y.to_css(dest)?;
}
// Preserve the `at <position>` even if it specified the default value.
if !matches!(self.position, GenericPositionOrAuto::Auto) {
if has_radii {
dest.write_char(' ')?;
}
dest.write_str("at ")?;
self.position.to_css(dest)?;
}
dest.write_char(')')
}
}
impl<L> Default for ShapeRadius<L> {
#[inline]
fn default() -> Self {
ShapeRadius::ClosestSide
}
}
impl<L> Animate for Polygon<L>
where
L: Animate,
{
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
if self.fill != other.fill {
return Err(());
}
let coordinates =
lists::by_computed_value::animate(&self.coordinates, &other.coordinates, procedure)?;
Ok(Polygon {
fill: self.fill,
coordinates,
})
}
}
impl<L> ComputeSquaredDistance for Polygon<L>
where
L: ComputeSquaredDistance,
{
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
if self.fill != other.fill {
return Err(());
}
lists::by_computed_value::squared_distance(&self.coordinates, &other.coordinates)
}
}
impl Default for FillRule {
#[inline]
fn default() -> Self {
FillRule::Nonzero
}
}
#[inline]
fn is_default<T: Default + PartialEq>(fill: &T) -> bool {
*fill == Default::default()
}
/// The shape function defined in css-shape-2.
/// shape() = shape(<fill-rule>? from <coordinate-pair>, <shape-command>#)
///
#[derive(
Clone,
Debug,
Deserialize,
MallocSizeOf,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
#[repr(C)]
pub struct Shape<Angle, Position, LengthPercentage> {
/// The filling rule for this shape.
pub fill: FillRule,
/// The shape command data. Note that the starting point will be the first command in this
/// slice.
// Note: The first command is always GenericShapeCommand::Move.
#[compute(field_bound)]
pub commands: crate::OwnedSlice<GenericShapeCommand<Angle, Position, LengthPercentage>>,
}
impl<Angle, Position, LengthPercentage> Shape<Angle, Position, LengthPercentage> {
/// Returns the slice of GenericShapeCommand<..>.
#[inline]
pub fn commands(&self) -> &[GenericShapeCommand<Angle, Position, LengthPercentage>] {
&self.commands
}
}
impl<Angle, Position, LengthPercentage> Animate for Shape<Angle, Position, LengthPercentage>
where
Angle: Animate,
Position: Animate,
LengthPercentage: Animate,
{
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
if self.fill != other.fill {
return Err(());
}
let commands =
lists::by_computed_value::animate(&self.commands, &other.commands, procedure)?;
Ok(Self {
fill: self.fill,
commands,
})
}
}
impl<Angle, Position, LengthPercentage> ComputeSquaredDistance
for Shape<Angle, Position, LengthPercentage>
where
Angle: ComputeSquaredDistance,
Position: ComputeSquaredDistance,
LengthPercentage: ComputeSquaredDistance,
{
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
if self.fill != other.fill {
return Err(());
}
lists::by_computed_value::squared_distance(&self.commands, &other.commands)
}
}
impl<Angle, Position, LengthPercentage> ToCss for Shape<Angle, Position, LengthPercentage>
where
Angle: ToCss + Zero,
Position: ToCss,
LengthPercentage: PartialEq + ToCss,
{
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
use style_traits::values::SequenceWriter;
// Per spec, we must have the first move command and at least one following command.
debug_assert!(self.commands.len() > 1);
dest.write_str("shape(")?;
if !is_default(&self.fill) {
self.fill.to_css(dest)?;
dest.write_char(' ')?;
}
dest.write_str("from ")?;
match &self.commands[0] {
ShapeCommand::Move {
point: CommandEndPoint::ToPosition(pos),
} => pos.to_css(dest)?,
ShapeCommand::Move {
point: CommandEndPoint::ByCoordinate(coord),
} => coord.to_css(dest)?,
_ => unreachable!("The first command must be move"),
}
dest.write_str(", ")?;
{
let mut writer = SequenceWriter::new(dest, ", ");
for command in self.commands.iter().skip(1) {
writer.item(command)?;
}
}
dest.write_char(')')
}
}
/// This is a more general shape(path) command type, for both shape() and path().
///
#[derive(
Animate,
Clone,
ComputeSquaredDistance,
Copy,
Debug,
Deserialize,
MallocSizeOf,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToAnimatedZero,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
#[allow(missing_docs)]
#[repr(C, u8)]
pub enum GenericShapeCommand<Angle, Position, LengthPercentage> {
/// The move command.
Move {
point: CommandEndPoint<Position, LengthPercentage>,
},
/// The line command.
Line {
point: CommandEndPoint<Position, LengthPercentage>,
},
/// The hline command.
HLine {
#[compute(field_bound)]
x: AxisEndPoint<LengthPercentage>,
},
/// The vline command.
VLine {
#[compute(field_bound)]
y: AxisEndPoint<LengthPercentage>,
},
/// The cubic Bézier curve command.
CubicCurve {
point: CommandEndPoint<Position, LengthPercentage>,
control1: ControlPoint<Position, LengthPercentage>,
control2: ControlPoint<Position, LengthPercentage>,
},
/// The quadratic Bézier curve command.
QuadCurve {
point: CommandEndPoint<Position, LengthPercentage>,
control1: ControlPoint<Position, LengthPercentage>,
},
/// The smooth command.
SmoothCubic {
point: CommandEndPoint<Position, LengthPercentage>,
control2: ControlPoint<Position, LengthPercentage>,
},
/// The smooth quadratic Bézier curve command.
SmoothQuad {
point: CommandEndPoint<Position, LengthPercentage>,
},
/// The arc command.
Arc {
point: CommandEndPoint<Position, LengthPercentage>,
radii: ArcRadii<LengthPercentage>,
arc_sweep: ArcSweep,
arc_size: ArcSize,
rotate: Angle,
},
/// The closepath command.
Close,
}
pub use self::GenericShapeCommand as ShapeCommand;
impl<Angle, Position, LengthPercentage> ToCss for ShapeCommand<Angle, Position, LengthPercentage>
where
Angle: ToCss + Zero,
Position: ToCss,
LengthPercentage: PartialEq + ToCss,
{
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
use self::ShapeCommand::*;
match *self {
Move { ref point } => {
dest.write_str("move ")?;
point.to_css(dest)
},
Line { ref point } => {
dest.write_str("line ")?;
point.to_css(dest)
},
HLine { ref x } => {
dest.write_str("hline ")?;
x.to_css(dest)
},
VLine { ref y } => {
dest.write_str("vline ")?;
y.to_css(dest)
},
CubicCurve {
ref point,
ref control1,
ref control2,
} => {
dest.write_str("curve ")?;
point.to_css(dest)?;
dest.write_str(" with ")?;
control1.to_css(dest, point.is_abs())?;
dest.write_char(' ')?;
dest.write_char('/')?;
dest.write_char(' ')?;
control2.to_css(dest, point.is_abs())
},
QuadCurve {
ref point,
ref control1,
} => {
dest.write_str("curve ")?;
point.to_css(dest)?;
dest.write_str(" with ")?;
control1.to_css(dest, point.is_abs())
},
SmoothCubic {
ref point,
ref control2,
} => {
dest.write_str("smooth ")?;
point.to_css(dest)?;
dest.write_str(" with ")?;
control2.to_css(dest, point.is_abs())
},
SmoothQuad { ref point } => {
dest.write_str("smooth ")?;
point.to_css(dest)
},
Arc {
ref point,
ref radii,
arc_sweep,
arc_size,
ref rotate,
} => {
dest.write_str("arc ")?;
point.to_css(dest)?;
dest.write_str(" of ")?;
radii.to_css(dest)?;
if matches!(arc_sweep, ArcSweep::Cw) {
dest.write_str(" cw")?;
}
if matches!(arc_size, ArcSize::Large) {
dest.write_str(" large")?;
}
if !rotate.is_zero() {
dest.write_str(" rotate ")?;
rotate.to_css(dest)?;
}
Ok(())
},
Close => dest.write_str("close"),
}
}
}
/// Defines the end point of the command, which can be specified in absolute or relative coordinates,
/// determined by their "to" or "by" components respectively.
#[allow(missing_docs)]
#[derive(
Animate,
Clone,
ComputeSquaredDistance,
Copy,
Debug,
Deserialize,
MallocSizeOf,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToAnimatedZero,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
#[repr(C, u8)]
pub enum CommandEndPoint<Position, LengthPercentage> {
ToPosition(Position),
ByCoordinate(CoordinatePair<LengthPercentage>),
}
impl<Position, LengthPercentage> CommandEndPoint<Position, LengthPercentage> {
/// Return true if it is absolute, i.e. it is To.
#[inline]
pub fn is_abs(&self) -> bool {
matches!(self, CommandEndPoint::ToPosition(_))
}
}
impl<Position, LengthPercentage> CommandEndPoint<Position, LengthPercentage> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
Position: ToCss,
LengthPercentage: ToCss,
{
match self {
CommandEndPoint::ToPosition(pos) => {
dest.write_str("to ")?;
pos.to_css(dest)
},
CommandEndPoint::ByCoordinate(coord) => {
dest.write_str("by ")?;
coord.to_css(dest)
},
}
}
}
/// Defines the end point for the commands <horizontal-line-command> and <vertical-line-command>, which
/// can be specified in absolute or relative values, determined by their "to" or "by" components respectively.
#[allow(missing_docs)]
#[derive(
Animate,
Clone,
Copy,
ComputeSquaredDistance,
Debug,
Deserialize,
MallocSizeOf,
PartialEq,
Parse,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToAnimatedZero,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
#[repr(u8)]
pub enum AxisEndPoint<LengthPercentage> {
ToPosition(#[compute(field_bound)] AxisPosition<LengthPercentage>),
ByCoordinate(LengthPercentage),
}
impl<LengthPercentage> AxisEndPoint<LengthPercentage> {
/// Return true if it is absolute, i.e. it is To.
#[inline]
pub fn is_abs(&self) -> bool {
matches!(self, AxisEndPoint::ToPosition(_))
}
}
impl<LengthPercentage: ToCss> ToCss for AxisEndPoint<LengthPercentage> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
if self.is_abs() {
dest.write_str("to ")?;
} else {
dest.write_str("by ")?;
}
match self {
AxisEndPoint::ToPosition(pos) => pos.to_css(dest),
AxisEndPoint::ByCoordinate(coord) => coord.to_css(dest),
}
}
}
/// Defines how the absolutely positioned end point for <horizontal-line-command> and
/// <vertical-line-command> is positioned.
#[allow(missing_docs)]
#[derive(
Animate,
Clone,
ComputeSquaredDistance,
Copy,
Debug,
Deserialize,
MallocSizeOf,
Parse,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToAnimatedZero,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(u8)]
pub enum AxisPosition<LengthPercentage> {
LengthPercent(LengthPercentage),
Keyword(AxisPositionKeyword),
}
/// The set of position keywords used in <horizontal-line-command> and <vertical-line-command>
/// for absolute positioning. Note: this is the shared union list between hline and vline, so
/// not every value is valid for either. I.e. hline cannot be positioned with top or y-start.
#[allow(missing_docs)]
#[derive(
Animate,
Clone,
ComputeSquaredDistance,
Copy,
Debug,
Deserialize,
MallocSizeOf,
Parse,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToAnimatedZero,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(u8)]
pub enum AxisPositionKeyword {
Center,
Left,
Right,
Top,
Bottom,
XStart,
XEnd,
YStart,
YEnd,
}
impl AxisPositionKeyword {
/// Returns the axis position keyword as its corresponding percentage.
#[inline]
pub fn as_percentage(&self) -> Percentage {
match self {
Self::Center => Percentage(0.5),
Self::Left | Self::Top | Self::XStart | Self::YStart => Percentage(0.),
Self::Right | Self::Bottom | Self::XEnd | Self::YEnd => Percentage(1.),
}
}
}
/// Defines a pair of coordinates, representing a rightward and downward offset, respectively, from
/// a specified reference point. Percentages are resolved against the width or height,
/// respectively, of the reference box.
#[allow(missing_docs)]
#[derive(
AddAssign,
Animate,
Clone,
ComputeSquaredDistance,
Copy,
Debug,
Deserialize,
MallocSizeOf,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToAnimatedZero,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(C)]
pub struct CoordinatePair<LengthPercentage> {
pub x: LengthPercentage,
pub y: LengthPercentage,
}
impl<LengthPercentage> CoordinatePair<LengthPercentage> {
/// Create a CoordinatePair.
#[inline]
pub fn new(x: LengthPercentage, y: LengthPercentage) -> Self {
Self { x, y }
}
}
/// Defines a control point for a quadratic or cubic Bézier curve, which can be specified
/// in absolute or relative coordinates.
#[allow(missing_docs)]
#[derive(
Animate,
Clone,
Copy,
ComputeSquaredDistance,
Debug,
Deserialize,
MallocSizeOf,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToAnimatedZero,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
#[repr(C, u8)]
pub enum ControlPoint<Position, LengthPercentage> {
Absolute(Position),
Relative(RelativeControlPoint<LengthPercentage>),
}
impl<Position, LengthPercentage> ControlPoint<Position, LengthPercentage> {
/// Serialize <control-point>
pub fn to_css<W>(&self, dest: &mut CssWriter<W>, is_end_point_abs: bool) -> fmt::Result
where
W: Write,
Position: ToCss,
LengthPercentage: ToCss,
{
match self {
ControlPoint::Absolute(pos) => pos.to_css(dest),
ControlPoint::Relative(point) => point.to_css(dest, is_end_point_abs),
}
}
}
/// Defines a relative control point to a quadratic or cubic Bézier curve, dependent on the
/// reference value. The default `None` is to be relative to the command’s starting point.
#[allow(missing_docs)]
#[derive(
Animate,
Clone,
Copy,
Debug,
Deserialize,
MallocSizeOf,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToAnimatedZero,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
#[repr(C)]
pub struct RelativeControlPoint<LengthPercentage> {
pub coord: CoordinatePair<LengthPercentage>,
pub reference: ControlReference,
}
impl<LengthPercentage: ToCss> RelativeControlPoint<LengthPercentage> {
fn to_css<W>(&self, dest: &mut CssWriter<W>, is_end_point_abs: bool) -> fmt::Result
where
W: Write,
{
self.coord.to_css(dest)?;
match self.reference {
ControlReference::Origin if is_end_point_abs => Ok(()),
ControlReference::Start if !is_end_point_abs => Ok(()),
other => {
dest.write_str(" from ")?;
other.to_css(dest)
},
}
}
}
impl<LengthPercentage: ComputeSquaredDistance> ComputeSquaredDistance
for RelativeControlPoint<LengthPercentage>
{
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
self.coord.compute_squared_distance(&other.coord)
}
}
/// Defines the point of reference for a <relative-control-point>.
///
/// When a reference is not specified, depending on whether the associated
/// <command-end-point> is absolutely or relatively positioned, the default
/// will be `Origin` or `Start`, respectively.
#[allow(missing_docs)]
#[derive(
Animate,
Clone,
Copy,
Debug,
Deserialize,
Eq,
MallocSizeOf,
PartialEq,
Parse,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToAnimatedZero,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(C)]
pub enum ControlReference {
Start,
End,
Origin,
}
/// Defines the radiuses for an <arc-command>.
///
/// The first <length-percentage> is the ellipse's horizontal radius, and the second is
/// the vertical radius. If only one value is provided, it is used for both radii, and any
/// <percentage> is resolved against the direction-agnostic size of the reference box.
#[allow(missing_docs)]
#[derive(
Animate,
Clone,
ComputeSquaredDistance,
Copy,
Debug,
Deserialize,
MallocSizeOf,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToAnimatedZero,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(C)]
pub struct ArcRadii<LengthPercentage> {
pub rx: LengthPercentage,
pub ry: Optional<LengthPercentage>,
}
/// This indicates that the arc that is traced around the ellipse clockwise or counter-clockwise
/// from the center.
#[derive(
Clone,
Copy,
Debug,
Deserialize,
FromPrimitive,
MallocSizeOf,
Parse,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToAnimatedZero,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(u8)]
pub enum ArcSweep {
/// Counter-clockwise. The default value. (This also represents 0 in the svg path.)
Ccw = 0,
/// Clockwise. (This also represents 1 in the svg path.)
Cw = 1,
}
impl Animate for ArcSweep {
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
use num_traits::FromPrimitive;
// If an arc command has different <arc-sweep> between its starting and ending list, then
// the interpolated result uses cw for any progress value between 0 and 1.
// Note: we cast progress from f64->f32->f64 to drop tiny noise near 0.0.
let progress = procedure.weights().1 as f32 as f64;
let procedure = Procedure::Interpolate { progress };
(*self as i32 as f32)
.animate(&(*other as i32 as f32), procedure)
.map(|v| ArcSweep::from_u8((v > 0.) as u8).unwrap_or(ArcSweep::Ccw))
}
}
impl ComputeSquaredDistance for ArcSweep {
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
(*self as i32).compute_squared_distance(&(*other as i32))
}
}
/// This indicates that the larger or smaller, respectively, of the two possible arcs must be
/// chosen.
#[derive(
Clone,
Copy,
Debug,
Deserialize,
FromPrimitive,
MallocSizeOf,
Parse,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToAnimatedZero,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(u8)]
pub enum ArcSize {
/// Choose the small one. The default value. (This also represents 0 in the svg path.)
Small = 0,
/// Choose the large one. (This also represents 1 in the svg path.)
Large = 1,
}
impl Animate for ArcSize {
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
use num_traits::FromPrimitive;
// If it has different <arc-size> keywords, then the interpolated result uses large for any
// progress value between 0 and 1.
// Note: we cast progress from f64->f32->f64 to drop tiny noise near 0.0.
let progress = procedure.weights().1 as f32 as f64;
let procedure = Procedure::Interpolate { progress };
(*self as i32 as f32)
.animate(&(*other as i32 as f32), procedure)
.map(|v| ArcSize::from_u8((v > 0.) as u8).unwrap_or(ArcSize::Small))
}
}
impl ComputeSquaredDistance for ArcSize {
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
(*self as i32).compute_squared_distance(&(*other as i32))
}
}