mirror of
https://github.com/servo/servo.git
synced 2025-08-02 04:00:32 +01:00
Handle multi-touch events from glutin
This commit is contained in:
parent
5c11c88e92
commit
ef93650db9
7 changed files with 227 additions and 102 deletions
|
@ -35,8 +35,9 @@ use msg::constellation_msg::{NavigationDirection, PipelineId, WindowSizeData};
|
||||||
use pipeline::CompositionPipeline;
|
use pipeline::CompositionPipeline;
|
||||||
use profile_traits::mem::{self, ReportKind, Reporter, ReporterRequest};
|
use profile_traits::mem::{self, ReportKind, Reporter, ReporterRequest};
|
||||||
use profile_traits::time::{self, ProfilerCategory, profile};
|
use profile_traits::time::{self, ProfilerCategory, profile};
|
||||||
use script_traits::CompositorEvent::{TouchDownEvent, TouchMoveEvent, TouchUpEvent};
|
use script_traits::CompositorEvent::TouchEvent;
|
||||||
use script_traits::{ConstellationControlMsg, LayoutControlMsg, MouseButton};
|
use script_traits::{ConstellationControlMsg, LayoutControlMsg, MouseButton};
|
||||||
|
use script_traits::{TouchEventType, TouchId};
|
||||||
use scrolling::ScrollingTimerProxy;
|
use scrolling::ScrollingTimerProxy;
|
||||||
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
@ -72,23 +73,6 @@ enum ReadyState {
|
||||||
ReadyToSaveImage,
|
ReadyToSaveImage,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The states of the touch input state machine.
|
|
||||||
///
|
|
||||||
/// TODO: Currently Add support for "flinging" (scrolling inertia), pinch zooming, better
|
|
||||||
/// support for multiple touch points.
|
|
||||||
enum TouchState {
|
|
||||||
/// Not tracking any touch point
|
|
||||||
Nothing,
|
|
||||||
/// A touchstart event was dispatched to the page, but the response wasn't received yet.
|
|
||||||
WaitingForScript,
|
|
||||||
/// Script is consuming the current touch point; don't perform default actions.
|
|
||||||
DefaultPrevented,
|
|
||||||
/// A single touch point is active and may perform click or pan default actions.
|
|
||||||
Touching,
|
|
||||||
/// A single touch point is active and has started panning.
|
|
||||||
Panning,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// NB: Never block on the constellation, because sometimes the constellation blocks on us.
|
/// NB: Never block on the constellation, because sometimes the constellation blocks on us.
|
||||||
pub struct IOCompositor<Window: WindowMethods> {
|
pub struct IOCompositor<Window: WindowMethods> {
|
||||||
/// The application window.
|
/// The application window.
|
||||||
|
@ -201,6 +185,27 @@ pub struct IOCompositor<Window: WindowMethods> {
|
||||||
pending_subpages: HashSet<PipelineId>,
|
pending_subpages: HashSet<PipelineId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The states of the touch input state machine.
|
||||||
|
///
|
||||||
|
/// TODO: Currently Add support for "flinging" (scrolling inertia), pinch zooming.
|
||||||
|
enum TouchState {
|
||||||
|
/// Not tracking any touch point
|
||||||
|
Nothing,
|
||||||
|
/// A touchstart event was dispatched to the page, but the response wasn't received yet.
|
||||||
|
/// Contains the number of active touch points.
|
||||||
|
WaitingForScript(u32),
|
||||||
|
/// Script is consuming the current touch sequence; don't perform default actions.
|
||||||
|
/// Contains the number of active touch points.
|
||||||
|
DefaultPrevented(u32),
|
||||||
|
/// A single touch point is active and may perform click or pan default actions.
|
||||||
|
Touching,
|
||||||
|
/// A single touch point is active and has started panning.
|
||||||
|
Panning,
|
||||||
|
/// A multi-touch gesture is in progress. Contains the number of active touch points.
|
||||||
|
MultiTouch(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub struct ScrollEvent {
|
pub struct ScrollEvent {
|
||||||
delta: TypedPoint2D<DevicePixel, f32>,
|
delta: TypedPoint2D<DevicePixel, f32>,
|
||||||
cursor: TypedPoint2D<DevicePixel, i32>,
|
cursor: TypedPoint2D<DevicePixel, i32>,
|
||||||
|
@ -537,10 +542,14 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
||||||
|
|
||||||
(Msg::TouchEventProcessed(result), ShutdownState::NotShuttingDown) => {
|
(Msg::TouchEventProcessed(result), ShutdownState::NotShuttingDown) => {
|
||||||
match self.touch_gesture_state {
|
match self.touch_gesture_state {
|
||||||
TouchState::WaitingForScript => {
|
TouchState::WaitingForScript(n) => {
|
||||||
self.touch_gesture_state = match result {
|
self.touch_gesture_state = match result {
|
||||||
EventResult::DefaultAllowed => TouchState::Touching,
|
EventResult::DefaultPrevented => TouchState::DefaultPrevented(n),
|
||||||
EventResult::DefaultPrevented => TouchState::DefaultPrevented,
|
EventResult::DefaultAllowed => if n > 1 {
|
||||||
|
TouchState::MultiTouch(n)
|
||||||
|
} else {
|
||||||
|
TouchState::Touching
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -1056,6 +1065,15 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
||||||
self.on_mouse_window_move_event_class(cursor);
|
self.on_mouse_window_move_event_class(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WindowEvent::Touch(event_type, identifier, location) => {
|
||||||
|
match event_type {
|
||||||
|
TouchEventType::Down => self.on_touch_down(identifier, location),
|
||||||
|
TouchEventType::Move => self.on_touch_move(identifier, location),
|
||||||
|
TouchEventType::Up => self.on_touch_up(identifier, location),
|
||||||
|
TouchEventType::Cancel => self.on_touch_cancel(identifier, location),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
WindowEvent::Scroll(delta, cursor) => {
|
WindowEvent::Scroll(delta, cursor) => {
|
||||||
self.on_scroll_window_event(delta, cursor);
|
self.on_scroll_window_event(delta, cursor);
|
||||||
}
|
}
|
||||||
|
@ -1127,8 +1145,8 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
||||||
if opts::get().convert_mouse_to_touch {
|
if opts::get().convert_mouse_to_touch {
|
||||||
match mouse_window_event {
|
match mouse_window_event {
|
||||||
MouseWindowEvent::Click(_, _) => {}
|
MouseWindowEvent::Click(_, _) => {}
|
||||||
MouseWindowEvent::MouseDown(_, p) => self.on_touch_down(0, p),
|
MouseWindowEvent::MouseDown(_, p) => self.on_touch_down(TouchId(0), p),
|
||||||
MouseWindowEvent::MouseUp(_, p) => self.on_touch_up(0, p),
|
MouseWindowEvent::MouseUp(_, p) => self.on_touch_up(TouchId(0), p),
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1146,7 +1164,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
||||||
|
|
||||||
fn on_mouse_window_move_event_class(&mut self, cursor: TypedPoint2D<DevicePixel, f32>) {
|
fn on_mouse_window_move_event_class(&mut self, cursor: TypedPoint2D<DevicePixel, f32>) {
|
||||||
if opts::get().convert_mouse_to_touch {
|
if opts::get().convert_mouse_to_touch {
|
||||||
self.on_touch_move(0, cursor);
|
self.on_touch_move(TouchId(0), cursor);
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1156,31 +1174,40 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_touch_down(&mut self, identifier: i32, point: TypedPoint2D<DevicePixel, f32>) {
|
fn on_touch_down(&mut self, identifier: TouchId, point: TypedPoint2D<DevicePixel, f32>) {
|
||||||
match self.touch_gesture_state {
|
self.touch_gesture_state = match self.touch_gesture_state {
|
||||||
TouchState::Nothing => {
|
TouchState::Nothing => {
|
||||||
// TODO: Don't wait for script if we know the page has no touch event listeners.
|
// TODO: Don't wait for script if we know the page has no touch event listeners.
|
||||||
self.first_touch_point = Some(point);
|
self.first_touch_point = Some(point);
|
||||||
self.last_touch_point = Some(point);
|
self.last_touch_point = Some(point);
|
||||||
self.touch_gesture_state = TouchState::WaitingForScript;
|
TouchState::WaitingForScript(1)
|
||||||
}
|
}
|
||||||
TouchState::WaitingForScript => {
|
TouchState::Touching |
|
||||||
// TODO: Queue events while waiting for script?
|
TouchState::Panning => {
|
||||||
|
// Was a single-touch sequence; now a multi-touch sequence:
|
||||||
|
TouchState::MultiTouch(2)
|
||||||
}
|
}
|
||||||
TouchState::DefaultPrevented => {}
|
TouchState::WaitingForScript(n) => {
|
||||||
TouchState::Touching => {}
|
TouchState::WaitingForScript(n + 1)
|
||||||
TouchState::Panning => {}
|
}
|
||||||
}
|
TouchState::DefaultPrevented(n) => {
|
||||||
|
TouchState::DefaultPrevented(n + 1)
|
||||||
|
}
|
||||||
|
TouchState::MultiTouch(n) => {
|
||||||
|
TouchState::MultiTouch(n + 1)
|
||||||
|
}
|
||||||
|
};
|
||||||
if let Some(result) = self.find_topmost_layer_at_point(point / self.scene.scale) {
|
if let Some(result) = self.find_topmost_layer_at_point(point / self.scene.scale) {
|
||||||
result.layer.send_event(self, TouchDownEvent(identifier, result.point.to_untyped()));
|
result.layer.send_event(self, TouchEvent(TouchEventType::Down, identifier,
|
||||||
|
result.point.to_untyped()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_touch_move(&mut self, identifier: i32, point: TypedPoint2D<DevicePixel, f32>) {
|
fn on_touch_move(&mut self, identifier: TouchId, point: TypedPoint2D<DevicePixel, f32>) {
|
||||||
match self.touch_gesture_state {
|
match self.touch_gesture_state {
|
||||||
TouchState::Nothing => warn!("Got unexpected touch move event"),
|
TouchState::Nothing => warn!("Got unexpected touch move event"),
|
||||||
|
|
||||||
TouchState::WaitingForScript => {
|
TouchState::WaitingForScript(_) => {
|
||||||
// TODO: Queue events while waiting for script?
|
// TODO: Queue events while waiting for script?
|
||||||
}
|
}
|
||||||
TouchState::Touching => {
|
TouchState::Touching => {
|
||||||
|
@ -1209,40 +1236,67 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
||||||
None => warn!("last_touch_point not set")
|
None => warn!("last_touch_point not set")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TouchState::DefaultPrevented => {
|
TouchState::DefaultPrevented(_) => {
|
||||||
// Send the event to script.
|
// Send the event to script.
|
||||||
if let Some(result) = self.find_topmost_layer_at_point(point / self.scene.scale) {
|
if let Some(result) = self.find_topmost_layer_at_point(point / self.scene.scale) {
|
||||||
result.layer.send_event(self,
|
result.layer.send_event(self, TouchEvent(TouchEventType::Move, identifier,
|
||||||
TouchMoveEvent(identifier, result.point.to_untyped()));
|
result.point.to_untyped()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
TouchState::MultiTouch(_) => {
|
||||||
|
// TODO: Pinch zooming.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.last_touch_point = Some(point);
|
self.last_touch_point = Some(point);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_touch_up(&mut self, identifier: i32, point: TypedPoint2D<DevicePixel, f32>) {
|
fn on_touch_up(&mut self, identifier: TouchId, point: TypedPoint2D<DevicePixel, f32>) {
|
||||||
// TODO: Track the number of active touch points, and don't reset stuff until it is zero.
|
|
||||||
self.first_touch_point = None;
|
|
||||||
self.last_touch_point = None;
|
|
||||||
|
|
||||||
// Send the event to script.
|
// Send the event to script.
|
||||||
if let Some(result) = self.find_topmost_layer_at_point(point / self.scene.scale) {
|
if let Some(result) = self.find_topmost_layer_at_point(point / self.scene.scale) {
|
||||||
result.layer.send_event(self, TouchUpEvent(identifier, result.point.to_untyped()));
|
result.layer.send_event(self, TouchEvent(TouchEventType::Up, identifier,
|
||||||
|
result.point.to_untyped()));
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.touch_gesture_state {
|
self.touch_gesture_state = match self.touch_gesture_state {
|
||||||
TouchState::Nothing => warn!("Got unexpected touch up event"),
|
|
||||||
|
|
||||||
TouchState::WaitingForScript => {}
|
|
||||||
TouchState::Touching => {
|
TouchState::Touching => {
|
||||||
// TODO: If the duration exceeds some threshold, send a contextmenu event instead.
|
// TODO: If the duration exceeds some threshold, send a contextmenu event instead.
|
||||||
// TODO: Don't send a click if preventDefault is called on the touchend event.
|
// TODO: Don't send a click if preventDefault is called on the touchend event.
|
||||||
self.simulate_mouse_click(point);
|
self.simulate_mouse_click(point);
|
||||||
|
TouchState::Nothing
|
||||||
}
|
}
|
||||||
TouchState::Panning => {}
|
TouchState::Nothing |
|
||||||
TouchState::DefaultPrevented => {}
|
TouchState::Panning |
|
||||||
|
TouchState::WaitingForScript(1) |
|
||||||
|
TouchState::DefaultPrevented(1) |
|
||||||
|
TouchState::MultiTouch(1) => {
|
||||||
|
TouchState::Nothing
|
||||||
|
}
|
||||||
|
TouchState::WaitingForScript(n) => TouchState::WaitingForScript(n - 1),
|
||||||
|
TouchState::DefaultPrevented(n) => TouchState::DefaultPrevented(n - 1),
|
||||||
|
TouchState::MultiTouch(n) => TouchState::MultiTouch(n - 1),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_touch_cancel(&mut self, identifier: TouchId, point: TypedPoint2D<DevicePixel, f32>) {
|
||||||
|
// Send the event to script.
|
||||||
|
if let Some(result) = self.find_topmost_layer_at_point(point / self.scene.scale) {
|
||||||
|
result.layer.send_event(self, TouchEvent(TouchEventType::Cancel, identifier,
|
||||||
|
result.point.to_untyped()));
|
||||||
}
|
}
|
||||||
self.touch_gesture_state = TouchState::Nothing;
|
|
||||||
|
self.touch_gesture_state = match self.touch_gesture_state {
|
||||||
|
TouchState::Nothing |
|
||||||
|
TouchState::Touching |
|
||||||
|
TouchState::Panning |
|
||||||
|
TouchState::WaitingForScript(1) |
|
||||||
|
TouchState::DefaultPrevented(1) |
|
||||||
|
TouchState::MultiTouch(1) => {
|
||||||
|
TouchState::Nothing
|
||||||
|
}
|
||||||
|
TouchState::WaitingForScript(n) => TouchState::WaitingForScript(n - 1),
|
||||||
|
TouchState::DefaultPrevented(n) => TouchState::DefaultPrevented(n - 1),
|
||||||
|
TouchState::MultiTouch(n) => TouchState::MultiTouch(n - 1),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// http://w3c.github.io/touch-events/#mouse-events
|
/// http://w3c.github.io/touch-events/#mouse-events
|
||||||
|
|
|
@ -13,7 +13,7 @@ use layers::geometry::DevicePixel;
|
||||||
use layers::platform::surface::NativeDisplay;
|
use layers::platform::surface::NativeDisplay;
|
||||||
use msg::constellation_msg::{Key, KeyModifiers, KeyState};
|
use msg::constellation_msg::{Key, KeyModifiers, KeyState};
|
||||||
use net_traits::net_error_list::NetError;
|
use net_traits::net_error_list::NetError;
|
||||||
use script_traits::MouseButton;
|
use script_traits::{MouseButton, TouchEventType, TouchId};
|
||||||
use std::fmt::{Debug, Error, Formatter};
|
use std::fmt::{Debug, Error, Formatter};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
@ -27,6 +27,7 @@ pub enum MouseWindowEvent {
|
||||||
MouseUp(MouseButton, TypedPoint2D<DevicePixel, f32>),
|
MouseUp(MouseButton, TypedPoint2D<DevicePixel, f32>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum WindowNavigateMsg {
|
pub enum WindowNavigateMsg {
|
||||||
Forward,
|
Forward,
|
||||||
|
@ -59,6 +60,8 @@ pub enum WindowEvent {
|
||||||
MouseWindowEventClass(MouseWindowEvent),
|
MouseWindowEventClass(MouseWindowEvent),
|
||||||
/// Sent when a mouse move.
|
/// Sent when a mouse move.
|
||||||
MouseWindowMoveEventClass(TypedPoint2D<DevicePixel, f32>),
|
MouseWindowMoveEventClass(TypedPoint2D<DevicePixel, f32>),
|
||||||
|
/// Touch event: type, identifier, point
|
||||||
|
Touch(TouchEventType, TouchId, TypedPoint2D<DevicePixel, f32>),
|
||||||
/// Sent when the user scrolls. The first point is the delta and the second point is the
|
/// Sent when the user scrolls. The first point is the delta and the second point is the
|
||||||
/// origin.
|
/// origin.
|
||||||
Scroll(TypedPoint2D<DevicePixel, f32>, TypedPoint2D<DevicePixel, i32>),
|
Scroll(TypedPoint2D<DevicePixel, f32>, TypedPoint2D<DevicePixel, i32>),
|
||||||
|
@ -88,6 +91,7 @@ impl Debug for WindowEvent {
|
||||||
WindowEvent::LoadUrl(..) => write!(f, "LoadUrl"),
|
WindowEvent::LoadUrl(..) => write!(f, "LoadUrl"),
|
||||||
WindowEvent::MouseWindowEventClass(..) => write!(f, "Mouse"),
|
WindowEvent::MouseWindowEventClass(..) => write!(f, "Mouse"),
|
||||||
WindowEvent::MouseWindowMoveEventClass(..) => write!(f, "MouseMove"),
|
WindowEvent::MouseWindowMoveEventClass(..) => write!(f, "MouseMove"),
|
||||||
|
WindowEvent::Touch(..) => write!(f, "Touch"),
|
||||||
WindowEvent::Scroll(..) => write!(f, "Scroll"),
|
WindowEvent::Scroll(..) => write!(f, "Scroll"),
|
||||||
WindowEvent::Zoom(..) => write!(f, "Zoom"),
|
WindowEvent::Zoom(..) => write!(f, "Zoom"),
|
||||||
WindowEvent::PinchZoom(..) => write!(f, "PinchZoom"),
|
WindowEvent::PinchZoom(..) => write!(f, "PinchZoom"),
|
||||||
|
|
|
@ -13,6 +13,7 @@ use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods;
|
||||||
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
|
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
|
||||||
use dom::bindings::codegen::Bindings::NodeFilterBinding::NodeFilter;
|
use dom::bindings::codegen::Bindings::NodeFilterBinding::NodeFilter;
|
||||||
use dom::bindings::codegen::Bindings::PerformanceBinding::PerformanceMethods;
|
use dom::bindings::codegen::Bindings::PerformanceBinding::PerformanceMethods;
|
||||||
|
use dom::bindings::codegen::Bindings::TouchBinding::TouchMethods;
|
||||||
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
||||||
use dom::bindings::codegen::InheritTypes::{ElementTypeId, HTMLElementTypeId, NodeTypeId};
|
use dom::bindings::codegen::InheritTypes::{ElementTypeId, HTMLElementTypeId, NodeTypeId};
|
||||||
use dom::bindings::codegen::UnionTypes::NodeOrString;
|
use dom::bindings::codegen::UnionTypes::NodeOrString;
|
||||||
|
@ -84,7 +85,7 @@ use net_traits::CookieSource::NonHTTP;
|
||||||
use net_traits::{AsyncResponseTarget, PendingAsyncLoad};
|
use net_traits::{AsyncResponseTarget, PendingAsyncLoad};
|
||||||
use num::ToPrimitive;
|
use num::ToPrimitive;
|
||||||
use script_task::{MainThreadScriptMsg, Runnable};
|
use script_task::{MainThreadScriptMsg, Runnable};
|
||||||
use script_traits::{MouseButton, UntrustedNodeAddress};
|
use script_traits::{MouseButton, TouchEventType, TouchId, UntrustedNodeAddress};
|
||||||
use selectors::states::*;
|
use selectors::states::*;
|
||||||
use std::ascii::AsciiExt;
|
use std::ascii::AsciiExt;
|
||||||
use std::borrow::ToOwned;
|
use std::borrow::ToOwned;
|
||||||
|
@ -175,8 +176,10 @@ pub struct Document {
|
||||||
/// This field is set to the document itself for inert documents.
|
/// This field is set to the document itself for inert documents.
|
||||||
/// https://html.spec.whatwg.org/multipage/#appropriate-template-contents-owner-document
|
/// https://html.spec.whatwg.org/multipage/#appropriate-template-contents-owner-document
|
||||||
appropriate_template_contents_owner_document: MutNullableHeap<JS<Document>>,
|
appropriate_template_contents_owner_document: MutNullableHeap<JS<Document>>,
|
||||||
// The collection of ElementStates that have been changed since the last restyle.
|
/// The collection of ElementStates that have been changed since the last restyle.
|
||||||
element_state_changes: DOMRefCell<HashMap<JS<Element>, ElementState>>,
|
element_state_changes: DOMRefCell<HashMap<JS<Element>, ElementState>>,
|
||||||
|
/// http://w3c.github.io/touch-events/#dfn-active-touch-point
|
||||||
|
active_touch_points: DOMRefCell<Vec<JS<Touch>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Document {
|
impl PartialEq for Document {
|
||||||
|
@ -728,9 +731,17 @@ impl Document {
|
||||||
|
|
||||||
pub fn handle_touch_event(&self,
|
pub fn handle_touch_event(&self,
|
||||||
js_runtime: *mut JSRuntime,
|
js_runtime: *mut JSRuntime,
|
||||||
identifier: i32,
|
event_type: TouchEventType,
|
||||||
point: Point2D<f32>,
|
TouchId(identifier): TouchId,
|
||||||
event_name: String) -> bool {
|
point: Point2D<f32>)
|
||||||
|
-> bool {
|
||||||
|
let event_name = match event_type {
|
||||||
|
TouchEventType::Down => "touchstart",
|
||||||
|
TouchEventType::Move => "touchmove",
|
||||||
|
TouchEventType::Up => "touchend",
|
||||||
|
TouchEventType::Cancel => "touchcancel",
|
||||||
|
};
|
||||||
|
|
||||||
let node = match self.hit_test(&point) {
|
let node = match self.hit_test(&point) {
|
||||||
Some(node_address) => node::from_untrusted_node_address(js_runtime, node_address),
|
Some(node_address) => node::from_untrusted_node_address(js_runtime, node_address),
|
||||||
None => return false
|
None => return false
|
||||||
|
@ -745,7 +756,7 @@ impl Document {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let target = el.upcast::<EventTarget>();
|
let target = Root::upcast::<EventTarget>(el);
|
||||||
let window = &*self.window;
|
let window = &*self.window;
|
||||||
|
|
||||||
let client_x = Finite::wrap(point.x as f64);
|
let client_x = Finite::wrap(point.x as f64);
|
||||||
|
@ -753,26 +764,58 @@ impl Document {
|
||||||
let page_x = Finite::wrap(point.x as f64 + window.PageXOffset() as f64);
|
let page_x = Finite::wrap(point.x as f64 + window.PageXOffset() as f64);
|
||||||
let page_y = Finite::wrap(point.y as f64 + window.PageYOffset() as f64);
|
let page_y = Finite::wrap(point.y as f64 + window.PageYOffset() as f64);
|
||||||
|
|
||||||
let touch = Touch::new(window, identifier, target,
|
let touch = Touch::new(window, identifier, target.r(),
|
||||||
client_x, client_y, // TODO: Get real screen coordinates?
|
client_x, client_y, // TODO: Get real screen coordinates?
|
||||||
client_x, client_y,
|
client_x, client_y,
|
||||||
page_x, page_y);
|
page_x, page_y);
|
||||||
|
|
||||||
|
match event_type {
|
||||||
|
TouchEventType::Down => {
|
||||||
|
// Add a new touch point
|
||||||
|
self.active_touch_points.borrow_mut().push(JS::from_rooted(&touch));
|
||||||
|
}
|
||||||
|
TouchEventType::Move => {
|
||||||
|
// Replace an existing touch point
|
||||||
|
let mut active_touch_points = self.active_touch_points.borrow_mut();
|
||||||
|
match active_touch_points.iter_mut().find(|t| t.Identifier() == identifier) {
|
||||||
|
Some(t) => *t = JS::from_rooted(&touch),
|
||||||
|
None => warn!("Got a touchmove event for a non-active touch point")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TouchEventType::Up |
|
||||||
|
TouchEventType::Cancel => {
|
||||||
|
// Remove an existing touch point
|
||||||
|
let mut active_touch_points = self.active_touch_points.borrow_mut();
|
||||||
|
match active_touch_points.iter().position(|t| t.Identifier() == identifier) {
|
||||||
|
Some(i) => { active_touch_points.swap_remove(i); }
|
||||||
|
None => warn!("Got a touchend event for a non-active touch point")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut touches = RootedVec::new();
|
let mut touches = RootedVec::new();
|
||||||
touches.push(JS::from_rooted(&touch));
|
touches.extend(self.active_touch_points.borrow().iter().cloned());
|
||||||
let touches = TouchList::new(window, touches.r());
|
|
||||||
|
let mut changed_touches = RootedVec::new();
|
||||||
|
changed_touches.push(JS::from_rooted(&touch));
|
||||||
|
|
||||||
|
let mut target_touches = RootedVec::new();
|
||||||
|
target_touches.extend(self.active_touch_points.borrow().iter().filter(
|
||||||
|
|t| t.Target() == target).cloned());
|
||||||
|
|
||||||
let event = TouchEvent::new(window,
|
let event = TouchEvent::new(window,
|
||||||
event_name,
|
event_name.to_owned(),
|
||||||
EventBubbles::Bubbles,
|
EventBubbles::Bubbles,
|
||||||
EventCancelable::Cancelable,
|
EventCancelable::Cancelable,
|
||||||
Some(window),
|
Some(window),
|
||||||
0i32,
|
0i32,
|
||||||
&touches, &touches, &touches,
|
&TouchList::new(window, touches.r()),
|
||||||
|
&TouchList::new(window, changed_touches.r()),
|
||||||
|
&TouchList::new(window, target_touches.r()),
|
||||||
// FIXME: modifier keys
|
// FIXME: modifier keys
|
||||||
false, false, false, false);
|
false, false, false, false);
|
||||||
let event = event.upcast::<Event>();
|
let event = event.upcast::<Event>();
|
||||||
let result = event.fire(target);
|
let result = event.fire(target.r());
|
||||||
|
|
||||||
window.reflow(ReflowGoal::ForDisplay,
|
window.reflow(ReflowGoal::ForDisplay,
|
||||||
ReflowQueryType::NoQuery,
|
ReflowQueryType::NoQuery,
|
||||||
|
@ -1269,6 +1312,7 @@ impl Document {
|
||||||
base_element: Default::default(),
|
base_element: Default::default(),
|
||||||
appropriate_template_contents_owner_document: Default::default(),
|
appropriate_template_contents_owner_document: Default::default(),
|
||||||
element_state_changes: DOMRefCell::new(HashMap::new()),
|
element_state_changes: DOMRefCell::new(HashMap::new()),
|
||||||
|
active_touch_points: DOMRefCell::new(Vec::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,12 +78,12 @@ use profile_traits::mem::{self, OpaqueSender, Report, ReportKind, ReportsChan};
|
||||||
use profile_traits::time::{self, ProfilerCategory, profile};
|
use profile_traits::time::{self, ProfilerCategory, profile};
|
||||||
use script_traits::CompositorEvent::{ClickEvent, ResizeEvent};
|
use script_traits::CompositorEvent::{ClickEvent, ResizeEvent};
|
||||||
use script_traits::CompositorEvent::{KeyEvent, MouseMoveEvent};
|
use script_traits::CompositorEvent::{KeyEvent, MouseMoveEvent};
|
||||||
use script_traits::CompositorEvent::{MouseDownEvent, MouseUpEvent};
|
use script_traits::CompositorEvent::{MouseDownEvent, MouseUpEvent, TouchEvent};
|
||||||
use script_traits::CompositorEvent::{TouchDownEvent, TouchMoveEvent, TouchUpEvent};
|
|
||||||
use script_traits::{CompositorEvent, ConstellationControlMsg};
|
use script_traits::{CompositorEvent, ConstellationControlMsg};
|
||||||
use script_traits::{InitialScriptState, MouseButton, NewLayoutInfo};
|
use script_traits::{InitialScriptState, MouseButton, NewLayoutInfo};
|
||||||
use script_traits::{OpaqueScriptLayoutChannel, ScriptState, ScriptTaskFactory};
|
use script_traits::{OpaqueScriptLayoutChannel, ScriptState, ScriptTaskFactory};
|
||||||
use script_traits::{TimerEvent, TimerEventChan, TimerEventRequest, TimerSource};
|
use script_traits::{TimerEvent, TimerEventChan, TimerEventRequest, TimerSource};
|
||||||
|
use script_traits::{TouchEventType, TouchId};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::borrow::ToOwned;
|
use std::borrow::ToOwned;
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
|
@ -1764,27 +1764,27 @@ impl ScriptTask {
|
||||||
std_mem::swap(&mut *self.mouse_over_targets.borrow_mut(), &mut *mouse_over_targets);
|
std_mem::swap(&mut *self.mouse_over_targets.borrow_mut(), &mut *mouse_over_targets);
|
||||||
}
|
}
|
||||||
|
|
||||||
TouchDownEvent(identifier, point) => {
|
TouchEvent(event_type, identifier, point) => {
|
||||||
let default_action_allowed =
|
let handled = self.handle_touch_event(pipeline_id, event_type, identifier, point);
|
||||||
self.handle_touch_event(pipeline_id, identifier, point, "touchstart");
|
match event_type {
|
||||||
if default_action_allowed {
|
TouchEventType::Down => {
|
||||||
// TODO: Wait to see if preventDefault is called on the first touchmove event.
|
if handled {
|
||||||
self.compositor.borrow_mut().send(ScriptToCompositorMsg::TouchEventProcessed(
|
// TODO: Wait to see if preventDefault is called on the first touchmove event.
|
||||||
EventResult::DefaultAllowed)).unwrap();
|
self.compositor.borrow_mut()
|
||||||
} else {
|
.send(ScriptToCompositorMsg::TouchEventProcessed(
|
||||||
self.compositor.borrow_mut().send(ScriptToCompositorMsg::TouchEventProcessed(
|
EventResult::DefaultAllowed)).unwrap();
|
||||||
EventResult::DefaultPrevented)).unwrap();
|
} else {
|
||||||
|
self.compositor.borrow_mut()
|
||||||
|
.send(ScriptToCompositorMsg::TouchEventProcessed(
|
||||||
|
EventResult::DefaultPrevented)).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// TODO: Calling preventDefault on a touchup event should prevent clicks.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TouchMoveEvent(identifier, point) => {
|
|
||||||
self.handle_touch_event(pipeline_id, identifier, point, "touchmove");
|
|
||||||
}
|
|
||||||
|
|
||||||
TouchUpEvent(identifier, point) => {
|
|
||||||
self.handle_touch_event(pipeline_id, identifier, point, "touchend");
|
|
||||||
}
|
|
||||||
|
|
||||||
KeyEvent(key, state, modifiers) => {
|
KeyEvent(key, state, modifiers) => {
|
||||||
let page = get_page(&self.root_page(), pipeline_id);
|
let page = get_page(&self.root_page(), pipeline_id);
|
||||||
let document = page.document();
|
let document = page.document();
|
||||||
|
@ -1806,13 +1806,13 @@ impl ScriptTask {
|
||||||
|
|
||||||
fn handle_touch_event(&self,
|
fn handle_touch_event(&self,
|
||||||
pipeline_id: PipelineId,
|
pipeline_id: PipelineId,
|
||||||
identifier: i32,
|
event_type: TouchEventType,
|
||||||
point: Point2D<f32>,
|
identifier: TouchId,
|
||||||
event_name: &str) -> bool {
|
point: Point2D<f32>)
|
||||||
|
-> bool {
|
||||||
let page = get_page(&self.root_page(), pipeline_id);
|
let page = get_page(&self.root_page(), pipeline_id);
|
||||||
let document = page.document();
|
let document = page.document();
|
||||||
document.r().handle_touch_event(self.js_runtime.rt(), identifier, point,
|
document.r().handle_touch_event(self.js_runtime.rt(), event_type, identifier, point)
|
||||||
event_name.to_owned())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// https://html.spec.whatwg.org/multipage/#navigating-across-documents
|
/// https://html.spec.whatwg.org/multipage/#navigating-across-documents
|
||||||
|
|
|
@ -158,6 +158,25 @@ pub enum MouseButton {
|
||||||
Right,
|
Right,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The type of input represented by a multi-touch event.
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum TouchEventType {
|
||||||
|
/// A new touch point came in contact with the screen.
|
||||||
|
Down,
|
||||||
|
/// An existing touch point changed location.
|
||||||
|
Move,
|
||||||
|
/// A touch point was removed from the screen.
|
||||||
|
Up,
|
||||||
|
/// The system stopped tracking a touch point.
|
||||||
|
Cancel,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An opaque identifier for a touch point.
|
||||||
|
///
|
||||||
|
/// http://w3c.github.io/touch-events/#widl-Touch-identifier
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub struct TouchId(pub i32);
|
||||||
|
|
||||||
/// Events from the compositor that the script task needs to know about
|
/// Events from the compositor that the script task needs to know about
|
||||||
pub enum CompositorEvent {
|
pub enum CompositorEvent {
|
||||||
/// The window was resized.
|
/// The window was resized.
|
||||||
|
@ -170,12 +189,8 @@ pub enum CompositorEvent {
|
||||||
MouseUpEvent(MouseButton, Point2D<f32>),
|
MouseUpEvent(MouseButton, Point2D<f32>),
|
||||||
/// The mouse was moved over a point.
|
/// The mouse was moved over a point.
|
||||||
MouseMoveEvent(Point2D<f32>),
|
MouseMoveEvent(Point2D<f32>),
|
||||||
/// A touch began at a point.
|
/// A touch event was generated with a touch ID and location.
|
||||||
TouchDownEvent(i32, Point2D<f32>),
|
TouchEvent(TouchEventType, TouchId, Point2D<f32>),
|
||||||
/// A touch was moved over a point.
|
|
||||||
TouchMoveEvent(i32, Point2D<f32>),
|
|
||||||
/// A touch ended at a point.
|
|
||||||
TouchUpEvent(i32, Point2D<f32>),
|
|
||||||
/// A key was pressed.
|
/// A key was pressed.
|
||||||
KeyEvent(Key, KeyState, KeyModifiers),
|
KeyEvent(Key, KeyState, KeyModifiers),
|
||||||
}
|
}
|
||||||
|
|
|
@ -272,12 +272,6 @@ impl DebugOptions {
|
||||||
pub fn new(debug_string: &str) -> Result<DebugOptions, &str> {
|
pub fn new(debug_string: &str) -> Result<DebugOptions, &str> {
|
||||||
let mut debug_options = DebugOptions::default();
|
let mut debug_options = DebugOptions::default();
|
||||||
|
|
||||||
// FIXME: Glutin currently converts touch input to mouse events on Android.
|
|
||||||
// Convert it back to touch events.
|
|
||||||
if cfg!(target_os = "android") {
|
|
||||||
debug_options.convert_mouse_to_touch = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for option in debug_string.split(',') {
|
for option in debug_string.split(',') {
|
||||||
match option {
|
match option {
|
||||||
"help" => debug_options.help = true,
|
"help" => debug_options.help = true,
|
||||||
|
|
|
@ -16,6 +16,7 @@ use gleam::gl;
|
||||||
use glutin;
|
use glutin;
|
||||||
#[cfg(feature = "window")]
|
#[cfg(feature = "window")]
|
||||||
use glutin::{Api, ElementState, Event, GlRequest, MouseButton, VirtualKeyCode, MouseScrollDelta};
|
use glutin::{Api, ElementState, Event, GlRequest, MouseButton, VirtualKeyCode, MouseScrollDelta};
|
||||||
|
use glutin::{TouchPhase};
|
||||||
use layers::geometry::DevicePixel;
|
use layers::geometry::DevicePixel;
|
||||||
use layers::platform::surface::NativeDisplay;
|
use layers::platform::surface::NativeDisplay;
|
||||||
#[cfg(feature = "window")]
|
#[cfg(feature = "window")]
|
||||||
|
@ -157,6 +158,8 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_window_event(&self, event: glutin::Event) -> bool {
|
fn handle_window_event(&self, event: glutin::Event) -> bool {
|
||||||
|
use script_traits::{TouchEventType, TouchId};
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
Event::KeyboardInput(element_state, _scan_code, virtual_key_code) => {
|
Event::KeyboardInput(element_state, _scan_code, virtual_key_code) => {
|
||||||
if virtual_key_code.is_some() {
|
if virtual_key_code.is_some() {
|
||||||
|
@ -216,6 +219,17 @@ impl Window {
|
||||||
self.pinch_zoom(factor);
|
self.pinch_zoom(factor);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Event::Touch(touch) => {
|
||||||
|
let phase = match touch.phase {
|
||||||
|
TouchPhase::Started => TouchEventType::Down,
|
||||||
|
TouchPhase::Moved => TouchEventType::Move,
|
||||||
|
TouchPhase::Ended => TouchEventType::Up,
|
||||||
|
TouchPhase::Cancelled => TouchEventType::Cancel,
|
||||||
|
};
|
||||||
|
let id = TouchId(touch.id as i32);
|
||||||
|
let point = Point2D::typed(touch.location.0 as f32, touch.location.1 as f32);
|
||||||
|
self.event_queue.borrow_mut().push(WindowEvent::Touch(phase, id, point));
|
||||||
|
}
|
||||||
Event::Refresh => {
|
Event::Refresh => {
|
||||||
self.event_queue.borrow_mut().push(WindowEvent::Refresh);
|
self.event_queue.borrow_mut().push(WindowEvent::Refresh);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue