mirror of
https://github.com/servo/servo.git
synced 2025-07-29 18:20:24 +01:00
Auto merge of #9811 - paulrouget:forceTouch, r=mbrubeck
forcetouch events https://developer.apple.com/library/mac/documentation/AppleApplications/Conceptual/SafariJSProgTopics/RespondingtoForceTouchEventsfromJavaScript.html Not sure how we want to land that yet. Maybe reproduce the webkit events (as in this PR), or as touch/mousemouse events. <!-- Reviewable:start --> [<img src="https://reviewable.io/review_button.svg" height="40" alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/9811) <!-- Reviewable:end -->
This commit is contained in:
commit
0ff8adb097
14 changed files with 246 additions and 8 deletions
|
@ -64,7 +64,7 @@ use net_traits::storage_thread::StorageType;
|
|||
use profile_traits::mem::ProfilerChan as MemProfilerChan;
|
||||
use profile_traits::time::ProfilerChan as TimeProfilerChan;
|
||||
use script_thread::ScriptChan;
|
||||
use script_traits::{LayoutMsg, ScriptMsg, TimerEventId, TimerSource, UntrustedNodeAddress};
|
||||
use script_traits::{LayoutMsg, ScriptMsg, TimerEventId, TimerSource, TouchpadPressurePhase, UntrustedNodeAddress};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use smallvec::SmallVec;
|
||||
use std::boxed::FnBox;
|
||||
|
@ -319,6 +319,7 @@ no_jsmanaged_fields!(AttrIdentifier);
|
|||
no_jsmanaged_fields!(AttrValue);
|
||||
no_jsmanaged_fields!(ElementSnapshot);
|
||||
no_jsmanaged_fields!(HttpsState);
|
||||
no_jsmanaged_fields!(TouchpadPressurePhase);
|
||||
|
||||
impl JSTraceable for ConstellationChan<ScriptMsg> {
|
||||
#[inline]
|
||||
|
|
|
@ -41,6 +41,7 @@ use dom::element::{Element, ElementCreator};
|
|||
use dom::event::{Event, EventBubbles, EventCancelable};
|
||||
use dom::eventtarget::EventTarget;
|
||||
use dom::focusevent::FocusEvent;
|
||||
use dom::forcetouchevent::ForceTouchEvent;
|
||||
use dom::htmlanchorelement::HTMLAnchorElement;
|
||||
use dom::htmlappletelement::HTMLAppletElement;
|
||||
use dom::htmlareaelement::HTMLAreaElement;
|
||||
|
@ -97,7 +98,7 @@ use script_thread::{MainThreadScriptChan, MainThreadScriptMsg, Runnable, ScriptC
|
|||
use script_traits::UntrustedNodeAddress;
|
||||
use script_traits::{AnimationState, MouseButton, MouseEventType, MozBrowserEvent};
|
||||
use script_traits::{ScriptMsg as ConstellationMsg, ScriptToCompositorMsg};
|
||||
use script_traits::{TouchEventType, TouchId};
|
||||
use script_traits::{TouchpadPressurePhase, TouchEventType, TouchId};
|
||||
use std::ascii::AsciiExt;
|
||||
use std::borrow::ToOwned;
|
||||
use std::boxed::FnBox;
|
||||
|
@ -218,6 +219,7 @@ pub struct Document {
|
|||
css_errors_store: DOMRefCell<Vec<CSSError>>,
|
||||
/// https://html.spec.whatwg.org/multipage/#concept-document-https-state
|
||||
https_state: Cell<HttpsState>,
|
||||
touchpad_pressure_phase: Cell<TouchpadPressurePhase>,
|
||||
}
|
||||
|
||||
#[derive(JSTraceable, HeapSizeOf)]
|
||||
|
@ -732,6 +734,70 @@ impl Document {
|
|||
ReflowReason::MouseEvent);
|
||||
}
|
||||
|
||||
pub fn handle_touchpad_pressure_event(&self,
|
||||
js_runtime: *mut JSRuntime,
|
||||
client_point: Point2D<f32>,
|
||||
pressure: f32,
|
||||
phase_now: TouchpadPressurePhase) {
|
||||
|
||||
let phase_before = self.touchpad_pressure_phase.get();
|
||||
self.touchpad_pressure_phase.set(phase_now);
|
||||
|
||||
if phase_before == TouchpadPressurePhase::BeforeClick &&
|
||||
phase_now == TouchpadPressurePhase::BeforeClick {
|
||||
return;
|
||||
}
|
||||
|
||||
let page_point = Point2D::new(client_point.x + self.window.PageXOffset() as f32,
|
||||
client_point.y + self.window.PageYOffset() as f32);
|
||||
let node = match self.window.hit_test_query(page_point, false) {
|
||||
Some(node_address) => node::from_untrusted_node_address(js_runtime, node_address),
|
||||
None => return
|
||||
};
|
||||
|
||||
let el = match node.downcast::<Element>() {
|
||||
Some(el) => Root::from_ref(el),
|
||||
None => {
|
||||
let parent = node.GetParentNode();
|
||||
match parent.and_then(Root::downcast::<Element>) {
|
||||
Some(parent) => parent,
|
||||
None => return
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let node = el.upcast::<Node>();
|
||||
let target = node.upcast();
|
||||
|
||||
let force = match phase_now {
|
||||
TouchpadPressurePhase::BeforeClick => pressure,
|
||||
TouchpadPressurePhase::AfterFirstClick => 1. + pressure,
|
||||
TouchpadPressurePhase::AfterSecondClick => 2. + pressure,
|
||||
};
|
||||
|
||||
if phase_now != TouchpadPressurePhase::BeforeClick {
|
||||
self.fire_forcetouch_event("servomouseforcechanged".to_owned(), target, force);
|
||||
}
|
||||
|
||||
if phase_before != TouchpadPressurePhase::AfterSecondClick &&
|
||||
phase_now == TouchpadPressurePhase::AfterSecondClick {
|
||||
self.fire_forcetouch_event("servomouseforcedown".to_owned(), target, force);
|
||||
}
|
||||
|
||||
if phase_before == TouchpadPressurePhase::AfterSecondClick &&
|
||||
phase_now != TouchpadPressurePhase::AfterSecondClick {
|
||||
self.fire_forcetouch_event("servomouseforceup".to_owned(), target, force);
|
||||
}
|
||||
}
|
||||
|
||||
fn fire_forcetouch_event(&self, event_name: String, target: &EventTarget, force: f32) {
|
||||
let force_event = ForceTouchEvent::new(&self.window,
|
||||
DOMString::from(event_name),
|
||||
force);
|
||||
let event = force_event.upcast::<Event>();
|
||||
event.fire(target);
|
||||
}
|
||||
|
||||
pub fn fire_mouse_event(&self, client_point: Point2D<f32>, target: &EventTarget, event_name: String) {
|
||||
let client_x = client_point.x.to_i32().unwrap_or(0);
|
||||
let client_y = client_point.y.to_i32().unwrap_or(0);
|
||||
|
@ -1593,6 +1659,7 @@ impl Document {
|
|||
dom_complete: Cell::new(Default::default()),
|
||||
css_errors_store: DOMRefCell::new(vec![]),
|
||||
https_state: Cell::new(HttpsState::None),
|
||||
touchpad_pressure_phase: Cell::new(TouchpadPressurePhase::BeforeClick),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
59
components/script/dom/forcetouchevent.rs
Normal file
59
components/script/dom/forcetouchevent.rs
Normal file
|
@ -0,0 +1,59 @@
|
|||
/* 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::ForceTouchEventBinding;
|
||||
use dom::bindings::codegen::Bindings::ForceTouchEventBinding::ForceTouchEventMethods;
|
||||
use dom::bindings::codegen::Bindings::UIEventBinding::UIEventMethods;
|
||||
use dom::bindings::global::GlobalRef;
|
||||
use dom::bindings::inheritance::Castable;
|
||||
use dom::bindings::js::{Root};
|
||||
use dom::bindings::num::Finite;
|
||||
use dom::bindings::reflector::reflect_dom_object;
|
||||
use dom::uievent::UIEvent;
|
||||
use dom::window::Window;
|
||||
use util::str::DOMString;
|
||||
|
||||
#[dom_struct]
|
||||
pub struct ForceTouchEvent {
|
||||
uievent: UIEvent,
|
||||
force: f32,
|
||||
}
|
||||
|
||||
impl ForceTouchEvent {
|
||||
fn new_inherited(force: f32) -> ForceTouchEvent {
|
||||
ForceTouchEvent {
|
||||
uievent: UIEvent::new_inherited(),
|
||||
force: force,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(window: &Window,
|
||||
type_: DOMString,
|
||||
force: f32) -> Root<ForceTouchEvent> {
|
||||
let event = box ForceTouchEvent::new_inherited(force);
|
||||
let ev = reflect_dom_object(event, GlobalRef::Window(window), ForceTouchEventBinding::Wrap);
|
||||
ev.upcast::<UIEvent>().InitUIEvent(type_, true, true, Some(window), 0);
|
||||
ev
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ForceTouchEventMethods for &'a ForceTouchEvent {
|
||||
|
||||
fn ServoForce(&self) -> Finite<f32> {
|
||||
Finite::wrap(self.force)
|
||||
}
|
||||
|
||||
fn SERVO_FORCE_AT_MOUSE_DOWN(&self) -> Finite<f32> {
|
||||
Finite::wrap(1.0)
|
||||
}
|
||||
|
||||
fn SERVO_FORCE_AT_FORCE_MOUSE_DOWN(&self) -> Finite<f32> {
|
||||
Finite::wrap(2.0)
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-event-istrusted
|
||||
fn IsTrusted(&self) -> bool {
|
||||
self.uievent.IsTrusted()
|
||||
}
|
||||
}
|
|
@ -264,6 +264,7 @@ pub mod file;
|
|||
pub mod filelist;
|
||||
pub mod filereader;
|
||||
pub mod focusevent;
|
||||
pub mod forcetouchevent;
|
||||
pub mod formdata;
|
||||
pub mod htmlanchorelement;
|
||||
pub mod htmlappletelement;
|
||||
|
|
35
components/script/dom/webidls/ForceTouchEvent.webidl
Normal file
35
components/script/dom/webidls/ForceTouchEvent.webidl
Normal file
|
@ -0,0 +1,35 @@
|
|||
/* -*- 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://developer.apple.com/library/mac/documentation/AppleApplications/Conceptual/SafariJSProgTopics/RespondingtoForceTouchEventsfromJavaScript.html
|
||||
|
||||
/**
|
||||
* Events: (copy/paste from apple.com)
|
||||
*
|
||||
* webkitmouseforcewillbegin: This event occurs immediately before the mousedown event. It allows you to
|
||||
* prevent the default system behavior, such as displaying a dictionary window when force clicking on a
|
||||
* word, in order to perform a custom action instead. To prevent the default system behavior, call the
|
||||
* preventDefault() method on the event.
|
||||
* webkitmouseforcedown: This event occurs after the mousedown event, once enough force has been applied
|
||||
* to register as a force click. The user receives haptic feedback representing the force click when this
|
||||
* event occurs.
|
||||
* webkitmouseforceup: This event occurs after a webkitmouseforcedown event, once enough force has been
|
||||
* released to exit the force click operation. The user receives haptic feedback representing the exit
|
||||
* from force click when this event occurs.
|
||||
* webkitmouseforcechanged: This event occurs whenever a change in trackpad force is detected between the
|
||||
* mousedown and mouseup events.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
[Pref="dom.forcetouch.enabled"]
|
||||
interface ForceTouchEvent : UIEvent {
|
||||
// Represents the amount of force required to perform a regular click.
|
||||
readonly attribute float SERVO_FORCE_AT_MOUSE_DOWN;
|
||||
// Represents the force required to perform a force click.
|
||||
readonly attribute float SERVO_FORCE_AT_FORCE_MOUSE_DOWN;
|
||||
// force level
|
||||
readonly attribute float servoForce;
|
||||
};
|
|
@ -79,7 +79,7 @@ use parse::xml::{self, parse_xml};
|
|||
use profile_traits::mem::{self, OpaqueSender, Report, ReportKind, ReportsChan};
|
||||
use profile_traits::time::{self, ProfilerCategory, profile};
|
||||
use script_traits::CompositorEvent::{KeyEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent};
|
||||
use script_traits::CompositorEvent::{TouchEvent};
|
||||
use script_traits::CompositorEvent::{TouchEvent, TouchpadPressureEvent};
|
||||
use script_traits::{CompositorEvent, ConstellationControlMsg, EventResult};
|
||||
use script_traits::{InitialScriptState, MouseButton, MouseEventType, MozBrowserEvent, NewLayoutInfo};
|
||||
use script_traits::{LayoutMsg, OpaqueScriptLayoutChannel, ScriptMsg as ConstellationMsg};
|
||||
|
@ -1969,6 +1969,12 @@ impl ScriptThread {
|
|||
}
|
||||
}
|
||||
|
||||
TouchpadPressureEvent(point, pressure, phase) => {
|
||||
let page = get_page(&self.root_page(), pipeline_id);
|
||||
let document = page.document();
|
||||
document.r().handle_touchpad_pressure_event(self.js_runtime.rt(), point, pressure, phase);
|
||||
}
|
||||
|
||||
KeyEvent(key, state, modifiers) => {
|
||||
let page = get_page(&self.root_page(), pipeline_id);
|
||||
let document = page.document();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue