feat(input_manager): directional D-pad navigation for game cards
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
This commit is contained in:
		| @@ -30,7 +30,7 @@ class MainWindowProtocol(Protocol): | |||||||
|     gamesListWidget: QWidget |     gamesListWidget: QWidget | ||||||
|     currentDetailPage: QWidget | None |     currentDetailPage: QWidget | None | ||||||
|     current_exec_line: str | None |     current_exec_line: str | None | ||||||
|     current_add_game_dialog: QDialog | None  # Добавляем для отслеживания диалога |     current_add_game_dialog: QDialog | None | ||||||
|  |  | ||||||
| # Mapping of actions to evdev button codes, includes PlayStation, Xbox, and Switch controllers | # Mapping of actions to evdev button codes, includes PlayStation, Xbox, and Switch controllers | ||||||
| BUTTONS = { | BUTTONS = { | ||||||
| @@ -117,6 +117,225 @@ class InputManager(QObject): | |||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             logger.error(f"Error in handle_fullscreen_slot: {e}", exc_info=True) |             logger.error(f"Error in handle_fullscreen_slot: {e}", exc_info=True) | ||||||
|  |  | ||||||
|  |     @Slot(int) | ||||||
|  |     def handle_button_slot(self, button_code: int) -> None: | ||||||
|  |         try: | ||||||
|  |             # Игнорировать события геймпада, если игра запущена | ||||||
|  |             if getattr(self._parent, '_gameLaunched', False): | ||||||
|  |                 return | ||||||
|  |  | ||||||
|  |             app = QApplication.instance() | ||||||
|  |             if not app: | ||||||
|  |                 return | ||||||
|  |             active = QApplication.activeWindow() | ||||||
|  |             focused = QApplication.focusWidget() | ||||||
|  |  | ||||||
|  |             # Закрытие AddGameDialog на кнопку B | ||||||
|  |             if button_code in BUTTONS['back'] and isinstance(active, QDialog): | ||||||
|  |                 active.reject()  # Закрываем диалог | ||||||
|  |                 return | ||||||
|  |  | ||||||
|  |             # FullscreenDialog | ||||||
|  |             if isinstance(active, FullscreenDialog): | ||||||
|  |                 if button_code in BUTTONS['prev_tab']: | ||||||
|  |                     active.show_prev() | ||||||
|  |                 elif button_code in BUTTONS['next_tab']: | ||||||
|  |                     active.show_next() | ||||||
|  |                 elif button_code in BUTTONS['back']: | ||||||
|  |                     active.close() | ||||||
|  |                 return | ||||||
|  |  | ||||||
|  |             # Context menu for GameCard | ||||||
|  |             if isinstance(focused, GameCard): | ||||||
|  |                 if button_code in BUTTONS['context_menu']: | ||||||
|  |                     pos = QPoint(focused.width() // 2, focused.height() // 2) | ||||||
|  |                     focused._show_context_menu(pos) | ||||||
|  |                     return | ||||||
|  |  | ||||||
|  |             # Game launch on detail page | ||||||
|  |             if (button_code in BUTTONS['confirm'] or button_code in BUTTONS['confirm_stick']) and self._parent.currentDetailPage is not None: | ||||||
|  |                 if self._parent.current_exec_line: | ||||||
|  |                     self._parent.toggleGame(self._parent.current_exec_line, None) | ||||||
|  |                     return | ||||||
|  |  | ||||||
|  |             # Standard navigation | ||||||
|  |             if button_code in BUTTONS['confirm'] or button_code in BUTTONS['confirm_stick']: | ||||||
|  |                 self._parent.activateFocusedWidget() | ||||||
|  |             elif button_code in BUTTONS['back'] or button_code in BUTTONS['menu']: | ||||||
|  |                 self._parent.goBackDetailPage(getattr(self._parent, 'currentDetailPage', None)) | ||||||
|  |             elif button_code in BUTTONS['add_game']: | ||||||
|  |                 self._parent.openAddGameDialog() | ||||||
|  |             elif button_code in BUTTONS['prev_tab']: | ||||||
|  |                 idx = (self._parent.stackedWidget.currentIndex() - 1) % len(self._parent.tabButtons) | ||||||
|  |                 self._parent.switchTab(idx) | ||||||
|  |                 self._parent.tabButtons[idx].setFocus(Qt.FocusReason.OtherFocusReason) | ||||||
|  |             elif button_code in BUTTONS['next_tab']: | ||||||
|  |                 idx = (self._parent.stackedWidget.currentIndex() + 1) % len(self._parent.tabButtons) | ||||||
|  |                 self._parent.switchTab(idx) | ||||||
|  |                 self._parent.tabButtons[idx].setFocus(Qt.FocusReason.OtherFocusReason) | ||||||
|  |         except Exception as e: | ||||||
|  |             logger.error(f"Error in handle_button_slot: {e}", exc_info=True) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     @Slot(int, int, float) | ||||||
|  |     def handle_dpad_slot(self, code: int, value: int, current_time: float) -> None: | ||||||
|  |         try: | ||||||
|  |             # Игнорировать события геймпада, если игра запущена | ||||||
|  |             if getattr(self._parent, '_gameLaunched', False): | ||||||
|  |                 return | ||||||
|  |  | ||||||
|  |             app = QApplication.instance() | ||||||
|  |             if not app: | ||||||
|  |                 return | ||||||
|  |             active = QApplication.activeWindow() | ||||||
|  |  | ||||||
|  |             # Fullscreen horizontal navigation | ||||||
|  |             if isinstance(active, FullscreenDialog) and code == ecodes.ABS_HAT0X: | ||||||
|  |                 if value < 0: | ||||||
|  |                     active.show_prev() | ||||||
|  |                 elif value > 0: | ||||||
|  |                     active.show_next() | ||||||
|  |                 return | ||||||
|  |  | ||||||
|  |             # Handle repeated D-pad movement | ||||||
|  |             if value != 0: | ||||||
|  |                 if not self.axis_moving: | ||||||
|  |                     self.axis_moving = True | ||||||
|  |                 elif (current_time - self.last_move_time) < self.current_axis_delay: | ||||||
|  |                     return | ||||||
|  |                 self.last_move_time = current_time | ||||||
|  |                 self.current_axis_delay = self.repeat_axis_move_delay | ||||||
|  |             else: | ||||||
|  |                 self.axis_moving = False | ||||||
|  |                 self.current_axis_delay = self.initial_axis_move_delay | ||||||
|  |                 return | ||||||
|  |  | ||||||
|  |             # Library tab navigation (index 0) | ||||||
|  |             if self._parent.stackedWidget.currentIndex() == 0 and code in (ecodes.ABS_HAT0X, ecodes.ABS_HAT0Y): | ||||||
|  |                 focused = QApplication.focusWidget() | ||||||
|  |                 game_cards = self._parent.gamesListWidget.findChildren(GameCard) | ||||||
|  |                 if not game_cards: | ||||||
|  |                     return | ||||||
|  |  | ||||||
|  |                 scroll_area = self._parent.gamesListWidget.parentWidget() | ||||||
|  |                 while scroll_area and not isinstance(scroll_area, QScrollArea): | ||||||
|  |                     scroll_area = scroll_area.parentWidget() | ||||||
|  |  | ||||||
|  |                 # If no focused widget or not a GameCard, focus the first card | ||||||
|  |                 if not isinstance(focused, GameCard) or focused not in game_cards: | ||||||
|  |                     game_cards[0].setFocus() | ||||||
|  |                     if scroll_area: | ||||||
|  |                         scroll_area.ensureWidgetVisible(game_cards[0], 50, 50) | ||||||
|  |                     return | ||||||
|  |  | ||||||
|  |                 # Group cards by rows based on y-coordinate | ||||||
|  |                 rows = {} | ||||||
|  |                 for card in game_cards: | ||||||
|  |                     y = card.pos().y() | ||||||
|  |                     if y not in rows: | ||||||
|  |                         rows[y] = [] | ||||||
|  |                     rows[y].append(card) | ||||||
|  |                 # Sort cards in each row by x-coordinate | ||||||
|  |                 for y in rows: | ||||||
|  |                     rows[y].sort(key=lambda c: c.pos().x()) | ||||||
|  |                 # Sort rows by y-coordinate | ||||||
|  |                 sorted_rows = sorted(rows.items(), key=lambda x: x[0]) | ||||||
|  |  | ||||||
|  |                 # Find current row and column | ||||||
|  |                 current_y = focused.pos().y() | ||||||
|  |                 current_row_idx = next(i for i, (y, _) in enumerate(sorted_rows) if y == current_y) | ||||||
|  |                 current_row = sorted_rows[current_row_idx][1] | ||||||
|  |                 current_col_idx = current_row.index(focused) | ||||||
|  |  | ||||||
|  |                 if code == ecodes.ABS_HAT0X and value != 0:  # Left/Right | ||||||
|  |                     if value < 0:  # Left | ||||||
|  |                         next_col_idx = current_col_idx - 1 | ||||||
|  |                         if next_col_idx >= 0: | ||||||
|  |                             next_card = current_row[next_col_idx] | ||||||
|  |                             next_card.setFocus() | ||||||
|  |                             if scroll_area: | ||||||
|  |                                 scroll_area.ensureWidgetVisible(next_card, 50, 50) | ||||||
|  |                         else: | ||||||
|  |                             # Move to the last card of the previous row if available | ||||||
|  |                             if current_row_idx > 0: | ||||||
|  |                                 prev_row = sorted_rows[current_row_idx - 1][1] | ||||||
|  |                                 next_card = prev_row[-1] if prev_row else None | ||||||
|  |                                 if next_card: | ||||||
|  |                                     next_card.setFocus() | ||||||
|  |                                     if scroll_area: | ||||||
|  |                                         scroll_area.ensureWidgetVisible(next_card, 50, 50) | ||||||
|  |                     elif value > 0:  # Right | ||||||
|  |                         next_col_idx = current_col_idx + 1 | ||||||
|  |                         if next_col_idx < len(current_row): | ||||||
|  |                             next_card = current_row[next_col_idx] | ||||||
|  |                             next_card.setFocus() | ||||||
|  |                             if scroll_area: | ||||||
|  |                                 scroll_area.ensureWidgetVisible(next_card, 50, 50) | ||||||
|  |                         else: | ||||||
|  |                             # Move to the first card of the next row if available | ||||||
|  |                             if current_row_idx < len(sorted_rows) - 1: | ||||||
|  |                                 next_row = sorted_rows[current_row_idx + 1][1] | ||||||
|  |                                 next_card = next_row[0] if next_row else None | ||||||
|  |                                 if next_card: | ||||||
|  |                                     next_card.setFocus() | ||||||
|  |                                     if scroll_area: | ||||||
|  |                                         scroll_area.ensureWidgetVisible(next_card, 50, 50) | ||||||
|  |  | ||||||
|  |                 elif code == ecodes.ABS_HAT0Y and value != 0:  # Up/Down | ||||||
|  |                     if value > 0:  # Down | ||||||
|  |                         next_row_idx = current_row_idx + 1 | ||||||
|  |                         if next_row_idx < len(sorted_rows): | ||||||
|  |                             next_row = sorted_rows[next_row_idx][1] | ||||||
|  |                             # Find card in same column or closest | ||||||
|  |                             target_x = focused.pos().x() | ||||||
|  |                             next_card = min( | ||||||
|  |                                 next_row, | ||||||
|  |                                 key=lambda c: abs(c.pos().x() - target_x), | ||||||
|  |                                 default=None | ||||||
|  |                             ) | ||||||
|  |                             if next_card: | ||||||
|  |                                 next_card.setFocus() | ||||||
|  |                                 if scroll_area: | ||||||
|  |                                     scroll_area.ensureWidgetVisible(next_card, 50, 50) | ||||||
|  |                     elif value < 0:  # Up | ||||||
|  |                         next_row_idx = current_row_idx - 1 | ||||||
|  |                         if next_row_idx >= 0: | ||||||
|  |                             next_row = sorted_rows[next_row_idx][1] | ||||||
|  |                             # Find card in same column or closest | ||||||
|  |                             target_x = focused.pos().x() | ||||||
|  |                             next_card = min( | ||||||
|  |                                 next_row, | ||||||
|  |                                 key=lambda c: abs(c.pos().x() - target_x), | ||||||
|  |                                 default=None | ||||||
|  |                             ) | ||||||
|  |                             if next_card: | ||||||
|  |                                 next_card.setFocus() | ||||||
|  |                                 if scroll_area: | ||||||
|  |                                     scroll_area.ensureWidgetVisible(next_card, 50, 50) | ||||||
|  |                         elif current_row_idx == 0: | ||||||
|  |                             self._parent.tabButtons[0].setFocus(Qt.FocusReason.OtherFocusReason) | ||||||
|  |  | ||||||
|  |             # Vertical navigation in other tabs | ||||||
|  |             elif code == ecodes.ABS_HAT0Y and value != 0: | ||||||
|  |                 focused = QApplication.focusWidget() | ||||||
|  |                 page = self._parent.stackedWidget.currentWidget() | ||||||
|  |                 if value > 0:  # Down | ||||||
|  |                     if isinstance(focused, NavLabel): | ||||||
|  |                         focusables = page.findChildren(QWidget, options=Qt.FindChildOption.FindChildrenRecursively) | ||||||
|  |                         focusables = [w for w in focusables if w.focusPolicy() & Qt.FocusPolicy.StrongFocus] | ||||||
|  |                         if focusables: | ||||||
|  |                             focusables[0].setFocus() | ||||||
|  |                             return | ||||||
|  |                     elif focused: | ||||||
|  |                         focused.focusNextChild() | ||||||
|  |                         return | ||||||
|  |                 elif value < 0 and focused:  # Up | ||||||
|  |                     focused.focusPreviousChild() | ||||||
|  |                     return | ||||||
|  |  | ||||||
|  |         except Exception as e: | ||||||
|  |             logger.error(f"Error in handle_dpad_slot: {e}", exc_info=True) | ||||||
|  |  | ||||||
|     def eventFilter(self, obj: QObject, event: QEvent) -> bool: |     def eventFilter(self, obj: QObject, event: QEvent) -> bool: | ||||||
|         app = QApplication.instance() |         app = QApplication.instance() | ||||||
|         if not app: |         if not app: | ||||||
| @@ -171,48 +390,114 @@ class InputManager(QObject): | |||||||
|                 focused._show_context_menu(pos) |                 focused._show_context_menu(pos) | ||||||
|                 return True |                 return True | ||||||
|  |  | ||||||
|         # Navigation in Library tab |         # Library tab navigation | ||||||
|         if self._parent.stackedWidget.currentIndex() == 0: |         if self._parent.stackedWidget.currentIndex() == 0: | ||||||
|             game_cards = self._parent.gamesListWidget.findChildren(GameCard) |             game_cards = self._parent.gamesListWidget.findChildren(GameCard) | ||||||
|             scroll_area = self._parent.gamesListWidget.parentWidget() |             scroll_area = self._parent.gamesListWidget.parentWidget() | ||||||
|             while scroll_area and not isinstance(scroll_area, QScrollArea): |             while scroll_area and not isinstance(scroll_area, QScrollArea): | ||||||
|                 scroll_area = scroll_area.parentWidget() |                 scroll_area = scroll_area.parentWidget() | ||||||
|  |  | ||||||
|             if isinstance(focused, GameCard): |             if key in (Qt.Key.Key_Left, Qt.Key.Key_Right, Qt.Key.Key_Up, Qt.Key.Key_Down): | ||||||
|                 current_index = game_cards.index(focused) if focused in game_cards else -1 |                 if not game_cards: | ||||||
|                 if key == Qt.Key.Key_Down: |                     return True | ||||||
|                     if current_index >= 0 and current_index + 1 < len(game_cards): |  | ||||||
|                         next_card = game_cards[current_index + 1] |                 # If no focused widget or not a GameCard, focus the first card | ||||||
|  |                 if not isinstance(focused, GameCard) or focused not in game_cards: | ||||||
|  |                     game_cards[0].setFocus() | ||||||
|  |                     if scroll_area: | ||||||
|  |                         scroll_area.ensureWidgetVisible(game_cards[0], 50, 50) | ||||||
|  |                     return True | ||||||
|  |  | ||||||
|  |                 # Group cards by rows based on y-coordinate | ||||||
|  |                 rows = {} | ||||||
|  |                 for card in game_cards: | ||||||
|  |                     y = card.pos().y() | ||||||
|  |                     if y not in rows: | ||||||
|  |                         rows[y] = [] | ||||||
|  |                     rows[y].append(card) | ||||||
|  |                 # Sort cards in each row by x-coordinate | ||||||
|  |                 for y in rows: | ||||||
|  |                     rows[y].sort(key=lambda c: c.pos().x()) | ||||||
|  |                 # Sort rows by y-coordinate | ||||||
|  |                 sorted_rows = sorted(rows.items(), key=lambda x: x[0]) | ||||||
|  |  | ||||||
|  |                 # Find current row and column | ||||||
|  |                 current_y = focused.pos().y() | ||||||
|  |                 current_row_idx = next(i for i, (y, _) in enumerate(sorted_rows) if y == current_y) | ||||||
|  |                 current_row = sorted_rows[current_row_idx][1] | ||||||
|  |                 current_col_idx = current_row.index(focused) | ||||||
|  |  | ||||||
|  |                 if key == Qt.Key.Key_Right: | ||||||
|  |                     next_col_idx = current_col_idx + 1 | ||||||
|  |                     if next_col_idx < len(current_row): | ||||||
|  |                         next_card = current_row[next_col_idx] | ||||||
|  |                         next_card.setFocus() | ||||||
|  |                         if scroll_area: | ||||||
|  |                             scroll_area.ensureWidgetVisible(next_card, 50, 50) | ||||||
|  |                         return True | ||||||
|  |                     else: | ||||||
|  |                         # Move to the first card of the next row if available | ||||||
|  |                         if current_row_idx < len(sorted_rows) - 1: | ||||||
|  |                             next_row = sorted_rows[current_row_idx + 1][1] | ||||||
|  |                             next_card = next_row[0] if next_row else None | ||||||
|  |                             if next_card: | ||||||
|  |                                 next_card.setFocus() | ||||||
|  |                                 if scroll_area: | ||||||
|  |                                     scroll_area.ensureWidgetVisible(next_card, 50, 50) | ||||||
|  |                             return True | ||||||
|  |                 elif key == Qt.Key.Key_Left: | ||||||
|  |                     next_col_idx = current_col_idx - 1 | ||||||
|  |                     if next_col_idx >= 0: | ||||||
|  |                         next_card = current_row[next_col_idx] | ||||||
|  |                         next_card.setFocus() | ||||||
|  |                         if scroll_area: | ||||||
|  |                             scroll_area.ensureWidgetVisible(next_card, 50, 50) | ||||||
|  |                         return True | ||||||
|  |                     else: | ||||||
|  |                         # Move to the last card of the previous row if available | ||||||
|  |                         if current_row_idx > 0: | ||||||
|  |                             prev_row = sorted_rows[current_row_idx - 1][1] | ||||||
|  |                             next_card = prev_row[-1] if prev_row else None | ||||||
|  |                             if next_card: | ||||||
|  |                                 next_card.setFocus() | ||||||
|  |                                 if scroll_area: | ||||||
|  |                                     scroll_area.ensureWidgetVisible(next_card, 50, 50) | ||||||
|  |                             return True | ||||||
|  |                 elif key == Qt.Key.Key_Down: | ||||||
|  |                     next_row_idx = current_row_idx + 1 | ||||||
|  |                     if next_row_idx < len(sorted_rows): | ||||||
|  |                         next_row = sorted_rows[next_row_idx][1] | ||||||
|  |                         target_x = focused.pos().x() | ||||||
|  |                         next_card = min( | ||||||
|  |                             next_row, | ||||||
|  |                             key=lambda c: abs(c.pos().x() - target_x), | ||||||
|  |                             default=None | ||||||
|  |                         ) | ||||||
|  |                         if next_card: | ||||||
|                             next_card.setFocus() |                             next_card.setFocus() | ||||||
|                             if scroll_area: |                             if scroll_area: | ||||||
|                                 scroll_area.ensureWidgetVisible(next_card, 50, 50) |                                 scroll_area.ensureWidgetVisible(next_card, 50, 50) | ||||||
|                         return True |                         return True | ||||||
|                 elif key == Qt.Key.Key_Up: |                 elif key == Qt.Key.Key_Up: | ||||||
|                     if current_index > 0: |                     next_row_idx = current_row_idx - 1 | ||||||
|                         prev_card = game_cards[current_index - 1] |                     if next_row_idx >= 0: | ||||||
|                         prev_card.setFocus() |                         next_row = sorted_rows[next_row_idx][1] | ||||||
|                         if scroll_area: |                         target_x = focused.pos().x() | ||||||
|                             scroll_area.ensureWidgetVisible(prev_card, 50, 50) |                         next_card = min( | ||||||
|                         return True |                             next_row, | ||||||
|                     elif current_index == 0: |                             key=lambda c: abs(c.pos().x() - target_x), | ||||||
|                         self._parent.tabButtons[0].setFocus() |                             default=None | ||||||
|                         return True |                         ) | ||||||
|                 elif key == Qt.Key.Key_Left: |                         if next_card: | ||||||
|                     if current_index > 0: |  | ||||||
|                         prev_card = game_cards[current_index - 1] |  | ||||||
|                         prev_card.setFocus() |  | ||||||
|                         if scroll_area: |  | ||||||
|                             scroll_area.ensureWidgetVisible(prev_card, 50, 50) |  | ||||||
|                         return True |  | ||||||
|                 elif key == Qt.Key.Key_Right: |  | ||||||
|                     if current_index >= 0 and current_index + 1 < len(game_cards): |  | ||||||
|                         next_card = game_cards[current_index + 1] |  | ||||||
|                             next_card.setFocus() |                             next_card.setFocus() | ||||||
|                             if scroll_area: |                             if scroll_area: | ||||||
|                                 scroll_area.ensureWidgetVisible(next_card, 50, 50) |                                 scroll_area.ensureWidgetVisible(next_card, 50, 50) | ||||||
|                         return True |                         return True | ||||||
|  |                     elif current_row_idx == 0: | ||||||
|  |                         self._parent.tabButtons[0].setFocus() | ||||||
|  |                         return True | ||||||
|  |  | ||||||
|         # Tab switching with Left/Right keys |         # Tab switching with Left/Right keys (non-GameCard focus) | ||||||
|         idx = self._parent.stackedWidget.currentIndex() |         idx = self._parent.stackedWidget.currentIndex() | ||||||
|         total = len(self._parent.tabButtons) |         total = len(self._parent.tabButtons) | ||||||
|         if key == Qt.Key.Key_Left and not isinstance(focused, GameCard): |         if key == Qt.Key.Key_Left and not isinstance(focused, GameCard): | ||||||
| @@ -272,6 +557,7 @@ class InputManager(QObject): | |||||||
|  |  | ||||||
|         return super().eventFilter(obj, event) |         return super().eventFilter(obj, event) | ||||||
|  |  | ||||||
|  |  | ||||||
|     def init_gamepad(self) -> None: |     def init_gamepad(self) -> None: | ||||||
|         self.check_gamepad() |         self.check_gamepad() | ||||||
|         threading.Thread(target=self.run_udev_monitor, daemon=True).start() |         threading.Thread(target=self.run_udev_monitor, daemon=True).start() | ||||||
| @@ -361,114 +647,6 @@ class InputManager(QObject): | |||||||
|                     pass |                     pass | ||||||
|             self.gamepad = None |             self.gamepad = None | ||||||
|  |  | ||||||
|     @Slot(int) |  | ||||||
|     def handle_button_slot(self, button_code: int) -> None: |  | ||||||
|         try: |  | ||||||
|             # Игнорировать события геймпада, если игра запущена |  | ||||||
|             if getattr(self._parent, '_gameLaunched', False): |  | ||||||
|                 return |  | ||||||
|  |  | ||||||
|             app = QApplication.instance() |  | ||||||
|             if not app: |  | ||||||
|                 return |  | ||||||
|             active = QApplication.activeWindow() |  | ||||||
|             focused = QApplication.focusWidget() |  | ||||||
|  |  | ||||||
|             # Закрытие AddGameDialog на кнопку B |  | ||||||
|             if button_code in BUTTONS['back'] and isinstance(active, QDialog): |  | ||||||
|                 active.reject()  # Закрываем диалог |  | ||||||
|                 return |  | ||||||
|  |  | ||||||
|             # FullscreenDialog |  | ||||||
|             if isinstance(active, FullscreenDialog): |  | ||||||
|                 if button_code in BUTTONS['prev_tab']: |  | ||||||
|                     active.show_prev() |  | ||||||
|                 elif button_code in BUTTONS['next_tab']: |  | ||||||
|                     active.show_next() |  | ||||||
|                 elif button_code in BUTTONS['back']: |  | ||||||
|                     active.close() |  | ||||||
|                 return |  | ||||||
|  |  | ||||||
|             # Context menu for GameCard |  | ||||||
|             if isinstance(focused, GameCard): |  | ||||||
|                 if button_code in BUTTONS['context_menu']: |  | ||||||
|                     pos = QPoint(focused.width() // 2, focused.height() // 2) |  | ||||||
|                     focused._show_context_menu(pos) |  | ||||||
|                     return |  | ||||||
|  |  | ||||||
|             # Game launch on detail page |  | ||||||
|             if (button_code in BUTTONS['confirm'] or button_code in BUTTONS['confirm_stick']) and self._parent.currentDetailPage is not None: |  | ||||||
|                 if self._parent.current_exec_line: |  | ||||||
|                     self._parent.toggleGame(self._parent.current_exec_line, None) |  | ||||||
|                     return |  | ||||||
|  |  | ||||||
|             # Standard navigation |  | ||||||
|             if button_code in BUTTONS['confirm'] or button_code in BUTTONS['confirm_stick']: |  | ||||||
|                 self._parent.activateFocusedWidget() |  | ||||||
|             elif button_code in BUTTONS['back'] or button_code in BUTTONS['menu']: |  | ||||||
|                 self._parent.goBackDetailPage(getattr(self._parent, 'currentDetailPage', None)) |  | ||||||
|             elif button_code in BUTTONS['add_game']: |  | ||||||
|                 self._parent.openAddGameDialog() |  | ||||||
|             elif button_code in BUTTONS['prev_tab']: |  | ||||||
|                 idx = (self._parent.stackedWidget.currentIndex() - 1) % len(self._parent.tabButtons) |  | ||||||
|                 self._parent.switchTab(idx) |  | ||||||
|                 self._parent.tabButtons[idx].setFocus(Qt.FocusReason.OtherFocusReason) |  | ||||||
|             elif button_code in BUTTONS['next_tab']: |  | ||||||
|                 idx = (self._parent.stackedWidget.currentIndex() + 1) % len(self._parent.tabButtons) |  | ||||||
|                 self._parent.switchTab(idx) |  | ||||||
|                 self._parent.tabButtons[idx].setFocus(Qt.FocusReason.OtherFocusReason) |  | ||||||
|         except Exception as e: |  | ||||||
|             logger.error(f"Error in handle_button_slot: {e}", exc_info=True) |  | ||||||
|  |  | ||||||
|     @Slot(int, int, float) |  | ||||||
|     def handle_dpad_slot(self, code: int, value: int, current_time: float) -> None: |  | ||||||
|         try: |  | ||||||
|             # Игнорировать события геймпада, если игра запущена |  | ||||||
|             if getattr(self._parent, '_gameLaunched', False): |  | ||||||
|                 return |  | ||||||
|  |  | ||||||
|             app = QApplication.instance() |  | ||||||
|             if not app: |  | ||||||
|                 return |  | ||||||
|             active = QApplication.activeWindow() |  | ||||||
|  |  | ||||||
|             # Fullscreen horizontal navigation |  | ||||||
|             if isinstance(active, FullscreenDialog) and code == ecodes.ABS_HAT0X: |  | ||||||
|                 if value < 0: |  | ||||||
|                     active.show_prev() |  | ||||||
|                 elif value > 0: |  | ||||||
|                     active.show_next() |  | ||||||
|                 return |  | ||||||
|  |  | ||||||
|             # Vertical navigation (DPAD up/down) |  | ||||||
|             if code == ecodes.ABS_HAT0Y: |  | ||||||
|                 if value == 0: |  | ||||||
|                     return |  | ||||||
|                 focused = QApplication.focusWidget() |  | ||||||
|                 page = self._parent.stackedWidget.currentWidget() |  | ||||||
|                 if value > 0: |  | ||||||
|                     if isinstance(focused, NavLabel): |  | ||||||
|                         focusables = page.findChildren(QWidget, options=Qt.FindChildOption.FindChildrenRecursively) |  | ||||||
|                         focusables = [w for w in focusables if w.focusPolicy() & Qt.FocusPolicy.StrongFocus] |  | ||||||
|                         if focusables: |  | ||||||
|                             focusables[0].setFocus() |  | ||||||
|                             return |  | ||||||
|                     elif focused: |  | ||||||
|                         focused.focusNextChild() |  | ||||||
|                         return |  | ||||||
|                 elif value < 0 and focused: |  | ||||||
|                     focused.focusPreviousChild() |  | ||||||
|                     return |  | ||||||
|  |  | ||||||
|             # Reset axis movement state |  | ||||||
|             if code == ecodes.ABS_HAT0X and value == 0: |  | ||||||
|                 self.axis_moving = False |  | ||||||
|                 self.current_axis_delay = self.initial_axis_move_delay |  | ||||||
|                 return |  | ||||||
|  |  | ||||||
|         except Exception as e: |  | ||||||
|             logger.error(f"Error in handle_dpad_slot: {e}", exc_info=True) |  | ||||||
|  |  | ||||||
|     def cleanup(self) -> None: |     def cleanup(self) -> None: | ||||||
|         try: |         try: | ||||||
|             self.running = False |             self.running = False | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user