mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-12 01:11:19 +00:00
M12: a batched focus event that changes nothing keeps the batch's edits, and an exit is noted as run only when it ran
This commit is contained in:
1 parent
2e8720c332
commit
3257bac8e0
3 files changed
+59
-7
No files matched your search
@@ -3866,8 +3866,9 @@ impl Session {
|
||||
guard(|| run_edit(&mut next, &edit, &mut cx))
|
||||
.unwrap_or_else(|m| Err(EditError::Invalid(format!("{} failed unexpectedly ({m}); the document was not changed", edit.label()))))?;
|
||||
// A field's enter or exit scripts that changed nothing leave no edit behind (no undo step,
|
||||
// nothing unsaved); what they showed or asked for still goes out.
|
||||
if cx.xfa_unchanged {
|
||||
// nothing unsaved); what they showed or asked for still goes out. Only an event on its own:
|
||||
// a batch keeps the other edits it holds.
|
||||
if cx.xfa_unchanged && matches!(edit, Edit::XfaEvent { .. }) {
|
||||
doc.js_output.append(cx.xfa_out);
|
||||
if cx.xfa_ran_away {
|
||||
doc.xfa_template = None;
|
||||
@@ -4037,6 +4038,7 @@ impl Session {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Undo the last edit of document `id`, keeping it for [`Session::redo`]. Returns its label.
|
||||
pub fn undo(&mut self, id: DocId) -> Result<Label, EditError> {
|
||||
let doc = self.doc_mut(id)?;
|
||||
let editor = doc.editor.as_mut().ok_or(EditError::NothingToUndo)?;
|
||||
@@ -4068,6 +4070,7 @@ impl Session {
|
||||
Ok(label)
|
||||
}
|
||||
|
||||
/// Redo the last edit of document `id` that [`Session::undo`] took back. Returns its label.
|
||||
pub fn redo(&mut self, id: DocId) -> Result<Label, EditError> {
|
||||
let doc = self.doc_mut(id)?;
|
||||
let editor = doc.editor.as_mut().ok_or(EditError::NothingToRedo)?;
|
||||
|
||||
@@ -4741,6 +4741,50 @@ fn an_exit_skip_does_not_outlive_its_change() {
|
||||
assert_eq!(field_text(&s, id, "exits"), (before.parse::<u32>().unwrap() + 1).to_string(), "after another edit, the exit runs");
|
||||
}
|
||||
|
||||
/// A focus event that changes nothing, batched with a form edit, doesn't cost the batch its edit.
|
||||
#[test]
|
||||
fn a_batch_keeps_its_edits_when_its_focus_event_changes_nothing() {
|
||||
let mut s = Session::new();
|
||||
let id = s.open("form.pdf", None, focus_form(), None).expect("opens");
|
||||
let som = {
|
||||
let doc = s.get(id).unwrap();
|
||||
let quiet = doc.form.iter().find(|f| f.name == "quiet").unwrap();
|
||||
crate::xfa::widget_som(&doc.editor.as_ref().unwrap().cos, quiet, 0).unwrap()
|
||||
};
|
||||
let batch = Edit::Batch {
|
||||
label: "Fill and leave".into(),
|
||||
edits: vec![
|
||||
Edit::SetFieldValue { name: "quiet".into(), value: FieldValue::Text("z".into()) },
|
||||
Edit::XfaEvent { som, activity: "exit".into() },
|
||||
],
|
||||
};
|
||||
s.apply(id, batch).unwrap();
|
||||
assert_eq!(field_text(&s, id, "quiet"), "z", "the value is kept");
|
||||
assert!(s.get(id).unwrap().dirty);
|
||||
}
|
||||
|
||||
/// A field's exit is noted as run (for the focus loss to skip) only when an exit script ran:
|
||||
/// with every script skipped, as when the event's budget is spent or a script ran away, nothing
|
||||
/// is noted and the next focus loss runs the exit.
|
||||
#[test]
|
||||
fn on_changes_notes_only_the_exits_that_ran() {
|
||||
let mut s = Session::new();
|
||||
let id = s.open("form.pdf", None, focus_form(), None).expect("opens");
|
||||
let doc = s.get(id).unwrap();
|
||||
let mut cos = doc.editor.as_ref().unwrap().cos.clone();
|
||||
let tpl = doc.xfa_template.clone().unwrap();
|
||||
let names = vec!["probe".to_string()];
|
||||
let mut exited = Vec::new();
|
||||
let mut out = crate::js::JsOutput::default();
|
||||
let mut stopped = crate::xfa::XfaRun { page_count: 1, out: &mut out, datasets: None, ran_away: true };
|
||||
crate::xfa::on_changes(&mut cos, &tpl, &names, &mut stopped, &mut exited).unwrap();
|
||||
assert!(exited.is_empty(), "no exit ran: {exited:?}");
|
||||
let mut out = crate::js::JsOutput::default();
|
||||
let mut running = crate::xfa::XfaRun { page_count: 1, out: &mut out, datasets: None, ran_away: false };
|
||||
crate::xfa::on_changes(&mut cos, &tpl, &names, &mut running, &mut exited).unwrap();
|
||||
assert_eq!(exited.len(), 1, "the exit ran: {exited:?}");
|
||||
}
|
||||
|
||||
/// Scripts that change nothing leave no edit: the document stays saved, with nothing to undo,
|
||||
/// and what they showed still reaches the viewer. A field without such scripts runs nothing.
|
||||
#[test]
|
||||
|
||||
@@ -839,13 +839,18 @@ pub(crate) fn on_changes(
|
||||
let som = r.by_som().iter().find(|(_, f)| *f == name).map(|(s, _)| s.clone());
|
||||
let Some(som) = som else { continue };
|
||||
let value = r.fields().get(name).map(|f| f.value.join("\n")).unwrap_or_default();
|
||||
let exits = r.events_of(&som, "exit");
|
||||
if !exits.is_empty() {
|
||||
exited.push(som.clone());
|
||||
}
|
||||
for ev in r.events_of(&som, "change").into_iter().chain(exits) {
|
||||
for ev in r.events_of(&som, "change") {
|
||||
r.run(&ev, &value)?;
|
||||
}
|
||||
// Noted only when an exit script ran: one the event's budget skipped, or one an error
|
||||
// before it stopped, is still to run when the field loses focus.
|
||||
let mut ran_exit = false;
|
||||
for ev in r.events_of(&som, "exit") {
|
||||
ran_exit |= r.run(&ev, &value)?.is_some();
|
||||
}
|
||||
if ran_exit {
|
||||
exited.push(som.clone());
|
||||
}
|
||||
for ev in r.events_of(&som, "validate") {
|
||||
let Some(o) = r.run(&ev, &value)? else { continue };
|
||||
if (o.result_bool == Some(false) || o.error.is_some()) && !value.trim().is_empty() {
|
||||
|
||||
Reference in new issue
Block a user