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
|
/// dragged, because each move event destroys the thing that would deliver
|
||||||
/// the next one.
|
/// the next one.
|
||||||
/// TRACES: FR-CAT-8
|
/// 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
|
/// 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
|
/// magnitude smaller, so it has usually landed by the time there is a session
|
||||||
/// session to apply it to — and this takes it straight from the channel. The
|
/// to apply it to — and this takes it straight from the channel. An original
|
||||||
/// timer covers the case where it has not, which is why this is not simply a
|
/// read from the cache can still beat it, and drawing then showed the
|
||||||
/// blocking receive: a slow or stalled sidecar request must not freeze the
|
/// photograph at its defaults and changed it a moment later. So the first
|
||||||
/// window with the photograph already decoded and on screen.
|
/// 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
|
/// `still_current` is false once the view has moved to another photograph,
|
||||||
/// difference is whether it was ever briefly shown at its defaults.
|
/// whose session this sidecar must not be applied to.
|
||||||
fn apply_when_ready(
|
fn apply_when_ready(
|
||||||
window: &AppWindow,
|
window: &AppWindow,
|
||||||
rx: Rc<std::sync::mpsc::Receiver<Option<dr_pipeline::Sidecar>>>,
|
rx: Rc<std::sync::mpsc::Receiver<Option<dr_pipeline::Sidecar>>>,
|
||||||
session: &Rc<RefCell<Option<DevelopSession>>>,
|
session: &Rc<RefCell<Option<DevelopSession>>>,
|
||||||
rows: &Rc<slint::VecModel<ParamRow>>,
|
rows: &Rc<slint::VecModel<ParamRow>>,
|
||||||
redraw: &Rc<dyn Fn(&AppWindow)>,
|
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 Ok(got) = rx.try_recv() {
|
||||||
if let Some(sidecar) = got {
|
if let Some(sidecar) = got {
|
||||||
presets::apply_stored_edit(window, &sidecar, session, rows);
|
presets::apply_stored_edit(window, &sidecar, session, rows);
|
||||||
}
|
}
|
||||||
|
redraw(window);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -824,17 +831,37 @@ fn apply_when_ready(
|
|||||||
let session = session.clone();
|
let session = session.clone();
|
||||||
let rows = rows.clone();
|
let rows = rows.clone();
|
||||||
let redraw = redraw.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 timer = Rc::new(slint::Timer::default());
|
||||||
let held = timer.clone();
|
let held = timer.clone();
|
||||||
timer.start(
|
timer.start(
|
||||||
slint::TimerMode::Repeated,
|
slint::TimerMode::Repeated,
|
||||||
std::time::Duration::from_millis(50),
|
std::time::Duration::from_millis(50),
|
||||||
move || {
|
move || {
|
||||||
let Ok(got) = rx.try_recv() else { return };
|
let Some(w) = weak.upgrade() else {
|
||||||
|
held.stop();
|
||||||
|
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();
|
held.stop();
|
||||||
let Some(w) = weak.upgrade() else { return };
|
let applied = match got {
|
||||||
let Some(sidecar) = got else { return };
|
Some(sidecar) => presets::apply_stored_edit(&w, &sidecar, &session, &rows),
|
||||||
if presets::apply_stored_edit(&w, &sidecar, &session, &rows) {
|
None => false,
|
||||||
|
};
|
||||||
|
if applied || !drawn.get() {
|
||||||
redraw(&w);
|
redraw(&w);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2244,6 +2271,13 @@ fn build_render_now(
|
|||||||
window.set_canvas_draft(draft);
|
window.set_canvas_draft(draft);
|
||||||
window.global::<Levels>().set_provisional(draft);
|
window.global::<Levels>().set_provisional(draft);
|
||||||
window.set_load_error("".into());
|
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
|
// The readout and the "Fit" button follow the session
|
||||||
// rather than the gesture, so a clamped zoom shows the
|
// rather than the gesture, so a clamped zoom shows the
|
||||||
// value that was actually applied.
|
// value that was actually applied.
|
||||||
@@ -2779,13 +2813,18 @@ fn wire_remote_open(
|
|||||||
log::debug!("{name}: landed after the view moved on");
|
log::debug!("{name}: landed after the view moved on");
|
||||||
return;
|
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_load_waiting("".into());
|
||||||
w.set_has_load_preview(false);
|
|
||||||
|
|
||||||
let bytes = match got {
|
let bytes = match got {
|
||||||
Ok(b) => b,
|
Ok(b) => b,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
w.set_load_pending(false);
|
||||||
|
w.set_has_load_preview(false);
|
||||||
job.fail(e.message.clone());
|
job.fail(e.message.clone());
|
||||||
log::warn!("{name}: {e}");
|
log::warn!("{name}: {e}");
|
||||||
// Offline needs its own words. "network error:
|
// Offline needs its own words. "network error:
|
||||||
@@ -2854,16 +2893,19 @@ fn wire_remote_open(
|
|||||||
// TRACES: FR-CAT-8
|
// TRACES: FR-CAT-8
|
||||||
// The stored edit, if it has landed. It
|
// The stored edit, if it has landed. It
|
||||||
// was started before the download of a
|
// was started before the download of a
|
||||||
// file thousands of times its size, so in
|
// file thousands of times its size, but
|
||||||
// practice it has; `apply_when_ready`
|
// an original read from the cache can
|
||||||
// covers the case where it has not rather
|
// beat it; `apply_when_ready` holds the
|
||||||
|
// first frame back a moment for it rather
|
||||||
// than blocking the UI thread on a socket.
|
// than blocking the UI thread on a socket.
|
||||||
|
let still = current.clone();
|
||||||
apply_when_ready(
|
apply_when_ready(
|
||||||
&w,
|
&w,
|
||||||
sidecar_rx.clone(),
|
sidecar_rx.clone(),
|
||||||
&session,
|
&session,
|
||||||
&rows,
|
&rows,
|
||||||
&redraw,
|
&redraw,
|
||||||
|
move || still.get() == mine,
|
||||||
);
|
);
|
||||||
// TRACES: FR-UI-4
|
// TRACES: FR-UI-4
|
||||||
// Under the same magnifier as the last
|
// Under the same magnifier as the last
|
||||||
@@ -2876,12 +2918,16 @@ fn wire_remote_open(
|
|||||||
// moves the point rather than losing it.
|
// moves the point rather than losing it.
|
||||||
resume_inspection(&session, &viewport, &inspection);
|
resume_inspection(&session, &viewport, &inspection);
|
||||||
sync_rows(&w, &rows, &session);
|
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 => {
|
None => {
|
||||||
*session.borrow_mut() = None;
|
*session.borrow_mut() = None;
|
||||||
rows.set_vec(Vec::<ParamRow>::new());
|
rows.set_vec(Vec::<ParamRow>::new());
|
||||||
w.global::<Develop>().set_enabled(false);
|
w.global::<Develop>().set_enabled(false);
|
||||||
|
w.set_load_pending(false);
|
||||||
|
w.set_has_load_preview(false);
|
||||||
if let Some(image) = l.fallback {
|
if let Some(image) = l.fallback {
|
||||||
w.set_canvas(image);
|
w.set_canvas(image);
|
||||||
}
|
}
|
||||||
@@ -2893,6 +2939,8 @@ fn wire_remote_open(
|
|||||||
log::warn!("{name}: {e}");
|
log::warn!("{name}: {e}");
|
||||||
*session.borrow_mut() = None;
|
*session.borrow_mut() = None;
|
||||||
w.global::<Develop>().set_enabled(false);
|
w.global::<Develop>().set_enabled(false);
|
||||||
|
w.set_load_pending(false);
|
||||||
|
w.set_has_load_preview(false);
|
||||||
w.set_load_error(e.into());
|
w.set_load_error(e.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user