Use keyboard-types crate

Have embedders send DOM keys to servo and use a strongly typed KeyboardEvent
from the W3C UI Events spec. All keyboard handling now uses the new types.

Introduce a ShortcutMatcher to recognize key bindings. Shortcuts are now
recognized in a uniform way.

Updated the winit port.
Updated webdriver integration.

part of #20331
This commit is contained in:
Pyfisch 2018-10-06 17:35:45 +02:00
parent 76ddbe4d7a
commit 0ccaa7e1a9
35 changed files with 762 additions and 1604 deletions

View file

@ -7,7 +7,7 @@
use clipboard_provider::ClipboardProvider;
use dom::bindings::str::DOMString;
use dom::keyboardevent::KeyboardEvent;
use msg::constellation_msg::{Key, KeyModifiers};
use keyboard_types::{Key, KeyState, Modifiers, ShortcutMatcher};
use std::borrow::ToOwned;
use std::cmp::{max, min};
use std::default::Default;
@ -130,17 +130,11 @@ pub enum Direction {
Backward,
}
/// Was the keyboard event accompanied by the standard control modifier,
/// i.e. cmd on Mac OS or ctrl on other platforms.
// Some shortcuts use Cmd on Mac and Control on other systems.
#[cfg(target_os = "macos")]
fn is_control_key(mods: KeyModifiers) -> bool {
mods.contains(KeyModifiers::SUPER) && !mods.contains(KeyModifiers::CONTROL | KeyModifiers::ALT)
}
pub const CMD_OR_CONTROL: Modifiers = Modifiers::META;
#[cfg(not(target_os = "macos"))]
fn is_control_key(mods: KeyModifiers) -> bool {
mods.contains(KeyModifiers::CONTROL) && !mods.contains(KeyModifiers::SUPER | KeyModifiers::ALT)
}
pub const CMD_OR_CONTROL: Modifiers = Modifiers::CONTROL;
/// The length in bytes of the first n characters in a UTF-8 string.
///
@ -685,155 +679,139 @@ impl<T: ClipboardProvider> TextInput<T> {
/// Process a given `KeyboardEvent` and return an action for the caller to execute.
pub fn handle_keydown(&mut self, event: &KeyboardEvent) -> KeyReaction {
if let Some(key) = event.get_key() {
self.handle_keydown_aux(event.printable(), key, event.get_key_modifiers())
} else {
KeyReaction::Nothing
}
let key = event.key();
let mods = event.modifiers();
self.handle_keydown_aux(key, mods, cfg!(target_os = "macos"))
}
// This function exists for easy unit testing.
// To test Mac OS shortcuts on other systems a flag is passed.
pub fn handle_keydown_aux(
&mut self,
printable: Option<char>,
key: Key,
mods: KeyModifiers,
mut mods: Modifiers,
macos: bool,
) -> KeyReaction {
let maybe_select = if mods.contains(KeyModifiers::SHIFT) {
let maybe_select = if mods.contains(Modifiers::SHIFT) {
Selection::Selected
} else {
Selection::NotSelected
};
match (printable, key) {
(_, Key::B) if mods.contains(KeyModifiers::CONTROL | KeyModifiers::ALT) => {
mods.remove(Modifiers::SHIFT);
ShortcutMatcher::new(KeyState::Down, key.clone(), mods)
.shortcut(Modifiers::CONTROL | Modifiers::ALT, 'B', || {
self.adjust_horizontal_by_word(Direction::Backward, maybe_select);
KeyReaction::RedrawSelection
},
(_, Key::F) if mods.contains(KeyModifiers::CONTROL | KeyModifiers::ALT) => {
})
.shortcut(Modifiers::CONTROL | Modifiers::ALT, 'F', || {
self.adjust_horizontal_by_word(Direction::Forward, maybe_select);
KeyReaction::RedrawSelection
},
(_, Key::A) if mods.contains(KeyModifiers::CONTROL | KeyModifiers::ALT) => {
})
.shortcut(Modifiers::CONTROL | Modifiers::ALT, 'A', || {
self.adjust_horizontal_to_line_end(Direction::Backward, maybe_select);
KeyReaction::RedrawSelection
},
(_, Key::E) if mods.contains(KeyModifiers::CONTROL | KeyModifiers::ALT) => {
})
.shortcut(Modifiers::CONTROL | Modifiers::ALT, 'E', || {
self.adjust_horizontal_to_line_end(Direction::Forward, maybe_select);
KeyReaction::RedrawSelection
},
#[cfg(target_os = "macos")]
(None, Key::A) if mods == KeyModifiers::CONTROL =>
{
})
.optional_shortcut(macos, Modifiers::CONTROL, 'A', || {
self.adjust_horizontal_to_line_end(Direction::Backward, maybe_select);
KeyReaction::RedrawSelection
}
#[cfg(target_os = "macos")]
(None, Key::E) if mods == KeyModifiers::CONTROL =>
{
})
.optional_shortcut(macos, Modifiers::CONTROL, 'E', || {
self.adjust_horizontal_to_line_end(Direction::Forward, maybe_select);
KeyReaction::RedrawSelection
}
(_, Key::A) if is_control_key(mods) => {
})
.shortcut(CMD_OR_CONTROL, 'A', || {
self.select_all();
KeyReaction::RedrawSelection
},
(_, Key::C) if is_control_key(mods) => {
})
.shortcut(CMD_OR_CONTROL, 'C', || {
if let Some(text) = self.get_selection_text() {
self.clipboard_provider.set_clipboard_contents(text);
}
KeyReaction::DispatchInput
},
(_, Key::V) if is_control_key(mods) => {
})
.shortcut(CMD_OR_CONTROL, 'V', || {
let contents = self.clipboard_provider.clipboard_contents();
self.insert_string(contents);
KeyReaction::DispatchInput
},
(Some(c), _) => {
self.insert_char(c);
KeyReaction::DispatchInput
},
(None, Key::Delete) => {
})
.shortcut(Modifiers::empty(), Key::Delete, || {
self.delete_char(Direction::Forward);
KeyReaction::DispatchInput
},
(None, Key::Backspace) => {
})
.shortcut(Modifiers::empty(), Key::Backspace, || {
self.delete_char(Direction::Backward);
KeyReaction::DispatchInput
},
#[cfg(target_os = "macos")]
(None, Key::Left) if mods.contains(KeyModifiers::SUPER) =>
{
})
.optional_shortcut(macos, Modifiers::META, Key::ArrowLeft, || {
self.adjust_horizontal_to_line_end(Direction::Backward, maybe_select);
KeyReaction::RedrawSelection
}
#[cfg(target_os = "macos")]
(None, Key::Right) if mods.contains(KeyModifiers::SUPER) =>
{
})
.optional_shortcut(macos, Modifiers::META, Key::ArrowRight, || {
self.adjust_horizontal_to_line_end(Direction::Forward, maybe_select);
KeyReaction::RedrawSelection
}
#[cfg(target_os = "macos")]
(None, Key::Up) if mods.contains(KeyModifiers::SUPER) =>
{
})
.optional_shortcut(macos, Modifiers::META, Key::ArrowUp, || {
self.adjust_horizontal_to_limit(Direction::Backward, maybe_select);
KeyReaction::RedrawSelection
}
#[cfg(target_os = "macos")]
(None, Key::Down) if mods.contains(KeyModifiers::SUPER) =>
{
})
.optional_shortcut(macos, Modifiers::META, Key::ArrowDown, || {
self.adjust_horizontal_to_limit(Direction::Forward, maybe_select);
KeyReaction::RedrawSelection
}
(None, Key::Left) if mods.contains(KeyModifiers::ALT) => {
})
.shortcut(Modifiers::ALT, Key::ArrowLeft, || {
self.adjust_horizontal_by_word(Direction::Backward, maybe_select);
KeyReaction::RedrawSelection
},
(None, Key::Right) if mods.contains(KeyModifiers::ALT) => {
})
.shortcut(Modifiers::ALT, Key::ArrowRight, || {
self.adjust_horizontal_by_word(Direction::Forward, maybe_select);
KeyReaction::RedrawSelection
},
(None, Key::Left) => {
})
.shortcut(Modifiers::empty(), Key::ArrowLeft, || {
self.adjust_horizontal_by_one(Direction::Backward, maybe_select);
KeyReaction::RedrawSelection
},
(None, Key::Right) => {
})
.shortcut(Modifiers::empty(), Key::ArrowRight, || {
self.adjust_horizontal_by_one(Direction::Forward, maybe_select);
KeyReaction::RedrawSelection
},
(None, Key::Up) => {
})
.shortcut(Modifiers::empty(), Key::ArrowUp, || {
self.adjust_vertical(-1, maybe_select);
KeyReaction::RedrawSelection
},
(None, Key::Down) => {
})
.shortcut(Modifiers::empty(), Key::ArrowDown, || {
self.adjust_vertical(1, maybe_select);
KeyReaction::RedrawSelection
},
(None, Key::Enter) | (None, Key::KpEnter) => self.handle_return(),
(None, Key::Home) => {
#[cfg(not(target_os = "macos"))]
{
self.edit_point.index = 0;
}
})
.shortcut(Modifiers::empty(), Key::Enter, || self.handle_return())
.optional_shortcut(macos, Modifiers::empty(), Key::Home, || {
self.edit_point.index = 0;
KeyReaction::RedrawSelection
},
(None, Key::End) => {
#[cfg(not(target_os = "macos"))]
{
self.edit_point.index = self.current_line_length();
self.assert_ok_selection();
}
})
.optional_shortcut(macos, Modifiers::empty(), Key::End, || {
self.edit_point.index = self.current_line_length();
self.assert_ok_selection();
KeyReaction::RedrawSelection
},
(None, Key::PageUp) => {
})
.shortcut(Modifiers::empty(), Key::PageUp, || {
self.adjust_vertical(-28, maybe_select);
KeyReaction::RedrawSelection
},
(None, Key::PageDown) => {
})
.shortcut(Modifiers::empty(), Key::PageDown, || {
self.adjust_vertical(28, maybe_select);
KeyReaction::RedrawSelection
},
_ => KeyReaction::Nothing,
}
})
.otherwise(|| {
if let Key::Character(ref c) = key {
self.insert_string(c.as_str());
return KeyReaction::DispatchInput;
}
KeyReaction::Nothing
})
.unwrap()
}
/// Whether the content is empty.