From 238df9cf3ea03d5f360cba727923422f197df47b Mon Sep 17 00:00:00 2001 From: pax Date: Sat, 4 Apr 2026 19:20:03 -0500 Subject: [PATCH] Slideshow mode, Win10 dark mode fixes, key propagation fix - Add "Slideshow Mode" to preview right-click context menu - Fix arrow key propagation in fullscreen/slideshow view - Flatten Fusion dark mode buttons with stylesheet - Fix Save button width, fix spinbox arrows on dark theme --- booru_viewer/gui/app.py | 80 +++++++++++++++++++++++++++++++++---- booru_viewer/gui/preview.py | 45 ++++++++++++++------- booru_viewer/gui/search.py | 2 +- 3 files changed, 103 insertions(+), 24 deletions(-) diff --git a/booru_viewer/gui/app.py b/booru_viewer/gui/app.py index 22419d7..fa85b34 100644 --- a/booru_viewer/gui/app.py +++ b/booru_viewer/gui/app.py @@ -301,7 +301,9 @@ class BooruApp(QMainWindow): self._preview.favorite_requested.connect(self._favorite_from_preview) self._preview.save_to_folder.connect(self._save_from_preview) self._preview.navigate.connect(self._navigate_preview) + self._preview.fullscreen_requested.connect(self._open_fullscreen_preview) self._preview.set_folders_callback(self._db.get_folders) + self._fullscreen_window = None self._preview.setMinimumWidth(300) right.addWidget(self._preview) @@ -701,6 +703,9 @@ class BooruApp(QMainWindow): idx = self._grid.selected_index if 0 <= idx < len(self._grid._thumbs): self._grid._thumbs[idx]._cached_path = path + # Update fullscreen if open + if self._fullscreen_window and self._fullscreen_window.isVisible(): + self._fullscreen_window.set_media(path, info) def _on_favorite_selected(self, fav) -> None: self._status.showMessage(f"Favorite #{fav.post_id}") @@ -793,6 +798,24 @@ class BooruApp(QMainWindow): self._db.add_folder(folder) self._save_to_library(self._posts[idx], target) + def _open_fullscreen_preview(self) -> None: + path = self._preview._current_path + if not path: + return + from .preview import FullscreenPreview + self._fullscreen_window = FullscreenPreview(parent=self) + self._fullscreen_window.navigate.connect(self._navigate_fullscreen) + self._fullscreen_window.set_media(path, self._preview._info_label.text()) + + def _navigate_fullscreen(self, direction: int) -> None: + self._navigate_preview(direction) + # For synchronous loads (cached/favorites), update immediately + if self._fullscreen_window and self._preview._current_path: + self._fullscreen_window.set_media( + self._preview._current_path, + self._preview._info_label.text(), + ) + def _close_preview(self) -> None: self._preview.clear() @@ -1320,22 +1343,63 @@ def _apply_windows_dark_mode(app: QApplication) -> None: from PySide6.QtGui import QPalette, QColor app.setStyle("Fusion") palette = QPalette() - palette.setColor(QPalette.ColorRole.Window, QColor(30, 30, 30)) + palette.setColor(QPalette.ColorRole.Window, QColor(32, 32, 32)) palette.setColor(QPalette.ColorRole.WindowText, QColor(255, 255, 255)) - palette.setColor(QPalette.ColorRole.Base, QColor(15, 15, 15)) - palette.setColor(QPalette.ColorRole.AlternateBase, QColor(35, 35, 35)) - palette.setColor(QPalette.ColorRole.ToolTipBase, QColor(255, 255, 255)) + palette.setColor(QPalette.ColorRole.Base, QColor(25, 25, 25)) + palette.setColor(QPalette.ColorRole.AlternateBase, QColor(38, 38, 38)) + palette.setColor(QPalette.ColorRole.ToolTipBase, QColor(50, 50, 50)) palette.setColor(QPalette.ColorRole.ToolTipText, QColor(255, 255, 255)) palette.setColor(QPalette.ColorRole.Text, QColor(255, 255, 255)) - palette.setColor(QPalette.ColorRole.Button, QColor(40, 40, 40)) + palette.setColor(QPalette.ColorRole.Button, QColor(51, 51, 51)) palette.setColor(QPalette.ColorRole.ButtonText, QColor(255, 255, 255)) palette.setColor(QPalette.ColorRole.BrightText, QColor(255, 0, 0)) - palette.setColor(QPalette.ColorRole.Link, QColor(42, 130, 218)) - palette.setColor(QPalette.ColorRole.Highlight, QColor(42, 130, 218)) - palette.setColor(QPalette.ColorRole.HighlightedText, QColor(0, 0, 0)) + palette.setColor(QPalette.ColorRole.Link, QColor(0, 120, 215)) + palette.setColor(QPalette.ColorRole.Highlight, QColor(0, 120, 215)) + palette.setColor(QPalette.ColorRole.HighlightedText, QColor(255, 255, 255)) + palette.setColor(QPalette.ColorRole.Mid, QColor(51, 51, 51)) + palette.setColor(QPalette.ColorRole.Dark, QColor(25, 25, 25)) + palette.setColor(QPalette.ColorRole.Shadow, QColor(0, 0, 0)) + palette.setColor(QPalette.ColorRole.Light, QColor(60, 60, 60)) + palette.setColor(QPalette.ColorRole.Midlight, QColor(55, 55, 55)) palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Text, QColor(127, 127, 127)) palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.ButtonText, QColor(127, 127, 127)) app.setPalette(palette) + # Flatten Fusion's 3D look + app.setStyleSheet(app.styleSheet() + """ + QPushButton { + border: 1px solid #555; + border-radius: 2px; + padding: 4px 12px; + } + QPushButton:hover { background-color: #444; } + QPushButton:pressed { background-color: #333; } + QComboBox { + border: 1px solid #555; + border-radius: 2px; + padding: 3px 6px; + } + QSpinBox { + border: 1px solid #555; + border-radius: 2px; + } + QLineEdit { + border: 1px solid #555; + border-radius: 2px; + padding: 3px; + } + QScrollBar:vertical { + background: #252525; + width: 12px; + } + QScrollBar::handle:vertical { + background: #555; + border-radius: 4px; + min-height: 20px; + } + QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { + height: 0; + } + """) except Exception: pass diff --git a/booru_viewer/gui/preview.py b/booru_viewer/gui/preview.py index 7de0426..69082a9 100644 --- a/booru_viewer/gui/preview.py +++ b/booru_viewer/gui/preview.py @@ -23,27 +23,34 @@ def _is_video(path: str) -> bool: class FullscreenPreview(QMainWindow): - """Fullscreen image viewer window.""" + """Fullscreen image viewer window with navigation.""" - def __init__(self, pixmap: QPixmap, info: str = "", parent=None) -> None: - super().__init__(parent) + navigate = Signal(int) # -1 = prev, +1 = next + + def __init__(self, parent=None) -> None: + super().__init__(parent, Qt.WindowType.Window) self.setWindowTitle("booru-viewer — Fullscreen") - self._preview = ImageViewer() - self._preview.set_image(pixmap, info) - self._preview.close_requested.connect(self.close) - self.setCentralWidget(self._preview) - if parent: - screen = parent.screen() - else: - from PySide6.QtWidgets import QApplication - screen = QApplication.primaryScreen() - if screen: - self.setGeometry(screen.geometry()) + self._viewer = ImageViewer() + self._viewer.close_requested.connect(self.close) + self.setCentralWidget(self._viewer) self.showFullScreen() + def set_media(self, path: str, info: str = "") -> None: + ext = Path(path).suffix.lower() + if ext == ".gif": + self._viewer.set_gif(path, info) + else: + pix = QPixmap(path) + if not pix.isNull(): + self._viewer.set_image(pix, info) + def keyPressEvent(self, event: QKeyEvent) -> None: - if event.key() in (Qt.Key.Key_Escape, Qt.Key.Key_Q, Qt.Key.Key_F): + if event.key() in (Qt.Key.Key_Escape, Qt.Key.Key_Q): self.close() + elif event.key() in (Qt.Key.Key_Left, Qt.Key.Key_H): + self.navigate.emit(-1) + elif event.key() in (Qt.Key.Key_Right, Qt.Key.Key_L): + self.navigate.emit(1) else: super().keyPressEvent(event) @@ -175,6 +182,8 @@ class ImageViewer(QWidget): elif event.key() == Qt.Key.Key_Minus: self._zoom = max(self._zoom / 1.2, 0.1) self.update() + else: + event.ignore() def resizeEvent(self, event) -> None: if self._pixmap: @@ -337,6 +346,7 @@ class ImagePreview(QWidget): save_to_folder = Signal(str) favorite_requested = Signal() navigate = Signal(int) # -1 = prev, +1 = next + fullscreen_requested = Signal() def __init__(self, parent: QWidget | None = None) -> None: super().__init__(parent) @@ -443,6 +453,9 @@ class ImagePreview(QWidget): if self._stack.currentIndex() == 0: reset_action = menu.addAction("Reset View") + slideshow_action = None + if self._current_path: + slideshow_action = menu.addAction("Slideshow Mode") clear_action = menu.addAction("Clear Preview") action = menu.exec(self.mapToGlobal(pos)) @@ -468,6 +481,8 @@ class ImagePreview(QWidget): elif action == reset_action: self._image_viewer._fit_to_view() self._image_viewer.update() + elif action == slideshow_action: + self.fullscreen_requested.emit() elif action == clear_action: self.close_requested.emit() diff --git a/booru_viewer/gui/search.py b/booru_viewer/gui/search.py index 6578014..b79a5cc 100644 --- a/booru_viewer/gui/search.py +++ b/booru_viewer/gui/search.py @@ -43,7 +43,7 @@ class SearchBar(QWidget): # Save search button self._save_btn = QPushButton("Save") - self._save_btn.setFixedWidth(40) + self._save_btn.setFixedWidth(50) self._save_btn.setToolTip("Save current search") self._save_btn.clicked.connect(self._save_current_search) layout.addWidget(self._save_btn)