Permalink
Browse files

Adding fail and execute methods to the IBehavior interface so they ar…

…e notified during Gesture execution of progress or failure status. Fixes bugs in Gesture around event cache management, and changes the notify methods to pass a clone of the events vector.

Adding documentation to the interfaces and updates to the readme.
  • Loading branch information...
1 parent 7d0ed9b commit 845370e034d0632f0aa39d6ce7231055720ced0c guyinthechair committed Feb 8, 2011
View
6 README
@@ -1,13 +1,13 @@
AS3 Gestures is a library for defining event-based Gestures, and Behaviors to respond to Gestures.
-A Gesture is a collection filtered events. A gesture can listen for as many types of events as it requires, but it only fires when the events occur in a spceific order.
+A Gesture is a collection filtered events. A gesture can listen for as many types of events as it requires, but it only fires when the events occur in a specific order.
When a Gesture is activated, it can respond to any number of Behavior objects. Behaviors implement IBehavior, and will receive a list of the Events that in the order they activated the Gesture.
Example:
-"ABCGesture" listens to events A, B, and C, so ABCGesture will be notified every time events A, B, and C are dispatched. But ABCGesture will only activate if the events occur in order A-B-C. When that happens, ABCGesture will fire to each behavior that is attached to it.
+"ABCGesture" listens to events A, B, and C, so ABCGesture will be notified every time events A, B, and C are dispatched. But ABCGesture will only activate if the events occur in order A-B-C. When that happens, ABCGesture will fire to each behavior attached to it.
-In this example, it can be said that there B has a dependency on A occuring, and C has a dependency on the A-B sequence occuring. This can be modeled thusly:
+In this example, it can be said that state B has a dependency on event A occuring, and C has a dependency on the A-B sequence. This can be modeled thusly:
<A>
<B>
<C/>
View
10 src/org/tinytlf/behaviors/Behavior.as
@@ -4,7 +4,15 @@ package org.tinytlf.behaviors
public class Behavior implements IBehavior
{
- final public function execute(events:Vector.<Event>):void
+ public function fail(event:Event):void
+ {
+ }
+
+ public function execute(event:Event):void
+ {
+ }
+
+ public function activate(events:Vector.<Event>):void
{
if(events.length <= 0)
return;
View
15 src/org/tinytlf/behaviors/IBehavior.as
@@ -4,6 +4,19 @@ package org.tinytlf.behaviors
public interface IBehavior
{
- function execute(events:Vector.<Event>):void;
+ /**
+ * Called when a Gesture has failed to execute based on a rogue Event.
+ */
+ function fail(event:Event):void;
+
+ /**
+ * Called each time an event satisfies a state in the Gesture's HSM.
+ */
+ function execute(event:Event):void;
+
+ /**
+ * Called when the gesture is entirely activated.
+ */
+ function activate(events:Vector.<Event>):void;
}
}
View
78 src/org/tinytlf/gestures/Gesture.as
@@ -2,10 +2,37 @@ package org.tinytlf.gestures
{
import flash.events.*;
import flash.utils.*;
- import org.tinytlf.utils.*;
import org.tinytlf.behaviors.IBehavior;
+ import org.tinytlf.utils.*;
+ /**
+ * The base Gesture implementation is a hierarchical-state-machine. Gestures
+ * are meant to listen to multiple events, and fire when the events have
+ * occurred in the proper sequence.
+ *
+ * To do this, the Gesture Author writes a series of Boolean functions that
+ * each accept a single flash.events.Event instance, returning true or false
+ * if the event meets the Gesture's criteria at that time.
+ *
+ * The function names should be appended to the Gesture's <code>hsm</code>
+ * member as XML nodes, which defines the stateful hierarchy for the
+ * Gesture.
+ *
+ * When the Gesture receives an Event input, it increments an iterator to
+ * the iterator's most recent XML node (or the root if there is no previous
+ * node). The Gesture calls the function with the name at the iterator's
+ * current location.
+ *
+ * When the filter function returns true, the event is cached, behaviors are
+ * notified of the successful filter, and the iterator moves into that
+ * node's child list.
+ *
+ * When the filter function returns false, the Gesture moves on, testing
+ * sibling nodes until one returns true. If no siblings match the supplied
+ * event, the event cache is cleared, the behaviors are notified of the
+ * failure, and the iterator returns to the HSM root state.
+ */
public class Gesture extends EventDispatcher implements IGesture
{
public function addSource(target:IEventDispatcher):void
@@ -15,7 +42,7 @@ package org.tinytlf.gestures
for each(var event:XML in events)
{
type = event.arg.@value.toString();
- target.addEventListener(type, execute, false, 0, true);
+ target.addEventListener(type, execute);
}
resetStates();
}
@@ -51,9 +78,12 @@ package org.tinytlf.gestures
protected var states:XMLList = <>{hsm}</>;
protected var events:Vector.<Event> = new <Event>[];
+ /**
+ * The filter function for this Gesture's HSM.
+ */
protected function execute(event:Event):void
{
- // Don't bother to filter if nobody will respond to us.
+ // Don't bother to filter if no behaviors are attached.
// Edit: What if a behavior is added in the middle of this gesture's
// activation sequence? We still want to track events, yes?
// if(behaviors.length <= 0)
@@ -67,6 +97,8 @@ package org.tinytlf.gestures
var result:Boolean = false;
var name:String;
+ var anySuccess:Boolean = false;
+
for each(var childState:XML in childStates)
{
name = childState.localName();
@@ -92,22 +124,36 @@ package org.tinytlf.gestures
if(!result)
{
//Not a successful series of events, clear the cache and move on.
- events.length = 0;
continue;
}
+ anySuccess = true;
+
+ //Notify behaviors that we've moved successfully down the ladder.
+ executeBehaviors(event);
+
//Hang onto this event (until we notify the behaviors).
- events.push(event);
+ if(events.indexOf(event) == -1)
+ events.push(event);
if(testNotifiable(childState))
{
- notifyBehaviors(events);
+ //Tell the behaviors we're finally activated.
+ activateBehaviors(events);
//Clear out the events list (don't want memory leaks!)
events.length = 0;
+ resetStates();
}
states += childState;
}
+
+ //If there were no successful states, reset and notify behaviors.
+ if(anySuccess == false)
+ {
+ events.length = 0;
+ failBehaviors(event);
+ }
}
protected function resetStates():void
@@ -125,11 +171,27 @@ package org.tinytlf.gestures
return (state.*.length() == 0);
}
- protected function notifyBehaviors(events:Vector.<Event>):void
+ protected function failBehaviors(event:Event):void
+ {
+ for each(var behavior:IBehavior in behaviors)
+ {
+ behavior.fail(event);
+ }
+ }
+
+ protected function executeBehaviors(event:Event):void
+ {
+ for each(var behavior:IBehavior in behaviors)
+ {
+ behavior.execute(event);
+ }
+ }
+
+ protected function activateBehaviors(events:Vector.<Event>):void
{
for each(var behavior:IBehavior in behaviors)
{
- behavior.execute(events);
+ behavior.activate(events.concat());
}
}
}
View
14 src/org/tinytlf/gestures/IGesture.as
@@ -6,10 +6,24 @@ package org.tinytlf.gestures
public interface IGesture
{
+ /**
+ * Instructs the Gesture to add its event listeners to the target
+ * dispatcher.
+ */
function addSource(dispatcher:IEventDispatcher):void;
+ /**
+ * Instructs the Gesture to remove its event listeners from the target
+ * dispatcher.
+ */
function removeSource(dispatcher:IEventDispatcher):void;
+ /**
+ * Adds the behavior to this Gesture's notify sequence.
+ */
function addBehavior(behavior:IBehavior):void;
+ /**
+ * Removes the behavior from this Gesture's notify sequence.
+ */
function removeBehavior(behavior:IBehavior):void;
}
}

0 comments on commit 845370e

Please sign in to comment.