Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support non-QWERTY keyboards, take 2 #11955

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -691,9 +691,9 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.composition_request = CompositionRequest::CompositeNow(reason)
}

(Msg::KeyEvent(key, state, modified), ShutdownState::NotShuttingDown) => {
(Msg::KeyEvent(ch, key, state, modified), ShutdownState::NotShuttingDown) => {
if state == KeyState::Pressed {
self.window.handle_key(key, modified);
self.window.handle_key(ch, key, modified);
}
}

@@ -1330,8 +1330,8 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.on_touchpad_pressure_event(cursor, pressure, stage);
}

WindowEvent::KeyEvent(key, state, modifiers) => {
self.on_key_event(key, state, modifiers);
WindowEvent::KeyEvent(ch, key, state, modifiers) => {
self.on_key_event(ch, key, state, modifiers);
}

WindowEvent::Quit => {
@@ -1840,8 +1840,8 @@ impl<Window: WindowMethods> IOCompositor<Window> {
}
}

fn on_key_event(&self, key: Key, state: KeyState, modifiers: KeyModifiers) {
let msg = ConstellationMsg::KeyEvent(key, state, modifiers);
fn on_key_event(&self, ch: Option<char>, key: Key, state: KeyState, modifiers: KeyModifiers) {
let msg = ConstellationMsg::KeyEvent(ch, key, state, modifiers);
if let Err(e) = self.constellation_chan.send(msg) {
warn!("Sending key event to constellation failed ({}).", e);
}
@@ -151,7 +151,7 @@ pub enum Msg {
/// Composite.
Recomposite(CompositingReason),
/// Sends an unconsumed key event back to the compositor.
KeyEvent(Key, KeyState, KeyModifiers),
KeyEvent(Option<char>, Key, KeyState, KeyModifiers),
/// Script has handled a touch event, and either prevented or allowed default actions.
TouchEventProcessed(EventResult),
/// Changes the cursor.
@@ -76,7 +76,7 @@ pub enum WindowEvent {
/// Sent when the user quits the application
Quit,
/// Sent when a key input state changes
KeyEvent(Key, KeyState, KeyModifiers),
KeyEvent(Option<char>, Key, KeyState, KeyModifiers),
/// Sent when Ctr+R/Apple+R is called to reload the current page.
Reload,
}
@@ -159,7 +159,7 @@ pub trait WindowMethods {
fn set_cursor(&self, cursor: Cursor);

/// Process a key event.
fn handle_key(&self, key: Key, mods: KeyModifiers);
fn handle_key(&self, ch: Option<char>, key: Key, mods: KeyModifiers);

/// Does this window support a clipboard
fn supports_clipboard(&self) -> bool;
@@ -48,6 +48,7 @@ use script_traits::{IFrameLoadInfo, IFrameSandboxState, TimerEventRequest};
use script_traits::{LayoutMsg as FromLayoutMsg, ScriptMsg as FromScriptMsg, ScriptThreadFactory};
use script_traits::{MozBrowserEvent, MozBrowserErrorType};
use std::borrow::ToOwned;
use std::char;
use std::collections::HashMap;
use std::io::Error as IOError;
use std::marker::PhantomData;
@@ -575,9 +576,9 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
debug!("constellation got get-pipeline-title message");
self.handle_get_pipeline_title_msg(pipeline_id);
}
FromCompositorMsg::KeyEvent(key, state, modifiers) => {
FromCompositorMsg::KeyEvent(ch, key, state, modifiers) => {
debug!("constellation got key event message");
self.handle_key_msg(key, state, modifiers);
self.handle_key_msg(ch, key, state, modifiers);
}
// Load a new page from a typed url
// If there is already a pending page (self.pending_frames), it will not be overridden;
@@ -804,8 +805,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
self.compositor_proxy.send(ToCompositorMsg::ChangePageTitle(pipeline_id, title))
}

FromScriptMsg::SendKeyEvent(key, key_state, key_modifiers) => {
self.compositor_proxy.send(ToCompositorMsg::KeyEvent(key, key_state, key_modifiers))
FromScriptMsg::SendKeyEvent(key, key_state, key_modifiers, ch) => {
self.compositor_proxy.send(ToCompositorMsg::KeyEvent(ch, key, key_state, key_modifiers))
}

FromScriptMsg::TouchEventProcessed(result) => {
@@ -1397,7 +1398,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
}
}

fn handle_key_msg(&mut self, key: Key, state: KeyState, mods: KeyModifiers) {
fn handle_key_msg(&mut self, ch: Option<char>, key: Key, state: KeyState, mods: KeyModifiers) {
// Send to the explicitly focused pipeline (if it exists), or the root
// frame's current pipeline. If neither exist, fall back to sending to
// the compositor below.
@@ -1408,7 +1409,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>

match pipeline_id {
Some(pipeline_id) => {
let event = CompositorEvent::KeyEvent(key, state, mods);
let event = CompositorEvent::KeyEvent(key, state, mods, ch);
let msg = ConstellationControlMsg::SendEvent(pipeline_id, event);
let result = match self.pipelines.get(&pipeline_id) {
Some(pipeline) => pipeline.script_chan.send(msg),
@@ -1419,7 +1420,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
}
},
None => {
let event = ToCompositorMsg::KeyEvent(key, state, mods);
let event = ToCompositorMsg::KeyEvent(ch, key, state, mods);
self.compositor_proxy.clone_compositor_proxy().send(event);
}
}
@@ -1630,7 +1631,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
None => return warn!("Pipeline {:?} SendKeys after closure.", pipeline_id),
};
for (key, mods, state) in cmd {
let event = CompositorEvent::KeyEvent(key, state, mods);
let event = CompositorEvent::KeyEvent(key, state, mods, None);
let control_msg = ConstellationControlMsg::SendEvent(pipeline_id, event);
if let Err(e) = script_channel.send(control_msg) {
return self.handle_send_error(pipeline_id, e);
@@ -5,7 +5,7 @@
//! The `ByteString` struct.

use std::ascii::AsciiExt;
use std::borrow::ToOwned;
use std::borrow::{ToOwned, Cow};
use std::fmt;
use std::hash::{Hash, Hasher};
use std::ops;
@@ -204,6 +204,15 @@ impl<'a> From<&'a str> for DOMString {
}
}

impl<'a> From<Cow<'a, str>> for DOMString {
fn from(contents: Cow<'a, str>) -> DOMString {
match contents {
Cow::Owned(s) => DOMString::from(s),
Cow::Borrowed(s) => DOMString::from(s),
}
}
}

impl From<DOMString> for Atom {
fn from(contents: DOMString) -> Atom {
Atom::from(contents.0)
@@ -1047,6 +1047,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,
@@ -1073,16 +1074,17 @@ impl Document {
}
.to_owned());

let props = KeyboardEvent::key_properties(key, modifiers);
let props = KeyboardEvent::key_properties(ch, key, modifiers);

let keyevent = KeyboardEvent::new(&self.window,
ev_type,
true,
true,
Some(&self.window),
0,
ch,
Some(key),
DOMString::from(props.key_string),
DOMString::from(props.key_string.clone()),
DOMString::from(props.code),
props.location,
is_repeating,
@@ -1091,7 +1093,7 @@ impl Document {
alt,
shift,
meta,
None,
props.char_code,
props.key_code);
let event = keyevent.upcast::<Event>();
event.fire(target);
@@ -1106,6 +1108,7 @@ impl Document {
true,
Some(&self.window),
0,
ch,
Some(key),
DOMString::from(props.key_string),
DOMString::from(props.code),
@@ -1117,15 +1120,15 @@ impl Document {
shift,
meta,
props.char_code,
0);
props.key_code);
let ev = event.upcast::<Event>();
ev.fire(target);
prevented = ev.DefaultPrevented();
// TODO: if keypress event is canceled, prevent firing input events
}

if !prevented {
constellation.send(ConstellationMsg::SendKeyEvent(key, state, modifiers)).unwrap();
constellation.send(ConstellationMsg::SendKeyEvent(key, state, modifiers, ch)).unwrap();
}

// This behavior is unspecced
@@ -17,7 +17,9 @@ use dom::uievent::UIEvent;
use dom::window::Window;
use msg::constellation_msg;
use msg::constellation_msg::{Key, KeyModifiers};
use std::borrow::Cow;
use std::cell::Cell;
use std::char;

no_jsmanaged_fields!(Key);

@@ -69,6 +71,7 @@ impl KeyboardEvent {
cancelable: bool,
view: Option<&Window>,
_detail: i32,
ch: Option<char>,
key: Option<Key>,
key_string: DOMString,
code: DOMString,
@@ -84,7 +87,7 @@ impl KeyboardEvent {
let ev = KeyboardEvent::new_uninitialized(window);
ev.InitKeyboardEvent(type_, canBubble, cancelable, view, key_string, location,
DOMString::new(), repeat, DOMString::new());
ev.key.set(key);
ev.key.set(logical_key(ch, key, location));
*ev.code.borrow_mut() = code;
ev.ctrl.set(ctrlKey);
ev.alt.set(altKey);
@@ -103,24 +106,30 @@ impl KeyboardEvent {
init.parent.parent.parent.bubbles,
init.parent.parent.parent.cancelable,
init.parent.parent.view.r(),
init.parent.parent.detail, key_from_string(&init.key, init.location),
init.parent.parent.detail,
None,
key_from_string(&init.key, init.location),
init.key.clone(), init.code.clone(), init.location,
init.repeat, init.isComposing, init.parent.ctrlKey,
init.parent.altKey, init.parent.shiftKey, init.parent.metaKey,
None, 0);
Ok(event)
}

pub fn key_properties(key: Key, mods: KeyModifiers)
pub fn key_properties(ch: Option<char>, key: Key, mods: KeyModifiers)
-> KeyEventProperties {
KeyEventProperties {
key_string: key_value(key, mods),
key_string: key_value(ch, key, mods),
code: code_value(key),
location: key_location(key),
char_code: key_charcode(key, mods),
char_code: ch.map(|ch| ch as u32),
key_code: key_keycode(key),
}
}

pub fn char(&self) -> Option<char> {
self.char_code.get().map(|code| char::from_u32(code).unwrap())
}
}


@@ -147,11 +156,26 @@ impl KeyboardEvent {
}
}

fn logical_key(ch: Option<char>, key: Option<Key>, location: u32) -> Option<Key> {
if let Some(ch) = ch {
let key = key_from_string(&format!("{}", ch), location);
if key.is_some() {
return key;
}
}

key
}


// https://w3c.github.io/uievents-key/#key-value-tables
pub fn key_value(key: Key, mods: KeyModifiers) -> &'static str {
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(constellation_msg::SHIFT);
match key {
Cow::from(match key {
Key::Space => " ",
Key::Apostrophe if shift => "\"",
Key::Apostrophe => "'",
@@ -321,7 +345,7 @@ pub fn key_value(key: Key, mods: KeyModifiers) -> &'static str {
Key::Menu => "ContextMenu",
Key::NavigateForward => "BrowserForward",
Key::NavigateBackward => "BrowserBack",
}
})
}

fn key_from_string(key_string: &str, location: u32) -> Option<Key> {
@@ -647,16 +671,6 @@ fn key_location(key: Key) -> u32 {
}
}

// https://w3c.github.io/uievents/#dom-keyboardevent-charcode
fn key_charcode(key: Key, mods: KeyModifiers) -> Option<u32> {
let key_string = key_value(key, mods);
if key_string.len() == 1 {
Some(key_string.chars().next().unwrap() as u32)
} else {
None
}
}

// https://w3c.github.io/uievents/#legacy-key-models
fn key_keycode(key: Key) -> u32 {
match key {
@@ -739,7 +753,7 @@ fn key_keycode(key: Key) -> u32 {

#[derive(HeapSizeOf)]
pub struct KeyEventProperties {
pub key_string: &'static str,
pub key_string: Cow<'static, str>,
pub code: &'static str,
pub location: u32,
pub char_code: Option<u32>,
@@ -1937,12 +1937,12 @@ impl ScriptThread {
document.r().handle_touchpad_pressure_event(self.js_runtime.rt(), point, pressure, phase);
}

KeyEvent(key, state, modifiers) => {
KeyEvent(key, state, modifiers, ch) => {
let document = match self.root_browsing_context().find(pipeline_id) {
Some(browsing_context) => browsing_context.active_document(),
None => return warn!("Message sent to closed pipeline {}.", pipeline_id),
};
document.dispatch_key_event(key, state, modifiers, &self.constellation_chan);
document.dispatch_key_event(ch, key, state, modifiers, &self.constellation_chan);
}
}
}
@@ -9,7 +9,7 @@ use dom::bindings::str::DOMString;
use dom::keyboardevent::{KeyboardEvent, key_value};
use msg::constellation_msg::{ALT, CONTROL, SHIFT, SUPER};
use msg::constellation_msg::{Key, KeyModifiers};
use std::borrow::ToOwned;
use std::borrow::{ToOwned, Borrow};
use std::cmp::{max, min};
use std::default::Default;
use std::ops::Range;
@@ -486,12 +486,17 @@ 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(key, event.get_key_modifiers())
self.handle_keydown_aux(event.char(), key, event.get_key_modifiers())
} else {
KeyReaction::Nothing
}
}
pub fn handle_keydown_aux(&mut self, key: Key, mods: KeyModifiers) -> KeyReaction {

pub fn handle_keydown_aux(&mut self, ch: Option<char>, key: Key, mods: KeyModifiers) -> KeyReaction {
if let Some(ch) = ch {
self.insert_char(ch);
return KeyReaction::DispatchInput;
}
let maybe_select = if mods.contains(SHIFT) { Selection::Selected } else { Selection::NotSelected };
match key {
Key::A if is_control_key(mods) => {
@@ -510,7 +515,7 @@ impl<T: ClipboardProvider> TextInput<T> {
KeyReaction::DispatchInput
},
_ if is_printable_key(key) => {
self.insert_string(key_value(key, mods));
self.insert_string::<&str>(key_value(None, key, mods).borrow());
KeyReaction::DispatchInput
}
Key::Space => {
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.