use pdfrum_form::field::text::{Disposition, Motion, TextAction, route_char, route_key};
use pdfrum_form::{Key, Modifiers};
const GENERAL: (Modifiers, bool) = (Modifiers::CONTROL, true);
const APPLE: (Modifiers, bool) = (Modifiers::META, false);
fn on_key(k: Key, m: Modifiers, (accel, redo_y): (Modifiers, bool)) -> Disposition {
route_key(k, m, accel, redo_y, false)
}
fn handled(d: Disposition) -> bool {
matches!(d, Disposition::Do(_))
}
#[test]
fn navigation_keys_are_handled() {
for platform in [GENERAL, APPLE] {
assert!(handled(on_key(Key::Left, Modifiers::NONE, platform)));
assert!(handled(on_key(Key::Home, Modifiers::CONTROL, platform)));
assert!(handled(on_key(Key::Home, Modifiers::NONE, platform)));
assert!(handled(on_key(Key::Up, Modifiers::NONE, platform)));
assert!(handled(on_key(Key::Right, Modifiers::NONE, platform)));
}
}
#[test]
fn the_clipboard_shortcuts_are_not_handled() {
const C: Key = Key::from_virtual(0x43);
const V: Key = Key::from_virtual(0x56);
const X: Key = Key::from_virtual(0x58);
for platform in [GENERAL, APPLE] {
let (accel, _) = platform;
for key in [C, V, X] {
assert_eq!(
on_key(key, accel, platform),
Disposition::Ignore,
"{key:?} belongs to the embedder"
);
}
}
}
#[test]
fn select_all_is_the_accelerator_with_a_and_nothing_else() {
for platform in [GENERAL, APPLE] {
let (accel, _) = platform;
assert_eq!(
on_key(Key::A, accel, platform),
Disposition::Do(TextAction::SelectAll)
);
assert_eq!(
on_key(Key::A, accel | Modifiers::SHIFT, platform),
Disposition::Ignore,
"the shifted form is explicitly not select-all"
);
}
assert_eq!(
on_key(Key::A, Modifiers::META, GENERAL),
Disposition::Ignore
);
assert_eq!(
on_key(Key::A, Modifiers::CONTROL, APPLE),
Disposition::Ignore
);
}
#[test]
fn select_all_does_not_happen_on_the_character_path() {
for (accel, _) in [GENERAL, APPLE] {
assert_eq!(
route_char('\u{01}', accel, accel, false, false),
Disposition::Ignore
);
assert_eq!(
route_char('a', accel, accel, false, false),
Disposition::Ignore,
"a modified character is neither a shortcut nor text"
);
}
}
#[test]
fn undo_and_redo_are_the_accelerator_with_z() {
for platform in [GENERAL, APPLE] {
let (accel, _) = platform;
assert_eq!(
on_key(Key::Z, accel, platform),
Disposition::Do(TextAction::Undo)
);
assert_eq!(
on_key(Key::Z, accel | Modifiers::SHIFT, platform),
Disposition::Do(TextAction::Redo)
);
}
assert_eq!(
on_key(Key::Z, Modifiers::META, GENERAL),
Disposition::Ignore
);
assert_eq!(
on_key(Key::Z, Modifiers::CONTROL, APPLE),
Disposition::Ignore
);
}
#[test]
fn the_accelerator_with_y_redoes_on_one_platform_only() {
assert_eq!(
on_key(Key::Y, Modifiers::CONTROL, GENERAL),
Disposition::Do(TextAction::Redo)
);
assert_eq!(
on_key(Key::Y, Modifiers::META, APPLE),
Disposition::Ignore,
"the same gesture does nothing on an Apple keyboard"
);
assert_eq!(
on_key(Key::Y, Modifiers::CONTROL | Modifiers::SHIFT, GENERAL),
Disposition::Ignore
);
for platform in [GENERAL, APPLE] {
let (accel, _) = platform;
assert_eq!(
on_key(Key::Z, accel | Modifiers::SHIFT, platform),
Disposition::Do(TextAction::Redo)
);
}
}
#[test]
fn keys_the_field_does_not_decide_on_fall_through() {
const NEWLINE: Key = Key::Newline;
const DIGIT_ZERO: Key = Key::from_virtual(0x30);
const DIGIT_NINE: Key = Key::from_virtual(0x39);
const LETTER_Z_PLAIN: Key = Key::Z;
const F1: Key = Key::from_virtual(0x70);
for key in [
NEWLINE,
Key::Return,
Key::Space,
DIGIT_ZERO,
DIGIT_NINE,
F1,
Key::Tab,
] {
assert_eq!(
on_key(key, Modifiers::NONE, GENERAL),
Disposition::Ignore,
"{key:?} is not handled by the text field"
);
}
assert_eq!(
on_key(LETTER_Z_PLAIN, Modifiers::NONE, GENERAL),
Disposition::Ignore
);
}
#[test]
fn modifier_keys_reported_as_keys_are_not_consumed() {
for key in [Key::Shift, Key::Control, Key::Space] {
assert_eq!(
on_key(key, Modifiers::NONE, GENERAL),
Disposition::Ignore,
"{key:?} is not the field's"
);
}
}
#[test]
fn a_read_only_field_consumes_without_acting() {
assert_eq!(
route_char('a', Modifiers::NONE, Modifiers::CONTROL, true, false),
Disposition::Consume
);
assert_ne!(
route_char('a', Modifiers::NONE, Modifiers::CONTROL, true, false),
Disposition::Ignore,
"consuming is not the same as ignoring"
);
}
#[test]
fn the_delete_control_character_is_ignored() {
assert_eq!(
route_char('\u{7F}', Modifiers::NONE, Modifiers::CONTROL, false, false),
Disposition::Ignore
);
assert_eq!(
route_char('\u{7F}', Modifiers::NONE, Modifiers::CONTROL, true, false),
Disposition::Ignore
);
}
#[test]
fn delete_with_a_selection_removes_the_selection() {
assert_eq!(
route_key(Key::Delete, Modifiers::NONE, Modifiers::CONTROL, true, true),
Disposition::Do(TextAction::ClearSelection)
);
assert_eq!(
route_key(
Key::Delete,
Modifiers::NONE,
Modifiers::CONTROL,
true,
false
),
Disposition::Do(TextAction::Delete)
);
}
#[test]
fn home_and_end_widen_with_the_accelerator_and_extend_with_shift() {
for platform in [GENERAL, APPLE] {
assert_eq!(
on_key(Key::Home, Modifiers::NONE, platform),
Disposition::Do(TextAction::Move {
motion: Motion::LineStart,
extend: false
})
);
assert_eq!(
on_key(Key::Home, Modifiers::CONTROL, platform),
Disposition::Do(TextAction::Move {
motion: Motion::DocStart,
extend: false
})
);
assert_eq!(
on_key(Key::End, Modifiers::CONTROL | Modifiers::SHIFT, platform),
Disposition::Do(TextAction::Move {
motion: Motion::DocEnd,
extend: true
})
);
}
}
#[test]
fn non_ascii_characters_are_ordinary_text() {
for ch in ['\u{05D1}', '\u{05D2}', '\u{05EA}'] {
assert_eq!(
route_char(ch, Modifiers::NONE, Modifiers::CONTROL, false, false),
Disposition::Do(TextAction::Insert(ch))
);
}
}