Skip to content

Commit

Permalink
Add the ability to create tags when adding a tag to a file (TagStudio…
Browse files Browse the repository at this point in the history
…Dev#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>
  • Loading branch information
3 people authored and CarterPillow committed Sep 7, 2024
1 parent 68f3b3e commit e2d83f2
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 68 deletions.
32 changes: 15 additions & 17 deletions tagstudio/src/qt/modals/build_tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,38 +5,36 @@

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)


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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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):
Expand Down
4 changes: 2 additions & 2 deletions tagstudio/src/qt/modals/folders_to_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -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;"
Expand Down
150 changes: 102 additions & 48 deletions tagstudio/src/qt/modals/tag_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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]
Expand Down Expand Up @@ -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"
Expand All @@ -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()
2 changes: 1 addition & 1 deletion tagstudio/src/qt/widgets/tag_box.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit e2d83f2

Please sign in to comment.