summaryrefslogtreecommitdiff
path: root/docs/ui-and-interactions.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/ui-and-interactions.md')
-rw-r--r--docs/ui-and-interactions.md390
1 files changed, 390 insertions, 0 deletions
diff --git a/docs/ui-and-interactions.md b/docs/ui-and-interactions.md
new file mode 100644
index 0000000..05081e1
--- /dev/null
+++ b/docs/ui-and-interactions.md
@@ -0,0 +1,390 @@
+# UI & Interactions
+
+## Design language (gthumb, but KISS)
+
+The look and layout are reminiscent of **gthumb**: a libadwaita header bar with
+prev/next + zoom + view-toggle, a thumbnail grid like gthumb's browser grid,
+and a full-window dark viewer like gthumb's viewer mode — familiar to GNOME
+image-app users.
+
+The **KISS** part — things gthumb has that ggaze deliberately drops:
+- No folder/sidebar tree (single folder; you point ggaze at a path).
+- No catalogs, albums, tags, or search bar.
+- No status bar clutter — the header subtitle carries `n / total` + filename.
+- No batch-tools or editing toolbars; quick enhance is a popup, not a pane.
+- No location/path entry; the current folder is shown, not editable.
+
+Optional, off by default: a slim one-line status footer (filename · zoom ·
+size) for those who want it — toggled in settings, hidden otherwise.
+
+## Window layout
+
+- `GtkApplicationWindow` holding a `GtkStack` with two views:
+ - **Grid view** — `GgazeGrid`: a flow/grid of thumbnails for the whole folder.
+ - **Large view** — `GgazeViewer`: the single-image canvas, fills the window.
+- An overlay on top (auto-hide) carries the header bar — title = filename,
+ subtitle = `n / total` (remaining) · `N marked` when marks exist.
+- Header bar actions (icons): prev, next, zoom-fit, zoom-100, toggle-grid,
+ open-file, slideshow, menu (sort, background, empty-`./Trash`, about).
+- No sidebar, no tree, no toolbar beyond the header bar.
+- Transparent background behind the image (configurable: black / dark / grey
+ / checkered for transparency).
+
+## Opening files & folders
+
+- **CLI arg**: `ggaze IMG_0001.jpg` opens that file (large view, its folder as
+ the navigator); `ggaze ~/Downloads/Camera/` opens the folder directly in the
+ grid.
+- **Drag-and-drop**: drop a file or a folder onto the window to open it — a
+ folder opens the grid; a file opens it (large view, parent folder as
+ navigator); multiple files open the first file's folder in the grid with the
+ first selected. A drop highlight shows the window is a drop target.
+- **`o`** opens a file dialog that can pick a file or a folder.
+
+## Views & modes
+
+- **Grid view** — default on launch (when opening a file, jump straight to
+ large view on that file; `Esc` returns to grid). Thumbnails from the cache,
+ selection follows the navigator cursor.
+- **Large view** — one image, header bar visible (auto-hide after inactivity).
+- **Fullscreen** — `f` toggles (from large view); header hidden, image centered
+ on a dark backdrop. `Esc` exits fullscreen back to large; `q` quits.
+- **Slideshow** — auto-advance every N seconds (GSettings), fullscreen-only by
+ default; any manual key pauses/resumes.
+
+View switching never loses the cursor: grid→large opens on the selected cell;
+large→grid scrolls that cell into view and focuses it. **Marks persist across
+views** (a check badge in grid, an indicator in large), so you can mark in
+detail view and move from either.
+
+- **Move popup** — transient, opened by `m`. Lists configured destinations,
+ each with an auto-assigned hotkey (`1`-`9`, `0`, then `a`-`z` in order). Type
+ the hotkey to move the marked set (or the current image if none marked);
+ `Esc` cancels. Order in settings = hotkey order.
+
+## Keybindings (default, all reassignable later via GSettings)
+
+**Every GUI element has a corresponding hotkey, and it is shown on the
+element itself** — menu items print their key (e.g. `Move … m`),
+header-bar buttons show it in their tooltip, the move popup prints the digit
+beside each destination. Conversely, **every action that has a hotkey is also
+reachable through the GUI** (a header-bar button or an entry in the `F10`
+menu). Keyboard and mouse are equally first-class; neither is a fallback.
+Navigation is **vi-style plus cursor keys**: `h`/`l` and `←`/`→` move
+prev/next through the shoot; `j`/`k` and `↑`/`↓` pan when zoomed.
+
+| Key | Action |
+|----------------|--------|
+| `h` / `Left` | previous image |
+| `l` / `Right` | next image |
+| `j` / `Down` | pan down (when zoomed) |
+| `k` / `Up` | pan up (when zoomed) |
+| `H` / `L` | pan left / right (when zoomed) |
+| `v` | toggle mark on current image |
+| `V` | range-mark from last mark to current |
+| `Ctrl+a` | mark all images |
+| `Ctrl+c` | copy image (or marked files) to clipboard |
+| `g` | first image |
+| `G` | last image |
+| `Enter` | grid → large (open selected) |
+| `Esc` / `Back` | context back: fullscreen → large → grid → quit |
+| `t` | toggle grid ↔ large |
+| `+` / `=` | zoom in (large) / grow thumbnails (grid) |
+| `-` / `_` | zoom out (large) / shrink thumbnails (grid) |
+| `0` | zoom 100% / fit toggle (large) / reset thumbnail size (grid) |
+| `Space` | hold to compare original vs modified (large) |
+| `f` | toggle fullscreen |
+| `s` | save enhanced copy (GEGL); no auto-save |
+| `S` | toggle slideshow |
+| `i` | toggle info overlay |
+| `r` | reload (re-read file from disk) |
+| `d` | move to `./Trash` (confirm via toast), then next; undoable |
+| `D` | delete permanently (no trash), then next; no undo |
+| `m` | move marks (or current) → destination popup |
+| `e` | open current image in an external program → popup |
+| `!` | run a shell script → popup (e.g. `usbimport`) |
+| `a` | quick enhance → preset popup (GEGL; non-destructive) |
+| `c` | crop tool (GEGL; non-destructive, large view) |
+| `R` | straighten tool (GEGL; non-destructive, large view) |
+| `[` / `]` | rotate 90° CCW / CW (GEGL; non-destructive, one-shot) |
+| `u` | undo last `d` / `m` (restore from `./Trash` or move back) |
+| `o` | open file dialog |
+| `,` | preferences (destinations, sort, background, …) |
+| `F10` | open app menu (sort, background, empty `./Trash`, about) |
+| `?` | shortcuts overlay |
+| `q` | quit (exits fullscreen first) |
+
+`Esc` is *contextual back*: if there are marks, it clears them first; then in
+fullscreen it returns to large view, in large view it returns to the grid, in
+the grid it quits. `q` always quits outright (exiting fullscreen first).
+
+## Mouse / touch
+
+- **Scroll** — `zoom` (default), `pan-when-zoomed`, or `navigate` next/prev
+ — `scroll-behavior` setting.
+- **Click-drag** — pan when zoomed in.
+- **Double-click** — toggle fit ↔ 100%.
+- **Middle-click** — toggle fullscreen.
+- **Touch pinch** — zoom; **swipe** — next/prev; **two-finger tap** — info.
+
+## Zoom behavior
+
+- Fit-to-window is the default on load.
+- `0` toggles fit ↔ 100% (double-click also toggles).
+- Zoom centers on cursor (mouse) / pinch midpoint (touch) / window center (keys).
+- Panning clamps so the image can't drift off-screen.
+
+## Info overlay (`i`)
+
+Small card, top-left or bottom-right:
+- filename, dimensions, format, file size
+- EXIF: camera, lens, focal length, aperture, shutter, ISO, date taken,
+ orientation (auto-applied on load)
+- shot number within the current burst group (once burst grouping lands)
+- color space (once color management lands)
+Loaded lazily; never blocks display of the pixels.
+
+## Grid view behavior
+
+- Thumbnails load from the `thumbnail` cache (M7), decoding lazily as cells
+ scroll into view; never block the grid on a full decode.
+- Trashed (`./Trash`) and permanently-deleted items stay listed but **dimmed**
+ with a small badge, so you can see culling progress at a glance. (Toggle to
+ hide them entirely via a menu option / setting.)
+- `h`/`l`/`j`/`k`, arrow keys, `g`/`G`, click, and type-to-search (jump by
+ filename prefix) all move the cursor: `h`/`l` prev/next cell, `j`/`k` row
+ down/up. `Enter` opens large; `d`/`D` work here too.
+- Re-sorting (name / capture time / size) reflows the grid and keeps the
+ current image visible.
+- **Resizable thumbnails**: `+`/`-` grow/shrink the thumbnails, and the grid
+ auto-reflows (GtkFlowBox/GtkGridView) to fit the window — more columns when
+ small, fewer when large. Size persists in GSettings (`thumbnail-size`) and
+ is restored on next launch; `0` resets to default.
+- Marks: `v` toggles a check badge on the current cell; `V` range-marks;
+ `Ctrl+a` marks all. `d`/`D`/`m` act on the marked set (or current if none).
+
+## Selection & moving
+
+- **Marks** are a lightweight multi-select, shared by grid and large views.
+ Toggle with `v`, range with `V`, all with `Ctrl+a`, clear with `Esc`.
+- **`m` → move popup**: a small popover listing the configured destinations.
+ Each entry shows an auto-assigned hotkey in order — `1`, `2`, `3` … (then `0`,
+ then `a`-`z`). Example:
+ ```
+ Move 3 images to:
+ 1 irregular ninja
+ 2 alt irregular ninja
+ 3 something else
+ ```
+ Press `2` to move to "alt irregular ninja"; `Esc` cancels. Destinations
+ are configured in Preferences (`,`) as an ordered list of name → path pairs.
+- **Move semantics**: `g_file_move` (rename on same filesystem, else copy +
+ delete). On name collision in the destination, suffix `-1`, `-2`, …. After
+ a move, files leave the current folder: the navigator drops them, the grid
+ removes their cells, and the counter updates.
+- **Undo**: `u` undoes the last `d` (restore from `./Trash`) **or** the last
+ `m` (move the set back to their original paths). One level of undo to start.
+
+## Copy to clipboard
+
+- **`Ctrl+c`** (or menu *Copy*) puts the current picture on the clipboard so
+ you can paste it into other apps (Katogram, GIMP, chat clients) — like gthumb.
+- **No marks** → copies the **displayed** image **pixels** as PNG (modified
+ if a preview is active, else original) via `GdkClipboard` +
+ `GdkContentProvider` for `image/png`; pastes as an image. Decoding runs in a
+ `GTask` thread so the UI doesn't block.
+- **Marks present** → copies the marked **files** as `text/uri-list` (plus a
+ `text/plain` path list), so file-aware apps and file managers can paste them.
+- A toast confirms ("Copied image" / "Copied 3 files"). `Ctrl+Shift+c` (later)
+ copies the **original** (un-modified) image or the path.
+
+## Opening in an external program
+
+- **`e` → external-program popup**: same popover pattern as `m`. Lists the
+ configured programs, each with an auto-assigned hotkey (`1`, `2`, … then `0`,
+ `a`-`z`). Example:
+ ```
+ Open IMG_0001.jpg in:
+ 1 GIMP
+ 2 ImageMagick identify
+ 3 Nomacs
+ ```
+ Press `2` to launch that program with the current image's path; `Esc`
+ cancels.
+- Acts on the **current image** (the **original file on disk**, not the
+ preview); a later option may pass the marked set (`%F`). To open the modified
+ version, export it first (`s`).
+- Programs are configured in Preferences (`,`) as an ordered list of
+ name → command pairs. The command uses freedesktop `Exec` placeholders:
+ `%f` = the single current file path (e.g. `gimp %f`, `identify %f`).
+- Launch is **detached and non-blocking** (GSubprocess); ggaze stays
+ responsive and the image stays open. Failures show a toast.
+
+## Running shell scripts
+
+- **`!` → scripts popup**: same popover pattern as `m`/`e`. Lists the
+ configured shell scripts, each with an auto-assigned hotkey (`1`, `2`, …
+ then `0`, `a`-`z`). Example:
+ ```
+ Run script:
+ 1 usbimport (import from camera)
+ 2 build contact sheet
+ ```
+ Press `1` to run that script; `Esc` cancels.
+- Scripts run **asynchronously** via a shell (`/bin/sh -c`), so pipes,
+ redirection, and `~/` expansion all work. ggaze never blocks on them; a
+ toast shows "running usbimport…" while it runs.
+- Placeholders: `%f` = current image path, `%d` = current folder (e.g.
+ `~/scripts/usbimport %d`, or just `~/scripts/usbimport` with no args).
+ Substituted paths are single-quoted to avoid shell injection from filenames.
+- On completion, ggaze **rescans the directory** (scripts like `usbimport`
+ add files); a toast reports success/failure and the exit status.
+- Scripts are configured in Preferences (`,`) as an ordered list of
+ name → command pairs (`a(ss)`), separate from `editors`.
+
+## Quick enhance (GEGL, optional)
+
+- **`a` → enhance popup**: same popover pattern as `m`/`e`/`!`. Lists
+ configurable presets, each with an auto-assigned hotkey (`1`, `2`, … then
+ `0`, `a`-`z`). Example:
+ ```
+ Enhance IMG_0001.jpg:
+ 1 Auto-fix
+ 2 Brightness
+ 3 Contrast
+ 4 Saturation
+ 5 Sharpen
+ 6 Denoise
+ ```
+ Press `1` to apply "Auto-fix" as a **non-destructive live preview**; press
+ it again (or `Esc`) to turn the preview off.
+- Presets are GEGL op graphs (e.g. Auto-fix = `gegl:stretch-contrast` →
+ `gegl:color-enhance`; Brightness = `gegl:exposure`; Contrast =
+ `gegl:brightness-contrast`; Saturation = `gegl:saturation`; Sharpen =
+ `gegl:sharpen`). Configurable in Preferences (`,`) as `enhance-presets`
+ (`a(ss)` name → gegl-graph); the strength is just a number in the graph
+ text — tune it there, no slider UI needed.
+- **`s`** (or menu *Save enhanced copy…*) writes the enhanced result to
+ `<name>-enhanced.<ext>` via a GEGL saver — the **original is never
+ touched**. ggaze **never auto-saves**: an enhance preview is a live overlay
+ only.
+- **Dirty state + prompt on navigate:** an active (un-exported) enhance
+ preview is "dirty". Navigating to another image (`h`/`l`/`g`/`G`/click) or
+ quitting with a dirty preview prompts **Save** (export the copy, then
+ proceed), **Discard** (drop the preview, proceed), or **Cancel** (stay).
+ `s` saves and clears dirty; toggling the preview off (`Esc`/re-press)
+ discards it directly (explicit, no prompt).
+- GEGL runs only when a preset is active or on export; the fast decode path
+ is unchanged, and enhance is **not** applied during `h`/`l` scrubbing (only
+ when settled on an image). If the build has no GEGL, `a` shows a "GEGL not
+ built in" toast. See [gegl.md](gegl.md).
+
+## Crop, straighten & rotate tools (GEGL)
+
+Non-destructive, like enhance — they add ops to the live preview graph; `s`
+exports the result and navigating away prompts Save/Discard/Cancel. Large view
+only; in grid, `c`/`R` first switch to large on the selected cell. If GEGL is
+not built in, all show the "GEGL not built in" toast.
+
+- **`c` → crop tool:** overlay an adjustable crop rectangle on the image.
+ - Mouse: drag inside to move, drag edges/corners to resize.
+ - Keyboard: `h`/`l`/`j`/`k` move the rectangle; `H`/`L`/`J`/`K` resize the
+ edges; `1`-`4` set aspect ratio (1:1, 3:2, 4:3, 16:9), `0` free.
+ - `Enter` applies (`gegl:crop`), `Esc` cancels.
+- **`R` → straighten tool:** level the horizon.
+ - Mouse: drag a line along the horizon; the image rotates to align it.
+ - Keyboard: `h`/`l` (or `+`/`-`) nudge the angle by ±0.5°; a grid overlay
+ helps. Optional auto-crop to remove the rotated corners.
+ - `Enter` applies (`gegl:rotate`), `Esc` cancels.
+- **`[` / `]` → rotate 90°:** one-shot, no overlay — `]` clockwise, `[`
+ counterclockwise; repeat to reach 180°/270°. Non-destructive
+ (`gegl:rotate-on-center`); `s` exports the rotated copy.
+- All compose with enhance presets in the same preview graph.
+
+## Compare original vs modified (hold)
+
+- **Hold `Space`** to momentarily show the **original** image; **release** to
+ return to the **modified** (preview-graph) image — a quick before/after to
+ decide whether to `s` save. Only meaningful when a preview (enhance / crop /
+ straighten / rotate) is active; otherwise original == modified, no-op.
+- Large view only. GUI: menu *Show original* (toggle) for mouse users.
+
+## Hotkey visibility
+
+Hotkeys are not hidden — each is printed on the element it triggers:
+
+- **Menu items** show their key right-aligned, e.g. `Copy Ctrl+c`,
+ `Move … m`, `Open in … e`, `Scripts … !`, `Enhance … a`,
+ `Crop … c`, `Straighten … R`, `Rotate 90° ] / [`, `Save enhanced copy …
+ s`, `Show original (hold) Space`, `Slideshow S`, `Trash d`, `Delete D`,
+ `Preferences … ,`, `Fullscreen f`.
+- **Header-bar buttons** show the key in the tooltip (plus an underline
+ mnemonic where GTK draws one).
+- **Popup** entries (move / open-in / scripts / enhance) lead with the
+ hotkey: `1 irregular ninja`, `1 GIMP`, `1 usbimport`, `1 Auto-fix`.
+- **Shortcuts overlay** (`?`) lists everything in one place.
+
+If an element has no direct key, it lives in the `F10` menu (navigable with
+arrows + `Enter` and mnemonic underlines).
+
+## Reachability (keyboard + GUI)
+
+Every action is reachable **two ways**:
+
+- **By keyboard** — a direct hotkey, or via the `F10` menu (arrows + `Enter`).
+- **By mouse/GUI** — a header-bar button for the common ones, or an entry in
+ the `F10` app menu for the rest.
+
+So the keyboard alone, or the mouse alone, can reach everything. Header-bar
+buttons and their hotkeys:
+
+| Element | Hotkey |
+|--------------------|---------------|
+| prev / next | `h` / `l` |
+| zoom fit / 100% | `Space` / `0` |
+| zoom in / out | `+` / `-` |
+| toggle grid/large | `t` |
+| open file | `o` |
+| slideshow | `S` |
+| fullscreen | `f` |
+| info overlay | `i` |
+| app menu | `F10` |
+| preferences | `,` |
+
+App menu (via `F10`) items — each reachable by mnemonic, by arrows + `Enter`,
+and by click: Copy, Move…, Open in…, Scripts…, Enhance…, Crop…, Straighten…,
+Rotate 90° CW/CCW, Save enhanced copy…, Show original, Trash, Delete, Sort (by
+name / capture time / size), background colour, hide-trashed toggle, empty
+`./Trash`, About, Preferences….
+The move popup and all dialogs (open, preferences, shortcuts overlay) are
+likewise fully operable by keyboard and by mouse.
+
+## Self-documenting
+
+The UI explains itself; the manual is a bonus, not a requirement.
+
+- Hotkeys are printed on their elements (see Hotkey visibility).
+- Tooltips on every header-bar button and menu item: name + key + a one-line
+ hint.
+- `?` opens a shortcuts overlay listing every action and its key.
+- The header shows `n / total` + filename (+ `N marked` when marks exist);
+ the grid dim-marks trashed/deleted items and check-badges marks; the move
+ popup shows each destination's hotkey and the count being moved ("Move 3
+ images to:").
+- The info overlay (`i`) shows EXIF, format, and size on demand.
+- Empty states ("no images in this folder") and toasts ("Moved 3 → irregular
+ ninja", "Undo") narrate every side-effect.
+
+If a user can't tell what a key or button does by looking, that's a bug.
+
+## Culling workflow notes
+- `d` moves the file to `<folder>/.Trash/` (created lazily), advancing to the
+ next image automatically, so `d d d` clears a run of rejects without
+ re-aiming. A transient toast confirms and offers **Undo** (`u`).
+- `D` permanently deletes (unlinks) — fast path for obvious garbage; **no
+ undo**, so the toast warns and the grid badge marks it. Deleting **>1 marked**
+ image asks for a confirm dialog first.
+- Counter in the header (`n / total`) reflects *remaining* images so you can
+ see the folder shrinking as you cull.
+- `./Trash` lives with the shoot: easy to inspect, empty via the menu, or
+ `rsync`/`rm -rf` from a shell. Never the system trash. \ No newline at end of file