Skip to main content

lipgloss/
position.rs

1//! Position and alignment types.
2
3/// Text alignment position.
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
5pub enum Position {
6    /// Align to the top or left.
7    #[default]
8    Top,
9    /// Align to the bottom or right.
10    Bottom,
11    /// Align to the center.
12    Center,
13    /// Alias for Top.
14    Left,
15    /// Alias for Bottom.
16    Right,
17}
18
19impl Position {
20    /// Convert position to a factor (0.0, 0.5, or 1.0).
21    pub fn factor(&self) -> f64 {
22        match self {
23            Position::Top | Position::Left => 0.0,
24            Position::Center => 0.5,
25            Position::Bottom | Position::Right => 1.0,
26        }
27    }
28}
29
30/// CSS-like sides specification for padding, margin, etc.
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
32pub struct Sides<T> {
33    pub top: T,
34    pub right: T,
35    pub bottom: T,
36    pub left: T,
37}
38
39impl<T: Copy> Sides<T> {
40    /// Create sides with all values the same.
41    pub const fn all(value: T) -> Self {
42        Self {
43            top: value,
44            right: value,
45            bottom: value,
46            left: value,
47        }
48    }
49
50    /// Create sides from individual values.
51    pub const fn new(top: T, right: T, bottom: T, left: T) -> Self {
52        Self {
53            top,
54            right,
55            bottom,
56            left,
57        }
58    }
59}
60
61impl<T: Copy + Default> Sides<T> {
62    /// Create sides with zero/default values.
63    pub fn zero() -> Self
64    where
65        T: Default,
66    {
67        Self::default()
68    }
69}
70
71// From implementations for CSS-like shorthand
72
73impl<T: Copy> From<T> for Sides<T> {
74    /// Single value: all sides.
75    fn from(all: T) -> Self {
76        Self::all(all)
77    }
78}
79
80impl<T: Copy> From<(T, T)> for Sides<T> {
81    /// Two values: (vertical, horizontal).
82    fn from((vertical, horizontal): (T, T)) -> Self {
83        Self {
84            top: vertical,
85            right: horizontal,
86            bottom: vertical,
87            left: horizontal,
88        }
89    }
90}
91
92impl<T: Copy> From<(T, T, T)> for Sides<T> {
93    /// Three values: (top, horizontal, bottom).
94    fn from((top, horizontal, bottom): (T, T, T)) -> Self {
95        Self {
96            top,
97            right: horizontal,
98            bottom,
99            left: horizontal,
100        }
101    }
102}
103
104impl<T: Copy> From<(T, T, T, T)> for Sides<T> {
105    /// Four values: (top, right, bottom, left) - clockwise.
106    fn from((top, right, bottom, left): (T, T, T, T)) -> Self {
107        Self {
108            top,
109            right,
110            bottom,
111            left,
112        }
113    }
114}
115
116impl<T: Copy> From<[T; 1]> for Sides<T> {
117    fn from([all]: [T; 1]) -> Self {
118        Self::all(all)
119    }
120}
121
122impl<T: Copy> From<[T; 2]> for Sides<T> {
123    fn from([vertical, horizontal]: [T; 2]) -> Self {
124        Self {
125            top: vertical,
126            right: horizontal,
127            bottom: vertical,
128            left: horizontal,
129        }
130    }
131}
132
133impl<T: Copy> From<[T; 4]> for Sides<T> {
134    fn from([top, right, bottom, left]: [T; 4]) -> Self {
135        Self {
136            top,
137            right,
138            bottom,
139            left,
140        }
141    }
142}
143
144#[cfg(test)]
145mod tests {
146    use super::*;
147
148    #[test]
149    fn test_position_factor() {
150        let eps = f64::EPSILON;
151        assert!((Position::Top.factor() - 0.0).abs() < eps);
152        assert!((Position::Center.factor() - 0.5).abs() < eps);
153        assert!((Position::Bottom.factor() - 1.0).abs() < eps);
154    }
155
156    #[test]
157    fn test_sides_from_single() {
158        let s: Sides<u16> = 5.into();
159        assert_eq!(s.top, 5);
160        assert_eq!(s.right, 5);
161        assert_eq!(s.bottom, 5);
162        assert_eq!(s.left, 5);
163    }
164
165    #[test]
166    fn test_sides_from_tuple2() {
167        let s: Sides<u16> = (5, 10).into();
168        assert_eq!(s.top, 5);
169        assert_eq!(s.right, 10);
170        assert_eq!(s.bottom, 5);
171        assert_eq!(s.left, 10);
172    }
173
174    #[test]
175    fn test_sides_from_tuple4() {
176        let s: Sides<u16> = (1, 2, 3, 4).into();
177        assert_eq!(s.top, 1);
178        assert_eq!(s.right, 2);
179        assert_eq!(s.bottom, 3);
180        assert_eq!(s.left, 4);
181    }
182}