mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-12 22:02:33 +00:00
forms: a push button with only a mouse-down action takes the press
Widget.mouse_down records an /AA /D action, so a push button acting only on mouse down (the LaTeX animate package's controls) is pressable and its press runs the action as Mouse Down through the widget trigger dispatch, instead of saying it has no action. Co-authored-by: Peter Yatsyshin <p.yatsyshin@googlemail.com>
This commit is contained in:
1 parent
c0f5f92b57
commit
0fba511e97
7 files changed
+34
-5
No files matched your search
@@ -2025,6 +2025,7 @@ mod widget_budget_review_tests {
|
||||
rotation: 0,
|
||||
caption: None,
|
||||
mouse_up_script: None,
|
||||
mouse_down: false,
|
||||
list: None,
|
||||
round: false,
|
||||
};
|
||||
|
||||
@@ -173,6 +173,7 @@ pub fn add_field(doc: &mut Document, page: usize, rect: [f64; 4], kind: &NewFiel
|
||||
rotation: 0,
|
||||
caption: None,
|
||||
mouse_up_script: None,
|
||||
mouse_down: false,
|
||||
list: None,
|
||||
round: true,
|
||||
};
|
||||
|
||||
@@ -140,6 +140,9 @@ pub struct Widget {
|
||||
pub caption: Option<String>,
|
||||
/// Text widget JavaScript to run on a mouse-up click.
|
||||
pub mouse_up_script: Option<String>,
|
||||
/// The widget has a mouse-down action (`/AA /D`): a press runs it (the LaTeX `animate`
|
||||
/// package's controls act on mouse down), so a push button with only that still takes a press.
|
||||
pub mouse_down: bool,
|
||||
/// List boxes: where the options are drawn, so a click can pick the one under the pointer.
|
||||
pub list: Option<appearance::ListRows>,
|
||||
/// Drawn round: a radio button, or a check box laid out for a round XFA check button
|
||||
|
||||
@@ -733,6 +733,7 @@ fn walk(
|
||||
} else {
|
||||
None
|
||||
},
|
||||
mouse_down: wd.get(b"AA").map(|a| doc.resolve(a)).as_deref().and_then(Object::as_dict).is_some_and(|aa| aa.contains(b"D")),
|
||||
list: (kind == FieldKind::List).then(|| appearance::list_rows_of(doc, inh.da.as_deref().unwrap_or("/Helv 0 Tf 0 g"), Some(d), wd)),
|
||||
round: kind == FieldKind::Radio || (kind == FieldKind::CheckBox && appearance::is_round(doc, wd)),
|
||||
})
|
||||
|
||||
@@ -58,6 +58,7 @@ fn field(name: &str, page: usize, rect: [f64; 4]) -> FormField {
|
||||
tooltip: None,
|
||||
widgets: vec![FormWidget {
|
||||
mouse_up_script: None,
|
||||
mouse_down: false,
|
||||
obj: pdfcraft_cos::ObjRef::new(4, 0),
|
||||
page: Some(page),
|
||||
rect,
|
||||
|
||||
@@ -432,11 +432,13 @@ pub(crate) fn tab_under<'a>(tabs: &[(&'a str, SignTab)], p: egui::Pos2) -> Optio
|
||||
tabs.iter().rev().find(|(_, tab)| tab.rect.contains(p)).map(|(name, _)| *name)
|
||||
}
|
||||
|
||||
/// Whether `w`, a widget of `f`, takes a press: fillable, a push button with an action, or an
|
||||
/// Whether `w`, a widget of `f`, takes a press: fillable, a push button with an action (a click
|
||||
/// or mouse-down one), or an
|
||||
/// empty signature field (signed by clicking it, Use a certificate). A radio button of a group can
|
||||
/// be read-only on its own.
|
||||
fn usable(f: &FormField, w: &FormWidget, allowed: bool) -> bool {
|
||||
let pressable = f.kind == FormFieldKind::PushButton && !f.read_only() && f.button.is_some();
|
||||
// A button acting only on mouse down (`/AA /D`) takes the press too, which runs it (#4751).
|
||||
let pressable = f.kind == FormFieldKind::PushButton && !f.read_only() && (f.button.is_some() || w.mouse_down);
|
||||
allowed && !w.read_only && (fillable(f) || pressable || f.kind == FormFieldKind::Signature)
|
||||
}
|
||||
|
||||
@@ -551,6 +553,8 @@ pub(crate) fn page_input(
|
||||
_ if f.read_only() || w.read_only => view.forms.notice = Some(FormNotice::ReadOnly(f.name.clone())),
|
||||
FormFieldKind::PushButton => match &f.button {
|
||||
Some(a) => view.forms.button = Some((f.name.clone(), pdfcraft_engine::js::field_object_id(f.obj), a.clone())),
|
||||
// Its press already ran its mouse-down action.
|
||||
None if w.mouse_down => {}
|
||||
None => view.forms.notice = Some(FormNotice::NoAction(f.name.clone())),
|
||||
},
|
||||
FormFieldKind::Text if let Some(script) = &w.mouse_up_script => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! A form field widget's pointer scripts in the app (#269, ported from #344): the pointer
|
||||
//! moving onto and off a widget runs its Mouse Enter and Mouse Exit JavaScript, and a click on a
|
||||
//! check box runs its Mouse Up JavaScript after the click stored the new value (#247).
|
||||
//! check box runs its Mouse Up JavaScript after the click stored the new value (#247). A push
|
||||
//! button with only a mouse-down action runs it when pressed (#4751).
|
||||
|
||||
use egui_kittest::Harness;
|
||||
use pdfcraft_ui_egui::PdfCraftApp;
|
||||
@@ -9,15 +10,16 @@ use pdfcraft_ui_egui::PdfCraftApp;
|
||||
/// `strike` (a check box) shows or hides `note` from its Mouse Up script by its own new value.
|
||||
fn form() -> Vec<u8> {
|
||||
let objs = [
|
||||
"<< /Type /Catalog /Pages 2 0 R /AcroForm << /Fields [4 0 R 5 0 R 6 0 R 8 0 R] /DA (/Helv 0 Tf 0 g) /DR << /Font << /Helv 7 0 R >> >> >> >>",
|
||||
"<< /Type /Catalog /Pages 2 0 R /AcroForm << /Fields [4 0 R 5 0 R 6 0 R 8 0 R 10 0 R] /DA (/Helv 0 Tf 0 g) /DR << /Font << /Helv 7 0 R >> >> >> >>",
|
||||
"<< /Type /Pages /Kids [3 0 R] /Count 1 /MediaBox [0 0 300 400] >>",
|
||||
"<< /Type /Page /Parent 2 0 R /Annots [4 0 R 5 0 R 6 0 R 8 0 R] >>",
|
||||
"<< /Type /Page /Parent 2 0 R /Annots [4 0 R 5 0 R 6 0 R 8 0 R 10 0 R] >>",
|
||||
"<< /Type /Annot /Subtype /Widget /FT /Tx /T (log) /V () /Rect [20 350 280 370] /P 3 0 R /F 4 >>",
|
||||
"<< /Type /Annot /Subtype /Widget /FT /Tx /T (hot) /V () /Rect [20 250 280 290] /P 3 0 R /F 4 /AA << /E << /S /JavaScript /JS (getField\\('log'\\).value += 'in;';) >> /X << /S /JavaScript /JS (getField\\('log'\\).value += 'out;';) >> >> >>",
|
||||
"<< /Type /Annot /Subtype /Widget /FT /Btn /T (strike) /V /Off /AS /Off /Rect [20 300 50 330] /P 3 0 R /F 4 /MK << /CA (4) >> /AP << /N << /Yes 9 0 R /Off 9 0 R >> >> /A << /S /JavaScript /JS (getField\\('note'\\).display = event.target.value == 'Off' ? display.hidden : display.visible; getField\\('log'\\).value += 'up:' + event.target.value + ';';) >> >>",
|
||||
"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >>",
|
||||
"<< /Type /Annot /Subtype /Widget /FT /Tx /T (note) /V (struck) /Rect [60 300 280 330] /P 3 0 R /F 6 >>",
|
||||
"<< /Type /XObject /Subtype /Form /BBox [0 0 30 30] /Length 0 >>\nstream\n\nendstream",
|
||||
"<< /Type /Annot /Subtype /Widget /FT /Btn /Ff 65536 /T (step) /Rect [20 200 120 230] /P 3 0 R /F 4 /MK << /CA (Step) >> /AA << /D << /S /JavaScript /JS (getField\\('log'\\).value += event.name + ';';) >> >> >>",
|
||||
];
|
||||
let mut out = b"%PDF-1.7\n".to_vec();
|
||||
let mut offsets = Vec::new();
|
||||
@@ -102,3 +104,19 @@ fn a_check_box_mouse_up_script_sees_the_value_its_click_stored() {
|
||||
crate::support::wait_until(&mut h, "unchecked and its Mouse Up ran", |app| field(app, "log").value.join(",") == "up:Yes;up:Off;");
|
||||
assert!(field(h.state(), "note").widgets[0].hidden, "hidden again");
|
||||
}
|
||||
|
||||
/// A push button with only a mouse-down action (`/AA /D`, as the LaTeX `animate` package's
|
||||
/// controls have) takes the press and runs it as Mouse Down, once per click (#4751).
|
||||
#[test]
|
||||
fn a_push_button_with_only_a_mouse_down_action_runs_it_on_press() {
|
||||
let mut h = harness();
|
||||
let step = centre(&h, "step");
|
||||
h.hover_at(step);
|
||||
h.run_steps(1);
|
||||
h.drag_at(step);
|
||||
h.run_steps(1);
|
||||
h.drop_at(step);
|
||||
crate::support::wait_until(&mut h, "Mouse Down ran", |app| field(app, "log").value.join(",") == "Mouse Down;");
|
||||
h.run_steps(4);
|
||||
assert_eq!(log(&h), "Mouse Down;", "the click runs nothing more");
|
||||
}
|
||||
Reference in new issue
Block a user