Implement WheelEvent Interface

Note: The WheelEvent interface supports rotation in all 3 spatial
dimensions. This implementation only supports two due to limitations
in the Glutin compositor.

The wheelevent interface is a dom interface that triggers for any
attached device that can rotate in one or more spatial dimensions.
Traditionally this is the mouse wheel though other devices could be
used as well. E.g. the trackball on a trackball mouse.
This commit is contained in:
Robert Snakard 2019-02-24 21:24:36 +00:00 committed by Josh Matthews
parent 8f11b52d9a
commit 35bca991ad
16 changed files with 347 additions and 146 deletions

View file

@ -97,6 +97,7 @@ use crate::dom::treewalker::TreeWalker;
use crate::dom::uievent::UIEvent;
use crate::dom::virtualmethods::vtable_for;
use crate::dom::webglcontextevent::WebGLContextEvent;
use crate::dom::wheelevent::WheelEvent;
use crate::dom::window::{ReflowReason, Window};
use crate::dom::windowproxy::WindowProxy;
use crate::fetch::FetchCanceller;
@ -135,7 +136,9 @@ use profile_traits::time::{TimerMetadata, TimerMetadataFrameType, TimerMetadataR
use ref_slice::ref_slice;
use script_layout_interface::message::{Msg, ReflowGoal};
use script_traits::{AnimationState, DocumentActivity, MouseButton, MouseEventType};
use script_traits::{MsDuration, ScriptMsg, TouchEventType, TouchId, UntrustedNodeAddress};
use script_traits::{
MsDuration, ScriptMsg, TouchEventType, TouchId, UntrustedNodeAddress, WheelDelta,
};
use servo_arc::Arc;
use servo_atoms::Atom;
use servo_config::pref;
@ -1218,6 +1221,53 @@ impl Document {
.reflow(ReflowGoal::Full, ReflowReason::MouseEvent);
}
#[allow(unsafe_code)]
pub fn handle_wheel_event(
&self,
js_runtime: *mut JSRuntime,
delta: WheelDelta,
client_point: Point2D<f32>,
node_address: Option<UntrustedNodeAddress>,
) {
let wheel_event_type_string = "wheel".to_owned();
debug!("{}: at {:?}", wheel_event_type_string, client_point);
let el = node_address.and_then(|address| {
let node = unsafe { node::from_untrusted_node_address(js_runtime, address) };
node.inclusive_ancestors(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<Element>)
.next()
});
let el = match el {
Some(el) => el,
None => return,
};
let node = el.upcast::<Node>();
debug!("{}: on {:?}", wheel_event_type_string, node.debug_str());
// https://w3c.github.io/uievents/#event-wheelevents
let event = WheelEvent::new(
&self.window,
DOMString::from(wheel_event_type_string),
EventBubbles::Bubbles,
EventCancelable::Cancelable,
Some(&self.window),
0i32,
Finite::wrap(delta.x),
Finite::wrap(delta.y),
Finite::wrap(delta.z),
delta.mode as u32,
);
let event = event.upcast::<Event>();
event.set_trusted(true);
let target = node.upcast();
event.fire(target);
}
#[allow(unsafe_code)]
pub fn handle_touch_event(
&self,

View file

@ -528,6 +528,7 @@ pub mod webgltexture;
pub mod webgluniformlocation;
pub mod webglvertexarrayobjectoes;
pub mod websocket;
pub mod wheelevent;
pub mod window;
pub mod windowproxy;
pub mod worker;

View file

@ -38,7 +38,7 @@ pub struct MouseEvent {
}
impl MouseEvent {
fn new_inherited() -> MouseEvent {
pub fn new_inherited() -> MouseEvent {
MouseEvent {
uievent: UIEvent::new_inherited(),
screen_x: Cell::new(0),

View file

@ -0,0 +1,33 @@
/* 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 https://mozilla.org/MPL/2.0/. */
// https://w3c.github.io/uievents/#interface-wheelevent
[Constructor(DOMString typeArg, optional WheelEventInit wheelEventInitDict),
Exposed=Window]
interface WheelEvent : MouseEvent {
const unsigned long DOM_DELTA_PIXEL = 0x00;
const unsigned long DOM_DELTA_LINE = 0x01;
const unsigned long DOM_DELTA_PAGE = 0x02;
readonly attribute double deltaX;
readonly attribute double deltaY;
readonly attribute double deltaZ;
readonly attribute unsigned long deltaMode;
};
// https://w3c.github.io/uievents/#idl-wheeleventinit
dictionary WheelEventInit : MouseEventInit {
double deltaX = 0.0;
double deltaY = 0.0;
double deltaZ = 0.0;
unsigned long deltaMode = 0;
};
// https://w3c.github.io/uievents/#idl-interface-WheelEvent-initializers
partial interface WheelEvent {
// Deprecated in DOM Level 3
void initWheelEvent (DOMString typeArg, boolean bubblesArg, boolean cancelableArg,
Window? viewArg, long detailArg,
double deltaX, double deltaY,
double deltaZ, unsigned long deltaMode);
};

View file

@ -0,0 +1,163 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use crate::dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
use crate::dom::bindings::codegen::Bindings::WheelEventBinding;
use crate::dom::bindings::codegen::Bindings::WheelEventBinding::WheelEventMethods;
use crate::dom::bindings::error::Fallible;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::num::Finite;
use crate::dom::bindings::reflector::reflect_dom_object;
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::event::{Event, EventBubbles, EventCancelable};
use crate::dom::mouseevent::MouseEvent;
use crate::dom::window::Window;
use dom_struct::dom_struct;
use std::cell::Cell;
#[dom_struct]
pub struct WheelEvent {
mouseevent: MouseEvent,
delta_x: Cell<Finite<f64>>,
delta_y: Cell<Finite<f64>>,
delta_z: Cell<Finite<f64>>,
delta_mode: Cell<u32>,
}
impl WheelEvent {
fn new_inherited() -> WheelEvent {
WheelEvent {
mouseevent: MouseEvent::new_inherited(),
delta_x: Cell::new(Finite::wrap(0.0)),
delta_y: Cell::new(Finite::wrap(0.0)),
delta_z: Cell::new(Finite::wrap(0.0)),
delta_mode: Cell::new(0),
}
}
pub fn new_unintialized(window: &Window) -> DomRoot<WheelEvent> {
reflect_dom_object(
Box::new(WheelEvent::new_inherited()),
window,
WheelEventBinding::Wrap,
)
}
pub fn new(
window: &Window,
type_: DOMString,
can_bubble: EventBubbles,
cancelable: EventCancelable,
view: Option<&Window>,
detail: i32,
delta_x: Finite<f64>,
delta_y: Finite<f64>,
delta_z: Finite<f64>,
delta_mode: u32,
) -> DomRoot<WheelEvent> {
let ev = WheelEvent::new_unintialized(window);
ev.InitWheelEvent(
type_,
bool::from(can_bubble),
bool::from(cancelable),
view,
detail,
delta_x,
delta_y,
delta_z,
delta_mode,
);
ev
}
pub fn Constructor(
window: &Window,
type_: DOMString,
init: &WheelEventBinding::WheelEventInit,
) -> Fallible<DomRoot<WheelEvent>> {
let event = WheelEvent::new(
window,
type_,
EventBubbles::from(init.parent.parent.parent.parent.bubbles),
EventCancelable::from(init.parent.parent.parent.parent.cancelable),
init.parent.parent.parent.view.deref(),
init.parent.parent.parent.detail,
init.deltaX,
init.deltaY,
init.deltaZ,
init.deltaMode,
);
Ok(event)
}
}
impl WheelEventMethods for WheelEvent {
// https://w3c.github.io/uievents/#widl-WheelEvent-deltaX
fn DeltaX(&self) -> Finite<f64> {
self.delta_x.get()
}
// https://w3c.github.io/uievents/#widl-WheelEvent-deltaY
fn DeltaY(&self) -> Finite<f64> {
self.delta_y.get()
}
// https://w3c.github.io/uievents/#widl-WheelEvent-deltaZ
fn DeltaZ(&self) -> Finite<f64> {
self.delta_z.get()
}
// https://w3c.github.io/uievents/#widl-WheelEvent-deltaMode
fn DeltaMode(&self) -> u32 {
self.delta_mode.get()
}
// https://w3c.github.io/uievents/#widl-WheelEvent-initWheelEvent
fn InitWheelEvent(
&self,
type_arg: DOMString,
can_bubble_arg: bool,
cancelable_arg: bool,
view_arg: Option<&Window>,
detail_arg: i32,
delta_x_arg: Finite<f64>,
delta_y_arg: Finite<f64>,
delta_z_arg: Finite<f64>,
delta_mode_arg: u32,
) {
if self.upcast::<Event>().dispatching() {
return;
}
self.upcast::<MouseEvent>().InitMouseEvent(
type_arg,
can_bubble_arg,
cancelable_arg,
view_arg,
detail_arg,
self.mouseevent.ScreenX(),
self.mouseevent.ScreenY(),
self.mouseevent.ClientX(),
self.mouseevent.ClientY(),
self.mouseevent.CtrlKey(),
self.mouseevent.AltKey(),
self.mouseevent.ShiftKey(),
self.mouseevent.MetaKey(),
self.mouseevent.Button(),
self.mouseevent.GetRelatedTarget().deref(),
);
self.delta_x.set(delta_x_arg);
self.delta_y.set(delta_y_arg);
self.delta_z.set(delta_z_arg);
self.delta_mode.set(delta_mode_arg);
}
// https://dom.spec.whatwg.org/#dom-event-istrusted
fn IsTrusted(&self) -> bool {
self.mouseevent.IsTrusted()
}
}

View file

@ -125,6 +125,7 @@ use script_layout_interface::message::{self, LayoutThreadInit, Msg, ReflowGoal};
use script_traits::webdriver_msg::WebDriverScriptCommand;
use script_traits::CompositorEvent::{
CompositionEvent, KeyboardEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent, TouchEvent,
WheelEvent,
};
use script_traits::{CompositorEvent, ConstellationControlMsg};
use script_traits::{DiscardBrowsingContext, DocumentActivity, EventResult};
@ -132,7 +133,7 @@ use script_traits::{InitialScriptState, JsEvalResult, LayoutMsg, LoadData};
use script_traits::{MouseButton, MouseEventType, NewLayoutInfo};
use script_traits::{Painter, ProgressiveWebMetricType, ScriptMsg, ScriptThreadFactory};
use script_traits::{ScriptToConstellationChan, TimerEvent, TimerSchedulerMsg};
use script_traits::{TimerSource, TouchEventType, TouchId, UntrustedNodeAddress};
use script_traits::{TimerSource, TouchEventType, TouchId, UntrustedNodeAddress, WheelDelta};
use script_traits::{UpdatePipelineIdReason, WindowSizeData, WindowSizeType};
use servo_atoms::Atom;
use servo_config::opts;
@ -3162,6 +3163,10 @@ impl ScriptThread {
}
},
WheelEvent(delta, point, node_address) => {
self.handle_wheel_event(pipeline_id, delta, point, node_address);
},
KeyboardEvent(key_event) => {
let document = match { self.documents.borrow().find_document(pipeline_id) } {
Some(document) => document,
@ -3229,6 +3234,20 @@ impl ScriptThread {
)
}
fn handle_wheel_event(
&self,
pipeline_id: PipelineId,
wheel_delta: WheelDelta,
point: Point2D<f32>,
node_address: Option<UntrustedNodeAddress>,
) {
let document = match { self.documents.borrow().find_document(pipeline_id) } {
Some(document) => document,
None => return warn!("Message sent to closed pipeline {}.", pipeline_id),
};
document.handle_wheel_event(self.js_runtime.rt(), wheel_delta, point, node_address);
}
/// <https://html.spec.whatwg.org/multipage/#navigating-across-documents>
/// The entry point for content to notify that a new load has been requested
/// for the given pipeline (specifically the "navigate" algorithm).