Skip to main content

style/values/computed/
calc.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//! Computed-value calc() leaf types.
6
7use super::{Angle, Length, Number, Percentage, Resolution, Time};
8use crate::derives::*;
9use crate::values::generics::calc::{self, CalcUnits, PositivePercentageBasis};
10use crate::Zero;
11use debug_unreachable::debug_unreachable;
12use serde::{Deserialize, Serialize};
13
14/// The computed leaf of a calc() expression.
15#[derive(
16    Clone,
17    Debug,
18    Deserialize,
19    MallocSizeOf,
20    PartialEq,
21    Serialize,
22    ToAnimatedZero,
23    ToCss,
24    ToResolvedValue,
25    ToTyped,
26)]
27#[allow(missing_docs)]
28#[repr(u8)]
29pub enum ComputedLeaf {
30    Length(Length),
31    Percentage(Percentage),
32    Number(Number),
33    Angle(Angle),
34    Time(Time),
35    Resolution(Resolution),
36}
37
38impl ComputedLeaf {
39    pub(super) fn is_zero_length(&self) -> bool {
40        match *self {
41            Self::Length(ref l) => l.is_zero(),
42            Self::Percentage(..)
43            | Self::Number(..)
44            | Self::Angle(..)
45            | Self::Time(..)
46            | Self::Resolution(..) => false,
47        }
48    }
49}
50
51impl calc::CalcNodeLeaf for ComputedLeaf {
52    fn unit(&self) -> CalcUnits {
53        match self {
54            Self::Length(_) => CalcUnits::LENGTH,
55            Self::Percentage(_) => CalcUnits::PERCENTAGE,
56            Self::Number(_) => CalcUnits::empty(),
57            Self::Angle(_) => CalcUnits::ANGLE,
58            Self::Time(_) => CalcUnits::TIME,
59            Self::Resolution(_) => CalcUnits::RESOLUTION,
60        }
61    }
62
63    fn unitless_value(&self) -> Option<f32> {
64        Some(match *self {
65            Self::Length(ref l) => l.px(),
66            Self::Percentage(ref p) => p.0,
67            Self::Number(n) => n,
68            Self::Angle(ref a) => a.degrees(),
69            Self::Time(ref t) => t.seconds(),
70            Self::Resolution(ref r) => r.dppx(),
71        })
72    }
73
74    fn new_number(value: f32) -> Self {
75        Self::Number(value)
76    }
77
78    fn as_number(&self) -> Option<f32> {
79        match *self {
80            Self::Length(_)
81            | Self::Percentage(_)
82            | Self::Angle(_)
83            | Self::Time(_)
84            | Self::Resolution(_) => None,
85            Self::Number(value) => Some(value),
86        }
87    }
88
89    fn as_angle_radians(&self) -> Option<f32> {
90        match *self {
91            Self::Angle(a) => Some(a.radians()),
92            _ => None,
93        }
94    }
95
96    fn new_angle_from_radians(radians: f32) -> Self {
97        Self::Angle(Angle::from_radians(radians))
98    }
99
100    fn compare(&self, other: &Self, basis: PositivePercentageBasis) -> Option<std::cmp::Ordering> {
101        use self::ComputedLeaf::*;
102        if std::mem::discriminant(self) != std::mem::discriminant(other) {
103            return None;
104        }
105
106        if matches!(self, Percentage(..)) && matches!(basis, PositivePercentageBasis::Unknown) {
107            return None;
108        }
109
110        let Ok(self_negative) = self.is_negative() else {
111            return None;
112        };
113        let Ok(other_negative) = other.is_negative() else {
114            return None;
115        };
116        if self_negative != other_negative {
117            return Some(if self_negative {
118                std::cmp::Ordering::Less
119            } else {
120                std::cmp::Ordering::Greater
121            });
122        }
123
124        match (self, other) {
125            (&Length(ref one), &Length(ref other)) => one.partial_cmp(other),
126            (&Percentage(ref one), &Percentage(ref other)) => one.partial_cmp(other),
127            (&Number(ref one), &Number(ref other)) => one.partial_cmp(other),
128            (&Angle(ref one), &Angle(ref other)) => one.partial_cmp(other),
129            (&Time(ref one), &Time(ref other)) => one.partial_cmp(other),
130            (&Resolution(ref one), &Resolution(ref other)) => one.partial_cmp(other),
131            _ => unsafe {
132                match *self {
133                    Length(..) | Percentage(..) | Number(..) | Angle(..) | Time(..)
134                    | Resolution(..) => {},
135                }
136                debug_unreachable!("Forgot to handle unit in compare()")
137            },
138        }
139    }
140
141    fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()> {
142        use self::ComputedLeaf::*;
143
144        // 0px plus anything else is equal to the right hand side.
145        if self.is_zero_length() {
146            *self = other.clone();
147            return Ok(());
148        }
149
150        if other.is_zero_length() {
151            return Ok(());
152        }
153
154        if std::mem::discriminant(self) != std::mem::discriminant(other) {
155            return Err(());
156        }
157
158        match (self, other) {
159            (&mut Length(ref mut one), &Length(ref other)) => {
160                *one += *other;
161            },
162            (&mut Percentage(ref mut one), &Percentage(ref other)) => {
163                one.0 += other.0;
164            },
165            (&mut Number(ref mut one), &Number(ref other)) => {
166                *one += *other;
167            },
168            (&mut Angle(ref mut one), &Angle(ref other)) => {
169                *one += *other;
170            },
171            (&mut Time(ref mut one), &Time(ref other)) => {
172                *one += *other;
173            },
174            (&mut Resolution(ref mut one), &Resolution(ref other)) => {
175                *one += *other;
176            },
177            _ => unsafe {
178                match *other {
179                    Length(..) | Percentage(..) | Number(..) | Angle(..) | Time(..)
180                    | Resolution(..) => {},
181                }
182                debug_unreachable!("Forgot to handle unit in try_sum_in_place()")
183            },
184        }
185
186        Ok(())
187    }
188
189    fn try_product_in_place(&mut self, other: &mut Self) -> bool {
190        if let Self::Number(ref mut left) = *self {
191            if let Self::Number(ref right) = *other {
192                // Both sides are numbers, so we can just modify the left side.
193                *left *= *right;
194                true
195            } else {
196                // The right side is not a number, so the result should be in the units of the right
197                // side.
198                if other.map(|v| v * *left).is_ok() {
199                    std::mem::swap(self, other);
200                    true
201                } else {
202                    false
203                }
204            }
205        } else if let Self::Number(ref right) = *other {
206            // The left side is not a number, but the right side is, so the result is the left
207            // side unit.
208            self.map(|v| v * *right).is_ok()
209        } else {
210            // Neither side is a number, so a product is not possible.
211            false
212        }
213    }
214
215    fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
216    where
217        O: Fn(f32, f32) -> f32,
218    {
219        use self::ComputedLeaf::*;
220        if std::mem::discriminant(self) != std::mem::discriminant(other) {
221            return Err(());
222        }
223        Ok(match (self, other) {
224            (&Length(ref one), &Length(ref other)) => {
225                Length(super::Length::new(op(one.px(), other.px())))
226            },
227            (&Percentage(one), &Percentage(other)) => {
228                Self::Percentage(super::Percentage(op(one.0, other.0)))
229            },
230            (&Number(one), &Number(other)) => Self::Number(op(one, other)),
231            (&Angle(ref one), &Angle(ref other)) => Self::Angle(super::Angle::from_degrees(op(
232                one.degrees(),
233                other.degrees(),
234            ))),
235            (&Time(ref one), &Time(ref other)) => Self::Time(super::Time::from_seconds(op(
236                one.seconds(),
237                other.seconds(),
238            ))),
239            (&Resolution(ref one), &Resolution(ref other)) => {
240                Self::Resolution(super::Resolution::from_dppx(op(one.dppx(), other.dppx())))
241            },
242            _ => unsafe {
243                match *self {
244                    Length(..) | Percentage(..) | Number(..) | Angle(..) | Time(..)
245                    | Resolution(..) => {},
246                }
247                debug_unreachable!("Forgot to handle unit in try_op()")
248            },
249        })
250    }
251
252    fn map(&mut self, mut op: impl FnMut(f32) -> f32) -> Result<(), ()> {
253        Ok(match self {
254            Self::Length(value) => {
255                *value = Length::new(op(value.px()));
256            },
257            Self::Percentage(value) => {
258                *value = Percentage(op(value.0));
259            },
260            Self::Number(value) => {
261                *value = op(*value);
262            },
263            Self::Angle(value) => {
264                *value = Angle::from_degrees(op(value.degrees()));
265            },
266            Self::Time(value) => {
267                *value = Time::from_seconds(op(value.seconds()));
268            },
269            Self::Resolution(value) => {
270                *value = Resolution::from_dppx(op(value.dppx()));
271            },
272        })
273    }
274
275    fn simplify(&mut self) {}
276
277    fn sort_key(&self) -> calc::SortKey {
278        match *self {
279            Self::Length(..) => calc::SortKey::Px,
280            Self::Percentage(..) => calc::SortKey::Percentage,
281            Self::Number(..) => calc::SortKey::Number,
282            Self::Angle(..) => calc::SortKey::Deg,
283            Self::Time(..) => calc::SortKey::S,
284            Self::Resolution(..) => calc::SortKey::Dppx,
285        }
286    }
287}