Remember where the photographer was
Opening the application was always a fresh arrival at the beginning of the library, whatever you had been doing when you closed it. What is written down is the view, the scope, the rating filter and the photograph on screen -- the open one in develop, the first visible one in the grid. Not just a scroll position: a position without the filter that produced it names a row of a list that no longer exists. Restoring them has an order for the same reason -- scope, then filter, then position, then the view -- because each step changes what an ordinal *means*. Addressed by remote path and collection UUID, never by an ordinal or a row id. `images.id` and `collections.id` are local to one catalog, and a grid ordinal is local to one ordering; a record naming either would land somewhere arbitrary on a second device and after any filter change on this one. Where the ordinal is needed, `library::ordinal_of_path` computes it through the grid's own `ORDER BY`, taken verbatim by a window function rather than spelled a second time as an inequality -- which is the mistake `grid_order_for` already warns about, and which a manually ordered collection would make unreadable. Every failure degrades rather than reports. A collection this device has not merged leaves the scope at the whole library; a photograph that has since been deleted falls back to when it was taken, which puts the grid in the right week; a torn file yields no place and the library opens at the top. Reopening develop is the one thing that requires an exact match, because a canvas on a path that no longer resolves is a filename over an empty frame. The record lives in `dr-types` beside `Settings` and the store lives here beside `SettingsStore`, for the reason `dr-types`' manifest gives: a JSON serialiser in `core/` would be paid for by every crate there. Two files and two lifetimes, though -- resetting preferences must not forget where you were. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -451,3 +451,84 @@ rearranges everything.
|
||||
tablet has neither.
|
||||
- **FR-UI-5.** Escape and the Android back gesture leave the innermost state
|
||||
first. Every mode added here joins that order.
|
||||
|
||||
---
|
||||
|
||||
## 7. Place — the other half of "where am I"
|
||||
|
||||
§1 asked how someone *finds* anything. This asks how they stop losing what they
|
||||
already found. Both are navigation; the second is the one nobody notices until
|
||||
it is wrong, and then notices constantly.
|
||||
|
||||
### 7.1 Three failures, one cause
|
||||
|
||||
The library grid is gated on an `if` in the markup, so **every** route away from
|
||||
it destroys the subtree and rebuilds it on return. The Flickable inside passes
|
||||
its viewport through zero on the way out. Three consequences, reported
|
||||
separately and all the same bug:
|
||||
|
||||
- *"Opening Settings and coming back puts me at the top."* The guard on the
|
||||
scroll handler tested `show-library`, which stays true while Settings, Import,
|
||||
People or the launch screen covers the grid. The teardown's scroll-to-zero
|
||||
passed it, and the remembered position was overwritten with 0.
|
||||
- *"Coming out of develop I lose the photograph I was editing."* The position
|
||||
restored was where the *grid* was, not what was open — and walking the photo
|
||||
roll moves the second a long way from the first.
|
||||
- *"Launching puts me at the beginning."* Nothing was written down at all.
|
||||
|
||||
`app.slint` now computes `library-visible` once — the same expression the `if`
|
||||
is spelled from — and Rust reads that rather than `show-library`. The two cannot
|
||||
drift apart, which is what let them drift in the first place.
|
||||
|
||||
### 7.2 Two positions, and which one wins
|
||||
|
||||
Returning from develop has two candidates: `resume_at`, where the grid was, and
|
||||
the open photograph. They agree in the ordinary case and disagree after a walk
|
||||
along the roll.
|
||||
|
||||
The rule is not "pick one". The grid seeks to the remembered position, then
|
||||
`reveal()`s the keyboard cursor, which is on the open photograph and which moves
|
||||
the viewport as little as will bring it into view. A frame inside the remembered
|
||||
screenful moves nothing; one outside it scrolls exactly far enough. One rule,
|
||||
both behaviours — and the cursor rather than the selection, so a set of forty
|
||||
photographs assembled in the grid survives having one of them opened.
|
||||
|
||||
### 7.3 A place is not a scroll position
|
||||
|
||||
What gets written down is the view, the scope, the filter and the photograph —
|
||||
because a position without the filter that produced it names a row of a list
|
||||
that no longer exists. Restoring them has an order for the same reason: scope,
|
||||
then filter, then position, then the view. Each step changes what an ordinal
|
||||
*means*.
|
||||
|
||||
Addressed by remote path and collection UUID, never by an ordinal or a row id.
|
||||
See FR-UI-8 and `dr_types::place` for why, and `library::ordinal_of_path` for the
|
||||
one place the ordering is inverted — through the grid's own `ORDER BY`, taken
|
||||
verbatim, rather than spelled a second time.
|
||||
|
||||
### 7.4 The handover, and when to refuse it
|
||||
|
||||
The record travels through `.darkroom-derived/place.json`, so a session begun on
|
||||
the desktop continues on the tablet. Newest wins; there is nothing to merge.
|
||||
|
||||
The interesting decision is the refusal. A place arriving from another device is
|
||||
welcome on the way in and unwelcome the moment the photographer has started
|
||||
working — a grid that jumped somewhere else mid-scroll because a round trip
|
||||
finally landed would have lost their place to the feature meant to keep it. So
|
||||
any scroll, scrub, scope change, filter or opened photograph closes the latch,
|
||||
and a record that arrives after that is still written to disk and simply takes
|
||||
effect at the next launch.
|
||||
|
||||
### 7.5 Two smaller instruments that were saying nothing
|
||||
|
||||
Both were "correct" in the sense of not being wrong, and both were useless.
|
||||
|
||||
- **The capture-time marker** rested greyed at mid-track until the first scroll,
|
||||
on the reasoning that anchoring it would imply a choice the user had not made.
|
||||
But the sidebar's claim is to say *when* you are, and that is known from the
|
||||
first frame. It is now seeded from wherever the view sits.
|
||||
- **The photo roll** brought the open frame into view by the shortest move,
|
||||
which put it hard against one edge with nothing on that side. It now centres
|
||||
on the first reveal of a develop session and steps minimally thereafter —
|
||||
a one-shot request the strip consumes, so an overlay screen rebuilding the
|
||||
view does not undo a roll the user has scrolled by hand.
|
||||
|
||||
Reference in New Issue
Block a user