Files
lightcraft/crates/ui-egui/src/reload.rs
T

81 lines
4.1 KiB
Rust

//! Reload from Disk in the app: the files are read on a worker thread (a raw's decode takes a
//! second or more, and a selection of them froze the window), with a row in the activity stack
//! that counts the files and can stop. What came of it is applied back here through the session's
//! command wrapper (one undo step, saved, sidecars auto-written), unless another library was opened
//! meanwhile or a reload started later has already been applied to the photo. Agents get the same with `wait: true`, which runs
//! the engine's command in place.
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use lightcraft_engine::EngineError;
use lightcraft_engine::cmd::reload;
use serde_json::{Value, json};
use crate::LightcraftApp;
/// The activity stack's label while the files are read (the menu item's name, already translated).
pub const LABEL: &str = "Reload from Disk";
/// `photo.reload` from the app: start reading the targeted files in the background →
/// `{background: true, count}`. `None` hands the command to the engine: with `wait: true`, or when
/// no targeted photo has a file (it then answers at once, refusing an empty selection as usual).
pub fn command(app: &mut LightcraftApp, p: &Value) -> Option<Result<Value, String>> {
if p.get("wait").and_then(Value::as_bool) == Some(true) {
return None;
}
let items = match reload::plan(&mut app.session, p) {
Ok(items) => items,
// named as `Session::execute` names a shared parameter check's refusal
Err(EngineError::BadParams { cmd, msg }) if cmd.is_empty() => {
return Some(Err(EngineError::BadParams { cmd: "photo.reload".into(), msg }.to_string()));
}
Err(e) => return Some(Err(e.to_string())),
};
if items.is_empty() {
return None;
}
let count = items.len();
// the photos shown from their embedded preview, to say afterwards whether they decode now
let preview_only: Vec<_> =
items.iter().filter(|i| app.session.catalog.photo(i.id).is_some_and(|ph| ph.preview_only.is_some())).map(|i| i.id).collect();
let library = app.session.library_generation();
let probe = reload::file_probe(&app.session);
let cancel = Arc::new(AtomicBool::new(false));
let flag = cancel.clone();
let stopped = cancel.clone();
let work = move |task: lightcraft_engine::activity::TaskHandle| {
let total = items.len() as u64;
task.progress(0, total);
let results = reload::probe(probe.as_ref(), &items, &flag, &|done| task.progress(done as u64, total));
(items, results)
};
let done = move |app: &mut LightcraftApp, ctx: &egui::Context, (items, results)| {
if app.session.library_generation() != library {
return;
}
// as the command: one undo step, the durable save and the automatic XMP sidecar write
if let Err(e) = app.session.execute_fn("photo.reload", |s| reload::apply(s, items, results).map(|ids| reload::to_json(&ids))) {
return app.toast_error(ctx, e.to_string());
}
let cleared = preview_only.iter().filter(|id| app.session.catalog.photo(**id).is_some_and(|ph| ph.preview_only.is_none())).count();
// stopped before the files were read: they weren't tried again, so say nothing about them
// (a raw that did decode before the stop is still reported)
if stopped.load(std::sync::atomic::Ordering::Relaxed) && cleared == 0 {
return;
}
let message = match (count, preview_only.len(), cleared) {
(1, 1, 1) => crate::i18n::tr("This raw is now developed from its raw data").to_string(),
(1, 1, _) => crate::i18n::tr("This raw variant still can't be decoded").to_string(),
(_, _, 0) => return,
(_, _, 1) => crate::i18n::tr("1 raw can now be developed from its raw data").to_string(),
(_, _, n) => crate::i18n::tr_format!("{n} raws can now be developed from their raw data", n = n),
};
app.toast(ctx, message);
};
if let Err(e) = crate::tasks::spawn_tracked(app, LABEL, "reload", cancel, work, done) {
return Some(Err(e));
}
Some(Ok(json!({"background": true, "count": count})))
}