Auto merge of #21881 - pyfisch:keyboard-types, r=paulrouget

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

What this PR does:

* allow the use non-ASCII keyboards for text input
* decouple keyboard event "key" from "code" (key meaning vs location)

What this PR does not do:

* completely improve keyboard events send from winit and webdriver
* add support for CompositionEvent or IME

Notes:

* The winit embedder does not send keyup events for printable keys (this is a regression)
* keyboard-types is on crates.io because I believe it to be useful outside of servo. If you prefer I can add a copy in this repo.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/21881)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2018-10-17 05:36:08 -04:00 committed by GitHub
commit 9a0404ac5f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 747 additions and 1673 deletions

View file

@ -58,6 +58,7 @@ image = "0.19"
ipc-channel = "0.11"
itertools = "0.7.6"
jstraceable_derive = {path = "../jstraceable_derive"}
keyboard-types = {version = "0.4.2-servo", features = ["serde"]}
lazy_static = "1"
libc = "0.2"
log = "0.4"

View file

@ -100,9 +100,10 @@ use hyper_serde::Serde;
use ipc_channel::ipc::{self, IpcSender};
use js::jsapi::{JSContext, JSObject, JSRuntime};
use js::jsapi::JS_GetRuntime;
use keyboard_types::{Key, KeyState, Modifiers};
use metrics::{InteractiveFlag, InteractiveMetrics, InteractiveWindow, ProfilerMetadataFactory, ProgressiveWebMetric};
use mime::{Mime, TopLevel, SubLevel};
use msg::constellation_msg::{BrowsingContextId, Key, KeyModifiers, KeyState};
use msg::constellation_msg::BrowsingContextId;
use net_traits::{FetchResponseMsg, IpcSend, ReferrerPolicy};
use net_traits::CookieSource::NonHTTP;
use net_traits::CoreResourceMsg::{GetCookiesForUrl, SetCookiesForUrl};
@ -769,10 +770,12 @@ impl Document {
// Step 1 is not handled here; the fragid is already obtained by the calling function
// Step 2: Simply use None to indicate the top of the document.
// Step 3 & 4
percent_decode(fragid.as_bytes()).decode_utf8().ok()
// Step 5
percent_decode(fragid.as_bytes())
.decode_utf8()
.ok()
// Step 5
.and_then(|decoded_fragid| self.get_element_by_id(&Atom::from(decoded_fragid)))
// Step 6
// Step 6
.or_else(|| self.get_anchor_by_name(fragid))
// Step 7 & 8
}
@ -805,7 +808,8 @@ impl Document {
rect.origin.x.to_nearest_px() as f32,
rect.origin.y.to_nearest_px() as f32,
)
}).or_else(|| {
})
.or_else(|| {
if fragment.is_empty() || fragment.eq_ignore_ascii_case("top") {
// FIXME(stshine): this should be the origin of the stacking context space,
// which may differ under the influence of writing mode.
@ -1348,13 +1352,7 @@ impl Document {
}
/// The entry point for all key processing for web content
pub fn dispatch_key_event(
&self,
ch: Option<char>,
key: Key,
state: KeyState,
modifiers: KeyModifiers,
) {
pub fn dispatch_key_event(&self, keyboard_event: ::keyboard_types::KeyboardEvent) {
let focused = self.get_focused_element();
let body = self.GetBody();
@ -1364,50 +1362,29 @@ impl Document {
(&None, &None) => self.window.upcast(),
};
let ctrl = modifiers.contains(KeyModifiers::CONTROL);
let alt = modifiers.contains(KeyModifiers::ALT);
let shift = modifiers.contains(KeyModifiers::SHIFT);
let meta = modifiers.contains(KeyModifiers::SUPER);
let is_composing = false;
let is_repeating = state == KeyState::Repeated;
let ev_type = DOMString::from(
match state {
KeyState::Pressed | KeyState::Repeated => "keydown",
KeyState::Released => "keyup",
}.to_owned(),
);
let props = KeyboardEvent::key_properties(ch, key, modifiers);
let keyevent = KeyboardEvent::new(
&self.window,
ev_type,
DOMString::from(keyboard_event.state.to_string()),
true,
true,
Some(&self.window),
0,
ch,
Some(key),
DOMString::from(props.key_string.clone()),
DOMString::from(props.code),
props.location,
is_repeating,
is_composing,
ctrl,
alt,
shift,
meta,
None,
props.key_code,
keyboard_event.key.clone(),
DOMString::from(keyboard_event.code.to_string()),
keyboard_event.location as u32,
keyboard_event.repeat,
keyboard_event.is_composing,
keyboard_event.modifiers,
0,
keyboard_event.key.legacy_keycode(),
);
let event = keyevent.upcast::<Event>();
event.fire(target);
let mut cancel_state = event.get_cancel_state();
// https://w3c.github.io/uievents/#keys-cancelable-keys
if state != KeyState::Released &&
props.is_printable() &&
if keyboard_event.state == KeyState::Down &&
keyboard_event.key.legacy_charcode() != 0 &&
cancel_state != EventDefault::Prevented
{
// https://w3c.github.io/uievents/#keypress-event-order
@ -1418,18 +1395,13 @@ impl Document {
true,
Some(&self.window),
0,
ch,
Some(key),
DOMString::from(props.key_string),
DOMString::from(props.code),
props.location,
is_repeating,
is_composing,
ctrl,
alt,
shift,
meta,
props.char_code,
keyboard_event.key.clone(),
DOMString::from(keyboard_event.code.to_string()),
keyboard_event.location as u32,
keyboard_event.repeat,
keyboard_event.is_composing,
keyboard_event.modifiers,
keyboard_event.key.legacy_charcode(),
0,
);
let ev = event.upcast::<Event>();
@ -1438,7 +1410,7 @@ impl Document {
}
if cancel_state == EventDefault::Allowed {
let msg = EmbedderMsg::KeyEvent(ch, key, state, modifiers);
let msg = EmbedderMsg::Keyboard(keyboard_event.clone());
self.send_to_embedder(msg);
// This behavior is unspecced
@ -1446,8 +1418,10 @@ impl Document {
// however *when* we do it is up to us.
// Here, we're dispatching it after the key event so the script has a chance to cancel it
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=27337
match key {
Key::Space if state == KeyState::Released => {
match keyboard_event.key {
Key::Character(ref letter)
if letter == " " && keyboard_event.state == KeyState::Up =>
{
let maybe_elem = target.downcast::<Element>();
if let Some(el) = maybe_elem {
synthetic_click_activation(
@ -1459,11 +1433,15 @@ impl Document {
ActivationSource::NotFromClick,
)
}
},
Key::Enter if state == KeyState::Released => {
}
Key::Enter if keyboard_event.state == KeyState::Up => {
let maybe_elem = target.downcast::<Element>();
if let Some(el) = maybe_elem {
if let Some(a) = el.as_maybe_activatable() {
let ctrl = keyboard_event.modifiers.contains(Modifiers::CONTROL);
let alt = keyboard_event.modifiers.contains(Modifiers::ALT);
let shift = keyboard_event.modifiers.contains(Modifiers::SHIFT);
let meta = keyboard_event.modifiers.contains(Modifiers::META);
a.implicit_submission(ctrl, alt, shift, meta);
}
}

View file

@ -4,7 +4,7 @@
use dom::bindings::cell::DomRefCell;
use dom::bindings::codegen::Bindings::KeyboardEventBinding;
use dom::bindings::codegen::Bindings::KeyboardEventBinding::{KeyboardEventConstants, KeyboardEventMethods};
use dom::bindings::codegen::Bindings::KeyboardEventBinding::KeyboardEventMethods;
use dom::bindings::codegen::Bindings::UIEventBinding::UIEventMethods;
use dom::bindings::error::Fallible;
use dom::bindings::inheritance::Castable;
@ -15,47 +15,39 @@ use dom::event::Event;
use dom::uievent::UIEvent;
use dom::window::Window;
use dom_struct::dom_struct;
use msg::constellation_msg::{Key, KeyModifiers};
use std::borrow::Cow;
use keyboard_types::{Key, Modifiers};
use std::cell::Cell;
unsafe_no_jsmanaged_fields!(Key);
unsafe_no_jsmanaged_fields!(Modifiers);
#[dom_struct]
pub struct KeyboardEvent {
uievent: UIEvent,
key: Cell<Option<Key>>,
key_string: DomRefCell<DOMString>,
key: DomRefCell<DOMString>,
typed_key: DomRefCell<Key>,
code: DomRefCell<DOMString>,
location: Cell<u32>,
ctrl: Cell<bool>,
alt: Cell<bool>,
shift: Cell<bool>,
meta: Cell<bool>,
modifiers: Cell<Modifiers>,
repeat: Cell<bool>,
is_composing: Cell<bool>,
char_code: Cell<Option<u32>>,
char_code: Cell<u32>,
key_code: Cell<u32>,
printable: Cell<Option<char>>,
}
impl KeyboardEvent {
fn new_inherited() -> KeyboardEvent {
KeyboardEvent {
uievent: UIEvent::new_inherited(),
key: Cell::new(None),
key_string: DomRefCell::new(DOMString::new()),
key: DomRefCell::new(DOMString::new()),
typed_key: DomRefCell::new(Key::Unidentified),
code: DomRefCell::new(DOMString::new()),
location: Cell::new(0),
ctrl: Cell::new(false),
alt: Cell::new(false),
shift: Cell::new(false),
meta: Cell::new(false),
modifiers: Cell::new(Modifiers::empty()),
repeat: Cell::new(false),
is_composing: Cell::new(false),
char_code: Cell::new(None),
char_code: Cell::new(0),
key_code: Cell::new(0),
printable: Cell::new(None),
}
}
@ -74,18 +66,13 @@ impl KeyboardEvent {
cancelable: bool,
view: Option<&Window>,
_detail: i32,
ch: Option<char>,
key: Option<Key>,
key_string: DOMString,
key: Key,
code: DOMString,
location: u32,
repeat: bool,
is_composing: bool,
ctrl_key: bool,
alt_key: bool,
shift_key: bool,
meta_key: bool,
char_code: Option<u32>,
modifiers: Modifiers,
char_code: u32,
key_code: u32,
) -> DomRoot<KeyboardEvent> {
let ev = KeyboardEvent::new_uninitialized(window);
@ -94,22 +81,18 @@ impl KeyboardEvent {
can_bubble,
cancelable,
view,
key_string,
DOMString::from(key.to_string()),
location,
DOMString::new(),
repeat,
DOMString::new(),
);
ev.key.set(key);
*ev.typed_key.borrow_mut() = key;
*ev.code.borrow_mut() = code;
ev.ctrl.set(ctrl_key);
ev.alt.set(alt_key);
ev.shift.set(shift_key);
ev.meta.set(meta_key);
ev.char_code.set(char_code);
ev.printable.set(ch);
ev.key_code.set(key_code);
ev.modifiers.set(modifiers);
ev.is_composing.set(is_composing);
ev.char_code.set(char_code);
ev.key_code.set(key_code);
ev
}
@ -118,6 +101,11 @@ impl KeyboardEvent {
type_: DOMString,
init: &KeyboardEventBinding::KeyboardEventInit,
) -> Fallible<DomRoot<KeyboardEvent>> {
let mut modifiers = Modifiers::empty();
modifiers.set(Modifiers::CONTROL, init.parent.ctrlKey);
modifiers.set(Modifiers::ALT, init.parent.altKey);
modifiers.set(Modifiers::SHIFT, init.parent.shiftKey);
modifiers.set(Modifiers::META, init.parent.metaKey);
let event = KeyboardEvent::new(
window,
type_,
@ -125,680 +113,27 @@ impl KeyboardEvent {
init.parent.parent.parent.cancelable,
init.parent.parent.view.r(),
init.parent.parent.detail,
None,
key_from_string(&init.key, init.location),
init.key.clone(),
Key::Unidentified,
init.code.clone(),
init.location,
init.repeat,
init.isComposing,
init.parent.ctrlKey,
init.parent.altKey,
init.parent.shiftKey,
init.parent.metaKey,
None,
modifiers,
0,
0,
);
*event.key.borrow_mut() = init.key.clone();
Ok(event)
}
pub fn key_properties(ch: Option<char>, key: Key, mods: KeyModifiers) -> KeyEventProperties {
KeyEventProperties {
key_string: key_value(ch, key, mods),
code: code_value(key),
location: key_location(key),
char_code: ch.map(|ch| ch as u32),
key_code: key_keycode(key),
}
}
}
impl KeyboardEvent {
pub fn printable(&self) -> Option<char> {
self.printable.get()
pub fn key(&self) -> Key {
self.typed_key.borrow().clone()
}
pub fn get_key(&self) -> Option<Key> {
self.key.get().clone()
}
pub fn get_key_modifiers(&self) -> KeyModifiers {
let mut result = KeyModifiers::empty();
if self.shift.get() {
result = result | KeyModifiers::SHIFT;
}
if self.ctrl.get() {
result = result | KeyModifiers::CONTROL;
}
if self.alt.get() {
result = result | KeyModifiers::ALT;
}
if self.meta.get() {
result = result | KeyModifiers::SUPER;
}
result
}
}
// https://w3c.github.io/uievents-key/#key-value-tables
pub fn key_value(ch: Option<char>, key: Key, mods: KeyModifiers) -> Cow<'static, str> {
if let Some(ch) = ch {
return Cow::from(format!("{}", ch));
}
let shift = mods.contains(KeyModifiers::SHIFT);
Cow::from(match key {
Key::Space => " ",
Key::Apostrophe if shift => "\"",
Key::Apostrophe => "'",
Key::Comma if shift => "<",
Key::Comma => ",",
Key::Minus if shift => "_",
Key::Minus => "-",
Key::Period if shift => ">",
Key::Period => ".",
Key::Slash if shift => "?",
Key::Slash => "/",
Key::GraveAccent if shift => "~",
Key::GraveAccent => "`",
Key::Num0 if shift => ")",
Key::Num0 => "0",
Key::Num1 if shift => "!",
Key::Num1 => "1",
Key::Num2 if shift => "@",
Key::Num2 => "2",
Key::Num3 if shift => "#",
Key::Num3 => "3",
Key::Num4 if shift => "$",
Key::Num4 => "4",
Key::Num5 if shift => "%",
Key::Num5 => "5",
Key::Num6 if shift => "^",
Key::Num6 => "6",
Key::Num7 if shift => "&",
Key::Num7 => "7",
Key::Num8 if shift => "*",
Key::Num8 => "8",
Key::Num9 if shift => "(",
Key::Num9 => "9",
Key::Semicolon if shift => ":",
Key::Semicolon => ";",
Key::Equal if shift => "+",
Key::Equal => "=",
Key::A if shift => "A",
Key::A => "a",
Key::B if shift => "B",
Key::B => "b",
Key::C if shift => "C",
Key::C => "c",
Key::D if shift => "D",
Key::D => "d",
Key::E if shift => "E",
Key::E => "e",
Key::F if shift => "F",
Key::F => "f",
Key::G if shift => "G",
Key::G => "g",
Key::H if shift => "H",
Key::H => "h",
Key::I if shift => "I",
Key::I => "i",
Key::J if shift => "J",
Key::J => "j",
Key::K if shift => "K",
Key::K => "k",
Key::L if shift => "L",
Key::L => "l",
Key::M if shift => "M",
Key::M => "m",
Key::N if shift => "N",
Key::N => "n",
Key::O if shift => "O",
Key::O => "o",
Key::P if shift => "P",
Key::P => "p",
Key::Q if shift => "Q",
Key::Q => "q",
Key::R if shift => "R",
Key::R => "r",
Key::S if shift => "S",
Key::S => "s",
Key::T if shift => "T",
Key::T => "t",
Key::U if shift => "U",
Key::U => "u",
Key::V if shift => "V",
Key::V => "v",
Key::W if shift => "W",
Key::W => "w",
Key::X if shift => "X",
Key::X => "x",
Key::Y if shift => "Y",
Key::Y => "y",
Key::Z if shift => "Z",
Key::Z => "z",
Key::LeftBracket if shift => "{",
Key::LeftBracket => "[",
Key::Backslash if shift => "|",
Key::Backslash => "\\",
Key::RightBracket if shift => "}",
Key::RightBracket => "]",
Key::World1 => "Unidentified",
Key::World2 => "Unidentified",
Key::Escape => "Escape",
Key::Enter => "Enter",
Key::Tab => "Tab",
Key::Backspace => "Backspace",
Key::Insert => "Insert",
Key::Delete => "Delete",
Key::Right => "ArrowRight",
Key::Left => "ArrowLeft",
Key::Down => "ArrowDown",
Key::Up => "ArrowUp",
Key::PageUp => "PageUp",
Key::PageDown => "PageDown",
Key::Home => "Home",
Key::End => "End",
Key::CapsLock => "CapsLock",
Key::ScrollLock => "ScrollLock",
Key::NumLock => "NumLock",
Key::PrintScreen => "PrintScreen",
Key::Pause => "Pause",
Key::F1 => "F1",
Key::F2 => "F2",
Key::F3 => "F3",
Key::F4 => "F4",
Key::F5 => "F5",
Key::F6 => "F6",
Key::F7 => "F7",
Key::F8 => "F8",
Key::F9 => "F9",
Key::F10 => "F10",
Key::F11 => "F11",
Key::F12 => "F12",
Key::F13 => "F13",
Key::F14 => "F14",
Key::F15 => "F15",
Key::F16 => "F16",
Key::F17 => "F17",
Key::F18 => "F18",
Key::F19 => "F19",
Key::F20 => "F20",
Key::F21 => "F21",
Key::F22 => "F22",
Key::F23 => "F23",
Key::F24 => "F24",
Key::F25 => "F25",
Key::Kp0 => "0",
Key::Kp1 => "1",
Key::Kp2 => "2",
Key::Kp3 => "3",
Key::Kp4 => "4",
Key::Kp5 => "5",
Key::Kp6 => "6",
Key::Kp7 => "7",
Key::Kp8 => "8",
Key::Kp9 => "9",
Key::KpDecimal => ".",
Key::KpDivide => "/",
Key::KpMultiply => "*",
Key::KpSubtract => "-",
Key::KpAdd => "+",
Key::KpEnter => "Enter",
Key::KpEqual => "=",
Key::LeftShift => "Shift",
Key::LeftControl => "Control",
Key::LeftAlt => "Alt",
Key::LeftSuper => "Super",
Key::RightShift => "Shift",
Key::RightControl => "Control",
Key::RightAlt => "Alt",
Key::RightSuper => "Super",
Key::Menu => "ContextMenu",
Key::NavigateForward => "BrowserForward",
Key::NavigateBackward => "BrowserBack",
})
}
fn key_from_string(key_string: &str, location: u32) -> Option<Key> {
match key_string {
" " => Some(Key::Space),
"\"" => Some(Key::Apostrophe),
"'" => Some(Key::Apostrophe),
"<" => Some(Key::Comma),
"," => Some(Key::Comma),
"_" => Some(Key::Minus),
"-" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Minus),
">" => Some(Key::Period),
"." if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Period),
"?" => Some(Key::Slash),
"/" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Slash),
"~" => Some(Key::GraveAccent),
"`" => Some(Key::GraveAccent),
")" => Some(Key::Num0),
"0" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num0),
"!" => Some(Key::Num1),
"1" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num1),
"@" => Some(Key::Num2),
"2" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num2),
"#" => Some(Key::Num3),
"3" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num3),
"$" => Some(Key::Num4),
"4" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num4),
"%" => Some(Key::Num5),
"5" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num5),
"^" => Some(Key::Num6),
"6" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num6),
"&" => Some(Key::Num7),
"7" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num7),
"*" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num8),
"8" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num8),
"(" => Some(Key::Num9),
"9" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num9),
":" => Some(Key::Semicolon),
";" => Some(Key::Semicolon),
"+" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Equal),
"=" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Equal),
"A" => Some(Key::A),
"a" => Some(Key::A),
"B" => Some(Key::B),
"b" => Some(Key::B),
"C" => Some(Key::C),
"c" => Some(Key::C),
"D" => Some(Key::D),
"d" => Some(Key::D),
"E" => Some(Key::E),
"e" => Some(Key::E),
"F" => Some(Key::F),
"f" => Some(Key::F),
"G" => Some(Key::G),
"g" => Some(Key::G),
"H" => Some(Key::H),
"h" => Some(Key::H),
"I" => Some(Key::I),
"i" => Some(Key::I),
"J" => Some(Key::J),
"j" => Some(Key::J),
"K" => Some(Key::K),
"k" => Some(Key::K),
"L" => Some(Key::L),
"l" => Some(Key::L),
"M" => Some(Key::M),
"m" => Some(Key::M),
"N" => Some(Key::N),
"n" => Some(Key::N),
"O" => Some(Key::O),
"o" => Some(Key::O),
"P" => Some(Key::P),
"p" => Some(Key::P),
"Q" => Some(Key::Q),
"q" => Some(Key::Q),
"R" => Some(Key::R),
"r" => Some(Key::R),
"S" => Some(Key::S),
"s" => Some(Key::S),
"T" => Some(Key::T),
"t" => Some(Key::T),
"U" => Some(Key::U),
"u" => Some(Key::U),
"V" => Some(Key::V),
"v" => Some(Key::V),
"W" => Some(Key::W),
"w" => Some(Key::W),
"X" => Some(Key::X),
"x" => Some(Key::X),
"Y" => Some(Key::Y),
"y" => Some(Key::Y),
"Z" => Some(Key::Z),
"z" => Some(Key::Z),
"{" => Some(Key::LeftBracket),
"[" => Some(Key::LeftBracket),
"|" => Some(Key::Backslash),
"\\" => Some(Key::Backslash),
"}" => Some(Key::RightBracket),
"]" => Some(Key::RightBracket),
"Escape" => Some(Key::Escape),
"Enter" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => {
Some(Key::Enter)
},
"Tab" => Some(Key::Tab),
"Backspace" => Some(Key::Backspace),
"Insert" => Some(Key::Insert),
"Delete" => Some(Key::Delete),
"ArrowRight" => Some(Key::Right),
"ArrowLeft" => Some(Key::Left),
"ArrowDown" => Some(Key::Down),
"ArrowUp" => Some(Key::Up),
"PageUp" => Some(Key::PageUp),
"PageDown" => Some(Key::PageDown),
"Home" => Some(Key::Home),
"End" => Some(Key::End),
"CapsLock" => Some(Key::CapsLock),
"ScrollLock" => Some(Key::ScrollLock),
"NumLock" => Some(Key::NumLock),
"PrintScreen" => Some(Key::PrintScreen),
"Pause" => Some(Key::Pause),
"F1" => Some(Key::F1),
"F2" => Some(Key::F2),
"F3" => Some(Key::F3),
"F4" => Some(Key::F4),
"F5" => Some(Key::F5),
"F6" => Some(Key::F6),
"F7" => Some(Key::F7),
"F8" => Some(Key::F8),
"F9" => Some(Key::F9),
"F10" => Some(Key::F10),
"F11" => Some(Key::F11),
"F12" => Some(Key::F12),
"F13" => Some(Key::F13),
"F14" => Some(Key::F14),
"F15" => Some(Key::F15),
"F16" => Some(Key::F16),
"F17" => Some(Key::F17),
"F18" => Some(Key::F18),
"F19" => Some(Key::F19),
"F20" => Some(Key::F20),
"F21" => Some(Key::F21),
"F22" => Some(Key::F22),
"F23" => Some(Key::F23),
"F24" => Some(Key::F24),
"F25" => Some(Key::F25),
"0" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::Kp0),
"1" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::Kp1),
"2" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::Kp2),
"3" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::Kp3),
"4" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::Kp4),
"5" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::Kp5),
"6" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::Kp6),
"7" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::Kp7),
"8" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::Kp8),
"9" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::Kp9),
"." if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::KpDecimal),
"/" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::KpDivide),
"*" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::KpMultiply),
"-" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::KpSubtract),
"+" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::KpAdd),
"Enter" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => {
Some(Key::KpEnter)
},
"=" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::KpEqual),
"Shift" if location == KeyboardEventConstants::DOM_KEY_LOCATION_LEFT => {
Some(Key::LeftShift)
},
"Control" if location == KeyboardEventConstants::DOM_KEY_LOCATION_LEFT => {
Some(Key::LeftControl)
},
"Alt" if location == KeyboardEventConstants::DOM_KEY_LOCATION_LEFT => Some(Key::LeftAlt),
"Super" if location == KeyboardEventConstants::DOM_KEY_LOCATION_LEFT => {
Some(Key::LeftSuper)
},
"Shift" if location == KeyboardEventConstants::DOM_KEY_LOCATION_RIGHT => {
Some(Key::RightShift)
},
"Control" if location == KeyboardEventConstants::DOM_KEY_LOCATION_RIGHT => {
Some(Key::RightControl)
},
"Alt" if location == KeyboardEventConstants::DOM_KEY_LOCATION_RIGHT => Some(Key::RightAlt),
"Super" if location == KeyboardEventConstants::DOM_KEY_LOCATION_RIGHT => {
Some(Key::RightSuper)
},
"ContextMenu" => Some(Key::Menu),
"BrowserForward" => Some(Key::NavigateForward),
"BrowserBack" => Some(Key::NavigateBackward),
_ => None,
}
}
// https://w3c.github.io/uievents-code/#code-value-tables
fn code_value(key: Key) -> &'static str {
match key {
Key::Space => "Space",
Key::Apostrophe => "Quote",
Key::Comma => "Comma",
Key::Minus => "Minus",
Key::Period => "Period",
Key::Slash => "Slash",
Key::GraveAccent => "Backquote",
Key::Num0 => "Digit0",
Key::Num1 => "Digit1",
Key::Num2 => "Digit2",
Key::Num3 => "Digit3",
Key::Num4 => "Digit4",
Key::Num5 => "Digit5",
Key::Num6 => "Digit6",
Key::Num7 => "Digit7",
Key::Num8 => "Digit8",
Key::Num9 => "Digit9",
Key::Semicolon => "Semicolon",
Key::Equal => "Equal",
Key::A => "KeyA",
Key::B => "KeyB",
Key::C => "KeyC",
Key::D => "KeyD",
Key::E => "KeyE",
Key::F => "KeyF",
Key::G => "KeyG",
Key::H => "KeyH",
Key::I => "KeyI",
Key::J => "KeyJ",
Key::K => "KeyK",
Key::L => "KeyL",
Key::M => "KeyM",
Key::N => "KeyN",
Key::O => "KeyO",
Key::P => "KeyP",
Key::Q => "KeyQ",
Key::R => "KeyR",
Key::S => "KeyS",
Key::T => "KeyT",
Key::U => "KeyU",
Key::V => "KeyV",
Key::W => "KeyW",
Key::X => "KeyX",
Key::Y => "KeyY",
Key::Z => "KeyZ",
Key::LeftBracket => "BracketLeft",
Key::Backslash => "Backslash",
Key::RightBracket => "BracketRight",
Key::World1 | Key::World2 => panic!("unknown char code for {:?}", key),
Key::Escape => "Escape",
Key::Enter => "Enter",
Key::Tab => "Tab",
Key::Backspace => "Backspace",
Key::Insert => "Insert",
Key::Delete => "Delete",
Key::Right => "ArrowRight",
Key::Left => "ArrowLeft",
Key::Down => "ArrowDown",
Key::Up => "ArrowUp",
Key::PageUp => "PageUp",
Key::PageDown => "PageDown",
Key::Home => "Home",
Key::End => "End",
Key::CapsLock => "CapsLock",
Key::ScrollLock => "ScrollLock",
Key::NumLock => "NumLock",
Key::PrintScreen => "PrintScreen",
Key::Pause => "Pause",
Key::F1 => "F1",
Key::F2 => "F2",
Key::F3 => "F3",
Key::F4 => "F4",
Key::F5 => "F5",
Key::F6 => "F6",
Key::F7 => "F7",
Key::F8 => "F8",
Key::F9 => "F9",
Key::F10 => "F10",
Key::F11 => "F11",
Key::F12 => "F12",
Key::F13 => "F13",
Key::F14 => "F14",
Key::F15 => "F15",
Key::F16 => "F16",
Key::F17 => "F17",
Key::F18 => "F18",
Key::F19 => "F19",
Key::F20 => "F20",
Key::F21 => "F21",
Key::F22 => "F22",
Key::F23 => "F23",
Key::F24 => "F24",
Key::F25 => "F25",
Key::Kp0 => "Numpad0",
Key::Kp1 => "Numpad1",
Key::Kp2 => "Numpad2",
Key::Kp3 => "Numpad3",
Key::Kp4 => "Numpad4",
Key::Kp5 => "Numpad5",
Key::Kp6 => "Numpad6",
Key::Kp7 => "Numpad7",
Key::Kp8 => "Numpad8",
Key::Kp9 => "Numpad9",
Key::KpDecimal => "NumpadDecimal",
Key::KpDivide => "NumpadDivide",
Key::KpMultiply => "NumpadMultiply",
Key::KpSubtract => "NumpadSubtract",
Key::KpAdd => "NumpadAdd",
Key::KpEnter => "NumpadEnter",
Key::KpEqual => "NumpadEqual",
Key::LeftShift | Key::RightShift => "Shift",
Key::LeftControl | Key::RightControl => "Control",
Key::LeftAlt | Key::RightAlt => "Alt",
Key::LeftSuper | Key::RightSuper => "Super",
Key::Menu => "ContextMenu",
Key::NavigateForward => "BrowserForward",
Key::NavigateBackward => "BrowserBackward",
}
}
fn key_location(key: Key) -> u32 {
match key {
Key::Kp0 |
Key::Kp1 |
Key::Kp2 |
Key::Kp3 |
Key::Kp4 |
Key::Kp5 |
Key::Kp6 |
Key::Kp7 |
Key::Kp8 |
Key::Kp9 |
Key::KpDecimal |
Key::KpDivide |
Key::KpMultiply |
Key::KpSubtract |
Key::KpAdd |
Key::KpEnter |
Key::KpEqual => KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD,
Key::LeftShift | Key::LeftAlt | Key::LeftControl | Key::LeftSuper => {
KeyboardEventConstants::DOM_KEY_LOCATION_LEFT
},
Key::RightShift | Key::RightAlt | Key::RightControl | Key::RightSuper => {
KeyboardEventConstants::DOM_KEY_LOCATION_RIGHT
},
_ => KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD,
}
}
// https://w3c.github.io/uievents/#legacy-key-models
fn key_keycode(key: Key) -> u32 {
match key {
// https://w3c.github.io/uievents/#legacy-key-models
Key::Backspace => 8,
Key::Tab => 9,
Key::Enter => 13,
Key::LeftShift | Key::RightShift => 16,
Key::LeftControl | Key::RightControl => 17,
Key::LeftAlt | Key::RightAlt => 18,
Key::CapsLock => 20,
Key::Escape => 27,
Key::Space => 32,
Key::PageUp => 33,
Key::PageDown => 34,
Key::End => 35,
Key::Home => 36,
Key::Left => 37,
Key::Up => 38,
Key::Right => 39,
Key::Down => 40,
Key::Delete => 46,
// https://w3c.github.io/uievents/#optionally-fixed-virtual-key-codes
Key::Semicolon => 186,
Key::Equal => 187,
Key::Comma => 188,
Key::Minus => 189,
Key::Period => 190,
Key::Slash => 191,
Key::LeftBracket => 219,
Key::Backslash => 220,
Key::RightBracket => 221,
Key::Apostrophe => 222,
//§ B.2.1.3
Key::Num0 |
Key::Num1 |
Key::Num2 |
Key::Num3 |
Key::Num4 |
Key::Num5 |
Key::Num6 |
Key::Num7 |
Key::Num8 |
Key::Num9 => key as u32 - Key::Num0 as u32 + '0' as u32,
//§ B.2.1.4
Key::A |
Key::B |
Key::C |
Key::D |
Key::E |
Key::F |
Key::G |
Key::H |
Key::I |
Key::J |
Key::K |
Key::L |
Key::M |
Key::N |
Key::O |
Key::P |
Key::Q |
Key::R |
Key::S |
Key::T |
Key::U |
Key::V |
Key::W |
Key::X |
Key::Y |
Key::Z => key as u32 - Key::A as u32 + 'A' as u32,
//§ B.2.1.8
_ => 0,
}
}
#[derive(MallocSizeOf)]
pub struct KeyEventProperties {
pub key_string: Cow<'static, str>,
pub code: &'static str,
pub location: u32,
pub char_code: Option<u32>,
pub key_code: u32,
}
impl KeyEventProperties {
pub fn is_printable(&self) -> bool {
self.char_code.is_some()
pub fn modifiers(&self) -> Modifiers {
self.modifiers.get()
}
}
@ -822,14 +157,14 @@ impl KeyboardEventMethods for KeyboardEvent {
self.upcast::<UIEvent>()
.InitUIEvent(type_arg, can_bubble_arg, cancelable_arg, view_arg, 0);
*self.key_string.borrow_mut() = key_arg;
*self.key.borrow_mut() = key_arg;
self.location.set(location_arg);
self.repeat.set(repeat);
}
// https://w3c.github.io/uievents/#widl-KeyboardEvent-key
fn Key(&self) -> DOMString {
self.key_string.borrow().clone()
self.key.borrow().clone()
}
// https://w3c.github.io/uievents/#widl-KeyboardEvent-code
@ -844,22 +179,22 @@ impl KeyboardEventMethods for KeyboardEvent {
// https://w3c.github.io/uievents/#widl-KeyboardEvent-ctrlKey
fn CtrlKey(&self) -> bool {
self.ctrl.get()
self.modifiers.get().contains(Modifiers::CONTROL)
}
// https://w3c.github.io/uievents/#widl-KeyboardEvent-shiftKey
fn ShiftKey(&self) -> bool {
self.shift.get()
self.modifiers.get().contains(Modifiers::SHIFT)
}
// https://w3c.github.io/uievents/#widl-KeyboardEvent-altKey
fn AltKey(&self) -> bool {
self.alt.get()
self.modifiers.get().contains(Modifiers::ALT)
}
// https://w3c.github.io/uievents/#widl-KeyboardEvent-metaKey
fn MetaKey(&self) -> bool {
self.meta.get()
self.modifiers.get().contains(Modifiers::META)
}
// https://w3c.github.io/uievents/#widl-KeyboardEvent-repeat
@ -874,20 +209,26 @@ impl KeyboardEventMethods for KeyboardEvent {
// https://w3c.github.io/uievents/#dom-keyboardevent-getmodifierstate
fn GetModifierState(&self, key_arg: DOMString) -> bool {
match &*key_arg {
"Ctrl" => self.CtrlKey(),
"Alt" => self.AltKey(),
"Shift" => self.ShiftKey(),
"Meta" => self.MetaKey(),
"AltGraph" | "CapsLock" | "NumLock" | "ScrollLock" | "Accel" | "Fn" | "FnLock" |
"Hyper" | "OS" | "Symbol" | "SymbolLock" => false, //FIXME
_ => false,
}
self.modifiers.get().contains(match &*key_arg {
"Alt" => Modifiers::ALT,
"AltGraph" => Modifiers::ALT_GRAPH,
"CapsLock" => Modifiers::CAPS_LOCK,
"Control" => Modifiers::CONTROL,
"Fn" => Modifiers::FN,
"FnLock" => Modifiers::FN_LOCK,
"Meta" => Modifiers::META,
"NumLock" => Modifiers::NUM_LOCK,
"ScrollLock" => Modifiers::SCROLL_LOCK,
"Shift" => Modifiers::SHIFT,
"Symbol" => Modifiers::SYMBOL,
"SymbolLock" => Modifiers::SYMBOL_LOCK,
_ => return false,
})
}
// https://w3c.github.io/uievents/#widl-KeyboardEvent-charCode
fn CharCode(&self) -> u32 {
self.char_code.get().unwrap_or(0)
self.char_code.get()
}
// https://w3c.github.io/uievents/#widl-KeyboardEvent-keyCode
@ -897,7 +238,11 @@ impl KeyboardEventMethods for KeyboardEvent {
// https://w3c.github.io/uievents/#widl-KeyboardEvent-which
fn Which(&self) -> u32 {
self.char_code.get().unwrap_or(self.KeyCode())
if self.char_code.get() != 0 {
self.char_code.get()
} else {
self.key_code.get()
}
}
// https://dom.spec.whatwg.org/#dom-event-istrusted

View file

@ -54,6 +54,7 @@ extern crate image;
extern crate ipc_channel;
#[macro_use]
extern crate jstraceable_derive;
extern crate keyboard_types;
#[macro_use]
extern crate lazy_static;
extern crate libc;

View file

@ -96,7 +96,7 @@ use script_traits::{ProgressiveWebMetricType, Painter, ScriptMsg, ScriptThreadFa
use script_traits::{ScriptToConstellationChan, TimerEvent, TimerSchedulerMsg};
use script_traits::{TimerSource, TouchEventType, TouchId, UntrustedNodeAddress};
use script_traits::{UpdatePipelineIdReason, WindowSizeData, WindowSizeType};
use script_traits::CompositorEvent::{KeyEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent, TouchEvent};
use script_traits::CompositorEvent::{KeyboardEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent, TouchEvent};
use script_traits::webdriver_msg::WebDriverScriptCommand;
use serviceworkerjob::{Job, JobQueue};
use servo_atoms::Atom;
@ -2823,6 +2823,7 @@ impl ScriptThread {
}
}
},
TouchEvent(event_type, identifier, point, node_address) => {
let touch_result = self.handle_touch_event(
pipeline_id,
@ -2848,12 +2849,12 @@ impl ScriptThread {
}
},
KeyEvent(ch, key, state, modifiers) => {
KeyboardEvent(key_event) => {
let document = match { self.documents.borrow().find_document(pipeline_id) } {
Some(document) => document,
None => return warn!("Message sent to closed pipeline {}.", pipeline_id),
};
document.dispatch_key_event(ch, key, state, modifiers);
document.dispatch_key_event(key_event);
},
}
}

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.