Make the range gesture visible, and offer the whole grid at once
Touch has had a range gesture for as long as selection mode has: double-tap the far end. It is invisible, it is unreliable on a grid that scrolls under the second tap, and it extends from the anchor *before* the two taps moved it — a rule subtle enough that the code needs two paragraphs to explain it to itself. Nobody who was not told about it has ever used it. "Select to…" is the same operation with state you can see. Press it, the strip stops reporting and says "Tap the last photograph", and the next cell taken is the far end. It reaches Rust as shift on `cell-pressed`, so it lands in `apply_press` as the ctrl+shift it already is, and there is no third selection policy to keep in step with the other two. This is deliberately not the sweep gesture. A drag that paints cells can only reach what is on screen, and the ranges that hurt on a tablet are longer than a screenful — between the two taps here the user may scroll as far as they like, and the run is resolved by the catalog rather than by what happened to be loaded. A sweep is still worth having for short runs; it is not what this should have rested on. "Select all" beside it, asked of the catalog for the same reason: a select-all that quietly meant "the hundred cells that happen to be loaded" is a lie the user cannot see until the export runs. The double-tap stays. It is tested, and an accelerator that costs nothing is worth keeping for whoever has already learnt it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1574,6 +1574,55 @@ pub fn wire<S, R, P, C>(
|
||||
});
|
||||
}
|
||||
|
||||
// TRACES: FR-CAT-5 | FR-UI-4
|
||||
// Everything the grid is showing.
|
||||
//
|
||||
// Asked of the catalog through `span`, not read off the loaded window, for
|
||||
// the reason spelled out in `apply_press`: the window is a hundred cells
|
||||
// over a library of thousands, and a "select all" that quietly meant
|
||||
// "select the hundred that happen to be loaded" is a lie the user cannot
|
||||
// see until the export runs. `library_total` is the count the same scope
|
||||
// and filter produced, so the run is the whole of what the grid claims.
|
||||
//
|
||||
// Replaces rather than adds. "All" is a statement about the result, not an
|
||||
// increment, and a user who wanted the rest kept would not have reached for
|
||||
// this.
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let ctl = ctl.clone();
|
||||
let visible = visible_ids.clone();
|
||||
window.on_library_select_all(move || {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
let ids = visible();
|
||||
let total = w.get_library_total().max(0) as usize;
|
||||
if total == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
// The loaded window is the fallback, not the answer — the same
|
||||
// trade the shift-click path makes. An empty span means no library
|
||||
// or a failed query, and selecting what is on screen beats
|
||||
// selecting nothing.
|
||||
let all = match ctl.span(0, total - 1) {
|
||||
run if run.is_empty() => ids.clone(),
|
||||
run => run,
|
||||
};
|
||||
|
||||
{
|
||||
let mut selection = ctl.selection.borrow_mut();
|
||||
selection.clear();
|
||||
selection.extend(all);
|
||||
}
|
||||
|
||||
// The first frame becomes the anchor, so a "Select to…" straight
|
||||
// afterwards describes a range from the top rather than from
|
||||
// wherever the last individual tap left it — which, after taking
|
||||
// everything, is not a place the user is still thinking about.
|
||||
*ctl.anchor.borrow_mut() = Some(0);
|
||||
sync_selection(&w, &ctl, &ids);
|
||||
});
|
||||
}
|
||||
|
||||
// TRACES: FR-CAT-5
|
||||
// A collection holding exactly what is selected.
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user