Merge: the library grid on its own Slint global, the last of the screen state off the root
This commit is contained in:
+126
-391
@@ -7,7 +7,7 @@ import { CropOverlay } from "crop.slint";
|
||||
import { HistoryPanel, HistoryRow, Steps } from "history.slint";
|
||||
import { LaunchScreen } from "launch.slint";
|
||||
import { IdentityScreen, IdentityPerson, IdentityFace } from "identity.slint";
|
||||
import { LibraryGrid, LibraryCell, TimelineBar, PhotoRoll, KeywordRow, PersonChip } from "library.slint";
|
||||
import { LibraryGrid, LibraryCell, TimelineBar, PhotoRoll, KeywordRow, PersonChip, Library } from "library.slint";
|
||||
import { GestureRow } from "gestures.slint";
|
||||
import { Button, PanelHeading, Label, Value, Caption, Panel, EmptyState, ProgressBar, ActivityRow } from "widgets.slint";
|
||||
import { CollectionsPanel, CollectionRow, Collections, OfflinePrompt, CollectionMenu,
|
||||
@@ -32,6 +32,7 @@ export { ViewMode, GradientHandle, HandleRole, SpotHandle, SpotRole }
|
||||
// panels take their wiring this way and why there is one global per family.
|
||||
export { Develop, Capture, Levels, Peaking, Adjustments, Framing, Transfer, Masking, Repair, Steps }
|
||||
export { Collections }
|
||||
export { Library }
|
||||
|
||||
/// Which top-level screen is on display. Exactly one at a time — replacing
|
||||
/// the `show-launch` / `show-library` / `show-identity` booleans, with
|
||||
@@ -288,8 +289,6 @@ export component AppWindow inherits Window {
|
||||
/// the two can drift apart again.
|
||||
out property <bool> library-visible:
|
||||
root.active-page == Page.none && root.active-view == View.library;
|
||||
in-out property <[LibraryCell]> library-cells;
|
||||
in property <int> library-total: 0;
|
||||
// --- background activity (FR-CAT-1, FR-NC-6c) ---
|
||||
//
|
||||
// Every background job in one register, filled by `activity.rs`. The
|
||||
@@ -311,74 +310,6 @@ export component AppWindow inherits Window {
|
||||
in property <int> activity-kept: 0;
|
||||
callback activity-clear-finished();
|
||||
|
||||
in property <bool> library-scanning: false;
|
||||
/// The catalog on this device is being opened and checked, on a worker,
|
||||
/// before the scan starts (`library_ui::open_catalog_soon`).
|
||||
///
|
||||
/// Separate from `library-scanning` because they are different answers to
|
||||
/// the same question: this one is reading a file that is already here, and
|
||||
/// that one is walking a tree over the network. Both are true at once for
|
||||
/// the first moment of a launch, and only the more specific of them is
|
||||
/// worth putting on an empty grid.
|
||||
in property <bool> library-opening: false;
|
||||
in property <string> library-status: "";
|
||||
in property <string> library-error: "";
|
||||
|
||||
// --- offline mode (FR-CAT-9) ---
|
||||
//
|
||||
// The library keeps working from local data when the server cannot be
|
||||
// reached: thumbnails come from the shards, and rating and sorting are
|
||||
// catalog operations that never needed the network. What stops is opening
|
||||
// an original that was never stored locally.
|
||||
//
|
||||
// `offline-reason` carries the transport's own message ("connection
|
||||
// refused", "dns error") because it is usually specific enough to act on,
|
||||
// where a bare "offline" leaves the user guessing whether it is their wifi
|
||||
// or the server.
|
||||
in property <bool> library-offline: false;
|
||||
in property <string> library-offline-reason: "";
|
||||
in property <string> library-offline-since: "";
|
||||
// Narrows the grid to images whose RAW is stored locally — the ones that
|
||||
// can actually be opened while offline. Off by default: the catalog is the
|
||||
// library, and hiding most of it the moment a connection drops would read
|
||||
// as data loss rather than as a filter.
|
||||
in-out property <bool> library-local-only: false;
|
||||
in property <int> library-local-count: 0;
|
||||
callback library-toggle-local-only();
|
||||
/// Whether the grid is narrowed to the timeline's visible span.
|
||||
in-out property <bool> library-range-active: false;
|
||||
callback library-toggle-date-range();
|
||||
/// TRACES: FR-CAT-5
|
||||
callback library-clear-selection();
|
||||
callback library-select-all();
|
||||
/// TRACES: FR-CAT-7
|
||||
/// Whether the grid is showing something with a manual order to change —
|
||||
/// a single manual collection, not a set and not a saved filter.
|
||||
in property <bool> library-reorderable: false;
|
||||
/// Move the selection beside the photograph at this row of the loaded
|
||||
/// window: after it where the drop landed on the cell's trailing half,
|
||||
/// before it otherwise.
|
||||
callback library-reorder-to(int, bool);
|
||||
callback library-collection-from-selection(string);
|
||||
/// TRACES: FR-CAT-6
|
||||
in property <string> library-range-from;
|
||||
in property <string> library-range-to;
|
||||
in property <bool> library-range-invalid: false;
|
||||
callback library-range-edited(string, string);
|
||||
/// The same range as a band on the capture-time axis, and the ends of it
|
||||
/// dragged. Fractions of the span the axis is drawn over — Rust owns the
|
||||
/// conversion both ways, so the band lands where the finger left it.
|
||||
in property <float> library-range-from-fraction: -1;
|
||||
in property <float> library-range-to-fraction: -1;
|
||||
callback library-timeline-range-changed(float, float);
|
||||
callback library-retry-connection();
|
||||
|
||||
// --- pinning a collection offline (FR-NC-6a) ---
|
||||
in property <bool> library-scope-pinned: false;
|
||||
in property <int> library-pin-done: 0;
|
||||
in property <int> library-pin-total: 0;
|
||||
callback library-toggle-pin-scope();
|
||||
|
||||
// The question a held collection asks: keep these on the device, or give
|
||||
// the disk back. Driven entirely from Rust — the counts and the byte sizes
|
||||
// in the labels are catalog reads, and an empty `title` is what closes it.
|
||||
@@ -408,32 +339,6 @@ export component AppWindow inherits Window {
|
||||
callback recovery-rebuild();
|
||||
callback recovery-dismiss();
|
||||
|
||||
in property <string> library-root-label: "";
|
||||
in-out property <[TimelineBar]> library-timeline;
|
||||
in property <string> library-timeline-label: "";
|
||||
in property <string> library-window-label: "";
|
||||
in property <int> library-offset: 0;
|
||||
|
||||
in property <int> library-sweep-done: 0;
|
||||
in property <int> library-sweep-total: 0;
|
||||
|
||||
in property <int> library-current-bucket: 0;
|
||||
in property <float> library-current-fraction: -1;
|
||||
in property <bool> library-timeline-anchored: false;
|
||||
|
||||
callback library-scrub-fraction(float);
|
||||
callback library-timeline-pinch(float);
|
||||
callback library-timeline-pan(float);
|
||||
callback library-timeline-zoom(int);
|
||||
in-out property <int> library-columns: 1;
|
||||
in property <bool> library-syncing: false;
|
||||
/// TRACES: FR-CAT-3 | FR-NC-3
|
||||
/// The whole-library thumbnail pass, launched from the settings page.
|
||||
/// `library-open` gates the button on there being a library to sweep —
|
||||
/// the page is reachable from the launch screen too.
|
||||
in property <bool> library-thumbnailing: false;
|
||||
in property <bool> library-open: false;
|
||||
|
||||
// --- people and faces (FR-CULL-8 … FR-CULL-12) ---
|
||||
//
|
||||
// A third top-level screen rather than a panel: naming a cluster and
|
||||
@@ -495,46 +400,9 @@ export component AppWindow inherits Window {
|
||||
callback identity-stop-indexing();
|
||||
callback identity-check-coverage();
|
||||
callback identity-delete-all();
|
||||
callback library-thumbnail-all();
|
||||
in-out property <length> library-cell-size: 180px;
|
||||
/// Whether this session is a touch one, which decides if the rating strip
|
||||
/// waits to be hovered or stands open. Seeded from the platform by Rust
|
||||
/// and latched by the grid on the first finger it sees.
|
||||
in-out property <bool> library-touched: false;
|
||||
callback library-zoom-cells(int);
|
||||
/// A pinch step on the grid: the ratio since the last update.
|
||||
callback library-pinch-cells(float);
|
||||
/// A pinch on the grid has begun, so the press that opened it was not one.
|
||||
callback library-pinch-started();
|
||||
in property <int> library-scroll-to: 0;
|
||||
in property <int> library-scroll-token: 0;
|
||||
/// Which row of the loaded window is the photograph currently open in
|
||||
/// develop, so the roll can mark it. `-1` when it is not in the window.
|
||||
in property <int> library-roll-current: -1;
|
||||
/// Whether the photo roll is out. Owned here rather than by the roll, so
|
||||
/// it survives the develop view being torn down and rebuilt.
|
||||
in-out property <bool> roll-open: false;
|
||||
/// Centre the roll on the open photograph the next time the strip settles.
|
||||
///
|
||||
/// Raised by Rust when a develop session begins — a photograph opened from
|
||||
/// the grid — and cleared by the strip itself once honoured. Owned here for
|
||||
/// the same reason `roll-open` is: the develop view is rebuilt whenever an
|
||||
/// overlay screen covers it, and a request raised before that must survive
|
||||
/// it. See `PhotoRoll::centre-request`.
|
||||
in-out property <bool> library-roll-centre: false;
|
||||
/// A photograph was chosen from the roll: the row within the loaded
|
||||
/// window, which is what a cell click reports too.
|
||||
callback library-roll-pick(int);
|
||||
callback library-sync-now();
|
||||
callback library-columns-changed();
|
||||
callback library-scrolled(int);
|
||||
/// How many cells the grid's viewport shows at once. Rust sizes the
|
||||
/// loaded window from it — see `library_ui::window_move`.
|
||||
callback library-viewport-cells(int);
|
||||
callback library-cell-clicked(int);
|
||||
callback library-rescan();
|
||||
/// Grid → launch screen, to pick a different folder or account.
|
||||
callback library-change();
|
||||
/// Develop → grid.
|
||||
callback back-to-library();
|
||||
|
||||
@@ -559,129 +427,6 @@ export component AppWindow inherits Window {
|
||||
/// Ask about a collection's local copies: the tray on a row, or a long
|
||||
/// press anywhere on it.
|
||||
callback collection-offline-menu(int);
|
||||
/// TRACES: FR-CAT-7 | FR-UI-4
|
||||
/// File the grid's selection in a collection without dragging it there:
|
||||
/// the target's id, and whether to take the images out of the collection
|
||||
/// currently being shown.
|
||||
callback library-file-in-collection(int, bool);
|
||||
/// TRACES: FR-CAT-5 | FR-CAT-6
|
||||
/// Keywording the grid's selection. The catalog has been searchable by
|
||||
/// keyword since it existed and there was nowhere to type one; this is it.
|
||||
///
|
||||
/// The vocabulary arrives already answered against the selection — each row
|
||||
/// says how many of the selected photographs carry that word — because only
|
||||
/// Rust knows what is selected, and a `.slint` file counting it would need
|
||||
/// the selection as a second model that could disagree with the first.
|
||||
in property <[KeywordRow]> library-keywords;
|
||||
/// The sheet is opening: recompute the rows against the selection as it
|
||||
/// stands now. Pulled rather than pushed, because the selection changes on
|
||||
/// every arrow key and the sheet is shut for almost all of them.
|
||||
callback library-keywords-opened();
|
||||
/// Put a keyword on the selection, creating it if it is new. By name, so a
|
||||
/// word typed into the field and a word tapped in the list are one path.
|
||||
callback library-assign-keyword(string);
|
||||
/// Take a keyword off the selection. Never deletes the keyword itself —
|
||||
/// it stays in the vocabulary and on every other photograph that carries it.
|
||||
callback library-unassign-keyword(string);
|
||||
/// TRACES: FR-UI-2
|
||||
/// Whether a tap in the grid selects rather than opens, and the button
|
||||
/// that turns it on. The long press does the same thing without it.
|
||||
in property <bool> library-select-mode: false;
|
||||
callback library-toggle-select-mode();
|
||||
/// TRACES: FR-CAT-7 | FR-UI-4
|
||||
/// The row a press has held long enough to pick up, or `-1`.
|
||||
///
|
||||
/// Set by the same hold that turns on selection mode and cleared by the
|
||||
/// grid when the press ends — `in-out` because both ends write it. See
|
||||
/// `held-row` in `library.slint` for what it draws and what it stops.
|
||||
in-out property <int> library-held-row: -1;
|
||||
/// A press on a cell ended, so the long-press timer can be cancelled.
|
||||
callback library-cell-press-ended();
|
||||
|
||||
// --- drag and drop ---
|
||||
//
|
||||
// Slint's own `DragArea`/`DropArea` carry this: pointer capture, the
|
||||
// click-versus-drag threshold, arbitration against the grid's Flickable,
|
||||
// and the image drawn under the cursor. So there is no badge to position
|
||||
// and no hover state to mirror here — only the payload, which Rust builds
|
||||
// from the selection, and the count the header shows.
|
||||
/// The drag payload, built by Rust from the current selection.
|
||||
pure callback library-drag-payload() -> data-transfer;
|
||||
/// The bitmap that travels under the cursor: one thumbnail, or a fanned
|
||||
/// stack where several images are being dragged. Composited in Rust.
|
||||
in property <image> library-drag-image;
|
||||
/// A drag began on a cell, so an unselected one can be promoted before the
|
||||
/// payload is read.
|
||||
callback library-drag-started(int);
|
||||
callback library-drag-finished();
|
||||
in property <int> library-selected-count: 0;
|
||||
|
||||
callback library-cell-pressed(int, bool, bool);
|
||||
callback library-remove-from-collection();
|
||||
|
||||
// --- where the selection is filed (FR-CAT-7) ---------------------------
|
||||
//
|
||||
// `library-remove-from-collection` can only ever mean the collection the
|
||||
// grid is scoped to — it is the only one that button can name. Opens the
|
||||
// membership sheet, whose state (`Collections.membership-open` and
|
||||
// `.membership-rows`) is on the sidebar's own global — only the library
|
||||
// side knows the selection has changed enough to be worth recomputing
|
||||
// the sheet against.
|
||||
callback library-open-membership();
|
||||
|
||||
// The keyboard cursor: where the arrows are in the library, as an image
|
||||
// ordinal. Rust owns it — clamping it needs the library's length, and
|
||||
// moving it may have to swap the loaded window underneath.
|
||||
in property <int> library-cursor: -1;
|
||||
callback library-move-cursor(int, bool);
|
||||
callback library-open-cursor();
|
||||
|
||||
// --- ratings and flags (FR-CAT-5, FR-CULL-4) ---
|
||||
//
|
||||
// Stars and pick/reject, set from the grid and persisted to the catalog
|
||||
// and the sidecar. Every image enters unrated, which is a state of its
|
||||
// own rather than a zero score.
|
||||
/// A star was clicked on a cell: row, then the rating 0..5.
|
||||
callback library-cell-rated(int, int);
|
||||
/// The trash target was clicked on one cell, by row.
|
||||
callback library-cell-trashed(int);
|
||||
/// The burst mark was clicked on one cell, by row (FR-CULL-5).
|
||||
callback library-burst-toggled(int);
|
||||
/// One frame of an open burst was named as the one the group folds to, by
|
||||
/// row (FR-CULL-5).
|
||||
callback library-burst-representative-chosen(int);
|
||||
/// Move the grid selection to the trash — the `Delete` key.
|
||||
callback library-trash-selection();
|
||||
/// A judgement key was pressed, applying to the whole selection. One of
|
||||
/// the two arguments is -1, saying which axis was *not* meant.
|
||||
callback library-judged(int, int);
|
||||
|
||||
/// Whose photographs the grid is narrowed to, one entry each.
|
||||
in property <[PersonChip]> library-filter-people;
|
||||
/// Whether those people are an intersection rather than a union.
|
||||
in property <bool> library-filter-people-all: false;
|
||||
callback library-filter-person-cleared(int);
|
||||
callback library-filter-people-mode-toggled();
|
||||
/// TRACES: FR-CULL-8a | FR-CULL-13
|
||||
/// Whether the grid is narrowed to photographs with nobody blinking.
|
||||
in property <bool> library-filter-eyes-open: false;
|
||||
callback library-filter-eyes-open-toggled();
|
||||
/// Everyone the library knows, for the filter bar's people tray.
|
||||
/// TRACES: FR-UI-4
|
||||
/// The gesture reference's rows, read from the generated table.
|
||||
in property <[GestureRow]> library-gestures;
|
||||
in property <[PersonChip]> library-people;
|
||||
callback library-people-listed();
|
||||
callback library-filter-person-toggled(int);
|
||||
in property <int> library-filter-min-rating: 0;
|
||||
in property <bool> library-filter-unjudged: false;
|
||||
in property <int> library-filter-flag: 0;
|
||||
/// Images per star count, index 0 unrated, for the filter chips.
|
||||
in-out property <[int]> library-rating-counts;
|
||||
|
||||
callback library-filter-min-rating-changed(int);
|
||||
callback library-filter-unjudged-changed(bool);
|
||||
callback library-filter-flag-changed(int);
|
||||
|
||||
callback next-image();
|
||||
callback prev-image();
|
||||
@@ -809,8 +554,6 @@ export component AppWindow inherits Window {
|
||||
in property <float> merge-fill-mirror: 48;
|
||||
in property <float> merge-fill-feather: 48;
|
||||
in property <bool> merge-done: false;
|
||||
/// Merge the grid's selection: opens the page and starts the alignment.
|
||||
callback library-merge-selection();
|
||||
/// The confirmation (FR-MRG-1). Nothing is written before this.
|
||||
callback merge-confirm();
|
||||
callback merge-abandon();
|
||||
@@ -1098,13 +841,6 @@ in property <bool> panel-visible: true;
|
||||
/// composes its own label around a selection count — so it is given the
|
||||
/// fact and not the sentence.
|
||||
in property <bool> export-to-server: false;
|
||||
/// Whether a batch is running. The grid's export button becomes its cancel
|
||||
/// button; there is nowhere else a long batch can be stopped from.
|
||||
in property <bool> library-exporting: false;
|
||||
/// Export the grid's selection in the background (FR-EXP-7).
|
||||
callback library-export-selection();
|
||||
/// TRACES: NFR-ARCH-3
|
||||
callback library-cancel-export();
|
||||
/// Show or hide the develop column.
|
||||
callback toggle-panel();
|
||||
/// Export the image on screen, using the settings as they stand.
|
||||
@@ -1410,7 +1146,7 @@ in property <bool> panel-visible: true;
|
||||
activity-running: root.activity-running;
|
||||
activity-kept: root.activity-kept;
|
||||
clear-finished() => { root.activity-clear-finished(); }
|
||||
thumbnailing: root.library-thumbnailing;
|
||||
thumbnailing: Library.library-thumbnailing;
|
||||
// The face pass reuses the Identity Manager's own state rather
|
||||
// than duplicating it: one job, startable from either place, and
|
||||
// a button that cannot disagree with the screen about whether it
|
||||
@@ -1421,8 +1157,8 @@ in property <bool> panel-visible: true;
|
||||
face-detector-labels: root.settings-face-detector-labels;
|
||||
face-detector-selected: root.settings-face-detector-selected;
|
||||
face-detector-picked(i) => { root.settings-face-detector-picked(i); }
|
||||
library-open: root.library-open;
|
||||
thumbnail-library() => { root.library-thumbnail-all(); }
|
||||
library-open: Library.library-open;
|
||||
thumbnail-library() => { Library.library-thumbnail-all(); }
|
||||
index-faces() => { root.identity-index(); }
|
||||
reindex-faces() => { root.identity-reindex(); }
|
||||
|
||||
@@ -1471,7 +1207,7 @@ in property <bool> panel-visible: true;
|
||||
confirm-all() => { root.identity-confirm-all(); }
|
||||
split-picked() => { root.identity-split-picked(); }
|
||||
compact: !root.expanded;
|
||||
photos-filtered: root.library-filter-people.length > 0;
|
||||
photos-filtered: Library.library-filter-people.length > 0;
|
||||
regrouping: root.identity-regrouping;
|
||||
regroup-status: root.identity-regroup-status;
|
||||
merge-probability: root.identity-merge-probability;
|
||||
@@ -1560,7 +1296,7 @@ in property <bool> panel-visible: true;
|
||||
height: 100%;
|
||||
rows: Collections.collection-rows;
|
||||
selected-id: root.collection-selected;
|
||||
total-images: root.library-total;
|
||||
total-images: Library.library-total;
|
||||
error: Collections.collection-error;
|
||||
|
||||
// TRACES: FR-NC-6a
|
||||
@@ -1568,10 +1304,10 @@ in property <bool> panel-visible: true;
|
||||
// referred to the tree rather than to the library.
|
||||
// They read the same window state the header did — one
|
||||
// source per fact — and call the same handlers.
|
||||
scanning: root.library-scanning;
|
||||
scope-pinned: root.library-scope-pinned;
|
||||
toggle-pin-scope() => { root.library-toggle-pin-scope(); }
|
||||
change-library() => { root.library-change(); }
|
||||
scanning: Library.library-scanning;
|
||||
scope-pinned: Library.library-scope-pinned;
|
||||
toggle-pin-scope() => { Library.library-toggle-pin-scope(); }
|
||||
change-library() => { Library.library-change(); }
|
||||
|
||||
select(id) => { root.collection-select(id); }
|
||||
toggle(id) => { Collections.collection-toggle(id); }
|
||||
@@ -1592,7 +1328,7 @@ in property <bool> panel-visible: true;
|
||||
rename-cancel() => { Collections.collection-rename-cancel(); }
|
||||
trash-count: Collections.trash-count;
|
||||
trash-label: Collections.trash-label;
|
||||
selected-count: root.library-selected-count;
|
||||
selected-count: Library.library-selected-count;
|
||||
|
||||
dropped-on(id) => { Collections.collection-dropped(id); }
|
||||
collection-drag-payload(id) => { return Collections.collection-drag-payload(id); }
|
||||
@@ -1610,50 +1346,50 @@ in property <bool> panel-visible: true;
|
||||
// 768 logical pixels, which fits the title and the status
|
||||
// line but not the library's buttons beside them.
|
||||
expanded: root.expanded;
|
||||
cells: root.library-cells;
|
||||
total: root.library-total;
|
||||
scanning: root.library-scanning;
|
||||
opening: root.library-opening;
|
||||
scan-status: root.library-status;
|
||||
scan-error: root.library-error;
|
||||
cells: Library.library-cells;
|
||||
total: Library.library-total;
|
||||
scanning: Library.library-scanning;
|
||||
opening: Library.library-opening;
|
||||
scan-status: Library.library-status;
|
||||
scan-error: Library.library-error;
|
||||
|
||||
offline: root.library-offline;
|
||||
offline-reason: root.library-offline-reason;
|
||||
offline-since: root.library-offline-since;
|
||||
retry-connection() => { root.library-retry-connection(); }
|
||||
local-only: root.library-local-only;
|
||||
local-count: root.library-local-count;
|
||||
toggle-local-only() => { root.library-toggle-local-only(); }
|
||||
range-active: root.library-range-active;
|
||||
toggle-date-range() => { root.library-toggle-date-range(); }
|
||||
clear-selection() => { root.library-clear-selection(); }
|
||||
select-all() => { root.library-select-all(); }
|
||||
reorderable: root.library-reorderable;
|
||||
reorder-to(row, after) => { root.library-reorder-to(row, after); }
|
||||
offline: Library.library-offline;
|
||||
offline-reason: Library.library-offline-reason;
|
||||
offline-since: Library.library-offline-since;
|
||||
retry-connection() => { Library.library-retry-connection(); }
|
||||
local-only: Library.library-local-only;
|
||||
local-count: Library.library-local-count;
|
||||
toggle-local-only() => { Library.library-toggle-local-only(); }
|
||||
range-active: Library.library-range-active;
|
||||
toggle-date-range() => { Library.library-toggle-date-range(); }
|
||||
clear-selection() => { Library.library-clear-selection(); }
|
||||
select-all() => { Library.library-select-all(); }
|
||||
reorderable: Library.library-reorderable;
|
||||
reorder-to(row, after) => { Library.library-reorder-to(row, after); }
|
||||
collection-from-selection(name) => {
|
||||
root.library-collection-from-selection(name);
|
||||
Library.library-collection-from-selection(name);
|
||||
}
|
||||
range-from: root.library-range-from;
|
||||
range-to: root.library-range-to;
|
||||
range-invalid: root.library-range-invalid;
|
||||
range-edited(a, b) => { root.library-range-edited(a, b); }
|
||||
range-from-fraction: root.library-range-from-fraction;
|
||||
range-to-fraction: root.library-range-to-fraction;
|
||||
range-from: Library.library-range-from;
|
||||
range-to: Library.library-range-to;
|
||||
range-invalid: Library.library-range-invalid;
|
||||
range-edited(a, b) => { Library.library-range-edited(a, b); }
|
||||
range-from-fraction: Library.library-range-from-fraction;
|
||||
range-to-fraction: Library.library-range-to-fraction;
|
||||
timeline-range-changed(a, b) => {
|
||||
root.library-timeline-range-changed(a, b);
|
||||
Library.library-timeline-range-changed(a, b);
|
||||
}
|
||||
|
||||
collections-visible: root.collections-visible;
|
||||
toggle-collections() => { root.toggle-collections(); }
|
||||
|
||||
pin-done: root.library-pin-done;
|
||||
pin-total: root.library-pin-total;
|
||||
root-label: root.library-root-label;
|
||||
timeline: root.library-timeline;
|
||||
timeline-label: root.library-timeline-label;
|
||||
window-label: root.library-window-label;
|
||||
offset: root.library-offset;
|
||||
selected-count: root.library-selected-count;
|
||||
pin-done: Library.library-pin-done;
|
||||
pin-total: Library.library-pin-total;
|
||||
root-label: Library.library-root-label;
|
||||
timeline: Library.library-timeline;
|
||||
timeline-label: Library.library-timeline-label;
|
||||
window-label: Library.library-window-label;
|
||||
offset: Library.library-offset;
|
||||
selected-count: Library.library-selected-count;
|
||||
scope-label: Collections.collection-scope-label;
|
||||
settings-armed: Transfer.armed;
|
||||
paste-settings-to-selection => {
|
||||
@@ -1664,108 +1400,108 @@ in property <bool> panel-visible: true;
|
||||
// sheet says it would act on and what the apply handler
|
||||
// reads back to decide it means the batch.
|
||||
open-presets => {
|
||||
root.preset-apply-count = root.library-selected-count;
|
||||
root.preset-apply-count = Library.library-selected-count;
|
||||
root.preset-can-save = false;
|
||||
root.presets-open = true;
|
||||
}
|
||||
|
||||
// TRACES: FR-EXP-7
|
||||
exporting: root.library-exporting;
|
||||
exporting: Library.library-exporting;
|
||||
export-to-server: root.export-to-server;
|
||||
export-selection => { root.library-export-selection(); }
|
||||
cancel-export => { root.library-cancel-export(); }
|
||||
export-selection => { Library.library-export-selection(); }
|
||||
cancel-export => { Library.library-cancel-export(); }
|
||||
// TRACES: FR-MRG-1
|
||||
merge-selection => { root.library-merge-selection(); }
|
||||
merge-selection => { Library.library-merge-selection(); }
|
||||
|
||||
sweep-done: root.library-sweep-done;
|
||||
sweep-total: root.library-sweep-total;
|
||||
sweep-done: Library.library-sweep-done;
|
||||
sweep-total: Library.library-sweep-total;
|
||||
|
||||
current-bucket: root.library-current-bucket;
|
||||
current-bucket-fraction: root.library-current-fraction;
|
||||
timeline-anchored: root.library-timeline-anchored;
|
||||
current-bucket: Library.library-current-bucket;
|
||||
current-bucket-fraction: Library.library-current-fraction;
|
||||
timeline-anchored: Library.library-timeline-anchored;
|
||||
|
||||
scrub-fraction(f) => { root.library-scrub-fraction(f); }
|
||||
timeline-pinch(r) => { root.library-timeline-pinch(r); }
|
||||
timeline-pan(d) => { root.library-timeline-pan(d); }
|
||||
timeline-zoom(d) => { root.library-timeline-zoom(d); }
|
||||
syncing: root.library-syncing;
|
||||
requested-cell-size: root.library-cell-size;
|
||||
touched <=> root.library-touched;
|
||||
zoom-cells(d) => { root.library-zoom-cells(d); }
|
||||
pinch-cells(r) => { root.library-pinch-cells(r); }
|
||||
pinch-started() => { root.library-pinch-started(); }
|
||||
scroll-to: root.library-scroll-to;
|
||||
scroll-token: root.library-scroll-token;
|
||||
sync-now() => { root.library-sync-now(); }
|
||||
scrub-fraction(f) => { Library.library-scrub-fraction(f); }
|
||||
timeline-pinch(r) => { Library.library-timeline-pinch(r); }
|
||||
timeline-pan(d) => { Library.library-timeline-pan(d); }
|
||||
timeline-zoom(d) => { Library.library-timeline-zoom(d); }
|
||||
syncing: Library.library-syncing;
|
||||
requested-cell-size: Library.library-cell-size;
|
||||
touched <=> Library.library-touched;
|
||||
zoom-cells(d) => { Library.library-zoom-cells(d); }
|
||||
pinch-cells(r) => { Library.library-pinch-cells(r); }
|
||||
pinch-started() => { Library.library-pinch-started(); }
|
||||
scroll-to: Library.library-scroll-to;
|
||||
scroll-token: Library.library-scroll-token;
|
||||
sync-now() => { Library.library-sync-now(); }
|
||||
columns-changed(n) => {
|
||||
root.library-columns = n;
|
||||
Library.library-columns = n;
|
||||
// Which cell begins a row just changed, and month headings sit on
|
||||
// row-leading cells — so they must be recomputed, not just moved.
|
||||
root.library-columns-changed();
|
||||
Library.library-columns-changed();
|
||||
}
|
||||
scrolled(i) => { root.library-scrolled(i); }
|
||||
viewport-cells-changed(n) => { root.library-viewport-cells(n); }
|
||||
cell-clicked(i) => { root.library-cell-clicked(i); }
|
||||
rescan() => { root.library-rescan(); }
|
||||
scrolled(i) => { Library.library-scrolled(i); }
|
||||
viewport-cells-changed(n) => { Library.library-viewport-cells(n); }
|
||||
cell-clicked(i) => { Library.library-cell-clicked(i); }
|
||||
rescan() => { Library.library-rescan(); }
|
||||
can-import: root.import-supported;
|
||||
open-import() => { root.import-open(); }
|
||||
open-settings() => { root.settings-open(); }
|
||||
open-people() => { root.identity-open(); }
|
||||
|
||||
cell-pressed(i, ctrl, shift) => {
|
||||
root.library-cell-pressed(i, ctrl, shift);
|
||||
Library.library-cell-pressed(i, ctrl, shift);
|
||||
}
|
||||
cell-press-ended() => { root.library-cell-press-ended(); }
|
||||
select-mode: root.library-select-mode;
|
||||
toggle-select-mode() => { root.library-toggle-select-mode(); }
|
||||
held-row <=> root.library-held-row;
|
||||
cell-press-ended() => { Library.library-cell-press-ended(); }
|
||||
select-mode: Library.library-select-mode;
|
||||
toggle-select-mode() => { Library.library-toggle-select-mode(); }
|
||||
held-row <=> Library.library-held-row;
|
||||
// The sidebar's rows, not a second model: the sheet files
|
||||
// into the same tree the sidebar draws.
|
||||
collections: Collections.collection-rows;
|
||||
file-in-collection(id, moves) => {
|
||||
root.library-file-in-collection(id, moves);
|
||||
Library.library-file-in-collection(id, moves);
|
||||
}
|
||||
keywords: root.library-keywords;
|
||||
keywords-opened() => { root.library-keywords-opened(); }
|
||||
assign-keyword(word) => { root.library-assign-keyword(word); }
|
||||
unassign-keyword(word) => { root.library-unassign-keyword(word); }
|
||||
cursor: root.library-cursor;
|
||||
keywords: Library.library-keywords;
|
||||
keywords-opened() => { Library.library-keywords-opened(); }
|
||||
assign-keyword(word) => { Library.library-assign-keyword(word); }
|
||||
unassign-keyword(word) => { Library.library-unassign-keyword(word); }
|
||||
cursor: Library.library-cursor;
|
||||
move-cursor(delta, extend) => {
|
||||
root.library-move-cursor(delta, extend);
|
||||
Library.library-move-cursor(delta, extend);
|
||||
}
|
||||
open-cursor() => { root.library-open-cursor(); }
|
||||
drag-image: root.library-drag-image;
|
||||
drag-payload() => { return root.library-drag-payload(); }
|
||||
drag-started(i) => { root.library-drag-started(i); }
|
||||
drag-finished() => { root.library-drag-finished(); }
|
||||
open-cursor() => { Library.library-open-cursor(); }
|
||||
drag-image: Library.library-drag-image;
|
||||
drag-payload() => { return Library.library-drag-payload(); }
|
||||
drag-started(i) => { Library.library-drag-started(i); }
|
||||
drag-finished() => { Library.library-drag-finished(); }
|
||||
remove-from-collection() => {
|
||||
root.library-remove-from-collection();
|
||||
Library.library-remove-from-collection();
|
||||
}
|
||||
open-membership() => { root.library-open-membership(); }
|
||||
open-membership() => { Library.library-open-membership(); }
|
||||
|
||||
filter-people: root.library-filter-people;
|
||||
filter-people-all: root.library-filter-people-all;
|
||||
filter-eyes-open: root.library-filter-eyes-open;
|
||||
people: root.library-people;
|
||||
gestures: root.library-gestures;
|
||||
filter-min-rating: root.library-filter-min-rating;
|
||||
filter-unjudged: root.library-filter-unjudged;
|
||||
filter-flag: root.library-filter-flag;
|
||||
rating-counts: root.library-rating-counts;
|
||||
filter-people: Library.library-filter-people;
|
||||
filter-people-all: Library.library-filter-people-all;
|
||||
filter-eyes-open: Library.library-filter-eyes-open;
|
||||
people: Library.library-people;
|
||||
gestures: Library.library-gestures;
|
||||
filter-min-rating: Library.library-filter-min-rating;
|
||||
filter-unjudged: Library.library-filter-unjudged;
|
||||
filter-flag: Library.library-filter-flag;
|
||||
rating-counts: Library.library-rating-counts;
|
||||
|
||||
cell-rated(i, n) => { root.library-cell-rated(i, n); }
|
||||
cell-trashed(i) => { root.library-cell-trashed(i); }
|
||||
burst-toggled(i) => { root.library-burst-toggled(i); }
|
||||
cell-rated(i, n) => { Library.library-cell-rated(i, n); }
|
||||
cell-trashed(i) => { Library.library-cell-trashed(i); }
|
||||
burst-toggled(i) => { Library.library-burst-toggled(i); }
|
||||
burst-representative-chosen(i) => {
|
||||
root.library-burst-representative-chosen(i);
|
||||
Library.library-burst-representative-chosen(i);
|
||||
}
|
||||
trash-selection() => { root.library-trash-selection(); }
|
||||
trash-selection() => { Library.library-trash-selection(); }
|
||||
// Derived from the sidebar's own selection rather than
|
||||
// mirrored in a second property: `-1` is already the sentinel
|
||||
// for the trash row (see collections.slint), and two sources
|
||||
// for one fact is how they come to disagree.
|
||||
viewing-trash: root.collection-selected == -1;
|
||||
judged(rating, flag) => { root.library-judged(rating, flag); }
|
||||
judged(rating, flag) => { Library.library-judged(rating, flag); }
|
||||
// `F2` renames whatever the grid is scoped to. Answered from
|
||||
// the sidebar's selection rather than a second property, for
|
||||
// the same reason `viewing-trash` above is: one fact, one
|
||||
@@ -1776,17 +1512,17 @@ in property <bool> panel-visible: true;
|
||||
Collections.collection-rename-start(root.collection-selected);
|
||||
}
|
||||
filter-min-rating-changed(n) => {
|
||||
root.library-filter-min-rating-changed(n);
|
||||
Library.library-filter-min-rating-changed(n);
|
||||
}
|
||||
filter-unjudged-toggled(on) => {
|
||||
root.library-filter-unjudged-changed(on);
|
||||
Library.library-filter-unjudged-changed(on);
|
||||
}
|
||||
filter-flag-changed(f) => { root.library-filter-flag-changed(f); }
|
||||
filter-person-cleared(id) => { root.library-filter-person-cleared(id); }
|
||||
filter-people-mode-toggled() => { root.library-filter-people-mode-toggled(); }
|
||||
filter-eyes-open-toggled() => { root.library-filter-eyes-open-toggled(); }
|
||||
people-listed() => { root.library-people-listed(); }
|
||||
filter-person-toggled(id) => { root.library-filter-person-toggled(id); }
|
||||
filter-flag-changed(f) => { Library.library-filter-flag-changed(f); }
|
||||
filter-person-cleared(id) => { Library.library-filter-person-cleared(id); }
|
||||
filter-people-mode-toggled() => { Library.library-filter-people-mode-toggled(); }
|
||||
filter-eyes-open-toggled() => { Library.library-filter-eyes-open-toggled(); }
|
||||
people-listed() => { Library.library-people-listed(); }
|
||||
filter-person-toggled(id) => { Library.library-filter-person-toggled(id); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1805,7 +1541,7 @@ in property <bool> panel-visible: true;
|
||||
position: root.total > 0 ? root.index + 1 + " / " + root.total : "";
|
||||
// Only where a library was opened; command-line files have no
|
||||
// grid to return to.
|
||||
can-return-to-library: root.library-total > 0;
|
||||
can-return-to-library: Library.library-total > 0;
|
||||
panel-visible: root.panel-visible;
|
||||
export-label: root.export-label;
|
||||
export-busy: root.export-busy;
|
||||
@@ -2315,7 +2051,6 @@ in property <bool> panel-visible: true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --- crop overlay ------------------------------------------
|
||||
//
|
||||
// Placed over the fitted image, so the fractions it draws are
|
||||
@@ -2657,7 +2392,7 @@ in property <bool> panel-visible: true;
|
||||
// instead — the controls ride on top of the roll's
|
||||
// band, at whatever height it currently stands.
|
||||
property <length> roll-clear:
|
||||
root.library-total > 0
|
||||
Library.library-total > 0
|
||||
? (root.roll-open
|
||||
? Theme.roll-strip + Theme.roll-reach
|
||||
: Theme.roll-reach)
|
||||
@@ -2770,14 +2505,14 @@ in property <bool> panel-visible: true;
|
||||
//
|
||||
// Last in the canvas, so it draws over the photograph
|
||||
// rather than under it.
|
||||
if root.library-total > 0: PhotoRoll {
|
||||
if Library.library-total > 0: PhotoRoll {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
cells: root.library-cells;
|
||||
current: root.library-roll-current;
|
||||
cells: Library.library-cells;
|
||||
current: Library.library-roll-current;
|
||||
open <=> root.roll-open;
|
||||
centre-request <=> root.library-roll-centre;
|
||||
pick(i) => { root.library-roll-pick(i); }
|
||||
centre-request <=> Library.library-roll-centre;
|
||||
pick(i) => { Library.library-roll-pick(i); }
|
||||
}
|
||||
|
||||
// Report size changes so the render target can be resized to
|
||||
@@ -3179,7 +2914,7 @@ in property <bool> panel-visible: true;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
open: Collections.membership-open;
|
||||
selected-count: root.library-selected-count;
|
||||
selected-count: Library.library-selected-count;
|
||||
rows: Collections.membership-rows;
|
||||
remove(id) => { Collections.membership-remove(id); }
|
||||
dismiss() => { Collections.membership-dismiss(); }
|
||||
|
||||
@@ -479,6 +479,276 @@ export struct LibraryCell {
|
||||
burst-representative: bool,
|
||||
}
|
||||
|
||||
// TRACES: FR-CAT-3 | FR-CAT-4 | FR-CAT-5 | FR-CAT-6 | FR-CAT-7 | FR-CULL-4 | FR-CULL-5
|
||||
// The library grid's own state: the loaded window of cells, the selection,
|
||||
// the timeline, the filter bar, ratings and flags, keywording, drag and drop,
|
||||
// and the routes into and out of the screen.
|
||||
//
|
||||
// A global rather than properties and callbacks on the window root — see
|
||||
// `session.slint` for why the develop panels stopped taking their wiring
|
||||
// through the window root, and `collections.slint` for the sidebar's own
|
||||
// global; the same reasoning applies here. What is shared with the sidebar
|
||||
// (the collection the grid is scoped to, whether the sidebar is shown) stays
|
||||
// on the window, because `run()`'s panel-layout wiring and the sidebar's own
|
||||
// Rust still reach it there.
|
||||
export global Library {
|
||||
in-out property <[LibraryCell]> library-cells;
|
||||
in property <int> library-total: 0;
|
||||
// Narrows the grid to images whose RAW is stored locally — the ones that
|
||||
// can actually be opened while offline. Off by default: the catalog is the
|
||||
// library, and hiding most of it the moment a connection drops would read
|
||||
// as data loss rather than as a filter.
|
||||
in-out property <bool> library-local-only: false;
|
||||
in property <int> library-local-count: 0;
|
||||
callback library-toggle-local-only();
|
||||
/// TRACES: FR-CAT-5
|
||||
callback library-clear-selection();
|
||||
callback library-select-all();
|
||||
/// TRACES: FR-CAT-7
|
||||
/// Whether the grid is showing something with a manual order to change —
|
||||
/// a single manual collection, not a set and not a saved filter.
|
||||
in property <bool> library-reorderable: false;
|
||||
/// Move the selection beside the photograph at this row of the loaded
|
||||
/// window: after it where the drop landed on the cell's trailing half,
|
||||
/// before it otherwise.
|
||||
callback library-reorder-to(int, bool);
|
||||
callback library-collection-from-selection(string);
|
||||
in-out property <int> library-columns: 1;
|
||||
in-out property <length> library-cell-size: 180px;
|
||||
/// Whether this session is a touch one, which decides if the rating strip
|
||||
/// waits to be hovered or stands open. Seeded from the platform by Rust
|
||||
/// and latched by the grid on the first finger it sees.
|
||||
in-out property <bool> library-touched: false;
|
||||
callback library-zoom-cells(int);
|
||||
/// A pinch step on the grid: the ratio since the last update.
|
||||
callback library-pinch-cells(float);
|
||||
/// A pinch on the grid has begun, so the press that opened it was not one.
|
||||
callback library-pinch-started();
|
||||
in property <int> library-scroll-to: 0;
|
||||
in property <int> library-scroll-token: 0;
|
||||
/// Which row of the loaded window is the photograph currently open in
|
||||
/// develop, so the roll can mark it. `-1` when it is not in the window.
|
||||
in property <int> library-roll-current: -1;
|
||||
/// Centre the roll on the open photograph the next time the strip settles.
|
||||
///
|
||||
/// Raised by Rust when a develop session begins — a photograph opened from
|
||||
/// the grid — and cleared by the strip itself once honoured. Owned here for
|
||||
/// the same reason `roll-open` is: the develop view is rebuilt whenever an
|
||||
/// overlay screen covers it, and a request raised before that must survive
|
||||
/// it. See `PhotoRoll::centre-request`.
|
||||
in-out property <bool> library-roll-centre: false;
|
||||
/// A photograph was chosen from the roll: the row within the loaded
|
||||
/// window, which is what a cell click reports too.
|
||||
callback library-roll-pick(int);
|
||||
callback library-columns-changed();
|
||||
callback library-scrolled(int);
|
||||
/// How many cells the grid's viewport shows at once. Rust sizes the
|
||||
/// loaded window from it — see `library_ui::window_move`.
|
||||
callback library-viewport-cells(int);
|
||||
callback library-cell-clicked(int);
|
||||
/// TRACES: FR-CAT-7 | FR-UI-4
|
||||
/// File the grid's selection in a collection without dragging it there:
|
||||
/// the target's id, and whether to take the images out of the collection
|
||||
/// currently being shown.
|
||||
callback library-file-in-collection(int, bool);
|
||||
/// TRACES: FR-UI-2
|
||||
/// Whether a tap in the grid selects rather than opens, and the button
|
||||
/// that turns it on. The long press does the same thing without it.
|
||||
in property <bool> library-select-mode: false;
|
||||
callback library-toggle-select-mode();
|
||||
/// TRACES: FR-CAT-7 | FR-UI-4
|
||||
/// The row a press has held long enough to pick up, or `-1`.
|
||||
///
|
||||
/// Set by the same hold that turns on selection mode and cleared by the
|
||||
/// grid when the press ends — `in-out` because both ends write it. See
|
||||
/// `held-row` in `library.slint` for what it draws and what it stops.
|
||||
in-out property <int> library-held-row: -1;
|
||||
/// A press on a cell ended, so the long-press timer can be cancelled.
|
||||
callback library-cell-press-ended();
|
||||
// --- drag and drop ---
|
||||
//
|
||||
// Slint's own `DragArea`/`DropArea` carry this: pointer capture, the
|
||||
// click-versus-drag threshold, arbitration against the grid's Flickable,
|
||||
// and the image drawn under the cursor. So there is no badge to position
|
||||
// and no hover state to mirror here — only the payload, which Rust builds
|
||||
// from the selection, and the count the header shows.
|
||||
/// The drag payload, built by Rust from the current selection.
|
||||
pure callback library-drag-payload() -> data-transfer;
|
||||
/// The bitmap that travels under the cursor: one thumbnail, or a fanned
|
||||
/// stack where several images are being dragged. Composited in Rust.
|
||||
in property <image> library-drag-image;
|
||||
/// A drag began on a cell, so an unselected one can be promoted before the
|
||||
/// payload is read.
|
||||
callback library-drag-started(int);
|
||||
callback library-drag-finished();
|
||||
in property <int> library-selected-count: 0;
|
||||
|
||||
callback library-cell-pressed(int, bool, bool);
|
||||
callback library-remove-from-collection();
|
||||
// --- where the selection is filed (FR-CAT-7) ---------------------------
|
||||
//
|
||||
// `library-remove-from-collection` can only ever mean the collection the
|
||||
// grid is scoped to — it is the only one that button can name. Opens the
|
||||
// membership sheet, whose state (`Collections.membership-open` and
|
||||
// `.membership-rows`) is on the sidebar's own global — only the library
|
||||
// side knows the selection has changed enough to be worth recomputing
|
||||
// the sheet against.
|
||||
callback library-open-membership();
|
||||
// The keyboard cursor: where the arrows are in the library, as an image
|
||||
// ordinal. Rust owns it — clamping it needs the library's length, and
|
||||
// moving it may have to swap the loaded window underneath.
|
||||
in property <int> library-cursor: -1;
|
||||
callback library-move-cursor(int, bool);
|
||||
callback library-open-cursor();
|
||||
/// TRACES: FR-CAT-5 | FR-CAT-6
|
||||
/// Keywording the grid's selection. The catalog has been searchable by
|
||||
/// keyword since it existed and there was nowhere to type one; this is it.
|
||||
///
|
||||
/// The vocabulary arrives already answered against the selection — each row
|
||||
/// says how many of the selected photographs carry that word — because only
|
||||
/// Rust knows what is selected, and a `.slint` file counting it would need
|
||||
/// the selection as a second model that could disagree with the first.
|
||||
in property <[KeywordRow]> library-keywords;
|
||||
/// The sheet is opening: recompute the rows against the selection as it
|
||||
/// stands now. Pulled rather than pushed, because the selection changes on
|
||||
/// every arrow key and the sheet is shut for almost all of them.
|
||||
callback library-keywords-opened();
|
||||
/// Put a keyword on the selection, creating it if it is new. By name, so a
|
||||
/// word typed into the field and a word tapped in the list are one path.
|
||||
callback library-assign-keyword(string);
|
||||
/// Take a keyword off the selection. Never deletes the keyword itself —
|
||||
/// it stays in the vocabulary and on every other photograph that carries it.
|
||||
callback library-unassign-keyword(string);
|
||||
// --- ratings and flags (FR-CAT-5, FR-CULL-4) ---
|
||||
//
|
||||
// Stars and pick/reject, set from the grid and persisted to the catalog
|
||||
// and the sidecar. Every image enters unrated, which is a state of its
|
||||
// own rather than a zero score.
|
||||
/// A star was clicked on a cell: row, then the rating 0..5.
|
||||
callback library-cell-rated(int, int);
|
||||
/// The trash target was clicked on one cell, by row.
|
||||
callback library-cell-trashed(int);
|
||||
/// The burst mark was clicked on one cell, by row (FR-CULL-5).
|
||||
callback library-burst-toggled(int);
|
||||
/// One frame of an open burst was named as the one the group folds to, by
|
||||
/// row (FR-CULL-5).
|
||||
callback library-burst-representative-chosen(int);
|
||||
/// Move the grid selection to the trash — the `Delete` key.
|
||||
callback library-trash-selection();
|
||||
/// A judgement key was pressed, applying to the whole selection. One of
|
||||
/// the two arguments is -1, saying which axis was *not* meant.
|
||||
callback library-judged(int, int);
|
||||
in property <int> library-filter-min-rating: 0;
|
||||
in property <bool> library-filter-unjudged: false;
|
||||
in property <int> library-filter-flag: 0;
|
||||
/// Images per star count, index 0 unrated, for the filter chips.
|
||||
in-out property <[int]> library-rating-counts;
|
||||
callback library-filter-min-rating-changed(int);
|
||||
callback library-filter-unjudged-changed(bool);
|
||||
callback library-filter-flag-changed(int);
|
||||
/// Whether the grid is narrowed to the timeline's visible span.
|
||||
in-out property <bool> library-range-active: false;
|
||||
callback library-toggle-date-range();
|
||||
/// TRACES: FR-CAT-6
|
||||
in property <string> library-range-from;
|
||||
in property <string> library-range-to;
|
||||
in property <bool> library-range-invalid: false;
|
||||
callback library-range-edited(string, string);
|
||||
/// The same range as a band on the capture-time axis, and the ends of it
|
||||
/// dragged. Fractions of the span the axis is drawn over — Rust owns the
|
||||
/// conversion both ways, so the band lands where the finger left it.
|
||||
in property <float> library-range-from-fraction: -1;
|
||||
in property <float> library-range-to-fraction: -1;
|
||||
callback library-timeline-range-changed(float, float);
|
||||
in property <string> library-root-label: "";
|
||||
in-out property <[TimelineBar]> library-timeline;
|
||||
in property <string> library-timeline-label: "";
|
||||
in property <string> library-window-label: "";
|
||||
in property <int> library-offset: 0;
|
||||
|
||||
in property <int> library-sweep-done: 0;
|
||||
in property <int> library-sweep-total: 0;
|
||||
|
||||
in property <int> library-current-bucket: 0;
|
||||
in property <float> library-current-fraction: -1;
|
||||
in property <bool> library-timeline-anchored: false;
|
||||
|
||||
callback library-scrub-fraction(float);
|
||||
callback library-timeline-pinch(float);
|
||||
callback library-timeline-pan(float);
|
||||
callback library-timeline-zoom(int);
|
||||
/// Whose photographs the grid is narrowed to, one entry each.
|
||||
in property <[PersonChip]> library-filter-people;
|
||||
/// Whether those people are an intersection rather than a union.
|
||||
in property <bool> library-filter-people-all: false;
|
||||
callback library-filter-person-cleared(int);
|
||||
callback library-filter-people-mode-toggled();
|
||||
/// TRACES: FR-CULL-8a | FR-CULL-13
|
||||
/// Whether the grid is narrowed to photographs with nobody blinking.
|
||||
in property <bool> library-filter-eyes-open: false;
|
||||
callback library-filter-eyes-open-toggled();
|
||||
/// Everyone the library knows, for the filter bar's people tray.
|
||||
/// TRACES: FR-UI-4
|
||||
/// The gesture reference's rows, read from the generated table.
|
||||
in property <[GestureRow]> library-gestures;
|
||||
in property <[PersonChip]> library-people;
|
||||
callback library-people-listed();
|
||||
callback library-filter-person-toggled(int);
|
||||
in property <bool> library-scanning: false;
|
||||
/// The catalog on this device is being opened and checked, on a worker,
|
||||
/// before the scan starts (`library_ui::open_catalog_soon`).
|
||||
///
|
||||
/// Separate from `library-scanning` because they are different answers to
|
||||
/// the same question: this one is reading a file that is already here, and
|
||||
/// that one is walking a tree over the network. Both are true at once for
|
||||
/// the first moment of a launch, and only the more specific of them is
|
||||
/// worth putting on an empty grid.
|
||||
in property <bool> library-opening: false;
|
||||
in property <string> library-status: "";
|
||||
in property <string> library-error: "";
|
||||
// --- offline mode (FR-CAT-9) ---
|
||||
//
|
||||
// The library keeps working from local data when the server cannot be
|
||||
// reached: thumbnails come from the shards, and rating and sorting are
|
||||
// catalog operations that never needed the network. What stops is opening
|
||||
// an original that was never stored locally.
|
||||
//
|
||||
// `offline-reason` carries the transport's own message ("connection
|
||||
// refused", "dns error") because it is usually specific enough to act on,
|
||||
// where a bare "offline" leaves the user guessing whether it is their wifi
|
||||
// or the server.
|
||||
in property <bool> library-offline: false;
|
||||
in property <string> library-offline-reason: "";
|
||||
in property <string> library-offline-since: "";
|
||||
callback library-retry-connection();
|
||||
// --- pinning a collection offline (FR-NC-6a) ---
|
||||
in property <bool> library-scope-pinned: false;
|
||||
in property <int> library-pin-done: 0;
|
||||
in property <int> library-pin-total: 0;
|
||||
callback library-toggle-pin-scope();
|
||||
in property <bool> library-syncing: false;
|
||||
/// TRACES: FR-CAT-3 | FR-NC-3
|
||||
/// The whole-library thumbnail pass, launched from the settings page.
|
||||
/// `library-open` gates the button on there being a library to sweep —
|
||||
/// the page is reachable from the launch screen too.
|
||||
in property <bool> library-thumbnailing: false;
|
||||
in property <bool> library-open: false;
|
||||
callback library-thumbnail-all();
|
||||
callback library-sync-now();
|
||||
callback library-rescan();
|
||||
/// Grid → launch screen, to pick a different folder or account.
|
||||
callback library-change();
|
||||
/// Merge the grid's selection: opens the page and starts the alignment.
|
||||
callback library-merge-selection();
|
||||
/// Whether a batch is running. The grid's export button becomes its cancel
|
||||
/// button; there is nowhere else a long batch can be stopped from.
|
||||
in property <bool> library-exporting: false;
|
||||
/// Export the grid's selection in the background (FR-EXP-7).
|
||||
callback library-export-selection();
|
||||
/// TRACES: NFR-ARCH-3
|
||||
callback library-cancel-export();
|
||||
}
|
||||
|
||||
// The photo roll: the grid's loaded window along the foot of the develop view.
|
||||
//
|
||||
// # Why this exists
|
||||
|
||||
Reference in New Issue
Block a user