Add the ability to create tags when adding a tag to a file (#262)

* Add the ability to create tags when adding a tag to a file.

* ui: unify tag widget appearance

Unify the tag widget appearance and remove unnecessary "*1" multiplication.

* refactor: change some var names & add docstrings

* feat: edit panel is opened before adding tag

* feat(ui): focus save button on add panel

Focus the save button on the Add Tag panel. This allows the user to promptly hit enter to add the tag as-is.

---------

Co-authored-by: bjorn-out <b.g.out@uva.nl>
Co-authored-by: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
This commit is contained in:
Björn Out
2024-08-28 06:59:21 +02:00
committed by GitHub
parent fda6077b20
commit 5ac40f5b11
4 changed files with 125 additions and 73 deletions

View File

@@ -5,30 +5,28 @@
import logging
from PySide6.QtCore import Signal, Qt
from PySide6.QtCore import Qt, Signal
from PySide6.QtWidgets import (
QWidget,
QVBoxLayout,
QLabel,
QPushButton,
QLineEdit,
QScrollArea,
QFrame,
QTextEdit,
QComboBox,
QFrame,
QLabel,
QLineEdit,
QPushButton,
QScrollArea,
QTextEdit,
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 @@ logging.basicConfig(format="%(message)s", level=logging.INFO)
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 @@ class BuildTagPanel(PanelWidget):
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 @@ class BuildTagPanel(PanelWidget):
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):

View File

@@ -342,8 +342,8 @@ class ModifiedTagWidget(
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;"

View File

@@ -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,
QHBoxLayout,
QPushButton,
QLineEdit,
QScrollArea,
QFrame,
QHBoxLayout,
QLineEdit,
QPushButton,
QScrollArea,
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 @@ class TagSearchPanel(PanelWidget):
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 @@ class TagSearchPanel(PanelWidget):
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 @@ class TagSearchPanel(PanelWidget):
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()

View File

@@ -76,7 +76,7 @@ class TagBoxWidget(FieldWidget):
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