views:

725

answers:

3

Does anyone have experience in hooking onto events. so we can modify there behavior without changing it's initial.

So lets say we want to modify the behavior of every click of a certain set of menu buttons that if you click them you would wait 30 seconds before the original callback is called.

I'm working on a project that will create a layer on top of an existing Air application that will allow to Manage to build in a way to allow micro-payments to enable full access to certain features.

And my guess was to use hooking for capturing your events, so we don't have to edit our application but just extend it. (correct me if I'm wrong :-))I

A: 

Off the top of my head, I think you can do this: Use an event handler for the click event and use a Timer to wait for as long as you want OR call do some processing and then on completion of the latter, call the action corresponding to the click.

Or, is there something I am missing?

dirkgently
+2  A: 

I don't think you will be able to change the event system in such a way without actually changing the application (if you can change the application it is not that hard to make a custom button that enables this behavior)

The simplest solution is probably to add another Sprite/Button on top of the button that you want to block -- this way you don't have to change your application that much and blocking is simple. Only drawback: mouseover behavior on the original button will not work since it does not get any mouse events.

Simon Groenewolt
+2  A: 

Yes, that should be posible with some modification.

Instead of dealying the event, you can stop it before it reaches it's original target, clone it and then refire the clone after the delay.

If you don't know how events work in the displaylist (anything you can see) in flash let me first explain it.

When you click a button a MouseEvent is fired, it begin in the capturing fase where it starts on the stage calling all eventlisteners registered for the "CLICK" event in the order of the priority set when the listeners was registered. If listerners has the same priority (the default is 0), the are called in a random order (properly the order that they where registered). Once all listeners for the stage has been called the next displayObject in the list of the buttons ancestors is called, and the same thing happens again. This continue all the way down to inner most displayobject that allow mouse events (could be a textfield label inside the button). Here the capturing phase ends and the target phase begins. The all the listeners on the innermost object is called in the target phase. Then the bubbling phase begin, where all the listernes are called on parents and grandparents all the way up to the stage in the same manner as before (but from the inside out).

It should be noted that in flash the target phase is handled as part of the bubbling phase.

And how can we use this?

Well when you register and event listener you specify the phase and priority. The defaults are bubbling phase and priority 0. So if we register a click event listener on the stage for the capturing phase with priority int.MAX_VALUE, we would get the event before any other listerne except perhaps another identical listener. The following code would prevent click events to registered by other listeners.

stage.addEventListener(MouseEvent.CLICK, stageClick, true, int.MAX_VALUE);

function stageClick(e:MouseEvent):void
{
    e.stopImmediatePropagation();
}

That raises some other problems, firstly you don't want to do it for everything.

  • One solution would be be to have a list of all buttons that should have their events modified, and checking against it with e.target.
  • Another solution would be to have all buttons that require a delay implement an interface (lets call it IButtonDelay). And then simple check if e.target is IButtondelay.
  • A third solution would be to only listen for the events on a containg parent (like a menubar), since it is unlikely that you would use the event before it reaches this parent anyway.

For the first two solutions you should prevent mouseinteraction for the children of the buttons to simplify detection (DisplayObjectContainer.mouseChildren = false). No matter what you choose here, next you would have to delay with a simple Timer. And then fire the event again after the delay.

e.target.dispatchEvent(e.clone());

That raises the problem of not delaying the refired event. My suggestion would be to instead fire an event object that inherits from MouseEvent instead of a simple clone. The MouseEvent is very simple to copy, it only has a few properties. So when you capture it the second time you can check if its a normal mouse click or your duplicated.

Using the solution with the inface on the buttons, it would be something like:

stage.addEventListener(MouseEvent.CLICK, stageClick, true, int.MAX_VALUE);

function stageClick(e:MouseEvent):void
{
    if(e.target is IDelayButton && !e is DelayedMouseEvent)
    {
        e.stopImmediatePropagation();
        DelayEvent(e);
    }
}

function DelayEvent(e:MouseEvent):void
{
    //You code for the delaying, cloning and refiring the event here
}

You might need to listen for more types of events (keyboard events), but most of the code can be made generic enough to include them.

Hope that helps and sorry about the long read.

Lillemanden
Very nice answer!
Andy Jacobs
Your welcome, happy coding.
Lillemanden