Browse multi-select: split library + bookmark actions, conditional visibility
The browse grid's multi-select right-click menu collapsed library and bookmark actions into a single "Remove All Bookmarks" entry that did *both* — it called delete_from_library and remove_bookmark per post, and was unconditionally visible regardless of selection state. Two problems: 1. There was no way to bulk-unsave files from the library without also stripping the bookmarks. Saved-but-not-bookmarked posts had no bulk-unsave path at all. 2. The single misleadingly-named action didn't match the single-post right-click menu's clean separation of "Save to Library / Unsave from Library" vs. "Bookmark as / Remove Bookmark". Reshape: split into four distinct actions, each with symmetric conditional visibility: - Save All to Library → shown only if any post is unsaved - Unsave All from Library → shown only if any post is saved (NEW) - Bookmark All → shown only if any post is unbookmarked - Remove All Bookmarks → shown only if any post is bookmarked Mixed selections show whichever subset of the four is relevant. The new Unsave All from Library calls a new _bulk_unsave method that mirrors the _bulk_save shape but synchronously (delete_from_library is a filesystem op, no httpx round-trip). Remove All Bookmarks now *only* removes bookmarks — it no longer touches the library, matching the single-post Remove Bookmark action's scope. Always-shown actions (Download All, Copy All URLs) stay below a separator at the bottom. Verified: - Multi-select unbookmarked+unsaved posts → only Save All / Bookmark All - Multi-select saved-not-bookmarked → only Unsave All / Bookmark All - Multi-select bookmarked+saved → only Unsave All / Remove All Bookmarks - Mixed selection → all four appear - Unsave All from Library removes files, leaves bookmarks - Remove All Bookmarks removes bookmarks, leaves files
This commit is contained in:
parent
c4efdb76f8
commit
db774fc33e
@ -2411,27 +2411,64 @@ class BooruApp(QMainWindow):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def _on_multi_context_menu(self, indices: list, pos) -> None:
|
def _on_multi_context_menu(self, indices: list, pos) -> None:
|
||||||
"""Context menu for multi-selected posts."""
|
"""Context menu for multi-selected posts.
|
||||||
|
|
||||||
|
Library and bookmark actions are split into independent
|
||||||
|
save/unsave and bookmark/remove-bookmark pairs (mirroring the
|
||||||
|
single-post menu's separation), with symmetric conditional
|
||||||
|
visibility: each action only appears when the selection actually
|
||||||
|
contains posts the action would affect. Save All to Library
|
||||||
|
appears only when at least one post is unsaved; Unsave All from
|
||||||
|
Library only when at least one is saved; Bookmark All only when
|
||||||
|
at least one is unbookmarked; Remove All Bookmarks only when at
|
||||||
|
least one is bookmarked.
|
||||||
|
"""
|
||||||
posts = [self._posts[i] for i in indices if 0 <= i < len(self._posts)]
|
posts = [self._posts[i] for i in indices if 0 <= i < len(self._posts)]
|
||||||
if not posts:
|
if not posts:
|
||||||
return
|
return
|
||||||
count = len(posts)
|
count = len(posts)
|
||||||
|
|
||||||
menu = QMenu(self)
|
site_id = self._site_combo.currentData()
|
||||||
fav_all = menu.addAction(f"Bookmark All ({count})")
|
any_bookmarked = bool(site_id) and any(self._db.is_bookmarked(site_id, p.id) for p in posts)
|
||||||
|
any_unbookmarked = bool(site_id) and any(not self._db.is_bookmarked(site_id, p.id) for p in posts)
|
||||||
|
any_saved = any(self._is_post_saved(p.id) for p in posts)
|
||||||
|
any_unsaved = any(not self._is_post_saved(p.id) for p in posts)
|
||||||
|
|
||||||
|
menu = QMenu(self)
|
||||||
|
|
||||||
|
# Library section
|
||||||
|
save_menu = None
|
||||||
|
save_unsorted = None
|
||||||
|
save_new = None
|
||||||
|
save_folder_actions: dict[int, str] = {}
|
||||||
|
if any_unsaved:
|
||||||
|
from ..core.config import library_folders
|
||||||
save_menu = menu.addMenu(f"Save All to Library ({count})")
|
save_menu = menu.addMenu(f"Save All to Library ({count})")
|
||||||
save_unsorted = save_menu.addAction("Unfiled")
|
save_unsorted = save_menu.addAction("Unfiled")
|
||||||
save_folder_actions = {}
|
|
||||||
from ..core.config import library_folders
|
|
||||||
for folder in library_folders():
|
for folder in library_folders():
|
||||||
a = save_menu.addAction(folder)
|
a = save_menu.addAction(folder)
|
||||||
save_folder_actions[id(a)] = folder
|
save_folder_actions[id(a)] = folder
|
||||||
save_menu.addSeparator()
|
save_menu.addSeparator()
|
||||||
save_new = save_menu.addAction("+ New Folder...")
|
save_new = save_menu.addAction("+ New Folder...")
|
||||||
|
|
||||||
|
unsave_lib_all = None
|
||||||
|
if any_saved:
|
||||||
|
unsave_lib_all = menu.addAction(f"Unsave All from Library ({count})")
|
||||||
|
|
||||||
|
# Bookmark section
|
||||||
|
if (any_unsaved or any_saved) and (any_unbookmarked or any_bookmarked):
|
||||||
menu.addSeparator()
|
menu.addSeparator()
|
||||||
|
|
||||||
|
fav_all = None
|
||||||
|
if any_unbookmarked:
|
||||||
|
fav_all = menu.addAction(f"Bookmark All ({count})")
|
||||||
|
|
||||||
|
unfav_all = None
|
||||||
|
if any_bookmarked:
|
||||||
unfav_all = menu.addAction(f"Remove All Bookmarks ({count})")
|
unfav_all = menu.addAction(f"Remove All Bookmarks ({count})")
|
||||||
|
|
||||||
|
# Always-shown actions
|
||||||
|
if any_unsaved or any_saved or any_unbookmarked or any_bookmarked:
|
||||||
menu.addSeparator()
|
menu.addSeparator()
|
||||||
batch_dl = menu.addAction(f"Download All ({count})...")
|
batch_dl = menu.addAction(f"Download All ({count})...")
|
||||||
copy_urls = menu.addAction("Copy All URLs")
|
copy_urls = menu.addAction("Copy All URLs")
|
||||||
@ -2440,11 +2477,11 @@ class BooruApp(QMainWindow):
|
|||||||
if not action:
|
if not action:
|
||||||
return
|
return
|
||||||
|
|
||||||
if action == fav_all:
|
if fav_all is not None and action == fav_all:
|
||||||
self._bulk_bookmark(indices, posts)
|
self._bulk_bookmark(indices, posts)
|
||||||
elif action == save_unsorted:
|
elif save_unsorted is not None and action == save_unsorted:
|
||||||
self._bulk_save(indices, posts, None)
|
self._bulk_save(indices, posts, None)
|
||||||
elif action == save_new:
|
elif save_new is not None and action == save_new:
|
||||||
from PySide6.QtWidgets import QInputDialog, QMessageBox
|
from PySide6.QtWidgets import QInputDialog, QMessageBox
|
||||||
name, ok = QInputDialog.getText(self, "New Folder", "Folder name:")
|
name, ok = QInputDialog.getText(self, "New Folder", "Folder name:")
|
||||||
if ok and name.strip():
|
if ok and name.strip():
|
||||||
@ -2457,25 +2494,24 @@ class BooruApp(QMainWindow):
|
|||||||
self._bulk_save(indices, posts, name.strip())
|
self._bulk_save(indices, posts, name.strip())
|
||||||
elif id(action) in save_folder_actions:
|
elif id(action) in save_folder_actions:
|
||||||
self._bulk_save(indices, posts, save_folder_actions[id(action)])
|
self._bulk_save(indices, posts, save_folder_actions[id(action)])
|
||||||
|
elif unsave_lib_all is not None and action == unsave_lib_all:
|
||||||
|
self._bulk_unsave(indices, posts)
|
||||||
elif action == batch_dl:
|
elif action == batch_dl:
|
||||||
from .dialogs import select_directory
|
from .dialogs import select_directory
|
||||||
dest = select_directory(self, "Download to folder")
|
dest = select_directory(self, "Download to folder")
|
||||||
if dest:
|
if dest:
|
||||||
self._batch_download_posts(posts, dest)
|
self._batch_download_posts(posts, dest)
|
||||||
elif action == unfav_all:
|
elif unfav_all is not None and action == unfav_all:
|
||||||
site_id = self._site_combo.currentData()
|
|
||||||
if site_id:
|
if site_id:
|
||||||
from ..core.cache import delete_from_library
|
|
||||||
for post in posts:
|
for post in posts:
|
||||||
# Single call now walks every library folder by post id.
|
|
||||||
delete_from_library(post.id)
|
|
||||||
self._db.remove_bookmark(site_id, post.id)
|
self._db.remove_bookmark(site_id, post.id)
|
||||||
for idx in indices:
|
for idx in indices:
|
||||||
if 0 <= idx < len(self._grid._thumbs):
|
if 0 <= idx < len(self._grid._thumbs):
|
||||||
self._grid._thumbs[idx].set_bookmarked(False)
|
self._grid._thumbs[idx].set_bookmarked(False)
|
||||||
self._grid._thumbs[idx].set_saved_locally(False)
|
|
||||||
self._grid._clear_multi()
|
self._grid._clear_multi()
|
||||||
self._status.showMessage(f"Removed {count} bookmarks")
|
self._status.showMessage(f"Removed {count} bookmarks")
|
||||||
|
if self._stack.currentIndex() == 1:
|
||||||
|
self._bookmarks_view.refresh()
|
||||||
elif action == copy_urls:
|
elif action == copy_urls:
|
||||||
urls = "\n".join(p.file_url for p in posts)
|
urls = "\n".join(p.file_url for p in posts)
|
||||||
QApplication.clipboard().setText(urls)
|
QApplication.clipboard().setText(urls)
|
||||||
@ -2537,6 +2573,29 @@ class BooruApp(QMainWindow):
|
|||||||
|
|
||||||
self._run_async(_do)
|
self._run_async(_do)
|
||||||
|
|
||||||
|
def _bulk_unsave(self, indices: list[int], posts: list[Post]) -> None:
|
||||||
|
"""Bulk-remove selected posts from the library.
|
||||||
|
|
||||||
|
Mirrors `_bulk_save` shape but synchronously — `delete_from_library`
|
||||||
|
is a filesystem op, no httpx round-trip needed. Touches only the
|
||||||
|
library (filesystem); bookmarks are a separate DB-backed concept
|
||||||
|
and stay untouched. The grid's saved-locally dot clears for every
|
||||||
|
selection slot regardless of whether the file was actually present
|
||||||
|
— the user's intent is "make these not-saved", and a missing file
|
||||||
|
is already not-saved.
|
||||||
|
"""
|
||||||
|
from ..core.cache import delete_from_library
|
||||||
|
for post in posts:
|
||||||
|
delete_from_library(post.id)
|
||||||
|
for idx in indices:
|
||||||
|
if 0 <= idx < len(self._grid._thumbs):
|
||||||
|
self._grid._thumbs[idx].set_saved_locally(False)
|
||||||
|
self._grid._clear_multi()
|
||||||
|
self._status.showMessage(f"Removed {len(posts)} from library")
|
||||||
|
if self._stack.currentIndex() == 2:
|
||||||
|
self._library_view.refresh()
|
||||||
|
self._update_fullscreen_state()
|
||||||
|
|
||||||
def _ensure_bookmarked(self, post: Post) -> None:
|
def _ensure_bookmarked(self, post: Post) -> None:
|
||||||
"""Bookmark a post if not already bookmarked."""
|
"""Bookmark a post if not already bookmarked."""
|
||||||
site_id = self._site_combo.currentData()
|
site_id = self._site_combo.currentData()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user