Native Cursors in AIR 2.6

One of the coolest new desktop features in AIR 2.6 is the ability to use native cursors. By "native cursor," I mean custom cursors at the OS level as opposed to the runtime level. In other words, rather than hiding the mouse cursor and rendering a sprite in its place (the old way to do custom cursors which doesn’t always perform well), in AIR 2.6, you can hand one or more bitmaps to the operating system along with a frame rate and a hot spot, and the cursor will be rendered (and optionally animated) in hardware.

Here’s an example:

Here’s everything you need to know about custom cursors in AIR 2.6:

  • The data property of MouseCursorData takes a vector of BitmapData objects so you can create an animation. Use the frameRate property to control the animation’s frame rate.
  • You can control which part of the cursor is the "hot spot" (the portion that registers clicks) using the hotSpot property of MouseCursorData.
  • When the cursor leaves the application’s native window, your custom cursor will revert to the default OS cursor.
  • The maximum cursor size is 32×32.

Full docs are available here.

And here’s the code. (I used the Smurf sprite sheet from here, though I put them all on a single row and scaled them down to 32×32 to simplify the code.)

package
{
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Sprite;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    import flash.ui.Mouse;
    import flash.ui.MouseCursorData;

    [SWF(backgroundColor="0x000000")]
    public class NativeCursorExample extends Sprite
    {
        [Embed(source="smurf.png")] private var SmurfClass:Class;

        private static const SMURF_WIDTH:uint  = 32;
        private static const SMURF_HEIGHT:uint = 32;

        private var SMURF_CURSOR_NAME:String = "smurf";

        public function NativeCursorExample()
        {
            var bitmaps:Vector.<BitmapData> = new Vector.<BitmapData>;

            var spriteSheet:Bitmap = new SmurfClass();

            var r:Rectangle;
            var bmd:BitmapData;
            var p:Point = new Point(0, 0);

            for (var i:uint = 0; i < 16; ++i)
            {
                r = new Rectangle((SMURF_WIDTH * i) , 0, SMURF_WIDTH, SMURF_HEIGHT); 
                bmd = new BitmapData(r.width, r.height, true, 0x000000);
                bmd.copyPixels(spriteSheet.bitmapData, r, p);
                bitmaps.push(bmd);
            }
            var mcd:MouseCursorData = new MouseCursorData();
            mcd.data = bitmaps;
            mcd.hotSpot = new Point(22, 15); // The smurf's nose
            mcd.frameRate = 24;
            Mouse.registerCursor(SMURF_CURSOR_NAME, mcd);
            Mouse.cursor = SMURF_CURSOR_NAME;
        }
    }
}

3 Responses to Native Cursors in AIR 2.6

  1. Quentin says:

    See this example project/class for easy cursor management!

  2. Pingback: You can use Smurf’s nose to resize window

  3. Steven Sacks says:

    When I do this, this is what I see in the output panel in FB.
    ———-
    typecheck com.jamcloud.ui.cursor::Cursor$/init()
    outer-scope = [global Object$]
    [com.jamcloud.ui.cursor::Cursor$~[O] *[A] *[A]] {} ()
    0:debugfile “E:\JamCloud\fdt\flash\trunk\src;com\jamcloud\ui\cursor;Cursor.as”
    [com.jamcloud.ui.cursor::Cursor$~[O] *[A] *[A]] {} ()
    4:debugline 18
    [com.jamcloud.ui.cursor::Cursor$~[O] *[A] *[A]] {} ()
    6:getlocal0
    [com.jamcloud.ui.cursor::Cursor$~[O] *[A] *[A]] {} (com.jamcloud.ui.cursor::Cursor$~[O])
    7:pushscope
    [com.jamcloud.ui.cursor::Cursor$~[O] *[A] *[A]] {com.jamcloud.ui.cursor::Cursor$~[O]} ()
    8:debug
    [com.jamcloud.ui.cursor::Cursor$~[O] *[A] *[A]] {com.jamcloud.ui.cursor::Cursor$~[O]} ()
    15:debug
    [com.jamcloud.ui.cursor::Cursor$~[O] *[A] *[A]] {com.jamcloud.ui.cursor::Cursor$~[O]} ()
    22:debugline 20
    [com.jamcloud.ui.cursor::Cursor$~[O] *[A] *[A]] {com.jamcloud.ui.cursor::Cursor$~[O]} ()
    24:findproperty com.jamcloud.ui.cursor:Cursor::cursorResizeVertical
    [com.jamcloud.ui.cursor::Cursor$~[O] *[A] *[A]] {com.jamcloud.ui.cursor::Cursor$~[O]} (com.jamcloud.ui.cursor::Cursor$~[O])
    27:findpropstrict com.jamcloud.skin.cursor::ResizeVerticalCursor
    [com.jamcloud.ui.cursor::Cursor$~[O] *[A] *[A]] {com.jamcloud.ui.cursor::Cursor$~[O]} (com.jamcloud.ui.cursor::Cursor$~[O] global~[O])
    30:constructprop 5978 0
    [com.jamcloud.ui.cursor::Cursor$~[O] *[A] *[A]] {com.jamcloud.ui.cursor::Cursor$~[O]} (com.jamcloud.ui.cursor::Cursor$~[O] com.jamcloud.skin.cursor::ResizeVerticalCursor~[O])
    34:setproperty com.jamcloud.ui.cursor:Cursor::cursorResizeVertical
    [com.jamcloud.ui.cursor::Cursor$~[O] *[A] *[A]] {com.jamcloud.ui.cursor::Cursor$~[O]} ()
    37:debugline 21
    [com.jamcloud.ui.cursor::Cursor$~[O] *[A] *[A]] {com.jamcloud.ui.cursor::Cursor$~[O]} ()
    39:findproperty com.jamcloud.ui.cursor:Cursor::cursorResizeHorizontal
    [com.jamcloud.ui.cursor::Cursor$~[O] *[A] *[A]] {com.jamcloud.ui.cursor::Cursor$~[O]} (com.jamcloud.ui.cursor::Cursor$~[O])
    42:findpropstrict com.jamcloud.skin.cursor::ResizeHorizontalCursor
    [com.jamcloud.ui.cursor::Cursor$~[O] *[A] *[A]] {com.jamcloud.ui.cursor::Cursor$~[O]} (com.jamcloud.ui.cursor::Cursor$~[O] global~[O])
    45:constructprop 5977 0
    [com.jamcloud.ui.cursor::Cursor$~[O] *[A] *[A]] {com.jamcloud.ui.cursor::Cursor$~[O]} (com.jamcloud.ui.cursor::Cursor$~[O] com.jamcloud.skin.cursor::ResizeHorizontalCursor~[O])
    49:setproperty com.jamcloud.ui.cursor:Cursor::cursorResizeHorizontal
    [com.jamcloud.ui.cursor::Cursor$~[O] *[A] *[A]] {com.jamcloud.ui.cursor::Cursor$~[O]} ()
    52:debugline 22
    [com.jamcloud.ui.cursor::Cursor$~[O] *[A] *[A]] {com.jamcloud.ui.cursor::Cursor$~[O]} ()
    54:getlex 9942
    [com.jamcloud.ui.cursor::Cursor$~[O] *[A] *[A]] {com.jamcloud.ui.cursor::Cursor$~[O]} (__AS3__.vec::Vector$[O])
    57:getlex 252
    [com.jamcloud.ui.cursor::Cursor$~[O] *[A] *[A]] {com.jamcloud.ui.cursor::Cursor$~[O]} (__AS3__.vec::Vector$[O] flash.display::BitmapData$[O])
    60:applytype 1
    [com.jamcloud.ui.cursor::Cursor$~[O] *[A] *[A]] {com.jamcloud.ui.cursor::Cursor$~[O]} (*~[A])
    62:construct 0
    [com.jamcloud.ui.cursor::Cursor$~[O] *[A] *[A]] {com.jamcloud.ui.cursor::Cursor$~[O]} (*~[A])
    64:coerce __AS3__.vec::Vector
    [com.jamcloud.ui.cursor::Cursor$~[O] *[A] *[A]] {com.jamcloud.ui.cursor::Cursor$~[O]} (__AS3__.vec::Vector.~[O])
    67:setlocal1
    [com.jamcloud.ui.cursor::Cursor$~[O] __AS3__.vec::Vector.~[O] *[A]] {com.jamcloud.ui.cursor::Cursor$~[O]} ()
    68:debugline 23
    [com.jamcloud.ui.cursor::Cursor$~[O] __AS3__.vec::Vector.~[O] *[A]] {com.jamcloud.ui.cursor::Cursor$~[O]} ()
    70:getlocal1
    [com.jamcloud.ui.cursor::Cursor$~[O] __AS3__.vec::Vector.~[O] *[A]] {com.jamcloud.ui.cursor::Cursor$~[O]} (__AS3__.vec::Vector.~[O])
    71:getlex 8321
    [com.jamcloud.ui.cursor::Cursor$~[O] __AS3__.vec::Vector.~[O] *[A]] {com.jamcloud.ui.cursor::Cursor$~[O]} (__AS3__.vec::Vector.~[O] flash.display::Bitmap[O])
    74:getproperty bitmapData
    [com.jamcloud.ui.cursor::Cursor$~[O] __AS3__.vec::Vector.~[O] *[A]] {com.jamcloud.ui.cursor::Cursor$~[O]} (__AS3__.vec::Vector.~[O] flash.display::BitmapData[O])
    77:callpropvoid http://adobe.com/AS3/2006/builtin::push 1
    [com.jamcloud.ui.cursor::Cursor$~[O] __AS3__.vec::Vector.~[O] *[A]] {com.jamcloud.ui.cursor::Cursor$~[O]} ()
    81:debugline 24
    [com.jamcloud.ui.cursor::Cursor$~[O] __AS3__.vec::Vector.~[O] *[A]] {com.jamcloud.ui.cursor::Cursor$~[O]} ()
    83:findpropstrict flash.ui::MouseCursorData
    [com.jamcloud.ui.cursor::Cursor$~[O] __AS3__.vec::Vector.~[O] *[A]] {com.jamcloud.ui.cursor::Cursor$~[O]} (Object~[A])
    86:constructprop 9944 0
    [com.jamcloud.ui.cursor::Cursor$~[O] __AS3__.vec::Vector.~[O] *[A]] {com.jamcloud.ui.cursor::Cursor$~[O]} (*[A])
    90:coerce flash.ui::MouseCursorData
    Exception fault: VerifyError: Error #1014: Class flash.ui::MouseCursorData could not be found.