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

Implement single-line text input #3585

Merged
merged 10 commits into from Nov 13, 2014

Dispatch keydown, keyup, and keypress events at appropriate times.

  • Loading branch information
jdm committed Nov 13, 2014
commit 329ba56fca3bd3808a37aae6bc3ed3c5a5d23524
@@ -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<Window: WindowMethods> IOCompositor<Window> {
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<Window: WindowMethods> IOCompositor<Window> {
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,
@@ -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<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
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<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
.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)));
});
}

@@ -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 {
@@ -50,6 +50,7 @@ pub struct WindowSizeData {
pub device_pixel_ratio: ScaleFactor<ViewportPx, DevicePixel, f32>,
}

#[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
@@ -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<u32>,
pub key_code: u32,
}

impl KeyEventProperties {
pub fn is_printable(&self) -> bool {
self.char_code.is_some()
}
}

impl<'a> KeyboardEventMethods for JSRef<'a, KeyboardEvent> {
@@ -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<EventTarget> = 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) {
@@ -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<f32>),
MouseUpEvent(uint, Point2D<f32>),
MouseMoveEvent(Point2D<f32>),
KeyEvent(Key, KeyState),
KeyEvent(Key, KeyState, KeyModifiers),
}

/// An opaque wrapper around script<->layout channels to avoid leaking message types into
@@ -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,
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.