Replace the six view booleans with View and Page enums
app.slint carried show-launch, show-library, show-identity, show-settings,
show-import and show-merge as separate booleans, so the root component chose
what to draw with five- and six-term conjunctions and nothing stopped two of
them being true at once. Replaced with two enums: View { develop, library,
identity, launch } for which top-level screen is showing, and Page { none,
settings, import, merge } for which page, if any, is drawn over it.
Two values rather than one, because the two questions are genuinely
different. Settings, Import and Merge are reachable from more than one View
and are drawn outermost without touching it — closing one has to return to
whichever View was already current, and today that works because the
underlying property is left alone while the page sits over it. A single
View with five or more variants would need a second field remembering what
to return to; Page needs nothing to remember, since View was never
overwritten in the first place. Identity, by contrast, genuinely replaces
the window the way Launch and Library do (see the existing "like the launch
screen" comment on its `if`), so it is a View variant, not a Page.
Every `if` chain in app.slint that used to compare four, five or six
booleans now compares active-view and active-page to at most one variant
each. library-visible collapsed from a six-term conjunction to
`active-page == Page.none && active-view == View.library`.
The Rust side follows: every set_show_*/get_show_* call in library_ui.rs,
identity_ui.rs, settings_ui.rs, merge_ui.rs, import_ui.rs, launch_ui.rs and
lib.rs now reads or writes active-view or active-page instead, including
lib.rs's startup match (View.launch vs View.develop, since a Startup that
skips the launch screen used to leave both old booleans false and fall
through the chain to develop) and identity_ui's close handler, which now
writes View.library or View.develop in one call where it used to write
show-library then show-identity separately.
back_one_step needed one deliberate adjustment beyond the mechanical
rename. Identity was never represented in NavState: back had nothing to do
when Identity was opened from the library (show-library stayed true,
unread by IdentityScreen's own condition) and could only reach ToLibrary
when opened from develop, which likewise wrote a property IdentityScreen
never read — so escaping out of Identity was invisible in both cases before
this change. With a single active-view, falling into the general case
would instead overwrite the value IdentityScreen's `if` does read and close
it as an unintended side effect. back_one_step now swallows the gesture
while View.identity is current, reproducing the same "nothing visible
happens" outcome for both origins without threading identity_ui's private
came-from-library state through lib.rs for one screen.
Verified with tools/manual/drive.py against a private Xvfb and the debug
build: launch screen to library, Settings opened and closed, Identity
opened and closed (including Escape doing nothing while it is open),
develop opened from a cell and closed both by the back button and by
Escape. Screenshots under verify/.
This commit is contained in:
+65
-26
@@ -32,6 +32,45 @@ 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 }
|
||||
|
||||
/// Which top-level screen is on display. Exactly one at a time — replacing
|
||||
/// the `show-launch` / `show-library` / `show-identity` booleans, with
|
||||
/// develop as "none of the above" wired into every conditional that drew any
|
||||
/// of the other three. Those conditionals compared four and five booleans at
|
||||
/// once to rule out two screens being true together; comparing one value to
|
||||
/// a variant cannot be wrong in that way.
|
||||
///
|
||||
/// `develop` is listed first, and so is the type's default: it is what the
|
||||
/// old booleans meant when every one of them was false, which is the state
|
||||
/// files named on the command line stay in permanently — there is no
|
||||
/// library behind them to switch to — and the state the window briefly
|
||||
/// holds at construction, before `lib.rs::run` chooses.
|
||||
export enum View {
|
||||
develop,
|
||||
library,
|
||||
identity,
|
||||
launch,
|
||||
}
|
||||
|
||||
/// Which page, if any, is drawn over the current `View`. `settings`,
|
||||
/// `import` and `merge` are reachable from more than one `View` — Settings
|
||||
/// from the library and from develop, Import and Merge from the library —
|
||||
/// and are drawn outermost so the view underneath is torn down rather than
|
||||
/// nested inside the one it is about to change. `none` is the ordinary case,
|
||||
/// and the type's default.
|
||||
///
|
||||
/// Kept apart from `View` rather than folded into it as three more variants:
|
||||
/// closing a page has to return to whichever view was already current, and
|
||||
/// today that works because the view's own property is left untouched while
|
||||
/// a page sits over it. Three more `View` variants would need a second field
|
||||
/// remembering what to return to; a separate, single-valued `Page` needs
|
||||
/// nothing to remember.
|
||||
export enum Page {
|
||||
none,
|
||||
settings,
|
||||
import,
|
||||
merge,
|
||||
}
|
||||
|
||||
export component AppWindow inherits Window {
|
||||
title: "DarkRoom";
|
||||
// The one raster asset in the UI, and the exception that the header of
|
||||
@@ -184,11 +223,16 @@ export component AppWindow inherits Window {
|
||||
/// remembered across photographs, which is state no screen holds.
|
||||
callback inspect-toggled(float, float);
|
||||
|
||||
// --- active view (FR-UI-8) ---
|
||||
//
|
||||
// The four top-level screens share one property; see `View`. Rust is the
|
||||
// only writer — nothing in this file assigns to it — so `in` is enough.
|
||||
in property <View> active-view: View.develop;
|
||||
|
||||
// --- launch screen (FR-NC-1, FR-NC-4) ---
|
||||
//
|
||||
// The app opens here when no library is configured, and returns here to
|
||||
// sign out or switch account.
|
||||
in property <bool> show-launch: false;
|
||||
in property <bool> launch-signed-in: false;
|
||||
in property <string> launch-account: "";
|
||||
in property <string> launch-root: "";
|
||||
@@ -228,13 +272,12 @@ export component AppWindow inherits Window {
|
||||
//
|
||||
// Shown after a library is opened, before an image is chosen. Cells are a
|
||||
// window over the catalog, not the whole of it.
|
||||
in property <bool> show-library: false;
|
||||
/// Whether the grid is actually on screen — the condition the `if` below
|
||||
/// is written with, hoisted so Rust can read the same answer.
|
||||
///
|
||||
/// **`show-library` is not that answer.** It says "the library rather than
|
||||
/// develop", and stays true while Settings, Import, People or the launch
|
||||
/// screen replaces the window. The grid subtree is torn down in all four
|
||||
/// **`active-view == View.library` is not enough on its own.** The view
|
||||
/// stays `library` while Settings, Import, Merge or People replaces the
|
||||
/// window as a page over it. The grid subtree is torn down in all four
|
||||
/// cases, and a Flickable being destroyed passes its viewport through zero
|
||||
/// on the way out — which reaches `on_library_scrolled` as a scroll to the
|
||||
/// top of the library and overwrote the position the user was at. That is
|
||||
@@ -243,8 +286,7 @@ export component AppWindow inherits Window {
|
||||
/// So the guard has to be this, and the `if` has to be spelled from it, or
|
||||
/// the two can drift apart again.
|
||||
out property <bool> library-visible:
|
||||
!root.show-import && !root.show-merge && !root.show-settings && !root.show-identity
|
||||
&& !root.show-launch && root.show-library;
|
||||
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) ---
|
||||
@@ -396,8 +438,7 @@ export component AppWindow inherits Window {
|
||||
// A third top-level screen rather than a panel: naming a cluster and
|
||||
// pulling a stranger out of it are tasks with their own rhythm, and they
|
||||
// need the whole window. Gated the same way the other screens are, so
|
||||
// exactly one is ever up.
|
||||
in property <bool> show-identity: false;
|
||||
// exactly one is ever up — see `View`.
|
||||
in property <[IdentityPerson]> identity-people;
|
||||
in property <[IdentityFace]> identity-faces;
|
||||
in property <int> identity-selected: -1;
|
||||
@@ -780,14 +821,14 @@ export component AppWindow inherits Window {
|
||||
|
||||
// --- settings (FR-EXP-1, FR-EXP-3, FR-NC-6a) ---
|
||||
//
|
||||
// A page rather than an overlay, and the outermost of the view conditions
|
||||
// below: it is reachable from the library and from develop, so guarding it
|
||||
// with `!show-library` or `!show-launch` would make which one you came from
|
||||
// decide whether it appears.
|
||||
// A page rather than an overlay, and outermost among the pages drawn over
|
||||
// a view: it is reachable from the library and from develop, so guarding
|
||||
// it with which view was current would make which one you came from
|
||||
// decide whether it appears. See `Page`.
|
||||
//
|
||||
// Every control saves on change (see `settings_ui.rs`), so there is no
|
||||
// dirty state here and nothing to confirm on the way out.
|
||||
in-out property <bool> show-settings: false;
|
||||
in-out property <Page> active-page: Page.none;
|
||||
|
||||
// --- import (FR-CAT-10, FR-CAT-11, FR-NC-7a) -------------------------
|
||||
//
|
||||
@@ -798,7 +839,6 @@ export component AppWindow inherits Window {
|
||||
// Unlike Settings, this page holds a *decision* rather than a set of saved
|
||||
// preferences, which is why it carries `import-can-start` and a running
|
||||
// state: everything here exists to be committed or abandoned.
|
||||
in-out property <bool> show-import: false;
|
||||
/// Whether this platform can import at all — see `dr_plat::imports_supported`.
|
||||
in property <bool> import-supported: false;
|
||||
|
||||
@@ -829,7 +869,6 @@ export component AppWindow inherits Window {
|
||||
// TRACES: FR-MRG-1 | FR-MRG-7
|
||||
// The merge page, on the import page's model: a decision to be
|
||||
// committed or abandoned, with a running state, over the library.
|
||||
in-out property <bool> show-merge: false;
|
||||
in property <string> merge-stage: "";
|
||||
in property <bool> merge-running: false;
|
||||
in property <float> merge-fraction: 0.0;
|
||||
@@ -1266,12 +1305,12 @@ in property <bool> panel-visible: true;
|
||||
}
|
||||
|
||||
// Settings, over everything. First in the file and first in z-order so the
|
||||
// conditions below can be read as "and settings is not open".
|
||||
// conditions below can be read as "and no page is open".
|
||||
// Outermost with the settings page, and before it: both are reachable
|
||||
// from the library and from develop, so guarding either with
|
||||
// `!show-library` would make which view you came from decide whether
|
||||
// from the library and from develop, so guarding either with which
|
||||
// view was current would make which view you came from decide whether
|
||||
// it appears.
|
||||
if root.show-import: ImportPage {
|
||||
if root.active-page == Page.import: ImportPage {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
@@ -1313,7 +1352,7 @@ in property <bool> panel-visible: true;
|
||||
close() => { root.import-close(); }
|
||||
}
|
||||
|
||||
if !root.show-import && root.show-merge: MergePage {
|
||||
if root.active-page == Page.merge: MergePage {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
stage: root.merge-stage;
|
||||
@@ -1346,7 +1385,7 @@ in property <bool> panel-visible: true;
|
||||
close() => { root.merge-close(); }
|
||||
}
|
||||
|
||||
if !root.show-import && !root.show-merge && root.show-settings: SettingsPage {
|
||||
if root.active-page == Page.settings: SettingsPage {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
@@ -1482,8 +1521,8 @@ in property <bool> panel-visible: true;
|
||||
}
|
||||
|
||||
// Like the launch screen, this replaces the window rather than
|
||||
// overlaying it — see `show-identity`.
|
||||
if !root.show-import && !root.show-merge && !root.show-settings && root.show-identity: IdentityScreen {
|
||||
// overlaying it — see `View`.
|
||||
if root.active-page == Page.none && root.active-view == View.identity: IdentityScreen {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
people: root.identity-people;
|
||||
@@ -1541,7 +1580,7 @@ in property <bool> panel-visible: true;
|
||||
|
||||
// The launch screen replaces the whole window rather than overlaying it:
|
||||
// there is no library to look at until an account is configured.
|
||||
if !root.show-import && !root.show-merge && !root.show-settings && !root.show-identity && root.show-launch: LaunchScreen {
|
||||
if root.active-page == Page.none && root.active-view == View.launch: LaunchScreen {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
signed-in: root.launch-signed-in;
|
||||
@@ -1835,7 +1874,7 @@ in property <bool> panel-visible: true;
|
||||
}
|
||||
}
|
||||
|
||||
if !root.show-import && !root.show-merge && !root.show-settings && !root.show-identity && !root.show-launch && !root.show-library: VerticalLayout {
|
||||
if root.active-page == Page.none && root.active-view == View.develop: VerticalLayout {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user