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