diff options
-rw-r--r-- | components/compositing/compositor.rs | 12 | ||||
-rw-r--r-- | components/compositing/compositor_thread.rs | 2 | ||||
-rw-r--r-- | components/compositing/windowing.rs | 4 | ||||
-rw-r--r-- | components/constellation/constellation.rs | 16 | ||||
-rw-r--r-- | components/script/dom/bindings/str.rs | 11 | ||||
-rw-r--r-- | components/script/dom/bindings/trace.rs | 2 | ||||
-rw-r--r-- | components/script/dom/document.rs | 9 | ||||
-rw-r--r-- | components/script/dom/keyboardevent.rs | 42 | ||||
-rw-r--r-- | components/script/script_thread.rs | 4 | ||||
-rw-r--r-- | components/script/textinput.rs | 68 | ||||
-rw-r--r-- | components/script_traits/lib.rs | 4 | ||||
-rw-r--r-- | components/script_traits/script_msg.rs | 2 | ||||
-rw-r--r-- | components/servo/Cargo.lock | 4 | ||||
-rw-r--r-- | ports/cef/Cargo.lock | 4 | ||||
-rw-r--r-- | ports/cef/browser_host.rs | 4 | ||||
-rw-r--r-- | ports/cef/window.rs | 2 | ||||
-rw-r--r-- | ports/geckolib/Cargo.lock | 4 | ||||
-rw-r--r-- | ports/glutin/window.rs | 159 | ||||
-rw-r--r-- | tests/unit/script/textinput.rs | 2 |
19 files changed, 233 insertions, 122 deletions
diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 270a6b656d0..8041fd7f1df 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -701,9 +701,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); } } @@ -1348,8 +1348,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 => { @@ -1880,8 +1880,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); } diff --git a/components/compositing/compositor_thread.rs b/components/compositing/compositor_thread.rs index 24b9caba0a7..49d0692e6c8 100644 --- a/components/compositing/compositor_thread.rs +++ b/components/compositing/compositor_thread.rs @@ -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. diff --git a/components/compositing/windowing.rs b/components/compositing/windowing.rs index b67803b07c6..2da658a9661 100644 --- a/components/compositing/windowing.rs +++ b/components/compositing/windowing.rs @@ -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; diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index 7c3a95dbc54..0920a4a523f 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -574,9 +574,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; @@ -803,8 +803,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(ch, key, key_state, key_modifiers) => { + self.compositor_proxy.send(ToCompositorMsg::KeyEvent(ch, key, key_state, key_modifiers)) } FromScriptMsg::TouchEventProcessed(result) => { @@ -1396,7 +1396,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. @@ -1407,7 +1407,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(ch, key, state, mods); let msg = ConstellationControlMsg::SendEvent(pipeline_id, event); let result = match self.pipelines.get(&pipeline_id) { Some(pipeline) => pipeline.script_chan.send(msg), @@ -1418,7 +1418,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); } } @@ -1629,7 +1629,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(None, key, state, mods); 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); diff --git a/components/script/dom/bindings/str.rs b/components/script/dom/bindings/str.rs index 69565520899..310285a8bcd 100644 --- a/components/script/dom/bindings/str.rs +++ b/components/script/dom/bindings/str.rs @@ -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) diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 7404e96b95d..e2c6e540a3d 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -276,7 +276,7 @@ impl<A: JSTraceable, B: JSTraceable, C: JSTraceable> JSTraceable for (A, B, C) { } } -no_jsmanaged_fields!(bool, f32, f64, String, Url, AtomicBool, AtomicUsize, UrlOrigin, Uuid); +no_jsmanaged_fields!(bool, f32, f64, String, Url, AtomicBool, AtomicUsize, UrlOrigin, Uuid, char); no_jsmanaged_fields!(usize, u8, u16, u32, u64); no_jsmanaged_fields!(isize, i8, i16, i32, i64); no_jsmanaged_fields!(Sender<T>); diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 2160a3a4556..43a4473689e 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -1044,6 +1044,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, @@ -1070,7 +1071,7 @@ 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, @@ -1078,8 +1079,9 @@ impl Document { 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, @@ -1103,6 +1105,7 @@ impl Document { true, Some(&self.window), 0, + ch, Some(key), DOMString::from(props.key_string), DOMString::from(props.code), @@ -1122,7 +1125,7 @@ impl Document { } if !prevented { - constellation.send(ConstellationMsg::SendKeyEvent(key, state, modifiers)).unwrap(); + constellation.send(ConstellationMsg::SendKeyEvent(ch, key, state, modifiers)).unwrap(); } // This behavior is unspecced diff --git a/components/script/dom/keyboardevent.rs b/components/script/dom/keyboardevent.rs index 4cfa1a61011..71b00767a8e 100644 --- a/components/script/dom/keyboardevent.rs +++ b/components/script/dom/keyboardevent.rs @@ -17,6 +17,7 @@ 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; no_jsmanaged_fields!(Key); @@ -36,6 +37,7 @@ pub struct KeyboardEvent { is_composing: Cell<bool>, char_code: Cell<Option<u32>>, key_code: Cell<u32>, + printable: Cell<Option<char>>, } impl KeyboardEvent { @@ -54,6 +56,7 @@ impl KeyboardEvent { is_composing: Cell::new(false), char_code: Cell::new(None), key_code: Cell::new(0), + printable: Cell::new(None), } } @@ -69,6 +72,7 @@ impl KeyboardEvent { cancelable: bool, view: Option<&Window>, _detail: i32, + ch: Option<char>, key: Option<Key>, key_string: DOMString, code: DOMString, @@ -91,6 +95,7 @@ impl KeyboardEvent { ev.shift.set(shiftKey); ev.meta.set(metaKey); ev.char_code.set(char_code); + ev.printable.set(ch); ev.key_code.set(key_code); ev.is_composing.set(isComposing); ev @@ -103,7 +108,9 @@ 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, @@ -111,13 +118,13 @@ impl KeyboardEvent { 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), } } @@ -125,6 +132,10 @@ impl KeyboardEvent { impl KeyboardEvent { + pub fn printable(&self) -> Option<char> { + self.printable.get() + } + pub fn get_key(&self) -> Option<Key> { self.key.get().clone() } @@ -147,11 +158,14 @@ impl KeyboardEvent { } } - // 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 +335,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 +661,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 +743,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>, diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 2367ecc0fd9..05689baac39 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -1937,12 +1937,12 @@ impl ScriptThread { document.r().handle_touchpad_pressure_event(self.js_runtime.rt(), point, pressure, phase); } - KeyEvent(key, state, modifiers) => { + KeyEvent(ch, key, state, modifiers) => { 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); } } } diff --git a/components/script/textinput.rs b/components/script/textinput.rs index a56bd983f34..871af9637c0 100644 --- a/components/script/textinput.rs +++ b/components/script/textinput.rs @@ -6,7 +6,7 @@ use clipboard_provider::ClipboardProvider; use dom::bindings::str::DOMString; -use dom::keyboardevent::{KeyboardEvent, key_value}; +use dom::keyboardevent::KeyboardEvent; use msg::constellation_msg::{ALT, CONTROL, SHIFT, SUPER}; use msg::constellation_msg::{Key, KeyModifiers}; use std::borrow::ToOwned; @@ -120,24 +120,6 @@ fn is_control_key(mods: KeyModifiers) -> bool { mods.contains(CONTROL) && !mods.contains(SUPER | ALT) } -fn is_printable_key(key: Key) -> bool { - match key { - Key::Space | Key::Apostrophe | Key::Comma | Key::Minus | - Key::Period | Key::Slash | Key::GraveAccent | Key::Num0 | - Key::Num1 | Key::Num2 | Key::Num3 | Key::Num4 | Key::Num5 | - Key::Num6 | Key::Num7 | Key::Num8 | Key::Num9 | Key::Semicolon | - Key::Equal | 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::LeftBracket | Key::Backslash | - Key::RightBracket | 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::KpEqual => true, - _ => false, - } -} - /// The length in bytes of the first n characters in a UTF-8 string. /// /// If the string has fewer than n characters, returns the length of the whole string. @@ -486,80 +468,80 @@ 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.printable(), 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, + printable: Option<char>, + key: Key, + mods: KeyModifiers) -> KeyReaction { let maybe_select = if mods.contains(SHIFT) { Selection::Selected } else { Selection::NotSelected }; - match key { - Key::A if is_control_key(mods) => { + match (printable, key) { + (Some('a'), _) if is_control_key(mods) => { self.select_all(); KeyReaction::RedrawSelection }, - Key::C if is_control_key(mods) => { + (Some('c'), _) if is_control_key(mods) => { if let Some(text) = self.get_selection_text() { self.clipboard_provider.set_clipboard_contents(text); } KeyReaction::DispatchInput }, - Key::V if is_control_key(mods) => { + (Some('v'), _) if is_control_key(mods) => { let contents = self.clipboard_provider.clipboard_contents(); self.insert_string(contents); KeyReaction::DispatchInput }, - _ if is_printable_key(key) => { - self.insert_string(key_value(key, mods)); - KeyReaction::DispatchInput - } - Key::Space => { - self.insert_char(' '); + (Some(c), _) => { + self.insert_char(c); KeyReaction::DispatchInput } - Key::Delete => { + (None, Key::Delete) => { self.delete_char(Direction::Forward); KeyReaction::DispatchInput } - Key::Backspace => { + (None, Key::Backspace) => { self.delete_char(Direction::Backward); KeyReaction::DispatchInput } - Key::Left => { + (None, Key::Left) => { self.adjust_horizontal_by_one(Direction::Backward, maybe_select); KeyReaction::RedrawSelection } - Key::Right => { + (None, Key::Right) => { self.adjust_horizontal_by_one(Direction::Forward, maybe_select); KeyReaction::RedrawSelection } - Key::Up => { + (None, Key::Up) => { self.adjust_vertical(-1, maybe_select); KeyReaction::RedrawSelection } - Key::Down => { + (None, Key::Down) => { self.adjust_vertical(1, maybe_select); KeyReaction::RedrawSelection } - Key::Enter | Key::KpEnter => self.handle_return(), - Key::Home => { + (None, Key::Enter) | (None, Key::KpEnter) => self.handle_return(), + (None, Key::Home) => { self.edit_point.index = 0; KeyReaction::RedrawSelection } - Key::End => { + (None, Key::End) => { self.edit_point.index = self.current_line_length(); self.assert_ok_selection(); KeyReaction::RedrawSelection } - Key::PageUp => { + (None, Key::PageUp) => { self.adjust_vertical(-28, maybe_select); KeyReaction::RedrawSelection } - Key::PageDown => { + (None, Key::PageDown) => { self.adjust_vertical(28, maybe_select); KeyReaction::RedrawSelection } - Key::Tab => KeyReaction::TriggerDefaultAction, + (None, Key::Tab) => KeyReaction::TriggerDefaultAction, _ => KeyReaction::Nothing, } } diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index bd82a498d46..5c6064a3d3e 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -282,7 +282,7 @@ pub enum CompositorEvent { /// Touchpad pressure event TouchpadPressureEvent(Point2D<f32>, f32, TouchpadPressurePhase), /// A key was pressed. - KeyEvent(Key, KeyState, KeyModifiers), + KeyEvent(Option<char>, Key, KeyState, KeyModifiers), } /// Touchpad pressure phase for TouchpadPressureEvent. @@ -586,7 +586,7 @@ pub enum ConstellationMsg { /// Query the constellation to see if the current compositor output is stable IsReadyToSaveImage(HashMap<PipelineId, Epoch>), /// Inform the constellation of a key event. - KeyEvent(Key, KeyState, KeyModifiers), + KeyEvent(Option<char>, Key, KeyState, KeyModifiers), /// Request to load a page. LoadUrl(PipelineId, LoadData), /// Request to navigate a frame. diff --git a/components/script_traits/script_msg.rs b/components/script_traits/script_msg.rs index 7fc871eb4cc..0ac5aeae4b4 100644 --- a/components/script_traits/script_msg.rs +++ b/components/script_traits/script_msg.rs @@ -103,7 +103,7 @@ pub enum ScriptMsg { /// https://html.spec.whatwg.org/multipage/#document.title SetTitle(PipelineId, Option<String>), /// Send a key event - SendKeyEvent(Key, KeyState, KeyModifiers), + SendKeyEvent(Option<char>, Key, KeyState, KeyModifiers), /// Get Window Informations size and position GetClientWindow(IpcSender<(Size2D<u32>, Point2D<i32>)>), /// Move the window to a point diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index 997fc8ab250..030f19a5038 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -146,7 +146,7 @@ dependencies = [ [[package]] name = "bincode" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1061,7 +1061,7 @@ name = "ipc-channel" version = "0.2.3" source = "git+https://github.com/servo/ipc-channel#48137d69955f5460da586c552de275ecdc3f4efe" dependencies = [ - "bincode 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", + "bincode 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ports/cef/Cargo.lock b/ports/cef/Cargo.lock index 4907cc20629..7071b45ddcf 100644 --- a/ports/cef/Cargo.lock +++ b/ports/cef/Cargo.lock @@ -120,7 +120,7 @@ dependencies = [ [[package]] name = "bincode" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -970,7 +970,7 @@ name = "ipc-channel" version = "0.2.3" source = "git+https://github.com/servo/ipc-channel#48137d69955f5460da586c552de275ecdc3f4efe" dependencies = [ - "bincode 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", + "bincode 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ports/cef/browser_host.rs b/ports/cef/browser_host.rs index bbf776ee839..fce7c6c5735 100644 --- a/ports/cef/browser_host.rs +++ b/ports/cef/browser_host.rs @@ -17,6 +17,7 @@ use libc::{c_double, c_int}; use msg::constellation_msg::{self, KeyModifiers, KeyState}; use script_traits::{MouseButton, TouchEventType}; use std::cell::{Cell, RefCell}; +use std::char; pub struct ServoCefBrowserHost { /// A reference to the browser. @@ -424,7 +425,8 @@ full_cef_class_impl! { if (*event).modifiers & EVENTFLAG_ALT_DOWN as u32 != 0 { key_modifiers = key_modifiers | constellation_msg::ALT; } - this.downcast().send_window_event(WindowEvent::KeyEvent(key, key_state, key_modifiers)) + let ch = char::from_u32((*event).character as u32); + this.downcast().send_window_event(WindowEvent::KeyEvent(ch, key, key_state, key_modifiers)) }} fn send_mouse_click_event(&this, diff --git a/ports/cef/window.rs b/ports/cef/window.rs index 1d4ee0a3281..e5c2cd5f4db 100644 --- a/ports/cef/window.rs +++ b/ports/cef/window.rs @@ -479,7 +479,7 @@ impl WindowMethods for Window { } } - fn handle_key(&self, _: Key, _: KeyModifiers) { + fn handle_key(&self, _: Option<char>, _: Key, _: KeyModifiers) { // TODO(negge) } diff --git a/ports/geckolib/Cargo.lock b/ports/geckolib/Cargo.lock index 27b841fb790..38213b2f352 100644 --- a/ports/geckolib/Cargo.lock +++ b/ports/geckolib/Cargo.lock @@ -72,7 +72,7 @@ dependencies = [ [[package]] name = "bincode" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -254,7 +254,7 @@ name = "ipc-channel" version = "0.2.4" source = "git+https://github.com/servo/ipc-channel#8411eeabf3a712006ad1b47637b2d8fe71177f85" dependencies = [ - "bincode 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", + "bincode 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ports/glutin/window.rs b/ports/glutin/window.rs index a6efae6aab4..00bd7ceff0a 100644 --- a/ports/glutin/window.rs +++ b/ports/glutin/window.rs @@ -14,10 +14,10 @@ use euclid::{Size2D, Point2D}; #[cfg(target_os = "windows")] use gdi32; use gleam::gl; use glutin; -use glutin::TouchPhase; #[cfg(target_os = "macos")] use glutin::os::macos::{ActivationPolicy, WindowBuilderExt}; use glutin::{Api, ElementState, Event, GlRequest, MouseButton, VirtualKeyCode, MouseScrollDelta}; +use glutin::{ScanCode, TouchPhase}; use layers::geometry::DevicePixel; use layers::platform::surface::NativeDisplay; use msg::constellation_msg::{KeyState, NONE, CONTROL, SHIFT, ALT, SUPER}; @@ -99,6 +99,12 @@ pub struct Window { mouse_pos: Cell<Point2D<i32>>, key_modifiers: Cell<KeyModifiers>, current_url: RefCell<Option<Url>>, + + /// The contents of the last ReceivedCharacter event for use in a subsequent KeyEvent. + pending_key_event_char: Cell<Option<char>>, + /// The list of keys that have been pressed but not yet released, to allow providing + /// the equivalent ReceivedCharacter data as was received for the press event. + pressed_key_map: RefCell<Vec<(ScanCode, char)>>, } #[cfg(not(target_os = "windows"))] @@ -175,6 +181,9 @@ impl Window { mouse_pos: Cell::new(Point2D::new(0, 0)), key_modifiers: Cell::new(KeyModifiers::empty()), current_url: RefCell::new(None), + + pending_key_event_char: Cell::new(None), + pressed_key_map: RefCell::new(vec![]), }; gl::clear_color(0.6, 0.6, 0.6, 1.0); @@ -232,7 +241,13 @@ impl Window { fn handle_window_event(&self, event: glutin::Event) -> bool { match event { - Event::KeyboardInput(element_state, _scan_code, Some(virtual_key_code)) => { + Event::ReceivedCharacter(ch) => { + assert!(self.pending_key_event_char.get().is_none()); + if !ch.is_control() { + self.pending_key_event_char.set(Some(ch)); + } + } + Event::KeyboardInput(element_state, scan_code, Some(virtual_key_code)) => { match virtual_key_code { VirtualKeyCode::LControl => self.toggle_modifier(LEFT_CONTROL), VirtualKeyCode::RControl => self.toggle_modifier(RIGHT_CONTROL), @@ -245,13 +260,39 @@ impl Window { _ => {} } + let ch = match element_state { + ElementState::Pressed => { + // Retrieve any previosly stored ReceivedCharacter value. + // Store the association between the scan code and the actual + // character value, if there is one. + let ch = self.pending_key_event_char + .get() + .and_then(|ch| filter_nonprintable(ch, virtual_key_code)); + self.pending_key_event_char.set(None); + if let Some(ch) = ch { + self.pressed_key_map.borrow_mut().push((scan_code, ch)); + } + ch + } + + ElementState::Released => { + // Retrieve the associated character value for this release key, + // if one was previously stored. + let idx = self.pressed_key_map + .borrow() + .iter() + .position(|&(code, _)| code == scan_code); + idx.map(|idx| self.pressed_key_map.borrow_mut().swap_remove(idx).1) + } + }; + if let Ok(key) = Window::glutin_key_to_script_key(virtual_key_code) { let state = match element_state { ElementState::Pressed => KeyState::Pressed, ElementState::Released => KeyState::Released, }; let modifiers = Window::glutin_mods_to_script_mods(self.key_modifiers.get()); - self.event_queue.borrow_mut().push(WindowEvent::KeyEvent(key, state, modifiers)); + self.event_queue.borrow_mut().push(WindowEvent::KeyEvent(ch, key, state, modifiers)); } } Event::KeyboardInput(_, _, None) => { @@ -803,48 +844,47 @@ impl WindowMethods for Window { } /// Helper function to handle keyboard events. - fn handle_key(&self, key: Key, mods: constellation_msg::KeyModifiers) { - match (mods, key) { - (_, Key::Equal) => { + fn handle_key(&self, ch: Option<char>, key: Key, mods: constellation_msg::KeyModifiers) { + match (mods, ch, key) { + (_, Some('+'), _) => { if mods & !SHIFT == CMD_OR_CONTROL { self.event_queue.borrow_mut().push(WindowEvent::Zoom(1.1)); } else if mods & !SHIFT == CMD_OR_CONTROL | ALT { self.event_queue.borrow_mut().push(WindowEvent::PinchZoom(1.1)); } } - (CMD_OR_CONTROL, Key::Minus) => { + (CMD_OR_CONTROL, Some('-'), _) => { self.event_queue.borrow_mut().push(WindowEvent::Zoom(1.0 / 1.1)); } - (_, Key::Minus) if mods == CMD_OR_CONTROL | ALT => { + (_, Some('-'), _) if mods == CMD_OR_CONTROL | ALT => { self.event_queue.borrow_mut().push(WindowEvent::PinchZoom(1.0 / 1.1)); } - (CMD_OR_CONTROL, Key::Num0) | - (CMD_OR_CONTROL, Key::Kp0) => { + (CMD_OR_CONTROL, Some('0'), _) => { self.event_queue.borrow_mut().push(WindowEvent::ResetZoom); } - (NONE, Key::NavigateForward) => { + (NONE, None, Key::NavigateForward) => { self.event_queue.borrow_mut().push(WindowEvent::Navigation(WindowNavigateMsg::Forward)); } - (NONE, Key::NavigateBackward) => { + (NONE, None, Key::NavigateBackward) => { self.event_queue.borrow_mut().push(WindowEvent::Navigation(WindowNavigateMsg::Back)); } - (NONE, Key::Escape) => { + (NONE, None, Key::Escape) => { if let Some(true) = PREFS.get("shell.builtin-key-shortcuts.enabled").as_boolean() { self.event_queue.borrow_mut().push(WindowEvent::Quit); } } - (CMD_OR_ALT, Key::Right) => { + (CMD_OR_ALT, None, Key::Right) => { self.event_queue.borrow_mut().push(WindowEvent::Navigation(WindowNavigateMsg::Forward)); } - (CMD_OR_ALT, Key::Left) => { + (CMD_OR_ALT, None, Key::Left) => { self.event_queue.borrow_mut().push(WindowEvent::Navigation(WindowNavigateMsg::Back)); } - (NONE, Key::PageDown) | - (NONE, Key::Space) => { + (NONE, None, Key::PageDown) | + (NONE, Some(' '), _) => { self.scroll_window(0.0, -self.framebuffer_size() .as_f32() @@ -852,8 +892,8 @@ impl WindowMethods for Window { .height + 2.0 * LINE_HEIGHT, TouchEventType::Move); } - (NONE, Key::PageUp) | - (SHIFT, Key::Space) => { + (NONE, None, Key::PageUp) | + (SHIFT, Some(' '), _) => { self.scroll_window(0.0, self.framebuffer_size() .as_f32() @@ -861,19 +901,19 @@ impl WindowMethods for Window { .height - 2.0 * LINE_HEIGHT, TouchEventType::Move); } - (NONE, Key::Up) => { + (NONE, None, Key::Up) => { self.scroll_window(0.0, 3.0 * LINE_HEIGHT, TouchEventType::Move); } - (NONE, Key::Down) => { + (NONE, None, Key::Down) => { self.scroll_window(0.0, -3.0 * LINE_HEIGHT, TouchEventType::Move); } - (NONE, Key::Left) => { + (NONE, None, Key::Left) => { self.scroll_window(LINE_HEIGHT, 0.0, TouchEventType::Move); } - (NONE, Key::Right) => { + (NONE, None, Key::Right) => { self.scroll_window(-LINE_HEIGHT, 0.0, TouchEventType::Move); } - (CMD_OR_CONTROL, Key::R) => { + (CMD_OR_CONTROL, Some('r'), _) => { if let Some(true) = PREFS.get("shell.builtin-key-shortcuts.enabled").as_boolean() { self.event_queue.borrow_mut().push(WindowEvent::Reload); } @@ -932,6 +972,77 @@ fn glutin_pressure_stage_to_touchpad_pressure_phase(stage: i64) -> TouchpadPress } } +fn filter_nonprintable(ch: char, key_code: VirtualKeyCode) -> Option<char> { + use glutin::VirtualKeyCode::*; + match key_code { + Escape | + F1 | + F2 | + F3 | + F4 | + F5 | + F6 | + F7 | + F8 | + F9 | + F10 | + F11 | + F12 | + F13 | + F14 | + F15 | + Snapshot | + Scroll | + Pause | + Insert | + Home | + Delete | + End | + PageDown | + PageUp | + Left | + Up | + Right | + Down | + Back | + LAlt | + LControl | + LMenu | + LShift | + LWin | + Mail | + MediaSelect | + MediaStop | + Mute | + MyComputer | + NavigateForward | + NavigateBackward | + NextTrack | + NoConvert | + PlayPause | + Power | + PrevTrack | + RAlt | + RControl | + RMenu | + RShift | + RWin | + Sleep | + Stop | + VolumeDown | + VolumeUp | + Wake | + WebBack | + WebFavorites | + WebForward | + WebHome | + WebRefresh | + WebSearch | + WebStop => None, + _ => Some(ch), + } +} + // These functions aren't actually called. They are here as a link // hack because Skia references them. diff --git a/tests/unit/script/textinput.rs b/tests/unit/script/textinput.rs index 4ee2c897bef..d5e9c1268d6 100644 --- a/tests/unit/script/textinput.rs +++ b/tests/unit/script/textinput.rs @@ -401,7 +401,7 @@ fn test_clipboard_paste() { SelectionDirection::None); assert_eq!(textinput.get_content(), "defg"); assert_eq!(textinput.edit_point.index, 0); - textinput.handle_keydown_aux(Key::V, MODIFIERS); + textinput.handle_keydown_aux(Some('v'), Key::V, MODIFIERS); assert_eq!(textinput.get_content(), "abcdefg"); } |