fix(ui): expand usage of esc for closing modals (#793)

* fix(ui): expand usage of esc for closing modals

* chore: remove log statements

* refactor: use Qt enum in place of magic number

* ui: use enter key to save panel widgets
This commit is contained in:
Travis Abendshien
2025-02-09 18:33:29 -08:00
committed by GitHub
parent abc7cc3915
commit a2b9237be4
10 changed files with 109 additions and 38 deletions

View File

@@ -1,8 +1,10 @@
# Copyright (C) 2024 Travis Abendshien (CyanVoxel).
# Copyright (C) 2025 Travis Abendshien (CyanVoxel).
# Licensed under the GPL-3.0 License.
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio
import structlog
from PySide6 import QtCore, QtGui
from PySide6.QtCore import Qt, Signal
from PySide6.QtWidgets import (
QHBoxLayout,
@@ -16,7 +18,10 @@ from PySide6.QtWidgets import (
from src.core.library import Library
from src.qt.translations import Translations
logger = structlog.get_logger(__name__)
# NOTE: This class doesn't inherit from PanelWidget? Seems like it predates that system?
class AddFieldModal(QWidget):
done = Signal(list)
@@ -35,11 +40,7 @@ class AddFieldModal(QWidget):
self.title_widget = QLabel()
self.title_widget.setObjectName("fieldTitle")
self.title_widget.setWordWrap(True)
self.title_widget.setStyleSheet(
# 'background:blue;'
# 'text-align:center;'
"font-weight:bold;" "font-size:14px;" "padding-top: 6px" ""
)
self.title_widget.setStyleSheet("font-weight:bold;" "font-size:14px;" "padding-top: 6px;")
Translations.translate_qobject(self.title_widget, "library.field.add")
self.title_widget.setAlignment(Qt.AlignmentFlag.AlignCenter)
@@ -50,18 +51,13 @@ class AddFieldModal(QWidget):
self.button_layout.setContentsMargins(6, 6, 6, 6)
self.button_layout.addStretch(1)
# self.cancel_button = QPushButton()
# self.cancel_button.setText('Cancel')
self.cancel_button = QPushButton()
Translations.translate_qobject(self.cancel_button, "generic.cancel")
self.cancel_button.clicked.connect(self.hide)
# self.cancel_button.clicked.connect(widget.reset)
self.button_layout.addWidget(self.cancel_button)
self.save_button = QPushButton()
Translations.translate_qobject(self.save_button, "generic.add")
# self.save_button.setAutoDefault(True)
self.save_button.setDefault(True)
self.save_button.clicked.connect(self.hide)
self.save_button.clicked.connect(
@@ -74,8 +70,6 @@ class AddFieldModal(QWidget):
self.root_layout.addWidget(self.title_widget)
self.root_layout.addWidget(self.list_widget)
# self.root_layout.setStretch(1,2)
self.root_layout.addStretch(1)
self.root_layout.addWidget(self.button_container)
@@ -85,5 +79,13 @@ class AddFieldModal(QWidget):
item = QListWidgetItem(f"{df.name} ({df.type.value})")
item.setData(Qt.ItemDataRole.UserRole, df.key)
self.list_widget.addItem(item)
self.list_widget.setFocus()
super().show()
def keyPressEvent(self, event: QtGui.QKeyEvent) -> None: # noqa N802
if event.key() == QtCore.Qt.Key.Key_Escape:
self.cancel_button.click()
else: # Other key presses
pass
return super().keyPressEvent(event)

View File

@@ -1,9 +1,10 @@
# Copyright (C) 2024 Travis Abendshien (CyanVoxel).
# Copyright (C) 2025 Travis Abendshien (CyanVoxel).
# Licensed under the GPL-3.0 License.
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio
import typing
from typing import TYPE_CHECKING, override
from PySide6 import QtCore, QtGui
from PySide6.QtCore import Qt, QThreadPool, Signal
from PySide6.QtGui import QStandardItem, QStandardItemModel
from PySide6.QtWidgets import (
@@ -20,7 +21,7 @@ from src.qt.translations import Translations
from src.qt.widgets.progress import ProgressWidget
# Only import for type checking/autocompletion, will not be imported at runtime.
if typing.TYPE_CHECKING:
if TYPE_CHECKING:
from src.qt.ts_qt import QtDriver
@@ -111,3 +112,11 @@ class DeleteUnlinkedEntriesModal(QWidget):
self.done.emit(),
)
)
@override
def keyPressEvent(self, event: QtGui.QKeyEvent) -> None: # noqa N802
if event.key() == QtCore.Qt.Key.Key_Escape:
self.cancel_button.click()
else: # Other key presses
pass
return super().keyPressEvent(event)

View File

@@ -1,12 +1,14 @@
# Copyright (C) 2025
# Licensed under the GPL-3.0 License.
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio
import enum
import shutil
from pathlib import Path
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, override
import structlog
from PySide6 import QtCore, QtGui
from PySide6.QtCore import Qt, QUrl
from PySide6.QtGui import QStandardItem, QStandardItemModel
from PySide6.QtWidgets import (
@@ -232,3 +234,11 @@ class DropImportModal(QWidget):
)
index += 1
return filepath.name
@override
def keyPressEvent(self, event: QtGui.QKeyEvent) -> None: # noqa N802
if event.key() == QtCore.Qt.Key.Key_Escape:
self.cancel_button.click()
else: # Other key presses
pass
return super().keyPressEvent(event)

View File

@@ -1,10 +1,11 @@
# Copyright (C) 2024 Travis Abendshien (CyanVoxel).
# Copyright (C) 2025 Travis Abendshien (CyanVoxel).
# Licensed under the GPL-3.0 License.
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio
import typing
from typing import TYPE_CHECKING, override
from PySide6 import QtCore, QtGui
from PySide6.QtCore import Qt
from PySide6.QtWidgets import (
QFileDialog,
@@ -20,7 +21,7 @@ from src.qt.modals.mirror_entities import MirrorEntriesModal
from src.qt.translations import Translations
# Only import for type checking/autocompletion, will not be imported at runtime.
if typing.TYPE_CHECKING:
if TYPE_CHECKING:
from src.qt.ts_qt import QtDriver
@@ -135,3 +136,11 @@ class FixDupeFilesModal(QWidget):
self.dupe_count.setText(
Translations.translate_formatted("file.duplicates.matches", count=count)
)
@override
def keyPressEvent(self, event: QtGui.QKeyEvent) -> None: # noqa N802
if event.key() == QtCore.Qt.Key.Key_Escape:
self.done_button.click()
else: # Other key presses
pass
return super().keyPressEvent(event)

View File

@@ -1,10 +1,11 @@
# Copyright (C) 2024 Travis Abendshien (CyanVoxel).
# Copyright (C) 2025 Travis Abendshien (CyanVoxel).
# Licensed under the GPL-3.0 License.
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio
import typing
from typing import TYPE_CHECKING, override
from PySide6 import QtCore, QtGui
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QHBoxLayout, QLabel, QPushButton, QVBoxLayout, QWidget
from src.core.library import Library
@@ -16,7 +17,7 @@ from src.qt.translations import Translations
from src.qt.widgets.progress import ProgressWidget
# Only import for type checking/autocompletion, will not be imported at runtime.
if typing.TYPE_CHECKING:
if TYPE_CHECKING:
from src.qt.ts_qt import QtDriver
@@ -144,3 +145,11 @@ class FixUnlinkedEntriesModal(QWidget):
"entries.unlinked.missing_count.some", count=self.missing_count
)
)
@override
def keyPressEvent(self, event: QtGui.QKeyEvent) -> None: # noqa N802
if event.key() == QtCore.Qt.Key.Key_Escape:
self.done_button.click()
else: # Other key presses
pass
return super().keyPressEvent(event)

View File

@@ -4,10 +4,12 @@
import math
import typing
from collections.abc import Sequence
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, override
import structlog
from PySide6 import QtCore, QtGui
from PySide6.QtCore import Qt
from PySide6.QtWidgets import (
QFrame,
@@ -25,7 +27,7 @@ from src.core.palette import ColorType, get_tag_color
from src.qt.flowlayout import FlowLayout
from src.qt.translations import Translations
if typing.TYPE_CHECKING:
if TYPE_CHECKING:
from src.qt.ts_qt import QtDriver
logger = structlog.get_logger(__name__)
@@ -104,7 +106,7 @@ def generate_preview_data(library: Library) -> BranchData:
branch.dirs[tag.name] = BranchData(tag=tag)
branch = branch.dirs[tag.name]
def _add_folders_to_tree(items: typing.Sequence[str]) -> BranchData:
def _add_folders_to_tree(items: Sequence[str]) -> BranchData:
branch = tree
for folder in items:
if folder not in branch.dirs:
@@ -245,6 +247,14 @@ class FoldersToTagsModal(QWidget):
if isinstance(child, TreeItem):
child.set_all_branches(hidden)
@override
def keyPressEvent(self, event: QtGui.QKeyEvent) -> None: # noqa N802
if event.key() == QtCore.Qt.Key.Key_Escape:
self.close()
else: # Other key presses
pass
return super().keyPressEvent(event)
class TreeItem(QWidget):
def __init__(self, data: BranchData, parent_tag: Tag | None = None):

View File

@@ -1,10 +1,10 @@
# Copyright (C) 2024 Travis Abendshien (CyanVoxel).
# Copyright (C) 2025 Travis Abendshien (CyanVoxel).
# Licensed under the GPL-3.0 License.
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio
import contextlib
import typing
from typing import TYPE_CHECKING, override
from warnings import catch_warnings
import src.qt.modals.build_tag as build_tag
@@ -36,7 +36,7 @@ from src.qt.widgets.tag import (
logger = structlog.get_logger(__name__)
# Only import for type checking/autocompletion, will not be imported at runtime.
if typing.TYPE_CHECKING:
if TYPE_CHECKING:
from src.qt.modals.build_tag import BuildTagPanel
@@ -337,16 +337,16 @@ class TagSearchPanel(PanelWidget):
self.search_field.setFocus()
return super().showEvent(event)
@override
def keyPressEvent(self, event: QtGui.QKeyEvent) -> None: # noqa N802
# When Escape is pressed, focus back on the search box.
# If focus is already on the search box, close the modal.
if event.key() == QtCore.Qt.Key.Key_Escape:
if self.search_field.hasFocus():
self.parentWidget().hide()
return super().keyPressEvent(event)
else:
self.search_field.setFocus()
self.search_field.selectAll()
return super().keyPressEvent(event)
def remove_tag(self, tag: Tag):
pass

View File

@@ -1,8 +1,11 @@
# Copyright (C) 2024 Travis Abendshien (CyanVoxel).
# Copyright (C) 2025 Travis Abendshien (CyanVoxel).
# Licensed under the GPL-3.0 License.
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio
from typing import override
import structlog
from PySide6 import QtCore, QtGui
from PySide6.QtCore import Qt
from PySide6.QtWidgets import (
QHBoxLayout,
@@ -110,3 +113,11 @@ class PagedPanel(QWidget):
item.setHidden(False)
elif isinstance(item, int):
self.button_nav_layout.addStretch(item)
@override
def keyPressEvent(self, event: QtGui.QKeyEvent) -> None: # noqa N802
if event.key() == QtCore.Qt.Key.Key_Escape:
self.close()
else: # Other key presses
pass
return super().keyPressEvent(event)

View File

@@ -1,14 +1,16 @@
# Copyright (C) 2024 Travis Abendshien (CyanVoxel).
# Copyright (C) 2025 Travis Abendshien (CyanVoxel).
# Licensed under the GPL-3.0 License.
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio
import logging
from typing import Callable
import structlog
from PySide6 import QtCore, QtGui
from PySide6.QtCore import Qt, Signal
from PySide6.QtWidgets import QHBoxLayout, QLabel, QPushButton, QVBoxLayout, QWidget
from src.qt.translations import Translations
logger = structlog.get_logger(__name__)
class PanelModal(QWidget):
saved = Signal()
@@ -96,7 +98,10 @@ class PanelModal(QWidget):
widget.parent_post_init()
def closeEvent(self, event): # noqa: N802
self.done_button.click()
if self.cancel_button:
self.cancel_button.click()
elif self.done_button:
self.done_button.click()
event.accept()
def setTitle(self, title: str): # noqa: N802
@@ -125,12 +130,19 @@ class PanelWidget(QWidget):
pass
def add_callback(self, callback: Callable, event: str = "returnPressed"):
logging.warning(f"add_callback not implemented for {self.__class__.__name__}")
logger.warning(f"[PanelModal] add_callback not implemented for {self.__class__.__name__}")
def keyPressEvent(self, event: QtGui.QKeyEvent) -> None: # noqa N802
if event.key() == QtCore.Qt.Key.Key_Escape:
if self.panel_cancel_button:
self.panel_cancel_button.click()
elif self.panel_done_button:
self.panel_done_button.click()
elif event.key() == Qt.Key.Key_Return or event.key() == Qt.Key.Key_Enter:
if self.panel_save_button:
self.panel_save_button.click()
elif self.panel_done_button:
self.panel_done_button.click()
else: # Other key presses
pass
return super().keyPressEvent(event)

View File

@@ -500,10 +500,9 @@ class FieldContainers(QWidget):
Translations["generic.cancel_alt"], QMessageBox.ButtonRole.DestructiveRole
)
remove_mb.addButton("&Remove", QMessageBox.ButtonRole.RejectRole)
remove_mb.setDefaultButton(cancel_button)
remove_mb.setEscapeButton(cancel_button)
result = remove_mb.exec_()
if result == 3: # TODO - what is this magic number?
if result == QMessageBox.ButtonRole.ActionRole.value:
callback()
def emit_badge_signals(self, tag_ids: list[int] | set[int], emit_on_absent: bool = True):