| // 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(); |
| } |
| } |