7 Commits
main ... main

Author SHA1 Message Date
84d5e46a74 chore(changelog): update
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-10-15 22:53:08 +05:00
4bc764d568 partially revert b1047ba18e
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-10-15 22:31:35 +05:00
9a18aa037e feat(autoinstall): no restart on autoinstall finished
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-10-15 21:58:40 +05:00
ed62d2d1c4 fix: resolve lambda variable capture issue in switchTab method
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-10-15 21:47:14 +05:00
accc9b18b6 chore(localization): update
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-10-15 15:31:56 +05:00
82249d7eab feat(settings): Added Gamepad type settings
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-10-15 15:30:31 +05:00
476c896940 chore(TODO): update
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-10-15 12:44:01 +05:00
15 changed files with 155 additions and 104 deletions

View File

@@ -3,6 +3,23 @@
Все заметные изменения в этом проекте фиксируются в этом файле. Все заметные изменения в этом проекте фиксируются в этом файле.
Формат основан на [Keep a Changelog](https://keepachangelog.com/) и придерживается принципов [Semantic Versioning](https://semver.org/). Формат основан на [Keep a Changelog](https://keepachangelog.com/) и придерживается принципов [Semantic Versioning](https://semver.org/).
## [Unreleased]
### Added
- В настройки добавлен пункт для выбора типа геймпада для подсказок по управлению
### Changed
- При завершении автоустановки приложение больше не перезапускается
### Fixed
- Исправлено наложение карточек при смене фильтра игр
### Contributors
- @Vector_null
---
## [0.1.7] - 2025-10-12 ## [0.1.7] - 2025-10-12
### Added ### Added

15
TODO.md
View File

@@ -1,6 +1,6 @@
- [X] Адаптировать структуру проекта для поддержки инструментов сборки - [X] Адаптировать структуру проекта для поддержки инструментов сборки
- [X] Добавить возможность управления с геймпада - [X] Добавить возможность управления с геймпада
- [ ] Добавить возможность управления с тачскрина - [X] Добавить возможность управления с тачскрина (Формально и так есть)
- [X] Добавить возможность управления с мыши и клавиатуры - [X] Добавить возможность управления с мыши и клавиатуры
- [X] Добавить систему тем [Документация](documentation/theme_guide) - [X] Добавить систему тем [Документация](documentation/theme_guide)
- [X] Вынести все константы, такие как уровень закругления карточек, в темы (частично выполнено) - [X] Вынести все константы, такие как уровень закругления карточек, в темы (частично выполнено)
@@ -11,18 +11,18 @@
- [ ] Разработать адаптивный дизайн (за эталон берётся Steam Deck с разрешением 1280×800) - [ ] Разработать адаптивный дизайн (за эталон берётся Steam Deck с разрешением 1280×800)
- [ ] Переделать скриншоты для соответствия [гайдлайнам Flathub](https://docs.flathub.org/docs/for-app-authors/metainfo-guidelines/quality-guidelines#screenshots) - [ ] Переделать скриншоты для соответствия [гайдлайнам Flathub](https://docs.flathub.org/docs/for-app-authors/metainfo-guidelines/quality-guidelines#screenshots)
- [X] Получать описания и названия игр из базы данных Steam - [X] Получать описания и названия игр из базы данных Steam
- [X] Получать обложки для игр из SteamGridDB или CDN Steam - [X] Получать обложки для игр из CDN Steam
- [X] Оптимизировать работу со Steam API для ускорения времени запуска - [X] Оптимизировать работу со Steam API для ускорения времени запуска
- [X] Улучшить функцию поиска в Steam API для исправления некорректного определения ID (например, Graven определялся как ENGRAVEN или GRAVENFALL, Spore — как SporeBound или Spore Valley) - [X] Улучшить функцию поиска в Steam API для исправления некорректного определения ID (например, Graven определялся как ENGRAVEN или GRAVENFALL, Spore — как SporeBound или Spore Valley)
- [ ] Убрать логи Steam API в релизной версии, так как они замедляют выполнение кода - [X] Убрать логи Steam API в релизной версии, так как они замедляют выполнение кода
- [X] Решить проблему с ограничением Steam API в 50 тысяч игр за один запрос (иногда нужные игры не попадают в выборку и остаются без обложки) - [X] Решить проблему с ограничением Steam API в 50 тысяч игр за один запрос (иногда нужные игры не попадают в выборку и остаются без обложки)
- [X] Избавиться от вызовов yad - [X] Избавиться от вызовов yad
- [X] Реализовать собственный системный трей вместо использования трея PortProton - [X] Реализовать собственный системный трей вместо использования трея PortProton
- [X] Добавить экранную клавиатуру в поиск (реализация собственной клавиатуры слишком затратна, поэтому используется встроенная в DE клавиатура: Maliit в KDE, gjs-osk в GNOME, Squeekboard в Phosh, клавиатура SteamOS и т.д.) - [X] Добавить экранную клавиатуру в поиск
- [X] Добавить сортировку карточек по различным критериям (доступны: по недавности, количеству наигранного времени, избранному или алфавиту) - [X] Добавить сортировку карточек по различным критериям (доступны: по недавности, количеству наигранного времени, избранному или алфавиту)
- [X] Добавить индикацию запуска приложения - [X] Добавить индикацию запуска приложения
- [X] Достигнуть паритета функциональности с Ingame - [X] Достигнуть паритета функциональности с Ingame
- [ ] Достигнуть паритета функциональности с PortProton - [ ] Достигнуть паритета функциональности с PortProton (остались настройки игр и обновление скриптов)
- [X] Добавить возможность изменения названия, описания и обложки через файлы `.local/share/PortProtonQT/custom_data/exe_name/{desc,name,cover}` - [X] Добавить возможность изменения названия, описания и обложки через файлы `.local/share/PortProtonQT/custom_data/exe_name/{desc,name,cover}`
- [X] Добавить встроенное переопределение названия, описания и обложки, например, по пути `portprotonqt/custom_data` [Документация](documentation/metadata_override/) - [X] Добавить встроенное переопределение названия, описания и обложки, например, по пути `portprotonqt/custom_data` [Документация](documentation/metadata_override/)
- [X] Добавить переводы в переопределения - [X] Добавить переводы в переопределения
@@ -49,7 +49,7 @@
- [X] Добавить недокументированные параметры конфигурации в GUI (time_detail_level, games_sort_method, games_display_filter) - [X] Добавить недокументированные параметры конфигурации в GUI (time_detail_level, games_sort_method, games_display_filter)
- [X] Добавить систему избранного для карточек - [X] Добавить систему избранного для карточек
- [X] Заменить все `print` на `logging` - [X] Заменить все `print` на `logging`
- [ ] Привести все логи к единому языку - [X] Привести все логи к единому языку
- [X] Уменьшить количество подстановок в переводах - [X] Уменьшить количество подстановок в переводах
- [X] Стилизовать все элементы без стилей (QMessageBox, QSlider, QDialog) - [X] Стилизовать все элементы без стилей (QMessageBox, QSlider, QDialog)
- [X] Убрать жёсткую привязку путей к стрелочкам QComboBox в `styles.py` - [X] Убрать жёсткую привязку путей к стрелочкам QComboBox в `styles.py`
@@ -62,7 +62,6 @@
- [X] Исправить некорректную работу слайдера увеличения размера карточек([Последствия регрессии после этого коммита](https://github.com/Boria138/PortProtonQt/commit/aebdd60b5537280f06a922ff80469cd4ab27bc63) - [X] Исправить некорректную работу слайдера увеличения размера карточек([Последствия регрессии после этого коммита](https://github.com/Boria138/PortProtonQt/commit/aebdd60b5537280f06a922ff80469cd4ab27bc63)
- [X] Исправить баг с наложением карточек друг на друга при изменении фильтра отображения ([Последствия регрессии после этого коммита](https://github.com/Boria138/PortProtonQt/commit/aebdd60b5537280f06a922ff80469cd4ab27bc63)) - [X] Исправить баг с наложением карточек друг на друга при изменении фильтра отображения ([Последствия регрессии после этого коммита](https://github.com/Boria138/PortProtonQt/commit/aebdd60b5537280f06a922ff80469cd4ab27bc63))
- [X] Скопировать логику управления с D-pad на стрелки с клавиатуры - [X] Скопировать логику управления с D-pad на стрелки с клавиатуры
- [ ] Доделать светлую тему - [X] Добавить подсказки к управлению с геймпада
- [ ] Добавить подсказки к управлению с геймпада
- [X] Добавить миниатюры к выбору файлов в диалоге добавления игры - [X] Добавить миниатюры к выбору файлов в диалоге добавления игры
- [X] Добавить быстрый доступ к смонтированным дискам к выбору файлов в диалоге добавления игры - [X] Добавить быстрый доступ к смонтированным дискам к выбору файлов в диалоге добавления игры

View File

@@ -21,9 +21,9 @@ Current translation status:
| Locale | Progress | Translated | | Locale | Progress | Translated |
| :----- | -------: | ---------: | | :----- | -------: | ---------: |
| [de_DE](./de_DE/LC_MESSAGES/messages.po) | 0% | 0 of 240 | | [de_DE](./de_DE/LC_MESSAGES/messages.po) | 0% | 0 of 241 |
| [es_ES](./es_ES/LC_MESSAGES/messages.po) | 0% | 0 of 240 | | [es_ES](./es_ES/LC_MESSAGES/messages.po) | 0% | 0 of 241 |
| [ru_RU](./ru_RU/LC_MESSAGES/messages.po) | 100% | 240 of 240 | | [ru_RU](./ru_RU/LC_MESSAGES/messages.po) | 100% | 241 of 241 |
--- ---

View File

@@ -21,9 +21,9 @@
| Локаль | Прогресс | Переведено | | Локаль | Прогресс | Переведено |
| :----- | -------: | ---------: | | :----- | -------: | ---------: |
| [de_DE](./de_DE/LC_MESSAGES/messages.po) | 0% | 0 из 240 | | [de_DE](./de_DE/LC_MESSAGES/messages.po) | 0% | 0 из 241 |
| [es_ES](./es_ES/LC_MESSAGES/messages.po) | 0% | 0 из 240 | | [es_ES](./es_ES/LC_MESSAGES/messages.po) | 0% | 0 из 241 |
| [ru_RU](./ru_RU/LC_MESSAGES/messages.po) | 100% | 240 из 240 | | [ru_RU](./ru_RU/LC_MESSAGES/messages.po) | 100% | 241 из 241 |
--- ---

View File

@@ -259,6 +259,25 @@ def save_rumble_config(rumble_enabled):
with open(CONFIG_FILE, "w", encoding="utf-8") as configfile: with open(CONFIG_FILE, "w", encoding="utf-8") as configfile:
cp.write(configfile) cp.write(configfile)
def read_gamepad_type():
"""Reads the gamepad type from the [Gamepad] section.
Returns 'xbox' if the parameter is missing.
"""
cp = read_config_safely(CONFIG_FILE)
if cp is None or not cp.has_section("Gamepad") or not cp.has_option("Gamepad", "type"):
save_gamepad_type("xbox")
return "xbox"
return cp.get("Gamepad", "type", fallback="xbox").lower()
def save_gamepad_type(gpad_type):
"""Saves the gamepad type to the [Gamepad] section."""
cp = read_config_safely(CONFIG_FILE) or configparser.ConfigParser()
if "Gamepad" not in cp:
cp["Gamepad"] = {}
cp["Gamepad"]["type"] = gpad_type
with open(CONFIG_FILE, "w", encoding="utf-8") as configfile:
cp.write(configfile)
def ensure_default_proxy_config(): def ensure_default_proxy_config():
"""Ensures the [Proxy] section exists in the configuration file. """Ensures the [Proxy] section exists in the configuration file.
Creates it with empty values if missing. Creates it with empty values if missing.

View File

@@ -56,16 +56,6 @@ class GameLibraryManager:
self.is_filtering = False self.is_filtering = False
self.dirty = False self.dirty = False
def force_update_cards_library(self):
if self.gamesListWidget and self.gamesListLayout:
self.gamesListLayout.invalidate()
self.gamesListWidget.updateGeometry()
widget = self.gamesListWidget
QTimer.singleShot(0, lambda: (
widget.adjustSize(),
widget.updateGeometry()
))
def create_games_library_widget(self): def create_games_library_widget(self):
"""Creates the games library widget with search, grid, and slider.""" """Creates the games library widget with search, grid, and slider."""
self.gamesLibraryWidget = QWidget() self.gamesLibraryWidget = QWidget()
@@ -227,6 +217,16 @@ class GameLibraryManager:
else: else:
self._update_game_grid_immediate() self._update_game_grid_immediate()
def force_update_cards_library(self):
if self.gamesListWidget and self.gamesListLayout:
self.gamesListLayout.invalidate()
self.gamesListWidget.updateGeometry()
widget = self.gamesListWidget
QTimer.singleShot(0, lambda: (
widget.adjustSize(),
widget.updateGeometry()
))
def _update_game_grid_immediate(self): def _update_game_grid_immediate(self):
"""Updates the game grid with the provided or current game list.""" """Updates the game grid with the provided or current game list."""
if self.gamesListLayout is None or self.gamesListWidget is None: if self.gamesListLayout is None or self.gamesListWidget is None:

View File

@@ -12,7 +12,7 @@ from portprotonqt.logger import get_logger
from portprotonqt.image_utils import FullscreenDialog 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, read_gamepad_type
from portprotonqt.dialogs import AddGameDialog, WinetricksDialog from portprotonqt.dialogs import AddGameDialog, WinetricksDialog
from portprotonqt.virtual_keyboard import VirtualKeyboard from portprotonqt.virtual_keyboard import VirtualKeyboard
@@ -87,8 +87,13 @@ class InputManager(QObject):
super().__init__(cast(QObject, main_window)) super().__init__(cast(QObject, main_window))
self._parent = main_window self._parent = main_window
self._gamepad_handling_enabled = True self._gamepad_handling_enabled = True
type_str = read_gamepad_type()
if type_str == "playstation":
self.gamepad_type = GamepadType.PLAYSTATION
elif type_str == "xbox":
self.gamepad_type = GamepadType.XBOX
else:
self.gamepad_type = GamepadType.UNKNOWN self.gamepad_type = GamepadType.UNKNOWN
# Ensure attributes exist on main_window
self._parent.currentDetailPage = getattr(self._parent, 'currentDetailPage', None) self._parent.currentDetailPage = getattr(self._parent, 'currentDetailPage', None)
self._parent.current_exec_line = getattr(self._parent, 'current_exec_line', None) self._parent.current_exec_line = getattr(self._parent, 'current_exec_line', None)
self._parent.current_add_game_dialog = getattr(self._parent, 'current_add_game_dialog', None) self._parent.current_add_game_dialog = getattr(self._parent, 'current_add_game_dialog', None)
@@ -271,38 +276,6 @@ class InputManager(QObject):
elif current_row_idx == 0: elif current_row_idx == 0:
self._parent.tabButtons[tab_index].setFocus(Qt.FocusReason.OtherFocusReason) self._parent.tabButtons[tab_index].setFocus(Qt.FocusReason.OtherFocusReason)
def detect_gamepad_type(self, device: InputDevice) -> GamepadType:
"""
Определяет тип геймпада по capabilities
"""
caps = device.capabilities()
keys = set(caps.get(ecodes.EV_KEY, []))
# Для EV_ABS вытаскиваем только коды (первый элемент кортежа)
abs_axes = {a if isinstance(a, int) else a[0] for a in caps.get(ecodes.EV_ABS, [])}
# Xbox layout
if {ecodes.BTN_SOUTH, ecodes.BTN_EAST, ecodes.BTN_NORTH, ecodes.BTN_WEST}.issubset(keys):
if {ecodes.ABS_X, ecodes.ABS_Y, ecodes.ABS_RX, ecodes.ABS_RY}.issubset(abs_axes):
self.gamepad_type = GamepadType.XBOX
return GamepadType.XBOX
# PlayStation layout
if ecodes.BTN_TOUCH in keys or (ecodes.BTN_DPAD_UP in keys and ecodes.BTN_EAST in keys):
self.gamepad_type = GamepadType.PLAYSTATION
logger.info(f"Detected {self.gamepad_type.value} controller: {device.name}")
return GamepadType.PLAYSTATION
# Steam Controller / Deck (трекпады)
if any(a for a in abs_axes if a >= ecodes.ABS_MT_SLOT):
self.gamepad_type = GamepadType.XBOX
logger.info(f"Detected {self.gamepad_type.value} controller: {device.name}")
return GamepadType.XBOX
# Fallback
self.gamepad_type = GamepadType.XBOX
return GamepadType.XBOX
def enable_file_explorer_mode(self, file_explorer): def enable_file_explorer_mode(self, file_explorer):
"""Настройка обработки геймпада для FileExplorer""" """Настройка обработки геймпада для FileExplorer"""
try: try:
@@ -1369,8 +1342,6 @@ class InputManager(QObject):
new_gamepad = self.find_gamepad() new_gamepad = self.find_gamepad()
if new_gamepad and new_gamepad != self.gamepad: if new_gamepad and new_gamepad != self.gamepad:
logger.info(f"Gamepad connected: {new_gamepad.name}") logger.info(f"Gamepad connected: {new_gamepad.name}")
self.detect_gamepad_type(new_gamepad)
logger.info(f"Detected gamepad type: {self.gamepad_type.value}")
self.stop_rumble() self.stop_rumble()
self.gamepad = new_gamepad self.gamepad = new_gamepad
if self.gamepad_thread: if self.gamepad_thread:

View File

@@ -9,7 +9,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PROJECT VERSION\n" "Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-10-12 17:14+0500\n" "POT-Creation-Date: 2025-10-15 15:31+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: de_DE\n" "Language: de_DE\n"
@@ -579,6 +579,9 @@ msgstr ""
msgid "Games Display Filter:" msgid "Games Display Filter:"
msgstr "" msgstr ""
msgid "Gamepad Type:"
msgstr ""
msgid "Proxy URL" msgid "Proxy URL"
msgstr "" msgstr ""

View File

@@ -9,7 +9,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PROJECT VERSION\n" "Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-10-12 17:14+0500\n" "POT-Creation-Date: 2025-10-15 15:31+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: es_ES\n" "Language: es_ES\n"
@@ -579,6 +579,9 @@ msgstr ""
msgid "Games Display Filter:" msgid "Games Display Filter:"
msgstr "" msgstr ""
msgid "Gamepad Type:"
msgstr ""
msgid "Proxy URL" msgid "Proxy URL"
msgstr "" msgstr ""

View File

@@ -9,7 +9,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PortProtonQt 0.1.1\n" "Project-Id-Version: PortProtonQt 0.1.1\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-10-12 17:14+0500\n" "POT-Creation-Date: 2025-10-15 15:31+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -577,6 +577,9 @@ msgstr ""
msgid "Games Display Filter:" msgid "Games Display Filter:"
msgstr "" msgstr ""
msgid "Gamepad Type:"
msgstr ""
msgid "Proxy URL" msgid "Proxy URL"
msgstr "" msgstr ""

View File

@@ -9,8 +9,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PROJECT VERSION\n" "Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-10-12 17:14+0500\n" "POT-Creation-Date: 2025-10-15 15:31+0500\n"
"PO-Revision-Date: 2025-10-12 17:13+0500\n" "PO-Revision-Date: 2025-10-15 15:31+0500\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language: ru_RU\n" "Language: ru_RU\n"
"Language-Team: ru_RU <LL@li.org>\n" "Language-Team: ru_RU <LL@li.org>\n"
@@ -588,6 +588,9 @@ msgstr "все"
msgid "Games Display Filter:" msgid "Games Display Filter:"
msgstr "Фильтр игр:" msgstr "Фильтр игр:"
msgid "Gamepad Type:"
msgstr "Тип геймпада:"
msgid "Proxy URL" msgid "Proxy URL"
msgstr "Адрес прокси" msgstr "Адрес прокси"

View File

@@ -29,7 +29,7 @@ from portprotonqt.config_utils import (
read_display_filter, read_favorites, save_favorites, save_time_config, save_sort_method, read_display_filter, read_favorites, save_favorites, save_time_config, save_sort_method,
save_display_filter, save_proxy_config, read_proxy_config, read_fullscreen_config, save_display_filter, save_proxy_config, read_proxy_config, read_fullscreen_config,
save_fullscreen_config, read_window_geometry, save_window_geometry, reset_config, save_fullscreen_config, read_window_geometry, save_window_geometry, reset_config,
clear_cache, read_auto_fullscreen_gamepad, save_auto_fullscreen_gamepad, read_rumble_config, save_rumble_config clear_cache, read_auto_fullscreen_gamepad, save_auto_fullscreen_gamepad, read_rumble_config, save_rumble_config, read_gamepad_type, save_gamepad_type
) )
from portprotonqt.localization import _, get_egs_language, read_metadata_translations from portprotonqt.localization import _, get_egs_language, read_metadata_translations
from portprotonqt.howlongtobeat_api import HowLongToBeat from portprotonqt.howlongtobeat_api import HowLongToBeat
@@ -100,7 +100,6 @@ class MainWindow(QMainWindow):
self.games_load_timer.timeout.connect(self.finalize_game_loading) self.games_load_timer.timeout.connect(self.finalize_game_loading)
self.games_loaded.connect(self.on_games_loaded) self.games_loaded.connect(self.on_games_loaded)
self.current_add_game_dialog = None self.current_add_game_dialog = None
self.current_display_filter = read_display_filter()
self.settingsDebounceTimer = QTimer(self) self.settingsDebounceTimer = QTimer(self)
self.settingsDebounceTimer.setSingleShot(True) self.settingsDebounceTimer.setSingleShot(True)
@@ -517,12 +516,26 @@ class MainWindow(QMainWindow):
self.install_monitor_timer = None self.install_monitor_timer = None
self.progress_bar.setRange(0, 100) self.progress_bar.setRange(0, 100)
self.progress_bar.setValue(100) self.progress_bar.setValue(100)
if exit_code == 0: if exit_code == 0:
self.update_status_message.emit(_("Installation completed successfully."), 5000) self.update_status_message.emit(_("Installation completed successfully."), 5000)
QTimer.singleShot(500, lambda: self.restart_application())
desktop_dir = self.portproton_location or ""
new_desktops = [e.path for e in os.scandir(desktop_dir) if e.name.endswith(".desktop")]
if new_desktops:
latest = max(new_desktops, key=os.path.getmtime)
self._process_desktop_file_async(
latest,
lambda result: (
self.game_library_manager.add_game_incremental(result)
if result else None
)
)
else: else:
self.update_status_message.emit(_("Installation failed."), 5000) self.update_status_message.emit(_("Installation failed."), 5000)
QMessageBox.warning(self, _("Error"), f"Installation failed (code: {exit_code}).") QMessageBox.warning(self, _("Error"), f"Installation failed (code: {exit_code}).")
self.progress_bar.setVisible(False) self.progress_bar.setVisible(False)
self.current_install_script = None self.current_install_script = None
if self.install_process: if self.install_process:
@@ -824,22 +837,23 @@ class MainWindow(QMainWindow):
if hasattr(self, "game_library_manager"): if hasattr(self, "game_library_manager"):
mgr = self.game_library_manager mgr = self.game_library_manager
if mgr.gamesListWidget and mgr.gamesListLayout: if mgr.gamesListWidget and mgr.gamesListLayout:
layout = mgr.gamesListLayout games_layout = mgr.gamesListLayout
widget = mgr.gamesListWidget games_widget = mgr.gamesListWidget
QTimer.singleShot(0, lambda: ( QTimer.singleShot(0, lambda: (
layout.invalidate(), games_layout.invalidate(),
widget.adjustSize(), games_widget.adjustSize(),
widget.updateGeometry() games_widget.updateGeometry()
)) ))
if hasattr(self, "autoInstallContainer") and hasattr(self, "autoInstallContainerLayout"): if hasattr(self, "autoInstallContainer") and hasattr(self, "autoInstallContainerLayout"):
layout = self.autoInstallContainerLayout auto_layout = self.autoInstallContainerLayout
widget = self.autoInstallContainer auto_widget = self.autoInstallContainer
QTimer.singleShot(0, lambda: ( QTimer.singleShot(0, lambda: (
layout.invalidate(), auto_layout.invalidate(),
widget.adjustSize(), auto_widget.adjustSize(),
widget.updateGeometry() auto_widget.updateGeometry()
)) ))
def openSystemOverlay(self): def openSystemOverlay(self):
"""Opens the system overlay dialog.""" """Opens the system overlay dialog."""
overlay = SystemOverlay(self, self.theme) overlay = SystemOverlay(self, self.theme)
@@ -1784,7 +1798,22 @@ class MainWindow(QMainWindow):
self.gamesDisplayCombo.setCurrentIndex(idx) self.gamesDisplayCombo.setCurrentIndex(idx)
formLayout.addRow(self.gamesDisplayTitle, self.gamesDisplayCombo) formLayout.addRow(self.gamesDisplayTitle, self.gamesDisplayCombo)
# 4. Proxy settings # 4 Gamepad Type
self.gamepadTypeCombo = QComboBox()
self.gamepadTypeCombo.addItems(["Xbox", "PlayStation"])
self.gamepadTypeCombo.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
self.gamepadTypeCombo.setStyleSheet(self.theme.SETTINGS_COMBO_STYLE)
self.gamepadTypeTitle = QLabel(_("Gamepad Type:"))
self.gamepadTypeTitle.setStyleSheet(self.theme.PARAMS_TITLE_STYLE)
self.gamepadTypeTitle.setFocusPolicy(Qt.FocusPolicy.NoFocus)
current_type_str = read_gamepad_type()
if current_type_str == "playstation":
self.gamepadTypeCombo.setCurrentText("PlayStation")
else:
self.gamepadTypeCombo.setCurrentText("Xbox")
formLayout.addRow(self.gamepadTypeTitle, self.gamepadTypeCombo)
# 5. Proxy settings
self.proxyUrlEdit = CustomLineEdit(self, theme=self.theme) self.proxyUrlEdit = CustomLineEdit(self, theme=self.theme)
self.proxyUrlEdit.setPlaceholderText(_("Proxy URL")) self.proxyUrlEdit.setPlaceholderText(_("Proxy URL"))
self.proxyUrlEdit.setStyleSheet(self.theme.PROXY_INPUT_STYLE) self.proxyUrlEdit.setStyleSheet(self.theme.PROXY_INPUT_STYLE)
@@ -1816,7 +1845,7 @@ class MainWindow(QMainWindow):
self.proxyPasswordTitle.setFocusPolicy(Qt.FocusPolicy.NoFocus) self.proxyPasswordTitle.setFocusPolicy(Qt.FocusPolicy.NoFocus)
formLayout.addRow(self.proxyPasswordTitle, self.proxyPasswordEdit) formLayout.addRow(self.proxyPasswordTitle, self.proxyPasswordEdit)
# 5. Fullscreen setting for application # 6. Fullscreen setting for application
self.fullscreenCheckBox = QCheckBox(_("Launch Application in Fullscreen")) self.fullscreenCheckBox = QCheckBox(_("Launch Application in Fullscreen"))
self.fullscreenCheckBox.setStyleSheet(self.theme.SETTINGS_CHECKBOX_STYLE) self.fullscreenCheckBox.setStyleSheet(self.theme.SETTINGS_CHECKBOX_STYLE)
self.fullscreenCheckBox.setFocusPolicy(Qt.FocusPolicy.StrongFocus) self.fullscreenCheckBox.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
@@ -1827,7 +1856,7 @@ class MainWindow(QMainWindow):
self.fullscreenCheckBox.setChecked(current_fullscreen) self.fullscreenCheckBox.setChecked(current_fullscreen)
formLayout.addRow(self.fullscreenTitle, self.fullscreenCheckBox) formLayout.addRow(self.fullscreenTitle, self.fullscreenCheckBox)
# 6. Automatic fullscreen on gamepad connection # 7. Automatic fullscreen on gamepad connection
self.autoFullscreenGamepadCheckBox = QCheckBox(_("Auto Fullscreen on Gamepad connected")) self.autoFullscreenGamepadCheckBox = QCheckBox(_("Auto Fullscreen on Gamepad connected"))
self.autoFullscreenGamepadCheckBox.setStyleSheet(self.theme.SETTINGS_CHECKBOX_STYLE) self.autoFullscreenGamepadCheckBox.setStyleSheet(self.theme.SETTINGS_CHECKBOX_STYLE)
self.autoFullscreenGamepadCheckBox.setFocusPolicy(Qt.FocusPolicy.StrongFocus) self.autoFullscreenGamepadCheckBox.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
@@ -1839,7 +1868,7 @@ class MainWindow(QMainWindow):
self.autoFullscreenGamepadCheckBox.setChecked(current_auto_fullscreen) self.autoFullscreenGamepadCheckBox.setChecked(current_auto_fullscreen)
formLayout.addRow(self.autoFullscreenGamepadTitle, self.autoFullscreenGamepadCheckBox) formLayout.addRow(self.autoFullscreenGamepadTitle, self.autoFullscreenGamepadCheckBox)
# 7. Gamepad haptic feedback config # 8. Gamepad haptic feedback config
self.gamepadRumbleCheckBox = QCheckBox(_("Gamepad haptic feedback")) self.gamepadRumbleCheckBox = QCheckBox(_("Gamepad haptic feedback"))
self.gamepadRumbleCheckBox.setFocusPolicy(Qt.FocusPolicy.StrongFocus) self.gamepadRumbleCheckBox.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
self.gamepadRumbleCheckBox.setStyleSheet(self.theme.SETTINGS_CHECKBOX_STYLE) self.gamepadRumbleCheckBox.setStyleSheet(self.theme.SETTINGS_CHECKBOX_STYLE)
@@ -1850,7 +1879,7 @@ class MainWindow(QMainWindow):
self.gamepadRumbleCheckBox.setChecked(current_rumble_state) self.gamepadRumbleCheckBox.setChecked(current_rumble_state)
formLayout.addRow(self.gamepadRumbleTitle, self.gamepadRumbleCheckBox) formLayout.addRow(self.gamepadRumbleTitle, self.gamepadRumbleCheckBox)
# # 8. Legendary Authentication # # 9. Legendary Authentication
# self.legendaryAuthButton = AutoSizeButton( # self.legendaryAuthButton = AutoSizeButton(
# _("Open Legendary Login"), # _("Open Legendary Login"),
# icon=self.theme_manager.get_icon("login")self.theme_manager.get_icon("login") # icon=self.theme_manager.get_icon("login")self.theme_manager.get_icon("login")
@@ -1997,12 +2026,9 @@ class MainWindow(QMainWindow):
def applySettingsDelayed(self): def applySettingsDelayed(self):
read_time_config() read_time_config()
display_filter = read_display_filter()
reload_needed = display_filter != self.current_display_filter
if reload_needed:
self.games = [] self.games = []
self.loadGames() self.loadGames()
self.current_display_filter = display_filter display_filter = read_display_filter()
for card in self.game_library_manager.game_card_cache.values(): for card in self.game_library_manager.game_card_cache.values():
card.update_badge_visibility(display_filter) card.update_badge_visibility(display_filter)
@@ -2017,10 +2043,6 @@ class MainWindow(QMainWindow):
filter_idx = self.gamesDisplayCombo.currentIndex() filter_idx = self.gamesDisplayCombo.currentIndex()
filter_key = self.filter_keys[filter_idx] filter_key = self.filter_keys[filter_idx]
old_filter = self.current_display_filter
save_display_filter(filter_key)
save_display_filter(filter_key) save_display_filter(filter_key)
proxy_url = self.proxyUrlEdit.text().strip() proxy_url = self.proxyUrlEdit.text().strip()
@@ -2037,7 +2059,19 @@ class MainWindow(QMainWindow):
rumble_enabled = self.gamepadRumbleCheckBox.isChecked() rumble_enabled = self.gamepadRumbleCheckBox.isChecked()
save_rumble_config(rumble_enabled) save_rumble_config(rumble_enabled)
if filter_key != old_filter: gamepad_type_text = self.gamepadTypeCombo.currentText()
gpad_type = "playstation" if gamepad_type_text == "PlayStation" else "xbox"
save_gamepad_type(gpad_type)
if hasattr(self, 'input_manager'):
if gpad_type == "playstation":
self.input_manager.gamepad_type = GamepadType.PLAYSTATION
elif gpad_type == "xbox":
self.input_manager.gamepad_type = GamepadType.XBOX
else:
self.input_manager.gamepad_type = GamepadType.UNKNOWN
self.updateControlHints()
for card in self.game_library_manager.game_card_cache.values(): for card in self.game_library_manager.game_card_cache.values():
card.update_badge_visibility(filter_key) card.update_badge_visibility(filter_key)
@@ -2049,7 +2083,6 @@ class MainWindow(QMainWindow):
self.currentDetailPage = None self.currentDetailPage = None
self.openGameDetailPage(*current_game) self.openGameDetailPage(*current_game)
self.current_display_filter = filter_key
self.settingsDebounceTimer.start() self.settingsDebounceTimer.start()
gamepad_connected = self.input_manager.find_gamepad() is not None gamepad_connected = self.input_manager.find_gamepad() is not None