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
This commit is contained in:
pax 2026-04-04 19:20:03 -05:00
parent 967bdb4612
commit 238df9cf3e
3 changed files with 103 additions and 24 deletions

View File

@ -301,7 +301,9 @@ class BooruApp(QMainWindow):
self._preview.favorite_requested.connect(self._favorite_from_preview) self._preview.favorite_requested.connect(self._favorite_from_preview)
self._preview.save_to_folder.connect(self._save_from_preview) self._preview.save_to_folder.connect(self._save_from_preview)
self._preview.navigate.connect(self._navigate_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._preview.set_folders_callback(self._db.get_folders)
self._fullscreen_window = None
self._preview.setMinimumWidth(300) self._preview.setMinimumWidth(300)
right.addWidget(self._preview) right.addWidget(self._preview)
@ -701,6 +703,9 @@ class BooruApp(QMainWindow):
idx = self._grid.selected_index idx = self._grid.selected_index
if 0 <= idx < len(self._grid._thumbs): if 0 <= idx < len(self._grid._thumbs):
self._grid._thumbs[idx]._cached_path = path 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: def _on_favorite_selected(self, fav) -> None:
self._status.showMessage(f"Favorite #{fav.post_id}") self._status.showMessage(f"Favorite #{fav.post_id}")
@ -793,6 +798,24 @@ class BooruApp(QMainWindow):
self._db.add_folder(folder) self._db.add_folder(folder)
self._save_to_library(self._posts[idx], target) 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: def _close_preview(self) -> None:
self._preview.clear() self._preview.clear()
@ -1320,22 +1343,63 @@ def _apply_windows_dark_mode(app: QApplication) -> None:
from PySide6.QtGui import QPalette, QColor from PySide6.QtGui import QPalette, QColor
app.setStyle("Fusion") app.setStyle("Fusion")
palette = QPalette() 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.WindowText, QColor(255, 255, 255))
palette.setColor(QPalette.ColorRole.Base, QColor(15, 15, 15)) palette.setColor(QPalette.ColorRole.Base, QColor(25, 25, 25))
palette.setColor(QPalette.ColorRole.AlternateBase, QColor(35, 35, 35)) palette.setColor(QPalette.ColorRole.AlternateBase, QColor(38, 38, 38))
palette.setColor(QPalette.ColorRole.ToolTipBase, QColor(255, 255, 255)) palette.setColor(QPalette.ColorRole.ToolTipBase, QColor(50, 50, 50))
palette.setColor(QPalette.ColorRole.ToolTipText, QColor(255, 255, 255)) palette.setColor(QPalette.ColorRole.ToolTipText, QColor(255, 255, 255))
palette.setColor(QPalette.ColorRole.Text, 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.ButtonText, QColor(255, 255, 255))
palette.setColor(QPalette.ColorRole.BrightText, QColor(255, 0, 0)) palette.setColor(QPalette.ColorRole.BrightText, QColor(255, 0, 0))
palette.setColor(QPalette.ColorRole.Link, QColor(42, 130, 218)) palette.setColor(QPalette.ColorRole.Link, QColor(0, 120, 215))
palette.setColor(QPalette.ColorRole.Highlight, QColor(42, 130, 218)) palette.setColor(QPalette.ColorRole.Highlight, QColor(0, 120, 215))
palette.setColor(QPalette.ColorRole.HighlightedText, QColor(0, 0, 0)) 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.Text, QColor(127, 127, 127))
palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.ButtonText, QColor(127, 127, 127)) palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.ButtonText, QColor(127, 127, 127))
app.setPalette(palette) 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: except Exception:
pass pass

View File

@ -23,27 +23,34 @@ def _is_video(path: str) -> bool:
class FullscreenPreview(QMainWindow): class FullscreenPreview(QMainWindow):
"""Fullscreen image viewer window.""" """Fullscreen image viewer window with navigation."""
def __init__(self, pixmap: QPixmap, info: str = "", parent=None) -> None: navigate = Signal(int) # -1 = prev, +1 = next
super().__init__(parent)
def __init__(self, parent=None) -> None:
super().__init__(parent, Qt.WindowType.Window)
self.setWindowTitle("booru-viewer — Fullscreen") self.setWindowTitle("booru-viewer — Fullscreen")
self._preview = ImageViewer() self._viewer = ImageViewer()
self._preview.set_image(pixmap, info) self._viewer.close_requested.connect(self.close)
self._preview.close_requested.connect(self.close) self.setCentralWidget(self._viewer)
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.showFullScreen() 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: 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() 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: else:
super().keyPressEvent(event) super().keyPressEvent(event)
@ -175,6 +182,8 @@ class ImageViewer(QWidget):
elif event.key() == Qt.Key.Key_Minus: elif event.key() == Qt.Key.Key_Minus:
self._zoom = max(self._zoom / 1.2, 0.1) self._zoom = max(self._zoom / 1.2, 0.1)
self.update() self.update()
else:
event.ignore()
def resizeEvent(self, event) -> None: def resizeEvent(self, event) -> None:
if self._pixmap: if self._pixmap:
@ -337,6 +346,7 @@ class ImagePreview(QWidget):
save_to_folder = Signal(str) save_to_folder = Signal(str)
favorite_requested = Signal() favorite_requested = Signal()
navigate = Signal(int) # -1 = prev, +1 = next navigate = Signal(int) # -1 = prev, +1 = next
fullscreen_requested = Signal()
def __init__(self, parent: QWidget | None = None) -> None: def __init__(self, parent: QWidget | None = None) -> None:
super().__init__(parent) super().__init__(parent)
@ -443,6 +453,9 @@ class ImagePreview(QWidget):
if self._stack.currentIndex() == 0: if self._stack.currentIndex() == 0:
reset_action = menu.addAction("Reset View") 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") clear_action = menu.addAction("Clear Preview")
action = menu.exec(self.mapToGlobal(pos)) action = menu.exec(self.mapToGlobal(pos))
@ -468,6 +481,8 @@ class ImagePreview(QWidget):
elif action == reset_action: elif action == reset_action:
self._image_viewer._fit_to_view() self._image_viewer._fit_to_view()
self._image_viewer.update() self._image_viewer.update()
elif action == slideshow_action:
self.fullscreen_requested.emit()
elif action == clear_action: elif action == clear_action:
self.close_requested.emit() self.close_requested.emit()

View File

@ -43,7 +43,7 @@ class SearchBar(QWidget):
# Save search button # Save search button
self._save_btn = QPushButton("Save") 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.setToolTip("Save current search")
self._save_btn.clicked.connect(self._save_current_search) self._save_btn.clicked.connect(self._save_current_search)
layout.addWidget(self._save_btn) layout.addWidget(self._save_btn)