Open a photograph with the edit it already carries, when DarkRoom has none

The library's DNGs carry the photographer's earlier develop settings
in their embedded XMP — the house style their photographs were made
with. A photograph opened with no edit of DarkRoom's now starts from
that earlier edit, translated (HSL bands, highlights, blacks and the
rest), as one undoable step named "Earlier Edit"; from there it is an
ordinary edit, saved with the photograph. Export does the same, so a
photograph never opened exports as opening it would show.

Only on positive evidence that there is no DarkRoom edit: a local file
with no sidecar beside it, or a server that answered "no such file"
with nothing in the cache. The stored-edit fetch now says which
(FetchedSidecar::absent). Offline, unreachable or unreadable never
counts — the earlier edit would otherwise be saved over a real edit
that merely failed to arrive.
This commit is contained in:
2026-10-03 14:22:49 -04:00
parent 013596e1bd
commit e12783da9f
6 changed files with 222 additions and 79 deletions
+48 -8
View File
@@ -334,6 +334,10 @@ pub(crate) fn open_session(
if dr_decode::probe(bytes) != Some(dr_types::Format::Jpeg) {
session.set_embedded_profile(dr_decode::dcp::embedded_in(bytes));
}
// TRACES: FR-DEV-6
// And the earlier edit stored in it, applied by the caller only once it
// knows DarkRoom has no edit of this photograph.
session.set_earlier_edit(dr_preset_xmp::read_embedded(bytes).map(|i| i.preset));
Ok(session)
}
@@ -820,7 +824,7 @@ pub(crate) fn refresh_export_label(window: &AppWindow) {
/// 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>>>,
rx: Rc<std::sync::mpsc::Receiver<library::FetchedSidecar>>,
session: &Rc<RefCell<Option<DevelopSession>>>,
rows: &Rc<slint::VecModel<ParamRow>>,
redraw: &Rc<dyn Fn(&AppWindow)>,
@@ -830,9 +834,7 @@ fn apply_when_ready(
// 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);
}
apply_fetched(window, got, session, rows);
redraw(window);
return;
}
@@ -867,10 +869,7 @@ fn apply_when_ready(
return;
};
held.stop();
let applied = match got {
Some(sidecar) => presets::apply_stored_edit(&w, &sidecar, &session, &rows),
None => false,
};
let applied = apply_fetched(&w, got, &session, &rows);
if applied || !drawn.get() {
redraw(&w);
}
@@ -878,6 +877,42 @@ fn apply_when_ready(
);
}
/// TRACES: FR-CAT-8 | FR-DEV-6
/// Apply what the stored-edit fetch answered: the edit, or — only where the
/// server said there is none — the photograph's earlier edit. An answer
/// that could not be had (offline, unreachable, unreadable) applies nothing,
/// so a real edit that failed to arrive is never saved over.
fn apply_fetched(
window: &AppWindow,
got: library::FetchedSidecar,
session: &Rc<RefCell<Option<DevelopSession>>>,
rows: &Rc<slint::VecModel<ParamRow>>,
) -> bool {
match got.sidecar {
Some(sidecar) => presets::apply_stored_edit(window, &sidecar, session, rows),
None if got.absent => adopt_earlier_edit(window, session, rows),
None => false,
}
}
/// TRACES: FR-DEV-6
/// Start the open photograph from its earlier edit, if it has one.
fn adopt_earlier_edit(
window: &AppWindow,
session: &Rc<RefCell<Option<DevelopSession>>>,
rows: &Rc<slint::VecModel<ParamRow>>,
) -> bool {
let adopted = session
.borrow_mut()
.as_mut()
.is_some_and(DevelopSession::adopt_earlier_edit);
if adopted {
log::info!("started from the photograph's earlier edit");
sync_rows(window, rows, session);
}
adopted
}
/// TRACES: FR-DEV-3f
/// Push the film choice out to the panel.
///
@@ -2623,6 +2658,11 @@ fn build_show(
*open_image.borrow_mut() = presets::Stored::Local(path.to_path_buf());
if let Some(sidecar) = presets::load_local(path) {
presets::apply_stored_edit(window, &sidecar, &session, &rows);
} else {
// TRACES: FR-DEV-6
// No edit of DarkRoom's beside the file: start
// from the earlier edit stored in it.
adopt_earlier_edit(window, &session, &rows);
}
// TRACES: FR-UI-4