use crate::values::animated::{lists, Animate, Procedure, ToAnimatedZero};
use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
use crate::values::generics::border::GenericBorderRadius;
use crate::values::generics::position::GenericPositionOrAuto;
use crate::values::generics::rect::Rect;
use crate::values::specified::svg_path::{PathCommand, 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 {
#[css(skip)]
ElementDependent,
FillBox,
StrokeBox,
ViewBox,
ShapeBox(ShapeBox),
}
impl Default for ShapeGeometryBox {
fn default() -> Self {
Self::ElementDependent
}
}
#[inline]
fn is_default_box_for_clip_path(b: &ShapeGeometryBox) -> bool {
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,
)]
#[repr(u8)]
pub enum ShapeBox {
MarginBox,
BorderBox,
PaddingBox,
ContentBox,
}
impl Default for ShapeBox {
fn default() -> Self {
ShapeBox::MarginBox
}
}
#[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<BasicShape, U> {
#[animation(error)]
None,
#[animation(error)]
Url(U),
Shape(
Box<BasicShape>,
#[css(skip_if = "is_default_box_for_clip_path")] ShapeGeometryBox,
),
#[animation(error)]
Box(ShapeGeometryBox),
}
pub use self::GenericClipPath as ClipPath;
#[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<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;
#[derive(
Animate,
Clone,
ComputeSquaredDistance,
Debug,
Deserialize,
MallocSizeOf,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(C, u8)]
pub enum GenericBasicShape<
Angle,
Position,
LengthPercentage,
NonNegativeLengthPercentage,
BasicShapeRect,
> {
Rect(BasicShapeRect),
Circle(
#[css(field_bound)]
#[shmem(field_bound)]
Circle<Position, NonNegativeLengthPercentage>,
),
Ellipse(
#[css(field_bound)]
#[shmem(field_bound)]
Ellipse<Position, NonNegativeLengthPercentage>,
),
Polygon(GenericPolygon<LengthPercentage>),
PathOrShape(
#[animation(field_bound)]
#[css(field_bound)]
GenericPathOrShapeFunction<Angle, 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, NonNegativeLengthPercentage> {
pub rect: Rect<LengthPercentage>,
#[shmem(field_bound)]
pub round: GenericBorderRadius<NonNegativeLengthPercentage>,
}
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<Position, NonNegativeLengthPercentage> {
pub position: GenericPositionOrAuto<Position>,
pub radius: GenericShapeRadius<NonNegativeLengthPercentage>,
}
#[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<Position, NonNegativeLengthPercentage> {
pub position: GenericPositionOrAuto<Position>,
pub semiaxis_x: GenericShapeRadius<NonNegativeLengthPercentage>,
pub semiaxis_y: GenericShapeRadius<NonNegativeLengthPercentage>,
}
#[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<NonNegativeLengthPercentage> {
Length(NonNegativeLengthPercentage),
#[animation(error)]
ClosestSide,
#[animation(error)]
FarthestSide,
}
pub use self::GenericShapeRadius as ShapeRadius;
#[derive(
Clone,
Debug,
Deserialize,
MallocSizeOf,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[css(comma, function = "polygon")]
#[repr(C)]
pub struct GenericPolygon<LengthPercentage> {
#[css(skip_if = "is_default")]
pub fill: FillRule,
#[css(iterable)]
pub coordinates: crate::OwnedSlice<PolygonCoord<LengthPercentage>>,
}
pub use self::GenericPolygon as 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);
#[derive(
Clone,
ComputeSquaredDistance,
Debug,
Deserialize,
MallocSizeOf,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(C, u8)]
pub enum GenericPathOrShapeFunction<Angle, LengthPercentage> {
Path(Path),
Shape(#[css(field_bound)] Shape<Angle, LengthPercentage>),
}
#[allow(missing_docs)]
#[derive(
Animate,
Clone,
ComputeSquaredDistance,
Copy,
Debug,
Deserialize,
Eq,
MallocSizeOf,
Parse,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(u8)]
pub enum FillRule {
Nonzero,
Evenodd,
}
#[derive(
Animate,
Clone,
ComputeSquaredDistance,
Debug,
Deserialize,
MallocSizeOf,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[css(comma, function = "path")]
#[repr(C)]
pub struct Path {
#[css(skip_if = "is_default")]
pub fill: FillRule,
pub path: SVGPathData,
}
impl Path {
#[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, NonNegativeLength> ToCss for InsetRect<Length, NonNegativeLength>
where
Length: ToCss + PartialEq,
NonNegativeLength: 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<Position, NonNegativeLengthPercentage> ToCss for Circle<Position, NonNegativeLengthPercentage>
where
Position: ToCss,
NonNegativeLengthPercentage: ToCss + PartialEq,
{
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)?;
}
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<Position, NonNegativeLengthPercentage> ToCss for Ellipse<Position, NonNegativeLengthPercentage>
where
Position: ToCss,
NonNegativeLengthPercentage: ToCss + PartialEq,
{
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)?;
}
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()
}
#[derive(
Clone,
Debug,
Deserialize,
MallocSizeOf,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
#[repr(C)]
pub struct Shape<Angle, LengthPercentage> {
pub fill: FillRule,
pub commands: crate::OwnedSlice<GenericShapeCommand<Angle, LengthPercentage>>,
}
impl<Angle, LengthPercentage> Shape<Angle, LengthPercentage> {
#[inline]
pub fn commands(&self) -> &[GenericShapeCommand<Angle, LengthPercentage>] {
&self.commands
}
}
impl<Angle, LengthPercentage> Animate for Shape<Angle, LengthPercentage>
where
Angle: 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, LengthPercentage> ComputeSquaredDistance for Shape<Angle, LengthPercentage>
where
Angle: 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, LengthPercentage> ToCss for Shape<Angle, LengthPercentage>
where
Angle: ToCss + Zero,
LengthPercentage: PartialEq + ToCss,
{
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
use style_traits::values::SequenceWriter;
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 {
by_to: _,
ref point,
} => point.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(')')
}
}
#[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, LengthPercentage> {
Move {
by_to: ByTo,
point: CoordinatePair<LengthPercentage>,
},
Line {
by_to: ByTo,
point: CoordinatePair<LengthPercentage>,
},
HLine { by_to: ByTo, x: LengthPercentage },
VLine { by_to: ByTo, y: LengthPercentage },
CubicCurve {
by_to: ByTo,
point: CoordinatePair<LengthPercentage>,
control1: CoordinatePair<LengthPercentage>,
control2: CoordinatePair<LengthPercentage>,
},
QuadCurve {
by_to: ByTo,
point: CoordinatePair<LengthPercentage>,
control1: CoordinatePair<LengthPercentage>,
},
SmoothCubic {
by_to: ByTo,
point: CoordinatePair<LengthPercentage>,
control2: CoordinatePair<LengthPercentage>,
},
SmoothQuad {
by_to: ByTo,
point: CoordinatePair<LengthPercentage>,
},
Arc {
by_to: ByTo,
point: CoordinatePair<LengthPercentage>,
radii: CoordinatePair<LengthPercentage>,
arc_sweep: ArcSweep,
arc_size: ArcSize,
rotate: Angle,
},
Close,
}
pub use self::GenericShapeCommand as ShapeCommand;
impl<Angle, LengthPercentage> ToCss for ShapeCommand<Angle, LengthPercentage>
where
Angle: ToCss + Zero,
LengthPercentage: PartialEq + ToCss,
{
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
use self::ShapeCommand::*;
match *self {
Move { by_to, ref point } => {
dest.write_str("move ")?;
by_to.to_css(dest)?;
dest.write_char(' ')?;
point.to_css(dest)
},
Line { by_to, ref point } => {
dest.write_str("line ")?;
by_to.to_css(dest)?;
dest.write_char(' ')?;
point.to_css(dest)
},
HLine { by_to, ref x } => {
dest.write_str("hline ")?;
by_to.to_css(dest)?;
dest.write_char(' ')?;
x.to_css(dest)
},
VLine { by_to, ref y } => {
dest.write_str("vline ")?;
by_to.to_css(dest)?;
dest.write_char(' ')?;
y.to_css(dest)
},
CubicCurve {
by_to,
ref point,
ref control1,
ref control2,
} => {
dest.write_str("curve ")?;
by_to.to_css(dest)?;
dest.write_char(' ')?;
point.to_css(dest)?;
dest.write_str(" via ")?;
control1.to_css(dest)?;
dest.write_char(' ')?;
control2.to_css(dest)
},
QuadCurve {
by_to,
ref point,
ref control1,
} => {
dest.write_str("curve ")?;
by_to.to_css(dest)?;
dest.write_char(' ')?;
point.to_css(dest)?;
dest.write_str(" via ")?;
control1.to_css(dest)
},
SmoothCubic {
by_to,
ref point,
ref control2,
} => {
dest.write_str("smooth ")?;
by_to.to_css(dest)?;
dest.write_char(' ')?;
point.to_css(dest)?;
dest.write_str(" via ")?;
control2.to_css(dest)
},
SmoothQuad { by_to, ref point } => {
dest.write_str("smooth ")?;
by_to.to_css(dest)?;
dest.write_char(' ')?;
point.to_css(dest)
},
Arc {
by_to,
ref point,
ref radii,
arc_sweep,
arc_size,
ref rotate,
} => {
dest.write_str("arc ")?;
by_to.to_css(dest)?;
dest.write_char(' ')?;
point.to_css(dest)?;
dest.write_str(" of ")?;
radii.x.to_css(dest)?;
if radii.x != radii.y {
dest.write_char(' ')?;
radii.y.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"),
}
}
}
#[derive(
Animate,
Clone,
ComputeSquaredDistance,
Copy,
Debug,
Deserialize,
MallocSizeOf,
Parse,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToAnimatedZero,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(u8)]
pub enum ByTo {
By,
To,
}
impl ByTo {
#[inline]
pub fn is_abs(&self) -> bool {
matches!(self, ByTo::To)
}
#[inline]
pub fn new(is_abs: bool) -> Self {
if is_abs {
Self::To
} else {
Self::By
}
}
}
#[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> {
#[inline]
pub fn new(x: LengthPercentage, y: LengthPercentage) -> Self {
Self { x, y }
}
}
#[derive(
Clone,
Copy,
Debug,
Deserialize,
FromPrimitive,
MallocSizeOf,
Parse,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToAnimatedZero,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(u8)]
pub enum ArcSweep {
Ccw = 0,
Cw = 1,
}
impl Animate for ArcSweep {
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
use num_traits::FromPrimitive;
(*self as i32)
.animate(&(*other as i32), 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))
}
}
#[derive(
Clone,
Copy,
Debug,
Deserialize,
FromPrimitive,
MallocSizeOf,
Parse,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedValue,
ToAnimatedZero,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(u8)]
pub enum ArcSize {
Small = 0,
Large = 1,
}
impl Animate for ArcSize {
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
use num_traits::FromPrimitive;
(*self as i32)
.animate(&(*other as i32), 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))
}
}