diff --git a/CHANGELOG.md b/CHANGELOG.md index b1b066c..c647ddd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,6 +64,14 @@ - Prev/Next buttons hide when at page boundaries instead of just disabling - Source URLs clickable in info panel, truncated at 60 chars for display +### Changed: scroll tilt navigation +- Scroll tilt left/right now navigates between posts everywhere — grid, embedded preview, and popout — mirroring the L/R keys +- Grid: moves selection one cell, falls through to `nav_before_start` / `nav_past_end` at the edges +- Preview/popout: emits the existing `navigate` signal (±1) +- Vertical scroll still adjusts video volume on the video stack; tilt and vertical can no longer interfere +- Fixed: tilting over the image preview no longer zooms the image out (latent bug — `angleDelta().y() == 0` on pure tilt fell into the zoom-out branch) +- `page_forward` / `page_back` grid signals removed (only consumer was the old tilt handler) + ### Improved: video controls - Seek step changed from 5s to ~3s for `,` and `.` keys - `,` and `.` seek keys now work in the main preview panel, not just popout diff --git a/README.md b/README.md index 4143867..3bdfba2 100644 --- a/README.md +++ b/README.md @@ -161,7 +161,7 @@ Categories=Graphics; | `Ctrl+A` | Select all | | `Ctrl+Click` / `Shift+Click` | Multi-select | | `Home` / `End` | Jump to first / last | -| Scroll tilt left / right | Previous / next page | +| Scroll tilt left / right | Previous / next thumbnail (one cell) | | `Ctrl+C` | Copy file to clipboard | | Right click | Context menu | @@ -169,7 +169,8 @@ Categories=Graphics; | Key | Action | |-----|--------| -| Scroll wheel | Zoom | +| Scroll wheel | Zoom (image) / volume (video) | +| Scroll tilt left / right | Previous / next post | | Middle click / `0` | Reset view | | Arrow keys / `h`/`j`/`k`/`l` | Navigate posts | | `,` / `.` | Seek 3s back / forward (video) | @@ -181,6 +182,7 @@ Categories=Graphics; | Key | Action | |-----|--------| | Arrow keys / `h`/`j`/`k`/`l` | Navigate posts | +| Scroll tilt left / right | Previous / next post | | `,` / `.` | Seek 3s (video) | | `Space` | Play / pause (video) | | Scroll wheel | Volume up / down (video) | diff --git a/booru_viewer/gui/app.py b/booru_viewer/gui/app.py index c356eef..c71bd5a 100644 --- a/booru_viewer/gui/app.py +++ b/booru_viewer/gui/app.py @@ -424,8 +424,6 @@ class BooruApp(QMainWindow): self._grid.multi_context_requested.connect(self._on_multi_context_menu) self._grid.nav_past_end.connect(self._on_nav_past_end) self._grid.nav_before_start.connect(self._on_nav_before_start) - self._grid.page_forward.connect(self._next_page) - self._grid.page_back.connect(self._prev_page) self._stack.addWidget(self._grid) self._bookmarks_view = BookmarksView(self._db) diff --git a/booru_viewer/gui/grid.py b/booru_viewer/gui/grid.py index 85793fd..ad607c5 100644 --- a/booru_viewer/gui/grid.py +++ b/booru_viewer/gui/grid.py @@ -288,10 +288,8 @@ class ThumbnailGrid(QScrollArea): multi_context_requested = Signal(list, object) # list[int], QPoint reached_bottom = Signal() # emitted when scrolled to the bottom reached_top = Signal() # emitted when scrolled to the top - nav_past_end = Signal() # keyboard nav past last post - nav_before_start = Signal() # keyboard nav before first post - page_forward = Signal() # scroll tilt right - page_back = Signal() # scroll tilt left + nav_past_end = Signal() # nav past last post (keyboard or scroll tilt) + nav_before_start = Signal() # nav before first post (keyboard or scroll tilt) def __init__(self, parent: QWidget | None = None) -> None: super().__init__(parent) @@ -483,15 +481,9 @@ class ThumbnailGrid(QScrollArea): return if key in (Qt.Key.Key_Right, Qt.Key.Key_L): - if idx + 1 >= len(self._thumbs): - self.nav_past_end.emit() - else: - self._select(idx + 1) + self._nav_horizontal(1) elif key in (Qt.Key.Key_Left, Qt.Key.Key_H): - if idx - 1 < 0: - self.nav_before_start.emit() - else: - self._select(idx - 1) + self._nav_horizontal(-1) elif key in (Qt.Key.Key_Down, Qt.Key.Key_J): target = idx + cols if target >= len(self._thumbs): @@ -536,12 +528,23 @@ class ThumbnailGrid(QScrollArea): if value <= 0 and sb.maximum() > 0: self.reached_top.emit() + def _nav_horizontal(self, direction: int) -> None: + """Move selection one cell left (-1) or right (+1); emit edge signals at boundaries.""" + idx = self._selected_index + target = idx + direction + if target < 0: + self.nav_before_start.emit() + elif target >= len(self._thumbs): + self.nav_past_end.emit() + else: + self._select(target) + def wheelEvent(self, event: QWheelEvent) -> None: delta = event.angleDelta().x() if delta > 30: - self.page_back.emit() + self._nav_horizontal(-1) elif delta < -30: - self.page_forward.emit() + self._nav_horizontal(1) else: super().wheelEvent(event) diff --git a/booru_viewer/gui/preview.py b/booru_viewer/gui/preview.py index 8f41cb2..9debb68 100644 --- a/booru_viewer/gui/preview.py +++ b/booru_viewer/gui/preview.py @@ -361,13 +361,23 @@ class FullscreenPreview(QMainWindow): elif key == Qt.Key.Key_Comma and self._stack.currentIndex() == 1: self._video._seek_relative(-1800) return True - if event.type() == QEvent.Type.Wheel and self._stack.currentIndex() == 1 and self.isActiveWindow(): - delta = event.angleDelta().y() - if delta: - vol = max(0, min(100, self._video.volume + (5 if delta > 0 else -5))) - self._video.volume = vol - self._show_overlay() + if event.type() == QEvent.Type.Wheel and self.isActiveWindow(): + # Horizontal tilt navigates between posts on either stack + tilt = event.angleDelta().x() + if tilt > 30: + self.navigate.emit(-1) return True + if tilt < -30: + self.navigate.emit(1) + return True + # Vertical wheel adjusts volume on the video stack only + if self._stack.currentIndex() == 1: + delta = event.angleDelta().y() + if delta: + vol = max(0, min(100, self._video.volume + (5 if delta > 0 else -5))) + self._video.volume = vol + self._show_overlay() + return True if event.type() == QEvent.Type.MouseMove and self.isActiveWindow(): # Map cursor position to window coordinates cursor_pos = self.mapFromGlobal(event.globalPosition().toPoint() if hasattr(event, 'globalPosition') else event.globalPos()) @@ -593,9 +603,13 @@ class ImageViewer(QWidget): def wheelEvent(self, event: QWheelEvent) -> None: if not self._pixmap: return + delta = event.angleDelta().y() + if delta == 0: + # Pure horizontal tilt — let parent handle (navigation) + event.ignore() + return mouse_pos = event.position() old_zoom = self._zoom - delta = event.angleDelta().y() factor = 1.15 if delta > 0 else 1 / 1.15 self._zoom = max(0.1, min(self._zoom * factor, 20.0)) ratio = self._zoom / old_zoom @@ -1367,10 +1381,19 @@ class ImagePreview(QWidget): super().mousePressEvent(event) def wheelEvent(self, event) -> None: + # Horizontal tilt navigates between posts on either stack + tilt = event.angleDelta().x() + if tilt > 30: + self.navigate.emit(-1) + return + if tilt < -30: + self.navigate.emit(1) + return if self._stack.currentIndex() == 1: delta = event.angleDelta().y() - vol = max(0, min(100, self._video_player.volume + (5 if delta > 0 else -5))) - self._video_player.volume = vol + if delta: + vol = max(0, min(100, self._video_player.volume + (5 if delta > 0 else -5))) + self._video_player.volume = vol else: super().wheelEvent(event)