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<'i, 't>(
36        context: &ParserContext,
37        input: &mut Parser<'i, 't>,
38    ) -> Result<Self, ParseError<'i>> {
39        if let Ok(arg) = input.try_parse(|i| -> Result<SuperellipseArg, ParseError<'i>> {
40            let location = i.current_source_location();
41            match i.next()? {
42                Token::Ident(ref ident) if ident.eq_ignore_ascii_case("infinity") => {
43                    Ok(SuperellipseArg::Infinity)
44                },
45                Token::Ident(ref ident) if ident.eq_ignore_ascii_case("-infinity") => {
46                    Ok(SuperellipseArg::NegativeInfinity)
47                },
48                _ => Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
49            }
50        }) {
51            return Ok(arg);
52        }
53        Number::parse(context, input).map(SuperellipseArg::Number)
54    }
55
56    /// Resolve to a numeric `K` value (possibly +/- infinity).
57    pub fn to_k(&self, context: &Context) -> f32 {
58        match self {
59            SuperellipseArg::Infinity => f32::INFINITY,
60            SuperellipseArg::NegativeInfinity => f32::NEG_INFINITY,
61            SuperellipseArg::Number(n) => n.to_computed_value(context),
62        }
63    }
64}
65
66/// The specified value of a single corner-shape (e.g. `corner-top-left-shape`).
67///
68/// <https://drafts.csswg.org/css-borders-4/#typedef-corner-shape-value>
69#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
70#[typed(todo_derive_fields)]
71pub enum CornerShape {
72    /// `round`, equivalent to `superellipse(1)`. Initial value.
73    Round,
74    /// `scoop`, equivalent to `superellipse(-1)`.
75    Scoop,
76    /// `bevel`, equivalent to `superellipse(0)`.
77    Bevel,
78    /// `notch`, equivalent to `superellipse(-infinity)`.
79    Notch,
80    /// `square`, equivalent to `superellipse(infinity)`.
81    Square,
82    /// `squircle`, equivalent to `superellipse(2)`.
83    Squircle,
84    /// `superellipse(<arg>)`.
85    #[css(function)]
86    Superellipse(SuperellipseArg),
87}
88
89impl CornerShape {
90    /// The initial value: `round`.
91    #[inline]
92    pub fn round() -> Self {
93        CornerShape::Round
94    }
95}
96
97impl Parse for CornerShape {
98    fn parse<'i, 't>(
99        context: &ParserContext,
100        input: &mut Parser<'i, 't>,
101    ) -> Result<Self, ParseError<'i>> {
102        // Try the `superellipse(...)` function first.
103        if let Ok(arg) = input.try_parse(|i| {
104            i.expect_function_matching("superellipse")?;
105            i.parse_nested_block(|i| SuperellipseArg::parse(context, i))
106        }) {
107            return Ok(CornerShape::Superellipse(arg));
108        }
109        Ok(try_match_ident_ignore_ascii_case! { input,
110            "round" => CornerShape::Round,
111            "scoop" => CornerShape::Scoop,
112            "bevel" => CornerShape::Bevel,
113            "notch" => CornerShape::Notch,
114            "square" => CornerShape::Square,
115            "squircle" => CornerShape::Squircle,
116        })
117    }
118}
119
120impl ToComputedValue for CornerShape {
121    type ComputedValue = computed::CornerShape;
122
123    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
124        // Per spec, the computed value is always the corresponding
125        // `superellipse(K)` value.
126        computed::CornerShape {
127            k: match self {
128                CornerShape::Round => 1.0,
129                CornerShape::Scoop => -1.0,
130                CornerShape::Bevel => 0.0,
131                CornerShape::Notch => f32::NEG_INFINITY,
132                CornerShape::Square => f32::INFINITY,
133                CornerShape::Squircle => 2.0,
134                CornerShape::Superellipse(arg) => arg.to_k(context),
135            },
136        }
137    }
138
139    fn from_computed_value(c: &Self::ComputedValue) -> Self {
140        let arg = if c.k == f32::INFINITY {
141            SuperellipseArg::Infinity
142        } else if c.k == f32::NEG_INFINITY {
143            SuperellipseArg::NegativeInfinity
144        } else {
145            SuperellipseArg::Number(Number::new(c.k))
146        };
147        CornerShape::Superellipse(arg)
148    }
149}
150
151/// The specified value of `corner-shape`. Stored per-corner.
152pub type CornerShapeRect = crate::values::generics::border::GenericCornerShapeRect<CornerShape>;
153
154impl CornerShapeRect {
155    /// Initial value: `round` for all four corners.
156    pub fn round() -> Self {
157        Self::all(CornerShape::Round)
158    }
159}