Experimental requireGestureToFail API implemented

This commit is contained in:
Pavel fljot 2012-03-06 23:28:12 +02:00
parent 4e02d4ae63
commit 2efa95b85c
5 changed files with 163 additions and 13 deletions

View file

@ -14,6 +14,7 @@ package org.gestouch.gestures
import flash.events.EventDispatcher; import flash.events.EventDispatcher;
import flash.geom.Point; import flash.geom.Point;
import flash.system.Capabilities; import flash.system.Capabilities;
import flash.utils.Dictionary;
[Event(name="stateChange", type="org.gestouch.events.GestureStateEvent")] [Event(name="stateChange", type="org.gestouch.events.GestureStateEvent")]
@ -46,6 +47,12 @@ package org.gestouch.gestures
protected var _touchesMap:Object = {}; protected var _touchesMap:Object = {};
protected var _centralPoint:Point = new Point(); protected var _centralPoint:Point = new Point();
protected var _localLocation: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) public function Gesture(target:InteractiveObject = null)
@ -177,12 +184,19 @@ package org.gestouch.gestures
*/ */
public function reset():void public function reset():void
{ {
//TODO //FIXME: proper state change?
_location.x = 0; _location.x = 0;
_location.y = 0; _location.y = 0;
_touchesMap = {}; _touchesMap = {};
_touchesCount = 0; _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); setState(GestureState.IDLE);
} }
@ -197,6 +211,7 @@ package org.gestouch.gestures
//TODO //TODO
reset(); reset();
target = null; target = null;
_gesturesToFail = null;
} }
@ -212,9 +227,13 @@ package org.gestouch.gestures
} }
/**
* <b>NB! Current implementation is highly experimental!</b> See examples for more info.
*/
public function requireGestureToFail(gesture:Gesture):void public function requireGestureToFail(gesture:Gesture):void
{ {
//TODO //TODO
_gesturesToFail[gesture] = true;
} }
@ -319,6 +338,39 @@ package org.gestouch.gestures
if (newState == GestureState.BEGAN || newState == GestureState.RECOGNIZED) 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)) if (delegate && !delegate.gestureShouldBegin(this))
{ {
setState(GestureState.FAILED); 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) 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); setState(GestureState.POSSIBLE);
} }
} }
@ -417,5 +486,37 @@ package org.gestouch.gestures
onTouchEnd(touch); 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);
}
}
} }
} }

View file

@ -152,6 +152,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));
}
}
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------

View file

@ -23,6 +23,9 @@ package org.gestouch.gestures
*/ */
public var direction:uint = PanGestureDirection.NO_DIRECTION; public var direction:uint = PanGestureDirection.NO_DIRECTION;
protected var _gestureBeginOffsetX:Number;
protected var _gestureBeginOffsetY:Number;
public function PanGesture(target:InteractiveObject = null) public function PanGesture(target:InteractiveObject = null)
{ {
@ -88,6 +91,15 @@ package org.gestouch.gestures
} }
override public function reset():void
{
_gestureBeginOffsetX = NaN;
_gestureBeginOffsetY = NaN;
super.reset();
}
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
@ -142,20 +154,14 @@ package org.gestouch.gestures
updateLocation(); updateLocation();
offsetX = _location.x - prevLocationX; offsetX = _location.x - prevLocationX;
offsetY = _location.y - prevLocationY; offsetY = _location.y - prevLocationY;
// Unfortunately we create several new point instances here, // acummulate begin offsets for the case when this gesture recognition is delayed by requireGestureToFail
// but thats not a big deal since this code executed only once per recognition session _gestureBeginOffsetX = (_gestureBeginOffsetX != _gestureBeginOffsetX) ? offsetX : _gestureBeginOffsetX + offsetX;
var offset:Point = new Point(offsetX, offsetY); _gestureBeginOffsetY = (_gestureBeginOffsetY != _gestureBeginOffsetY) ? offsetY : _gestureBeginOffsetY + offsetY;
if (offset.length > slop)
{
var slopVector:Point = offset.clone();
slopVector.normalize(slop);
offset = offset.subtract(slopVector);
}
if (setState(GestureState.BEGAN) && hasEventListener(PanGestureEvent.GESTURE_PAN)) if (setState(GestureState.BEGAN) && hasEventListener(PanGestureEvent.GESTURE_PAN))
{ {
dispatchEvent(new PanGestureEvent(PanGestureEvent.GESTURE_PAN, false, false, GestureState.BEGAN, 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(); 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));
}
}
} }
} }

View file

@ -176,11 +176,12 @@ package org.gestouch.gestures
} }
else if (absVelX >= minVelocity && (minOffset != minOffset || absOffsetX >= minOffset)) else if (absVelX >= minVelocity && (minOffset != minOffset || absOffsetX >= minOffset))
{ {
_offset.y = 0;
if (setState(GestureState.RECOGNIZED) && hasEventListener(SwipeGestureEvent.GESTURE_SWIPE)) if (setState(GestureState.RECOGNIZED) && hasEventListener(SwipeGestureEvent.GESTURE_SWIPE))
{ {
_localLocation = target.globalToLocal(_location);//refresh local location in case target moved _localLocation = target.globalToLocal(_location);//refresh local location in case target moved
dispatchEvent(new SwipeGestureEvent(SwipeGestureEvent.GESTURE_SWIPE, false, false, GestureState.RECOGNIZED, 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)) else if (absVelY >= minVelocity && (minOffset != minOffset || absOffsetY >= minOffset))
{ {
_offset.x = 0;
if (setState(GestureState.RECOGNIZED) && hasEventListener(SwipeGestureEvent.GESTURE_SWIPE)) if (setState(GestureState.RECOGNIZED) && hasEventListener(SwipeGestureEvent.GESTURE_SWIPE))
{ {
_localLocation = target.globalToLocal(_location);//refresh local location in case target moved _localLocation = target.globalToLocal(_location);//refresh local location in case target moved
dispatchEvent(new SwipeGestureEvent(SwipeGestureEvent.GESTURE_SWIPE, false, false, GestureState.RECOGNIZED, 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); 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));
}
}
} }
} }

View file

@ -152,6 +152,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));
}
}
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------