Hold one key to see the photograph before you touched it

FR-DEV-7 asks for the current edit against the unedited original and nothing
implemented it. What the develop view had was history navigation, which
*changes* the edit rather than previewing against it — so the only way to
look was to undo, look, and redo, and that puts two real steps on the stack
at exactly the moment a photographer suspects they have overcooked a frame
and is least sure of what they are doing.

Holding the "Before" button, or backslash, renders the graph with every
adjustment stripped and hands it straight back afterwards: the same
suspend-render-restore shape the crop overlay already uses to show an
uncropped frame and an export uses to suspend the zoom. Nothing is recorded,
no rows are re-synced, and the photograph is still modified when the key
comes up — the panel goes on describing the edit the photographer has,
because only the canvas is answering a question.

The framing deliberately stays on. A held comparison is a question about
tone and colour, and re-cropping the canvas under someone's thumb would move
the detail they are comparing; worse, the zoom is a rectangle of the *framed*
image, so dropping the crop at 4× would quietly show a different part of the
photograph rather than the same part unedited. What the crop took away is
already compared in Compose, which shows the whole frame.

Not a split screen: that halves the working image on the tablet this column
was sized for, and the comparison photographers describe making is a flick
back and forth rather than two pictures side by side. Press-and-hold is one
gesture on a finger and on a mouse, which is what FR-DEV-3b's mapping wants,
and it has no mode to be stranded in — the button reports both edges, so a
press the system cancels puts the original down too.
This commit is contained in:
2026-09-06 19:01:51 +02:00
parent 9b674a88d8
commit 2584b9ecbc
5 changed files with 291 additions and 48 deletions
+64
View File
@@ -219,6 +219,24 @@ export component AppWindow inherits Window {
callback undo();
callback redo();
// --- before and after (FR-DEV-7) ---
/// TRACES: FR-DEV-7
/// Whether the canvas is currently showing the unedited original.
///
/// Rust owns it, and the ownership is the point: this is *not* a history
/// position and must never become one. Held down, the canvas renders the
/// photograph with the adjustments taken off and the framing left on; let
/// go, the edit comes back. Nothing is recorded, so a photographer can
/// check whether they have overcooked a frame without paying two steps of
/// undo for the look.
in property <bool> showing-original: false;
/// The comparison is being held, or has been let go.
///
/// Both edges through one callback rather than a press and a release, so
/// the two cannot get out of step and strand the view on the original.
callback compare-original(bool);
/// Every step, newest first. Rust owns the order and stamps each row with
/// its own position in the stack, so nothing here does arithmetic to turn
/// a row back into a step.
@@ -2182,11 +2200,30 @@ in property <bool> panel-visible: true;
// the canvas is noticed.
key-released(event) => {
root.shift-held = event.modifiers.shift;
// TRACES: FR-DEV-7
// Letting go of backslash puts the edit back. The
// release has to be handled here and not inferred
// from the next press, or a photographer who holds
// it and then reaches for a slider would be
// adjusting an image they cannot see.
if (event.text == "\\") {
root.compare-original(false);
return accept;
}
return reject;
}
key-pressed(event) => {
root.shift-held = event.modifiers.shift;
// TRACES: FR-DEV-7
// Hold backslash to see the unedited original.
// Rust ignores a repeat that says what it already
// knows, so the key repeating under a long look
// costs nothing.
if (event.text == "\\") {
root.compare-original(true);
return accept;
}
// Ctrl+Z and Ctrl+Shift+Z (FR-DEV-5). Both cases
// of the letter, because the logical key that
// reaches us carries the shift: holding it for
@@ -2335,6 +2372,33 @@ in property <bool> panel-visible: true;
// frame nobody was looking at the middle of.
clicked => { root.inspect-toggled(-1, -1); }
}
// TRACES: FR-DEV-7 | FR-DEV-3b | NFR-A11Y-2
// The unedited original, for as long as it is held.
//
// **A hold rather than a split screen.** A split view
// halves the working image, which on the tablet this
// column was sized for leaves neither half worth
// judging; and the comparison a photographer actually
// describes making is a flick back and forth, not two
// pictures side by side. A hold is also the cheaper
// thing to build: one extra render of a graph that is
// already there, against a second canvas that would
// have to be laid out, sized and kept in step.
//
// Press-and-hold is the same gesture with a finger and
// with a mouse, which is what FR-DEV-3b's mapping
// wants — no long-press timer, no mode, and no way to
// be left in it. The keyboard's backslash does the
// same thing on both edges; see the focus scope below.
Button {
text: "Before";
enabled: root.adjust-enabled;
active: root.showing-original;
accessible-checkable: true;
accessible-checked: root.showing-original;
held(down) => { root.compare-original(down); }
}
}
// The photo roll, along the foot of the canvas.