Hand Me That Cursor, Would You?

by

Today I’d like to talk about a basic technique that’s a little deceptive when migrating from AS2 to AS3: creating a clickable button from a Sprite or MovieClip.

So you create a new Sprite or custom view class that subclasses Sprite. For purposes of argument, here’s a simple button class:

package
{
    import flash.display.Sprite;
    import flash.filters.BevelFilter;

    public class CustomButton extends Sprite
    {
        public function CustomButton(width:Number = 100, height:Number = 20)
        {
            super();
            graphics.beginFill(0xaaaaaa);
            graphics.drawRoundRect(0, 0, width, height, 6, 6);
            graphics.endFill();
            filters = [new BevelFilter(2)];
        }
    }
}

Then you do two things new to AS3. You add it to your display list, and you attach an event listener:

var button:CustomButton = new CustomButton();
addChild(button);
button.addEventListener(MouseEvent.CLICK, onButtonClick);

You’ll notice that (if you do something in onButtonClick) the button is functional. But the problem is: no hand cursor! What the devil?

For a second let’s recall AS2. To get our little pointy finger cursor, all we had to do was add an onRelease callback to the MovieClip or MovieClip subclass. The code above proves the analog of this in AS3 does not apply. We didn’t automatically get a hand cursor by virtue of adding a CLICK listener.

Remember, though, in AS2, there was a property on MovieClips called useHandCursor? Setting this to false could disable the hand cursor on that object. Well, Sprites and MovieClips still have this property in AS3. You might think: why not set this to true? Unfortunately, that’s not it either.

The proper way to make a Sprite/MovieClip show the system hand cursor when the mouse is over it is to use the new buttonMode property. See it in LiveDocs here.

buttonMode tells a Sprite if it should behave like a button or not. It defaults to false: even if you have event listeners set on the Sprite, it will not behave like a button. You need to manually set buttonMode to true.

“Behaving like a button” means three things.

  1. The system hand cursor is used.
  2. The item appears in the tab order.
  3. When the item has tab focus and you press space, it broadcasts the CLICK event.
  4. (Minor) if you have _over, _up, and _down frame labels they will be used as button states.

As far as hand cursors go, useHandCursor can control whether the hand cursor is used with the object or not, but it means nothing if buttonMode is not set.

tabEnabled can set whether the item appears in the tab order. This property doesn’t require buttonMode to be true to take effect. In other words, you can have something in the tab ordering without it needing to be a button. But remember, without buttonMode, when you tab to the item and press space, it won’t trigger a CLICK event.

Some problems we run into: If your clickable object contains anything else clickable, the target of those events will be the inner object. Furthermore, if you have a TextField inside of a button mode Sprite, the mouse cursor won’t appear as a hand cursor above that TextField, even if you have no event listeners on it and it’s not selectable. You can see this in the demonstration below. Try clicking inside the flash movie and tabbing around as well. The button mode button inside button 1 (on the left) is getting in the tab order, and the text field inside button 2 (on the right) is stealing the cursor type.

In order to fix these issues, if they are indeed problematic, you can use these additional properties of InteractiveObject and DisplayObjectContainer.

tabEnabled
Whether this item can be tabbed to.
tabChildren
Whether the children of this item can be tabbed to.
mouseEnabled
Whether this item accepts mouse events.
mouseChildren
Whether the children of this item accept mouse events.

By setting mouseChildren to false, you can treat any complex compositions of objects as one mouse target, even if the inner items would otherwise be clickable.

If you see this message, you need to install Flash Player 9.

Play around with the example to see how these properties can affect the mouse cursor, ability to be tabbed to, and the target of events.

In short: buttonMode makes a Sprite a button. useHandCursor is only meant to override behavior set by buttonMode. You can also make buttons by extending the SimpleButton class.

  • http://mx.coldstorageonline.com Mike J

    Great information for complex composite controls.

    For the example of creating a simple button though you can also inherit from flash.display.SimpleButton and that behavior comes with it (tab stop, space triggers click event), as well as simplicity in managing up / over / down states (just assign a display object like a shape to each of the properties for the state).

  • http://partlyhuman.com Roger Braunstein

    Odd, in mims flava of the blog the SWF appears to be the wrong swf. I don’t know why this is, but it resolves itself when you view full. But you’re already doing that! So why am I talking to you?!

  • http://peko.gasubasu.com peko

    Domo, it`s help me a lot!

  • http://svza.blogspot.com swanky.wu

    thx…

    it’s save me…

  • http://www.talesfromtheinterweb.com Sunil

    Thanks for that! I’m just starting out on AS3 and was scratching my head on that one.

  • nick

    Very helpful! Was struggling with this one.

  • http://www.friendly-stranger.com Pickels

    Thanks for the info. I am also new to as 3.0 and this helped a lot. =)

  • Pingback: Flash Notes: Properties, Button Event Listeners » Webomatica - Technology and Entertainment Digest

  • http://tr13.com Tim

    Thanks! Helped me a ton. I have a TextField embedded in a Sprite which I wanted to have a hand cursor. Here is what I did with your help:

    var txtHolder_sp:Sprite = new Sprite;
    var Text_txt:TextField = new TextField;
    txtHolder_sp.buttonMode = true;
    txtHolder_sp.mouseChildren = false;/* This allows the hand cursor to turn on if a TextField is embedded in the Sprite.*/
    Text_txt.text = ‘clickable text’

  • http://tr13.com Tim

    I added two lines at the end to make the embeding clear:

    var txtHolder_sp:Sprite = new Sprite;
    var Text_txt:TextField = new TextField;
    txtHolder_sp.buttonMode = true;
    txtHolder_sp.mouseChildren = false;/* This allows the hand cursor to turn on if a TextField is embedded in the Sprite.*/
    Text_txt.text = ‘clickable text’
    txtHolder.addChild(nameText);//enbed TextField in the Sprite
    this.addChild(txtHolder);//show the Sprite on the stage

  • http://tr13.com Tim

    whoops should be:

    txtHolder_sp.addChild(Text_txt);//enbed TextField in the Sprite
    this.addChild(txtHolder_sp);//show the Sprite on the stage

    How do I edit a comment I left here? Thanks again.

  • Xander

    Oh my god thank you!

    It was so hard to find information on the web how to make a text behave as button and using a hand cursor in flash

  • http://www.seventy6.com seventy6

    nice simple tutorial. cheers n

  • http://am3portfolio.com AM3

    Setting mouseChildren to false is not good if you are storing variables in your button. This will kill any stored button data.
    txtHolder_sp.mouseChildren = false;

    My solution was to just add a 0% alpha graphic (mc, sprite…etc) above the text. This fixed my AS3 button text cursor issue.

    - Alex

  • Bacca

    The problem with having a MovieClip or Sprite above your textField is that you then have no way of clicking the textField.\n\nIf the click event is based on the textField because you wish to access the target.text you are pretty boned.

  • Timo

    Great, thanks — was scratching my head over this one…

  • http://Puerta121 Pablo

    Thanks so much !!

  • http://tsohonis.com sam tsohonis

    Hello
    I am trying to get the tab navigation set up on an array of 48 MC buttons I have physically on the stage. I can use stage.focus to force one into focus but the tab key just will not work for me. Is this because I am calling the tab-related properties in an array?


    for each (var item in btnArray){
    item.focusEnabled = true;
    item.tabChildren = false;
    item.buttonMode = true;
    item.useHandCursor = true;
    item.focusRect=true;
    item.tabIndex = btnArray.indexOf(item);
    item.addEventListener(MouseEvent.MOUSE_OVER,btnGlow);
    item.addEventListener(MouseEvent.MOUSE_OUT, btnFade);
    item.addEventListener(MouseEvent.CLICK, loadPic);
    item.addEventListener(FocusEvent.FOCUS_IN, focusIt);
    item.addEventListener(FocusEvent.FOCUS_OUT, blurIt);
    function focusIt(e:FocusEvent):void{
    e.currentTarget.gotoAndStop(6);
    }
    function blurIt(e:FocusEvent):void{
    e.currentTarget.gotoAndStop(1);
    }
    trace(item.tabIndex);

  • http://www.twitter.com/indextwo Lawrie

    Dude, I had *no* idea about the _up, _over and _down frame labels on MCs. That is mental.