diff --git a/tagstudio/src/core/enums.py b/tagstudio/src/core/enums.py
index 781bba0a..749428ca 100644
--- a/tagstudio/src/core/enums.py
+++ b/tagstudio/src/core/enums.py
@@ -20,10 +20,11 @@ class Theme(str, enum.Enum):
COLOR_DARK_LABEL = "#DD000000"
COLOR_BG = "#65000000"
- COLOR_HOVER = "#65AAAAAA"
- COLOR_PRESSED = "#65EEEEEE"
- COLOR_DISABLED = "#65F39CAA"
- COLOR_DISABLED_BG = "#65440D12"
+ COLOR_HOVER = "#65444444"
+ COLOR_PRESSED = "#65777777"
+ COLOR_DISABLED_BG = "#30000000"
+ COLOR_FORBIDDEN = "#65F39CAA"
+ COLOR_FORBIDDEN_BG = "#65440D12"
class OpenStatus(enum.IntEnum):
diff --git a/tagstudio/src/qt/widgets/preview/field_containers.py b/tagstudio/src/qt/widgets/preview/field_containers.py
index ec07281b..ae00a6c0 100644
--- a/tagstudio/src/qt/widgets/preview/field_containers.py
+++ b/tagstudio/src/qt/widgets/preview/field_containers.py
@@ -60,7 +60,7 @@ class FieldContainers(QWidget):
self.is_open: bool = False
self.common_fields: list = []
self.mixed_fields: list = []
- self.selected: list[Entry] = []
+ self.cached_entries: list[Entry] = []
self.containers: list[FieldContainer] = []
self.panel_bg_color = (
@@ -108,7 +108,7 @@ class FieldContainers(QWidget):
def update_from_entry(self, entry: Entry):
"""Update tags and fields from a single Entry source."""
- self.selected = [self.lib.get_entry_full(entry.id)]
+ self.cached_entries = [self.lib.get_entry_full(entry.id)]
logger.info(
"[FieldContainers] Updating Selection",
path=entry.path,
@@ -116,7 +116,7 @@ class FieldContainers(QWidget):
tags=entry.tags,
)
- entry_ = self.selected[0]
+ entry_ = self.cached_entries[0]
container_len: int = len(entry_.fields)
container_index = 0
@@ -139,6 +139,11 @@ class FieldContainers(QWidget):
if i > (container_len - 1):
c.setHidden(True)
+ def hide_containers(self):
+ """Hide all field and tag containers."""
+ for c in self.containers:
+ c.setHidden(True)
+
def get_tag_categories(self, tags: set[Tag]) -> dict[Tag, set[Tag | None]]:
"""Get a dictionary of category tags mapped to their respective tags."""
cats: dict[Tag, set[Tag | None]] = {}
@@ -184,29 +189,35 @@ class FieldContainers(QWidget):
return Translations.translate_formatted("library.field.confirm_remove", name=name)
def add_field_to_selected(self, field_list: list):
- """Add list of entry fields to one or more selected items."""
+ """Add list of entry fields to one or more selected items.
+
+ Uses the current driver selection, NOT the field containers cache.
+ """
logger.info(
"[FieldContainers][add_field_to_selected]",
- selected=[x.path for x in self.selected],
+ selected=self.driver.selected,
fields=field_list,
)
- for entry in self.selected:
+ for entry_id in self.driver.selected:
for field_item in field_list:
self.lib.add_entry_field_type(
- entry.id,
+ entry_id,
field_id=field_item.data(Qt.ItemDataRole.UserRole),
)
def add_tags_to_selected(self, tags: list[int]):
- """Add list of tags to one or more selected items."""
+ """Add list of tags to one or more selected items.
+
+ Uses the current driver selection, NOT the field containers cache.
+ """
logger.info(
"[FieldContainers][add_tags_to_selected]",
- selected=[x.path for x in self.selected],
+ selected=self.driver.selected,
tags=tags,
)
- for entry in self.selected:
+ for entry_id in self.driver.selected:
self.lib.add_tags_to_entry(
- entry.id,
+ entry_id,
tag_ids=tags,
)
self.tags_updated.emit()
@@ -251,7 +262,7 @@ class FieldContainers(QWidget):
save_callback=(
lambda content: (
self.update_field(field, content),
- self.update_from_entry(self.selected[0]),
+ self.update_from_entry(self.cached_entries[0]),
)
),
)
@@ -265,7 +276,7 @@ class FieldContainers(QWidget):
prompt=self.remove_field_prompt(field.type.type.value),
callback=lambda: (
self.remove_field(field),
- self.update_from_entry(self.selected[0]),
+ self.update_from_entry(self.cached_entries[0]),
),
)
)
@@ -291,7 +302,7 @@ class FieldContainers(QWidget):
save_callback=(
lambda content: (
self.update_field(field, content),
- self.update_from_entry(self.selected[0]),
+ self.update_from_entry(self.cached_entries[0]),
)
),
)
@@ -301,7 +312,7 @@ class FieldContainers(QWidget):
prompt=self.remove_field_prompt(field.type.name),
callback=lambda: (
self.remove_field(field),
- self.update_from_entry(self.selected[0]),
+ self.update_from_entry(self.cached_entries[0]),
),
)
)
@@ -330,7 +341,7 @@ class FieldContainers(QWidget):
prompt=self.remove_field_prompt(field.type.name),
callback=lambda: (
self.remove_field(field),
- self.update_from_entry(self.selected[0]),
+ self.update_from_entry(self.cached_entries[0]),
),
)
)
@@ -351,7 +362,7 @@ class FieldContainers(QWidget):
prompt=self.remove_field_prompt(field.type.name),
callback=lambda: (
self.remove_field(field),
- self.update_from_entry(self.selected[0]),
+ self.update_from_entry(self.cached_entries[0]),
),
)
)
@@ -404,7 +415,7 @@ class FieldContainers(QWidget):
inner_widget.updated.connect(
lambda: (
self.write_tag_container(index, tags, category_tag),
- self.update_from_entry(self.selected[0]),
+ self.update_from_entry(self.cached_entries[0]),
)
)
else:
@@ -421,9 +432,9 @@ class FieldContainers(QWidget):
logger.info(
"[FieldContainers] Removing Field",
field=field,
- selected=[x.path for x in self.selected],
+ selected=[x.path for x in self.cached_entries],
)
- entry_ids = [e.id for e in self.selected]
+ entry_ids = [e.id for e in self.cached_entries]
self.lib.remove_entry_field(field, entry_ids)
# # if the field is meta tags, update the badges
@@ -437,7 +448,7 @@ class FieldContainers(QWidget):
(TextField, DatetimeField), # , TagBoxField)
), f"instance: {type(field)}"
- entry_ids = [e.id for e in self.selected]
+ entry_ids = [e.id for e in self.cached_entries]
assert entry_ids, "No entries selected"
self.lib.update_entry_field(
diff --git a/tagstudio/src/qt/widgets/preview/file_attributes.py b/tagstudio/src/qt/widgets/preview/file_attributes.py
index 19820115..6a99b945 100644
--- a/tagstudio/src/qt/widgets/preview/file_attributes.py
+++ b/tagstudio/src/qt/widgets/preview/file_attributes.py
@@ -46,7 +46,7 @@ class FileAttributes(QWidget):
root_layout = QVBoxLayout(self)
root_layout.setContentsMargins(0, 0, 0, 0)
- root_layout.setSpacing(6)
+ root_layout.setSpacing(0)
label_bg_color = (
Theme.COLOR_BG_DARK.value
@@ -87,17 +87,20 @@ class FileAttributes(QWidget):
self.date_created_label.setAlignment(Qt.AlignmentFlag.AlignLeft)
self.date_created_label.setTextFormat(Qt.TextFormat.RichText)
self.date_created_label.setStyleSheet(self.date_style)
+ self.date_created_label.setHidden(True)
self.date_modified_label = QLabel()
self.date_modified_label.setObjectName("dateModifiedLabel")
self.date_modified_label.setAlignment(Qt.AlignmentFlag.AlignLeft)
self.date_modified_label.setTextFormat(Qt.TextFormat.RichText)
self.date_modified_label.setStyleSheet(self.date_style)
+ self.date_modified_label.setHidden(True)
self.dimensions_label = QLabel()
self.dimensions_label.setObjectName("dimensionsLabel")
self.dimensions_label.setWordWrap(True)
self.dimensions_label.setStyleSheet(self.properties_style)
+ self.dimensions_label.setHidden(True)
self.date_container = QWidget()
date_layout = QVBoxLayout(self.date_container)
@@ -142,12 +145,18 @@ class FileAttributes(QWidget):
stats = {}
if not filepath:
+ self.layout().setSpacing(0)
+ self.file_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.file_label.setText("No Items Selected")
self.file_label.set_file_path("")
self.file_label.setCursor(Qt.CursorShape.ArrowCursor)
self.dimensions_label.setText("")
+ self.dimensions_label.setHidden(True)
else:
+ self.layout().setSpacing(6)
+ self.file_label.setAlignment(Qt.AlignmentFlag.AlignLeft)
self.file_label.set_file_path(filepath)
+ self.dimensions_label.setHidden(False)
file_str: str = ""
separator: str = f"{os.path.sep}" # Gray
@@ -229,7 +238,10 @@ class FileAttributes(QWidget):
def update_multi_selection(self, count: int):
# Multiple Selected Items
+ self.layout().setSpacing(0)
+ self.file_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.file_label.setText(f"{count} Items Selected")
self.file_label.setCursor(Qt.CursorShape.ArrowCursor)
self.file_label.set_file_path("")
self.dimensions_label.setText("")
+ self.dimensions_label.setHidden(True)
diff --git a/tagstudio/src/qt/widgets/preview/preview_thumb.py b/tagstudio/src/qt/widgets/preview/preview_thumb.py
index 057551d9..7ac502df 100644
--- a/tagstudio/src/qt/widgets/preview/preview_thumb.py
+++ b/tagstudio/src/qt/widgets/preview/preview_thumb.py
@@ -368,6 +368,10 @@ class PreviewThumb(QWidget):
return stats
+ def hide_preview(self):
+ """Completely hide the file preview."""
+ self.switch_preview("")
+
def resizeEvent(self, event: QResizeEvent) -> None: # noqa: N802
self.update_image_size((self.size().width(), self.size().height()))
return super().resizeEvent(event)
diff --git a/tagstudio/src/qt/widgets/preview/recent_libraries.py b/tagstudio/src/qt/widgets/preview/recent_libraries.py
index fd2fb2b2..e43d43fa 100644
--- a/tagstudio/src/qt/widgets/preview/recent_libraries.py
+++ b/tagstudio/src/qt/widgets/preview/recent_libraries.py
@@ -99,7 +99,7 @@ class RecentLibraries(QWidget):
# "}"
# f"QPushButton::hover{{background-color:{Theme.COLOR_HOVER.value};}}"
# f"QPushButton::pressed{{background-color:{Theme.COLOR_PRESSED.value};}}"
-# f"QPushButton::disabled{{background-color:{Theme.COLOR_DISABLED_BG.value};}}"
+# f"QPushButton::disabled{{background-color:{Theme.COLOR_FORBIDDEN_BG.value};}}"
# )
# btn.setCursor(Qt.CursorShape.PointingHandCursor)
diff --git a/tagstudio/src/qt/widgets/preview_panel.py b/tagstudio/src/qt/widgets/preview_panel.py
index 009ce524..89c73311 100644
--- a/tagstudio/src/qt/widgets/preview_panel.py
+++ b/tagstudio/src/qt/widgets/preview_panel.py
@@ -2,22 +2,26 @@
# Licensed under the GPL-3.0 License.
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio
-import traceback
import typing
from pathlib import Path
from warnings import catch_warnings
import structlog
from PySide6.QtCore import Qt, Signal
-from PySide6.QtWidgets import QHBoxLayout, QSplitter, QVBoxLayout, QWidget
+from PySide6.QtWidgets import (
+ QHBoxLayout,
+ QPushButton,
+ QSplitter,
+ QVBoxLayout,
+ QWidget,
+)
+from src.core.enums import Theme
from src.core.library.alchemy.library import Library
from src.core.library.alchemy.models import Entry
-from src.qt.helpers.qbutton_wrapper import QPushButtonWrapper
+from src.core.palette import ColorType, UiColor, get_ui_color
from src.qt.modals.add_field import AddFieldModal
from src.qt.modals.tag_search import TagSearchPanel
from src.qt.widgets.panel import PanelModal
-
-# from src.qt.modals.add_tag import AddTagModal
from src.qt.widgets.preview.field_containers import FieldContainers
from src.qt.widgets.preview.file_attributes import FileAttributes
from src.qt.widgets.preview.preview_thumb import PreviewThumb
@@ -34,6 +38,30 @@ class PreviewPanel(QWidget):
tags_updated = Signal()
+ # TODO: There should be a global button theme somewhere.
+ button_style = (
+ f"QPushButton{{"
+ f"background-color:{Theme.COLOR_BG.value};"
+ "border-radius:6px;"
+ "text-align: center;"
+ f"}}"
+ f"QPushButton::hover{{"
+ f"background-color:{Theme.COLOR_HOVER.value};"
+ f"border-color:{get_ui_color(ColorType.BORDER, UiColor.THEME_DARK)};"
+ f"border-style:solid;"
+ f"border-width: 2px;"
+ f"}}"
+ f"QPushButton::pressed{{"
+ f"background-color:{Theme.COLOR_PRESSED.value};"
+ f"border-color:{get_ui_color(ColorType.LIGHT_ACCENT, UiColor.THEME_DARK)};"
+ f"border-style:solid;"
+ f"border-width: 2px;"
+ f"}}"
+ f"QPushButton::disabled{{"
+ f"background-color:{Theme.COLOR_DISABLED_BG.value};"
+ f"}}"
+ )
+
def __init__(self, library: Library, driver: "QtDriver"):
super().__init__()
self.lib = library
@@ -46,10 +74,8 @@ class PreviewPanel(QWidget):
self.fields = FieldContainers(library, driver)
tsp = TagSearchPanel(self.driver.lib)
- # tsp.tag_chosen.connect(lambda x: self.add_tag_callback(x))
self.add_tag_modal = PanelModal(tsp, "Add Tags", "Add Tags")
- # self.add_tag_modal = AddTagModal(self.lib)
self.add_field_modal = AddFieldModal(self.lib)
preview_section = QWidget()
@@ -65,25 +91,24 @@ class PreviewPanel(QWidget):
splitter = QSplitter()
splitter.setOrientation(Qt.Orientation.Vertical)
splitter.setHandleWidth(12)
- # splitter.splitterMoved.connect(
- # lambda: self.thumb.update_image_size(
- # (
- # self.thumb.image_container.size().width(),
- # self.thumb.image_container.size().height(),
- # )
- # )
- # )
+
add_buttons_container = QWidget()
add_buttons_layout = QHBoxLayout(add_buttons_container)
add_buttons_layout.setContentsMargins(0, 0, 0, 0)
add_buttons_layout.setSpacing(6)
- self.add_tag_button = QPushButtonWrapper()
+ self.add_tag_button = QPushButton()
+ self.add_tag_button.setEnabled(False)
self.add_tag_button.setCursor(Qt.CursorShape.PointingHandCursor)
+ self.add_tag_button.setMinimumHeight(28)
+ self.add_tag_button.setStyleSheet(PreviewPanel.button_style)
self.add_tag_button.setText("Add Tag")
- self.add_field_button = QPushButtonWrapper()
+ self.add_field_button = QPushButton()
+ self.add_field_button.setEnabled(False)
self.add_field_button.setCursor(Qt.CursorShape.PointingHandCursor)
+ self.add_field_button.setMinimumHeight(28)
+ self.add_field_button.setStyleSheet(PreviewPanel.button_style)
self.add_field_button.setText("Add Field")
add_buttons_layout.addWidget(self.add_tag_button)
@@ -107,12 +132,14 @@ class PreviewPanel(QWidget):
def update_widgets(self) -> bool:
"""Render the panel widgets with the newest data from the Library."""
# No Items Selected
- # items: list[Entry] = [self.driver.frame_content[x] for x in self.driver.selected]
if len(self.driver.selected) == 0:
- # TODO: Clear everything to default
- # self.file_attrs.update_blank()
+ self.thumb.hide_preview()
self.file_attrs.update_stats()
self.file_attrs.update_date_label()
+ self.fields.hide_containers()
+
+ self.add_tag_button.setEnabled(False)
+ self.add_field_button.setEnabled(False)
# One Item Selected
elif len(self.driver.selected) == 1:
@@ -121,48 +148,54 @@ class PreviewPanel(QWidget):
ext: str = filepath.suffix.lower()
stats: dict = self.thumb.update_preview(filepath, ext)
- try:
- self.file_attrs.update_stats(filepath, ext, stats)
- self.file_attrs.update_date_label(filepath)
- self.fields.update_from_entry(entry)
- self.update_add_tag_button(entry)
- self.update_add_field_button(entry)
- except Exception as e:
- logger.error("[Preview Panel] Error updating selection", error=e)
- traceback.print_exc()
+ self.file_attrs.update_stats(filepath, ext, stats)
+ self.file_attrs.update_date_label(filepath)
+ self.fields.update_from_entry(entry)
+ self.update_add_tag_button(entry)
+ self.update_add_field_button(entry)
+
+ self.add_tag_button.setEnabled(True)
+ self.add_field_button.setEnabled(True)
# Multiple Selected Items
elif len(self.driver.selected) > 1:
- # Render mixed selection
+ # items: list[Entry] = [self.lib.get_entry_full(x) for x in self.driver.selected]
+ # TODO: Render mixed selection
+ self.thumb.hide_preview() # TODO: Allow for mixed editing
self.file_attrs.update_multi_selection(len(self.driver.selected))
self.file_attrs.update_date_label()
+ self.fields.hide_containers() # TODO: Allow for mixed editing
+ self.update_add_tag_button()
+ self.update_add_field_button()
# self.fields.update_from_entries(items)
# self.file_attrs.update_selection_count()
+ self.add_tag_button.setEnabled(True)
+ self.add_field_button.setEnabled(True)
+
# self.thumb.update_widgets()
# # self.file_attrs.update_widgets()
# self.fields.update_widgets()
return True
- def update_add_field_button(self, entry: Entry):
+ def update_add_field_button(self, entry: Entry | None = None):
with catch_warnings(record=True):
self.add_field_modal.done.disconnect()
self.add_field_button.clicked.disconnect()
# TODO: Remove all "is_connected" instances across the codebase
self.add_field_modal.is_connected = False
- self.add_field_button.is_connected = False
self.add_field_modal.done.connect(
lambda f: (
self.fields.add_field_to_selected(f),
- self.fields.update_from_entry(entry),
+ (self.fields.update_from_entry(entry) if entry else ()),
)
)
self.add_field_modal.is_connected = True
self.add_field_button.clicked.connect(self.add_field_modal.show)
- def update_add_tag_button(self, entry: Entry):
+ def update_add_tag_button(self, entry: Entry = None):
with catch_warnings(record=True):
self.add_tag_modal.widget.tag_chosen.disconnect()
self.add_tag_button.clicked.disconnect()
@@ -170,7 +203,7 @@ class PreviewPanel(QWidget):
self.add_tag_modal.widget.tag_chosen.connect(
lambda t: (
self.fields.add_tags_to_selected(t),
- self.fields.update_from_entry(entry),
+ (self.fields.update_from_entry(entry) if entry else ()),
)
)
@@ -180,6 +213,3 @@ class PreviewPanel(QWidget):
self.add_tag_modal.show(),
)
)
- self.add_tag_button.is_connected = True
-
- # self.add_field_button.clicked.connect(self.add_tag_modal.show)