UNPKG

@nativescript-community/gesturehandler

Version:

Declarative API exposing platform native touch and gesture system to NativeScript.

450 lines 16.7 kB
import { Utils } from '@nativescript/core'; import { GestureStateTypes, GestureTypes, GesturesObserver as NGesturesObserver, SwipeDirection, TouchAction, toString as gestureToString } from '@nativescript/core/ui/gestures'; import { Manager } from './gesturehandler'; import { GestureHandlerStateEvent, GestureHandlerTouchEvent, GestureState, HandlerType, ROOT_GESTURE_HANDLER_TAG } from './gesturehandler.common'; export function observe(target, type, callback, context) { if (!callback) { return null; } const observer = new GesturesObserver(target, callback, context); observer.observe(type); return observer; } let TAG = 0; export class GesturesObserver { //@ts-ignore get callback() { return this._callback; } get target() { return this._target; } //@ts-ignore get context() { return this._context; } constructor(target, callback, context) { this._eventData = {}; this._target = target; if (!target['TAP_HANDLER_TAG']) { target['TAP_HANDLER_TAG'] = TAG++; } if (!target['LONGPRESS_HANDLER_TAG']) { target['LONGPRESS_HANDLER_TAG'] = TAG++; } if (!target['DOUBLE_TAP_HANDLER_TAG']) { target['DOUBLE_TAP_HANDLER_TAG'] = TAG++; } if (!target['PINCH_HANDLER_TAG']) { target['PINCH_HANDLER_TAG'] = TAG++; } if (!target['PAN_HANDLER_TAG']) { target['PAN_HANDLER_TAG'] = TAG++; } this._callback = callback; this._context = context; } disconnect() { this._detach(); if (this.target) { this.target.off('loaded', this._onTargetLoaded); this.target.off('unloaded', this._onTargetUnloaded); this._onTargetLoaded = null; this._onTargetUnloaded = null; } // clears target, context and callback references // remove gesture observer from map if (this.target) { const list = this.target.getGestureObservers(this.type); if (list && list.length > 0) { for (let i = 0; i < list.length; i++) { if (list[i].callback === this.callback) { break; } } list.length = 0; delete this.target._gestureObservers[this.type]; if (this.target._gestureHandlers && this.target._gestureHandlers[this.type]) { delete this.target._gestureHandlers[this.type]; } } } if (this.nObserver) { this.nObserver.disconnect(); this.nObserver = null; } this._target = null; this._callback = null; this._context = null; this.gestureHandler = null; } observe(type) { if (this.target) { this.type = type; this._onTargetLoaded = (args) => { this._attach(this.target, type); }; this._onTargetUnloaded = (args) => { this._detach(); }; this.target.on('loaded', this._onTargetLoaded); this.target.on('unloaded', this._onTargetUnloaded); if (this.target.isLoaded) { this._attach(this.target, type); } } } _detach() { if (this.gestureHandler) { // dont detach events. It will be done on dispose // this.gestureHandler.off(GestureHandlerStateEvent); // this.gestureHandler.off(GestureHandlerTouchEvent); this.gestureHandler.detachFromView(this.target); } this._notifyTouch = false; this._eventData = {}; } emitEvent(type, event) { if (this.callback) { let eventData = this._eventData[type]; if (!eventData) { switch (type) { case GestureTypes.pan: this._eventData[type] = eventData = new PanGestureEventData(type); break; case GestureTypes.pinch: this._eventData[type] = eventData = new PinchGestureEventData(type); break; case GestureTypes.swipe: this._eventData[type] = eventData = new SwipeGestureEventData(type); break; default: this._eventData[type] = eventData = new CommonGestureEventData(type); break; } eventData.handler = this.gestureHandler; } eventData.prepare(this.target, event); _executeCallback(this, eventData); // this.callback.call(this._context, { // eventName: gestureToString(type), // object: event.data.view, // type, // ...event.data, // ...event.data.extraData, // }); } } onGestureStateChange(type, triggerOnstate = -1) { return (event) => { if (triggerOnstate !== -1 && event.data.state !== triggerOnstate) { return; } this.emitEvent(type, event); }; } onGestureTouchChange(type) { return (event) => { if (event.data.state === GestureState.ACTIVE) { this.emitEvent(type, event); } }; } _attach(target, type) { if (type & GestureTypes.touch) { this._notifyTouch = true; if (__IOS__) { // let s not reimplement it for touch const nObserver = new NGesturesObserver(target, this.callback, this.context); nObserver.observe(type); this.nObserver = nObserver; } return; } const manager = Manager.getInstance(); // if (!target._gestureHandlers) { // target._gestureHandlers = {}; // } let gestureHandler = this.gestureHandler; if (!gestureHandler) { if (type & GestureTypes.tap) { gestureHandler = manager.createGestureHandler(HandlerType.TAP, target['TAP_HANDLER_TAG'], { simultaneousHandlers: [ROOT_GESTURE_HANDLER_TAG], waitFor: [target['LONGPRESS_HANDLER_TAG'], target['DOUBLE_TAP_HANDLER_TAG']] }); gestureHandler.on(GestureHandlerStateEvent, this.onGestureStateChange(GestureTypes.tap, GestureState.ACTIVE), this); } if (type & GestureTypes.longPress) { gestureHandler = manager.createGestureHandler(HandlerType.LONG_PRESS, target['LONGPRESS_HANDLER_TAG'], { simultaneousHandlers: [ROOT_GESTURE_HANDLER_TAG] }); gestureHandler.on(GestureHandlerStateEvent, this.onGestureStateChange(GestureTypes.longPress, __IOS__ ? GestureState.BEGAN : GestureState.ACTIVE), this); } if (type & GestureTypes.doubleTap) { gestureHandler = manager.createGestureHandler(HandlerType.TAP, target['DOUBLE_TAP_HANDLER_TAG'], { numberOfTaps: 2, simultaneousHandlers: [ROOT_GESTURE_HANDLER_TAG] }); gestureHandler.on(GestureHandlerStateEvent, this.onGestureStateChange(GestureTypes.doubleTap, GestureState.ACTIVE), this); } if (type & GestureTypes.pinch) { gestureHandler = manager.createGestureHandler(HandlerType.PINCH, target['PINCH_HANDLER_TAG'], { simultaneousHandlers: [target['PAN_HANDLER_TAG'], ROOT_GESTURE_HANDLER_TAG] }); gestureHandler.on(GestureHandlerStateEvent, this.onGestureStateChange(GestureTypes.pinch), this); gestureHandler.on(GestureHandlerTouchEvent, this.onGestureTouchChange(GestureTypes.pinch), this); } if (type & GestureTypes.swipe) { gestureHandler = manager.createGestureHandler(HandlerType.FLING, TAG++, { simultaneousHandlers: [ROOT_GESTURE_HANDLER_TAG] }); gestureHandler.on(GestureHandlerStateEvent, this.onGestureStateChange(GestureTypes.swipe, GestureState.ACTIVE), this); } if (type & GestureTypes.pan) { gestureHandler = manager.createGestureHandler(HandlerType.PAN, target['PAN_HANDLER_TAG'], { simultaneousHandlers: [target['PINCH_HANDLER_TAG'], ROOT_GESTURE_HANDLER_TAG] }); gestureHandler.on(GestureHandlerStateEvent, this.onGestureStateChange(GestureTypes.pan), this); gestureHandler.on(GestureHandlerTouchEvent, this.onGestureTouchChange(GestureTypes.pan), this); } if (type & GestureTypes.rotation) { gestureHandler = manager.createGestureHandler(HandlerType.ROTATION, TAG++, { simultaneousHandlers: [ROOT_GESTURE_HANDLER_TAG] }); gestureHandler.on(GestureHandlerStateEvent, this.onGestureStateChange(GestureTypes.rotation, GestureState.ACTIVE), this); gestureHandler.on(GestureHandlerTouchEvent, this.onGestureTouchChange(GestureTypes.rotation), this); } } gestureHandler.attachToView(target); this.gestureHandler = gestureHandler; } androidOnTouchEvent(motionEvent) { if (this._notifyTouch) { let eventData = this._eventData[GestureTypes.touch]; if (!eventData) { eventData = this._eventData[GestureTypes.touch] = new AndroidTouchGestureEventData(); eventData.handler = this.gestureHandler; } eventData.prepare(this.target, motionEvent); _executeCallback(this, eventData); } } } class AndroidPointer { constructor(id, event) { this.event = event; this.ios = undefined; this.android = id; } getX() { return this.event.getX(this.android) / Utils.layout.getDisplayDensity(); } getY() { return this.event.getY(this.android) / Utils.layout.getDisplayDensity(); } } class GesturePointer { constructor(index, x, y) { this.x = x; this.y = y; this.android = index; } getX() { return this.x; } getY() { return this.y; } } class Pointer { get location() { if (!this._location) { this._location = this.ios.locationInView(this._view.nativeViewProtected); } return this._location; } constructor(touch, targetView, location) { this._location = location; this.ios = touch; this._view = targetView; } getX() { return this.location.x; } getY() { return this.location.y; } } class CommonGestureEventData { constructor(type) { this.eventName = gestureToString(type); this.type = type; } prepare(view, event) { this.view = view; this.object = view; this.ios = event.data.ios; this.android = event.data.android; this._activePointers = undefined; this._allPointers = undefined; this.eventData = event.data; switch (this.eventData.state) { case GestureState.BEGAN: this.state = GestureStateTypes.began; break; case GestureState.CANCELLED: case GestureState.FAILED: this.state = GestureStateTypes.cancelled; break; case GestureState.ACTIVE: this.state = GestureStateTypes.changed; break; case GestureState.END: this.state = GestureStateTypes.ended; break; } } get extraData() { return this.eventData.extraData; } getPointerCount() { return this.extraData.numberOfPointers; } getActivePointers() { // Only one active pointer in Android if (!this._activePointers) { if (__ANDROID__) { const positions = this.extraData.positions; this._activePointers = [new GesturePointer(0, positions[0], positions[1])]; } else { this._activePointers = [new Pointer(this.ios, this.view)]; } } return this._activePointers; } getAllPointers() { if (!this._allPointers) { this._allPointers = []; const positions = this.extraData.positions; for (let i = 0; i < this.getPointerCount(); i++) { if (__ANDROID__) { this._allPointers.push(new GesturePointer(i, positions[i * 2], positions[i * 2 + 1])); } else { this._allPointers = [new Pointer(this.ios, this.view, { x: positions[i * 2], y: positions[i * 2 + 1] })]; } } } return this._allPointers; } getX() { return this.extraData.x; } getY() { return this.extraData.y; } getActionType(e) { switch (e.getActionMasked()) { case android.view.MotionEvent.ACTION_DOWN: case android.view.MotionEvent.ACTION_POINTER_DOWN: return TouchAction.down; case android.view.MotionEvent.ACTION_MOVE: return TouchAction.move; case android.view.MotionEvent.ACTION_UP: case android.view.MotionEvent.ACTION_POINTER_UP: return TouchAction.up; case android.view.MotionEvent.ACTION_CANCEL: return TouchAction.cancel; } return ''; } } class AndroidTouchGestureEventData { constructor() { this.eventName = gestureToString(GestureTypes.touch); this.type = GestureTypes.touch; this.ios = undefined; } prepare(view, e) { this.view = view; this.object = view; this.android = e; this.action = this.getActionType(e); this._activePointers = undefined; this._allPointers = undefined; } getPointerCount() { return this.android.getPointerCount(); } getActivePointers() { // Only one active pointer in Android if (!this._activePointers) { this._activePointers = [new AndroidPointer(this.android.getActionIndex(), this.android)]; } return this._activePointers; } getAllPointers() { if (!this._allPointers) { this._allPointers = []; for (let i = 0; i < this.getPointerCount(); i++) { this._allPointers.push(new AndroidPointer(i, this.android)); } } return this._allPointers; } getX() { return this.getActivePointers()[0].getX(); } getY() { return this.getActivePointers()[0].getY(); } getActionType(e) { switch (e.getActionMasked()) { case android.view.MotionEvent.ACTION_DOWN: case android.view.MotionEvent.ACTION_POINTER_DOWN: return TouchAction.down; case android.view.MotionEvent.ACTION_MOVE: return TouchAction.move; case android.view.MotionEvent.ACTION_UP: case android.view.MotionEvent.ACTION_POINTER_UP: return TouchAction.up; case android.view.MotionEvent.ACTION_CANCEL: return TouchAction.cancel; } return ''; } } function _getSwipeDirection(direction) { return SwipeDirection[direction]; } class SwipeGestureEventData extends CommonGestureEventData { get direction() { return _getSwipeDirection(this.extraData.direction); } } class PanGestureEventData extends CommonGestureEventData { get deltaX() { return this.eventData.extraData.translationX; } get deltaY() { return this.eventData.extraData.translationY; } } class PinchGestureEventData extends CommonGestureEventData { getFocusX() { return this.eventData.extraData.focalX; } getFocusY() { return this.eventData.extraData.focalY; } get scale() { return this.eventData.extraData.scale; } } function _executeCallback(observer, args) { if (observer && observer.callback) { observer.callback.call(observer._context, args); } } //# sourceMappingURL=gestures_override.js.map