Add keyboard special key support for web content interaction
Enter, Backspace, Tab, Escape, Delete, arrow keys, Home, End, PageUp, PageDown now forward to Servo. Previously only printable characters reached the web engine, making forms unusable.
This commit is contained in:
@@ -10,15 +10,21 @@ impl ElyShell {
|
|||||||
_window: &mut Window,
|
_window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
let Some(text) = typed_text_from_key_down(event) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let Some((tab_id, requested_url)) = self.active_external_tab_target() else {
|
let Some((tab_id, requested_url)) = self.active_external_tab_target() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.type_text_in_external_web_viewport(tab_id, requested_url, text, cx) {
|
if let Some(text) = printable_text_from_key_down(event) {
|
||||||
cx.stop_propagation();
|
if self.type_text_in_external_web_viewport(tab_id, requested_url, text, cx) {
|
||||||
|
cx.stop_propagation();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(text) = special_key_text(event) {
|
||||||
|
if self.type_text_in_external_web_viewport(tab_id, requested_url, text, cx) {
|
||||||
|
cx.stop_propagation();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +42,7 @@ impl ElyShell {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn typed_text_from_key_down(event: &KeyDownEvent) -> Option<&str> {
|
fn printable_text_from_key_down(event: &KeyDownEvent) -> Option<&str> {
|
||||||
let modifiers = &event.keystroke.modifiers;
|
let modifiers = &event.keystroke.modifiers;
|
||||||
if modifiers.control || modifiers.platform || modifiers.function {
|
if modifiers.control || modifiers.platform || modifiers.function {
|
||||||
return None;
|
return None;
|
||||||
@@ -52,17 +58,39 @@ fn typed_text_from_key_down(event: &KeyDownEvent) -> Option<&str> {
|
|||||||
Some(text)
|
Some(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn special_key_text(event: &KeyDownEvent) -> Option<&'static str> {
|
||||||
|
if event.keystroke.modifiers.platform || event.keystroke.modifiers.control {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
match event.keystroke.key.as_str() {
|
||||||
|
"enter" => Some("\n"),
|
||||||
|
"backspace" => Some("\x08"),
|
||||||
|
"tab" => Some("\t"),
|
||||||
|
"escape" => Some("\x1b"),
|
||||||
|
"delete" => Some("\x7f"),
|
||||||
|
"left" => Some("\u{F702}"),
|
||||||
|
"right" => Some("\u{F703}"),
|
||||||
|
"up" => Some("\u{F700}"),
|
||||||
|
"down" => Some("\u{F701}"),
|
||||||
|
"home" => Some("\u{F729}"),
|
||||||
|
"end" => Some("\u{F72B}"),
|
||||||
|
"pageup" => Some("\u{F72C}"),
|
||||||
|
"pagedown" => Some("\u{F72D}"),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use gpui::{KeyDownEvent, Keystroke, Modifiers};
|
use gpui::{KeyDownEvent, Keystroke, Modifiers};
|
||||||
|
|
||||||
use super::typed_text_from_key_down;
|
use super::{printable_text_from_key_down, special_key_text};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn typed_text_uses_printable_key_char() {
|
fn typed_text_uses_printable_key_char() {
|
||||||
let event = key_down("e", Some("e"), Modifiers::none());
|
let event = key_down("e", Some("e"), Modifiers::none());
|
||||||
|
|
||||||
assert_eq!(typed_text_from_key_down(&event), Some("e"));
|
assert_eq!(printable_text_from_key_down(&event), Some("e"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -71,7 +99,7 @@ mod tests {
|
|||||||
modifiers.shift = true;
|
modifiers.shift = true;
|
||||||
let event = key_down("1", Some("!"), modifiers);
|
let event = key_down("1", Some("!"), modifiers);
|
||||||
|
|
||||||
assert_eq!(typed_text_from_key_down(&event), Some("!"));
|
assert_eq!(printable_text_from_key_down(&event), Some("!"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -80,7 +108,36 @@ mod tests {
|
|||||||
modifiers.platform = true;
|
modifiers.platform = true;
|
||||||
let event = key_down("l", None, modifiers);
|
let event = key_down("l", None, modifiers);
|
||||||
|
|
||||||
assert_eq!(typed_text_from_key_down(&event), None);
|
assert_eq!(printable_text_from_key_down(&event), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn special_key_enter() {
|
||||||
|
let event = key_down("enter", None, Modifiers::none());
|
||||||
|
assert_eq!(special_key_text(&event), Some("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn special_key_backspace() {
|
||||||
|
let event = key_down("backspace", None, Modifiers::none());
|
||||||
|
assert_eq!(special_key_text(&event), Some("\x08"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn special_key_ignored_with_platform_modifier() {
|
||||||
|
let mut modifiers = Modifiers::none();
|
||||||
|
modifiers.platform = true;
|
||||||
|
let event = key_down("enter", None, modifiers);
|
||||||
|
assert_eq!(special_key_text(&event), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn special_key_arrows() {
|
||||||
|
let event = key_down("left", None, Modifiers::none());
|
||||||
|
assert_eq!(special_key_text(&event), Some("\u{F702}"));
|
||||||
|
|
||||||
|
let event = key_down("right", None, Modifiers::none());
|
||||||
|
assert_eq!(special_key_text(&event), Some("\u{F703}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn key_down(key: &str, key_char: Option<&str>, modifiers: Modifiers) -> KeyDownEvent {
|
fn key_down(key: &str, key_char: Option<&str>, modifiers: Modifiers) -> KeyDownEvent {
|
||||||
|
|||||||
@@ -1,6 +1,25 @@
|
|||||||
use servo::Code;
|
use servo::{Code, Key};
|
||||||
|
|
||||||
pub(crate) fn keyboard_code_for_character(character: char) -> Code {
|
pub(crate) fn key_and_code_for_character(character: char) -> (Key, Code) {
|
||||||
|
match character {
|
||||||
|
'\n' | '\r' => (Key::Enter, Code::Enter),
|
||||||
|
'\x08' => (Key::Backspace, Code::Backspace),
|
||||||
|
'\t' => (Key::Tab, Code::Tab),
|
||||||
|
'\x1b' => (Key::Escape, Code::Escape),
|
||||||
|
'\x7f' => (Key::Delete, Code::Delete),
|
||||||
|
'\u{F700}' => (Key::ArrowUp, Code::ArrowUp),
|
||||||
|
'\u{F701}' => (Key::ArrowDown, Code::ArrowDown),
|
||||||
|
'\u{F702}' => (Key::ArrowLeft, Code::ArrowLeft),
|
||||||
|
'\u{F703}' => (Key::ArrowRight, Code::ArrowRight),
|
||||||
|
'\u{F729}' => (Key::Home, Code::Home),
|
||||||
|
'\u{F72B}' => (Key::End, Code::End),
|
||||||
|
'\u{F72C}' => (Key::PageUp, Code::PageUp),
|
||||||
|
'\u{F72D}' => (Key::PageDown, Code::PageDown),
|
||||||
|
_ => (Key::Character(character.to_string()), printable_code(character)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn printable_code(character: char) -> Code {
|
||||||
match character {
|
match character {
|
||||||
'a' | 'A' => Code::KeyA,
|
'a' | 'A' => Code::KeyA,
|
||||||
'b' | 'B' => Code::KeyB,
|
'b' | 'B' => Code::KeyB,
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
use servo::{
|
use servo::{
|
||||||
DevicePoint, InputEvent, Key, KeyState, KeyboardEvent, Location, Modifiers, MouseButton,
|
DevicePoint, InputEvent, KeyState, KeyboardEvent, Location, Modifiers, MouseButton,
|
||||||
MouseButtonAction, MouseButtonEvent, MouseMoveEvent, TouchEvent, TouchEventType, TouchId,
|
MouseButtonAction, MouseButtonEvent, MouseMoveEvent, TouchEvent, TouchEventType, TouchId,
|
||||||
WebView, WebViewPoint,
|
WebView, WebViewPoint,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::keyboard::keyboard_code_for_character;
|
use crate::keyboard::key_and_code_for_character;
|
||||||
|
|
||||||
pub(super) fn send_mouse_click(webview: &WebView, x: u32, y: u32) {
|
pub(super) fn send_mouse_click(webview: &WebView, x: u32, y: u32) {
|
||||||
let point = point(x, y);
|
let point = point(x, y);
|
||||||
@@ -32,8 +32,7 @@ pub(super) fn send_touch_tap(webview: &WebView, x: u32, y: u32) {
|
|||||||
|
|
||||||
pub(super) fn send_keyboard_text(webview: &WebView, text: &str) {
|
pub(super) fn send_keyboard_text(webview: &WebView, text: &str) {
|
||||||
for character in text.chars() {
|
for character in text.chars() {
|
||||||
let key = Key::Character(character.to_string());
|
let (key, code) = key_and_code_for_character(character);
|
||||||
let code = keyboard_code_for_character(character);
|
|
||||||
webview.notify_input_event(InputEvent::Keyboard(KeyboardEvent::new_without_event(
|
webview.notify_input_event(InputEvent::Keyboard(KeyboardEvent::new_without_event(
|
||||||
KeyState::Down,
|
KeyState::Down,
|
||||||
key.clone(),
|
key.clone(),
|
||||||
|
|||||||
Reference in New Issue
Block a user