feat(settings): added initial gamepad navigation
All checks were successful
Code check / Check code (push) Successful in 1m20s
All checks were successful
Code check / Check code (push) Successful in 1m20s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
This commit is contained in:
@@ -125,6 +125,15 @@ def create_dialog_hints_widget(theme, main_window, input_manager, context='defau
|
||||
("prev_tab", _("Prev Tab")), # LB / L1
|
||||
("next_tab", _("Next Tab")), # RB / R1
|
||||
]
|
||||
elif context == 'settings':
|
||||
dialog_actions = [
|
||||
("confirm", _("Toggle")), # A / Cross
|
||||
("add_game", _("Save")), # X / Triangle
|
||||
("prev_dir", _("Search")), # Y / Square
|
||||
("back", _("Cancel")), # B / Circle
|
||||
("prev_tab", _("Prev Tab")), # LB / L1
|
||||
("next_tab", _("Next Tab")), # RB / R1
|
||||
]
|
||||
|
||||
hints_labels = [] # Store for updates (returned for class storage)
|
||||
|
||||
@@ -1723,6 +1732,8 @@ class ExeSettingsDialog(QDialog):
|
||||
self.setModal(True)
|
||||
self.resize(1100, 720)
|
||||
self.setStyleSheet(self.theme.MAIN_WINDOW_STYLE + self.theme.MESSAGE_BOX_STYLE)
|
||||
# Set focus policy to handle focus changes properly
|
||||
self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
||||
|
||||
# Load toggle settings from config module
|
||||
self.toggle_settings = get_toggle_settings()
|
||||
@@ -1741,6 +1752,27 @@ class ExeSettingsDialog(QDialog):
|
||||
|
||||
self.current_theme_name = read_theme_from_config()
|
||||
|
||||
# Enable settings dialog-specific mode
|
||||
if self.input_manager:
|
||||
self.input_manager.enable_settings_mode(self)
|
||||
|
||||
# Create hints widget using common function
|
||||
self.hints_widget, self.hints_labels = create_dialog_hints_widget(self.theme, self.main_window, self.input_manager, context='settings')
|
||||
self.main_layout.addWidget(self.hints_widget)
|
||||
|
||||
# Connect signals
|
||||
if self.input_manager:
|
||||
self.input_manager.button_event.connect(
|
||||
lambda *args: update_dialog_hints(self.hints_labels, self.main_window, self.input_manager, theme_manager, self.current_theme_name)
|
||||
)
|
||||
self.input_manager.dpad_moved.connect(
|
||||
lambda *args: update_dialog_hints(self.hints_labels, self.main_window, self.input_manager, theme_manager, self.current_theme_name)
|
||||
)
|
||||
update_dialog_hints(self.hints_labels, self.main_window, self.input_manager, theme_manager, self.current_theme_name)
|
||||
|
||||
# Initialize virtual keyboard
|
||||
self.init_virtual_keyboard()
|
||||
|
||||
# Load current settings (includes list-db)
|
||||
self.load_current_settings()
|
||||
|
||||
@@ -1757,6 +1789,19 @@ class ExeSettingsDialog(QDialog):
|
||||
self.main_layout.setContentsMargins(10, 10, 10, 10)
|
||||
self.main_layout.setSpacing(10)
|
||||
|
||||
# Search bar
|
||||
search_layout = QHBoxLayout()
|
||||
self.search_label = QLabel(_("Search:"))
|
||||
self.search_label.setStyleSheet(self.theme.PARAMS_TITLE_STYLE)
|
||||
self.search_edit = QLineEdit()
|
||||
self.search_edit.setStyleSheet(self.theme.ADDGAME_INPUT_STYLE)
|
||||
self.search_edit.setPlaceholderText(_("Search settings..."))
|
||||
self.search_edit.textChanged.connect(self.filter_settings)
|
||||
self.search_edit.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
||||
search_layout.addWidget(self.search_label)
|
||||
search_layout.addWidget(self.search_edit)
|
||||
self.main_layout.addLayout(search_layout)
|
||||
|
||||
# Tab widget
|
||||
self.tab_widget = QTabWidget()
|
||||
self.tab_widget.setStyleSheet(self.theme.WINETRICKS_TAB_STYLE)
|
||||
@@ -2009,6 +2054,9 @@ class ExeSettingsDialog(QDialog):
|
||||
if len(setting['options']) == 1:
|
||||
combo.setEnabled(False)
|
||||
|
||||
# Set focus policy for combo box
|
||||
combo.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
||||
|
||||
self.advanced_table.setCellWidget(row, 1, combo)
|
||||
self.advanced_widgets[setting['key']] = combo
|
||||
self.original_display_values[setting['key']] = current_val
|
||||
@@ -2029,6 +2077,75 @@ class ExeSettingsDialog(QDialog):
|
||||
desc_item.setTextAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
|
||||
self.advanced_table.setItem(row, 2, desc_item)
|
||||
|
||||
def init_virtual_keyboard(self):
|
||||
"""Initialize virtual keyboard"""
|
||||
self.keyboard = VirtualKeyboard(self, theme=self.theme, button_width=40)
|
||||
self.keyboard.hide()
|
||||
self.keyboard.current_input_widget = None
|
||||
|
||||
def show_virtual_keyboard(self, widget=None):
|
||||
"""Show virtual keyboard for search or text input"""
|
||||
if not widget:
|
||||
# Default to search edit
|
||||
widget = self.search_edit
|
||||
|
||||
if not widget or not widget.isVisible():
|
||||
return
|
||||
|
||||
# Set the current input widget
|
||||
self.keyboard.current_input_widget = widget
|
||||
|
||||
# Position the keyboard
|
||||
keyboard_height = 220
|
||||
self.keyboard.setFixedWidth(self.width())
|
||||
self.keyboard.setFixedHeight(keyboard_height)
|
||||
self.keyboard.move(0, self.height() - keyboard_height)
|
||||
|
||||
# Show and raise keyboard
|
||||
self.keyboard.setParent(self)
|
||||
self.keyboard.show()
|
||||
self.keyboard.raise_()
|
||||
|
||||
# Focus on first button of keyboard
|
||||
first_button = self.keyboard.findFirstFocusableButton()
|
||||
if first_button:
|
||||
# First hide the current focus to prevent conflicts
|
||||
focused_widget = QApplication.focusWidget()
|
||||
if focused_widget and focused_widget != self.keyboard:
|
||||
focused_widget.clearFocus()
|
||||
|
||||
# Then focus the keyboard button
|
||||
QTimer.singleShot(50, lambda: first_button.setFocus())
|
||||
|
||||
def filter_settings(self, text):
|
||||
"""Filter settings based on search text."""
|
||||
# Filter main settings table
|
||||
search_text = text.lower()
|
||||
for row in range(self.settings_table.rowCount()):
|
||||
name_item = self.settings_table.item(row, 0) # Setting name
|
||||
desc_item = self.settings_table.item(row, 2) # Description
|
||||
should_show = False
|
||||
|
||||
if name_item and search_text in name_item.text().lower():
|
||||
should_show = True
|
||||
elif desc_item and search_text in desc_item.text().lower():
|
||||
should_show = True
|
||||
|
||||
self.settings_table.setRowHidden(row, not should_show)
|
||||
|
||||
# Filter advanced settings table
|
||||
for row in range(self.advanced_table.rowCount()):
|
||||
name_item = self.advanced_table.item(row, 0) # Setting name
|
||||
desc_item = self.advanced_table.item(row, 2) # Description
|
||||
should_show = False
|
||||
|
||||
if name_item and search_text in name_item.text().lower():
|
||||
should_show = True
|
||||
elif desc_item and search_text in desc_item.text().lower():
|
||||
should_show = True
|
||||
|
||||
self.advanced_table.setRowHidden(row, not should_show)
|
||||
|
||||
def apply_changes(self):
|
||||
"""Apply changes by collecting diffs from both main and advanced tabs."""
|
||||
changes = []
|
||||
@@ -2089,8 +2206,40 @@ class ExeSettingsDialog(QDialog):
|
||||
self.load_current_settings()
|
||||
QMessageBox.information(self, _("Success"), _("Settings updated successfully."))
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
"""Override key press event to handle combo box interaction properly."""
|
||||
# If a combo box in the advanced table is active and has an open dropdown,
|
||||
# we need to handle Escape key specially to prevent dialog closure
|
||||
focused_widget = QApplication.focusWidget()
|
||||
if (event.key() == Qt.Key.Key_Escape and
|
||||
isinstance(focused_widget, QComboBox) and
|
||||
focused_widget.view().isVisible()):
|
||||
# If a combo box dropdown is open, just close the dropdown instead of the dialog
|
||||
focused_widget.hidePopup()
|
||||
self.advanced_table.setFocus()
|
||||
return
|
||||
super().keyPressEvent(event)
|
||||
|
||||
def closeEvent(self, event):
|
||||
# Hide virtual keyboard if visible
|
||||
if hasattr(self, 'keyboard') and self.keyboard.isVisible():
|
||||
self.keyboard.hide()
|
||||
if self.input_manager:
|
||||
self.input_manager.disable_settings_mode()
|
||||
super().closeEvent(event)
|
||||
|
||||
def reject(self):
|
||||
# Hide virtual keyboard if visible
|
||||
if hasattr(self, 'keyboard') and self.keyboard.isVisible():
|
||||
self.keyboard.hide()
|
||||
if self.input_manager:
|
||||
self.input_manager.disable_settings_mode()
|
||||
super().reject()
|
||||
|
||||
def accept(self):
|
||||
# Hide virtual keyboard if visible
|
||||
if hasattr(self, 'keyboard') and self.keyboard.isVisible():
|
||||
self.keyboard.hide()
|
||||
if self.input_manager:
|
||||
self.input_manager.disable_settings_mode()
|
||||
super().accept()
|
||||
|
||||
@@ -526,6 +526,39 @@ class InputManager(QObject):
|
||||
except Exception as e:
|
||||
logger.error(f"Error restoring gamepad handlers from Winetricks: {e}")
|
||||
|
||||
def enable_settings_mode(self, settings_dialog):
|
||||
"""Setup gamepad handling for ExeSettingsDialog"""
|
||||
try:
|
||||
self.settings_dialog = settings_dialog
|
||||
self.original_button_handler = self.handle_button_slot
|
||||
self.original_dpad_handler = self.handle_dpad_slot
|
||||
self.original_gamepad_state = self._gamepad_handling_enabled
|
||||
self.handle_button_slot = self.handle_settings_button
|
||||
self.handle_dpad_slot = self.handle_settings_dpad
|
||||
self._gamepad_handling_enabled = True
|
||||
# Reset dpad timer for table nav
|
||||
self.dpad_timer.stop()
|
||||
self.current_dpad_code = None
|
||||
self.current_dpad_value = 0
|
||||
logger.debug("Gamepad handling successfully connected for SettingsDialog")
|
||||
except Exception as e:
|
||||
logger.error(f"Error connecting gamepad handlers for SettingsDialog: {e}")
|
||||
|
||||
def disable_settings_mode(self):
|
||||
"""Restore original main window handlers"""
|
||||
try:
|
||||
if self.settings_dialog:
|
||||
self.handle_button_slot = self.original_button_handler
|
||||
self.handle_dpad_slot = self.original_dpad_handler
|
||||
self._gamepad_handling_enabled = self.original_gamepad_state
|
||||
self.settings_dialog = None
|
||||
self.dpad_timer.stop()
|
||||
self.current_dpad_code = None
|
||||
self.current_dpad_value = 0
|
||||
logger.debug("Gamepad handling successfully restored from Settings")
|
||||
except Exception as e:
|
||||
logger.error(f"Error restoring gamepad handlers from Settings: {e}")
|
||||
|
||||
def handle_winetricks_button(self, button_code, value):
|
||||
if self.winetricks_dialog is None:
|
||||
return
|
||||
@@ -592,6 +625,143 @@ class InputManager(QObject):
|
||||
except Exception as e:
|
||||
logger.error(f"Error in handle_winetricks_button: {e}")
|
||||
|
||||
def handle_settings_button(self, button_code, value):
|
||||
if self.settings_dialog is None or value == 0:
|
||||
return
|
||||
|
||||
try:
|
||||
kb = getattr(self.settings_dialog, 'keyboard', None)
|
||||
|
||||
# Virtual keyboard
|
||||
if kb and kb.isVisible():
|
||||
if button_code in BUTTONS['back']:
|
||||
kb.hide()
|
||||
if kb.current_input_widget:
|
||||
kb.current_input_widget.setFocus()
|
||||
return
|
||||
if button_code in BUTTONS['confirm'] or button_code in BUTTONS['context_menu']:
|
||||
kb.activateFocusedKey()
|
||||
return
|
||||
if button_code in BUTTONS['prev_tab']:
|
||||
kb.on_lang_click()
|
||||
return
|
||||
if button_code in BUTTONS['next_tab']:
|
||||
kb.on_shift_click(not kb.shift_pressed)
|
||||
return
|
||||
if button_code in BUTTONS['add_game']:
|
||||
kb.on_backspace_pressed()
|
||||
return
|
||||
return
|
||||
|
||||
# Pop-ups
|
||||
popup = QApplication.activePopupWidget()
|
||||
if popup:
|
||||
if isinstance(popup, (QMessageBox, QDialog)):
|
||||
if button_code in BUTTONS['confirm'] | BUTTONS['back']:
|
||||
popup.accept()
|
||||
return
|
||||
if isinstance(popup, QMenu):
|
||||
if button_code in BUTTONS['confirm'] and popup.activeAction():
|
||||
popup.activeAction().trigger()
|
||||
return
|
||||
if button_code in BUTTONS['back']:
|
||||
popup.close()
|
||||
return
|
||||
|
||||
table = self._get_current_settings_table()
|
||||
|
||||
# Ищем любой открытый комбобокс в Advanced-вкладке
|
||||
open_combo = None
|
||||
if table is not None and table == self.settings_dialog.advanced_table:
|
||||
for r in range(table.rowCount()):
|
||||
w = table.cellWidget(r, 1)
|
||||
if isinstance(w, QComboBox) and w.view().isVisible():
|
||||
open_combo = w
|
||||
break
|
||||
|
||||
# B — закрываем открытый комбобокс или весь диалог
|
||||
if button_code in BUTTONS['back']:
|
||||
if open_combo:
|
||||
open_combo.hidePopup()
|
||||
if table is not None:
|
||||
table.setFocus()
|
||||
else:
|
||||
self.settings_dialog.reject()
|
||||
return
|
||||
|
||||
# A — главное действие
|
||||
if button_code in BUTTONS['confirm']:
|
||||
# Если есть открытый комбобокс — подтверждаем выбор в нём
|
||||
if open_combo:
|
||||
view = open_combo.view()
|
||||
model_index = view.currentIndex()
|
||||
if model_index.isValid():
|
||||
open_combo.setCurrentIndex(model_index.row())
|
||||
open_combo.hidePopup()
|
||||
if table is not None:
|
||||
table.setFocus()
|
||||
return
|
||||
|
||||
# Обычная логика: чекбоксы, открытие комбо, ввод текста
|
||||
focused = QApplication.focusWidget()
|
||||
if isinstance(focused, QTableWidget) and table and focused.currentRow() >= 0:
|
||||
row = focused.currentRow()
|
||||
cell = focused.cellWidget(row, 1)
|
||||
|
||||
# Main tab — чекбоксы
|
||||
if table == self.settings_dialog.settings_table:
|
||||
item = focused.item(row, 1)
|
||||
# Only allow toggling if the item is user checkable (enabled)
|
||||
if item and item.flags() & Qt.ItemFlag.ItemIsUserCheckable:
|
||||
item.setCheckState(
|
||||
Qt.CheckState.Checked
|
||||
if item.checkState() == Qt.CheckState.Unchecked
|
||||
else Qt.CheckState.Unchecked
|
||||
)
|
||||
return
|
||||
|
||||
# Advanced tab
|
||||
if isinstance(cell, QComboBox):
|
||||
# Only allow opening combo box if it's enabled
|
||||
if cell.isEnabled():
|
||||
cell.showPopup() # открываем, если закрыт
|
||||
cell.setFocus()
|
||||
return
|
||||
if isinstance(cell, QLineEdit):
|
||||
cell.setFocus()
|
||||
self.settings_dialog.show_virtual_keyboard(cell)
|
||||
return
|
||||
|
||||
if isinstance(focused, QLineEdit):
|
||||
self.settings_dialog.show_virtual_keyboard(focused)
|
||||
return
|
||||
|
||||
# X — Apply
|
||||
if button_code in BUTTONS['add_game']:
|
||||
self.settings_dialog.apply_changes()
|
||||
return
|
||||
|
||||
# Y — поиск + клавиатура
|
||||
if button_code in BUTTONS['prev_dir']:
|
||||
self.settings_dialog.search_edit.setFocus()
|
||||
self.settings_dialog.show_virtual_keyboard(self.settings_dialog.search_edit)
|
||||
return
|
||||
|
||||
# LB / RB — переключение вкладок
|
||||
if button_code in BUTTONS['prev_tab']:
|
||||
idx = max(0, self.settings_dialog.tab_widget.currentIndex() - 1)
|
||||
self.settings_dialog.tab_widget.setCurrentIndex(idx)
|
||||
self._focus_first_row_in_current_settings_table()
|
||||
elif button_code in BUTTONS['next_tab']:
|
||||
idx = min(self.settings_dialog.tab_widget.count() - 1, self.settings_dialog.tab_widget.currentIndex() + 1)
|
||||
self.settings_dialog.tab_widget.setCurrentIndex(idx)
|
||||
self._focus_first_row_in_current_settings_table()
|
||||
else:
|
||||
self._parent.activateFocusedWidget()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in handle_settings_button: {e}")
|
||||
|
||||
def handle_winetricks_dpad(self, code, value, now):
|
||||
if self.winetricks_dialog is None:
|
||||
return
|
||||
@@ -617,9 +787,21 @@ class InputManager(QObject):
|
||||
current_row = table.currentRow()
|
||||
if code == ecodes.ABS_HAT0Y: # Up/Down: Navigate rows
|
||||
if value < 0: # Up
|
||||
new_row = max(0, current_row - 1)
|
||||
# Find the next visible row above the current row
|
||||
new_row = current_row - 1
|
||||
while new_row >= 0 and table.isRowHidden(new_row):
|
||||
new_row -= 1
|
||||
if new_row < 0:
|
||||
# If no visible row above, stay at current position
|
||||
new_row = current_row
|
||||
elif value > 0: # Down
|
||||
new_row = min(table.rowCount() - 1, current_row + 1)
|
||||
# Find the next visible row below the current row
|
||||
new_row = current_row + 1
|
||||
while new_row < table.rowCount() and table.isRowHidden(new_row):
|
||||
new_row += 1
|
||||
if new_row >= table.rowCount():
|
||||
# If no visible row below, stay at current position
|
||||
new_row = current_row
|
||||
else:
|
||||
return
|
||||
if new_row != current_row:
|
||||
@@ -658,6 +840,175 @@ class InputManager(QObject):
|
||||
table.setCurrentCell(0, 0)
|
||||
table.setFocus(Qt.FocusReason.OtherFocusReason)
|
||||
|
||||
def _focus_first_row_in_current_settings_table(self):
|
||||
"""Focus the first row in the current settings table after tab switch."""
|
||||
if self.settings_dialog is None:
|
||||
return
|
||||
current_table = self._get_current_settings_table()
|
||||
if current_table and current_table.rowCount() > 0:
|
||||
# For the advanced settings table, focus on column 1 (value column) which contains the widgets
|
||||
# For the main settings table, focus on column 0 (name column) which contains checkboxes
|
||||
focus_column = 1 if current_table == self.settings_dialog.advanced_table else 0
|
||||
current_table.setCurrentCell(0, focus_column)
|
||||
current_table.setFocus(Qt.FocusReason.OtherFocusReason)
|
||||
|
||||
def _get_current_settings_table(self):
|
||||
"""Get the current visible table from the settings dialog's tab widget."""
|
||||
if self.settings_dialog is None:
|
||||
return None
|
||||
current_index = self.settings_dialog.tab_widget.currentIndex()
|
||||
if current_index == 0:
|
||||
return self.settings_dialog.settings_table
|
||||
elif current_index == 1:
|
||||
return self.settings_dialog.advanced_table
|
||||
return None
|
||||
|
||||
def handle_settings_dpad(self, code, value, now):
|
||||
if self.settings_dialog is None:
|
||||
return
|
||||
try:
|
||||
# Check if virtual keyboard is visible - if so, handle keyboard navigation instead
|
||||
if (hasattr(self.settings_dialog, 'keyboard') and
|
||||
self.settings_dialog.keyboard.isVisible()):
|
||||
# Handle keyboard navigation with D-pad
|
||||
if code in (ecodes.ABS_HAT0X, ecodes.ABS_X):
|
||||
normalized_value = 0
|
||||
if code == ecodes.ABS_X: # Left stick
|
||||
# Apply deadzone
|
||||
if abs(value) < self.dead_zone:
|
||||
self.current_dpad_code = None
|
||||
self.current_dpad_value = 0
|
||||
self.axis_moving = False
|
||||
self.dpad_timer.stop()
|
||||
return
|
||||
normalized_value = 1 if value > self.dead_zone else -1
|
||||
else: # D-pad
|
||||
normalized_value = value # D-pad already gives -1, 0, 1
|
||||
|
||||
if normalized_value != 0:
|
||||
if normalized_value > 0: # Right
|
||||
self.settings_dialog.keyboard.move_focus_right()
|
||||
elif normalized_value < 0: # Left
|
||||
self.settings_dialog.keyboard.move_focus_left()
|
||||
return
|
||||
|
||||
# Handle vertical navigation for keyboard
|
||||
elif code in (ecodes.ABS_HAT0Y, ecodes.ABS_Y):
|
||||
normalized_value = 0
|
||||
if code == ecodes.ABS_Y: # Left stick
|
||||
# Apply deadzone
|
||||
if abs(value) < self.dead_zone:
|
||||
self.current_dpad_code = None
|
||||
self.current_dpad_value = 0
|
||||
self.axis_moving = False
|
||||
self.dpad_timer.stop()
|
||||
return
|
||||
normalized_value = 1 if value > self.dead_zone else -1
|
||||
else: # D-pad
|
||||
normalized_value = value # D-pad already gives -1, 0, 1
|
||||
|
||||
if normalized_value != 0:
|
||||
if normalized_value > 0: # Down
|
||||
self.settings_dialog.keyboard.move_focus_down()
|
||||
elif normalized_value < 0: # Up
|
||||
self.settings_dialog.keyboard.move_focus_up()
|
||||
return
|
||||
return # Don't continue with table navigation if keyboard is visible
|
||||
|
||||
# Get the current settings table first
|
||||
table = self._get_current_settings_table()
|
||||
if not table or table.rowCount() == 0:
|
||||
return
|
||||
|
||||
# Check if a combo box in advanced settings has an open dropdown - if so, handle combo box navigation
|
||||
if (table == self.settings_dialog.advanced_table and
|
||||
table.currentRow() >= 0 and
|
||||
table.currentColumn() == 1): # Value column
|
||||
cell_widget = table.cellWidget(table.currentRow(), 1)
|
||||
if isinstance(cell_widget, QComboBox) and cell_widget.view().isVisible():
|
||||
# Only handle combo box dropdown navigation for vertical movements
|
||||
if code == ecodes.ABS_HAT0Y and value != 0:
|
||||
current_index = cell_widget.currentIndex()
|
||||
if value < 0: # Up: move to previous item
|
||||
new_index = max(0, current_index - 1)
|
||||
elif value > 0: # Down: move to next item
|
||||
new_index = min(cell_widget.count() - 1, current_index + 1)
|
||||
else:
|
||||
return
|
||||
if new_index != current_index:
|
||||
cell_widget.setCurrentIndex(new_index)
|
||||
cell_widget.setCurrentText(cell_widget.itemText(new_index)) # Ensure text is updated
|
||||
# If combo box is active, don't continue with table navigation (for any direction)
|
||||
return # Don't continue with table navigation if combo box is active
|
||||
# If not a combo box or dropdown not visible, continue with regular table navigation below
|
||||
# Continue with regular table navigation
|
||||
|
||||
if value == 0: # Release: Stop repeat
|
||||
self.dpad_timer.stop()
|
||||
self.current_dpad_code = None
|
||||
self.current_dpad_value = 0
|
||||
return
|
||||
|
||||
# Start/update repeat timer for hold navigation
|
||||
if self.current_dpad_code != code or self.current_dpad_value != value:
|
||||
self.dpad_timer.stop()
|
||||
self.dpad_timer.setInterval(150 if self.dpad_timer.isActive() else 300) # Initial slower, then faster repeat
|
||||
self.dpad_timer.start()
|
||||
self.current_dpad_code = code
|
||||
self.current_dpad_value = value
|
||||
|
||||
current_row = table.currentRow()
|
||||
if code == ecodes.ABS_HAT0Y: # Up/Down: Navigate rows
|
||||
if value < 0: # Up
|
||||
# Find the next visible row above the current row
|
||||
new_row = current_row - 1
|
||||
while new_row >= 0 and table.isRowHidden(new_row):
|
||||
new_row -= 1
|
||||
if new_row < 0:
|
||||
# If no visible row above, stay at current position
|
||||
new_row = current_row
|
||||
elif value > 0: # Down
|
||||
# Find the next visible row below the current row
|
||||
new_row = current_row + 1
|
||||
while new_row < table.rowCount() and table.isRowHidden(new_row):
|
||||
new_row += 1
|
||||
if new_row >= table.rowCount():
|
||||
# If no visible row below, stay at current position
|
||||
new_row = current_row
|
||||
else:
|
||||
return
|
||||
if new_row != current_row:
|
||||
# For the advanced settings table, focus on column 1 (value column) which contains the widgets
|
||||
# For the main settings table, focus on column 0 (name column) which contains checkboxes
|
||||
focus_column = 1 if table == self.settings_dialog.advanced_table else 0
|
||||
table.setCurrentCell(new_row, focus_column)
|
||||
table.setFocus(Qt.FocusReason.OtherFocusReason)
|
||||
elif code == ecodes.ABS_HAT0X: # Left/Right: Switch tabs or navigate in cells
|
||||
if value < 0: # Left
|
||||
current_col = table.currentColumn()
|
||||
if current_col > 0:
|
||||
# Move to previous column in the same row
|
||||
table.setCurrentCell(current_row, max(0, current_col - 1))
|
||||
else:
|
||||
# Switch to previous tab if at first column
|
||||
current_index = self.settings_dialog.tab_widget.currentIndex()
|
||||
new_index = max(0, current_index - 1)
|
||||
self.settings_dialog.tab_widget.setCurrentIndex(new_index)
|
||||
self._focus_first_row_in_current_settings_table()
|
||||
elif value > 0: # Right
|
||||
current_col = table.currentColumn()
|
||||
if current_col < table.columnCount() - 1:
|
||||
# Move to next column in the same row
|
||||
table.setCurrentCell(current_row, min(table.columnCount() - 1, current_col + 1))
|
||||
else:
|
||||
# Switch to next tab if at last column
|
||||
current_index = self.settings_dialog.tab_widget.currentIndex()
|
||||
new_index = min(self.settings_dialog.tab_widget.count() - 1, current_index + 1)
|
||||
self.settings_dialog.tab_widget.setCurrentIndex(new_index)
|
||||
self._focus_first_row_in_current_settings_table()
|
||||
except Exception as e:
|
||||
logger.error(f"Error in handle_settings_dpad: {e}")
|
||||
|
||||
def handle_navigation_repeat(self):
|
||||
"""Плавное повторение движения с переменной скоростью для FileExplorer"""
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user