Scroll tilt navigates one cell/post in grid, preview, and popout

This commit is contained in:
pax 2026-04-07 08:50:13 -05:00
parent 92b7a16ab2
commit 56cb5ce1df
5 changed files with 61 additions and 27 deletions

View File

@ -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

View File

@ -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) |

View File

@ -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)

View File

@ -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)

View File

@ -361,7 +361,17 @@ 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():
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)))
@ -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,8 +1381,17 @@ 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()
if delta:
vol = max(0, min(100, self._video_player.volume + (5 if delta > 0 else -5)))
self._video_player.volume = vol
else: