Move Viewport + _DRIFT_TOLERANCE from preview.py to popout/viewport.py (no behavior change)

Step 2 of the gui/app.py + gui/preview.py structural refactor. Pure
move: the popout viewport NamedTuple and the drift-tolerance constant
are now in their own module under popout/. preview.py grows another
re-export shim line so FullscreenPreview's method bodies (which
reference Viewport and _DRIFT_TOLERANCE by bare name) keep working
unchanged. Shim removed in commit 14. See docs/REFACTOR_PLAN.md.
This commit is contained in:
pax 2026-04-08 13:49:47 -05:00
parent cd7b8a3cca
commit 18a86358e2
3 changed files with 37 additions and 31 deletions

View File

View File

@ -0,0 +1,36 @@
"""Popout viewport math: persistent intent + drift tolerance."""
from __future__ import annotations
from typing import NamedTuple
class Viewport(NamedTuple):
"""Where and how large the user wants popout content to appear.
Three numbers, no aspect. Aspect is a property of the currently-
displayed post and is recomputed from actual content on every
navigation. The viewport stays put across navigations; the window
rect is a derived projection (Viewport, content_aspect) (x,y,w,h).
`long_side` is the binding edge length: for landscape it becomes
width, for portrait it becomes height. Symmetric across the two
orientations, which is the property that breaks the
width-anchor ratchet that the previous `_fit_to_content` had.
"""
center_x: float
center_y: float
long_side: float
# Maximum drift between our last-dispatched window rect and the current
# Hyprland-reported rect that we still treat as "no user action happened."
# Anything within this tolerance is absorbed (Hyprland gap rounding,
# subpixel accumulation, decoration accounting). Anything beyond it is
# treated as "the user dragged or resized the window externally" and the
# persistent viewport gets updated from current state.
#
# 2px is small enough not to false-positive on real user drags (which
# are always tens of pixels minimum) and large enough to absorb the
# 1-2px per-nav drift that compounds across many navigations.
_DRIFT_TOLERANCE = 2

View File

@ -18,37 +18,6 @@ import mpv as mpvlib
_log = logging.getLogger("booru") _log = logging.getLogger("booru")
class Viewport(NamedTuple):
"""Where and how large the user wants popout content to appear.
Three numbers, no aspect. Aspect is a property of the currently-
displayed post and is recomputed from actual content on every
navigation. The viewport stays put across navigations; the window
rect is a derived projection (Viewport, content_aspect) (x,y,w,h).
`long_side` is the binding edge length: for landscape it becomes
width, for portrait it becomes height. Symmetric across the two
orientations, which is the property that breaks the
width-anchor ratchet that the previous `_fit_to_content` had.
"""
center_x: float
center_y: float
long_side: float
# Maximum drift between our last-dispatched window rect and the current
# Hyprland-reported rect that we still treat as "no user action happened."
# Anything within this tolerance is absorbed (Hyprland gap rounding,
# subpixel accumulation, decoration accounting). Anything beyond it is
# treated as "the user dragged or resized the window externally" and the
# persistent viewport gets updated from current state.
#
# 2px is small enough not to false-positive on real user drags (which
# are always tens of pixels minimum) and large enough to absorb the
# 1-2px per-nav drift that compounds across many navigations.
_DRIFT_TOLERANCE = 2
## Overlay styling for the popout's translucent toolbar / controls bar ## Overlay styling for the popout's translucent toolbar / controls bar
## now lives in the bundled themes (themes/*.qss). The widgets get their ## now lives in the bundled themes (themes/*.qss). The widgets get their
## object names set in code (FullscreenPreview / VideoPlayer) so theme QSS ## object names set in code (FullscreenPreview / VideoPlayer) so theme QSS
@ -2269,3 +2238,4 @@ class ImagePreview(QWidget):
# -- Refactor compatibility shims (deleted in commit 14) -- # -- Refactor compatibility shims (deleted in commit 14) --
from .media.constants import VIDEO_EXTENSIONS, _is_video # re-export for refactor compat from .media.constants import VIDEO_EXTENSIONS, _is_video # re-export for refactor compat
from .popout.viewport import Viewport, _DRIFT_TOLERANCE # re-export for refactor compat