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