Move the library's routes and status onto its own Slint global

The scan/opening/status/error lines, offline mode and its retry, pinning a
collection offline, the sync and thumbnail-sweep state, and the callbacks
that route the grid to a rescan, a sync, another library, a panorama merge
or an export — the last of what AppWindow still carried under the
library- prefix — move onto the `Library` global started earlier on this
branch. Rust reaches them through window.global::<Library>() rather than
window.set_/get_/on_/invoke_ on the root.

library-visible is the one name that stays: it is computed from
active-page and active-view, the shell's own routing state, which a
global cannot read. AppWindow now declares no other library- property or
callback.
This commit is contained in:
2026-09-20 22:22:55 +02:00
parent 6bf67cefc4
commit 1e472fd251
20 changed files with 542 additions and 433 deletions
+24 -77
View File
@@ -310,40 +310,8 @@ 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: "";
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
@@ -374,13 +342,6 @@ export component AppWindow inherits Window {
callback recovery-rebuild();
callback recovery-dismiss();
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) ---
//
@@ -443,14 +404,9 @@ export component AppWindow inherits Window {
callback identity-stop-indexing();
callback identity-check-coverage();
callback identity-delete-all();
callback library-thumbnail-all();
/// 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;
callback library-sync-now();
callback library-rescan();
/// Grid → launch screen, to pick a different folder or account.
callback library-change();
/// Develop → grid.
callback back-to-library();
@@ -608,8 +564,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();
@@ -897,13 +851,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.
@@ -1209,7 +1156,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
@@ -1220,8 +1167,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(); }
@@ -1367,10 +1314,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); }
@@ -1411,15 +1358,15 @@ in property <bool> panel-visible: true;
expanded: root.expanded;
cells: Library.library-cells;
total: Library.library-total;
scanning: root.library-scanning;
opening: root.library-opening;
scan-status: root.library-status;
scan-error: root.library-error;
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(); }
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(); }
@@ -1445,8 +1392,8 @@ in property <bool> panel-visible: true;
collections-visible: root.collections-visible;
toggle-collections() => { root.toggle-collections(); }
pin-done: root.library-pin-done;
pin-total: root.library-pin-total;
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;
@@ -1469,12 +1416,12 @@ in property <bool> panel-visible: 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: Library.library-sweep-done;
sweep-total: Library.library-sweep-total;
@@ -1487,7 +1434,7 @@ in property <bool> panel-visible: true;
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: root.library-syncing;
syncing: Library.library-syncing;
requested-cell-size: Library.library-cell-size;
touched <=> Library.library-touched;
zoom-cells(d) => { Library.library-zoom-cells(d); }
@@ -1495,7 +1442,7 @@ in property <bool> panel-visible: true;
pinch-started() => { Library.library-pinch-started(); }
scroll-to: Library.library-scroll-to;
scroll-token: Library.library-scroll-token;
sync-now() => { root.library-sync-now(); }
sync-now() => { Library.library-sync-now(); }
columns-changed(n) => {
Library.library-columns = n;
// Which cell begins a row just changed, and month headings sit on
@@ -1505,7 +1452,7 @@ in property <bool> panel-visible: true;
scrolled(i) => { Library.library-scrolled(i); }
viewport-cells-changed(n) => { Library.library-viewport-cells(n); }
cell-clicked(i) => { Library.library-cell-clicked(i); }
rescan() => { root.library-rescan(); }
rescan() => { Library.library-rescan(); }
can-import: root.import-supported;
open-import() => { root.import-open(); }
open-settings() => { root.settings-open(); }
+53
View File
@@ -694,6 +694,59 @@ export global Library {
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.