mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-26 11:17:04 +08:00
fix(core): drop support for plural event/gesture names (#10539)
This commit is contained in:
@ -163,7 +163,7 @@ export var test_Observable_addEventListener_MultipleEvents = function () {
|
|||||||
obj.addEventListener(events, callback);
|
obj.addEventListener(events, callback);
|
||||||
obj.set('testName', 1);
|
obj.set('testName', 1);
|
||||||
obj.test();
|
obj.test();
|
||||||
TKUnit.assert(receivedCount === 2, 'Callbacks not raised properly.');
|
TKUnit.assert(receivedCount === 0, "Expected no event handlers to fire upon the 'propertyChange' event when listening for event name 'propertyChange,tested', as we have dropped support for listening to plural event names.");
|
||||||
};
|
};
|
||||||
|
|
||||||
export var test_Observable_addEventListener_MultipleEvents_ShouldTrim = function () {
|
export var test_Observable_addEventListener_MultipleEvents_ShouldTrim = function () {
|
||||||
@ -176,13 +176,14 @@ export var test_Observable_addEventListener_MultipleEvents_ShouldTrim = function
|
|||||||
|
|
||||||
var events = Observable.propertyChangeEvent + ' , ' + TESTED_NAME;
|
var events = Observable.propertyChangeEvent + ' , ' + TESTED_NAME;
|
||||||
obj.addEventListener(events, callback);
|
obj.addEventListener(events, callback);
|
||||||
TKUnit.assert(obj.hasListeners(Observable.propertyChangeEvent), 'Observable.addEventListener for multiple events should trim each event name.');
|
TKUnit.assert(obj.hasListeners(events), "Expected a listener to be present for event name 'propertyChange , tested', as we have dropped support for splitting plural event names.");
|
||||||
TKUnit.assert(obj.hasListeners(TESTED_NAME), 'Observable.addEventListener for multiple events should trim each event name.');
|
TKUnit.assert(!obj.hasListeners(Observable.propertyChangeEvent), "Expected no listeners to be present for event name 'propertyChange', as we have dropped support for splitting plural event names.");
|
||||||
|
TKUnit.assert(!obj.hasListeners(TESTED_NAME), "Expected no listeners to be present for event name 'tested', as we have dropped support for splitting plural event names.");
|
||||||
|
|
||||||
obj.set('testName', 1);
|
obj.set('testName', 1);
|
||||||
obj.test();
|
obj.test();
|
||||||
|
|
||||||
TKUnit.assert(receivedCount === 2, 'Callbacks not raised properly.');
|
TKUnit.assert(receivedCount === 0, "Expected no event handlers to fire upon the 'propertyChange' event when listening for event name 'propertyChange , tested', as we have dropped support for listening to plural event names (and trimming whitespace in event names).");
|
||||||
};
|
};
|
||||||
|
|
||||||
export var test_Observable_addEventListener_MultipleCallbacks = function () {
|
export var test_Observable_addEventListener_MultipleCallbacks = function () {
|
||||||
@ -223,7 +224,7 @@ export var test_Observable_addEventListener_MultipleCallbacks_MultipleEvents = f
|
|||||||
obj.set('testName', 1);
|
obj.set('testName', 1);
|
||||||
obj.test();
|
obj.test();
|
||||||
|
|
||||||
TKUnit.assert(receivedCount === 4, 'The propertyChanged notification should be raised twice.');
|
TKUnit.assert(receivedCount === 0, "Expected no event handlers to fire upon the 'propertyChange' event when listening for event name 'propertyChange , tested' with two different callbacks, as we have dropped support for listening to plural event names (and trimming whitespace in event names).");
|
||||||
};
|
};
|
||||||
|
|
||||||
export var test_Observable_removeEventListener_SingleEvent_SingleCallback = function () {
|
export var test_Observable_removeEventListener_SingleEvent_SingleCallback = function () {
|
||||||
@ -341,19 +342,22 @@ export var test_Observable_removeEventListener_MultipleEvents_SingleCallback = f
|
|||||||
|
|
||||||
var events = Observable.propertyChangeEvent + ' , ' + TESTED_NAME;
|
var events = Observable.propertyChangeEvent + ' , ' + TESTED_NAME;
|
||||||
obj.addEventListener(events, callback);
|
obj.addEventListener(events, callback);
|
||||||
|
TKUnit.assert(obj.hasListeners(events), "Expected a listener to be present for event name 'propertyChange , tested', as we have dropped support for splitting plural event names.");
|
||||||
|
TKUnit.assert(!obj.hasListeners(Observable.propertyChangeEvent), "Expected no listeners to be present for event name 'propertyChange', as we have dropped support for splitting plural event names.");
|
||||||
|
TKUnit.assert(!obj.hasListeners(TESTED_NAME), "Expected no listeners to be present for event name 'tested', as we have dropped support for splitting plural event names.");
|
||||||
|
TKUnit.assert(receivedCount === 0, "Expected no event handlers to fire upon the 'propertyChange' event when listening for event name 'propertyChange , tested', as we have dropped support for listening to plural event names (and trimming whitespace in event names).");
|
||||||
|
|
||||||
obj.set('testName', 1);
|
obj.set('testName', 1);
|
||||||
obj.test();
|
obj.test();
|
||||||
|
|
||||||
obj.removeEventListener(events, callback);
|
obj.removeEventListener(events, callback);
|
||||||
|
|
||||||
TKUnit.assert(!obj.hasListeners(Observable.propertyChangeEvent), 'Expected result for hasObservers is false');
|
TKUnit.assert(!obj.hasListeners(events), "Expected the listener for event name 'propertyChange , tested' to have been removed, as we have dropped support for splitting plural event names.");
|
||||||
TKUnit.assert(!obj.hasListeners(TESTED_NAME), 'Expected result for hasObservers is false.');
|
|
||||||
|
|
||||||
obj.set('testName', 2);
|
obj.set('testName', 2);
|
||||||
obj.test();
|
obj.test();
|
||||||
|
|
||||||
TKUnit.assert(receivedCount === 2, 'Expected receive count is 2');
|
TKUnit.assert(receivedCount === 0, "Expected no event handlers to fire upon the 'propertyChange' event when listening for event name 'propertyChange , tested', as we have dropped support for listening to plural event names (and trimming whitespace in event names).");
|
||||||
};
|
};
|
||||||
|
|
||||||
export var test_Observable_removeEventListener_SingleEvent_NoCallbackSpecified = function () {
|
export var test_Observable_removeEventListener_SingleEvent_NoCallbackSpecified = function () {
|
||||||
|
@ -89,8 +89,6 @@ const _globalEventHandlers: {
|
|||||||
};
|
};
|
||||||
} = {};
|
} = {};
|
||||||
|
|
||||||
const eventNamesRegex = /\s*,\s*/;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Observable is used when you want to be notified when a change occurs. Use on/off methods to add/remove listener.
|
* Observable is used when you want to be notified when a change occurs. Use on/off methods to add/remove listener.
|
||||||
* Please note that should you be using the `new Observable({})` constructor, it is **obsolete** since v3.0,
|
* Please note that should you be using the `new Observable({})` constructor, it is **obsolete** since v3.0,
|
||||||
@ -153,54 +151,53 @@ export class Observable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A basic method signature to hook an event listener (shortcut alias to the addEventListener method).
|
* A basic method signature to hook an event listener (shortcut alias to the addEventListener method).
|
||||||
* @param eventNames - String corresponding to events (e.g. "propertyChange"). Optionally could be used more events separated by `,` (e.g. "propertyChange", "change").
|
* @param eventName Name of the event to attach to.
|
||||||
* @param callback - Callback function which will be executed when event is raised.
|
* @param callback - Callback function which will be executed when event is raised.
|
||||||
* @param thisArg - An optional parameter which will be used as `this` context for callback execution.
|
* @param thisArg - An optional parameter which will be used as `this` context for callback execution.
|
||||||
*/
|
*/
|
||||||
public on(eventNames: string, callback: (data: EventData) => void, thisArg?: any): void {
|
public on(eventName: string, callback: (data: EventData) => void, thisArg?: any): void {
|
||||||
this.addEventListener(eventNames, callback, thisArg);
|
this.addEventListener(eventName, callback, thisArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds one-time listener function for the event named `event`.
|
* Adds one-time listener function for the event named `event`.
|
||||||
* @param event Name of the event to attach to.
|
* @param eventName Name of the event to attach to.
|
||||||
* @param callback A function to be called when the specified event is raised.
|
* @param callback A function to be called when the specified event is raised.
|
||||||
* @param thisArg An optional parameter which when set will be used as "this" in callback method call.
|
* @param thisArg An optional parameter which when set will be used as "this" in callback method call.
|
||||||
*/
|
*/
|
||||||
public once(event: string, callback: (data: EventData) => void, thisArg?: any): void {
|
public once(eventName: string, callback: (data: EventData) => void, thisArg?: any): void {
|
||||||
this.addEventListener(event, callback, thisArg, true);
|
this.addEventListener(eventName, callback, thisArg, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shortcut alias to the removeEventListener method.
|
* Shortcut alias to the removeEventListener method.
|
||||||
*/
|
*/
|
||||||
public off(eventNames: string, callback?: (data: EventData) => void, thisArg?: any): void {
|
public off(eventName: string, callback?: (data: EventData) => void, thisArg?: any): void {
|
||||||
this.removeEventListener(eventNames, callback, thisArg);
|
this.removeEventListener(eventName, callback, thisArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a listener for the specified event name.
|
* Adds a listener for the specified event name.
|
||||||
* @param eventNames Comma delimited names of the events to attach the listener to.
|
* @param eventName Name of the event to attach to.
|
||||||
* @param callback A function to be called when some of the specified event(s) is raised.
|
* @param callback A function to be called when some of the specified event(s) is raised.
|
||||||
* @param thisArg An optional parameter which when set will be used as "this" in callback method call.
|
* @param thisArg An optional parameter which when set will be used as "this" in callback method call.
|
||||||
*/
|
*/
|
||||||
public addEventListener(eventNames: string, callback: (data: EventData) => void, thisArg?: any, once?: boolean): void {
|
public addEventListener(eventName: string, callback: (data: EventData) => void, thisArg?: any, once?: boolean): void {
|
||||||
once = once || undefined;
|
once = once || undefined;
|
||||||
thisArg = thisArg || undefined;
|
thisArg = thisArg || undefined;
|
||||||
|
|
||||||
if (typeof eventNames !== 'string') {
|
if (typeof eventName !== 'string') {
|
||||||
throw new TypeError('Event name(s) must be a string.');
|
throw new TypeError('Event name must be a string.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof callback !== 'function') {
|
if (typeof callback !== 'function') {
|
||||||
throw new TypeError('Callback, if provided, must be a function.');
|
throw new TypeError('Callback, if provided, must be a function.');
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const eventName of eventNames.trim().split(eventNamesRegex)) {
|
|
||||||
const list = this._getEventList(eventName, true);
|
const list = this._getEventList(eventName, true);
|
||||||
if (Observable._indexOfListener(list, callback, thisArg) !== -1) {
|
if (Observable._indexOfListener(list, callback, thisArg) !== -1) {
|
||||||
// Already added.
|
// Already added.
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
list.push({
|
list.push({
|
||||||
@ -209,18 +206,17 @@ export class Observable {
|
|||||||
once,
|
once,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes listener(s) for the specified event name.
|
* Removes listener(s) for the specified event name.
|
||||||
* @param eventNames Comma delimited names of the events the specified listener is associated with.
|
* @param eventName Name of the event to attach to.
|
||||||
* @param callback An optional parameter pointing to a specific listener. If not defined, all listeners for the event names will be removed.
|
* @param callback An optional parameter pointing to a specific listener. If not defined, all listeners for the event names will be removed.
|
||||||
* @param thisArg An optional parameter which when set will be used to refine search of the correct callback which will be removed as event listener.
|
* @param thisArg An optional parameter which when set will be used to refine search of the correct callback which will be removed as event listener.
|
||||||
*/
|
*/
|
||||||
public removeEventListener(eventNames: string, callback?: (data: EventData) => void, thisArg?: any): void {
|
public removeEventListener(eventName: string, callback?: (data: EventData) => void, thisArg?: any): void {
|
||||||
thisArg = thisArg || undefined;
|
thisArg = thisArg || undefined;
|
||||||
|
|
||||||
if (typeof eventNames !== 'string') {
|
if (typeof eventName !== 'string') {
|
||||||
throw new TypeError('Events name(s) must be string.');
|
throw new TypeError('Events name(s) must be string.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,10 +224,9 @@ export class Observable {
|
|||||||
throw new TypeError('callback must be function.');
|
throw new TypeError('callback must be function.');
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const eventName of eventNames.trim().split(eventNamesRegex)) {
|
|
||||||
const entries = this._observers[eventName];
|
const entries = this._observers[eventName];
|
||||||
if (!entries) {
|
if (!entries) {
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Observable.innerRemoveEventListener(entries, callback, thisArg);
|
Observable.innerRemoveEventListener(entries, callback, thisArg);
|
||||||
@ -241,7 +236,6 @@ export class Observable {
|
|||||||
delete this._observers[eventName];
|
delete this._observers[eventName];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Please avoid using the static event-handling APIs as they will be removed
|
* Please avoid using the static event-handling APIs as they will be removed
|
||||||
@ -297,11 +291,11 @@ export class Observable {
|
|||||||
* in future.
|
* in future.
|
||||||
* @deprecated
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
public static removeEventListener(eventNames: string, callback?: (data: EventData) => void, thisArg?: any): void {
|
public static removeEventListener(eventName: string, callback?: (data: EventData) => void, thisArg?: any): void {
|
||||||
thisArg = thisArg || undefined;
|
thisArg = thisArg || undefined;
|
||||||
|
|
||||||
if (typeof eventNames !== 'string') {
|
if (typeof eventName !== 'string') {
|
||||||
throw new TypeError('Event name(s) must be a string.');
|
throw new TypeError('Event name must be a string.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (callback && typeof callback !== 'function') {
|
if (callback && typeof callback !== 'function') {
|
||||||
@ -310,10 +304,9 @@ export class Observable {
|
|||||||
|
|
||||||
const eventClass = this.name === 'Observable' ? '*' : this.name;
|
const eventClass = this.name === 'Observable' ? '*' : this.name;
|
||||||
|
|
||||||
for (const eventName of eventNames.trim().split(eventNamesRegex)) {
|
|
||||||
const entries = _globalEventHandlers?.[eventClass]?.[eventName];
|
const entries = _globalEventHandlers?.[eventClass]?.[eventName];
|
||||||
if (!entries) {
|
if (!entries) {
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Observable.innerRemoveEventListener(entries, callback, thisArg);
|
Observable.innerRemoveEventListener(entries, callback, thisArg);
|
||||||
@ -329,19 +322,18 @@ export class Observable {
|
|||||||
delete _globalEventHandlers[eventClass];
|
delete _globalEventHandlers[eventClass];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Please avoid using the static event-handling APIs as they will be removed
|
* Please avoid using the static event-handling APIs as they will be removed
|
||||||
* in future.
|
* in future.
|
||||||
* @deprecated
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
public static addEventListener(eventNames: string, callback: (data: EventData) => void, thisArg?: any, once?: boolean): void {
|
public static addEventListener(eventName: string, callback: (data: EventData) => void, thisArg?: any, once?: boolean): void {
|
||||||
once = once || undefined;
|
once = once || undefined;
|
||||||
thisArg = thisArg || undefined;
|
thisArg = thisArg || undefined;
|
||||||
|
|
||||||
if (typeof eventNames !== 'string') {
|
if (typeof eventName !== 'string') {
|
||||||
throw new TypeError('Event name(s) must be a string.');
|
throw new TypeError('Event name must be a string.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof callback !== 'function') {
|
if (typeof callback !== 'function') {
|
||||||
@ -353,7 +345,6 @@ export class Observable {
|
|||||||
_globalEventHandlers[eventClass] = {};
|
_globalEventHandlers[eventClass] = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const eventName of eventNames.trim().split(eventNamesRegex)) {
|
|
||||||
if (!_globalEventHandlers[eventClass][eventName]) {
|
if (!_globalEventHandlers[eventClass][eventName]) {
|
||||||
_globalEventHandlers[eventClass][eventName] = [];
|
_globalEventHandlers[eventClass][eventName] = [];
|
||||||
}
|
}
|
||||||
@ -364,7 +355,6 @@ export class Observable {
|
|||||||
|
|
||||||
_globalEventHandlers[eventClass][eventName].push({ callback, thisArg, once });
|
_globalEventHandlers[eventClass][eventName].push({ callback, thisArg, once });
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private _globalNotify<T extends EventData>(eventClass: string, eventType: string, data: T): void {
|
private _globalNotify<T extends EventData>(eventClass: string, eventType: string, data: T): void {
|
||||||
// Check for the Global handlers for JUST this class
|
// Check for the Global handlers for JUST this class
|
||||||
@ -464,11 +454,9 @@ export class Observable {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public _emit(eventNames: string): void {
|
public _emit(eventName: string): void {
|
||||||
for (const eventName of eventNames.trim().split(eventNamesRegex)) {
|
|
||||||
this.notify({ eventName, object: this });
|
this.notify({ eventName, object: this });
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private _getEventList(eventName: string, createIfNeeded?: boolean): Array<ListenerEntry> | undefined {
|
private _getEventList(eventName: string, createIfNeeded?: boolean): Array<ListenerEntry> | undefined {
|
||||||
if (!eventName) {
|
if (!eventName) {
|
||||||
|
@ -4,6 +4,7 @@ import { ViewBase } from '../view-base';
|
|||||||
// Requires
|
// Requires
|
||||||
import { unsetValue } from '../properties';
|
import { unsetValue } from '../properties';
|
||||||
import { Observable, PropertyChangeData } from '../../../data/observable';
|
import { Observable, PropertyChangeData } from '../../../data/observable';
|
||||||
|
import { fromString as gestureFromString } from '../../../ui/gestures/gestures-common';
|
||||||
import { addWeakEventListener, removeWeakEventListener } from '../weak-event-listener';
|
import { addWeakEventListener, removeWeakEventListener } from '../weak-event-listener';
|
||||||
import { bindingConstants, parentsRegex } from '../../builder/binding-builder';
|
import { bindingConstants, parentsRegex } from '../../builder/binding-builder';
|
||||||
import { escapeRegexSymbols } from '../../../utils';
|
import { escapeRegexSymbols } from '../../../utils';
|
||||||
@ -87,25 +88,45 @@ export interface ValueConverter {
|
|||||||
toView: (...params: any[]) => any;
|
toView: (...params: any[]) => any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalizes "ontap" to "tap", and "ondoubletap" to "ondoubletap".
|
||||||
|
*
|
||||||
|
* Removes the leading "on" from an event gesture name, for example:
|
||||||
|
* - "ontap" -> "tap"
|
||||||
|
* - "ondoubletap" -> "doubletap"
|
||||||
|
* - "onTap" -> "Tap"
|
||||||
|
*
|
||||||
|
* Be warned that, as event/gesture names in NativeScript are case-sensitive,
|
||||||
|
* this may produce an invalid event/gesture name (i.e. "doubletap" would fail
|
||||||
|
* to match the "doubleTap" gesture name), and so it is up to the consumer to
|
||||||
|
* handle the output properly.
|
||||||
|
*/
|
||||||
export function getEventOrGestureName(name: string): string {
|
export function getEventOrGestureName(name: string): string {
|
||||||
return name.indexOf('on') === 0 ? name.substr(2, name.length - 2) : name;
|
return name.indexOf('on') === 0 ? name.slice(2) : name;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: method fromString from "ui/gestures";
|
|
||||||
export function isGesture(eventOrGestureName: string): boolean {
|
export function isGesture(eventOrGestureName: string): boolean {
|
||||||
|
// Not sure whether this trimming and lowercasing is still needed in practice
|
||||||
|
// (all Core tests pass without it), so worth revisiting in future. I think
|
||||||
|
// this is used exclusively by the XML flavour, and my best guess is that
|
||||||
|
// maybe it's to handle how getEventOrGestureName("onTap") might pass "Tap"
|
||||||
|
// into this.
|
||||||
const t = eventOrGestureName.trim().toLowerCase();
|
const t = eventOrGestureName.trim().toLowerCase();
|
||||||
|
|
||||||
|
// Would be nice to have a convenience function for getting all GestureState
|
||||||
|
// names in `gestures-common.ts`, but when I tried introducing it, it created
|
||||||
|
// a circular dependency that crashed the automated tests app.
|
||||||
return t === 'tap' || t === 'doubletap' || t === 'pinch' || t === 'pan' || t === 'swipe' || t === 'rotation' || t === 'longpress' || t === 'touch';
|
return t === 'tap' || t === 'doubletap' || t === 'pinch' || t === 'pan' || t === 'swipe' || t === 'rotation' || t === 'longpress' || t === 'touch';
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Make this instance function so that we dont need public statc tapEvent = "tap"
|
// TODO: Make this instance function so that we dont need public static tapEvent = "tap"
|
||||||
// in controls. They will just override this one and provide their own event support.
|
// in controls. They will just override this one and provide their own event support.
|
||||||
export function isEventOrGesture(name: string, view: ViewBase): boolean {
|
export function isEventOrGesture(name: string, view: ViewBase): boolean {
|
||||||
if (typeof name === 'string') {
|
if (typeof name === 'string') {
|
||||||
const eventOrGestureName = getEventOrGestureName(name);
|
const eventOrGestureName = getEventOrGestureName(name);
|
||||||
const evt = `${eventOrGestureName}Event`;
|
const evt = `${eventOrGestureName}Event`;
|
||||||
|
|
||||||
return (view.constructor && evt in view.constructor) || isGesture(eventOrGestureName.toLowerCase());
|
return (view.constructor && evt in view.constructor) || isGesture(eventOrGestureName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -305,11 +305,11 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
|
|||||||
|
|
||||||
// Coerce "tap" -> GestureTypes.tap
|
// Coerce "tap" -> GestureTypes.tap
|
||||||
// Coerce "loaded" -> undefined
|
// Coerce "loaded" -> undefined
|
||||||
const gesture: GestureTypes | undefined = gestureFromString(normalizedName);
|
const gestureType: GestureTypes | undefined = gestureFromString(normalizedName);
|
||||||
|
|
||||||
// If it's a gesture (and this Observable declares e.g. `static tapEvent`)
|
// If it's a gesture (and this Observable declares e.g. `static tapEvent`)
|
||||||
if (gesture && !this._isEvent(normalizedName)) {
|
if (gestureType && !this._isEvent(normalizedName)) {
|
||||||
this._observe(gesture, callback, thisArg);
|
this._observe(gestureType, callback, thisArg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -324,11 +324,11 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
|
|||||||
|
|
||||||
// Coerce "tap" -> GestureTypes.tap
|
// Coerce "tap" -> GestureTypes.tap
|
||||||
// Coerce "loaded" -> undefined
|
// Coerce "loaded" -> undefined
|
||||||
const gesture: GestureTypes | undefined = gestureFromString(normalizedName);
|
const gestureType: GestureTypes | undefined = gestureFromString(normalizedName);
|
||||||
|
|
||||||
// If it's a gesture (and this Observable declares e.g. `static tapEvent`)
|
// If it's a gesture (and this Observable declares e.g. `static tapEvent`)
|
||||||
if (gesture && !this._isEvent(normalizedName)) {
|
if (gestureType && !this._isEvent(normalizedName)) {
|
||||||
this._disconnectGestureObservers(gesture, callback, thisArg);
|
this._disconnectGestureObservers(gestureType, callback, thisArg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,59 +280,45 @@ export interface RotationGestureEventData extends GestureEventDataWithState {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a string representation of a gesture type.
|
* Returns a string representation of a gesture type.
|
||||||
* @param type - Type of the gesture.
|
* @param type - The singular type of the gesture. Looks for an exact match, so
|
||||||
* @param separator(optional) - Text separator between gesture type strings.
|
* passing plural types like `GestureTypes.tap & GestureTypes.doubleTap` will
|
||||||
|
* simply return undefined.
|
||||||
*/
|
*/
|
||||||
export function toString(type: GestureTypes, separator?: string): string {
|
export function toString(type: GestureTypes): (typeof GestureTypes)[GestureTypes] | undefined {
|
||||||
// We can get stronger typings with `keyof typeof GestureTypes`, but sadly
|
switch (type) {
|
||||||
// indexing into an enum simply returns `string`, so we'd have to type-assert
|
case GestureTypes.tap:
|
||||||
// all of the below anyway. Even this `(typeof GestureTypes)[GestureTypes]` is
|
return GestureTypes[GestureTypes.tap];
|
||||||
// more for documentation than for type-safety (it resolves to `string`, too).
|
|
||||||
const types = new Array<(typeof GestureTypes)[GestureTypes]>();
|
|
||||||
|
|
||||||
if (type & GestureTypes.tap) {
|
case GestureTypes.doubleTap:
|
||||||
types.push(GestureTypes[GestureTypes.tap]);
|
return GestureTypes[GestureTypes.doubleTap];
|
||||||
|
|
||||||
|
case GestureTypes.pinch:
|
||||||
|
return GestureTypes[GestureTypes.pinch];
|
||||||
|
|
||||||
|
case GestureTypes.pan:
|
||||||
|
return GestureTypes[GestureTypes.pan];
|
||||||
|
|
||||||
|
case GestureTypes.swipe:
|
||||||
|
return GestureTypes[GestureTypes.swipe];
|
||||||
|
|
||||||
|
case GestureTypes.rotation:
|
||||||
|
return GestureTypes[GestureTypes.rotation];
|
||||||
|
|
||||||
|
case GestureTypes.longPress:
|
||||||
|
return GestureTypes[GestureTypes.longPress];
|
||||||
|
|
||||||
|
case GestureTypes.touch:
|
||||||
|
return GestureTypes[GestureTypes.touch];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type & GestureTypes.doubleTap) {
|
|
||||||
types.push(GestureTypes[GestureTypes.doubleTap]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type & GestureTypes.pinch) {
|
|
||||||
types.push(GestureTypes[GestureTypes.pinch]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type & GestureTypes.pan) {
|
|
||||||
types.push(GestureTypes[GestureTypes.pan]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type & GestureTypes.swipe) {
|
|
||||||
types.push(GestureTypes[GestureTypes.swipe]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type & GestureTypes.rotation) {
|
|
||||||
types.push(GestureTypes[GestureTypes.rotation]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type & GestureTypes.longPress) {
|
|
||||||
types.push(GestureTypes[GestureTypes.longPress]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type & GestureTypes.touch) {
|
|
||||||
types.push(GestureTypes[GestureTypes.touch]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return types.join(separator);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: toString could return the text of multiple GestureTypes.
|
|
||||||
// Souldn't fromString do split on separator and return multiple GestureTypes?
|
|
||||||
/**
|
/**
|
||||||
* Returns a gesture type enum value from a string (case insensitive).
|
* Returns a gesture type enum value from a string (case insensitive).
|
||||||
* @param type - A string representation of a gesture type (e.g. Tap).
|
*
|
||||||
|
* @param type - A string representation of a single gesture type (e.g. "tap").
|
||||||
*/
|
*/
|
||||||
export function fromString(type: string): GestureTypes | undefined {
|
export function fromString(type: (typeof GestureTypes)[GestureTypes]): GestureTypes | undefined {
|
||||||
return GestureTypes[type.trim()];
|
return GestureTypes[type];
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class GesturesObserverBase implements GesturesObserverDefinition {
|
export abstract class GesturesObserverBase implements GesturesObserverDefinition {
|
||||||
@ -340,7 +326,8 @@ export abstract class GesturesObserverBase implements GesturesObserverDefinition
|
|||||||
private _target: View;
|
private _target: View;
|
||||||
private _context?: any;
|
private _context?: any;
|
||||||
|
|
||||||
public type: GestureTypes;
|
/** This is populated on the first call to observe(). */
|
||||||
|
type: GestureTypes;
|
||||||
|
|
||||||
public get callback(): (args: GestureEventData) => void {
|
public get callback(): (args: GestureEventData) => void {
|
||||||
return this._callback;
|
return this._callback;
|
||||||
|
@ -27,14 +27,14 @@ function initializeTapAndDoubleTapGestureListener() {
|
|||||||
class TapAndDoubleTapGestureListenerImpl extends android.view.GestureDetector.SimpleOnGestureListener {
|
class TapAndDoubleTapGestureListenerImpl extends android.view.GestureDetector.SimpleOnGestureListener {
|
||||||
private _observer: GesturesObserver;
|
private _observer: GesturesObserver;
|
||||||
private _target: View;
|
private _target: View;
|
||||||
private _type: number;
|
private _type: GestureTypes;
|
||||||
|
|
||||||
private _lastUpTime = 0;
|
private _lastUpTime = 0;
|
||||||
private _tapTimeoutId: number;
|
private _tapTimeoutId: number;
|
||||||
|
|
||||||
private static DoubleTapTimeout = android.view.ViewConfiguration.getDoubleTapTimeout();
|
private static DoubleTapTimeout = android.view.ViewConfiguration.getDoubleTapTimeout();
|
||||||
|
|
||||||
constructor(observer: GesturesObserver, target: View, type: number) {
|
constructor(observer: GesturesObserver, target: View, type: GestureTypes) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this._observer = observer;
|
this._observer = observer;
|
||||||
@ -61,7 +61,7 @@ function initializeTapAndDoubleTapGestureListener() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public onLongPress(motionEvent: android.view.MotionEvent): void {
|
public onLongPress(motionEvent: android.view.MotionEvent): void {
|
||||||
if (this._type & GestureTypes.longPress) {
|
if (this._type === GestureTypes.longPress) {
|
||||||
const args = _getLongPressArgs(GestureTypes.longPress, this._target, GestureStateTypes.began, motionEvent);
|
const args = _getLongPressArgs(GestureTypes.longPress, this._target, GestureStateTypes.began, motionEvent);
|
||||||
_executeCallback(this._observer, args);
|
_executeCallback(this._observer, args);
|
||||||
}
|
}
|
||||||
@ -70,14 +70,14 @@ function initializeTapAndDoubleTapGestureListener() {
|
|||||||
private _handleSingleTap(motionEvent: android.view.MotionEvent): void {
|
private _handleSingleTap(motionEvent: android.view.MotionEvent): void {
|
||||||
if (this._target.getGestureObservers(GestureTypes.doubleTap)) {
|
if (this._target.getGestureObservers(GestureTypes.doubleTap)) {
|
||||||
this._tapTimeoutId = timer.setTimeout(() => {
|
this._tapTimeoutId = timer.setTimeout(() => {
|
||||||
if (this._type & GestureTypes.tap) {
|
if (this._type === GestureTypes.tap) {
|
||||||
const args = _getTapArgs(GestureTypes.tap, this._target, motionEvent);
|
const args = _getTapArgs(GestureTypes.tap, this._target, motionEvent);
|
||||||
_executeCallback(this._observer, args);
|
_executeCallback(this._observer, args);
|
||||||
}
|
}
|
||||||
timer.clearTimeout(this._tapTimeoutId);
|
timer.clearTimeout(this._tapTimeoutId);
|
||||||
}, TapAndDoubleTapGestureListenerImpl.DoubleTapTimeout);
|
}, TapAndDoubleTapGestureListenerImpl.DoubleTapTimeout);
|
||||||
} else {
|
} else {
|
||||||
if (this._type & GestureTypes.tap) {
|
if (this._type === GestureTypes.tap) {
|
||||||
const args = _getTapArgs(GestureTypes.tap, this._target, motionEvent);
|
const args = _getTapArgs(GestureTypes.tap, this._target, motionEvent);
|
||||||
_executeCallback(this._observer, args);
|
_executeCallback(this._observer, args);
|
||||||
}
|
}
|
||||||
@ -88,7 +88,7 @@ function initializeTapAndDoubleTapGestureListener() {
|
|||||||
if (this._tapTimeoutId) {
|
if (this._tapTimeoutId) {
|
||||||
timer.clearTimeout(this._tapTimeoutId);
|
timer.clearTimeout(this._tapTimeoutId);
|
||||||
}
|
}
|
||||||
if (this._type & GestureTypes.doubleTap) {
|
if (this._type === GestureTypes.doubleTap) {
|
||||||
const args = _getTapArgs(GestureTypes.doubleTap, this._target, motionEvent);
|
const args = _getTapArgs(GestureTypes.doubleTap, this._target, motionEvent);
|
||||||
_executeCallback(this._observer, args);
|
_executeCallback(this._observer, args);
|
||||||
}
|
}
|
||||||
@ -252,12 +252,16 @@ export class GesturesObserver extends GesturesObserverBase {
|
|||||||
private _onTargetUnloaded: (data: EventData) => void;
|
private _onTargetUnloaded: (data: EventData) => void;
|
||||||
|
|
||||||
public observe(type: GestureTypes) {
|
public observe(type: GestureTypes) {
|
||||||
if (this.target) {
|
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this._onTargetLoaded = (args) => {
|
|
||||||
|
if (!this.target) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._onTargetLoaded = () => {
|
||||||
this._attach(this.target, type);
|
this._attach(this.target, type);
|
||||||
};
|
};
|
||||||
this._onTargetUnloaded = (args) => {
|
this._onTargetUnloaded = () => {
|
||||||
this._detach();
|
this._detach();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -268,7 +272,6 @@ export class GesturesObserver extends GesturesObserverBase {
|
|||||||
this._attach(this.target, type);
|
this._attach(this.target, type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public disconnect() {
|
public disconnect() {
|
||||||
this._detach();
|
this._detach();
|
||||||
@ -280,6 +283,7 @@ export class GesturesObserver extends GesturesObserverBase {
|
|||||||
this._onTargetLoaded = null;
|
this._onTargetLoaded = null;
|
||||||
this._onTargetUnloaded = null;
|
this._onTargetUnloaded = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// clears target, context and callback references
|
// clears target, context and callback references
|
||||||
super.disconnect();
|
super.disconnect();
|
||||||
}
|
}
|
||||||
@ -297,43 +301,57 @@ export class GesturesObserver extends GesturesObserverBase {
|
|||||||
private _attach(target: View, type: GestureTypes) {
|
private _attach(target: View, type: GestureTypes) {
|
||||||
this._detach();
|
this._detach();
|
||||||
|
|
||||||
let recognizer;
|
let recognizer: unknown;
|
||||||
|
|
||||||
if (type & GestureTypes.tap || type & GestureTypes.doubleTap || type & GestureTypes.longPress) {
|
switch (type) {
|
||||||
|
// Whether it's a tap, doubleTap, or longPress, we handle with the same
|
||||||
|
// listener. It'll listen for all three of these gesture types, but only
|
||||||
|
// notify if the type it was registered with matched the relevant gesture.
|
||||||
|
case GestureTypes.tap:
|
||||||
|
case GestureTypes.doubleTap:
|
||||||
|
case GestureTypes.longPress: {
|
||||||
initializeTapAndDoubleTapGestureListener();
|
initializeTapAndDoubleTapGestureListener();
|
||||||
recognizer = this._simpleGestureDetector = <any>new androidx.core.view.GestureDetectorCompat(target._context, new TapAndDoubleTapGestureListener(this, this.target, type));
|
recognizer = this._simpleGestureDetector = <any>new androidx.core.view.GestureDetectorCompat(target._context, new TapAndDoubleTapGestureListener(this, this.target, type));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
case GestureTypes.pinch: {
|
||||||
if (type & GestureTypes.pinch) {
|
|
||||||
initializePinchGestureListener();
|
initializePinchGestureListener();
|
||||||
recognizer = this._scaleGestureDetector = new android.view.ScaleGestureDetector(target._context, new PinchGestureListener(this, this.target));
|
recognizer = this._scaleGestureDetector = new android.view.ScaleGestureDetector(target._context, new PinchGestureListener(this, this.target));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type & GestureTypes.swipe) {
|
case GestureTypes.swipe: {
|
||||||
initializeSwipeGestureListener();
|
initializeSwipeGestureListener();
|
||||||
recognizer = this._swipeGestureDetector = <any>new androidx.core.view.GestureDetectorCompat(target._context, new SwipeGestureListener(this, this.target));
|
recognizer = this._swipeGestureDetector = <any>new androidx.core.view.GestureDetectorCompat(target._context, new SwipeGestureListener(this, this.target));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type & GestureTypes.pan) {
|
case GestureTypes.pan: {
|
||||||
recognizer = this._panGestureDetector = new CustomPanGestureDetector(this, this.target);
|
recognizer = this._panGestureDetector = new CustomPanGestureDetector(this, this.target);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type & GestureTypes.rotation) {
|
case GestureTypes.rotation: {
|
||||||
recognizer = this._rotateGestureDetector = new CustomRotateGestureDetector(this, this.target);
|
recognizer = this._rotateGestureDetector = new CustomRotateGestureDetector(this, this.target);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type & GestureTypes.touch) {
|
case GestureTypes.touch: {
|
||||||
this._notifyTouch = true;
|
this._notifyTouch = true;
|
||||||
} else {
|
// For touch events, return early rather than breaking from the switch
|
||||||
|
// statement.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.target.notify({
|
this.target.notify({
|
||||||
eventName: GestureEvents.gestureAttached,
|
eventName: GestureEvents.gestureAttached,
|
||||||
object: this.target,
|
object: this.target,
|
||||||
type,
|
type: type,
|
||||||
view: this.target,
|
view: this.target,
|
||||||
android: recognizer,
|
android: recognizer,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public androidOnTouchEvent(motionEvent: android.view.MotionEvent) {
|
public androidOnTouchEvent(motionEvent: android.view.MotionEvent) {
|
||||||
if (this._notifyTouch) {
|
if (this._notifyTouch) {
|
||||||
@ -430,7 +448,13 @@ class PinchGestureEventData implements PinchGestureEventData {
|
|||||||
public eventName = toString(GestureTypes.pinch);
|
public eventName = toString(GestureTypes.pinch);
|
||||||
public ios;
|
public ios;
|
||||||
|
|
||||||
constructor(public view: View, public android: android.view.ScaleGestureDetector, public scale: number, public object: any, public state: GestureStateTypes) {}
|
constructor(
|
||||||
|
public view: View,
|
||||||
|
public android: android.view.ScaleGestureDetector,
|
||||||
|
public scale: number,
|
||||||
|
public object: any,
|
||||||
|
public state: GestureStateTypes,
|
||||||
|
) {}
|
||||||
|
|
||||||
getFocusX(): number {
|
getFocusX(): number {
|
||||||
return this.android.getFocusX() / layout.getDisplayDensity();
|
return this.android.getFocusX() / layout.getDisplayDensity();
|
||||||
@ -669,7 +693,10 @@ class Pointer implements Pointer {
|
|||||||
public android: number;
|
public android: number;
|
||||||
public ios: any = undefined;
|
public ios: any = undefined;
|
||||||
|
|
||||||
constructor(id: number, private event: android.view.MotionEvent) {
|
constructor(
|
||||||
|
id: number,
|
||||||
|
private event: android.view.MotionEvent,
|
||||||
|
) {
|
||||||
this.android = id;
|
this.android = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
4
packages/core/ui/gestures/index.d.ts
vendored
4
packages/core/ui/gestures/index.d.ts
vendored
@ -27,7 +27,9 @@ export class GesturesObserver {
|
|||||||
disconnect();
|
disconnect();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gesture type attached to the observer.
|
* Singular gesture type (e.g. GestureTypes.tap) attached to the observer.
|
||||||
|
* Does not support plural gesture types (e.g.
|
||||||
|
* GestureTypes.tap & GestureTypes.doubleTap).
|
||||||
*/
|
*/
|
||||||
type: GestureTypes;
|
type: GestureTypes;
|
||||||
|
|
||||||
|
@ -91,27 +91,32 @@ class UIGestureRecognizerImpl extends NSObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class GesturesObserver extends GesturesObserverBase {
|
export class GesturesObserver extends GesturesObserverBase {
|
||||||
private _recognizers: {};
|
private readonly _recognizers: { [type: string]: RecognizerCache } = {};
|
||||||
|
|
||||||
private _onTargetLoaded: (data: EventData) => void;
|
private _onTargetLoaded: (data: EventData) => void;
|
||||||
private _onTargetUnloaded: (data: EventData) => void;
|
private _onTargetUnloaded: (data: EventData) => void;
|
||||||
|
|
||||||
constructor(target: View, callback: (args: GestureEventData) => void, context: any) {
|
|
||||||
super(target, callback, context);
|
|
||||||
this._recognizers = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
public androidOnTouchEvent(motionEvent: android.view.MotionEvent): void {
|
public androidOnTouchEvent(motionEvent: android.view.MotionEvent): void {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observes a singular GestureTypes value (e.g. GestureTypes.tap).
|
||||||
|
*
|
||||||
|
* Does not support observing plural GestureTypes values, e.g.
|
||||||
|
* GestureTypes.tap & GestureTypes.doubleTap.
|
||||||
|
*/
|
||||||
public observe(type: GestureTypes) {
|
public observe(type: GestureTypes) {
|
||||||
if (this.target) {
|
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this._onTargetLoaded = (args) => {
|
|
||||||
|
if (!this.target) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._onTargetLoaded = () => {
|
||||||
this._attach(this.target, type);
|
this._attach(this.target, type);
|
||||||
};
|
};
|
||||||
this._onTargetUnloaded = (args) => {
|
this._onTargetUnloaded = () => {
|
||||||
this._detach();
|
this._detach();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -122,55 +127,69 @@ export class GesturesObserver extends GesturesObserverBase {
|
|||||||
this._attach(this.target, type);
|
this._attach(this.target, type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a singular GestureTypes value (e.g. GestureTypes.tap), adds a
|
||||||
|
* UIGestureRecognizer for it and populates a RecognizerCache entry in
|
||||||
|
* this._recognizers.
|
||||||
|
*
|
||||||
|
* Does not support attaching plural GestureTypes values, e.g.
|
||||||
|
* GestureTypes.tap & GestureTypes.doubleTap.
|
||||||
|
*/
|
||||||
private _attach(target: View, type: GestureTypes) {
|
private _attach(target: View, type: GestureTypes) {
|
||||||
this._detach();
|
this._detach();
|
||||||
|
|
||||||
if (target && target.nativeViewProtected && target.nativeViewProtected.addGestureRecognizer) {
|
const nativeView = target?.nativeViewProtected as UIView | undefined;
|
||||||
const nativeView = <UIView>target.nativeViewProtected;
|
if (!nativeView?.addGestureRecognizer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (type & GestureTypes.tap) {
|
switch (type) {
|
||||||
|
case GestureTypes.tap: {
|
||||||
nativeView.addGestureRecognizer(
|
nativeView.addGestureRecognizer(
|
||||||
this._createRecognizer(GestureTypes.tap, (args) => {
|
this._createRecognizer(GestureTypes.tap, (args) => {
|
||||||
if (args.view) {
|
if (args.view) {
|
||||||
this._executeCallback(_getTapData(args));
|
this._executeCallback(_getTapData(args));
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type & GestureTypes.doubleTap) {
|
case GestureTypes.doubleTap: {
|
||||||
nativeView.addGestureRecognizer(
|
nativeView.addGestureRecognizer(
|
||||||
this._createRecognizer(GestureTypes.doubleTap, (args) => {
|
this._createRecognizer(GestureTypes.doubleTap, (args) => {
|
||||||
if (args.view) {
|
if (args.view) {
|
||||||
this._executeCallback(_getTapData(args));
|
this._executeCallback(_getTapData(args));
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type & GestureTypes.pinch) {
|
case GestureTypes.pinch: {
|
||||||
nativeView.addGestureRecognizer(
|
nativeView.addGestureRecognizer(
|
||||||
this._createRecognizer(GestureTypes.pinch, (args) => {
|
this._createRecognizer(GestureTypes.pinch, (args) => {
|
||||||
if (args.view) {
|
if (args.view) {
|
||||||
this._executeCallback(_getPinchData(args));
|
this._executeCallback(_getPinchData(args));
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type & GestureTypes.pan) {
|
case GestureTypes.pan: {
|
||||||
nativeView.addGestureRecognizer(
|
nativeView.addGestureRecognizer(
|
||||||
this._createRecognizer(GestureTypes.pan, (args) => {
|
this._createRecognizer(GestureTypes.pan, (args) => {
|
||||||
if (args.view) {
|
if (args.view) {
|
||||||
this._executeCallback(_getPanData(args, target.nativeViewProtected));
|
this._executeCallback(_getPanData(args, target.nativeViewProtected));
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type & GestureTypes.swipe) {
|
case GestureTypes.swipe: {
|
||||||
nativeView.addGestureRecognizer(
|
nativeView.addGestureRecognizer(
|
||||||
this._createRecognizer(
|
this._createRecognizer(
|
||||||
GestureTypes.swipe,
|
GestureTypes.swipe,
|
||||||
@ -179,8 +198,8 @@ export class GesturesObserver extends GesturesObserverBase {
|
|||||||
this._executeCallback(_getSwipeData(args));
|
this._executeCallback(_getSwipeData(args));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UISwipeGestureRecognizerDirection.Down
|
UISwipeGestureRecognizerDirection.Down,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
nativeView.addGestureRecognizer(
|
nativeView.addGestureRecognizer(
|
||||||
@ -191,8 +210,8 @@ export class GesturesObserver extends GesturesObserverBase {
|
|||||||
this._executeCallback(_getSwipeData(args));
|
this._executeCallback(_getSwipeData(args));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UISwipeGestureRecognizerDirection.Left
|
UISwipeGestureRecognizerDirection.Left,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
nativeView.addGestureRecognizer(
|
nativeView.addGestureRecognizer(
|
||||||
@ -203,8 +222,8 @@ export class GesturesObserver extends GesturesObserverBase {
|
|||||||
this._executeCallback(_getSwipeData(args));
|
this._executeCallback(_getSwipeData(args));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UISwipeGestureRecognizerDirection.Right
|
UISwipeGestureRecognizerDirection.Right,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
nativeView.addGestureRecognizer(
|
nativeView.addGestureRecognizer(
|
||||||
@ -215,49 +234,49 @@ export class GesturesObserver extends GesturesObserverBase {
|
|||||||
this._executeCallback(_getSwipeData(args));
|
this._executeCallback(_getSwipeData(args));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UISwipeGestureRecognizerDirection.Up
|
UISwipeGestureRecognizerDirection.Up,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type & GestureTypes.rotation) {
|
case GestureTypes.rotation: {
|
||||||
nativeView.addGestureRecognizer(
|
nativeView.addGestureRecognizer(
|
||||||
this._createRecognizer(GestureTypes.rotation, (args) => {
|
this._createRecognizer(GestureTypes.rotation, (args) => {
|
||||||
if (args.view) {
|
if (args.view) {
|
||||||
this._executeCallback(_getRotationData(args));
|
this._executeCallback(_getRotationData(args));
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type & GestureTypes.longPress) {
|
case GestureTypes.longPress: {
|
||||||
nativeView.addGestureRecognizer(
|
nativeView.addGestureRecognizer(
|
||||||
this._createRecognizer(GestureTypes.longPress, (args) => {
|
this._createRecognizer(GestureTypes.longPress, (args) => {
|
||||||
if (args.view) {
|
if (args.view) {
|
||||||
this._executeCallback(_getLongPressData(args));
|
this._executeCallback(_getLongPressData(args));
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type & GestureTypes.touch) {
|
case GestureTypes.touch: {
|
||||||
nativeView.addGestureRecognizer(this._createRecognizer(GestureTypes.touch));
|
nativeView.addGestureRecognizer(this._createRecognizer(GestureTypes.touch));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _detach() {
|
private _detach() {
|
||||||
if (this.target && this.target.nativeViewProtected) {
|
for (const type in this._recognizers) {
|
||||||
for (const name in this._recognizers) {
|
const item = this._recognizers[type];
|
||||||
if (this._recognizers.hasOwnProperty(name)) {
|
this.target?.nativeViewProtected?.removeGestureRecognizer(item.recognizer);
|
||||||
const item = <RecognizerCache>this._recognizers[name];
|
|
||||||
this.target.nativeViewProtected.removeGestureRecognizer(item.recognizer);
|
|
||||||
|
|
||||||
item.recognizer = null;
|
item.recognizer = null;
|
||||||
item.target = null;
|
item.target = null;
|
||||||
}
|
delete this._recognizers[type];
|
||||||
}
|
|
||||||
this._recognizers = {};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,6 +290,7 @@ export class GesturesObserver extends GesturesObserverBase {
|
|||||||
this._onTargetLoaded = null;
|
this._onTargetLoaded = null;
|
||||||
this._onTargetUnloaded = null;
|
this._onTargetUnloaded = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// clears target, context and callback references
|
// clears target, context and callback references
|
||||||
super.disconnect();
|
super.disconnect();
|
||||||
}
|
}
|
||||||
@ -281,9 +301,14 @@ export class GesturesObserver extends GesturesObserverBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createRecognizer(type: GestureTypes, callback?: (args: GestureEventData) => void, swipeDirection?: UISwipeGestureRecognizerDirection): UIGestureRecognizer {
|
/**
|
||||||
let recognizer: UIGestureRecognizer;
|
* Creates a UIGestureRecognizer (and populates a RecognizerCache entry in
|
||||||
let name = toString(type);
|
* this._recognizers) corresponding to the singular GestureTypes value passed
|
||||||
|
* in.
|
||||||
|
*/
|
||||||
|
private _createRecognizer(type: GestureTypes, callback?: (args: GestureEventData) => void, swipeDirection?: UISwipeGestureRecognizerDirection): UIGestureRecognizer | undefined {
|
||||||
|
let recognizer: UIGestureRecognizer | undefined;
|
||||||
|
let typeString = toString(type);
|
||||||
const target = _createUIGestureRecognizerTarget(this, type, callback, this.context);
|
const target = _createUIGestureRecognizerTarget(this, type, callback, this.context);
|
||||||
const recognizerType = _getUIGestureRecognizerType(type);
|
const recognizerType = _getUIGestureRecognizerType(type);
|
||||||
|
|
||||||
@ -291,7 +316,8 @@ export class GesturesObserver extends GesturesObserverBase {
|
|||||||
recognizer = recognizerType.alloc().initWithTargetAction(target, 'recognize');
|
recognizer = recognizerType.alloc().initWithTargetAction(target, 'recognize');
|
||||||
|
|
||||||
if (type === GestureTypes.swipe && swipeDirection) {
|
if (type === GestureTypes.swipe && swipeDirection) {
|
||||||
name = name + swipeDirection.toString();
|
// e.g. "swipe1"
|
||||||
|
typeString += swipeDirection.toString();
|
||||||
(<UISwipeGestureRecognizer>recognizer).direction = swipeDirection;
|
(<UISwipeGestureRecognizer>recognizer).direction = swipeDirection;
|
||||||
} else if (type === GestureTypes.touch) {
|
} else if (type === GestureTypes.touch) {
|
||||||
(<TouchGestureRecognizer>recognizer).observer = this;
|
(<TouchGestureRecognizer>recognizer).observer = this;
|
||||||
@ -301,16 +327,16 @@ export class GesturesObserver extends GesturesObserverBase {
|
|||||||
|
|
||||||
if (recognizer) {
|
if (recognizer) {
|
||||||
recognizer.delegate = recognizerDelegateInstance;
|
recognizer.delegate = recognizerDelegateInstance;
|
||||||
this._recognizers[name] = <RecognizerCache>{
|
this._recognizers[typeString] = {
|
||||||
recognizer: recognizer,
|
recognizer,
|
||||||
target: target,
|
target,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.target.notify({
|
this.target.notify({
|
||||||
eventName: GestureEvents.gestureAttached,
|
eventName: GestureEvents.gestureAttached,
|
||||||
object: this.target,
|
object: this.target,
|
||||||
type,
|
type: type,
|
||||||
view: this.target,
|
view: this.target,
|
||||||
ios: recognizer,
|
ios: recognizer,
|
||||||
});
|
});
|
||||||
@ -329,28 +355,27 @@ interface RecognizerCache {
|
|||||||
target: any;
|
target: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _getUIGestureRecognizerType(type: GestureTypes): any {
|
function _getUIGestureRecognizerType(type: GestureTypes): typeof UIGestureRecognizer | null {
|
||||||
let nativeType = null;
|
switch (type) {
|
||||||
|
case GestureTypes.tap:
|
||||||
if (type === GestureTypes.tap) {
|
return UITapGestureRecognizer;
|
||||||
nativeType = UITapGestureRecognizer;
|
case GestureTypes.doubleTap:
|
||||||
} else if (type === GestureTypes.doubleTap) {
|
return UITapGestureRecognizer;
|
||||||
nativeType = UITapGestureRecognizer;
|
case GestureTypes.pinch:
|
||||||
} else if (type === GestureTypes.pinch) {
|
return UIPinchGestureRecognizer;
|
||||||
nativeType = UIPinchGestureRecognizer;
|
case GestureTypes.pan:
|
||||||
} else if (type === GestureTypes.pan) {
|
return UIPanGestureRecognizer;
|
||||||
nativeType = UIPanGestureRecognizer;
|
case GestureTypes.swipe:
|
||||||
} else if (type === GestureTypes.swipe) {
|
return UISwipeGestureRecognizer;
|
||||||
nativeType = UISwipeGestureRecognizer;
|
case GestureTypes.rotation:
|
||||||
} else if (type === GestureTypes.rotation) {
|
return UIRotationGestureRecognizer;
|
||||||
nativeType = UIRotationGestureRecognizer;
|
case GestureTypes.longPress:
|
||||||
} else if (type === GestureTypes.longPress) {
|
return UILongPressGestureRecognizer;
|
||||||
nativeType = UILongPressGestureRecognizer;
|
case GestureTypes.touch:
|
||||||
} else if (type === GestureTypes.touch) {
|
return TouchGestureRecognizer;
|
||||||
nativeType = TouchGestureRecognizer;
|
default:
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nativeType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getState(recognizer: UIGestureRecognizer) {
|
function getState(recognizer: UIGestureRecognizer) {
|
||||||
|
Reference in New Issue
Block a user