diff --git a/portprotonqt/input_manager.py b/portprotonqt/input_manager.py index 8fa1ff4..cef1c96 100644 --- a/portprotonqt/input_manager.py +++ b/portprotonqt/input_manager.py @@ -1201,7 +1201,6 @@ class InputManager(QObject): active = QApplication.activeWindow() focused = QApplication.focusWidget() popup = QApplication.activePopupWidget() - modal_dialog = QApplication.activeModalWidget() if not app or not active: return @@ -1301,13 +1300,6 @@ class InputManager(QObject): menu.setFocus(Qt.FocusReason.OtherFocusReason) return - # Game launch on detail page - if (button_code in BUTTONS['confirm']) and self._parent.currentDetailPage is not None and modal_dialog is None: - if self._parent.current_exec_line: - self.trigger_rumble() - self._parent.toggleGame(self._parent.current_exec_line, None) - return - # Standard navigation if button_code in BUTTONS['confirm']: self._parent.activateFocusedWidget() @@ -1607,6 +1599,50 @@ class InputManager(QObject): self._navigate_game_cards(container, current_index, code, value) return + # Button navigation on detail pages (horizontal layout) + if code in (ecodes.ABS_HAT0X, ecodes.ABS_HAT0Y): + focused = QApplication.focusWidget() + page = self._parent.stackedWidget.currentWidget() + + # Check if we're on a detail page and focused widget is a button + if isinstance(focused, AutoSizeButton): + # Find all buttons in the same horizontal layout (same parent, same Y position) + parent_widget = focused.parentWidget() + if parent_widget: + # Find all AutoSizeButtons in the parent that are horizontally aligned + buttons = parent_widget.findChildren(AutoSizeButton) + # Filter buttons that are approximately on the same horizontal level (similar Y positions) + y_tolerance = 20 # pixels tolerance for vertical alignment + current_y = focused.geometry().y() + focused.geometry().height() // 2 + aligned_buttons = [] + for btn in buttons: + btn_center_y = btn.geometry().y() + btn.geometry().height() // 2 + if abs(btn_center_y - current_y) <= y_tolerance: + aligned_buttons.append(btn) + + # Sort buttons by x position for left-to-right navigation + if len(aligned_buttons) > 1: + aligned_buttons.sort(key=lambda b: b.geometry().x() + b.geometry().width() // 2) + + # Find current button index + try: + current_index = aligned_buttons.index(focused) + except ValueError: + current_index = -1 + + if current_index >= 0: + if code == ecodes.ABS_HAT0X: # Horizontal navigation (left/right) + if value < 0 and current_index > 0: # Left + aligned_buttons[current_index - 1].setFocus(Qt.FocusReason.OtherFocusReason) + return + elif value > 0 and current_index < len(aligned_buttons) - 1: # Right + aligned_buttons[current_index + 1].setFocus(Qt.FocusReason.OtherFocusReason) + return + elif code == ecodes.ABS_HAT0Y: # Vertical navigation (up/down) + # For buttons on the same row, up/down should go to other controls + # So we'll continue to the next section of code for general navigation + pass + # Vertical navigation in other tabs if code == ecodes.ABS_HAT0Y and value != 0: focused = QApplication.focusWidget() diff --git a/portprotonqt/main_window.py b/portprotonqt/main_window.py index 076b0ca..c818e35 100644 --- a/portprotonqt/main_window.py +++ b/portprotonqt/main_window.py @@ -2723,6 +2723,7 @@ class MainWindow(QMainWindow): playButton.setFixedSize(120, 40) playButton.setStyleSheet(self.theme.PLAY_BUTTON_STYLE) + playButton.setFocusPolicy(Qt.FocusPolicy.StrongFocus) playButton.clicked.connect(lambda: self.toggleGame(exec_line, playButton)) buttons_layout.addWidget(playButton, alignment=Qt.AlignmentFlag.AlignLeft) @@ -2748,6 +2749,61 @@ class MainWindow(QMainWindow): # Анимация self.detail_animations.animate_detail_page(detailPage, load_image_and_restore_effect, cleanup_animation) + # Update page reference + self.currentDetailPage = detailPage + + original_load = load_image_and_restore_effect + + def enhanced_load(): + original_load() + QTimer.singleShot(50, try_set_focus) + + def try_set_focus(): + if not (playButton and not playButton.isHidden()): + return + + # Ensure page is active + self.stackedWidget.setCurrentWidget(detailPage) + detailPage.setFocus(Qt.FocusReason.OtherFocusReason) + playButton.setFocus(Qt.FocusReason.OtherFocusReason) + playButton.update() + detailPage.raise_() + self.activateWindow() + + if playButton.hasFocus(): + logger.debug("Play button successfully received focus") + else: + logger.debug("Retrying focus...") + QTimer.singleShot(20, retry_focus) + + def retry_focus(): + if not (playButton and not playButton.isHidden() and not playButton.hasFocus()): + return + + QApplication.processEvents() + self.activateWindow() + self.stackedWidget.setCurrentWidget(detailPage) + detailPage.raise_() + playButton.setFocus(Qt.FocusReason.OtherFocusReason) + playButton.update() + + if not playButton.hasFocus(): + logger.debug("Final retry...") + playButton.setFocusPolicy(Qt.FocusPolicy.StrongFocus) + playButton.setFocus(Qt.FocusReason.OtherFocusReason) + QApplication.processEvents() + + if playButton.hasFocus(): + logger.debug("Play button received focus after final retry") + else: + logger.debug("Play button still doesn't have focus") + + self.detail_animations.animate_detail_page( + detailPage, + enhanced_load, + cleanup_animation + ) + def toggleFavoriteInDetailPage(self, game_name, label): favorites = read_favorites() if game_name in favorites: diff --git a/portprotonqt/themes/standart/styles.py b/portprotonqt/themes/standart/styles.py index 997bc95..08534dd 100644 --- a/portprotonqt/themes/standart/styles.py +++ b/portprotonqt/themes/standart/styles.py @@ -647,6 +647,9 @@ PLAY_BUTTON_STYLE = f""" QPushButton:pressed {{ background: {color_a}; }} + QPushButton:focus {{ + background: {color_a}; + }} """ # СТИЛЬ КНОПКИ "ОБЗОР..." В ДИАЛОГЕ "ДОБАВИТЬ ИГРУ"