popout: refit window with correct aspect when leaving tiled layout

behavior change: navigating to a different-aspect image/video while
tiled then un-tiling now resizes the floating window to the current
content's aspect and resets the image viewer zoom. Previously the
window restored to the old floating geometry with the wrong aspect
locked.

Stash content dims on the tiled early-return in _fit_to_content, then
detect the tiled-to-floating transition via a debounced resizeEvent
check that re-runs the fit.
This commit is contained in:
pax 2026-04-12 22:18:21 -05:00
parent 57108cd0b5
commit 2cdab574ca

View File

@ -333,6 +333,15 @@ class FullscreenPreview(QMainWindow):
# Qt fallback path) skip viewport updates triggered by our own # Qt fallback path) skip viewport updates triggered by our own
# programmatic geometry changes. # programmatic geometry changes.
self._applying_dispatch: bool = False self._applying_dispatch: bool = False
# Stashed content dims from the tiled early-return in
# _fit_to_content. When the user un-tiles the window, resizeEvent
# fires — the debounce timer re-runs _fit_to_content with these
# dims so the floating window gets the correct aspect ratio.
self._tiled_pending_content: tuple[int, int] | None = None
self._untile_refit_timer = QTimer(self)
self._untile_refit_timer.setSingleShot(True)
self._untile_refit_timer.setInterval(50)
self._untile_refit_timer.timeout.connect(self._check_untile_refit)
# Last known windowed geometry — captured on entering fullscreen so # Last known windowed geometry — captured on entering fullscreen so
# F11 → windowed can land back on the same spot. Seeded from saved # F11 → windowed can land back on the same spot. Seeded from saved
# geometry when the popout opens windowed, so even an immediate # geometry when the popout opens windowed, so even an immediate
@ -1315,7 +1324,9 @@ class FullscreenPreview(QMainWindow):
floating = None floating = None
if floating is False: if floating is False:
hyprland.resize(self.windowTitle(), 0, 0) # tiled: just set keep_aspect_ratio hyprland.resize(self.windowTitle(), 0, 0) # tiled: just set keep_aspect_ratio
self._tiled_pending_content = (content_w, content_h)
return return
self._tiled_pending_content = None
aspect = content_w / content_h aspect = content_w / content_h
screen = self.screen() screen = self.screen()
if screen is None: if screen is None:
@ -1380,6 +1391,18 @@ class FullscreenPreview(QMainWindow):
self._pending_position_restore = None self._pending_position_restore = None
self._pending_size = None self._pending_size = None
def _check_untile_refit(self) -> None:
"""Debounced callback: re-run fit if we left tiled under new content."""
if self._tiled_pending_content is not None:
cw, ch = self._tiled_pending_content
self._fit_to_content(cw, ch)
# Reset image zoom/offset so the image fits the new window
# geometry cleanly — the viewer's state is stale from the
# tiled layout.
if self._stack.currentIndex() == 0:
self._viewer._fit_to_view()
self._viewer.update()
def _show_overlay(self) -> None: def _show_overlay(self) -> None:
"""Show toolbar and video controls, restart auto-hide timer.""" """Show toolbar and video controls, restart auto-hide timer."""
if not self._ui_visible: if not self._ui_visible:
@ -1646,6 +1669,8 @@ class FullscreenPreview(QMainWindow):
# position source on Wayland). # position source on Wayland).
import os import os
if os.environ.get("HYPRLAND_INSTANCE_SIGNATURE"): if os.environ.get("HYPRLAND_INSTANCE_SIGNATURE"):
if self._tiled_pending_content is not None:
self._untile_refit_timer.start()
return return
if self._applying_dispatch or self.isFullScreen(): if self._applying_dispatch or self.isFullScreen():
return return