From 329ba56fca3bd3808a37aae6bc3ed3c5a5d23524 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Sat, 4 Oct 2014 09:46:50 -0400 Subject: [PATCH] Dispatch keydown, keyup, and keypress events at appropriate times. --- components/compositing/compositor.rs | 10 ++--- components/compositing/constellation.rs | 10 ++--- components/compositing/windowing.rs | 4 +- components/msg/constellation_msg.rs | 12 +++++- components/script/dom/keyboardevent.rs | 27 ++++++++++++ components/script/script_task.rs | 56 +++++++++++++++++++++++-- components/script_traits/lib.rs | 4 +- ports/glfw/window.rs | 20 ++++++++- 8 files changed, 124 insertions(+), 19 deletions(-) diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 738d5abab0a..e20ea06f0e8 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -43,7 +43,7 @@ use servo_msg::compositor_msg::{Blank, Epoch, FinishedLoading, IdleRenderState, use servo_msg::compositor_msg::{ReadyState, RenderingRenderState, RenderState, Scrollable}; use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, LoadUrlMsg}; use servo_msg::constellation_msg::{NavigateMsg, LoadData, PipelineId, ResizedWindowMsg}; -use servo_msg::constellation_msg::{WindowSizeData, KeyState, Key}; +use servo_msg::constellation_msg::{WindowSizeData, KeyState, Key, KeyModifiers}; use servo_msg::constellation_msg; use servo_util::geometry::{PagePx, ScreenPx, ViewportPx}; use servo_util::memory::MemoryProfilerChan; @@ -707,8 +707,8 @@ impl IOCompositor { self.on_navigation_window_event(direction); } - KeyEvent(key, state) => { - self.on_key_event(key, state); + KeyEvent(key, state, modifiers) => { + self.on_key_event(key, state, modifiers); } FinishedWindowEvent => { @@ -882,9 +882,9 @@ impl IOCompositor { chan.send(NavigateMsg(direction)) } - fn on_key_event(&self, key: Key, state: KeyState) { + fn on_key_event(&self, key: Key, state: KeyState, modifiers: KeyModifiers) { let ConstellationChan(ref chan) = self.constellation_chan; - chan.send(constellation_msg::KeyEvent(key, state)) + chan.send(constellation_msg::KeyEvent(key, state, modifiers)) } fn convert_buffer_requests_to_pipeline_requests_map(&self, diff --git a/components/compositing/constellation.rs b/components/compositing/constellation.rs index 096bc76870e..d8831c02c90 100644 --- a/components/compositing/constellation.rs +++ b/components/compositing/constellation.rs @@ -22,7 +22,7 @@ use servo_msg::constellation_msg::{IFrameSandboxState, IFrameUnsandboxed, InitLo use servo_msg::constellation_msg::{LoadCompleteMsg, LoadUrlMsg, LoadData, Msg, NavigateMsg}; use servo_msg::constellation_msg::{NavigationType, PipelineId, RendererReadyMsg, ResizedWindowMsg}; use servo_msg::constellation_msg::{ScriptLoadedURLInIFrameMsg, SubpageId, WindowSizeData}; -use servo_msg::constellation_msg::{KeyEvent, Key, KeyState}; +use servo_msg::constellation_msg::{KeyEvent, Key, KeyState, KeyModifiers}; use servo_msg::constellation_msg; use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient}; use servo_net::resource_task::ResourceTask; @@ -452,9 +452,9 @@ impl Constellation { debug!("constellation got window resize message"); self.handle_resized_window_msg(new_size); } - KeyEvent(key, state) => { + KeyEvent(key, state, modifiers) => { debug!("constellation got key event message"); - self.handle_key_msg(key, state); + self.handle_key_msg(key, state, modifiers); } } true @@ -767,10 +767,10 @@ impl Constellation { .any(|current_frame| current_frame.contains(pipeline_id)) } - fn handle_key_msg(&self, key: Key, state: KeyState) { + fn handle_key_msg(&self, key: Key, state: KeyState, mods: KeyModifiers) { self.current_frame().as_ref().map(|frame| { let ScriptControlChan(ref chan) = frame.pipeline.script_chan; - chan.send(SendEventMsg(frame.pipeline.id, script_traits::KeyEvent(key, state))); + chan.send(SendEventMsg(frame.pipeline.id, script_traits::KeyEvent(key, state, mods))); }); } diff --git a/components/compositing/windowing.rs b/components/compositing/windowing.rs index c564c34b29a..e9b95f551b7 100644 --- a/components/compositing/windowing.rs +++ b/components/compositing/windowing.rs @@ -11,7 +11,7 @@ use geom::scale_factor::ScaleFactor; use geom::size::TypedSize2D; use layers::geometry::DevicePixel; use layers::platform::surface::NativeGraphicsMetadata; -use servo_msg::constellation_msg::{Key, KeyState}; +use servo_msg::constellation_msg::{Key, KeyState, KeyModifiers}; use servo_msg::compositor_msg::{ReadyState, RenderState}; use servo_util::geometry::ScreenPx; use std::fmt::{FormatError, Formatter, Show}; @@ -60,7 +60,7 @@ pub enum WindowEvent { /// Sent when the user quits the application QuitWindowEvent, /// Sent when a key input state changes - KeyEvent(Key, KeyState), + KeyEvent(Key, KeyState, KeyModifiers), } impl Show for WindowEvent { diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs index 40280a89afc..550bafd4096 100644 --- a/components/msg/constellation_msg.rs +++ b/components/msg/constellation_msg.rs @@ -50,6 +50,7 @@ pub struct WindowSizeData { pub device_pixel_ratio: ScaleFactor, } +#[deriving(PartialEq)] pub enum KeyState { Pressed, Released, @@ -181,6 +182,15 @@ pub enum Key { KeyMenu, } +bitflags! { + flags KeyModifiers: u8 { + const Shift = 0x01, + const Control = 0x02, + const Alt = 0x04, + const Super = 0x08, + } +} + /// Messages from the compositor and script to the constellation. pub enum Msg { ExitMsg, @@ -193,7 +203,7 @@ pub enum Msg { NavigateMsg(NavigationDirection), RendererReadyMsg(PipelineId), ResizedWindowMsg(WindowSizeData), - KeyEvent(Key, KeyState), + KeyEvent(Key, KeyState, KeyModifiers), } /// Similar to net::resource_task::LoadData diff --git a/components/script/dom/keyboardevent.rs b/components/script/dom/keyboardevent.rs index be96a181b30..2c8f5e49a10 100644 --- a/components/script/dom/keyboardevent.rs +++ b/components/script/dom/keyboardevent.rs @@ -14,6 +14,7 @@ use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object}; use dom::event::{Event, KeyboardEventTypeId}; use dom::uievent::UIEvent; use dom::window::Window; +use servo_msg::constellation_msg; use servo_util::str::DOMString; use std::cell::{RefCell, Cell}; @@ -109,6 +110,32 @@ impl KeyboardEvent { None, 0); Ok(event) } + + pub fn key_properties(key: constellation_msg::Key) -> KeyEventProperties { + match key { + _ => KeyEventProperties { + key: "".to_string(), + code: "".to_string(), + location: 0, + char_code: None, + key_code: 0, + } + } + } +} + +pub struct KeyEventProperties { + pub key: DOMString, + pub code: DOMString, + pub location: u32, + pub char_code: Option, + pub key_code: u32, +} + +impl KeyEventProperties { + pub fn is_printable(&self) -> bool { + self.char_code.is_some() + } } impl<'a> KeyboardEventMethods for JSRef<'a, KeyboardEvent> { diff --git a/components/script/script_task.rs b/components/script/script_task.rs index 31f3abb289c..ff499fb474b 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -10,6 +10,7 @@ use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, Documen use dom::bindings::codegen::Bindings::DOMRectBinding::DOMRectMethods; use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods; use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods; +use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::codegen::InheritTypes::{EventTargetCast, NodeCast, EventCast, ElementCast}; use dom::bindings::conversions; use dom::bindings::conversions::{FromJSValConvertible, Empty}; @@ -23,6 +24,7 @@ use dom::element::{HTMLSelectElementTypeId, HTMLTextAreaElementTypeId, HTMLOptio use dom::event::{Event, Bubbles, DoesNotBubble, Cancelable, NotCancelable}; use dom::uievent::UIEvent; use dom::eventtarget::{EventTarget, EventTargetHelpers}; +use dom::keyboardevent::KeyboardEvent; use dom::node; use dom::node::{ElementNodeTypeId, Node, NodeHelpers}; use dom::window::{Window, WindowHelpers}; @@ -46,7 +48,9 @@ use script_traits::{ScriptControlChan, ReflowCompleteMsg, UntrustedNodeAddress, use servo_msg::compositor_msg::{FinishedLoading, LayerId, Loading}; use servo_msg::compositor_msg::{ScriptListener}; use servo_msg::constellation_msg::{ConstellationChan, LoadCompleteMsg, LoadUrlMsg, NavigationDirection}; -use servo_msg::constellation_msg::{LoadData, PipelineId, Failure, FailureMsg, WindowSizeData}; +use servo_msg::constellation_msg::{LoadData, PipelineId, Failure, FailureMsg, WindowSizeData, Key, KeyState}; +use servo_msg::constellation_msg::{KeyModifiers, Super, Shift, Control, Alt, Repeated, Pressed}; +use servo_msg::constellation_msg::{Released}; use servo_msg::constellation_msg; use servo_net::image_cache_task::ImageCacheTask; use servo_net::resource_task::ResourceTask; @@ -908,12 +912,58 @@ impl ScriptTask { self.handle_mouse_move_event(pipeline_id, point); } - KeyEvent(key, state) => { - println!("key {} is {}", key as int, state as int); + KeyEvent(key, state, modifiers) => { + self.dispatch_key_event(key, state, modifiers, pipeline_id); } } } + /// The entry point for all key processing for web content + fn dispatch_key_event(&self, key: Key, + state: KeyState, + modifiers: KeyModifiers, + pipeline_id: PipelineId) { + println!("key {} is {}", key as int, state as int); + let page = get_page(&*self.page.borrow(), pipeline_id); + let frame = page.frame(); + let window = frame.as_ref().unwrap().window.root(); + let doc = window.Document().root(); + let body = doc.GetBody().root(); + + let target: JSRef = match body { + Some(body) => EventTargetCast::from_ref(*body), + None => EventTargetCast::from_ref(*window), + }; + + let ctrl = modifiers.contains(Control); + let alt = modifiers.contains(Alt); + let shift = modifiers.contains(Shift); + let meta = modifiers.contains(Super); + + let is_composing = false; + let is_repeating = state == Repeated; + let ev_type = match state { + Pressed | Repeated => "keydown", + Released => "keyup", + }.to_string(); + + let props = KeyboardEvent::key_properties(key); + + let event = KeyboardEvent::new(*window, ev_type, true, true, Some(*window), 0, + props.key.clone(), props.code.clone(), props.location, + is_repeating, is_composing, ctrl, alt, shift, meta, + props.char_code, props.key_code).root(); + let _ = target.DispatchEvent(EventCast::from_ref(*event)); + + if state != Released && props.is_printable() { + let event = KeyboardEvent::new(*window, "keypress".to_string(), true, true, Some(*window), + 0, props.key.clone(), props.code.clone(), props.location, + is_repeating, is_composing, ctrl, alt, shift, meta, + props.char_code, props.key_code).root(); + let _ = target.DispatchEvent(EventCast::from_ref(*event)); + } + } + /// The entry point for content to notify that a new load has been requested /// for the given pipeline. fn trigger_load(&self, pipeline_id: PipelineId, load_data: LoadData) { diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index e0e300307fe..10bc1964e01 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -25,7 +25,7 @@ extern crate serialize; use devtools_traits::DevtoolsControlChan; use libc::c_void; use servo_msg::constellation_msg::{ConstellationChan, PipelineId, Failure, WindowSizeData}; -use servo_msg::constellation_msg::{LoadData, SubpageId, Key, KeyState}; +use servo_msg::constellation_msg::{LoadData, SubpageId, Key, KeyState, KeyModifiers}; use servo_msg::compositor_msg::ScriptListener; use servo_net::image_cache_task::ImageCacheTask; use servo_net::resource_task::ResourceTask; @@ -75,7 +75,7 @@ pub enum CompositorEvent { MouseDownEvent(uint, Point2D), MouseUpEvent(uint, Point2D), MouseMoveEvent(Point2D), - KeyEvent(Key, KeyState), + KeyEvent(Key, KeyState, KeyModifiers), } /// An opaque wrapper around script<->layout channels to avoid leaking message types into diff --git a/ports/glfw/window.rs b/ports/glfw/window.rs index bc75c083853..2e943197370 100644 --- a/ports/glfw/window.rs +++ b/ports/glfw/window.rs @@ -216,7 +216,8 @@ impl Window { glfw::Release => constellation_msg::Released, glfw::Repeat => constellation_msg::Repeated, }; - self.event_queue.borrow_mut().push(KeyEvent(key, state)); + let modifiers = glfw_mods_to_script_mods(mods); + self.event_queue.borrow_mut().push(KeyEvent(key, state, modifiers)); }, glfw::FramebufferSizeEvent(width, height) => { self.event_queue.borrow_mut().push( @@ -436,6 +437,23 @@ extern "C" fn on_framebuffer_size(_glfw_window: *mut glfw::ffi::GLFWwindow, } } +fn glfw_mods_to_script_mods(mods: glfw::Modifiers) -> constellation_msg::KeyModifiers { + let mut result = constellation_msg::KeyModifiers::from_bits(0).unwrap(); + if mods.contains(glfw::Shift) { + result.insert(constellation_msg::Shift); + } + if mods.contains(glfw::Alt) { + result.insert(constellation_msg::Alt); + } + if mods.contains(glfw::Control) { + result.insert(constellation_msg::Control); + } + if mods.contains(glfw::Super) { + result.insert(constellation_msg::Super); + } + result +} + fn glfw_key_to_script_key(key: glfw::Key) -> constellation_msg::Key { match key { glfw::KeySpace => constellation_msg::KeySpace,