Skip to content

Commit

Permalink
feat(ui): show filenames in thumbnail grid (Closes #85) (#633)
Browse files Browse the repository at this point in the history
* feat(ui): add filename label under thumbnails

* refactor(ui): organize menu items

* feat: make thumbnail filenames toggleable

* refactor variables, tweak spacing

* fix(ui): only change cursor on thumb_button

* revert changes to ../search_library/../ts_library.sqlite

* fix: don't ever call .setHidden(False) on visible widgets

* refactor: rename filename toggles to setters

* fix: remove duplicate open_on_start_action
  • Loading branch information
CyanVoxel authored Dec 20, 2024
1 parent 24fa76e commit 8387676
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 58 deletions.
1 change: 1 addition & 0 deletions tagstudio/src/core/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class SettingItems(str, enum.Enum):
LAST_LIBRARY = "last_library"
LIBS_LIST = "libs_list"
WINDOW_SHOW_LIBS = "window_show_libs"
SHOW_FILENAMES = "show_filenames"
AUTOPLAY = "autoplay_videos"


Expand Down
94 changes: 53 additions & 41 deletions tagstudio/src/qt/ts_qt.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,7 @@
import structlog
from humanfriendly import format_timespan
from PySide6 import QtCore
from PySide6.QtCore import (
QObject,
QSettings,
Qt,
QThread,
QThreadPool,
QTimer,
Signal,
)
from PySide6.QtCore import QObject, QSettings, Qt, QThread, QThreadPool, QTimer, Signal
from PySide6.QtGui import (
QAction,
QColor,
Expand Down Expand Up @@ -264,15 +256,12 @@ def start(self) -> None:

file_menu = QMenu("&File", menu_bar)
edit_menu = QMenu("&Edit", menu_bar)
view_menu = QMenu("&View", menu_bar)
tools_menu = QMenu("&Tools", menu_bar)
macros_menu = QMenu("&Macros", menu_bar)
window_menu = QMenu("&Window", menu_bar)
help_menu = QMenu("&Help", menu_bar)

# File Menu ============================================================
# file_menu.addAction(QAction('&New Library', menu_bar))
# file_menu.addAction(QAction('&Open Library', menu_bar))

open_library_action = QAction("&Open/Create Library", menu_bar)
open_library_action.triggered.connect(lambda: self.open_library_from_dialog())
open_library_action.setShortcut(
Expand Down Expand Up @@ -302,8 +291,6 @@ def start(self) -> None:

file_menu.addSeparator()

# refresh_lib_action = QAction('&Refresh Directories', self.main_window)
# refresh_lib_action.triggered.connect(lambda: self.lib.refresh_dir())
add_new_files_action = QAction("&Refresh Directories", menu_bar)
add_new_files_action.triggered.connect(
lambda: self.callback_library_needed_check(self.add_new_files_callback)
Expand All @@ -315,13 +302,23 @@ def start(self) -> None:
)
)
add_new_files_action.setStatusTip("Ctrl+R")
# file_menu.addAction(refresh_lib_action)
file_menu.addAction(add_new_files_action)
file_menu.addSeparator()

close_library_action = QAction("&Close Library", menu_bar)
close_library_action.triggered.connect(self.close_library)
file_menu.addAction(close_library_action)
file_menu.addSeparator()

open_on_start_action = QAction("Open Library on Start", self)
open_on_start_action.setCheckable(True)
open_on_start_action.setChecked(
bool(self.settings.value(SettingItems.START_LOAD_LAST, defaultValue=True, type=bool))
)
open_on_start_action.triggered.connect(
lambda checked: self.settings.setValue(SettingItems.START_LOAD_LAST, checked)
)
file_menu.addAction(open_on_start_action)

# Edit Menu ============================================================
new_tag_action = QAction("New &Tag", menu_bar)
Expand Down Expand Up @@ -364,15 +361,32 @@ def start(self) -> None:
tag_database_action.triggered.connect(lambda: self.show_tag_database())
edit_menu.addAction(tag_database_action)

check_action = QAction("Open library on start", self)
check_action.setCheckable(True)
check_action.setChecked(
bool(self.settings.value(SettingItems.START_LOAD_LAST, defaultValue=True, type=bool))
# View Menu ============================================================
show_libs_list_action = QAction("Show Recent Libraries", menu_bar)
show_libs_list_action.setCheckable(True)
show_libs_list_action.setChecked(
bool(self.settings.value(SettingItems.WINDOW_SHOW_LIBS, defaultValue=True, type=bool))
)
check_action.triggered.connect(
lambda checked: self.settings.setValue(SettingItems.START_LOAD_LAST, checked)
show_libs_list_action.triggered.connect(
lambda checked: (
self.settings.setValue(SettingItems.WINDOW_SHOW_LIBS, checked),
self.toggle_libs_list(checked),
)
)
window_menu.addAction(check_action)
view_menu.addAction(show_libs_list_action)

show_filenames_action = QAction("Show Filenames in Grid", menu_bar)
show_filenames_action.setCheckable(True)
show_filenames_action.setChecked(
bool(self.settings.value(SettingItems.SHOW_FILENAMES, defaultValue=True, type=bool))
)
show_filenames_action.triggered.connect(
lambda checked: (
self.settings.setValue(SettingItems.SHOW_FILENAMES, checked),
self.show_grid_filenames(checked),
)
)
view_menu.addAction(show_filenames_action)

# Tools Menu ===========================================================
def create_fix_unlinked_entries_modal():
Expand Down Expand Up @@ -407,19 +421,6 @@ def create_dupe_files_modal():
)
macros_menu.addAction(self.autofill_action)

show_libs_list_action = QAction("Show Recent Libraries", menu_bar)
show_libs_list_action.setCheckable(True)
show_libs_list_action.setChecked(
bool(self.settings.value(SettingItems.WINDOW_SHOW_LIBS, defaultValue=True, type=bool))
)
show_libs_list_action.triggered.connect(
lambda checked: (
self.settings.setValue(SettingItems.WINDOW_SHOW_LIBS, checked),
self.toggle_libs_list(checked),
)
)
window_menu.addAction(show_libs_list_action)

def create_folders_tags_modal():
if not hasattr(self, "folders_modal"):
self.folders_modal = FoldersToTagsModal(self.lib, self)
Expand All @@ -429,7 +430,7 @@ def create_folders_tags_modal():
folders_to_tags_action.triggered.connect(create_folders_tags_modal)
macros_menu.addAction(folders_to_tags_action)

# Help Menu ==========================================================
# Help Menu ============================================================
self.repo_action = QAction("Visit GitHub Repository", menu_bar)
self.repo_action.triggered.connect(
lambda: webbrowser.open("https://github.com/TagStudioDev/TagStudio")
Expand All @@ -439,9 +440,9 @@ def create_folders_tags_modal():

menu_bar.addMenu(file_menu)
menu_bar.addMenu(edit_menu)
menu_bar.addMenu(view_menu)
menu_bar.addMenu(tools_menu)
menu_bar.addMenu(macros_menu)
menu_bar.addMenu(window_menu)
menu_bar.addMenu(help_menu)

self.main_window.searchField.textChanged.connect(self.update_completions_list)
Expand Down Expand Up @@ -551,6 +552,10 @@ def toggle_libs_list(self, value: bool):
self.preview_panel.libs_flow_container.hide()
self.preview_panel.update()

def show_grid_filenames(self, value: bool):
for thumb in self.item_thumbs:
thumb.set_filename_visibility(value)

def callback_library_needed_check(self, func):
"""Check if loaded library has valid path before executing the button function."""
if self.lib.library_dir:
Expand Down Expand Up @@ -833,9 +838,9 @@ def thumb_size_callback(self, index: int):
it.thumb_button.setIcon(blank_icon)
it.resize(self.thumb_size, self.thumb_size)
it.thumb_size = (self.thumb_size, self.thumb_size)
it.setMinimumSize(self.thumb_size, self.thumb_size)
it.setMaximumSize(self.thumb_size, self.thumb_size)
it.setFixedSize(self.thumb_size, self.thumb_size)
it.thumb_button.thumb_size = (self.thumb_size, self.thumb_size)
it.set_filename_visibility(it.show_filename_label)
self.flow_container.layout().setSpacing(
min(self.thumb_size // spacing_divisor, min_spacing)
)
Expand Down Expand Up @@ -883,7 +888,14 @@ def _init_thumb_grid(self):
# TODO - init after library is loaded, it can have different page_size
for grid_idx in range(self.filter.page_size):
item_thumb = ItemThumb(
None, self.lib, self, (self.thumb_size, self.thumb_size), grid_idx
None,
self.lib,
self,
(self.thumb_size, self.thumb_size),
grid_idx,
bool(
self.settings.value(SettingItems.SHOW_FILENAMES, defaultValue=True, type=bool)
),
)

layout.addWidget(item_thumb)
Expand Down
86 changes: 71 additions & 15 deletions tagstudio/src/qt/widgets/item_thumb.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,16 @@ class ItemThumb(FlowWidget):
"padding-left: 1px;"
)

filename_style = "font-size:10px;"

def __init__(
self,
mode: ItemType,
library: Library,
driver: "QtDriver",
thumb_size: tuple[int, int],
grid_idx: int,
show_filename_label: bool = False,
):
super().__init__()
self.grid_idx = grid_idx
Expand All @@ -125,17 +128,33 @@ def __init__(
self.driver = driver
self.item_id: int | None = None
self.thumb_size: tuple[int, int] = thumb_size
self.show_filename_label: bool = show_filename_label
self.label_height = 12
self.label_spacing = 4
self.setMinimumSize(*thumb_size)
self.setMaximumSize(*thumb_size)
self.setMouseTracking(True)
check_size = 24
self.setFixedSize(
thumb_size[0],
thumb_size[1]
+ ((self.label_height + self.label_spacing) if show_filename_label else 0),
)

self.thumb_container = QWidget()
self.base_layout = QVBoxLayout(self)
self.base_layout.setContentsMargins(0, 0, 0, 0)
self.base_layout.setSpacing(0)
self.setLayout(self.base_layout)

# +----------+
# | ARC FAV| Top Right: Favorite & Archived Badges
# | |
# | |
# |EXT #| Lower Left: File Type, Tag Group Icon, or Collation Icon
# +----------+ Lower Right: Collation Count, Video Length, or Word Count
#
# Filename Underneath: (Optional) Filename

# Thumbnail ============================================================

Expand All @@ -145,9 +164,9 @@ def __init__(
# || ||
# |*--------*|
# +----------+
self.base_layout = QVBoxLayout(self)
self.base_layout.setObjectName("baseLayout")
self.base_layout.setContentsMargins(0, 0, 0, 0)
self.thumb_layout = QVBoxLayout(self.thumb_container)
self.thumb_layout.setObjectName("baseLayout")
self.thumb_layout.setContentsMargins(0, 0, 0, 0)

# +----------+
# |[~~~~~~~~]|
Expand All @@ -160,15 +179,15 @@ def __init__(
self.top_layout.setContentsMargins(6, 6, 6, 6)
self.top_container = QWidget()
self.top_container.setLayout(self.top_layout)
self.base_layout.addWidget(self.top_container)
self.thumb_layout.addWidget(self.top_container)

# +----------+
# |[~~~~~~~~]|
# | ^ |
# | | |
# | v |
# +----------+
self.base_layout.addStretch(2)
self.thumb_layout.addStretch(2)

# +----------+
# |[~~~~~~~~]|
Expand All @@ -181,19 +200,20 @@ def __init__(
self.bottom_layout.setContentsMargins(6, 6, 6, 6)
self.bottom_container = QWidget()
self.bottom_container.setLayout(self.bottom_layout)
self.base_layout.addWidget(self.bottom_container)
self.thumb_layout.addWidget(self.bottom_container)

self.thumb_button = ThumbButton(self, thumb_size)
self.thumb_button = ThumbButton(self.thumb_container, thumb_size)
self.renderer = ThumbRenderer()
self.renderer.updated.connect(
lambda ts, i, s, ext: (
lambda ts, i, s, fn, ext: (
self.update_thumb(ts, image=i),
self.update_size(ts, size=s),
self.set_filename_text(fn),
self.set_extension(ext),
)
)
self.thumb_button.setFlat(True)
self.thumb_button.setLayout(self.base_layout)
self.thumb_button.setLayout(self.thumb_layout)
self.thumb_button.setContextMenuPolicy(Qt.ContextMenuPolicy.ActionsContextMenu)

self.opener = FileOpenerHelper("")
Expand Down Expand Up @@ -285,6 +305,16 @@ def __init__(
self.badges[badge_type] = badge
self.cb_layout.addWidget(badge)

# Filename Label =======================================================
self.file_label = QLabel(text="Filename")
self.file_label.setStyleSheet(ItemThumb.filename_style)
self.file_label.setMaximumHeight(self.label_height)
if not show_filename_label:
self.file_label.setHidden(True)

self.base_layout.addWidget(self.thumb_container)
self.base_layout.addWidget(self.file_label)

self.set_mode(mode)

@property
Expand All @@ -298,11 +328,11 @@ def is_archived(self):
def set_mode(self, mode: ItemType | None) -> None:
if mode is None:
self.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents, on=True)
self.unsetCursor()
self.thumb_button.unsetCursor()
self.thumb_button.setHidden(True)
elif mode == ItemType.ENTRY and self.mode != ItemType.ENTRY:
self.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents, on=False)
self.setCursor(Qt.CursorShape.PointingHandCursor)
self.thumb_button.setCursor(Qt.CursorShape.PointingHandCursor)
self.thumb_button.setHidden(False)
self.cb_container.setHidden(False)
# Count Badge depends on file extension (video length, word count)
Expand All @@ -312,7 +342,7 @@ def set_mode(self, mode: ItemType | None) -> None:
self.ext_badge.setHidden(True)
elif mode == ItemType.COLLATION and self.mode != ItemType.COLLATION:
self.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents, on=False)
self.setCursor(Qt.CursorShape.PointingHandCursor)
self.thumb_button.setCursor(Qt.CursorShape.PointingHandCursor)
self.thumb_button.setHidden(False)
self.cb_container.setHidden(True)
self.ext_badge.setHidden(True)
Expand All @@ -321,7 +351,7 @@ def set_mode(self, mode: ItemType | None) -> None:
self.item_type_badge.setHidden(False)
elif mode == ItemType.TAG_GROUP and self.mode != ItemType.TAG_GROUP:
self.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents, on=False)
self.setCursor(Qt.CursorShape.PointingHandCursor)
self.thumb_button.setCursor(Qt.CursorShape.PointingHandCursor)
self.thumb_button.setHidden(False)
self.ext_badge.setHidden(True)
self.count_badge.setHidden(False)
Expand Down Expand Up @@ -366,14 +396,40 @@ def set_count(self, count: str) -> None:
self.ext_badge.setHidden(True)
self.count_badge.setHidden(True)

def set_filename_text(self, filename: Path | str | None):
self.file_label.setText(str(filename))

def set_filename_visibility(self, set_visible: bool):
"""Toggle the visibility of the filename label.
Args:
set_visible (bool): Show the filename, true or false.
"""
if set_visible:
if self.file_label.isHidden():
self.file_label.setHidden(False)
self.setFixedHeight(self.thumb_size[1] + self.label_height + self.label_spacing)
else:
self.file_label.setHidden(True)
self.setFixedHeight(self.thumb_size[1])
self.show_filename_label = set_visible

def update_thumb(self, timestamp: float, image: QPixmap | None = None):
"""Update attributes of a thumbnail element."""
if timestamp > ItemThumb.update_cutoff:
self.thumb_button.setIcon(image if image else QPixmap())

def update_size(self, timestamp: float, size: QSize):
"""Updates attributes of a thumbnail element."""
if timestamp > ItemThumb.update_cutoff and self.thumb_button.iconSize != size:
"""Updates attributes of a thumbnail element.
Args:
timestamp (float | None): The UTC timestamp for when this call was
originally dispatched. Used to skip outdated jobs.
size (QSize): The new thumbnail size to set.
"""
if timestamp > ItemThumb.update_cutoff:
self.thumb_size = size.toTuple() # type: ignore
self.thumb_button.setIconSize(size)
self.thumb_button.setMinimumSize(size)
self.thumb_button.setMaximumSize(size)
Expand Down
Loading

0 comments on commit 8387676

Please sign in to comment.