diff --git a/booru_viewer/gui/media/image_viewer.py b/booru_viewer/gui/media/image_viewer.py index 1c505ef..efe3d35 100644 --- a/booru_viewer/gui/media/image_viewer.py +++ b/booru_viewer/gui/media/image_viewer.py @@ -149,6 +149,24 @@ class ImageViewer(QWidget): event.ignore() def resizeEvent(self, event) -> None: - if self._pixmap: + if not self._pixmap: + return + pw, ph = self._pixmap.width(), self._pixmap.height() + if pw == 0 or ph == 0: + return + # Only re-fit if the user was at fit-to-view at the *previous* + # size. If they had explicitly zoomed/panned, leave _zoom and + # _offset alone — clobbering them on every resize (F11 toggle, + # manual window drag, splitter move) loses their state. Use + # event.oldSize() to compute the prior fit-to-view zoom and + # compare to current _zoom; the 0.001 epsilon absorbs float + # drift but is tighter than any wheel/key zoom step (±20%). + old = event.oldSize() + if old.isValid() and old.width() > 0 and old.height() > 0: + old_fit = min(old.width() / pw, old.height() / ph) + if abs(self._zoom - old_fit) < 0.001: + self._fit_to_view() + else: + # First resize (no valid old size) — default to fit. self._fit_to_view() - self.update() + self.update() diff --git a/booru_viewer/gui/popout/window.py b/booru_viewer/gui/popout/window.py index e7bfa3e..25c3638 100644 --- a/booru_viewer/gui/popout/window.py +++ b/booru_viewer/gui/popout/window.py @@ -905,15 +905,39 @@ class FullscreenPreview(QMainWindow): pass def _enter_fullscreen(self) -> None: - """Enter fullscreen — capture windowed geometry first so F11 back can restore it.""" + """Enter fullscreen — capture windowed geometry first so F11 back can restore it. + + Also capture the current windowed state into the persistent + `_viewport` so the F11-exit restore lands at the user's actual + pre-F11 position, not at a stale viewport from before they last + dragged the window. The drift detection in `_derive_viewport_for_fit` + only fires when `_last_dispatched_rect` is set AND a fit is being + computed — neither path catches the "user dragged the popout + with Super+drag and then immediately pressed F11" sequence, + because Hyprland Super+drag doesn't fire Qt's moveEvent and no + nav has happened to trigger a fit. Capturing fresh into + `_viewport` here makes the restore correct regardless. + """ from PySide6.QtCore import QRect win = self._hyprctl_get_window() if win and win.get("at") and win.get("size"): x, y = win["at"] w, h = win["size"] self._windowed_geometry = QRect(x, y, w, h) + self._viewport = Viewport( + center_x=x + w / 2, + center_y=y + h / 2, + long_side=float(max(w, h)), + ) else: self._windowed_geometry = self.frameGeometry() + rect = self._windowed_geometry + if rect.width() > 0 and rect.height() > 0: + self._viewport = Viewport( + center_x=rect.x() + rect.width() / 2, + center_y=rect.y() + rect.height() / 2, + long_side=float(max(rect.width(), rect.height())), + ) self.showFullScreen() def _exit_fullscreen(self) -> None: