mirror of
https://github.com/TagStudioDev/TagStudio.git
synced 2026-01-31 07:10:45 +00:00
feat(ui): option to change tag primary click behavior (#945)
* feat: add settings field * feat: change click behaviour based on settings value * fix: make ignore comment pyright specific to shut up mypy * fix: add german and english translations for new strings * fix: settings dropdowns were always english not matter the selected language
This commit is contained in:
@@ -24,6 +24,15 @@ class ShowFilepathOption(int, enum.Enum):
|
||||
DEFAULT = SHOW_RELATIVE_PATHS
|
||||
|
||||
|
||||
class TagClickActionOption(int, enum.Enum):
|
||||
"""Values representing the options for the "tag_click_action" setting."""
|
||||
|
||||
OPEN_EDIT = 0
|
||||
SET_SEARCH = 1
|
||||
ADD_TO_SEARCH = 2
|
||||
DEFAULT = OPEN_EDIT
|
||||
|
||||
|
||||
class Theme(str, enum.Enum):
|
||||
COLOR_BG_DARK = "#65000000"
|
||||
COLOR_BG_LIGHT = "#22000000"
|
||||
|
||||
@@ -11,7 +11,7 @@ import structlog
|
||||
import toml
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from tagstudio.core.enums import ShowFilepathOption
|
||||
from tagstudio.core.enums import ShowFilepathOption, TagClickActionOption
|
||||
|
||||
if platform.system() == "Windows":
|
||||
DEFAULT_GLOBAL_SETTINGS_PATH = (
|
||||
@@ -50,6 +50,7 @@ class GlobalSettings(BaseModel):
|
||||
page_size: int = Field(default=100)
|
||||
show_filepath: ShowFilepathOption = Field(default=ShowFilepathOption.DEFAULT)
|
||||
theme: Theme = Field(default=Theme.SYSTEM)
|
||||
tag_click_action: TagClickActionOption = Field(default=TagClickActionOption.DEFAULT)
|
||||
|
||||
date_format: str = Field(default="%x")
|
||||
hour_format: bool = Field(default=True)
|
||||
|
||||
@@ -125,6 +125,9 @@ class BrowsingState:
|
||||
def with_sorting_direction(self, ascending: bool) -> "BrowsingState":
|
||||
return replace(self, ascending=ascending)
|
||||
|
||||
def with_search_query(self, search_query: str) -> "BrowsingState":
|
||||
return replace(self, query=search_query)
|
||||
|
||||
|
||||
class FieldTypeEnum(enum.Enum):
|
||||
TEXT_LINE = "Text Line"
|
||||
|
||||
@@ -17,7 +17,7 @@ from PySide6.QtWidgets import (
|
||||
QWidget,
|
||||
)
|
||||
|
||||
from tagstudio.core.enums import ShowFilepathOption
|
||||
from tagstudio.core.enums import ShowFilepathOption, TagClickActionOption
|
||||
from tagstudio.core.global_settings import Theme
|
||||
from tagstudio.qt.translations import DEFAULT_TRANSLATION, LANGUAGES, Translations
|
||||
from tagstudio.qt.widgets.panel import PanelModal, PanelWidget
|
||||
@@ -25,17 +25,11 @@ from tagstudio.qt.widgets.panel import PanelModal, PanelWidget
|
||||
if TYPE_CHECKING:
|
||||
from tagstudio.qt.ts_qt import QtDriver
|
||||
|
||||
FILEPATH_OPTION_MAP: dict[ShowFilepathOption, str] = {
|
||||
ShowFilepathOption.SHOW_FULL_PATHS: Translations["settings.filepath.option.full"],
|
||||
ShowFilepathOption.SHOW_RELATIVE_PATHS: Translations["settings.filepath.option.relative"],
|
||||
ShowFilepathOption.SHOW_FILENAMES_ONLY: Translations["settings.filepath.option.name"],
|
||||
}
|
||||
FILEPATH_OPTION_MAP: dict[ShowFilepathOption, str] = {}
|
||||
|
||||
THEME_MAP: dict[Theme, str] = {
|
||||
Theme.DARK: Translations["settings.theme.dark"],
|
||||
Theme.LIGHT: Translations["settings.theme.light"],
|
||||
Theme.SYSTEM: Translations["settings.theme.system"],
|
||||
}
|
||||
THEME_MAP: dict[Theme, str] = {}
|
||||
|
||||
TAG_CLICK_ACTION_MAP: dict[TagClickActionOption, str] = {}
|
||||
|
||||
DATE_FORMAT_MAP: dict[str, str] = {
|
||||
"%d/%m/%y": "21/08/24",
|
||||
@@ -61,6 +55,29 @@ class SettingsPanel(PanelWidget):
|
||||
|
||||
def __init__(self, driver: "QtDriver"):
|
||||
super().__init__()
|
||||
# set these "constants" because language will be loaded from config shortly after startup
|
||||
# and we want to use the current language for the dropdowns
|
||||
global FILEPATH_OPTION_MAP, THEME_MAP, TAG_CLICK_ACTION_MAP
|
||||
FILEPATH_OPTION_MAP = {
|
||||
ShowFilepathOption.SHOW_FULL_PATHS: Translations["settings.filepath.option.full"],
|
||||
ShowFilepathOption.SHOW_RELATIVE_PATHS: Translations[
|
||||
"settings.filepath.option.relative"
|
||||
],
|
||||
ShowFilepathOption.SHOW_FILENAMES_ONLY: Translations["settings.filepath.option.name"],
|
||||
}
|
||||
THEME_MAP = {
|
||||
Theme.DARK: Translations["settings.theme.dark"],
|
||||
Theme.LIGHT: Translations["settings.theme.light"],
|
||||
Theme.SYSTEM: Translations["settings.theme.system"],
|
||||
}
|
||||
TAG_CLICK_ACTION_MAP = {
|
||||
TagClickActionOption.OPEN_EDIT: Translations["settings.tag_click_action.open_edit"],
|
||||
TagClickActionOption.SET_SEARCH: Translations["settings.tag_click_action.set_search"],
|
||||
TagClickActionOption.ADD_TO_SEARCH: Translations[
|
||||
"settings.tag_click_action.add_to_search"
|
||||
],
|
||||
}
|
||||
|
||||
self.driver = driver
|
||||
self.setMinimumSize(400, 300)
|
||||
|
||||
@@ -158,13 +175,27 @@ class SettingsPanel(PanelWidget):
|
||||
self.theme_combobox = QComboBox()
|
||||
for k in THEME_MAP:
|
||||
self.theme_combobox.addItem(THEME_MAP[k], k)
|
||||
theme: Theme = self.driver.settings.theme
|
||||
theme = self.driver.settings.theme
|
||||
if theme not in THEME_MAP:
|
||||
theme = Theme.DEFAULT
|
||||
self.theme_combobox.setCurrentIndex(list(THEME_MAP.keys()).index(theme))
|
||||
self.theme_combobox.currentIndexChanged.connect(self.__update_restart_label)
|
||||
form_layout.addRow(Translations["settings.theme.label"], self.theme_combobox)
|
||||
|
||||
# Tag Click Action
|
||||
self.tag_click_action_combobox = QComboBox()
|
||||
for k in TAG_CLICK_ACTION_MAP:
|
||||
self.tag_click_action_combobox.addItem(TAG_CLICK_ACTION_MAP[k], k)
|
||||
tag_click_action = self.driver.settings.tag_click_action
|
||||
if tag_click_action not in TAG_CLICK_ACTION_MAP:
|
||||
tag_click_action = TagClickActionOption.DEFAULT
|
||||
self.tag_click_action_combobox.setCurrentIndex(
|
||||
list(TAG_CLICK_ACTION_MAP.keys()).index(tag_click_action)
|
||||
)
|
||||
form_layout.addRow(
|
||||
Translations["settings.tag_click_action.label"], self.tag_click_action_combobox
|
||||
)
|
||||
|
||||
# Date Format
|
||||
self.dateformat_combobox = QComboBox()
|
||||
for k in DATE_FORMAT_MAP:
|
||||
@@ -206,6 +237,7 @@ class SettingsPanel(PanelWidget):
|
||||
"page_size": int(self.page_size_line_edit.text()),
|
||||
"show_filepath": self.filepath_combobox.currentData(),
|
||||
"theme": self.theme_combobox.currentData(),
|
||||
"tag_click_action": self.tag_click_action_combobox.currentData(),
|
||||
"date_format": self.dateformat_combobox.currentData(),
|
||||
"hour_format": self.hourformat_checkbox.isChecked(),
|
||||
"zero_padding": self.zeropadding_checkbox.isChecked(),
|
||||
@@ -221,6 +253,7 @@ class SettingsPanel(PanelWidget):
|
||||
driver.settings.page_size = settings["page_size"]
|
||||
driver.settings.show_filepath = settings["show_filepath"]
|
||||
driver.settings.theme = settings["theme"]
|
||||
driver.settings.tag_click_action = settings["tag_click_action"]
|
||||
driver.settings.date_format = settings["date_format"]
|
||||
driver.settings.hour_format = settings["hour_format"]
|
||||
driver.settings.zero_padding = settings["zero_padding"]
|
||||
|
||||
@@ -98,15 +98,17 @@ class TagWidget(QWidget):
|
||||
on_click = Signal()
|
||||
on_edit = Signal()
|
||||
|
||||
tag: Tag | None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
tag: Tag | None,
|
||||
has_edit: bool,
|
||||
has_remove: bool,
|
||||
library: "Library | None" = None,
|
||||
on_remove_callback: FunctionType = None,
|
||||
on_click_callback: FunctionType = None,
|
||||
on_edit_callback: FunctionType = None,
|
||||
on_remove_callback: FunctionType | None = None,
|
||||
on_click_callback: FunctionType | None = None,
|
||||
on_edit_callback: FunctionType | None = None,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.tag = tag
|
||||
@@ -123,10 +125,18 @@ class TagWidget(QWidget):
|
||||
self.bg_button = QPushButton(self)
|
||||
self.bg_button.setFlat(True)
|
||||
|
||||
# add callbacks
|
||||
if on_remove_callback is not None:
|
||||
self.on_remove.connect(on_remove_callback)
|
||||
if on_click_callback is not None:
|
||||
self.on_click.connect(on_click_callback)
|
||||
if on_edit_callback is not None:
|
||||
self.on_edit.connect(on_edit_callback)
|
||||
|
||||
# add edit action
|
||||
if has_edit:
|
||||
edit_action = QAction(self)
|
||||
edit_action.setText(Translations["generic.edit"])
|
||||
edit_action.triggered.connect(on_edit_callback)
|
||||
edit_action.triggered.connect(self.on_edit.emit)
|
||||
self.bg_button.addAction(edit_action)
|
||||
# if on_click_callback:
|
||||
|
||||
@@ -8,6 +8,7 @@ import typing
|
||||
import structlog
|
||||
from PySide6.QtCore import Signal
|
||||
|
||||
from tagstudio.core.enums import TagClickActionOption
|
||||
from tagstudio.core.library.alchemy.enums import BrowsingState
|
||||
from tagstudio.core.library.alchemy.models import Tag
|
||||
from tagstudio.qt.flowlayout import FlowLayout
|
||||
@@ -26,6 +27,8 @@ class TagBoxWidget(FieldWidget):
|
||||
updated = Signal()
|
||||
error_occurred = Signal(Exception)
|
||||
|
||||
driver: "QtDriver"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
tags: set[Tag],
|
||||
@@ -50,11 +53,11 @@ class TagBoxWidget(FieldWidget):
|
||||
tags_ = sorted(list(tags), key=lambda tag: self.driver.lib.tag_display_name(tag.id))
|
||||
logger.info("[TagBoxWidget] Tags:", tags=tags)
|
||||
while self.base_layout.itemAt(0):
|
||||
self.base_layout.takeAt(0).widget().deleteLater()
|
||||
self.base_layout.takeAt(0).widget().deleteLater() # pyright: ignore[reportOptionalMemberAccess]
|
||||
|
||||
for tag in tags_:
|
||||
tag_widget = TagWidget(tag, library=self.driver.lib, has_edit=True, has_remove=True)
|
||||
tag_widget.on_click.connect(lambda t=tag: self.edit_tag(t))
|
||||
tag_widget.on_click.connect(lambda t=tag: self.__on_tag_clicked(t))
|
||||
|
||||
tag_widget.on_remove.connect(
|
||||
lambda tag_id=tag.id: (
|
||||
@@ -73,6 +76,26 @@ class TagBoxWidget(FieldWidget):
|
||||
|
||||
self.base_layout.addWidget(tag_widget)
|
||||
|
||||
def __on_tag_clicked(self, tag: Tag):
|
||||
match self.driver.settings.tag_click_action:
|
||||
case TagClickActionOption.OPEN_EDIT:
|
||||
self.edit_tag(tag)
|
||||
case TagClickActionOption.SET_SEARCH:
|
||||
self.driver.update_browsing_state(BrowsingState.from_tag_id(tag.id))
|
||||
case TagClickActionOption.ADD_TO_SEARCH:
|
||||
# NOTE: modifying the ast and then setting that would be nicer
|
||||
# than this string manipulation, but also much more complex,
|
||||
# due to needing to implement a visitor that turns an AST to a string
|
||||
# So if that exists when you read this, change the following accordingly.
|
||||
current = self.driver.browsing_history.current
|
||||
suffix = BrowsingState.from_tag_id(tag.id).query
|
||||
assert suffix is not None
|
||||
self.driver.update_browsing_state(
|
||||
current.with_search_query(
|
||||
f"{current.query} {suffix}" if current.query else suffix
|
||||
)
|
||||
)
|
||||
|
||||
def edit_tag(self, tag: Tag):
|
||||
assert isinstance(tag, Tag), f"tag is {type(tag)}"
|
||||
build_tag_panel = BuildTagPanel(self.driver.lib, tag=tag)
|
||||
|
||||
@@ -243,6 +243,10 @@
|
||||
"settings.restart_required": "Bitte TagStudio neustarten, um Änderungen anzuwenden.",
|
||||
"settings.show_filenames_in_grid": "Dateinamen in Raster darstellen",
|
||||
"settings.show_recent_libraries": "Zuletzt verwendete Bibliotheken anzeigen",
|
||||
"settings.tag_click_action.label": "Tag Klick Aktion",
|
||||
"settings.tag_click_action.add_to_search": "Tag zu Suche hinzufügen",
|
||||
"settings.tag_click_action.open_edit": "Tag bearbeiten",
|
||||
"settings.tag_click_action.set_search": "Nach Tag suchen",
|
||||
"settings.theme.dark": "Dunkel",
|
||||
"settings.theme.label": "Design:",
|
||||
"settings.theme.light": "Hell",
|
||||
|
||||
@@ -243,6 +243,10 @@
|
||||
"settings.restart_required": "Please restart TagStudio for changes to take effect.",
|
||||
"settings.show_filenames_in_grid": "Show Filenames in Grid",
|
||||
"settings.show_recent_libraries": "Show Recent Libraries",
|
||||
"settings.tag_click_action.label": "Tag Click Action",
|
||||
"settings.tag_click_action.add_to_search": "Add Tag to Search",
|
||||
"settings.tag_click_action.open_edit": "Edit Tag",
|
||||
"settings.tag_click_action.set_search": "Search for Tag",
|
||||
"settings.theme.dark": "Dark",
|
||||
"settings.theme.label": "Theme:",
|
||||
"settings.theme.light": "Light",
|
||||
|
||||
Reference in New Issue
Block a user