Files
DarkRoom/docs/gestures.md
T
dtourolleandClaude Opus 5 4dc954f01a
Benchmarks / CPU and I/O (per commit) (push) Successful in 3m53s
Benchmarks / Frame budget (on demand) (push) Skipped
Build and test / Desktop (Linux) (push) Failing after 58s
Build and test / Layer separation (push) Successful in 45s
🐳 Android image / Build and push (push) Successful in 4s
Build and test / android-image (push) Successful in 4s
Traceability / Requirement traces (push) Failing after 1m0s
Build and test / Android (aarch64) (push) Failing after 31s
Take the photo roll's grab band off the buttons that end a mode
"Done Cropping", "Done Repairing", "Done Masking" and "Fit" float over
the foot of the canvas. So does the photo roll's swipe handler, and a
gesture handler is not a layout box — it is an input surface. A press
inside one is delayed, then offered to that handler's own children and
to nothing else: `input_event_filter_before_children` returns
`DelayForwarding`, which aborts the hit-test traversal outright, and the
replay afterwards visits only the handler's subtree. Everything behind
it is never asked, hover included.

The band was `strip-height + reach` — 136px along the bottom — whether
the roll was out or away. So the button that ends a mode was drawn, was
lit, and did nothing for as long as a library was open, which is the
whole time anybody is developing from one. The tool rail kept working
because it is a sibling of the canvas rather than behind the roll, which
is exactly why this looked like two dead buttons rather than a dead
region.

The band now goes where the roll goes. The handler carries the strip
instead of standing still while the strip animates inside it: closed,
only `reach` is on screen and the rest hangs below the window where
nothing can press it; open, it still covers the thumbnails, which is
what lets a swipe down anywhere across them put the roll away. The
180ms travel moved from the strip onto the handler, so the drawn
positions in both states are what they were.

The controls are then positioned against that band rather than against
the bottom of the canvas, and ride up with the strip when it comes out.
Reordering them in front of the roll would have been the other fix, and
it is the wrong one — the band would become the thing that cannot be
reached, and a gesture nobody can start is worse than a button with a
second way out.

`roll-strip` and `roll-reach` are tokens now, because two files have to
agree on where that band is for either of them to keep out of it.

The bottom of the photograph comes back with it: the crop's lower
handles and a repair placed near the bottom edge were inside the same
136px and had the same fault.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 19:42:17 +02:00

6.8 KiB

How the application is driven

Every entry here is extracted from the comment beside the code that implements it, so this file cannot describe a gesture the application does not have. Add one by writing a GESTURE: block next to the implementation; there is nowhere else to write it.

16 gestures, in 2 places.

People

Pull a face out of the wrong person

  • Touch — Tap the faces that do not belong, then "Split off"
  • Pointer — Click the faces that do not belong, then "Split off"

Grouping over-merges on siblings, on parents and children, and on the same person a decade apart, so splitting is as prominent as merging. A tool that can only merge makes its own errors permanent.

ui/dr-ui/ui/identity.slint:130

Rule on a suggested face

  • Touch — Tick to confirm it, cross to reject it
  • Pointer — Tick to confirm it, cross to reject it

A face is either the system's guess or the user's judgement, and the two are never conflated. A rejection is remembered, so the face is not suggested for that person again. The gesture note above is the whole label: a tick and a cross are only "confirm" and "reject" to someone who can see the suggestion they sit beside, and IconButton's fallback would announce them as "check" and "cross" — two icon names that say nothing about which person is being ruled on.

ui/dr-ui/ui/identity.slint:150

See a person's photographs

  • Touch — Choose them in the rail, then "Show photos"
  • Pointer — Choose them in the rail, then "Show photos"

This is the point of having identified anybody. Without it the screen is a filing cabinet with no drawer handles.

ui/dr-ui/ui/identity.slint:571

Change how faces are grouped

  • Touch — "Grouping…", move the dials, then Regroup
  • Pointer — "Grouping…", move the dials, then Regroup

The right match confidence is a property of your library, not of the model. "What would this do?" answers for this library without writing anything; names, confirmations and the groups you have set aside are kept whatever the dials say.

ui/dr-ui/ui/identity.slint:608

Library grid

Start selecting several photographs

  • Touch — Press and hold a photograph, or press Select in the header
  • Pointer — Ctrl-click, or press Select in the header

Touch has no ctrl, so without a mode there is no way to select a second photograph — the first tap would open it. The hold is the fast way in and the button is the one that can be found.

ui/dr-ui/ui/library.slint:1217

Add or remove one photograph

  • Touch — While selecting, tap it
  • Pointer — Ctrl-click it

While selecting, a tap never opens. That is the whole point of the mode: one meaning per gesture at a time. Press Done to get tap-to-open back.

ui/dr-ui/ui/library.slint:1226

Leave selecting

  • Touch — Press Done in the header
  • Pointer — Press Done in the header
  • Keyboard — Escape

ui/dr-ui/ui/library.slint:1234

Pick a photograph up to drag it

  • Touch — Press and hold it until a ring opens around it, then drag
  • Pointer — Drag it

A finger on a photograph might be starting a scroll, and for the first half-second the grid assumes it is. Holding says otherwise, and the ring is the grid saying it heard — from there the drag cannot be lost to a scroll. A mouse never waits: the cursor is precise enough that a sideways drag is unambiguous from the first pixel.

ui/dr-ui/ui/library.slint:1264

Select a range

  • Touch — While selecting, press "Select to…", then tap the last photograph of the run
  • Pointer — Shift-click the last photograph of the run

This replaced a double tap, which had no visible state and could take forty photographs by accident. The run is resolved by the catalog rather than by what is on screen, so the grid can scroll between the two taps — the ranges that hurt on a tablet are longer than a screenful, which is exactly where a finger sweep runs out.

ui/dr-ui/ui/library.slint:1329

Find photographs with two people in them

  • Touch — Open the People chip on the filter bar, tap each name, then switch the chip beside them to "all of them"
  • Pointer — Open the People chip on the filter bar, click each name, then switch the chip beside them to "all of them"

"Any of them" is a union and "all of them" is an intersection. The tray is where both terms and the choice between them live, because a filter belongs on the filter bar.

ui/dr-ui/ui/library.slint:2011

Resize the thumbnails

  • Touch — Pinch the grid with two fingers
  • Pointer — Ctrl and the scroll wheel

There is no wheel on a tablet, so without the pinch the cell size could only be changed by a control a finger cannot reach.

ui/dr-ui/ui/library.slint:2664

File photographs in a collection

  • Touch — Drag a photograph — or a whole selection — onto a collection in the sidebar. Starting a drag stops the press becoming a hold, so it cannot leave you in selection mode.
  • Pointer — Drag a photograph — or a whole selection — onto a collection in the sidebar

The selection is what the drag carries, which is why selecting several is worth the mode: forty photographs file in one gesture.

ui/dr-ui/ui/library.slint:2827

Open a photograph

  • Touch — Tap it — a single tap, any length
  • Pointer — Click it

A tap opens; a tap that moved does not. Travel is what separates a deliberate tap from a hand brushing past, and it is the only thing that does: the two are the same length. An earlier version required the finger to dwell 120 ms instead, and that rejected ordinary taps — a real tap is often quicker than a brush.

ui/dr-ui/ui/library.slint:3095

Rate a photograph without opening it

  • Touch — Tap a star on the cell
  • Pointer — Hover the cell, then click a star
  • Keyboard — 0 to 5 on the selection

A star has to take the press without it also reaching the cell, or every rating throws the user into develop.

ui/dr-ui/ui/library.slint:3215

Drop the selection but keep selecting

  • Touch — Press Clear in the selection strip
  • Pointer — Press Clear in the selection strip

Distinct from Done, which leaves the mode entirely. Clearing keeps it, so the next selection can start straight away.

ui/dr-ui/ui/library.slint:3879

Select everything the grid is showing

  • Touch — While selecting, press "Select all"
  • Pointer — While selecting, press "Select all"

A scoped grid of two hundred frames is two hundred taps otherwise, and "all of them, except those three" is a far more common shape than the taps it took to say it.

ui/dr-ui/ui/library.slint:3896