blob: 39183c7d8ada992e29dedd5d40e563f598ce62f4 [file] [log] [blame]
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// @dart = 2.9
part of touch;
/**
* Wraps a callback with translations of mouse events to touch events. Use
* this function to invoke your callback that expects touch events after
* touch events are created from the actual mouse events.
*/
EventListener mouseToTouchCallback(EventListener callback) {
return (Event e) {
var touches = <Touch>[];
var targetTouches = <Touch>[];
var changedTouches = <Touch>[];
final mockTouch = new MockTouch(e);
final mockTouchList = <Touch>[mockTouch];
if (e.type == 'mouseup') {
changedTouches = mockTouchList;
} else {
touches = mockTouchList;
targetTouches = mockTouchList;
}
callback(new MockTouchEvent(e, touches, targetTouches, changedTouches));
// Required to prevent spurious selection changes while tracking touches
// on devices that don't support touch events.
e.preventDefault();
};
}
/** Helper method to attach event listeners to a [node]. */
void _addEventListeners(Element node, EventListener onStart,
EventListener onMove, EventListener onEnd, EventListener onCancel,
[bool capture = false]) {
Function removeListeners;
onEndWrapper(e) {
removeListeners();
return onEnd(e);
}
onLeaveWrapper(e) {
removeListeners();
return onEnd(e);
}
onCancelWrapper(e) {
removeListeners();
return onCancel(e);
}
if (Device.supportsTouch) {
var touchMoveSub;
var touchEndSub;
var touchLeaveSub;
var touchCancelSub;
removeListeners = () {
touchMoveSub.cancel();
touchEndSub.cancel();
touchLeaveSub.cancel();
touchCancelSub.cancel();
};
Element.touchStartEvent.forTarget(node, useCapture: capture).listen((e) {
touchMoveSub = Element.touchMoveEvent
.forTarget(document, useCapture: capture)
.listen(onMove);
touchEndSub = Element.touchEndEvent
.forTarget(document, useCapture: capture)
.listen(onEndWrapper);
touchLeaveSub = Element.touchLeaveEvent
.forTarget(document, useCapture: capture)
.listen(onLeaveWrapper);
touchCancelSub = Element.touchCancelEvent
.forTarget(document, useCapture: capture)
.listen(onCancelWrapper);
return onStart(e);
});
} else {
onStart = mouseToTouchCallback(onStart);
onMove = mouseToTouchCallback(onMove);
onEnd = mouseToTouchCallback(onEnd);
// onLeave will never be called if the device does not support touches.
var mouseMoveSub;
var mouseUpSub;
var touchCancelSub;
removeListeners = () {
mouseMoveSub.cancel();
mouseUpSub.cancel();
touchCancelSub.cancel();
};
Element.mouseDownEvent.forTarget(node, useCapture: capture).listen((e) {
mouseMoveSub = Element.mouseMoveEvent
.forTarget(document, useCapture: capture)
.listen(onMove);
mouseUpSub = Element.mouseUpEvent
.forTarget(document, useCapture: capture)
.listen(onEndWrapper);
touchCancelSub = Element.touchCancelEvent
.forTarget(document, useCapture: capture)
.listen(onCancelWrapper);
return onStart(e);
});
}
}
/**
* Gets whether the given touch event targets the node, or one of the node's
* children.
*/
bool _touchEventTargetsNode(event, Node node) {
Node target = event.changedTouches[0].target;
// TODO(rnystrom): Move this into Dom.
// Walk up the parents looking for the node.
while (target != null) {
if (target == node) {
return true;
}
target = target.parent;
}
return false;
}
abstract class Touchable {
/**
* Provide the HTML element that should respond to touch events.
*/
Element getElement();
/**
* The object has received a touchend event.
*/
void onTouchEnd();
/**
* The object has received a touchstart event.
* Returns return true if you want to allow a drag sequence to begin,
* false you want to disable dragging for the duration of this touch.
*/
bool onTouchStart(TouchEvent e);
}
abstract class Draggable implements Touchable {
/**
* The object's drag sequence is now complete.
*/
void onDragEnd();
/**
* The object has been dragged to a new position.
*/
void onDragMove();
/**
* The object has started dragging.
* Returns true to allow a drag sequence to begin (custom behavior),
* false to disable dragging for this touch duration (allow native scrolling).
*/
bool onDragStart(TouchEvent e);
bool get verticalEnabled;
bool get horizontalEnabled;
}
class MockTouch implements Touch {
MouseEvent wrapped;
MockTouch(MouseEvent this.wrapped) {}
int get clientX => wrapped.client.x;
int get clientY => wrapped.client.y;
get client => wrapped.client;
int get identifier => 0;
int get pageX => wrapped.page.x;
int get pageY => wrapped.page.y;
int get screenX => wrapped.screen.x;
int get screenY {
return wrapped.screen.y;
}
EventTarget get target => wrapped.target;
double get force {
throw new UnimplementedError();
}
Point get page {
throw new UnimplementedError();
}
int get radiusX {
throw new UnimplementedError();
}
int get radiusY {
throw new UnimplementedError();
}
String get region {
throw new UnimplementedError();
}
num get rotationAngle {
throw new UnimplementedError();
}
Point get screen {
throw new UnimplementedError();
}
num get webkitForce {
throw new UnimplementedError();
}
int get webkitRadiusX {
throw new UnimplementedError();
}
int get webkitRadiusY {
throw new UnimplementedError();
}
num get webkitRotationAngle {
throw new UnimplementedError();
}
}
class MockTouchList extends Object
with ListMixin<Touch>, ImmutableListMixin<Touch>
implements TouchList {
final List<Touch> values;
MockTouchList(this.values);
static bool get supported => true;
int get length => values.length;
Touch operator [](int index) => values[index];
void operator []=(int index, Touch value) {
throw new UnsupportedError("Cannot assign element of immutable List.");
}
set length(int value) {
throw new UnsupportedError("Cannot resize immutable List.");
}
Touch item(int index) => values[index];
}
class MockTouchEvent implements TouchEvent {
dynamic /*MouseEvent*/ wrapped;
final TouchList touches;
final TouchList targetTouches;
final TouchList changedTouches;
MockTouchEvent(MouseEvent this.wrapped, List<Touch> touches,
List<Touch> targetTouches, List<Touch> changedTouches)
: touches = new MockTouchList(touches),
targetTouches = new MockTouchList(targetTouches),
changedTouches = new MockTouchList(changedTouches);
bool get bubbles => wrapped.bubbles;
bool get cancelBubble => wrapped.cancelBubble;
void set cancelBubble(bool value) {
wrapped.cancelBubble = value;
}
bool get cancelable => wrapped.cancelable;
EventTarget get currentTarget => wrapped.currentTarget;
bool get defaultPrevented => wrapped.defaultPrevented;
int get eventPhase => wrapped.eventPhase;
void set returnValue(bool value) {
wrapped.returnValue = value;
}
bool get returnValue => wrapped.returnValue;
EventTarget get target => wrapped.target;
/*At different times, int, double, and String*/
get timeStamp => wrapped.timeStamp;
String get type => wrapped.type;
void preventDefault() {
wrapped.preventDefault();
}
void stopImmediatePropagation() {
wrapped.stopImmediatePropagation();
}
void stopPropagation() {
wrapped.stopPropagation();
}
int get charCode => wrapped.charCode;
int get detail => wrapped.detail;
// TODO(sra): keyCode is not on MouseEvent.
//int get keyCode => (wrapped as KeyboardEvent).keyCode;
int get layerX => wrapped.layer.x;
int get layerY => wrapped.layer.y;
int get pageX => wrapped.page.x;
int get pageY => wrapped.page.y;
Window get view => wrapped.view;
int get which => wrapped.which;
bool get altKey => wrapped.altKey;
bool get ctrlKey => wrapped.ctrlKey;
bool get metaKey => wrapped.metaKey;
bool get shiftKey => wrapped.shiftKey;
DataTransfer get clipboardData {
throw new UnimplementedError();
}
List<EventTarget> deepPath() {
throw new UnimplementedError();
}
bool get isTrusted {
throw new UnimplementedError();
}
Point get layer {
throw new UnimplementedError();
}
Element get matchingTarget {
throw new UnimplementedError();
}
Point get page {
throw new UnimplementedError();
}
List<EventTarget> get path {
throw new UnimplementedError();
}
bool get scoped {
throw new UnimplementedError();
}
Point get screen {
throw new UnimplementedError();
}
/*InputDeviceCapabilities*/ get sourceCapabilities {
throw new UnimplementedError();
}
/*InputDevice*/ get sourceDevice {
throw new UnimplementedError();
}
bool get composed {
throw new UnimplementedError();
}
List<EventTarget> composedPath() {
throw new UnimplementedError();
}
}