Commit Graph

3 Commits

Author SHA1 Message Date
e10a71b00a Viewer input fixes: tap during fling stops the fling (no open); zoom sticks at native resolution
1) Tap/touch during a fling: the ListView's Flickable already stops the
   fling when a press lands (its press handler resets the fling
   animation — slint core items/flickable.rs, remove_binding =
   set_animated_value(current, none)); the bug was that the press is
   then forwarded to the cell (~100 ms later, FORWARD_DELAY) and the
   release opened the viewer. The cell TouchArea now reports its
   pointer-event down (1.18 has no pressed callback) via
   cell-pressed → on_cell_pressing, which marks the tap as a
   fling-stopper when the grid moved within the last 150 ms (the tick
   tracks raw list-y changes in grid_motion); on_open_image then
   swallows the tap.

2) Zooming past max resolution moved the image: zoom_at applied the
   raw factor to tx/ty BEFORE clamping s — at the cap s stayed put
   but the translation still shifted about the (moving) pinch
   center, so the image followed the fingers like a two-finger drag.
   zoom_at now clamps s first (factor derived from the clamped s:
   at the cap factor = 1, a true no-op), and Input::pinch sticks at
   both caps (the handler reports a CUMULATIVE scale from the pinch
   start, so past the cap every later update would otherwise drift).
   The zoom cap is now NATIVE resolution: max(fit, 1.0) — 1 device
   pixel per image pixel (small images keep fit); zooming further
   only pixelates, so it is not allowed (was 20× fit).

Tests: zoom_at_past_cap_is_noop, pinch_sticks_at_caps (new),
clamp_zoom_bounds (native cap + small-image fit), updated
pinch_handler_sequence (4× fit now clamps at native). 36/36.
Emulator: fling+tap → 0 viewer opens, grid settled; normal tap
still opens (cached path).
2026-10-29 12:30:42 -04:00
9a1654136f M4.1: instant viewer (no splash) + idle full-res prefetch
open_viewer never shows 'Loading…' anymore:
 1. full-res LRU cache hit (8 entries / 64 MP of RGBA bytes) -> the
    image is shown in the same frame as the tap (decode already done
    by idle prefetch or a previous view); the ~3 ms GPU upload is the
    grid's proven hitch-free path (slint::Image is !Send, so the
    cache holds bytes and uploads on tap);
 2. else the tapped cell's THUMBNAIL (already a GPU texture, same
    aspect) is shown instantly and the full decode (urgent: bypasses
    the idle budget) swaps in silently on arrival - same aspect, so
    s is rescaled by old/new (Input::rebase_content), tx/ty (viewer
    px) stay put: identical bounds, one content change, any
    pan/zoom/pinch the user did on the thumb is preserved;
 3. only if the cell has no painted thumb either is there a black
    frame (ViewerUi.waiting names the path the viewer waits on; a
    failed decode prints an error only for that path).

Tick step 4c (prefetch_full): when the viewport is stable, the
viewer is closed and the thumb pipeline is caught up (inflight
empty after FILL - during fast scrolling it stays non-empty, so
prefetch only starts once scrolling is done), decode + cache one
center-out visible image per tick, max 2 in flight. In-flight
decodes are NOT cancelled by scrolling: results still land in the
cache (that IS the value). All caches clear on set_list / world
reset.

Verified on the emulator: cache-hit tap '(full 1200x1600 cached)'
instant; uncached tap '(thumb 159x400 up, full in flight)' ->
'(swapped in)' 137 ms later; corrupt file -> error text, no splash.
New unit test: rebase_content_preserves_viewer_mapping (34 pass).
2026-10-29 11:53:45 -04:00
02dec62405 M4: full-screen viewer with pinch/pan zoom
- src/viewer.rs: full-size decode (turbojpeg DCT shrink, 2x-screen cap
  bounded by GPU max texture, EXIF orientation applied) + transform
  model (fit/clamp/zoom_at) + single-finger pan & double-tap state
  machine. 18 unit tests.
- Pinch: Slint 1.18's ScaleRotateGestureHandler. Key finding: the CORE
  (TouchState in internal/core/input.rs), not the backend, synthesizes
  PinchGesture from raw two-finger touches on every backend including
  Android — the second finger never reaches a TouchArea, so manual
  two-finger math is dead code (the earlier draft was removed).
- main.slint: Viewer overlay (image + status + close button + full-
  surface TouchArea + ScaleRotateGestureHandler). The close button must
  be declared AFTER the full-surface TouchArea: 1.18 TouchArea grabs
  every press (GrabMouse) and aborts the front-to-back sibling
  traversal.
- lib.rs: open/close/pinch/pointer wiring; Android back key (via the
  vendor CloseRequested patch) closes the viewer first.
- vendor: androidwindowadapter back-key -> CloseRequested patch
  (Slint 1.18 has no back-key close handling).
- gputex.rs: max_texture_size() for the decode cap.
- HANDOFF: M4 done; new gotchas 6.14-16; emulator two-finger sendevent
  (MT-B) injection recipe.

Verified on emulator (pad_avd): real two-finger pinch zoom, double-tap
zoom/reset, pan, button + back close, grid scroll unaffected. Phone
(Pixel 9 Pro, release aarch64): grid, viewer open, 4080x3072 decode,
double-tap, back. Pinch not injectable on the user build (SELinux
blocks /dev/input writes) — covered by unit tests + emulator.
2026-10-29 10:51:56 -04:00