feat(virtual_keyboard): press X to backspace
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
This commit is contained in:
		| @@ -72,7 +72,7 @@ class InputManager(QObject): | ||||
|     for seamless UI interaction. | ||||
|     """ | ||||
|     # Signals for gamepad events | ||||
|     button_pressed = Signal(int)  # Signal for button presses | ||||
|     button_event = Signal(int, int)  # Signal for button events: (code, value) where value=1 (press), 0 (release) | ||||
|     dpad_moved = Signal(int, int, float)  # Signal for D-pad movements | ||||
|     toggle_fullscreen = Signal(bool)  # Signal for toggling fullscreen mode (True for fullscreen, False for normal) | ||||
|  | ||||
| @@ -131,7 +131,7 @@ class InputManager(QObject): | ||||
|         self.current_dpad_value = 0    # Tracks the current D-pad direction value (e.g., -1, 1) | ||||
|  | ||||
|         # Connect signals to slots | ||||
|         self.button_pressed.connect(self.handle_button_slot) | ||||
|         self.button_event.connect(self.handle_button_slot) | ||||
|         self.dpad_moved.connect(self.handle_dpad_slot) | ||||
|         self.toggle_fullscreen.connect(self.handle_fullscreen_slot) | ||||
|  | ||||
| @@ -202,7 +202,9 @@ class InputManager(QObject): | ||||
|         except Exception as e: | ||||
|             logger.error(f"Error restoring gamepad handlers: {e}") | ||||
|  | ||||
|     def handle_file_explorer_button(self, button_code): | ||||
|     def handle_file_explorer_button(self, button_code, value): | ||||
|         if value == 0:  # Ignore releases | ||||
|                     return | ||||
|         try: | ||||
|             popup = QApplication.activePopupWidget() | ||||
|             if isinstance(popup, QMenu): | ||||
| @@ -442,26 +444,31 @@ class InputManager(QObject): | ||||
|             except Exception as e: | ||||
|                 logger.error(f"Error stopping rumble: {e}", exc_info=True) | ||||
|  | ||||
|     @Slot(int) | ||||
|     def handle_button_slot(self, button_code: int) -> None: | ||||
|     @Slot(int, int) | ||||
|     def handle_button_slot(self, button_code: int, value: int) -> None: | ||||
|         active_window = QApplication.activeWindow() | ||||
|  | ||||
|         # Обработка виртуальной клавиатуры в AddGameDialog | ||||
|         # Обработка виртуальной клавиатуры в AddGameDialog (handle both press and release) | ||||
|         if isinstance(active_window, AddGameDialog): | ||||
|             focused = QApplication.focusWidget() | ||||
|             if button_code in BUTTONS['confirm'] and isinstance(focused, QLineEdit): | ||||
|                 # Показываем клавиатуру при нажатии A на поле ввода | ||||
|             if button_code in BUTTONS['confirm'] and value == 1 and isinstance(focused, QLineEdit): | ||||
|                 # Показываем клавиатуру при нажатии A на поле ввода (only on press) | ||||
|                 active_window.show_keyboard_for_widget(focused) | ||||
|                 return | ||||
|  | ||||
|             # Если клавиатура видима, обрабатываем её кнопки | ||||
|             # Если клавиатура видима, обрабатываем её кнопки (including release) | ||||
|             if hasattr(active_window, 'keyboard') and active_window.keyboard.isVisible(): | ||||
|                 self.handle_virtual_keyboard(button_code) | ||||
|                 self.handle_virtual_keyboard(button_code, value) | ||||
|                 return | ||||
|  | ||||
|         # Main window keyboard handling (including release) | ||||
|         keyboard = getattr(self._parent, 'keyboard', None) | ||||
|         if keyboard and keyboard.isVisible(): | ||||
|             self.handle_virtual_keyboard(button_code) | ||||
|             self.handle_virtual_keyboard(button_code, value) | ||||
|             return | ||||
|  | ||||
|         # Ignore releases for all other (non-keyboard) button handling | ||||
|         if value == 0: | ||||
|             return | ||||
|  | ||||
|         if not self._gamepad_handling_enabled: | ||||
| @@ -1051,6 +1058,52 @@ class InputManager(QObject): | ||||
|         except Exception as e: | ||||
|             logger.error(f"Error in handle_dpad_slot: {e}", exc_info=True) | ||||
|  | ||||
|     def handle_virtual_keyboard(self, button_code: int, value: int) -> None: | ||||
|         # Проверяем клавиатуру в активном окне | ||||
|         active_window = QApplication.activeWindow() | ||||
|         keyboard = None | ||||
|  | ||||
|         # Сначала проверяем AddGameDialog | ||||
|         if isinstance(active_window, AddGameDialog): | ||||
|             keyboard = getattr(active_window, 'keyboard', None) | ||||
|         else: | ||||
|             # Если это не AddGameDialog, проверяем клавиатуру в главном окне | ||||
|             keyboard = getattr(self._parent, 'keyboard', None) | ||||
|  | ||||
|         if not keyboard or not isinstance(keyboard, VirtualKeyboard) or not keyboard.isVisible(): | ||||
|             return | ||||
|  | ||||
|         # Обработка кнопок геймпада | ||||
|         if button_code in BUTTONS['confirm']:  # Кнопка A/Cross - подтверждение | ||||
|             if value == 1: | ||||
|                 keyboard.activateFocusedKey() | ||||
|         elif button_code in BUTTONS['back']:  # Кнопка B/Circle - скрыть клавиатуру | ||||
|             if value == 1: | ||||
|                 keyboard.hide() | ||||
|                 # Возвращаем фокус на поле ввода | ||||
|                 if keyboard.current_input_widget: | ||||
|                     keyboard.current_input_widget.setFocus() | ||||
|         elif button_code in BUTTONS['prev_tab']:  # LB/L1 - переключение раскладки | ||||
|             if value == 1: | ||||
|                 keyboard.on_lang_click() | ||||
|         elif button_code in BUTTONS['next_tab']:  # RB/R1 - переключение Shift | ||||
|             if value == 1: | ||||
|                 keyboard.on_shift_click(not keyboard.shift_pressed) | ||||
|         elif button_code in BUTTONS['context_menu']:  # Кнопка Start - подтверждение | ||||
|             if value == 1: | ||||
|                 keyboard.activateFocusedKey() | ||||
|         elif button_code in BUTTONS['menu']:  # Кнопка Select - скрыть клавиатуру | ||||
|             if value == 1: | ||||
|                 keyboard.hide() | ||||
|                 # Возвращаем фокус на поле ввода | ||||
|                 if keyboard.current_input_widget: | ||||
|                     keyboard.current_input_widget.setFocus() | ||||
|         elif button_code in BUTTONS['add_game']:  # Кнопка X - Backspace (now holdable) | ||||
|             if value == 1: | ||||
|                 keyboard.on_backspace_pressed() | ||||
|             elif value == 0: | ||||
|                 keyboard.stop_backspace_repeat() | ||||
|  | ||||
|     def eventFilter(self, obj: QObject, event: QEvent) -> bool: | ||||
|         app = QApplication.instance() | ||||
|         if not app: | ||||
| @@ -1357,11 +1410,12 @@ class InputManager(QObject): | ||||
|                 if not app or not active: | ||||
|                     continue | ||||
|  | ||||
|                 if event.type == ecodes.EV_KEY and event.value == 1: | ||||
|                     if event.code in BUTTONS['menu'] and not self._is_gamescope_session: | ||||
|                 if event.type == ecodes.EV_KEY: | ||||
|                     # Emit on both press (1) and release (0) | ||||
|                     self.button_event.emit(event.code, event.value) | ||||
|                     # Special handling for menu on press only | ||||
|                     if event.value == 1 and event.code in BUTTONS['menu'] and not self._is_gamescope_session: | ||||
|                         self.toggle_fullscreen.emit(not self._is_fullscreen) | ||||
|                     else: | ||||
|                         self.button_pressed.emit(event.code) | ||||
|                 elif event.type == ecodes.EV_ABS: | ||||
|                     if event.code in {ecodes.ABS_Z, ecodes.ABS_RZ}: | ||||
|                         # Проверяем, достаточно ли времени прошло с последнего срабатывания | ||||
| @@ -1370,17 +1424,19 @@ class InputManager(QObject): | ||||
|                         if event.code == ecodes.ABS_Z:  # LT/L2 | ||||
|                             if event.value > 128 and not self.lt_pressed: | ||||
|                                 self.lt_pressed = True | ||||
|                                 self.button_pressed.emit(event.code) | ||||
|                                 self.button_event.emit(event.code, 1)  # Emit as press | ||||
|                                 self.last_trigger_time = now | ||||
|                             elif event.value <= 128 and self.lt_pressed: | ||||
|                                 self.lt_pressed = False | ||||
|                                 self.button_event.emit(event.code, 0)  # Emit as release | ||||
|                         elif event.code == ecodes.ABS_RZ:  # RT/R2 | ||||
|                             if event.value > 128 and not self.rt_pressed: | ||||
|                                 self.rt_pressed = True | ||||
|                                 self.button_pressed.emit(event.code) | ||||
|                                 self.button_event.emit(event.code, 1)  # Emit as press | ||||
|                                 self.last_trigger_time = now | ||||
|                             elif event.value <= 128 and self.rt_pressed: | ||||
|                                 self.rt_pressed = False | ||||
|                                 self.button_event.emit(event.code, 0)  # Emit as release | ||||
|                     else: | ||||
|                         self.dpad_moved.emit(event.code, event.value, now) | ||||
|         except OSError as e: | ||||
| @@ -1413,40 +1469,3 @@ class InputManager(QObject): | ||||
|             self.gamepad_type = GamepadType.UNKNOWN | ||||
|         except Exception as e: | ||||
|             logger.error(f"Error during cleanup: {e}", exc_info=True) | ||||
|  | ||||
|     def handle_virtual_keyboard(self, button_code: int) -> None: | ||||
|         # Проверяем клавиатуру в активном окне | ||||
|         active_window = QApplication.activeWindow() | ||||
|         keyboard = None | ||||
|  | ||||
|         # Сначала проверяем AddGameDialog | ||||
|         if isinstance(active_window, AddGameDialog): | ||||
|             keyboard = getattr(active_window, 'keyboard', None) | ||||
|         else: | ||||
|             # Если это не AddGameDialog, проверяем клавиатуру в главном окне | ||||
|             keyboard = getattr(self._parent, 'keyboard', None) | ||||
|  | ||||
|         if not keyboard or not isinstance(keyboard, VirtualKeyboard) or not keyboard.isVisible(): | ||||
|             return | ||||
|  | ||||
|         # Обработка кнопок геймпада | ||||
|         if button_code in BUTTONS['confirm']:  # Кнопка A/Cross - подтверждение | ||||
|             keyboard.activateFocusedKey() | ||||
|         elif button_code in BUTTONS['back']:  # Кнопка B/Circle - скрыть клавиатуру | ||||
|             keyboard.hide() | ||||
|             # Возвращаем фокус на поле ввода | ||||
|             if keyboard.current_input_widget: | ||||
|                 keyboard.current_input_widget.setFocus() | ||||
|         elif button_code in BUTTONS['prev_tab']:  # LB/L1 - переключение раскладки | ||||
|             keyboard.on_lang_click() | ||||
|         elif button_code in BUTTONS['next_tab']:  # RB/R1 - переключение Shift | ||||
|             keyboard.on_shift_click(not keyboard.shift_pressed) | ||||
|         elif button_code in BUTTONS['context_menu']:  # Кнопка Start - подтверждение | ||||
|             keyboard.activateFocusedKey() | ||||
|         elif button_code in BUTTONS['menu']:  # Кнопка Select - скрыть клавиатуру | ||||
|             keyboard.hide() | ||||
|             # Возвращаем фокус на поле ввода | ||||
|             if keyboard.current_input_widget: | ||||
|                 keyboard.current_input_widget.setFocus() | ||||
|         elif button_code in BUTTONS['add_game']:  # Кнопка X - Backspace | ||||
|             keyboard.on_backspace_click() | ||||
|   | ||||
| @@ -145,7 +145,7 @@ class MainWindow(QMainWindow): | ||||
|         headerLayout.addStretch() | ||||
|  | ||||
|         self.input_manager = InputManager(self) # type: ignore | ||||
|         self.input_manager.button_pressed.connect(self.updateControlHints) | ||||
|         self.input_manager.button_event.connect(self.updateControlHints) | ||||
|         self.input_manager.dpad_moved.connect(self.updateControlHints) | ||||
|  | ||||
|         # 2. НАВИГАЦИЯ (КНОПКИ ВКЛАДОК) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user