tawasuyu

tawasuyu/taffy

A high performance rust-powered UI layout library

Rust 499HTML 245JavaScript 8Markdown 71218 Sutichaykuna

⎇ bd0150f5fca7eadd62553898d3b064ccc3c54897 src/style/dimension.rs Rust · 332 siq'ikuna · 11.3 KB Ñawpa willay Pi imata llamkarqan ch'uya

Validate tag when deserializing values (#805) bd0150f5f · Nico Burns ·

1//! Style types for representing lengths / sizes
2use super::CompactLength;
3use crate::geometry::Rect;
4use crate::style_helpers::{FromLength, FromPercent, TaffyAuto, TaffyZero};
5
6/// A unit of linear measurement
7///
8/// This is commonly combined with [`Rect`], [`Point`](crate::geometry::Point) and [`Size<T>`](crate::geometry::Size).
9#[derive(Copy, Clone, PartialEq, Debug)]
10#[cfg_attr(feature = "serde", derive(Serialize))]
11pub struct LengthPercentage(pub(crate) CompactLength);
12impl TaffyZero for LengthPercentage {
13    const ZERO: Self = Self(CompactLength::ZERO);
14}
15impl FromLength for LengthPercentage {
16    fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
17        Self::length(value.into())
18    }
19}
20impl FromPercent for LengthPercentage {
21    fn from_percent<Input: Into<f32> + Copy>(value: Input) -> Self {
22        Self::percent(value.into())
23    }
24}
25impl LengthPercentage {
26    /// An absolute length in some abstract units. Users of Taffy may define what they correspond
27    /// to in their application (pixels, logical pixels, mm, etc) as they see fit.
28    #[inline(always)]
29    pub const fn length(val: f32) -> Self {
30        Self(CompactLength::length(val))
31    }
32
33    /// A percentage length relative to the size of the containing block.
34    ///
35    /// **NOTE: percentages are represented as a f32 value in the range [0.0, 1.0] NOT the range [0.0, 100.0]**
36    #[inline(always)]
37    pub const fn percent(val: f32) -> Self {
38        Self(CompactLength::percent(val))
39    }
40
41    /// A `calc()` value. The value passed here is treated as an opaque handle to
42    /// the actual calc representation and may be a pointer, index, etc.
43    ///
44    /// The low 3 bits are used as a tag value and will be returned as 0.
45    #[inline(always)]
46    pub fn calc(ptr: *const ()) -> Self {
47        Self(CompactLength::calc(ptr))
48    }
49
50    /// Create a LengthPercentage from a raw `CompactLength`.
51    /// # Safety
52    /// CompactLength must represent a valid variant for LengthPercentage
53    #[allow(unsafe_code)]
54    pub unsafe fn from_raw(val: CompactLength) -> Self {
55        Self(val)
56    }
57
58    /// Get the underlying `CompactLength` representation of the value
59    pub fn into_raw(self) -> CompactLength {
60        self.0
61    }
62}
63
64#[cfg(feature = "serde")]
65impl<'de> serde::Deserialize<'de> for LengthPercentage {
66    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
67    where
68        D: serde::Deserializer<'de>,
69    {
70        let inner = CompactLength::deserialize(deserializer)?;
71        // Note: validation intentionally excludes the CALC_TAG as deserializing calc() values is not supported
72        if matches!(inner.tag(), CompactLength::LENGTH_TAG | CompactLength::PERCENT_TAG) {
73            Ok(Self(inner))
74        } else {
75            Err(serde::de::Error::custom("Invalid tag"))
76        }
77    }
78}
79
80/// A unit of linear measurement
81///
82/// This is commonly combined with [`Rect`], [`Point`](crate::geometry::Point) and [`Size<T>`](crate::geometry::Size).
83#[derive(Copy, Clone, PartialEq, Debug)]
84#[cfg_attr(feature = "serde", derive(Serialize))]
85pub struct LengthPercentageAuto(pub(crate) CompactLength);
86impl TaffyZero for LengthPercentageAuto {
87    const ZERO: Self = Self(CompactLength::ZERO);
88}
89impl TaffyAuto for LengthPercentageAuto {
90    const AUTO: Self = Self(CompactLength::AUTO);
91}
92impl FromLength for LengthPercentageAuto {
93    fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
94        Self::length(value.into())
95    }
96}
97impl FromPercent for LengthPercentageAuto {
98    fn from_percent<Input: Into<f32> + Copy>(value: Input) -> Self {
99        Self::percent(value.into())
100    }
101}
102impl From<LengthPercentage> for LengthPercentageAuto {
103    fn from(input: LengthPercentage) -> Self {
104        Self(input.0)
105    }
106}
107
108impl LengthPercentageAuto {
109    /// An absolute length in some abstract units. Users of Taffy may define what they correspond
110    /// to in their application (pixels, logical pixels, mm, etc) as they see fit.
111    #[inline(always)]
112    pub const fn length(val: f32) -> Self {
113        Self(CompactLength::length(val))
114    }
115
116    /// A percentage length relative to the size of the containing block.
117    ///
118    /// **NOTE: percentages are represented as a f32 value in the range [0.0, 1.0] NOT the range [0.0, 100.0]**
119    #[inline(always)]
120    pub const fn percent(val: f32) -> Self {
121        Self(CompactLength::percent(val))
122    }
123
124    /// The dimension should be automatically computed according to algorithm-specific rules
125    /// regarding the default size of boxes.
126    #[inline(always)]
127    pub const fn auto() -> Self {
128        Self(CompactLength::auto())
129    }
130
131    /// A `calc()` value. The value passed here is treated as an opaque handle to
132    /// the actual calc representation and may be a pointer, index, etc.
133    ///
134    /// The low 3 bits are used as a tag value and will be returned as 0.
135    #[inline]
136    pub fn calc(ptr: *const ()) -> Self {
137        Self(CompactLength::calc(ptr))
138    }
139
140    /// Create a LengthPercentageAuto from a raw `CompactLength`.
141    /// # Safety
142    /// CompactLength must represent a valid variant for LengthPercentageAuto
143    #[allow(unsafe_code)]
144    pub unsafe fn from_raw(val: CompactLength) -> Self {
145        Self(val)
146    }
147
148    /// Get the underlying `CompactLength` representation of the value
149    pub fn into_raw(self) -> CompactLength {
150        self.0
151    }
152
153    /// Returns:
154    ///   - Some(length) for Length variants
155    ///   - Some(resolved) using the provided context for Percent variants
156    ///   - None for Auto variants
157    #[inline(always)]
158    pub fn resolve_to_option(self, context: f32, calc_resolver: impl Fn(*const (), f32) -> f32) -> Option<f32> {
159        match self.0.tag() {
160            CompactLength::LENGTH_TAG => Some(self.0.value()),
161            CompactLength::PERCENT_TAG => Some(context * self.0.value()),
162            CompactLength::AUTO_TAG => None,
163            _ if self.0.is_calc() => Some(calc_resolver(self.0.calc_value(), context)),
164            _ => unreachable!("LengthPercentageAuto values cannot be constructed with other tags"),
165        }
166    }
167
168    /// Returns true if value is LengthPercentageAuto::Auto
169    #[inline(always)]
170    pub fn is_auto(self) -> bool {
171        self.0.is_auto()
172    }
173}
174
175#[cfg(feature = "serde")]
176impl<'de> serde::Deserialize<'de> for LengthPercentageAuto {
177    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
178    where
179        D: serde::Deserializer<'de>,
180    {
181        let inner = CompactLength::deserialize(deserializer)?;
182        // Note: validation intentionally excludes the CALC_TAG as deserializing calc() values is not supported
183        if matches!(inner.tag(), CompactLength::LENGTH_TAG | CompactLength::PERCENT_TAG | CompactLength::AUTO_TAG) {
184            Ok(Self(inner))
185        } else {
186            Err(serde::de::Error::custom("Invalid tag"))
187        }
188    }
189}
190
191/// A unit of linear measurement
192///
193/// This is commonly combined with [`Rect`], [`Point`](crate::geometry::Point) and [`Size<T>`](crate::geometry::Size).
194#[derive(Copy, Clone, PartialEq, Debug)]
195#[cfg_attr(feature = "serde", derive(Serialize))]
196pub struct Dimension(pub(crate) CompactLength);
197impl TaffyZero for Dimension {
198    const ZERO: Self = Self(CompactLength::ZERO);
199}
200impl TaffyAuto for Dimension {
201    const AUTO: Self = Self(CompactLength::AUTO);
202}
203impl FromLength for Dimension {
204    fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
205        Self::length(value.into())
206    }
207}
208impl FromPercent for Dimension {
209    fn from_percent<Input: Into<f32> + Copy>(value: Input) -> Self {
210        Self::percent(value.into())
211    }
212}
213impl From<LengthPercentage> for Dimension {
214    fn from(input: LengthPercentage) -> Self {
215        Self(input.0)
216    }
217}
218impl From<LengthPercentageAuto> for Dimension {
219    fn from(input: LengthPercentageAuto) -> Self {
220        Self(input.0)
221    }
222}
223
224impl Dimension {
225    /// An absolute length in some abstract units. Users of Taffy may define what they correspond
226    /// to in their application (pixels, logical pixels, mm, etc) as they see fit.
227    #[inline(always)]
228    pub const fn length(val: f32) -> Self {
229        Self(CompactLength::length(val))
230    }
231
232    /// A percentage length relative to the size of the containing block.
233    ///
234    /// **NOTE: percentages are represented as a f32 value in the range [0.0, 1.0] NOT the range [0.0, 100.0]**
235    #[inline(always)]
236    pub const fn percent(val: f32) -> Self {
237        Self(CompactLength::percent(val))
238    }
239
240    /// The dimension should be automatically computed according to algorithm-specific rules
241    /// regarding the default size of boxes.
242    #[inline(always)]
243    pub const fn auto() -> Self {
244        Self(CompactLength::auto())
245    }
246
247    /// A `calc()` value. The value passed here is treated as an opaque handle to
248    /// the actual calc representation and may be a pointer, index, etc.
249    ///
250    /// The low 3 bits are used as a tag value and will be returned as 0.
251    #[inline]
252    pub fn calc(ptr: *const ()) -> Self {
253        Self(CompactLength::calc(ptr))
254    }
255
256    /// Create a LengthPercentageAuto from a raw `CompactLength`.
257    /// # Safety
258    /// CompactLength must represent a valid variant for LengthPercentageAuto
259    #[allow(unsafe_code)]
260    pub unsafe fn from_raw(val: CompactLength) -> Self {
261        Self(val)
262    }
263
264    /// Get the underlying `CompactLength` representation of the value
265    pub fn into_raw(self) -> CompactLength {
266        self.0
267    }
268
269    /// Get Length value if value is Length variant
270    #[cfg(feature = "grid")]
271    pub fn into_option(self) -> Option<f32> {
272        match self.0.tag() {
273            CompactLength::LENGTH_TAG => Some(self.0.value()),
274            _ => None,
275        }
276    }
277    /// Returns true if value is Auto
278    #[inline(always)]
279    pub fn is_auto(self) -> bool {
280        self.0.is_auto()
281    }
282
283    /// Get the raw `CompactLength` tag
284    pub fn tag(self) -> usize {
285        self.0.tag()
286    }
287
288    /// Get the raw `CompactLength` value for non-calc variants that have a numeric parameter
289    pub fn value(self) -> f32 {
290        self.0.value()
291    }
292}
293
294#[cfg(feature = "serde")]
295impl<'de> serde::Deserialize<'de> for Dimension {
296    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
297    where
298        D: serde::Deserializer<'de>,
299    {
300        let inner = CompactLength::deserialize(deserializer)?;
301        // Note: validation intentionally excludes the CALC_TAG as deserializing calc() values is not supported
302        if matches!(inner.tag(), CompactLength::LENGTH_TAG | CompactLength::PERCENT_TAG | CompactLength::AUTO_TAG) {
303            Ok(Self(inner))
304        } else {
305            Err(serde::de::Error::custom("Invalid tag"))
306        }
307    }
308}
309
310impl Rect<Dimension> {
311    /// Create a new Rect with length values
312    #[must_use]
313    pub const fn from_length(start: f32, end: f32, top: f32, bottom: f32) -> Self {
314        Rect {
315            left: Dimension(CompactLength::length(start)),
316            right: Dimension(CompactLength::length(end)),
317            top: Dimension(CompactLength::length(top)),
318            bottom: Dimension(CompactLength::length(bottom)),
319        }
320    }
321
322    /// Create a new Rect with percentage values
323    #[must_use]
324    pub const fn from_percent(start: f32, end: f32, top: f32, bottom: f32) -> Self {
325        Rect {
326            left: Dimension(CompactLength::percent(start)),
327            right: Dimension(CompactLength::percent(end)),
328            top: Dimension(CompactLength::percent(top)),
329            bottom: Dimension(CompactLength::percent(bottom)),
330        }
331    }
332}