Leave the stars on screen on a touch device

**Hover is not something a finger does, but Slint reports it anyway.**
`has-hover` goes true for any pointer event carrying a position, a touch
press included, and false again on the `Exit` that follows the release.

So the rating strip did appear on a tablet — for exactly the length of a
tap. It flashed on under the finger, vanished as it lifted, and the tap
carried on through to the cell and opened the photograph. An unjudged
frame could not be rated from the grid at all. The previous fix stopped
the strip disappearing when a *pointer* moved onto it; this is the same
symptom with a different cause, and hover was the wrong signal in the
first place.

The strip now stands open where the session is a touch one. That is
seeded from the platform rather than inferred, because inference needs a
press to reach a cell and a quick flick never delivers one — the Flickable
claims the gesture before the delay it would forward after — and a
control that only appears once the finger is down has appeared too late
to aim at. The grid still latches on the first non-zero touch id it sees,
which is what covers a touchscreen on the desktop.

One-way on purpose: a tablet with a mouse plugged in keeps the strips
once touched, which is the harmless direction to be wrong in. The
alternative is chrome that comes and goes as the user changes hands.
This commit is contained in:
2026-08-21 22:35:14 +02:00
parent 2fba685e16
commit 3085ec4d2e
3 changed files with 55 additions and 5 deletions
+38 -5
View File
@@ -1186,6 +1186,34 @@ export component LibraryGrid inherits Rectangle {
// Nothing in the event stream distinguishes that survivor from a real tap,
// so the grid has to remember that a pinch just happened. The latch is
// raised when the gesture starts and lowered a beat after it ends.
// --- has this session been touched? -----------------------------------
//
// **Hover is not a thing a finger does, but Slint reports it anyway.**
// `has-hover` goes true for any pointer event carrying a position, a touch
// press included, and false again on the `Exit` that follows the release.
// So on a tablet the rating strip did appear — for exactly the length of a
// tap. It flashed on under the finger, vanished as it lifted, and the tap
// went through to the cell and opened the photograph. There was no way to
// rate an unjudged frame from the grid at all.
//
// The strip therefore stops keying off hover as soon as there is evidence
// that this is a touch session, and evidence is what `touch-finger-id`
// is: zero for a mouse, never zero for a finger. Latched rather than
// sampled per event, because the strip has to be on screen *before* the
// finger arrives to be worth aiming at.
//
// One-way on purpose. A tablet with a mouse plugged in keeps the strips
// once it has been touched, which is the harmless direction to be wrong
// in — the alternative is chrome that comes and goes as the user changes
// hands.
//
// Seeded by Rust as well as latched here, because the latch alone needs a
// press to reach a cell and a quick flick never delivers one — the
// Flickable claims the gesture before the delay it forwards after. On
// Android touch is not evidence to be gathered, it is the platform, so it
// starts true there and this is left to catch a touchscreen on the desktop.
in-out property <bool> touched: false;
property <bool> pinching: false;
settle := Timer {
interval: 350ms;
@@ -2202,6 +2230,10 @@ export component LibraryGrid inherits Rectangle {
if (ev.kind == PointerEventKind.down) {
self.down-finger = ev.touch-finger-id;
self.click-pending = false;
// A finger, not a pointer — see `touched`.
if (ev.touch-finger-id != 0) {
root.touched = true;
}
// Not while the grid is being pinched, nor in
// the moment after: this is the finger left
// over from the gesture, handed back as a new
@@ -2290,11 +2322,12 @@ export component LibraryGrid inherits Rectangle {
rating: cell.rating;
// Empty stars appear once the pointer is over the
// cell, so there is something to aim at without
// filling the grid with chrome. On touch there is no
// hover, so the strip is always present where a
// rating exists — and a long-press is not needed to
// discover it.
show-empty: cell-touch.has-hover;
// filling the grid with chrome. That trade only
// works where there is a pointer to hover with: on
// touch the strip stands open, because a control
// that appears under the finger is a control that
// appears too late to aim at. See `touched`.
show-empty: cell-touch.has-hover || root.touched;
can-trash: !root.viewing-trash;
rate(n) => { root.cell-rated(i, n); }
trash() => { root.cell-trashed(i); }