Skip to main content

photon_ui/layout/
constraint.rs

1use std::fmt;
2
3/// A size constraint for layout elements.
4#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
5pub enum Constraint {
6    /// Minimum size in terminal columns or rows.
7    Min(u16),
8    /// Maximum size in terminal columns or rows.
9    Max(u16),
10    /// Exact size in terminal columns or rows.
11    Length(u16),
12    /// Percentage of the total available space.
13    Percentage(u16),
14    /// Proportional fraction of the total space (numerator, denominator).
15    Ratio(u32, u32),
16    /// Grows to fill remaining space with the given priority.
17    Fill(u16),
18}
19
20impl Constraint {
21    /// Create a vector of [`Constraint::Length`] values.
22    pub fn from_lengths<T>(lengths: T) -> Vec<Self>
23    where
24        T: IntoIterator<Item = u16>, {
25        lengths.into_iter().map(Self::Length).collect()
26    }
27
28    /// Create a vector of [`Constraint::Ratio`] values.
29    pub fn from_ratios<T>(ratios: T) -> Vec<Self>
30    where
31        T: IntoIterator<Item = (u32, u32)>, {
32        ratios.into_iter().map(|(n, d)| Self::Ratio(n, d)).collect()
33    }
34
35    /// Create a vector of [`Constraint::Percentage`] values.
36    pub fn from_percentages<T>(percentages: T) -> Vec<Self>
37    where
38        T: IntoIterator<Item = u16>, {
39        percentages.into_iter().map(Self::Percentage).collect()
40    }
41
42    /// Create a vector of [`Constraint::Min`] values.
43    pub fn from_mins<T>(mins: T) -> Vec<Self>
44    where
45        T: IntoIterator<Item = u16>, {
46        mins.into_iter().map(Self::Min).collect()
47    }
48
49    /// Create a vector of [`Constraint::Max`] values.
50    pub fn from_maxes<T>(maxes: T) -> Vec<Self>
51    where
52        T: IntoIterator<Item = u16>, {
53        maxes.into_iter().map(Self::Max).collect()
54    }
55
56    /// Create a vector of [`Constraint::Fill`] values.
57    pub fn from_fills<T>(fills: T) -> Vec<Self>
58    where
59        T: IntoIterator<Item = u16>, {
60        fills.into_iter().map(Self::Fill).collect()
61    }
62}
63
64impl From<u16> for Constraint {
65    fn from(length: u16) -> Self {
66        Self::Length(length)
67    }
68}
69
70impl From<&Self> for Constraint {
71    fn from(constraint: &Self) -> Self {
72        *constraint
73    }
74}
75
76impl AsRef<Self> for Constraint {
77    fn as_ref(&self) -> &Self {
78        self
79    }
80}
81
82impl Default for Constraint {
83    fn default() -> Self {
84        Self::Percentage(100)
85    }
86}
87
88impl fmt::Display for Constraint {
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90        match self {
91            | Self::Percentage(p) => write!(f, "Percentage({p})"),
92            | Self::Ratio(n, d) => write!(f, "Ratio({n}, {d})"),
93            | Self::Length(l) => write!(f, "Length({l})"),
94            | Self::Fill(l) => write!(f, "Fill({l})"),
95            | Self::Max(m) => write!(f, "Max({m})"),
96            | Self::Min(m) => write!(f, "Min({m})"),
97        }
98    }
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104
105    #[test]
106    fn constraint_default() {
107        assert_eq!(Constraint::default(), Constraint::Percentage(100));
108    }
109
110    #[test]
111    fn constraint_from_u16() {
112        let c: Constraint = 10.into();
113        assert_eq!(c, Constraint::Length(10));
114    }
115
116    #[test]
117    fn constraint_from_ref() {
118        let c = Constraint::Length(5);
119        assert_eq!(Constraint::from(&c), c);
120    }
121
122    #[test]
123    fn constraint_as_ref() {
124        let c = Constraint::Length(5);
125        assert_eq!(c.as_ref(), &Constraint::Length(5));
126    }
127
128    #[test]
129    fn constraint_display() {
130        assert_eq!(Constraint::Percentage(50).to_string(), "Percentage(50)");
131        assert_eq!(Constraint::Ratio(1, 2).to_string(), "Ratio(1, 2)");
132        assert_eq!(Constraint::Length(10).to_string(), "Length(10)");
133        assert_eq!(Constraint::Fill(1).to_string(), "Fill(1)");
134        assert_eq!(Constraint::Max(20).to_string(), "Max(20)");
135        assert_eq!(Constraint::Min(5).to_string(), "Min(5)");
136    }
137
138    #[test]
139    fn constraint_from_lengths() {
140        let c = Constraint::from_lengths([1, 2, 3]);
141        assert_eq!(
142            c,
143            vec![
144                Constraint::Length(1),
145                Constraint::Length(2),
146                Constraint::Length(3)
147            ]
148        );
149    }
150
151    #[test]
152    fn constraint_from_ratios() {
153        let c = Constraint::from_ratios([(1, 4), (1, 2)]);
154        assert_eq!(c, vec![Constraint::Ratio(1, 4), Constraint::Ratio(1, 2)]);
155    }
156
157    #[test]
158    fn constraint_from_percentages() {
159        let c = Constraint::from_percentages([25, 50]);
160        assert_eq!(
161            c,
162            vec![Constraint::Percentage(25), Constraint::Percentage(50)]
163        );
164    }
165
166    #[test]
167    fn constraint_from_mins() {
168        let c = Constraint::from_mins([1, 2]);
169        assert_eq!(c, vec![Constraint::Min(1), Constraint::Min(2)]);
170    }
171
172    #[test]
173    fn constraint_from_maxes() {
174        let c = Constraint::from_maxes([10, 20]);
175        assert_eq!(c, vec![Constraint::Max(10), Constraint::Max(20)]);
176    }
177
178    #[test]
179    fn constraint_from_fills() {
180        let c = Constraint::from_fills([1, 2, 3]);
181        assert_eq!(
182            c,
183            vec![
184                Constraint::Fill(1),
185                Constraint::Fill(2),
186                Constraint::Fill(3)
187            ]
188        );
189    }
190}