gioser

tawasuyu/taffy

A high performance rust-powered UI layout library

HTML 5364Rust 86Markdown 12TOML 91841 Sutichaykuna

⎇ 6740b9e59f12d3d0e3decb1514817a794f83325f src/style/compact_length.rs Rust · 309 siq'ikuna · 11.2 KB Ñawpa willay Pi imata llamkarqan ch'uya

Move CompactLength into it's own module (#787) 6740b9e59 · Nico Burns ·

1//! A tagged-pointer abstraction that allows size styles in Taffy to be represented
2//! in just 64 bits. Wrapped by types in the `super::dimension` and `super::grid` modules.
3use super::LengthPercentage;
4use crate::style_helpers::{
5    FromFr, FromLength, FromPercent, TaffyAuto, TaffyFitContent, TaffyMaxContent, TaffyMinContent, TaffyZero,
6};
7use compat::{f32_from_bits, f32_to_bits};
8
9/// A representation of a length as a compact 64-bit tagged pointer
10#[derive(Copy, Clone, PartialEq, Debug)]
11#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
12pub struct CompactLength(u64);
13
14impl CompactLength {
15    // Masks
16
17    /// The low byte (8 bits)
18    pub const TAG_MASK: u64 = 0b11111111;
19    /// The low 3 bits
20    pub const CALC_TAG_MASK: u64 = 0b111;
21    /// The high 63 bits
22    pub const CALC_PTR_MASK: u64 = u64::MAX ^ 0b111;
23
24    // Primary tags
25
26    /// The tag indicating a calc() value
27    pub const CALC_TAG: u64 = 0b000;
28    /// The tag indicating a length value
29    pub const LENGTH_TAG: u64 = 0b0000_0001;
30    /// The tag indicating a percentage value
31    pub const PERCENT_TAG: u64 = 0b0000_0010;
32    /// The tag indicating an auto value
33    pub const AUTO_TAG: u64 = 0b0000_0011;
34    /// The tag indicating an fr value
35    pub const FR_TAG: u64 = 0b0000_0100;
36    /// The tag indicating a min-content value
37    pub const MIN_CONTENT_TAG: u64 = 0b00000111;
38    /// The tag indicating a max-content value
39    pub const MAX_CONTENT_TAG: u64 = 0b00001111;
40    /// The tag indicating a fit-content value with px limit
41    pub const FIT_CONTENT_PX_TAG: u64 = 0b00010111;
42    /// The tag indicating a fit-content value with percent limit
43    pub const FIT_CONTENT_PERCENT_TAG: u64 = 0b00011111;
44}
45
46impl CompactLength {
47    /// An absolute length in some abstract units. Users of Taffy may define what they correspond
48    /// to in their application (pixels, logical pixels, mm, etc) as they see fit.
49    #[inline(always)]
50    pub const fn length(val: f32) -> Self {
51        Self(((f32_to_bits(val) as u64) << 32) | Self::LENGTH_TAG)
52    }
53
54    /// A percentage length relative to the size of the containing block.
55    ///
56    /// **NOTE: percentages are represented as a f32 value in the range [0.0, 1.0] NOT the range [0.0, 100.0]**
57    #[inline(always)]
58    pub const fn percent(val: f32) -> Self {
59        Self(((f32_to_bits(val) as u64) << 32) | Self::PERCENT_TAG)
60    }
61
62    /// A `calc()` value. The value passed here is treated as an opaque handle to
63    /// the actual calc representation and may be a pointer, index, etc.
64    ///
65    /// The low 3 bits are used as a tag value and will be returned as 0.
66    #[inline]
67    pub fn calc(ptr: *const ()) -> Self {
68        assert_ne!(ptr as u64, 0);
69        assert_eq!(ptr as u64 & 0b111, 0);
70        Self(ptr as u64 | Self::CALC_TAG)
71    }
72
73    /// The dimension should be automatically computed according to algorithm-specific rules
74    /// regarding the default size of boxes.
75    #[inline(always)]
76    pub const fn auto() -> Self {
77        Self(Self::AUTO_TAG)
78    }
79
80    /// The dimension as a fraction of the total available grid space (`fr` units in CSS)
81    /// Specified value is the numerator of the fraction. Denominator is the sum of all fraction specified in that grid dimension
82    /// Spec: <https://www.w3.org/TR/css3-grid-layout/#fr-unit>
83    #[inline(always)]
84    pub const fn fr(val: f32) -> Self {
85        Self(((f32_to_bits(val) as u64) << 32) | Self::FR_TAG)
86    }
87
88    /// The size should be the "min-content" size.
89    /// This is the smallest size that can fit the item's contents with ALL soft line-wrapping opportunities taken
90    #[inline(always)]
91    pub const fn min_content() -> Self {
92        Self(Self::MIN_CONTENT_TAG)
93    }
94
95    /// The size should be the "max-content" size.
96    /// This is the smallest size that can fit the item's contents with NO soft line-wrapping opportunities taken
97    #[inline(always)]
98    pub const fn max_content() -> Self {
99        Self(Self::MAX_CONTENT_TAG)
100    }
101
102    /// The size should be computed according to the "fit content" formula:
103    ///    `max(min_content, min(max_content, limit))`
104    /// where:
105    ///    - `min_content` is the [min-content](Self::min_content) size
106    ///    - `max_content` is the [max-content](Self::max_content) size
107    ///    - `limit` is a LENGTH value passed to this function
108    ///
109    /// The effect of this is that the item takes the size of `limit` clamped
110    /// by the min-content and max-content sizes.
111    #[inline(always)]
112    pub const fn fit_content_px(limit: f32) -> Self {
113        Self(((f32_to_bits(limit) as u64) << 32) | Self::FIT_CONTENT_PX_TAG)
114    }
115
116    /// The size should be computed according to the "fit content" formula:
117    ///    `max(min_content, min(max_content, limit))`
118    /// where:
119    ///    - `min_content` is the [min-content](Self::min_content) size
120    ///    - `max_content` is the [max-content](Self::max_content) size
121    ///    - `limit` is a PERCENTAGE value passed to this function
122    ///
123    /// The effect of this is that the item takes the size of `limit` clamped
124    /// by the min-content and max-content sizes.
125    #[inline(always)]
126    pub const fn fit_content_percent(limit: f32) -> Self {
127        Self(((f32_to_bits(limit) as u64) << 32) | Self::FIT_CONTENT_PERCENT_TAG)
128    }
129
130    /// Get the primary tag
131    #[inline(always)]
132    pub const fn tag(self) -> u64 {
133        self.0 & Self::TAG_MASK
134    }
135
136    /// Get the numeric value associated with the `CompactLength`
137    /// (e.g. the pixel value for a LENGTH variant)
138    #[inline(always)]
139    pub const fn value(self) -> f32 {
140        f32_from_bits((self.0 >> 32) as u32)
141    }
142
143    /// Get the numeric value associated with the `CompactLength`
144    /// (e.g. the pixel value for a LENGTH variant)
145    #[inline(always)]
146    pub const fn calc_value(self) -> u64 {
147        self.0
148    }
149
150    /// Returns true if the value is 0 px
151    #[inline(always)]
152    pub const fn is_calc(self) -> bool {
153        self.0 & Self::CALC_TAG_MASK == 0
154    }
155
156    /// Returns true if the value is 0 px
157    #[inline(always)]
158    pub const fn is_zero(self) -> bool {
159        self.0 == Self::ZERO.0
160    }
161
162    /// Returns true if the value is a length or percentage value
163    #[inline(always)]
164    pub const fn is_length_or_percentage(self) -> bool {
165        matches!(self.tag(), Self::LENGTH_TAG | Self::PERCENT_TAG)
166    }
167
168    /// Returns true if the value is auto
169    #[inline(always)]
170    pub const fn is_auto(self) -> bool {
171        self.tag() == Self::AUTO_TAG
172    }
173
174    /// Returns true if the value is min-content
175    #[inline(always)]
176    pub const fn is_min_content(self) -> bool {
177        matches!(self.tag(), Self::MIN_CONTENT_TAG)
178    }
179
180    /// Returns true if the value is max-content
181    #[inline(always)]
182    pub const fn is_max_content(self) -> bool {
183        matches!(self.tag(), Self::MAX_CONTENT_TAG)
184    }
185
186    /// Returns true if the value is a fit-content(...) value
187    #[inline(always)]
188    pub const fn is_fit_content(self) -> bool {
189        matches!(self.tag(), Self::FIT_CONTENT_PX_TAG | Self::FIT_CONTENT_PERCENT_TAG)
190    }
191
192    /// Returns true if the value is max-content or a fit-content(...) value
193    #[inline(always)]
194    pub const fn is_max_or_fit_content(self) -> bool {
195        matches!(self.tag(), Self::MAX_CONTENT_TAG | Self::FIT_CONTENT_PX_TAG | Self::FIT_CONTENT_PERCENT_TAG)
196    }
197
198    /// Returns true if the max track sizing function is `MaxContent`, `FitContent` or `Auto` else false.
199    /// "In all cases, treat auto and fit-content() as max-content, except where specified otherwise for fit-content()."
200    /// See: <https://www.w3.org/TR/css-grid-1/#algo-terms>
201    #[inline(always)]
202    pub fn is_max_content_alike(&self) -> bool {
203        matches!(
204            self.tag(),
205            CompactLength::AUTO_TAG
206                | CompactLength::MAX_CONTENT_TAG
207                | CompactLength::FIT_CONTENT_PX_TAG
208                | CompactLength::FIT_CONTENT_PERCENT_TAG
209        )
210    }
211
212    /// Returns true if the min track sizing function is `MinContent` or `MaxContent`, else false.
213    #[inline(always)]
214    pub fn is_min_or_max_content(&self) -> bool {
215        matches!(self.tag(), Self::MIN_CONTENT_TAG | Self::MAX_CONTENT_TAG)
216    }
217
218    /// Returns true if the value is auto, min-content, max-content, or fit-content(...)
219    #[inline(always)]
220    pub const fn is_intrinsic(self) -> bool {
221        matches!(
222            self.tag(),
223            Self::AUTO_TAG
224                | Self::MIN_CONTENT_TAG
225                | Self::MAX_CONTENT_TAG
226                | Self::FIT_CONTENT_PX_TAG
227                | Self::FIT_CONTENT_PERCENT_TAG
228        )
229    }
230
231    /// Returns true if the value is and fr value
232    #[inline(always)]
233    pub const fn is_fr(self) -> bool {
234        self.tag() == Self::FR_TAG
235    }
236
237    /// Whether the track sizing functions depends on the size of the parent node
238    #[inline(always)]
239    pub const fn uses_percentage(self) -> bool {
240        // TODO: handle calc() values
241        matches!(self.tag(), CompactLength::PERCENT_TAG | CompactLength::FIT_CONTENT_PERCENT_TAG) || self.is_calc()
242    }
243
244    /// Resolve percentage values against the passed parent_size, returning Some(value)
245    /// Non-percentage values always return None.
246    #[inline(always)]
247    pub fn resolved_percentage_size(self, parent_size: f32, calc_resolver: impl Fn(u64, f32) -> f32) -> Option<f32> {
248        match self.tag() {
249            CompactLength::PERCENT_TAG => Some(self.value() * parent_size),
250            _ if self.is_calc() => Some(calc_resolver(self.0, parent_size)),
251            _ => None,
252        }
253    }
254}
255
256impl TaffyZero for CompactLength {
257    const ZERO: Self = Self::length(0.0);
258}
259impl TaffyAuto for CompactLength {
260    const AUTO: Self = Self::auto();
261}
262impl TaffyMinContent for CompactLength {
263    const MIN_CONTENT: Self = Self::min_content();
264}
265impl TaffyMaxContent for CompactLength {
266    const MAX_CONTENT: Self = Self::max_content();
267}
268impl FromLength for CompactLength {
269    fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
270        Self::length(value.into())
271    }
272}
273impl FromPercent for CompactLength {
274    fn from_percent<Input: Into<f32> + Copy>(value: Input) -> Self {
275        Self::percent(value.into())
276    }
277}
278impl FromFr for CompactLength {
279    fn from_fr<Input: Into<f32> + Copy>(value: Input) -> Self {
280        Self::fr(value.into())
281    }
282}
283impl TaffyFitContent for CompactLength {
284    fn fit_content(lp: LengthPercentage) -> Self {
285        let value = lp.0.value();
286        match lp.0.tag() {
287            Self::LENGTH_TAG => Self::fit_content_px(value),
288            Self::PERCENT_TAG => Self::fit_content_percent(value),
289            _ => unreachable!(),
290        }
291    }
292}
293
294/// Note: these two functions are copied directly from the std (core) library. But by duplicating them
295/// here we can reduce MSRV from 1.83 all the way down to 1.65 while retaining const constructors.
296mod compat {
297    #![allow(unsafe_code)]
298
299    /// Raw transmutation from `f32` to `u32`.
300    pub const fn f32_to_bits(val: f32) -> u32 {
301        // SAFETY: `u32` is a plain old datatype so we can always transmute to it.
302        unsafe { core::mem::transmute(val) }
303    }
304    /// Raw transmutation from `u32` to `f32`.
305    pub const fn f32_from_bits(v: u32) -> f32 {
306        // SAFETY: `u32` is a plain old datatype so we can always transmute from it.
307        unsafe { core::mem::transmute(v) }
308    }
309}