Skip to main content

float_pigment_layout/
types.rs

1use core::{fmt::Display, ops::Deref};
2
3use alloc::{string::String, vec::Vec};
4use euclid::UnknownUnit;
5use float_pigment_css::typing::{Direction, WritingMode};
6
7use crate::LayoutTreeNode;
8pub(crate) use float_pigment_css::length_num::{length_sum, LengthNum};
9
10/// Position with size.
11pub type Rect<L> = euclid::Rect<L, UnknownUnit>;
12
13/// Size.
14pub type Size<L> = euclid::Size2D<L, UnknownUnit>;
15
16/// Size, but each value can be `None` for undetermined.
17pub type OptionSize<L> = euclid::Size2D<OptionNum<L>, UnknownUnit>;
18
19/// 2D Vector.
20pub type Vector<L> = euclid::Vector2D<L, euclid::UnknownUnit>;
21
22/// Position.
23pub type Point<L> = euclid::Point2D<L, euclid::UnknownUnit>;
24
25pub(crate) trait PointGetter<T> {
26    fn main_axis(&self, dir: AxisDirection) -> T;
27    fn cross_axis(&self, dir: AxisDirection) -> T;
28}
29
30impl<L: LengthNum> PointGetter<L> for Point<L> {
31    fn main_axis(&self, dir: AxisDirection) -> L {
32        match dir {
33            AxisDirection::Horizontal => self.x,
34            AxisDirection::Vertical => self.y,
35        }
36    }
37    fn cross_axis(&self, dir: AxisDirection) -> L {
38        match dir {
39            AxisDirection::Horizontal => self.y,
40            AxisDirection::Vertical => self.x,
41        }
42    }
43}
44
45pub(crate) trait SizeGetter<T: Copy> {
46    fn main_size(&self, dir: AxisDirection) -> T;
47    fn cross_size(&self, dir: AxisDirection) -> T;
48}
49
50pub(crate) trait SizeSetter<T> {
51    fn set_main_size(&mut self, dir: AxisDirection, value: T);
52    fn set_cross_size(&mut self, dir: AxisDirection, value: T);
53}
54
55impl<T: Copy> SizeGetter<T> for euclid::Size2D<T, UnknownUnit> {
56    #[inline]
57    fn main_size(&self, dir: AxisDirection) -> T {
58        match dir {
59            AxisDirection::Horizontal => self.width,
60            AxisDirection::Vertical => self.height,
61        }
62    }
63
64    #[inline]
65    fn cross_size(&self, dir: AxisDirection) -> T {
66        match dir {
67            AxisDirection::Horizontal => self.height,
68            AxisDirection::Vertical => self.width,
69        }
70    }
71}
72
73impl<T> SizeSetter<T> for euclid::Size2D<T, UnknownUnit> {
74    #[inline]
75    fn set_main_size(&mut self, dir: AxisDirection, value: T) {
76        match dir {
77            AxisDirection::Horizontal => self.width = value,
78            AxisDirection::Vertical => self.height = value,
79        }
80    }
81
82    #[inline]
83    fn set_cross_size(&mut self, dir: AxisDirection, value: T) {
84        match dir {
85            AxisDirection::Horizontal => self.height = value,
86            AxisDirection::Vertical => self.width = value,
87        }
88    }
89}
90
91pub(crate) trait SizeProxy<T> {
92    fn new_with_dir(dir: AxisDirection, main_size: T, cross_size: T) -> Self;
93}
94
95impl<T> SizeProxy<T> for euclid::Size2D<T, UnknownUnit> {
96    #[inline(always)]
97    fn new_with_dir(dir: AxisDirection, main_size: T, cross_size: T) -> Self {
98        let (width, height) = match dir {
99            AxisDirection::Horizontal => (main_size, cross_size),
100            AxisDirection::Vertical => (cross_size, main_size),
101        };
102        Self::new(width, height)
103    }
104}
105
106pub(crate) trait OrZero<L: LengthNum>: Sized {
107    fn or_zero(&self) -> Size<L>;
108}
109
110impl<L: LengthNum> OrZero<L> for OptionSize<L> {
111    #[inline(always)]
112    fn or_zero(&self) -> Size<L> {
113        Size::new(self.width.or_zero(), self.height.or_zero())
114    }
115}
116
117pub(crate) trait VectorGetter<T> {
118    #[allow(unused)]
119    fn main_axis(&self, dir: AxisDirection) -> T;
120    fn cross_axis(&self, dir: AxisDirection) -> T;
121}
122
123impl<L: LengthNum> VectorGetter<L> for Vector<L> {
124    #[inline]
125    fn main_axis(&self, dir: AxisDirection) -> L {
126        match dir {
127            AxisDirection::Horizontal => self.x,
128            AxisDirection::Vertical => self.y,
129        }
130    }
131    #[inline]
132    fn cross_axis(&self, dir: AxisDirection) -> L {
133        match dir {
134            AxisDirection::Horizontal => self.y,
135            AxisDirection::Vertical => self.x,
136        }
137    }
138}
139
140pub(crate) trait VectorSetter<T> {
141    fn set_main_axis(&mut self, dir: AxisDirection, value: T);
142    fn set_cross_axis(&mut self, dir: AxisDirection, value: T);
143}
144
145impl<L: LengthNum> VectorSetter<L> for Vector<L> {
146    #[inline(always)]
147    fn set_main_axis(&mut self, dir: AxisDirection, value: L) {
148        match dir {
149            AxisDirection::Horizontal => self.x = value,
150            AxisDirection::Vertical => self.y = value,
151        }
152    }
153    #[inline(always)]
154    fn set_cross_axis(&mut self, dir: AxisDirection, value: L) {
155        match dir {
156            AxisDirection::Horizontal => self.y = value,
157            AxisDirection::Vertical => self.x = value,
158        }
159    }
160}
161
162pub(crate) trait VectorProxy<T> {
163    fn new_with_dir(dir: AxisDirection, main_axis: T, cross_axis: T) -> Self;
164}
165
166impl<L: LengthNum> VectorProxy<L> for Vector<L> {
167    fn new_with_dir(dir: AxisDirection, main_axis: L, cross_axis: L) -> Self {
168        let (x, y) = match dir {
169            AxisDirection::Horizontal => (main_axis, cross_axis),
170            AxisDirection::Vertical => (cross_axis, main_axis),
171        };
172        Self::new(x, y)
173    }
174}
175
176/// A length type that can be undefined or auto.
177#[derive(Debug, Clone, Copy, PartialEq)]
178pub enum DefLength<L: LengthNum, T: PartialEq + Clone = i32> {
179    /// The length is undetermined.
180    Undefined,
181
182    /// The length is auto.
183    Auto,
184
185    /// A fixed value.
186    Points(L),
187
188    /// A ratio.
189    Percent(f32),
190
191    /// Custom length value.
192    ///
193    /// Will be resolved by `LayoutTreeNode::resolve_custom_length`.
194    Custom(T),
195}
196
197impl<L: LengthNum, T: PartialEq + Display + Clone> Display for DefLength<L, T> {
198    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
199        match self {
200            Self::Undefined => write!(f, "Undefined"),
201            Self::Auto => write!(f, "Auto"),
202            Self::Points(x) => write!(f, "Points({})", L::to_f32(*x)),
203            Self::Percent(x) => write!(f, "Percent({})", *x),
204            Self::Custom(x) => write!(f, "Custom({})", *x),
205        }
206    }
207}
208
209impl<L: LengthNum, T: PartialEq + Clone> Default for DefLength<L, T> {
210    fn default() -> Self {
211        Self::Undefined
212    }
213}
214
215impl<L: LengthNum, T: PartialEq + Clone> DefLength<L, T> {
216    pub(crate) fn resolve<N: LayoutTreeNode<Length = L, LengthCustom = T>>(
217        &self,
218        parent: OptionNum<L>,
219        node: &N,
220    ) -> OptionNum<L> {
221        match self {
222            Self::Undefined => OptionNum::none(),
223            Self::Auto => OptionNum::none(),
224            Self::Points(x) => OptionNum::some(*x),
225            Self::Percent(x) => parent * *x,
226            Self::Custom(x) => {
227                OptionNum::some(node.resolve_custom_length(x, parent.unwrap_or(L::zero())))
228            }
229        }
230    }
231
232    pub(crate) fn resolve_with_auto<N: LayoutTreeNode<Length = L, LengthCustom = T>>(
233        &self,
234        parent: OptionNum<L>,
235        node: &N,
236    ) -> OptionNum<L> {
237        match self {
238            Self::Undefined => OptionNum::some(L::zero()),
239            Self::Auto => OptionNum::none(),
240            Self::Points(x) => OptionNum::some(*x),
241            Self::Percent(x) => parent * *x,
242            Self::Custom(x) => {
243                OptionNum::some(node.resolve_custom_length(x, parent.unwrap_or(L::zero())))
244            }
245        }
246    }
247
248    pub(crate) fn resolve_num<N: LayoutTreeNode<Length = L, LengthCustom = T>>(
249        &self,
250        parent: L,
251        node: &N,
252    ) -> OptionNum<L> {
253        match self {
254            Self::Undefined => OptionNum::none(),
255            Self::Auto => OptionNum::none(),
256            Self::Points(x) => OptionNum::some(*x),
257            Self::Percent(x) => OptionNum::some(parent.mul_f32(*x)),
258            Self::Custom(x) => OptionNum::some(node.resolve_custom_length(x, parent)),
259        }
260    }
261}
262
263/// A number or undetermined.
264#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
265pub struct OptionNum<L>(Option<L>);
266
267impl<L: LengthNum> OptionNum<L> {
268    /// Convert to a hashable value.
269    pub fn to_hashable(&self) -> OptionNum<L::Hashable> {
270        match self.0 {
271            None => OptionNum(None),
272            Some(x) => OptionNum(Some(x.to_hashable())),
273        }
274    }
275
276    /// New undetermined value.
277    #[inline]
278    pub fn none() -> Self {
279        Self(None)
280    }
281
282    /// New number.
283    #[inline]
284    pub fn some(v: L) -> Self {
285        Self(Some(v))
286    }
287
288    /// New zero value.
289    #[inline]
290    pub fn zero() -> Self {
291        Self(Some(L::zero()))
292    }
293
294    /// Return `true` for undetermined value.
295    #[inline]
296    pub fn is_none(&self) -> bool {
297        self.0.is_none()
298    }
299
300    /// Return `true` for number.
301    #[inline]
302    pub fn is_some(&self) -> bool {
303        self.0.is_some()
304    }
305
306    /// Convert to a number with `Option`.
307    #[inline]
308    pub fn val(&self) -> Option<L> {
309        self.0
310    }
311
312    /// Unwrap to a number, default to `rhs`.
313    #[inline]
314    pub fn unwrap_or(self, rhs: L) -> L {
315        self.0.unwrap_or(rhs)
316    }
317
318    /// Unwrap to a number, default to `f()`.
319    #[inline]
320    pub fn unwrap_or_else(self, f: impl FnOnce() -> L) -> L {
321        self.0.unwrap_or_else(f)
322    }
323
324    /// If `self` is undetermined, return `rhs`.
325    #[inline]
326    pub fn or(self, rhs: Self) -> Self {
327        Self(self.0.or(rhs.0))
328    }
329
330    /// Unwrap to a number, default to zero.
331    #[inline]
332    pub fn or_zero(self) -> L {
333        self.0.unwrap_or_else(L::zero)
334    }
335
336    /// Map the number.
337    #[inline]
338    pub fn map(self, f: impl FnOnce(L) -> L) -> Self {
339        Self(self.0.map(f))
340    }
341}
342
343impl<L: LengthNum> From<Option<L>> for OptionNum<L> {
344    fn from(value: Option<L>) -> Self {
345        Self(value)
346    }
347}
348
349impl<L: LengthNum> core::ops::Add<L> for OptionNum<L> {
350    type Output = Self;
351
352    fn add(self, rhs: L) -> Self {
353        self.map(|x| x + rhs)
354    }
355}
356
357impl<L: LengthNum> core::ops::Sub<L> for OptionNum<L> {
358    type Output = Self;
359
360    fn sub(self, rhs: L) -> Self {
361        self.map(|x| x - rhs)
362    }
363}
364
365impl<L: LengthNum> core::ops::Mul<f32> for OptionNum<L> {
366    type Output = Self;
367
368    fn mul(self, rhs: f32) -> Self {
369        self.map(|x| x.mul_f32(rhs))
370    }
371}
372
373impl<L: LengthNum> core::ops::Mul<i32> for OptionNum<L> {
374    type Output = Self;
375
376    fn mul(self, rhs: i32) -> Self {
377        self.map(|x| x.mul_i32(rhs))
378    }
379}
380
381impl<L: LengthNum> core::ops::Add<Self> for OptionNum<L> {
382    type Output = Self;
383
384    fn add(self, rhs: Self) -> Self {
385        match rhs.val() {
386            Some(x) => self + x,
387            None => self,
388        }
389    }
390}
391
392impl<L: LengthNum> core::ops::Sub<Self> for OptionNum<L> {
393    type Output = Self;
394
395    fn sub(self, rhs: Self) -> Self {
396        match rhs.val() {
397            Some(x) => self - x,
398            None => self,
399        }
400    }
401}
402
403pub(crate) trait MinMax {
404    fn min(self, rhs: Self) -> Self;
405    fn max(self, rhs: Self) -> Self;
406}
407
408impl<L: LengthNum> MinMax for L {
409    fn min(self, rhs: Self) -> Self {
410        if self < rhs {
411            self
412        } else {
413            rhs
414        }
415    }
416
417    fn max(self, rhs: Self) -> Self {
418        if self > rhs {
419            self
420        } else {
421            rhs
422        }
423    }
424}
425
426pub(crate) trait MaybeMinMax<In, Out> {
427    fn maybe_min(self, rhs: In) -> Out;
428    fn maybe_max(self, rhs: In) -> Out;
429}
430
431impl<L: LengthNum> MaybeMinMax<OptionNum<L>, L> for L {
432    fn maybe_min(self, rhs: OptionNum<L>) -> L {
433        match rhs.val() {
434            None => self,
435            Some(x) => self.min(x),
436        }
437    }
438
439    fn maybe_max(self, rhs: OptionNum<L>) -> L {
440        match rhs.val() {
441            None => self,
442            Some(x) => self.max(x),
443        }
444    }
445}
446
447impl<L: LengthNum> MaybeMinMax<Self, Self> for OptionNum<L> {
448    fn maybe_min(self, rhs: Self) -> Self {
449        match self.val() {
450            None => OptionNum::none(),
451            Some(x) => match rhs.val() {
452                None => self,
453                Some(y) => OptionNum::some(x.min(y)),
454            },
455        }
456    }
457
458    fn maybe_max(self, rhs: Self) -> Self {
459        match self.val() {
460            None => OptionNum::none(),
461            Some(x) => match rhs.val() {
462                None => self,
463                Some(y) => OptionNum::some(x.max(y)),
464            },
465        }
466    }
467}
468
469impl<L: LengthNum> MaybeMinMax<L, Self> for OptionNum<L> {
470    fn maybe_min(self, rhs: L) -> OptionNum<L> {
471        match self.val() {
472            None => OptionNum::none(),
473            Some(x) => OptionNum::some(x.min(rhs)),
474        }
475    }
476
477    fn maybe_max(self, rhs: L) -> OptionNum<L> {
478        match self.val() {
479            None => OptionNum::none(),
480            Some(x) => OptionNum::some(x.max(rhs)),
481        }
482    }
483}
484
485impl<L: LengthNum> MaybeMinMax<OptionSize<L>, Size<L>> for Size<L> {
486    fn maybe_min(self, rhs: OptionSize<L>) -> Size<L> {
487        let width = match rhs.width.val() {
488            None => self.width,
489            Some(x) => self.width.min(x),
490        };
491        let height = match rhs.height.val() {
492            None => self.height,
493            Some(x) => self.height.min(x),
494        };
495        Size::new(width, height)
496    }
497
498    fn maybe_max(self, rhs: OptionSize<L>) -> Size<L> {
499        let width = match rhs.width.val() {
500            None => self.width,
501            Some(x) => self.width.max(x),
502        };
503        let height = match rhs.height.val() {
504            None => self.height,
505            Some(x) => self.height.max(x),
506        };
507        Size::new(width, height)
508    }
509}
510
511/// Four edge lengths.
512#[allow(missing_docs)]
513#[derive(Debug, Clone, Copy, PartialEq)]
514pub struct Edge<L: LengthNum> {
515    pub left: L,
516    pub right: L,
517    pub top: L,
518    pub bottom: L,
519}
520
521impl<L: LengthNum> Edge<L> {
522    /// New zero-length edges.
523    #[inline(always)]
524    pub fn zero() -> Self {
525        Self {
526            left: L::zero(),
527            right: L::zero(),
528            top: L::zero(),
529            bottom: L::zero(),
530        }
531    }
532
533    /// Get the sum of horizontal lengths.
534    #[inline(always)]
535    pub fn horizontal(&self) -> L {
536        self.left + self.right
537    }
538
539    /// Get the sum of vertical lengths.
540    #[inline(always)]
541    pub fn vertical(&self) -> L {
542        self.top + self.bottom
543    }
544
545    #[inline(always)]
546    pub(crate) fn main_axis_sum(&self, dir: AxisDirection) -> L {
547        match dir {
548            AxisDirection::Vertical => self.vertical(),
549            AxisDirection::Horizontal => self.horizontal(),
550        }
551    }
552
553    #[inline(always)]
554    pub(crate) fn cross_axis_sum(&self, dir: AxisDirection) -> L {
555        match dir {
556            AxisDirection::Vertical => self.horizontal(),
557            AxisDirection::Horizontal => self.vertical(),
558        }
559    }
560
561    #[inline(always)]
562    pub(crate) fn main_axis_start(&self, dir: AxisDirection, rev: AxisReverse) -> L {
563        match (dir, rev) {
564            (AxisDirection::Horizontal, AxisReverse::NotReversed) => self.left,
565            (AxisDirection::Horizontal, AxisReverse::Reversed) => self.right,
566            (AxisDirection::Vertical, AxisReverse::NotReversed) => self.top,
567            (AxisDirection::Vertical, AxisReverse::Reversed) => self.bottom,
568        }
569    }
570
571    #[allow(unused)]
572    #[inline(always)]
573    pub(crate) fn main_axis_end(&self, dir: AxisDirection, rev: AxisReverse) -> L {
574        match (dir, rev) {
575            (AxisDirection::Horizontal, AxisReverse::NotReversed) => self.right,
576            (AxisDirection::Horizontal, AxisReverse::Reversed) => self.left,
577            (AxisDirection::Vertical, AxisReverse::NotReversed) => self.bottom,
578            (AxisDirection::Vertical, AxisReverse::Reversed) => self.top,
579        }
580    }
581
582    #[inline(always)]
583    pub(crate) fn cross_axis_start(&self, dir: AxisDirection, rev: AxisReverse) -> L {
584        match (dir, rev) {
585            (AxisDirection::Horizontal, AxisReverse::NotReversed) => self.top,
586            (AxisDirection::Horizontal, AxisReverse::Reversed) => self.bottom,
587            (AxisDirection::Vertical, AxisReverse::NotReversed) => self.left,
588            (AxisDirection::Vertical, AxisReverse::Reversed) => self.right,
589        }
590    }
591
592    #[allow(unused)]
593    #[inline(always)]
594    pub(crate) fn cross_axis_end(&self, dir: AxisDirection, rev: AxisReverse) -> L {
595        match (dir, rev) {
596            (AxisDirection::Horizontal, AxisReverse::NotReversed) => self.bottom,
597            (AxisDirection::Horizontal, AxisReverse::Reversed) => self.top,
598            (AxisDirection::Vertical, AxisReverse::NotReversed) => self.right,
599            (AxisDirection::Vertical, AxisReverse::Reversed) => self.left,
600        }
601    }
602}
603
604impl<L: LengthNum> core::ops::Add<Edge<L>> for Edge<L> {
605    type Output = Self;
606
607    fn add(self, rhs: Edge<L>) -> Self {
608        Self {
609            left: self.left + rhs.left,
610            right: self.right + rhs.right,
611            top: self.top + rhs.top,
612            bottom: self.bottom + rhs.bottom,
613        }
614    }
615}
616
617impl<L: LengthNum> core::ops::Sub<Edge<L>> for Edge<L> {
618    type Output = Self;
619    fn sub(self, rhs: Edge<L>) -> Self {
620        Self {
621            left: self.left - rhs.left,
622            right: self.right - rhs.right,
623            top: self.top - rhs.top,
624            bottom: self.bottom - rhs.bottom,
625        }
626    }
627}
628
629/// Four edge lengths, each edge can be undetermined.
630#[allow(missing_docs)]
631#[derive(Debug, Clone, Copy, PartialEq)]
632pub struct EdgeOption<L: LengthNum> {
633    pub left: OptionNum<L>,
634    pub right: OptionNum<L>,
635    pub top: OptionNum<L>,
636    pub bottom: OptionNum<L>,
637}
638
639impl<L: LengthNum> EdgeOption<L> {
640    /// Unwrap to `Edge`, default to zero.
641    #[inline(always)]
642    pub fn or_zero(&self) -> Edge<L> {
643        Edge {
644            left: self.left.or_zero(),
645            right: self.right.or_zero(),
646            top: self.top.or_zero(),
647            bottom: self.bottom.or_zero(),
648        }
649    }
650
651    /// Get the sum of horizontal lengths, default to zero.
652    #[inline(always)]
653    pub fn horizontal(&self) -> L {
654        self.left.or_zero() + self.right.or_zero()
655    }
656
657    /// Get the sum of vertical lengths, default to zero.
658    #[inline(always)]
659    pub fn vertical(&self) -> L {
660        self.top.or_zero() + self.bottom.or_zero()
661    }
662
663    pub(crate) fn is_left_right_either_none(&self) -> bool {
664        self.left.is_none() || self.right.is_none()
665    }
666
667    pub(crate) fn is_top_bottom_either_none(&self) -> bool {
668        self.top.is_none() || self.bottom.is_none()
669    }
670
671    #[inline(always)]
672    pub(crate) fn main_axis_sum(&self, dir: AxisDirection) -> L {
673        match dir {
674            AxisDirection::Horizontal => self.horizontal(),
675            AxisDirection::Vertical => self.vertical(),
676        }
677    }
678
679    #[inline(always)]
680    pub(crate) fn cross_axis_sum(&self, dir: AxisDirection) -> L {
681        match dir {
682            AxisDirection::Horizontal => self.vertical(),
683            AxisDirection::Vertical => self.horizontal(),
684        }
685    }
686
687    #[inline(always)]
688    pub(crate) fn main_axis_start(&self, dir: AxisDirection, rev: AxisReverse) -> OptionNum<L> {
689        match (dir, rev) {
690            (AxisDirection::Horizontal, AxisReverse::NotReversed) => self.left,
691            (AxisDirection::Horizontal, AxisReverse::Reversed) => self.right,
692            (AxisDirection::Vertical, AxisReverse::NotReversed) => self.top,
693            (AxisDirection::Vertical, AxisReverse::Reversed) => self.bottom,
694        }
695    }
696
697    #[inline(always)]
698    pub(crate) fn set_main_axis_start(
699        &mut self,
700        dir: AxisDirection,
701        rev: AxisReverse,
702        value: OptionNum<L>,
703    ) {
704        match (dir, rev) {
705            (AxisDirection::Horizontal, AxisReverse::NotReversed) => self.left = value,
706            (AxisDirection::Horizontal, AxisReverse::Reversed) => self.right = value,
707            (AxisDirection::Vertical, AxisReverse::NotReversed) => self.top = value,
708            (AxisDirection::Vertical, AxisReverse::Reversed) => self.bottom = value,
709        }
710    }
711
712    #[inline(always)]
713    pub(crate) fn main_axis_end(&self, dir: AxisDirection, rev: AxisReverse) -> OptionNum<L> {
714        match (dir, rev) {
715            (AxisDirection::Horizontal, AxisReverse::NotReversed) => self.right,
716            (AxisDirection::Horizontal, AxisReverse::Reversed) => self.left,
717            (AxisDirection::Vertical, AxisReverse::NotReversed) => self.bottom,
718            (AxisDirection::Vertical, AxisReverse::Reversed) => self.top,
719        }
720    }
721
722    #[inline(always)]
723    pub(crate) fn set_main_axis_end(
724        &mut self,
725        dir: AxisDirection,
726        rev: AxisReverse,
727        value: OptionNum<L>,
728    ) {
729        match (dir, rev) {
730            (AxisDirection::Horizontal, AxisReverse::NotReversed) => self.right = value,
731            (AxisDirection::Horizontal, AxisReverse::Reversed) => self.left = value,
732            (AxisDirection::Vertical, AxisReverse::NotReversed) => self.bottom = value,
733            (AxisDirection::Vertical, AxisReverse::Reversed) => self.top = value,
734        }
735    }
736
737    #[inline(always)]
738    pub(crate) fn cross_axis_start(&self, dir: AxisDirection, rev: AxisReverse) -> OptionNum<L> {
739        match (dir, rev) {
740            (AxisDirection::Horizontal, AxisReverse::NotReversed) => self.top,
741            (AxisDirection::Horizontal, AxisReverse::Reversed) => self.bottom,
742            (AxisDirection::Vertical, AxisReverse::NotReversed) => self.left,
743            (AxisDirection::Vertical, AxisReverse::Reversed) => self.right,
744        }
745    }
746
747    #[inline(always)]
748    pub(crate) fn set_cross_axis_start(
749        &mut self,
750        dir: AxisDirection,
751        rev: AxisReverse,
752        value: OptionNum<L>,
753    ) {
754        match (dir, rev) {
755            (AxisDirection::Horizontal, AxisReverse::NotReversed) => self.top = value,
756            (AxisDirection::Horizontal, AxisReverse::Reversed) => self.bottom = value,
757            (AxisDirection::Vertical, AxisReverse::NotReversed) => self.left = value,
758            (AxisDirection::Vertical, AxisReverse::Reversed) => self.right = value,
759        }
760    }
761
762    #[inline(always)]
763    pub(crate) fn cross_axis_end(&self, dir: AxisDirection, rev: AxisReverse) -> OptionNum<L> {
764        match (dir, rev) {
765            (AxisDirection::Horizontal, AxisReverse::NotReversed) => self.bottom,
766            (AxisDirection::Horizontal, AxisReverse::Reversed) => self.top,
767            (AxisDirection::Vertical, AxisReverse::NotReversed) => self.right,
768            (AxisDirection::Vertical, AxisReverse::Reversed) => self.left,
769        }
770    }
771
772    #[inline(always)]
773    pub(crate) fn set_cross_axis_end(
774        &mut self,
775        dir: AxisDirection,
776        rev: AxisReverse,
777        value: OptionNum<L>,
778    ) {
779        match (dir, rev) {
780            (AxisDirection::Horizontal, AxisReverse::NotReversed) => self.bottom = value,
781            (AxisDirection::Horizontal, AxisReverse::Reversed) => self.top = value,
782            (AxisDirection::Vertical, AxisReverse::NotReversed) => self.right = value,
783            (AxisDirection::Vertical, AxisReverse::Reversed) => self.left = value,
784        }
785    }
786}
787
788#[derive(Debug, Clone, Copy, PartialEq)]
789pub(crate) enum AxisDirection {
790    Horizontal,
791    Vertical,
792}
793
794#[derive(Debug, Clone, Copy, PartialEq, Hash)]
795pub(crate) enum AxisReverse {
796    NotReversed,
797    Reversed,
798}
799
800#[derive(Debug, Clone, Copy, PartialEq)]
801pub(crate) struct AxisInfo {
802    pub(crate) dir: AxisDirection,
803    pub(crate) main_dir_rev: AxisReverse,
804    pub(crate) cross_dir_rev: AxisReverse,
805}
806
807impl AxisInfo {
808    pub(crate) fn from_writing_mode(writing_mode: WritingMode) -> Self {
809        let (dir, main_dir_rev, cross_dir_rev) = match writing_mode {
810            WritingMode::HorizontalTb => (
811                AxisDirection::Vertical,
812                AxisReverse::NotReversed,
813                AxisReverse::NotReversed,
814            ),
815            WritingMode::VerticalLr => (
816                AxisDirection::Horizontal,
817                AxisReverse::NotReversed,
818                AxisReverse::NotReversed,
819            ),
820            WritingMode::VerticalRl => (
821                AxisDirection::Horizontal,
822                AxisReverse::Reversed,
823                AxisReverse::NotReversed,
824            ),
825        };
826        Self {
827            dir,
828            main_dir_rev,
829            cross_dir_rev,
830        }
831    }
832
833    /// Create AxisInfo considering both writing-mode and direction.
834
835    pub(crate) fn from_writing_mode_and_direction(
836        writing_mode: WritingMode,
837        direction: Direction,
838    ) -> Self {
839        let rtl_reverse = if matches!(direction, Direction::RTL) {
840            AxisReverse::Reversed
841        } else {
842            AxisReverse::NotReversed
843        };
844        let (dir, main_dir_rev, cross_dir_rev) = match writing_mode {
845            // horizontal-tb: block(main) = top->bottom, inline(cross) = left->right
846            WritingMode::HorizontalTb => (
847                AxisDirection::Vertical,
848                AxisReverse::NotReversed,
849                rtl_reverse,
850            ),
851            // vertical-lr: block(main) = left->right, inline(cross) = top->bottom
852            WritingMode::VerticalLr => (
853                AxisDirection::Horizontal,
854                AxisReverse::NotReversed,
855                rtl_reverse,
856            ),
857            // vertical-rl: block(main) = right->left (reversed), inline(cross) = top->bottom
858            WritingMode::VerticalRl => (
859                AxisDirection::Horizontal,
860                AxisReverse::Reversed,
861                rtl_reverse,
862            ),
863        };
864        Self {
865            dir,
866            main_dir_rev,
867            cross_dir_rev,
868        }
869    }
870}
871#[derive(Debug, Clone, Copy, PartialEq)]
872pub(crate) struct MinMaxSize<L: LengthNum> {
873    pub(crate) min_width: OptionNum<L>,
874    pub(crate) max_width: OptionNum<L>,
875    pub(crate) min_height: OptionNum<L>,
876    pub(crate) max_height: OptionNum<L>,
877}
878
879#[derive(Debug, Clone, Copy, PartialEq)]
880pub(crate) struct MinMaxLimit<L: LengthNum> {
881    pub(crate) min_width: L,
882    pub(crate) max_width: OptionNum<L>,
883    pub(crate) min_height: L,
884    pub(crate) max_height: OptionNum<L>,
885}
886
887impl<L: LengthNum> MinMaxLimit<L> {
888    pub(crate) fn normalized_size(&self, v: OptionSize<L>) -> Normalized<OptionSize<L>> {
889        Normalized(OptionSize::new(
890            v.width
891                .map(|x| x.maybe_min(self.max_width).max(self.min_width)),
892            v.height
893                .map(|x| x.maybe_min(self.max_height).max(self.min_height)),
894        ))
895    }
896
897    pub(crate) fn width(&self, x: L) -> L {
898        x.maybe_min(self.max_width).max(self.min_width)
899    }
900
901    pub(crate) fn height(&self, x: L) -> L {
902        x.maybe_min(self.max_height).max(self.min_height)
903    }
904
905    pub(crate) fn maybe(&self) -> MinMaxLimitMaybe<'_, L> {
906        MinMaxLimitMaybe(self)
907    }
908
909    #[inline(always)]
910    pub(crate) fn main_size(&self, x: L, dir: AxisDirection) -> L {
911        match dir {
912            AxisDirection::Horizontal => self.width(x),
913            AxisDirection::Vertical => self.height(x),
914        }
915    }
916
917    #[inline(always)]
918    pub(crate) fn cross_size(&self, x: L, dir: AxisDirection) -> L {
919        match dir {
920            AxisDirection::Horizontal => self.height(x),
921            AxisDirection::Vertical => self.width(x),
922        }
923    }
924
925    #[inline(always)]
926    pub(crate) fn min_main_size(&self, dir: AxisDirection) -> L {
927        match dir {
928            AxisDirection::Horizontal => self.min_width,
929            AxisDirection::Vertical => self.min_height,
930        }
931    }
932
933    #[inline(always)]
934    #[allow(unused)]
935    pub(crate) fn max_main_size(&self, dir: AxisDirection) -> OptionNum<L> {
936        match dir {
937            AxisDirection::Horizontal => self.max_width,
938            AxisDirection::Vertical => self.max_height,
939        }
940    }
941
942    #[inline(always)]
943    #[allow(unused)]
944    pub(crate) fn min_cross_size(&self, dir: AxisDirection) -> L {
945        match dir {
946            AxisDirection::Horizontal => self.min_height,
947            AxisDirection::Vertical => self.min_width,
948        }
949    }
950
951    #[inline(always)]
952    #[allow(unused)]
953    pub(crate) fn max_cross_size(&self, dir: AxisDirection) -> OptionNum<L> {
954        match dir {
955            AxisDirection::Horizontal => self.max_height,
956            AxisDirection::Vertical => self.max_width,
957        }
958    }
959
960    #[inline(always)]
961    #[allow(unused)]
962    pub(crate) fn set_min_cross_size(&mut self, x: L, dir: AxisDirection) {
963        match dir {
964            AxisDirection::Horizontal => self.min_height = x,
965            AxisDirection::Vertical => self.min_width = x,
966        }
967    }
968
969    #[inline(always)]
970    #[allow(unused)]
971    pub(crate) fn set_max_cross_size(&mut self, x: OptionNum<L>, dir: AxisDirection) {
972        match dir {
973            AxisDirection::Horizontal => self.max_height = x,
974            AxisDirection::Vertical => self.max_width = x,
975        }
976    }
977
978    #[inline(always)]
979    #[allow(unused)]
980    pub(crate) fn set_min_main_size(&mut self, x: L, dir: AxisDirection) {
981        match dir {
982            AxisDirection::Horizontal => self.min_width = x,
983            AxisDirection::Vertical => self.min_height = x,
984        }
985    }
986
987    #[inline(always)]
988    #[allow(unused)]
989    pub(crate) fn set_max_main_size(&mut self, x: OptionNum<L>, dir: AxisDirection) {
990        match dir {
991            AxisDirection::Horizontal => self.max_width = x,
992            AxisDirection::Vertical => self.max_height = x,
993        }
994    }
995}
996
997pub(crate) struct MinMaxLimitMaybe<'a, L: LengthNum>(&'a MinMaxLimit<L>);
998
999impl<'a, L: LengthNum> MinMaxLimitMaybe<'a, L> {
1000    pub(crate) fn width(&self, v: OptionNum<L>) -> OptionNum<L> {
1001        v.map(|x| x.maybe_min(self.0.max_width).max(self.0.min_width))
1002    }
1003
1004    pub(crate) fn height(&self, v: OptionNum<L>) -> OptionNum<L> {
1005        v.map(|x| x.maybe_min(self.0.max_height).max(self.0.min_height))
1006    }
1007
1008    #[inline(always)]
1009    #[allow(unused)]
1010    pub(crate) fn main_size(&self, dir: AxisDirection, v: OptionNum<L>) -> OptionNum<L> {
1011        match dir {
1012            AxisDirection::Horizontal => self.width(v),
1013            AxisDirection::Vertical => self.height(v),
1014        }
1015    }
1016
1017    #[inline(always)]
1018    pub(crate) fn cross_size(&self, dir: AxisDirection, v: OptionNum<L>) -> OptionNum<L> {
1019        match dir {
1020            AxisDirection::Horizontal => self.height(v),
1021            AxisDirection::Vertical => self.width(v),
1022        }
1023    }
1024}
1025
1026#[derive(Debug, Clone, Copy, PartialEq, Hash)]
1027pub(crate) struct Normalized<T>(pub(crate) T);
1028
1029impl<T> Deref for Normalized<T> {
1030    type Target = T;
1031
1032    fn deref(&self) -> &Self::Target {
1033        &self.0
1034    }
1035}
1036
1037#[inline(always)]
1038pub(crate) fn size_to_option<L: LengthNum>(size: Size<L>) -> OptionSize<L> {
1039    OptionSize::new(OptionNum::some(size.width), OptionNum::some(size.height))
1040}
1041
1042#[allow(missing_docs)]
1043#[derive(Debug, Clone, PartialEq)]
1044pub enum LayoutGridTemplate<L: LengthNum, T: PartialEq + Clone = i32> {
1045    None,
1046    TrackList(Vec<LayoutTrackListItem<L, T>>),
1047}
1048
1049impl<L: LengthNum, T: PartialEq + Clone> Default for LayoutGridTemplate<L, T> {
1050    fn default() -> Self {
1051        Self::None
1052    }
1053}
1054
1055#[allow(missing_docs)]
1056#[derive(Debug, Clone, PartialEq)]
1057pub enum LayoutTrackListItem<L: LengthNum, T: PartialEq + Clone = i32> {
1058    LineNames(Vec<String>),
1059    TrackSize(LayoutTrackSize<L, T>),
1060}
1061
1062impl<L: LengthNum, T: PartialEq + Clone> LayoutTrackListItem<L, T> {
1063    /// Returns `true` if this item is a track size with an intrinsic sizing
1064    /// `auto`, `min-content`, `max-content` are intrinsic.
1065    pub fn is_intrinsic(&self) -> bool {
1066        matches!(
1067            self,
1068            LayoutTrackListItem::TrackSize(
1069                LayoutTrackSize::Length(DefLength::Auto)
1070                    | LayoutTrackSize::MinContent
1071                    | LayoutTrackSize::MaxContent
1072            )
1073        )
1074    }
1075}
1076
1077#[allow(missing_docs)]
1078#[derive(Debug, Clone, PartialEq)]
1079pub enum LayoutTrackSize<L: LengthNum, T: PartialEq + Clone = i32> {
1080    MinContent,
1081    MaxContent,
1082    Fr(f32),
1083    Length(DefLength<L, T>),
1084}
1085
1086/// CSS Grid ยง7.6: Implicit Track Sizing
1087/// <https://www.w3.org/TR/css-grid-1/#auto-tracks>
1088///
1089/// The `grid-auto-rows` and `grid-auto-columns` properties specify the size
1090/// of implicitly-created grid tracks.
1091#[allow(missing_docs)]
1092#[derive(Debug, Clone, PartialEq)]
1093pub struct LayoutGridAuto<L: LengthNum, T: PartialEq + Clone = i32>(pub Vec<LayoutTrackSize<L, T>>);
1094
1095impl<L: LengthNum, T: PartialEq + Clone> Default for LayoutGridAuto<L, T> {
1096    fn default() -> Self {
1097        // Default is a single `auto` track
1098        Self(vec![LayoutTrackSize::Length(DefLength::Auto)])
1099    }
1100}
1101
1102impl<L: LengthNum, T: PartialEq + Clone> LayoutGridAuto<L, T> {
1103    /// Get the track size for an implicit track at the given index.
1104    /// Cycles through the list if index exceeds the list length.
1105    pub fn get(&self, index: usize) -> LayoutTrackSize<L, T> {
1106        if self.0.is_empty() {
1107            LayoutTrackSize::Length(DefLength::Auto)
1108        } else {
1109            self.0[index % self.0.len()].clone()
1110        }
1111    }
1112
1113    /// Get the number of track sizes in the list.
1114    pub fn len(&self) -> usize {
1115        self.0.len()
1116    }
1117
1118    /// Check if the list is empty.
1119    pub fn is_empty(&self) -> bool {
1120        self.0.is_empty()
1121    }
1122}
1123
1124#[cfg(test)]
1125mod test {
1126
1127    use crate::{AxisDirection, Normalized, OptionSize, Size, SizeGetter, SizeSetter};
1128
1129    #[test]
1130    fn option_size_get_main_size() {
1131        let os = OptionSize::new(crate::OptionNum::some(10.), crate::OptionNum::some(20.));
1132        let size = os.main_size(AxisDirection::Horizontal);
1133        // let size = main_size(target, dir)
1134        assert_eq!(size.val(), Some(10.));
1135        let size = os.main_size(AxisDirection::Vertical);
1136        assert_eq!(size.val(), Some(20.));
1137    }
1138
1139    #[test]
1140    fn option_size_get_cross_size() {
1141        let os = OptionSize::new(crate::OptionNum::some(10.), crate::OptionNum::some(20.));
1142        let size = os.cross_size(AxisDirection::Horizontal);
1143        assert_eq!(size.val(), Some(20.));
1144        let size = os.cross_size(AxisDirection::Vertical);
1145        assert_eq!(size.val(), Some(10.));
1146    }
1147
1148    #[test]
1149    fn normalized_option_size_get_size() {
1150        let os = OptionSize::new(crate::OptionNum::some(10.), crate::OptionNum::some(20.));
1151        let normalized = Normalized(os);
1152        let size = normalized.main_size(AxisDirection::Horizontal);
1153        assert_eq!(size.val(), Some(10.));
1154        let size = normalized.main_size(AxisDirection::Vertical);
1155        assert_eq!(size.val(), Some(20.));
1156    }
1157
1158    #[test]
1159    fn size_get_size() {
1160        let s = Size::new(10.0_f32, 20.0_f32);
1161        let size = s.main_size(AxisDirection::Horizontal);
1162        assert_eq!(size, 10.);
1163        let size = s.main_size(AxisDirection::Vertical);
1164        assert_eq!(size, 20.);
1165    }
1166
1167    #[test]
1168    fn size_set_size() {
1169        let mut s = Size::new(10.0_f32, 20.0_f32);
1170        s.set_main_size(AxisDirection::Horizontal, 20.);
1171        assert_eq!(s.main_size(AxisDirection::Horizontal), 20.);
1172        s.set_cross_size(AxisDirection::Horizontal, 30.);
1173        assert_eq!(s.cross_size(AxisDirection::Horizontal), 30.);
1174    }
1175}