1use astrelis_core::math::Vec2;
7use std::fmt;
8
9#[derive(Debug, Clone, Copy, PartialEq)]
23pub enum Length {
24 Px(f32),
26 Percent(f32),
28 Auto,
30 Vw(f32),
32 Vh(f32),
34 Vmin(f32),
36 Vmax(f32),
38}
39
40impl Length {
41 pub fn px(value: f32) -> Self {
43 Self::Px(value)
44 }
45
46 pub fn percent(value: f32) -> Self {
48 Self::Percent(value)
49 }
50
51 pub fn auto() -> Self {
53 Self::Auto
54 }
55
56 pub fn vw(value: f32) -> Self {
58 Self::Vw(value)
59 }
60
61 pub fn vh(value: f32) -> Self {
63 Self::Vh(value)
64 }
65
66 pub fn vmin(value: f32) -> Self {
68 Self::Vmin(value)
69 }
70
71 pub fn vmax(value: f32) -> Self {
73 Self::Vmax(value)
74 }
75
76 pub fn resolve(self, viewport_size: Vec2) -> Self {
98 match self {
99 Length::Vw(v) => Length::Px(v * viewport_size.x / 100.0),
100 Length::Vh(v) => Length::Px(v * viewport_size.y / 100.0),
101 Length::Vmin(v) => {
102 let min = viewport_size.x.min(viewport_size.y);
103 Length::Px(v * min / 100.0)
104 }
105 Length::Vmax(v) => {
106 let max = viewport_size.x.max(viewport_size.y);
107 Length::Px(v * max / 100.0)
108 }
109 other => other, }
111 }
112
113 pub fn to_dimension(self) -> taffy::Dimension {
119 match self {
120 Length::Px(v) => taffy::Dimension::Length(v),
121 Length::Percent(v) => taffy::Dimension::Percent(v / 100.0),
122 Length::Auto => taffy::Dimension::Auto,
123 Length::Vw(_) | Length::Vh(_) | Length::Vmin(_) | Length::Vmax(_) => {
124 panic!(
125 "Viewport-relative units must be resolved to pixels before converting to Taffy dimension. \
126 Call .resolve(viewport_size) first."
127 );
128 }
129 }
130 }
131
132 pub fn from_dimension(dim: taffy::Dimension) -> Self {
134 match dim {
135 taffy::Dimension::Length(v) => Length::Px(v),
136 taffy::Dimension::Percent(v) => Length::Percent(v * 100.0),
137 taffy::Dimension::Auto => Length::Auto,
138 }
139 }
140
141 pub fn is_px(&self) -> bool {
143 matches!(self, Length::Px(_))
144 }
145
146 pub fn is_percent(&self) -> bool {
148 matches!(self, Length::Percent(_))
149 }
150
151 pub fn is_auto(&self) -> bool {
153 matches!(self, Length::Auto)
154 }
155
156 pub fn is_viewport(&self) -> bool {
158 matches!(
159 self,
160 Length::Vw(_) | Length::Vh(_) | Length::Vmin(_) | Length::Vmax(_)
161 )
162 }
163}
164
165impl From<f32> for Length {
166 fn from(value: f32) -> Self {
167 Length::Px(value)
168 }
169}
170
171impl From<Length> for taffy::Dimension {
172 fn from(length: Length) -> Self {
173 length.to_dimension()
174 }
175}
176
177impl fmt::Display for Length {
178 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
179 match self {
180 Length::Px(v) => write!(f, "{}px", v),
181 Length::Percent(v) => write!(f, "{}%", v),
182 Length::Auto => write!(f, "auto"),
183 Length::Vw(v) => write!(f, "{}vw", v),
184 Length::Vh(v) => write!(f, "{}vh", v),
185 Length::Vmin(v) => write!(f, "{}vmin", v),
186 Length::Vmax(v) => write!(f, "{}vmax", v),
187 }
188 }
189}
190
191#[derive(Debug, Clone, Copy, PartialEq)]
195pub enum LengthAuto {
196 Px(f32),
198 Percent(f32),
200 Auto,
202 Vw(f32),
204 Vh(f32),
206 Vmin(f32),
208 Vmax(f32),
210}
211
212impl LengthAuto {
213 pub fn px(value: f32) -> Self {
215 Self::Px(value)
216 }
217
218 pub fn percent(value: f32) -> Self {
220 Self::Percent(value)
221 }
222
223 pub fn auto() -> Self {
225 Self::Auto
226 }
227
228 pub fn vw(value: f32) -> Self {
230 Self::Vw(value)
231 }
232
233 pub fn vh(value: f32) -> Self {
235 Self::Vh(value)
236 }
237
238 pub fn vmin(value: f32) -> Self {
240 Self::Vmin(value)
241 }
242
243 pub fn vmax(value: f32) -> Self {
245 Self::Vmax(value)
246 }
247
248 pub fn resolve(self, viewport_size: Vec2) -> Self {
250 match self {
251 LengthAuto::Vw(v) => LengthAuto::Px(v * viewport_size.x / 100.0),
252 LengthAuto::Vh(v) => LengthAuto::Px(v * viewport_size.y / 100.0),
253 LengthAuto::Vmin(v) => {
254 let min = viewport_size.x.min(viewport_size.y);
255 LengthAuto::Px(v * min / 100.0)
256 }
257 LengthAuto::Vmax(v) => {
258 let max = viewport_size.x.max(viewport_size.y);
259 LengthAuto::Px(v * max / 100.0)
260 }
261 other => other,
262 }
263 }
264
265 pub fn to_length_percentage_auto(self) -> taffy::LengthPercentageAuto {
271 match self {
272 LengthAuto::Px(v) => taffy::LengthPercentageAuto::Length(v),
273 LengthAuto::Percent(v) => taffy::LengthPercentageAuto::Percent(v / 100.0),
274 LengthAuto::Auto => taffy::LengthPercentageAuto::Auto,
275 LengthAuto::Vw(_) | LengthAuto::Vh(_) | LengthAuto::Vmin(_) | LengthAuto::Vmax(_) => {
276 panic!(
277 "Viewport-relative units must be resolved to pixels before converting to Taffy dimension. \
278 Call .resolve(viewport_size) first."
279 );
280 }
281 }
282 }
283
284 pub fn from_length_percentage_auto(lpa: taffy::LengthPercentageAuto) -> Self {
286 match lpa {
287 taffy::LengthPercentageAuto::Length(v) => LengthAuto::Px(v),
288 taffy::LengthPercentageAuto::Percent(v) => LengthAuto::Percent(v * 100.0),
289 taffy::LengthPercentageAuto::Auto => LengthAuto::Auto,
290 }
291 }
292}
293
294impl From<f32> for LengthAuto {
295 fn from(value: f32) -> Self {
296 LengthAuto::Px(value)
297 }
298}
299
300impl From<Length> for LengthAuto {
301 fn from(length: Length) -> Self {
302 match length {
303 Length::Px(v) => LengthAuto::Px(v),
304 Length::Percent(v) => LengthAuto::Percent(v),
305 Length::Auto => LengthAuto::Auto,
306 Length::Vw(v) => LengthAuto::Vw(v),
307 Length::Vh(v) => LengthAuto::Vh(v),
308 Length::Vmin(v) => LengthAuto::Vmin(v),
309 Length::Vmax(v) => LengthAuto::Vmax(v),
310 }
311 }
312}
313
314impl From<LengthAuto> for taffy::LengthPercentageAuto {
315 fn from(length: LengthAuto) -> Self {
316 length.to_length_percentage_auto()
317 }
318}
319
320impl fmt::Display for LengthAuto {
321 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
322 match self {
323 LengthAuto::Px(v) => write!(f, "{}px", v),
324 LengthAuto::Percent(v) => write!(f, "{}%", v),
325 LengthAuto::Auto => write!(f, "auto"),
326 LengthAuto::Vw(v) => write!(f, "{}vw", v),
327 LengthAuto::Vh(v) => write!(f, "{}vh", v),
328 LengthAuto::Vmin(v) => write!(f, "{}vmin", v),
329 LengthAuto::Vmax(v) => write!(f, "{}vmax", v),
330 }
331 }
332}
333
334#[derive(Debug, Clone, Copy, PartialEq)]
338pub enum LengthPercentage {
339 Px(f32),
341 Percent(f32),
343 Vw(f32),
345 Vh(f32),
347 Vmin(f32),
349 Vmax(f32),
351}
352
353impl LengthPercentage {
354 pub fn px(value: f32) -> Self {
356 Self::Px(value)
357 }
358
359 pub fn percent(value: f32) -> Self {
361 Self::Percent(value)
362 }
363
364 pub fn vw(value: f32) -> Self {
366 Self::Vw(value)
367 }
368
369 pub fn vh(value: f32) -> Self {
371 Self::Vh(value)
372 }
373
374 pub fn vmin(value: f32) -> Self {
376 Self::Vmin(value)
377 }
378
379 pub fn vmax(value: f32) -> Self {
381 Self::Vmax(value)
382 }
383
384 pub fn resolve(self, viewport_size: Vec2) -> Self {
386 match self {
387 LengthPercentage::Vw(v) => LengthPercentage::Px(v * viewport_size.x / 100.0),
388 LengthPercentage::Vh(v) => LengthPercentage::Px(v * viewport_size.y / 100.0),
389 LengthPercentage::Vmin(v) => {
390 let min = viewport_size.x.min(viewport_size.y);
391 LengthPercentage::Px(v * min / 100.0)
392 }
393 LengthPercentage::Vmax(v) => {
394 let max = viewport_size.x.max(viewport_size.y);
395 LengthPercentage::Px(v * max / 100.0)
396 }
397 other => other,
398 }
399 }
400
401 pub fn to_length_percentage(self) -> taffy::LengthPercentage {
407 match self {
408 LengthPercentage::Px(v) => taffy::LengthPercentage::Length(v),
409 LengthPercentage::Percent(v) => taffy::LengthPercentage::Percent(v / 100.0),
410 LengthPercentage::Vw(_)
411 | LengthPercentage::Vh(_)
412 | LengthPercentage::Vmin(_)
413 | LengthPercentage::Vmax(_) => {
414 panic!(
415 "Viewport-relative units must be resolved to pixels before converting to Taffy dimension. \
416 Call .resolve(viewport_size) first."
417 );
418 }
419 }
420 }
421
422 pub fn from_length_percentage(lp: taffy::LengthPercentage) -> Self {
424 match lp {
425 taffy::LengthPercentage::Length(v) => LengthPercentage::Px(v),
426 taffy::LengthPercentage::Percent(v) => LengthPercentage::Percent(v * 100.0),
427 }
428 }
429}
430
431impl From<f32> for LengthPercentage {
432 fn from(value: f32) -> Self {
433 LengthPercentage::Px(value)
434 }
435}
436
437impl From<LengthPercentage> for taffy::LengthPercentage {
438 fn from(length: LengthPercentage) -> Self {
439 length.to_length_percentage()
440 }
441}
442
443impl fmt::Display for LengthPercentage {
444 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
445 match self {
446 LengthPercentage::Px(v) => write!(f, "{}px", v),
447 LengthPercentage::Percent(v) => write!(f, "{}%", v),
448 LengthPercentage::Vw(v) => write!(f, "{}vw", v),
449 LengthPercentage::Vh(v) => write!(f, "{}vh", v),
450 LengthPercentage::Vmin(v) => write!(f, "{}vmin", v),
451 LengthPercentage::Vmax(v) => write!(f, "{}vmax", v),
452 }
453 }
454}
455
456pub fn length(value: f32) -> taffy::Dimension {
458 taffy::Dimension::Length(value)
459}
460
461pub fn percent(value: f32) -> taffy::Dimension {
463 taffy::Dimension::Percent(value / 100.0)
464}
465
466pub fn auto() -> taffy::Dimension {
468 taffy::Dimension::Auto
469}
470
471pub fn vw(value: f32) -> Length {
480 Length::Vw(value)
481}
482
483pub fn vh(value: f32) -> Length {
492 Length::Vh(value)
493}
494
495pub fn vmin(value: f32) -> Length {
504 Length::Vmin(value)
505}
506
507pub fn vmax(value: f32) -> Length {
516 Length::Vmax(value)
517}
518
519#[cfg(test)]
520mod tests {
521 use super::*;
522
523 #[test]
524 fn test_length_px() {
525 let len = Length::Px(100.0);
526 assert!(len.is_px());
527 assert!(!len.is_percent());
528 assert!(!len.is_auto());
529 assert_eq!(len.to_string(), "100px");
530 }
531
532 #[test]
533 fn test_length_percent() {
534 let len = Length::Percent(50.0);
535 assert!(!len.is_px());
536 assert!(len.is_percent());
537 assert!(!len.is_auto());
538 assert_eq!(len.to_string(), "50%");
539 }
540
541 #[test]
542 fn test_length_auto() {
543 let len = Length::Auto;
544 assert!(!len.is_px());
545 assert!(!len.is_percent());
546 assert!(len.is_auto());
547 assert_eq!(len.to_string(), "auto");
548 }
549
550 #[test]
551 fn test_length_from_f32() {
552 let len: Length = 100.0.into();
553 assert_eq!(len, Length::Px(100.0));
554 }
555
556 #[test]
557 fn test_length_to_dimension() {
558 let len = Length::Px(100.0);
559 let dim = len.to_dimension();
560 assert!(matches!(dim, taffy::Dimension::Length(100.0)));
561
562 let len = Length::Percent(50.0);
563 let dim = len.to_dimension();
564 assert!(matches!(dim, taffy::Dimension::Percent(0.5)));
565
566 let len = Length::Auto;
567 let dim = len.to_dimension();
568 assert!(matches!(dim, taffy::Dimension::Auto));
569 }
570
571 #[test]
572 fn test_length_percentage() {
573 let lp = LengthPercentage::Px(100.0);
574 assert_eq!(lp.to_string(), "100px");
575
576 let lp = LengthPercentage::Percent(50.0);
577 assert_eq!(lp.to_string(), "50%");
578 }
579
580 #[test]
581 fn test_length_auto_conversion() {
582 let la = LengthAuto::Px(100.0);
583 assert_eq!(la.to_string(), "100px");
584
585 let len: LengthAuto = Length::Percent(50.0).into();
586 assert_eq!(len, LengthAuto::Percent(50.0));
587 }
588
589 #[test]
590 fn test_helper_functions() {
591 let dim = length(100.0);
592 assert!(matches!(dim, taffy::Dimension::Length(100.0)));
593
594 let dim = percent(50.0);
595 assert!(matches!(dim, taffy::Dimension::Percent(0.5)));
596
597 let dim = auto();
598 assert!(matches!(dim, taffy::Dimension::Auto));
599 }
600}