Skip to main content

style/values/specified/
corner_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//! Specified types for the `corner-shape` family of properties.
6//!
7//! <https://drafts.csswg.org/css-borders-4/#corner-shaping>
8
9use crate::derives::*;
10use crate::parser::{Parse, ParserContext};
11use crate::values::computed::corner_shape as computed;
12use crate::values::computed::{Context, ToComputedValue};
13use crate::values::specified::Number;
14use cssparser::{Parser, Token};
15use style_traits::{ParseError, StyleParseErrorKind};
16
17/// The argument to the `superellipse()` function.
18///
19/// `superellipse(K)` defines the corner shape using the unit equation
20/// `x^(2^K) + y^(2^K) = 1`. `infinity` and `-infinity` are accepted as
21/// special-cased keyword arguments.
22#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
23#[typed(todo_derive_fields)]
24pub enum SuperellipseArg {
25    /// `<number>` argument.
26    Number(Number),
27    /// The `infinity` keyword.
28    Infinity,
29    /// The `-infinity` keyword (lexed as the literal text `-infinity`).
30    #[css(keyword = "-infinity")]
31    NegativeInfinity,
32}
33
34impl SuperellipseArg {
35    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
36        if let Ok(arg) = input.try_parse(|i| -> Result<SuperellipseArg, ParseError> {
37            match i.next()? {
38                Token::Ident(ident) if ident.eq_ignore_ascii_case("infinity") => {
39                    Ok(SuperellipseArg::Infinity)
40                },
41                Token::Ident(ident) if ident.eq_ignore_ascii_case("-infinity") => {
42                    Ok(SuperellipseArg::NegativeInfinity)
43                },
44                _ => Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError)),
45            }
46        }) {
47            return Ok(arg);
48        }
49        Number::parse(context, input).map(SuperellipseArg::Number)
50    }
51
52    /// Resolve to a numeric `K` value (possibly +/- infinity).
53    pub fn to_k(&self, context: &Context) -> f32 {
54        match self {
55            SuperellipseArg::Infinity => f32::INFINITY,
56            SuperellipseArg::NegativeInfinity => f32::NEG_INFINITY,
57            SuperellipseArg::Number(n) => n.to_computed_value(context),
58        }
59    }
60}
61
62/// The specified value of a single corner-shape (e.g. `corner-top-left-shape`).
63///
64/// <https://drafts.csswg.org/css-borders-4/#typedef-corner-shape-value>
65#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
66#[typed(todo_derive_fields)]
67pub enum CornerShape {
68    /// `round`, equivalent to `superellipse(1)`. Initial value.
69    Round,
70    /// `scoop`, equivalent to `superellipse(-1)`.
71    Scoop,
72    /// `bevel`, equivalent to `superellipse(0)`.
73    Bevel,
74    /// `notch`, equivalent to `superellipse(-infinity)`.
75    Notch,
76    /// `square`, equivalent to `superellipse(infinity)`.
77    Square,
78    /// `squircle`, equivalent to `superellipse(2)`.
79    Squircle,
80    /// `superellipse(<arg>)`.
81    #[css(function)]
82    Superellipse(SuperellipseArg),
83}
84
85impl CornerShape {
86    /// The initial value: `round`.
87    #[inline]
88    pub fn round() -> Self {
89        CornerShape::Round
90    }
91}
92
93impl Parse for CornerShape {
94    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
95        // Try the `superellipse(...)` function first.
96        if let Ok(arg) = input.try_parse(|i| {
97            i.expect_function_matching("superellipse")?;
98            i.parse_nested_block(|i| SuperellipseArg::parse(context, i))
99        }) {
100            return Ok(CornerShape::Superellipse(arg));
101        }
102        Ok(try_match_ident_ignore_ascii_case! { input,
103            "round" => CornerShape::Round,
104            "scoop" => CornerShape::Scoop,
105            "bevel" => CornerShape::Bevel,
106            "notch" => CornerShape::Notch,
107            "square" => CornerShape::Square,
108            "squircle" => CornerShape::Squircle,
109        })
110    }
111}
112
113impl ToComputedValue for CornerShape {
114    type ComputedValue = computed::CornerShape;
115
116    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
117        // Per spec, the computed value is always the corresponding
118        // `superellipse(K)` value.
119        computed::CornerShape {
120            k: match self {
121                CornerShape::Round => 1.0,
122                CornerShape::Scoop => -1.0,
123                CornerShape::Bevel => 0.0,
124                CornerShape::Notch => f32::NEG_INFINITY,
125                CornerShape::Square => f32::INFINITY,
126                CornerShape::Squircle => 2.0,
127                CornerShape::Superellipse(arg) => arg.to_k(context),
128            },
129        }
130    }
131
132    fn from_computed_value(c: &Self::ComputedValue) -> Self {
133        let arg = if c.k == f32::INFINITY {
134            SuperellipseArg::Infinity
135        } else if c.k == f32::NEG_INFINITY {
136            SuperellipseArg::NegativeInfinity
137        } else {
138            SuperellipseArg::Number(Number::new(c.k))
139        };
140        CornerShape::Superellipse(arg)
141    }
142}
143
144/// The specified value of `corner-shape`. Stored per-corner.
145pub type CornerShapeRect = crate::values::generics::border::GenericCornerShapeRect<CornerShape>;
146
147impl CornerShapeRect {
148    /// Initial value: `round` for all four corners.
149    pub fn round() -> Self {
150        Self::all(CornerShape::Round)
151    }
152}