Skip to main content

style/values/generics/
basic_shape.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! CSS handling for the [`basic-shape`](https://drafts.csswg.org/css-shapes/#typedef-basic-shape)
6//! types that are generic over their `ToCss` implementations.
7
8use crate::derives::*;
9use crate::values::animated::{lists, Animate, Procedure, ToAnimatedZero};
10use crate::values::computed::Percentage;
11use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
12use crate::values::generics::{
13    border::GenericBorderRadius, position::GenericPositionOrAuto, rect::Rect, NonNegative, Optional,
14};
15use crate::values::specified::svg_path::{PathCommand, SVGPathData};
16use crate::Zero;
17use std::fmt::{self, Write};
18use style_traits::{CssWriter, ToCss};
19
20/// <https://drafts.fxtf.org/css-masking-1/#typedef-geometry-box>
21#[allow(missing_docs)]
22#[derive(
23    Animate,
24    Clone,
25    ComputeSquaredDistance,
26    Copy,
27    Debug,
28    MallocSizeOf,
29    PartialEq,
30    Parse,
31    SpecifiedValueInfo,
32    ToAnimatedValue,
33    ToComputedValue,
34    ToCss,
35    ToResolvedValue,
36    ToShmem,
37    ToTyped,
38)]
39#[repr(u8)]
40pub enum ShapeGeometryBox {
41    /// Depending on which kind of element this style value applied on, the
42    /// default value of the reference-box can be different.  For an HTML
43    /// element, the default value of reference-box is border-box; for an SVG
44    /// element, the default value is fill-box.  Since we can not determine the
45    /// default value at parsing time, we keep this value to make a decision on
46    /// it.
47    #[css(skip)]
48    ElementDependent,
49    FillBox,
50    StrokeBox,
51    ViewBox,
52    ShapeBox(ShapeBox),
53}
54
55impl Default for ShapeGeometryBox {
56    fn default() -> Self {
57        Self::ElementDependent
58    }
59}
60
61/// Skip the serialization if the author omits the box or specifies border-box.
62#[inline]
63fn is_default_box_for_clip_path(b: &ShapeGeometryBox) -> bool {
64    // Note: for clip-path, ElementDependent is always border-box, so we have to check both of them
65    // for serialization.
66    matches!(b, ShapeGeometryBox::ElementDependent)
67        || matches!(b, ShapeGeometryBox::ShapeBox(ShapeBox::BorderBox))
68}
69
70/// https://drafts.csswg.org/css-shapes-1/#typedef-shape-box
71#[allow(missing_docs)]
72#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
73#[derive(
74    Animate,
75    Clone,
76    Copy,
77    ComputeSquaredDistance,
78    Debug,
79    Eq,
80    MallocSizeOf,
81    Parse,
82    PartialEq,
83    SpecifiedValueInfo,
84    ToAnimatedValue,
85    ToComputedValue,
86    ToCss,
87    ToResolvedValue,
88    ToShmem,
89    ToTyped,
90)]
91#[repr(u8)]
92pub enum ShapeBox {
93    MarginBox,
94    BorderBox,
95    PaddingBox,
96    ContentBox,
97}
98
99impl Default for ShapeBox {
100    fn default() -> Self {
101        ShapeBox::MarginBox
102    }
103}
104
105/// A value for the `clip-path` property.
106#[allow(missing_docs)]
107#[derive(
108    Animate,
109    Clone,
110    ComputeSquaredDistance,
111    Debug,
112    MallocSizeOf,
113    PartialEq,
114    SpecifiedValueInfo,
115    ToAnimatedValue,
116    ToComputedValue,
117    ToCss,
118    ToResolvedValue,
119    ToShmem,
120    ToTyped,
121)]
122#[animation(no_bound(U))]
123#[repr(u8)]
124pub enum GenericClipPath<BasicShape, U> {
125    #[animation(error)]
126    None,
127    #[animation(error)]
128    // XXX This will likely change to skip since it seems Typed OM Level 1
129    // won't be updated to cover this case even though there's some preparation
130    // in WPT tests for this.
131    #[typed(todo)]
132    Url(U),
133    #[typed(skip)]
134    Shape(
135        #[animation(field_bound)] Box<BasicShape>,
136        #[css(skip_if = "is_default_box_for_clip_path")] ShapeGeometryBox,
137    ),
138    #[animation(error)]
139    Box(ShapeGeometryBox),
140}
141
142pub use self::GenericClipPath as ClipPath;
143
144/// A value for the `shape-outside` property.
145#[allow(missing_docs)]
146#[derive(
147    Animate,
148    Clone,
149    ComputeSquaredDistance,
150    Debug,
151    MallocSizeOf,
152    PartialEq,
153    SpecifiedValueInfo,
154    ToAnimatedValue,
155    ToComputedValue,
156    ToCss,
157    ToResolvedValue,
158    ToShmem,
159    ToTyped,
160)]
161#[animation(no_bound(I))]
162#[repr(u8)]
163#[typed(todo_derive_fields)]
164pub enum GenericShapeOutside<BasicShape, I> {
165    #[animation(error)]
166    None,
167    #[animation(error)]
168    Image(I),
169    Shape(Box<BasicShape>, #[css(skip_if = "is_default")] ShapeBox),
170    #[animation(error)]
171    Box(ShapeBox),
172}
173
174pub use self::GenericShapeOutside as ShapeOutside;
175
176/// The <basic-shape>.
177///
178/// https://drafts.csswg.org/css-shapes-1/#supported-basic-shapes
179#[derive(
180    Animate,
181    Clone,
182    ComputeSquaredDistance,
183    Debug,
184    Deserialize,
185    MallocSizeOf,
186    PartialEq,
187    Serialize,
188    SpecifiedValueInfo,
189    ToAnimatedValue,
190    ToComputedValue,
191    ToCss,
192    ToResolvedValue,
193    ToShmem,
194)]
195#[repr(C, u8)]
196pub enum GenericBasicShape<Angle, Position, LengthPercentage, BasicShapeRect> {
197    /// The <basic-shape-rect>.
198    Rect(BasicShapeRect),
199    /// Defines a circle with a center and a radius.
200    Circle(
201        #[animation(field_bound)]
202        #[css(field_bound)]
203        #[shmem(field_bound)]
204        Circle<Position, LengthPercentage>,
205    ),
206    /// Defines an ellipse with a center and x-axis/y-axis radii.
207    Ellipse(
208        #[animation(field_bound)]
209        #[css(field_bound)]
210        #[shmem(field_bound)]
211        Ellipse<Position, LengthPercentage>,
212    ),
213    /// Defines a polygon with pair arguments.
214    Polygon(GenericPolygon<LengthPercentage>),
215    /// Defines a path() or shape().
216    PathOrShape(
217        #[animation(field_bound)]
218        #[css(field_bound)]
219        #[compute(field_bound)]
220        GenericPathOrShapeFunction<Angle, Position, LengthPercentage>,
221    ),
222}
223
224pub use self::GenericBasicShape as BasicShape;
225
226/// <https://drafts.csswg.org/css-shapes/#funcdef-inset>
227#[allow(missing_docs)]
228#[derive(
229    Animate,
230    Clone,
231    ComputeSquaredDistance,
232    Debug,
233    Deserialize,
234    MallocSizeOf,
235    PartialEq,
236    Serialize,
237    SpecifiedValueInfo,
238    ToAnimatedValue,
239    ToComputedValue,
240    ToResolvedValue,
241    ToShmem,
242)]
243#[css(function = "inset")]
244#[repr(C)]
245pub struct GenericInsetRect<LengthPercentage> {
246    pub rect: Rect<LengthPercentage>,
247    #[shmem(field_bound)]
248    #[animation(field_bound)]
249    pub round: GenericBorderRadius<NonNegative<LengthPercentage>>,
250}
251
252pub use self::GenericInsetRect as InsetRect;
253
254/// <https://drafts.csswg.org/css-shapes/#funcdef-circle>
255#[allow(missing_docs)]
256#[derive(
257    Animate,
258    Clone,
259    ComputeSquaredDistance,
260    Copy,
261    Debug,
262    Deserialize,
263    MallocSizeOf,
264    PartialEq,
265    Serialize,
266    SpecifiedValueInfo,
267    ToAnimatedValue,
268    ToComputedValue,
269    ToResolvedValue,
270    ToShmem,
271)]
272#[css(function)]
273#[repr(C)]
274pub struct Circle<Position, LengthPercentage> {
275    pub position: GenericPositionOrAuto<Position>,
276    #[animation(field_bound)]
277    pub radius: GenericShapeRadius<LengthPercentage>,
278}
279
280/// <https://drafts.csswg.org/css-shapes/#funcdef-ellipse>
281#[allow(missing_docs)]
282#[derive(
283    Animate,
284    Clone,
285    ComputeSquaredDistance,
286    Copy,
287    Debug,
288    Deserialize,
289    MallocSizeOf,
290    PartialEq,
291    Serialize,
292    SpecifiedValueInfo,
293    ToAnimatedValue,
294    ToComputedValue,
295    ToResolvedValue,
296    ToShmem,
297)]
298#[css(function)]
299#[repr(C)]
300pub struct Ellipse<Position, LengthPercentage> {
301    pub position: GenericPositionOrAuto<Position>,
302    #[animation(field_bound)]
303    pub semiaxis_x: GenericShapeRadius<LengthPercentage>,
304    #[animation(field_bound)]
305    pub semiaxis_y: GenericShapeRadius<LengthPercentage>,
306}
307
308/// <https://drafts.csswg.org/css-shapes/#typedef-shape-radius>
309#[allow(missing_docs)]
310#[derive(
311    Animate,
312    Clone,
313    ComputeSquaredDistance,
314    Copy,
315    Debug,
316    Deserialize,
317    MallocSizeOf,
318    Parse,
319    PartialEq,
320    Serialize,
321    SpecifiedValueInfo,
322    ToAnimatedValue,
323    ToComputedValue,
324    ToCss,
325    ToResolvedValue,
326    ToShmem,
327)]
328#[repr(C, u8)]
329pub enum GenericShapeRadius<LengthPercentage> {
330    Length(
331        #[animation(field_bound)]
332        #[parse(field_bound)]
333        NonNegative<LengthPercentage>,
334    ),
335    #[animation(error)]
336    ClosestSide,
337    #[animation(error)]
338    FarthestSide,
339}
340
341pub use self::GenericShapeRadius as ShapeRadius;
342
343/// A generic type for representing the `polygon()` function
344///
345/// <https://drafts.csswg.org/css-shapes/#funcdef-polygon>
346#[derive(
347    Clone,
348    Debug,
349    Deserialize,
350    MallocSizeOf,
351    PartialEq,
352    Serialize,
353    SpecifiedValueInfo,
354    ToAnimatedValue,
355    ToComputedValue,
356    ToCss,
357    ToResolvedValue,
358    ToShmem,
359)]
360#[css(comma, function = "polygon")]
361#[repr(C)]
362pub struct GenericPolygon<LengthPercentage> {
363    /// The filling rule for a polygon.
364    #[css(skip_if = "is_default")]
365    pub fill: FillRule,
366    /// A collection of (x, y) coordinates to draw the polygon.
367    #[css(iterable)]
368    pub coordinates: crate::OwnedSlice<PolygonCoord<LengthPercentage>>,
369}
370
371pub use self::GenericPolygon as Polygon;
372
373/// Coordinates for Polygon.
374#[derive(
375    Animate,
376    Clone,
377    ComputeSquaredDistance,
378    Debug,
379    Deserialize,
380    MallocSizeOf,
381    PartialEq,
382    Serialize,
383    SpecifiedValueInfo,
384    ToAnimatedValue,
385    ToComputedValue,
386    ToCss,
387    ToResolvedValue,
388    ToShmem,
389)]
390#[repr(C)]
391pub struct PolygonCoord<LengthPercentage>(pub LengthPercentage, pub LengthPercentage);
392
393/// path() function or shape() function.
394#[derive(
395    Clone,
396    ComputeSquaredDistance,
397    Debug,
398    Deserialize,
399    MallocSizeOf,
400    PartialEq,
401    Serialize,
402    SpecifiedValueInfo,
403    ToAnimatedValue,
404    ToComputedValue,
405    ToCss,
406    ToResolvedValue,
407    ToShmem,
408)]
409#[repr(C, u8)]
410pub enum GenericPathOrShapeFunction<Angle, Position, LengthPercentage> {
411    /// Defines a path with SVG path syntax.
412    Path(Path),
413    /// Defines a shape function, which is identical to path() but it uses the CSS syntax.
414    Shape(
415        #[css(field_bound)]
416        #[compute(field_bound)]
417        Shape<Angle, Position, LengthPercentage>,
418    ),
419}
420
421// https://drafts.csswg.org/css-shapes/#typedef-fill-rule
422// NOTE: Basic shapes spec says that these are the only two values, however
423// https://www.w3.org/TR/SVG/painting.html#FillRuleProperty
424// says that it can also be `inherit`
425#[allow(missing_docs)]
426#[derive(
427    Animate,
428    Clone,
429    ComputeSquaredDistance,
430    Copy,
431    Debug,
432    Deserialize,
433    Eq,
434    MallocSizeOf,
435    Parse,
436    PartialEq,
437    Serialize,
438    SpecifiedValueInfo,
439    ToAnimatedValue,
440    ToComputedValue,
441    ToCss,
442    ToResolvedValue,
443    ToShmem,
444    ToTyped,
445)]
446#[repr(u8)]
447pub enum FillRule {
448    Nonzero,
449    Evenodd,
450}
451
452/// The path function.
453///
454/// https://drafts.csswg.org/css-shapes-1/#funcdef-basic-shape-path
455#[derive(
456    Animate,
457    Clone,
458    ComputeSquaredDistance,
459    Debug,
460    Deserialize,
461    MallocSizeOf,
462    PartialEq,
463    Serialize,
464    SpecifiedValueInfo,
465    ToAnimatedValue,
466    ToComputedValue,
467    ToCss,
468    ToResolvedValue,
469    ToShmem,
470)]
471#[css(comma, function = "path")]
472#[repr(C)]
473pub struct Path {
474    /// The filling rule for the svg path.
475    #[css(skip_if = "is_default")]
476    pub fill: FillRule,
477    /// The svg path data.
478    pub path: SVGPathData,
479}
480
481impl Path {
482    /// Returns the slice of PathCommand.
483    #[inline]
484    pub fn commands(&self) -> &[PathCommand] {
485        self.path.commands()
486    }
487}
488
489impl<B, U> ToAnimatedZero for ClipPath<B, U> {
490    fn to_animated_zero(&self) -> Result<Self, ()> {
491        Err(())
492    }
493}
494
495impl<B, U> ToAnimatedZero for ShapeOutside<B, U> {
496    fn to_animated_zero(&self) -> Result<Self, ()> {
497        Err(())
498    }
499}
500
501impl<Length> ToCss for InsetRect<Length>
502where
503    Length: ToCss + PartialEq + Zero,
504{
505    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
506    where
507        W: Write,
508    {
509        dest.write_str("inset(")?;
510        self.rect.to_css(dest)?;
511        if !self.round.is_zero() {
512            dest.write_str(" round ")?;
513            self.round.to_css(dest)?;
514        }
515        dest.write_char(')')
516    }
517}
518
519impl<Position, LengthPercentage> ToCss for Circle<Position, LengthPercentage>
520where
521    LengthPercentage: ToCss + PartialEq,
522    Position: ToCss,
523{
524    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
525    where
526        W: Write,
527    {
528        let has_radius = self.radius != Default::default();
529
530        dest.write_str("circle(")?;
531        if has_radius {
532            self.radius.to_css(dest)?;
533        }
534
535        // Preserve the `at <position>` even if it specified the default value.
536        // https://github.com/w3c/csswg-drafts/issues/8695
537        if !matches!(self.position, GenericPositionOrAuto::Auto) {
538            if has_radius {
539                dest.write_char(' ')?;
540            }
541            dest.write_str("at ")?;
542            self.position.to_css(dest)?;
543        }
544        dest.write_char(')')
545    }
546}
547
548impl<Position, LengthPercentage> ToCss for Ellipse<Position, LengthPercentage>
549where
550    LengthPercentage: ToCss + PartialEq,
551    Position: ToCss,
552{
553    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
554    where
555        W: Write,
556    {
557        let has_radii =
558            self.semiaxis_x != Default::default() || self.semiaxis_y != Default::default();
559
560        dest.write_str("ellipse(")?;
561        if has_radii {
562            self.semiaxis_x.to_css(dest)?;
563            dest.write_char(' ')?;
564            self.semiaxis_y.to_css(dest)?;
565        }
566
567        // Preserve the `at <position>` even if it specified the default value.
568        // https://github.com/w3c/csswg-drafts/issues/8695
569        if !matches!(self.position, GenericPositionOrAuto::Auto) {
570            if has_radii {
571                dest.write_char(' ')?;
572            }
573            dest.write_str("at ")?;
574            self.position.to_css(dest)?;
575        }
576        dest.write_char(')')
577    }
578}
579
580impl<L> Default for ShapeRadius<L> {
581    #[inline]
582    fn default() -> Self {
583        ShapeRadius::ClosestSide
584    }
585}
586
587impl<L> Animate for Polygon<L>
588where
589    L: Animate,
590{
591    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
592        if self.fill != other.fill {
593            return Err(());
594        }
595        let coordinates =
596            lists::by_computed_value::animate(&self.coordinates, &other.coordinates, procedure)?;
597        Ok(Polygon {
598            fill: self.fill,
599            coordinates,
600        })
601    }
602}
603
604impl<L> ComputeSquaredDistance for Polygon<L>
605where
606    L: ComputeSquaredDistance,
607{
608    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
609        if self.fill != other.fill {
610            return Err(());
611        }
612        lists::by_computed_value::squared_distance(&self.coordinates, &other.coordinates)
613    }
614}
615
616impl Default for FillRule {
617    #[inline]
618    fn default() -> Self {
619        FillRule::Nonzero
620    }
621}
622
623#[inline]
624fn is_default<T: Default + PartialEq>(fill: &T) -> bool {
625    *fill == Default::default()
626}
627
628/// The shape function defined in css-shape-2.
629/// shape() = shape(<fill-rule>? from <coordinate-pair>, <shape-command>#)
630///
631/// https://drafts.csswg.org/css-shapes-2/#shape-function
632#[derive(
633    Clone,
634    Debug,
635    Deserialize,
636    MallocSizeOf,
637    PartialEq,
638    Serialize,
639    SpecifiedValueInfo,
640    ToAnimatedValue,
641    ToComputedValue,
642    ToResolvedValue,
643    ToShmem,
644)]
645#[repr(C)]
646pub struct Shape<Angle, Position, LengthPercentage> {
647    /// The filling rule for this shape.
648    pub fill: FillRule,
649    /// The shape command data. Note that the starting point will be the first command in this
650    /// slice.
651    // Note: The first command is always GenericShapeCommand::Move.
652    #[compute(field_bound)]
653    pub commands: crate::OwnedSlice<GenericShapeCommand<Angle, Position, LengthPercentage>>,
654}
655
656impl<Angle, Position, LengthPercentage> Shape<Angle, Position, LengthPercentage> {
657    /// Returns the slice of GenericShapeCommand<..>.
658    #[inline]
659    pub fn commands(&self) -> &[GenericShapeCommand<Angle, Position, LengthPercentage>] {
660        &self.commands
661    }
662}
663
664impl<Angle, Position, LengthPercentage> Animate for Shape<Angle, Position, LengthPercentage>
665where
666    Angle: Animate,
667    Position: Animate,
668    LengthPercentage: Animate,
669{
670    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
671        if self.fill != other.fill {
672            return Err(());
673        }
674        let commands =
675            lists::by_computed_value::animate(&self.commands, &other.commands, procedure)?;
676        Ok(Self {
677            fill: self.fill,
678            commands,
679        })
680    }
681}
682
683impl<Angle, Position, LengthPercentage> ComputeSquaredDistance
684    for Shape<Angle, Position, LengthPercentage>
685where
686    Angle: ComputeSquaredDistance,
687    Position: ComputeSquaredDistance,
688    LengthPercentage: ComputeSquaredDistance,
689{
690    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
691        if self.fill != other.fill {
692            return Err(());
693        }
694        lists::by_computed_value::squared_distance(&self.commands, &other.commands)
695    }
696}
697
698impl<Angle, Position, LengthPercentage> ToCss for Shape<Angle, Position, LengthPercentage>
699where
700    Angle: ToCss + Zero,
701    Position: ToCss,
702    LengthPercentage: PartialEq + ToCss,
703{
704    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
705    where
706        W: Write,
707    {
708        use style_traits::values::SequenceWriter;
709
710        // Per spec, we must have the first move command and at least one following command.
711        debug_assert!(self.commands.len() > 1);
712
713        dest.write_str("shape(")?;
714        if !is_default(&self.fill) {
715            self.fill.to_css(dest)?;
716            dest.write_char(' ')?;
717        }
718        dest.write_str("from ")?;
719        match &self.commands[0] {
720            ShapeCommand::Move {
721                point: CommandEndPoint::ToPosition(pos),
722            } => pos.to_css(dest)?,
723            ShapeCommand::Move {
724                point: CommandEndPoint::ByCoordinate(coord),
725            } => coord.to_css(dest)?,
726            _ => unreachable!("The first command must be move"),
727        }
728        dest.write_str(", ")?;
729        {
730            let mut writer = SequenceWriter::new(dest, ", ");
731            for command in self.commands.iter().skip(1) {
732                writer.item(command)?;
733            }
734        }
735        dest.write_char(')')
736    }
737}
738
739/// This is a more general shape(path) command type, for both shape() and path().
740///
741/// https://www.w3.org/TR/SVG11/paths.html#PathData
742/// https://drafts.csswg.org/css-shapes-2/#shape-function
743#[derive(
744    Animate,
745    Clone,
746    ComputeSquaredDistance,
747    Copy,
748    Debug,
749    Deserialize,
750    MallocSizeOf,
751    PartialEq,
752    Serialize,
753    SpecifiedValueInfo,
754    ToAnimatedValue,
755    ToAnimatedZero,
756    ToComputedValue,
757    ToResolvedValue,
758    ToShmem,
759)]
760#[allow(missing_docs)]
761#[repr(C, u8)]
762pub enum GenericShapeCommand<Angle, Position, LengthPercentage> {
763    /// The move command.
764    Move {
765        point: CommandEndPoint<Position, LengthPercentage>,
766    },
767    /// The line command.
768    Line {
769        point: CommandEndPoint<Position, LengthPercentage>,
770    },
771    /// The hline command.
772    HLine {
773        #[compute(field_bound)]
774        x: AxisEndPoint<LengthPercentage>,
775    },
776    /// The vline command.
777    VLine {
778        #[compute(field_bound)]
779        y: AxisEndPoint<LengthPercentage>,
780    },
781    /// The cubic Bézier curve command.
782    CubicCurve {
783        point: CommandEndPoint<Position, LengthPercentage>,
784        control1: ControlPoint<Position, LengthPercentage>,
785        control2: ControlPoint<Position, LengthPercentage>,
786    },
787    /// The quadratic Bézier curve command.
788    QuadCurve {
789        point: CommandEndPoint<Position, LengthPercentage>,
790        control1: ControlPoint<Position, LengthPercentage>,
791    },
792    /// The smooth command.
793    SmoothCubic {
794        point: CommandEndPoint<Position, LengthPercentage>,
795        control2: ControlPoint<Position, LengthPercentage>,
796    },
797    /// The smooth quadratic Bézier curve command.
798    SmoothQuad {
799        point: CommandEndPoint<Position, LengthPercentage>,
800    },
801    /// The arc command.
802    Arc {
803        point: CommandEndPoint<Position, LengthPercentage>,
804        radii: ArcRadii<LengthPercentage>,
805        arc_sweep: ArcSweep,
806        arc_size: ArcSize,
807        rotate: Angle,
808    },
809    /// The closepath command.
810    Close,
811}
812
813pub use self::GenericShapeCommand as ShapeCommand;
814
815impl<Angle, Position, LengthPercentage> ToCss for ShapeCommand<Angle, Position, LengthPercentage>
816where
817    Angle: ToCss + Zero,
818    Position: ToCss,
819    LengthPercentage: PartialEq + ToCss,
820{
821    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
822    where
823        W: fmt::Write,
824    {
825        use self::ShapeCommand::*;
826        match *self {
827            Move { ref point } => {
828                dest.write_str("move ")?;
829                point.to_css(dest)
830            },
831            Line { ref point } => {
832                dest.write_str("line ")?;
833                point.to_css(dest)
834            },
835            HLine { ref x } => {
836                dest.write_str("hline ")?;
837                x.to_css(dest)
838            },
839            VLine { ref y } => {
840                dest.write_str("vline ")?;
841                y.to_css(dest)
842            },
843            CubicCurve {
844                ref point,
845                ref control1,
846                ref control2,
847            } => {
848                dest.write_str("curve ")?;
849                point.to_css(dest)?;
850                dest.write_str(" with ")?;
851                control1.to_css(dest, point.is_abs())?;
852                dest.write_char(' ')?;
853                dest.write_char('/')?;
854                dest.write_char(' ')?;
855                control2.to_css(dest, point.is_abs())
856            },
857            QuadCurve {
858                ref point,
859                ref control1,
860            } => {
861                dest.write_str("curve ")?;
862                point.to_css(dest)?;
863                dest.write_str(" with ")?;
864                control1.to_css(dest, point.is_abs())
865            },
866            SmoothCubic {
867                ref point,
868                ref control2,
869            } => {
870                dest.write_str("smooth ")?;
871                point.to_css(dest)?;
872                dest.write_str(" with ")?;
873                control2.to_css(dest, point.is_abs())
874            },
875            SmoothQuad { ref point } => {
876                dest.write_str("smooth ")?;
877                point.to_css(dest)
878            },
879            Arc {
880                ref point,
881                ref radii,
882                arc_sweep,
883                arc_size,
884                ref rotate,
885            } => {
886                dest.write_str("arc ")?;
887                point.to_css(dest)?;
888                dest.write_str(" of ")?;
889                radii.to_css(dest)?;
890
891                if matches!(arc_sweep, ArcSweep::Cw) {
892                    dest.write_str(" cw")?;
893                }
894
895                if matches!(arc_size, ArcSize::Large) {
896                    dest.write_str(" large")?;
897                }
898
899                if !rotate.is_zero() {
900                    dest.write_str(" rotate ")?;
901                    rotate.to_css(dest)?;
902                }
903                Ok(())
904            },
905            Close => dest.write_str("close"),
906        }
907    }
908}
909
910/// Defines the end point of the command, which can be specified in absolute or relative coordinates,
911/// determined by their "to" or "by" components respectively.
912/// https://drafts.csswg.org/css-shapes/#typedef-shape-command-end-point
913#[allow(missing_docs)]
914#[derive(
915    Animate,
916    Clone,
917    ComputeSquaredDistance,
918    Copy,
919    Debug,
920    Deserialize,
921    MallocSizeOf,
922    PartialEq,
923    Serialize,
924    SpecifiedValueInfo,
925    ToAnimatedValue,
926    ToAnimatedZero,
927    ToComputedValue,
928    ToResolvedValue,
929    ToShmem,
930)]
931#[repr(C, u8)]
932pub enum CommandEndPoint<Position, LengthPercentage> {
933    ToPosition(Position),
934    ByCoordinate(CoordinatePair<LengthPercentage>),
935}
936
937impl<Position, LengthPercentage> CommandEndPoint<Position, LengthPercentage> {
938    /// Return true if it is absolute, i.e. it is To.
939    #[inline]
940    pub fn is_abs(&self) -> bool {
941        matches!(self, CommandEndPoint::ToPosition(_))
942    }
943}
944
945impl<Position, LengthPercentage> CommandEndPoint<Position, LengthPercentage> {
946    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
947    where
948        W: Write,
949        Position: ToCss,
950        LengthPercentage: ToCss,
951    {
952        match self {
953            CommandEndPoint::ToPosition(pos) => {
954                dest.write_str("to ")?;
955                pos.to_css(dest)
956            },
957            CommandEndPoint::ByCoordinate(coord) => {
958                dest.write_str("by ")?;
959                coord.to_css(dest)
960            },
961        }
962    }
963}
964
965/// Defines the end point for the commands <horizontal-line-command> and <vertical-line-command>, which
966/// can be specified in absolute or relative values, determined by their "to" or "by" components respectively.
967/// https://drafts.csswg.org/css-shapes-1/#typedef-shape-horizontal-line-command
968/// https://drafts.csswg.org/css-shapes-1/#typedef-shape-vertical-line-command
969#[allow(missing_docs)]
970#[derive(
971    Animate,
972    Clone,
973    Copy,
974    ComputeSquaredDistance,
975    Debug,
976    Deserialize,
977    MallocSizeOf,
978    PartialEq,
979    Parse,
980    Serialize,
981    SpecifiedValueInfo,
982    ToAnimatedValue,
983    ToAnimatedZero,
984    ToComputedValue,
985    ToResolvedValue,
986    ToShmem,
987)]
988#[repr(u8)]
989pub enum AxisEndPoint<LengthPercentage> {
990    ToPosition(#[compute(field_bound)] AxisPosition<LengthPercentage>),
991    ByCoordinate(LengthPercentage),
992}
993
994impl<LengthPercentage> AxisEndPoint<LengthPercentage> {
995    /// Return true if it is absolute, i.e. it is To.
996    #[inline]
997    pub fn is_abs(&self) -> bool {
998        matches!(self, AxisEndPoint::ToPosition(_))
999    }
1000}
1001
1002impl<LengthPercentage: ToCss> ToCss for AxisEndPoint<LengthPercentage> {
1003    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1004    where
1005        W: Write,
1006    {
1007        if self.is_abs() {
1008            dest.write_str("to ")?;
1009        } else {
1010            dest.write_str("by ")?;
1011        }
1012        match self {
1013            AxisEndPoint::ToPosition(pos) => pos.to_css(dest),
1014            AxisEndPoint::ByCoordinate(coord) => coord.to_css(dest),
1015        }
1016    }
1017}
1018
1019/// Defines how the absolutely positioned end point for <horizontal-line-command> and
1020/// <vertical-line-command> is positioned.
1021/// https://drafts.csswg.org/css-shapes-1/#typedef-shape-horizontal-line-command
1022/// https://drafts.csswg.org/css-shapes-1/#typedef-shape-vertical-line-command
1023#[allow(missing_docs)]
1024#[derive(
1025    Animate,
1026    Clone,
1027    ComputeSquaredDistance,
1028    Copy,
1029    Debug,
1030    Deserialize,
1031    MallocSizeOf,
1032    Parse,
1033    PartialEq,
1034    Serialize,
1035    SpecifiedValueInfo,
1036    ToAnimatedValue,
1037    ToAnimatedZero,
1038    ToCss,
1039    ToResolvedValue,
1040    ToShmem,
1041)]
1042#[repr(u8)]
1043pub enum AxisPosition<LengthPercentage> {
1044    LengthPercent(LengthPercentage),
1045    Keyword(AxisPositionKeyword),
1046}
1047
1048/// The set of position keywords used in <horizontal-line-command> and <vertical-line-command>
1049/// for absolute positioning. Note: this is the shared union list between hline and vline, so
1050/// not every value is valid for either. I.e. hline cannot be positioned with top or y-start.
1051/// https://drafts.csswg.org/css-shapes-1/#typedef-shape-horizontal-line-command
1052/// https://drafts.csswg.org/css-shapes-1/#typedef-shape-vertical-line-command
1053#[allow(missing_docs)]
1054#[derive(
1055    Animate,
1056    Clone,
1057    ComputeSquaredDistance,
1058    Copy,
1059    Debug,
1060    Deserialize,
1061    MallocSizeOf,
1062    Parse,
1063    PartialEq,
1064    Serialize,
1065    SpecifiedValueInfo,
1066    ToAnimatedValue,
1067    ToAnimatedZero,
1068    ToCss,
1069    ToResolvedValue,
1070    ToShmem,
1071)]
1072#[repr(u8)]
1073pub enum AxisPositionKeyword {
1074    Center,
1075    Left,
1076    Right,
1077    Top,
1078    Bottom,
1079    XStart,
1080    XEnd,
1081    YStart,
1082    YEnd,
1083}
1084
1085impl AxisPositionKeyword {
1086    /// Returns the axis position keyword as its corresponding percentage.
1087    #[inline]
1088    pub fn as_percentage(&self) -> Percentage {
1089        match self {
1090            Self::Center => Percentage(0.5),
1091            Self::Left | Self::Top | Self::XStart | Self::YStart => Percentage(0.),
1092            Self::Right | Self::Bottom | Self::XEnd | Self::YEnd => Percentage(1.),
1093        }
1094    }
1095}
1096
1097/// Defines a pair of coordinates, representing a rightward and downward offset, respectively, from
1098/// a specified reference point. Percentages are resolved against the width or height,
1099/// respectively, of the reference box.
1100/// https://drafts.csswg.org/css-shapes-2/#typedef-shape-coordinate-pair
1101#[allow(missing_docs)]
1102#[derive(
1103    AddAssign,
1104    Animate,
1105    Clone,
1106    ComputeSquaredDistance,
1107    Copy,
1108    Debug,
1109    Deserialize,
1110    MallocSizeOf,
1111    PartialEq,
1112    Serialize,
1113    SpecifiedValueInfo,
1114    ToAnimatedValue,
1115    ToAnimatedZero,
1116    ToComputedValue,
1117    ToCss,
1118    ToResolvedValue,
1119    ToShmem,
1120)]
1121#[repr(C)]
1122pub struct CoordinatePair<LengthPercentage> {
1123    pub x: LengthPercentage,
1124    pub y: LengthPercentage,
1125}
1126
1127impl<LengthPercentage> CoordinatePair<LengthPercentage> {
1128    /// Create a CoordinatePair.
1129    #[inline]
1130    pub fn new(x: LengthPercentage, y: LengthPercentage) -> Self {
1131        Self { x, y }
1132    }
1133}
1134
1135/// Defines a control point for a quadratic or cubic Bézier curve, which can be specified
1136/// in absolute or relative coordinates.
1137/// https://drafts.csswg.org/css-shapes/#typedef-shape-control-point
1138#[allow(missing_docs)]
1139#[derive(
1140    Animate,
1141    Clone,
1142    Copy,
1143    ComputeSquaredDistance,
1144    Debug,
1145    Deserialize,
1146    MallocSizeOf,
1147    PartialEq,
1148    Serialize,
1149    SpecifiedValueInfo,
1150    ToAnimatedValue,
1151    ToAnimatedZero,
1152    ToComputedValue,
1153    ToResolvedValue,
1154    ToShmem,
1155)]
1156#[repr(C, u8)]
1157pub enum ControlPoint<Position, LengthPercentage> {
1158    Absolute(Position),
1159    Relative(RelativeControlPoint<LengthPercentage>),
1160}
1161
1162impl<Position, LengthPercentage> ControlPoint<Position, LengthPercentage> {
1163    /// Serialize <control-point>
1164    pub fn to_css<W>(&self, dest: &mut CssWriter<W>, is_end_point_abs: bool) -> fmt::Result
1165    where
1166        W: Write,
1167        Position: ToCss,
1168        LengthPercentage: ToCss,
1169    {
1170        match self {
1171            ControlPoint::Absolute(pos) => pos.to_css(dest),
1172            ControlPoint::Relative(point) => point.to_css(dest, is_end_point_abs),
1173        }
1174    }
1175}
1176
1177/// Defines a relative control point to a quadratic or cubic Bézier curve, dependent on the
1178/// reference value. The default `None` is to be relative to the command’s starting point.
1179/// https://drafts.csswg.org/css-shapes/#typedef-shape-relative-control-point
1180#[allow(missing_docs)]
1181#[derive(
1182    Animate,
1183    Clone,
1184    Copy,
1185    Debug,
1186    Deserialize,
1187    MallocSizeOf,
1188    PartialEq,
1189    Serialize,
1190    SpecifiedValueInfo,
1191    ToAnimatedValue,
1192    ToAnimatedZero,
1193    ToComputedValue,
1194    ToResolvedValue,
1195    ToShmem,
1196)]
1197#[repr(C)]
1198pub struct RelativeControlPoint<LengthPercentage> {
1199    pub coord: CoordinatePair<LengthPercentage>,
1200    pub reference: ControlReference,
1201}
1202
1203impl<LengthPercentage: ToCss> RelativeControlPoint<LengthPercentage> {
1204    fn to_css<W>(&self, dest: &mut CssWriter<W>, is_end_point_abs: bool) -> fmt::Result
1205    where
1206        W: Write,
1207    {
1208        self.coord.to_css(dest)?;
1209        match self.reference {
1210            ControlReference::Origin if is_end_point_abs => Ok(()),
1211            ControlReference::Start if !is_end_point_abs => Ok(()),
1212            other => {
1213                dest.write_str(" from ")?;
1214                other.to_css(dest)
1215            },
1216        }
1217    }
1218}
1219
1220impl<LengthPercentage: ComputeSquaredDistance> ComputeSquaredDistance
1221    for RelativeControlPoint<LengthPercentage>
1222{
1223    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
1224        self.coord.compute_squared_distance(&other.coord)
1225    }
1226}
1227
1228/// Defines the point of reference for a <relative-control-point>.
1229///
1230/// When a reference is not specified, depending on whether the associated
1231/// <command-end-point> is absolutely or relatively positioned, the default
1232/// will be `Origin` or `Start`, respectively.
1233/// https://drafts.csswg.org/css-shapes/#typedef-shape-relative-control-point
1234#[allow(missing_docs)]
1235#[derive(
1236    Animate,
1237    Clone,
1238    Copy,
1239    Debug,
1240    Deserialize,
1241    Eq,
1242    MallocSizeOf,
1243    PartialEq,
1244    Parse,
1245    Serialize,
1246    SpecifiedValueInfo,
1247    ToAnimatedValue,
1248    ToAnimatedZero,
1249    ToComputedValue,
1250    ToCss,
1251    ToResolvedValue,
1252    ToShmem,
1253)]
1254#[repr(C)]
1255pub enum ControlReference {
1256    Start,
1257    End,
1258    Origin,
1259}
1260
1261/// Defines the radiuses for an <arc-command>.
1262///
1263/// The first <length-percentage> is the ellipse's horizontal radius, and the second is
1264/// the vertical radius. If only one value is provided, it is used for both radii, and any
1265/// <percentage> is resolved against the direction-agnostic size of the reference box.
1266/// https://drafts.csswg.org/css-shapes-1/#typedef-shape-arc-command
1267#[allow(missing_docs)]
1268#[derive(
1269    Animate,
1270    Clone,
1271    ComputeSquaredDistance,
1272    Copy,
1273    Debug,
1274    Deserialize,
1275    MallocSizeOf,
1276    PartialEq,
1277    Serialize,
1278    SpecifiedValueInfo,
1279    ToAnimatedValue,
1280    ToAnimatedZero,
1281    ToComputedValue,
1282    ToCss,
1283    ToResolvedValue,
1284    ToShmem,
1285)]
1286#[repr(C)]
1287pub struct ArcRadii<LengthPercentage> {
1288    pub rx: LengthPercentage,
1289    pub ry: Optional<LengthPercentage>,
1290}
1291
1292/// This indicates that the arc that is traced around the ellipse clockwise or counter-clockwise
1293/// from the center.
1294/// https://drafts.csswg.org/css-shapes-2/#typedef-shape-arc-sweep
1295#[derive(
1296    Clone,
1297    Copy,
1298    Debug,
1299    Deserialize,
1300    FromPrimitive,
1301    MallocSizeOf,
1302    Parse,
1303    PartialEq,
1304    Serialize,
1305    SpecifiedValueInfo,
1306    ToAnimatedValue,
1307    ToAnimatedZero,
1308    ToComputedValue,
1309    ToCss,
1310    ToResolvedValue,
1311    ToShmem,
1312)]
1313#[repr(u8)]
1314pub enum ArcSweep {
1315    /// Counter-clockwise. The default value. (This also represents 0 in the svg path.)
1316    Ccw = 0,
1317    /// Clockwise. (This also represents 1 in the svg path.)
1318    Cw = 1,
1319}
1320
1321impl Animate for ArcSweep {
1322    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
1323        use num_traits::FromPrimitive;
1324        // If an arc command has different <arc-sweep> between its starting and ending list, then
1325        // the interpolated result uses cw for any progress value between 0 and 1.
1326        // Note: we cast progress from f64->f32->f64 to drop tiny noise near 0.0.
1327        let progress = procedure.weights().1 as f32 as f64;
1328        let procedure = Procedure::Interpolate { progress };
1329        (*self as i32 as f32)
1330            .animate(&(*other as i32 as f32), procedure)
1331            .map(|v| ArcSweep::from_u8((v > 0.) as u8).unwrap_or(ArcSweep::Ccw))
1332    }
1333}
1334
1335impl ComputeSquaredDistance for ArcSweep {
1336    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
1337        (*self as i32).compute_squared_distance(&(*other as i32))
1338    }
1339}
1340
1341/// This indicates that the larger or smaller, respectively, of the two possible arcs must be
1342/// chosen.
1343/// https://drafts.csswg.org/css-shapes-2/#typedef-shape-arc-size
1344#[derive(
1345    Clone,
1346    Copy,
1347    Debug,
1348    Deserialize,
1349    FromPrimitive,
1350    MallocSizeOf,
1351    Parse,
1352    PartialEq,
1353    Serialize,
1354    SpecifiedValueInfo,
1355    ToAnimatedValue,
1356    ToAnimatedZero,
1357    ToComputedValue,
1358    ToCss,
1359    ToResolvedValue,
1360    ToShmem,
1361)]
1362#[repr(u8)]
1363pub enum ArcSize {
1364    /// Choose the small one. The default value. (This also represents 0 in the svg path.)
1365    Small = 0,
1366    /// Choose the large one. (This also represents 1 in the svg path.)
1367    Large = 1,
1368}
1369
1370impl Animate for ArcSize {
1371    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
1372        use num_traits::FromPrimitive;
1373        // If it has different <arc-size> keywords, then the interpolated result uses large for any
1374        // progress value between 0 and 1.
1375        // Note: we cast progress from f64->f32->f64 to drop tiny noise near 0.0.
1376        let progress = procedure.weights().1 as f32 as f64;
1377        let procedure = Procedure::Interpolate { progress };
1378        (*self as i32 as f32)
1379            .animate(&(*other as i32 as f32), procedure)
1380            .map(|v| ArcSize::from_u8((v > 0.) as u8).unwrap_or(ArcSize::Small))
1381    }
1382}
1383
1384impl ComputeSquaredDistance for ArcSize {
1385    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
1386        (*self as i32).compute_squared_distance(&(*other as i32))
1387    }
1388}