Skip to content

Commit 0cdb1a8

Browse files
ui: keyboard navigation for editing tags (#407)
* build_tag modal: make Tag Title selected when opening modal * build_tag modal: Tab now changes focus when aliases field is in focus * fix: unpredictable tab order in build tag modal * feat: implement proper tab order * ci: fix mypy errors * fix: merge issue 1 * fix: merge issue 2 * refactor: TagDatabasePanel now inherits from TagSearchPanel for code deduplication * Revert "refactor: TagDatabasePanel now inherits from TagSearchPanel for code deduplication" This reverts commit fac589b.
1 parent 5caf869 commit 0cdb1a8

File tree

4 files changed

+40
-10
lines changed

4 files changed

+40
-10
lines changed

tagstudio/src/qt/modals/build_tag.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ def __init__(self, library: Library, tag: Tag | None = None):
112112
self.aliases_table.verticalHeader().setVisible(False)
113113
self.aliases_table.horizontalHeader().setStretchLastSection(True)
114114
self.aliases_table.setColumnWidth(0, 35)
115+
self.aliases_table.setTabKeyNavigation(False)
116+
self.aliases_table.setFocusPolicy(Qt.FocusPolicy.NoFocus)
115117

116118
self.alias_add_button = QPushButton()
117119
self.alias_add_button.setText("+")
@@ -136,6 +138,7 @@ def __init__(self, library: Library, tag: Tag | None = None):
136138
self.parent_tags_scroll_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
137139

138140
self.scroll_area = QScrollArea()
141+
self.scroll_area.setFocusPolicy(Qt.FocusPolicy.NoFocus)
139142
# self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
140143
self.scroll_area.setWidgetResizable(True)
141144
self.scroll_area.setFrameShadow(QFrame.Shadow.Plain)
@@ -239,8 +242,6 @@ def __init__(self, library: Library, tag: Tag | None = None):
239242
self.new_item_id = sys.maxsize
240243

241244
self.set_tag(tag or Tag(name=Translations["tag.new"]))
242-
if tag is None:
243-
self.name_field.selectAll()
244245

245246
def backspace(self):
246247
focused_widget = QApplication.focusWidget()
@@ -305,6 +306,7 @@ def set_parent_tags(self):
305306
while self.parent_tags_scroll_layout.itemAt(0):
306307
self.parent_tags_scroll_layout.takeAt(0).widget().deleteLater()
307308

309+
last: QWidget = self.aliases_table.cellWidget(self.aliases_table.rowCount() - 1, 1)
308310
c = QWidget()
309311
layout = QVBoxLayout(c)
310312
layout.setContentsMargins(0, 0, 0, 0)
@@ -314,6 +316,10 @@ def set_parent_tags(self):
314316
tw = TagWidget(tag, has_edit=False, has_remove=True)
315317
tw.on_remove.connect(lambda t=tag_id: self.remove_parent_tag_callback(t))
316318
layout.addWidget(tw)
319+
self.setTabOrder(last, tw.bg_button)
320+
last = tw.bg_button
321+
self.setTabOrder(last, self.name_field)
322+
317323
self.parent_tags_scroll_layout.addWidget(c)
318324

319325
def add_aliases(self):
@@ -349,6 +355,7 @@ def _set_aliases(self):
349355

350356
self.alias_names.clear()
351357

358+
last: QWidget = self.panel_save_button or self.color_field
352359
for alias_id in self.alias_ids:
353360
alias = self.lib.get_alias(self.tag.id, alias_id)
354361

@@ -375,6 +382,12 @@ def _set_aliases(self):
375382
self.aliases_table.setCellWidget(row, 1, new_item)
376383
self.aliases_table.setCellWidget(row, 0, remove_btn)
377384

385+
self.setTabOrder(last, self.aliases_table.cellWidget(row, 1))
386+
self.setTabOrder(
387+
self.aliases_table.cellWidget(row, 1), self.aliases_table.cellWidget(row, 0)
388+
)
389+
last = self.aliases_table.cellWidget(row, 0)
390+
378391
def _alias_name_change(self, item: CustomTableItem):
379392
self.new_alias_names[item.id] = item.text()
380393

@@ -424,3 +437,14 @@ def build_tag(self) -> Tag:
424437

425438
logger.info("built tag", tag=tag)
426439
return tag
440+
441+
def parent_post_init(self):
442+
self.setTabOrder(self.name_field, self.shorthand_field)
443+
self.setTabOrder(self.shorthand_field, self.alias_add_button)
444+
self.setTabOrder(self.alias_add_button, self.parent_tags_add_button)
445+
self.setTabOrder(self.parent_tags_add_button, self.color_field)
446+
self.setTabOrder(self.color_field, self.panel_cancel_button)
447+
self.setTabOrder(self.panel_cancel_button, self.panel_save_button)
448+
self.setTabOrder(self.panel_save_button, self.aliases_table.cellWidget(0, 1))
449+
self.name_field.selectAll()
450+
self.name_field.setFocus()

tagstudio/src/qt/ts_qt.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -706,14 +706,14 @@ def backup_library(self):
706706
)
707707

708708
def add_tag_action_callback(self):
709+
panel = BuildTagPanel(self.lib)
709710
self.modal = PanelModal(
710-
BuildTagPanel(self.lib),
711+
panel,
711712
has_save=True,
712713
)
713714
Translations.translate_with_setter(self.modal.setTitle, "tag.new")
714715
Translations.translate_with_setter(self.modal.setWindowTitle, "tag.add")
715716

716-
panel: BuildTagPanel = self.modal.widget
717717
self.modal.saved.connect(
718718
lambda: (
719719
self.lib.add_tag(

tagstudio/src/qt/widgets/panel.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class PanelModal(QWidget):
1616
# figure out what you want from this.
1717
def __init__(
1818
self,
19-
widget,
19+
widget: "PanelWidget",
2020
title: str = "",
2121
window_title: str = "",
2222
done_callback: Callable | None = None,
@@ -89,8 +89,10 @@ def __init__(
8989

9090
self.root_layout.addWidget(self.title_widget)
9191
self.root_layout.addWidget(widget)
92+
widget.parent_modal = self
9293
self.root_layout.setStretch(1, 2)
9394
self.root_layout.addWidget(self.button_container)
95+
widget.parent_post_init()
9496

9597
def closeEvent(self, event): # noqa: N802
9698
self.done_button.click()
@@ -104,6 +106,7 @@ class PanelWidget(QWidget):
104106
"""Used for widgets that go in a modal panel, ex. for editing or searching."""
105107

106108
done = Signal()
109+
parent_modal: PanelModal = None
107110
panel_save_button: QPushButton | None = None
108111
panel_cancel_button: QPushButton | None = None
109112
panel_done_button: QPushButton | None = None
@@ -117,5 +120,8 @@ def get_content(self) -> str:
117120
def reset(self):
118121
pass
119122

123+
def parent_post_init(self):
124+
pass
125+
120126
def add_callback(self, callback: Callable, event: str = "returnPressed"):
121127
logging.warning(f"add_callback not implemented for {self.__class__.__name__}")

tagstudio/src/qt/widgets/preview_panel.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@ def __init__(self, library: Library, driver: "QtDriver"):
7373
self.file_attrs = FileAttributes(library, driver)
7474
self.fields = FieldContainers(library, driver)
7575

76-
tag_search_panel = TagSearchPanel(self.driver.lib)
76+
self.tag_search_panel = TagSearchPanel(self.driver.lib)
7777
self.add_tag_modal = PanelModal(
78-
tag_search_panel, Translations.translate_formatted("tag.add.plural")
78+
self.tag_search_panel, Translations.translate_formatted("tag.add.plural")
7979
)
8080
Translations.translate_with_setter(self.add_tag_modal.setWindowTitle, "tag.add.plural")
8181

@@ -195,10 +195,10 @@ def update_add_field_button(self, entry_id: int | None = None):
195195

196196
def update_add_tag_button(self, entry_id: int = None):
197197
with catch_warnings(record=True):
198-
self.add_tag_modal.widget.tag_chosen.disconnect()
198+
self.tag_search_panel.tag_chosen.disconnect()
199199
self.add_tag_button.clicked.disconnect()
200200

201-
self.add_tag_modal.widget.tag_chosen.connect(
201+
self.tag_search_panel.tag_chosen.connect(
202202
lambda t: (
203203
self.fields.add_tags_to_selected(t),
204204
(self.fields.update_from_entry(entry_id) if entry_id else ()),
@@ -207,7 +207,7 @@ def update_add_tag_button(self, entry_id: int = None):
207207

208208
self.add_tag_button.clicked.connect(
209209
lambda: (
210-
self.add_tag_modal.widget.update_tags(),
210+
self.tag_search_panel.update_tags(),
211211
self.add_tag_modal.show(),
212212
)
213213
)

0 commit comments

Comments
 (0)