Wire the launch screen into the app
The app now opens on the login screen when there is nothing else to show
— no local paths and no configured library — and goes straight to the
images otherwise. Making someone click past a login they already
completed is pure friction.
launch.slint imported by app.slint, replacing the window rather
than overlaying it: there is no library to look at
until an account is configured
launch_ui.rs the Slint wiring, kept out of lib.rs so the launch
flow can change without touching the develop window
Login runs on a worker thread and posts results back through a channel,
since Slint's event loop is single-threaded and a 20-minute browser wait
cannot block it. The system browser is opened via xdg-open, never an
embedded webview (FR-NC-1).
Sign-out deletes the local credential even if server-side revocation
fails: a network error must not leave a usable secret on the machine.
Format tick-boxes persist on each toggle, so a selection survives a
crash before the library is opened.
Two things deliberately incomplete rather than faked:
- "Choose folder" lists the account's folders and reports them, but
there is no picker widget yet, so selection still happens via the
connect example.
- "Open library" logs the request. Opening a remote library needs the
scan-and-cache path, which belongs with the catalog work in flight.
Earlier I broke the other in-flight dr-ui work by calling
slint_build::compile twice, which replaces the generated module. The
correct wiring is an import inside app.slint, which is what this does.
30 dr-ui tests passing; both launch paths verified by running the app.
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
//! Order is data, not code: operations run in the sequence this holds them,
|
||||
//! so reordering the pipeline needs no code change.
|
||||
|
||||
use crate::descriptor::{LocalizedKey, OpDescriptor, OpId, ParamId, ParamKind};
|
||||
use crate::descriptor::{LocalizedKey, OpDescriptor, OpId, ParamId, ParamKind, Presentation};
|
||||
use crate::framing::{CropRect, Framing};
|
||||
use crate::operation::{compose_with_framing, ComposedShader, Operation};
|
||||
use crate::ops;
|
||||
@@ -28,6 +28,12 @@ pub struct OpCapability {
|
||||
/// mark a section as modified, or to offer a per-operation reset.
|
||||
pub active: bool,
|
||||
pub params: Vec<ParamCapability>,
|
||||
/// A hint that several of `params` form one conceptual control.
|
||||
///
|
||||
/// `None` means one control per parameter. A UI that does not implement
|
||||
/// the named widget may ignore this and render sliders — the parameters
|
||||
/// are ordinary scalars either way, so nothing becomes unreachable.
|
||||
pub presentation: Option<Presentation>,
|
||||
}
|
||||
|
||||
/// TRACES: FR-DEV-3a | FR-DEV-3b
|
||||
@@ -81,6 +87,10 @@ impl EditGraph {
|
||||
Box::new(ops::Contrast::new()),
|
||||
Box::new(ops::HighlightsShadows::new()),
|
||||
Box::new(ops::BlacksWhites::new()),
|
||||
// After the region controls, so the curve is the final word
|
||||
// on tone: a photographer reaches for it to fix what the
|
||||
// fixed-weight controls could not place exactly.
|
||||
Box::new(ops::ToneCurve::new()),
|
||||
Box::new(ops::Brilliance::new()),
|
||||
Box::new(ops::Vibrance::new()),
|
||||
Box::new(ops::Saturation::new()),
|
||||
@@ -157,6 +167,7 @@ impl EditGraph {
|
||||
value: op.param(p.id),
|
||||
})
|
||||
.collect(),
|
||||
presentation: op.presentation(),
|
||||
}
|
||||
});
|
||||
|
||||
@@ -178,6 +189,10 @@ impl EditGraph {
|
||||
value: self.framing.param(p.id),
|
||||
})
|
||||
.collect(),
|
||||
// Framing is not an `Operation`, so it has no `presentation` to
|
||||
// ask for. A crop overlay is a viewport interaction rather than a
|
||||
// panel widget, which is a different mechanism again.
|
||||
presentation: None,
|
||||
};
|
||||
|
||||
ops.chain(std::iter::once(framing)).collect()
|
||||
|
||||
Reference in New Issue
Block a user