ori_core/
layout.rs

1use std::ops::Range;
2
3use glam::Vec2;
4
5use crate::StyleAttributeEnum;
6
7#[derive(Clone, Copy, Debug, PartialEq)]
8pub struct BoxConstraints {
9    pub min: Vec2,
10    pub max: Vec2,
11}
12
13impl BoxConstraints {
14    pub const UNBOUNDED: Self = Self {
15        min: Vec2::ZERO,
16        max: Vec2::splat(f32::INFINITY),
17    };
18
19    pub fn new(min: Vec2, max: Vec2) -> Self {
20        Self {
21            min: min.ceil(),
22            max: max.ceil(),
23        }
24    }
25
26    pub fn window(width: u32, height: u32) -> Self {
27        Self {
28            min: Vec2::ZERO,
29            max: Vec2::new(width as f32, height as f32),
30        }
31    }
32
33    pub fn loose(self) -> Self {
34        Self {
35            min: self.min,
36            max: Vec2::splat(f32::INFINITY),
37        }
38    }
39
40    pub fn loose_x(self) -> Self {
41        Self {
42            min: self.min,
43            max: Vec2::new(f32::INFINITY, self.max.x),
44        }
45    }
46
47    pub fn loose_y(self) -> Self {
48        Self {
49            min: self.min,
50            max: Vec2::new(self.max.y, f32::INFINITY),
51        }
52    }
53
54    pub fn shrink(self, size: Vec2) -> Self {
55        Self {
56            min: self.min - size,
57            max: self.max - size,
58        }
59    }
60
61    pub fn constrain(self, size: Vec2) -> Vec2 {
62        size.clamp(self.min, self.max)
63    }
64
65    pub fn height(self) -> Range<f32> {
66        self.min.y..self.max.y
67    }
68
69    pub fn width(self) -> Range<f32> {
70        self.min.x..self.max.x
71    }
72}
73
74#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
75pub enum Axis {
76    Horizontal,
77    #[default]
78    Vertical,
79}
80
81impl Axis {
82    pub const fn cross(self) -> Self {
83        match self {
84            Axis::Horizontal => Axis::Vertical,
85            Axis::Vertical => Axis::Horizontal,
86        }
87    }
88
89    pub const fn minor(self, size: Vec2) -> f32 {
90        match self {
91            Axis::Horizontal => size.y,
92            Axis::Vertical => size.x,
93        }
94    }
95
96    pub const fn major(self, size: Vec2) -> f32 {
97        match self {
98            Axis::Horizontal => size.x,
99            Axis::Vertical => size.y,
100        }
101    }
102
103    pub const fn pack(self, major: f32, minor: f32) -> Vec2 {
104        match self {
105            Axis::Horizontal => Vec2::new(major, minor),
106            Axis::Vertical => Vec2::new(minor, major),
107        }
108    }
109}
110
111impl StyleAttributeEnum for Axis {
112    fn from_str(s: &str) -> Option<Self> {
113        match s {
114            "horizontal" | "row" => Some(Axis::Horizontal),
115            "vertical" | "column" => Some(Axis::Vertical),
116            _ => None,
117        }
118    }
119
120    fn to_str(&self) -> &str {
121        match self {
122            Axis::Horizontal => "horizontal",
123            Axis::Vertical => "vertical",
124        }
125    }
126}
127
128#[derive(Clone, Copy, Debug, PartialEq, Eq)]
129pub enum JustifyContent {
130    Start,
131    Center,
132    End,
133    SpaceBetween,
134    SpaceAround,
135    SpaceEvenly,
136}
137
138impl JustifyContent {
139    pub fn justify(&self, children: &[f32], container_size: f32, gap: f32) -> Vec<f32> {
140        if children.is_empty() {
141            return Vec::new();
142        }
143
144        let mut positions = Vec::with_capacity(children.len());
145
146        let total_gap = gap * (children.len() - 1) as f32;
147        let total_size = children.iter().sum::<f32>() + total_gap;
148
149        match self {
150            JustifyContent::Start => {
151                let mut position = 0.0;
152
153                for &child in children {
154                    positions.push(position);
155                    position += child + gap;
156                }
157            }
158            JustifyContent::Center => {
159                let mut position = container_size / 2.0 - total_size / 2.0;
160
161                for &child in children {
162                    positions.push(position);
163                    position += child + gap;
164                }
165            }
166            JustifyContent::End => {
167                let mut position = container_size - total_size;
168
169                for &child in children {
170                    positions.push(position);
171                    position += child + gap;
172                }
173            }
174            JustifyContent::SpaceBetween => {
175                let gap = (container_size - total_size) / (children.len() - 1) as f32;
176
177                let mut position = 0.0;
178
179                for &child in children {
180                    positions.push(position);
181                    position += child + gap;
182                }
183            }
184            JustifyContent::SpaceAround => {
185                let gap = (container_size - total_size) / children.len() as f32;
186
187                let mut position = gap / 2.0;
188
189                for &child in children {
190                    positions.push(position);
191                    position += child + gap;
192                }
193            }
194            JustifyContent::SpaceEvenly => {
195                let gap = container_size / children.len() as f32;
196
197                let mut position = gap / 2.0;
198
199                for _ in children {
200                    positions.push(position);
201                    position += gap;
202                }
203            }
204        }
205
206        positions
207    }
208}
209
210impl Default for JustifyContent {
211    fn default() -> Self {
212        Self::Start
213    }
214}
215
216impl StyleAttributeEnum for JustifyContent {
217    fn from_str(s: &str) -> Option<Self> {
218        match s {
219            "start" => Some(JustifyContent::Start),
220            "center" => Some(JustifyContent::Center),
221            "end" => Some(JustifyContent::End),
222            "space-between" => Some(JustifyContent::SpaceBetween),
223            "space-around" => Some(JustifyContent::SpaceAround),
224            "space-evenly" => Some(JustifyContent::SpaceEvenly),
225            _ => None,
226        }
227    }
228
229    fn to_str(&self) -> &str {
230        match self {
231            JustifyContent::Start => "start",
232            JustifyContent::Center => "center",
233            JustifyContent::End => "end",
234            JustifyContent::SpaceBetween => "space-between",
235            JustifyContent::SpaceAround => "space-around",
236            JustifyContent::SpaceEvenly => "space-evenly",
237        }
238    }
239}
240
241#[derive(Clone, Copy, Debug, PartialEq, Eq)]
242pub enum AlignItems {
243    Start,
244    Center,
245    End,
246    Stretch,
247}
248
249impl Default for AlignItems {
250    fn default() -> Self {
251        Self::Start
252    }
253}
254
255impl AlignItems {
256    pub fn align(&self, start: f32, end: f32, size: f32) -> f32 {
257        match self {
258            AlignItems::Start => start,
259            AlignItems::Center => start + (end - start - size) / 2.0,
260            AlignItems::End => end - size,
261            AlignItems::Stretch => start,
262        }
263    }
264}
265
266impl StyleAttributeEnum for AlignItems {
267    fn from_str(s: &str) -> Option<Self> {
268        match s {
269            "start" => Some(AlignItems::Start),
270            "center" => Some(AlignItems::Center),
271            "end" => Some(AlignItems::End),
272            "stretch" => Some(AlignItems::Stretch),
273            _ => None,
274        }
275    }
276
277    fn to_str(&self) -> &str {
278        match self {
279            AlignItems::Start => "start",
280            AlignItems::Center => "center",
281            AlignItems::End => "end",
282            AlignItems::Stretch => "stretch",
283        }
284    }
285}