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.
37 lines
1.4 KiB
Python
37 lines
1.4 KiB
Python
"""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
|