diff --git a/src/components/app/test/gesture-collision/main.html b/src/components/app/test/gesture-collision/main.html
index 69a5f933ea..30d995cbf2 100644
--- a/src/components/app/test/gesture-collision/main.html
+++ b/src/components/app/test/gesture-collision/main.html
@@ -22,6 +22,16 @@
+
+ Apple
+
+
+
+
+ Apple
+
+
+
@@ -94,6 +104,16 @@
+
+ Apple
+
+
+
+
+ Apple
+
+
+
diff --git a/src/components/app/test/gesture-collision/page1.html b/src/components/app/test/gesture-collision/page1.html
index 9d8061d33a..6c685e1f71 100644
--- a/src/components/app/test/gesture-collision/page1.html
+++ b/src/components/app/test/gesture-collision/page1.html
@@ -74,6 +74,16 @@
+
+ Apple
+
+
+
+
+ Apple
+
+
+
diff --git a/src/components/toggle/toggle-gesture.ts b/src/components/toggle/toggle-gesture.ts
new file mode 100644
index 0000000000..c9cbe2236d
--- /dev/null
+++ b/src/components/toggle/toggle-gesture.ts
@@ -0,0 +1,45 @@
+import { GestureController, GesturePriority, GESTURE_TOGGLE } from '../../gestures/gesture-controller';
+import { PanGesture } from '../../gestures/drag-gesture';
+import { pointerCoord } from '../../util/dom';
+import { NativeRafDebouncer } from '../../util/debouncer';
+import { Toggle } from './toggle';
+
+/**
+ * @private
+ */
+export class ToggleGesture extends PanGesture {
+
+ constructor(public toogle: Toggle, gestureCtrl: GestureController) {
+ super(toogle.getNativeElement(), {
+ maxAngle: 20,
+ threshold: 0,
+ debouncer: new NativeRafDebouncer(),
+ gesture: gestureCtrl.createGesture({
+ name: GESTURE_TOGGLE,
+ priority: GesturePriority.Toggle,
+ })
+ });
+ }
+
+ canStart(ev: any): boolean {
+ return true;
+ }
+
+ onDragStart(ev: any) {
+ ev.preventDefault();
+
+ this.toogle._onDragStart(pointerCoord(ev).x);
+ }
+
+ onDragMove(ev: any) {
+ ev.preventDefault();
+
+ this.toogle._onDragMove(pointerCoord(ev).x);
+ }
+
+ onDragEnd(ev: any) {
+ ev.preventDefault();
+
+ this.toogle._onDragEnd(pointerCoord(ev).x);
+ }
+}
diff --git a/src/components/toggle/toggle.ts b/src/components/toggle/toggle.ts
index e829f8b929..d84f977b16 100644
--- a/src/components/toggle/toggle.ts
+++ b/src/components/toggle/toggle.ts
@@ -3,13 +3,13 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Config } from '../../config/config';
import { Form, IonicTapInput } from '../../util/form';
-import { isTrueProperty } from '../../util/util';
+import { isTrueProperty, assert } from '../../util/util';
import { Ion } from '../ion';
import { Item } from '../item/item';
-import { pointerCoord } from '../../util/dom';
import { Key } from '../../util/key';
import { Haptic } from '../../util/haptic';
-import { UIEventManager } from '../../util/ui-event-manager';
+import { ToggleGesture } from './toggle-gesture';
+import { GestureController } from '../../gestures/gesture-controller';
export const TOGGLE_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
@@ -76,28 +76,18 @@ export const TOGGLE_VALUE_ACCESSOR: any = {
encapsulation: ViewEncapsulation.None,
})
export class Toggle extends Ion implements IonicTapInput, AfterContentInit, ControlValueAccessor, OnDestroy {
- /** @private */
- _checked: boolean = false;
- /** @private */
- _init: boolean = false;
- /** @private */
- _disabled: boolean = false;
- /** @private */
- _labelId: string;
- /** @private */
- _activated: boolean = false;
- /** @private */
- _startX: number;
- /** @private */
- _msPrv: number = 0;
- /** @private */
- _fn: Function = null;
- /** @private */
- _events: UIEventManager = new UIEventManager();
- /**
- * @private
- */
+ _checked: boolean = false;
+ _init: boolean = false;
+ _disabled: boolean = false;
+ _labelId: string;
+ _activated: boolean = false;
+ _startX: number;
+ _msPrv: number = 0;
+ _fn: Function = null;
+ _gesture: ToggleGesture;
+
+ /** @private */
id: string;
/**
@@ -126,8 +116,9 @@ export class Toggle extends Ion implements IonicTapInput, AfterContentInit, Cont
config: Config,
elementRef: ElementRef,
renderer: Renderer,
- public _haptic: Haptic,
- @Optional() public _item: Item
+ private _haptic: Haptic,
+ @Optional() public _item: Item,
+ private _gestureCtrl: GestureController
) {
super(config, elementRef, renderer, 'toggle');
_form.register(this);
@@ -142,59 +133,63 @@ export class Toggle extends Ion implements IonicTapInput, AfterContentInit, Cont
/**
* @private
*/
- pointerDown(ev: UIEvent): boolean {
- this._startX = pointerCoord(ev).x;
- this._activated = true;
- return true;
+ ngAfterContentInit() {
+ this._init = true;
+ this._gesture = new ToggleGesture(this, this._gestureCtrl);
+ this._gesture.listen();
}
/**
* @private
*/
- pointerMove(ev: UIEvent) {
- if (this._startX) {
- let currentX = pointerCoord(ev).x;
- console.debug('toggle, pointerMove', ev.type, currentX);
+ _onDragStart(startX: number) {
+ this._startX = startX;
+ this._activated = true;
+ }
- if (this._checked) {
- if (currentX + 15 < this._startX) {
- this.onChange(false);
- this._haptic.selection();
- this._startX = currentX;
- this._activated = true;
- }
+ /**
+ * @private
+ */
+ _onDragMove(currentX: number) {
+ assert(this._startX, '_startX must be valid');
- } else if (currentX - 15 > this._startX) {
- this.onChange(true);
- // Create a haptic event
+ console.debug('toggle, pointerMove', currentX);
+
+ if (this._checked) {
+ if (currentX + 15 < this._startX) {
+ this.onChange(false);
this._haptic.selection();
this._startX = currentX;
- this._activated = (currentX < this._startX + 5);
+ this._activated = true;
}
+
+ } else if (currentX - 15 > this._startX) {
+ this.onChange(true);
+ this._haptic.selection();
+ this._startX = currentX;
+ this._activated = (currentX < this._startX + 5);
}
}
/**
* @private
*/
- pointerUp(ev: UIEvent) {
- if (this._startX) {
- let endX = pointerCoord(ev).x;
+ _onDragEnd(endX: number) {
+ assert(this._startX, '_startX must be valid');
- if (this.checked) {
- if (this._startX + 4 > endX) {
- this.onChange(false);
- this._haptic.selection();
- }
-
- } else if (this._startX - 4 < endX) {
- this.onChange(true);
+ if (this.checked) {
+ if (this._startX + 4 > endX) {
+ this.onChange(false);
this._haptic.selection();
}
- this._activated = false;
- this._startX = null;
+ } else if (this._startX - 4 < endX) {
+ this.onChange(true);
+ this._haptic.selection();
}
+
+ this._activated = false;
+ this._startX = null;
}
/**
@@ -273,19 +268,6 @@ export class Toggle extends Ion implements IonicTapInput, AfterContentInit, Cont
*/
onTouched() {}
- /**
- * @private
- */
- ngAfterContentInit() {
- this._init = true;
- this._events.pointerEvents({
- elementRef: this._elementRef,
- pointerDown: this.pointerDown.bind(this),
- pointerMove: this.pointerMove.bind(this),
- pointerUp: this.pointerUp.bind(this)
- });
- }
-
/**
* @private
*/
@@ -310,7 +292,7 @@ export class Toggle extends Ion implements IonicTapInput, AfterContentInit, Cont
*/
ngOnDestroy() {
this._form.deregister(this);
- this._events.unlistenAll();
+ this._gesture.destroy();
this._fn = null;
}
diff --git a/src/gestures/drag-gesture.ts b/src/gestures/drag-gesture.ts
index d76101503e..25619e767f 100644
--- a/src/gestures/drag-gesture.ts
+++ b/src/gestures/drag-gesture.ts
@@ -23,6 +23,7 @@ export interface PanGestureConfig {
* @private
*/
export class PanGesture {
+
private debouncer: Debouncer;
private events: UIEventManager = new UIEventManager(false);
private pointerEvents: PointerEvents;
@@ -58,7 +59,9 @@ export class PanGesture {
capture: opts.capture,
passive: opts.passive
};
- this.detector = new PanRecognizer(opts.direction, opts.threshold, opts.maxAngle);
+ if (opts.threshold > 0) {
+ this.detector = new PanRecognizer(opts.direction, opts.threshold, opts.maxAngle);
+ }
}
listen() {
@@ -100,40 +103,42 @@ export class PanGesture {
return false;
}
}
-
- let coord = pointerCoord(ev);
- this.detector.start(coord);
this.started = true;
this.captured = false;
+
+ const coord = pointerCoord(ev);
+ if (this.detector) {
+ this.detector.start(coord);
+
+ } else {
+ if (!this.tryToCapture(ev)) {
+ this.started = false;
+ this.captured = false;
+ this.gestute.release();
+ return false;
+ }
+ }
return true;
}
pointerMove(ev: any) {
- if (!this.started) {
+ assert(this.started === true, 'started must be true');
+ if (this.captured) {
+ this.debouncer.debounce(() => {
+ this.onDragMove(ev);
+ });
return;
}
- this.debouncer.debounce(() => {
- if (this.captured) {
- this.onDragMove(ev);
- return;
- }
- let coord = pointerCoord(ev);
- if (this.detector.detect(coord)) {
- if (this.detector.pan() !== 0 &&
- (!this.gestute || this.gestute.capture())) {
- this.onDragStart(ev);
- this.captured = true;
- return;
+ assert(this.detector, 'detector has to be valid');
+ const coord = pointerCoord(ev);
+ if (this.detector.detect(coord)) {
+ if (this.detector.pan() !== 0) {
+ if (!this.tryToCapture(ev)) {
+ this.abort(ev);
}
-
- // Detection/capturing was not successful, aborting!
- this.started = false;
- this.captured = false;
- this.pointerEvents.stop();
- this.notCaptured(ev);
}
- });
+ }
}
pointerUp(ev: any) {
@@ -151,6 +156,26 @@ export class PanGesture {
this.started = false;
}
+ tryToCapture(ev: any): boolean {
+ assert(this.started === true, 'started has be true');
+ assert(this.captured === false, 'captured has be false');
+
+ if (this.gestute && !this.gestute.capture()) {
+ return false;
+ }
+ this.onDragStart(ev);
+ this.captured = true;
+ return true;
+ }
+
+ abort(ev: any) {
+ this.started = false;
+ this.captured = false;
+ this.gestute.release();
+ this.pointerEvents.stop();
+ this.notCaptured(ev);
+ }
+
getNativeElement(): HTMLElement {
return this.element;
}
diff --git a/src/gestures/gesture-controller.ts b/src/gestures/gesture-controller.ts
index 1e5b3d749b..a5a8410150 100644
--- a/src/gestures/gesture-controller.ts
+++ b/src/gestures/gesture-controller.ts
@@ -14,6 +14,9 @@ export const GESTURE_ITEM_SWIPE = 'item-swipe';
/** @private */
export const GESTURE_REFRESHER = 'refresher';
+/** @private */
+export const GESTURE_TOGGLE = 'toggle';
+
/**
* @private
*/
@@ -24,11 +27,13 @@ export const enum GesturePriority {
Normal = 0,
High = 10,
VeryHigh = 20,
+ VeryVeryHigh = 30,
SlidingItem = Low,
MenuSwipe = High,
GoBackSwipe = VeryHigh,
Refresher = Normal,
+ Toggle = VeryVeryHigh
}
/**
diff --git a/src/gestures/recognizers.ts b/src/gestures/recognizers.ts
index b2495c1e78..ecd2cfb92c 100644
--- a/src/gestures/recognizers.ts
+++ b/src/gestures/recognizers.ts
@@ -2,15 +2,17 @@ import { PointerCoordinates } from '../util/dom';
export class PanRecognizer {
+
private startCoord: PointerCoordinates;
private dirty: boolean = false;
private threshold: number;
private maxCosine: number;
+
private _angle: any = 0;
private _isPan: number = 0;
constructor(private direction: string, threshold: number, maxAngle: number) {
- let radians = maxAngle * (Math.PI / 180);
+ const radians = maxAngle * (Math.PI / 180);
this.maxCosine = Math.cos(radians);
this.threshold = threshold * threshold;
}
@@ -26,12 +28,13 @@ export class PanRecognizer {
if (!this.dirty) {
return false;
}
- let deltaX = (coord.x - this.startCoord.x);
- let deltaY = (coord.y - this.startCoord.y);
- let distance = deltaX * deltaX + deltaY * deltaY;
+ const deltaX = (coord.x - this.startCoord.x);
+ const deltaY = (coord.y - this.startCoord.y);
+ const distance = deltaX * deltaX + deltaY * deltaY;
+
if (distance >= this.threshold) {
- let angle = Math.atan2(deltaY, deltaX);
- let cosine = (this.direction === 'y')
+ var angle = Math.atan2(deltaY, deltaX);
+ var cosine = (this.direction === 'y')
? Math.sin(angle)
: Math.cos(angle);