From ae6fc36ec18f9c3ecd7362061d58c8fa24b492e9 Mon Sep 17 00:00:00 2001 From: Nedyalko Nikolov Date: Tue, 2 Jun 2015 10:30:19 +0300 Subject: [PATCH] gestureObservers exposed to public via getGestureObservers method. --- .../xml-declaration/xml-declaration-tests.ts | 4 +- apps/ui-tests-app/pages/gestures.ts | 28 ++++-- ui/core/view-common.ts | 29 ++++-- ui/core/view.android.ts | 65 +++++-------- ui/core/view.d.ts | 11 ++- ui/gestures/gestures-common.ts | 62 +++++++++++- ui/gestures/gestures.android.ts | 96 ++++++++++++------- ui/gestures/gestures.d.ts | 20 +++- ui/gestures/gestures.ios.ts | 55 +++++------ 9 files changed, 242 insertions(+), 128 deletions(-) diff --git a/apps/tests/xml-declaration/xml-declaration-tests.ts b/apps/tests/xml-declaration/xml-declaration-tests.ts index 247f51fc9..563f874d9 100644 --- a/apps/tests/xml-declaration/xml-declaration-tests.ts +++ b/apps/tests/xml-declaration/xml-declaration-tests.ts @@ -187,10 +187,10 @@ export function test_parse_ShouldParseBindingsToGestures() { p.bindingContext = context; var lbl = p.content; - var observer = (lbl)._gestureObservers[gesturesModule.GestureTypes.tap][0]; + var observer = (lbl).getGestureObservers(gesturesModule.GestureTypes.tap)[0]; TKUnit.assert(observer !== undefined, "Expected result: true."); - TKUnit.assert(observer._context === context, "Context should be equal to binding context. Actual result: " + observer._context); + TKUnit.assert(observer.context === context, "Context should be equal to binding context. Actual result: " + observer.context); }; export function test_parse_ShouldParseSubProperties() { diff --git a/apps/ui-tests-app/pages/gestures.ts b/apps/ui-tests-app/pages/gestures.ts index 2907d95d0..d72851431 100644 --- a/apps/ui-tests-app/pages/gestures.ts +++ b/apps/ui-tests-app/pages/gestures.ts @@ -62,34 +62,48 @@ export function createPage() { rotaionLabel.text = "Gestures detection disabled"; }); - var observer1 = tapLabel.observe(gestures.GestureTypes.tap, function (args: gestures.GestureEventData) { + tapLabel.observe(gestures.GestureTypes.tap, function (args: gestures.GestureEventData) { tapLabel.text = "Tap gesture detected"; }); - var observer2 = doubletapLabel.observe(gestures.GestureTypes.doubleTap, function (args: gestures.GestureEventData) { + var observer1 = tapLabel.getGestureObservers(gestures.GestureTypes.tap)[0]; + + doubletapLabel.observe(gestures.GestureTypes.doubleTap, function (args: gestures.GestureEventData) { doubletapLabel.text = "Double Tap gesture detected"; }); - var observer3 = longpressLabel.observe(gestures.GestureTypes.longPress, function (args: gestures.GestureEventData) { + var observer2 = doubletapLabel.getGestureObservers(gestures.GestureTypes.doubleTap)[0]; + + longpressLabel.observe(gestures.GestureTypes.longPress, function (args: gestures.GestureEventData) { longpressLabel.text = "Long Press gesture detected"; }); - var observer4 = swipeLabel.observe(gestures.GestureTypes.swipe, function (args: gestures.SwipeGestureEventData) { + var observer3 = longpressLabel.getGestureObservers(gestures.GestureTypes.longPress)[0]; + + swipeLabel.observe(gestures.GestureTypes.swipe, function (args: gestures.SwipeGestureEventData) { swipeLabel.text = "Swipe Direction: " + args.direction; }); - var observer5 = panLabel.observe(gestures.GestureTypes.pan, function (args: gestures.PanGestureEventData) { + var observer4 = swipeLabel.getGestureObservers(gestures.GestureTypes.swipe)[0]; + + panLabel.observe(gestures.GestureTypes.pan, function (args: gestures.PanGestureEventData) { panLabel.text = "Pan deltaX:" + args.deltaX + "; deltaY:" + args.deltaY + ";"; }); - var observer6 = pinchLabel.observe(gestures.GestureTypes.pinch, function (args: gestures.PinchGestureEventData) { + var observer5 = panLabel.getGestureObservers(gestures.GestureTypes.pan)[0]; + + pinchLabel.observe(gestures.GestureTypes.pinch, function (args: gestures.PinchGestureEventData) { pinchLabel.text = "Pinch Scale: " + args.scale; }); - var observer7 = rotaionLabel.observe(gestures.GestureTypes.rotation, function (args: gestures.RotationGestureEventData) { + var observer6 = pinchLabel.getGestureObservers(gestures.GestureTypes.pinch)[0]; + + rotaionLabel.observe(gestures.GestureTypes.rotation, function (args: gestures.RotationGestureEventData) { rotaionLabel.text = "Rotation: " + args.rotation; }); + var observer7 = rotaionLabel.getGestureObservers(gestures.GestureTypes.rotation)[0]; + var page = new pages.Page(); page.content = stack; return page; diff --git a/ui/core/view-common.ts b/ui/core/view-common.ts index ea8c661f0..2d5c0fc63 100644 --- a/ui/core/view-common.ts +++ b/ui/core/view-common.ts @@ -132,7 +132,16 @@ export class View extends proxy.ProxyObject implements definition.View { public _cssClasses: Array = []; - public _gestureObservers = {}; + public _gestureObservers: Map>; + + public getGestureObservers(type: gestures.GestureTypes): Array { + var result; + if (this._gestureObservers) { + result = this._gestureObservers.get(type) ? this._gestureObservers.get(type).slice(0) : undefined; + } + return result; + } + private _updatingInheritedProperties: boolean; public _options: definition.Options; @@ -147,7 +156,7 @@ export class View extends proxy.ProxyObject implements definition.View { this._visualState = visualStateConstants.Normal; } - observe(type: number, callback: (args: gestures.GestureEventData) => void, thisArg?: any): void { + observe(type: gestures.GestureTypes, callback: (args: gestures.GestureEventData) => void, thisArg?: any): void { var gesturesList = this._getGesturesList(type, true); gesturesList.push(gestures.observe(this, type, callback, thisArg)); } @@ -157,10 +166,18 @@ export class View extends proxy.ProxyObject implements definition.View { throw new Error("GestureType must be a valid gesture!"); } - var list = this._gestureObservers[gestureType]; - if (!list && createIfNeeded) { - list = []; - this._gestureObservers[gestureType] = list; + var list: Array; + if (this._gestureObservers && this._gestureObservers.has(gestureType)) { + list = this._gestureObservers.get(gestureType); + } + else { + if (createIfNeeded) { + list = []; + if (!this._gestureObservers) { + this._gestureObservers = new Map>(); + } + this._gestureObservers.set(gestureType, list); + } } return list; } diff --git a/ui/core/view.android.ts b/ui/core/view.android.ts index 633a5e432..9e4a73d94 100644 --- a/ui/core/view.android.ts +++ b/ui/core/view.android.ts @@ -78,6 +78,15 @@ export class View extends viewCommon.View { this._updateOnTouchListener(this.isUserInteractionEnabled); } + observe(type: gestures.GestureTypes, callback: (args: gestures.GestureEventData) => void, thisArg?: any): void { + super.observe(type, callback, thisArg); + if (this.isLoaded && !this.touchListenerIsSet) { + this.setOnTouchListener(); + } + } + + private touchListenerIsSet: boolean; + public onLoaded() { super.onLoaded(); this.setOnTouchListener(); @@ -87,11 +96,17 @@ export class View extends viewCommon.View { super.onUnloaded(); if (this._nativeView && this._nativeView.setOnTouchListener) { this._nativeView.setOnTouchListener(null); + this.touchListenerIsSet = false; } } + private hasGestureObservers() { + return this._gestureObservers ? this._gestureObservers.size > 0 : false; + } + private setOnTouchListener() { - if (this._nativeView && this._nativeView.setOnTouchListener && Object.keys(this._gestureObservers).length > 0) { + if (this._nativeView && this._nativeView.setOnTouchListener && this.hasGestureObservers()) { + this.touchListenerIsSet = true; var that = new WeakRef(this); if (this._nativeView.setClickable) { this._nativeView.setClickable(true); @@ -102,47 +117,15 @@ export class View extends viewCommon.View { if (!owner) { return false; } + var i; - for (var prop in owner._gestureObservers) { - if (owner._gestureObservers.hasOwnProperty(prop)) { - for (i = 0; i < owner._gestureObservers[prop].length; i++) { - var gestureObserver = owner._gestureObservers[prop][i]; - if (gestureObserver._simpleGestureDetector) { - gestureObserver._simpleGestureDetector.onTouchEvent(motionEvent); - } - - if (gestureObserver._scaleGestureDetector) { - gestureObserver._scaleGestureDetector.onTouchEvent(motionEvent); - } - - if (gestureObserver._swipeGestureDetector) { - gestureObserver._swipeGestureDetector.onTouchEvent(motionEvent); - } - - if (gestureObserver._panGestureDetector) { - gestureObserver._panGestureDetector.onTouchEvent(motionEvent); - } - - if (gestureObserver.type & gestures.GestureTypes.rotation && motionEvent.getPointerCount() === 2) { - - var deltaX = motionEvent.getX(0) - motionEvent.getX(1); - var deltaY = motionEvent.getY(0) - motionEvent.getY(1); - var radians = Math.atan(deltaY / deltaX); - var degrees = radians * (180 / Math.PI); - - var args = { - type: gestures.GestureTypes.rotation, - view: owner, - android: motionEvent, - rotation: degrees, - ios: null - } - - //var observer = that.get(); - if (gestureObserver.callback) { - gestureObserver.callback.call(gestureObserver._context, args); - } - + for (var gestType in gestures.GestureTypes) { + if (gestures.GestureTypes.hasOwnProperty(gestType)) { + var gestArray = owner.getGestureObservers(gestType); + if (gestArray) { + for (i = 0; i < gestArray.length; i++) { + var gestObserver = gestArray[i]; + gestObserver.androidOnTouchEvent(motionEvent); } } } diff --git a/ui/core/view.d.ts b/ui/core/view.d.ts index 23155e898..8d792bc4e 100644 --- a/ui/core/view.d.ts +++ b/ui/core/view.d.ts @@ -355,7 +355,15 @@ declare module "ui/core/view" { */ public focus(): boolean; - observe(type: number, callback: (args: gestures.GestureEventData) => void, thisArg?: any); + public getGestureObservers(type: gestures.GestureTypes): Array; + + /** + * Adds a gesture observer. + * @param type - Type of the gesture. + * @param callback - A function that will be executed when gesture is received. + * @param thisArg - An optional parameter which will be used as `this` context for callback execution. + */ + observe(type: gestures.GestureTypes, callback: (args: gestures.GestureEventData) => void, thisArg?: any); /** * A basic method signature to hook an event listener (shortcut alias to the addEventListener method). @@ -386,6 +394,7 @@ declare module "ui/core/view" { // TODO: Implement logic for stripping these lines out //@private + _gestureObservers: Map>; _isInheritedChange(): boolean; _domId: number; _cssClasses: Array; diff --git a/ui/gestures/gestures-common.ts b/ui/gestures/gestures-common.ts index 4ace2eea1..7752b44cd 100644 --- a/ui/gestures/gestures-common.ts +++ b/ui/gestures/gestures-common.ts @@ -28,9 +28,9 @@ export enum SwipeDirection { down = 1 << 3 } -export function observe(target: view.View, type: number, callback: (args: definition.GestureEventData) => void, thisArg?: any): definition.GesturesObserver { - var observer = new definition.GesturesObserver(callback); - observer.observe(target, type, thisArg); +export function observe(target: view.View, type: definition.GestureTypes, callback: (args: definition.GestureEventData) => void, thisArg?: any): definition.GesturesObserver { + var observer = new definition.GesturesObserver(target, callback, thisArg); + observer.observe(type); return observer; } @@ -88,4 +88,60 @@ export function fromString(type: string): definition.GestureTypes { } return undefined; +} + +export class GesturesObserver implements definition.GesturesObserver { + private _callback: (args: definition.GestureEventData) => void; + private _target: view.View; + private _context: any; + + public type: definition.GestureTypes; + + public get callback(): (args: definition.GestureEventData) => void { + return this._callback; + } + + public get target(): view.View { + return this._target; + } + + public get context() { + return this._context; + } + + constructor(target: view.View, callback: (args: definition.GestureEventData) => void, context: any) { + this._target = target; + this._callback = callback; + this._context = context; + } + + public androidOnTouchEvent(motionEvent: android.view.MotionEvent) { + // + } + + public observe(type: definition.GestureTypes) { + // + } + + public disconnect() { + // remove gesture observer from map + if (this.target) { + var gestureObserversArray = this.target._gestureObservers.get(this.type); + if (gestureObserversArray) { + var i; + for (i = 0; i < gestureObserversArray.length; i++) { + if (gestureObserversArray[i].callback === this.callback) { + break; + } + } + gestureObserversArray.splice(i, 1); + if (gestureObserversArray.length === 0) { + this.target._gestureObservers.delete(this.type); + } + } + } + this._target = null; + this._callback = null; + this._context = null; + } } \ No newline at end of file diff --git a/ui/gestures/gestures.android.ts b/ui/gestures/gestures.android.ts index 2acf7d453..268c5c463 100644 --- a/ui/gestures/gestures.android.ts +++ b/ui/gestures/gestures.android.ts @@ -11,11 +11,7 @@ require("utils/module-merge").merge(common, exports); var SWIPE_THRESHOLD = 100; var SWIPE_VELOCITY_THRESHOLD = 100; -export class GesturesObserver implements definition.GesturesObserver { - private _callback: (args: definition.GestureEventData) => void; - private _target: view.View; - private _context: any; - +export class GesturesObserver extends common.GesturesObserver { private _onTouchListener: android.view.View.OnTouchListener; public _simpleGestureDetector: android.view.GestureDetector; public _scaleGestureDetector: android.view.ScaleGestureDetector; @@ -24,35 +20,24 @@ export class GesturesObserver implements definition.GesturesObserver { private _onTargetLoaded: (data: observable.EventData) => void; private _onTargetUnloaded: (data: observable.EventData) => void; - public type: definition.GestureTypes; - constructor(callback: (args: definition.GestureEventData) => void) { - this._callback = callback; - } - - get callback(): (args: definition.GestureEventData) => void { - return this._callback; - } - - public observe(target: view.View, type: definition.GestureTypes, thisArg?: any) { - if (target) { + public observe(type: definition.GestureTypes) { + if (this.target) { this.type = type; - this._target = target; - this._context = thisArg; this._onTargetLoaded = args => { - trace.write(this._target + ".target loaded. android:" + this._target.android, "gestures"); - this._attach(target, type); + trace.write(this.target + ".target loaded. android:" + this.target._nativeView, "gestures"); + this._attach(this.target, type); }; this._onTargetUnloaded = args => { - trace.write(this._target + ".target unloaded. android:" + this._target.android, "gestures"); + trace.write(this.target + ".target unloaded. android:" + this.target._nativeView, "gestures"); this._dettach(); }; - target.on(view.View.loadedEvent, this._onTargetLoaded); - target.on(view.View.unloadedEvent, this._onTargetUnloaded); + this.target.on(view.View.loadedEvent, this._onTargetLoaded); + this.target.on(view.View.unloadedEvent, this._onTargetUnloaded); - if (target.isLoaded) { - this._attach(target, type); + if (this.target.isLoaded) { + this._attach(this.target, type); } } } @@ -60,18 +45,19 @@ export class GesturesObserver implements definition.GesturesObserver { public disconnect() { this._dettach(); - if (this._target) { - this._target.off(view.View.loadedEvent, this._onTargetLoaded); - this._target.off(view.View.unloadedEvent, this._onTargetUnloaded); + if (this.target) { + this.target.off(view.View.loadedEvent, this._onTargetLoaded); + this.target.off(view.View.unloadedEvent, this._onTargetUnloaded); this._onTargetLoaded = null; this._onTargetUnloaded = null; - this._target = null; } + // clears target, context and callback references + super.disconnect(); } private _dettach() { - trace.write(this._target + "._detach() android:" + this._target.android, "gestures"); + trace.write(this.target + "._detach() android:" + this.target._nativeView, "gestures"); this._onTouchListener = null; this._simpleGestureDetector = null; @@ -81,23 +67,63 @@ export class GesturesObserver implements definition.GesturesObserver { } private _attach(target: view.View, type: definition.GestureTypes) { - trace.write(this._target + "._attach() android:" + this._target.android, "gestures"); + trace.write(this.target + "._attach() android:" + this.target._nativeView, "gestures"); this._dettach(); if (type & definition.GestureTypes.tap || type & definition.GestureTypes.doubleTap || type & definition.GestureTypes.longPress) { - this._simpleGestureDetector = new android.support.v4.view.GestureDetectorCompat(target._context, new TapAndDoubleTapGestureListener(this, this._target, type)); + this._simpleGestureDetector = new android.support.v4.view.GestureDetectorCompat(target._context, new TapAndDoubleTapGestureListener(this, this.target, type)); } if (type & definition.GestureTypes.pinch) { - this._scaleGestureDetector = new android.view.ScaleGestureDetector(target._context, new PinchGestureListener(this, this._target)); + this._scaleGestureDetector = new android.view.ScaleGestureDetector(target._context, new PinchGestureListener(this, this.target)); } if (type & definition.GestureTypes.swipe) { - this._swipeGestureDetector = new android.support.v4.view.GestureDetectorCompat(target._context, new SwipeGestureListener(this, this._target)); + this._swipeGestureDetector = new android.support.v4.view.GestureDetectorCompat(target._context, new SwipeGestureListener(this, this.target)); } if (type & definition.GestureTypes.pan) { - this._panGestureDetector = new android.support.v4.view.GestureDetectorCompat(target._context, new PanGestureListener(this, this._target)); + this._panGestureDetector = new android.support.v4.view.GestureDetectorCompat(target._context, new PanGestureListener(this, this.target)); + } + } + + public androidOnTouchEvent(motionEvent: android.view.MotionEvent) { + if (this._simpleGestureDetector) { + this._simpleGestureDetector.onTouchEvent(motionEvent); + } + + if (this._scaleGestureDetector) { + this._scaleGestureDetector.onTouchEvent(motionEvent); + } + + if (this._swipeGestureDetector) { + this._swipeGestureDetector.onTouchEvent(motionEvent); + } + + if (this._panGestureDetector) { + this._panGestureDetector.onTouchEvent(motionEvent); + } + + if (this.type & definition.GestureTypes.rotation && motionEvent.getPointerCount() === 2) { + + var deltaX = motionEvent.getX(0) - motionEvent.getX(1); + var deltaY = motionEvent.getY(0) - motionEvent.getY(1); + var radians = Math.atan(deltaY / deltaX); + var degrees = radians * (180 / Math.PI); + + var args = { + type: definition.GestureTypes.rotation, + view: this.target, + android: motionEvent, + rotation: degrees, + ios: null + } + + //var observer = that.get(); + if (this.callback) { + this.callback.call(this.context, args); + } + } } } diff --git a/ui/gestures/gestures.d.ts b/ui/gestures/gestures.d.ts index 07f153acf..297a7c960 100644 --- a/ui/gestures/gestures.d.ts +++ b/ui/gestures/gestures.d.ts @@ -119,14 +119,13 @@ declare module "ui/gestures" { * Creates an instance of GesturesObserver class. * @param callback - A function that will be executed when a gesture is received. */ - constructor(callback: (args: GestureEventData) => void); + constructor(target: view.View, callback: (args: GestureEventData) => void, context: any); /** * Registers a gesture observer to a view and gesture. - * @param target - View which will be watched for originating a specific gesture. * @param type - Type of the gesture. */ - observe(target: view.View, type: GestureTypes, thisArg?: any); + observe(type: GestureTypes); /** * Disconnects the gesture observer. @@ -137,6 +136,21 @@ declare module "ui/gestures" { * Gesture type attached to the observer. */ type: GestureTypes; + + /** + * A function that will be executed when a gesture is received. + */ + callback: (args: GestureEventData) => void; + + /** + * A context which will be used as `this` in callback execution. + */ + context: any; + + /** + * An internal Android specific method used to pass the motion event to the correct gesture observer. + */ + androidOnTouchEvent: (motionEvent: android.view.MotionEvent) => void; } /** diff --git a/ui/gestures/gestures.ios.ts b/ui/gestures/gestures.ios.ts index 09510c002..d061c2077 100644 --- a/ui/gestures/gestures.ios.ts +++ b/ui/gestures/gestures.ios.ts @@ -38,9 +38,9 @@ class UIGestureRecognizerImpl extends NSObject { }; public recognize(recognizer: UIGestureRecognizer): void { - var callback = this._callback ? this._callback : this._owner._callback; + var callback = this._callback ? this._callback : this._owner.callback; var type = this._type; - var target = this._owner._target; + var target = this._owner.target; var args = { type: type, @@ -55,40 +55,34 @@ class UIGestureRecognizerImpl extends NSObject { } } -export class GesturesObserver implements definition.GesturesObserver { - public _callback: (args: definition.GestureEventData) => void; - public _target: view.View; - public type: definition.GestureTypes; +export class GesturesObserver extends common.GesturesObserver { private _recognizers: {}; - private _context: any; private _onTargetLoaded: (data: observable.EventData) => void; private _onTargetUnloaded: (data: observable.EventData) => void; - constructor(callback: (args: definition.GestureEventData) => void) { - this._callback = callback; + constructor(target: view.View, callback: (args: definition.GestureEventData) => void, context: any) { + super(target, callback, context); this._recognizers = {}; } - public observe(target: view.View, type: definition.GestureTypes, thisArg?: any) { - if (target) { + public observe(type: definition.GestureTypes) { + if (this.target) { this.type = type; - this._target = target; - this._context = thisArg; this._onTargetLoaded = args => { - trace.write(this._target + ".target loaded. _nativeView:" + this._target._nativeView, "gestures"); - this._attach(target, type); + trace.write(this.target + ".target loaded. _nativeView:" + this.target._nativeView, "gestures"); + this._attach(this.target, type); }; this._onTargetUnloaded = args => { - trace.write(this._target + ".target unloaded. _nativeView:" + this._target._nativeView, "gestures"); + trace.write(this.target + ".target unloaded. _nativeView:" + this.target._nativeView, "gestures"); this._dettach(); }; - target.on(view.View.loadedEvent, this._onTargetLoaded); - target.on(view.View.unloadedEvent, this._onTargetUnloaded); + this.target.on(view.View.loadedEvent, this._onTargetLoaded); + this.target.on(view.View.unloadedEvent, this._onTargetUnloaded); - if (target.isLoaded) { - this._attach(target, type); + if (this.target.isLoaded) { + this._attach(this.target, type); } } } @@ -154,12 +148,12 @@ export class GesturesObserver implements definition.GesturesObserver { } private _dettach() { - trace.write(this._target + "._dettach() _nativeView:" + this._target._nativeView, "gestures"); - if (this._target && this._target._nativeView) { + trace.write(this.target + "._dettach() _nativeView:" + this.target._nativeView, "gestures"); + if (this.target && this.target._nativeView) { for (var name in this._recognizers) { if (this._recognizers.hasOwnProperty(name)) { var item = this._recognizers[name]; - this._target._nativeView.removeGestureRecognizer(item.recognizer); + this.target._nativeView.removeGestureRecognizer(item.recognizer); item.recognizer = null; item.target = null; @@ -172,26 +166,27 @@ export class GesturesObserver implements definition.GesturesObserver { public disconnect() { this._dettach(); - if (this._target) { - this._target.off(view.View.loadedEvent, this._onTargetLoaded); - this._target.off(view.View.unloadedEvent, this._onTargetUnloaded); + if (this.target) { + this.target.off(view.View.loadedEvent, this._onTargetLoaded); + this.target.off(view.View.unloadedEvent, this._onTargetUnloaded); this._onTargetLoaded = null; this._onTargetUnloaded = null; - this._target = null; } + // clears target, context and callback references + super.disconnect(); } private _executeCallback(args: definition.GestureEventData) { - if (this._callback) { - this._callback.call(this._context, args); + if (this.callback) { + this.callback.call(this.context, args); } } private _createRecognizer(type: definition.GestureTypes, callback?: (args: definition.GestureEventData) => void, swipeDirection?: UISwipeGestureRecognizerDirection): UIGestureRecognizer { var recognizer: UIGestureRecognizer; var name = definition.toString(type); - var target = _createUIGestureRecognizerTarget(this, type, callback, this._context); + var target = _createUIGestureRecognizerTarget(this, type, callback, this.context); var recognizerType = _getUIGestureRecognizerType(type); if (recognizerType) {