diff --git a/REUSE.toml b/REUSE.toml index 70e2cf7c..1d39078d 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -45,6 +45,12 @@ SPDX-FileCopyrightText = "(c) 2026 Boxicons" SPDX-License-Identifier = "MIT" [[annotations]] -path = ["src/tagstudio/resources/qt/images/dupe_file_stat.png"] +path = [ + "src/tagstudio/resources/qt/images/dupe_file_stat.png", + "src/tagstudio/resources/qt/images/hint_field_add.png", + "src/tagstudio/resources/qt/images/hint_field_create.png", + "src/tagstudio/resources/qt/images/hint_tag_add.png", + "src/tagstudio/resources/qt/images/hint_tag_create.png", +] SPDX-FileCopyrightText = "(c) github:google/material-design-icons Contributors" SPDX-License-Identifier = "Apache-2.0" diff --git a/docs/assets/tag_field_bars/field_bar_search.png b/docs/assets/tag_field_bars/field_bar_search.png index 10f4fca6..c7c8063c 100644 Binary files a/docs/assets/tag_field_bars/field_bar_search.png and b/docs/assets/tag_field_bars/field_bar_search.png differ diff --git a/docs/assets/tag_field_bars/tag_bar_new.png b/docs/assets/tag_field_bars/tag_bar_new.png index 7327d906..3a8b83f1 100644 Binary files a/docs/assets/tag_field_bars/tag_bar_new.png and b/docs/assets/tag_field_bars/tag_bar_new.png differ diff --git a/docs/assets/tag_field_bars/tag_bar_search_match.png b/docs/assets/tag_field_bars/tag_bar_search_match.png index 3e993c4d..24431ccc 100644 Binary files a/docs/assets/tag_field_bars/tag_bar_search_match.png and b/docs/assets/tag_field_bars/tag_bar_search_match.png differ diff --git a/docs/assets/tag_field_bars/tag_bar_shift_off.png b/docs/assets/tag_field_bars/tag_bar_shift_off.png index a52deb6b..32b16034 100644 Binary files a/docs/assets/tag_field_bars/tag_bar_shift_off.png and b/docs/assets/tag_field_bars/tag_bar_shift_off.png differ diff --git a/docs/assets/tag_field_bars/tag_bar_shift_on.png b/docs/assets/tag_field_bars/tag_bar_shift_on.png index be3179a0..f3d72833 100644 Binary files a/docs/assets/tag_field_bars/tag_bar_shift_on.png and b/docs/assets/tag_field_bars/tag_bar_shift_on.png differ diff --git a/src/tagstudio/qt/controllers/field_suggest_box.py b/src/tagstudio/qt/controllers/field_suggest_box.py index 42250dfe..54fc36d8 100644 --- a/src/tagstudio/qt/controllers/field_suggest_box.py +++ b/src/tagstudio/qt/controllers/field_suggest_box.py @@ -86,6 +86,24 @@ class FieldSuggestBox(SuggestBox[BaseFieldTemplate]): else: return ([], []) + @override + def _on_shift_held(self, held: bool) -> None: + if held: + self.set_hint_icon(self._rm.hint_field_create) + else: + self._update_hint_icon() + + return super()._on_shift_held(held) + + @override + def _update_hint_icon(self) -> None: + if self.layout().search_field.text() and len(self._search_results) > 0: + self.set_hint_icon(self._rm.hint_field_add) + elif self.layout().search_field.text(): + self.set_hint_icon(self._rm.hint_field_create) + else: + self.set_hint_icon(None) + @override def _set_item_widget(self, item: BaseFieldTemplate | None, index: int) -> None: """Set the field template of a field template widget at a specific index.""" diff --git a/src/tagstudio/qt/controllers/suggest_box.py b/src/tagstudio/qt/controllers/suggest_box.py index 7b3bd13f..48de05ef 100644 --- a/src/tagstudio/qt/controllers/suggest_box.py +++ b/src/tagstudio/qt/controllers/suggest_box.py @@ -4,8 +4,9 @@ from typing import Any, override import structlog +from PIL import Image, ImageQt from PySide6.QtCore import Signal -from PySide6.QtGui import QShowEvent +from PySide6.QtGui import QPixmap, QShowEvent from PySide6.QtWidgets import QGraphicsOpacityEffect, QWidget from tagstudio.core.library.alchemy.library import Library @@ -13,6 +14,8 @@ from tagstudio.qt.controllers.autofill_line_edit import QtCore, QtGui from tagstudio.qt.controllers.modal_content import ModalContent from tagstudio.qt.controllers.underlined_widget import UnderlinedWidget from tagstudio.qt.global_settings import GlobalSettings +from tagstudio.qt.helpers.color_overlay import auto_theme_overlay +from tagstudio.qt.resource_manager import ResourceManager from tagstudio.qt.views.stylesheets.stylesheets import ( autofill_line_edit_style, autofill_line_edit_top_style, @@ -50,6 +53,7 @@ class SuggestBox[T](QWidget): super().__init__() self._lib = library self._settings = settings + self._rm = ResourceManager() self._limit = 5 self._is_shift_held = False self._search_results: list[T] = [] @@ -81,6 +85,13 @@ class SuggestBox[T](QWidget): self.layout().search_field.shift_holding.connect(lambda held: self._on_shift_held(held)) + def set_hint_icon(self, icon: Image.Image | None) -> None: + if icon: + pixmap = QPixmap.fromImage(ImageQt.ImageQt(auto_theme_overlay(icon))) + self.layout().hint_icon_action.setIcon(pixmap) + else: + self.layout().hint_icon_action.setIcon(QPixmap()) + def _on_shift_held(self, held: bool) -> None: if held: self._is_shift_held = True @@ -105,6 +116,7 @@ class SuggestBox[T](QWidget): def _on_search_query_changed(self, query: str) -> None: self._update_items(query) + self._update_hint_icon() def _on_search_query_submitted(self, query: str, always_create: bool = False) -> None: # Focus search field if no query @@ -137,6 +149,9 @@ class SuggestBox[T](QWidget): def _is_excluded(self, item: T) -> bool: return _item_id(item) in self.excluded + def _update_hint_icon(self) -> None: + raise NotImplementedError() + def _update_items(self, query: str | None = None) -> None: """Update the item list given a search query.""" logger.info("[SearchPanel] Updating items", limit=self._limit) diff --git a/src/tagstudio/qt/controllers/tag_suggest_box.py b/src/tagstudio/qt/controllers/tag_suggest_box.py index 27f9030a..bf38b7ae 100644 --- a/src/tagstudio/qt/controllers/tag_suggest_box.py +++ b/src/tagstudio/qt/controllers/tag_suggest_box.py @@ -91,6 +91,24 @@ class TagSuggestBox(SuggestBox[Tag]): self.item_chosen.emit(item.id) self.done.emit() + @override + def _on_shift_held(self, held: bool) -> None: + if held: + self.set_hint_icon(self._rm.hint_tag_create) + else: + self._update_hint_icon() + + return super()._on_shift_held(held) + + @override + def _update_hint_icon(self) -> None: + if self.layout().search_field.text() and len(self._search_results) > 0: + self.set_hint_icon(self._rm.hint_tag_add) + elif self.layout().search_field.text(): + self.set_hint_icon(self._rm.hint_tag_create) + else: + self.set_hint_icon(None) + @override def _search_items(self, query: str) -> tuple[list[Tag], list[Tag]]: if query != "": diff --git a/src/tagstudio/qt/resource_manager.pyi b/src/tagstudio/qt/resource_manager.pyi index 986ed884..46e6ca58 100644 --- a/src/tagstudio/qt/resource_manager.pyi +++ b/src/tagstudio/qt/resource_manager.pyi @@ -36,6 +36,10 @@ class ResourceManager: edit: Image.Image file_generic: Image.Image font: Image.Image + hint_field_add: Image.Image + hint_field_create: Image.Image + hint_tag_add: Image.Image + hint_tag_create: Image.Image icon: Image.Image ignored_stat: Image.Image ignored: Image.Image diff --git a/src/tagstudio/qt/resources.json b/src/tagstudio/qt/resources.json index 6533ce38..3fbdefba 100644 --- a/src/tagstudio/qt/resources.json +++ b/src/tagstudio/qt/resources.json @@ -71,6 +71,22 @@ "mode": "pil", "path": "qt/images/file_icons/font.png" }, + "hint_field_add": { + "mode": "pil", + "path": "qt/images/hint_field_add.png" + }, + "hint_field_create": { + "mode": "pil", + "path": "qt/images/hint_field_create.png" + }, + "hint_tag_add": { + "mode": "pil", + "path": "qt/images/hint_tag_add.png" + }, + "hint_tag_create": { + "mode": "pil", + "path": "qt/images/hint_tag_create.png" + }, "icon": { "mode": "pil", "path": "icon.png" diff --git a/src/tagstudio/qt/views/suggest_box_view.py b/src/tagstudio/qt/views/suggest_box_view.py index 39184e61..763b9744 100644 --- a/src/tagstudio/qt/views/suggest_box_view.py +++ b/src/tagstudio/qt/views/suggest_box_view.py @@ -4,6 +4,7 @@ import structlog from PySide6.QtCore import Qt +from PySide6.QtGui import QPixmap from PySide6.QtWidgets import QFrame, QHBoxLayout, QScrollArea, QSizePolicy, QVBoxLayout, QWidget from tagstudio.qt.controllers.autofill_line_edit import AutofillLineEdit @@ -18,6 +19,7 @@ logger = structlog.get_logger(__name__) class SuggestBoxView(QVBoxLayout): def __init__(self, placeholder_text: str = "") -> None: super().__init__() + self.setContentsMargins(0, 0, 0, 0) self.setSpacing(0) @@ -70,6 +72,9 @@ class SuggestBoxView(QVBoxLayout): self.search_field.setObjectName("search_field") self.search_field.setMinimumHeight(28) self.search_field.setPlaceholderText(placeholder_text) + self.hint_icon_action = self.search_field.addAction( + QPixmap(), AutofillLineEdit.ActionPosition.TrailingPosition + ) self.scroll_area.setFocusProxy(self.search_field) # Finalize Layout diff --git a/src/tagstudio/resources/qt/images/hint_field_add.png b/src/tagstudio/resources/qt/images/hint_field_add.png new file mode 100644 index 00000000..dabe8d92 Binary files /dev/null and b/src/tagstudio/resources/qt/images/hint_field_add.png differ diff --git a/src/tagstudio/resources/qt/images/hint_field_create.png b/src/tagstudio/resources/qt/images/hint_field_create.png new file mode 100644 index 00000000..f8ee8f19 Binary files /dev/null and b/src/tagstudio/resources/qt/images/hint_field_create.png differ diff --git a/src/tagstudio/resources/qt/images/hint_tag_add.png b/src/tagstudio/resources/qt/images/hint_tag_add.png new file mode 100644 index 00000000..50a1e89a Binary files /dev/null and b/src/tagstudio/resources/qt/images/hint_tag_add.png differ diff --git a/src/tagstudio/resources/qt/images/hint_tag_create.png b/src/tagstudio/resources/qt/images/hint_tag_create.png new file mode 100644 index 00000000..10c94b30 Binary files /dev/null and b/src/tagstudio/resources/qt/images/hint_tag_create.png differ