mirror of
https://github.com/scratchfoundation/Gestouch.git
synced 2024-11-27 01:35:40 -05:00
v0.4
Squashed commit of the following: commit1eb3dfa9e1
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Tue Aug 7 17:47:43 2012 +0300 Bumped version to 0.4 commit3acafd1dfb
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Tue Aug 7 17:46:53 2012 +0300 Minor fix for Gesture state machine to dispatch STATE_CHANGE even when cycling around CHANGED state commit2678e12de8
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Tue Aug 7 17:46:12 2012 +0300 Add protected from a mistyped event listening commita3b618e90a
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Tue Aug 7 17:38:10 2012 +0300 Move native adapters to extensions package commit9c817b7472
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Wed Aug 1 19:41:58 2012 +0300 Implement touch cancelation handling commit7bfb1fae36
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Fri Jul 20 14:52:18 2012 +0300 Minor type fix commit7bacbf087b
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Fri Jul 20 14:49:48 2012 +0300 Minor changes commit7278e863d4
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Fri Jul 20 14:48:52 2012 +0300 Refactor dirty gestures reset commiteab6cb4a1c
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Fri Jul 20 14:47:52 2012 +0300 Remove unnecessary casting commit4bdefd12bb
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Fri Jul 13 18:27:20 2012 +0300 Minor cleanup in LongPressGesture commitc5f91e6de1
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Fri Jul 13 18:08:07 2012 +0300 Improve internal algorithm for TapGesture commited2efc1954
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Fri Jul 13 16:51:06 2012 +0300 Improve internal algorithm for SwipeGesture commitc2125a06e1
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Thu Jul 12 12:46:11 2012 +0300 Remove unnecessary imports commit4a8e6feada
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Wed Jul 11 22:47:55 2012 +0300 Improve internal algorithm for SwipeGesture but still under question commit850ed9849f
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Wed Jul 11 13:34:50 2012 +0300 Change rotation values from degrees to radians commit7be7c8c40a
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Wed Jul 11 13:28:43 2012 +0300 Improve internal algorithm for TransformGesture commitf8149935db
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Tue Jul 10 17:11:46 2012 +0300 Improve internal algorithm for ZoomGesture commitfd55468579
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Tue Jul 10 17:11:30 2012 +0300 Improve internal algorithm for RotateGesture commit0555813e25
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Mon Jul 9 16:47:09 2012 +0300 Hotfix for gesture state machine validation commitc2d31b743b
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Thu Jul 5 10:17:53 2012 +0300 Fix Stage + Starling gestures simultaneous recognition commit37f6220eb5
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Wed Jul 4 23:44:16 2012 +0300 Changed default PanGesture#maxNumTouchesRequired to uint.MAX_VALUE commit2e02b13581
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Wed Jul 4 23:43:01 2012 +0300 Improved early failing strategy implementation as in iOS UIGestureRecognizers. Also fixes bugs with new validating state machine. commit6273cc33e6
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Wed Jul 4 21:55:34 2012 +0300 Bumped version to 0.4-beta commit00040bb2e2
Merge:62fa492
963c660
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Wed Jul 4 21:41:26 2012 +0300 Merge branch 'refs/heads/features/starling' into develop commit963c66024e
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Wed Jul 4 21:40:55 2012 +0300 Update README commit2d52729f7c
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Wed Jul 4 21:36:55 2012 +0300 Update "requireGestureToFail" API implementation commit60d9cb6744
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Wed Jul 4 21:34:49 2012 +0300 Fixed potential bug with registering gesture target in case when previous gesture target was GC-ed, but gesture instance was reused to set a new target commitbbfb3fc34c
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Tue Jul 3 23:23:48 2012 +0300 Minor performance improvement via "use namespace" access commit193332b9d0
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Tue Jul 3 23:12:38 2012 +0300 Change GestureState to "real" enum commit039a7d79f4
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Mon Jul 2 22:33:50 2012 +0300 Fixed and slightly improved gesture reset commit09c35a5d97
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Mon Jul 2 18:29:33 2012 +0300 Minor tabs and spaces cleanup commitcdd90d7479
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Mon Jul 2 18:28:30 2012 +0300 Moved IDisplayListAdapter creating and retrieving logic to Gestouch class commit32e9ff979c
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Sun Jul 1 12:57:31 2012 +0300 Update to always include Stage in hierarchy so that gestures registered on Stage will always react on touch commitd072f6e478
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Sun Jul 1 11:48:10 2012 +0300 Fix for potential RTE related to "contains" logic commit33108a1bc7
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Fri Jun 1 13:51:15 2012 +0300 Changed global system gesture slop to variable commit8d870f4e93
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Fri Jun 1 13:49:29 2012 +0300 Improved event handling autocompletion (for FD) commit54cc5d42bd
Merge:e9132fe
62fa492
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Wed May 30 20:54:47 2012 +0300 Merge branch 'refs/heads/develop' into features/starling commite9132fec9b
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Tue May 29 17:03:16 2012 +0300 Massive refactoring of input layer commite0a892654d
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Tue May 29 17:01:54 2012 +0300 Untyped globalToLocal call fix commit0adfac4c5d
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Fri May 4 14:54:25 2012 +0300 Fixed touch/mouse event handling condition in StarlingInputAdapter commit62fa492d66
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Mon Apr 2 17:17:48 2012 +0300 Fixed initial offset calculation for PanGesture commit696b6367f2
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Mon Apr 2 17:16:48 2012 +0300 Fixed location for TapGesture commit86439e7627
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Sun Mar 18 12:19:43 2012 +0200 README update for simplified API commitd1150b2a35
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Sun Mar 18 12:17:01 2012 +0200 Simplified API for Gesture target commit63a5df8761
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Fri Mar 16 01:04:50 2012 +0200 Bumped version to 0.4-alpha commitc983ebe32a
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Fri Mar 16 01:03:40 2012 +0200 Readme quick update for Starling commit13dbd61014
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Tue Mar 13 14:05:50 2012 +0200 starling initial commit commite15c7e7318
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Wed Mar 14 18:36:05 2012 +0200 Bumped version to 0.3.1 Releasing bunch of fixes commitffb8ce1ec3
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Wed Mar 14 18:00:38 2012 +0200 Proper state changing on calling Gesture#reset() commit13231a4708
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Wed Mar 14 17:49:49 2012 +0200 Fix for LongPressGesture to work correctly with minPressDuration of zero (and the new IDLE state) commita449965e39
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Wed Mar 14 17:34:24 2012 +0200 Fix to output GestureStateEvent#toString() propely commitbcb3dfb61f
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Wed Mar 14 16:58:39 2012 +0200 Removed some redundant code commit82742a8465
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Wed Mar 14 15:32:51 2012 +0200 Made Gesture weak-referencing target commite14bbd11bb
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Thu Mar 8 13:40:04 2012 +0200 Input adapters fix to catch events on empty stage commit97486ba2fe
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Wed Mar 7 01:12:50 2012 +0200 Bumped version to 0.3 commit764ca1522f
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Wed Mar 7 01:05:04 2012 +0200 Readme updates commit2efa95b85c
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Tue Mar 6 23:28:12 2012 +0200 Experimental requireGestureToFail API implemented commit4e02d4ae63
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Tue Mar 6 23:27:39 2012 +0200 Reformat condition commit7cdec34be4
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Tue Mar 6 23:17:23 2012 +0200 Swipe gesture algorithm rewritten for better recognition and failing commit9edcd04878
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Tue Mar 6 12:00:38 2012 +0200 Tiny cleanup commitb922057845
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Tue Mar 6 01:22:42 2012 +0200 New gesture for free transformation more precise and performant then combination of 3 commit5f28227c75
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Tue Mar 6 01:12:46 2012 +0200 Using custom GestureEvent and TransformGestureEvent from now on commit06df91ce04
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Tue Mar 6 00:52:50 2012 +0200 Custom GestureEvent and TransformGestureEvent because native one have useless phase and stupid constants commit398e41f610
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Tue Mar 6 00:51:25 2012 +0200 TouchesManager should not clone touches because they must persist during touch session. also good for performance. commit49b1139b4f
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Tue Mar 6 00:50:34 2012 +0200 Touch properties update commit242966790a
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Sat Mar 3 17:41:29 2012 +0200 New gesture state commit4d5bef0252
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Fri Mar 2 20:33:16 2012 +0200 Touch properties updates (and corresponding gestures fixes) commitb56107e059
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Thu Mar 1 23:56:45 2012 +0200 Minor cleanup for Gesture class commit51e8435b2d
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Thu Mar 1 18:12:35 2012 +0200 Input adapters initialization and disposing commit895e662bd5
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Wed Feb 29 22:20:09 2012 +0200 Minor performance fix for SwipeGesture commit3dcc78c267
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Wed Feb 29 13:54:59 2012 +0200 Added direction for PanGesture commit09dc1ddfc4
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Tue Feb 21 20:14:13 2012 +0200 Touch#time fix (affects SwipeGesture) commit0532f4bfbb
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Mon Feb 20 17:43:43 2012 +0200 Moved some IGesturesManager methods under gestouch_internal namespace commit3ba8a3df86
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Fri Feb 17 17:53:15 2012 +0200 Put back automatic input adapter initialization commit6d8b733d51
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Fri Feb 3 15:57:11 2012 +0200 Moved input logic out to separate classes commitad767a4937
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Thu Feb 2 16:57:16 2012 +0200 Bugfix for mouse/finger release out of stage commit47f2f848e4
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Fri Dec 30 18:26:16 2011 +0200 Optimized event dispatching commit3e1b5948b2
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Fri Dec 30 16:47:52 2011 +0200 Small optimization for PanGesture commitd950550d16
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Fri Dec 30 03:19:56 2011 +0200 Catch all the Touch/Mouse events in capture phase GesturesManager now captures TOUCH_BEGIN or MOUSE_DOWN from the stage in capture phase to be able to prevent their propagation at the target without affecting the gesture regoznition. commit9d9fcd20ba
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Tue Nov 22 10:40:42 2011 +0200 Fix central point calculation for more precise transformations commita036db1aef
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Tue Nov 22 02:54:11 2011 +0200 Initial commit for the new architecture commit9144538e46
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Tue Nov 1 14:10:05 2011 +0200 Added Gesture#enabled property commitd3ddb825b5
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Tue Oct 25 15:19:05 2011 +0300 Fix condition for dispatching GestureTrackingEvent.GESTURE_TRACKING_END commitfbc4ab7422
Merge:dc489ba
e508862
Author: Pavel fljot <pavel.fljot@gmail.com> Date: Tue Oct 25 13:47:51 2011 +0300 Merge branch 'refs/heads/master' into develop
This commit is contained in:
parent
07d82c206c
commit
b445a5cbe7
44 changed files with 1705 additions and 987 deletions
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<AS3Classpath>
|
||||
<AS3LibraryFolder>libs</AS3LibraryFolder>
|
||||
<AS3Classpath generateProblems="true" sdkBased="false" type="source" useAsSharedCode="false">src</AS3Classpath>
|
||||
<AS3Classpath generateProblems="false" sdkBased="true" type="lib" useAsSharedCode="false">frameworks/libs/air/airglobal.swc</AS3Classpath>
|
||||
<AS3Classpath generateProblems="false" sdkBased="true" type="lib" useAsSharedCode="false">frameworks/libs/mobile/mobilecomponents.swc</AS3Classpath>
|
||||
|
@ -10,4 +11,5 @@
|
|||
<AS3Classpath generateProblems="false" sdkBased="true" type="lib" useAsSharedCode="false">frameworks/libs/air/servicemonitor.swc</AS3Classpath>
|
||||
<AS3Classpath generateProblems="false" sdkBased="true" type="lib" useAsSharedCode="false">frameworks/themes/Mobile/mobile.swc</AS3Classpath>
|
||||
<AS3Classpath generateProblems="false" sdkBased="true" type="lib" useAsSharedCode="false">frameworks/libs/mx/mx.swc</AS3Classpath>
|
||||
<AS3Classpath generateProblems="false" sdkBased="false" type="lib" useAsSharedCode="false">libs/starling.swc</AS3Classpath>
|
||||
</AS3Classpath>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
h1. Gestouch: NUI gestures detection framework for mouse, touch and multitouch AS3 development.
|
||||
h1. Gestouch: multitouch gesture recognition library for Flash (ActionScript) development.
|
||||
|
||||
Gestouch is a ActionScript library/framework that helps you to deal with single- and multitouch gestures for building better NUI (Natural User Interface).
|
||||
Gestouch is a ActionScript (AS3) library that helps you to deal with single- and multitouch gestures for building better NUI (Natural User Interface).
|
||||
|
||||
|
||||
h3. Why? There's already gesture support in Flash/AIR!
|
||||
|
@ -13,9 +13,9 @@ _Upd:_ With "native way" you also won't get anything out of Stage3D and of custo
|
|||
h3. What Gestouch does in short?
|
||||
|
||||
Well basically there's 3 distinctive tasks to solve.
|
||||
# To provide various input. It can be standard MouseEvents, TouchEvents or more complex things like custom input via TUIO protocol for your hand-made installation. So what we get here is Touches (touch points).
|
||||
# To recognize gesture out of touch points. Each type of Gesture has it's own inner algorithms that ...
|
||||
# To manage gestures relations. Because they may "overlap" and once some has been recognized probably we don't want other to do so.
|
||||
# To provide various input. It can be native MouseEvents, TouchEvents or more complex things like custom input via TUIO protocol for your hand-made installation. So what we get here is Touches (touch points).
|
||||
# To recognize gesture analyzing touches. Each type of Gesture has it's own inner algorithms that ...
|
||||
# To manage gestures conflicts. As multiple gestures may be recognized simultaneously, we need to be able to control whether it's allowed or some of them should not be recognized (fail).
|
||||
|
||||
Gestouch solves these 3 tasks.
|
||||
I was hardly inspired by Apple team, how they solved this (quite recently to my big surprise! I thought they had it right from the beginning) in they Cocoa-touch UIKit framework. Gestouch is very similar in many ways. But I wouldn't call it "direct port" because 1) the whole architecture was implemented based just on conference videos and user documentation 2) flash platform is a different platform with own specialization, needs, etc.
|
||||
|
@ -23,12 +23,11 @@ So I want Gestouch to go far beyond that.
|
|||
|
||||
Features:
|
||||
* Pretty neat architecture! Very similar to Apple's UIGestureRecognizers (Cocoa-Touch UIKit)
|
||||
* Works with any display list hierarchy structures: native DisplayList (pure AS3/Flex/your UI framework), Starling or ND2D (Stage3D) and 3D libs...
|
||||
* Doesn't require any additional software (may use runtime's build-in touch support)
|
||||
* Works across all platforms (where Flash Player or AIR run of course) in exactly same way
|
||||
* Doesn't break your DisplayList architecture (could be easily used for Flex development)
|
||||
* Extendable. You can write your own application-specific gestures
|
||||
* Open-source and free
|
||||
* *_+Planning to make it work with Stage3D. Hello Starling!+_*
|
||||
|
||||
|
||||
|
||||
|
@ -69,9 +68,40 @@ private function onFreeTransform(event:TransformGestureEvent):void
|
|||
|
||||
|
||||
|
||||
h3. Advanced usage: Starling, ...
|
||||
|
||||
Recent changes made it possible to work with "Starling":http://www.starling-framework.org display list objects as well as any other display list hierarchical structures, e.g. other Stage3D frameworks that have display objects hierarchy like "ND2D":https://github.com/nulldesign/nd2d or even 3D libraries.
|
||||
In order to use Gestouch with Starling do the following:
|
||||
<pre><code>starling = new Starling(MyStarlingRootClass, stage);
|
||||
/* setup & start your Starling instance here */
|
||||
|
||||
// Gestouch initialization step 1 of 3:
|
||||
// Initialize native (default) input adapter. Needed for non-DisplayList usage.
|
||||
Gestouch.inputAdapter ||= new NativeInputAdapter(stage);
|
||||
|
||||
// Gestouch initialization step 2 of 3:
|
||||
// Register instance of StarlingDisplayListAdapter to be used for objects of type starling.display.DisplayObject.
|
||||
// What it does: helps to build hierarchy (chain of parents) for any Starling display object and
|
||||
// acts as a adapter for gesture target to provide strong-typed access to methods like globalToLocal() and contains().
|
||||
Gestouch.addDisplayListAdapter(starling.display.DisplayObject, new StarlingDisplayListAdapter());
|
||||
|
||||
// Gestouch initialization step 3 of 3:
|
||||
// Initialize and register StarlingTouchHitTester.
|
||||
// What it does: finds appropriate target for the new touches (uses Starling Stage#hitTest() method)
|
||||
// What does "-1" mean: priority for this hit-tester. Since Stage3D layer sits behind native DisplayList
|
||||
// we give it lower priority in the sense of interactivity.
|
||||
Gestouch.addTouchHitTester(new StarlingTouchHitTester(starling), -1);
|
||||
// NB! Use Gestouch#removeTouchHitTester() method if you manage multiple Starling instances during
|
||||
// your application lifetime.
|
||||
</code></pre>
|
||||
|
||||
Now you can register gesture in familiar, exactly same way:
|
||||
<pre><code>var tap:TapGesture = new TapGesture(starlingSprite);</code></pre>
|
||||
|
||||
|
||||
|
||||
h3. Roadmap, TODOs
|
||||
|
||||
* *Stage3D support.* Hello Starling! Must move away from target as InteractiveObject to some abstract adapters.
|
||||
* "Massive gestures" & Clusters. For bigger form-factor multitouch usage, when gestures must be a bit less about separate fingers but rather touch clusters (massive multitouch)
|
||||
* -Simulator (for testing multitouch gestures without special devices)- With new architecture it must be relatively easy to create SimulatorInputAdapter
|
||||
* Chained gestures concept? To transfer touches from one gesture to another. Example: press/hold for circular menu, then drag it around.
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
<arg value="-include-sources=${src.dir}"/>
|
||||
<!-- Exclude Flex Framework classes (some mx events comes from binding). -->
|
||||
<arg value="-external-library-path+=${flexSDK.dir}/frameworks/libs"/>
|
||||
<!-- Exclude any external classes (such as Starling framework classes) -->
|
||||
<arg value="-external-library-path+=${libs.dir}"/>
|
||||
<!-- Keep the metatags (Apparat?). -->
|
||||
<arg value="-keep-as3-metadata+=Abstract"/>
|
||||
<!-- Boolean mosh pit! -->
|
||||
|
@ -44,6 +46,7 @@
|
|||
</delete>
|
||||
<java jar="${FLEX_HOME}/lib/asdoc.jar" dir="${FLEX_HOME}/frameworks" fork="true" failonerror="true">
|
||||
<arg line="-source-path ${src.dir}"/>
|
||||
<arg line="-external-library-path+=${libs.dir}"/>
|
||||
<arg line="-doc-sources ${src.dir}"/>
|
||||
<arg line="-output ${asdoc.dir}"/>
|
||||
<arg value="-keep-xml=true"/>
|
||||
|
|
BIN
libs/starling.swc
Normal file
BIN
libs/starling.swc
Normal file
Binary file not shown.
125
src/org/gestouch/core/Gestouch.as
Normal file
125
src/org/gestouch/core/Gestouch.as
Normal file
|
@ -0,0 +1,125 @@
|
|||
package org.gestouch.core
|
||||
{
|
||||
import org.gestouch.extensions.native.NativeDisplayListAdapter;
|
||||
import org.gestouch.extensions.native.NativeTouchHitTester;
|
||||
|
||||
import flash.display.DisplayObject;
|
||||
import flash.utils.Dictionary;
|
||||
import flash.utils.getQualifiedClassName;
|
||||
|
||||
|
||||
/**
|
||||
* @author Pavel fljot
|
||||
*/
|
||||
final public class Gestouch
|
||||
{
|
||||
private static const _displayListAdaptersMap:Dictionary = new Dictionary();
|
||||
|
||||
{
|
||||
initClass();
|
||||
}
|
||||
|
||||
|
||||
/** @private */
|
||||
private static var _inputAdapter:IInputAdapter;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public static function get inputAdapter():IInputAdapter
|
||||
{
|
||||
return _inputAdapter;
|
||||
}
|
||||
public static function set inputAdapter(value:IInputAdapter):void
|
||||
{
|
||||
if (_inputAdapter == value)
|
||||
return;
|
||||
|
||||
_inputAdapter = value;
|
||||
if (inputAdapter)
|
||||
{
|
||||
inputAdapter.touchesManager = touchesManager;
|
||||
inputAdapter.init();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static var _touchesManager:TouchesManager;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public static function get touchesManager():TouchesManager
|
||||
{
|
||||
return _touchesManager ||= new TouchesManager(gesturesManager);
|
||||
}
|
||||
|
||||
|
||||
private static var _gesturesManager:GesturesManager;
|
||||
public static function get gesturesManager():GesturesManager
|
||||
{
|
||||
return _gesturesManager ||= new GesturesManager();
|
||||
}
|
||||
|
||||
|
||||
public static function addDisplayListAdapter(targetClass:Class, adapter:IDisplayListAdapter):void
|
||||
{
|
||||
if (!targetClass || !adapter)
|
||||
{
|
||||
throw new Error("Argument error: both arguments required.");
|
||||
}
|
||||
|
||||
_displayListAdaptersMap[targetClass] = adapter;
|
||||
}
|
||||
|
||||
|
||||
public static function addTouchHitTester(hitTester:ITouchHitTester, priority:int = 0):void
|
||||
{
|
||||
touchesManager.gestouch_internal::addTouchHitTester(hitTester, priority);
|
||||
}
|
||||
|
||||
|
||||
public static function removeTouchHitTester(hitTester:ITouchHitTester):void
|
||||
{
|
||||
touchesManager.gestouch_internal::removeInputAdapter(hitTester);
|
||||
}
|
||||
|
||||
|
||||
// public static function getTouches(target:Object = null):Array
|
||||
// {
|
||||
// return touchesManager.getTouches(target);
|
||||
// }
|
||||
|
||||
gestouch_internal static function createGestureTargetAdapter(target:Object):IDisplayListAdapter
|
||||
{
|
||||
const adapter:IDisplayListAdapter = Gestouch.gestouch_internal::getDisplayListAdapter(target);
|
||||
if (adapter)
|
||||
{
|
||||
return new (adapter.reflect())(target);
|
||||
}
|
||||
|
||||
throw new Error("Cannot create adapter for target " + target + " of type " + getQualifiedClassName(target) + ".");
|
||||
}
|
||||
|
||||
|
||||
gestouch_internal static function getDisplayListAdapter(object:Object):IDisplayListAdapter
|
||||
{
|
||||
for (var key:Object in _displayListAdaptersMap)
|
||||
{
|
||||
var targetClass:Class = key as Class;
|
||||
if (object is targetClass)
|
||||
{
|
||||
return _displayListAdaptersMap[key] as IDisplayListAdapter;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private static function initClass():void
|
||||
{
|
||||
addTouchHitTester(new NativeTouchHitTester());
|
||||
addDisplayListAdapter(DisplayObject, new NativeDisplayListAdapter());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,17 +1,100 @@
|
|||
package org.gestouch.core
|
||||
{
|
||||
import flash.errors.IllegalOperationError;
|
||||
|
||||
|
||||
/**
|
||||
* @author Pavel fljot
|
||||
*/
|
||||
public class GestureState
|
||||
{
|
||||
public static const IDLE:uint = 1 << 0;//1
|
||||
public static const POSSIBLE:uint = 1 << 1;//2
|
||||
public static const RECOGNIZED:uint = 1 << 2;//4
|
||||
public static const BEGAN:uint = 1 << 3;//8
|
||||
public static const CHANGED:uint = 1 << 4;//16
|
||||
public static const ENDED:uint = 1 << 5;//32
|
||||
public static const CANCELLED:uint = 1 << 6;//64
|
||||
public static const FAILED:uint = 1 << 7;//128
|
||||
public static const IDLE:GestureState = new GestureState(1 << 0, "IDLE");
|
||||
public static const POSSIBLE:GestureState = new GestureState(1 << 1, "POSSIBLE");
|
||||
public static const RECOGNIZED:GestureState = new GestureState(1 << 2, "RECOGNIZED");
|
||||
public static const BEGAN:GestureState = new GestureState(1 << 3, "BEGAN");
|
||||
public static const CHANGED:GestureState = new GestureState(1 << 4, "CHANGED");
|
||||
public static const ENDED:GestureState = new GestureState(1 << 5, "ENDED");
|
||||
public static const CANCELLED:GestureState = new GestureState(1 << 6, "CANCELLED");
|
||||
public static const FAILED:GestureState = new GestureState(1 << 7, "FAILED");
|
||||
|
||||
private static const endStatesBitMask:uint =
|
||||
GestureState.CANCELLED.toUint() |
|
||||
GestureState.RECOGNIZED.toUint() |
|
||||
GestureState.ENDED.toUint() |
|
||||
GestureState.FAILED.toUint();
|
||||
|
||||
private static var allStatesInitialized:Boolean;
|
||||
|
||||
|
||||
private var value:uint;
|
||||
private var name:String;
|
||||
private var validTransitionsBitMask:uint;
|
||||
|
||||
{
|
||||
_initClass();
|
||||
}
|
||||
|
||||
|
||||
public function GestureState(value:uint, name:String)
|
||||
{
|
||||
if (allStatesInitialized)
|
||||
{
|
||||
throw new IllegalOperationError("You cannot create gesture states." +
|
||||
"Use predefined constats like GestureState.RECOGNIZED");
|
||||
}
|
||||
|
||||
this.value = value;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
private static function _initClass():void
|
||||
{
|
||||
IDLE.setValidNextStates(POSSIBLE);
|
||||
POSSIBLE.setValidNextStates(RECOGNIZED, BEGAN, FAILED);
|
||||
RECOGNIZED.setValidNextStates(IDLE);
|
||||
BEGAN.setValidNextStates(CHANGED, ENDED, CANCELLED);
|
||||
CHANGED.setValidNextStates(CHANGED, ENDED, CANCELLED);
|
||||
ENDED.setValidNextStates(IDLE);
|
||||
FAILED.setValidNextStates(IDLE);
|
||||
CANCELLED.setValidNextStates(IDLE);
|
||||
|
||||
allStatesInitialized = true;
|
||||
}
|
||||
|
||||
|
||||
public function toString():String
|
||||
{
|
||||
return "GestureState." + name;
|
||||
}
|
||||
|
||||
|
||||
public function toUint():uint
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
private function setValidNextStates(...states):void
|
||||
{
|
||||
var mask:uint;
|
||||
for each (var state:GestureState in states)
|
||||
{
|
||||
mask = mask | state.value;
|
||||
}
|
||||
validTransitionsBitMask = mask;
|
||||
}
|
||||
|
||||
|
||||
gestouch_internal function canTransitionTo(state:GestureState):Boolean
|
||||
{
|
||||
return (validTransitionsBitMask & state.value) > 0;
|
||||
}
|
||||
|
||||
|
||||
gestouch_internal function get isEndState():Boolean
|
||||
{
|
||||
return (endStatesBitMask & value) > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,117 +1,37 @@
|
|||
package org.gestouch.core
|
||||
{
|
||||
import flash.errors.IllegalOperationError;
|
||||
import org.gestouch.gestures.Gesture;
|
||||
import org.gestouch.input.MouseInputAdapter;
|
||||
import org.gestouch.input.TouchInputAdapter;
|
||||
import org.gestouch.input.NativeInputAdapter;
|
||||
|
||||
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.ui.Multitouch;
|
||||
import flash.events.IEventDispatcher;
|
||||
import flash.utils.Dictionary;
|
||||
import flash.utils.getQualifiedClassName;
|
||||
|
||||
/**
|
||||
* @author Pavel fljot
|
||||
*/
|
||||
public class GesturesManager implements IGesturesManager
|
||||
public class GesturesManager
|
||||
{
|
||||
public static var initDefaultInputAdapter:Boolean = true;
|
||||
private static var _instance:IGesturesManager;
|
||||
private static var _allowInstantiation:Boolean;
|
||||
|
||||
protected const _touchesManager:ITouchesManager = TouchesManager.getInstance();
|
||||
protected const _frameTickerShape:Shape = new Shape();
|
||||
protected var _inputAdapters:Vector.<IInputAdapter> = new Vector.<IInputAdapter>();
|
||||
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.<Gesture> = new Vector.<Gesture>();
|
||||
protected var _dirtyGesturesLength:uint = 0;
|
||||
protected var _dirtyGesturesCount:uint = 0;
|
||||
protected var _dirtyGesturesMap:Dictionary = new Dictionary(true);
|
||||
protected var _stage:Stage;
|
||||
|
||||
use namespace gestouch_internal;
|
||||
|
||||
|
||||
public function GesturesManager()
|
||||
{
|
||||
if (Object(this).constructor == GesturesManager && !_allowInstantiation)
|
||||
{
|
||||
throw new Error("Do not instantiate GesturesManager directly.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function get inputAdapters():Vector.<IInputAdapter>
|
||||
{
|
||||
return _inputAdapters.concat();
|
||||
}
|
||||
|
||||
|
||||
public static function setImplementation(value:IGesturesManager):void
|
||||
{
|
||||
if (!value)
|
||||
{
|
||||
throw new ArgumentError("value cannot be null.");
|
||||
}
|
||||
if (_instance)
|
||||
{
|
||||
throw new Error("Instance of GesturesManager is already created. If you want to have own implementation of single GesturesManager instace, you should set it earlier.");
|
||||
}
|
||||
_instance = value;
|
||||
}
|
||||
|
||||
|
||||
public static function getInstance():IGesturesManager
|
||||
{
|
||||
if (!_instance)
|
||||
{
|
||||
_allowInstantiation = true;
|
||||
_instance = new GesturesManager();
|
||||
_allowInstantiation = false;
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public function addInputAdapter(inputAdapter:IInputAdapter):void
|
||||
{
|
||||
if (!inputAdapter)
|
||||
{
|
||||
throw new Error("Input adapter must be non null.");
|
||||
}
|
||||
|
||||
if (_inputAdapters.indexOf(inputAdapter) > -1)
|
||||
return;//TODO: throw Error or ignore?
|
||||
|
||||
_inputAdapters.push(inputAdapter);
|
||||
inputAdapter.touchesManager = _touchesManager;
|
||||
inputAdapter.gesturesManager = this;
|
||||
inputAdapter.init();
|
||||
}
|
||||
|
||||
|
||||
public function removeInputAdapter(inputAdapter:IInputAdapter, dispose:Boolean = true):void
|
||||
{
|
||||
if (!inputAdapter)
|
||||
{
|
||||
throw new Error("Input adapter must be non null.");
|
||||
}
|
||||
var index:int = _inputAdapters.indexOf(inputAdapter);
|
||||
if (index == -1)
|
||||
{
|
||||
throw new Error("This input manager is not registered.");
|
||||
}
|
||||
|
||||
_inputAdapters.splice(index, 1);
|
||||
if (dispose)
|
||||
{
|
||||
inputAdapter.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -123,29 +43,21 @@ package org.gestouch.core
|
|||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
protected function installStage(stage:Stage):void
|
||||
protected function onStageAvailable(stage:Stage):void
|
||||
{
|
||||
_stage = stage;
|
||||
|
||||
if (Multitouch.supportsTouchEvents)
|
||||
{
|
||||
addInputAdapter(new TouchInputAdapter(stage));
|
||||
}
|
||||
else
|
||||
{
|
||||
addInputAdapter(new MouseInputAdapter(stage));
|
||||
}
|
||||
Gestouch.inputAdapter ||= new NativeInputAdapter(stage);
|
||||
}
|
||||
|
||||
|
||||
protected function resetDirtyGestures():void
|
||||
{
|
||||
for each (var gesture:Gesture in _dirtyGestures)
|
||||
for (var gesture:Object in _dirtyGesturesMap)
|
||||
{
|
||||
gesture.reset();
|
||||
(gesture as Gesture).reset();
|
||||
}
|
||||
_dirtyGestures.length = 0;
|
||||
_dirtyGesturesLength = 0;
|
||||
_dirtyGesturesCount = 0;
|
||||
_dirtyGesturesMap = new Dictionary(true);
|
||||
_frameTickerShape.removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
|
||||
}
|
||||
|
@ -157,30 +69,43 @@ package org.gestouch.core
|
|||
{
|
||||
throw new ArgumentError("Argument 'gesture' must be not null.");
|
||||
}
|
||||
if (_gesturesMap[gesture])
|
||||
|
||||
const target:Object = gesture.target;
|
||||
if (!target)
|
||||
{
|
||||
throw new Error("This gesture is already registered.. something wrong.");
|
||||
throw new IllegalOperationError("Gesture must have target.");
|
||||
}
|
||||
|
||||
var target:Object = gesture.target;
|
||||
var targetGestures:Vector.<Gesture> = _gesturesForTargetMap[target] as Vector.<Gesture>;
|
||||
if (!targetGestures)
|
||||
if (targetGestures)
|
||||
{
|
||||
targetGestures = _gesturesForTargetMap[gesture.target] = new Vector.<Gesture>();
|
||||
if (targetGestures.indexOf(gesture) == -1)
|
||||
{
|
||||
targetGestures.push(gesture);
|
||||
}
|
||||
}
|
||||
targetGestures.push(gesture);
|
||||
else
|
||||
{
|
||||
targetGestures = _gesturesForTargetMap[target] = new Vector.<Gesture>();
|
||||
targetGestures[0] = gesture;
|
||||
}
|
||||
|
||||
|
||||
_gesturesMap[gesture] = true;
|
||||
|
||||
if (GesturesManager.initDefaultInputAdapter)
|
||||
if (!_stage)
|
||||
{
|
||||
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 (targetAsDO.stage)
|
||||
{
|
||||
onStageAvailable(targetAsDO.stage);
|
||||
}
|
||||
else
|
||||
{
|
||||
targetAsDO.addEventListener(Event.ADDED_TO_STAGE, gestureTarget_addedToStageHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -194,21 +119,28 @@ package org.gestouch.core
|
|||
}
|
||||
|
||||
|
||||
var target:InteractiveObject = gesture.target;
|
||||
var targetGestures:Vector.<Gesture> = _gesturesForTargetMap[target] as Vector.<Gesture>;
|
||||
if (targetGestures.length > 1)
|
||||
var target:Object = gesture.target;
|
||||
// check for target because it could be already GC-ed (since target reference is weak)
|
||||
if (target)
|
||||
{
|
||||
targetGestures.splice(targetGestures.indexOf(gesture), 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete _gesturesForTargetMap[target];
|
||||
target.removeEventListener(Event.ADDED_TO_STAGE, gestureTarget_addedToStageHandler);
|
||||
var targetGestures:Vector.<Gesture> = _gesturesForTargetMap[target] as Vector.<Gesture>;
|
||||
if (targetGestures.length > 1)
|
||||
{
|
||||
targetGestures.splice(targetGestures.indexOf(gesture), 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete _gesturesForTargetMap[target];
|
||||
if (target is IEventDispatcher)
|
||||
{
|
||||
(target as IEventDispatcher).removeEventListener(Event.ADDED_TO_STAGE, gestureTarget_addedToStageHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete _gesturesMap[gesture];
|
||||
|
||||
//TODO: decide about gesture state and _dirtyGestures
|
||||
gesture.reset();
|
||||
}
|
||||
|
||||
|
||||
|
@ -216,8 +148,8 @@ package org.gestouch.core
|
|||
{
|
||||
if (!_dirtyGesturesMap[gesture])
|
||||
{
|
||||
_dirtyGestures.push(gesture);
|
||||
_dirtyGesturesLength++;
|
||||
_dirtyGesturesMap[gesture] = true;
|
||||
_dirtyGesturesCount++;
|
||||
_frameTickerShape.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
|
||||
}
|
||||
}
|
||||
|
@ -225,11 +157,12 @@ package org.gestouch.core
|
|||
|
||||
gestouch_internal function onGestureRecognized(gesture:Gesture):void
|
||||
{
|
||||
const target:Object = gesture.target;
|
||||
|
||||
for (var key:Object in _gesturesMap)
|
||||
{
|
||||
var otherGesture:Gesture = key as Gesture;
|
||||
var target:DisplayObject = gesture.target;
|
||||
var otherTarget:DisplayObject = otherGesture.target;
|
||||
var otherTarget:Object = otherGesture.target;
|
||||
|
||||
// conditions for otherGesture "own properties"
|
||||
if (otherGesture != gesture &&
|
||||
|
@ -237,10 +170,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;
|
||||
|
@ -250,9 +183,9 @@ package org.gestouch.core
|
|||
(!gestureDelegate || !gestureDelegate.gesturesShouldRecognizeSimultaneously(gesture, otherGesture)) &&
|
||||
(!otherGestureDelegate || !otherGestureDelegate.gesturesShouldRecognizeSimultaneously(otherGesture, gesture)))
|
||||
{
|
||||
otherGesture.gestouch_internal::setState_internal(GestureState.FAILED);
|
||||
otherGesture.setState_internal(GestureState.FAILED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -260,32 +193,46 @@ package org.gestouch.core
|
|||
|
||||
gestouch_internal function onTouchBegin(touch:Touch):void
|
||||
{
|
||||
if (_dirtyGesturesLength > 0)
|
||||
{
|
||||
resetDirtyGestures();
|
||||
}
|
||||
|
||||
var gesture:Gesture;
|
||||
var i:uint;
|
||||
|
||||
// This vector will contain active gestures for specific touch (ID) during all touch session.
|
||||
var gesturesForTouch:Vector.<Gesture> = _gesturesForTouchMap[touch.id] as Vector.<Gesture>;
|
||||
// This vector will contain active gestures for specific touch during all touch session.
|
||||
var gesturesForTouch:Vector.<Gesture> = _gesturesForTouchMap[touch] as Vector.<Gesture>;
|
||||
if (!gesturesForTouch)
|
||||
{
|
||||
gesturesForTouch = new Vector.<Gesture>();
|
||||
_gesturesForTouchMap[touch.id] = gesturesForTouch;
|
||||
gesturesForTouch = new Vector.<Gesture>();
|
||||
_gesturesForTouchMap[touch] = gesturesForTouch;
|
||||
}
|
||||
else
|
||||
{
|
||||
// touch object may be pooled in the future
|
||||
gesturesForTouch.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
var target:Object = touch.target;
|
||||
const displayListAdapter:IDisplayListAdapter = Gestouch.gestouch_internal::getDisplayListAdapter(target);
|
||||
if (!displayListAdapter)
|
||||
{
|
||||
throw new Error("Display list adapter not found for target of type '" + getQualifiedClassName(target) + "'.");
|
||||
}
|
||||
const hierarchy:Vector.<Object> = displayListAdapter.getHierarchy(target);
|
||||
const hierarchyLength:uint = hierarchy.length;
|
||||
if (hierarchyLength == 0)
|
||||
{
|
||||
throw new Error("No hierarchy build for target '" + target +"'. Something is wrong with that IDisplayListAdapter.");
|
||||
}
|
||||
if (_stage && !(hierarchy[hierarchyLength - 1] is Stage))
|
||||
{
|
||||
// Looks like some non-native (non DisplayList) hierarchy
|
||||
// but we must always handle gestures with Stage target
|
||||
// since Stage is anyway the top-most parent
|
||||
hierarchy[hierarchyLength] = _stage;
|
||||
}
|
||||
|
||||
// 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.<Gesture>;
|
||||
while (target)
|
||||
for each (target in hierarchy)
|
||||
{
|
||||
gesturesForTarget = _gesturesForTargetMap[target] as Vector.<Gesture>;
|
||||
if (gesturesForTarget)
|
||||
|
@ -293,7 +240,7 @@ package org.gestouch.core
|
|||
i = gesturesForTarget.length;
|
||||
while (i-- > 0)
|
||||
{
|
||||
gesture = gesturesForTarget[i] as Gesture;
|
||||
gesture = gesturesForTarget[i];
|
||||
if (gesture.enabled &&
|
||||
(!gesture.delegate || gesture.delegate.gestureShouldReceiveTouch(gesture, touch)))
|
||||
{
|
||||
|
@ -302,8 +249,6 @@ package org.gestouch.core
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
target = target.parent;
|
||||
}
|
||||
|
||||
// Then we populate them with this touch and event.
|
||||
|
@ -311,11 +256,11 @@ package org.gestouch.core
|
|||
i = gesturesForTouch.length;
|
||||
while (i-- > 0)
|
||||
{
|
||||
gesture = gesturesForTouch[i] as Gesture;
|
||||
gesture = gesturesForTouch[i];
|
||||
// Check for state because previous (i+1) gesture may already abort current (i) one
|
||||
if (gesture.state != GestureState.FAILED)
|
||||
if (!_dirtyGesturesMap[gesture])
|
||||
{
|
||||
gesture.gestouch_internal::touchBeginHandler(touch);
|
||||
gesture.touchBeginHandler(touch);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -327,21 +272,16 @@ package org.gestouch.core
|
|||
|
||||
gestouch_internal function onTouchMove(touch:Touch):void
|
||||
{
|
||||
if (_dirtyGesturesLength > 0)
|
||||
{
|
||||
resetDirtyGestures();
|
||||
}
|
||||
|
||||
var gesturesForTouch:Vector.<Gesture> = _gesturesForTouchMap[touch.id] as Vector.<Gesture>;
|
||||
var gesturesForTouch:Vector.<Gesture> = _gesturesForTouchMap[touch] as Vector.<Gesture>;
|
||||
var gesture:Gesture;
|
||||
var i:int = gesturesForTouch.length;
|
||||
var i:uint = gesturesForTouch.length;
|
||||
while (i-- > 0)
|
||||
{
|
||||
gesture = gesturesForTouch[i] as Gesture;
|
||||
gesture = gesturesForTouch[i];
|
||||
|
||||
if (gesture.state != GestureState.FAILED && gesture.isTrackingTouch(touch.id))
|
||||
if (!_dirtyGesturesMap[gesture] && gesture.isTrackingTouch(touch.id))
|
||||
{
|
||||
gesture.gestouch_internal::touchMoveHandler(touch);
|
||||
gesture.touchMoveHandler(touch);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -354,29 +294,39 @@ package org.gestouch.core
|
|||
|
||||
gestouch_internal function onTouchEnd(touch:Touch):void
|
||||
{
|
||||
if (_dirtyGesturesLength > 0)
|
||||
{
|
||||
resetDirtyGestures();
|
||||
}
|
||||
|
||||
var gesturesForTouch:Vector.<Gesture> = _gesturesForTouchMap[touch.id] as Vector.<Gesture>;
|
||||
var gesturesForTouch:Vector.<Gesture> = _gesturesForTouchMap[touch] as Vector.<Gesture>;
|
||||
var gesture:Gesture;
|
||||
var i:int = gesturesForTouch.length;
|
||||
var i:uint = gesturesForTouch.length;
|
||||
while (i-- > 0)
|
||||
{
|
||||
gesture = gesturesForTouch[i] as Gesture;
|
||||
|
||||
if (gesture.state != GestureState.FAILED && gesture.isTrackingTouch(touch.id))
|
||||
{
|
||||
gesture.gestouch_internal::touchEndHandler(touch);
|
||||
gesture = gesturesForTouch[i];
|
||||
|
||||
if (!_dirtyGesturesMap[gesture] && gesture.isTrackingTouch(touch.id))
|
||||
{
|
||||
gesture.touchEndHandler(touch);
|
||||
}
|
||||
}
|
||||
|
||||
gesturesForTouch.length = 0;// release for GC
|
||||
}
|
||||
|
||||
|
||||
gestouch_internal function onTouchCancel(touch:Touch):void
|
||||
{
|
||||
//TODO
|
||||
var gesturesForTouch:Vector.<Gesture> = _gesturesForTouchMap[touch] as Vector.<Gesture>;
|
||||
var gesture:Gesture;
|
||||
var i:uint = gesturesForTouch.length;
|
||||
while (i-- > 0)
|
||||
{
|
||||
gesture = gesturesForTouch[i];
|
||||
|
||||
if (!_dirtyGesturesMap[gesture] && gesture.isTrackingTouch(touch.id))
|
||||
{
|
||||
gesture.touchCancelHandler(touch);
|
||||
}
|
||||
}
|
||||
|
||||
gesturesForTouch.length = 0;// release for GC
|
||||
}
|
||||
|
||||
|
||||
|
@ -386,15 +336,15 @@ package org.gestouch.core
|
|||
//
|
||||
// Event handlers
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
protected function gestureTarget_addedToStageHandler(event:Event):void
|
||||
{
|
||||
var target:DisplayObject = event.target as DisplayObject;
|
||||
target.removeEventListener(Event.ADDED_TO_STAGE, gestureTarget_addedToStageHandler);
|
||||
if (!_stage && GesturesManager.initDefaultInputAdapter)
|
||||
if (!_stage)
|
||||
{
|
||||
installStage(target.stage);
|
||||
onStageAvailable(target.stage);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
12
src/org/gestouch/core/IDisplayListAdapter.as
Normal file
12
src/org/gestouch/core/IDisplayListAdapter.as
Normal file
|
@ -0,0 +1,12 @@
|
|||
package org.gestouch.core
|
||||
{
|
||||
/**
|
||||
* @author Pavel fljot
|
||||
*/
|
||||
public interface IDisplayListAdapter extends IGestureTargetAdapter
|
||||
{
|
||||
function getHierarchy(target:Object):Vector.<Object>;
|
||||
|
||||
function reflect():Class;
|
||||
}
|
||||
}
|
15
src/org/gestouch/core/IGestureTargetAdapter.as
Normal file
15
src/org/gestouch/core/IGestureTargetAdapter.as
Normal file
|
@ -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(object:Object):Boolean;
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package org.gestouch.core
|
||||
{
|
||||
/**
|
||||
* The class that implements this interface must also
|
||||
* implement next methods under gestouch_internal namespace:
|
||||
*
|
||||
* function addGesture(gesture:Gesture):void;
|
||||
* function removeGesture(gesture:Gesture):void;
|
||||
* function scheduleGestureStateReset(gesture:Gesture):void;
|
||||
* function onGestureRecognized(gesture:Gesture):void;
|
||||
*/
|
||||
public interface IGesturesManager
|
||||
{
|
||||
function addInputAdapter(inputAdapter:IInputAdapter):void;
|
||||
function removeInputAdapter(inputAdapter:IInputAdapter, dispose:Boolean = true):void;
|
||||
}
|
||||
}
|
|
@ -5,10 +5,14 @@ package org.gestouch.core
|
|||
*/
|
||||
public interface IInputAdapter
|
||||
{
|
||||
function set touchesManager(value:ITouchesManager):void;
|
||||
function set gesturesManager(value:IGesturesManager):void;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function set touchesManager(value:TouchesManager):void;
|
||||
|
||||
/**
|
||||
* Called when input adapter is set.
|
||||
*/
|
||||
function init():void;
|
||||
function dispose():void;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
14
src/org/gestouch/core/ITouchHitTester.as
Normal file
14
src/org/gestouch/core/ITouchHitTester.as
Normal file
|
@ -0,0 +1,14 @@
|
|||
package org.gestouch.core
|
||||
{
|
||||
import flash.display.InteractiveObject;
|
||||
import flash.geom.Point;
|
||||
|
||||
|
||||
/**
|
||||
* @author Pavel fljot
|
||||
*/
|
||||
public interface ITouchHitTester
|
||||
{
|
||||
function hitTest(point:Point, nativeTarget:InteractiveObject):Object;
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package org.gestouch.core
|
||||
{
|
||||
/**
|
||||
* @author Pavel fljot
|
||||
*/
|
||||
public interface ITouchesManager
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
package org.gestouch.core
|
||||
{
|
||||
import flash.display.InteractiveObject;
|
||||
import flash.geom.Point;
|
||||
|
||||
|
||||
|
@ -19,13 +18,15 @@ 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;
|
||||
public var pressure:Number;
|
||||
|
||||
// public var lastMove:Point;
|
||||
|
||||
use namespace gestouch_internal;
|
||||
|
||||
|
||||
public function Touch(id:uint = 0)
|
||||
|
@ -39,13 +40,16 @@ package org.gestouch.core
|
|||
{
|
||||
return _location.clone();
|
||||
}
|
||||
gestouch_internal function setLocation(value:Point):void
|
||||
gestouch_internal function setLocation(x:Number, y:Number, time:uint):void
|
||||
{
|
||||
_location = value;
|
||||
_location = new Point(x, y);
|
||||
_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 +57,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));
|
||||
setLocation(x, y, time);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,22 +1,28 @@
|
|||
package org.gestouch.core
|
||||
{
|
||||
import flash.display.InteractiveObject;
|
||||
import flash.display.Stage;
|
||||
import flash.geom.Point;
|
||||
import flash.utils.Dictionary;
|
||||
import flash.utils.getTimer;
|
||||
|
||||
|
||||
/**
|
||||
* @author Pavel fljot
|
||||
*/
|
||||
public class TouchesManager implements ITouchesManager
|
||||
public class TouchesManager
|
||||
{
|
||||
private static var _instance:ITouchesManager;
|
||||
private static var _allowInstantiation:Boolean;
|
||||
|
||||
protected var _gesturesManager:GesturesManager;
|
||||
protected var _touchesMap:Object = {};
|
||||
protected var _hitTesters:Vector.<ITouchHitTester> = new Vector.<ITouchHitTester>();
|
||||
protected var _hitTesterPrioritiesMap:Dictionary = new Dictionary(true);
|
||||
|
||||
use namespace gestouch_internal;
|
||||
|
||||
|
||||
public function TouchesManager()
|
||||
public function TouchesManager(gesturesManager:GesturesManager)
|
||||
{
|
||||
if (Object(this).constructor == TouchesManager && !_allowInstantiation)
|
||||
{
|
||||
throw new Error("Do not instantiate TouchesManager directly.");
|
||||
}
|
||||
_gesturesManager = gesturesManager;
|
||||
}
|
||||
|
||||
|
||||
|
@ -27,77 +33,192 @@ package org.gestouch.core
|
|||
}
|
||||
|
||||
|
||||
public static function setImplementation(value:ITouchesManager):void
|
||||
public function getTouches(target:Object = null):Array
|
||||
{
|
||||
if (!value)
|
||||
const touches:Array = [];
|
||||
if (!target || target is Stage)
|
||||
{
|
||||
throw new ArgumentError("value cannot be null.");
|
||||
// return all touches
|
||||
var i:uint = 0;
|
||||
for each (var touch:Touch in _touchesMap)
|
||||
{
|
||||
touches[i++] = touch;
|
||||
}
|
||||
}
|
||||
if (_instance)
|
||||
else
|
||||
{
|
||||
throw new Error("Instance of TouchesManager is already created. If you want to have own implementation of single TouchesManager instace, you should set it earlier.");
|
||||
//TODO
|
||||
}
|
||||
_instance = value;
|
||||
}
|
||||
|
||||
|
||||
public static function getInstance():ITouchesManager
|
||||
{
|
||||
if (!_instance)
|
||||
{
|
||||
_allowInstantiation = true;
|
||||
_instance = new TouchesManager();
|
||||
_allowInstantiation = false;
|
||||
}
|
||||
|
||||
return _instance;
|
||||
|
||||
return touches;
|
||||
}
|
||||
|
||||
|
||||
public function createTouch():Touch
|
||||
gestouch_internal function addTouchHitTester(touchHitTester:ITouchHitTester, priority:int = 0):void
|
||||
{
|
||||
if (!touchHitTester)
|
||||
{
|
||||
throw new ArgumentError("Argument must be non null.");
|
||||
}
|
||||
|
||||
if (_hitTesters.indexOf(touchHitTester) == -1)
|
||||
{
|
||||
_hitTesters.push(touchHitTester);
|
||||
}
|
||||
|
||||
_hitTesterPrioritiesMap[touchHitTester] = priority;
|
||||
// Sort hit testers using their priorities
|
||||
_hitTesters.sort(hitTestersSorter);
|
||||
}
|
||||
|
||||
|
||||
gestouch_internal function removeInputAdapter(touchHitTester:ITouchHitTester):void
|
||||
{
|
||||
if (!touchHitTester)
|
||||
{
|
||||
throw new ArgumentError("Argument must be non null.");
|
||||
}
|
||||
|
||||
var index:int = _hitTesters.indexOf(touchHitTester);
|
||||
if (index == -1)
|
||||
{
|
||||
throw new Error("This touchHitTester is not registered.");
|
||||
}
|
||||
|
||||
_hitTesters.splice(index, 1);
|
||||
delete _hitTesterPrioritiesMap[touchHitTester];
|
||||
}
|
||||
|
||||
|
||||
gestouch_internal function onTouchBegin(touchID:uint, x:Number, y:Number, nativeTarget:InteractiveObject = null):Boolean
|
||||
{
|
||||
if (touchID in _touchesMap)
|
||||
return false;// touch with specified ID is already registered and being tracked
|
||||
|
||||
const location:Point = new Point(x, y);
|
||||
|
||||
for each (var registeredTouch:Touch in _touchesMap)
|
||||
{
|
||||
// Check if touch at the same location exists.
|
||||
// In case we listen to both TouchEvents and MouseEvents, one of them will come first
|
||||
// (right now looks like MouseEvent dispatched first, but who know what Adobe will
|
||||
// do tomorrow). This check helps to filter out the one comes after.
|
||||
|
||||
// NB! According to the tests with some IR multitouch frame and Windows computer
|
||||
// TouchEvent comes first, but the following MouseEvent has slightly offset location
|
||||
// (1px both axis). That is why Point#distance() used instead of Point#equals()
|
||||
|
||||
if (Point.distance(registeredTouch.location, location) < 2)
|
||||
return false;
|
||||
}
|
||||
|
||||
const touch:Touch = createTouch();
|
||||
touch.id = touchID;
|
||||
|
||||
var target:Object;
|
||||
var altTarget:Object;
|
||||
for each (var hitTester:ITouchHitTester in _hitTesters)
|
||||
{
|
||||
target = hitTester.hitTest(location, nativeTarget);
|
||||
if (target)
|
||||
{
|
||||
if ((target is Stage))
|
||||
{
|
||||
// NB! Target is flash.display::Stage is a special case. If it is true, we want
|
||||
// to give a try to a lower-priority (Stage3D) hit-testers.
|
||||
altTarget = target;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We found a target.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!target && !altTarget)
|
||||
{
|
||||
throw new Error("Not touch target found (hit test)." +
|
||||
"Something is wrong, at least flash.display::Stage should be found." +
|
||||
"See Gestouch#addTouchHitTester() and Gestouch#inputAdapter.");
|
||||
}
|
||||
|
||||
touch.target = target || altTarget;
|
||||
touch.setLocation(x, y, getTimer());
|
||||
|
||||
_touchesMap[touchID] = touch;
|
||||
_activeTouchesCount++;
|
||||
|
||||
_gesturesManager.onTouchBegin(touch);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
gestouch_internal function onTouchMove(touchID:uint, x:Number, y:Number):void
|
||||
{
|
||||
const touch:Touch = _touchesMap[touchID] as Touch;
|
||||
if (!touch)
|
||||
return;// touch with specified ID isn't registered
|
||||
|
||||
touch.updateLocation(x, y, getTimer());
|
||||
|
||||
_gesturesManager.onTouchMove(touch);
|
||||
}
|
||||
|
||||
|
||||
gestouch_internal function onTouchEnd(touchID:uint, x:Number, y:Number):void
|
||||
{
|
||||
const touch:Touch = _touchesMap[touchID] as Touch;
|
||||
if (!touch)
|
||||
return;// touch with specified ID isn't registered
|
||||
|
||||
touch.updateLocation(x, y, getTimer());
|
||||
|
||||
delete _touchesMap[touchID];
|
||||
_activeTouchesCount--;
|
||||
|
||||
_gesturesManager.onTouchEnd(touch);
|
||||
}
|
||||
|
||||
|
||||
gestouch_internal function onTouchCancel(touchID:uint, x:Number, y:Number):void
|
||||
{
|
||||
const touch:Touch = _touchesMap[touchID] as Touch;
|
||||
if (!touch)
|
||||
return;// touch with specified ID isn't registered
|
||||
|
||||
touch.updateLocation(x, y, getTimer());
|
||||
|
||||
delete _touchesMap[touchID];
|
||||
_activeTouchesCount--;
|
||||
|
||||
_gesturesManager.onTouchCancel(touch);
|
||||
}
|
||||
|
||||
|
||||
protected function createTouch():Touch
|
||||
{
|
||||
//TODO: pool
|
||||
return new Touch();
|
||||
}
|
||||
|
||||
|
||||
public function addTouch(touch:Touch):Touch
|
||||
/**
|
||||
* Sorts from higher priority to lower. Items with the same priority keep the order
|
||||
* of addition, e.g.:
|
||||
* add(a), add(b), add(c, -1), add(d, 1) will be ordered to
|
||||
* d, a, b, c
|
||||
*/
|
||||
protected function hitTestersSorter(x:ITouchHitTester, y:ITouchHitTester):Number
|
||||
{
|
||||
if (_touchesMap.hasOwnProperty(touch.id))
|
||||
{
|
||||
throw new Error("Touch with id " + touch.id + " is already registered.");
|
||||
}
|
||||
const d:int = int(_hitTesterPrioritiesMap[x]) - int(_hitTesterPrioritiesMap[y]);
|
||||
if (d > 0)
|
||||
return -1;
|
||||
else if (d < 0)
|
||||
return 1;
|
||||
|
||||
_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;
|
||||
return _hitTesters.indexOf(x) > _hitTesters.indexOf(y) ? 1 : -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.gestouch.events
|
||||
{
|
||||
import org.gestouch.core.GestureState;
|
||||
|
||||
import flash.events.Event;
|
||||
|
||||
|
||||
|
@ -8,7 +10,7 @@ package org.gestouch.events
|
|||
*/
|
||||
public class GestureEvent extends Event
|
||||
{
|
||||
public var gestureState:uint;
|
||||
public var gestureState:GestureState;
|
||||
public var stageX:Number;
|
||||
public var stageY:Number;
|
||||
public var localX:Number;
|
||||
|
@ -16,7 +18,7 @@ package org.gestouch.events
|
|||
|
||||
|
||||
public function GestureEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false,
|
||||
gestureState:uint = 0,
|
||||
gestureState:GestureState = null,
|
||||
stageX:Number = 0, stageY:Number = 0,
|
||||
localX:Number = 0, localY:Number = 0)
|
||||
{
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.gestouch.events
|
||||
{
|
||||
import org.gestouch.core.GestureState;
|
||||
|
||||
import flash.events.Event;
|
||||
|
||||
|
||||
|
@ -10,11 +12,11 @@ package org.gestouch.events
|
|||
{
|
||||
public static const STATE_CHANGE:String = "stateChange";
|
||||
|
||||
public var newState:uint;
|
||||
public var oldState:uint;
|
||||
public var newState:GestureState;
|
||||
public var oldState:GestureState;
|
||||
|
||||
|
||||
public function GestureStateEvent(type:String, newState:uint, oldState:uint)
|
||||
public function GestureStateEvent(type:String, newState:GestureState, oldState:GestureState)
|
||||
{
|
||||
super(type, false, false);
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.gestouch.events
|
||||
{
|
||||
import org.gestouch.core.GestureState;
|
||||
|
||||
import flash.events.Event;
|
||||
|
||||
|
||||
|
@ -12,7 +14,7 @@ package org.gestouch.events
|
|||
|
||||
|
||||
public function LongPressGestureEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false,
|
||||
gestureState:uint = 0,
|
||||
gestureState:GestureState = null,
|
||||
stageX:Number = 0, stageY:Number = 0,
|
||||
localX:Number = 0, localY:Number = 0)
|
||||
{
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.gestouch.events
|
||||
{
|
||||
import org.gestouch.core.GestureState;
|
||||
|
||||
import flash.events.Event;
|
||||
|
||||
|
||||
|
@ -12,7 +14,7 @@ package org.gestouch.events
|
|||
|
||||
|
||||
public function PanGestureEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false,
|
||||
gestureState:uint = 0,
|
||||
gestureState:GestureState = null,
|
||||
stageX:Number = 0, stageY:Number = 0,
|
||||
localX:Number = 0, localY:Number = 0,
|
||||
offsetX:Number = 0, offsetY:Number = 0)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.gestouch.events
|
||||
{
|
||||
import org.gestouch.core.GestureState;
|
||||
|
||||
import flash.events.Event;
|
||||
|
||||
|
||||
|
@ -12,7 +14,7 @@ package org.gestouch.events
|
|||
|
||||
|
||||
public function RotateGestureEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false,
|
||||
gestureState:uint = 0,
|
||||
gestureState:GestureState = null,
|
||||
stageX:Number = 0, stageY:Number = 0,
|
||||
localX:Number = 0, localY:Number = 0,
|
||||
rotation:Number = 0)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.gestouch.events
|
||||
{
|
||||
import org.gestouch.core.GestureState;
|
||||
|
||||
import flash.events.Event;
|
||||
|
||||
|
||||
|
@ -12,7 +14,7 @@ package org.gestouch.events
|
|||
|
||||
|
||||
public function SwipeGestureEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false,
|
||||
gestureState:uint = 0,
|
||||
gestureState:GestureState = null,
|
||||
stageX:Number = 0, stageY:Number = 0,
|
||||
localX:Number = 0, localY:Number = 0,
|
||||
offsetX:Number = 0, offsetY:Number = 0)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.gestouch.events
|
||||
{
|
||||
import org.gestouch.core.GestureState;
|
||||
|
||||
import flash.events.Event;
|
||||
|
||||
|
||||
|
@ -12,7 +14,8 @@ package org.gestouch.events
|
|||
|
||||
|
||||
public function TapGestureEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false,
|
||||
gestureState:uint = 0, stageX:Number = 0, stageY:Number = 0,
|
||||
gestureState:GestureState = null,
|
||||
stageX:Number = 0, stageY:Number = 0,
|
||||
localX:Number = 0, localY:Number = 0)
|
||||
{
|
||||
super(type, bubbles, cancelable, gestureState, stageX, stageY, localX, localY);
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.gestouch.events
|
||||
{
|
||||
import org.gestouch.core.GestureState;
|
||||
|
||||
import flash.events.Event;
|
||||
|
||||
|
||||
|
@ -18,7 +20,7 @@ package org.gestouch.events
|
|||
|
||||
|
||||
public function TransformGestureEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false,
|
||||
gestureState:uint = 0,
|
||||
gestureState:GestureState = null,
|
||||
stageX:Number = 0, stageY:Number = 0,
|
||||
localX:Number = 0, localY:Number = 0,
|
||||
scaleX:Number = 1.0, scaleY:Number = 1.0,
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.gestouch.events
|
||||
{
|
||||
import org.gestouch.core.GestureState;
|
||||
|
||||
import flash.events.Event;
|
||||
|
||||
|
||||
|
@ -12,7 +14,7 @@ package org.gestouch.events
|
|||
|
||||
|
||||
public function ZoomGestureEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false,
|
||||
gestureState:uint = 0,
|
||||
gestureState:GestureState = null,
|
||||
stageX:Number = 0, stageY:Number = 0,
|
||||
localX:Number = 0, localY:Number = 0,
|
||||
scaleX:Number = 1.0, scaleY:Number = 1.0)
|
||||
|
|
104
src/org/gestouch/extensions/native/NativeDisplayListAdapter.as
Normal file
104
src/org/gestouch/extensions/native/NativeDisplayListAdapter.as
Normal file
|
@ -0,0 +1,104 @@
|
|||
package org.gestouch.extensions.native
|
||||
{
|
||||
import flash.display.DisplayObject;
|
||||
import flash.display.DisplayObjectContainer;
|
||||
import flash.display.Stage;
|
||||
import flash.geom.Point;
|
||||
import flash.utils.Dictionary;
|
||||
import org.gestouch.core.IDisplayListAdapter;
|
||||
|
||||
|
||||
/**
|
||||
* @author Pavel fljot
|
||||
*/
|
||||
final public class NativeDisplayListAdapter implements IDisplayListAdapter
|
||||
{
|
||||
private var _targetWeekStorage:Dictionary;
|
||||
|
||||
|
||||
public function NativeDisplayListAdapter(target:DisplayObject = null)
|
||||
{
|
||||
if (target)
|
||||
{
|
||||
_targetWeekStorage = new Dictionary(true);
|
||||
_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(object:Object):Boolean
|
||||
{
|
||||
const targetAsDOC:DisplayObjectContainer = this.target as DisplayObjectContainer;
|
||||
if (targetAsDOC is Stage)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
const objectAsDO:DisplayObject = object as DisplayObject;
|
||||
if (objectAsDO)
|
||||
{
|
||||
return (targetAsDOC && targetAsDOC.contains(objectAsDO));
|
||||
}
|
||||
/**
|
||||
* There might be case when we use some old "software" 3D library for instace,
|
||||
* which viewport is added to classic Display List. So native stage, root and some other
|
||||
* sprites will actually be parents of 3D objects. To ensure all gestures (both for
|
||||
* native and 3D objects) work correctly with each other contains() method should be
|
||||
* a bit more sophisticated.
|
||||
* But as all 3D engines (at least it looks like that) are moving towards Stage3D layer
|
||||
* this task doesn't seem significant anymore. So I leave this implementation as
|
||||
* comments in case someone will actually need it.
|
||||
* Just uncomment this and it should work.
|
||||
|
||||
// else: more complex case.
|
||||
// object is not of the same type as this.target (flash.display::DisplayObject)
|
||||
// it might we some 3D library object in it's viewport (which itself is in DisplayList).
|
||||
// So we perform more general check:
|
||||
const adapter:IDisplayListAdapter = Gestouch.gestouch_internal::getDisplayListAdapter(object);
|
||||
if (adapter)
|
||||
{
|
||||
return adapter.getHierarchy(object).indexOf(this.target) > -1;
|
||||
}
|
||||
*/
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public function getHierarchy(genericTarget:Object):Vector.<Object>
|
||||
{
|
||||
var list:Vector.<Object> = new Vector.<Object>();
|
||||
var i:uint = 0;
|
||||
var target:DisplayObject = genericTarget as DisplayObject;
|
||||
while (target)
|
||||
{
|
||||
list[i] = target;
|
||||
target = target.parent;
|
||||
i++;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
public function reflect():Class
|
||||
{
|
||||
return NativeDisplayListAdapter;
|
||||
}
|
||||
}
|
||||
}
|
19
src/org/gestouch/extensions/native/NativeTouchHitTester.as
Normal file
19
src/org/gestouch/extensions/native/NativeTouchHitTester.as
Normal file
|
@ -0,0 +1,19 @@
|
|||
package org.gestouch.extensions.native
|
||||
{
|
||||
import org.gestouch.core.ITouchHitTester;
|
||||
|
||||
import flash.display.InteractiveObject;
|
||||
import flash.geom.Point;
|
||||
|
||||
|
||||
/**
|
||||
* @author Pavel fljot
|
||||
*/
|
||||
final public class NativeTouchHitTester implements ITouchHitTester
|
||||
{
|
||||
public function hitTest(point:Point, nativeTarget:InteractiveObject):Object
|
||||
{
|
||||
return nativeTarget;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package org.gestouch.extensions.starling
|
||||
{
|
||||
import starling.core.Starling;
|
||||
import starling.display.DisplayObject;
|
||||
import starling.display.DisplayObjectContainer;
|
||||
|
||||
import org.gestouch.core.IDisplayListAdapter;
|
||||
|
||||
import flash.geom.Point;
|
||||
import flash.utils.Dictionary;
|
||||
|
||||
|
||||
/**
|
||||
* @author Pavel fljot
|
||||
*/
|
||||
final public class StarlingDisplayListAdapter implements IDisplayListAdapter
|
||||
{
|
||||
private var targetWeekStorage:Dictionary;
|
||||
|
||||
|
||||
public function StarlingDisplayListAdapter(target:DisplayObject = null)
|
||||
{
|
||||
if (target)
|
||||
{
|
||||
targetWeekStorage = new Dictionary(true);
|
||||
targetWeekStorage[target] = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function get target():Object
|
||||
{
|
||||
for (var key:Object in targetWeekStorage)
|
||||
{
|
||||
return key;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public function globalToLocal(point:Point):Point
|
||||
{
|
||||
point = StarlingUtils.adjustGlobalPoint(Starling.current, point);
|
||||
return (target as DisplayObject).globalToLocal(point);
|
||||
}
|
||||
|
||||
|
||||
public function contains(object:Object):Boolean
|
||||
{
|
||||
const targetAsDOC:DisplayObjectContainer = this.target as DisplayObjectContainer;
|
||||
const objectAsDO:DisplayObject = object as DisplayObject;
|
||||
return (targetAsDOC && objectAsDO && targetAsDOC.contains(objectAsDO));
|
||||
}
|
||||
|
||||
|
||||
public function getHierarchy(genericTarget:Object):Vector.<Object>
|
||||
{
|
||||
var list:Vector.<Object> = new Vector.<Object>();
|
||||
var i:uint = 0;
|
||||
var target:DisplayObject = genericTarget as DisplayObject;
|
||||
while (target)
|
||||
{
|
||||
list[i] = target;
|
||||
target = target.parent;
|
||||
i++;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
public function reflect():Class
|
||||
{
|
||||
return StarlingDisplayListAdapter;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package org.gestouch.extensions.starling
|
||||
{
|
||||
import starling.core.Starling;
|
||||
|
||||
import org.gestouch.core.ITouchHitTester;
|
||||
|
||||
import flash.display.InteractiveObject;
|
||||
import flash.geom.Point;
|
||||
|
||||
|
||||
/**
|
||||
* @author Pavel fljot
|
||||
*/
|
||||
final public class StarlingTouchHitTester implements ITouchHitTester
|
||||
{
|
||||
private var starling:Starling;
|
||||
|
||||
|
||||
public function StarlingTouchHitTester(starling:Starling)
|
||||
{
|
||||
if (!starling)
|
||||
{
|
||||
throw ArgumentError("Missing starling argument.");
|
||||
}
|
||||
|
||||
this.starling = starling;
|
||||
}
|
||||
|
||||
|
||||
public function hitTest(point:Point, nativeTarget:InteractiveObject):Object
|
||||
{
|
||||
point = StarlingUtils.adjustGlobalPoint(starling, point);
|
||||
return starling.stage.hitTest(point, true);
|
||||
}
|
||||
}
|
||||
}
|
38
src/org/gestouch/extensions/starling/StarlingUtils.as
Normal file
38
src/org/gestouch/extensions/starling/StarlingUtils.as
Normal file
|
@ -0,0 +1,38 @@
|
|||
package org.gestouch.extensions.starling
|
||||
{
|
||||
import starling.display.Stage;
|
||||
import starling.core.Starling;
|
||||
|
||||
import flash.geom.Point;
|
||||
import flash.geom.Rectangle;
|
||||
|
||||
|
||||
/**
|
||||
* @author Pavel fljot
|
||||
*/
|
||||
final public class StarlingUtils
|
||||
{
|
||||
/**
|
||||
* Transforms real global point (in the scope of flash.display::Stage) into
|
||||
* starling stage "global" coordinates.
|
||||
*/
|
||||
public static function adjustGlobalPoint(starling:Starling, point:Point):Point
|
||||
{
|
||||
const vp:Rectangle = starling.viewPort;
|
||||
const stStage:Stage = starling.stage;
|
||||
|
||||
if (vp.x != 0 || vp.y != 0 ||
|
||||
stStage.stageWidth != vp.width || stStage.stageHeight != vp.height)
|
||||
{
|
||||
point = point.clone();
|
||||
|
||||
// Same transformation they do in Starling
|
||||
// WTF!? https://github.com/PrimaryFeather/Starling-Framework/issues/72
|
||||
point.x = stStage.stageWidth * (point.x - vp.x) / vp.width;
|
||||
point.y = stStage.stageHeight * (point.y - vp.y) / vp.height;
|
||||
}
|
||||
|
||||
return point;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,28 +1,32 @@
|
|||
package org.gestouch.gestures
|
||||
{
|
||||
import org.gestouch.core.Gestouch;
|
||||
import org.gestouch.core.GestureState;
|
||||
import org.gestouch.core.GesturesManager;
|
||||
import org.gestouch.core.IGestureDelegate;
|
||||
import org.gestouch.core.IGesturesManager;
|
||||
import org.gestouch.core.IGestureTargetAdapter;
|
||||
import org.gestouch.core.Touch;
|
||||
import org.gestouch.core.gestouch_internal;
|
||||
import org.gestouch.events.GestureStateEvent;
|
||||
|
||||
import flash.display.InteractiveObject;
|
||||
import flash.errors.IllegalOperationError;
|
||||
import flash.events.EventDispatcher;
|
||||
import flash.geom.Point;
|
||||
import flash.system.Capabilities;
|
||||
import flash.utils.Dictionary;
|
||||
|
||||
|
||||
/**
|
||||
* Dispatched when the state of the gesture changes.
|
||||
*
|
||||
* @eventType org.gestouch.events.GestureStateEvent
|
||||
* @see #state
|
||||
*/
|
||||
[Event(name="stateChange", type="org.gestouch.events.GestureStateEvent")]
|
||||
/**
|
||||
* Base class for all gestures. Gesture is essentially a detector that tracks touch points
|
||||
* in order detect specific gesture motion and form gesture event on target.
|
||||
*
|
||||
* TODO:
|
||||
* -
|
||||
*
|
||||
* @author Pavel fljot
|
||||
*/
|
||||
public class Gesture extends EventDispatcher
|
||||
|
@ -32,10 +36,10 @@ package org.gestouch.gestures
|
|||
* (not an accidental offset on touch),
|
||||
* based on 20 pixels on a 252ppi device.
|
||||
*/
|
||||
public static const DEFAULT_SLOP:uint = Math.round(20 / 252 * flash.system.Capabilities.screenDPI);
|
||||
public static var DEFAULT_SLOP:uint = Math.round(20 / 252 * flash.system.Capabilities.screenDPI);
|
||||
|
||||
|
||||
protected const _gesturesManager:IGesturesManager = GesturesManager.getInstance();
|
||||
protected const _gesturesManager:GesturesManager = Gestouch.gesturesManager;
|
||||
/**
|
||||
* Map (generic object) of tracking touch points, where keys are touch points IDs.
|
||||
*/
|
||||
|
@ -47,10 +51,12 @@ package org.gestouch.gestures
|
|||
* @see requireGestureToFail()
|
||||
*/
|
||||
protected var _gesturesToFail:Dictionary = new Dictionary(true);
|
||||
protected var _pendingRecognizedState:uint;
|
||||
protected var _pendingRecognizedState:GestureState;
|
||||
|
||||
use namespace gestouch_internal;
|
||||
|
||||
|
||||
public function Gesture(target:InteractiveObject = null)
|
||||
public function Gesture(target:Object = null)
|
||||
{
|
||||
super();
|
||||
|
||||
|
@ -61,9 +67,22 @@ package org.gestouch.gestures
|
|||
|
||||
|
||||
/** @private */
|
||||
private var _targetWeekStorage:Dictionary;
|
||||
protected var _targetAdapter:IGestureTargetAdapter;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
gestouch_internal function get targetAdapter():IGestureTargetAdapter
|
||||
{
|
||||
return _targetAdapter;
|
||||
}
|
||||
protected function get targetAdapter():IGestureTargetAdapter
|
||||
{
|
||||
return _targetAdapter;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* FIXME
|
||||
* InteractiveObject (DisplayObject) which this gesture is tracking the actual gesture motion on.
|
||||
*
|
||||
* <p>Could be some image, component (like map) or the larger view like Stage.</p>
|
||||
|
@ -74,29 +93,18 @@ 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;
|
||||
return _targetAdapter ? _targetAdapter.target : null;
|
||||
}
|
||||
public function set target(value:InteractiveObject):void
|
||||
public function set target(value:Object):void
|
||||
{
|
||||
var target:InteractiveObject = this.target;
|
||||
var target:Object = 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;
|
||||
}
|
||||
_targetAdapter = value ? Gestouch.createGestureTargetAdapter(value) : null;
|
||||
installTarget(value);
|
||||
}
|
||||
|
||||
|
@ -117,11 +125,18 @@ package org.gestouch.gestures
|
|||
return;
|
||||
|
||||
_enabled = value;
|
||||
//TODO
|
||||
if (!_enabled && state != GestureState.IDLE)
|
||||
|
||||
if (!_enabled)
|
||||
{
|
||||
setState(GestureState.CANCELLED);
|
||||
reset();
|
||||
if (state == GestureState.POSSIBLE)
|
||||
{
|
||||
setState(GestureState.FAILED);
|
||||
}
|
||||
else
|
||||
if (state == GestureState.BEGAN || state == GestureState.CHANGED)
|
||||
{
|
||||
setState(GestureState.CANCELLED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -148,8 +163,8 @@ package org.gestouch.gestures
|
|||
}
|
||||
|
||||
|
||||
protected var _state:uint = GestureState.IDLE;
|
||||
public function get state():uint
|
||||
protected var _state:GestureState = GestureState.IDLE;
|
||||
public function get state():GestureState
|
||||
{
|
||||
return _state;
|
||||
}
|
||||
|
@ -173,7 +188,6 @@ package org.gestouch.gestures
|
|||
*/
|
||||
public function get location():Point
|
||||
{
|
||||
//TODO: to clone or not clone? performance & convention or ...
|
||||
return _location.clone();
|
||||
}
|
||||
|
||||
|
@ -186,6 +200,17 @@ package org.gestouch.gestures
|
|||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
override public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void
|
||||
{
|
||||
if (!eventTypeIsValid(type))
|
||||
{
|
||||
throw new ArgumentError("Event type does not match any of allowed values.");
|
||||
}
|
||||
|
||||
super.addEventListener(type, listener, useCapture, priority, useWeakReference);
|
||||
}
|
||||
|
||||
|
||||
[Abstract]
|
||||
/**
|
||||
* Reflects gesture class (for better perfomance).
|
||||
|
@ -213,7 +238,7 @@ package org.gestouch.gestures
|
|||
*/
|
||||
public function reset():void
|
||||
{
|
||||
var state:uint = this.state;//caching getter
|
||||
var state:GestureState = this.state;//caching getter
|
||||
|
||||
if (state == GestureState.IDLE)
|
||||
return;// Do nothing as we're in IDLE and nothing to reset
|
||||
|
@ -228,14 +253,14 @@ package org.gestouch.gestures
|
|||
var gestureToFail:Gesture = key as Gesture;
|
||||
gestureToFail.removeEventListener(GestureStateEvent.STATE_CHANGE, gestureToFail_stateChangeHandler);
|
||||
}
|
||||
_pendingRecognizedState = 0;
|
||||
_pendingRecognizedState = null;
|
||||
|
||||
if (state == GestureState.POSSIBLE)
|
||||
{
|
||||
// manual reset() call. Set to FAILED to keep our State Machine clean and stable
|
||||
setState(GestureState.FAILED);
|
||||
}
|
||||
else if (state == GestureState.BEGAN || state == GestureState.RECOGNIZED)
|
||||
else if (state == GestureState.BEGAN || state == GestureState.CHANGED)
|
||||
{
|
||||
// manual reset() call. Set to CANCELLED to keep our State Machine clean and stable
|
||||
setState(GestureState.CANCELLED);
|
||||
|
@ -285,6 +310,11 @@ package org.gestouch.gestures
|
|||
public function requireGestureToFail(gesture:Gesture):void
|
||||
{
|
||||
//TODO
|
||||
if (!gesture)
|
||||
{
|
||||
throw new ArgumentError();
|
||||
}
|
||||
|
||||
_gesturesToFail[gesture] = true;
|
||||
}
|
||||
|
||||
|
@ -310,11 +340,11 @@ 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(target:Object):void
|
||||
{
|
||||
if (target)
|
||||
{
|
||||
_gesturesManager.gestouch_internal::addGesture(this);
|
||||
_gesturesManager.addGesture(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -326,11 +356,11 @@ 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(target:Object):void
|
||||
{
|
||||
if (target)
|
||||
{
|
||||
_gesturesManager.gestouch_internal::removeGesture(this);
|
||||
_gesturesManager.removeGesture(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -348,6 +378,19 @@ package org.gestouch.gestures
|
|||
}
|
||||
|
||||
|
||||
protected function failOrIgnoreTouch(touch:Touch):void
|
||||
{
|
||||
if (state == GestureState.POSSIBLE)
|
||||
{
|
||||
setState(GestureState.FAILED);
|
||||
}
|
||||
else if (state != GestureState.IDLE)
|
||||
{
|
||||
ignoreTouch(touch);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Abstract]
|
||||
/**
|
||||
* <p><b>NB!</b> This is abstract method and must be overridden.</p>
|
||||
|
@ -375,30 +418,47 @@ package org.gestouch.gestures
|
|||
}
|
||||
|
||||
|
||||
protected function setState(newState:uint):Boolean
|
||||
/**
|
||||
*
|
||||
*/
|
||||
protected function onTouchCancel(touch:Touch):void
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
protected function setState(newState:GestureState):Boolean
|
||||
{
|
||||
if (_state == newState && _state == GestureState.CHANGED)
|
||||
{
|
||||
// shortcut for better performance
|
||||
if (hasEventListener(GestureStateEvent.STATE_CHANGE))
|
||||
{
|
||||
dispatchEvent(new GestureStateEvent(GestureStateEvent.STATE_CHANGE, _state, _state));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//TODO: is state sequence validation needed? e.g.:
|
||||
//POSSIBLE should be followed by BEGAN or RECOGNIZED or FAILED
|
||||
//BEGAN should be follwed by CHANGED or ENDED or CANCELLED
|
||||
//CHANGED should be followed by CHANGED or ENDED or CANCELLED
|
||||
//...
|
||||
if (!_state.canTransitionTo(newState))
|
||||
{
|
||||
throw new IllegalOperationError("You cannot change from state " +
|
||||
_state + " to state " + newState + ".");
|
||||
}
|
||||
|
||||
|
||||
if (newState == GestureState.BEGAN || newState == GestureState.RECOGNIZED)
|
||||
{
|
||||
var gestureToFail:Gesture;
|
||||
var key:*;
|
||||
// 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)
|
||||
for (key in _gesturesToFail)
|
||||
{
|
||||
gestureToFail = key as Gesture;
|
||||
if (gestureToFail.state != GestureState.IDLE && gestureToFail.state != GestureState.POSSIBLE
|
||||
&& gestureToFail.state != GestureState.FAILED)
|
||||
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
|
||||
|
@ -406,7 +466,7 @@ package org.gestouch.gestures
|
|||
return false;
|
||||
}
|
||||
}
|
||||
// then we check of other required-to-fail gestures are actually tracked (not IDLE)
|
||||
// then we check if other required-to-fail gestures are actually tracked (not IDLE)
|
||||
// and not still not recognized (e.g. POSSIBLE state)
|
||||
for (key in _gesturesToFail)
|
||||
{
|
||||
|
@ -415,6 +475,13 @@ package org.gestouch.gestures
|
|||
{
|
||||
// Other gesture might fail soon, so we postpone state change
|
||||
_pendingRecognizedState = newState;
|
||||
|
||||
for (key in _gesturesToFail)
|
||||
{
|
||||
gestureToFail = key as Gesture;
|
||||
gestureToFail.addEventListener(GestureStateEvent.STATE_CHANGE, gestureToFail_stateChangeHandler, false, 0, true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
// else if gesture is in IDLE state it means it doesn't track anything,
|
||||
|
@ -430,12 +497,12 @@ package org.gestouch.gestures
|
|||
}
|
||||
}
|
||||
|
||||
var oldState:uint = _state;
|
||||
var oldState:GestureState = _state;
|
||||
_state = newState;
|
||||
|
||||
if (((GestureState.CANCELLED | GestureState.RECOGNIZED | GestureState.ENDED | GestureState.FAILED) & _state) > 0)
|
||||
if (_state.isEndState)
|
||||
{
|
||||
_gesturesManager.gestouch_internal::scheduleGestureStateReset(this);
|
||||
_gesturesManager.scheduleGestureStateReset(this);
|
||||
}
|
||||
|
||||
//TODO: what if RTE happens in event handlers?
|
||||
|
@ -447,14 +514,14 @@ package org.gestouch.gestures
|
|||
|
||||
if (_state == GestureState.BEGAN || _state == GestureState.RECOGNIZED)
|
||||
{
|
||||
_gesturesManager.gestouch_internal::onGestureRecognized(this);
|
||||
_gesturesManager.onGestureRecognized(this);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
gestouch_internal function setState_internal(state:uint):void
|
||||
gestouch_internal function setState_internal(state:GestureState):void
|
||||
{
|
||||
setState(state);
|
||||
}
|
||||
|
@ -481,7 +548,7 @@ package org.gestouch.gestures
|
|||
updateCentralPoint();
|
||||
_location.x = _centralPoint.x;
|
||||
_location.y = _centralPoint.y;
|
||||
_localLocation = target.globalToLocal(_location);
|
||||
_localLocation = targetAdapter.globalToLocal(_location);
|
||||
}
|
||||
|
||||
|
||||
|
@ -496,6 +563,13 @@ package org.gestouch.gestures
|
|||
}
|
||||
|
||||
|
||||
protected function eventTypeIsValid(type:String):Boolean
|
||||
{
|
||||
// propertyChange just in case for bindings?
|
||||
return type == GestureStateEvent.STATE_CHANGE || type == "propertyChange";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
@ -513,12 +587,6 @@ 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);
|
||||
}
|
||||
}
|
||||
|
@ -540,12 +608,30 @@ package org.gestouch.gestures
|
|||
}
|
||||
|
||||
|
||||
gestouch_internal function touchCancelHandler(touch:Touch):void
|
||||
{
|
||||
delete _touchesMap[touch.id];
|
||||
_touchesCount--;
|
||||
|
||||
onTouchCancel(touch);
|
||||
|
||||
if (!state.isEndState)
|
||||
{
|
||||
if (state == GestureState.BEGAN || state == GestureState.CHANGED)
|
||||
{
|
||||
setState(GestureState.CANCELLED);
|
||||
}
|
||||
else
|
||||
{
|
||||
setState(GestureState.FAILED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function gestureToFail_stateChangeHandler(event:GestureStateEvent):void
|
||||
{
|
||||
if (state != GestureState.POSSIBLE)
|
||||
return;//just in case..FIXME?
|
||||
|
||||
if (!_pendingRecognizedState)
|
||||
if (!_pendingRecognizedState || state != GestureState.POSSIBLE)
|
||||
return;
|
||||
|
||||
if (event.newState == GestureState.FAILED)
|
||||
|
@ -560,15 +646,20 @@ package org.gestouch.gestures
|
|||
}
|
||||
}
|
||||
|
||||
// at this point all gestures-to-fail are either in IDLE or in FAILED states
|
||||
if (setState(_pendingRecognizedState))
|
||||
{
|
||||
onDelayedRecognize();
|
||||
}
|
||||
}
|
||||
else if (event.newState != GestureState.POSSIBLE)
|
||||
else if (event.newState != GestureState.IDLE && event.newState != GestureState.POSSIBLE)
|
||||
{
|
||||
// NB: _other_ gesture may switch to IDLE state if it was in FAILED when
|
||||
// _this_ gesture initially attempted to switch to one of recognized state.
|
||||
// ...and that's OK (we ignore that)
|
||||
|
||||
setState(GestureState.FAILED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,14 +4,18 @@ package org.gestouch.gestures
|
|||
import org.gestouch.core.Touch;
|
||||
import org.gestouch.events.LongPressGestureEvent;
|
||||
|
||||
import flash.display.InteractiveObject;
|
||||
import flash.events.TimerEvent;
|
||||
import flash.utils.Timer;
|
||||
|
||||
|
||||
/**
|
||||
* TODO: -location
|
||||
* - check on iOS (Obj-C) what happens when numTouchesRequired=2, two finger down, then quickly release one.
|
||||
*
|
||||
* @eventType org.gestouch.events.LongPressGestureEvent
|
||||
*/
|
||||
[Event(name="gestureLongPress", type="org.gestouch.events.LongPressGestureEvent")]
|
||||
/**
|
||||
* TODO:
|
||||
* - add numTapsRequired
|
||||
*
|
||||
* @author Pavel fljot
|
||||
*/
|
||||
|
@ -21,16 +25,16 @@ package org.gestouch.gestures
|
|||
/**
|
||||
* The minimum time interval in millisecond fingers must press on the target for the gesture to be recognized.
|
||||
*
|
||||
* @default 500
|
||||
*/
|
||||
public var minPressDuration:uint = 500;
|
||||
* @default 500
|
||||
*/
|
||||
public var minPressDuration:uint = 500;
|
||||
public var slop:Number = Gesture.DEFAULT_SLOP;
|
||||
|
||||
protected var _timer:Timer;
|
||||
protected var _numTouchesRequiredReached:Boolean;
|
||||
|
||||
|
||||
public function LongPressGesture(target:InteractiveObject = null)
|
||||
public function LongPressGesture(target:Object = null)
|
||||
{
|
||||
super(target);
|
||||
}
|
||||
|
@ -49,7 +53,7 @@ package org.gestouch.gestures
|
|||
return TapGesture;
|
||||
}
|
||||
|
||||
|
||||
|
||||
override public function reset():void
|
||||
{
|
||||
super.reset();
|
||||
|
@ -67,6 +71,12 @@ package org.gestouch.gestures
|
|||
//
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
override protected function eventTypeIsValid(type:String):Boolean
|
||||
{
|
||||
return type == LongPressGestureEvent.GESTURE_LONG_PRESS || super.eventTypeIsValid(type);
|
||||
}
|
||||
|
||||
|
||||
override protected function preinit():void
|
||||
{
|
||||
super.preinit();
|
||||
|
@ -80,14 +90,7 @@ package org.gestouch.gestures
|
|||
{
|
||||
if (touchesCount > numTouchesRequired)
|
||||
{
|
||||
if (state == GestureState.BEGAN || state == GestureState.CHANGED)
|
||||
{
|
||||
ignoreTouch(touch);
|
||||
}
|
||||
else
|
||||
{
|
||||
setState(GestureState.FAILED);
|
||||
}
|
||||
failOrIgnoreTouch(touch);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -121,10 +124,9 @@ package org.gestouch.gestures
|
|||
|
||||
override protected function onTouchEnd(touch:Touch):void
|
||||
{
|
||||
//TODO: check proper condition (behavior) on iOS native
|
||||
if (_numTouchesRequiredReached)
|
||||
{
|
||||
if (((GestureState.BEGAN | GestureState.CHANGED) & state) > 0)
|
||||
if (state == GestureState.BEGAN || state == GestureState.CHANGED)
|
||||
{
|
||||
updateLocation();
|
||||
if (setState(GestureState.ENDED) && hasEventListener(LongPressGestureEvent.GESTURE_LONG_PRESS))
|
||||
|
@ -144,7 +146,7 @@ package org.gestouch.gestures
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
override protected function onDelayedRecognize():void
|
||||
{
|
||||
if (hasEventListener(LongPressGestureEvent.GESTURE_LONG_PRESS))
|
||||
|
|
|
@ -4,9 +4,13 @@ package org.gestouch.gestures
|
|||
import org.gestouch.core.Touch;
|
||||
import org.gestouch.events.PanGestureEvent;
|
||||
|
||||
import flash.display.InteractiveObject;
|
||||
import flash.geom.Point;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @eventType org.gestouch.events.PanGestureEvent
|
||||
*/
|
||||
[Event(name="gesturePan", type="org.gestouch.events.PanGestureEvent")]
|
||||
/**
|
||||
* TODO:
|
||||
|
@ -27,14 +31,14 @@ package org.gestouch.gestures
|
|||
protected var _gestureBeginOffsetY:Number;
|
||||
|
||||
|
||||
public function PanGesture(target:InteractiveObject = null)
|
||||
public function PanGesture(target:Object = null)
|
||||
{
|
||||
super(target);
|
||||
}
|
||||
|
||||
|
||||
/** @private */
|
||||
private var _maxNumTouchesRequired:uint = 1;
|
||||
private var _maxNumTouchesRequired:uint = uint.MAX_VALUE;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -108,19 +112,24 @@ package org.gestouch.gestures
|
|||
//
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
override protected function eventTypeIsValid(type:String):Boolean
|
||||
{
|
||||
return type == PanGestureEvent.GESTURE_PAN || super.eventTypeIsValid(type);
|
||||
}
|
||||
|
||||
|
||||
override protected function onTouchBegin(touch:Touch):void
|
||||
{
|
||||
if (touchesCount > maxNumTouchesRequired)
|
||||
{
|
||||
//TODO
|
||||
ignoreTouch(touch);
|
||||
failOrIgnoreTouch(touch);
|
||||
return;
|
||||
}
|
||||
|
||||
if (touchesCount >= minNumTouchesRequired)
|
||||
{
|
||||
updateLocation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -136,6 +145,10 @@ package org.gestouch.gestures
|
|||
|
||||
if (state == GestureState.POSSIBLE)
|
||||
{
|
||||
prevLocationX = _location.x;
|
||||
prevLocationY = _location.y;
|
||||
updateLocation();
|
||||
|
||||
// Check if finger moved enough for gesture to be recognized
|
||||
var locationOffset:Point = touch.locationOffset;
|
||||
if (direction == PanGestureDirection.VERTICAL)
|
||||
|
@ -149,9 +162,6 @@ package org.gestouch.gestures
|
|||
|
||||
if (locationOffset.length > slop || slop != slop)//faster isNaN(slop)
|
||||
{
|
||||
prevLocationX = _location.x;
|
||||
prevLocationY = _location.y;
|
||||
updateLocation();
|
||||
offsetX = _location.x - prevLocationX;
|
||||
offsetY = _location.y - prevLocationY;
|
||||
// acummulate begin offsets for the case when this gesture recognition is delayed by requireGestureToFail
|
||||
|
|
|
@ -3,11 +3,14 @@ package org.gestouch.gestures
|
|||
import org.gestouch.core.GestureState;
|
||||
import org.gestouch.core.Touch;
|
||||
import org.gestouch.events.RotateGestureEvent;
|
||||
import org.gestouch.utils.GestureUtils;
|
||||
|
||||
import flash.display.InteractiveObject;
|
||||
import flash.geom.Point;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @eventType org.gestouch.events.RotateGestureEvent
|
||||
*/
|
||||
[Event(name="gestureRotate", type="org.gestouch.events.RotateGestureEvent")]
|
||||
/**
|
||||
* TODO:
|
||||
|
@ -17,14 +20,15 @@ package org.gestouch.gestures
|
|||
*/
|
||||
public class RotateGesture extends Gesture
|
||||
{
|
||||
public var slop:Number = Gesture.DEFAULT_SLOP >> 1;
|
||||
public var slop:Number = Gesture.DEFAULT_SLOP;
|
||||
|
||||
protected var _touch1:Touch;
|
||||
protected var _touch2:Touch;
|
||||
protected var _transformVector:Point;
|
||||
protected var _thresholdAngle:Number;
|
||||
|
||||
|
||||
public function RotateGesture(target:InteractiveObject = null)
|
||||
public function RotateGesture(target:Object = null)
|
||||
{
|
||||
super(target);
|
||||
}
|
||||
|
@ -52,12 +56,17 @@ package org.gestouch.gestures
|
|||
//
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
override protected function eventTypeIsValid(type:String):Boolean
|
||||
{
|
||||
return type == RotateGestureEvent.GESTURE_ROTATE || super.eventTypeIsValid(type);
|
||||
}
|
||||
|
||||
|
||||
override protected function onTouchBegin(touch:Touch):void
|
||||
{
|
||||
if (touchesCount > 2)
|
||||
{
|
||||
//TODO
|
||||
ignoreTouch(touch);
|
||||
failOrIgnoreTouch(touch);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -70,6 +79,9 @@ package org.gestouch.gestures
|
|||
_touch2 = touch;
|
||||
|
||||
_transformVector = _touch2.location.subtract(_touch1.location);
|
||||
|
||||
// @see chord length formula
|
||||
_thresholdAngle = Math.asin(slop / (2 * _transformVector.length)) * 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,38 +91,41 @@ package org.gestouch.gestures
|
|||
if (touchesCount < 2)
|
||||
return;
|
||||
|
||||
var recognized:Boolean = true;
|
||||
var currTransformVector:Point = _touch2.location.subtract(_touch1.location);
|
||||
var rotation:Number = Math.atan2(currTransformVector.y, currTransformVector.x) - Math.atan2(_transformVector.y, _transformVector.x);
|
||||
|
||||
if (state == GestureState.POSSIBLE && slop > 0 && touch.locationOffset.length < slop)
|
||||
if (state == GestureState.POSSIBLE)
|
||||
{
|
||||
recognized = false;
|
||||
const absRotation:Number = rotation >= 0 ? rotation : -rotation;
|
||||
if (absRotation < _thresholdAngle)
|
||||
{
|
||||
// not recognized yet
|
||||
return;
|
||||
}
|
||||
|
||||
// adjust angle to avoid initial "jump"
|
||||
rotation = rotation > 0 ? rotation - _thresholdAngle : rotation + _thresholdAngle;
|
||||
}
|
||||
|
||||
if (recognized)
|
||||
_transformVector.x = currTransformVector.x;
|
||||
_transformVector.y = currTransformVector.y;
|
||||
|
||||
updateLocation();
|
||||
|
||||
if (state == GestureState.POSSIBLE)
|
||||
{
|
||||
var currTransformVector:Point = _touch2.location.subtract(_touch1.location);
|
||||
var rotation:Number = Math.atan2(currTransformVector.y, currTransformVector.x) - Math.atan2(_transformVector.y, _transformVector.x);
|
||||
rotation *= GestureUtils.RADIANS_TO_DEGREES;
|
||||
_transformVector.x = currTransformVector.x;
|
||||
_transformVector.y = currTransformVector.y;
|
||||
|
||||
updateLocation();
|
||||
|
||||
if (state == GestureState.POSSIBLE)
|
||||
if (setState(GestureState.BEGAN) && hasEventListener(RotateGestureEvent.GESTURE_ROTATE))
|
||||
{
|
||||
if (setState(GestureState.BEGAN) && hasEventListener(RotateGestureEvent.GESTURE_ROTATE))
|
||||
{
|
||||
dispatchEvent(new RotateGestureEvent(RotateGestureEvent.GESTURE_ROTATE, false, false, GestureState.BEGAN,
|
||||
_location.x, _location.y, _localLocation.x, _localLocation.y, rotation));
|
||||
}
|
||||
dispatchEvent(new RotateGestureEvent(RotateGestureEvent.GESTURE_ROTATE, false, false, GestureState.BEGAN,
|
||||
_location.x, _location.y, _localLocation.x, _localLocation.y, rotation));
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
if (setState(GestureState.CHANGED) && hasEventListener(RotateGestureEvent.GESTURE_ROTATE))
|
||||
{
|
||||
if (setState(GestureState.CHANGED) && hasEventListener(RotateGestureEvent.GESTURE_ROTATE))
|
||||
{
|
||||
dispatchEvent(new RotateGestureEvent(RotateGestureEvent.GESTURE_ROTATE, false, false, GestureState.CHANGED,
|
||||
_location.x, _location.y, _localLocation.x, _localLocation.y, rotation));
|
||||
}
|
||||
dispatchEvent(new RotateGestureEvent(RotateGestureEvent.GESTURE_ROTATE, false, false, GestureState.CHANGED,
|
||||
_location.x, _location.y, _localLocation.x, _localLocation.y, rotation));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,35 +3,85 @@ package org.gestouch.gestures
|
|||
import org.gestouch.core.GestureState;
|
||||
import org.gestouch.core.Touch;
|
||||
import org.gestouch.events.SwipeGestureEvent;
|
||||
import org.gestouch.utils.GestureUtils;
|
||||
|
||||
import flash.display.InteractiveObject;
|
||||
import flash.events.TimerEvent;
|
||||
import flash.geom.Point;
|
||||
import flash.system.Capabilities;
|
||||
import flash.utils.Timer;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @eventType org.gestouch.events.SwipeGestureEvent
|
||||
*/
|
||||
[Event(name="gestureSwipe", type="org.gestouch.events.SwipeGestureEvent")]
|
||||
/**
|
||||
* TODO:
|
||||
* -check native behavior on iDevice
|
||||
* Recognition logic:<br/>
|
||||
* 1. should be recognized during <code>maxDuration</code> period<br/>
|
||||
* 2. velocity >= minVelocity <b>OR</b> offset >= minOffset
|
||||
*
|
||||
*
|
||||
* @author Pavel fljot
|
||||
*/
|
||||
public class SwipeGesture extends Gesture
|
||||
{
|
||||
public var slop:Number = Gesture.DEFAULT_SLOP;
|
||||
private static const ANGLE:Number = 40 * GestureUtils.DEGREES_TO_RADIANS;
|
||||
private static const MAX_DURATION:uint = 500;
|
||||
private static const MIN_OFFSET:Number = Capabilities.screenDPI / 6;
|
||||
private static const MIN_VELOCITY:Number = 2 * MIN_OFFSET / MAX_DURATION;
|
||||
|
||||
/**
|
||||
* "Dirty" region around touch begin location which does not taken into account
|
||||
* for gesture failing conditions. It might be useful in case your device is too sensitive
|
||||
* (so when you just touch the <i>screen</i> you get undesired accidental movement).<br/>
|
||||
* <b>NB! Unlike in other gestures, default value for slop is 0 here.</b>
|
||||
*
|
||||
* @default 0
|
||||
*/
|
||||
public var slop:Number = 0;
|
||||
public var numTouchesRequired:uint = 1;
|
||||
public var minVelocity:Number = 0.8;
|
||||
public var minOffset:Number = Gesture.DEFAULT_SLOP;
|
||||
public var direction:uint = SwipeGestureDirection.ORTHOGONAL;
|
||||
public var maxDirectionalOffset:Number = Gesture.DEFAULT_SLOP << 1;
|
||||
|
||||
/**
|
||||
* The duration of period (in milliseconds) in which SwipeGesture must be recognized.
|
||||
* If gesture is not recognized during this period it fails. Default value is 500 (half a
|
||||
* second) and generally should not be changed. You can change it though for some special
|
||||
* cases, most likely together with <code>minVelocity</code> and <code>minOffset</code>
|
||||
* to achieve really custom behavior.
|
||||
*
|
||||
* @default 500
|
||||
*
|
||||
* @see #minVelocity
|
||||
* @see #minOffset
|
||||
*/
|
||||
public var maxDuration:uint = MAX_DURATION;
|
||||
|
||||
/**
|
||||
* Minimum offset (in pixels) for gesture to be recognized.
|
||||
* Default value is <code>Capabilities.screenDPI / 6</code> and generally should not
|
||||
* be changed.
|
||||
*/
|
||||
public var minOffset:Number = MIN_OFFSET;
|
||||
|
||||
/**
|
||||
* Minimum velocity (in pixels per millisecond) for gesture to be recognized.
|
||||
* Default value is <code>2 * minOffset / maxDuration</code> and generally should not
|
||||
* be changed.
|
||||
*
|
||||
* @see #minOffset
|
||||
* @see #minDuration
|
||||
*/
|
||||
public var minVelocity:Number = MIN_VELOCITY;
|
||||
|
||||
protected var _offset:Point = new Point();
|
||||
protected var _startTime:int;
|
||||
protected var _noDirection:Boolean;
|
||||
protected var _avrgVel:Point = new Point();
|
||||
protected var _prevAvrgVel:Point = new Point();
|
||||
protected var _decelerationCounter:uint = 0;
|
||||
protected var _timer:Timer;
|
||||
|
||||
|
||||
public function SwipeGesture(target:InteractiveObject = null)
|
||||
public function SwipeGesture(target:Object = null)
|
||||
{
|
||||
super(target);
|
||||
}
|
||||
|
@ -50,14 +100,14 @@ package org.gestouch.gestures
|
|||
return SwipeGesture;
|
||||
}
|
||||
|
||||
|
||||
|
||||
override public function reset():void
|
||||
{
|
||||
_startTime = 0;
|
||||
_offset.x = 0;
|
||||
_offset.y = 0;
|
||||
_decelerationCounter = 0;
|
||||
|
||||
_timer.reset();
|
||||
|
||||
super.reset();
|
||||
}
|
||||
|
||||
|
@ -70,12 +120,26 @@ package org.gestouch.gestures
|
|||
//
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
override protected function eventTypeIsValid(type:String):Boolean
|
||||
{
|
||||
return type == SwipeGestureEvent.GESTURE_SWIPE || super.eventTypeIsValid(type);
|
||||
}
|
||||
|
||||
|
||||
override protected function preinit():void
|
||||
{
|
||||
super.preinit();
|
||||
|
||||
_timer = new Timer(maxDuration, 1);
|
||||
_timer.addEventListener(TimerEvent.TIMER_COMPLETE, timer_timerCompleteHandler);
|
||||
}
|
||||
|
||||
|
||||
override protected function onTouchBegin(touch:Touch):void
|
||||
{
|
||||
if (touchesCount > numTouchesRequired)
|
||||
{
|
||||
//TODO: or ignore?
|
||||
setState(GestureState.FAILED);
|
||||
failOrIgnoreTouch(touch);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -83,6 +147,10 @@ package org.gestouch.gestures
|
|||
{
|
||||
// Because we want to fail as quick as possible
|
||||
_startTime = touch.time;
|
||||
|
||||
_timer.reset();
|
||||
_timer.delay = maxDuration;
|
||||
_timer.start();
|
||||
}
|
||||
if (touchesCount == numTouchesRequired)
|
||||
{
|
||||
|
@ -100,6 +168,10 @@ package org.gestouch.gestures
|
|||
if (touchesCount < numTouchesRequired)
|
||||
return;
|
||||
|
||||
var totalTime:int = touch.time - _startTime;
|
||||
if (totalTime == 0)
|
||||
return;//It was somehow THAT MUCH performant on one Android tablet
|
||||
|
||||
var prevCentralPointX:Number = _centralPoint.x;
|
||||
var prevCentralPointY:Number = _centralPoint.y;
|
||||
updateCentralPoint();
|
||||
|
@ -107,43 +179,19 @@ package org.gestouch.gestures
|
|||
_offset.x = _centralPoint.x - _location.x;
|
||||
_offset.y = _centralPoint.y - _location.y;
|
||||
var offsetLength:Number = _offset.length;
|
||||
|
||||
if (offsetLength < slop)
|
||||
{
|
||||
// no need in processing - we're in the very beginning of movement
|
||||
return;
|
||||
}
|
||||
|
||||
// average velocity (total offset to total duration)
|
||||
_prevAvrgVel.x = _avrgVel.x;
|
||||
_prevAvrgVel.y = _avrgVel.y;
|
||||
var absPrevAvrgVel:Number = _prevAvrgVel.length;
|
||||
var totalTime:int = touch.time - _startTime;
|
||||
_avrgVel.x = _offset.x / totalTime;
|
||||
_avrgVel.y = _offset.y / totalTime;
|
||||
var avrgVel:Number = _avrgVel.length;
|
||||
|
||||
|
||||
if (avrgVel * 0.95 < absPrevAvrgVel)
|
||||
{
|
||||
_decelerationCounter++;
|
||||
}
|
||||
if (_decelerationCounter > 5 || avrgVel < 0.1)
|
||||
{
|
||||
setState(GestureState.FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_noDirection)
|
||||
{
|
||||
// We should quickly fail if we have noticable deceleration
|
||||
// or first movement happend way later after touch
|
||||
|
||||
if (avrgVel >= minVelocity && (minOffset != minOffset || offsetLength >= minOffset))
|
||||
if (avrgVel >= minVelocity || offsetLength >= minOffset)
|
||||
{
|
||||
if (setState(GestureState.RECOGNIZED) && hasEventListener(SwipeGestureEvent.GESTURE_SWIPE))
|
||||
{
|
||||
_localLocation = target.globalToLocal(_location);//refresh local location in case target moved
|
||||
_localLocation = targetAdapter.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));
|
||||
}
|
||||
|
@ -161,25 +209,25 @@ package org.gestouch.gestures
|
|||
{
|
||||
var absOffsetX:Number = _offset.x > 0 ? _offset.x : -_offset.x;
|
||||
|
||||
if ((SwipeGestureDirection.HORIZONTAL & direction) == 0)
|
||||
{
|
||||
// horizontal velocity is greater then vertical, but we're not interested in any horizontal direction
|
||||
setState(GestureState.FAILED);
|
||||
}
|
||||
else if (recentOffsetX < 0 && (direction & SwipeGestureDirection.LEFT) == 0 ||
|
||||
recentOffsetX > 0 && (direction & SwipeGestureDirection.RIGHT) == 0 ||
|
||||
Math.abs(_offset.y) > maxDirectionalOffset)
|
||||
if ((recentOffsetX < 0 && (direction & SwipeGestureDirection.LEFT) == 0) ||
|
||||
(recentOffsetX > 0 && (direction & SwipeGestureDirection.RIGHT) == 0) ||
|
||||
Math.abs(Math.atan(_offset.y/_offset.x)) > ANGLE)
|
||||
{
|
||||
// movement in opposite direction
|
||||
// or too much diagonally
|
||||
setState(GestureState.FAILED);
|
||||
|
||||
// Give some tolerance for accidental offset on finger press (slop)
|
||||
if (offsetLength > slop || slop != slop)//faster isNaN()
|
||||
{
|
||||
setState(GestureState.FAILED);
|
||||
}
|
||||
}
|
||||
else if (absVelX >= minVelocity && (minOffset != minOffset || absOffsetX >= minOffset))
|
||||
else if (absVelX >= minVelocity || absOffsetX >= minOffset)
|
||||
{
|
||||
_offset.y = 0;
|
||||
if (setState(GestureState.RECOGNIZED) && hasEventListener(SwipeGestureEvent.GESTURE_SWIPE))
|
||||
{
|
||||
_localLocation = target.globalToLocal(_location);//refresh local location in case target moved
|
||||
_localLocation = targetAdapter.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));
|
||||
}
|
||||
|
@ -189,36 +237,37 @@ package org.gestouch.gestures
|
|||
{
|
||||
var absOffsetY:Number = _offset.y > 0 ? _offset.y : -_offset.y;
|
||||
|
||||
if ((SwipeGestureDirection.VERTICAL & direction) == 0)
|
||||
{
|
||||
// horizontal velocity is greater then vertical, but we're not interested in any horizontal direction
|
||||
setState(GestureState.FAILED);
|
||||
}
|
||||
else if (recentOffsetY < 0 && (direction & SwipeGestureDirection.UP) == 0 ||
|
||||
recentOffsetY > 0 && (direction & SwipeGestureDirection.DOWN) == 0 ||
|
||||
Math.abs(_offset.x) > maxDirectionalOffset)
|
||||
if ((recentOffsetY < 0 && (direction & SwipeGestureDirection.UP) == 0) ||
|
||||
(recentOffsetY > 0 && (direction & SwipeGestureDirection.DOWN) == 0) ||
|
||||
Math.abs(Math.atan(_offset.x/_offset.y)) > ANGLE)
|
||||
{
|
||||
// movement in opposite direction
|
||||
// or too much diagonally
|
||||
setState(GestureState.FAILED);
|
||||
|
||||
// Give some tolerance for accidental offset on finger press (slop)
|
||||
if (offsetLength > slop || slop != slop)//faster isNaN()
|
||||
{
|
||||
setState(GestureState.FAILED);
|
||||
}
|
||||
}
|
||||
else if (absVelY >= minVelocity && (minOffset != minOffset || absOffsetY >= minOffset))
|
||||
else if (absVelY >= minVelocity || absOffsetY >= minOffset)
|
||||
{
|
||||
_offset.x = 0;
|
||||
if (setState(GestureState.RECOGNIZED) && hasEventListener(SwipeGestureEvent.GESTURE_SWIPE))
|
||||
{
|
||||
_localLocation = target.globalToLocal(_location);//refresh local location in case target moved
|
||||
_localLocation = targetAdapter.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));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
// Give some tolerance for accidental offset on finger press (slop)
|
||||
else if (offsetLength > slop || slop != slop)//faster isNaN()
|
||||
{
|
||||
setState(GestureState.FAILED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override protected function onTouchEnd(touch:Touch):void
|
||||
|
@ -234,10 +283,27 @@ package org.gestouch.gestures
|
|||
{
|
||||
if (hasEventListener(SwipeGestureEvent.GESTURE_SWIPE))
|
||||
{
|
||||
_localLocation = target.globalToLocal(_location);//refresh local location in case target moved
|
||||
_localLocation = targetAdapter.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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Event handlers
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
protected function timer_timerCompleteHandler(event:TimerEvent):void
|
||||
{
|
||||
if (state == GestureState.POSSIBLE)
|
||||
{
|
||||
setState(GestureState.FAILED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,14 +4,16 @@ package org.gestouch.gestures
|
|||
import org.gestouch.core.Touch;
|
||||
import org.gestouch.events.TapGestureEvent;
|
||||
|
||||
import flash.display.InteractiveObject;
|
||||
import flash.events.TimerEvent;
|
||||
import flash.utils.Timer;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @eventType org.gestouch.events.TapGestureEvent
|
||||
*/
|
||||
[Event(name="gestureTap", type="org.gestouch.events.TapGestureEvent")]
|
||||
/**
|
||||
* TODO: check failing conditions (iDevice)
|
||||
*
|
||||
* @author Pavel fljot
|
||||
*/
|
||||
|
@ -19,7 +21,7 @@ package org.gestouch.gestures
|
|||
{
|
||||
public var numTouchesRequired:uint = 1;
|
||||
public var numTapsRequired:uint = 1;
|
||||
public var slop:Number = Gesture.DEFAULT_SLOP;
|
||||
public var slop:Number = Gesture.DEFAULT_SLOP << 2;//iOS has 45px for 132 dpi screen
|
||||
public var maxTapDelay:uint = 400;
|
||||
public var maxTapDuration:uint = 1500;
|
||||
|
||||
|
@ -28,7 +30,7 @@ package org.gestouch.gestures
|
|||
protected var _tapCounter:uint = 0;
|
||||
|
||||
|
||||
public function TapGesture(target:InteractiveObject = null)
|
||||
public function TapGesture(target:Object = null)
|
||||
{
|
||||
super(target);
|
||||
}
|
||||
|
@ -47,17 +49,17 @@ package org.gestouch.gestures
|
|||
return TapGesture;
|
||||
}
|
||||
|
||||
|
||||
|
||||
override public function reset():void
|
||||
{
|
||||
_numTouchesRequiredReached = false;
|
||||
_tapCounter = 0;
|
||||
_timer.reset();
|
||||
|
||||
|
||||
super.reset();
|
||||
}
|
||||
|
||||
|
||||
|
||||
override public function canPreventGesture(preventedGesture:Gesture):Boolean
|
||||
{
|
||||
if (preventedGesture is TapGesture &&
|
||||
|
@ -77,6 +79,12 @@ package org.gestouch.gestures
|
|||
//
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
override protected function eventTypeIsValid(type:String):Boolean
|
||||
{
|
||||
return type == TapGestureEvent.GESTURE_TAP || super.eventTypeIsValid(type);
|
||||
}
|
||||
|
||||
|
||||
override protected function preinit():void
|
||||
{
|
||||
super.preinit();
|
||||
|
@ -90,9 +98,7 @@ package org.gestouch.gestures
|
|||
{
|
||||
if (touchesCount > numTouchesRequired)
|
||||
{
|
||||
// We put more fingers then required at the same time,
|
||||
// so treat that as failed
|
||||
setState(GestureState.FAILED);
|
||||
failOrIgnoreTouch(touch);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -105,7 +111,8 @@ package org.gestouch.gestures
|
|||
|
||||
if (touchesCount == numTouchesRequired)
|
||||
{
|
||||
_numTouchesRequiredReached = true;
|
||||
_numTouchesRequiredReached = true;
|
||||
updateLocation();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,7 +130,6 @@ package org.gestouch.gestures
|
|||
{
|
||||
if (!_numTouchesRequiredReached)
|
||||
{
|
||||
//TODO: check this condition on iDevice
|
||||
setState(GestureState.FAILED);
|
||||
}
|
||||
else if (touchesCount == 0)
|
||||
|
@ -136,7 +142,6 @@ package org.gestouch.gestures
|
|||
|
||||
if (_tapCounter == numTapsRequired)
|
||||
{
|
||||
updateLocation();
|
||||
if (setState(GestureState.RECOGNIZED) && hasEventListener(TapGestureEvent.GESTURE_TAP))
|
||||
{
|
||||
dispatchEvent(new TapGestureEvent(TapGestureEvent.GESTURE_TAP, false, false, GestureState.RECOGNIZED,
|
||||
|
|
|
@ -3,12 +3,14 @@ package org.gestouch.gestures
|
|||
import org.gestouch.core.GestureState;
|
||||
import org.gestouch.core.Touch;
|
||||
import org.gestouch.events.TransformGestureEvent;
|
||||
import org.gestouch.utils.GestureUtils;
|
||||
|
||||
import flash.display.InteractiveObject;
|
||||
import flash.geom.Point;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @eventType org.gestouch.events.TransformGestureEvent
|
||||
*/
|
||||
[Event(name="gestureTransform", type="org.gestouch.events.TransformGestureEvent")]
|
||||
/**
|
||||
* @author Pavel fljot
|
||||
|
@ -22,7 +24,7 @@ package org.gestouch.gestures
|
|||
protected var _transformVector:Point;
|
||||
|
||||
|
||||
public function TransformGesture(target:InteractiveObject = null)
|
||||
public function TransformGesture(target:Object = null)
|
||||
{
|
||||
super(target);
|
||||
}
|
||||
|
@ -59,12 +61,17 @@ package org.gestouch.gestures
|
|||
//
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
override protected function eventTypeIsValid(type:String):Boolean
|
||||
{
|
||||
return type == TransformGestureEvent.GESTURE_TRANSFORM || super.eventTypeIsValid(type);
|
||||
}
|
||||
|
||||
|
||||
override protected function onTouchBegin(touch:Touch):void
|
||||
{
|
||||
if (touchesCount > 2)
|
||||
{
|
||||
//TODO: to ignore or to keep this touch somewhere?
|
||||
ignoreTouch(touch);
|
||||
failOrIgnoreTouch(touch);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -97,51 +104,60 @@ package org.gestouch.gestures
|
|||
var prevLocation:Point = _location.clone();
|
||||
updateLocation();
|
||||
|
||||
var recognized:Boolean = true;
|
||||
var currTransformVector:Point;
|
||||
|
||||
if (state == GestureState.POSSIBLE && slop > 0 && touch.locationOffset.length < slop)
|
||||
if (state == GestureState.POSSIBLE)
|
||||
{
|
||||
recognized = false;
|
||||
if (slop > 0 && touch.locationOffset.length < slop)
|
||||
{
|
||||
// Not recognized yet
|
||||
if (_touch2)
|
||||
{
|
||||
// Recalculate _transformVector to avoid initial "jump" on recognize
|
||||
_transformVector = _touch2.location.subtract(_touch1.location);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (recognized)
|
||||
if (_touch2 && !currTransformVector)
|
||||
{
|
||||
var prevLocalLocation:Point;
|
||||
var offsetX:Number = _location.x - prevLocation.x;
|
||||
var offsetY:Number = _location.y - prevLocation.y;
|
||||
var scale:Number = 1;
|
||||
var rotation:Number = 0;
|
||||
if (_touch2)
|
||||
currTransformVector = _touch2.location.subtract(_touch1.location);
|
||||
}
|
||||
|
||||
var prevLocalLocation:Point;
|
||||
var offsetX:Number = _location.x - prevLocation.x;
|
||||
var offsetY:Number = _location.y - prevLocation.y;
|
||||
var scale:Number = 1;
|
||||
var rotation:Number = 0;
|
||||
if (_touch2)
|
||||
{
|
||||
rotation = Math.atan2(currTransformVector.y, currTransformVector.x) - Math.atan2(_transformVector.y, _transformVector.x);
|
||||
scale = currTransformVector.length / _transformVector.length;
|
||||
_transformVector = _touch2.location.subtract(_touch1.location);
|
||||
}
|
||||
|
||||
|
||||
if (state == GestureState.POSSIBLE)
|
||||
{
|
||||
if (setState(GestureState.BEGAN) && hasEventListener(TransformGestureEvent.GESTURE_TRANSFORM))
|
||||
{
|
||||
var currTransformVector:Point = _touch2.location.subtract(_touch1.location);
|
||||
rotation = Math.atan2(currTransformVector.y, currTransformVector.x) - Math.atan2(_transformVector.y, _transformVector.x);
|
||||
rotation *= GestureUtils.RADIANS_TO_DEGREES;
|
||||
scale = currTransformVector.length / _transformVector.length;
|
||||
_transformVector = _touch2.location.subtract(_touch1.location);
|
||||
// Note that we dispatch previous location point which gives a way to perform
|
||||
// accurate UI redraw. See examples project for more info.
|
||||
prevLocalLocation = targetAdapter.globalToLocal(prevLocation);
|
||||
dispatchEvent(new TransformGestureEvent(TransformGestureEvent.GESTURE_TRANSFORM, false, false, GestureState.BEGAN,
|
||||
prevLocation.x, prevLocation.y, prevLocalLocation.x, prevLocalLocation.y, scale, scale, rotation, offsetX, offsetY));
|
||||
}
|
||||
|
||||
|
||||
if (state == GestureState.POSSIBLE)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (setState(GestureState.CHANGED) && hasEventListener(TransformGestureEvent.GESTURE_TRANSFORM))
|
||||
{
|
||||
if (setState(GestureState.BEGAN) && hasEventListener(TransformGestureEvent.GESTURE_TRANSFORM))
|
||||
{
|
||||
// Note that we dispatch previous location point which gives a way to perform
|
||||
// accurate UI redraw. See examples project for more info.
|
||||
prevLocalLocation = target.globalToLocal(prevLocation);
|
||||
dispatchEvent(new TransformGestureEvent(TransformGestureEvent.GESTURE_TRANSFORM, false, false, GestureState.BEGAN,
|
||||
prevLocation.x, prevLocation.y, prevLocalLocation.x, prevLocalLocation.y, scale, scale, rotation, offsetX, offsetY));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (setState(GestureState.CHANGED) && hasEventListener(TransformGestureEvent.GESTURE_TRANSFORM))
|
||||
{
|
||||
// Note that we dispatch previous location point which gives a way to perform
|
||||
// accurate UI redraw. See examples project for more info.
|
||||
prevLocalLocation = target.globalToLocal(prevLocation);
|
||||
dispatchEvent(new TransformGestureEvent(TransformGestureEvent.GESTURE_TRANSFORM, false, false, GestureState.CHANGED,
|
||||
prevLocation.x, prevLocation.y, prevLocalLocation.x, prevLocalLocation.y, scale, scale, rotation, offsetX, offsetY));
|
||||
}
|
||||
// Note that we dispatch previous location point which gives a way to perform
|
||||
// accurate UI redraw. See examples project for more info.
|
||||
prevLocalLocation = targetAdapter.globalToLocal(prevLocation);
|
||||
dispatchEvent(new TransformGestureEvent(TransformGestureEvent.GESTURE_TRANSFORM, false, false, GestureState.CHANGED,
|
||||
prevLocation.x, prevLocation.y, prevLocalLocation.x, prevLocalLocation.y, scale, scale, rotation, offsetX, offsetY));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,27 +4,30 @@ package org.gestouch.gestures
|
|||
import org.gestouch.core.Touch;
|
||||
import org.gestouch.events.ZoomGestureEvent;
|
||||
|
||||
import flash.display.InteractiveObject;
|
||||
import flash.geom.Point;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @eventType org.gestouch.events.ZoomGestureEvent
|
||||
*/
|
||||
[Event(name="gestureZoom", type="org.gestouch.events.ZoomGestureEvent")]
|
||||
/**
|
||||
* TODO:
|
||||
* -check native behavior on iDevice
|
||||
*
|
||||
* @author Pavel fljot
|
||||
*/
|
||||
public class ZoomGesture extends Gesture
|
||||
{
|
||||
public var slop:Number = Gesture.DEFAULT_SLOP >> 1;
|
||||
public var slop:Number = Gesture.DEFAULT_SLOP;
|
||||
public var lockAspectRatio:Boolean = true;
|
||||
|
||||
protected var _touch1:Touch;
|
||||
protected var _touch2:Touch;
|
||||
protected var _transformVector:Point;
|
||||
protected var _initialDistance:Number;
|
||||
|
||||
|
||||
public function ZoomGesture(target:InteractiveObject = null)
|
||||
public function ZoomGesture(target:Object = null)
|
||||
{
|
||||
super(target);
|
||||
}
|
||||
|
@ -52,12 +55,17 @@ package org.gestouch.gestures
|
|||
//
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
override protected function eventTypeIsValid(type:String):Boolean
|
||||
{
|
||||
return type == ZoomGestureEvent.GESTURE_ZOOM || super.eventTypeIsValid(type);
|
||||
}
|
||||
|
||||
|
||||
override protected function onTouchBegin(touch:Touch):void
|
||||
{
|
||||
if (touchesCount > 2)
|
||||
{
|
||||
//TODO
|
||||
ignoreTouch(touch);
|
||||
failOrIgnoreTouch(touch);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -70,6 +78,7 @@ package org.gestouch.gestures
|
|||
_touch2 = touch;
|
||||
|
||||
_transformVector = _touch2.location.subtract(_touch1.location);
|
||||
_initialDistance = _transformVector.length;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,48 +88,59 @@ package org.gestouch.gestures
|
|||
if (touchesCount < 2)
|
||||
return;
|
||||
|
||||
var recognized:Boolean = true;
|
||||
var currTransformVector:Point = _touch2.location.subtract(_touch1.location);
|
||||
var scaleX:Number;
|
||||
var scaleY:Number;
|
||||
|
||||
if (state == GestureState.POSSIBLE && slop > 0 && touch.locationOffset.length < slop)
|
||||
if (state == GestureState.POSSIBLE)
|
||||
{
|
||||
recognized = false;
|
||||
const d:Number = currTransformVector.length - _initialDistance;
|
||||
const absD:Number = d >= 0 ? d : -d;
|
||||
if (absD < slop)
|
||||
{
|
||||
// Not recognized yet
|
||||
return;
|
||||
}
|
||||
|
||||
if (slop > 0)
|
||||
{
|
||||
// adjust _transformVector to avoid initial "jump"
|
||||
const slopVector:Point = currTransformVector.clone();
|
||||
slopVector.normalize(_initialDistance + (d >= 0 ? slop : -slop));
|
||||
_transformVector = slopVector;
|
||||
}
|
||||
}
|
||||
|
||||
if (recognized)
|
||||
|
||||
if (lockAspectRatio)
|
||||
{
|
||||
var currTransformVector:Point = _touch2.location.subtract(_touch1.location);
|
||||
var scaleX:Number;
|
||||
var scaleY:Number;
|
||||
if (lockAspectRatio)
|
||||
scaleX = scaleY = currTransformVector.length / _transformVector.length;
|
||||
}
|
||||
else
|
||||
{
|
||||
scaleX = currTransformVector.x / _transformVector.x;
|
||||
scaleY = currTransformVector.y / _transformVector.y;
|
||||
}
|
||||
|
||||
_transformVector.x = currTransformVector.x;
|
||||
_transformVector.y = currTransformVector.y;
|
||||
|
||||
updateLocation();
|
||||
|
||||
if (state == GestureState.POSSIBLE)
|
||||
{
|
||||
if (setState(GestureState.BEGAN) && hasEventListener(ZoomGestureEvent.GESTURE_ZOOM))
|
||||
{
|
||||
scaleX = scaleY = currTransformVector.length / _transformVector.length;
|
||||
dispatchEvent(new ZoomGestureEvent(ZoomGestureEvent.GESTURE_ZOOM, false, false, GestureState.BEGAN,
|
||||
_location.x, _location.y, _localLocation.x, _localLocation.y, scaleX, scaleY));
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
if (setState(GestureState.CHANGED) && hasEventListener(ZoomGestureEvent.GESTURE_ZOOM))
|
||||
{
|
||||
scaleX = currTransformVector.x / _transformVector.x;
|
||||
scaleY = currTransformVector.y / _transformVector.y;
|
||||
}
|
||||
|
||||
_transformVector.x = currTransformVector.x;
|
||||
_transformVector.y = currTransformVector.y;
|
||||
|
||||
updateLocation();
|
||||
|
||||
if (state == GestureState.POSSIBLE)
|
||||
{
|
||||
if (setState(GestureState.BEGAN) && hasEventListener(ZoomGestureEvent.GESTURE_ZOOM))
|
||||
{
|
||||
dispatchEvent(new ZoomGestureEvent(ZoomGestureEvent.GESTURE_ZOOM, false, false, GestureState.BEGAN,
|
||||
_location.x, _location.y, _localLocation.x, _localLocation.y, scaleX, scaleY));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (setState(GestureState.CHANGED) && hasEventListener(ZoomGestureEvent.GESTURE_ZOOM))
|
||||
{
|
||||
dispatchEvent(new ZoomGestureEvent(ZoomGestureEvent.GESTURE_ZOOM, false, false, GestureState.CHANGED,
|
||||
_location.x, _location.y, _localLocation.x, _localLocation.y, scaleX, scaleY));
|
||||
}
|
||||
dispatchEvent(new ZoomGestureEvent(ZoomGestureEvent.GESTURE_ZOOM, false, false, GestureState.CHANGED,
|
||||
_location.x, _location.y, _localLocation.x, _localLocation.y, scaleX, scaleY));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -141,7 +161,7 @@ package org.gestouch.gestures
|
|||
else if (state == GestureState.POSSIBLE)
|
||||
{
|
||||
setState(GestureState.FAILED);
|
||||
}
|
||||
}
|
||||
}
|
||||
else//== 1
|
||||
{
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
package org.gestouch.input
|
||||
{
|
||||
import org.gestouch.core.IGesturesManager;
|
||||
import org.gestouch.core.IInputAdapter;
|
||||
import org.gestouch.core.ITouchesManager;
|
||||
|
||||
|
||||
/**
|
||||
* @author Pavel fljot
|
||||
*/
|
||||
public class AbstractInputAdapter implements IInputAdapter
|
||||
{
|
||||
protected var _touchesManager:ITouchesManager;
|
||||
protected var _gesturesManager:IGesturesManager;
|
||||
|
||||
|
||||
public function AbstractInputAdapter()
|
||||
{
|
||||
if (Object(this).constructor == AbstractInputAdapter)
|
||||
{
|
||||
throw new Error("This is abstract class and should not be directly instantiated.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function set touchesManager(value:ITouchesManager):void
|
||||
{
|
||||
_touchesManager = value;
|
||||
}
|
||||
|
||||
|
||||
public function set gesturesManager(value:IGesturesManager):void
|
||||
{
|
||||
_gesturesManager = value;
|
||||
}
|
||||
|
||||
|
||||
[Abstract]
|
||||
public function init():void
|
||||
{
|
||||
throw new Error("This is abstract method.");
|
||||
}
|
||||
|
||||
|
||||
[Abstract]
|
||||
public function dispose():void
|
||||
{
|
||||
throw new Error("This is abstract method.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,136 +0,0 @@
|
|||
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;
|
||||
|
||||
|
||||
/**
|
||||
* @author Pavel fljot
|
||||
*/
|
||||
public class MouseInputAdapter extends AbstractInputAdapter
|
||||
{
|
||||
private static const PRIMARY_TOUCH_POINT_ID:uint = 0;
|
||||
|
||||
protected var _stage:Stage;
|
||||
|
||||
|
||||
public function MouseInputAdapter(stage:Stage)
|
||||
{
|
||||
super();
|
||||
|
||||
if (!stage)
|
||||
{
|
||||
throw new Error("Stage must be not null.");
|
||||
}
|
||||
|
||||
_stage = stage;
|
||||
}
|
||||
|
||||
|
||||
override public function init():void
|
||||
{
|
||||
_stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler, true);
|
||||
_stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);// to catch with EventPhase.AT_TARGET
|
||||
}
|
||||
|
||||
|
||||
override public function dispose():void
|
||||
{
|
||||
_stage.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler, true);
|
||||
_stage.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
|
||||
uninstallStageListeners();
|
||||
}
|
||||
|
||||
|
||||
protected function installStageListeners():void
|
||||
{
|
||||
// Maximum priority to prevent event hijacking
|
||||
_stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler, true, int.MAX_VALUE);
|
||||
_stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler, false, int.MAX_VALUE);
|
||||
_stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler, true, int.MAX_VALUE);
|
||||
_stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler, false, int.MAX_VALUE);
|
||||
}
|
||||
|
||||
|
||||
protected function uninstallStageListeners():void
|
||||
{
|
||||
_stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler, true);
|
||||
_stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
|
||||
_stage.removeEventListener(MouseEvent.MOUSE_UP, mouseUpHandler, true);
|
||||
_stage.removeEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
|
||||
}
|
||||
|
||||
|
||||
protected function mouseDownHandler(event:MouseEvent):void
|
||||
{
|
||||
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();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
|
||||
// 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);
|
||||
|
||||
if (_touchesManager.activeTouchesCount == 0)
|
||||
{
|
||||
uninstallStageListeners();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
224
src/org/gestouch/input/NativeInputAdapter.as
Normal file
224
src/org/gestouch/input/NativeInputAdapter.as
Normal file
|
@ -0,0 +1,224 @@
|
|||
package org.gestouch.input
|
||||
{
|
||||
import org.gestouch.core.IInputAdapter;
|
||||
import org.gestouch.core.TouchesManager;
|
||||
import org.gestouch.core.gestouch_internal;
|
||||
|
||||
import flash.display.InteractiveObject;
|
||||
import flash.display.Stage;
|
||||
import flash.events.EventPhase;
|
||||
import flash.events.MouseEvent;
|
||||
import flash.events.TouchEvent;
|
||||
import flash.ui.Multitouch;
|
||||
import flash.ui.MultitouchInputMode;
|
||||
|
||||
|
||||
/**
|
||||
* @author Pavel fljot
|
||||
*/
|
||||
public class NativeInputAdapter implements IInputAdapter
|
||||
{
|
||||
protected static const MOUSE_TOUCH_POINT_ID:uint = 0;
|
||||
|
||||
protected var _stage:Stage;
|
||||
protected var _explicitlyHandleTouchEvents:Boolean;
|
||||
protected var _explicitlyHandleMouseEvents:Boolean;
|
||||
|
||||
use namespace gestouch_internal;
|
||||
|
||||
|
||||
{
|
||||
Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
|
||||
}
|
||||
|
||||
|
||||
public function NativeInputAdapter(stage:Stage,
|
||||
explicitlyHandleTouchEvents:Boolean = false,
|
||||
explicitlyHandleMouseEvents:Boolean = false)
|
||||
{
|
||||
super();
|
||||
|
||||
if (!stage)
|
||||
{
|
||||
throw new ArgumentError("Stage must be not null.");
|
||||
}
|
||||
|
||||
_stage = stage;
|
||||
|
||||
_explicitlyHandleTouchEvents = explicitlyHandleTouchEvents;
|
||||
_explicitlyHandleMouseEvents = explicitlyHandleMouseEvents;
|
||||
}
|
||||
|
||||
|
||||
protected var _touchesManager:TouchesManager;
|
||||
public function set touchesManager(value:TouchesManager):void
|
||||
{
|
||||
_touchesManager = value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Public methods
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
public function init():void
|
||||
{
|
||||
if (Multitouch.supportsTouchEvents || _explicitlyHandleTouchEvents)
|
||||
{
|
||||
_stage.addEventListener(TouchEvent.TOUCH_BEGIN, touchBeginHandler, true);
|
||||
_stage.addEventListener(TouchEvent.TOUCH_BEGIN, touchBeginHandler, false);
|
||||
_stage.addEventListener(TouchEvent.TOUCH_MOVE, touchMoveHandler, true);
|
||||
_stage.addEventListener(TouchEvent.TOUCH_MOVE, touchMoveHandler, false);
|
||||
// Maximum priority to prevent event hijacking and loosing the touch
|
||||
_stage.addEventListener(TouchEvent.TOUCH_END, touchEndHandler, true, int.MAX_VALUE);
|
||||
_stage.addEventListener(TouchEvent.TOUCH_END, touchEndHandler, false, int.MAX_VALUE);
|
||||
}
|
||||
|
||||
if (!Multitouch.supportsTouchEvents || _explicitlyHandleMouseEvents)
|
||||
{
|
||||
_stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler, true);
|
||||
_stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function onDispose():void
|
||||
{
|
||||
_touchesManager = null;
|
||||
|
||||
_stage.removeEventListener(TouchEvent.TOUCH_BEGIN, touchBeginHandler, true);
|
||||
_stage.removeEventListener(TouchEvent.TOUCH_BEGIN, touchBeginHandler, false);
|
||||
_stage.removeEventListener(TouchEvent.TOUCH_MOVE, touchMoveHandler, true);
|
||||
_stage.removeEventListener(TouchEvent.TOUCH_MOVE, touchMoveHandler, false);
|
||||
_stage.removeEventListener(TouchEvent.TOUCH_END, touchEndHandler, true);
|
||||
_stage.removeEventListener(TouchEvent.TOUCH_END, touchEndHandler, false);
|
||||
|
||||
_stage.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler, true);
|
||||
_stage.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler, false);
|
||||
unstallMouseListeners();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Private methods
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
protected function installMouseListeners():void
|
||||
{
|
||||
_stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler, true);
|
||||
_stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler, false);
|
||||
// Maximum priority to prevent event hijacking
|
||||
_stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler, true, int.MAX_VALUE);
|
||||
_stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler, false, int.MAX_VALUE);
|
||||
}
|
||||
|
||||
|
||||
protected function unstallMouseListeners():void
|
||||
{
|
||||
_stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler, true);
|
||||
_stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler, false);
|
||||
// Maximum priority to prevent event hijacking
|
||||
_stage.removeEventListener(MouseEvent.MOUSE_UP, mouseUpHandler, true);
|
||||
_stage.removeEventListener(MouseEvent.MOUSE_UP, mouseUpHandler, false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Event handlers
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
protected function touchBeginHandler(event:TouchEvent):void
|
||||
{
|
||||
// We listen in EventPhase.CAPTURE_PHASE or EventPhase.AT_TARGET
|
||||
// (to catch on empty stage) phases only
|
||||
if (event.eventPhase == EventPhase.BUBBLING_PHASE)
|
||||
return;
|
||||
|
||||
_touchesManager.onTouchBegin(event.touchPointID, event.stageX, event.stageY, event.target as InteractiveObject);
|
||||
}
|
||||
|
||||
|
||||
protected function touchMoveHandler(event:TouchEvent):void
|
||||
{
|
||||
// We listen in EventPhase.CAPTURE_PHASE or EventPhase.AT_TARGET
|
||||
// (to catch on empty stage) phases only
|
||||
if (event.eventPhase == EventPhase.BUBBLING_PHASE)
|
||||
return;
|
||||
|
||||
_touchesManager.onTouchMove(event.touchPointID, event.stageX, event.stageY);
|
||||
}
|
||||
|
||||
|
||||
protected function touchEndHandler(event:TouchEvent):void
|
||||
{
|
||||
// We listen in EventPhase.CAPTURE_PHASE or EventPhase.AT_TARGET
|
||||
// (to catch on empty stage) phases only
|
||||
if (event.eventPhase == EventPhase.BUBBLING_PHASE)
|
||||
return;
|
||||
|
||||
if (event.hasOwnProperty("isTouchPointCanceled") && event["isTouchPointCanceled"])
|
||||
{
|
||||
_touchesManager.onTouchCancel(event.touchPointID, event.stageX, event.stageY);
|
||||
}
|
||||
else
|
||||
{
|
||||
_touchesManager.onTouchEnd(event.touchPointID, event.stageX, event.stageY);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function mouseDownHandler(event:MouseEvent):void
|
||||
{
|
||||
// We listen in EventPhase.CAPTURE_PHASE or EventPhase.AT_TARGET
|
||||
// (to catch on empty stage) phases only
|
||||
if (event.eventPhase == EventPhase.BUBBLING_PHASE)
|
||||
return;
|
||||
|
||||
const touchAccepted:Boolean = _touchesManager.onTouchBegin(MOUSE_TOUCH_POINT_ID, event.stageX, event.stageY, event.target as InteractiveObject);
|
||||
|
||||
if (touchAccepted)
|
||||
{
|
||||
installMouseListeners();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function mouseMoveHandler(event:MouseEvent):void
|
||||
{
|
||||
// We listen in EventPhase.CAPTURE_PHASE or EventPhase.AT_TARGET
|
||||
// (to catch on empty stage) phases only
|
||||
if (event.eventPhase == EventPhase.BUBBLING_PHASE)
|
||||
return;
|
||||
|
||||
_touchesManager.onTouchMove(MOUSE_TOUCH_POINT_ID, event.stageX, event.stageY);
|
||||
}
|
||||
|
||||
|
||||
protected function mouseUpHandler(event:MouseEvent):void
|
||||
{
|
||||
// We listen in EventPhase.CAPTURE_PHASE or EventPhase.AT_TARGET
|
||||
// (to catch on empty stage) phases only
|
||||
if (event.eventPhase == EventPhase.BUBBLING_PHASE)
|
||||
return;
|
||||
|
||||
_touchesManager.onTouchEnd(MOUSE_TOUCH_POINT_ID, event.stageX, event.stageY);
|
||||
|
||||
if (_touchesManager.activeTouchesCount == 0)
|
||||
{
|
||||
unstallMouseListeners();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,17 +1,33 @@
|
|||
package org.gestouch.input
|
||||
{
|
||||
import org.gestouch.input.AbstractInputAdapter;
|
||||
import org.gestouch.core.IInputAdapter;
|
||||
import org.gestouch.core.TouchesManager;
|
||||
|
||||
|
||||
/**
|
||||
* TODO: You can implement your own TUIO Input Adapter (and supply touchesManager with
|
||||
* touch info), but IMHO it is way easier to use NativeInputAdapter and any TUIO library
|
||||
* and manually dispatch native TouchEvents using DisplayObjectContainer#getObjectsUnderPoint()
|
||||
*
|
||||
* @see NativeInputAdapter
|
||||
* @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/DisplayObjectContainer.html#getObjectsUnderPoint() DisplayObjectContainer#getObjectsUnderPoint()
|
||||
*
|
||||
* @author Pavel fljot
|
||||
*/
|
||||
public class TUIOInputAdapter extends AbstractInputAdapter
|
||||
public class TUIOInputAdapter implements IInputAdapter
|
||||
{
|
||||
public function TUIOInputAdapter()
|
||||
public function init():void
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public function onDispose():void
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public function set touchesManager(value:TouchesManager):void
|
||||
{
|
||||
super();
|
||||
//TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,186 +0,0 @@
|
|||
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;
|
||||
|
||||
|
||||
/**
|
||||
* @author Pavel fljot
|
||||
*/
|
||||
public class TouchInputAdapter extends AbstractInputAdapter
|
||||
{
|
||||
protected var _stage:Stage;
|
||||
/**
|
||||
* The hash map of touches instantiated via TouchEvent.
|
||||
* Used to avoid collisions (double processing) with MouseInputAdapter.
|
||||
*
|
||||
* TODO: any better way?
|
||||
*/
|
||||
protected var _touchesMap:Object = {};
|
||||
|
||||
{
|
||||
Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
|
||||
}
|
||||
|
||||
|
||||
public function TouchInputAdapter(stage:Stage)
|
||||
{
|
||||
super();
|
||||
|
||||
if (!stage)
|
||||
{
|
||||
throw new Error("Stage must be not null.");
|
||||
}
|
||||
|
||||
_stage = stage;
|
||||
}
|
||||
|
||||
|
||||
override public function init():void
|
||||
{
|
||||
_stage.addEventListener(TouchEvent.TOUCH_BEGIN, touchBeginHandler, true);
|
||||
_stage.addEventListener(TouchEvent.TOUCH_BEGIN, touchBeginHandler);// to catch with EventPhase.AT_TARGET
|
||||
}
|
||||
|
||||
|
||||
override public function dispose():void
|
||||
{
|
||||
_stage.removeEventListener(TouchEvent.TOUCH_BEGIN, touchBeginHandler, true);
|
||||
_stage.removeEventListener(TouchEvent.TOUCH_BEGIN, touchBeginHandler);
|
||||
uninstallStageListeners();
|
||||
}
|
||||
|
||||
|
||||
protected function installStageListeners():void
|
||||
{
|
||||
// Maximum priority to prevent event hijacking
|
||||
_stage.addEventListener(TouchEvent.TOUCH_MOVE, touchMoveHandler, true, int.MAX_VALUE);
|
||||
_stage.addEventListener(TouchEvent.TOUCH_MOVE, touchMoveHandler, false, int.MAX_VALUE);
|
||||
_stage.addEventListener(TouchEvent.TOUCH_END, touchEndHandler, true, int.MAX_VALUE);
|
||||
_stage.addEventListener(TouchEvent.TOUCH_END, touchEndHandler, false, int.MAX_VALUE);
|
||||
}
|
||||
|
||||
|
||||
protected function uninstallStageListeners():void
|
||||
{
|
||||
_stage.removeEventListener(TouchEvent.TOUCH_MOVE, touchMoveHandler, true);
|
||||
_stage.removeEventListener(TouchEvent.TOUCH_MOVE, touchMoveHandler);
|
||||
_stage.removeEventListener(TouchEvent.TOUCH_END, touchEndHandler, true);
|
||||
_stage.removeEventListener(TouchEvent.TOUCH_END, touchEndHandler);
|
||||
}
|
||||
|
||||
|
||||
protected function touchBeginHandler(event:TouchEvent):void
|
||||
{
|
||||
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;
|
||||
|
||||
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"))
|
||||
{
|
||||
touch.gestouch_internal::setTime(event["timestamp"]);
|
||||
touch.gestouch_internal::setBeginTime(event["timestamp"]);
|
||||
}
|
||||
else
|
||||
{
|
||||
touch.gestouch_internal::setTime(getTimer());
|
||||
touch.gestouch_internal::setBeginTime(getTimer());
|
||||
}
|
||||
|
||||
_touchesManager.addTouch(touch);
|
||||
_touchesMap[touch.id] = true;
|
||||
|
||||
_gesturesManager.gestouch_internal::onTouchBegin(touch);
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
|
||||
// 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];
|
||||
|
||||
if (_touchesManager.activeTouchesCount == 0)
|
||||
{
|
||||
uninstallStageListeners();
|
||||
}
|
||||
|
||||
// TODO: handle cancelled touch:
|
||||
// if (event.hasOwnProperty("isTouchPointCanceled") && event["isTouchPointCanceled"] && ...
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -1 +1 @@
|
|||
project.version = 0.3.1
|
||||
project.version = 0.4
|
Loading…
Reference in a new issue