forked from Boria138/PortProtonQt
Compare commits
16 Commits
main
...
debian-tes
| Author | SHA1 | Date | |
|---|---|---|---|
|
b8773f3b68
|
|||
|
f4275dd465
|
|||
|
c8b91c4687
|
|||
|
4aaeb2e809
|
|||
|
b6ea9350fa
|
|||
|
29d25cec01
|
|||
|
a634de5462
|
|||
|
1ba1781994
|
|||
|
0aae292f61
|
|||
|
3ef433af0c
|
|||
|
|
9fe33e02d8 | ||
|
2ac91a759d
|
|||
|
2c82bff204
|
|||
|
0889aa883e
|
|||
|
7780dcfc4d
|
|||
|
9ef39ae2b6
|
@@ -8,6 +8,36 @@ env:
|
||||
PACKAGE: "portprotonqt"
|
||||
|
||||
jobs:
|
||||
build-deb:
|
||||
name: Build Debian Package
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y build-essential git python3-dev python3-pip python3-wheel debhelper dh-python \
|
||||
devscripts python3-all bash-completion python3-setuptools
|
||||
|
||||
- name: Checkout repo
|
||||
uses: https://gitea.com/actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
||||
|
||||
- name: Build Debian package
|
||||
run: |
|
||||
# Copy the debian directory to the root for proper packaging
|
||||
cp -r build-aux/debian .
|
||||
|
||||
# Build the package
|
||||
dpkg-buildpackage -us -uc -b
|
||||
|
||||
mv ../*.deb .
|
||||
|
||||
- name: Upload Debian package
|
||||
uses: https://gitea.com/actions/gitea-upload-artifact@v4
|
||||
with:
|
||||
name: PortProtonQt-Debian
|
||||
path: "*.deb"
|
||||
|
||||
build-appimage:
|
||||
name: Build AppImage
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
@@ -8,7 +8,7 @@ on:
|
||||
|
||||
env:
|
||||
# Common version, will be used for tagging the release
|
||||
VERSION: 0.1.8
|
||||
VERSION: 0.1.9
|
||||
PKGDEST: "/tmp/portprotonqt"
|
||||
PACKAGE: "portprotonqt"
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
@@ -147,9 +147,37 @@ jobs:
|
||||
name: PortProtonQt-RPM-Fedora-${{ matrix.fedora_version }}
|
||||
path: /home/rpmbuild/RPMS/**/*.rpm
|
||||
|
||||
build-deb:
|
||||
name: Build Debian Package
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y build-essential git python3-dev python3-pip python3-wheel debhelper dh-python \
|
||||
devscripts python3-all bash-completion python3-setuptools
|
||||
|
||||
- name: Checkout repo
|
||||
uses: https://gitea.com/actions/checkout@v4
|
||||
|
||||
- name: Build Debian package
|
||||
run: |
|
||||
# Copy the debian directory to the root for proper packaging
|
||||
cp -r build-aux/debian .
|
||||
|
||||
# Build the package
|
||||
dpkg-buildpackage -us -uc -b
|
||||
|
||||
- name: Upload Debian package
|
||||
uses: https://gitea.com/actions/gitea-upload-artifact@v4
|
||||
with:
|
||||
name: PortProtonQt-Debian
|
||||
path: "*.deb"
|
||||
|
||||
release:
|
||||
name: Create and Publish Release
|
||||
needs: [build-appimage, build-arch, build-fedora]
|
||||
needs: [build-appimage, build-arch, build-fedora, build-deb]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: https://gitea.com/actions/checkout@v4
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Все заметные изменения в этом проекте фиксируются в этом файле.
|
||||
Формат основан на [Keep a Changelog](https://keepachangelog.com/) и придерживается принципов [Semantic Versioning](https://semver.org/).
|
||||
|
||||
## [Unreleased]
|
||||
## [0.1.9] - 2025-12-01
|
||||
|
||||
### Added
|
||||
- Добавлены основные и расширенные настройки для `.exe`-файлов
|
||||
@@ -18,11 +18,15 @@
|
||||
- Ускорено чтение конфигов за счёт уменьшения количества обращений к файловой системе.
|
||||
- Из стандартной темы удалены неиспользуемые шрифты
|
||||
- Улучшена совместимость с Qt 6.10
|
||||
- Ускорен запуск программы
|
||||
- В диалог редактирования ярылыка добавлен placeholder с уточнением того что в качевстве обложки можно использовать и ссылку, а не только файл
|
||||
- Ссылку на обложку в диалоге редактирования ярлыка теперь можно указывать без протокола вроде http или https
|
||||
|
||||
### Fixed
|
||||
- Добавлено больше проверок на None для избежания вылетов
|
||||
- Улучшена работа с потоками для избежания вылетов
|
||||
- Исправлен запуск PortProton из Flatpak: теперь используется `flatpak run`, а не `start.sh`
|
||||
- Исправлено применение обложки по ссылке например со steamgriddb.com/
|
||||
|
||||
### Contributors
|
||||
- @Vector_null
|
||||
|
||||
@@ -16,7 +16,7 @@ source .venv/bin/activate
|
||||
|
||||
### Установка (release)
|
||||
|
||||
Выберите подходящий пакет для вашей системы или AppImage.
|
||||
Выберите подходящий пакет для вашей системы (deb, rpm, arch) или AppImage.
|
||||
|
||||
Запуск производится по команде portprotonqt или по ярлыку в меню
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ AppDir:
|
||||
id: ru.linux_gaming.PortProtonQt
|
||||
name: PortProtonQt
|
||||
icon: ru.linux_gaming.PortProtonQt
|
||||
version: 0.1.8
|
||||
version: 0.1.9
|
||||
exec: usr/bin/python3
|
||||
exec_args: "-m portprotonqt.app $@"
|
||||
apt:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
pkgname=portprotonqt
|
||||
pkgver=0.1.8
|
||||
pkgver=0.1.9
|
||||
pkgrel=1
|
||||
pkgdesc="Modern GUI for managing and launching games from PortProton, Steam, and Epic Games Store"
|
||||
arch=('any')
|
||||
|
||||
5
build-aux/debian/changelog
Normal file
5
build-aux/debian/changelog
Normal file
@@ -0,0 +1,5 @@
|
||||
portprotonqt (0.1.9-1) unstable; urgency=medium
|
||||
|
||||
* Initial release for Debian packaging
|
||||
|
||||
-- Boris Yumankulov <boria138@altlinux.org> Wed, 03 Dec 2025 12:00:00 +0000
|
||||
44
build-aux/debian/control
Normal file
44
build-aux/debian/control
Normal file
@@ -0,0 +1,44 @@
|
||||
Source: portprotonqt
|
||||
Section: python
|
||||
Priority: optional
|
||||
Maintainer: Boris Yumankulov <boria138@altlinux.org>
|
||||
Build-Depends: debhelper-compat (= 13),
|
||||
dh-python,
|
||||
python3-all,
|
||||
python3-setuptools,
|
||||
python3-pip
|
||||
Standards-Version: 4.6.0
|
||||
Homepage: https://git.linux-gaming.ru/Boria138/PortProtonQt
|
||||
Rules-Requires-Root: no
|
||||
X-Python3-Version: >= 3.10
|
||||
|
||||
Package: portprotonqt
|
||||
Architecture: all
|
||||
Depends: ${python3:Depends}, ${misc:Depends},
|
||||
python3-babel,
|
||||
python3-beautifulsoup4,
|
||||
python3-evdev,
|
||||
python3-icoextract,
|
||||
python3-numpy,
|
||||
python3-orjson,
|
||||
python3-pillow,
|
||||
python3-psutil,
|
||||
python3-pyside6,
|
||||
python3-pyudev,
|
||||
python3-rapidfuzz,
|
||||
python3-requests,
|
||||
python3-tqdm,
|
||||
python3-vdf,
|
||||
python3-websocket-client,
|
||||
perl-image-exiftool,
|
||||
xdg-utils,
|
||||
cabextract,
|
||||
curl,
|
||||
gzip,
|
||||
unzip,
|
||||
unrar
|
||||
Description: Modern GUI for managing and launching games from PortProton, Steam, and Epic Games Store
|
||||
This application provides a sleek, intuitive graphical interface for managing and launching games from PortProton, Steam, and Epic Games Store.
|
||||
It consolidates your game libraries into a single, user-friendly hub for seamless navigation and organization.
|
||||
Its lightweight structure and cross-platform support deliver a cohesive gaming experience, eliminating the need for multiple launchers.
|
||||
Unique PortProton integration enhances Linux gaming, enabling effortless play of Windows-based titles with minimal setup.
|
||||
24
build-aux/debian/copyright
Normal file
24
build-aux/debian/copyright
Normal file
@@ -0,0 +1,24 @@
|
||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: PortProtonQt
|
||||
Source: https://git.linux-gaming.ru/Boria138/PortProtonQt
|
||||
|
||||
Files: *
|
||||
Copyright: 2023-2025 Boria138, BlackSnaker, Mikhail Tergoev(Castro-Fidel)
|
||||
License: GPL-3.0+
|
||||
|
||||
License: GPL-3.0+
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
.
|
||||
On Debian systems, the complete text of the GPL-3.0+ license
|
||||
can be found in /usr/share/common-licenses/GPL-3.
|
||||
25
build-aux/debian/rules
Executable file
25
build-aux/debian/rules
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
%:
|
||||
dh $@ --with python3
|
||||
|
||||
override_dh_auto_build:
|
||||
python3 -m pip install --user --no-deps --ignore-installed .
|
||||
|
||||
override_dh_auto_install:
|
||||
# Install Python modules
|
||||
python3 -m pip install --no-deps --ignore-installed --prefix=$(CURDIR)/debian/portprotonqt/usr .
|
||||
|
||||
# Install desktop files and icons
|
||||
install -d $(CURDIR)/debian/portprotonqt/usr/share/applications
|
||||
install -m 644 $(CURDIR)/../build-aux/share/applications/ru.linux_gaming.PortProtonQt.desktop \
|
||||
$(CURDIR)/debian/portprotonqt/usr/share/applications/
|
||||
|
||||
install -d $(CURDIR)/debian/portprotonqt/usr/share/icons/hicolor/scalable/apps
|
||||
install -m 644 $(CURDIR)/../build-aux/share/icons/hicolor/scalable/apps/ru.linux_gaming.PortProtonQt.svg \
|
||||
$(CURDIR)/debian/portprotonqt/usr/share/icons/hicolor/scalable/apps/
|
||||
|
||||
# Install metainfo file
|
||||
install -d $(CURDIR)/debian/portprotonqt/usr/share/metainfo
|
||||
install -m 644 $(CURDIR)/../build-aux/share/metainfo/ru.linux_gaming.PortProtonQt.metainfo.xml \
|
||||
$(CURDIR)/debian/portprotonqt/usr/share/metainfo/
|
||||
1
build-aux/debian/source/format
Normal file
1
build-aux/debian/source/format
Normal file
@@ -0,0 +1 @@
|
||||
3.0 (quilt)
|
||||
7
build-aux/debian/source/options
Normal file
7
build-aux/debian/source/options
Normal file
@@ -0,0 +1,7 @@
|
||||
extend-diff-ignore = "^[^/]*[.]egg-info/"
|
||||
compression = "gzip"
|
||||
tar-ignore = "dev-scripts"
|
||||
tar-ignore = ".*"
|
||||
tar-ignore = "__pycache__"
|
||||
tar-ignore = "build-aux"
|
||||
tar-ignore = "data"
|
||||
@@ -1,5 +1,5 @@
|
||||
%global pypi_name portprotonqt
|
||||
%global pypi_version 0.1.8
|
||||
%global pypi_version 0.1.9
|
||||
%global oname PortProtonQt
|
||||
%global _python_no_extras_requires 1
|
||||
|
||||
|
||||
@@ -1373,7 +1373,7 @@
|
||||
},
|
||||
{
|
||||
"normalized_name": "arena breakout infinite",
|
||||
"status": "Broken"
|
||||
"status": "Denied"
|
||||
},
|
||||
{
|
||||
"normalized_name": "pixel gun 3d pc",
|
||||
@@ -4316,7 +4316,7 @@
|
||||
"status": "Broken"
|
||||
},
|
||||
{
|
||||
"normalized_name": "solo leveling arise",
|
||||
"normalized_name": "solo leveling arise overdrive",
|
||||
"status": "Running"
|
||||
},
|
||||
{
|
||||
@@ -4527,10 +4527,6 @@
|
||||
"normalized_name": "project wraith",
|
||||
"status": "Broken"
|
||||
},
|
||||
{
|
||||
"normalized_name": "solo leveling arise",
|
||||
"status": "Broken"
|
||||
},
|
||||
{
|
||||
"normalized_name": "freedom wars",
|
||||
"status": "Running"
|
||||
@@ -4542,5 +4538,9 @@
|
||||
{
|
||||
"normalized_name": "no more room in hell 2",
|
||||
"status": "Running"
|
||||
},
|
||||
{
|
||||
"normalized_name": "call of duty black ops 7",
|
||||
"status": "Denied"
|
||||
}
|
||||
]
|
||||
Binary file not shown.
13130
data/games_appid.json
13130
data/games_appid.json
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -1,4 +1,128 @@
|
||||
[
|
||||
{
|
||||
"normalized_title": "metal gear solid v the phantom pain",
|
||||
"slug": "metal-gear-solid-v-the-phantom-pain"
|
||||
},
|
||||
{
|
||||
"normalized_title": "battlefield bad company 2",
|
||||
"slug": "battlefield-bad-company-2"
|
||||
},
|
||||
{
|
||||
"normalized_title": "call of duty black ops",
|
||||
"slug": "call-of-duty-black-ops"
|
||||
},
|
||||
{
|
||||
"normalized_title": "call of duty modern warfare 2 (2009)",
|
||||
"slug": "call-of-duty-modern-warfare-2-2009"
|
||||
},
|
||||
{
|
||||
"normalized_title": "call of duty black ops cold war",
|
||||
"slug": "call-of-duty-black-ops-cold-war"
|
||||
},
|
||||
{
|
||||
"normalized_title": "call of duty infinite warfare",
|
||||
"slug": "call-of-duty-infinite-warfare"
|
||||
},
|
||||
{
|
||||
"normalized_title": "lost planet 2",
|
||||
"slug": "lost-planet-2"
|
||||
},
|
||||
{
|
||||
"normalized_title": "lost planet extreme condition colonies",
|
||||
"slug": "lost-planet-extreme-condition-colonies-edition"
|
||||
},
|
||||
{
|
||||
"normalized_title": "starcraft",
|
||||
"slug": "starcraft-remastered"
|
||||
},
|
||||
{
|
||||
"normalized_title": "the entropy centre",
|
||||
"slug": "the-entropy-centre"
|
||||
},
|
||||
{
|
||||
"normalized_title": "metal gear solid v ground zeroes",
|
||||
"slug": "metal-gear-solid-v-ground-zeroes"
|
||||
},
|
||||
{
|
||||
"normalized_title": "escape from tarkov",
|
||||
"slug": "escape-from-tarkov"
|
||||
},
|
||||
{
|
||||
"normalized_title": "command & conquer generals",
|
||||
"slug": "command-conquer-generals"
|
||||
},
|
||||
{
|
||||
"normalized_title": "command & conquer generals zero hour",
|
||||
"slug": "command-conquer-generals-zero-hour"
|
||||
},
|
||||
{
|
||||
"normalized_title": "absolum",
|
||||
"slug": "absolum"
|
||||
},
|
||||
{
|
||||
"normalized_title": "tom clancy's splinter cell chaos theory",
|
||||
"slug": "tom-clancys-splinter-cell-chaos-theory"
|
||||
},
|
||||
{
|
||||
"normalized_title": "winter burrow",
|
||||
"slug": "winter-burrow"
|
||||
},
|
||||
{
|
||||
"normalized_title": "forager",
|
||||
"slug": "forager"
|
||||
},
|
||||
{
|
||||
"normalized_title": "wall world",
|
||||
"slug": "wall-world"
|
||||
},
|
||||
{
|
||||
"normalized_title": "grand theft auto iv the",
|
||||
"slug": "grand-theft-auto-iv-the-complete-edition"
|
||||
},
|
||||
{
|
||||
"normalized_title": "voidtrain",
|
||||
"slug": "voidtrain"
|
||||
},
|
||||
{
|
||||
"normalized_title": "jdm japanese drift master",
|
||||
"slug": "jdm-japanese-drift-master"
|
||||
},
|
||||
{
|
||||
"normalized_title": "lego harry potter collection",
|
||||
"slug": "lego-harry-potter-collection"
|
||||
},
|
||||
{
|
||||
"normalized_title": "life is strange season",
|
||||
"slug": "life-is-strange-complete-season"
|
||||
},
|
||||
{
|
||||
"normalized_title": "земский собор [демо]",
|
||||
"slug": "zemskij-sobor-demo"
|
||||
},
|
||||
{
|
||||
"normalized_title": "syberia",
|
||||
"slug": "syberia-remastered"
|
||||
},
|
||||
{
|
||||
"normalized_title": "europa universalis v",
|
||||
"slug": "europa-universalis-v"
|
||||
},
|
||||
{
|
||||
"normalized_title": "no i'm not a human",
|
||||
"slug": "no-im-not-a-human"
|
||||
},
|
||||
{
|
||||
"normalized_title": "dispatch digital deluxe",
|
||||
"slug": "dispatch-digital-deluxe-edition"
|
||||
},
|
||||
{
|
||||
"normalized_title": "cossacks 3 digital deluxe",
|
||||
"slug": "cossacks-3-digital-deluxe"
|
||||
},
|
||||
{
|
||||
"normalized_title": "battlefield 2",
|
||||
"slug": "battlefield-2"
|
||||
},
|
||||
{
|
||||
"normalized_title": "split/second",
|
||||
"slug": "split-second"
|
||||
@@ -11,10 +135,6 @@
|
||||
"normalized_title": "foundation",
|
||||
"slug": "foundation"
|
||||
},
|
||||
{
|
||||
"normalized_title": "земский собор [демо]",
|
||||
"slug": "zemskij-sobor-demo"
|
||||
},
|
||||
{
|
||||
"normalized_title": "crusader kings 3",
|
||||
"slug": "crusader-kings-3"
|
||||
@@ -1411,10 +1531,6 @@
|
||||
"normalized_title": "world of sea battle",
|
||||
"slug": "world-of-sea-battle"
|
||||
},
|
||||
{
|
||||
"normalized_title": "escape from tarkov",
|
||||
"slug": "escape-from-tarkov"
|
||||
},
|
||||
{
|
||||
"normalized_title": "bayonetta",
|
||||
"slug": "bayonetta"
|
||||
@@ -1539,10 +1655,6 @@
|
||||
"normalized_title": "call of duty 2",
|
||||
"slug": "call-of-duty-2"
|
||||
},
|
||||
{
|
||||
"normalized_title": "call of duty infinite warfare",
|
||||
"slug": "call-of-duty-infinite-warfare"
|
||||
},
|
||||
{
|
||||
"normalized_title": "call of duty world at war",
|
||||
"slug": "call-of-duty-world-at-war"
|
||||
@@ -1735,10 +1847,6 @@
|
||||
"normalized_title": "elden ring",
|
||||
"slug": "elden-ring"
|
||||
},
|
||||
{
|
||||
"normalized_title": "starcraft",
|
||||
"slug": "starcraft-remastered"
|
||||
},
|
||||
{
|
||||
"normalized_title": "cataclismo",
|
||||
"slug": "cataclismo"
|
||||
|
||||
Binary file not shown.
@@ -21,9 +21,9 @@ Current translation status:
|
||||
|
||||
| Locale | Progress | Translated |
|
||||
| :----- | -------: | ---------: |
|
||||
| [de_DE](./de_DE/LC_MESSAGES/messages.po) | 0% | 0 of 338 |
|
||||
| [es_ES](./es_ES/LC_MESSAGES/messages.po) | 0% | 0 of 338 |
|
||||
| [ru_RU](./ru_RU/LC_MESSAGES/messages.po) | 100% | 338 of 338 |
|
||||
| [de_DE](./de_DE/LC_MESSAGES/messages.po) | 0% | 0 of 341 |
|
||||
| [es_ES](./es_ES/LC_MESSAGES/messages.po) | 0% | 0 of 341 |
|
||||
| [ru_RU](./ru_RU/LC_MESSAGES/messages.po) | 100% | 341 of 341 |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
|
||||
| Локаль | Прогресс | Переведено |
|
||||
| :----- | -------: | ---------: |
|
||||
| [de_DE](./de_DE/LC_MESSAGES/messages.po) | 0% | 0 из 338 |
|
||||
| [es_ES](./es_ES/LC_MESSAGES/messages.po) | 0% | 0 из 338 |
|
||||
| [ru_RU](./ru_RU/LC_MESSAGES/messages.po) | 100% | 338 из 338 |
|
||||
| [de_DE](./de_DE/LC_MESSAGES/messages.po) | 0% | 0 из 341 |
|
||||
| [es_ES](./es_ES/LC_MESSAGES/messages.po) | 0% | 0 из 341 |
|
||||
| [ru_RU](./ru_RU/LC_MESSAGES/messages.po) | 100% | 341 из 341 |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
"""PortProtonQt - A project to rewrite PortProton (PortWINE) using PySide."""
|
||||
__version__ = "0.1.9"
|
||||
|
||||
@@ -17,7 +17,7 @@ from portprotonqt.cli import parse_args
|
||||
|
||||
__app_id__ = "ru.linux_gaming.PortProtonQt"
|
||||
__app_name__ = "PortProtonQt"
|
||||
__app_version__ = "0.1.8"
|
||||
__app_version__ = "0.1.9"
|
||||
|
||||
def get_version():
|
||||
try:
|
||||
|
||||
@@ -183,23 +183,39 @@ def get_portproton_start_command():
|
||||
if not portproton_path:
|
||||
return None
|
||||
|
||||
# Check if flatpak command exists before trying to run it
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["flatpak", "list"],
|
||||
subprocess.run(
|
||||
["flatpak", "--version"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
timeout=10
|
||||
timeout=5
|
||||
)
|
||||
if "ru.linux_gaming.PortProton" in result.stdout:
|
||||
logger.info("Detected Flatpak installation")
|
||||
return ["flatpak", "run", "ru.linux_gaming.PortProton"]
|
||||
except subprocess.TimeoutExpired:
|
||||
logger.warning("Flatpak list command timed out")
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.warning(f"Error checking flatpak list: {e}")
|
||||
pass
|
||||
flatpak_available = True
|
||||
except FileNotFoundError:
|
||||
flatpak_available = False
|
||||
except Exception:
|
||||
flatpak_available = False
|
||||
|
||||
if flatpak_available:
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["flatpak", "list"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
timeout=10
|
||||
)
|
||||
if "ru.linux_gaming.PortProton" in result.stdout:
|
||||
logger.info("Detected Flatpak installation")
|
||||
return ["flatpak", "run", "ru.linux_gaming.PortProton"]
|
||||
except subprocess.TimeoutExpired:
|
||||
logger.warning("Flatpak list command timed out")
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.warning(f"Error checking flatpak list: {e}")
|
||||
pass
|
||||
|
||||
start_sh_path = os.path.join(portproton_path, "data", "scripts", "start.sh")
|
||||
if os.path.exists(start_sh_path):
|
||||
|
||||
@@ -1035,7 +1035,15 @@ Icon={icon_path}
|
||||
)
|
||||
return
|
||||
|
||||
if os.path.isfile(new_cover_path):
|
||||
# Check if new_cover_path is a URL by checking for common image extensions
|
||||
image_extensions = ('.jpg', '.jpeg', '.png', '.bmp', '.gif', '.webp')
|
||||
has_image_extension = any(new_cover_path.lower().endswith(ext) for ext in image_extensions)
|
||||
|
||||
# Consider it a URL if it has image extension and is not a local file
|
||||
is_url = has_image_extension and not os.path.isfile(new_cover_path)
|
||||
|
||||
# Use the downloaded file path if we have a URL and the file was downloaded, otherwise use the local file
|
||||
if os.path.isfile(new_cover_path) or (is_url and dialog.last_cover_path and os.path.isfile(dialog.last_cover_path)):
|
||||
exe_name = os.path.splitext(os.path.basename(new_exe_path))[0]
|
||||
xdg_data_home = os.getenv(
|
||||
"XDG_DATA_HOME",
|
||||
@@ -1043,16 +1051,25 @@ Icon={icon_path}
|
||||
)
|
||||
custom_folder = os.path.join(xdg_data_home, "PortProtonQt", "custom_data", exe_name)
|
||||
os.makedirs(custom_folder, exist_ok=True)
|
||||
ext = os.path.splitext(new_cover_path)[1].lower()
|
||||
|
||||
# Use the actual cover file path (either from URL download or local file)
|
||||
cover_to_copy = dialog.last_cover_path if is_url and dialog.last_cover_path and os.path.isfile(dialog.last_cover_path) else new_cover_path
|
||||
ext = os.path.splitext(cover_to_copy)[1].lower()
|
||||
if ext in [".png", ".jpg", ".jpeg", ".bmp"]:
|
||||
try:
|
||||
shutil.copyfile(new_cover_path, os.path.join(custom_folder, f"cover{ext}"))
|
||||
shutil.copyfile(cover_to_copy, os.path.join(custom_folder, f"cover{ext}"))
|
||||
except OSError as e:
|
||||
self.signals.show_warning_dialog.emit(
|
||||
_("Error"),
|
||||
_("Failed to copy cover image: {error}").format(error=str(e))
|
||||
)
|
||||
return
|
||||
else:
|
||||
self.signals.show_warning_dialog.emit(
|
||||
_("Error"),
|
||||
_("Unsupported image format: {extension}").format(extension=ext)
|
||||
)
|
||||
return
|
||||
|
||||
def add_to_steam(self, game_name, exec_line, cover_path):
|
||||
"""
|
||||
|
||||
@@ -10,7 +10,7 @@ from PySide6.QtWidgets import (
|
||||
from PySide6.QtCore import Qt, QObject, Signal, QMimeDatabase, QTimer, QThreadPool, QRunnable, Slot, QProcess, QProcessEnvironment
|
||||
from icoextract import IconExtractor, IconExtractorError
|
||||
from PIL import Image
|
||||
from portprotonqt.config_utils import get_portproton_location, read_favorite_folders, read_theme_from_config
|
||||
from portprotonqt.config_utils import get_portproton_location, get_portproton_start_command, read_favorite_folders, read_theme_from_config
|
||||
from portprotonqt.localization import _
|
||||
from portprotonqt.logger import get_logger
|
||||
from portprotonqt.theme_manager import ThemeManager
|
||||
@@ -906,6 +906,7 @@ class AddGameDialog(QDialog):
|
||||
|
||||
self.coverEdit = CustomLineEdit(self, theme=self.theme)
|
||||
self.coverEdit.setStyleSheet(self.theme.ADDGAME_INPUT_STYLE)
|
||||
self.coverEdit.setPlaceholderText(_("Enter local path or URL for cover image"))
|
||||
if cover_path:
|
||||
self.coverEdit.setText(cover_path)
|
||||
|
||||
@@ -949,7 +950,12 @@ class AddGameDialog(QDialog):
|
||||
# Подключение сигналов
|
||||
self.select_button.clicked.connect(self.accept)
|
||||
self.cancel_button.clicked.connect(self.reject)
|
||||
self.coverEdit.textChanged.connect(self.updatePreview)
|
||||
# Set up a timer for debounced cover preview updates
|
||||
self.cover_preview_timer = QTimer(self)
|
||||
self.cover_preview_timer.setSingleShot(True)
|
||||
self.cover_preview_timer.timeout.connect(self.updatePreview)
|
||||
|
||||
self.coverEdit.textChanged.connect(self.onCoverTextChanged)
|
||||
self.exeEdit.textChanged.connect(self.updatePreview)
|
||||
|
||||
# Установка одинаковой ширины для кнопок и полей ввода
|
||||
@@ -1094,22 +1100,32 @@ class AddGameDialog(QDialog):
|
||||
self.coverPreview.setText(_("Failed to download cover"))
|
||||
logger.warning(f"Failed to download cover to {file_path}")
|
||||
|
||||
def onCoverTextChanged(self):
|
||||
"""Handle cover text changes with debounce."""
|
||||
# Restart the timer to delay the preview update
|
||||
self.cover_preview_timer.start(500) # 500ms delay
|
||||
|
||||
def updatePreview(self):
|
||||
"""Update the cover preview image."""
|
||||
cover_path = self.coverEdit.text().strip()
|
||||
exe_path = self.exeEdit.text().strip()
|
||||
|
||||
# Check if cover_path is a URL
|
||||
url_pattern = r'^https?://[^\s/$.?#].[^\s]*$'
|
||||
if re.match(url_pattern, cover_path):
|
||||
# Check if cover_path is a URL by checking for common image extensions
|
||||
image_extensions = ('.jpg', '.jpeg', '.png', '.bmp', '.gif', '.webp')
|
||||
has_image_extension = any(cover_path.lower().endswith(ext) for ext in image_extensions)
|
||||
|
||||
# Consider it a URL if it has image extension and is not a local file
|
||||
if has_image_extension and not os.path.isfile(cover_path):
|
||||
# Create a temporary file for the downloaded image
|
||||
fd, local_path = tempfile.mkstemp(suffix=".png")
|
||||
os.close(fd)
|
||||
os.unlink(local_path)
|
||||
|
||||
# Start asynchronous download
|
||||
# Add protocol if not present
|
||||
download_url = cover_path if cover_path.startswith(('http://', 'https://')) else f'https://{cover_path}'
|
||||
self.downloader.download_async(
|
||||
url=cover_path,
|
||||
url=download_url,
|
||||
local_path=local_path,
|
||||
timeout=10,
|
||||
callback=self.handleDownloadedCover
|
||||
@@ -1701,8 +1717,10 @@ class ExeSettingsDialog(QDialog):
|
||||
if self.portproton_path is None:
|
||||
logger.error("PortProton location not found")
|
||||
return
|
||||
base_path = os.path.join(self.portproton_path, "data")
|
||||
self.start_sh = [os.path.join(base_path, "scripts", "start.sh")]
|
||||
self.start_sh = get_portproton_start_command()
|
||||
if self.start_sh is None:
|
||||
logger.error("PortProton start command not found")
|
||||
return
|
||||
|
||||
self.dist_options = []
|
||||
self.prefix_options = []
|
||||
@@ -1776,9 +1794,9 @@ class ExeSettingsDialog(QDialog):
|
||||
self.load_current_settings()
|
||||
|
||||
def _get_process_args(self, subcommand_args):
|
||||
"""Get the full arguments for QProcess.start, handling flatpak separator."""
|
||||
if self.start_sh[0] == "flatpak":
|
||||
return self.start_sh[1:] + ["--"] + subcommand_args
|
||||
"""Get the full arguments for QProcess.start, handling flatpak format."""
|
||||
if self.start_sh and self.start_sh[0] == "flatpak":
|
||||
return self.start_sh + subcommand_args
|
||||
else:
|
||||
return self.start_sh + subcommand_args
|
||||
|
||||
@@ -1814,7 +1832,7 @@ class ExeSettingsDialog(QDialog):
|
||||
# Connect tab change to update description hint
|
||||
self.tab_widget.currentChanged.connect(self.on_table_selection_changed)
|
||||
|
||||
# Main settings table
|
||||
# Main settings table with preloader
|
||||
self.settings_table = QTableWidget()
|
||||
self.settings_table.setAlternatingRowColors(True)
|
||||
self.settings_table.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
||||
@@ -1829,11 +1847,30 @@ class ExeSettingsDialog(QDialog):
|
||||
self.settings_table.verticalHeader().setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents)
|
||||
self.settings_table.setTextElideMode(Qt.TextElideMode.ElideNone)
|
||||
self.settings_table.setStyleSheet(self.theme.WINETRICKS_TABBLE_STYLE)
|
||||
self.main_tab_layout.addWidget(self.settings_table)
|
||||
|
||||
# Create preloader for main settings table
|
||||
self.settings_preloader = Preloader()
|
||||
settings_preloader_container = QWidget()
|
||||
settings_preloader_layout = QVBoxLayout(settings_preloader_container)
|
||||
settings_preloader_layout.addStretch()
|
||||
settings_preloader_hlayout = QHBoxLayout()
|
||||
settings_preloader_hlayout.addStretch()
|
||||
settings_preloader_hlayout.addWidget(self.settings_preloader)
|
||||
settings_preloader_hlayout.addStretch()
|
||||
settings_preloader_layout.addLayout(settings_preloader_hlayout)
|
||||
settings_preloader_layout.addStretch()
|
||||
settings_preloader_layout.setContentsMargins(0, 0, 0, 0)
|
||||
settings_preloader_layout.setSpacing(0)
|
||||
|
||||
# Create stacked widget for main settings
|
||||
self.settings_container = QStackedWidget()
|
||||
self.settings_container.addWidget(settings_preloader_container) # Index 0: preloader
|
||||
self.settings_container.addWidget(self.settings_table) # Index 1: table
|
||||
self.main_tab_layout.addWidget(self.settings_container)
|
||||
# Connect selection changed signal for the main table
|
||||
self.settings_table.currentCellChanged.connect(self.on_table_selection_changed)
|
||||
|
||||
# Advanced settings table
|
||||
# Advanced settings table with preloader
|
||||
self.advanced_table = QTableWidget()
|
||||
self.advanced_table.setAlternatingRowColors(True)
|
||||
self.advanced_table.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
||||
@@ -1850,7 +1887,26 @@ class ExeSettingsDialog(QDialog):
|
||||
self.advanced_table.verticalHeader().setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents)
|
||||
self.advanced_table.setTextElideMode(Qt.TextElideMode.ElideNone)
|
||||
self.advanced_table.setStyleSheet(self.theme.WINETRICKS_TABBLE_STYLE)
|
||||
self.advanced_tab_layout.addWidget(self.advanced_table)
|
||||
|
||||
# Create preloader for advanced settings table
|
||||
self.advanced_preloader = Preloader()
|
||||
advanced_preloader_container = QWidget()
|
||||
advanced_preloader_layout = QVBoxLayout(advanced_preloader_container)
|
||||
advanced_preloader_layout.addStretch()
|
||||
advanced_preloader_hlayout = QHBoxLayout()
|
||||
advanced_preloader_hlayout.addStretch()
|
||||
advanced_preloader_hlayout.addWidget(self.advanced_preloader)
|
||||
advanced_preloader_hlayout.addStretch()
|
||||
advanced_preloader_layout.addLayout(advanced_preloader_hlayout)
|
||||
advanced_preloader_layout.addStretch()
|
||||
advanced_preloader_layout.setContentsMargins(0, 0, 0, 0)
|
||||
advanced_preloader_layout.setSpacing(0)
|
||||
|
||||
# Create stacked widget for advanced settings
|
||||
self.advanced_container = QStackedWidget()
|
||||
self.advanced_container.addWidget(advanced_preloader_container) # Index 0: preloader
|
||||
self.advanced_container.addWidget(self.advanced_table) # Index 1: table
|
||||
self.advanced_tab_layout.addWidget(self.advanced_container)
|
||||
# Connect selection changed signal for the advanced table
|
||||
self.advanced_table.currentCellChanged.connect(self.on_table_selection_changed)
|
||||
|
||||
@@ -1888,9 +1944,14 @@ class ExeSettingsDialog(QDialog):
|
||||
|
||||
def load_current_settings(self):
|
||||
"""Load available toggles first, then current settings."""
|
||||
# Show preloaders initially
|
||||
self.settings_container.setCurrentIndex(0) # Show preloader for main settings
|
||||
self.advanced_container.setCurrentIndex(0) # Show preloader for advanced settings
|
||||
|
||||
process = QProcess(self)
|
||||
process.finished.connect(self.on_list_db_finished)
|
||||
process.start(self.start_sh[0], ["cli", "--list-db"])
|
||||
args = self._get_process_args(["cli", "--list-db"])
|
||||
process.start(args[0], args[1:])
|
||||
|
||||
def on_list_db_finished(self, exit_code, exit_status):
|
||||
"""Handle --list-db output and extract available keys and system info."""
|
||||
@@ -1937,7 +1998,8 @@ class ExeSettingsDialog(QDialog):
|
||||
# Load current settings
|
||||
process = QProcess(self)
|
||||
process.finished.connect(self.on_show_ppdb_finished)
|
||||
process.start(self.start_sh[0], ["cli", "--show-ppdb", f"{self.exe_path}"])
|
||||
args = self._get_process_args(["cli", "--show-ppdb", f"{self.exe_path}"])
|
||||
process.start(args[0], args[1:])
|
||||
|
||||
def on_show_ppdb_finished(self, exit_code, exit_status):
|
||||
"""Handle --show-ppdb output."""
|
||||
@@ -1977,6 +2039,10 @@ class ExeSettingsDialog(QDialog):
|
||||
self.populate_table()
|
||||
self.populate_advanced()
|
||||
|
||||
# Show the loaded content and hide preloaders
|
||||
self.settings_container.setCurrentIndex(1) # Show main settings table
|
||||
self.advanced_container.setCurrentIndex(1) # Show advanced settings table
|
||||
|
||||
def populate_table(self):
|
||||
"""Populate the table with settings that are available in both lists."""
|
||||
self.settings_table.setRowCount(0)
|
||||
@@ -2285,8 +2351,9 @@ class ExeSettingsDialog(QDialog):
|
||||
|
||||
process = QProcess(self)
|
||||
process.finished.connect(self.on_edit_db_finished)
|
||||
args = ["cli", "--edit-db", self.exe_path] + changes
|
||||
process.start(self.start_sh[0], args)
|
||||
process_args = ["cli", "--edit-db", self.exe_path] + changes
|
||||
args = self._get_process_args(process_args)
|
||||
process.start(args[0], args[1:])
|
||||
self.apply_button.setEnabled(False)
|
||||
|
||||
def on_edit_db_finished(self, exit_code, exit_status):
|
||||
|
||||
@@ -505,17 +505,25 @@ class GameLibraryManager:
|
||||
"""Clears all widgets from the layout."""
|
||||
if layout is None:
|
||||
return
|
||||
# Remove all widgets from the layout and clean up caches
|
||||
while layout.count():
|
||||
child = layout.takeAt(0)
|
||||
if child.widget():
|
||||
widget = child.widget()
|
||||
# Clean up cache if widget exists in it
|
||||
for key, card in list(self.game_card_cache.items()):
|
||||
if card == widget:
|
||||
del self.game_card_cache[key]
|
||||
if key in self.pending_images:
|
||||
del self.pending_images[key]
|
||||
break
|
||||
# Always schedule widget for deletion regardless of cache state
|
||||
widget.deleteLater()
|
||||
|
||||
# Also clear the cache completely if needed (in case layout wasn't in sync)
|
||||
self.game_card_cache.clear()
|
||||
self.pending_images.clear()
|
||||
|
||||
def set_games(self, games: list[tuple]):
|
||||
"""Sets the games list and updates the filtered games."""
|
||||
self.games = games
|
||||
|
||||
@@ -1939,8 +1939,10 @@ class InputManager(QObject):
|
||||
active_win.show_next()
|
||||
return True # Consume event to prevent tab switching
|
||||
|
||||
# Handle tab switching with Left/Right arrow keys when not in GameCard focus or QLineEdit
|
||||
if key in (Qt.Key.Key_Left, Qt.Key.Key_Right) and not isinstance(focused, GameCard | QLineEdit) and not self.file_explorer:
|
||||
# Handle tab switching with Left/Right arrow keys when not in GameCard focus or QLineEdit or QTableWidget or AutoSizeButton
|
||||
if (key in (Qt.Key.Key_Left, Qt.Key.Key_Right) and
|
||||
not isinstance(focused, GameCard | QLineEdit | QTableWidget | AutoSizeButton) and
|
||||
not self.file_explorer):
|
||||
idx = self._parent.stackedWidget.currentIndex()
|
||||
total = len(self._parent.tabButtons)
|
||||
if key == Qt.Key.Key_Left:
|
||||
@@ -1976,12 +1978,6 @@ class InputManager(QObject):
|
||||
self.dpad_moved.emit(dpad_code, dpad_value, now)
|
||||
return True
|
||||
|
||||
# Launch/stop game on detail page
|
||||
if self._parent.currentDetailPage and key in (Qt.Key.Key_Return, Qt.Key.Key_Enter):
|
||||
if self._parent.current_exec_line:
|
||||
self._parent.toggleGame(self._parent.current_exec_line, None)
|
||||
return True
|
||||
|
||||
# Context menu for GameCard
|
||||
if isinstance(focused, GameCard):
|
||||
if key == Qt.Key.Key_F10 and modifiers & Qt.KeyboardModifier.ShiftModifier:
|
||||
@@ -1991,6 +1987,18 @@ class InputManager(QObject):
|
||||
|
||||
# General actions: Activate, Back, Add
|
||||
if key in (Qt.Key.Key_Return, Qt.Key.Key_Enter):
|
||||
# Special handling for table widgets with checkboxes
|
||||
if isinstance(focused, QTableWidget):
|
||||
current_row = focused.currentRow()
|
||||
current_col = focused.currentColumn()
|
||||
if current_row >= 0 and current_col >= 0:
|
||||
# Check if the cell contains a checkbox
|
||||
item = focused.item(current_row, current_col)
|
||||
if item and (item.flags() & Qt.ItemFlag.ItemIsUserCheckable):
|
||||
# Toggle the checkbox state
|
||||
new_state = Qt.CheckState.Checked if item.checkState() == Qt.CheckState.Unchecked else Qt.CheckState.Unchecked
|
||||
item.setCheckState(new_state)
|
||||
return True
|
||||
self._parent.activateFocusedWidget()
|
||||
return True
|
||||
elif key in (Qt.Key.Key_Escape, Qt.Key.Key_Backspace):
|
||||
@@ -2345,9 +2353,12 @@ class InputManager(QObject):
|
||||
|
||||
self.button_event.emit(event.code, event.value)
|
||||
# Special handling for menu on press only
|
||||
# Only handle menu button if our main window is currently active
|
||||
if (event.value == 1 and event.code in BUTTONS['menu'] and
|
||||
not self._is_gamescope_session and not self.in_guide_combination_attempt):
|
||||
self.toggle_fullscreen.emit(not self._is_fullscreen)
|
||||
# Check if our main window is the currently active window
|
||||
if self._parent.isActiveWindow():
|
||||
self.toggle_fullscreen.emit(not self._is_fullscreen)
|
||||
elif event.type == ecodes.EV_ABS:
|
||||
if event.code in {ecodes.ABS_Z, ecodes.ABS_RZ}:
|
||||
# Trigger handling for UI
|
||||
|
||||
Binary file not shown.
@@ -9,7 +9,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2025-11-24 23:48+0500\n"
|
||||
"POT-Creation-Date: 2025-11-30 13:20+0500\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language: de_DE\n"
|
||||
@@ -217,6 +217,10 @@ msgstr ""
|
||||
msgid "Failed to copy cover image: {error}"
|
||||
msgstr ""
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Unsupported image format: {extension}"
|
||||
msgstr ""
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Failed to add '{game_name}' to Steam: {error}"
|
||||
msgstr ""
|
||||
@@ -320,6 +324,9 @@ msgstr ""
|
||||
msgid "Custom Cover:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enter local path or URL for cover image"
|
||||
msgstr ""
|
||||
|
||||
msgid "Cover Preview:"
|
||||
msgstr ""
|
||||
|
||||
@@ -452,6 +459,9 @@ msgstr ""
|
||||
msgid "Unknown Game"
|
||||
msgstr ""
|
||||
|
||||
msgid "Starting PortProton..."
|
||||
msgstr ""
|
||||
|
||||
msgid "Library"
|
||||
msgstr ""
|
||||
|
||||
@@ -473,6 +483,9 @@ msgstr ""
|
||||
msgid "Fullscreen"
|
||||
msgstr ""
|
||||
|
||||
msgid "Refresh Grid"
|
||||
msgstr ""
|
||||
|
||||
msgid "Installation already in progress."
|
||||
msgstr ""
|
||||
|
||||
@@ -492,9 +505,6 @@ msgstr ""
|
||||
msgid "Installation error."
|
||||
msgstr ""
|
||||
|
||||
msgid "Refresh Grid"
|
||||
msgstr ""
|
||||
|
||||
msgid "Game library refreshed"
|
||||
msgstr ""
|
||||
|
||||
|
||||
Binary file not shown.
@@ -9,7 +9,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2025-11-24 23:48+0500\n"
|
||||
"POT-Creation-Date: 2025-11-30 13:20+0500\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language: es_ES\n"
|
||||
@@ -217,6 +217,10 @@ msgstr ""
|
||||
msgid "Failed to copy cover image: {error}"
|
||||
msgstr ""
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Unsupported image format: {extension}"
|
||||
msgstr ""
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Failed to add '{game_name}' to Steam: {error}"
|
||||
msgstr ""
|
||||
@@ -320,6 +324,9 @@ msgstr ""
|
||||
msgid "Custom Cover:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enter local path or URL for cover image"
|
||||
msgstr ""
|
||||
|
||||
msgid "Cover Preview:"
|
||||
msgstr ""
|
||||
|
||||
@@ -452,6 +459,9 @@ msgstr ""
|
||||
msgid "Unknown Game"
|
||||
msgstr ""
|
||||
|
||||
msgid "Starting PortProton..."
|
||||
msgstr ""
|
||||
|
||||
msgid "Library"
|
||||
msgstr ""
|
||||
|
||||
@@ -473,6 +483,9 @@ msgstr ""
|
||||
msgid "Fullscreen"
|
||||
msgstr ""
|
||||
|
||||
msgid "Refresh Grid"
|
||||
msgstr ""
|
||||
|
||||
msgid "Installation already in progress."
|
||||
msgstr ""
|
||||
|
||||
@@ -492,9 +505,6 @@ msgstr ""
|
||||
msgid "Installation error."
|
||||
msgstr ""
|
||||
|
||||
msgid "Refresh Grid"
|
||||
msgstr ""
|
||||
|
||||
msgid "Game library refreshed"
|
||||
msgstr ""
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PortProtonQt 0.1.1\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2025-11-24 23:48+0500\n"
|
||||
"POT-Creation-Date: 2025-11-30 13:20+0500\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -215,6 +215,10 @@ msgstr ""
|
||||
msgid "Failed to copy cover image: {error}"
|
||||
msgstr ""
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Unsupported image format: {extension}"
|
||||
msgstr ""
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Failed to add '{game_name}' to Steam: {error}"
|
||||
msgstr ""
|
||||
@@ -318,6 +322,9 @@ msgstr ""
|
||||
msgid "Custom Cover:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enter local path or URL for cover image"
|
||||
msgstr ""
|
||||
|
||||
msgid "Cover Preview:"
|
||||
msgstr ""
|
||||
|
||||
@@ -450,6 +457,9 @@ msgstr ""
|
||||
msgid "Unknown Game"
|
||||
msgstr ""
|
||||
|
||||
msgid "Starting PortProton..."
|
||||
msgstr ""
|
||||
|
||||
msgid "Library"
|
||||
msgstr ""
|
||||
|
||||
@@ -471,6 +481,9 @@ msgstr ""
|
||||
msgid "Fullscreen"
|
||||
msgstr ""
|
||||
|
||||
msgid "Refresh Grid"
|
||||
msgstr ""
|
||||
|
||||
msgid "Installation already in progress."
|
||||
msgstr ""
|
||||
|
||||
@@ -490,9 +503,6 @@ msgstr ""
|
||||
msgid "Installation error."
|
||||
msgstr ""
|
||||
|
||||
msgid "Refresh Grid"
|
||||
msgstr ""
|
||||
|
||||
msgid "Game library refreshed"
|
||||
msgstr ""
|
||||
|
||||
|
||||
Binary file not shown.
@@ -9,8 +9,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2025-11-24 23:48+0500\n"
|
||||
"PO-Revision-Date: 2025-11-24 23:47+0500\n"
|
||||
"POT-Creation-Date: 2025-11-30 13:20+0500\n"
|
||||
"PO-Revision-Date: 2025-11-30 13:18+0500\n"
|
||||
"Last-Translator: \n"
|
||||
"Language: ru_RU\n"
|
||||
"Language-Team: ru_RU <LL@li.org>\n"
|
||||
@@ -222,6 +222,10 @@ msgstr "Не удалось сохранить файл .desktop: {error}"
|
||||
msgid "Failed to copy cover image: {error}"
|
||||
msgstr "Не удалось скопировать обложку: {error}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Unsupported image format: {extension}"
|
||||
msgstr "Неподдерживаемый формат изображения: {extension}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Failed to add '{game_name}' to Steam: {error}"
|
||||
msgstr "Не удалось добавить '{game_name}' в Steam: {error}"
|
||||
@@ -327,6 +331,9 @@ msgstr "Обзор..."
|
||||
msgid "Custom Cover:"
|
||||
msgstr "Обложка:"
|
||||
|
||||
msgid "Enter local path or URL for cover image"
|
||||
msgstr "Введите локальный путь или URL обложки"
|
||||
|
||||
msgid "Cover Preview:"
|
||||
msgstr "Предпросмотр обложки:"
|
||||
|
||||
@@ -459,6 +466,9 @@ msgstr "В ожидании"
|
||||
msgid "Unknown Game"
|
||||
msgstr "Неизвестная игра"
|
||||
|
||||
msgid "Starting PortProton..."
|
||||
msgstr "Инициализация PortProton"
|
||||
|
||||
msgid "Library"
|
||||
msgstr "Библиотека"
|
||||
|
||||
@@ -480,6 +490,9 @@ msgstr "Назад"
|
||||
msgid "Fullscreen"
|
||||
msgstr "Полный экран"
|
||||
|
||||
msgid "Refresh Grid"
|
||||
msgstr "Обновить"
|
||||
|
||||
msgid "Installation already in progress."
|
||||
msgstr "Установка уже выполняется."
|
||||
|
||||
@@ -499,9 +512,6 @@ msgstr "Установка не удалась."
|
||||
msgid "Installation error."
|
||||
msgstr "Ошибка установки."
|
||||
|
||||
msgid "Refresh Grid"
|
||||
msgstr "Обновить"
|
||||
|
||||
msgid "Game library refreshed"
|
||||
msgstr "Игровая библиотека обновлена"
|
||||
|
||||
|
||||
@@ -780,11 +780,10 @@ class MainWindow(QMainWindow):
|
||||
self.pending_games = []
|
||||
self.games = []
|
||||
|
||||
# Show initial progress bar and status message immediately
|
||||
# Show initial progress bar immediately
|
||||
self.progress_bar.setRange(0, 100) # Set to determinate range
|
||||
self.progress_bar.setValue(0)
|
||||
self.progress_bar.setVisible(True)
|
||||
self.update_status_message.emit(_("Loading games..."), 0)
|
||||
|
||||
# Process events to keep UI responsive
|
||||
QApplication.processEvents()
|
||||
@@ -899,8 +898,8 @@ class MainWindow(QMainWindow):
|
||||
self.update_status_message.emit
|
||||
)
|
||||
|
||||
# Run loading with minimal delay to allow UI to be responsive
|
||||
QTimer.singleShot(100, start_loading) # Reduced to 100ms
|
||||
# Run loading immediately to show status without delay
|
||||
start_loading()
|
||||
|
||||
def _load_steam_games_async(self, callback: Callable[[list[tuple]], None]):
|
||||
steam_games = []
|
||||
@@ -1200,8 +1199,32 @@ class MainWindow(QMainWindow):
|
||||
self.progress_bar.setRange(0, 0) # Indeterminate
|
||||
self.update_status_message.emit(_("Refreshing game library..."), 0)
|
||||
|
||||
# Clear the game card cache and layout to force reload of custom data
|
||||
if hasattr(self, 'game_library_manager') and self.game_library_manager:
|
||||
# Clear the cache to ensure custom data is reloaded
|
||||
self.game_library_manager.game_card_cache.clear()
|
||||
self.game_library_manager.pending_images.clear()
|
||||
# Clear search indices to rebuild with fresh data
|
||||
if hasattr(self.game_library_manager, '_build_search_indices'):
|
||||
# Mark for full rebuild of search indices
|
||||
self.game_library_manager.dirty = True # Force full update
|
||||
|
||||
# Also clear the layout to ensure old widgets are removed
|
||||
if (hasattr(self.game_library_manager, 'gamesListLayout') and
|
||||
self.game_library_manager.gamesListLayout and
|
||||
hasattr(self.game_library_manager, 'gamesListWidget') and
|
||||
self.game_library_manager.gamesListWidget):
|
||||
# Remove all widgets from the layout
|
||||
self.game_library_manager.clear_layout(self.game_library_manager.gamesListLayout)
|
||||
|
||||
# Force layout update to ensure UI changes are visible
|
||||
self.game_library_manager.gamesListWidget.updateGeometry()
|
||||
if hasattr(self.game_library_manager, 'gamesListLayout'):
|
||||
self.game_library_manager.gamesListLayout.update()
|
||||
|
||||
# Reload games using the existing loadGames functionality
|
||||
QTimer.singleShot(0, self.loadGames)
|
||||
# Use a small delay to allow UI to update before starting the refresh
|
||||
QTimer.singleShot(50, lambda: self.loadGames())
|
||||
|
||||
def on_search_text_changed(self, text: str):
|
||||
"""Search text change handler with debounce."""
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
[build-system]
|
||||
requires = ["setuptools >= 77.0.3"]
|
||||
requires = ["setuptools >= 77.0.3", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "portprotonqt"
|
||||
version = "0.1.8"
|
||||
version = "0.1.9"
|
||||
description = "A project to rewrite PortProton (PortWINE) using PySide"
|
||||
readme = "README.md"
|
||||
license = { text = "GPL-3.0" }
|
||||
@@ -46,12 +46,13 @@ dependencies = [
|
||||
[project.scripts]
|
||||
portprotonqt = "portprotonqt.app:main"
|
||||
|
||||
[tool.setuptools]
|
||||
packages = ["portprotonqt"]
|
||||
include-package-data = true
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
"portprotonqt" = ["themes/**/*", "locales/**/*"]
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
exclude = ["build-aux", "dev-scripts", "documentation", "data"]
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = [
|
||||
"E", # pycodestyle errors
|
||||
|
||||
Reference in New Issue
Block a user