1#[derive(Default)]
2pub struct Button {
3 pub label: Option<crate::ui::text::Text>,
4 pub label_size: f32,
5 pub text_alignment: Alignment,
6
7 pub pos_x: usize,
9 pub pos_y: usize,
11
12 pub width: usize,
14 pub height: usize,
16
17 pub border_size_idle: usize,
19 pub border_size_hovered: usize,
21 pub border_size_clicked: usize,
23
24 pub shadow_size_idle: usize,
26 pub shadow_size_hovered: usize,
28 pub shadow_size_clicked: usize,
30 pub shadow_intensity_idle: u8,
32 pub shadow_intensity_hovered: u8,
34 pub shadow_intensity_clicked: u8,
36
37 pub label_col_idle: crate::color::Color,
39 pub border_col_idle: crate::color::Color,
41 pub bg_col_idle: crate::color::Color,
43
44 pub label_col_hovered: crate::color::Color,
46 pub border_col_hovered: crate::color::Color,
48 pub bg_col_hovered: crate::color::Color,
50
51 pub label_col_clicked: crate::color::Color,
53 pub border_col_clicked: crate::color::Color,
55 pub bg_col_clicked: crate::color::Color,
57
58 pub button_type: ButtonType,
60
61 pub state: ButtonState,
63
64 pub toggled: bool,
66
67 pub radius: usize,
69}
70
71impl Button {
72 pub fn label(mut self, text: &str, font: crate::ttf::Font, size: f32) -> Self {
74 self.label = Some(crate::ui::text::Text::new(text, font));
75 self.label_size = size;
76 self
77 }
78
79 pub fn label_alignment(mut self, alignment: Alignment) -> Self {
81 self.text_alignment = alignment;
82 self
83 }
84
85 pub fn position(mut self, x: usize, y: usize) -> Self {
87 self.pos_x = x;
88 self.pos_y = y;
89 self
90 }
91
92 pub fn size(mut self, width: usize, height: usize) -> Self {
94 self.width = width;
95 self.height = height;
96 self
97 }
98
99 pub fn border(mut self, size: usize) -> Self {
101 self.border_size_idle = size;
102 self.border_size_hovered = size;
103 self.border_size_clicked = size;
104 self
105 }
106
107 pub fn shadow(mut self, size: usize, intensity: u8) -> Self {
110 self.shadow_size_idle = size;
111 self.shadow_size_hovered = size;
112 self.shadow_size_clicked = size;
113 self.shadow_intensity_idle = intensity;
114 self.shadow_intensity_hovered = intensity;
115 self.shadow_intensity_clicked = intensity;
116 self
117 }
118
119 pub fn idle_shadow(mut self, size: usize, intensity: u8) -> Self {
121 self.shadow_size_idle = size;
122 self.shadow_intensity_idle = intensity;
123 self
124 }
125
126 pub fn hover_shadow(mut self, size: usize, intensity: u8) -> Self {
128 self.shadow_size_hovered = size;
129 self.shadow_intensity_hovered = intensity;
130 self
131 }
132
133 pub fn click_shadow(mut self, size: usize, intensity: u8) -> Self {
135 self.shadow_size_clicked = size;
136 self.shadow_intensity_clicked = intensity;
137 self
138 }
139
140 pub fn label_color(mut self, color: crate::color::Color) -> Self {
142 self.label_col_hovered = color.clone();
143 self.label_col_clicked = color.clone();
144 self.label_col_idle = color;
145 self
146 }
147
148 pub fn idle_label_col(mut self, color: crate::color::Color) -> Self {
150 self.label_col_idle = color;
151 self
152 }
153
154 pub fn hover_label_col(mut self, color: crate::color::Color) -> Self {
156 self.label_col_hovered = color;
157 self
158 }
159
160 pub fn click_label_col(mut self, color: crate::color::Color) -> Self {
162 self.label_col_clicked = color;
163 self
164 }
165
166 pub fn border_color(mut self, color: crate::color::Color) -> Self {
168 self.border_col_hovered = color.clone();
169 self.border_col_clicked = color.clone();
170 self.border_col_idle = color;
171 self
172 }
173
174 pub fn background(mut self, color: crate::color::Color) -> Self {
176 self.bg_col_hovered = color.clone();
177 self.bg_col_clicked = color.clone();
178 self.bg_col_idle = color;
179 self
180 }
181
182 pub fn idle_bg(mut self, color: crate::color::Color) -> Self {
184 self.bg_col_idle = color;
185 self
186 }
187
188 pub fn hover_bg(mut self, color: crate::color::Color) -> Self {
190 self.bg_col_hovered = color;
191 self
192 }
193
194 pub fn click_bg(mut self, color: crate::color::Color) -> Self {
196 self.bg_col_clicked = color;
197 self
198 }
199
200 pub fn button_type(mut self, button_type: ButtonType) -> Self {
202 self.button_type = button_type;
203 self
204 }
205
206 pub fn radius(mut self, radius: usize) -> Self {
208 self.radius = radius;
209 self
210 }
211
212 fn draw_shadow(&self, window: &mut crate::window::Window) {
213 let shadow_depth;
214 let shadow_size;
215 let border_size;
216
217 match self.state {
218 ButtonState::Idle => {
219 shadow_depth = self.shadow_intensity_idle;
220 shadow_size = self.shadow_size_idle;
221 border_size = self.border_size_idle;
222 },
223 ButtonState::Hovered => {
224 shadow_depth = self.shadow_intensity_hovered;
225 shadow_size = self.shadow_size_hovered;
226 border_size = self.border_size_hovered;
227 },
228 ButtonState::Clicked => {
229 shadow_depth = self.shadow_intensity_clicked;
230 shadow_size = self.shadow_size_clicked;
231 border_size = self.border_size_clicked;
232 }
233 }
234
235 let inner_x = self.pos_x + border_size;
237 let inner_y = self.pos_y + border_size;
238 let inner_w = self.width.saturating_sub(border_size * 2);
239 let inner_h = self.height.saturating_sub(border_size * 2);
240 let inner_r = self.radius.saturating_sub(border_size) as f32;
241
242 let cx = inner_x as f32 + inner_w as f32 / 2.0;
243 let cy = inner_y as f32 + inner_h as f32 / 2.0;
244 let hw = inner_w as f32 / 2.0;
245 let hh = inner_h as f32 / 2.0;
246
247 for py in inner_y..inner_y + inner_h {
248 for px in inner_x..inner_x + inner_w {
249 let pfx = px as f32 + 0.5;
251 let pfy = py as f32 + 0.5;
252 let dx = (pfx - cx).abs() - (hw - inner_r);
253 let dy = (pfy - cy).abs() - (hh - inner_r);
254 let outside = (dx.max(0.0).powi(2) + dy.max(0.0).powi(2)).sqrt();
255 let inside = dx.max(dy).min(0.0);
256 let dist = outside + inside - inner_r; let depth = -dist; if depth > 0.0 && depth <= shadow_size as f32 {
262 let t = 1.0 - (depth / shadow_size as f32);
263 let blend = (shadow_depth as f32 * t) as i32;
264 if blend > 0 {
265 darken_pixel(window, px, py, blend);
266 }
267 }
268 }
269 }
270 }
271
272 fn draw_button(&self, window: &mut crate::window::Window) {
274 let bg_col: &crate::color::Color;
275 let border_col: &crate::color::Color;
276 let label_col: &crate::color::Color;
277 let border_size;
278
279 match self.state {
280 ButtonState::Idle => {
281 bg_col = &self.bg_col_idle;
282 border_col = &self.border_col_idle;
283 label_col = &self.label_col_idle;
284 border_size = self.border_size_idle;
285 }
286 ButtonState::Hovered =>{
287 bg_col = &self.bg_col_hovered;
288 border_col = &self.border_col_hovered;
289 label_col = &self.label_col_hovered;
290 border_size = self.border_size_hovered;
291 },
292 ButtonState::Clicked => {
293 bg_col = &self.bg_col_clicked;
294 border_col = &self.border_col_clicked;
295 label_col = &self.label_col_clicked;
296 border_size = self.border_size_clicked;
297 },
298 }
299
300 window.draw_rect_f(
301 self.pos_x,
302 self.pos_y,
303 self.width,
304 self.height,
305 self.radius,
306 bg_col,
307 0,
308 );
309
310 for i in 0..border_size {
311 window.draw_rect(
312 self.pos_x + i,
313 self.pos_y + i,
314 self.width - i * 2,
315 self.height - i * 2,
316 self.radius.saturating_sub(i),
317 border_col,
318 );
319 }
320
321 if let Some(label) = &self.label {
322 let lm = label.font.font.horizontal_line_metrics(self.label_size).unwrap();
323
324 let y_pos = (self.pos_y as f32 + (self.height as f32 / 2.0) - (lm.ascent / 2.0)
325 + (lm.descent / 3.0))
326 .max(0.0) as usize;
327
328 let text_width = label.get_width_precise(self.label_size);
329
330 match self.text_alignment {
331 Alignment::Left => {
332 window.draw_text(
333 self.pos_x + border_size + 4,
334 y_pos,
335 &label,
336 self.label_size,
337 label_col,
338 );
339 }
340 Alignment::Right => {
341 let x = (self.pos_x + self.width) as f32 - text_width - 4.0;
342 window.draw_text(
343 x.max(0.0) as usize,
344 y_pos,
345 &label,
346 self.label_size,
347 label_col,
348 );
349 }
350 Alignment::Center => {
351 let x = self.pos_x as f32 + (self.width as f32 / 2.0) - (text_width / 2.0);
352 window.draw_text(
353 x.max(0.0) as usize,
354 y_pos,
355 &label,
356 self.label_size,
357 label_col,
358 );
359 }
360 }
361 }
362
363 }
364
365 pub fn is_hovered(&self, window: &crate::window::Window) -> bool {
366 let state = window.get_mouse_state();
367 (state.pos_x as usize) > self.pos_x
368 && (state.pos_y as usize) > self.pos_y
369 && (state.pos_x as usize) < self.pos_x + self.width
370 && (state.pos_y as usize) < self.pos_y + self.height
371 }
372
373 pub fn is_left_clicked(&self, window: &crate::window::Window) -> bool {
374 let state = window.get_mouse_state();
375 self.is_hovered(window) && state.lmb_clicked
376 }
377
378 pub fn is_right_clicked(&self, window: &crate::window::Window) -> bool {
379 let state = window.get_mouse_state();
380 self.is_hovered(window) && state.rmb_clicked
381 }
382
383 fn update(&mut self, window: &mut crate::window::Window) {
384 let mouse = window.get_mouse_state();
385 let hovered = (mouse.pos_x as usize) > self.pos_x
386 && (mouse.pos_y as usize) > self.pos_y
387 && (mouse.pos_x as usize) < self.pos_x + self.width
388 && (mouse.pos_y as usize) < self.pos_y + self.height;
389 let clicked = hovered && (mouse.lmb_clicked || mouse.rmb_clicked);
390
391 match self.button_type {
392 ButtonType::Push => {
393 self.state = if clicked {
394 ButtonState::Clicked
395 } else if hovered {
396 ButtonState::Hovered
397 } else {
398 ButtonState::Idle
399 };
400 }
401 ButtonType::Toggle => {
402 if clicked && !self.toggled {
403 self.toggled = true;
404 } else if clicked && self.toggled {
405 self.toggled = false;
406 }
407 self.state = if self.toggled {
408 ButtonState::Clicked
409 } else if hovered {
410 ButtonState::Hovered
411 } else {
412 ButtonState::Idle
413 };
414 }
415 }
416 }
417
418 pub fn draw(&mut self, window: &mut crate::window::Window) {
420 self.update(window);
421 self.draw_button(window);
422 self.draw_shadow(window);
423 }
424}
425
426#[derive(Default)]
427pub enum ButtonType {
428 #[default]
429 Push,
430 Toggle,
431}
432
433#[derive(Default)]
434pub enum Alignment {
435 Left,
436 Right,
437 #[default]
438 Center,
439}
440
441#[derive(Default)]
442pub enum ButtonState {
443 #[default]
444 Idle,
445 Hovered,
446 Clicked,
447}
448
449fn darken_pixel(window: &mut crate::window::Window, x: usize, y: usize, blend: i32) {
450 let existing = window.get_pixel(x, y);
451 let er = ((existing >> 16) & 0xFF) as i32;
452 let eg = ((existing >> 8) & 0xFF) as i32;
453 let eb = ((existing) & 0xFF) as i32;
454
455 let r = (er - blend).clamp(0, 255) as u32;
456 let g = (eg - blend).clamp(0, 255) as u32;
457 let b = (eb - blend).clamp(0, 255) as u32;
458
459 window.draw_pixel(
460 x,
461 y,
462 &crate::color::Color::from(0xFF000000 | (r << 16) | (g << 8) | b),
463 );
464}