forked from Boria138/PortProtonQt
feat(wine settings): make winetricks work with gamepad
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
This commit is contained in:
@@ -5,7 +5,7 @@ from typing import cast, TYPE_CHECKING
|
|||||||
from PySide6.QtGui import QPixmap, QIcon, QTextCursor
|
from PySide6.QtGui import QPixmap, QIcon, QTextCursor
|
||||||
from PySide6.QtWidgets import (
|
from PySide6.QtWidgets import (
|
||||||
QDialog, QFormLayout, QHBoxLayout, QLabel, QVBoxLayout, QListWidget, QScrollArea, QWidget, QListWidgetItem, QSizePolicy, QApplication, QProgressBar, QScroller,
|
QDialog, QFormLayout, QHBoxLayout, QLabel, QVBoxLayout, QListWidget, QScrollArea, QWidget, QListWidgetItem, QSizePolicy, QApplication, QProgressBar, QScroller,
|
||||||
QTabWidget, QTableWidget, QHeaderView, QMessageBox, QTableWidgetItem, QTextEdit
|
QTabWidget, QTableWidget, QHeaderView, QMessageBox, QTableWidgetItem, QTextEdit, QAbstractItemView
|
||||||
)
|
)
|
||||||
|
|
||||||
from PySide6.QtCore import Qt, QObject, Signal, QMimeDatabase, QTimer, QThreadPool, QRunnable, Slot, QProcess, QProcessEnvironment
|
from PySide6.QtCore import Qt, QObject, Signal, QMimeDatabase, QTimer, QThreadPool, QRunnable, Slot, QProcess, QProcessEnvironment
|
||||||
@@ -979,7 +979,6 @@ Icon={icon_path}
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
return desktop_entry, desktop_path
|
return desktop_entry, desktop_path
|
||||||
|
|
||||||
class WinetricksDialog(QDialog):
|
class WinetricksDialog(QDialog):
|
||||||
"""Dialog for managing Winetricks components in a prefix."""
|
"""Dialog for managing Winetricks components in a prefix."""
|
||||||
|
|
||||||
@@ -1105,6 +1104,9 @@ class WinetricksDialog(QDialog):
|
|||||||
|
|
||||||
# DLLs tab
|
# DLLs tab
|
||||||
self.dll_table = QTableWidget()
|
self.dll_table = QTableWidget()
|
||||||
|
self.dll_table.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
||||||
|
self.dll_table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
|
||||||
|
self.dll_table.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
|
||||||
self.dll_table.setColumnCount(3)
|
self.dll_table.setColumnCount(3)
|
||||||
self.dll_table.setHorizontalHeaderLabels([_("Set"), _("Libraries"), _("Information")])
|
self.dll_table.setHorizontalHeaderLabels([_("Set"), _("Libraries"), _("Information")])
|
||||||
self.dll_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeMode.Fixed)
|
self.dll_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeMode.Fixed)
|
||||||
@@ -1116,6 +1118,9 @@ class WinetricksDialog(QDialog):
|
|||||||
|
|
||||||
# Fonts tab
|
# Fonts tab
|
||||||
self.fonts_table = QTableWidget()
|
self.fonts_table = QTableWidget()
|
||||||
|
self.fonts_table.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
||||||
|
self.fonts_table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
|
||||||
|
self.fonts_table.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
|
||||||
self.fonts_table.setColumnCount(3)
|
self.fonts_table.setColumnCount(3)
|
||||||
self.fonts_table.setHorizontalHeaderLabels([_("Set"), _("Fonts"), _("Information")])
|
self.fonts_table.setHorizontalHeaderLabels([_("Set"), _("Fonts"), _("Information")])
|
||||||
self.fonts_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeMode.Fixed)
|
self.fonts_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeMode.Fixed)
|
||||||
@@ -1127,6 +1132,9 @@ class WinetricksDialog(QDialog):
|
|||||||
|
|
||||||
# Settings tab
|
# Settings tab
|
||||||
self.settings_table = QTableWidget()
|
self.settings_table = QTableWidget()
|
||||||
|
self.settings_table.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
||||||
|
self.settings_table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
|
||||||
|
self.settings_table.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
|
||||||
self.settings_table.setColumnCount(3)
|
self.settings_table.setColumnCount(3)
|
||||||
self.settings_table.setHorizontalHeaderLabels([_("Set"), _("Settings"), _("Information")])
|
self.settings_table.setHorizontalHeaderLabels([_("Set"), _("Settings"), _("Information")])
|
||||||
self.settings_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeMode.Fixed)
|
self.settings_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeMode.Fixed)
|
||||||
@@ -1156,6 +1164,10 @@ class WinetricksDialog(QDialog):
|
|||||||
self.force_button.clicked.connect(lambda: self.install_selected(force=True))
|
self.force_button.clicked.connect(lambda: self.install_selected(force=True))
|
||||||
self.install_button.clicked.connect(lambda: self.install_selected(force=False))
|
self.install_button.clicked.connect(lambda: self.install_selected(force=False))
|
||||||
|
|
||||||
|
# Set initial focus to the first table
|
||||||
|
self.dll_table.setCurrentCell(0, 0)
|
||||||
|
self.dll_table.setFocus(Qt.FocusReason.OtherFocusReason)
|
||||||
|
|
||||||
def load_lists(self):
|
def load_lists(self):
|
||||||
"""Load and populate the lists for DLLs, Fonts, and Settings"""
|
"""Load and populate the lists for DLLs, Fonts, and Settings"""
|
||||||
if not os.path.exists(self.winetricks_path):
|
if not os.path.exists(self.winetricks_path):
|
||||||
@@ -1180,7 +1192,6 @@ class WinetricksDialog(QDialog):
|
|||||||
# Settings
|
# Settings
|
||||||
self._start_list_process("settings", self.settings_table, self.get_settings_exclusions(), env, cwd)
|
self._start_list_process("settings", self.settings_table, self.get_settings_exclusions(), env, cwd)
|
||||||
|
|
||||||
|
|
||||||
def _start_list_process(self, category, table, exclusion_pattern, env, cwd):
|
def _start_list_process(self, category, table, exclusion_pattern, env, cwd):
|
||||||
"""Запускает QProcess для списка."""
|
"""Запускает QProcess для списка."""
|
||||||
process = QProcess(self)
|
process = QProcess(self)
|
||||||
@@ -1197,6 +1208,10 @@ class WinetricksDialog(QDialog):
|
|||||||
output = bytes(process.readAllStandardOutput().data()).decode('utf-8', 'ignore')
|
output = bytes(process.readAllStandardOutput().data()).decode('utf-8', 'ignore')
|
||||||
if exit_code == 0 and exit_status == QProcess.ExitStatus.NormalExit:
|
if exit_code == 0 and exit_status == QProcess.ExitStatus.NormalExit:
|
||||||
self.populate_table(table, output, exclusion_pattern, self.log_path)
|
self.populate_table(table, output, exclusion_pattern, self.log_path)
|
||||||
|
# Restore focus after populating
|
||||||
|
if table.rowCount() > 0:
|
||||||
|
table.setCurrentCell(0, 0)
|
||||||
|
table.setFocus(Qt.FocusReason.OtherFocusReason)
|
||||||
else:
|
else:
|
||||||
error_output = bytes(process.readAllStandardError().data()).decode('utf-8', 'ignore')
|
error_output = bytes(process.readAllStandardError().data()).decode('utf-8', 'ignore')
|
||||||
logger.error(f"Failed to list {category}: {error_output}")
|
logger.error(f"Failed to list {category}: {error_output}")
|
||||||
|
@@ -5,7 +5,7 @@ from typing import Protocol, cast
|
|||||||
from evdev import InputDevice, InputEvent, ecodes, list_devices, ff
|
from evdev import InputDevice, InputEvent, ecodes, list_devices, ff
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from pyudev import Context, Monitor, MonitorObserver, Device
|
from pyudev import Context, Monitor, MonitorObserver, Device
|
||||||
from PySide6.QtWidgets import QWidget, QStackedWidget, QApplication, QScrollArea, QLineEdit, QDialog, QMenu, QComboBox, QListView, QMessageBox, QListWidget
|
from PySide6.QtWidgets import QWidget, QStackedWidget, QApplication, QScrollArea, QLineEdit, QDialog, QMenu, QComboBox, QListView, QMessageBox, QListWidget, QTableWidget, QAbstractItemView
|
||||||
from PySide6.QtCore import Qt, QObject, QEvent, QPoint, Signal, Slot, QTimer
|
from PySide6.QtCore import Qt, QObject, QEvent, QPoint, Signal, Slot, QTimer
|
||||||
from PySide6.QtGui import QKeyEvent, QMouseEvent
|
from PySide6.QtGui import QKeyEvent, QMouseEvent
|
||||||
from portprotonqt.logger import get_logger
|
from portprotonqt.logger import get_logger
|
||||||
@@ -13,7 +13,7 @@ from portprotonqt.image_utils import FullscreenDialog
|
|||||||
from portprotonqt.custom_widgets import NavLabel, AutoSizeButton
|
from portprotonqt.custom_widgets import NavLabel, AutoSizeButton
|
||||||
from portprotonqt.game_card import GameCard
|
from portprotonqt.game_card import GameCard
|
||||||
from portprotonqt.config_utils import read_fullscreen_config, read_window_geometry, save_window_geometry, read_auto_fullscreen_gamepad, read_rumble_config
|
from portprotonqt.config_utils import read_fullscreen_config, read_window_geometry, save_window_geometry, read_auto_fullscreen_gamepad, read_rumble_config
|
||||||
from portprotonqt.dialogs import AddGameDialog
|
from portprotonqt.dialogs import AddGameDialog, WinetricksDialog
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
@@ -446,7 +446,6 @@ class InputManager(QObject):
|
|||||||
if not self._gamepad_handling_enabled:
|
if not self._gamepad_handling_enabled:
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
|
|
||||||
app = QApplication.instance()
|
app = QApplication.instance()
|
||||||
active = QApplication.activeWindow()
|
active = QApplication.activeWindow()
|
||||||
focused = QApplication.focusWidget()
|
focused = QApplication.focusWidget()
|
||||||
@@ -551,6 +550,38 @@ class InputManager(QObject):
|
|||||||
self._parent.toggleGame(self._parent.current_exec_line, None)
|
self._parent.toggleGame(self._parent.current_exec_line, None)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if isinstance(active, WinetricksDialog):
|
||||||
|
if button_code in BUTTONS['confirm']: # A button - toggle checkbox
|
||||||
|
current_table = active.tab_widget.currentWidget()
|
||||||
|
if isinstance(current_table, QTableWidget):
|
||||||
|
current_row = current_table.currentRow()
|
||||||
|
if current_row >= 0:
|
||||||
|
checkbox = current_table.item(current_row, 0)
|
||||||
|
if checkbox:
|
||||||
|
checkbox.setCheckState(
|
||||||
|
Qt.CheckState.Unchecked if checkbox.checkState() == Qt.CheckState.Checked else Qt.CheckState.Checked
|
||||||
|
)
|
||||||
|
return
|
||||||
|
elif button_code in BUTTONS['add_game']: # X button - install
|
||||||
|
active.install_selected(force=False)
|
||||||
|
return
|
||||||
|
elif button_code in BUTTONS['prev_dir']: # Y button - force install
|
||||||
|
active.install_selected(force=True)
|
||||||
|
return
|
||||||
|
elif button_code in BUTTONS['back']: # B button - close dialog
|
||||||
|
active.reject()
|
||||||
|
return
|
||||||
|
elif button_code in BUTTONS['prev_tab']: # LB - previous tab
|
||||||
|
current_idx = active.tab_widget.currentIndex()
|
||||||
|
new_idx = (current_idx - 1) % active.tab_widget.count()
|
||||||
|
active.tab_widget.setCurrentIndex(new_idx)
|
||||||
|
return
|
||||||
|
elif button_code in BUTTONS['next_tab']: # RB - next tab
|
||||||
|
current_idx = active.tab_widget.currentIndex()
|
||||||
|
new_idx = (current_idx + 1) % active.tab_widget.count()
|
||||||
|
active.tab_widget.setCurrentIndex(new_idx)
|
||||||
|
return
|
||||||
|
|
||||||
# Standard navigation
|
# Standard navigation
|
||||||
if button_code in BUTTONS['confirm']:
|
if button_code in BUTTONS['confirm']:
|
||||||
self._parent.activateFocusedWidget()
|
self._parent.activateFocusedWidget()
|
||||||
@@ -581,6 +612,7 @@ class InputManager(QObject):
|
|||||||
new_value = max(size_slider.value() - 10, size_slider.minimum())
|
new_value = max(size_slider.value() - 10, size_slider.minimum())
|
||||||
size_slider.setValue(new_value)
|
size_slider.setValue(new_value)
|
||||||
self._parent.on_slider_released()
|
self._parent.on_slider_released()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error in handle_button_slot: {e}", exc_info=True)
|
logger.error(f"Error in handle_button_slot: {e}", exc_info=True)
|
||||||
|
|
||||||
@@ -641,7 +673,7 @@ class InputManager(QObject):
|
|||||||
elif value < 0: # Left
|
elif value < 0: # Left
|
||||||
active.focusPreviousChild()
|
active.focusPreviousChild()
|
||||||
return
|
return
|
||||||
elif isinstance(active, QDialog) and code == ecodes.ABS_HAT0Y and value != 0: # Keep up/down for other dialogs
|
elif isinstance(active, QDialog) and code == ecodes.ABS_HAT0Y and value != 0 and not isinstance(focused, QTableWidget): # Skip if focused on table
|
||||||
if not focused or not active.focusWidget():
|
if not focused or not active.focusWidget():
|
||||||
# If no widget is focused, focus the first focusable widget
|
# If no widget is focused, focus the first focusable widget
|
||||||
focusables = active.findChildren(QWidget, options=Qt.FindChildOption.FindChildrenRecursively)
|
focusables = active.findChildren(QWidget, options=Qt.FindChildOption.FindChildrenRecursively)
|
||||||
@@ -694,6 +726,52 @@ class InputManager(QObject):
|
|||||||
active.show_next()
|
active.show_next()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Table navigation
|
||||||
|
if isinstance(focused, QTableWidget):
|
||||||
|
row_count = focused.rowCount()
|
||||||
|
if row_count <= 0:
|
||||||
|
return
|
||||||
|
current_row = focused.currentRow()
|
||||||
|
if current_row < 0:
|
||||||
|
current_row = 0
|
||||||
|
focused.setCurrentCell(0, 0)
|
||||||
|
|
||||||
|
if code == ecodes.ABS_HAT0Y and value != 0:
|
||||||
|
# Vertical navigation
|
||||||
|
if value > 0: # Down
|
||||||
|
new_row = min(current_row + 1, row_count - 1)
|
||||||
|
elif value < 0: # Up
|
||||||
|
new_row = max(current_row - 1, 0)
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
focused.setCurrentCell(new_row, focused.currentColumn())
|
||||||
|
item = focused.item(new_row, focused.currentColumn())
|
||||||
|
if item:
|
||||||
|
focused.scrollToItem(
|
||||||
|
item,
|
||||||
|
QAbstractItemView.ScrollHint.PositionAtCenter
|
||||||
|
)
|
||||||
|
focused.setFocus(Qt.FocusReason.OtherFocusReason)
|
||||||
|
return
|
||||||
|
elif code == ecodes.ABS_HAT0X and value != 0:
|
||||||
|
# Horizontal navigation
|
||||||
|
col_count = focused.columnCount()
|
||||||
|
current_col = focused.currentColumn()
|
||||||
|
if current_col < 0:
|
||||||
|
current_col = 0
|
||||||
|
|
||||||
|
if value < 0: # Left
|
||||||
|
new_col = max(current_col - 1, 0)
|
||||||
|
elif value > 0: # Right
|
||||||
|
new_col = min(current_col + 1, col_count - 1)
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
focused.setCurrentCell(focused.currentRow(), new_col)
|
||||||
|
focused.setFocus(Qt.FocusReason.OtherFocusReason)
|
||||||
|
return
|
||||||
|
|
||||||
# Library tab navigation (index 0)
|
# Library tab navigation (index 0)
|
||||||
if self._parent.stackedWidget.currentIndex() == 0 and code in (ecodes.ABS_HAT0X, ecodes.ABS_HAT0Y):
|
if self._parent.stackedWidget.currentIndex() == 0 and code in (ecodes.ABS_HAT0X, ecodes.ABS_HAT0Y):
|
||||||
focused = QApplication.focusWidget()
|
focused = QApplication.focusWidget()
|
||||||
|
Reference in New Issue
Block a user