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