diff --git a/.settings/com.powerflasher.fdt.classpath b/.settings/com.powerflasher.fdt.classpath index 75e92cc..9292307 100644 --- a/.settings/com.powerflasher.fdt.classpath +++ b/.settings/com.powerflasher.fdt.classpath @@ -1,5 +1,6 @@ + libs src frameworks/libs/air/airglobal.swc frameworks/libs/mobile/mobilecomponents.swc @@ -10,4 +11,5 @@ frameworks/libs/air/servicemonitor.swc frameworks/themes/Mobile/mobile.swc frameworks/libs/mx/mx.swc + libs/starling.swc diff --git a/build.xml b/build.xml index a094265..63664da 100644 --- a/build.xml +++ b/build.xml @@ -21,6 +21,8 @@ + + @@ -44,6 +46,7 @@ + diff --git a/libs/starling.swc b/libs/starling.swc new file mode 100644 index 0000000..6fc810d Binary files /dev/null and b/libs/starling.swc differ diff --git a/src/org/gestouch/core/DisplayListAdapter.as b/src/org/gestouch/core/DisplayListAdapter.as new file mode 100644 index 0000000..5151cf9 --- /dev/null +++ b/src/org/gestouch/core/DisplayListAdapter.as @@ -0,0 +1,27 @@ +package org.gestouch.core +{ + import flash.display.DisplayObject; + import org.gestouch.core.IDisplayListAdapter; + + + /** + * @author Pavel fljot + */ + public class DisplayListAdapter implements IDisplayListAdapter + { + public function getHierarchy(genericTarget:Object):Vector. + { + var list:Vector. = new Vector.(); + var i:uint = 0; + var target:DisplayObject = genericTarget as DisplayObject; + while (target) + { + list[i] = target; + target = target.parent; + i++; + } + + return list; + } + } +} \ No newline at end of file diff --git a/src/org/gestouch/core/DisplayObjectAdapter.as b/src/org/gestouch/core/DisplayObjectAdapter.as new file mode 100644 index 0000000..cb0e3ae --- /dev/null +++ b/src/org/gestouch/core/DisplayObjectAdapter.as @@ -0,0 +1,46 @@ +package org.gestouch.core +{ + import flash.display.DisplayObject; + import flash.display.DisplayObjectContainer; + import flash.geom.Point; + import flash.utils.Dictionary; + + + + /** + * @author Pavel fljot + */ + final public class DisplayObjectAdapter implements IGestureTargetAdapter + { + private var _targetWeekStorage:Dictionary = new Dictionary(true); + + + public function DisplayObjectAdapter(target:DisplayObject) + { + _targetWeekStorage[target] = true; + } + + + public function get target():Object + { + for (var key:Object in _targetWeekStorage) + { + return key; + } + return null; + } + + + public function globalToLocal(point:Point):Point + { + return (target as DisplayObject).globalToLocal(point); + } + + + public function contains(target:Object):Boolean + { + const targetAsDOC:DisplayObjectContainer = this.target as DisplayObjectContainer; + return (targetAsDOC && targetAsDOC.contains(target as DisplayObject)); + } + } +} \ No newline at end of file diff --git a/src/org/gestouch/core/GesturesManager.as b/src/org/gestouch/core/GesturesManager.as index 8cae7a2..831092d 100644 --- a/src/org/gestouch/core/GesturesManager.as +++ b/src/org/gestouch/core/GesturesManager.as @@ -5,11 +5,10 @@ package org.gestouch.core import org.gestouch.input.TouchInputAdapter; import flash.display.DisplayObject; - import flash.display.DisplayObjectContainer; - import flash.display.InteractiveObject; import flash.display.Shape; import flash.display.Stage; import flash.events.Event; + import flash.events.IEventDispatcher; import flash.ui.Multitouch; import flash.utils.Dictionary; @@ -25,9 +24,10 @@ package org.gestouch.core protected const _touchesManager:ITouchesManager = TouchesManager.getInstance(); protected const _frameTickerShape:Shape = new Shape(); protected var _inputAdapters:Vector. = new Vector.(); + protected var _displayListAdaptersMap:Dictionary = new Dictionary(); protected var _stage:Stage; protected var _gesturesMap:Dictionary = new Dictionary(true); - protected var _gesturesForTouchMap:Array = []; + protected var _gesturesForTouchMap:Dictionary = new Dictionary(); protected var _gesturesForTargetMap:Dictionary = new Dictionary(true); protected var _dirtyGestures:Vector. = new Vector.(); protected var _dirtyGesturesLength:uint = 0; @@ -40,6 +40,9 @@ package org.gestouch.core { throw new Error("Do not instantiate GesturesManager directly."); } + + _touchesManager.gesturesManager = this; + _displayListAdaptersMap[DisplayObject] = new DisplayListAdapter(); } @@ -90,7 +93,6 @@ package org.gestouch.core _inputAdapters.push(inputAdapter); inputAdapter.touchesManager = _touchesManager; - inputAdapter.gesturesManager = this; inputAdapter.init(); } @@ -115,6 +117,16 @@ package org.gestouch.core } + public function addDisplayListAdapter(targetClass:Class, adapter:IDisplayListAdapter):void + { + if (!targetClass || !adapter) + { + throw new Error("Argument error: both arguments required."); + } + _displayListAdaptersMap[targetClass] = adapter; + } + + //-------------------------------------------------------------------------- @@ -166,7 +178,7 @@ package org.gestouch.core var targetGestures:Vector. = _gesturesForTargetMap[target] as Vector.; if (!targetGestures) { - targetGestures = _gesturesForTargetMap[gesture.target] = new Vector.(); + targetGestures = _gesturesForTargetMap[target] = new Vector.(); } targetGestures.push(gesture); @@ -174,13 +186,17 @@ package org.gestouch.core if (GesturesManager.initDefaultInputAdapter) { - if (!_stage && gesture.target.stage) + var targetAsDO:DisplayObject = target as DisplayObject; + if (targetAsDO) { - installStage(gesture.target.stage); - } - else - { - gesture.target.addEventListener(Event.ADDED_TO_STAGE, gestureTarget_addedToStageHandler); + if (!_stage && targetAsDO.stage) + { + installStage(targetAsDO.stage); + } + else + { + targetAsDO.addEventListener(Event.ADDED_TO_STAGE, gestureTarget_addedToStageHandler); + } } } } @@ -194,7 +210,7 @@ package org.gestouch.core } - var target:InteractiveObject = gesture.target; + var target:Object = gesture.target; var targetGestures:Vector. = _gesturesForTargetMap[target] as Vector.; if (targetGestures.length > 1) { @@ -203,7 +219,10 @@ package org.gestouch.core else { delete _gesturesForTargetMap[target]; - target.removeEventListener(Event.ADDED_TO_STAGE, gestureTarget_addedToStageHandler); + if (target is IEventDispatcher) + { + (target as IEventDispatcher).removeEventListener(Event.ADDED_TO_STAGE, gestureTarget_addedToStageHandler); + } } delete _gesturesMap[gesture]; @@ -228,8 +247,8 @@ package org.gestouch.core for (var key:Object in _gesturesMap) { var otherGesture:Gesture = key as Gesture; - var target:DisplayObject = gesture.target; - var otherTarget:DisplayObject = otherGesture.target; + var target:Object = gesture.target; + var otherTarget:Object = otherGesture.target; // conditions for otherGesture "own properties" if (otherGesture != gesture && @@ -237,10 +256,10 @@ package org.gestouch.core otherGesture.enabled && otherGesture.state == GestureState.POSSIBLE) { - // conditions for otherGesture target if (otherTarget == target || - (target is DisplayObjectContainer && (target as DisplayObjectContainer).contains(otherTarget)) || - (otherTarget is DisplayObjectContainer && (otherTarget as DisplayObjectContainer).contains(target))) + gesture.targetAdapter.contains(otherTarget) || + otherGesture.targetAdapter.contains(target) + ) { var gestureDelegate:IGestureDelegate = gesture.delegate; var otherGestureDelegate:IGestureDelegate = otherGesture.delegate; @@ -268,24 +287,37 @@ package org.gestouch.core var gesture:Gesture; var i:uint; - // This vector will contain active gestures for specific touch (ID) during all touch session. - var gesturesForTouch:Vector. = _gesturesForTouchMap[touch.id] as Vector.; + // This vector will contain active gestures for specific touch during all touch session. + var gesturesForTouch:Vector. = _gesturesForTouchMap[touch] as Vector.; if (!gesturesForTouch) { - gesturesForTouch = new Vector.(); - _gesturesForTouchMap[touch.id] = gesturesForTouch; + gesturesForTouch = _gesturesForTouchMap[touch] = new Vector.(); } else { gesturesForTouch.length = 0; - } + } + var target:Object = touch.target; + var hierarchy:Vector.; + for (var key:* in _displayListAdaptersMap) + { + var targetClass:Class = key as Class; + if (target is targetClass) + { + hierarchy = (_displayListAdaptersMap[key] as IDisplayListAdapter).getHierarchy(target); + break; + } + } + if (!hierarchy) + { + throw new Error("Display list adapter not found for target of type '" + targetClass + "'."); + } // Create a sorted(!) list of gestures which are interested in this touch. // Sorting priority: deeper target has higher priority, recently added gesture has higher priority. - var target:InteractiveObject = touch.target; var gesturesForTarget:Vector.; - while (target) + for each (target in hierarchy) { gesturesForTarget = _gesturesForTargetMap[target] as Vector.; if (gesturesForTarget) @@ -302,8 +334,6 @@ package org.gestouch.core } } } - - target = target.parent; } // Then we populate them with this touch and event. @@ -332,7 +362,7 @@ package org.gestouch.core resetDirtyGestures(); } - var gesturesForTouch:Vector. = _gesturesForTouchMap[touch.id] as Vector.; + var gesturesForTouch:Vector. = _gesturesForTouchMap[touch] as Vector.; var gesture:Gesture; var i:int = gesturesForTouch.length; while (i-- > 0) @@ -359,7 +389,7 @@ package org.gestouch.core resetDirtyGestures(); } - var gesturesForTouch:Vector. = _gesturesForTouchMap[touch.id] as Vector.; + var gesturesForTouch:Vector. = _gesturesForTouchMap[touch] as Vector.; var gesture:Gesture; var i:int = gesturesForTouch.length; while (i-- > 0) @@ -371,6 +401,8 @@ package org.gestouch.core gesture.gestouch_internal::touchEndHandler(touch); } } + + gesturesForTouch.length = 0;// release for GC } diff --git a/src/org/gestouch/core/IDisplayListAdapter.as b/src/org/gestouch/core/IDisplayListAdapter.as new file mode 100644 index 0000000..3a77af8 --- /dev/null +++ b/src/org/gestouch/core/IDisplayListAdapter.as @@ -0,0 +1,10 @@ +package org.gestouch.core +{ + /** + * @author Pavel fljot + */ + public interface IDisplayListAdapter + { + function getHierarchy(target:Object):Vector.; + } +} \ No newline at end of file diff --git a/src/org/gestouch/core/IGestureTargetAdapter.as b/src/org/gestouch/core/IGestureTargetAdapter.as new file mode 100644 index 0000000..c66eb0f --- /dev/null +++ b/src/org/gestouch/core/IGestureTargetAdapter.as @@ -0,0 +1,15 @@ +package org.gestouch.core +{ + import flash.geom.Point; + /** + * @author Pavel fljot + */ + public interface IGestureTargetAdapter + { + function get target():Object; + + function globalToLocal(point:Point):Point; + + function contains(target:Object):Boolean; + } +} \ No newline at end of file diff --git a/src/org/gestouch/core/IGesturesManager.as b/src/org/gestouch/core/IGesturesManager.as index 297f39a..c73412a 100644 --- a/src/org/gestouch/core/IGesturesManager.as +++ b/src/org/gestouch/core/IGesturesManager.as @@ -13,5 +13,7 @@ package org.gestouch.core { function addInputAdapter(inputAdapter:IInputAdapter):void; function removeInputAdapter(inputAdapter:IInputAdapter, dispose:Boolean = true):void; + + function addDisplayListAdapter(targetClass:Class, adapter:IDisplayListAdapter):void; } } \ No newline at end of file diff --git a/src/org/gestouch/core/IInputAdapter.as b/src/org/gestouch/core/IInputAdapter.as index 337ff0e..83d9f3d 100644 --- a/src/org/gestouch/core/IInputAdapter.as +++ b/src/org/gestouch/core/IInputAdapter.as @@ -6,7 +6,6 @@ package org.gestouch.core public interface IInputAdapter { function set touchesManager(value:ITouchesManager):void; - function set gesturesManager(value:IGesturesManager):void; function init():void; function dispose():void; diff --git a/src/org/gestouch/core/ITouchesManager.as b/src/org/gestouch/core/ITouchesManager.as index 8ae9639..2243c21 100644 --- a/src/org/gestouch/core/ITouchesManager.as +++ b/src/org/gestouch/core/ITouchesManager.as @@ -5,12 +5,14 @@ package org.gestouch.core */ public interface ITouchesManager { + function set gesturesManager(value:IGesturesManager):void; + function get activeTouchesCount():uint; - function createTouch():Touch; - function addTouch(touch:Touch):Touch; - function removeTouch(touch:Touch):Touch; - function getTouch(touchPointID:int):Touch; - function hasTouch(touchPointID:int):Boolean; + function onTouchBegin(inputAdapter:IInputAdapter, touchID:uint, x:Number, y:Number, target:Object):void; + function onTouchMove(inputAdapter:IInputAdapter, touchID:uint, x:Number, y:Number):void; + function onTouchEnd(inputAdapter:IInputAdapter, touchID:uint, x:Number, y:Number):void; + + function onInputAdapterDispose(inputAdapter:IInputAdapter):void; } } \ No newline at end of file diff --git a/src/org/gestouch/core/Touch.as b/src/org/gestouch/core/Touch.as index 9a707fa..1f2a372 100644 --- a/src/org/gestouch/core/Touch.as +++ b/src/org/gestouch/core/Touch.as @@ -1,6 +1,5 @@ package org.gestouch.core { - import flash.display.InteractiveObject; import flash.geom.Point; @@ -19,7 +18,7 @@ package org.gestouch.core /** * The original event target for this touch (touch began with). */ - public var target:InteractiveObject; + public var target:Object; public var sizeX:Number; public var sizeY:Number; @@ -39,13 +38,16 @@ package org.gestouch.core { return _location.clone(); } - gestouch_internal function setLocation(value:Point):void + gestouch_internal function setLocation(value:Point, time:uint):void { _location = value; _beginLocation = _location.clone(); _previousLocation = _location.clone(); + + _time = time; + _beginTime = time; } - gestouch_internal function updateLocation(x:Number, y:Number):void + gestouch_internal function updateLocation(x:Number, y:Number, time:uint):void { if (_location) { @@ -53,10 +55,11 @@ package org.gestouch.core _previousLocation.y = _location.y; _location.x = x; _location.y = y; + _time = time; } else { - gestouch_internal::setLocation(new Point(x, y)); + gestouch_internal::setLocation(new Point(x, y), time); } } diff --git a/src/org/gestouch/core/TouchesManager.as b/src/org/gestouch/core/TouchesManager.as index aa42691..b4d2526 100644 --- a/src/org/gestouch/core/TouchesManager.as +++ b/src/org/gestouch/core/TouchesManager.as @@ -1,5 +1,8 @@ package org.gestouch.core { + import flash.geom.Point; + import flash.utils.Dictionary; + import flash.utils.getTimer; /** * @author Pavel fljot */ @@ -20,6 +23,13 @@ package org.gestouch.core } + protected var _gesturesManager:IGesturesManager; + public function set gesturesManager(value:IGesturesManager):void + { + _gesturesManager = value; + } + + protected var _activeTouchesCount:uint; public function get activeTouchesCount():uint { @@ -54,50 +64,112 @@ package org.gestouch.core } - public function createTouch():Touch + public function onTouchBegin(inputAdapter:IInputAdapter, touchID:uint, x:Number, y:Number, target:Object):void + { + var overlappingTouches:Dictionary = _touchesMap[touchID] as Dictionary; + if (overlappingTouches) + { + // In case we listen to both TouchEvents and MouseEvents, one of them will come first + // (right now looks like MouseEvent dispatches first, but who know what Adobe will + // do tomorrow). This check is to filter out the one comes second. + for each (var registeredTouch:Touch in overlappingTouches) + { + if (registeredTouch.target == target) + return; + } + } + else + { + overlappingTouches = _touchesMap[touchID] = new Dictionary(); + _activeTouchesCount++; + } + + var touch:Touch = createTouch(); + touch.id = touchID; + touch.target = target; + touch.gestouch_internal::setLocation(new Point(x, y), getTimer()); + overlappingTouches[inputAdapter] = touch; + + _gesturesManager.gestouch_internal::onTouchBegin(touch); + } + + + public function onTouchMove(inputAdapter:IInputAdapter, touchID:uint, x:Number, y:Number):void + { + var overlappingTouches:Dictionary = _touchesMap[touchID] as Dictionary; + if (!overlappingTouches) + return;//this touch isn't properly registered.. some fake + + var touch:Touch = overlappingTouches[inputAdapter] as Touch; + if (!touch) + return;//touch with this ID from this inputAdapter is not registered. see workaround reason above + + touch.gestouch_internal::updateLocation(x, y, getTimer()); + + _gesturesManager.gestouch_internal::onTouchMove(touch); + } + + + public function onTouchEnd(inputAdapter:IInputAdapter, touchID:uint, x:Number, y:Number):void + { + var overlappingTouches:Dictionary = _touchesMap[touchID] as Dictionary; + if (!overlappingTouches) + return;//this touch isn't properly registered.. some fake + + var touch:Touch = overlappingTouches[inputAdapter] as Touch; + if (!touch) + return;//touch with this ID from this inputAdapter is not registered. see workaround reason above + + touch.gestouch_internal::updateLocation(x, y, getTimer()); + + delete overlappingTouches[inputAdapter]; + var empty:Boolean = true; + for (var key:Object in overlappingTouches) + { + empty = false; + break; + } + if (empty) + { + delete _touchesMap[touchID]; + _activeTouchesCount--; + } + + _gesturesManager.gestouch_internal::onTouchEnd(touch); + } + + + /** + * Must be called by IInputAdapter#dispose() to remove all the touches invoked by it. + */ + public function onInputAdapterDispose(inputAdapter:IInputAdapter):void + { + for (var touchID:Object in _touchesMap) + { + var overlappingTouches:Dictionary = _touchesMap[touchID] as Dictionary; + if (overlappingTouches[inputAdapter]) + { + delete overlappingTouches[inputAdapter]; + var empty:Boolean = true; + for (var key:Object in overlappingTouches) + { + empty = false; + break; + } + if (empty) + { + delete _touchesMap[touchID]; + _activeTouchesCount--; + } + } + } + } + + + protected function createTouch():Touch { //TODO: pool return new Touch(); } - - - public function addTouch(touch:Touch):Touch - { - if (_touchesMap.hasOwnProperty(touch.id)) - { - throw new Error("Touch with id " + touch.id + " is already registered."); - } - - _touchesMap[touch.id] = touch; - _activeTouchesCount++; - - return touch; - } - - - public function removeTouch(touch:Touch):Touch - { - if (!_touchesMap.hasOwnProperty(touch.id)) - { - throw new Error("Touch with id " + touch.id + " is not registered."); - } - - delete _touchesMap[touch.id]; - _activeTouchesCount--; - - return touch; - } - - - public function hasTouch(touchPointID:int):Boolean - { - return _touchesMap.hasOwnProperty(touchPointID); - } - - - public function getTouch(touchPointID:int):Touch - { - return _touchesMap[touchPointID] as Touch; - } } } \ No newline at end of file diff --git a/src/org/gestouch/extensions/starling/StarlingDisplayListAdapter.as b/src/org/gestouch/extensions/starling/StarlingDisplayListAdapter.as new file mode 100644 index 0000000..bd860b8 --- /dev/null +++ b/src/org/gestouch/extensions/starling/StarlingDisplayListAdapter.as @@ -0,0 +1,27 @@ +package org.gestouch.extensions.starling +{ + import starling.display.DisplayObject; + import org.gestouch.core.IDisplayListAdapter; + + + /** + * @author Pavel fljot + */ + public class StarlingDisplayListAdapter implements IDisplayListAdapter + { + public function getHierarchy(genericTarget:Object):Vector. + { + var list:Vector. = new Vector.(); + var i:uint = 0; + var target:DisplayObject = genericTarget as DisplayObject; + while (target) + { + list[i] = target; + target = target.parent; + i++; + } + + return list; + } + } +} \ No newline at end of file diff --git a/src/org/gestouch/extensions/starling/StarlingDisplayObjectAdapter.as b/src/org/gestouch/extensions/starling/StarlingDisplayObjectAdapter.as new file mode 100644 index 0000000..e53f829 --- /dev/null +++ b/src/org/gestouch/extensions/starling/StarlingDisplayObjectAdapter.as @@ -0,0 +1,47 @@ +package org.gestouch.extensions.starling +{ + import org.gestouch.core.IGestureTargetAdapter; + import starling.display.DisplayObject; + import starling.display.DisplayObjectContainer; + import flash.geom.Point; + import flash.utils.Dictionary; + + + + /** + * @author Pavel fljot + */ + final public class StarlingDisplayObjectAdapter implements IGestureTargetAdapter + { + private var _targetWeekStorage:Dictionary = new Dictionary(true); + + + public function StarlingDisplayObjectAdapter(target:DisplayObject) + { + _targetWeekStorage[target] = true; + } + + + public function get target():Object + { + for (var key:Object in _targetWeekStorage) + { + return key; + } + return null; + } + + + public function globalToLocal(point:Point):Point + { + return (target as DisplayObject).globalToLocal(point); + } + + + public function contains(target:Object):Boolean + { + const targetAsDOC:DisplayObjectContainer = this.target as DisplayObjectContainer; + return (targetAsDOC && targetAsDOC.contains(target as DisplayObject)); + } + } +} \ No newline at end of file diff --git a/src/org/gestouch/extensions/starling/StarlingInputAdapter.as b/src/org/gestouch/extensions/starling/StarlingInputAdapter.as new file mode 100644 index 0000000..213ef05 --- /dev/null +++ b/src/org/gestouch/extensions/starling/StarlingInputAdapter.as @@ -0,0 +1,185 @@ +package org.gestouch.extensions.starling +{ + import flash.events.EventPhase; + import flash.events.MouseEvent; + import flash.events.TouchEvent; + import flash.geom.Point; + import flash.ui.Mouse; + import org.gestouch.input.AbstractInputAdapter; + import starling.core.Starling; + + + + /** + * @author Pavel fljot + */ + public class StarlingInputAdapter extends AbstractInputAdapter + { + private static const PRIMARY_TOUCH_POINT_ID:uint = 0; + + protected var _starling:Starling; + + + public function StarlingInputAdapter(starling:Starling) + { + super(); + + if (!starling) + { + throw new Error("Argument error."); + } + + _starling = starling; + } + + + protected function get supportsTouchEvents():Boolean + { + // just like in Starling (starling.core::Starling) + return !(Mouse.supportsCursor || !Starling.multitouchEnabled); + } + + + override public function init():void + { + // We want to begin tracking only those touches that happen on Stage3D layer, + // e.g. event.target == nativeStage. That's we don't listen for touch begin + // in capture phase (as we do for native display list). + if (supportsTouchEvents) + { + _starling.nativeStage.addEventListener(TouchEvent.TOUCH_BEGIN, touchBeginHandler); + } + else + { + _starling.nativeStage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler); + } + } + + + override public function dispose():void + { + _starling.nativeStage.removeEventListener(TouchEvent.TOUCH_BEGIN, touchBeginHandler); + _starling.nativeStage.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler); + uninstallStageListeners(); + _starling = null; + _touchesManager.onInputAdapterDispose(this); + _touchesManager = null; + } + + + protected function installStageListeners():void + { + // Maximum priority to prevent event hijacking + if (supportsTouchEvents) + { + _starling.nativeStage.addEventListener(TouchEvent.TOUCH_MOVE, touchMoveHandler, true, int.MAX_VALUE); + _starling.nativeStage.addEventListener(TouchEvent.TOUCH_MOVE, touchMoveHandler, false, int.MAX_VALUE); + _starling.nativeStage.addEventListener(TouchEvent.TOUCH_END, touchEndHandler, true, int.MAX_VALUE); + _starling.nativeStage.addEventListener(TouchEvent.TOUCH_END, touchEndHandler, false, int.MAX_VALUE); + } + else + { + _starling.nativeStage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler, true, int.MAX_VALUE); + _starling.nativeStage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler, false, int.MAX_VALUE); + _starling.nativeStage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler, true, int.MAX_VALUE); + _starling.nativeStage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler, false, int.MAX_VALUE); + } + } + + + protected function uninstallStageListeners():void + { + _starling.nativeStage.removeEventListener(TouchEvent.TOUCH_MOVE, touchMoveHandler, true); + _starling.nativeStage.removeEventListener(TouchEvent.TOUCH_MOVE, touchMoveHandler, false); + _starling.nativeStage.removeEventListener(TouchEvent.TOUCH_END, touchEndHandler, true); + _starling.nativeStage.removeEventListener(TouchEvent.TOUCH_END, touchEndHandler, false); + _starling.nativeStage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler, true); + _starling.nativeStage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler, false); + _starling.nativeStage.removeEventListener(MouseEvent.MOUSE_UP, mouseUpHandler, true); + _starling.nativeStage.removeEventListener(MouseEvent.MOUSE_UP, mouseUpHandler, false); + } + + + protected function mouseDownHandler(event:MouseEvent):void + { + // We ignore event with bubbling phase because it happened on some native InteractiveObject, + // which basically hovers Stage3D layer. So we treat it as if Starling wouldn't recieve any input. + if (event.eventPhase == EventPhase.BUBBLING_PHASE) + return; + + var target:Object = _starling.stage.hitTest(new Point(event.stageX, event.stageY), true); + _touchesManager.onTouchBegin(this, PRIMARY_TOUCH_POINT_ID, event.stageX, event.stageY, target); + + if (_touchesManager.activeTouchesCount > 0) + { + installStageListeners(); + } + } + + + protected function mouseMoveHandler(event:MouseEvent):void + { + if (event.eventPhase == EventPhase.BUBBLING_PHASE) + return;//we listen in capture or at_target (to catch on empty stage) + + _touchesManager.onTouchMove(this, PRIMARY_TOUCH_POINT_ID, event.stageX, event.stageY); + } + + + protected function mouseUpHandler(event:MouseEvent):void + { + if (event.eventPhase == EventPhase.BUBBLING_PHASE) + return;//we listen in capture or at_target (to catch on empty stage) + + _touchesManager.onTouchEnd(this, PRIMARY_TOUCH_POINT_ID, event.stageX, event.stageY); + + if (_touchesManager.activeTouchesCount == 0) + { + uninstallStageListeners(); + } + } + + + protected function touchBeginHandler(event:TouchEvent):void + { + // We ignore event with bubbling phase because it happened on some native InteractiveObject, + // which basically hovers Stage3D layer. So we treat it as if Starling wouldn't recieve any input. + if (event.eventPhase == EventPhase.BUBBLING_PHASE) + return; + + var target:Object = _starling.stage.hitTest(new Point(event.stageX, event.stageY), true); + _touchesManager.onTouchBegin(this, event.touchPointID, event.stageX, event.stageY, target); + + if (_touchesManager.activeTouchesCount > 0) + { + installStageListeners(); + } + } + + + protected function touchMoveHandler(event:TouchEvent):void + { + if (event.eventPhase == EventPhase.BUBBLING_PHASE) + return;//we listen in capture or at_target (to catch on empty stage) + + _touchesManager.onTouchMove(this, event.touchPointID, event.stageX, event.stageY); + } + + + protected function touchEndHandler(event:TouchEvent):void + { + if (event.eventPhase == EventPhase.BUBBLING_PHASE) + return;//we listen in capture or at_target (to catch on empty stage) + + _touchesManager.onTouchEnd(this, event.touchPointID, event.stageX, event.stageY); + + if (_touchesManager.activeTouchesCount == 0) + { + uninstallStageListeners(); + } + + // TODO: handle cancelled touch: + // if (event.hasOwnProperty("isTouchPointCanceled") && event["isTouchPointCanceled"] && ... + } + } +} \ No newline at end of file diff --git a/src/org/gestouch/gestures/Gesture.as b/src/org/gestouch/gestures/Gesture.as index c2b6cd1..72766ea 100644 --- a/src/org/gestouch/gestures/Gesture.as +++ b/src/org/gestouch/gestures/Gesture.as @@ -3,12 +3,12 @@ package org.gestouch.gestures import org.gestouch.core.GestureState; import org.gestouch.core.GesturesManager; import org.gestouch.core.IGestureDelegate; + import org.gestouch.core.IGestureTargetAdapter; import org.gestouch.core.IGesturesManager; import org.gestouch.core.Touch; import org.gestouch.core.gestouch_internal; import org.gestouch.events.GestureStateEvent; - import flash.display.InteractiveObject; import flash.events.EventDispatcher; import flash.geom.Point; import flash.system.Capabilities; @@ -50,20 +50,29 @@ package org.gestouch.gestures protected var _pendingRecognizedState:uint; - public function Gesture(target:InteractiveObject = null) + public function Gesture(targetAdapter:IGestureTargetAdapter = null) { super(); preinit(); - this.target = target; + setTarget(targetAdapter); } /** @private */ - private var _targetWeekStorage:Dictionary; + protected var _targetAdapter:IGestureTargetAdapter; + /** + * + */ + public function get targetAdapter():IGestureTargetAdapter + { + return _targetAdapter; + } + /** + * FIXME * InteractiveObject (DisplayObject) which this gesture is tracking the actual gesture motion on. * * Could be some image, component (like map) or the larger view like Stage. @@ -74,30 +83,9 @@ package org.gestouch.gestures * * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/InteractiveObject.html */ - public function get target():InteractiveObject + public function get target():Object { - for (var key:Object in _targetWeekStorage) - { - return key as InteractiveObject; - } - return null; - } - public function set target(value:InteractiveObject):void - { - var target:InteractiveObject = this.target; - if (target == value) - return; - - uninstallTarget(target); - for (var key:Object in _targetWeekStorage) - { - delete _targetWeekStorage[key]; - } - if (value) - { - (_targetWeekStorage ||= new Dictionary(true))[value] = true; - } - installTarget(value); + return _targetAdapter ? _targetAdapter.target : null; } @@ -185,6 +173,17 @@ package org.gestouch.gestures // Public methods // //-------------------------------------------------------------------------- + + public function setTarget(targetAdapter:IGestureTargetAdapter):void + { + if (_targetAdapter == targetAdapter) + return; + + uninstallTarget(this.targetAdapter); + _targetAdapter = targetAdapter; + installTarget(this.targetAdapter); + } + [Abstract] /** @@ -261,7 +260,7 @@ package org.gestouch.gestures { //TODO reset(); - target = null; + setTarget(null); delegate = null; _gesturesToFail = null; } @@ -310,9 +309,9 @@ package org.gestouch.gestures * * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/InteractiveObject.html */ - protected function installTarget(target:InteractiveObject):void + protected function installTarget(targetAdapter:IGestureTargetAdapter):void { - if (target) + if (targetAdapter) { _gesturesManager.gestouch_internal::addGesture(this); } @@ -326,9 +325,9 @@ package org.gestouch.gestures * * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/InteractiveObject.html */ - protected function uninstallTarget(target:InteractiveObject):void + protected function uninstallTarget(targetAdapter:IGestureTargetAdapter):void { - if (target) + if (targetAdapter) { _gesturesManager.gestouch_internal::removeGesture(this); } diff --git a/src/org/gestouch/gestures/LongPressGesture.as b/src/org/gestouch/gestures/LongPressGesture.as index a99ea6b..716d277 100644 --- a/src/org/gestouch/gestures/LongPressGesture.as +++ b/src/org/gestouch/gestures/LongPressGesture.as @@ -1,10 +1,10 @@ package org.gestouch.gestures { import org.gestouch.core.GestureState; + import org.gestouch.core.IGestureTargetAdapter; import org.gestouch.core.Touch; import org.gestouch.events.LongPressGestureEvent; - import flash.display.InteractiveObject; import flash.events.TimerEvent; import flash.utils.Timer; @@ -30,7 +30,7 @@ package org.gestouch.gestures protected var _numTouchesRequiredReached:Boolean; - public function LongPressGesture(target:InteractiveObject = null) + public function LongPressGesture(target:IGestureTargetAdapter = null) { super(target); } diff --git a/src/org/gestouch/gestures/PanGesture.as b/src/org/gestouch/gestures/PanGesture.as index ddf74bf..f592a93 100644 --- a/src/org/gestouch/gestures/PanGesture.as +++ b/src/org/gestouch/gestures/PanGesture.as @@ -1,10 +1,10 @@ package org.gestouch.gestures { import org.gestouch.core.GestureState; + import org.gestouch.core.IGestureTargetAdapter; import org.gestouch.core.Touch; import org.gestouch.events.PanGestureEvent; - import flash.display.InteractiveObject; import flash.geom.Point; [Event(name="gesturePan", type="org.gestouch.events.PanGestureEvent")] @@ -27,7 +27,7 @@ package org.gestouch.gestures protected var _gestureBeginOffsetY:Number; - public function PanGesture(target:InteractiveObject = null) + public function PanGesture(target:IGestureTargetAdapter = null) { super(target); } diff --git a/src/org/gestouch/gestures/RotateGesture.as b/src/org/gestouch/gestures/RotateGesture.as index da1665b..4045053 100644 --- a/src/org/gestouch/gestures/RotateGesture.as +++ b/src/org/gestouch/gestures/RotateGesture.as @@ -1,11 +1,11 @@ package org.gestouch.gestures { import org.gestouch.core.GestureState; + import org.gestouch.core.IGestureTargetAdapter; import org.gestouch.core.Touch; import org.gestouch.events.RotateGestureEvent; import org.gestouch.utils.GestureUtils; - import flash.display.InteractiveObject; import flash.geom.Point; [Event(name="gestureRotate", type="org.gestouch.events.RotateGestureEvent")] @@ -24,7 +24,7 @@ package org.gestouch.gestures protected var _transformVector:Point; - public function RotateGesture(target:InteractiveObject = null) + public function RotateGesture(target:IGestureTargetAdapter = null) { super(target); } diff --git a/src/org/gestouch/gestures/SwipeGesture.as b/src/org/gestouch/gestures/SwipeGesture.as index 9665493..249ee13 100644 --- a/src/org/gestouch/gestures/SwipeGesture.as +++ b/src/org/gestouch/gestures/SwipeGesture.as @@ -1,10 +1,10 @@ package org.gestouch.gestures { import org.gestouch.core.GestureState; + import org.gestouch.core.IGestureTargetAdapter; import org.gestouch.core.Touch; import org.gestouch.events.SwipeGestureEvent; - import flash.display.InteractiveObject; import flash.geom.Point; [Event(name="gestureSwipe", type="org.gestouch.events.SwipeGestureEvent")] @@ -31,7 +31,7 @@ package org.gestouch.gestures protected var _decelerationCounter:uint = 0; - public function SwipeGesture(target:InteractiveObject = null) + public function SwipeGesture(target:IGestureTargetAdapter = null) { super(target); } diff --git a/src/org/gestouch/gestures/TapGesture.as b/src/org/gestouch/gestures/TapGesture.as index fc5881b..ba33ca8 100644 --- a/src/org/gestouch/gestures/TapGesture.as +++ b/src/org/gestouch/gestures/TapGesture.as @@ -1,10 +1,10 @@ package org.gestouch.gestures { import org.gestouch.core.GestureState; + import org.gestouch.core.IGestureTargetAdapter; import org.gestouch.core.Touch; import org.gestouch.events.TapGestureEvent; - import flash.display.InteractiveObject; import flash.events.TimerEvent; import flash.utils.Timer; @@ -28,7 +28,7 @@ package org.gestouch.gestures protected var _tapCounter:uint = 0; - public function TapGesture(target:InteractiveObject = null) + public function TapGesture(target:IGestureTargetAdapter = null) { super(target); } diff --git a/src/org/gestouch/gestures/TransformGesture.as b/src/org/gestouch/gestures/TransformGesture.as index b3a2daa..3c9c20c 100644 --- a/src/org/gestouch/gestures/TransformGesture.as +++ b/src/org/gestouch/gestures/TransformGesture.as @@ -1,11 +1,11 @@ package org.gestouch.gestures { import org.gestouch.core.GestureState; + import org.gestouch.core.IGestureTargetAdapter; import org.gestouch.core.Touch; import org.gestouch.events.TransformGestureEvent; import org.gestouch.utils.GestureUtils; - import flash.display.InteractiveObject; import flash.geom.Point; @@ -22,7 +22,7 @@ package org.gestouch.gestures protected var _transformVector:Point; - public function TransformGesture(target:InteractiveObject = null) + public function TransformGesture(target:IGestureTargetAdapter = null) { super(target); } diff --git a/src/org/gestouch/gestures/ZoomGesture.as b/src/org/gestouch/gestures/ZoomGesture.as index 09608b3..ebc367e 100644 --- a/src/org/gestouch/gestures/ZoomGesture.as +++ b/src/org/gestouch/gestures/ZoomGesture.as @@ -1,10 +1,10 @@ package org.gestouch.gestures { import org.gestouch.core.GestureState; + import org.gestouch.core.IGestureTargetAdapter; import org.gestouch.core.Touch; import org.gestouch.events.ZoomGestureEvent; - import flash.display.InteractiveObject; import flash.geom.Point; [Event(name="gestureZoom", type="org.gestouch.events.ZoomGestureEvent")] @@ -24,7 +24,7 @@ package org.gestouch.gestures protected var _transformVector:Point; - public function ZoomGesture(target:InteractiveObject = null) + public function ZoomGesture(target:IGestureTargetAdapter = null) { super(target); } diff --git a/src/org/gestouch/input/AbstractInputAdapter.as b/src/org/gestouch/input/AbstractInputAdapter.as index 8543af5..a870a62 100644 --- a/src/org/gestouch/input/AbstractInputAdapter.as +++ b/src/org/gestouch/input/AbstractInputAdapter.as @@ -1,6 +1,5 @@ package org.gestouch.input { - import org.gestouch.core.IGesturesManager; import org.gestouch.core.IInputAdapter; import org.gestouch.core.ITouchesManager; @@ -11,7 +10,6 @@ package org.gestouch.input public class AbstractInputAdapter implements IInputAdapter { protected var _touchesManager:ITouchesManager; - protected var _gesturesManager:IGesturesManager; public function AbstractInputAdapter() @@ -29,12 +27,6 @@ package org.gestouch.input } - public function set gesturesManager(value:IGesturesManager):void - { - _gesturesManager = value; - } - - [Abstract] public function init():void { diff --git a/src/org/gestouch/input/MouseInputAdapter.as b/src/org/gestouch/input/MouseInputAdapter.as index 4a3e8ea..9a7e4b0 100644 --- a/src/org/gestouch/input/MouseInputAdapter.as +++ b/src/org/gestouch/input/MouseInputAdapter.as @@ -1,14 +1,8 @@ package org.gestouch.input { - import org.gestouch.core.Touch; - import org.gestouch.core.gestouch_internal; - - import flash.display.InteractiveObject; import flash.display.Stage; import flash.events.EventPhase; import flash.events.MouseEvent; - import flash.geom.Point; - import flash.utils.getTimer; /** @@ -46,6 +40,8 @@ package org.gestouch.input _stage.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler, true); _stage.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler); uninstallStageListeners(); + _touchesManager.onInputAdapterDispose(this); + _touchesManager = null; } @@ -72,23 +68,13 @@ package org.gestouch.input { if (event.eventPhase == EventPhase.BUBBLING_PHASE) return;//we listen in capture or at_target (to catch on empty stage) - // Way to prevent MouseEvent/TouchEvent collisions. - // Also helps to ignore possible fake events. - if (_touchesManager.hasTouch(PRIMARY_TOUCH_POINT_ID)) - return; - installStageListeners(); + _touchesManager.onTouchBegin(this, PRIMARY_TOUCH_POINT_ID, event.stageX, event.stageY, event.target); - var touch:Touch = _touchesManager.createTouch(); - touch.target = event.target as InteractiveObject; - touch.id = PRIMARY_TOUCH_POINT_ID; - touch.gestouch_internal::setLocation(new Point(event.stageX, event.stageY)); - touch.gestouch_internal::setTime(getTimer()); - touch.gestouch_internal::setBeginTime(getTimer()); - - _touchesManager.addTouch(touch); - - _gesturesManager.gestouch_internal::onTouchBegin(touch); + if (_touchesManager.activeTouchesCount > 0) + { + installStageListeners(); + } } @@ -96,16 +82,8 @@ package org.gestouch.input { if (event.eventPhase == EventPhase.BUBBLING_PHASE) return;//we listen in capture or at_target (to catch on empty stage) - // Way to prevent MouseEvent/TouchEvent collisions. - // Also helps to ignore possible fake events. - if (!_touchesManager.hasTouch(PRIMARY_TOUCH_POINT_ID)) - return; - var touch:Touch = _touchesManager.getTouch(PRIMARY_TOUCH_POINT_ID); - touch.gestouch_internal::updateLocation(event.stageX, event.stageY); - touch.gestouch_internal::setTime(getTimer()); - - _gesturesManager.gestouch_internal::onTouchMove(touch); + _touchesManager.onTouchMove(this, PRIMARY_TOUCH_POINT_ID, event.stageX, event.stageY); } @@ -114,18 +92,7 @@ package org.gestouch.input if (event.eventPhase == EventPhase.BUBBLING_PHASE) return;//we listen in capture or at_target (to catch on empty stage) - // Way to prevent MouseEvent/TouchEvent collisions. - // Also helps to ignore possible fake events. - if (!_touchesManager.hasTouch(PRIMARY_TOUCH_POINT_ID)) - return; - - var touch:Touch = _touchesManager.getTouch(PRIMARY_TOUCH_POINT_ID); - touch.gestouch_internal::updateLocation(event.stageX, event.stageY); - touch.gestouch_internal::setTime(getTimer()); - - _gesturesManager.gestouch_internal::onTouchEnd(touch); - - _touchesManager.removeTouch(touch); + _touchesManager.onTouchEnd(this, PRIMARY_TOUCH_POINT_ID, event.stageX, event.stageY); if (_touchesManager.activeTouchesCount == 0) { diff --git a/src/org/gestouch/input/TouchInputAdapter.as b/src/org/gestouch/input/TouchInputAdapter.as index 99f82f4..920cd2d 100644 --- a/src/org/gestouch/input/TouchInputAdapter.as +++ b/src/org/gestouch/input/TouchInputAdapter.as @@ -1,16 +1,10 @@ package org.gestouch.input { - import org.gestouch.core.Touch; - import org.gestouch.core.gestouch_internal; - - import flash.display.InteractiveObject; import flash.display.Stage; import flash.events.EventPhase; import flash.events.TouchEvent; - import flash.geom.Point; import flash.ui.Multitouch; import flash.ui.MultitouchInputMode; - import flash.utils.getTimer; /** @@ -57,6 +51,8 @@ package org.gestouch.input _stage.removeEventListener(TouchEvent.TOUCH_BEGIN, touchBeginHandler, true); _stage.removeEventListener(TouchEvent.TOUCH_BEGIN, touchBeginHandler); uninstallStageListeners(); + _touchesManager.onInputAdapterDispose(this); + _touchesManager = null; } @@ -83,36 +79,13 @@ package org.gestouch.input { if (event.eventPhase == EventPhase.BUBBLING_PHASE) return;//we listen in capture or at_target (to catch on empty stage) - // Way to prevent MouseEvent/TouchEvent collisions. - // Also helps to ignore possible fake events. - if (_touchesManager.hasTouch(event.touchPointID)) - return; + + _touchesManager.onTouchBegin(this, event.touchPointID, event.stageX, event.stageY, event.target); - installStageListeners(); - - var touch:Touch = _touchesManager.createTouch(); - touch.id = event.touchPointID; - touch.target = event.target as InteractiveObject; - touch.gestouch_internal::setLocation(new Point(event.stageX, event.stageY)); - touch.sizeX = event.sizeX; - touch.sizeY = event.sizeY; - touch.pressure = event.pressure; - //TODO: conditional compilation? - if (event.hasOwnProperty("timestamp")) + if (_touchesManager.activeTouchesCount > 0) { - touch.gestouch_internal::setTime(event["timestamp"]); - touch.gestouch_internal::setBeginTime(event["timestamp"]); + installStageListeners(); } - else - { - touch.gestouch_internal::setTime(getTimer()); - touch.gestouch_internal::setBeginTime(getTimer()); - } - - _touchesManager.addTouch(touch); - _touchesMap[touch.id] = true; - - _gesturesManager.gestouch_internal::onTouchBegin(touch); } @@ -120,27 +93,8 @@ package org.gestouch.input { if (event.eventPhase == EventPhase.BUBBLING_PHASE) return;//we listen in capture or at_target (to catch on empty stage) - // Way to prevent MouseEvent/TouchEvent collisions. - // Also helps to ignore possible fake events. - if (!_touchesManager.hasTouch(event.touchPointID) || !_touchesMap.hasOwnProperty(event.touchPointID)) - return; - var touch:Touch = _touchesManager.getTouch(event.touchPointID); - touch.gestouch_internal::updateLocation(event.stageX, event.stageY); - touch.sizeX = event.sizeX; - touch.sizeY = event.sizeY; - touch.pressure = event.pressure; - //TODO: conditional compilation? - if (event.hasOwnProperty("timestamp")) - { - touch.gestouch_internal::setTime(event["timestamp"]); - } - else - { - touch.gestouch_internal::setTime(getTimer()); - } - - _gesturesManager.gestouch_internal::onTouchMove(touch); + _touchesManager.onTouchMove(this, event.touchPointID, event.stageX, event.stageY); } @@ -149,30 +103,7 @@ package org.gestouch.input if (event.eventPhase == EventPhase.BUBBLING_PHASE) return;//we listen in capture or at_target (to catch on empty stage) - // Way to prevent MouseEvent/TouchEvent collisions. - // Also helps to ignore possible fake events. - if (!_touchesManager.hasTouch(event.touchPointID)) - return; - - var touch:Touch = _touchesManager.getTouch(event.touchPointID); - touch.gestouch_internal::updateLocation(event.stageX, event.stageY); - touch.sizeX = event.sizeX; - touch.sizeY = event.sizeY; - touch.pressure = event.pressure; - //TODO: conditional compilation? - if (event.hasOwnProperty("timestamp")) - { - touch.gestouch_internal::setTime(event["timestamp"]); - } - else - { - touch.gestouch_internal::setTime(getTimer()); - } - - _gesturesManager.gestouch_internal::onTouchEnd(touch); - - _touchesManager.removeTouch(touch); - delete _touchesMap[touch.id]; + _touchesManager.onTouchEnd(this, event.touchPointID, event.stageX, event.stageY); if (_touchesManager.activeTouchesCount == 0) { diff --git a/src/org/gestouch/utils/GestureUtils.as b/src/org/gestouch/utils/GestureUtils.as index 4d57d69..13eef7b 100644 --- a/src/org/gestouch/utils/GestureUtils.as +++ b/src/org/gestouch/utils/GestureUtils.as @@ -1,5 +1,6 @@ package org.gestouch.utils { + import flash.geom.Point; import flash.system.Capabilities; /** * Set of constants. @@ -24,5 +25,6 @@ package org.gestouch.utils * Precalculated coefficient Math.PI * 2 */ public static const PI_DOUBLE:Number = Math.PI * 2; + public static const GLOBAL_ZERO:Point = new Point(); } } \ No newline at end of file
Could be some image, component (like map) or the larger view like Stage.