diff --git a/tagstudio/src/qt/modals/build_tag.py b/tagstudio/src/qt/modals/build_tag.py index c7fa543e1..bebb3d3ef 100644 --- a/tagstudio/src/qt/modals/build_tag.py +++ b/tagstudio/src/qt/modals/build_tag.py @@ -5,30 +5,28 @@ import logging -from PySide6.QtCore import Signal, Qt +from PySide6.QtCore import Qt, Signal from PySide6.QtWidgets import ( - QWidget, - QVBoxLayout, + QComboBox, + QFrame, QLabel, - QPushButton, QLineEdit, + QPushButton, QScrollArea, - QFrame, QTextEdit, - QComboBox, + QVBoxLayout, + QWidget, ) - +from src.core.constants import TAG_COLORS from src.core.library import Library, Tag from src.core.palette import ColorType, get_tag_color -from src.core.constants import TAG_COLORS -from src.qt.widgets.panel import PanelWidget, PanelModal -from src.qt.widgets.tag import TagWidget from src.qt.modals.tag_search import TagSearchPanel +from src.qt.widgets.panel import PanelModal, PanelWidget +from src.qt.widgets.tag import TagWidget - -ERROR = f"[ERROR]" -WARNING = f"[WARNING]" -INFO = f"[INFO]" +ERROR = "[ERROR]" +WARNING = "[WARNING]" +INFO = "[INFO]" logging.basicConfig(format="%(message)s", level=logging.INFO) @@ -36,7 +34,7 @@ class BuildTagPanel(PanelWidget): on_edit = Signal(Tag) - def __init__(self, library, tag_id: int = -1): + def __init__(self, library, tag_id: int = -1, tag_name: str = "New Tag"): super().__init__() self.lib: Library = library # self.callback = callback @@ -117,7 +115,7 @@ def __init__(self, library, tag_id: int = -1): self.subtags_add_button = QPushButton() self.subtags_add_button.setText("+") tsp = TagSearchPanel(self.lib) - tsp.tag_chosen.connect(lambda x: self.add_subtag_callback(x)) + tsp.tag_created.connect(lambda x: self.add_subtag_callback(x)) self.add_tag_modal = PanelModal(tsp, "Add Parent Tags", "Add Parent Tags") self.subtags_add_button.clicked.connect(self.add_tag_modal.show) self.subtags_layout.addWidget(self.subtags_add_button) @@ -163,7 +161,7 @@ def __init__(self, library, tag_id: int = -1): if tag_id >= 0: self.tag = self.lib.get_tag(tag_id) else: - self.tag = Tag(-1, "New Tag", "", [], [], "") + self.tag = Tag(-1, tag_name, "", [], [], "") self.set_tag(self.tag) def add_subtag_callback(self, tag_id: int): diff --git a/tagstudio/src/qt/modals/folders_to_tags.py b/tagstudio/src/qt/modals/folders_to_tags.py index 2486a76e9..33d686a95 100644 --- a/tagstudio/src/qt/modals/folders_to_tags.py +++ b/tagstudio/src/qt/modals/folders_to_tags.py @@ -342,8 +342,8 @@ def __init__(self, tag: Tag, parentTag: Tag) -> None: f"font-weight: 600;" f"border-color:{get_tag_color(ColorType.BORDER, tag.color)};" f"border-radius: 6px;" - f"border-style:inset;" - f"border-width: {math.ceil(1*self.devicePixelRatio())}px;" + f"border-style:solid;" + f"border-width: {math.ceil(self.devicePixelRatio())}px;" f"padding-right: 4px;" f"padding-bottom: 1px;" f"padding-left: 4px;" diff --git a/tagstudio/src/qt/modals/tag_search.py b/tagstudio/src/qt/modals/tag_search.py index a7740925b..fc9085281 100644 --- a/tagstudio/src/qt/modals/tag_search.py +++ b/tagstudio/src/qt/modals/tag_search.py @@ -5,42 +5,38 @@ import logging import math - -from PySide6.QtCore import Signal, Qt, QSize +import src.qt.modals.build_tag as bt +from PySide6.QtCore import QSize, Qt, Signal from PySide6.QtWidgets import ( - QWidget, - QVBoxLayout, + QFrame, QHBoxLayout, - QPushButton, QLineEdit, + QPushButton, QScrollArea, - QFrame, + QVBoxLayout, + QWidget, ) - from src.core.constants import TAG_COLORS from src.core.library import Library from src.core.palette import ColorType, get_tag_color -from src.qt.widgets.panel import PanelWidget +from src.qt.widgets.panel import PanelModal, PanelWidget from src.qt.widgets.tag import TagWidget - -ERROR = f"[ERROR]" -WARNING = f"[WARNING]" -INFO = f"[INFO]" +ERROR = "[ERROR]" +WARNING = "[WARNING]" +INFO = "[INFO]" logging.basicConfig(format="%(message)s", level=logging.INFO) class TagSearchPanel(PanelWidget): - tag_chosen = Signal(int) + tag_created = Signal(int) - def __init__(self, library): + def __init__(self, library: "Library"): super().__init__() self.lib: Library = library - # self.callback = callback - self.first_tag_id = None + self.first_tag_id: int | None = None self.tag_limit = 100 - # self.selected_tag: int = 0 self.setMinimumSize(300, 400) self.root_layout = QVBoxLayout(self) self.root_layout.setContentsMargins(6, 0, 6, 0) @@ -56,57 +52,38 @@ def __init__(self, library): lambda checked=False: self.on_return(self.search_field.text()) ) - # self.content_container = QWidget() - # self.content_layout = QHBoxLayout(self.content_container) - self.scroll_contents = QWidget() self.scroll_layout = QVBoxLayout(self.scroll_contents) self.scroll_layout.setContentsMargins(6, 0, 6, 0) self.scroll_layout.setAlignment(Qt.AlignmentFlag.AlignTop) self.scroll_area = QScrollArea() - # self.scroll_area.setStyleSheet('background: #000000;') self.scroll_area.setVerticalScrollBarPolicy( Qt.ScrollBarPolicy.ScrollBarAlwaysOn ) - # self.scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) self.scroll_area.setWidgetResizable(True) self.scroll_area.setFrameShadow(QFrame.Shadow.Plain) self.scroll_area.setFrameShape(QFrame.Shape.NoFrame) - # sa.setMaximumWidth(self.preview_size[0]) self.scroll_area.setWidget(self.scroll_contents) - # self.add_button = QPushButton() - # self.root_layout.addWidget(self.add_button) - # self.add_button.setText('Add Tag') - # # self.done_button.clicked.connect(lambda checked=False, x=1101: (callback(x), self.hide())) - # self.add_button.clicked.connect(lambda checked=False, x=1101: callback(x)) - # # self.setLayout(self.root_layout) - self.root_layout.addWidget(self.search_field) self.root_layout.addWidget(self.scroll_area) self.update_tags("") - # def reset(self): - # self.search_field.setText('') - # self.update_tags('') - # self.search_field.setFocus() - def on_return(self, text: str): if text and self.first_tag_id is not None: # callback(self.first_tag_id) - self.tag_chosen.emit(self.first_tag_id) + self.tag_created.emit(self.first_tag_id) self.search_field.setText("") self.update_tags() + elif text: + self.create_and_add_tag(text) + self.parentWidget().hide() else: - self.search_field.setFocus() self.parentWidget().hide() def update_tags(self, query: str = ""): - # for c in self.scroll_layout.children(): - # c.widget().deleteLater() while self.scroll_layout.count(): - # logging.info(f"I'm deleting { self.scroll_layout.itemAt(0).widget()}") self.scroll_layout.takeAt(0).widget().deleteLater() found_tags = self.lib.search_tags(query, include_cluster=True)[: self.tag_limit] @@ -153,10 +130,7 @@ def update_tags(self, query: str = ""): f"border-radius: 6px;" f"border-style:solid;" f"border-width: {math.ceil(1*self.devicePixelRatio())}px;" - # f'padding-top: 1.5px;' - # f'padding-right: 4px;' f"padding-bottom: 5px;" - # f'padding-left: 4px;' f"font-size: 20px;" f"}}" f"QPushButton::hover" @@ -167,15 +141,95 @@ def update_tags(self, query: str = ""): f"}}" ) - ab.clicked.connect(lambda checked=False, x=tag_id: self.tag_chosen.emit(x)) + ab.clicked.connect(lambda checked=False, x=tag_id: self.tag_created.emit(x)) l.addWidget(tw) l.addWidget(ab) self.scroll_layout.addWidget(c) + # Add a create tag button if a query is entered + if query: + c = self.create_tag_button(query) + self.scroll_layout.addWidget(c) + self.search_field.setFocus() - # def enterEvent(self, event: QEnterEvent) -> None: - # self.search_field.setFocus() - # return super().enterEvent(event) - # self.focusOutEvent + def create_tag_button(self, name: str) -> QWidget: + """Construct a "Create Tag" button. + + Args: + name (str): The name of the tag to give. + """ + c = QWidget() + l = QHBoxLayout(c) + l.setContentsMargins(0, 0, 0, 0) + l.setSpacing(3) + + create_and_add_button = QPushButton(self) + create_and_add_button.setFlat(True) + create_and_add_button.setText( + f"Create && Add \"{name.replace("&", "&&")}\" Tag" + ) + + inner_layout = QHBoxLayout() + inner_layout.setObjectName("innerLayout") + inner_layout.setContentsMargins(2, 2, 2, 2) + create_and_add_button.setLayout(inner_layout) + create_and_add_button.setMinimumSize(math.ceil(22 * 1.5), 22) + + create_and_add_button.setStyleSheet( + f"QPushButton{{" + f"background: {get_tag_color(ColorType.PRIMARY, "dark gray")};" + f"color: {get_tag_color(ColorType.TEXT, "dark gray")};" + f"font-weight: 600;" + f"border-color:{get_tag_color(ColorType.BORDER, "dark gray")};" + f"border-radius: 6px;" + f"border-style:solid;" + f"border-width: {math.ceil(self.devicePixelRatio())}px;" + f"padding-right: 4px;" + f"padding-bottom: 1px;" + f"padding-left: 4px;" + f"font-size: 13px" + f"}}" + f"QPushButton::hover{{" + f"border-color:{get_tag_color(ColorType.LIGHT_ACCENT, "dark gray")};" + f"}}" + ) + + create_and_add_button.clicked.connect( + lambda x=name: self.create_and_add_tag(name) + ) + l.addWidget(create_and_add_button) + + return c + + def create_and_add_tag(self, name: str): + """Open "Add Tag" panel to create and add a new tag with the given name. + + Args: + name (str): The name of the tag to give. + """ + self.add_tag_modal = PanelModal( + bt.BuildTagPanel(self.lib, tag_name=name), + "New Tag", + "Add Tag", + has_save=True, + ) + self.add_tag_modal.saved.connect(lambda n=name: self.on_tag_modal_saved(n)) + self.add_tag_modal.save_button.setFocus() + self.add_tag_modal.show() + + def on_tag_modal_saved(self, name): + """Callback for actions to perform when a new "Create & Add" tag is confirmed. + Args: + name (str): The name of the tag to give. + """ + panel: bt.BuildTagPanel = self.add_tag_modal.widget + self.tag_created.emit(self.lib.add_tag_to_library(panel.build_tag())) + self.add_tag_modal.hide() + self.update_tags(name) + + def showEvent(self, event): + # Clear search field and focus when showing modal + self.search_field.setText("") + self.search_field.setFocus() diff --git a/tagstudio/src/qt/widgets/tag_box.py b/tagstudio/src/qt/widgets/tag_box.py index 06b8b1fe5..2b1e2b424 100644 --- a/tagstudio/src/qt/widgets/tag_box.py +++ b/tagstudio/src/qt/widgets/tag_box.py @@ -76,7 +76,7 @@ def __init__( f"}}" ) tsp = TagSearchPanel(self.lib) - tsp.tag_chosen.connect(lambda x: self.add_tag_callback(x)) + tsp.tag_created.connect(lambda x: self.add_tag_callback(x)) self.add_modal = PanelModal(tsp, title, "Add Tags") self.add_button.clicked.connect( lambda: (tsp.update_tags(), self.add_modal.show()) # type: ignore