M5: Alt+Left / Alt+Right go back through the view history with a form field selected, judged by each press's modifiers (#3632)

Prepare's arrow nudge read the frame's Alt, so an Alt+arrow whose Alt was
let go in the same frame nudged the field instead. Each press's own
modifiers now decide, as for objects, pictures and comments.
This commit is contained in:
Adrian
2026-10-28 21:41:33 -04:00
parent 5d6f36f060
commit 08f3991ab9
2 changed files with 53 additions and 15 deletions
+19 -15
View File
@@ -618,30 +618,34 @@ pub(crate) fn delete_selected(view: &mut DocView) -> Option<Edit> {
/// as the object nudge does (`object_ui::nudge_keys`) and as dragging it does (#2608). The keys
/// are consumed before the view turns the page with them; one press is one undo step.
pub(crate) fn nudge_keys(ctx: &egui::Context, view: &mut DocView, doc: &Document) {
// `consume_key` lets an extra Alt through, and Alt+↑/↓ belongs to Calculation order's move.
if ctx.egui_wants_keyboard_input() || view.prepare.grab.is_some() || ctx.input(|i| i.modifiers.alt) {
if ctx.egui_wants_keyboard_input() || view.prepare.grab.is_some() {
return;
}
let Some((name, wi)) = view.prepare.selected.clone() else { return };
let Some(w) = doc.form.iter().find(|f| f.name == name).and_then(|f| f.widgets.get(wi)) else { return };
let Some(page) = w.page else { return };
let Some(xf) = view.page_xform(page) else { return };
use egui::{Key, Modifiers};
use egui::Key;
let mut step = egui::Vec2::ZERO;
ctx.input_mut(|i| {
for (key, direction) in [
(Key::ArrowLeft, egui::vec2(-1.0, 0.0)),
(Key::ArrowRight, egui::vec2(1.0, 0.0)),
(Key::ArrowUp, egui::vec2(0.0, -1.0)),
(Key::ArrowDown, egui::vec2(0.0, 1.0)),
] {
// Shift first: a plain key pattern also matches a Shift press.
if i.consume_key(Modifiers::SHIFT, key) {
step += direction * 10.0;
} else if i.consume_key(Modifiers::NONE, key) {
step += direction;
// Only an arrow pressed alone or with Shift, judged by each press's own modifiers (the
// frame's can differ when a key is let go in the same frame). Alt+← / Alt+→ go back and
// forward through the view history (#3632) and Alt+↑ / ↓ is Calculation order's move.
i.events.retain(|e| {
let egui::Event::Key { key, pressed: true, modifiers, .. } = e else { return true };
if modifiers.alt || modifiers.ctrl || modifiers.command || modifiers.mac_cmd {
return true;
}
}
let direction = match key {
Key::ArrowLeft => egui::vec2(-1.0, 0.0),
Key::ArrowRight => egui::vec2(1.0, 0.0),
Key::ArrowUp => egui::vec2(0.0, -1.0),
Key::ArrowDown => egui::vec2(0.0, 1.0),
_ => return true,
};
step += direction * if modifiers.shift { 10.0 } else { 1.0 };
false
});
});
if step == egui::Vec2::ZERO {
return;
+34
View File
@@ -156,6 +156,40 @@ fn arrow_keys_nudge_the_selected_field_instead_of_turning_the_page() {
assert_eq!(h.state().views[0].current, 0, "the arrows moved the field, not the page");
}
/// #3632: Alt+← / Alt+→ go back and forward through the view history with a field selected, and
/// leave the field where it is, even when Alt is let go in the same frame as the arrow, so the
/// frame has no modifiers and only the press carries Alt.
#[test]
fn alt_arrows_navigate_instead_of_nudging_the_selected_field() {
let mut h = harness();
assert!(h.state_mut().apply_edit(pdfcraft_engine::Edit::InsertBlankPage { at: 1, width: 612.0, height: 792.0 }));
h.run_steps(2);
h.state_mut().execute("form.add.text");
h.run_steps(1);
let p = at(&h, 40.0, 250.0);
h.hover_at(p);
h.run_steps(1);
h.drag_at(p);
h.run_steps(1);
h.drop_at(p);
h.run_steps(4);
let name = h.state().views[0].prepare.selected.clone().expect("the placed field is selected").0;
let before = rect_of(&h, &name);
h.state_mut().views[0].go_to_page(1);
h.run_steps(2);
h.state_mut().views[0].go_to_page(0);
h.run_steps(2);
assert!(h.state().views[0].prepare.selected.is_some(), "still selected, so a nudge could take the keys");
let arrow = |key, modifiers| egui::Event::Key { key, physical_key: None, pressed: true, repeat: false, modifiers };
h.event(arrow(egui::Key::ArrowLeft, egui::Modifiers::ALT));
h.run_steps(4);
assert_eq!(h.state().views[0].current, 1, "an Alt+← press went back");
h.event(arrow(egui::Key::ArrowRight, egui::Modifiers::ALT));
h.run_steps(4);
assert_eq!(h.state().views[0].current, 0, "an Alt+→ press went forward");
assert_eq!(rect_of(&h, &name), before, "the field didn't move");
}
#[test]
fn format_validate_and_calculate_tabs() {
use pdfcraft_engine::form_scripts::{Calculate, Format, Validate};