mirror of
https://github.com/servo/servo.git
synced 2025-08-04 21:20:23 +01:00
auto merge of #3585 : jdm/servo/input, r=gw
This attempts to implement a bunch of the DOM Level 3 Events spec by implementing the KeyboardEvent interface, the document focus context, and dispatching keyup/keydown/keypress events appropriately. There's also some support for multiline text input that's untested.
This commit is contained in:
commit
2ffa845cf4
26 changed files with 1593 additions and 69 deletions
|
@ -19,7 +19,7 @@ use windowing::{MouseWindowEvent, MouseWindowEventClass, MouseWindowMouseDownEve
|
|||
use windowing::{MouseWindowMouseUpEvent, MouseWindowMoveEventClass, NavigationWindowEvent};
|
||||
use windowing::{QuitWindowEvent, RefreshWindowEvent, ResizeWindowEvent, ScrollWindowEvent};
|
||||
use windowing::{WindowEvent, WindowMethods, WindowNavigateMsg, ZoomWindowEvent};
|
||||
use windowing::{PinchZoomWindowEvent};
|
||||
use windowing::{PinchZoomWindowEvent, KeyEvent};
|
||||
|
||||
use azure::azure_hl;
|
||||
use std::cmp;
|
||||
|
@ -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};
|
||||
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,6 +707,10 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
self.on_navigation_window_event(direction);
|
||||
}
|
||||
|
||||
KeyEvent(key, state, modifiers) => {
|
||||
self.on_key_event(key, state, modifiers);
|
||||
}
|
||||
|
||||
FinishedWindowEvent => {
|
||||
let exit = opts::get().exit_after_load;
|
||||
if exit {
|
||||
|
@ -878,6 +882,11 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
chan.send(NavigateMsg(direction))
|
||||
}
|
||||
|
||||
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, modifiers))
|
||||
}
|
||||
|
||||
fn convert_buffer_requests_to_pipeline_requests_map(&self,
|
||||
requests: Vec<(Rc<Layer<CompositorData>>,
|
||||
Vec<BufferRequest>)>) ->
|
||||
|
|
|
@ -13,7 +13,8 @@ use gfx::render_task;
|
|||
use layers::geometry::DevicePixel;
|
||||
use layout_traits::{LayoutControlChan, LayoutTaskFactory, ExitNowMsg};
|
||||
use libc;
|
||||
use script_traits::{ResizeMsg, ResizeInactiveMsg, ExitPipelineMsg};
|
||||
use script_traits;
|
||||
use script_traits::{ResizeMsg, ResizeInactiveMsg, ExitPipelineMsg, SendEventMsg};
|
||||
use script_traits::{ScriptControlChan, ScriptTaskFactory};
|
||||
use servo_msg::compositor_msg::LayerId;
|
||||
use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, FailureMsg, Failure, FrameRectMsg};
|
||||
|
@ -21,6 +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, KeyModifiers};
|
||||
use servo_msg::constellation_msg;
|
||||
use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient};
|
||||
use servo_net::resource_task::ResourceTask;
|
||||
|
@ -450,6 +452,10 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
|
|||
debug!("constellation got window resize message");
|
||||
self.handle_resized_window_msg(new_size);
|
||||
}
|
||||
KeyEvent(key, state, modifiers) => {
|
||||
debug!("constellation got key event message");
|
||||
self.handle_key_msg(key, state, modifiers);
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
@ -761,6 +767,13 @@ 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, 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, mods)));
|
||||
});
|
||||
}
|
||||
|
||||
fn handle_renderer_ready_msg(&mut self, pipeline_id: PipelineId) {
|
||||
debug!("Renderer {} ready to send paint msg", pipeline_id);
|
||||
// This message could originate from a pipeline in the navigation context or
|
||||
|
|
|
@ -11,6 +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, KeyModifiers};
|
||||
use servo_msg::compositor_msg::{ReadyState, RenderState};
|
||||
use servo_util::geometry::ScreenPx;
|
||||
use std::fmt::{FormatError, Formatter, Show};
|
||||
|
@ -58,6 +59,8 @@ pub enum WindowEvent {
|
|||
FinishedWindowEvent,
|
||||
/// Sent when the user quits the application
|
||||
QuitWindowEvent,
|
||||
/// Sent when a key input state changes
|
||||
KeyEvent(Key, KeyState, KeyModifiers),
|
||||
}
|
||||
|
||||
impl Show for WindowEvent {
|
||||
|
@ -66,6 +69,7 @@ impl Show for WindowEvent {
|
|||
IdleWindowEvent => write!(f, "Idle"),
|
||||
RefreshWindowEvent => write!(f, "Refresh"),
|
||||
ResizeWindowEvent(..) => write!(f, "Resize"),
|
||||
KeyEvent(..) => write!(f, "Key"),
|
||||
LoadUrlWindowEvent(..) => write!(f, "LoadUrl"),
|
||||
MouseWindowEventClass(..) => write!(f, "Mouse"),
|
||||
MouseWindowMoveEventClass(..) => write!(f, "MouseMove"),
|
||||
|
|
|
@ -50,6 +50,148 @@ pub struct WindowSizeData {
|
|||
pub device_pixel_ratio: ScaleFactor<ViewportPx, DevicePixel, f32>,
|
||||
}
|
||||
|
||||
#[deriving(PartialEq)]
|
||||
pub enum KeyState {
|
||||
Pressed,
|
||||
Released,
|
||||
Repeated,
|
||||
}
|
||||
|
||||
//N.B. Straight up copied from glfw-rs
|
||||
#[deriving(Show)]
|
||||
pub enum Key {
|
||||
KeySpace,
|
||||
KeyApostrophe,
|
||||
KeyComma,
|
||||
KeyMinus,
|
||||
KeyPeriod,
|
||||
KeySlash,
|
||||
Key0,
|
||||
Key1,
|
||||
Key2,
|
||||
Key3,
|
||||
Key4,
|
||||
Key5,
|
||||
Key6,
|
||||
Key7,
|
||||
Key8,
|
||||
Key9,
|
||||
KeySemicolon,
|
||||
KeyEqual,
|
||||
KeyA,
|
||||
KeyB,
|
||||
KeyC,
|
||||
KeyD,
|
||||
KeyE,
|
||||
KeyF,
|
||||
KeyG,
|
||||
KeyH,
|
||||
KeyI,
|
||||
KeyJ,
|
||||
KeyK,
|
||||
KeyL,
|
||||
KeyM,
|
||||
KeyN,
|
||||
KeyO,
|
||||
KeyP,
|
||||
KeyQ,
|
||||
KeyR,
|
||||
KeyS,
|
||||
KeyT,
|
||||
KeyU,
|
||||
KeyV,
|
||||
KeyW,
|
||||
KeyX,
|
||||
KeyY,
|
||||
KeyZ,
|
||||
KeyLeftBracket,
|
||||
KeyBackslash,
|
||||
KeyRightBracket,
|
||||
KeyGraveAccent,
|
||||
KeyWorld1,
|
||||
KeyWorld2,
|
||||
|
||||
KeyEscape,
|
||||
KeyEnter,
|
||||
KeyTab,
|
||||
KeyBackspace,
|
||||
KeyInsert,
|
||||
KeyDelete,
|
||||
KeyRight,
|
||||
KeyLeft,
|
||||
KeyDown,
|
||||
KeyUp,
|
||||
KeyPageUp,
|
||||
KeyPageDown,
|
||||
KeyHome,
|
||||
KeyEnd,
|
||||
KeyCapsLock,
|
||||
KeyScrollLock,
|
||||
KeyNumLock,
|
||||
KeyPrintScreen,
|
||||
KeyPause,
|
||||
KeyF1,
|
||||
KeyF2,
|
||||
KeyF3,
|
||||
KeyF4,
|
||||
KeyF5,
|
||||
KeyF6,
|
||||
KeyF7,
|
||||
KeyF8,
|
||||
KeyF9,
|
||||
KeyF10,
|
||||
KeyF11,
|
||||
KeyF12,
|
||||
KeyF13,
|
||||
KeyF14,
|
||||
KeyF15,
|
||||
KeyF16,
|
||||
KeyF17,
|
||||
KeyF18,
|
||||
KeyF19,
|
||||
KeyF20,
|
||||
KeyF21,
|
||||
KeyF22,
|
||||
KeyF23,
|
||||
KeyF24,
|
||||
KeyF25,
|
||||
KeyKp0,
|
||||
KeyKp1,
|
||||
KeyKp2,
|
||||
KeyKp3,
|
||||
KeyKp4,
|
||||
KeyKp5,
|
||||
KeyKp6,
|
||||
KeyKp7,
|
||||
KeyKp8,
|
||||
KeyKp9,
|
||||
KeyKpDecimal,
|
||||
KeyKpDivide,
|
||||
KeyKpMultiply,
|
||||
KeyKpSubtract,
|
||||
KeyKpAdd,
|
||||
KeyKpEnter,
|
||||
KeyKpEqual,
|
||||
KeyLeftShift,
|
||||
KeyLeftControl,
|
||||
KeyLeftAlt,
|
||||
KeyLeftSuper,
|
||||
KeyRightShift,
|
||||
KeyRightControl,
|
||||
KeyRightAlt,
|
||||
KeyRightSuper,
|
||||
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,
|
||||
|
@ -62,6 +204,7 @@ pub enum Msg {
|
|||
NavigateMsg(NavigationDirection),
|
||||
RendererReadyMsg(PipelineId),
|
||||
ResizedWindowMsg(WindowSizeData),
|
||||
KeyEvent(Key, KeyState, KeyModifiers),
|
||||
}
|
||||
|
||||
/// Similar to net::resource_task::LoadData
|
||||
|
|
|
@ -4280,7 +4280,7 @@ class CGDictionary(CGThing):
|
|||
d = self.dictionary
|
||||
if d.parent:
|
||||
inheritance = " pub parent: %s::%s<'a, 'b>,\n" % (self.makeModuleName(d.parent),
|
||||
self.makeClassName(d.parent))
|
||||
self.makeClassName(d.parent))
|
||||
else:
|
||||
inheritance = ""
|
||||
memberDecls = [" pub %s: %s," %
|
||||
|
@ -4347,12 +4347,7 @@ class CGDictionary(CGThing):
|
|||
|
||||
@staticmethod
|
||||
def makeModuleName(dictionary):
|
||||
name = dictionary.identifier.name
|
||||
if name.endswith('Init'):
|
||||
return toBindingNamespace(name.replace('Init', ''))
|
||||
#XXXjdm This breaks on the test webidl files, sigh.
|
||||
#raise TypeError("No idea how to find this dictionary's definition: " + name)
|
||||
return "/* uh oh */ %s" % name
|
||||
return dictionary.module()
|
||||
|
||||
def getMemberType(self, memberInfo):
|
||||
member, (_, _, declType, _) = memberInfo
|
||||
|
@ -4535,7 +4530,7 @@ class CGBindingRoot(CGThing):
|
|||
'dom::bindings::utils::{DOMJSClass, JSCLASS_DOM_GLOBAL}',
|
||||
'dom::bindings::utils::{FindEnumStringIndex, GetArrayIndexFromId}',
|
||||
'dom::bindings::utils::{GetPropertyOnPrototype, GetProtoOrIfaceArray}',
|
||||
'dom::bindings::utils::{HasPropertyOnPrototype, IntVal}',
|
||||
'dom::bindings::utils::{HasPropertyOnPrototype, IntVal, UintVal}',
|
||||
'dom::bindings::utils::{Reflectable}',
|
||||
'dom::bindings::utils::{squirrel_away_unique}',
|
||||
'dom::bindings::utils::{ThrowingConstructor, unwrap, unwrap_jsmanaged}',
|
||||
|
@ -5430,7 +5425,8 @@ class GlobalGenRoots():
|
|||
def Bindings(config):
|
||||
|
||||
descriptors = (set(d.name + "Binding" for d in config.getDescriptors(register=True)) |
|
||||
set(d.unroll().module() for d in config.callbacks))
|
||||
set(d.unroll().module() for d in config.callbacks) |
|
||||
set(d.module() for d in config.getDictionaries()))
|
||||
curr = CGList([CGGeneric("pub mod %s;\n" % name) for name in sorted(descriptors)])
|
||||
curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
|
||||
return curr
|
||||
|
|
|
@ -1422,6 +1422,9 @@ class IDLDictionary(IDLObjectWithScope):
|
|||
self.identifier.name,
|
||||
[member.location] + locations)
|
||||
|
||||
def module(self):
|
||||
return self.location.filename().split('/')[-1].split('.webidl')[0] + 'Binding'
|
||||
|
||||
def addExtendedAttributes(self, attrs):
|
||||
assert len(attrs) == 0
|
||||
|
||||
|
|
|
@ -1,5 +1,14 @@
|
|||
--- WebIDL.py
|
||||
+++ WebIDL.py
|
||||
@@ -1422,6 +1422,9 @@ class IDLDictionary(IDLObjectWithScope):
|
||||
self.identifier.name,
|
||||
[member.location] + locations)
|
||||
|
||||
+ def module(self):
|
||||
+ return self.location.filename().split('/')[-1].split('.webidl')[0] + 'Binding'
|
||||
+
|
||||
def addExtendedAttributes(self, attrs):
|
||||
assert len(attrs) == 0
|
||||
@@ -3398,6 +3398,9 @@ class IDLCallbackType(IDLType, IDLObjectWithScope):
|
||||
self._treatNonCallableAsNull = False
|
||||
self._treatNonObjectAsNull = False
|
||||
|
|
|
@ -65,8 +65,12 @@ impl<'a> CustomEventMethods for JSRef<'a, CustomEvent> {
|
|||
can_bubble: bool,
|
||||
cancelable: bool,
|
||||
detail: JSVal) {
|
||||
self.detail.set(detail);
|
||||
let event: JSRef<Event> = EventCast::from_ref(self);
|
||||
if event.dispatching() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.detail.set(detail);
|
||||
event.InitEvent(type_, can_bubble, cancelable);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,6 +96,10 @@ pub struct Document {
|
|||
anchors: MutNullableJS<HTMLCollection>,
|
||||
applets: MutNullableJS<HTMLCollection>,
|
||||
ready_state: Cell<DocumentReadyState>,
|
||||
/// The element that has most recently requested focus for itself.
|
||||
possibly_focused: MutNullableJS<Element>,
|
||||
/// The element that currently has the document focus context.
|
||||
focused: MutNullableJS<Element>,
|
||||
}
|
||||
|
||||
impl DocumentDerived for EventTarget {
|
||||
|
@ -178,6 +182,10 @@ pub trait DocumentHelpers<'a> {
|
|||
fn load_anchor_href(self, href: DOMString);
|
||||
fn find_fragment_node(self, fragid: DOMString) -> Option<Temporary<Element>>;
|
||||
fn set_ready_state(self, state: DocumentReadyState);
|
||||
fn get_focused_element(self) -> Option<Temporary<Element>>;
|
||||
fn begin_focus_transaction(self);
|
||||
fn request_focus(self, elem: JSRef<Element>);
|
||||
fn commit_focus_transaction(self);
|
||||
}
|
||||
|
||||
impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> {
|
||||
|
@ -327,6 +335,30 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> {
|
|||
let target: JSRef<EventTarget> = EventTargetCast::from_ref(self);
|
||||
let _ = target.DispatchEvent(*event);
|
||||
}
|
||||
|
||||
/// Return the element that currently has focus.
|
||||
// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#events-focusevent-doc-focus
|
||||
fn get_focused_element(self) -> Option<Temporary<Element>> {
|
||||
self.focused.get()
|
||||
}
|
||||
|
||||
/// Initiate a new round of checking for elements requesting focus. The last element to call
|
||||
/// `request_focus` before `commit_focus_transaction` is called will receive focus.
|
||||
fn begin_focus_transaction(self) {
|
||||
self.possibly_focused.clear();
|
||||
}
|
||||
|
||||
/// Request that the given element receive focus once the current transaction is complete.
|
||||
fn request_focus(self, elem: JSRef<Element>) {
|
||||
self.possibly_focused.assign(Some(elem))
|
||||
}
|
||||
|
||||
/// Reassign the focus context to the element that last requested focus during this
|
||||
/// transaction, or none if no elements requested it.
|
||||
fn commit_focus_transaction(self) {
|
||||
//TODO: dispatch blur, focus, focusout, and focusin events
|
||||
self.focused.assign(self.possibly_focused.get());
|
||||
}
|
||||
}
|
||||
|
||||
#[deriving(PartialEq)]
|
||||
|
@ -390,6 +422,8 @@ impl Document {
|
|||
anchors: Default::default(),
|
||||
applets: Default::default(),
|
||||
ready_state: Cell::new(ready_state),
|
||||
possibly_focused: Default::default(),
|
||||
focused: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ use dom::document::{Document, DocumentHelpers, LayoutDocumentHelpers};
|
|||
use dom::domtokenlist::DOMTokenList;
|
||||
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
|
||||
use dom::htmlcollection::HTMLCollection;
|
||||
use dom::htmlinputelement::{HTMLInputElement, LayoutHTMLInputElementHelpers};
|
||||
use dom::htmlinputelement::{HTMLInputElement, RawLayoutHTMLInputElementHelpers};
|
||||
use dom::htmlserializer::serialize;
|
||||
use dom::htmltablecellelement::{HTMLTableCellElement, HTMLTableCellElementHelpers};
|
||||
use dom::node::{ElementNodeTypeId, Node, NodeHelpers, NodeIterator, document_from_node};
|
||||
|
@ -231,17 +231,24 @@ pub trait RawLayoutElementHelpers {
|
|||
-> Option<i32>;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(unrooted_must_root)]
|
||||
unsafe fn get_attr_for_layout<'a>(elem: &'a Element, namespace: &Namespace, name: &Atom) -> Option<&'a JS<Attr>> {
|
||||
// cast to point to T in RefCell<T> directly
|
||||
let attrs: *const Vec<JS<Attr>> = mem::transmute(&elem.attrs);
|
||||
(*attrs).iter().find(|attr: & &JS<Attr>| {
|
||||
let attr = attr.unsafe_get();
|
||||
*name == (*attr).local_name_atom_forever() &&
|
||||
(*attr).namespace() == namespace
|
||||
})
|
||||
}
|
||||
|
||||
impl RawLayoutElementHelpers for Element {
|
||||
#[inline]
|
||||
#[allow(unrooted_must_root)]
|
||||
unsafe fn get_attr_val_for_layout<'a>(&'a self, namespace: &Namespace, name: &Atom)
|
||||
-> Option<&'a str> {
|
||||
let attrs = self.attrs.borrow_for_layout();
|
||||
(*attrs).iter().find(|attr: & &JS<Attr>| {
|
||||
let attr = attr.unsafe_get();
|
||||
*name == (*attr).local_name_atom_forever() &&
|
||||
(*attr).namespace() == namespace
|
||||
}).map(|attr| {
|
||||
get_attr_for_layout(self, namespace, name).map(|attr| {
|
||||
let attr = attr.unsafe_get();
|
||||
(*attr).value_ref_forever()
|
||||
})
|
||||
|
@ -337,6 +344,7 @@ impl RawLayoutElementHelpers for Element {
|
|||
|
||||
pub trait LayoutElementHelpers {
|
||||
unsafe fn html_element_in_html_document_for_layout(&self) -> bool;
|
||||
unsafe fn has_attr_for_layout(&self, namespace: &Namespace, name: &Atom) -> bool;
|
||||
}
|
||||
|
||||
impl LayoutElementHelpers for JS<Element> {
|
||||
|
@ -349,6 +357,10 @@ impl LayoutElementHelpers for JS<Element> {
|
|||
let node: JS<Node> = self.transmute_copy();
|
||||
node.owner_doc_for_layout().is_html_document_for_layout()
|
||||
}
|
||||
|
||||
unsafe fn has_attr_for_layout(&self, namespace: &Namespace, name: &Atom) -> bool {
|
||||
get_attr_for_layout(&*self.unsafe_get(), namespace, name).is_some()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ElementHelpers<'a> {
|
||||
|
|
|
@ -29,7 +29,7 @@ pub enum EventPhase {
|
|||
pub enum EventTypeId {
|
||||
CustomEventTypeId,
|
||||
HTMLEventTypeId,
|
||||
KeyEventTypeId,
|
||||
KeyboardEventTypeId,
|
||||
MessageEventTypeId,
|
||||
MouseEventTypeId,
|
||||
ProgressEventTypeId,
|
||||
|
@ -219,10 +219,11 @@ impl<'a> EventMethods for JSRef<'a, Event> {
|
|||
type_: DOMString,
|
||||
bubbles: bool,
|
||||
cancelable: bool) {
|
||||
self.initialized.set(true);
|
||||
if self.dispatching.get() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.initialized.set(true);
|
||||
self.stop_propagation.set(false);
|
||||
self.stop_immediate.set(false);
|
||||
self.canceled.set(false);
|
||||
|
|
|
@ -13,16 +13,20 @@ use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementM
|
|||
use dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods;
|
||||
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, HTMLFormElementCast, HTMLInputElementCast, NodeCast};
|
||||
use dom::bindings::codegen::InheritTypes::{HTMLInputElementDerived, HTMLFieldSetElementDerived};
|
||||
use dom::bindings::codegen::InheritTypes::KeyboardEventCast;
|
||||
use dom::bindings::js::{JS, JSRef, Temporary, OptionalRootable, ResultRootable};
|
||||
use dom::bindings::utils::{Reflectable, Reflector};
|
||||
use dom::document::{Document, DocumentHelpers};
|
||||
use dom::element::{AttributeHandlers, Element, HTMLInputElementTypeId};
|
||||
use dom::element::{AttributeHandlers, Element, HTMLInputElementTypeId, LayoutElementHelpers};
|
||||
use dom::element::RawLayoutElementHelpers;
|
||||
use dom::event::Event;
|
||||
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
|
||||
use dom::htmlelement::HTMLElement;
|
||||
use dom::keyboardevent::KeyboardEvent;
|
||||
use dom::htmlformelement::{InputElement, FormOwner, HTMLFormElement, HTMLFormElementHelpers, NotFromFormSubmitMethod};
|
||||
use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId, document_from_node, window_from_node};
|
||||
use dom::virtualmethods::VirtualMethods;
|
||||
use textinput::{Single, TextInput, TriggerDefaultAction, DispatchInput, Nothing};
|
||||
|
||||
use servo_util::str::DOMString;
|
||||
use string_cache::Atom;
|
||||
|
@ -51,9 +55,8 @@ pub struct HTMLInputElement {
|
|||
htmlelement: HTMLElement,
|
||||
input_type: Cell<InputType>,
|
||||
checked: Cell<bool>,
|
||||
uncommitted_value: DOMRefCell<Option<String>>,
|
||||
value: DOMRefCell<Option<String>>,
|
||||
size: Cell<u32>,
|
||||
textinput: DOMRefCell<TextInput>,
|
||||
}
|
||||
|
||||
impl HTMLInputElementDerived for EventTarget {
|
||||
|
@ -70,9 +73,8 @@ impl HTMLInputElement {
|
|||
htmlelement: HTMLElement::new_inherited(HTMLInputElementTypeId, localName, prefix, document),
|
||||
input_type: Cell::new(InputText),
|
||||
checked: Cell::new(false),
|
||||
uncommitted_value: DOMRefCell::new(None),
|
||||
value: DOMRefCell::new(None),
|
||||
size: Cell::new(DEFAULT_INPUT_SIZE),
|
||||
textinput: DOMRefCell::new(TextInput::new(Single, "".to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,40 +86,55 @@ impl HTMLInputElement {
|
|||
}
|
||||
|
||||
pub trait LayoutHTMLInputElementHelpers {
|
||||
unsafe fn get_value_for_layout(&self) -> String;
|
||||
unsafe fn get_value_for_layout(self) -> String;
|
||||
unsafe fn get_size_for_layout(self) -> u32;
|
||||
}
|
||||
|
||||
pub trait RawLayoutHTMLInputElementHelpers {
|
||||
unsafe fn get_size_for_layout(&self) -> u32;
|
||||
}
|
||||
|
||||
impl LayoutHTMLInputElementHelpers for HTMLInputElement {
|
||||
impl LayoutHTMLInputElementHelpers for JS<HTMLInputElement> {
|
||||
#[allow(unrooted_must_root)]
|
||||
unsafe fn get_value_for_layout(&self) -> String {
|
||||
match self.input_type.get() {
|
||||
unsafe fn get_value_for_layout(self) -> String {
|
||||
unsafe fn get_raw_textinput_value(input: JS<HTMLInputElement>) -> Option<String> {
|
||||
let elem: JS<Element> = input.transmute_copy();
|
||||
if !elem.has_attr_for_layout(&ns!(""), &atom!("value")) {
|
||||
return None;
|
||||
}
|
||||
Some((*input.unsafe_get()).textinput.borrow_for_layout().get_content())
|
||||
}
|
||||
|
||||
unsafe fn get_raw_attr_value(input: JS<HTMLInputElement>) -> Option<String> {
|
||||
let elem: JS<Element> = input.transmute_copy();
|
||||
(*elem.unsafe_get()).get_attr_val_for_layout(&ns!(""), &atom!("value"))
|
||||
.map(|s| s.to_string())
|
||||
}
|
||||
|
||||
match (*self.unsafe_get()).input_type.get() {
|
||||
InputCheckbox | InputRadio => "".to_string(),
|
||||
InputFile | InputImage => "".to_string(),
|
||||
InputButton(ref default) => self.value.borrow_for_layout().clone()
|
||||
InputButton(ref default) => get_raw_attr_value(self)
|
||||
.or_else(|| default.map(|v| v.to_string()))
|
||||
.unwrap_or_else(|| "".to_string()),
|
||||
InputPassword => {
|
||||
let raw = self.value.borrow_for_layout().clone().unwrap_or_else(|| "".to_string());
|
||||
String::from_char(raw.len(), '*')
|
||||
let raw = get_raw_textinput_value(self).unwrap_or_else(|| "".to_string());
|
||||
String::from_char(raw.len(), '●')
|
||||
}
|
||||
_ => self.value.borrow_for_layout().clone().unwrap_or_else(|| "".to_string()),
|
||||
_ => get_raw_textinput_value(self).unwrap_or_else(|| "".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unrooted_must_root)]
|
||||
unsafe fn get_size_for_layout(&self) -> u32 {
|
||||
self.size.get()
|
||||
unsafe fn get_size_for_layout(self) -> u32 {
|
||||
(*self.unsafe_get()).get_size_for_layout()
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutHTMLInputElementHelpers for JS<HTMLInputElement> {
|
||||
unsafe fn get_value_for_layout(&self) -> String {
|
||||
(*self.unsafe_get()).get_value_for_layout()
|
||||
}
|
||||
|
||||
impl RawLayoutHTMLInputElementHelpers for HTMLInputElement {
|
||||
#[allow(unrooted_must_root)]
|
||||
unsafe fn get_size_for_layout(&self) -> u32 {
|
||||
(*self.unsafe_get()).get_size_for_layout()
|
||||
self.size.get()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,7 +173,7 @@ impl<'a> HTMLInputElementMethods for JSRef<'a, HTMLInputElement> {
|
|||
|
||||
// https://html.spec.whatwg.org/multipage/forms.html#dom-input-value
|
||||
fn Value(self) -> DOMString {
|
||||
self.value.borrow().clone().unwrap_or("".to_string())
|
||||
self.textinput.borrow().get_content()
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/forms.html#dom-input-value
|
||||
|
@ -309,7 +326,7 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {
|
|||
self.force_relayout();
|
||||
}
|
||||
&atom!("value") => {
|
||||
*self.value.borrow_mut() = Some(attr.value().as_slice().to_string());
|
||||
self.textinput.borrow_mut().set_content(attr.value().as_slice().to_string());
|
||||
self.force_relayout();
|
||||
}
|
||||
&atom!("name") => {
|
||||
|
@ -353,7 +370,7 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {
|
|||
self.force_relayout();
|
||||
}
|
||||
&atom!("value") => {
|
||||
*self.value.borrow_mut() = None;
|
||||
self.textinput.borrow_mut().set_content("".to_string());
|
||||
self.force_relayout();
|
||||
}
|
||||
&atom!("name") => {
|
||||
|
@ -415,6 +432,23 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {
|
|||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
//TODO: set the editing position for text inputs
|
||||
|
||||
let doc = document_from_node(*self).root();
|
||||
doc.request_focus(ElementCast::from_ref(*self));
|
||||
} else if "keydown" == event.Type().as_slice() && !event.DefaultPrevented() &&
|
||||
(self.input_type.get() == InputText || self.input_type.get() == InputPassword) {
|
||||
let keyevent: Option<JSRef<KeyboardEvent>> = KeyboardEventCast::to_ref(event);
|
||||
keyevent.map(|event| {
|
||||
match self.textinput.borrow_mut().handle_keydown(event) {
|
||||
TriggerDefaultAction => (),
|
||||
DispatchInput => {
|
||||
self.force_relayout();
|
||||
}
|
||||
Nothing => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
639
components/script/dom/keyboardevent.rs
Normal file
639
components/script/dom/keyboardevent.rs
Normal file
|
@ -0,0 +1,639 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use dom::bindings::codegen::Bindings::KeyboardEventBinding;
|
||||
use dom::bindings::codegen::Bindings::KeyboardEventBinding::{KeyboardEventMethods, KeyboardEventConstants};
|
||||
use dom::bindings::codegen::Bindings::UIEventBinding::UIEventMethods;
|
||||
use dom::bindings::codegen::InheritTypes::{EventCast, UIEventCast, KeyboardEventDerived};
|
||||
use dom::bindings::error::Fallible;
|
||||
use dom::bindings::global::GlobalRef;
|
||||
use dom::bindings::global;
|
||||
use dom::bindings::js::{JSRef, Temporary, RootedReference};
|
||||
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};
|
||||
|
||||
#[jstraceable]
|
||||
#[must_root]
|
||||
pub struct KeyboardEvent {
|
||||
uievent: UIEvent,
|
||||
key: RefCell<DOMString>,
|
||||
code: RefCell<DOMString>,
|
||||
location: Cell<u32>,
|
||||
ctrl: Cell<bool>,
|
||||
alt: Cell<bool>,
|
||||
shift: Cell<bool>,
|
||||
meta: Cell<bool>,
|
||||
repeat: Cell<bool>,
|
||||
is_composing: Cell<bool>,
|
||||
char_code: Cell<Option<u32>>,
|
||||
key_code: Cell<u32>,
|
||||
}
|
||||
|
||||
impl KeyboardEventDerived for Event {
|
||||
fn is_keyboardevent(&self) -> bool {
|
||||
*self.type_id() == KeyboardEventTypeId
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyboardEvent {
|
||||
fn new_inherited() -> KeyboardEvent {
|
||||
KeyboardEvent {
|
||||
uievent: UIEvent::new_inherited(KeyboardEventTypeId),
|
||||
key: RefCell::new("".to_string()),
|
||||
code: RefCell::new("".to_string()),
|
||||
location: Cell::new(0),
|
||||
ctrl: Cell::new(false),
|
||||
alt: Cell::new(false),
|
||||
shift: Cell::new(false),
|
||||
meta: Cell::new(false),
|
||||
repeat: Cell::new(false),
|
||||
is_composing: Cell::new(false),
|
||||
char_code: Cell::new(None),
|
||||
key_code: Cell::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_uninitialized(window: JSRef<Window>) -> Temporary<KeyboardEvent> {
|
||||
reflect_dom_object(box KeyboardEvent::new_inherited(),
|
||||
&global::Window(window),
|
||||
KeyboardEventBinding::Wrap)
|
||||
}
|
||||
|
||||
pub fn new(window: JSRef<Window>,
|
||||
type_: DOMString,
|
||||
canBubble: bool,
|
||||
cancelable: bool,
|
||||
view: Option<JSRef<Window>>,
|
||||
_detail: i32,
|
||||
key: DOMString,
|
||||
code: DOMString,
|
||||
location: u32,
|
||||
repeat: bool,
|
||||
isComposing: bool,
|
||||
ctrlKey: bool,
|
||||
altKey: bool,
|
||||
shiftKey: bool,
|
||||
metaKey: bool,
|
||||
char_code: Option<u32>,
|
||||
key_code: u32) -> Temporary<KeyboardEvent> {
|
||||
let ev = KeyboardEvent::new_uninitialized(window).root();
|
||||
ev.deref().InitKeyboardEvent(type_, canBubble, cancelable, view, key, location,
|
||||
"".to_string(), repeat, "".to_string());
|
||||
*ev.code.borrow_mut() = code;
|
||||
ev.ctrl.set(ctrlKey);
|
||||
ev.alt.set(altKey);
|
||||
ev.shift.set(shiftKey);
|
||||
ev.meta.set(metaKey);
|
||||
ev.char_code.set(char_code);
|
||||
ev.key_code.set(key_code);
|
||||
ev.is_composing.set(isComposing);
|
||||
Temporary::from_rooted(*ev)
|
||||
}
|
||||
|
||||
pub fn Constructor(global: &GlobalRef,
|
||||
type_: DOMString,
|
||||
init: &KeyboardEventBinding::KeyboardEventInit) -> Fallible<Temporary<KeyboardEvent>> {
|
||||
let event = KeyboardEvent::new(global.as_window(), type_,
|
||||
init.parent.parent.parent.bubbles,
|
||||
init.parent.parent.parent.cancelable,
|
||||
init.parent.parent.view.root_ref(),
|
||||
init.parent.parent.detail,
|
||||
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: constellation_msg::Key, mods: constellation_msg::KeyModifiers)
|
||||
-> KeyEventProperties {
|
||||
KeyEventProperties {
|
||||
key: key_value(key, mods),
|
||||
code: code_value(key),
|
||||
location: key_location(key),
|
||||
char_code: key_charcode(key, mods),
|
||||
key_code: key_keycode(key),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3Events-key.html
|
||||
fn key_value(key: constellation_msg::Key, mods: constellation_msg::KeyModifiers) -> &'static str {
|
||||
let shift = mods.contains(constellation_msg::SHIFT);
|
||||
match key {
|
||||
constellation_msg::KeySpace => " ",
|
||||
constellation_msg::KeyApostrophe if shift => "\"",
|
||||
constellation_msg::KeyApostrophe => "'",
|
||||
constellation_msg::KeyComma if shift => "<",
|
||||
constellation_msg::KeyComma => ",",
|
||||
constellation_msg::KeyMinus if shift => "_",
|
||||
constellation_msg::KeyMinus => "-",
|
||||
constellation_msg::KeyPeriod if shift => ">",
|
||||
constellation_msg::KeyPeriod => ".",
|
||||
constellation_msg::KeySlash if shift => "?",
|
||||
constellation_msg::KeySlash => "/",
|
||||
constellation_msg::Key0 if shift => ")",
|
||||
constellation_msg::Key0 => "0",
|
||||
constellation_msg::Key1 if shift => "!",
|
||||
constellation_msg::Key1 => "1",
|
||||
constellation_msg::Key2 if shift => "@",
|
||||
constellation_msg::Key2 => "2",
|
||||
constellation_msg::Key3 if shift => "#",
|
||||
constellation_msg::Key3 => "3",
|
||||
constellation_msg::Key4 if shift => "$",
|
||||
constellation_msg::Key4 => "4",
|
||||
constellation_msg::Key5 if shift => "%",
|
||||
constellation_msg::Key5 => "5",
|
||||
constellation_msg::Key6 if shift => "^",
|
||||
constellation_msg::Key6 => "6",
|
||||
constellation_msg::Key7 if shift => "&",
|
||||
constellation_msg::Key7 => "7",
|
||||
constellation_msg::Key8 if shift => "*",
|
||||
constellation_msg::Key8 => "8",
|
||||
constellation_msg::Key9 if shift => "(",
|
||||
constellation_msg::Key9 => "9",
|
||||
constellation_msg::KeySemicolon if shift => ":",
|
||||
constellation_msg::KeySemicolon => ";",
|
||||
constellation_msg::KeyEqual if shift => "+",
|
||||
constellation_msg::KeyEqual => "=",
|
||||
constellation_msg::KeyA if shift => "A",
|
||||
constellation_msg::KeyA => "a",
|
||||
constellation_msg::KeyB if shift => "B",
|
||||
constellation_msg::KeyB => "b",
|
||||
constellation_msg::KeyC if shift => "C",
|
||||
constellation_msg::KeyC => "c",
|
||||
constellation_msg::KeyD if shift => "D",
|
||||
constellation_msg::KeyD => "d",
|
||||
constellation_msg::KeyE if shift => "E",
|
||||
constellation_msg::KeyE => "e",
|
||||
constellation_msg::KeyF if shift => "F",
|
||||
constellation_msg::KeyF => "f",
|
||||
constellation_msg::KeyG if shift => "G",
|
||||
constellation_msg::KeyG => "g",
|
||||
constellation_msg::KeyH if shift => "H",
|
||||
constellation_msg::KeyH => "h",
|
||||
constellation_msg::KeyI if shift => "I",
|
||||
constellation_msg::KeyI => "i",
|
||||
constellation_msg::KeyJ if shift => "J",
|
||||
constellation_msg::KeyJ => "j",
|
||||
constellation_msg::KeyK if shift => "K",
|
||||
constellation_msg::KeyK => "k",
|
||||
constellation_msg::KeyL if shift => "L",
|
||||
constellation_msg::KeyL => "l",
|
||||
constellation_msg::KeyM if shift => "M",
|
||||
constellation_msg::KeyM => "m",
|
||||
constellation_msg::KeyN if shift => "N",
|
||||
constellation_msg::KeyN => "n",
|
||||
constellation_msg::KeyO if shift => "O",
|
||||
constellation_msg::KeyO => "o",
|
||||
constellation_msg::KeyP if shift => "P",
|
||||
constellation_msg::KeyP => "p",
|
||||
constellation_msg::KeyQ if shift => "Q",
|
||||
constellation_msg::KeyQ => "q",
|
||||
constellation_msg::KeyR if shift => "R",
|
||||
constellation_msg::KeyR => "r",
|
||||
constellation_msg::KeyS if shift => "S",
|
||||
constellation_msg::KeyS => "s",
|
||||
constellation_msg::KeyT if shift => "T",
|
||||
constellation_msg::KeyT => "t",
|
||||
constellation_msg::KeyU if shift => "U",
|
||||
constellation_msg::KeyU => "u",
|
||||
constellation_msg::KeyV if shift => "V",
|
||||
constellation_msg::KeyV => "v",
|
||||
constellation_msg::KeyW if shift => "W",
|
||||
constellation_msg::KeyW => "w",
|
||||
constellation_msg::KeyX if shift => "X",
|
||||
constellation_msg::KeyX => "x",
|
||||
constellation_msg::KeyY if shift => "Y",
|
||||
constellation_msg::KeyY => "y",
|
||||
constellation_msg::KeyZ if shift => "Z",
|
||||
constellation_msg::KeyZ => "z",
|
||||
constellation_msg::KeyLeftBracket if shift => "{",
|
||||
constellation_msg::KeyLeftBracket => "[",
|
||||
constellation_msg::KeyBackslash if shift => "|",
|
||||
constellation_msg::KeyBackslash => "\\",
|
||||
constellation_msg::KeyRightBracket if shift => "}",
|
||||
constellation_msg::KeyRightBracket => "]",
|
||||
constellation_msg::KeyGraveAccent => "Dead",
|
||||
constellation_msg::KeyWorld1 => "Unidentified",
|
||||
constellation_msg::KeyWorld2 => "Unidentified",
|
||||
constellation_msg::KeyEscape => "Escape",
|
||||
constellation_msg::KeyEnter => "Enter",
|
||||
constellation_msg::KeyTab => "Tab",
|
||||
constellation_msg::KeyBackspace => "Backspace",
|
||||
constellation_msg::KeyInsert => "Insert",
|
||||
constellation_msg::KeyDelete => "Delete",
|
||||
constellation_msg::KeyRight => "ArrowRight",
|
||||
constellation_msg::KeyLeft => "ArrowLeft",
|
||||
constellation_msg::KeyDown => "ArrowDown",
|
||||
constellation_msg::KeyUp => "ArrowUp",
|
||||
constellation_msg::KeyPageUp => "PageUp",
|
||||
constellation_msg::KeyPageDown => "PageDown",
|
||||
constellation_msg::KeyHome => "Home",
|
||||
constellation_msg::KeyEnd => "End",
|
||||
constellation_msg::KeyCapsLock => "CapsLock",
|
||||
constellation_msg::KeyScrollLock => "ScrollLock",
|
||||
constellation_msg::KeyNumLock => "NumLock",
|
||||
constellation_msg::KeyPrintScreen => "PrintScreen",
|
||||
constellation_msg::KeyPause => "Pause",
|
||||
constellation_msg::KeyF1 => "F1",
|
||||
constellation_msg::KeyF2 => "F2",
|
||||
constellation_msg::KeyF3 => "F3",
|
||||
constellation_msg::KeyF4 => "F4",
|
||||
constellation_msg::KeyF5 => "F5",
|
||||
constellation_msg::KeyF6 => "F6",
|
||||
constellation_msg::KeyF7 => "F7",
|
||||
constellation_msg::KeyF8 => "F8",
|
||||
constellation_msg::KeyF9 => "F9",
|
||||
constellation_msg::KeyF10 => "F10",
|
||||
constellation_msg::KeyF11 => "F11",
|
||||
constellation_msg::KeyF12 => "F12",
|
||||
constellation_msg::KeyF13 => "F13",
|
||||
constellation_msg::KeyF14 => "F14",
|
||||
constellation_msg::KeyF15 => "F15",
|
||||
constellation_msg::KeyF16 => "F16",
|
||||
constellation_msg::KeyF17 => "F17",
|
||||
constellation_msg::KeyF18 => "F18",
|
||||
constellation_msg::KeyF19 => "F19",
|
||||
constellation_msg::KeyF20 => "F20",
|
||||
constellation_msg::KeyF21 => "F21",
|
||||
constellation_msg::KeyF22 => "F22",
|
||||
constellation_msg::KeyF23 => "F23",
|
||||
constellation_msg::KeyF24 => "F24",
|
||||
constellation_msg::KeyF25 => "F25",
|
||||
constellation_msg::KeyKp0 => "0",
|
||||
constellation_msg::KeyKp1 => "1",
|
||||
constellation_msg::KeyKp2 => "2",
|
||||
constellation_msg::KeyKp3 => "3",
|
||||
constellation_msg::KeyKp4 => "4",
|
||||
constellation_msg::KeyKp5 => "5",
|
||||
constellation_msg::KeyKp6 => "6",
|
||||
constellation_msg::KeyKp7 => "7",
|
||||
constellation_msg::KeyKp8 => "8",
|
||||
constellation_msg::KeyKp9 => "9",
|
||||
constellation_msg::KeyKpDecimal => ".",
|
||||
constellation_msg::KeyKpDivide => "/",
|
||||
constellation_msg::KeyKpMultiply => "*",
|
||||
constellation_msg::KeyKpSubtract => "-",
|
||||
constellation_msg::KeyKpAdd => "+",
|
||||
constellation_msg::KeyKpEnter => "Enter",
|
||||
constellation_msg::KeyKpEqual => "=",
|
||||
constellation_msg::KeyLeftShift => "Shift",
|
||||
constellation_msg::KeyLeftControl => "Control",
|
||||
constellation_msg::KeyLeftAlt => "Alt",
|
||||
constellation_msg::KeyLeftSuper => "Super",
|
||||
constellation_msg::KeyRightShift => "Shift",
|
||||
constellation_msg::KeyRightControl => "Control",
|
||||
constellation_msg::KeyRightAlt => "Alt",
|
||||
constellation_msg::KeyRightSuper => "Super",
|
||||
constellation_msg::KeyMenu => "ContextMenu",
|
||||
}
|
||||
}
|
||||
|
||||
// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3Events-code.html
|
||||
fn code_value(key: constellation_msg::Key) -> &'static str {
|
||||
match key {
|
||||
constellation_msg::KeySpace => "Space",
|
||||
constellation_msg::KeyApostrophe => "Quote",
|
||||
constellation_msg::KeyComma => "Comma",
|
||||
constellation_msg::KeyMinus => "Minus",
|
||||
constellation_msg::KeyPeriod => "Period",
|
||||
constellation_msg::KeySlash => "Slash",
|
||||
constellation_msg::Key0 => "Digit0",
|
||||
constellation_msg::Key1 => "Digit1",
|
||||
constellation_msg::Key2 => "Digit2",
|
||||
constellation_msg::Key3 => "Digit3",
|
||||
constellation_msg::Key4 => "Digit4",
|
||||
constellation_msg::Key5 => "Digit5",
|
||||
constellation_msg::Key6 => "Digit6",
|
||||
constellation_msg::Key7 => "Digit7",
|
||||
constellation_msg::Key8 => "Digit8",
|
||||
constellation_msg::Key9 => "Digit9",
|
||||
constellation_msg::KeySemicolon => "Semicolon",
|
||||
constellation_msg::KeyEqual => "Equals",
|
||||
constellation_msg::KeyA => "KeyA",
|
||||
constellation_msg::KeyB => "KeyB",
|
||||
constellation_msg::KeyC => "KeyC",
|
||||
constellation_msg::KeyD => "KeyD",
|
||||
constellation_msg::KeyE => "KeyE",
|
||||
constellation_msg::KeyF => "KeyF",
|
||||
constellation_msg::KeyG => "KeyG",
|
||||
constellation_msg::KeyH => "KeyH",
|
||||
constellation_msg::KeyI => "KeyI",
|
||||
constellation_msg::KeyJ => "KeyJ",
|
||||
constellation_msg::KeyK => "KeyK",
|
||||
constellation_msg::KeyL => "KeyL",
|
||||
constellation_msg::KeyM => "KeyM",
|
||||
constellation_msg::KeyN => "KeyN",
|
||||
constellation_msg::KeyO => "KeyO",
|
||||
constellation_msg::KeyP => "KeyP",
|
||||
constellation_msg::KeyQ => "KeyQ",
|
||||
constellation_msg::KeyR => "KeyR",
|
||||
constellation_msg::KeyS => "KeyS",
|
||||
constellation_msg::KeyT => "KeyT",
|
||||
constellation_msg::KeyU => "KeyU",
|
||||
constellation_msg::KeyV => "KeyV",
|
||||
constellation_msg::KeyW => "KeyW",
|
||||
constellation_msg::KeyX => "KeyX",
|
||||
constellation_msg::KeyY => "KeyY",
|
||||
constellation_msg::KeyZ => "KeyZ",
|
||||
constellation_msg::KeyLeftBracket => "BracketLeft",
|
||||
constellation_msg::KeyBackslash => "Backslash",
|
||||
constellation_msg::KeyRightBracket => "BracketRight",
|
||||
|
||||
constellation_msg::KeyGraveAccent |
|
||||
constellation_msg::KeyWorld1 |
|
||||
constellation_msg::KeyWorld2 => panic!("unknown char code for {}", key),
|
||||
|
||||
constellation_msg::KeyEscape => "Escape",
|
||||
constellation_msg::KeyEnter => "Enter",
|
||||
constellation_msg::KeyTab => "Tab",
|
||||
constellation_msg::KeyBackspace => "Backspace",
|
||||
constellation_msg::KeyInsert => "Insert",
|
||||
constellation_msg::KeyDelete => "Delete",
|
||||
constellation_msg::KeyRight => "ArrowRight",
|
||||
constellation_msg::KeyLeft => "ArrowLeft",
|
||||
constellation_msg::KeyDown => "ArrowDown",
|
||||
constellation_msg::KeyUp => "ArrowUp",
|
||||
constellation_msg::KeyPageUp => "PageUp",
|
||||
constellation_msg::KeyPageDown => "PageDown",
|
||||
constellation_msg::KeyHome => "Home",
|
||||
constellation_msg::KeyEnd => "End",
|
||||
constellation_msg::KeyCapsLock => "CapsLock",
|
||||
constellation_msg::KeyScrollLock => "ScrollLock",
|
||||
constellation_msg::KeyNumLock => "NumLock",
|
||||
constellation_msg::KeyPrintScreen => "PrintScreen",
|
||||
constellation_msg::KeyPause => "Pause",
|
||||
constellation_msg::KeyF1 => "F1",
|
||||
constellation_msg::KeyF2 => "F2",
|
||||
constellation_msg::KeyF3 => "F3",
|
||||
constellation_msg::KeyF4 => "F4",
|
||||
constellation_msg::KeyF5 => "F5",
|
||||
constellation_msg::KeyF6 => "F6",
|
||||
constellation_msg::KeyF7 => "F7",
|
||||
constellation_msg::KeyF8 => "F8",
|
||||
constellation_msg::KeyF9 => "F9",
|
||||
constellation_msg::KeyF10 => "F10",
|
||||
constellation_msg::KeyF11 => "F11",
|
||||
constellation_msg::KeyF12 => "F12",
|
||||
constellation_msg::KeyF13 => "F13",
|
||||
constellation_msg::KeyF14 => "F14",
|
||||
constellation_msg::KeyF15 => "F15",
|
||||
constellation_msg::KeyF16 => "F16",
|
||||
constellation_msg::KeyF17 => "F17",
|
||||
constellation_msg::KeyF18 => "F18",
|
||||
constellation_msg::KeyF19 => "F19",
|
||||
constellation_msg::KeyF20 => "F20",
|
||||
constellation_msg::KeyF21 => "F21",
|
||||
constellation_msg::KeyF22 => "F22",
|
||||
constellation_msg::KeyF23 => "F23",
|
||||
constellation_msg::KeyF24 => "F24",
|
||||
constellation_msg::KeyF25 => "F25",
|
||||
constellation_msg::KeyKp0 => "Numpad0",
|
||||
constellation_msg::KeyKp1 => "Numpad1",
|
||||
constellation_msg::KeyKp2 => "Numpad2",
|
||||
constellation_msg::KeyKp3 => "Numpad3",
|
||||
constellation_msg::KeyKp4 => "Numpad4",
|
||||
constellation_msg::KeyKp5 => "Numpad5",
|
||||
constellation_msg::KeyKp6 => "Numpad6",
|
||||
constellation_msg::KeyKp7 => "Numpad7",
|
||||
constellation_msg::KeyKp8 => "Numpad8",
|
||||
constellation_msg::KeyKp9 => "Numpad9",
|
||||
constellation_msg::KeyKpDecimal => "NumpadDecimal",
|
||||
constellation_msg::KeyKpDivide => "NumpadDivide",
|
||||
constellation_msg::KeyKpMultiply => "NumpadMultiply",
|
||||
constellation_msg::KeyKpSubtract => "NumpadSubtract",
|
||||
constellation_msg::KeyKpAdd => "NumpadAdd",
|
||||
constellation_msg::KeyKpEnter => "NumpadEnter",
|
||||
constellation_msg::KeyKpEqual => "NumpadEquals",
|
||||
constellation_msg::KeyLeftShift | constellation_msg::KeyRightShift => "Shift",
|
||||
constellation_msg::KeyLeftControl | constellation_msg::KeyRightControl => "Control",
|
||||
constellation_msg::KeyLeftAlt | constellation_msg::KeyRightAlt => "Alt",
|
||||
constellation_msg::KeyLeftSuper | constellation_msg::KeyRightSuper => "Super",
|
||||
constellation_msg::KeyMenu => "Menu",
|
||||
}
|
||||
}
|
||||
|
||||
fn key_location(key: constellation_msg::Key) -> u32 {
|
||||
match key {
|
||||
constellation_msg::KeyKp0 | constellation_msg::KeyKp1 | constellation_msg::KeyKp2 |
|
||||
constellation_msg::KeyKp3 | constellation_msg::KeyKp4 | constellation_msg::KeyKp5 |
|
||||
constellation_msg::KeyKp6 | constellation_msg::KeyKp7 | constellation_msg::KeyKp8 |
|
||||
constellation_msg::KeyKp9 | constellation_msg::KeyKpDecimal |
|
||||
constellation_msg::KeyKpDivide | constellation_msg::KeyKpMultiply |
|
||||
constellation_msg::KeyKpSubtract | constellation_msg::KeyKpAdd |
|
||||
constellation_msg::KeyKpEnter | constellation_msg::KeyKpEqual =>
|
||||
KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD,
|
||||
|
||||
constellation_msg::KeyLeftShift | constellation_msg::KeyLeftAlt |
|
||||
constellation_msg::KeyLeftControl | constellation_msg::KeyLeftSuper =>
|
||||
KeyboardEventConstants::DOM_KEY_LOCATION_LEFT,
|
||||
|
||||
constellation_msg::KeyRightShift | constellation_msg::KeyRightAlt |
|
||||
constellation_msg::KeyRightControl | constellation_msg::KeyRightSuper =>
|
||||
KeyboardEventConstants::DOM_KEY_LOCATION_RIGHT,
|
||||
|
||||
_ => KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD,
|
||||
}
|
||||
}
|
||||
|
||||
// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#widl-KeyboardEvent-charCode
|
||||
fn key_charcode(key: constellation_msg::Key, mods: constellation_msg::KeyModifiers) -> Option<u32> {
|
||||
let key = key_value(key, mods);
|
||||
if key.len() == 1 {
|
||||
Some(key.char_at(0) as u32)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#legacy-key-models
|
||||
fn key_keycode(key: constellation_msg::Key) -> u32 {
|
||||
match key {
|
||||
// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#legacy-key-models
|
||||
constellation_msg::KeyBackspace => 8,
|
||||
constellation_msg::KeyTab => 9,
|
||||
constellation_msg::KeyEnter => 13,
|
||||
constellation_msg::KeyLeftShift | constellation_msg::KeyRightShift => 16,
|
||||
constellation_msg::KeyLeftControl | constellation_msg::KeyRightControl => 17,
|
||||
constellation_msg::KeyLeftAlt | constellation_msg::KeyRightAlt => 18,
|
||||
constellation_msg::KeyCapsLock => 20,
|
||||
constellation_msg::KeyEscape => 27,
|
||||
constellation_msg::KeySpace => 32,
|
||||
constellation_msg::KeyPageUp => 33,
|
||||
constellation_msg::KeyPageDown => 34,
|
||||
constellation_msg::KeyEnd => 35,
|
||||
constellation_msg::KeyHome => 36,
|
||||
constellation_msg::KeyLeft => 37,
|
||||
constellation_msg::KeyUp => 38,
|
||||
constellation_msg::KeyRight => 39,
|
||||
constellation_msg::KeyDown => 40,
|
||||
constellation_msg::KeyDelete => 46,
|
||||
|
||||
// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#optionally-fixed-virtual-key-codes
|
||||
constellation_msg::KeySemicolon => 186,
|
||||
constellation_msg::KeyEqual => 187,
|
||||
constellation_msg::KeyComma => 188,
|
||||
constellation_msg::KeyMinus => 189,
|
||||
constellation_msg::KeyPeriod => 190,
|
||||
constellation_msg::KeySlash => 191,
|
||||
constellation_msg::KeyLeftBracket => 219,
|
||||
constellation_msg::KeyBackslash => 220,
|
||||
constellation_msg::KeyRightBracket => 221,
|
||||
constellation_msg::KeyApostrophe => 222,
|
||||
|
||||
//§ B.2.1.3
|
||||
constellation_msg::Key0 |
|
||||
constellation_msg::Key1 |
|
||||
constellation_msg::Key2 |
|
||||
constellation_msg::Key3 |
|
||||
constellation_msg::Key4 |
|
||||
constellation_msg::Key5 |
|
||||
constellation_msg::Key6 |
|
||||
constellation_msg::Key7 |
|
||||
constellation_msg::Key8 |
|
||||
constellation_msg::Key9 => key as u32 - constellation_msg::Key0 as u32 + '0' as u32,
|
||||
|
||||
//§ B.2.1.4
|
||||
constellation_msg::KeyA |
|
||||
constellation_msg::KeyB |
|
||||
constellation_msg::KeyC |
|
||||
constellation_msg::KeyD |
|
||||
constellation_msg::KeyE |
|
||||
constellation_msg::KeyF |
|
||||
constellation_msg::KeyG |
|
||||
constellation_msg::KeyH |
|
||||
constellation_msg::KeyI |
|
||||
constellation_msg::KeyJ |
|
||||
constellation_msg::KeyK |
|
||||
constellation_msg::KeyL |
|
||||
constellation_msg::KeyM |
|
||||
constellation_msg::KeyN |
|
||||
constellation_msg::KeyO |
|
||||
constellation_msg::KeyP |
|
||||
constellation_msg::KeyQ |
|
||||
constellation_msg::KeyR |
|
||||
constellation_msg::KeyS |
|
||||
constellation_msg::KeyT |
|
||||
constellation_msg::KeyU |
|
||||
constellation_msg::KeyV |
|
||||
constellation_msg::KeyW |
|
||||
constellation_msg::KeyX |
|
||||
constellation_msg::KeyY |
|
||||
constellation_msg::KeyZ => key as u32 - constellation_msg::KeyA as u32 + 'A' as u32,
|
||||
|
||||
//§ B.2.1.8
|
||||
_ => 0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct KeyEventProperties {
|
||||
pub key: &'static str,
|
||||
pub code: &'static str,
|
||||
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> {
|
||||
fn InitKeyboardEvent(self,
|
||||
typeArg: DOMString,
|
||||
canBubbleArg: bool,
|
||||
cancelableArg: bool,
|
||||
viewArg: Option<JSRef<Window>>,
|
||||
keyArg: DOMString,
|
||||
locationArg: u32,
|
||||
_modifiersListArg: DOMString,
|
||||
repeat: bool,
|
||||
_locale: DOMString) {
|
||||
let event: JSRef<Event> = EventCast::from_ref(self);
|
||||
if event.dispatching() {
|
||||
return;
|
||||
}
|
||||
|
||||
let uievent: JSRef<UIEvent> = UIEventCast::from_ref(self);
|
||||
uievent.InitUIEvent(typeArg, canBubbleArg, cancelableArg, viewArg, 0);
|
||||
*self.key.borrow_mut() = keyArg;
|
||||
self.location.set(locationArg);
|
||||
self.repeat.set(repeat);
|
||||
}
|
||||
|
||||
fn Key(self) -> DOMString {
|
||||
self.key.borrow().clone()
|
||||
}
|
||||
|
||||
fn Code(self) -> DOMString {
|
||||
self.code.borrow().clone()
|
||||
}
|
||||
|
||||
fn Location(self) -> u32 {
|
||||
self.location.get()
|
||||
}
|
||||
|
||||
fn CtrlKey(self) -> bool {
|
||||
self.ctrl.get()
|
||||
}
|
||||
|
||||
fn ShiftKey(self) -> bool {
|
||||
self.shift.get()
|
||||
}
|
||||
|
||||
fn AltKey(self) -> bool {
|
||||
self.alt.get()
|
||||
}
|
||||
|
||||
fn MetaKey(self) -> bool {
|
||||
self.meta.get()
|
||||
}
|
||||
|
||||
fn Repeat(self) -> bool {
|
||||
self.repeat.get()
|
||||
}
|
||||
|
||||
fn IsComposing(self) -> bool {
|
||||
self.is_composing.get()
|
||||
}
|
||||
|
||||
// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#widl-KeyboardEvent-getModifierState
|
||||
fn GetModifierState(self, keyArg: DOMString) -> bool {
|
||||
match keyArg.as_slice() {
|
||||
"Ctrl" => self.CtrlKey(),
|
||||
"Alt" => self.AltKey(),
|
||||
"Shift" => self.ShiftKey(),
|
||||
"Meta" => self.MetaKey(),
|
||||
"AltGraph" | "CapsLock" | "NumLock" | "ScrollLock" | "Accel" |
|
||||
"Fn" | "FnLock" | "Hyper" | "OS" | "Symbol" | "SymbolLock" => false, //FIXME
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn CharCode(self) -> u32 {
|
||||
self.char_code.get().unwrap_or(0)
|
||||
}
|
||||
|
||||
fn KeyCode(self) -> u32 {
|
||||
self.key_code.get()
|
||||
}
|
||||
|
||||
fn Which(self) -> u32 {
|
||||
self.char_code.get().unwrap_or(self.KeyCode())
|
||||
}
|
||||
}
|
||||
|
||||
impl Reflectable for KeyboardEvent {
|
||||
fn reflector<'a>(&'a self) -> &'a Reflector {
|
||||
self.uievent.reflector()
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
use dom::bindings::codegen::Bindings::MouseEventBinding;
|
||||
use dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
|
||||
use dom::bindings::codegen::Bindings::UIEventBinding::UIEventMethods;
|
||||
use dom::bindings::codegen::InheritTypes::{UIEventCast, MouseEventDerived};
|
||||
use dom::bindings::codegen::InheritTypes::{EventCast, UIEventCast, MouseEventDerived};
|
||||
use dom::bindings::error::Fallible;
|
||||
use dom::bindings::global::GlobalRef;
|
||||
use dom::bindings::global;
|
||||
|
@ -21,7 +21,7 @@ use std::default::Default;
|
|||
|
||||
#[dom_struct]
|
||||
pub struct MouseEvent {
|
||||
mouseevent: UIEvent,
|
||||
uievent: UIEvent,
|
||||
screen_x: Cell<i32>,
|
||||
screen_y: Cell<i32>,
|
||||
client_x: Cell<i32>,
|
||||
|
@ -43,7 +43,7 @@ impl MouseEventDerived for Event {
|
|||
impl MouseEvent {
|
||||
fn new_inherited() -> MouseEvent {
|
||||
MouseEvent {
|
||||
mouseevent: UIEvent::new_inherited(MouseEventTypeId),
|
||||
uievent: UIEvent::new_inherited(MouseEventTypeId),
|
||||
screen_x: Cell::new(0),
|
||||
screen_y: Cell::new(0),
|
||||
client_x: Cell::new(0),
|
||||
|
@ -91,13 +91,13 @@ impl MouseEvent {
|
|||
type_: DOMString,
|
||||
init: &MouseEventBinding::MouseEventInit) -> Fallible<Temporary<MouseEvent>> {
|
||||
let event = MouseEvent::new(global.as_window(), type_,
|
||||
init.parent.parent.bubbles,
|
||||
init.parent.parent.cancelable,
|
||||
init.parent.view.root_ref(),
|
||||
init.parent.detail,
|
||||
init.parent.parent.parent.bubbles,
|
||||
init.parent.parent.parent.cancelable,
|
||||
init.parent.parent.view.root_ref(),
|
||||
init.parent.parent.detail,
|
||||
init.screenX, init.screenY,
|
||||
init.clientX, init.clientY, init.ctrlKey,
|
||||
init.altKey, init.shiftKey, init.metaKey,
|
||||
init.clientX, init.clientY, init.parent.ctrlKey,
|
||||
init.parent.altKey, init.parent.shiftKey, init.parent.metaKey,
|
||||
init.button, init.relatedTarget.root_ref());
|
||||
Ok(event)
|
||||
}
|
||||
|
@ -160,6 +160,11 @@ impl<'a> MouseEventMethods for JSRef<'a, MouseEvent> {
|
|||
metaKeyArg: bool,
|
||||
buttonArg: i16,
|
||||
relatedTargetArg: Option<JSRef<EventTarget>>) {
|
||||
let event: JSRef<Event> = EventCast::from_ref(self);
|
||||
if event.dispatching() {
|
||||
return;
|
||||
}
|
||||
|
||||
let uievent: JSRef<UIEvent> = UIEventCast::from_ref(self);
|
||||
uievent.InitUIEvent(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg);
|
||||
self.screen_x.set(screenXArg);
|
||||
|
@ -175,9 +180,8 @@ impl<'a> MouseEventMethods for JSRef<'a, MouseEvent> {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
impl Reflectable for MouseEvent {
|
||||
fn reflector<'a>(&'a self) -> &'a Reflector {
|
||||
self.mouseevent.reflector()
|
||||
self.uievent.reflector()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,6 +89,10 @@ impl<'a> UIEventMethods for JSRef<'a, UIEvent> {
|
|||
view: Option<JSRef<Window>>,
|
||||
detail: i32) {
|
||||
let event: JSRef<Event> = EventCast::from_ref(self);
|
||||
if event.dispatching() {
|
||||
return;
|
||||
}
|
||||
|
||||
event.InitEvent(type_, can_bubble, cancelable);
|
||||
self.view.assign(view);
|
||||
self.detail.set(detail);
|
||||
|
|
58
components/script/dom/webidls/KeyboardEvent.webidl
Normal file
58
components/script/dom/webidls/KeyboardEvent.webidl
Normal file
|
@ -0,0 +1,58 @@
|
|||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* The origin of this IDL file is
|
||||
* https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#interface-KeyboardEvent
|
||||
*
|
||||
*/
|
||||
|
||||
[Constructor(DOMString typeArg, optional KeyboardEventInit keyboardEventInitDict)]
|
||||
interface KeyboardEvent : UIEvent {
|
||||
// KeyLocationCode
|
||||
const unsigned long DOM_KEY_LOCATION_STANDARD = 0x00;
|
||||
const unsigned long DOM_KEY_LOCATION_LEFT = 0x01;
|
||||
const unsigned long DOM_KEY_LOCATION_RIGHT = 0x02;
|
||||
const unsigned long DOM_KEY_LOCATION_NUMPAD = 0x03;
|
||||
readonly attribute DOMString key;
|
||||
readonly attribute DOMString code;
|
||||
readonly attribute unsigned long location;
|
||||
readonly attribute boolean ctrlKey;
|
||||
readonly attribute boolean shiftKey;
|
||||
readonly attribute boolean altKey;
|
||||
readonly attribute boolean metaKey;
|
||||
readonly attribute boolean repeat;
|
||||
readonly attribute boolean isComposing;
|
||||
boolean getModifierState (DOMString keyArg);
|
||||
};
|
||||
|
||||
// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#idl-interface-KeyboardEvent-initializers
|
||||
partial interface KeyboardEvent {
|
||||
// Originally introduced (and deprecated) in DOM Level 3
|
||||
void initKeyboardEvent (DOMString typeArg, boolean bubblesArg, boolean cancelableArg, Window? viewArg, DOMString keyArg, unsigned long locationArg, DOMString modifiersListArg, boolean repeat, DOMString locale);
|
||||
};
|
||||
|
||||
// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#KeyboardEvent-supplemental-interface
|
||||
partial interface KeyboardEvent {
|
||||
// The following support legacy user agents
|
||||
readonly attribute unsigned long charCode;
|
||||
readonly attribute unsigned long keyCode;
|
||||
readonly attribute unsigned long which;
|
||||
};
|
||||
|
||||
// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#interface-KeyboardEvent
|
||||
dictionary KeyboardEventInit : SharedKeyboardAndMouseEventInit {
|
||||
DOMString key = "";
|
||||
DOMString code = "";
|
||||
unsigned long location = 0;
|
||||
boolean repeat = false;
|
||||
boolean isComposing = false;
|
||||
};
|
||||
|
||||
// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#events-KeyboardEventInit-supplemental
|
||||
/*partial dictionary KeyboardEventInit {
|
||||
unsigned long charCode = 0;
|
||||
unsigned long keyCode = 0;
|
||||
unsigned long which = 0;
|
||||
};*/
|
|
@ -22,15 +22,11 @@ interface MouseEvent : UIEvent {
|
|||
};
|
||||
|
||||
// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#idl-def-MouseEventInit
|
||||
dictionary MouseEventInit : UIEventInit {
|
||||
dictionary MouseEventInit : SharedKeyboardAndMouseEventInit {
|
||||
long screenX = 0;
|
||||
long screenY = 0;
|
||||
long clientX = 0;
|
||||
long clientY = 0;
|
||||
boolean ctrlKey = false;
|
||||
boolean shiftKey = false;
|
||||
boolean altKey = false;
|
||||
boolean metaKey = false;
|
||||
short button = 0;
|
||||
//unsigned short buttons = 0;
|
||||
EventTarget? relatedTarget = null;
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#idl-def-SharedKeyboardAndMouseEventInit
|
||||
dictionary SharedKeyboardAndMouseEventInit : UIEventInit {
|
||||
boolean ctrlKey = false;
|
||||
boolean shiftKey = false;
|
||||
boolean altKey = false;
|
||||
boolean metaKey = false;
|
||||
boolean keyModifierStateAltGraph = false;
|
||||
boolean keyModifierStateCapsLock = false;
|
||||
boolean keyModifierStateFn = false;
|
||||
boolean keyModifierStateFnLock = false;
|
||||
boolean keyModifierStateHyper = false;
|
||||
boolean keyModifierStateNumLock = false;
|
||||
boolean keyModifierStateOS = false;
|
||||
boolean keyModifierStateScrollLock = false;
|
||||
boolean keyModifierStateSuper = false;
|
||||
boolean keyModifierStateSymbol = false;
|
||||
boolean keyModifierStateSymbolLock = false;
|
||||
};
|
|
@ -382,6 +382,7 @@ impl<'a> WindowHelpers for JSRef<'a, Window> {
|
|||
fn handle_fire_timer(self, timer_id: TimerId, cx: *mut JSContext) {
|
||||
let this_value = self.reflector().get_jsobject();
|
||||
self.timers.fire_timer(timer_id, this_value, cx);
|
||||
self.flush_layout();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -178,6 +178,7 @@ pub mod dom {
|
|||
pub mod htmlulistelement;
|
||||
pub mod htmlvideoelement;
|
||||
pub mod htmlunknownelement;
|
||||
pub mod keyboardevent;
|
||||
pub mod location;
|
||||
pub mod messageevent;
|
||||
pub mod mouseevent;
|
||||
|
@ -220,3 +221,4 @@ pub mod layout_interface;
|
|||
pub mod page;
|
||||
pub mod script_task;
|
||||
mod timers;
|
||||
pub mod textinput;
|
||||
|
|
|
@ -9,7 +9,9 @@ use dom::bindings::cell::DOMRefCell;
|
|||
use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, DocumentReadyStateValues};
|
||||
use dom::bindings::codegen::Bindings::DOMRectBinding::DOMRectMethods;
|
||||
use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
|
||||
use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
|
||||
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 +25,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};
|
||||
|
@ -42,11 +45,13 @@ use script_traits::{CompositorEvent, ResizeEvent, ReflowEvent, ClickEvent, Mouse
|
|||
use script_traits::{MouseMoveEvent, MouseUpEvent, ConstellationControlMsg, ScriptTaskFactory};
|
||||
use script_traits::{ResizeMsg, AttachLayoutMsg, LoadMsg, ViewportMsg, SendEventMsg};
|
||||
use script_traits::{ResizeInactiveMsg, ExitPipelineMsg, NewLayoutInfo, OpaqueScriptLayoutChannel};
|
||||
use script_traits::{ScriptControlChan, ReflowCompleteMsg, UntrustedNodeAddress};
|
||||
use script_traits::{ScriptControlChan, ReflowCompleteMsg, UntrustedNodeAddress, KeyEvent};
|
||||
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;
|
||||
|
@ -907,9 +912,69 @@ impl ScriptTask {
|
|||
MouseMoveEvent(point) => {
|
||||
self.handle_mouse_move_event(pipeline_id, point);
|
||||
}
|
||||
|
||||
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) {
|
||||
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 focused = doc.get_focused_element().root();
|
||||
let body = doc.GetBody().root();
|
||||
|
||||
let target: JSRef<EventTarget> = match (&focused, &body) {
|
||||
(&Some(ref focused), _) => EventTargetCast::from_ref(**focused),
|
||||
(&None, &Some(ref body)) => EventTargetCast::from_ref(**body),
|
||||
(&None, &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, modifiers);
|
||||
|
||||
let keyevent = KeyboardEvent::new(*window, ev_type, true, true, Some(*window), 0,
|
||||
props.key.to_string(), props.code.to_string(),
|
||||
props.location, is_repeating, is_composing,
|
||||
ctrl, alt, shift, meta,
|
||||
None, props.key_code).root();
|
||||
let event = EventCast::from_ref(*keyevent);
|
||||
let _ = target.DispatchEvent(event);
|
||||
|
||||
// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#keys-cancelable-keys
|
||||
if state != Released && props.is_printable() && !event.DefaultPrevented() {
|
||||
// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#keypress-event-order
|
||||
let event = KeyboardEvent::new(*window, "keypress".to_string(), true, true, Some(*window),
|
||||
0, props.key.to_string(), props.code.to_string(),
|
||||
props.location, is_repeating, is_composing,
|
||||
ctrl, alt, shift, meta,
|
||||
props.char_code, 0).root();
|
||||
let _ = target.DispatchEvent(EventCast::from_ref(*event));
|
||||
|
||||
// TODO: if keypress event is canceled, prevent firing input events
|
||||
}
|
||||
|
||||
window.flush_layout();
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
|
@ -1011,6 +1076,9 @@ impl ScriptTask {
|
|||
match *page.frame() {
|
||||
Some(ref frame) => {
|
||||
let window = frame.window.root();
|
||||
let doc = window.Document().root();
|
||||
doc.begin_focus_transaction();
|
||||
|
||||
let event =
|
||||
Event::new(&global::Window(*window),
|
||||
"click".to_string(),
|
||||
|
@ -1018,6 +1086,7 @@ impl ScriptTask {
|
|||
let eventtarget: JSRef<EventTarget> = EventTargetCast::from_ref(node);
|
||||
let _ = eventtarget.dispatch_event_with_target(None, *event);
|
||||
|
||||
doc.commit_focus_transaction();
|
||||
window.flush_layout();
|
||||
}
|
||||
None => {}
|
||||
|
@ -1093,7 +1162,7 @@ impl ScriptTask {
|
|||
}
|
||||
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
299
components/script/textinput.rs
Normal file
299
components/script/textinput.rs
Normal file
|
@ -0,0 +1,299 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! Common handling of keyboard input and state management for text input controls
|
||||
|
||||
use dom::bindings::codegen::Bindings::KeyboardEventBinding::KeyboardEventMethods;
|
||||
use dom::bindings::js::JSRef;
|
||||
use dom::keyboardevent::KeyboardEvent;
|
||||
use servo_util::str::DOMString;
|
||||
|
||||
use std::cmp::{min, max};
|
||||
use std::default::Default;
|
||||
|
||||
#[jstraceable]
|
||||
struct TextPoint {
|
||||
/// 0-based line number
|
||||
line: uint,
|
||||
/// 0-based column number
|
||||
index: uint,
|
||||
}
|
||||
|
||||
/// Encapsulated state for handling keyboard input in a single or multiline text input control.
|
||||
#[jstraceable]
|
||||
pub struct TextInput {
|
||||
/// Current text input content, split across lines without trailing '\n'
|
||||
lines: Vec<DOMString>,
|
||||
/// Current cursor input point
|
||||
edit_point: TextPoint,
|
||||
/// Selection range, beginning and end point that can span multiple lines.
|
||||
_selection: Option<(TextPoint, TextPoint)>,
|
||||
/// Is this a multiline input?
|
||||
multiline: bool,
|
||||
}
|
||||
|
||||
/// Resulting action to be taken by the owner of a text input that is handling an event.
|
||||
pub enum KeyReaction {
|
||||
TriggerDefaultAction,
|
||||
DispatchInput,
|
||||
Nothing,
|
||||
}
|
||||
|
||||
impl Default for TextPoint {
|
||||
fn default() -> TextPoint {
|
||||
TextPoint {
|
||||
line: 0,
|
||||
index: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Control whether this control should allow multiple lines.
|
||||
#[deriving(PartialEq)]
|
||||
pub enum Lines {
|
||||
Single,
|
||||
Multiple,
|
||||
}
|
||||
|
||||
/// The direction in which to delete a character.
|
||||
#[deriving(PartialEq)]
|
||||
enum DeleteDir {
|
||||
Forward,
|
||||
Backward
|
||||
}
|
||||
|
||||
impl TextInput {
|
||||
/// Instantiate a new text input control
|
||||
pub fn new(lines: Lines, initial: DOMString) -> TextInput {
|
||||
let mut i = TextInput {
|
||||
lines: vec!(),
|
||||
edit_point: Default::default(),
|
||||
_selection: None,
|
||||
multiline: lines == Multiple,
|
||||
};
|
||||
i.set_content(initial);
|
||||
i
|
||||
}
|
||||
|
||||
/// Return the current line under the editing point
|
||||
fn get_current_line(&self) -> &DOMString {
|
||||
&self.lines[self.edit_point.line]
|
||||
}
|
||||
|
||||
/// Insert a character at the current editing point
|
||||
fn insert_char(&mut self, ch: char) {
|
||||
//TODO: handle replacing selection with character
|
||||
let new_line = {
|
||||
let prefix = self.get_current_line().as_slice().slice_chars(0, self.edit_point.index);
|
||||
let suffix = self.get_current_line().as_slice().slice_chars(self.edit_point.index,
|
||||
self.current_line_length());
|
||||
let mut new_line = prefix.to_string();
|
||||
new_line.push(ch);
|
||||
new_line.push_str(suffix.as_slice());
|
||||
new_line
|
||||
};
|
||||
|
||||
self.lines[self.edit_point.line] = new_line;
|
||||
self.edit_point.index += 1;
|
||||
}
|
||||
|
||||
/// Remove a character at the current editing point
|
||||
fn delete_char(&mut self, dir: DeleteDir) {
|
||||
let forward = dir == Forward;
|
||||
|
||||
//TODO: handle deleting selection
|
||||
let prefix_end = if forward {
|
||||
self.edit_point.index
|
||||
} else {
|
||||
if self.multiline {
|
||||
//TODO: handle backspacing from position 0 of current line
|
||||
if self.edit_point.index == 0 {
|
||||
return;
|
||||
}
|
||||
} else if self.edit_point.index == 0 {
|
||||
return;
|
||||
}
|
||||
self.edit_point.index - 1
|
||||
};
|
||||
let suffix_start = if forward {
|
||||
let is_eol = self.edit_point.index == self.current_line_length() - 1;
|
||||
if self.multiline {
|
||||
//TODO: handle deleting from end position of current line
|
||||
if is_eol {
|
||||
return;
|
||||
}
|
||||
} else if is_eol {
|
||||
return;
|
||||
}
|
||||
self.edit_point.index + 1
|
||||
} else {
|
||||
self.edit_point.index
|
||||
};
|
||||
|
||||
let new_line = {
|
||||
let prefix = self.get_current_line().as_slice().slice_chars(0, prefix_end);
|
||||
let suffix = self.get_current_line().as_slice().slice_chars(suffix_start,
|
||||
self.current_line_length());
|
||||
let mut new_line = prefix.to_string();
|
||||
new_line.push_str(suffix);
|
||||
new_line
|
||||
};
|
||||
|
||||
self.lines[self.edit_point.line] = new_line;
|
||||
|
||||
if !forward {
|
||||
self.adjust_horizontal(-1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the length of the current line under the editing point.
|
||||
fn current_line_length(&self) -> uint {
|
||||
self.lines[self.edit_point.line].len()
|
||||
}
|
||||
|
||||
/// Adjust the editing point position by a given of lines. The resulting column is
|
||||
/// as close to the original column position as possible.
|
||||
fn adjust_vertical(&mut self, adjust: int) {
|
||||
if !self.multiline {
|
||||
return;
|
||||
}
|
||||
|
||||
if adjust < 0 && self.edit_point.line as int + adjust < 0 {
|
||||
self.edit_point.index = 0;
|
||||
self.edit_point.line = 0;
|
||||
return;
|
||||
} else if adjust > 0 && self.edit_point.line >= min(0, self.lines.len() - adjust as uint) {
|
||||
self.edit_point.index = self.current_line_length();
|
||||
self.edit_point.line = self.lines.len() - 1;
|
||||
return;
|
||||
}
|
||||
|
||||
self.edit_point.line = (self.edit_point.line as int + adjust) as uint;
|
||||
self.edit_point.index = min(self.current_line_length(), self.edit_point.index);
|
||||
}
|
||||
|
||||
/// Adjust the editing point position by a given number of columns. If the adjustment
|
||||
/// requested is larger than is available in the current line, the editing point is
|
||||
/// adjusted vertically and the process repeats with the remaining adjustment requested.
|
||||
fn adjust_horizontal(&mut self, adjust: int) {
|
||||
if adjust < 0 {
|
||||
if self.multiline {
|
||||
let remaining = self.edit_point.index;
|
||||
if adjust.abs() as uint > remaining {
|
||||
self.edit_point.index = 0;
|
||||
self.adjust_vertical(-1);
|
||||
self.edit_point.index = self.current_line_length();
|
||||
self.adjust_horizontal(adjust + remaining as int);
|
||||
} else {
|
||||
self.edit_point.index = (self.edit_point.index as int + adjust) as uint;
|
||||
}
|
||||
} else {
|
||||
self.edit_point.index = max(0, self.edit_point.index as int + adjust) as uint;
|
||||
}
|
||||
} else {
|
||||
if self.multiline {
|
||||
let remaining = self.current_line_length() - self.edit_point.index;
|
||||
if adjust as uint > remaining {
|
||||
self.edit_point.index = 0;
|
||||
self.adjust_vertical(1);
|
||||
self.adjust_horizontal(adjust - remaining as int);
|
||||
} else {
|
||||
self.edit_point.index += adjust as uint;
|
||||
}
|
||||
} else {
|
||||
self.edit_point.index = min(self.current_line_length(),
|
||||
self.edit_point.index + adjust as uint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Deal with a newline input.
|
||||
fn handle_return(&mut self) -> KeyReaction {
|
||||
if !self.multiline {
|
||||
return TriggerDefaultAction;
|
||||
}
|
||||
|
||||
//TODO: support replacing selection with newline
|
||||
let prefix = self.get_current_line().as_slice().slice_chars(0, self.edit_point.index).to_string();
|
||||
let suffix = self.get_current_line().as_slice().slice_chars(self.edit_point.index,
|
||||
self.current_line_length()).to_string();
|
||||
self.lines[self.edit_point.line] = prefix;
|
||||
self.lines.insert(self.edit_point.line + 1, suffix);
|
||||
return DispatchInput;
|
||||
}
|
||||
|
||||
/// Process a given `KeyboardEvent` and return an action for the caller to execute.
|
||||
pub fn handle_keydown(&mut self, event: JSRef<KeyboardEvent>) -> KeyReaction {
|
||||
match event.Key().as_slice() {
|
||||
// printable characters have single-character key values
|
||||
c if c.len() == 1 => {
|
||||
self.insert_char(c.char_at(0));
|
||||
return DispatchInput;
|
||||
}
|
||||
"Space" => {
|
||||
self.insert_char(' ');
|
||||
DispatchInput
|
||||
}
|
||||
"Delete" => {
|
||||
self.delete_char(Forward);
|
||||
DispatchInput
|
||||
}
|
||||
"Backspace" => {
|
||||
self.delete_char(Backward);
|
||||
DispatchInput
|
||||
}
|
||||
"ArrowLeft" => {
|
||||
self.adjust_horizontal(-1);
|
||||
Nothing
|
||||
}
|
||||
"ArrowRight" => {
|
||||
self.adjust_horizontal(1);
|
||||
Nothing
|
||||
}
|
||||
"ArrowUp" => {
|
||||
self.adjust_vertical(-1);
|
||||
Nothing
|
||||
}
|
||||
"ArrowDown" => {
|
||||
self.adjust_vertical(1);
|
||||
Nothing
|
||||
}
|
||||
"Enter" => self.handle_return(),
|
||||
"Home" => {
|
||||
self.edit_point.index = 0;
|
||||
Nothing
|
||||
}
|
||||
"End" => {
|
||||
self.edit_point.index = self.current_line_length();
|
||||
Nothing
|
||||
}
|
||||
"Tab" => TriggerDefaultAction,
|
||||
_ => Nothing,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the current contents of the text input. Multiple lines are joined by \n.
|
||||
pub fn get_content(&self) -> DOMString {
|
||||
let mut content = "".to_string();
|
||||
for (i, line) in self.lines.iter().enumerate() {
|
||||
content.push_str(line.as_slice());
|
||||
if i < self.lines.len() - 1 {
|
||||
content.push('\n');
|
||||
}
|
||||
}
|
||||
content
|
||||
}
|
||||
|
||||
/// Set the current contents of the text input. If this is control supports multiple lines,
|
||||
/// any \n encountered will be stripped and force a new logical line.
|
||||
pub fn set_content(&mut self, content: DOMString) {
|
||||
self.lines = if self.multiline {
|
||||
content.as_slice().split('\n').map(|s| s.to_string()).collect()
|
||||
} else {
|
||||
vec!(content)
|
||||
};
|
||||
self.edit_point.line = min(self.edit_point.line, self.lines.len() - 1);
|
||||
self.edit_point.index = min(self.edit_point.index, self.current_line_length() - 1);
|
||||
}
|
||||
}
|
|
@ -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};
|
||||
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;
|
||||
|
@ -74,7 +74,8 @@ pub enum CompositorEvent {
|
|||
ClickEvent(uint, Point2D<f32>),
|
||||
MouseDownEvent(uint, Point2D<f32>),
|
||||
MouseUpEvent(uint, Point2D<f32>),
|
||||
MouseMoveEvent(Point2D<f32>)
|
||||
MouseMoveEvent(Point2D<f32>),
|
||||
KeyEvent(Key, KeyState, KeyModifiers),
|
||||
}
|
||||
|
||||
/// An opaque wrapper around script<->layout channels to avoid leaking message types into
|
||||
|
|
|
@ -10,7 +10,7 @@ use alert::{Alert, AlertMethods};
|
|||
use compositing::compositor_task::{mod, CompositorProxy, CompositorReceiver};
|
||||
use compositing::windowing::{Forward, Back};
|
||||
use compositing::windowing::{IdleWindowEvent, ResizeWindowEvent, LoadUrlWindowEvent};
|
||||
use compositing::windowing::{MouseWindowClickEvent, MouseWindowMouseDownEvent};
|
||||
use compositing::windowing::{KeyEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent};
|
||||
use compositing::windowing::{MouseWindowEventClass, MouseWindowMoveEventClass};
|
||||
use compositing::windowing::{MouseWindowMouseUpEvent, RefreshWindowEvent};
|
||||
use compositing::windowing::{NavigationWindowEvent, ScrollWindowEvent, ZoomWindowEvent};
|
||||
|
@ -25,6 +25,7 @@ use layers::platform::surface::NativeGraphicsMetadata;
|
|||
use libc::c_int;
|
||||
use msg::compositor_msg::{FinishedLoading, Blank, Loading, PerformingLayout, ReadyState};
|
||||
use msg::compositor_msg::{IdleRenderState, RenderState, RenderingRenderState};
|
||||
use msg::constellation_msg;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::comm::Receiver;
|
||||
use std::rc::Rc;
|
||||
|
@ -207,8 +208,16 @@ impl Window {
|
|||
match event {
|
||||
glfw::KeyEvent(key, _, action, mods) => {
|
||||
if action == glfw::Press {
|
||||
self.handle_key(key, mods)
|
||||
self.handle_key(key, mods);
|
||||
}
|
||||
let key = glfw_key_to_script_key(key);
|
||||
let state = match action {
|
||||
glfw::Press => constellation_msg::Pressed,
|
||||
glfw::Release => constellation_msg::Released,
|
||||
glfw::Repeat => constellation_msg::Repeated,
|
||||
};
|
||||
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(
|
||||
|
@ -428,3 +437,152 @@ 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
|
||||
}
|
||||
|
||||
macro_rules! glfw_keys_to_script_keys(
|
||||
($key:expr, $($name:ident),+) => (
|
||||
match $key {
|
||||
$(glfw::$name => constellation_msg::$name,)+
|
||||
}
|
||||
);
|
||||
)
|
||||
|
||||
fn glfw_key_to_script_key(key: glfw::Key) -> constellation_msg::Key {
|
||||
glfw_keys_to_script_keys!(key,
|
||||
KeySpace,
|
||||
KeyApostrophe,
|
||||
KeyComma,
|
||||
KeyMinus,
|
||||
KeyPeriod,
|
||||
KeySlash,
|
||||
Key0,
|
||||
Key1,
|
||||
Key2,
|
||||
Key3,
|
||||
Key4,
|
||||
Key5,
|
||||
Key6,
|
||||
Key7,
|
||||
Key8,
|
||||
Key9,
|
||||
KeySemicolon,
|
||||
KeyEqual,
|
||||
KeyA,
|
||||
KeyB,
|
||||
KeyC,
|
||||
KeyD,
|
||||
KeyE,
|
||||
KeyF,
|
||||
KeyG,
|
||||
KeyH,
|
||||
KeyI,
|
||||
KeyJ,
|
||||
KeyK,
|
||||
KeyL,
|
||||
KeyM,
|
||||
KeyN,
|
||||
KeyO,
|
||||
KeyP,
|
||||
KeyQ,
|
||||
KeyR,
|
||||
KeyS,
|
||||
KeyT,
|
||||
KeyU,
|
||||
KeyV,
|
||||
KeyW,
|
||||
KeyX,
|
||||
KeyY,
|
||||
KeyZ,
|
||||
KeyLeftBracket,
|
||||
KeyBackslash,
|
||||
KeyRightBracket,
|
||||
KeyGraveAccent,
|
||||
KeyWorld1,
|
||||
KeyWorld2,
|
||||
|
||||
KeyEscape,
|
||||
KeyEnter,
|
||||
KeyTab,
|
||||
KeyBackspace,
|
||||
KeyInsert,
|
||||
KeyDelete,
|
||||
KeyRight,
|
||||
KeyLeft,
|
||||
KeyDown,
|
||||
KeyUp,
|
||||
KeyPageUp,
|
||||
KeyPageDown,
|
||||
KeyHome,
|
||||
KeyEnd,
|
||||
KeyCapsLock,
|
||||
KeyScrollLock,
|
||||
KeyNumLock,
|
||||
KeyPrintScreen,
|
||||
KeyPause,
|
||||
KeyF1,
|
||||
KeyF2,
|
||||
KeyF3,
|
||||
KeyF4,
|
||||
KeyF5,
|
||||
KeyF6,
|
||||
KeyF7,
|
||||
KeyF8,
|
||||
KeyF9,
|
||||
KeyF10,
|
||||
KeyF11,
|
||||
KeyF12,
|
||||
KeyF13,
|
||||
KeyF14,
|
||||
KeyF15,
|
||||
KeyF16,
|
||||
KeyF17,
|
||||
KeyF18,
|
||||
KeyF19,
|
||||
KeyF20,
|
||||
KeyF21,
|
||||
KeyF22,
|
||||
KeyF23,
|
||||
KeyF24,
|
||||
KeyF25,
|
||||
KeyKp0,
|
||||
KeyKp1,
|
||||
KeyKp2,
|
||||
KeyKp3,
|
||||
KeyKp4,
|
||||
KeyKp5,
|
||||
KeyKp6,
|
||||
KeyKp7,
|
||||
KeyKp8,
|
||||
KeyKp9,
|
||||
KeyKpDecimal,
|
||||
KeyKpDivide,
|
||||
KeyKpMultiply,
|
||||
KeyKpSubtract,
|
||||
KeyKpAdd,
|
||||
KeyKpEnter,
|
||||
KeyKpEqual,
|
||||
KeyLeftShift,
|
||||
KeyLeftControl,
|
||||
KeyLeftAlt,
|
||||
KeyLeftSuper,
|
||||
KeyRightShift,
|
||||
KeyRightControl,
|
||||
KeyRightAlt,
|
||||
KeyRightSuper,
|
||||
KeyMenu)
|
||||
}
|
||||
|
|
|
@ -142,6 +142,7 @@ var interfaceNamesInGlobalScope = [
|
|||
"HTMLUListElement",
|
||||
"HTMLUnknownElement",
|
||||
"HTMLVideoElement",
|
||||
"KeyboardEvent",
|
||||
"Location",
|
||||
"MessageEvent",
|
||||
"MouseEvent",
|
||||
|
|
7
tests/html/test_focus.html
Normal file
7
tests/html/test_focus.html
Normal file
|
@ -0,0 +1,7 @@
|
|||
<body>
|
||||
<input id="focused">
|
||||
<script>
|
||||
document.body.addEventListener('keydown', function() { alert("body"); }, false);
|
||||
document.getElementById('focused').addEventListener('keydown', function() { alert("input"); }, false);
|
||||
</script>
|
||||
</body>
|
Loading…
Add table
Add a link
Reference in a new issue