Skip to content

Commit c100bab

Browse files
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
1 parent 3999d5d commit c100bab

File tree

8 files changed

+106
-19
lines changed

8 files changed

+106
-19
lines changed

src/tagstudio/core/enums.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ class ShowFilepathOption(int, enum.Enum):
2424
DEFAULT = SHOW_RELATIVE_PATHS
2525

2626

27+
class TagClickActionOption(int, enum.Enum):
28+
"""Values representing the options for the "tag_click_action" setting."""
29+
30+
OPEN_EDIT = 0
31+
SET_SEARCH = 1
32+
ADD_TO_SEARCH = 2
33+
DEFAULT = OPEN_EDIT
34+
35+
2736
class Theme(str, enum.Enum):
2837
COLOR_BG_DARK = "#65000000"
2938
COLOR_BG_LIGHT = "#22000000"

src/tagstudio/core/global_settings.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import toml
1212
from pydantic import BaseModel, Field
1313

14-
from tagstudio.core.enums import ShowFilepathOption
14+
from tagstudio.core.enums import ShowFilepathOption, TagClickActionOption
1515

1616
if platform.system() == "Windows":
1717
DEFAULT_GLOBAL_SETTINGS_PATH = (
@@ -50,6 +50,7 @@ class GlobalSettings(BaseModel):
5050
page_size: int = Field(default=100)
5151
show_filepath: ShowFilepathOption = Field(default=ShowFilepathOption.DEFAULT)
5252
theme: Theme = Field(default=Theme.SYSTEM)
53+
tag_click_action: TagClickActionOption = Field(default=TagClickActionOption.DEFAULT)
5354

5455
date_format: str = Field(default="%x")
5556
hour_format: bool = Field(default=True)

src/tagstudio/core/library/alchemy/enums.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ def with_sorting_mode(self, mode: SortingModeEnum) -> "BrowsingState":
125125
def with_sorting_direction(self, ascending: bool) -> "BrowsingState":
126126
return replace(self, ascending=ascending)
127127

128+
def with_search_query(self, search_query: str) -> "BrowsingState":
129+
return replace(self, query=search_query)
130+
128131

129132
class FieldTypeEnum(enum.Enum):
130133
TEXT_LINE = "Text Line"

src/tagstudio/qt/modals/settings_panel.py

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,19 @@
1717
QWidget,
1818
)
1919

20-
from tagstudio.core.enums import ShowFilepathOption
20+
from tagstudio.core.enums import ShowFilepathOption, TagClickActionOption
2121
from tagstudio.core.global_settings import Theme
2222
from tagstudio.qt.translations import DEFAULT_TRANSLATION, LANGUAGES, Translations
2323
from tagstudio.qt.widgets.panel import PanelModal, PanelWidget
2424

2525
if TYPE_CHECKING:
2626
from tagstudio.qt.ts_qt import QtDriver
2727

28-
FILEPATH_OPTION_MAP: dict[ShowFilepathOption, str] = {
29-
ShowFilepathOption.SHOW_FULL_PATHS: Translations["settings.filepath.option.full"],
30-
ShowFilepathOption.SHOW_RELATIVE_PATHS: Translations["settings.filepath.option.relative"],
31-
ShowFilepathOption.SHOW_FILENAMES_ONLY: Translations["settings.filepath.option.name"],
32-
}
28+
FILEPATH_OPTION_MAP: dict[ShowFilepathOption, str] = {}
3329

34-
THEME_MAP: dict[Theme, str] = {
35-
Theme.DARK: Translations["settings.theme.dark"],
36-
Theme.LIGHT: Translations["settings.theme.light"],
37-
Theme.SYSTEM: Translations["settings.theme.system"],
38-
}
30+
THEME_MAP: dict[Theme, str] = {}
31+
32+
TAG_CLICK_ACTION_MAP: dict[TagClickActionOption, str] = {}
3933

4034
DATE_FORMAT_MAP: dict[str, str] = {
4135
"%d/%m/%y": "21/08/24",
@@ -61,6 +55,29 @@ class SettingsPanel(PanelWidget):
6155

6256
def __init__(self, driver: "QtDriver"):
6357
super().__init__()
58+
# set these "constants" because language will be loaded from config shortly after startup
59+
# and we want to use the current language for the dropdowns
60+
global FILEPATH_OPTION_MAP, THEME_MAP, TAG_CLICK_ACTION_MAP
61+
FILEPATH_OPTION_MAP = {
62+
ShowFilepathOption.SHOW_FULL_PATHS: Translations["settings.filepath.option.full"],
63+
ShowFilepathOption.SHOW_RELATIVE_PATHS: Translations[
64+
"settings.filepath.option.relative"
65+
],
66+
ShowFilepathOption.SHOW_FILENAMES_ONLY: Translations["settings.filepath.option.name"],
67+
}
68+
THEME_MAP = {
69+
Theme.DARK: Translations["settings.theme.dark"],
70+
Theme.LIGHT: Translations["settings.theme.light"],
71+
Theme.SYSTEM: Translations["settings.theme.system"],
72+
}
73+
TAG_CLICK_ACTION_MAP = {
74+
TagClickActionOption.OPEN_EDIT: Translations["settings.tag_click_action.open_edit"],
75+
TagClickActionOption.SET_SEARCH: Translations["settings.tag_click_action.set_search"],
76+
TagClickActionOption.ADD_TO_SEARCH: Translations[
77+
"settings.tag_click_action.add_to_search"
78+
],
79+
}
80+
6481
self.driver = driver
6582
self.setMinimumSize(400, 300)
6683

@@ -158,13 +175,27 @@ def on_page_size_changed():
158175
self.theme_combobox = QComboBox()
159176
for k in THEME_MAP:
160177
self.theme_combobox.addItem(THEME_MAP[k], k)
161-
theme: Theme = self.driver.settings.theme
178+
theme = self.driver.settings.theme
162179
if theme not in THEME_MAP:
163180
theme = Theme.DEFAULT
164181
self.theme_combobox.setCurrentIndex(list(THEME_MAP.keys()).index(theme))
165182
self.theme_combobox.currentIndexChanged.connect(self.__update_restart_label)
166183
form_layout.addRow(Translations["settings.theme.label"], self.theme_combobox)
167184

185+
# Tag Click Action
186+
self.tag_click_action_combobox = QComboBox()
187+
for k in TAG_CLICK_ACTION_MAP:
188+
self.tag_click_action_combobox.addItem(TAG_CLICK_ACTION_MAP[k], k)
189+
tag_click_action = self.driver.settings.tag_click_action
190+
if tag_click_action not in TAG_CLICK_ACTION_MAP:
191+
tag_click_action = TagClickActionOption.DEFAULT
192+
self.tag_click_action_combobox.setCurrentIndex(
193+
list(TAG_CLICK_ACTION_MAP.keys()).index(tag_click_action)
194+
)
195+
form_layout.addRow(
196+
Translations["settings.tag_click_action.label"], self.tag_click_action_combobox
197+
)
198+
168199
# Date Format
169200
self.dateformat_combobox = QComboBox()
170201
for k in DATE_FORMAT_MAP:
@@ -206,6 +237,7 @@ def get_settings(self) -> dict:
206237
"page_size": int(self.page_size_line_edit.text()),
207238
"show_filepath": self.filepath_combobox.currentData(),
208239
"theme": self.theme_combobox.currentData(),
240+
"tag_click_action": self.tag_click_action_combobox.currentData(),
209241
"date_format": self.dateformat_combobox.currentData(),
210242
"hour_format": self.hourformat_checkbox.isChecked(),
211243
"zero_padding": self.zeropadding_checkbox.isChecked(),
@@ -221,6 +253,7 @@ def update_settings(self, driver: "QtDriver"):
221253
driver.settings.page_size = settings["page_size"]
222254
driver.settings.show_filepath = settings["show_filepath"]
223255
driver.settings.theme = settings["theme"]
256+
driver.settings.tag_click_action = settings["tag_click_action"]
224257
driver.settings.date_format = settings["date_format"]
225258
driver.settings.hour_format = settings["hour_format"]
226259
driver.settings.zero_padding = settings["zero_padding"]

src/tagstudio/qt/widgets/tag.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,15 +98,17 @@ class TagWidget(QWidget):
9898
on_click = Signal()
9999
on_edit = Signal()
100100

101+
tag: Tag | None
102+
101103
def __init__(
102104
self,
103105
tag: Tag | None,
104106
has_edit: bool,
105107
has_remove: bool,
106108
library: "Library | None" = None,
107-
on_remove_callback: FunctionType = None,
108-
on_click_callback: FunctionType = None,
109-
on_edit_callback: FunctionType = None,
109+
on_remove_callback: FunctionType | None = None,
110+
on_click_callback: FunctionType | None = None,
111+
on_edit_callback: FunctionType | None = None,
110112
) -> None:
111113
super().__init__()
112114
self.tag = tag
@@ -123,10 +125,18 @@ def __init__(
123125
self.bg_button = QPushButton(self)
124126
self.bg_button.setFlat(True)
125127

128+
# add callbacks
129+
if on_remove_callback is not None:
130+
self.on_remove.connect(on_remove_callback)
131+
if on_click_callback is not None:
132+
self.on_click.connect(on_click_callback)
133+
if on_edit_callback is not None:
134+
self.on_edit.connect(on_edit_callback)
135+
136+
# add edit action
126137
if has_edit:
127138
edit_action = QAction(self)
128139
edit_action.setText(Translations["generic.edit"])
129-
edit_action.triggered.connect(on_edit_callback)
130140
edit_action.triggered.connect(self.on_edit.emit)
131141
self.bg_button.addAction(edit_action)
132142
# if on_click_callback:

src/tagstudio/qt/widgets/tag_box.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import structlog
99
from PySide6.QtCore import Signal
1010

11+
from tagstudio.core.enums import TagClickActionOption
1112
from tagstudio.core.library.alchemy.enums import BrowsingState
1213
from tagstudio.core.library.alchemy.models import Tag
1314
from tagstudio.qt.flowlayout import FlowLayout
@@ -26,6 +27,8 @@ class TagBoxWidget(FieldWidget):
2627
updated = Signal()
2728
error_occurred = Signal(Exception)
2829

30+
driver: "QtDriver"
31+
2932
def __init__(
3033
self,
3134
tags: set[Tag],
@@ -50,11 +53,11 @@ def set_tags(self, tags: typing.Iterable[Tag]):
5053
tags_ = sorted(list(tags), key=lambda tag: self.driver.lib.tag_display_name(tag.id))
5154
logger.info("[TagBoxWidget] Tags:", tags=tags)
5255
while self.base_layout.itemAt(0):
53-
self.base_layout.takeAt(0).widget().deleteLater()
56+
self.base_layout.takeAt(0).widget().deleteLater() # pyright: ignore[reportOptionalMemberAccess]
5457

5558
for tag in tags_:
5659
tag_widget = TagWidget(tag, library=self.driver.lib, has_edit=True, has_remove=True)
57-
tag_widget.on_click.connect(lambda t=tag: self.edit_tag(t))
60+
tag_widget.on_click.connect(lambda t=tag: self.__on_tag_clicked(t))
5861

5962
tag_widget.on_remove.connect(
6063
lambda tag_id=tag.id: (
@@ -73,6 +76,26 @@ def set_tags(self, tags: typing.Iterable[Tag]):
7376

7477
self.base_layout.addWidget(tag_widget)
7578

79+
def __on_tag_clicked(self, tag: Tag):
80+
match self.driver.settings.tag_click_action:
81+
case TagClickActionOption.OPEN_EDIT:
82+
self.edit_tag(tag)
83+
case TagClickActionOption.SET_SEARCH:
84+
self.driver.update_browsing_state(BrowsingState.from_tag_id(tag.id))
85+
case TagClickActionOption.ADD_TO_SEARCH:
86+
# NOTE: modifying the ast and then setting that would be nicer
87+
# than this string manipulation, but also much more complex,
88+
# due to needing to implement a visitor that turns an AST to a string
89+
# So if that exists when you read this, change the following accordingly.
90+
current = self.driver.browsing_history.current
91+
suffix = BrowsingState.from_tag_id(tag.id).query
92+
assert suffix is not None
93+
self.driver.update_browsing_state(
94+
current.with_search_query(
95+
f"{current.query} {suffix}" if current.query else suffix
96+
)
97+
)
98+
7699
def edit_tag(self, tag: Tag):
77100
assert isinstance(tag, Tag), f"tag is {type(tag)}"
78101
build_tag_panel = BuildTagPanel(self.driver.lib, tag=tag)

src/tagstudio/resources/translations/de.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,10 @@
243243
"settings.restart_required": "Bitte TagStudio neustarten, um Änderungen anzuwenden.",
244244
"settings.show_filenames_in_grid": "Dateinamen in Raster darstellen",
245245
"settings.show_recent_libraries": "Zuletzt verwendete Bibliotheken anzeigen",
246+
"settings.tag_click_action.label": "Tag Klick Aktion",
247+
"settings.tag_click_action.add_to_search": "Tag zu Suche hinzufügen",
248+
"settings.tag_click_action.open_edit": "Tag bearbeiten",
249+
"settings.tag_click_action.set_search": "Nach Tag suchen",
246250
"settings.theme.dark": "Dunkel",
247251
"settings.theme.label": "Design:",
248252
"settings.theme.light": "Hell",

src/tagstudio/resources/translations/en.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,10 @@
243243
"settings.restart_required": "Please restart TagStudio for changes to take effect.",
244244
"settings.show_filenames_in_grid": "Show Filenames in Grid",
245245
"settings.show_recent_libraries": "Show Recent Libraries",
246+
"settings.tag_click_action.label": "Tag Click Action",
247+
"settings.tag_click_action.add_to_search": "Add Tag to Search",
248+
"settings.tag_click_action.open_edit": "Edit Tag",
249+
"settings.tag_click_action.set_search": "Search for Tag",
246250
"settings.theme.dark": "Dark",
247251
"settings.theme.label": "Theme:",
248252
"settings.theme.light": "Light",

0 commit comments

Comments
 (0)