How to check the mouse is over something clickable

actionscript-3flash

I'm building a specialized viewer application which loads external SWFs.

var content:Sprite = ...
content.addChild(loader);

I listen to my top level content Sprite MOUSE_OVER and MOUSE_OUT events.
The over handler hides default cursor and displays a custom (zoom) cursor.
Out handler changes cursor back to default.

What I want to achieve is NOT showing the zoom cursor when the mouse is over a clickable item in the externally loaded SWF like a button or textlink.

The current code works perfectly for AVM1 swfs.
The problem lies with loaded swfs using AS3.
For these the content MOUSE_OUT is fired and instantly MOUSE_OVER is fired as well, so the cursor remains wrong.

What this probably boils down to is: How to check the mouse is over a clickable child of content?

Best Answer

Good question!

I've done a quick test. There is a method called getObjectsUnderPoint() which

returns an array of objects that lie under the specified point and are children (or grandchildren, and so on) of this DisplayObjectContainer instance

according to the documentation.

Also

any child objects that are inaccessible for security reasons are omitted from the returned array. To determine whether this security restriction affects the returned array, call the areInaccessibleObjectsUnderPoint() method.

So I don't know how helpful this might be.

Here's my test nevertheless:

//make some clips interactive
for(var i:int = 0 ; i < numChildren ; i+=2){
    MovieClip(getChildAt(i)).mouseEnabled = true;
    MovieClip(getChildAt(i)).buttonMode = true;
    MovieClip(getChildAt(i)).mouseChildren = false;
}
//reuse this array
var clips:Array;
addEventListener(Event.ENTER_FRAME, checkClickables);
function checkClickables(event:Event):void {
    clips   = this.getObjectsUnderPoint(new Point(mouseX,mouseY));
    for(i = 0 ; i < clips.length ; i++){
        if(isInteractiveObject(clips[i])){
            trace(clips[i]);
        }
    }
}
function isInteractiveObject(object:*):Boolean{
    var extendsClass:XMLList = describeType(object).extendsClass;
    for each(var classDef:XML in extendsClass){
        if(classDef.@type == 'flash.display::InteractiveObject') return true;
    }
    return false;
}

I have some clips on stage, and I'm making some of them 'clickable'. Then onEnterFrame I'm checking if there are any instances of a subclass of InteractiveObject( the superclass for anything clickable )

For some reason in my test, getObjectsUnderPoint() returned only Shape objects, no Sprites. Maybe it might work in your configuration.

Also it seems a bit complicated.

Here's my second approach:

function getInteractiveObjects(container:DisplayObjectContainer):Array{
    var result:Array = [];
    for(var i:int = 0 ; i < container.numChildren ; i++){
        if(container.getChildAt(i) is Sprite || container.getChildAt(i) is MovieClip) result.push(container.getChildAt(i));
        if(container.getChildAt(i) is DisplayObjectContainer){
            var clips:Array = getInteractiveObjects(DisplayObjectContainer(container.getChildAt(i)));
            var clipsNum:int = clips.length;
            for(var j:int = 0 ; j < clipsNum ; j++)
                if(clips[j] is Sprite || clips[j] is MovieClip) result.push(clips[j])
        }
    }
    return result;
}

Use a recursive function that gets potentially interactive objective inside a clips(like the loader content). If it is either a Sprite or MovieClip, it's 'clickable'.

var iclips:Array = getInteractiveObjects(this);
for(i  = 0 ; i < iclips.length ; i++){
    trace(iclips[i].name);
    //do extra tests here
    if(iclips[i].hasEventListener(MouseEvent.ROLL_OVER)) trace(iclips[i].name + ' has roll over');
    //etc.
}

You can then do extra tests, depending on what exactly you're looking for ( either it has and event listener for mouse events, either it is mouseEnabled, has buttonMode set to true, etc. )

This way you don't need to check for any mouse over ( don't have to wait for user interaction or call a function all the time ) and once you got the clips that don't behave the way you want to, you can change their behavior (mouseChildren = false, or something).

HTH

Related Topic