• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Style types for representing lengths / sizes
2 
3 use crate::geometry::{Rect, Size};
4 use crate::style_helpers::{FromLength, FromPercent, TaffyAuto, TaffyMaxContent, TaffyMinContent, TaffyZero};
5 use crate::util::sys::abs;
6 
7 /// A unit of linear measurement
8 ///
9 /// This is commonly combined with [`Rect`], [`Point`](crate::geometry::Point) and [`Size<T>`].
10 #[derive(Copy, Clone, PartialEq, Debug)]
11 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
12 pub enum LengthPercentage {
13     /// An absolute length in some abstract units. Users of Taffy may define what they correspond
14     /// to in their application (pixels, logical pixels, mm, etc) as they see fit.
15     Length(f32),
16     /// A percentage length relative to the size of the containing block.
17     ///
18     /// **NOTE: percentages are represented as a f32 value in the range [0.0, 1.0] NOT the range [0.0, 100.0]**
19     Percent(f32),
20 }
21 impl TaffyZero for LengthPercentage {
22     const ZERO: Self = Self::Length(0.0);
23 }
24 impl FromLength for LengthPercentage {
from_length<Input: Into<f32> + Copy>(value: Input) -> Self25     fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
26         Self::Length(value.into())
27     }
28 }
29 impl FromPercent for LengthPercentage {
from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self30     fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
31         Self::Percent(percent.into())
32     }
33 }
34 
35 /// A unit of linear measurement
36 ///
37 /// This is commonly combined with [`Rect`], [`Point`](crate::geometry::Point) and [`Size<T>`].
38 #[derive(Copy, Clone, PartialEq, Debug)]
39 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
40 pub enum LengthPercentageAuto {
41     /// An absolute length in some abstract units. Users of Taffy may define what they correspond
42     /// to in their application (pixels, logical pixels, mm, etc) as they see fit.
43     Length(f32),
44     /// A percentage length relative to the size of the containing block.
45     ///
46     /// **NOTE: percentages are represented as a f32 value in the range [0.0, 1.0] NOT the range [0.0, 100.0]**
47     Percent(f32),
48     /// The dimension should be automatically computed
49     Auto,
50 }
51 impl TaffyZero for LengthPercentageAuto {
52     const ZERO: Self = Self::Length(0.0);
53 }
54 impl TaffyAuto for LengthPercentageAuto {
55     const AUTO: Self = Self::Auto;
56 }
57 impl FromLength for LengthPercentageAuto {
from_length<Input: Into<f32> + Copy>(value: Input) -> Self58     fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
59         Self::Length(value.into())
60     }
61 }
62 impl FromPercent for LengthPercentageAuto {
from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self63     fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
64         Self::Percent(percent.into())
65     }
66 }
67 
68 impl From<LengthPercentage> for LengthPercentageAuto {
from(input: LengthPercentage) -> Self69     fn from(input: LengthPercentage) -> Self {
70         match input {
71             LengthPercentage::Length(value) => Self::Length(value),
72             LengthPercentage::Percent(value) => Self::Percent(value),
73         }
74     }
75 }
76 
77 impl LengthPercentageAuto {
78     /// Returns:
79     ///   - Some(length) for Length variants
80     ///   - Some(resolved) using the provided context for Percent variants
81     ///   - None for Auto variants
82     #[inline(always)]
resolve_to_option(self, context: f32) -> Option<f32>83     pub fn resolve_to_option(self, context: f32) -> Option<f32> {
84         match self {
85             Self::Length(length) => Some(length),
86             Self::Percent(percent) => Some(context * percent),
87             Self::Auto => None,
88         }
89     }
90 
91     /// Returns true if value is LengthPercentageAuto::Auto
92     #[inline(always)]
is_auto(self) -> bool93     pub fn is_auto(self) -> bool {
94         self == Self::Auto
95     }
96 }
97 
98 /// A unit of linear measurement
99 ///
100 /// This is commonly combined with [`Rect`], [`Point`](crate::geometry::Point) and [`Size<T>`].
101 #[derive(Copy, Clone, PartialEq, Debug)]
102 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
103 pub enum Dimension {
104     /// An absolute length in some abstract units. Users of Taffy may define what they correspond
105     /// to in their application (pixels, logical pixels, mm, etc) as they see fit.
106     Length(f32),
107     /// A percentage length relative to the size of the containing block.
108     ///
109     /// **NOTE: percentages are represented as a f32 value in the range [0.0, 1.0] NOT the range [0.0, 100.0]**
110     Percent(f32),
111     /// The dimension should be automatically computed
112     Auto,
113 }
114 impl TaffyZero for Dimension {
115     const ZERO: Self = Self::Length(0.0);
116 }
117 impl TaffyAuto for Dimension {
118     const AUTO: Self = Self::Auto;
119 }
120 impl FromLength for Dimension {
from_length<Input: Into<f32> + Copy>(value: Input) -> Self121     fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
122         Self::Length(value.into())
123     }
124 }
125 impl FromPercent for Dimension {
from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self126     fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
127         Self::Percent(percent.into())
128     }
129 }
130 
131 impl From<LengthPercentage> for Dimension {
from(input: LengthPercentage) -> Self132     fn from(input: LengthPercentage) -> Self {
133         match input {
134             LengthPercentage::Length(value) => Self::Length(value),
135             LengthPercentage::Percent(value) => Self::Percent(value),
136         }
137     }
138 }
139 
140 impl From<LengthPercentageAuto> for Dimension {
from(input: LengthPercentageAuto) -> Self141     fn from(input: LengthPercentageAuto) -> Self {
142         match input {
143             LengthPercentageAuto::Length(value) => Self::Length(value),
144             LengthPercentageAuto::Percent(value) => Self::Percent(value),
145             LengthPercentageAuto::Auto => Self::Auto,
146         }
147     }
148 }
149 
150 impl Dimension {
151     /// Get Length value if value is Length variant
152     #[cfg(feature = "grid")]
into_option(self) -> Option<f32>153     pub fn into_option(self) -> Option<f32> {
154         match self {
155             Dimension::Length(value) => Some(value),
156             _ => None,
157         }
158     }
159 }
160 
161 impl Rect<Dimension> {
162     /// Create a new Rect with [`Dimension::Length`]
163     #[must_use]
from_length(start: f32, end: f32, top: f32, bottom: f32) -> Self164     pub const fn from_length(start: f32, end: f32, top: f32, bottom: f32) -> Self {
165         Rect {
166             left: Dimension::Length(start),
167             right: Dimension::Length(end),
168             top: Dimension::Length(top),
169             bottom: Dimension::Length(bottom),
170         }
171     }
172 
173     /// Create a new Rect with [`Dimension::Percent`]
174     #[must_use]
from_percent(start: f32, end: f32, top: f32, bottom: f32) -> Self175     pub const fn from_percent(start: f32, end: f32, top: f32, bottom: f32) -> Self {
176         Rect {
177             left: Dimension::Percent(start),
178             right: Dimension::Percent(end),
179             top: Dimension::Percent(top),
180             bottom: Dimension::Percent(bottom),
181         }
182     }
183 }
184 
185 /// The amount of space available to a node in a given axis
186 /// <https://www.w3.org/TR/css-sizing-3/#available>
187 #[derive(Copy, Clone, Debug, PartialEq)]
188 #[cfg_attr(feature = "serde", derive(Serialize))]
189 pub enum AvailableSpace {
190     /// The amount of space available is the specified number of pixels
191     Definite(f32),
192     /// The amount of space available is indefinite and the node should be laid out under a min-content constraint
193     MinContent,
194     /// The amount of space available is indefinite and the node should be laid out under a max-content constraint
195     MaxContent,
196 }
197 impl TaffyZero for AvailableSpace {
198     const ZERO: Self = Self::Definite(0.0);
199 }
200 impl TaffyMaxContent for AvailableSpace {
201     const MAX_CONTENT: Self = Self::MaxContent;
202 }
203 impl TaffyMinContent for AvailableSpace {
204     const MIN_CONTENT: Self = Self::MinContent;
205 }
206 impl FromLength for AvailableSpace {
from_length<Input: Into<f32> + Copy>(value: Input) -> Self207     fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
208         Self::Definite(value.into())
209     }
210 }
211 
212 impl AvailableSpace {
213     /// Returns true for definite values, else false
is_definite(self) -> bool214     pub fn is_definite(self) -> bool {
215         matches!(self, AvailableSpace::Definite(_))
216     }
217 
218     /// Convert to Option
219     /// Definite values become Some(value). Constraints become None.
into_option(self) -> Option<f32>220     pub fn into_option(self) -> Option<f32> {
221         match self {
222             AvailableSpace::Definite(value) => Some(value),
223             _ => None,
224         }
225     }
226 
227     /// Return the definite value or a default value
unwrap_or(self, default: f32) -> f32228     pub fn unwrap_or(self, default: f32) -> f32 {
229         self.into_option().unwrap_or(default)
230     }
231 
232     /// Return the definite value. Panic is the value is not definite.
233     #[track_caller]
unwrap(self) -> f32234     pub fn unwrap(self) -> f32 {
235         self.into_option().unwrap()
236     }
237 
238     /// Return self if definite or a default value
or(self, default: AvailableSpace) -> AvailableSpace239     pub fn or(self, default: AvailableSpace) -> AvailableSpace {
240         match self {
241             AvailableSpace::Definite(_) => self,
242             _ => default,
243         }
244     }
245 
246     /// Return self if definite or a the result of the default value callback
or_else(self, default_cb: impl FnOnce() -> AvailableSpace) -> AvailableSpace247     pub fn or_else(self, default_cb: impl FnOnce() -> AvailableSpace) -> AvailableSpace {
248         match self {
249             AvailableSpace::Definite(_) => self,
250             _ => default_cb(),
251         }
252     }
253 
254     /// Return the definite value or the result of the default value callback
unwrap_or_else(self, default_cb: impl FnOnce() -> f32) -> f32255     pub fn unwrap_or_else(self, default_cb: impl FnOnce() -> f32) -> f32 {
256         self.into_option().unwrap_or_else(default_cb)
257     }
258 
259     /// If passed value is Some then return AvailableSpace::Definite containing that value, else return self
maybe_set(self, value: Option<f32>) -> AvailableSpace260     pub fn maybe_set(self, value: Option<f32>) -> AvailableSpace {
261         match value {
262             Some(value) => AvailableSpace::Definite(value),
263             None => self,
264         }
265     }
266 
267     /// If passed value is Some then return AvailableSpace::Definite containing that value, else return self
map_definite_value(self, map_function: impl FnOnce(f32) -> f32) -> AvailableSpace268     pub fn map_definite_value(self, map_function: impl FnOnce(f32) -> f32) -> AvailableSpace {
269         match self {
270             AvailableSpace::Definite(value) => AvailableSpace::Definite(map_function(value)),
271             _ => self,
272         }
273     }
274 
275     /// Compute free_space given the passed used_space
compute_free_space(&self, used_space: f32) -> f32276     pub fn compute_free_space(&self, used_space: f32) -> f32 {
277         match self {
278             AvailableSpace::MaxContent => f32::INFINITY,
279             AvailableSpace::MinContent => 0.0,
280             AvailableSpace::Definite(available_space) => available_space - used_space,
281         }
282     }
283 
284     /// Compare equality with another AvailableSpace, treating definite values
285     /// that are within f32::EPSILON of each other as equal
is_roughly_equal(self, other: AvailableSpace) -> bool286     pub fn is_roughly_equal(self, other: AvailableSpace) -> bool {
287         use AvailableSpace::*;
288         match (self, other) {
289             (Definite(a), Definite(b)) => abs(a - b) < f32::EPSILON,
290             (MinContent, MinContent) => true,
291             (MaxContent, MaxContent) => true,
292             _ => false,
293         }
294     }
295 }
296 
297 impl From<f32> for AvailableSpace {
from(value: f32) -> Self298     fn from(value: f32) -> Self {
299         Self::Definite(value)
300     }
301 }
302 
303 impl From<Option<f32>> for AvailableSpace {
from(option: Option<f32>) -> Self304     fn from(option: Option<f32>) -> Self {
305         match option {
306             Some(value) => Self::Definite(value),
307             None => Self::MaxContent,
308         }
309     }
310 }
311 
312 impl Size<AvailableSpace> {
313     /// Convert `Size<AvailableSpace>` into `Size<Option<f32>>`
into_options(self) -> Size<Option<f32>>314     pub fn into_options(self) -> Size<Option<f32>> {
315         Size { width: self.width.into_option(), height: self.height.into_option() }
316     }
317 
318     /// If passed value is Some then return AvailableSpace::Definite containing that value, else return self
maybe_set(self, value: Size<Option<f32>>) -> Size<AvailableSpace>319     pub fn maybe_set(self, value: Size<Option<f32>>) -> Size<AvailableSpace> {
320         Size { width: self.width.maybe_set(value.width), height: self.height.maybe_set(value.height) }
321     }
322 }
323