1pub use egui::Key;
2
3#[derive(Clone, Copy)]
4pub enum HorizontalAlignment {
5 Centered,
6 Left,
7 Right,
8}
9
10#[derive(Clone, Copy)]
11pub enum VerticalAlignment {
12 Centered,
13 Top,
14 Bottom,
15}
16
17pub mod color {
18
19 pub enum ColorError {
20 HueOutOfRange,
21 SaturationOutOfRange,
22 LightnessOutOfRange,
23 }
24
25 pub const BLACK: Color = Color::rgb(0, 0, 0);
26 pub const WHITE: Color = Color::rgb(255, 255, 255);
27
28 pub struct Color {
29 r: u8,
30 g: u8,
31 b: u8,
32 }
33
34 impl Color {
35 pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
36 Self { r, g, b }
37 }
38
39 pub fn hsl(h: u16, s: f32, l: f32) -> Result<Self, ColorError> {
40 if h > 360 {
41 return Err(ColorError::HueOutOfRange);
42 }
43
44 if !(0. ..=1.).contains(&s) {
45 return Err(ColorError::SaturationOutOfRange);
46 }
47
48 if !(0. ..=1.).contains(&l) {
49 return Err(ColorError::LightnessOutOfRange);
50 }
51
52 let (r, g, b) = if s == 0. {
53 (l, l, l)
54 } else {
55 let q = if l < 0.5 {
56 l * (1_f32 + s)
57 } else {
58 l + s - l * s
59 };
60 let p = 2. * l - q;
61 (
62 Color::hue_to_rgb(p, q, f32::from(h) + 1. / 3.),
63 Color::hue_to_rgb(p, q, f32::from(h)),
64 Color::hue_to_rgb(p, q, f32::from(h) - 1. / 3.),
65 )
66 };
67
68 Ok(Self {
69 r: (r * 255.) as u8,
70 g: (g * 255.) as u8,
71 b: (b * 255.) as u8,
72 })
73 }
74
75 fn hue_to_rgb(p: f32, q: f32, mut t: f32) -> f32 {
76 if t < 0. {
77 t += 1.;
78 }
79
80 if t > 1. {
81 t -= 1.;
82 }
83
84 if t < 1. / 6. {
85 return p + (q - p) * 6. * t;
86 }
87
88 if t < 0.5 {
89 return q;
90 }
91
92 if t < 2. / 3. {
93 return p + (q - p) * (2. / 3. - t) * 6.;
94 }
95
96 p
97 }
98
99 pub fn red(&self) -> u8 {
100 self.r
101 }
102
103 pub fn blue(&self) -> u8 {
104 self.b
105 }
106
107 pub fn green(&self) -> u8 {
108 self.g
109 }
110 }
111}
112
113pub struct Text {
114 color: color::Color,
115 text: String,
116 layout: Layout,
117}
118
119impl Text {
120 pub fn new(text: &str) -> Self {
121 Self {
122 text: text.to_owned(),
123 color: color::BLACK,
124 layout: Layout::Cell(TextCell::centered()),
125 }
126 }
127
128 pub const fn color(mut self, color: color::Color) -> Self {
129 self.color = color;
130 self
131 }
132
133 pub fn text(&self) -> &str {
134 &self.text
135 }
136}
137
138impl Component for Text {
139 fn layout(&self) -> &Layout {
140 &self.layout
141 }
142}
143
144pub struct TextCell {
145 horizontal_alignment: HorizontalAlignment,
146 vertical_alignment: VerticalAlignment,
147 horizontal_offset: i32,
148 vertical_offset: i32,
149}
150
151impl TextCell {
152 pub fn centered() -> Self {
153 Self {
154 horizontal_alignment: HorizontalAlignment::Centered,
155 vertical_alignment: VerticalAlignment::Centered,
156 horizontal_offset: 0,
157 vertical_offset: 0,
158 }
159 }
160
161 pub fn align_horizontally(mut self, horizontal_alignment: HorizontalAlignment) -> Self {
162 self.horizontal_alignment = horizontal_alignment;
163 self
164 }
165
166 pub fn align_vertically(mut self, vertical_alignment: VerticalAlignment) -> Self {
167 self.vertical_alignment = vertical_alignment;
168 self
169 }
170
171 pub fn offset_horizontally(mut self, horizontal_offset: i32) -> Self {
172 self.horizontal_offset = horizontal_offset;
173 self
174 }
175
176 pub fn offset_vertically(mut self, vertical_offset: i32) -> Self {
177 self.vertical_offset = vertical_offset;
178 self
179 }
180
181 pub fn horizontal_offset(&self) -> i32 {
182 self.horizontal_offset
183 }
184
185 pub fn vertical_offset(&self) -> i32 {
186 self.vertical_offset
187 }
188
189 pub fn horizontal_alignment(&self) -> HorizontalAlignment {
190 self.horizontal_alignment
191 }
192
193 pub fn vertical_alignment(&self) -> VerticalAlignment {
194 self.vertical_alignment
195 }
196}
197
198pub enum Layout {
200 Column(Column),
201 Row(Row),
202 Grid(Grid),
203
204 Cell(TextCell),
206
207 Empty,
208}
209
210pub struct Grid {
211 components: Vec<Vec<Option<Box<dyn Component>>>>,
212}
213
214impl Grid {
215 pub fn of_size(width: u32, height: u32) -> Self {
216 let mut components = Vec::new();
217 for _column in 0..width {
218 let mut row_vector = Vec::new();
219 for _row in 0..height {
220 row_vector.push(None)
221 }
222 components.push(row_vector);
223 }
224
225 Self { components }
226 }
227
228 pub fn set(&mut self, row: u32, column: u32, component: Box<dyn Component>) {
229 self.components[row as usize][column as usize] = Some(component);
230 }
231}
232
233impl From<Grid> for Layout {
234 fn from(value: Grid) -> Self {
235 Self::Grid(value)
236 }
237}
238
239pub struct Row {
240 components: Vec<Box<dyn Component>>,
241}
242
243#[derive(Default)]
244pub struct Column {
245 components: Vec<Box<dyn Component>>,
246}
247
248impl Column {
249 pub fn add_to_bottom(&mut self, component: Box<dyn Component>) {
250 self.components.push(component);
251 }
252
253 pub fn add_to_top(&mut self, component: Box<dyn Component>) {
254 let mut components = vec![component];
255 components.append(&mut self.components);
256 self.components = components;
257 }
258
259 pub fn height(&self) -> usize {
260 self.components.len()
261 }
262
263 pub fn into_layout(self) -> Layout {
264 self.into()
265 }
266}
267
268impl From<Column> for Layout {
269 fn from(value: Column) -> Self {
270 Self::Column(value)
271 }
272}
273
274pub trait Component {
275 fn layout(&self) -> &Layout;
276
277 fn update(&mut self) {}
280 fn on_mouse_down(&mut self) {}
281 fn on_mouse_up(&mut self) {}
282 fn on_key_down(&mut self, key: Key) {}
283 fn on_key_up(&mut self) {}
284 fn on_right_mouse_down(&mut self) {}
285 fn on_right_mouse_up(&mut self) {}
286}
287
288pub trait Parent {
289 fn children(&self) -> Vec<&dyn Component>;
290}
291
292impl<T: Component + ?Sized> Parent for &T {
293 fn children(&self) -> Vec<&dyn Component> {
294 match self.layout() {
295 Layout::Column(column) => column
296 .components
297 .iter()
298 .map(|component| component.as_ref())
299 .collect(),
300 Layout::Row(row) => row
301 .components
302 .iter()
303 .map(|component| component.as_ref())
304 .collect(),
305 Layout::Grid(grid) => grid
306 .components
307 .iter()
308 .flatten()
309 .filter(|component| component.is_some())
310 .map(|component| component.as_ref().unwrap().as_ref())
311 .collect(),
312 Layout::Empty | Layout::Cell(_) => Vec::new(),
313 }
314 }
315}
316
317pub enum Length {
318 Percent(i32),
319 Pixels(i32),
320 FitContent,
321}
322
323pub trait Unit {
324 fn percent(&self) -> Length;
325 fn pixels(&self) -> Length;
326}
327
328impl<T: Into<i32> + Clone> Unit for T {
329 fn percent(&self) -> Length {
330 Length::Percent(self.clone().into())
331 }
332
333 fn pixels(&self) -> Length {
334 Length::Pixels(self.clone().into())
335 }
336}