Rust · 5376 bytes Raw Blame History
1 //! Power control buttons widget
2 //!
3 //! Shutdown, reboot, and suspend buttons with hover effects.
4
5 use crate::icons;
6 use crate::render::rounded_rectangle;
7 use anyhow::Result;
8 use cairo::Context;
9
10 /// Power button action types
11 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
12 pub enum PowerAction {
13 Shutdown,
14 Reboot,
15 Suspend,
16 }
17
18 /// Individual power button
19 struct PowerButton {
20 action: PowerAction,
21 x: f64,
22 y: f64,
23 size: f64,
24 hovered: bool,
25 }
26
27 impl PowerButton {
28 fn new(action: PowerAction, x: f64, y: f64, size: f64) -> Self {
29 Self {
30 action,
31 x,
32 y,
33 size,
34 hovered: false,
35 }
36 }
37
38 fn contains(&self, px: f64, py: f64) -> bool {
39 px >= self.x && px <= self.x + self.size && py >= self.y && py <= self.y + self.size
40 }
41
42 fn render(&self, ctx: &Context) -> Result<()> {
43 let icon_size = self.size * 0.5;
44 let icon_x = self.x + (self.size - icon_size) / 2.0;
45 let icon_y = self.y + (self.size - icon_size) / 2.0;
46
47 // Background with hover effect
48 if self.hovered {
49 // Glow effect on hover
50 ctx.set_source_rgba(1.0, 1.0, 1.0, 0.15);
51 rounded_rectangle(ctx, self.x, self.y, self.size, self.size, 12.0);
52 ctx.fill()?;
53
54 // Brighter icon on hover
55 let (r, g, b) = self.hover_color();
56 ctx.set_source_rgb(r, g, b);
57 } else {
58 // Normal semi-transparent background
59 ctx.set_source_rgba(1.0, 1.0, 1.0, 0.05);
60 rounded_rectangle(ctx, self.x, self.y, self.size, self.size, 12.0);
61 ctx.fill()?;
62
63 // Normal icon color
64 ctx.set_source_rgba(0.8, 0.8, 0.8, 0.9);
65 }
66
67 // Draw the icon
68 match self.action {
69 PowerAction::Shutdown => icons::draw_power(ctx, icon_x, icon_y, icon_size),
70 PowerAction::Reboot => icons::draw_reboot(ctx, icon_x, icon_y, icon_size),
71 PowerAction::Suspend => icons::draw_suspend(ctx, icon_x, icon_y, icon_size),
72 }
73
74 Ok(())
75 }
76
77 /// Get the hover highlight color for each action
78 fn hover_color(&self) -> (f64, f64, f64) {
79 match self.action {
80 PowerAction::Shutdown => (1.0, 0.4, 0.4), // Red tint
81 PowerAction::Reboot => (0.4, 0.8, 1.0), // Blue tint
82 PowerAction::Suspend => (0.9, 0.8, 0.4), // Yellow/gold tint
83 }
84 }
85 }
86
87 /// Power buttons panel
88 pub struct PowerButtons {
89 buttons: Vec<PowerButton>,
90 }
91
92 impl PowerButtons {
93 /// Create power buttons positioned in bottom-right corner of given area
94 /// For multi-monitor: pass the primary monitor's x, y, width, height
95 pub fn new(area_x: f64, area_y: f64, area_width: f64, area_height: f64) -> Self {
96 let button_size = 48.0;
97 let spacing = 12.0;
98 let margin = 24.0;
99
100 // Position in bottom-right corner of the area
101 let start_x = area_x + area_width - margin - (button_size * 3.0 + spacing * 2.0);
102 let y = area_y + area_height - margin - button_size;
103
104 let buttons = vec![
105 PowerButton::new(PowerAction::Suspend, start_x, y, button_size),
106 PowerButton::new(
107 PowerAction::Reboot,
108 start_x + button_size + spacing,
109 y,
110 button_size,
111 ),
112 PowerButton::new(
113 PowerAction::Shutdown,
114 start_x + (button_size + spacing) * 2.0,
115 y,
116 button_size,
117 ),
118 ];
119
120 Self { buttons }
121 }
122
123 /// Render all power buttons
124 pub fn render(&self, ctx: &Context) -> Result<()> {
125 for button in &self.buttons {
126 button.render(ctx)?;
127 }
128 Ok(())
129 }
130
131 /// Update hover state based on mouse position
132 /// Returns true if any hover state changed
133 pub fn update_hover(&mut self, mouse_x: f64, mouse_y: f64) -> bool {
134 let mut changed = false;
135 for button in &mut self.buttons {
136 let was_hovered = button.hovered;
137 button.hovered = button.contains(mouse_x, mouse_y);
138 if button.hovered != was_hovered {
139 changed = true;
140 }
141 }
142 changed
143 }
144
145 /// Clear all hover states
146 pub fn clear_hover(&mut self) {
147 for button in &mut self.buttons {
148 button.hovered = false;
149 }
150 }
151
152 /// Check if a click hit any button and return the action
153 pub fn handle_click(&self, click_x: f64, click_y: f64) -> Option<PowerAction> {
154 for button in &self.buttons {
155 if button.contains(click_x, click_y) {
156 return Some(button.action);
157 }
158 }
159 None
160 }
161
162 /// Get which button is currently hovered (for cursor changes)
163 pub fn hovered_action(&self) -> Option<PowerAction> {
164 self.buttons
165 .iter()
166 .find(|b| b.hovered)
167 .map(|b| b.action)
168 }
169
170 /// Get the hovered button info for tooltip rendering (action, center_x, top_y)
171 pub fn hovered_tooltip_info(&self) -> Option<(PowerAction, f64, f64)> {
172 self.buttons.iter().find(|b| b.hovered).map(|b| {
173 let center_x = b.x + b.size / 2.0;
174 let top_y = b.y;
175 (b.action, center_x, top_y)
176 })
177 }
178 }
179