Custom Chrome with Adobe AIR (Customizing a Desktop App’s Window with Flash) – Getting Started Walkthrough

Introduction:

I’ve been working with AIR for quite some time, but have procrastinated getting into this part. The feature has been around for a long time too.
With this new mini-project (Anatomically Incorrect Dinosaurs) that I’ve been working on, I decided to look into it again. I was so not sorry. AIR is amazing.
NativeWindow opened up a world of possibilities for me (game design/development wise). For example, I could make the user’s actual desktop a level! There’s a lot that can be done in terms of “breaking the fourth wall”.

In this case, I thought it would be cool if the window would look like some old-school DOS thing, and be self-aware. By that I mean it would comment on things you do, complain if you minimize it, or try to push it off-screen. Turn the actual WINDOW into a game.
This is a gimmick I’m also exploring for the Steam version of Tetrageddon Games, so it seemed right to test it out on Anatomically Incorrect Dinosaurs first.

To play with my finished window (see it in action!), you can DOWNLOAD it here:
DOWNLOAD FOR MAC
DOWNLOAD FOR WINDOWS

Other Articles & Docs:

The NativeWindow class is what makes this all possible, and it’s all pretty self-explanatory.

There are also a number of other tutorials out there. Ones I found insightful where…

* Customizing the look and feel of a window (Adobe)
* Creating a custom chrome for AIR application (pretty old, but still good)

* Display screens in AIR (Adobe Docs)
* Creating windows (Adobe Docs)
* Managing windows (Adobe Docs, important)

(And for working with multiple monitors)
* How to use two fullscreen windows in two monitors with Adobe AIR
* How to display a single fullscreen window across multiple screens in Adobe AIR

Opening “New” Windows & Managing Windows:

What I loved about this is that you can “open” as many windows as you want (see NativeWindowInitOptions for setup ), and it sort of reminded me of working with the display list, except it’s all “happening” on the desktop.
Populating these windows with content is very similar to populating any sprite/mc with content. Just addChild to that window’s stage.
Essentially the user’s screen is all yours, and you can (very flexibly) get the screen’s size (for placement, example: Screen.mainScreen.bounds.width or .height ), or multiple screens.
From the docs :

“The screen API contains a single class, Screen, which provides static members for getting system screen information, and instance members for describing a particular screen.

A computer system can have several monitors or displays attached, which can correspond to several desktop screens arranged in a virtual space. The AIR Screen class provides information about the screens, their relative arrangement, and their usable space. If more than one monitor maps to the same screen, only one screen exists. If the size of a screen is larger than the display area of the monitor, there is no way to determine which portion of the screen is currently visible.

A screen represents an independent desktop display area. Screens are described as rectangles within the virtual desktop. The upper-left corner of screen designated as the primary display is the origin of the virtual desktop coordinate system. All values used to describe a screen are provided in pixels.”

I suggest reading Display screens in AIR. It says everything.

BASIC example is:

//how to here: http://help.adobe.com/en_US/as3/dev/WS5b3ccc516d4fbf351e63e3d118666ade46-7e0a.html
//http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/NativeWindowInitOptions.html
//setup
var NWI_initOptions:NativeWindowInitOptions = new NativeWindowInitOptions();
//customizing...
NWI_initOptions.maximizable = false;
NWI_initOptions.minimizable = false;
NWI_initOptions.resizable = false;
//no "system window" - your own interface
NWI_initOptions.systemChrome = NativeWindowSystemChrome.NONE; 
NWI_initOptions.transparent = true;
//
//new window now...
var NW_window:NativeWindow = new NativeWindow(NWI_initOptions);
//for all properties see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/NativeWindow.html
//NW_window.title = "MY TITLE";
//setup the stage
NW_window.stage.align = "TL"; 
NW_window.stage.scaleMode = "noScale"; 
//
//Specifies whether the window is displayed in the top-most group of windows. 
//The alwaysInFront setting has no affect on windows owned by another window.
//other setings include orderToFront, orderInFrontOf, orderToBack, orderBehind, and activate
NW_window.alwaysInFront = true;
//
//Add some movieclip, or whatever, to it...
var mc_popUp:MovieClip = new WINOW_POPUP();
NW_window.stage.addChild(mc_popUp);
mc_popUp.name = "mc_popUp";
mc_popUp.txt_msg.text = str_txt;
//
//set the width and height to that of the window's contents
NW_window.width = mc_popUp.width;
NW_window.height = mc_popUp.height;
//
//Placement...
//Do not save the values returned by the Screen class methods and properties.
//The user or operating system can change the available screens and their arrangement at any time. 
NW_window.x = (Screen.mainScreen.bounds.width - NW_window.width)/2;
NW_window.y = Screen.mainScreen.bounds.height - NW_window.height - 200;
//
//Now activate...
//To activate a window, call the NativeWindow activate() method. 
//Activating a window brings the window to the front, gives it keyboard and mouse focus, 
//and, if necessary, makes it visible by restoring the window or setting the visible property to true.
NW_window.activate();
//


Customizing Your Window:

This was so basic that it confused me a bit. Thought I was doing something wrong, but no. It’s just really simple…

All that is required is your design (visually however you want that to be) and you control the window with your normal event listeners. This old devnet article has a good rundown.
Also see Public Methods in the Native Window docs for an overview of most of the functionality.

For example, if you wanted to set the initial position of the window (center it)…

function evnt_window_initPosition(event:Event) {//starting position
	stage.nativeWindow.x = (Screen.mainScreen.bounds.width - stage.width)/2;
	stage.nativeWindow.y = (Screen.mainScreen.bounds.height - stage.height)/2;
}

and…

stage.addEventListener(Event.ACTIVATE, evnt_window_initPosition);

Note that (as in this doc) Screen.mainScreen.bounds is your friend for any placing sizing… Also:

“Do not save the values returned by the Screen class methods and properties. The user or operating system can change the available screens and their arrangement at any time.”

Events like minimizing, maximizing, or closing can be added as your basic mouse button event handlers…

this.stage.nativeWindow.minimize();//minimize
this.stage.nativeWindow.maximize();//maximize
this.stage.nativeWindow.close();//close
this.stage.nativeWindow.startMove();//to move the window (drag around).

Interestingly enough you can also detect if the window HAS been maximized or minimized (current state). This would be done in a NativeWindowDisplayStateEvent.DISPLAY_STATE_CHANGE event.
For example…

function event_window_checkDisplayState(event:NativeWindowDisplayStateEvent)
{
	//
	//trace("Current state "+stage.nativeWindow.displayState);
	//
	//get minimize or maximize events...
	//
	if(stage.nativeWindow.displayState == NativeWindowDisplayState.MINIMIZED || stage.nativeWindow.displayState == "minimized"){
		trace("window is minimized")
	}
	if(stage.nativeWindow.displayState == NativeWindowDisplayState.NORMAL || stage.nativeWindow.displayState == "maximized"){
		trace("window is maximized")
	}
}

stage.nativeWindow.addEventListener(NativeWindowDisplayStateEvent.DISPLAY_STATE_CHANGE, event_window_checkDisplayState);

Conclusion:

I’m really not sure what else there is to add to this. The entire thing has been so basic I feel ridiculous putting this into writing. (emphasis intended)

Now I have no idea why I haven’t done this sooner, because it opens up so many doors to so many fun little features that I can add to my games.

Actually… I could finish with a rant!
Ok! Here we go…

A Rant:

I’m really utterly exhausted about hearing the “Flash is dead” argument. I’ve been with the technology since it was called Future Splash, and ever since I can remember, people have been saying that… or hating on it. Microsoft first coined that phrase when they where pushing Silverlight (then Sparkle)… To be honest, nothing died. It just evolved.
I see all these platforms evolving, and becoming wonderful tools that I love using (all of them). I love HTML5 for what it is (HTML5 is an umbrella term, but to keep things simple I just say HTML5), but it’s no Flash. I love Flash for all the things it is, but it’s no HTML5. Both of these solutions have their pet peeves (don’t get me started on HTML5). In the end, I wouldn’t want to go without any of these.
I am also not saying this because I am a “non-technical amateur” that’s fixated on “just-one-thing” (also gotten enough of that criticism). I know what I’m talking about, and have been a web-developer ever since ever. I’m into .php, javascript (from Ajax to jquery), MySQL, CSS, WordPress Theming… The list goes on. I’ve been developing for the web so long that I’ve seen all these things grow with it.
Arguing what killed what is like arguing what screw driver head is better than the other. I’m totally tired of hearing it.
To me, whenever I hear another “developer” arguing that “Flash is dead” it just goes to show that they are amateurs. I can’t event take the conversation (or person) serious anymore.

On a personal level, all this has stopped “getting to me” after I won the IGF Nuovo Award. If your work is good people will not judge it (or your perceived technical skills) based on the technology you use. The end user just sees your good work. It really meant a lot to me seeing all these people playing the game, totally enjoying it, and not one of them thought any less of it because of the tools I’m using. Turns out the people that made me feel that way about my skill-set where just amateurs themselves. Silly how I took that seriously.

At any rate, thanks for listening. I will not comment on this any further! :)

Anatomically Incorrect Dinosaurs will be awesome, and is coming soon.
Some screenshots…