Come back from develop on the photograph you were editing

Leaving develop returned to where the *grid* was, which after a walk
along the photo roll can be a thousand rows from the frame you had just
finished. So the one photograph you were certainly interested in was the
one the grid came back without.

Two positions, and the rule is not to pick one of them. The grid seeks to
the remembered position, then reveals the keyboard cursor -- which is now
put on the open photograph, and which moves the viewport as little as
will bring its row into view. A frame inside the remembered screenful
moves nothing at all; one outside it scrolls exactly far enough. One
rule, both behaviours.

The cursor rather than the selection, deliberately: `place_cursor` also
rewrites the selection, and a set of forty photographs assembled in the
grid must survive having one of them opened.

`reveal()` now also runs on the grid's `init`, since `cursor-row` is
initialised rather than changed when the subtree is rebuilt and no
handler would otherwise fire. Both it and the roll's centring defer while
the element has no height yet -- `init` runs before layout, where a
height of zero makes every row look off screen -- and a latch brings the
first real height back to the cursor without letting every later resize
haul the viewport around.

The capture-time marker follows the same move, for the same reason:
`load_window` rebuilds the axis only when the scope, the filter or the
total has changed, and none of them has. It is the same library seen from
a different row.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-30 20:35:23 +02:00
co-authored by Claude Opus 5
parent 53dc2d171e
commit 0ff1e01ec3
4 changed files with 229 additions and 108 deletions
+37 -3
View File
@@ -2737,6 +2737,15 @@ export component LibraryGrid inherits Rectangle {
if (root.cursor < 0) {
return;
}
// Nothing to reveal *into* before layout has run, and this
// is now called from `init` as well as on a cursor move —
// where a height of zero would make every row look off
// screen and scroll the grid to the cursor when `seek` had
// just put it somewhere deliberate.
if (self.height <= 0px) {
return;
}
self.revealed = true;
let top = Theme.gap + self.cursor-row * self.pitch;
let shown = -self.viewport-y;
let bottom = max(0px, self.viewport-height - self.height);
@@ -2747,13 +2756,38 @@ export component LibraryGrid inherits Rectangle {
}
}
/// Whether a reveal has run against real geometry yet.
///
/// The safety net for the deferral above, and deliberately a
/// latch rather than a plain `changed height` handler: the first
/// height a rebuilt grid is given must bring the cursor into
/// view, and every height *after* that is a window being
/// resized, where dragging a corner should not keep hauling the
/// viewport back to a cursor the user is not looking at.
property <bool> revealed: false;
changed height => {
if (!self.revealed) {
self.reveal();
}
}
// Also on creation, which is what returning from the develop
// view needs. `show-library` gates an `if`, so the grid is built
// anew and `token` is *initialised* to the already-bumped value
// view needs. The grid is gated on an `if`, so it is built anew
// and `token` is *initialised* to the already-bumped value
// rather than changing to it — no `changed` handler fires, and
// without this the restored position would be dropped and the
// view would sit at the top.
init => { self.seek(); }
//
// `reveal()` after `seek()`, and the order is the point: seek
// puts the viewport where the grid was left, reveal then moves
// it as little as will bring the cursor's row into view. Coming
// back from develop the cursor is on the photograph that was
// open, so a frame inside the remembered screenful moves
// nothing at all and one outside it is scrolled just far enough
// to be seen. `cursor-row` is *initialised* here too, so its own
// `changed` handler never fires and this is the only thing that
// can do it.
init => { self.seek(); self.reveal(); }
// Sized to the **whole library**, not the loaded window. The
// scrollbar has to represent 23,971 images or there is no way to