gardesk/gardisplay / 0626bbb

Browse files

add button widget

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
0626bbbdf31b04dc20484544717f5bfee5f1a357
Parents
d8541fd
Tree
fe43534

2 changed files

StatusFile+-
A gardisplay/src/ui/widgets/button.rs 100 0
A gardisplay/src/ui/widgets/mod.rs 5 0
gardisplay/src/ui/widgets/button.rsadded
@@ -0,0 +1,100 @@
1
+//! Simple button widget.
2
+
3
+use gartk_core::{InputEvent, MouseButton, Point, Rect, Theme};
4
+use gartk_render::Renderer;
5
+
6
+/// A clickable button.
7
+pub struct Button {
8
+    rect: Rect,
9
+    label: String,
10
+    hovered: bool,
11
+    pressed: bool,
12
+    enabled: bool,
13
+}
14
+
15
+impl Button {
16
+    /// Create a new button.
17
+    pub fn new(x: i32, y: i32, width: u32, height: u32, label: &str) -> Self {
18
+        Self {
19
+            rect: Rect::new(x, y, width, height),
20
+            label: label.to_string(),
21
+            hovered: false,
22
+            pressed: false,
23
+            enabled: true,
24
+        }
25
+    }
26
+
27
+    /// Set button position.
28
+    pub fn set_position(&mut self, x: i32, y: i32) {
29
+        self.rect.x = x;
30
+        self.rect.y = y;
31
+    }
32
+
33
+    /// Set enabled state.
34
+    #[allow(dead_code)]
35
+    pub fn set_enabled(&mut self, enabled: bool) {
36
+        self.enabled = enabled;
37
+    }
38
+
39
+    /// Check if a point is inside the button.
40
+    fn contains(&self, point: Point) -> bool {
41
+        self.rect.contains_point(point)
42
+    }
43
+
44
+    /// Handle input event. Returns true if button was clicked.
45
+    pub fn handle_event(&mut self, event: &InputEvent) -> bool {
46
+        if !self.enabled {
47
+            return false;
48
+        }
49
+
50
+        match event {
51
+            InputEvent::MouseMove(e) => {
52
+                self.hovered = self.contains(e.position);
53
+                false
54
+            }
55
+            InputEvent::MousePress(e) if e.button == Some(MouseButton::Left) => {
56
+                if self.contains(e.position) {
57
+                    self.pressed = true;
58
+                }
59
+                false
60
+            }
61
+            InputEvent::MouseRelease(e) if e.button == Some(MouseButton::Left) => {
62
+                let clicked = self.pressed && self.contains(e.position);
63
+                self.pressed = false;
64
+                clicked
65
+            }
66
+            _ => false,
67
+        }
68
+    }
69
+
70
+    /// Render the button.
71
+    pub fn render(&self, renderer: &Renderer, theme: &Theme) -> anyhow::Result<()> {
72
+        let (bg_color, text_color) = if !self.enabled {
73
+            (theme.item_background, theme.item_description)
74
+        } else if self.pressed {
75
+            (theme.selection_background, theme.foreground)
76
+        } else if self.hovered {
77
+            (theme.item_hover_background, theme.foreground)
78
+        } else {
79
+            (theme.item_background, theme.foreground)
80
+        };
81
+
82
+        // Background
83
+        renderer.fill_rounded_rect(self.rect, 6.0, bg_color)?;
84
+
85
+        // Border
86
+        let border_color = if self.hovered && self.enabled {
87
+            theme.selection_background
88
+        } else {
89
+            theme.border
90
+        };
91
+        renderer.stroke_rounded_rect(self.rect, 6.0, border_color, 1.0)?;
92
+
93
+        // Label (centered)
94
+        let text_x = self.rect.x as f64 + (self.rect.width as f64 / 2.0) - (self.label.len() as f64 * 3.5);
95
+        let text_y = self.rect.y as f64 + (self.rect.height as f64 / 2.0) - 6.0;
96
+        renderer.text_default(&self.label, text_x, text_y, text_color)?;
97
+
98
+        Ok(())
99
+    }
100
+}
gardisplay/src/ui/widgets/mod.rsadded
@@ -0,0 +1,5 @@
1
+//! UI widgets for gardisplay.
2
+
3
+mod button;
4
+
5
+pub use button::Button;