1
- import sys
2
1
from PyQt6 .QtWidgets import QApplication ,QWidget ,QPushButton ,QVBoxLayout ,QHBoxLayout ,QLabel ,QStyleOption ,QStyle
3
2
from PyQt6 import QtCore ,QtGui
4
3
from PyQt6 .QtGui import QCursor
@@ -154,11 +153,19 @@ def show_main_window(self):
154
153
155
154
156
155
class MainWindow (BaseStyledWidget ,AnimatedWidget ):
157
- def __init__ (self ,parent_button ,uptime ,blur ,blur_background ,animation_duration ,button_row ,buttons ):
156
+ def __init__ (self ,parent_button ,uptime ,blur ,blur_background ,animation_duration ,button_row ,buttons ):
158
157
super (MainWindow ,self ).__init__ (animation_duration )
159
158
160
159
self .overlay = OverlayWidget (animation_duration ,uptime )
161
160
self .parent_button = parent_button
161
+ self .button_row = button_row # Store button_row as instance attribute
162
+
163
+ # Add focus policy to allow keyboard focus
164
+ self .setFocusPolicy (Qt .FocusPolicy .StrongFocus )
165
+
166
+ # Initialize variables to track focused button
167
+ self .buttons_list = []
168
+ self .current_focus_index = - 1
162
169
163
170
self .setProperty ("class" ,"power-menu-popup" )
164
171
self .setWindowFlags (Qt .WindowType .FramelessWindowHint | Qt .WindowType .WindowStaysOnTopHint | Qt .WindowType .Tool )
@@ -175,8 +182,6 @@ def __init__(self, parent_button, uptime,blur, blur_background, animation_durati
175
182
icon ,text = button_info
176
183
self .buttons_info .append ((icon ,text ,action_method ,button_name ))
177
184
178
-
179
-
180
185
main_layout = QVBoxLayout ()
181
186
button_layout1 = QHBoxLayout ()
182
187
button_layout2 = QHBoxLayout ()
@@ -190,6 +195,9 @@ def __init__(self, parent_button, uptime,blur, blur_background, animation_durati
190
195
button .setProperty ("class" ,f"button{ class_name } " )
191
196
button_layout = QVBoxLayout (button )
192
197
198
+ # Store buttons in a list for navigation
199
+ self .buttons_list .append (button )
200
+
193
201
# Only add icon label if icon is not empty or None
194
202
if icon :
195
203
icon_label = QLabel (f'{ icon } ' ,self )
@@ -214,6 +222,7 @@ def __init__(self, parent_button, uptime,blur, blur_background, animation_durati
214
222
215
223
button .clicked .connect (action )
216
224
button .installEventFilter (self )
225
+
217
226
218
227
main_layout .addLayout (button_layout1 )
219
228
main_layout .addLayout (button_layout2 )
@@ -241,7 +250,6 @@ def __init__(self, parent_button, uptime,blur, blur_background, animation_durati
241
250
BorderColor = "None"
242
251
)
243
252
244
-
245
253
self .fade_in ()
246
254
247
255
def center_on_screen (self ):
@@ -255,7 +263,6 @@ def center_on_screen(self):
255
263
self .move (x ,y )
256
264
self .overlay .update_geometry (screen_geometry )# Update overlay geometry to match screen
257
265
258
-
259
266
def paintEvent (self ,event ):
260
267
option = QStyleOption ()
261
268
option .initFrom (self )
@@ -276,7 +283,107 @@ def eventFilter(self, source, event):
276
283
def keyPressEvent (self ,event ):
277
284
if event .key ()== Qt .Key .Key_Escape :
278
285
self .cancel_action ()
279
- super (MainWindow ,self ).keyPressEvent (event )
286
+ event .accept ()# Mark event as handled
287
+ elif event .key ()== Qt .Key .Key_Right :
288
+ self .navigate_focus (1 )
289
+ event .accept ()# Mark event as handled
290
+ elif event .key ()== Qt .Key .Key_Left :
291
+ self .navigate_focus (- 1 )
292
+ event .accept ()# Mark event as handled
293
+ elif event .key ()== Qt .Key .Key_Down :
294
+ self .navigate_focus (self .button_row )
295
+ event .accept ()# Mark event as handled
296
+ elif event .key ()== Qt .Key .Key_Up :
297
+ self .navigate_focus (- self .button_row )
298
+ event .accept ()# Mark event as handled
299
+ elif event .key ()in (Qt .Key .Key_Return ,Qt .Key .Key_Enter ,Qt .Key .Key_Space ):
300
+ # Trigger click on the focused button
301
+ if 0 <= self .current_focus_index < len (self .buttons_list ):
302
+ self .buttons_list [self .current_focus_index ].click ()
303
+ event .accept ()# Mark event as handled
304
+ else :
305
+ super (MainWindow ,self ).keyPressEvent (event )
306
+
307
+ def navigate_focus (self ,step ):
308
+ """Navigate button focus by step."""
309
+ if not self .buttons_list :
310
+ return
311
+
312
+ total_buttons = len (self .buttons_list )
313
+
314
+ # If no button is currently focused, start with the appropriate first button
315
+ if self .current_focus_index < 0 or self .current_focus_index >= total_buttons :
316
+ if step > 0 :
317
+ # When pressing right arrow with no selection, select the first button
318
+ new_index = 0
319
+ elif step < 0 :
320
+ # When pressing left arrow with no selection, select the last button
321
+ new_index = total_buttons - 1
322
+ else :
323
+ # For other keys with no selection, default to first button
324
+ new_index = 0
325
+ else :
326
+ # Normal navigation with existing selection
327
+ current = self .current_focus_index
328
+
329
+ # Simple navigation with wrapping
330
+ if step == 1 :# Right
331
+ new_index = (current + 1 )% total_buttons
332
+ elif step == - 1 :# Left
333
+ new_index = (current - 1 )% total_buttons
334
+ elif step == self .button_row :# Down
335
+ new_index = (current + self .button_row )% total_buttons
336
+ elif step == - self .button_row :# Up
337
+ new_index = (current - self .button_row )% total_buttons
338
+ else :
339
+ new_index = current # No change
340
+
341
+ self .set_focused_button (new_index )
342
+
343
+ def set_focused_button (self ,index ):
344
+ """Set focus to the button at the given index."""
345
+ if not self .buttons_list :
346
+ return
347
+
348
+ # Safety check - ensure index is valid
349
+ if index < 0 or index >= len (self .buttons_list ):
350
+ return
351
+
352
+ # Update our internal tracking
353
+ self .current_focus_index = index
354
+
355
+ # First, remove hover from all buttons
356
+ for i ,button in enumerate (self .buttons_list ):
357
+ # Parse class components
358
+ class_parts = button .property ('class' ).split ()
359
+ # Remove any hover class if present
360
+ if 'hover' in class_parts :
361
+ class_parts .remove ('hover' )
362
+ # Set class without hover
363
+ clean_class = ' ' .join (class_parts )
364
+ button .setProperty ("class" ,clean_class )
365
+ button .style ().unpolish (button )
366
+ button .style ().polish (button )
367
+
368
+ # Then apply hover to the selected button
369
+ current_button = self .buttons_list [self .current_focus_index ]
370
+ current_class = current_button .property ('class' )
371
+
372
+ # Add hover class
373
+ hover_class = f"{ current_class } hover"
374
+ current_button .setProperty ("class" ,hover_class )
375
+ current_button .style ().unpolish (current_button )
376
+ current_button .style ().polish (current_button )
377
+
378
+ self .setFocus ()
379
+
380
+ def showEvent (self ,event ):
381
+ """Override show event to set focus."""
382
+ super (MainWindow ,self ).showEvent (event )
383
+ # Set focus to the window and first button when shown
384
+ self .setFocus ()
385
+ self .current_focus_index = - 1
386
+
280
387
281
388
def signout_action (self ):
282
389
self .power_operations .signout ()
@@ -303,4 +410,4 @@ def hibernate_action(self):
303
410
self .power_operations .hibernate ()
304
411
305
412
def cancel_action (self ):
306
- self .power_operations .cancel ()
413
+ self .power_operations .cancel ()