diff --git a/CrossPlatformModules.csproj b/CrossPlatformModules.csproj
index ac544e11a..6a6ffe7f0 100644
--- a/CrossPlatformModules.csproj
+++ b/CrossPlatformModules.csproj
@@ -229,6 +229,10 @@
Designer
+
+
+ Designer
+
Designer
diff --git a/apps/ui-tests-app/mainPage.ts b/apps/ui-tests-app/mainPage.ts
index 43d742cf4..db06d6d60 100644
--- a/apps/ui-tests-app/mainPage.ts
+++ b/apps/ui-tests-app/mainPage.ts
@@ -79,6 +79,7 @@ examples.set("modalview", "modal-view/modal-view");
examples.set("nordic", "nordic/nordic");
examples.set("gestures", "pages/gestures");
+examples.set("touch", "pages/touch-event");
examples.set("handlers", "pages/handlers");
//examples.set("listview_binding", "pages/listview_binding");
examples.set("console", "pages/console");
diff --git a/apps/ui-tests-app/pages/touch-event.ts b/apps/ui-tests-app/pages/touch-event.ts
new file mode 100644
index 000000000..9105b0a2b
--- /dev/null
+++ b/apps/ui-tests-app/pages/touch-event.ts
@@ -0,0 +1,30 @@
+import { Page, View, TextView } from "ui";
+import gestures = require("ui/gestures");
+
+export function onTouch(args: gestures.TouchGestureEventData) {
+ var msg = " touch action: " + args.action +
+ " x: " + Math.round(args.getX()) + " y: " + Math.round(args.getY()) +
+ " count: " + args.getPointerCount();
+
+ var p;
+ msg += " ACTIVE: ";
+ var pointers = args.getActivePointers();
+ for (var index = 0; index < pointers.length; index++) {
+ p = pointers[index];
+ msg += " p" + index + "[" + Math.round(p.getX()) + ", " + Math.round(p.getY()) + "]"
+ }
+
+ msg += " ALL: ";
+ pointers = args.getAllPointers();
+ for (var index = 0; index < pointers.length; index++) {
+ p = pointers[index];
+ msg += " p" + index + "[" + Math.round(p.getX()) + ", " + Math.round(p.getY()) + "]"
+ }
+
+ console.log(msg);
+ (args.view.page.getViewById("output")).text += msg + "\n";
+}
+
+export function clear(args) {
+ args.object.page.getViewById("output").text = "";
+}
diff --git a/apps/ui-tests-app/pages/touch-event.xml b/apps/ui-tests-app/pages/touch-event.xml
new file mode 100644
index 000000000..2d29e7f3d
--- /dev/null
+++ b/apps/ui-tests-app/pages/touch-event.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
index 39af7a965..d7bf4287a 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -373,6 +373,7 @@
"apps/ui-tests-app/pages/i61.ts",
"apps/ui-tests-app/pages/i73.ts",
"apps/ui-tests-app/pages/listview_binding.ts",
+ "apps/ui-tests-app/pages/touch-event.ts",
"apps/ui-tests-app/segmented-bar/all.ts",
"apps/ui-tests-app/segmented-bar/clean.ts",
"apps/ui-tests-app/text-field/text-field.ts",
diff --git a/ui/core/view.android.ts b/ui/core/view.android.ts
index d6c2161c2..1d704c8e3 100644
--- a/ui/core/view.android.ts
+++ b/ui/core/view.android.ts
@@ -79,7 +79,7 @@ function onIsUserInteractionEnabledPropertyChanged(data: dependencyObservable.Pr
export class View extends viewCommon.View {
private _disableUserInteractionListener: android.view.View.OnTouchListener = new android.view.View.OnTouchListener({
- onTouch: function (view: android.view.View, event: android.view.MotionEvent) {
+ onTouch: function(view: android.view.View, event: android.view.MotionEvent) {
return true;
}
});
@@ -141,7 +141,7 @@ export class View extends viewCommon.View {
this._nativeView.setClickable(true);
}
this._nativeView.setOnTouchListener(new android.view.View.OnTouchListener({
- onTouch: function (view: android.view.View, motionEvent: android.view.MotionEvent) {
+ onTouch: function(view: android.view.View, motionEvent: android.view.MotionEvent) {
var owner = that.get();
if (!owner) {
return false;
@@ -201,7 +201,7 @@ export class View extends viewCommon.View {
if (this._childrenCount > 0) {
// Notify each child for the _onAttached event
var that = this;
- var eachChild = function (child: View): boolean {
+ var eachChild = function(child: View): boolean {
child._onAttached(context);
if (!child._isAddedToNativeVisualTree) {
// since we have lazy loading of the android widgets, we need to add the native instances at this point.
@@ -217,7 +217,7 @@ export class View extends viewCommon.View {
if (this._childrenCount > 0) {
// Detach children first
var that = this;
- var eachChild = function (child: View): boolean {
+ var eachChild = function(child: View): boolean {
if (child._isAddedToNativeVisualTree) {
that._removeViewFromNativeVisualTree(child);
}
@@ -397,7 +397,7 @@ export class CustomLayoutView extends View implements viewDefinition.CustomLayou
super._addViewToNativeVisualTree(child);
if (this._nativeView && child._nativeView) {
- var types : typeof typesModule = require("utils/types");
+ var types: typeof typesModule = require("utils/types");
if (types.isNullOrUndefined(atIndex) || atIndex >= this._nativeView.getChildCount()) {
this._nativeView.addView(child._nativeView);
diff --git a/ui/gestures/gestures-common.ts b/ui/gestures/gestures-common.ts
index 90226ec46..3e4a2ab86 100644
--- a/ui/gestures/gestures-common.ts
+++ b/ui/gestures/gestures-common.ts
@@ -8,7 +8,8 @@ export enum GestureTypes {
pan = 1 << 3,
swipe = 1 << 4,
rotation = 1 << 5,
- longPress = 1 << 6
+ longPress = 1 << 6,
+ touch = 1 << 7
}
export enum GestureStateTypes {
@@ -25,8 +26,15 @@ export enum SwipeDirection {
down = 1 << 3
}
-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);
+export module TouchAction {
+ export var down = "down";
+ export var up = "up";
+ export var move = "move";
+ export var cancel = "cancel";
+}
+
+export function observe(target: view.View, type: definition.GestureTypes, callback: (args: definition.GestureEventData) => void, context?: any): definition.GesturesObserver {
+ var observer = new definition.GesturesObserver(target, callback, context);
observer.observe(type);
return observer;
}
@@ -62,6 +70,10 @@ export function toString(type: GestureTypes, separator?: string): string {
types.push("longPress");
}
+ if (type & definition.GestureTypes.touch) {
+ types.push("touch");
+ }
+
return types.join(separator);
}
@@ -82,6 +94,8 @@ export function fromString(type: string): definition.GestureTypes {
return definition.GestureTypes.rotation;
} else if (t === "longpress") {
return definition.GestureTypes.longPress;
+ } else if (t === "touch") {
+ return definition.GestureTypes.touch;
}
return undefined;
diff --git a/ui/gestures/gestures.android.ts b/ui/gestures/gestures.android.ts
index d242beec4..abfe72b38 100644
--- a/ui/gestures/gestures.android.ts
+++ b/ui/gestures/gestures.android.ts
@@ -4,6 +4,7 @@ import observable = require("data/observable");
import view = require("ui/core/view");
import trace = require("trace");
import utils = require("utils/utils");
+import types = require("utils/types");
global.moduleMerge(common, exports);
@@ -13,13 +14,15 @@ const INVALID_POINTER_ID = -1;
const TO_DEGREES = (180 / Math.PI);
export class GesturesObserver extends common.GesturesObserver {
- private _onTouchListener: android.view.View.OnTouchListener;
+ private _notifyTouch: boolean;
private _simpleGestureDetector: android.view.GestureDetector;
private _scaleGestureDetector: android.view.ScaleGestureDetector;
private _swipeGestureDetector: android.view.GestureDetector;
private _panGestureDetector: CustomPanGestureDetector;
private _rotateGestureDetector: CustomRotateGestureDetector;
+ private _eventData: TouchGestureEventData;
+
private _onTargetLoaded: (data: observable.EventData) => void;
private _onTargetUnloaded: (data: observable.EventData) => void;
@@ -61,12 +64,13 @@ export class GesturesObserver extends common.GesturesObserver {
private _detach() {
trace.write(this.target + "._detach() android:" + this.target._nativeView, "gestures");
- this._onTouchListener = null;
+ this._notifyTouch = false
this._simpleGestureDetector = null;
this._scaleGestureDetector = null;
this._swipeGestureDetector = null;
this._panGestureDetector = null;
this._rotateGestureDetector = null;
+ this._eventData = null;
}
private _attach(target: view.View, type: definition.GestureTypes) {
@@ -92,9 +96,22 @@ export class GesturesObserver extends common.GesturesObserver {
if (type & definition.GestureTypes.rotation) {
this._rotateGestureDetector = new CustomRotateGestureDetector(this, this.target);
}
+
+ if (type & definition.GestureTypes.touch) {
+ this._notifyTouch = true;
+ }
}
public androidOnTouchEvent(motionEvent: android.view.MotionEvent) {
+ if (this._notifyTouch) {
+ if (!this._eventData) {
+ this._eventData = new TouchGestureEventData();
+ }
+
+ this._eventData.prepare(this.target, motionEvent);
+ _executeCallback(this, this._eventData);
+ }
+
if (this._simpleGestureDetector) {
this._simpleGestureDetector.onTouchEvent(motionEvent);
}
@@ -117,18 +134,6 @@ export class GesturesObserver extends common.GesturesObserver {
}
}
-function getState(e: android.view.MotionEvent): common.GestureStateTypes {
- if (e.getActionMasked() === android.view.MotionEvent.ACTION_DOWN) {
- return common.GestureStateTypes.began;
- } else if (e.getActionMasked() === android.view.MotionEvent.ACTION_CANCEL) {
- return common.GestureStateTypes.cancelled;
- } else if (e.getActionMasked() === android.view.MotionEvent.ACTION_MOVE) {
- return common.GestureStateTypes.changed;
- } else if (e.getActionMasked() === android.view.MotionEvent.ACTION_UP) {
- return common.GestureStateTypes.ended;
- }
-}
-
function _getArgs(type: definition.GestureTypes, view: view.View, e: android.view.MotionEvent): definition.GestureEventData {
return {
type: type,
@@ -586,4 +591,95 @@ class CustomRotateGestureDetector {
return Math.atan2((secondY - firstY), (secondX - firstX));
}
+}
+
+class Pointer implements definition.Pointer {
+ public android: number;
+ public ios: any = undefined;
+
+ constructor(id: number, private event: android.view.MotionEvent) {
+ this.android = id;
+ }
+
+ getX(): number {
+ return this.event.getX(this.android) / utils.layout.getDisplayDensity();
+ }
+
+ getY(): number {
+ return this.event.getY(this.android) / utils.layout.getDisplayDensity();
+ }
+}
+
+class TouchGestureEventData implements definition.TouchGestureEventData {
+ eventName: string = definition.toString(definition.GestureTypes.touch);
+ type: definition.GestureTypes = definition.GestureTypes.touch;
+ ios: any = undefined;
+ action: string;
+ view: view.View;
+ android: android.view.MotionEvent;
+ object: any;
+
+ private _activePointers: Array;
+ private _allPointers: Array;
+
+ public prepare(view: view.View, e: android.view.MotionEvent) {
+ this.view = view;
+ this.object = view;
+ this.android = e;
+ this.action = this.getActionType(e);
+
+ this._activePointers = undefined;
+ this._allPointers = undefined;
+ }
+
+ getPointerCount(): number {
+ return this.android.getPointerCount();
+ }
+
+ getActivePointers(): Array {
+ // Only one active pointer in Android
+ if (!this._activePointers) {
+ this._activePointers = [new Pointer(this.android.getActionIndex(), this.android)];
+ }
+ return this._activePointers;
+ }
+
+ getAllPointers(): Array {
+ if (!this._allPointers) {
+ this._allPointers = [];
+ for (var i = 0; i < this.getPointerCount(); i++) {
+ this._allPointers.push(new Pointer(i, this.android));
+ }
+ }
+
+ return this._allPointers;
+ }
+
+ getX(): number {
+ return this.getActivePointers()[0].getX();
+ }
+
+ getY(): number {
+ return this.getActivePointers()[0].getY();
+ }
+
+ private getActionType(e: android.view.MotionEvent): string {
+ switch (e.getActionMasked()) {
+ case android.view.MotionEvent.ACTION_DOWN:
+ case android.view.MotionEvent.ACTION_POINTER_DOWN:
+ return common.TouchAction.down;
+
+ case android.view.MotionEvent.ACTION_MOVE:
+ return common.TouchAction.move;
+
+ case android.view.MotionEvent.ACTION_UP:
+ case android.view.MotionEvent.ACTION_POINTER_UP:
+ return common.TouchAction.up;
+
+ case android.view.MotionEvent.ACTION_CANCEL:
+ return common.TouchAction.cancel;
+ }
+
+ return "";
+ }
}
\ No newline at end of file
diff --git a/ui/gestures/gestures.d.ts b/ui/gestures/gestures.d.ts
index f233cbb2f..6b720f028 100644
--- a/ui/gestures/gestures.d.ts
+++ b/ui/gestures/gestures.d.ts
@@ -36,7 +36,11 @@ declare module "ui/gestures" {
/**
* Denotes long press gesture.
*/
- longPress
+ longPress,
+ /**
+ * Denotes touch action.
+ */
+ touch
}
/**
@@ -44,7 +48,7 @@ declare module "ui/gestures" {
*/
export enum GestureStateTypes {
/**
- * Gesture cancelled.
+ * Gesture canceled.
*/
cancelled,
/**
@@ -83,6 +87,31 @@ declare module "ui/gestures" {
down
}
+ /**
+ * Defines a touch action
+ */
+ export module TouchAction {
+ /**
+ * Down action.
+ */
+ export var down: string;
+
+ /**
+ * Up action.
+ */
+ export var up: string;
+
+ /**
+ * Move action.
+ */
+ export var move: string;
+
+ /**
+ * Cancel action.
+ */
+ export var cancel: string;
+ }
+
/**
* Provides gesture event data.
*/
@@ -105,6 +134,67 @@ declare module "ui/gestures" {
android: any
}
+ /**
+ * Provides gesture event data.
+ */
+ export interface TouchGestureEventData extends GestureEventData {
+ /**
+ * Gets action of the touch. Possible values: 'up', 'move', 'down', 'cancel'
+ */
+ action: string;
+
+ /**
+ * Gets the X coordinate of this event inside the view that triggered the event.
+ */
+ getX(): number;
+
+ /**
+ * Gets the Y coordinate of this event inside the view that triggered the event.
+ */
+ getY(): number;
+
+ /**
+ * Gets the number of pointers in the event.
+ */
+ getPointerCount(): number;
+
+ /**
+ * Gets the pointers that triggered the event.
+ * Note: In Android there is aways only one active pointer.
+ */
+ getActivePointers(): Array;
+
+ /**
+ * Gets all pointers.
+ */
+ getAllPointers(): Array;
+ }
+
+ /**
+ * Pointer is an object representing a finger (or other object) that is touching the screen.
+ */
+ export interface Pointer {
+ /**
+ * The id of the pointer.
+ */
+ android: any;
+
+ /**
+ * The UITouch object associated to the touch
+ */
+ ios: any;
+
+ /**
+ * Gets the X coordinate of the pointer inside the view that triggered the event.
+ */
+ getX(): number;
+
+ /**
+ * Gets the Y coordinate of the pointer inside the view that triggered the event.
+ */
+ getY(): number;
+ }
+
/**
* Provides gesture event data for pinch gesture.
*/
@@ -150,7 +240,9 @@ declare module "ui/gestures" {
export class GesturesObserver {
/**
* Creates an instance of GesturesObserver class.
+ * @param target - The view for which the observer is created.
* @param callback - A function that will be executed when a gesture is received.
+ * @param context - default this argument for the callbacks.
*/
constructor(target: view.View, callback: (args: GestureEventData) => void, context: any);
@@ -191,8 +283,9 @@ declare module "ui/gestures" {
* @param target - View which will be watched for originating a specific gesture.
* @param type - Type of the gesture.
* @param callback - A function that will be executed when a gesture is received.
+ * @param context - this argument for the callback.
*/
- export function observe(target: view.View, type: GestureTypes, callback: (args: GestureEventData) => void, thisArg?: any): GesturesObserver;
+ export function observe(target: view.View, type: GestureTypes, callback: (args: GestureEventData) => void, context?: any): GesturesObserver;
/**
* Returns a string representation of a gesture type.
diff --git a/ui/gestures/gestures.ios.ts b/ui/gestures/gestures.ios.ts
index dcf224878..31a4086f5 100644
--- a/ui/gestures/gestures.ios.ts
+++ b/ui/gestures/gestures.ios.ts
@@ -3,6 +3,7 @@ import definition = require("ui/gestures");
import view = require("ui/core/view");
import observable = require("data/observable");
import trace = require("trace");
+import types = require("utils/types");
global.moduleMerge(common, exports);
@@ -151,6 +152,10 @@ export class GesturesObserver extends common.GesturesObserver {
if (type & definition.GestureTypes.longPress) {
nativeView.addGestureRecognizer(this._createRecognizer(definition.GestureTypes.longPress));
}
+
+ if (type & definition.GestureTypes.touch) {
+ nativeView.addGestureRecognizer(this._createRecognizer(definition.GestureTypes.touch));
+ }
}
}
@@ -184,7 +189,7 @@ export class GesturesObserver extends common.GesturesObserver {
super.disconnect();
}
- private _executeCallback(args: definition.GestureEventData) {
+ public _executeCallback(args: definition.GestureEventData) {
if (this.callback) {
this.callback.call(this.context, args);
}
@@ -197,13 +202,14 @@ export class GesturesObserver extends common.GesturesObserver {
var recognizerType = _getUIGestureRecognizerType(type);
if (recognizerType) {
+ recognizer = recognizerType.alloc().initWithTargetAction(target, "recognize");
+
if (type === definition.GestureTypes.swipe && swipeDirection) {
name = name + swipeDirection.toString();
- recognizer = recognizerType.alloc().initWithTargetAction(target, "recognize");
(recognizer).direction = swipeDirection;
}
- else {
- recognizer = recognizerType.alloc().initWithTargetAction(target, "recognize");
+ else if (type === definition.GestureTypes.touch) {
+ (recognizer).observer = this;
}
if (recognizer) {
@@ -216,8 +222,8 @@ export class GesturesObserver extends common.GesturesObserver {
}
}
-function _createUIGestureRecognizerTarget(owner: GesturesObserver, type: definition.GestureTypes, callback?: (args: definition.GestureEventData) => void, thisArg?: any): any {
- return UIGestureRecognizerImpl.initWithOwnerTypeCallback(new WeakRef(owner), type, callback, thisArg);
+function _createUIGestureRecognizerTarget(owner: GesturesObserver, type: definition.GestureTypes, callback?: (args: definition.GestureEventData) => void, context?: any): any {
+ return UIGestureRecognizerImpl.initWithOwnerTypeCallback(new WeakRef(owner), type, callback, context);
}
interface RecognizerCache {
@@ -242,6 +248,8 @@ function _getUIGestureRecognizerType(type: definition.GestureTypes): any {
nativeType = UIRotationGestureRecognizer;
} else if (type === definition.GestureTypes.longPress) {
nativeType = UILongPressGestureRecognizer;
+ } else if (type === definition.GestureTypes.touch) {
+ nativeType = TouchGestureRecognizer;
}
return nativeType;
@@ -331,3 +339,134 @@ function _getRotationData(args: definition.GestureEventData): definition.Rotatio
state: getState(recognizer)
};
}
+
+class TouchGestureRecognizer extends UIGestureRecognizer {
+ public observer: GesturesObserver;
+ private _eventData: TouchGestureEventData;
+
+ touchesBeganWithEvent(touches: NSSet, event: any): void {
+ this.executeCallback(common.TouchAction.down, touches, event);
+ }
+
+ touchesMovedWithEvent(touches: NSSet, event: any): void {
+ this.executeCallback(common.TouchAction.move, touches, event);
+ }
+
+ touchesEndedWithEvent(touches: NSSet, event: any): void {
+ this.executeCallback(common.TouchAction.up, touches, event);
+ }
+
+ touchesCancelledWithEvent(touches: NSSet, event: any): void {
+ this.executeCallback(common.TouchAction.cancel, touches, event);
+ }
+
+ private executeCallback(action: string, touches: NSSet, event: any): void {
+ if (!this._eventData) {
+ this._eventData = new TouchGestureEventData();
+ }
+
+ this._eventData.prepare(this.observer.target, action, touches, event);
+ this.observer._executeCallback(this._eventData);
+ }
+}
+
+class Pointer implements definition.Pointer {
+ public android: any = undefined;
+ public ios: UITouch = undefined;
+
+ private _view: view.View;
+
+ private _location: CGPoint;
+ private get location(): CGPoint {
+ if (!this._location) {
+ this._location = this.ios.locationInView(this._view._nativeView);
+ }
+
+ return this._location;
+ }
+
+ constructor(touch: UITouch, targetView: view.View) {
+ this.ios = touch;
+ this._view = targetView;
+ }
+
+ getX(): number {
+ return this.location.x;
+ }
+
+ getY(): number {
+ return this.location.x;
+ }
+}
+
+class TouchGestureEventData implements definition.TouchGestureEventData {
+ eventName: string = definition.toString(definition.GestureTypes.touch);
+ type: definition.GestureTypes = definition.GestureTypes.touch;
+ android: any = undefined;
+ action: string;
+ view: view.View;
+ ios: { touches: NSSet, event: { allTouches: () => NSSet } };
+ object: any;
+
+ private _activePointers: Array;
+ private _allPointers: Array;
+ private _mainPointer: UITouch;
+
+ public prepare(view: view.View, action: string, touches: NSSet, event: any) {
+ this.action = action;
+ this.view = view;
+ this.object = view;
+ this.ios = {
+ touches: touches,
+ event: event
+ };
+
+ this._mainPointer = undefined;
+ this._activePointers = undefined;
+ this._allPointers = undefined;
+ }
+
+ getPointerCount(): number {
+ return this.ios.event.allTouches().count;
+ }
+
+ private getMainPointer(): UITouch {
+ if (types.isUndefined(this._mainPointer)) {
+ this._mainPointer = this.ios.touches.anyObject();
+ }
+ return this._mainPointer;
+ }
+
+ getActivePointers(): Array {
+ if (!this._activePointers) {
+ this._activePointers = [];
+
+ for (let i = 0, nsArr = this.ios.touches.allObjects; i < nsArr.count; i++) {
+ this._activePointers.push(new Pointer(nsArr.objectAtIndex(i), this.view));
+ }
+ }
+
+ return this._activePointers;
+ }
+
+ getAllPointers(): Array {
+ if (!this._allPointers) {
+ this._allPointers = [];
+
+ let nsArr = this.ios.event.allTouches().allObjects;
+ for (var i = 0; i < nsArr.count; i++) {
+ this._allPointers.push(new Pointer(nsArr.objectAtIndex(i), this.view));
+ }
+ }
+
+ return this._allPointers;
+ }
+
+ getX(): number {
+ return this.getMainPointer().locationInView(this.view._nativeView).x;
+ }
+
+ getY(): number {
+ return this.getMainPointer().locationInView(this.view._nativeView).y
+ }
+}
\ No newline at end of file