Make a pinch zoom the grid and nothing else

Two faults left over from making the gesture reach the grid at all.

**It still opened photographs.** Checking the finger id stops the
synthetic release Slint emits when the *second* finger lands, but not the
other end of the gesture: lifting one finger of two leaves the other one
down, and Slint replays that survivor as a fresh `Pressed` on whatever is
under it — which is how it hands the pointer back to ordinary handling.
Under it is a cell. So the cell was selected, and lifting that last finger
was a complete, well-formed click on the same finger that pressed. No
part of the event stream distinguishes it from a real tap, so the grid now
remembers that a pinch just happened: a latch raised when the gesture
starts and lowered a beat after it ends, during which cells take neither
presses nor clicks.

The press that *opens* a pinch is undone rather than suppressed — it has
already happened by the time a second finger makes it a pinch. Undoing it
has to be exact, or a cancel that restored the selection but left the
anchor moved would make the next shift-click select a run from a cell
nobody pointed at, so capture and restore are a tested pair.

**And it was not smooth.** Two reasons. The pinch was thresholded into ±1
steps of 25%, so the grid lurched and then sat still; it now takes the
ratio since the last update and tracks the fingers, with the drawn cell
still landing on whole column counts because the columns divide the
width. And `zoom-cells` was the one geometry change still reloading
inline — a full catalog re-read, 360-row model rebuild and thumbnail
batch per step, on the thread drawing the frame. It goes through the same
settle timer as the rest now.
This commit is contained in:
2026-08-21 22:03:46 +02:00
parent 6ae0af3f72
commit 2fba685e16
4 changed files with 241 additions and 20 deletions
+51 -1
View File
@@ -3607,7 +3607,57 @@ pub fn wire<F>(
// of the request key, so cells that now want the large resolution
// simply miss and ask for it, while the 256px ones they already
// hold stay served.
load_window(&w, &ctl);
//
// Deferred, like every other geometry change. This one was still
// reloading inline — a full catalog re-read and model rebuild per
// step, which is what a wheel spun through six steps paid six
// times over.
schedule_reload(&w, &ctl);
});
}
// TRACES: FR-UI-4
// A pinch, which is continuous where the wheel is stepped.
//
// Given the ratio since the last update rather than a direction, so the
// grid tracks the fingers instead of jumping a fixed 25% per threshold
// crossing. What the user is setting is the size class — how big they want
// a thumbnail to be — and the drawn cell follows from it by dividing the
// width, so the visible result still lands on whole column counts. Feeding
// a continuous value in is what decides *when* it crosses.
{
let weak = window.as_weak();
let ctl = ctl.clone();
window.on_library_pinch_cells(move |ratio| {
let Some(w) = weak.upgrade() else { return };
if !(ratio.is_finite() && ratio > 0.0) {
return;
}
let current = w.get_library_cell_size();
let next = (current * ratio).clamp(MIN_CELL_SIZE, MAX_CELL_SIZE);
if (next - current).abs() < 0.5 {
return;
}
w.set_library_cell_size(next);
schedule_reload(&w, &ctl);
});
}
// TRACES: FR-UI-4
// A pinch has begun, so the press that started it was not a press.
//
// A pinch opens as one finger on a cell — which selects it — and only
// becomes a pinch when the second lands. Without this the user is left
// holding a selection they never made, on a photograph they were only
// reaching past.
{
let weak = window.as_weak();
let ctl = ctl.clone();
let coll = coll_ctl.clone();
window.on_library_pinch_started(move || {
let Some(w) = weak.upgrade() else { return };
crate::collections_ui::cancel_press(&w, &coll, &ctl.visible_ids());
});
}