Drive develop, the grid, the sidebar and People from the keyboard
An audit of every action by view against the keys the handlers bind left develop without zoom, pan, fit or a way back to the grid, the grid without select-none, thumbnail size or keywording, People with no key at all, and the export and copy sheets without Enter. It also found the reverse gap FR-UI-5 forbids: pick and reject had no route but P, X and U, and the 2026-09-19 amendment's judging in develop had not been built. Develop: Ctrl+= and Ctrl+Plus zoom in and Ctrl+- out about the middle of the view, Ctrl+0 fits and Ctrl+1 goes to 1:1, Shift and an arrow pan a magnified view, G goes back to the grid, Ctrl+Y redoes, and Enter keeps a crop that hid a mask. 0-5, P, X and U rate and flag the open photograph without moving on, with stars and Pick/Reject in the top bar as the pointer and touch route. = and - nudge the control last moved by a hundredth of its travel; the framing sliders, perspective included, now count as "last moved", so R puts them back as well. J turns the selected mask part's join chip. Grid: Ctrl+D and Ctrl+Shift+A clear the selection, = and - resize the thumbnails, Ctrl+K opens keywording, and Flag in the selection bar gives pick and reject a pointer and touch route. Sidebar: Enter commits a collection's name, and Enter or Escape hands the keyboard back to the grid, where it used to go nowhere until something was clicked. People: Up and Down walk the rail, F2 puts the name field under the keys, and Escape or Back now leave the screen the way its back button does instead of doing nothing. Sheets: Enter does what the export or copy sheet's button does. The choices follow Lightroom where it has one. No new key steals typing: the grid's and People's keys live on focus holders that are not ancestors of any text field, and the sheets' Enter comes after a focused field has had it. Every binding is tagged beside its handler, and the gate added in the previous commit holds the two to each other.
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
// the confirm/reject pair sits on the face itself rather than behind a menu.
|
||||
|
||||
import { Theme } from "theme.slint";
|
||||
import { Keys } from "keys.slint";
|
||||
import { Panel, Button, IconButton, Field, ListView } from "widgets.slint";
|
||||
import { SliderRow } from "controls.slint";
|
||||
import { Icon } from "icons.slint";
|
||||
@@ -290,6 +291,11 @@ export component IdentityScreen inherits Rectangle {
|
||||
in property <string> back-label: "‹ Library";
|
||||
|
||||
callback person-picked(int);
|
||||
/// TRACES: FR-UI-5
|
||||
/// The person above (-1) or below (1) the selected one on the rail — the
|
||||
/// arrow keys. Rust walks the rail, because the selection is an id and
|
||||
/// only Rust can find it in the list.
|
||||
callback person-stepped(int);
|
||||
callback rename(string);
|
||||
callback confirm-face(int);
|
||||
callback reject-face(int);
|
||||
@@ -376,6 +382,50 @@ export component IdentityScreen inherits Rectangle {
|
||||
name-field.text = root.selected-name;
|
||||
}
|
||||
|
||||
// GESTURE: Move between people
|
||||
// where: People
|
||||
// touch: Tap a person on the rail
|
||||
// pointer: Click a person on the rail
|
||||
// keys: `Up` and `Down`, along the rail
|
||||
//
|
||||
// GESTURE: Name a person
|
||||
// where: People
|
||||
// touch: Tap the name field over their faces, type, and press Enter
|
||||
// pointer: Click the name field over their faces, type, and press Enter
|
||||
// keys: `F2` puts the name field under the keys
|
||||
// why: The rename key everywhere else. Enter finishes the name and
|
||||
// hands the keys back to the rail, so naming a run of people
|
||||
// is `Down`, `F2`, a name, Enter, over and over.
|
||||
//
|
||||
// TRACES: FR-UI-5
|
||||
// The screen's keys, on a holder of no size that takes focus on show, as
|
||||
// the grid's judgement keys do: without a focus item nothing on this
|
||||
// screen hears a key at all — Escape included, which bubbles from here to
|
||||
// the shell and leaves. Not an ancestor of the name field, so the arrows
|
||||
// move the caret there rather than the selection.
|
||||
screen-keys := FocusScope {
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
init => { self.focus(); }
|
||||
|
||||
// KEYMAP: People
|
||||
key-pressed(event) => {
|
||||
if (Keys.chord(event) == "Up") {
|
||||
root.person-stepped(-1);
|
||||
return accept;
|
||||
}
|
||||
if (Keys.chord(event) == "Down") {
|
||||
root.person-stepped(1);
|
||||
return accept;
|
||||
}
|
||||
if (Keys.chord(event) == "F2" && root.selected-person >= 0) {
|
||||
name-field.take-focus();
|
||||
return accept;
|
||||
}
|
||||
return reject;
|
||||
}
|
||||
}
|
||||
|
||||
HorizontalLayout {
|
||||
// ── the people rail ───────────────────────────────────────────────
|
||||
Panel {
|
||||
@@ -570,7 +620,9 @@ export component IdentityScreen inherits Rectangle {
|
||||
// and the screen looks stuck.
|
||||
accepted(t) => {
|
||||
root.rename(t);
|
||||
name-field.release-focus();
|
||||
// To the screen's keys rather than to nothing, so
|
||||
// the arrows walk the rail again straight away.
|
||||
screen-keys.focus();
|
||||
}
|
||||
}
|
||||
if root.selected-person < 0: Text {
|
||||
|
||||
Reference in New Issue
Block a user