From 1ef0c1b948229488b279883ba2164423e905fe4f Mon Sep 17 00:00:00 2001 From: Eric Kaden <608341+EricKaden@users.noreply.github.com> Date: Mon, 19 Oct 2026 23:20:41 -0400 Subject: [PATCH] Forms: a background form's highlight request applies at once --- crates/engine/src/js.rs | 15 +++++++++++++++ crates/ui-egui/src/js_ui.rs | 19 +++++++++++-------- crates/ui-egui/src/lib.rs | 3 +++ crates/ui-egui/tests/pickers.rs | 16 ++++++++++++++++ 4 files changed, 45 insertions(+), 8 deletions(-) diff --git a/crates/engine/src/js.rs b/crates/engine/src/js.rs index 84a3dc6b4d..a3ba953e3e 100644 --- a/crates/engine/src/js.rs +++ b/crates/engine/src/js.rs @@ -194,6 +194,21 @@ impl Session { self.docs.iter_mut().find(|d| d.id == id).map(|d| std::mem::take(&mut d.js_output)).unwrap_or_default() } + /// The last field-highlight change (`app.runtimeHighlight`) scripts asked for in document + /// `id`, taken out of its waiting output; the rest stays for [`Session::take_js_output`]. + pub fn take_js_highlight(&mut self, id: DocId) -> Option { + let doc = self.docs.iter_mut().find(|d| d.id == id)?; + let mut last = None; + doc.js_output.requests.retain(|r| match r { + Request::Highlight(on) => { + last = Some(*on); + false + } + _ => true, + }); + last + } + /// Run `script` in document `id`, for a button (`target`: the field, event "Mouse Up") or /// the console (no target). Field changes and `resetForm` are applied as one undoable step; /// everything else the script asked for is returned. diff --git a/crates/ui-egui/src/js_ui.rs b/crates/ui-egui/src/js_ui.rs index 669077f387..dec380ced6 100644 --- a/crates/ui-egui/src/js_ui.rs +++ b/crates/ui-egui/src/js_ui.rs @@ -67,14 +67,7 @@ impl PdfCraftApp { &[("u", &u)], ), Request::SaveAs => self.run_command("file.save_as"), - // As the Highlight fields button does: this document and the next ones. - Request::Highlight(on) => { - if let Some(i) = view { - self.views[i].highlight_fields = on; - } - self.view_defaults.highlight_fields = on; - self.session.set_field_highlight(on); - } + Request::Highlight(on) => self.script_highlight(id, on), Request::Focus(_) | Request::Beep | Request::Reset(_) => {} } } @@ -83,6 +76,16 @@ impl PdfCraftApp { } } + /// A form's script turned field highlighting on or off (`app.runtimeHighlight`): as the + /// Highlight fields button does, for document `id` and the next ones. + pub(crate) fn script_highlight(&mut self, id: DocId, on: bool) { + if let Some(v) = self.views.iter_mut().find(|v| v.id == id) { + v.highlight_fields = on; + } + self.view_defaults.highlight_fields = on; + self.session.set_field_highlight(on); + } + /// Run a push button's JavaScript (its Mouse Up action). pub fn run_button_script(&mut self, id: DocId, field: &str, script: &str) { // A script reads the fields: include what's still being typed in one (#166). diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index 55935ea092..d3c8dfabd0 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -1380,6 +1380,9 @@ impl PdfCraftApp { if activate { let out = self.session.take_js_output(id); self.handle_js(id, out); + } else if let Some(on) = self.session.take_js_highlight(id) { + // The field highlight a form asks for isn't about the current document: now. + self.script_highlight(id, on); } Ok(id) } diff --git a/crates/ui-egui/tests/pickers.rs b/crates/ui-egui/tests/pickers.rs index bf9df10b39..dd8fadabd8 100644 --- a/crates/ui-egui/tests/pickers.rs +++ b/crates/ui-egui/tests/pickers.rs @@ -746,6 +746,22 @@ fn a_background_startup_forms_script_notice_waits_until_its_tab_is_selected() { assert!(h.state().toast.is_none(), "the notice is delivered once"); } +/// A form opened behind the current document that turns field highlighting on gets it at +/// once, and so does the next document: only requests that act on the current document (a +/// print, a save, a message) wait for its tab. +#[test] +fn a_background_forms_highlight_request_applies_at_once() { + let h = harness(&[3]); + assert!(!h.state().view_defaults.highlight_fields); + deliver_startup(&h, "late-form.pdf", pdfcraft_xfa::fixtures::shell(&pdfcraft_xfa::fixtures::layout_ready_template())); + let mut h = h; + h.run_steps(2); + assert_eq!(h.state().active, Some(0), "it opened behind the current document"); + assert!(h.state().views[1].highlight_fields, "the background form is highlighted"); + assert!(!h.state().views[0].highlight_fields, "the current document is left as it was"); + assert!(h.state().view_defaults.highlight_fields, "the next document opens highlighted"); +} + /// Embedded bytes and the PDF's original /UF name stay independent of the Save suggestion. fn attachment_fixture() -> Vec { attachment_pdf("invoice\u{202e}fdp.exe", b"hello notes")