feat: replace QFileDialog with custom FileExplorer for Legendary import
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
This commit is contained in:
@ -6,14 +6,14 @@ import subprocess
|
||||
import threading
|
||||
import logging
|
||||
import orjson
|
||||
from PySide6.QtWidgets import QMessageBox, QDialog, QMenu, QFileDialog
|
||||
from PySide6.QtWidgets import QMessageBox, QDialog, QMenu
|
||||
from PySide6.QtCore import QUrl, QPoint, QObject, Signal, Qt
|
||||
from PySide6.QtGui import QDesktopServices
|
||||
from portprotonqt.localization import _
|
||||
from portprotonqt.config_utils import parse_desktop_entry, read_favorites, save_favorites
|
||||
from portprotonqt.steam_api import is_game_in_steam, add_to_steam, remove_from_steam
|
||||
from portprotonqt.egs_api import add_egs_to_steam, get_egs_executable, remove_egs_from_steam
|
||||
from portprotonqt.dialogs import AddGameDialog, generate_thumbnail
|
||||
from portprotonqt.dialogs import AddGameDialog, FileExplorer, generate_thumbnail
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -282,35 +282,56 @@ class ContextMenuManager:
|
||||
"""
|
||||
if not self._check_portproton():
|
||||
return
|
||||
folder_path = QFileDialog.getExistingDirectory(
|
||||
self.parent, _("Select Game Installation Folder"), os.path.expanduser("~")
|
||||
)
|
||||
if not folder_path:
|
||||
self._show_status_message(_("No folder selected"))
|
||||
return
|
||||
if not os.path.exists(self.legendary_path):
|
||||
self.signals.show_warning_dialog.emit(
|
||||
_("Error"),
|
||||
_("Legendary executable not found at {path}").format(path=self.legendary_path)
|
||||
)
|
||||
return
|
||||
def run_import():
|
||||
cmd = [self.legendary_path, "import", app_name, folder_path]
|
||||
try:
|
||||
subprocess.run(cmd, capture_output=True, text=True, check=True)
|
||||
self.signals.show_info_dialog.emit(
|
||||
_("Success"),
|
||||
_("Imported '{game_name}' to Legendary").format(game_name=game_name)
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
self.signals.show_warning_dialog.emit(
|
||||
_("Error"),
|
||||
_("Failed to import '{game_name}' to Legendary: {error}").format(
|
||||
game_name=game_name, error=e.stderr
|
||||
|
||||
# Используем FileExplorer с directory_only=True
|
||||
file_explorer = FileExplorer(
|
||||
parent=self.parent,
|
||||
theme=self.theme,
|
||||
initial_path=os.path.expanduser("~"),
|
||||
directory_only=True
|
||||
)
|
||||
|
||||
def on_folder_selected(folder_path):
|
||||
if not folder_path:
|
||||
self._show_status_message(_("No folder selected"))
|
||||
return
|
||||
def run_import():
|
||||
cmd = [self.legendary_path, "import", app_name, folder_path]
|
||||
try:
|
||||
subprocess.run(cmd, capture_output=True, text=True, check=True)
|
||||
self.signals.show_info_dialog.emit(
|
||||
_("Success"),
|
||||
_("Imported '{game_name}' to Legendary").format(game_name=game_name)
|
||||
)
|
||||
)
|
||||
self._show_status_message(_("Importing '{game_name}' to Legendary...").format(game_name=game_name))
|
||||
threading.Thread(target=run_import, daemon=True).start()
|
||||
except subprocess.CalledProcessError as e:
|
||||
self.signals.show_warning_dialog.emit(
|
||||
_("Error"),
|
||||
_("Failed to import '{game_name}' to Legendary: {error}").format(
|
||||
game_name=game_name, error=e.stderr
|
||||
)
|
||||
)
|
||||
self._show_status_message(_("Importing '{game_name}' to Legendary...").format(game_name=game_name))
|
||||
threading.Thread(target=run_import, daemon=True).start()
|
||||
|
||||
# Подключаем сигнал выбора файла/папки
|
||||
file_explorer.file_signal.file_selected.connect(on_folder_selected)
|
||||
|
||||
# Центрируем FileExplorer относительно родительского виджета
|
||||
parent_widget = self.parent
|
||||
if parent_widget:
|
||||
parent_geometry = parent_widget.geometry()
|
||||
center_point = parent_geometry.center()
|
||||
file_explorer_geometry = file_explorer.geometry()
|
||||
file_explorer_geometry.moveCenter(center_point)
|
||||
file_explorer.setGeometry(file_explorer_geometry)
|
||||
|
||||
file_explorer.show()
|
||||
|
||||
def toggle_favorite(self, game_card, add: bool):
|
||||
"""
|
||||
|
@ -88,12 +88,13 @@ class FileSelectedSignal(QObject):
|
||||
file_selected = Signal(str) # Сигнал с путем к выбранному файлу
|
||||
|
||||
class FileExplorer(QDialog):
|
||||
def __init__(self, parent=None, theme=None, file_filter=None, initial_path=None):
|
||||
def __init__(self, parent=None, theme=None, file_filter=None, initial_path=None, directory_only=False):
|
||||
super().__init__(parent)
|
||||
self.theme = theme if theme else default_styles
|
||||
self.theme_manager = ThemeManager()
|
||||
self.file_signal = FileSelectedSignal()
|
||||
self.file_filter = file_filter # Store the file filter
|
||||
self.directory_only = directory_only # Store the directory_only flag
|
||||
self.mime_db = QMimeDatabase() # Initialize QMimeDatabase for mimetype detection
|
||||
self.path_history = {} # Dictionary to store last selected item per directory
|
||||
self.initial_path = initial_path # Store initial path if provided
|
||||
@ -216,13 +217,21 @@ class FileExplorer(QDialog):
|
||||
full_path = os.path.join(self.current_path, selected)
|
||||
|
||||
if os.path.isdir(full_path):
|
||||
# Если выбрана директория, нормализуем путь
|
||||
self.current_path = os.path.normpath(full_path)
|
||||
self.update_file_list()
|
||||
if self.directory_only:
|
||||
# Подтверждаем выбор директории
|
||||
self.file_signal.file_selected.emit(os.path.normpath(full_path))
|
||||
self.accept()
|
||||
else:
|
||||
# Открываем директорию
|
||||
self.current_path = os.path.normpath(full_path)
|
||||
self.update_file_list()
|
||||
else:
|
||||
# Для файла отправляем нормализованный путь
|
||||
self.file_signal.file_selected.emit(os.path.normpath(full_path))
|
||||
self.accept()
|
||||
if not self.directory_only:
|
||||
# Для файла отправляем нормализованный путь
|
||||
self.file_signal.file_selected.emit(os.path.normpath(full_path))
|
||||
self.accept()
|
||||
else:
|
||||
logger.debug("Selected item is not a directory, ignoring: %s", full_path)
|
||||
|
||||
def previous_dir(self):
|
||||
"""Возврат к родительской директории"""
|
||||
@ -288,14 +297,7 @@ class FileExplorer(QDialog):
|
||||
items = os.listdir(self.current_path)
|
||||
dirs = [d for d in items if os.path.isdir(os.path.join(self.current_path, d))]
|
||||
|
||||
# Apply file filter if provided
|
||||
files = [f for f in items if os.path.isfile(os.path.join(self.current_path, f))]
|
||||
if self.file_filter:
|
||||
if isinstance(self.file_filter, str):
|
||||
files = [f for f in files if f.lower().endswith(self.file_filter)]
|
||||
elif isinstance(self.file_filter, tuple):
|
||||
files = [f for f in files if any(f.lower().endswith(ext) for ext in self.file_filter)]
|
||||
|
||||
# Добавляем директории
|
||||
for d in sorted(dirs):
|
||||
item = QListWidgetItem(f"{d}/")
|
||||
folder_icon = self.theme_manager.get_icon("folder")
|
||||
@ -307,25 +309,34 @@ class FileExplorer(QDialog):
|
||||
item.setIcon(folder_icon)
|
||||
self.file_list.addItem(item)
|
||||
|
||||
for f in sorted(files):
|
||||
item = QListWidgetItem(f)
|
||||
file_path = os.path.join(self.current_path, f)
|
||||
mime_type = self.mime_db.mimeTypeForFile(file_path).name()
|
||||
# Добавляем файлы только если directory_only=False
|
||||
if not self.directory_only:
|
||||
files = [f for f in items if os.path.isfile(os.path.join(self.current_path, f))]
|
||||
if self.file_filter:
|
||||
if isinstance(self.file_filter, str):
|
||||
files = [f for f in files if f.lower().endswith(self.file_filter)]
|
||||
elif isinstance(self.file_filter, tuple):
|
||||
files = [f for f in files if any(f.lower().endswith(ext) for ext in self.file_filter)]
|
||||
|
||||
if mime_type.startswith("image/"):
|
||||
pixmap = QPixmap(file_path)
|
||||
if not pixmap.isNull():
|
||||
item.setIcon(QIcon(pixmap.scaled(64, 64, Qt.AspectRatioMode.KeepAspectRatio)))
|
||||
elif file_path.lower().endswith(".exe"):
|
||||
tmp = tempfile.NamedTemporaryFile(suffix='.png', delete=False)
|
||||
tmp.close()
|
||||
if generate_thumbnail(file_path, tmp.name, size=64):
|
||||
pixmap = QPixmap(tmp.name)
|
||||
for f in sorted(files):
|
||||
item = QListWidgetItem(f)
|
||||
file_path = os.path.join(self.current_path, f)
|
||||
mime_type = self.mime_db.mimeTypeForFile(file_path).name()
|
||||
|
||||
if mime_type.startswith("image/"):
|
||||
pixmap = QPixmap(file_path)
|
||||
if not pixmap.isNull():
|
||||
item.setIcon(QIcon(pixmap))
|
||||
os.unlink(tmp.name)
|
||||
item.setIcon(QIcon(pixmap.scaled(64, 64, Qt.AspectRatioMode.KeepAspectRatio)))
|
||||
elif file_path.lower().endswith(".exe"):
|
||||
tmp = tempfile.NamedTemporaryFile(suffix='.png', delete=False)
|
||||
tmp.close()
|
||||
if generate_thumbnail(file_path, tmp.name, size=64):
|
||||
pixmap = QPixmap(tmp.name)
|
||||
if not pixmap.isNull():
|
||||
item.setIcon(QIcon(pixmap))
|
||||
os.unlink(tmp.name)
|
||||
|
||||
self.file_list.addItem(item)
|
||||
self.file_list.addItem(item)
|
||||
|
||||
self.path_label.setText(_("Path: ") + self.current_path)
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import time
|
||||
import threading
|
||||
import os
|
||||
from typing import Protocol, cast
|
||||
from evdev import InputDevice, InputEvent, ecodes, list_devices, ff
|
||||
from pyudev import Context, Monitor, MonitorObserver, Device
|
||||
@ -162,8 +163,28 @@ class InputManager(QObject):
|
||||
if not self.file_explorer or not hasattr(self.file_explorer, 'file_list'):
|
||||
return
|
||||
|
||||
if button_code in BUTTONS['confirm']:
|
||||
self.file_explorer.select_item()
|
||||
if button_code in BUTTONS['add_game']:
|
||||
if self.file_explorer.file_list.count() == 0:
|
||||
return
|
||||
selected = self.file_explorer.file_list.currentItem().text()
|
||||
full_path = os.path.join(self.file_explorer.current_path, selected)
|
||||
if os.path.isdir(full_path):
|
||||
# Подтверждаем выбор директории
|
||||
self.file_explorer.file_signal.file_selected.emit(os.path.normpath(full_path))
|
||||
self.file_explorer.accept()
|
||||
else:
|
||||
logger.debug("Selected item is not a directory: %s", full_path)
|
||||
elif button_code in BUTTONS['confirm']:
|
||||
if self.file_explorer.file_list.count() == 0:
|
||||
return
|
||||
selected = self.file_explorer.file_list.currentItem().text()
|
||||
full_path = os.path.join(self.file_explorer.current_path, selected)
|
||||
if os.path.isdir(full_path):
|
||||
# Открываем директорию
|
||||
self.file_explorer.current_path = os.path.normpath(full_path)
|
||||
self.file_explorer.update_file_list()
|
||||
else:
|
||||
logger.debug("Selected item is not a directory, cannot open: %s", full_path)
|
||||
elif button_code in BUTTONS['back']:
|
||||
self.file_explorer.close()
|
||||
elif button_code in BUTTONS['prev_dir']:
|
||||
|
Reference in New Issue
Block a user