4 Commits

Author SHA1 Message Date
4d7caa33b5 feat(FileExplorer): add prev dir action on Y and Square thanks to @Vector_null
All checks were successful
Code and build check / Check code (push) Successful in 1m29s
Code and build check / Build with uv (push) Successful in 53s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-06-27 10:37:51 +05:00
7fb05322ad fix: returned game list update on game delete
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-06-27 10:28:21 +05:00
fea5ff9877 feat(FileExplorer): add quick navigation for mounted drives
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-06-26 21:36:23 +05:00
dc06f78c43 fix(FileExplorer): normalize path handling for parent directory navigation
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-06-26 21:10:44 +05:00
3 changed files with 98 additions and 8 deletions

View File

@@ -601,7 +601,6 @@ Icon={icon_path}
return False
def delete_game(self, game_name, exec_line):
"""Delete the .desktop file and associated custom data for the game."""
reply = QMessageBox.question(
self.parent,
_("Confirm Deletion"),
@@ -647,6 +646,10 @@ Icon={icon_path}
_("Failed to delete custom data: {error}").format(error=str(e))
)
# Перезагрузка списка игр и обновление сетки
self.load_games()
self.update_game_grid()
def add_to_menu(self, game_name, exec_line):
"""Copy the .desktop file to ~/.local/share/applications."""
if not self._check_portproton():

View File

@@ -4,7 +4,7 @@ from typing import cast, TYPE_CHECKING
from PySide6.QtGui import QPixmap
from PySide6.QtWidgets import (
QDialog, QLineEdit, QFormLayout, QPushButton,
QHBoxLayout, QDialogButtonBox, QLabel, QVBoxLayout, QListWidget
QHBoxLayout, QDialogButtonBox, QLabel, QVBoxLayout, QListWidget, QScrollArea, QWidget
)
from PySide6.QtCore import Qt, QObject, Signal
from icoextract import IconExtractor, IconExtractorError
@@ -111,6 +111,30 @@ class FileExplorer(QDialog):
if self.input_manager:
self.input_manager.enable_file_explorer_mode(self)
# Initialize drives list
self.update_drives_list()
def get_mounted_drives(self):
"""Получение списка смонтированных дисков из /proc/mounts, исключая системные пути"""
mounted_drives = []
try:
with open('/proc/mounts') as f:
for line in f:
parts = line.strip().split()
if len(parts) < 2:
continue
mount_point = parts[1]
# Исключаем системные и временные пути
if mount_point.startswith(('/run', '/dev', '/sys', '/proc', '/tmp', '/snap', '/var/lib')):
continue
# Проверяем, является ли точка монтирования директорией и доступна ли она
if os.path.isdir(mount_point) and os.access(mount_point, os.R_OK):
mounted_drives.append(mount_point)
return sorted(mounted_drives)
except Exception as e:
logger.error(f"Ошибка при получении смонтированных дисков: {e}")
return []
def setup_ui(self):
"""Настройка интерфейса"""
self.setWindowTitle("File Explorer")
@@ -121,6 +145,18 @@ class FileExplorer(QDialog):
self.main_layout.setSpacing(10)
self.setLayout(self.main_layout)
# Панель для смонтированных дисков
self.drives_layout = QHBoxLayout()
self.drives_scroll = QScrollArea()
self.drives_scroll.setWidgetResizable(True)
self.drives_container = QWidget()
self.drives_container.setLayout(self.drives_layout)
self.drives_scroll.setWidget(self.drives_container)
self.drives_scroll.setStyleSheet(FileExplorerStyles.BUTTON_STYLE)
self.drives_scroll.setFixedHeight(50)
self.main_layout.addWidget(self.drives_scroll)
# Путь
self.path_label = QLabel()
self.path_label.setStyleSheet(FileExplorerStyles.PATH_LABEL_STYLE)
self.main_layout.addWidget(self.path_label)
@@ -172,12 +208,58 @@ class FileExplorer(QDialog):
full_path = os.path.join(self.current_path, selected)
if os.path.isdir(full_path):
self.current_path = full_path
# Если выбрана директория, нормализуем путь
self.current_path = os.path.normpath(full_path)
self.update_file_list()
else:
self.file_signal.file_selected.emit(full_path)
# Для файла отправляем нормализованный путь
self.file_signal.file_selected.emit(os.path.normpath(full_path))
self.accept()
def previous_dir(self):
"""Возврат к родительской директории"""
try:
if self.current_path == "/":
return # Уже в корне
# Нормализуем путь (убираем конечный слеш, если есть)
normalized_path = os.path.normpath(self.current_path)
# Получаем родительскую директорию
parent_dir = os.path.dirname(normalized_path)
if not parent_dir:
parent_dir = "/"
self.current_path = parent_dir
self.update_file_list()
except Exception as e:
logger.error(f"Error navigating to parent directory: {e}")
def update_drives_list(self):
"""Обновление списка смонтированных дисков"""
for i in reversed(range(self.drives_layout.count())):
widget = self.drives_layout.itemAt(i).widget()
if widget:
widget.deleteLater()
drives = self.get_mounted_drives()
for drive in drives:
drive_name = os.path.basename(drive) or drive.split('/')[-1] or drive
button = QPushButton(drive_name)
button.setStyleSheet(FileExplorerStyles.BUTTON_STYLE)
button.clicked.connect(lambda checked, path=drive: self.change_drive(path))
self.drives_layout.addWidget(button)
self.drives_layout.addStretch()
def change_drive(self, drive_path):
"""Переход к выбранному диску"""
if os.path.isdir(drive_path) and os.access(drive_path, os.R_OK):
self.current_path = os.path.normpath(drive_path)
self.update_file_list()
else:
logger.warning(f"Путь диска недоступен: {drive_path}")
def update_file_list(self):
"""Обновление списка файлов"""
self.file_list.clear()
@@ -191,7 +273,10 @@ class FileExplorer(QDialog):
# 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:
files = [f for f in files if f.lower().endswith(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):
self.file_list.addItem(f"{d}/")
@@ -230,7 +315,6 @@ class FileExplorer(QDialog):
if self.input_manager:
self.input_manager.disable_file_explorer_mode()
super().accept()
class AddGameDialog(QDialog):
def __init__(self, parent=None, theme=None, edit_mode=False, game_name=None, exe_path=None, cover_path=None):
super().__init__(parent)

View File

@@ -44,6 +44,7 @@ BUTTONS = {
'confirm': {ecodes.BTN_SOUTH}, # A (Xbox) / Cross (PS)
'back': {ecodes.BTN_EAST}, # B (Xbox) / Circle (PS)
'add_game': {ecodes.BTN_NORTH}, # X (Xbox) / Triangle (PS)
'prev_dir': {ecodes.BTN_WEST}, # Y (Xbox) / Square (PS)
'prev_tab': {ecodes.BTN_TL}, # LB (Xbox) / L1 (PS)
'next_tab': {ecodes.BTN_TR}, # RB (Xbox) / R1 (PS)
'context_menu': {ecodes.BTN_START}, # Start (Xbox) / Options (PS)
@@ -161,10 +162,12 @@ class InputManager(QObject):
if not self.file_explorer or not hasattr(self.file_explorer, 'file_list'):
return
if button_code in BUTTONS['confirm']: # Кнопка A
if button_code in BUTTONS['confirm']:
self.file_explorer.select_item()
elif button_code in BUTTONS['back']: # Кнопка B
elif button_code in BUTTONS['back']:
self.file_explorer.close()
elif button_code in BUTTONS['prev_dir']:
self.file_explorer.previous_dir()
else:
if self.original_button_handler:
self.original_button_handler(button_code)