From 80089a81d57425c5a14e169ad5040d47b43829d3 Mon Sep 17 00:00:00 2001 From: dervart Date: Wed, 2 Jul 2025 21:25:53 +0700 Subject: [PATCH] feat(context_menu_manager): add class CustomLineEdit --- portprotonqt/context_menu_manager.py | 68 ++++++++++++++++++- portprotonqt/main_window.py | 12 ++-- .../themes/standart/images/icons/copy.svg | 1 + .../themes/standart/images/icons/cut.svg | 1 + .../themes/standart/images/icons/delete.svg | 2 +- .../themes/standart/images/icons/paste.svg | 1 + .../themes/standart/images/icons/redo.svg | 1 + .../standart/images/icons/select_all.svg | 1 + .../themes/standart/images/icons/undo.svg | 1 + portprotonqt/themes/standart/styles.py | 24 +++---- 10 files changed, 90 insertions(+), 22 deletions(-) create mode 100644 portprotonqt/themes/standart/images/icons/copy.svg create mode 100644 portprotonqt/themes/standart/images/icons/cut.svg create mode 100644 portprotonqt/themes/standart/images/icons/paste.svg create mode 100644 portprotonqt/themes/standart/images/icons/redo.svg create mode 100644 portprotonqt/themes/standart/images/icons/select_all.svg create mode 100644 portprotonqt/themes/standart/images/icons/undo.svg diff --git a/portprotonqt/context_menu_manager.py b/portprotonqt/context_menu_manager.py index 2c84b74..04c7f8f 100644 --- a/portprotonqt/context_menu_manager.py +++ b/portprotonqt/context_menu_manager.py @@ -8,7 +8,7 @@ import logging import orjson import psutil import signal -from PySide6.QtWidgets import QMessageBox, QDialog, QMenu +from PySide6.QtWidgets import QMessageBox, QDialog, QMenu, QLineEdit, QApplication from PySide6.QtCore import QUrl, QPoint, QObject, Signal, Qt from PySide6.QtGui import QDesktopServices, QIcon from portprotonqt.localization import _ @@ -1080,3 +1080,69 @@ Icon={icon_path} _("Error"), _("Failed to open folder: {error}").format(error=str(e)) ) + +class CustomLineEdit(QLineEdit): + + def __init__(self, *args, theme=None, **kwargs): + super().__init__(*args, **kwargs) + self.theme = theme + self.theme_manager = ThemeManager() + + def contextMenuEvent(self, event): + def get_safe_icon(icon_name: str) -> QIcon: + icon = self.theme_manager.get_icon(icon_name) + if isinstance(icon, QIcon): + return icon + elif isinstance(icon, str) and os.path.exists(icon): + return QIcon(icon) + return QIcon() + + menu = QMenu(self) + if self.theme and hasattr(self.theme, "CONTEXT_MENU_STYLE"): + menu.setStyleSheet(self.theme.CONTEXT_MENU_STYLE) + + # Undo + undo = menu.addAction(get_safe_icon("undo"), _("Undo\tCtrl+Z")) + undo.triggered.connect(self.undo) + undo.setEnabled(self.isUndoAvailable()) + + # Redo + redo = menu.addAction(get_safe_icon("redo"), _("Redo\tCtrl+Y")) + redo.triggered.connect(self.redo) + redo.setEnabled(self.isRedoAvailable()) + + menu.addSeparator() + + # Cut + cut = menu.addAction(get_safe_icon("cut"), _("Cut\tCtrl+X")) + cut.triggered.connect(self.cut) + cut.setEnabled(self.hasSelectedText()) + + # Copy + copy = menu.addAction(get_safe_icon("copy"), _("Copy\tCtrl+C")) + copy.triggered.connect(self.copy) + copy.setEnabled(self.hasSelectedText()) + + # Paste + paste = menu.addAction(get_safe_icon("paste"), _("Paste\tCtrl+V")) + paste.triggered.connect(self.paste) + paste.setEnabled(QApplication.clipboard().mimeData().hasText()) + + # Delete + delete = menu.addAction(get_safe_icon("delete"), _("Delete\tDel")) + delete.triggered.connect(self._delete_selected_text) + delete.setEnabled(self.hasSelectedText()) + + menu.addSeparator() + + # Select All + select_all = menu.addAction(get_safe_icon("select_all"), _("Select All\tCtrl+A")) + select_all.triggered.connect(self.selectAll) + select_all.setEnabled(bool(self.text())) + + menu.exec(event.globalPos()) + + def _delete_selected_text(self): + cursor_pos = self.cursorPosition() + self.backspace() + self.setCursorPosition(cursor_pos) diff --git a/portprotonqt/main_window.py b/portprotonqt/main_window.py index 16584d9..211132a 100644 --- a/portprotonqt/main_window.py +++ b/portprotonqt/main_window.py @@ -12,7 +12,7 @@ from portprotonqt.dialogs import AddGameDialog, FileExplorer from portprotonqt.game_card import GameCard from portprotonqt.custom_widgets import FlowLayout, ClickableLabel, AutoSizeButton, NavLabel from portprotonqt.input_manager import InputManager -from portprotonqt.context_menu_manager import ContextMenuManager +from portprotonqt.context_menu_manager import ContextMenuManager, CustomLineEdit from portprotonqt.system_overlay import SystemOverlay from portprotonqt.image_utils import load_pixmap_async, round_corners, ImageCarousel @@ -589,7 +589,7 @@ class MainWindow(QMainWindow): self.addGameButton.clicked.connect(self.openAddGameDialog) layout.addWidget(self.addGameButton, alignment=Qt.AlignmentFlag.AlignRight) - self.searchEdit = QLineEdit() + self.searchEdit = CustomLineEdit(self, theme=self.theme) icon: QIcon = cast(QIcon, self.theme_manager.get_icon("search")) action_pos = cast(QLineEdit.ActionPosition, QLineEdit.ActionPosition.LeadingPosition) self.search_action = self.searchEdit.addAction(icon, action_pos) @@ -1039,7 +1039,7 @@ class MainWindow(QMainWindow): formLayout.addRow(self.gamesDisplayTitle, self.gamesDisplayCombo) # 4. Proxy settings - self.proxyUrlEdit = QLineEdit() + self.proxyUrlEdit = CustomLineEdit(self, theme=self.theme) self.proxyUrlEdit.setPlaceholderText(_("Proxy URL")) self.proxyUrlEdit.setStyleSheet(self.theme.PROXY_INPUT_STYLE) self.proxyUrlEdit.setFocusPolicy(Qt.FocusPolicy.StrongFocus) @@ -1051,7 +1051,7 @@ class MainWindow(QMainWindow): self.proxyUrlEdit.setText(proxy_cfg["http"]) formLayout.addRow(self.proxyUrlTitle, self.proxyUrlEdit) - self.proxyUserEdit = QLineEdit() + self.proxyUserEdit = CustomLineEdit(self, theme=self.theme) self.proxyUserEdit.setPlaceholderText(_("Proxy Username")) self.proxyUserEdit.setStyleSheet(self.theme.PROXY_INPUT_STYLE) self.proxyUserEdit.setFocusPolicy(Qt.FocusPolicy.StrongFocus) @@ -1060,7 +1060,7 @@ class MainWindow(QMainWindow): self.proxyUserTitle.setFocusPolicy(Qt.FocusPolicy.NoFocus) formLayout.addRow(self.proxyUserTitle, self.proxyUserEdit) - self.proxyPasswordEdit = QLineEdit() + self.proxyPasswordEdit = CustomLineEdit(self, theme=self.theme) self.proxyPasswordEdit.setPlaceholderText(_("Proxy Password")) self.proxyPasswordEdit.setEchoMode(QLineEdit.EchoMode.Password) self.proxyPasswordEdit.setStyleSheet(self.theme.PROXY_INPUT_STYLE) @@ -1359,7 +1359,7 @@ class MainWindow(QMainWindow): self.themeMetainfoLabel.setWordWrap(True) self.themeInfoLayout.addWidget(self.themeMetainfoLabel) - self.applyButton = AutoSizeButton(_("Apply Theme"), icon=self.theme_manager.get_icon("update")) + self.applyButton = AutoSizeButton(_("Apply Theme"), icon=self.theme_manager.get_icon("apply")) self.applyButton.setStyleSheet(self.theme.ACTION_BUTTON_STYLE) self.applyButton.setObjectName("actionButton") self.themeInfoLayout.addWidget(self.applyButton) diff --git a/portprotonqt/themes/standart/images/icons/copy.svg b/portprotonqt/themes/standart/images/icons/copy.svg new file mode 100644 index 0000000..3f23b76 --- /dev/null +++ b/portprotonqt/themes/standart/images/icons/copy.svg @@ -0,0 +1 @@ + diff --git a/portprotonqt/themes/standart/images/icons/cut.svg b/portprotonqt/themes/standart/images/icons/cut.svg new file mode 100644 index 0000000..55df7a9 --- /dev/null +++ b/portprotonqt/themes/standart/images/icons/cut.svg @@ -0,0 +1 @@ + diff --git a/portprotonqt/themes/standart/images/icons/delete.svg b/portprotonqt/themes/standart/images/icons/delete.svg index 0a7ef6d..0d44f54 100644 --- a/portprotonqt/themes/standart/images/icons/delete.svg +++ b/portprotonqt/themes/standart/images/icons/delete.svg @@ -1 +1 @@ - + diff --git a/portprotonqt/themes/standart/images/icons/paste.svg b/portprotonqt/themes/standart/images/icons/paste.svg new file mode 100644 index 0000000..e15aea0 --- /dev/null +++ b/portprotonqt/themes/standart/images/icons/paste.svg @@ -0,0 +1 @@ + diff --git a/portprotonqt/themes/standart/images/icons/redo.svg b/portprotonqt/themes/standart/images/icons/redo.svg new file mode 100644 index 0000000..18110bc --- /dev/null +++ b/portprotonqt/themes/standart/images/icons/redo.svg @@ -0,0 +1 @@ + diff --git a/portprotonqt/themes/standart/images/icons/select_all.svg b/portprotonqt/themes/standart/images/icons/select_all.svg new file mode 100644 index 0000000..fae609b --- /dev/null +++ b/portprotonqt/themes/standart/images/icons/select_all.svg @@ -0,0 +1 @@ + diff --git a/portprotonqt/themes/standart/images/icons/undo.svg b/portprotonqt/themes/standart/images/icons/undo.svg new file mode 100644 index 0000000..706fd9d --- /dev/null +++ b/portprotonqt/themes/standart/images/icons/undo.svg @@ -0,0 +1 @@ + diff --git a/portprotonqt/themes/standart/styles.py b/portprotonqt/themes/standart/styles.py index d885cf5..a103820 100644 --- a/portprotonqt/themes/standart/styles.py +++ b/portprotonqt/themes/standart/styles.py @@ -103,9 +103,10 @@ CONTEXT_MENU_STYLE = f""" font-family: '{font_family}'; font-size: {font_size_a}; padding: 5px; + min-width: 150px; }} QMenu::icon {{ - margin-left: 15px; + margin-left: 15px; }} QMenu::item {{ padding: 8px 20px 8px 10px; @@ -117,6 +118,9 @@ CONTEXT_MENU_STYLE = f""" background: {color_a}; color: {color_f}; }} + QMenu::item:disabled {{ + color: #7f7f7f; + }} QMenu::item:hover {{ background: {color_a}; color: {color_f}; @@ -127,6 +131,11 @@ CONTEXT_MENU_STYLE = f""" border: {border_b} rgba(255, 255, 255, 0.3); border-radius: {border_radius_a}; }} + QMenu::separator {{ + height: 1px; + background-color: #7f7f7f; + margin: 4px 8px; + }} """ # ГЛОБАЛЬНЫЙ СТИЛЬ ДЛЯ ОКНА (ФОН), ЛЭЙБЛОВ, КНОПОК @@ -742,19 +751,6 @@ PROXY_INPUT_STYLE = f""" border: {border_c} {color_a}; background-color: {color_e}; }} - QMenu {{ - border: {border_b} rgba(255, 255, 255, 0.2); - padding: 5px 10px; - background: {color_d}; - }} - QMenu::item {{ - padding: 0px 10px; - border: 10px solid {color_h}; /* reserve space for selection border */ - }} - QMenu::item:selected {{ - background: {color_c}; - border-radius: {border_radius_a}; - }} """ SETTINGS_COMBO_STYLE = f"""