diff --git a/src/org/gestouch/gestures/Gesture.as b/src/org/gestouch/gestures/Gesture.as index 767e981..c3b9b7f 100644 --- a/src/org/gestouch/gestures/Gesture.as +++ b/src/org/gestouch/gestures/Gesture.as @@ -14,6 +14,7 @@ package org.gestouch.gestures import flash.events.EventDispatcher; import flash.geom.Point; import flash.system.Capabilities; + import flash.utils.Dictionary; [Event(name="stateChange", type="org.gestouch.events.GestureStateEvent")] @@ -46,6 +47,12 @@ package org.gestouch.gestures protected var _touchesMap:Object = {}; protected var _centralPoint:Point = new Point(); protected var _localLocation:Point; + /** + * List of gesture we require to fail. + * @see requireGestureToFail() + */ + protected var _gesturesToFail:Dictionary = new Dictionary(true); + protected var _pendingRecognizedState:uint; public function Gesture(target:InteractiveObject = null) @@ -177,12 +184,19 @@ package org.gestouch.gestures */ public function reset():void { - //TODO + //FIXME: proper state change? _location.x = 0; _location.y = 0; _touchesMap = {}; _touchesCount = 0; + for (var key:* in _gesturesToFail) + { + var gestureToFail:Gesture = key as Gesture; + gestureToFail.removeEventListener(GestureStateEvent.STATE_CHANGE, gestureToFail_stateChangeHandler); + } + _pendingRecognizedState = 0; + setState(GestureState.IDLE); } @@ -197,6 +211,7 @@ package org.gestouch.gestures //TODO reset(); target = null; + _gesturesToFail = null; } @@ -212,9 +227,13 @@ package org.gestouch.gestures } + /** + * NB! Current implementation is highly experimental! See examples for more info. + */ public function requireGestureToFail(gesture:Gesture):void { //TODO + _gesturesToFail[gesture] = true; } @@ -319,6 +338,39 @@ package org.gestouch.gestures if (newState == GestureState.BEGAN || newState == GestureState.RECOGNIZED) { + var gestureToFail:Gesture; + // first we check if other required-to-fail gestures recognized + // TODO: is this really necessary? using "requireGestureToFail" API assume that + // required-to-fail gesture always recognizes AFTER this one. + for (var key:* in _gesturesToFail) + { + gestureToFail = key as Gesture; + if (gestureToFail.state != GestureState.IDLE && gestureToFail.state != GestureState.POSSIBLE + && gestureToFail.state != GestureState.FAILED) + { + // Looks like other gesture won't fail, + // which means the required condition will not happen, so we must fail + setState(GestureState.FAILED); + return false; + } + } + // then we check of other required-to-fail gestures are actually tracked (not IDLE) + // and not still not recognized (e.g. POSSIBLE state) + for (key in _gesturesToFail) + { + gestureToFail = key as Gesture; + if (gestureToFail.state == GestureState.POSSIBLE) + { + // Other gesture might fail soon, so we postpone state change + _pendingRecognizedState = newState; + return false; + } + // else if gesture is in IDLE state it means it doesn't track anything, + // so we simply ignore it as it doesn't seem like conflict from this perspective + // (perspective of using "requireGestureToFail" API) + } + + if (delegate && !delegate.gestureShouldBegin(this)) { setState(GestureState.FAILED); @@ -381,6 +433,17 @@ package org.gestouch.gestures } + /** + * Executed once requiredToFail gestures have been failed and + * pending (delayed) recognized state has been entered. + * You must dispatch gesture event here. + */ + protected function onDelayedRecognize():void + { + + } + + //-------------------------------------------------------------------------- @@ -398,6 +461,12 @@ package org.gestouch.gestures if (_touchesCount == 1 && state == GestureState.IDLE) { + for (var key:* in _gesturesToFail) + { + var gestureToFail:Gesture = key as Gesture; + gestureToFail.addEventListener(GestureStateEvent.STATE_CHANGE, gestureToFail_stateChangeHandler, false, 0, true); + } + setState(GestureState.POSSIBLE); } } @@ -417,5 +486,37 @@ package org.gestouch.gestures onTouchEnd(touch); } + + + protected function gestureToFail_stateChangeHandler(event:GestureStateEvent):void + { + if (state != GestureState.POSSIBLE) + return;//just in case..FIXME? + + if (!_pendingRecognizedState) + return; + + if (event.newState == GestureState.FAILED) + { + for (var key:* in _gesturesToFail) + { + var gestureToFail:Gesture = key as Gesture; + if (gestureToFail.state == GestureState.POSSIBLE) + { + // we're still waiting for some gesture to fail + return; + } + } + + if (setState(_pendingRecognizedState)) + { + onDelayedRecognize(); + } + } + else if (event.newState != GestureState.POSSIBLE) + { + setState(GestureState.FAILED); + } + } } } \ No newline at end of file diff --git a/src/org/gestouch/gestures/LongPressGesture.as b/src/org/gestouch/gestures/LongPressGesture.as index 1318fa6..932aa17 100644 --- a/src/org/gestouch/gestures/LongPressGesture.as +++ b/src/org/gestouch/gestures/LongPressGesture.as @@ -151,6 +151,16 @@ package org.gestouch.gestures } } + + override protected function onDelayedRecognize():void + { + if (hasEventListener(LongPressGestureEvent.GESTURE_LONG_PRESS)) + { + dispatchEvent(new LongPressGestureEvent(LongPressGestureEvent.GESTURE_LONG_PRESS, false, false, GestureState.BEGAN, + _location.x, _location.y, _localLocation.x, _localLocation.y)); + } + } + diff --git a/src/org/gestouch/gestures/PanGesture.as b/src/org/gestouch/gestures/PanGesture.as index d06affa..ddf74bf 100644 --- a/src/org/gestouch/gestures/PanGesture.as +++ b/src/org/gestouch/gestures/PanGesture.as @@ -23,6 +23,9 @@ package org.gestouch.gestures */ public var direction:uint = PanGestureDirection.NO_DIRECTION; + protected var _gestureBeginOffsetX:Number; + protected var _gestureBeginOffsetY:Number; + public function PanGesture(target:InteractiveObject = null) { @@ -87,6 +90,15 @@ package org.gestouch.gestures return PanGesture; } + + override public function reset():void + { + _gestureBeginOffsetX = NaN; + _gestureBeginOffsetY = NaN; + + super.reset(); + } + @@ -142,20 +154,14 @@ package org.gestouch.gestures updateLocation(); offsetX = _location.x - prevLocationX; offsetY = _location.y - prevLocationY; - // Unfortunately we create several new point instances here, - // but thats not a big deal since this code executed only once per recognition session - var offset:Point = new Point(offsetX, offsetY); - if (offset.length > slop) - { - var slopVector:Point = offset.clone(); - slopVector.normalize(slop); - offset = offset.subtract(slopVector); - } + // acummulate begin offsets for the case when this gesture recognition is delayed by requireGestureToFail + _gestureBeginOffsetX = (_gestureBeginOffsetX != _gestureBeginOffsetX) ? offsetX : _gestureBeginOffsetX + offsetX; + _gestureBeginOffsetY = (_gestureBeginOffsetY != _gestureBeginOffsetY) ? offsetY : _gestureBeginOffsetY + offsetY; if (setState(GestureState.BEGAN) && hasEventListener(PanGestureEvent.GESTURE_PAN)) { dispatchEvent(new PanGestureEvent(PanGestureEvent.GESTURE_PAN, false, false, GestureState.BEGAN, - _location.x, _location.y, _localLocation.x, _localLocation.y, offset.x, offset.y)); + _location.x, _location.y, _localLocation.x, _localLocation.y, offsetX, offsetY)); } } } @@ -198,5 +204,15 @@ package org.gestouch.gestures updateLocation(); } } + + + override protected function onDelayedRecognize():void + { + if (hasEventListener(PanGestureEvent.GESTURE_PAN)) + { + dispatchEvent(new PanGestureEvent(PanGestureEvent.GESTURE_PAN, false, false, GestureState.BEGAN, + _location.x, _location.y, _localLocation.x, _localLocation.y, _gestureBeginOffsetX, _gestureBeginOffsetY)); + } + } } } \ No newline at end of file diff --git a/src/org/gestouch/gestures/SwipeGesture.as b/src/org/gestouch/gestures/SwipeGesture.as index 5f83b13..9665493 100644 --- a/src/org/gestouch/gestures/SwipeGesture.as +++ b/src/org/gestouch/gestures/SwipeGesture.as @@ -176,11 +176,12 @@ package org.gestouch.gestures } else if (absVelX >= minVelocity && (minOffset != minOffset || absOffsetX >= minOffset)) { + _offset.y = 0; if (setState(GestureState.RECOGNIZED) && hasEventListener(SwipeGestureEvent.GESTURE_SWIPE)) { _localLocation = target.globalToLocal(_location);//refresh local location in case target moved dispatchEvent(new SwipeGestureEvent(SwipeGestureEvent.GESTURE_SWIPE, false, false, GestureState.RECOGNIZED, - _location.x, _location.y, _localLocation.x, _localLocation.y, _offset.x, 0)); + _location.x, _location.y, _localLocation.x, _localLocation.y, _offset.x, _offset.y)); } } } @@ -203,11 +204,12 @@ package org.gestouch.gestures } else if (absVelY >= minVelocity && (minOffset != minOffset || absOffsetY >= minOffset)) { + _offset.x = 0; if (setState(GestureState.RECOGNIZED) && hasEventListener(SwipeGestureEvent.GESTURE_SWIPE)) { _localLocation = target.globalToLocal(_location);//refresh local location in case target moved dispatchEvent(new SwipeGestureEvent(SwipeGestureEvent.GESTURE_SWIPE, false, false, GestureState.RECOGNIZED, - _location.x, _location.y, _localLocation.x, _localLocation.y, 0, _offset.y)); + _location.x, _location.y, _localLocation.x, _localLocation.y, _offset.x, _offset.y)); } } } @@ -226,5 +228,16 @@ package org.gestouch.gestures setState(GestureState.FAILED); } } + + + override protected function onDelayedRecognize():void + { + if (hasEventListener(SwipeGestureEvent.GESTURE_SWIPE)) + { + _localLocation = target.globalToLocal(_location);//refresh local location in case target moved + dispatchEvent(new SwipeGestureEvent(SwipeGestureEvent.GESTURE_SWIPE, false, false, GestureState.RECOGNIZED, + _location.x, _location.y, _localLocation.x, _localLocation.y, _offset.x, _offset.y)); + } + } } } \ No newline at end of file diff --git a/src/org/gestouch/gestures/TapGesture.as b/src/org/gestouch/gestures/TapGesture.as index 9d7507c..fc5881b 100644 --- a/src/org/gestouch/gestures/TapGesture.as +++ b/src/org/gestouch/gestures/TapGesture.as @@ -151,6 +151,16 @@ package org.gestouch.gestures } } + + override protected function onDelayedRecognize():void + { + if (hasEventListener(TapGestureEvent.GESTURE_TAP)) + { + dispatchEvent(new TapGestureEvent(TapGestureEvent.GESTURE_TAP, false, false, GestureState.RECOGNIZED, + _location.x, _location.y, _localLocation.x, _localLocation.y)); + } + } +