Compare commits
2
Commits
825c5af20a
...
v0.19.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5c93ae795 | ||
|
|
379dd1afcc |
+37
-37
File diff suppressed because one or more lines are too long
+67
-19
@@ -794,29 +794,36 @@ pub(crate) fn refresh_export_label(window: &AppWindow) {
|
||||
/// dragged, because each move event destroys the thing that would deliver
|
||||
/// the next one.
|
||||
/// TRACES: FR-CAT-8
|
||||
/// Apply a fetched sidecar to the open session, now or as soon as it arrives.
|
||||
/// Apply a fetched sidecar to the open session, then draw its first frame.
|
||||
///
|
||||
/// The sidecar fetch is started beside the image fetch and is three orders of
|
||||
/// magnitude smaller, so it has almost always landed by the time there is a
|
||||
/// session to apply it to — and this takes it straight from the channel. The
|
||||
/// timer covers the case where it has not, which is why this is not simply a
|
||||
/// blocking receive: a slow or stalled sidecar request must not freeze the
|
||||
/// window with the photograph already decoded and on screen.
|
||||
/// magnitude smaller, so it has usually landed by the time there is a session
|
||||
/// to apply it to — and this takes it straight from the channel. An original
|
||||
/// read from the cache can still beat it, and drawing then showed the
|
||||
/// photograph at its defaults and changed it a moment later. So the first
|
||||
/// frame waits up to `SIDECAR_GRACE` for the edit, with the grid's thumbnail
|
||||
/// still standing in; past that it is drawn at its defaults, and a later
|
||||
/// arrival redraws. Not a blocking receive: a stalled request must not freeze
|
||||
/// the window.
|
||||
///
|
||||
/// A late arrival redraws, so the image is correct either way; the only
|
||||
/// difference is whether it was ever briefly shown at its defaults.
|
||||
/// `still_current` is false once the view has moved to another photograph,
|
||||
/// whose session this sidecar must not be applied to.
|
||||
fn apply_when_ready(
|
||||
window: &AppWindow,
|
||||
rx: Rc<std::sync::mpsc::Receiver<Option<dr_pipeline::Sidecar>>>,
|
||||
session: &Rc<RefCell<Option<DevelopSession>>>,
|
||||
rows: &Rc<slint::VecModel<ParamRow>>,
|
||||
redraw: &Rc<dyn Fn(&AppWindow)>,
|
||||
still_current: impl Fn() -> bool + 'static,
|
||||
) {
|
||||
// Already here — the overwhelmingly common case.
|
||||
const SIDECAR_GRACE: std::time::Duration = std::time::Duration::from_millis(400);
|
||||
|
||||
// Already here — the common case.
|
||||
if let Ok(got) = rx.try_recv() {
|
||||
if let Some(sidecar) = got {
|
||||
presets::apply_stored_edit(window, &sidecar, session, rows);
|
||||
}
|
||||
redraw(window);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -824,17 +831,37 @@ fn apply_when_ready(
|
||||
let session = session.clone();
|
||||
let rows = rows.clone();
|
||||
let redraw = redraw.clone();
|
||||
let deadline = std::time::Instant::now() + SIDECAR_GRACE;
|
||||
let drawn = Cell::new(false);
|
||||
let timer = Rc::new(slint::Timer::default());
|
||||
let held = timer.clone();
|
||||
timer.start(
|
||||
slint::TimerMode::Repeated,
|
||||
std::time::Duration::from_millis(50),
|
||||
move || {
|
||||
let Ok(got) = rx.try_recv() else { return };
|
||||
let Some(w) = weak.upgrade() else {
|
||||
held.stop();
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
let Some(sidecar) = got else { return };
|
||||
if presets::apply_stored_edit(&w, &sidecar, &session, &rows) {
|
||||
return;
|
||||
};
|
||||
if !still_current() {
|
||||
held.stop();
|
||||
return;
|
||||
}
|
||||
let Ok(got) = rx.try_recv() else {
|
||||
// Waited long enough: draw at the defaults and keep
|
||||
// listening, so a late edit still lands.
|
||||
if !drawn.get() && std::time::Instant::now() >= deadline {
|
||||
drawn.set(true);
|
||||
redraw(&w);
|
||||
}
|
||||
return;
|
||||
};
|
||||
held.stop();
|
||||
let applied = match got {
|
||||
Some(sidecar) => presets::apply_stored_edit(&w, &sidecar, &session, &rows),
|
||||
None => false,
|
||||
};
|
||||
if applied || !drawn.get() {
|
||||
redraw(&w);
|
||||
}
|
||||
},
|
||||
@@ -2244,6 +2271,13 @@ fn build_render_now(
|
||||
window.set_canvas_draft(draft);
|
||||
window.global::<Levels>().set_provisional(draft);
|
||||
window.set_load_error("".into());
|
||||
// TRACES: FR-NC-6a
|
||||
// The library path keeps the grid's thumbnail up until
|
||||
// here, the first frame of the new photograph, because
|
||||
// the canvas still holds the last photograph's texture
|
||||
// until this line replaces it.
|
||||
window.set_load_pending(false);
|
||||
window.set_has_load_preview(false);
|
||||
// The readout and the "Fit" button follow the session
|
||||
// rather than the gesture, so a clamped zoom shows the
|
||||
// value that was actually applied.
|
||||
@@ -2779,13 +2813,18 @@ fn wire_remote_open(
|
||||
log::debug!("{name}: landed after the view moved on");
|
||||
return;
|
||||
}
|
||||
w.set_load_pending(false);
|
||||
// The bar goes, and the thumbnail goes back to full
|
||||
// strength; the thumbnail itself stays until the first
|
||||
// frame of this photograph replaces it in `render_now`.
|
||||
// Dropping it here showed the last photograph's texture,
|
||||
// still in the canvas, for the length of the decode.
|
||||
w.set_load_waiting("".into());
|
||||
w.set_has_load_preview(false);
|
||||
|
||||
let bytes = match got {
|
||||
Ok(b) => b,
|
||||
Err(e) => {
|
||||
w.set_load_pending(false);
|
||||
w.set_has_load_preview(false);
|
||||
job.fail(e.message.clone());
|
||||
log::warn!("{name}: {e}");
|
||||
// Offline needs its own words. "network error:
|
||||
@@ -2854,16 +2893,19 @@ fn wire_remote_open(
|
||||
// TRACES: FR-CAT-8
|
||||
// The stored edit, if it has landed. It
|
||||
// was started before the download of a
|
||||
// file thousands of times its size, so in
|
||||
// practice it has; `apply_when_ready`
|
||||
// covers the case where it has not rather
|
||||
// file thousands of times its size, but
|
||||
// an original read from the cache can
|
||||
// beat it; `apply_when_ready` holds the
|
||||
// first frame back a moment for it rather
|
||||
// than blocking the UI thread on a socket.
|
||||
let still = current.clone();
|
||||
apply_when_ready(
|
||||
&w,
|
||||
sidecar_rx.clone(),
|
||||
&session,
|
||||
&rows,
|
||||
&redraw,
|
||||
move || still.get() == mine,
|
||||
);
|
||||
// TRACES: FR-UI-4
|
||||
// Under the same magnifier as the last
|
||||
@@ -2876,12 +2918,16 @@ fn wire_remote_open(
|
||||
// moves the point rather than losing it.
|
||||
resume_inspection(&session, &viewport, &inspection);
|
||||
sync_rows(&w, &rows, &session);
|
||||
redraw(&w);
|
||||
// No redraw here: `apply_when_ready` makes
|
||||
// the first one, once the edit is applied
|
||||
// or has been waited for long enough.
|
||||
}
|
||||
None => {
|
||||
*session.borrow_mut() = None;
|
||||
rows.set_vec(Vec::<ParamRow>::new());
|
||||
w.global::<Develop>().set_enabled(false);
|
||||
w.set_load_pending(false);
|
||||
w.set_has_load_preview(false);
|
||||
if let Some(image) = l.fallback {
|
||||
w.set_canvas(image);
|
||||
}
|
||||
@@ -2893,6 +2939,8 @@ fn wire_remote_open(
|
||||
log::warn!("{name}: {e}");
|
||||
*session.borrow_mut() = None;
|
||||
w.global::<Develop>().set_enabled(false);
|
||||
w.set_load_pending(false);
|
||||
w.set_has_load_preview(false);
|
||||
w.set_load_error(e.into());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user