M10: review of #1609: an automatic run's result waits for Read mode and dialogs

CodeRabbit: Edit refuses to start recognition in Read mode or beneath a dialog,
but a run started a moment earlier could finish after the user had switched to
Read mode or opened a dialog, and poll_ocr applied its result there. The result
of a run Edit started is now left until both are gone; a run the user asked for
completes as before. The unit test uses a made-up result, so it needs no OCR
models, and fails without the check.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
PhilipandClaude Sonnet 5.5 committed 2026-10-21 21:43:58 +01:00
1 parent 22636e3a79
commit 90c15aa514
1 file changed
+43
+43
View File
@@ -400,6 +400,16 @@ impl PdfCraftApp {
let Some(run) = self.ocr_run.as_ref() else { return };
let (doc, progress, automatic) = (run.doc, run.progress.clone(), run.automatic);
let Ok(mut s) = progress.lock() else { return };
// The result of a run Edit started waits while Read mode or a dialog is up: nothing may change
// the document beneath them, and the run may have been started before they appeared. A run
// the user asked for completes as before.
if automatic && s.result.is_some() && (self.mode == crate::Mode::Read || self.modal_open()) {
drop(s);
if let Some(ctx) = &self.ctx {
ctx.request_repaint_after(std::time::Duration::from_millis(200));
}
return;
}
let Some(result) = s.result.take() else {
let msg = crate::i18n::fmt(
tl!("Recognizing text… page {d} of {t}"),
@@ -539,4 +549,37 @@ mod tests {
app.session.apply(id, add).unwrap();
assert_eq!(found(&mut app), Some(0), "a picture over the page makes it a scan");
}
/// A finished run Edit started is applied only once Read mode and dialogs are gone; one the user
/// asked for is applied at once. The result is made up, so no OCR models are needed.
#[test]
fn the_result_of_an_automatic_run_waits_for_read_mode_and_dialogs() {
let word = || pdfcraft_engine::ocr::PlacedWord { text: "scanned".into(), origin: [72.0, 700.0], across: [60.0, 0.0], up: [0.0, 12.0] };
let finished = |app: &PdfCraftApp, automatic: bool| {
let page = pdfcraft_engine::ocr::OcrPage { page: 0, words: vec![word()], already_on_page: 0, skipped: None };
let progress = Arc::new(Mutex::new(OcrProgress { done: 1, total: 1, result: Some(Ok(vec![page])), cancel: false }));
OcrRun { doc: app.active_ids().unwrap().1, progress, automatic }
};
let label = |app: &PdfCraftApp| app.session.get(app.active_ids().unwrap().1).unwrap().can_undo().map(String::from);
let mut app = app_with(scan());
app.ocr_run = Some(finished(&app, true));
app.mode = Mode::Read;
app.poll_ocr();
assert!(app.ocr_run.is_some() && label(&app).is_none(), "waits in Read mode");
app.mode = Mode::Edit;
app.dialog = Some(Dialog::JsConsole);
app.poll_ocr();
assert!(app.ocr_run.is_some() && label(&app).is_none(), "waits beneath a dialog");
app.dialog = None;
app.poll_ocr();
assert!(app.ocr_run.is_none(), "taken once they are gone");
assert_eq!(label(&app).as_deref(), Some("Recognize text"));
// A run the user asked for is not held back.
let mut app = app_with(scan());
app.ocr_run = Some(finished(&app, false));
app.mode = Mode::Read;
app.poll_ocr();
assert_eq!(label(&app).as_deref(), Some("Recognize text"));
}
}