mirror of
https://github.com/TagStudioDev/TagStudio.git
synced 2026-02-01 07:39:10 +00:00
refactor(ui): recycle tag list in TagSearchPanel (#788)
* feat(ui): recycle tag list in `TagSearchPanel` * chore: address mypy warnings * fix: order results from sql before limiting * fix(ui): check for self.exclude before remaking sets * fix(ui): only init tag manager and file ext manager once * fix(ui:): remove redundant tag search panel updates * update code comments and docstrings * feat(ui): add tag view limit dropdown * ensure disconnection of file_extension_panel.saved
This commit is contained in:
committed by
GitHub
parent
26d3b1908b
commit
466af1e6a6
@@ -212,6 +212,7 @@
|
||||
"tag.add.plural": "Add Tags",
|
||||
"tag.add": "Add Tag",
|
||||
"tag.aliases": "Aliases",
|
||||
"tag.all_tags": "All Tags",
|
||||
"tag.choose_color": "Choose Tag Color",
|
||||
"tag.color": "Color",
|
||||
"tag.confirm_delete": "Are you sure you want to delete the tag \"{tag_name}\"?",
|
||||
@@ -228,6 +229,7 @@
|
||||
"tag.search_for_tag": "Search for Tag",
|
||||
"tag.shorthand": "Shorthand",
|
||||
"tag.tag_name_required": "Tag Name (Required)",
|
||||
"tag.view_limit": "View Limit:",
|
||||
"view.size.0": "Mini",
|
||||
"view.size.1": "Small",
|
||||
"view.size.2": "Medium",
|
||||
|
||||
@@ -765,16 +765,16 @@ class Library:
|
||||
|
||||
return res
|
||||
|
||||
def search_tags(self, name: str | None) -> list[set[Tag]]:
|
||||
def search_tags(self, name: str | None, limit: int = 100) -> list[set[Tag]]:
|
||||
"""Return a list of Tag records matching the query."""
|
||||
tag_limit = 100
|
||||
|
||||
with Session(self.engine) as session:
|
||||
query = select(Tag).outerjoin(TagAlias)
|
||||
query = select(Tag).outerjoin(TagAlias).order_by(func.lower(Tag.name))
|
||||
query = query.options(
|
||||
selectinload(Tag.parent_tags),
|
||||
selectinload(Tag.aliases),
|
||||
).limit(tag_limit)
|
||||
)
|
||||
if limit > 0:
|
||||
query = query.limit(limit)
|
||||
|
||||
if name:
|
||||
query = query.where(
|
||||
@@ -806,6 +806,7 @@ class Library:
|
||||
logger.info(
|
||||
"searching tags",
|
||||
search=name,
|
||||
limit=limit,
|
||||
statement=str(query),
|
||||
results=len(res),
|
||||
)
|
||||
|
||||
@@ -18,19 +18,19 @@ logger = structlog.get_logger(__name__)
|
||||
|
||||
# TODO: Once this class is removed, the `is_tag_chooser` option of `TagSearchPanel`
|
||||
# will most likely be enabled in every case
|
||||
# and the possibilty of disabling it can therefore be removed
|
||||
# and the possibility of disabling it can therefore be removed
|
||||
|
||||
|
||||
class TagDatabasePanel(TagSearchPanel):
|
||||
def __init__(self, library: Library):
|
||||
def __init__(self, driver, library: Library):
|
||||
super().__init__(library, is_tag_chooser=False)
|
||||
self.driver = driver
|
||||
|
||||
self.create_tag_button = QPushButton()
|
||||
Translations.translate_qobject(self.create_tag_button, "tag.create")
|
||||
self.create_tag_button.clicked.connect(lambda: self.build_tag(self.search_field.text()))
|
||||
|
||||
self.root_layout.addWidget(self.create_tag_button)
|
||||
self.update_tags()
|
||||
|
||||
def build_tag(self, name: str):
|
||||
panel = BuildTagPanel(self.lib)
|
||||
@@ -39,7 +39,7 @@ class TagDatabasePanel(TagSearchPanel):
|
||||
has_save=True,
|
||||
)
|
||||
Translations.translate_with_setter(self.modal.setTitle, "tag.new")
|
||||
Translations.translate_with_setter(self.modal.setWindowTitle, "tag.add")
|
||||
Translations.translate_with_setter(self.modal.setWindowTitle, "tag.new")
|
||||
if name.strip():
|
||||
panel.name_field.setText(name)
|
||||
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio
|
||||
|
||||
|
||||
import contextlib
|
||||
import typing
|
||||
from warnings import catch_warnings
|
||||
|
||||
import src.qt.modals.build_tag as build_tag
|
||||
import structlog
|
||||
@@ -11,8 +13,10 @@ from PySide6 import QtCore, QtGui
|
||||
from PySide6.QtCore import QSize, Qt, Signal
|
||||
from PySide6.QtGui import QShowEvent
|
||||
from PySide6.QtWidgets import (
|
||||
QComboBox,
|
||||
QFrame,
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
QLineEdit,
|
||||
QPushButton,
|
||||
QScrollArea,
|
||||
@@ -21,7 +25,7 @@ from PySide6.QtWidgets import (
|
||||
)
|
||||
from src.core.constants import RESERVED_TAG_END, RESERVED_TAG_START
|
||||
from src.core.library import Library, Tag
|
||||
from src.core.library.alchemy.enums import TagColorEnum
|
||||
from src.core.library.alchemy.enums import FilterState, TagColorEnum
|
||||
from src.core.palette import ColorType, get_tag_color
|
||||
from src.qt.translations import Translations
|
||||
from src.qt.widgets.panel import PanelModal, PanelWidget
|
||||
@@ -44,6 +48,11 @@ class TagSearchPanel(PanelWidget):
|
||||
is_tag_chooser: bool
|
||||
exclude: list[int]
|
||||
|
||||
_limit_items: list[int | str] = [25, 50, 100, 250, 500, Translations["tag.all_tags"]]
|
||||
_default_limit_idx: int = 0 # 50 Tag Limit (Default)
|
||||
cur_limit_idx: int = _default_limit_idx
|
||||
tag_limit: int | str = _limit_items[_default_limit_idx]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
library: Library,
|
||||
@@ -52,14 +61,37 @@ class TagSearchPanel(PanelWidget):
|
||||
):
|
||||
super().__init__()
|
||||
self.lib = library
|
||||
self.driver = None
|
||||
self.exclude = exclude or []
|
||||
|
||||
self.is_tag_chooser = is_tag_chooser
|
||||
self.create_button_in_layout: bool = False
|
||||
|
||||
self.setMinimumSize(300, 400)
|
||||
self.root_layout = QVBoxLayout(self)
|
||||
self.root_layout.setContentsMargins(6, 0, 6, 0)
|
||||
|
||||
self.limit_container = QWidget()
|
||||
self.limit_layout = QHBoxLayout(self.limit_container)
|
||||
self.limit_layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.limit_layout.setSpacing(12)
|
||||
self.limit_layout.addStretch(1)
|
||||
|
||||
self.limit_title = QLabel()
|
||||
Translations.translate_qobject(self.limit_title, "tag.view_limit")
|
||||
self.limit_layout.addWidget(self.limit_title)
|
||||
|
||||
self.limit_combobox = QComboBox()
|
||||
self.limit_combobox.setEditable(False)
|
||||
self.limit_combobox.addItems([str(x) for x in TagSearchPanel._limit_items])
|
||||
self.limit_combobox.setCurrentIndex(TagSearchPanel._default_limit_idx)
|
||||
self.limit_combobox.currentIndexChanged.connect(self.update_limit)
|
||||
self.previous_limit: int = (
|
||||
TagSearchPanel.tag_limit if isinstance(TagSearchPanel.tag_limit, int) else -1
|
||||
)
|
||||
self.limit_layout.addWidget(self.limit_combobox)
|
||||
self.limit_layout.addStretch(1)
|
||||
|
||||
self.search_field = QLineEdit()
|
||||
self.search_field.setObjectName("searchField")
|
||||
self.search_field.setMinimumSize(QSize(0, 32))
|
||||
@@ -79,53 +111,19 @@ class TagSearchPanel(PanelWidget):
|
||||
self.scroll_area.setFrameShape(QFrame.Shape.NoFrame)
|
||||
self.scroll_area.setWidget(self.scroll_contents)
|
||||
|
||||
self.root_layout.addWidget(self.limit_container)
|
||||
self.root_layout.addWidget(self.search_field)
|
||||
self.root_layout.addWidget(self.scroll_area)
|
||||
|
||||
def __build_tag_widget(self, tag: Tag):
|
||||
has_remove_button = False
|
||||
if not self.is_tag_chooser:
|
||||
has_remove_button = tag.id not in range(RESERVED_TAG_START, RESERVED_TAG_END)
|
||||
|
||||
tag_widget = TagWidget(
|
||||
tag,
|
||||
library=self.lib,
|
||||
has_edit=True,
|
||||
has_remove=has_remove_button,
|
||||
)
|
||||
|
||||
tag_widget.on_edit.connect(lambda t=tag: self.edit_tag(t))
|
||||
tag_widget.on_remove.connect(lambda t=tag: self.remove_tag(t))
|
||||
|
||||
# NOTE: A solution to this would be to pass the driver to TagSearchPanel, however that
|
||||
# creates an exponential amount of work trying to fix the preexisting tests.
|
||||
|
||||
# tag_widget.search_for_tag_action.triggered.connect(
|
||||
# lambda checked=False, tag_id=tag.id: (
|
||||
# self.driver.main_window.searchField.setText(f"tag_id:{tag_id}"),
|
||||
# self.driver.filter_items(FilterState.from_tag_id(tag_id)),
|
||||
# )
|
||||
# )
|
||||
|
||||
tag_id = tag.id
|
||||
tag_widget.bg_button.clicked.connect(lambda: self.tag_chosen.emit(tag_id))
|
||||
return tag_widget
|
||||
|
||||
def build_create_tag_button(self, query: str | None):
|
||||
"""Constructs a Create Tag Button."""
|
||||
container = QWidget()
|
||||
row = QHBoxLayout(container)
|
||||
row.setContentsMargins(0, 0, 0, 0)
|
||||
row.setSpacing(3)
|
||||
def set_driver(self, driver):
|
||||
"""Set the QtDriver for this search panel. Used for main window operations."""
|
||||
self.driver = driver
|
||||
|
||||
def build_create_button(self, query: str | None):
|
||||
"""Constructs a "Create & Add Tag" QPushButton."""
|
||||
create_button = QPushButton(self)
|
||||
Translations.translate_qobject(create_button, "tag.create_add", query=query)
|
||||
create_button.setFlat(True)
|
||||
|
||||
inner_layout = QHBoxLayout()
|
||||
inner_layout.setObjectName("innerLayout")
|
||||
inner_layout.setContentsMargins(2, 2, 2, 2)
|
||||
create_button.setLayout(inner_layout)
|
||||
create_button.setMinimumSize(22, 22)
|
||||
|
||||
create_button.setStyleSheet(
|
||||
@@ -156,10 +154,7 @@ class TagSearchPanel(PanelWidget):
|
||||
f"}}"
|
||||
)
|
||||
|
||||
create_button.clicked.connect(lambda: self.create_and_add_tag(query))
|
||||
row.addWidget(create_button)
|
||||
|
||||
return container
|
||||
return create_button
|
||||
|
||||
def create_and_add_tag(self, name: str):
|
||||
"""Opens "Create Tag" panel to create and add a new tag with given name."""
|
||||
@@ -188,26 +183,34 @@ class TagSearchPanel(PanelWidget):
|
||||
|
||||
self.build_tag_modal.name_field.setText(name)
|
||||
self.add_tag_modal.saved.connect(on_tag_modal_saved)
|
||||
self.add_tag_modal.save_button.setFocus()
|
||||
self.add_tag_modal.show()
|
||||
|
||||
def update_tags(self, query: str | None = None):
|
||||
logger.info("[Tag Search Super Class] Updating Tags")
|
||||
"""Update the tag list given a search query."""
|
||||
logger.info("[TagSearchPanel] Updating Tags")
|
||||
|
||||
# TODO: Look at recycling rather than deleting and re-initializing
|
||||
while self.scroll_layout.count():
|
||||
self.scroll_layout.takeAt(0).widget().deleteLater()
|
||||
# Remove the "Create & Add" button if one exists
|
||||
create_button: QPushButton | None = None
|
||||
if self.create_button_in_layout and self.scroll_layout.count():
|
||||
create_button = self.scroll_layout.takeAt(self.scroll_layout.count() - 1).widget() # type: ignore
|
||||
create_button.deleteLater()
|
||||
self.create_button_in_layout = False
|
||||
|
||||
# Get results for the search query
|
||||
query_lower = "" if not query else query.lower()
|
||||
tag_results: list[set[Tag]] = self.lib.search_tags(name=query)
|
||||
tag_results[0] = {t for t in tag_results[0] if t.id not in self.exclude}
|
||||
tag_results[1] = {t for t in tag_results[1] if t.id not in self.exclude}
|
||||
# Only use the tag limit if it's an actual number (aka not "All Tags")
|
||||
tag_limit = TagSearchPanel.tag_limit if isinstance(TagSearchPanel.tag_limit, int) else -1
|
||||
tag_results: list[set[Tag]] = self.lib.search_tags(name=query, limit=tag_limit)
|
||||
if self.exclude:
|
||||
tag_results[0] = {t for t in tag_results[0] if t.id not in self.exclude}
|
||||
tag_results[1] = {t for t in tag_results[1] if t.id not in self.exclude}
|
||||
|
||||
# Sort and prioritize the results
|
||||
results_0 = list(tag_results[0])
|
||||
results_0.sort(key=lambda tag: tag.name.lower())
|
||||
results_1 = list(tag_results[1])
|
||||
results_1.sort(key=lambda tag: tag.name.lower())
|
||||
raw_results = list(results_0 + results_1)[:100]
|
||||
raw_results = list(results_0 + results_1)
|
||||
priority_results: set[Tag] = set()
|
||||
all_results: list[Tag] = []
|
||||
|
||||
@@ -219,18 +222,99 @@ class TagSearchPanel(PanelWidget):
|
||||
all_results = sorted(list(priority_results), key=lambda tag: len(tag.name)) + [
|
||||
r for r in raw_results if r not in priority_results
|
||||
]
|
||||
if tag_limit > 0:
|
||||
all_results = all_results[:tag_limit]
|
||||
|
||||
if all_results:
|
||||
self.first_tag_id = None
|
||||
self.first_tag_id = all_results[0].id if len(all_results) > 0 else all_results[0].id
|
||||
for tag in all_results:
|
||||
self.scroll_layout.addWidget(self.__build_tag_widget(tag))
|
||||
|
||||
else:
|
||||
self.first_tag_id = None
|
||||
|
||||
# Update every tag widget with the new search result data
|
||||
norm_previous = self.previous_limit if self.previous_limit > 0 else len(self.lib.tags)
|
||||
norm_limit = tag_limit if tag_limit > 0 else len(self.lib.tags)
|
||||
range_limit = max(norm_previous, norm_limit)
|
||||
for i in range(0, range_limit):
|
||||
tag = None
|
||||
with contextlib.suppress(IndexError):
|
||||
tag = all_results[i]
|
||||
self.set_tag_widget(tag=tag, index=i)
|
||||
self.previous_limit = tag_limit
|
||||
|
||||
# Add back the "Create & Add" button
|
||||
if query and query.strip():
|
||||
c = self.build_create_tag_button(query)
|
||||
self.scroll_layout.addWidget(c)
|
||||
cb: QPushButton = self.build_create_button(query)
|
||||
with catch_warnings(record=True):
|
||||
cb.clicked.disconnect()
|
||||
cb.clicked.connect(lambda: self.create_and_add_tag(query or ""))
|
||||
Translations.translate_qobject(cb, "tag.create_add", query=query)
|
||||
self.scroll_layout.addWidget(cb)
|
||||
self.create_button_in_layout = True
|
||||
|
||||
def set_tag_widget(self, tag: Tag | None, index: int):
|
||||
"""Set the tag of a tag widget at a specific index."""
|
||||
# Create any new tag widgets needed up to the given index
|
||||
if self.scroll_layout.count() <= index:
|
||||
while self.scroll_layout.count() <= index:
|
||||
new_tw = TagWidget(tag=None, has_edit=True, has_remove=True, library=self.lib)
|
||||
new_tw.setHidden(True)
|
||||
self.scroll_layout.addWidget(new_tw)
|
||||
|
||||
# Assign the tag to the widget at the given index.
|
||||
tag_widget: TagWidget = self.scroll_layout.itemAt(index).widget() # type: ignore
|
||||
tag_widget.set_tag(tag)
|
||||
|
||||
# Set tag widget viability and potentially return early
|
||||
tag_widget.setHidden(bool(not tag))
|
||||
if not tag:
|
||||
return
|
||||
|
||||
# Configure any other aspects of the tag widget
|
||||
has_remove_button = False
|
||||
if not self.is_tag_chooser:
|
||||
has_remove_button = tag.id not in range(RESERVED_TAG_START, RESERVED_TAG_END)
|
||||
tag_widget.has_remove = has_remove_button
|
||||
|
||||
with catch_warnings(record=True):
|
||||
tag_widget.on_edit.disconnect()
|
||||
tag_widget.on_remove.disconnect()
|
||||
tag_widget.bg_button.clicked.disconnect()
|
||||
|
||||
tag_id = tag.id
|
||||
tag_widget.on_edit.connect(lambda t=tag: self.edit_tag(t))
|
||||
tag_widget.on_remove.connect(lambda t=tag: self.remove_tag(t))
|
||||
tag_widget.bg_button.clicked.connect(lambda: self.tag_chosen.emit(tag_id))
|
||||
|
||||
if self.driver:
|
||||
tag_widget.search_for_tag_action.triggered.connect(
|
||||
lambda checked=False, tag_id=tag.id: (
|
||||
self.driver.main_window.searchField.setText(f"tag_id:{tag_id}"),
|
||||
self.driver.filter_items(FilterState.from_tag_id(tag_id)),
|
||||
)
|
||||
)
|
||||
tag_widget.search_for_tag_action.setEnabled(True)
|
||||
else:
|
||||
tag_widget.search_for_tag_action.setEnabled(False)
|
||||
|
||||
def update_limit(self, index: int):
|
||||
logger.info("[TagSearchPanel] Updating tag limit")
|
||||
TagSearchPanel.cur_limit_idx = index
|
||||
|
||||
if index < len(self._limit_items) - 1:
|
||||
TagSearchPanel.tag_limit = int(self._limit_items[index])
|
||||
else:
|
||||
TagSearchPanel.tag_limit = -1
|
||||
|
||||
# Method was called outside the limit_combobox callback
|
||||
if index != self.limit_combobox.currentIndex():
|
||||
self.limit_combobox.setCurrentIndex(index)
|
||||
|
||||
if self.previous_limit == TagSearchPanel.tag_limit:
|
||||
return
|
||||
|
||||
self.update_tags(self.search_field.text())
|
||||
|
||||
def on_return(self, text: str):
|
||||
if text:
|
||||
@@ -246,7 +330,9 @@ class TagSearchPanel(PanelWidget):
|
||||
self.parentWidget().hide()
|
||||
|
||||
def showEvent(self, event: QShowEvent) -> None: # noqa N802
|
||||
self.update_limit(TagSearchPanel.cur_limit_idx)
|
||||
self.update_tags()
|
||||
self.scroll_area.verticalScrollBar().setValue(0)
|
||||
self.search_field.setText("")
|
||||
self.search_field.setFocus()
|
||||
return super().showEvent(event)
|
||||
|
||||
@@ -16,6 +16,7 @@ import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
from queue import Queue
|
||||
from warnings import catch_warnings
|
||||
|
||||
# this import has side-effect of import PySide resources
|
||||
import src.qt.resources_rc # noqa: F401
|
||||
@@ -136,6 +137,8 @@ class QtDriver(DriverMixin, QObject):
|
||||
SIGTERM = Signal()
|
||||
|
||||
preview_panel: PreviewPanel
|
||||
tag_manager_panel: PanelModal
|
||||
file_extension_panel: PanelModal | None = None
|
||||
tag_search_panel: TagSearchPanel
|
||||
add_tag_modal: PanelModal
|
||||
|
||||
@@ -291,8 +294,20 @@ class QtDriver(DriverMixin, QObject):
|
||||
icon.addFile(str(icon_path))
|
||||
app.setWindowIcon(icon)
|
||||
|
||||
# Initialize the main window's tag search panel
|
||||
# Initialize the Tag Manager panel
|
||||
self.tag_manager_panel = PanelModal(
|
||||
widget=TagDatabasePanel(self, self.lib),
|
||||
done_callback=lambda: self.preview_panel.update_widgets(update_preview=False),
|
||||
has_save=False,
|
||||
)
|
||||
Translations.translate_with_setter(self.tag_manager_panel.setTitle, "tag_manager.title")
|
||||
Translations.translate_with_setter(
|
||||
self.tag_manager_panel.setWindowTitle, "tag_manager.title"
|
||||
)
|
||||
|
||||
# Initialize the Tag Search panel
|
||||
self.tag_search_panel = TagSearchPanel(self.lib, is_tag_chooser=True)
|
||||
self.tag_search_panel.set_driver(self)
|
||||
self.add_tag_modal = PanelModal(
|
||||
widget=self.tag_search_panel,
|
||||
title=Translations.translate_formatted("tag.add.plural"),
|
||||
@@ -487,13 +502,12 @@ class QtDriver(DriverMixin, QObject):
|
||||
Translations.translate_qobject(
|
||||
self.manage_file_ext_action, "menu.edit.manage_file_extensions"
|
||||
)
|
||||
self.manage_file_ext_action.triggered.connect(self.show_file_extension_modal)
|
||||
edit_menu.addAction(self.manage_file_ext_action)
|
||||
self.manage_file_ext_action.setEnabled(False)
|
||||
|
||||
self.tag_manager_action = QAction(menu_bar)
|
||||
Translations.translate_qobject(self.tag_manager_action, "menu.edit.manage_tags")
|
||||
self.tag_manager_action.triggered.connect(lambda: self.show_tag_manager())
|
||||
self.tag_manager_action.triggered.connect(self.tag_manager_panel.show)
|
||||
self.tag_manager_action.setShortcut(
|
||||
QtCore.QKeyCombination(
|
||||
QtCore.Qt.KeyboardModifier(QtCore.Qt.KeyboardModifier.ControlModifier),
|
||||
@@ -750,6 +764,27 @@ class QtDriver(DriverMixin, QObject):
|
||||
|
||||
self.splash.finish(self.main_window)
|
||||
|
||||
def init_file_extension_manager(self):
|
||||
"""Initialize the File Extension panel."""
|
||||
if self.file_extension_panel:
|
||||
with catch_warnings(record=True):
|
||||
self.manage_file_ext_action.triggered.disconnect()
|
||||
self.file_extension_panel.saved.disconnect()
|
||||
self.file_extension_panel.deleteLater()
|
||||
self.file_extension_panel = None
|
||||
|
||||
panel = FileExtensionModal(self.lib)
|
||||
self.file_extension_panel = PanelModal(
|
||||
panel,
|
||||
has_save=True,
|
||||
)
|
||||
Translations.translate_with_setter(self.file_extension_panel.setTitle, "ignore_list.title")
|
||||
Translations.translate_with_setter(
|
||||
self.file_extension_panel.setWindowTitle, "ignore_list.title"
|
||||
)
|
||||
self.file_extension_panel.saved.connect(lambda: (panel.save(), self.filter_items()))
|
||||
self.manage_file_ext_action.triggered.connect(self.file_extension_panel.show)
|
||||
|
||||
def show_grid_filenames(self, value: bool):
|
||||
for thumb in self.item_thumbs:
|
||||
thumb.set_filename_visibility(value)
|
||||
@@ -902,28 +937,6 @@ class QtDriver(DriverMixin, QObject):
|
||||
for entry_id in self.selected:
|
||||
self.lib.add_tags_to_entry(entry_id, tag_ids)
|
||||
|
||||
def show_tag_manager(self):
|
||||
self.modal = PanelModal(
|
||||
widget=TagDatabasePanel(self.lib),
|
||||
done_callback=lambda: self.preview_panel.update_widgets(update_preview=False),
|
||||
has_save=False,
|
||||
)
|
||||
Translations.translate_with_setter(self.modal.setTitle, "tag_manager.title")
|
||||
Translations.translate_with_setter(self.modal.setWindowTitle, "tag_manager.title")
|
||||
self.modal.show()
|
||||
|
||||
def show_file_extension_modal(self):
|
||||
panel = FileExtensionModal(self.lib)
|
||||
self.modal = PanelModal(
|
||||
panel,
|
||||
has_save=True,
|
||||
)
|
||||
Translations.translate_with_setter(self.modal.setTitle, "ignore_list.title")
|
||||
Translations.translate_with_setter(self.modal.setWindowTitle, "ignore_list.title")
|
||||
|
||||
self.modal.saved.connect(lambda: (panel.save(), self.filter_items()))
|
||||
self.modal.show()
|
||||
|
||||
def add_new_files_callback(self):
|
||||
"""Run when user initiates adding new files to the Library."""
|
||||
tracker = RefreshDirTracker(self.lib)
|
||||
@@ -1668,6 +1681,8 @@ class QtDriver(DriverMixin, QObject):
|
||||
)
|
||||
self.main_window.setAcceptDrops(True)
|
||||
|
||||
self.init_file_extension_manager()
|
||||
|
||||
self.selected.clear()
|
||||
self.set_select_actions_visibility()
|
||||
self.save_library_backup_action.setEnabled(True)
|
||||
|
||||
@@ -211,9 +211,4 @@ class PreviewPanel(QWidget):
|
||||
)
|
||||
)
|
||||
|
||||
self.add_tag_button.clicked.connect(
|
||||
lambda: (
|
||||
self.tag_search_panel.update_tags(),
|
||||
self.add_tag_modal.show(),
|
||||
)
|
||||
)
|
||||
self.add_tag_button.clicked.connect(self.add_tag_modal.show)
|
||||
|
||||
@@ -105,7 +105,7 @@ class TagWidget(QWidget):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
tag: Tag,
|
||||
tag: Tag | None,
|
||||
has_edit: bool,
|
||||
has_remove: bool,
|
||||
library: "Library | None" = None,
|
||||
@@ -127,10 +127,7 @@ class TagWidget(QWidget):
|
||||
|
||||
self.bg_button = QPushButton(self)
|
||||
self.bg_button.setFlat(True)
|
||||
if self.lib:
|
||||
self.bg_button.setText(escape_text(self.lib.tag_display_name(tag.id)))
|
||||
else:
|
||||
self.bg_button.setText(escape_text(tag.name))
|
||||
|
||||
if has_edit:
|
||||
edit_action = QAction(self)
|
||||
edit_action.setText(Translations.translate_formatted("generic.edit"))
|
||||
@@ -153,9 +150,38 @@ class TagWidget(QWidget):
|
||||
self.inner_layout.setObjectName("innerLayout")
|
||||
self.inner_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self.remove_button = QPushButton(self)
|
||||
self.remove_button.setFlat(True)
|
||||
self.remove_button.setText("–")
|
||||
self.remove_button.setHidden(True)
|
||||
self.remove_button.setMinimumSize(22, 22)
|
||||
self.remove_button.setMaximumSize(22, 22)
|
||||
self.remove_button.clicked.connect(self.on_remove.emit)
|
||||
self.remove_button.setHidden(True)
|
||||
self.inner_layout.addWidget(self.remove_button)
|
||||
self.inner_layout.addStretch(1)
|
||||
|
||||
self.bg_button.setLayout(self.inner_layout)
|
||||
self.bg_button.setMinimumSize(44, 22)
|
||||
|
||||
self.bg_button.setMinimumHeight(22)
|
||||
self.bg_button.setMaximumHeight(22)
|
||||
|
||||
self.base_layout.addWidget(self.bg_button)
|
||||
|
||||
# NOTE: Do this if you don't want the tag to stretch, like in a search.
|
||||
# self.bg_button.setMaximumWidth(self.bg_button.sizeHint().width())
|
||||
|
||||
self.bg_button.clicked.connect(self.on_click.emit)
|
||||
|
||||
self.set_tag(tag)
|
||||
|
||||
def set_tag(self, tag: Tag | None) -> None:
|
||||
self.tag = tag
|
||||
|
||||
if not tag:
|
||||
return
|
||||
|
||||
primary_color = get_primary_color(tag)
|
||||
border_color = (
|
||||
get_border_color(primary_color)
|
||||
@@ -200,55 +226,42 @@ class TagWidget(QWidget):
|
||||
f"outline:none;"
|
||||
f"}}"
|
||||
)
|
||||
self.bg_button.setMinimumHeight(22)
|
||||
self.bg_button.setMaximumHeight(22)
|
||||
|
||||
self.base_layout.addWidget(self.bg_button)
|
||||
self.remove_button.setStyleSheet(
|
||||
f"QPushButton{{"
|
||||
f"color: rgba{primary_color.toTuple()};"
|
||||
f"background: rgba{text_color.toTuple()};"
|
||||
f"font-weight: 800;"
|
||||
f"border-radius: 5px;"
|
||||
f"border-width: 4;"
|
||||
f"border-color: rgba(0,0,0,0);"
|
||||
f"padding-bottom: 4px;"
|
||||
f"font-size: 14px"
|
||||
f"}}"
|
||||
f"QPushButton::hover{{"
|
||||
f"background: rgba{primary_color.toTuple()};"
|
||||
f"color: rgba{text_color.toTuple()};"
|
||||
f"border-color: rgba{highlight_color.toTuple()};"
|
||||
f"border-width: 2;"
|
||||
f"border-radius: 6px;"
|
||||
f"}}"
|
||||
f"QPushButton::pressed{{"
|
||||
f"background: rgba{border_color.toTuple()};"
|
||||
f"color: rgba{highlight_color.toTuple()};"
|
||||
f"}}"
|
||||
f"QPushButton::focus{{"
|
||||
f"background: rgba{border_color.toTuple()};"
|
||||
f"outline:none;"
|
||||
f"}}"
|
||||
)
|
||||
|
||||
if has_remove:
|
||||
self.remove_button = QPushButton(self)
|
||||
self.remove_button.setFlat(True)
|
||||
self.remove_button.setText("–")
|
||||
self.remove_button.setHidden(True)
|
||||
self.remove_button.setStyleSheet(
|
||||
f"QPushButton{{"
|
||||
f"color: rgba{primary_color.toTuple()};"
|
||||
f"background: rgba{text_color.toTuple()};"
|
||||
f"font-weight: 800;"
|
||||
f"border-radius: 5px;"
|
||||
f"border-width: 4;"
|
||||
f"border-color: rgba(0,0,0,0);"
|
||||
f"padding-bottom: 4px;"
|
||||
f"font-size: 14px"
|
||||
f"}}"
|
||||
f"QPushButton::hover{{"
|
||||
f"background: rgba{primary_color.toTuple()};"
|
||||
f"color: rgba{text_color.toTuple()};"
|
||||
f"border-color: rgba{highlight_color.toTuple()};"
|
||||
f"border-width: 2;"
|
||||
f"border-radius: 6px;"
|
||||
f"}}"
|
||||
f"QPushButton::pressed{{"
|
||||
f"background: rgba{border_color.toTuple()};"
|
||||
f"color: rgba{highlight_color.toTuple()};"
|
||||
f"}}"
|
||||
f"QPushButton::focus{{"
|
||||
f"background: rgba{border_color.toTuple()};"
|
||||
f"outline:none;"
|
||||
f"}}"
|
||||
)
|
||||
self.remove_button.setMinimumSize(22, 22)
|
||||
self.remove_button.setMaximumSize(22, 22)
|
||||
self.remove_button.clicked.connect(self.on_remove.emit)
|
||||
if self.lib:
|
||||
self.bg_button.setText(escape_text(self.lib.tag_display_name(tag.id)))
|
||||
else:
|
||||
self.bg_button.setText(escape_text(tag.name))
|
||||
|
||||
if has_remove:
|
||||
self.inner_layout.addWidget(self.remove_button)
|
||||
self.inner_layout.addStretch(1)
|
||||
|
||||
# NOTE: Do this if you don't want the tag to stretch, like in a search.
|
||||
# self.bg_button.setMaximumWidth(self.bg_button.sizeHint().width())
|
||||
|
||||
self.bg_button.clicked.connect(self.on_click.emit)
|
||||
def set_has_remove(self, has_remove: bool):
|
||||
self.has_remove = has_remove
|
||||
|
||||
def enterEvent(self, event: QEnterEvent) -> None: # noqa: N802
|
||||
if self.has_remove:
|
||||
|
||||
Reference in New Issue
Block a user