by Joe Ward

Joe Ward

Created

10 June 2010

Requirements
Prerequisite knowledge

General experience of building applications
with Flash CS3 is suggested.
For more details on getting started with
this Quick Start, refer to Building the Quick
Start sample applications with Flash.

Required products

Sample files

User level

Intermediate
 
When you turn off system chrome for a window, you gain tremendous creative possibilities, but you also lose the automatic management of window size, position, and display state that the operating system provides for standard windows. The Custom Chrome example application, shown in Figure 1, illustrates how to leverage some of the creative advantages of providing your own chrome and how to replace the window management services that are no longer provided by the operating system.
 
The Custom Chrome example application demonstrates alternatives to the default system chrome.
Figure 1. The Custom Chrome example application demonstrates alternatives to the default system chrome.
 
 
Note: This is a sample application provided, as is, for instructional purposes.
 

 
Types of chrome

The Custom Chrome example uses four general types of window chrome:
 
  • buttons: SimpleButton objects and mouse-click events to close, maximize, minimize, and restore the window.
  • move handles: Bitmap-based Sprites and mouse-down events to allow moving the window.
  • resize grippers: Bitmap-based Sprite and the mouse-down event to allow resizing the window. The Custom Chrome example only implements resizing from the bottom-right corner, but AIR supports resizing from each edge and corner of a window.
  • background: Draws an irregular background using a Sprite object and vector drawing commands.

 
Understanding the code

This example demonstrates how to add your own chrome using the Flash APIs for drawing bitmap and vector graphics. This article does not describe all of the ActionScript classes used in the application. For information on these, see the ActionScript 3 Reference for the Flash Platform.
 
 
Initializing a Window
The CustomChrome class extends the Sprite class so that it can be used as the root class in the application SWF file. AIR automatically creates the window, instantiates an instance of the CustomChrome class, and adds the CustomChrome object to the window stage.
 
The CustomChrome constructor adds an event listener to detect when it has been added to the stage. The handler for the addedToStage event, gets the window instance using the nativeWindow property of the stage, and initializes the window properties.
 
Note: You may notice that you can access the stage directly from the class constructor without waiting for the addedToStage event. However, it can be a bad habit since this is only true for the main Sprite class in the initial window of an AIR application.
 
Initialization tasks include:
 
  • Adding event listeners for the basic window events:
win.addEventListener(NativeWindowBoundsEvent.RESIZE,onBoundsChange); win.addEventListener(NativeWindowBoundsEvent.MOVE,onBoundsChange); win.addEventListener(NativeWindowDisplayStateEvent.DISPLAY_STATE_CHANGE, onDisplayStateChange);
Custom Chrome uses these events to reposition and redraw the window chrome elements when the window is resized and also to control which elements to display for a particular window display state.
 
  • Setting the stage properties:
stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE;
Setting the stage alignment to topLeft means that the x and y coordinates of all sprites added to the window are initialized to (0,0). Setting the stage scaling mode to noScale prevents the stage from scaling as the window is resized. Since the stage doesn't scale, the contents of the window are clipped when the window is resized smaller unless the contents are repositioned and redrawn.
 
  • Adding the chrome:
addChildAt(background,0); addChildAt(topChrome,1); addChildAt(closeButton,2); addChildAt(minimizeButton,2); addChildAt(maximizeButton,2); addChildAt(restoreButton,2); bottomGroup.addChild(bottomChrome); bottomGroup.addChild(gripperChrome); addChildAt(bottomGroup,1);
The addChildAt() method is used to control the depth ordering of the sprites used for the chrome elements. (If addChild() is used, then the ordering is set by the order in which the sprites are added to their container.) Because Custom Chrome hides both the bottomChrome and gripperChrome sprites when the window is maximized, these sprites are grouped in the same container, bottomGroup, so that their visibility can be controlled with a single property: bottomGroup.visible.
 
 
Defining the chrome elements
Custom Chrome defines a Chrome class to serve as the base class for all the chrome elements.
 
The constructor for the Chrome class takes a rectangle to define the chrome placement and size, a string defining how to anchor the control to the window border, and an optional function reference used to attach the chrome element to its related control function. For example, the Close button is defined with the following declaration:
 
private var closeButton:CloseButton = new CloseButton(new Rectangle(65,8,0,0), 'TR', onCloseCommand)
The control is anchored to the window border according to the anchorType parameter. A value of TR specifies that the control is anchored to the top-right corner.
 
Button chrome
 
To implement the buttons for controlling the window state, Custom Chrome extends the Chrome class and adds a SimpleButton object as a child. The images for the button states are loaded by the button class. Each button class also adds the event listener and passes the offset and the anchorType parameters to the Chrome super class.
 
Move handle and resize gripper chrome
 
To implement the move handles and resize grippers Custom Chrome extends the Chrome base class and adds the chrome image as a child. PNG files are used for the images so that transparent areas can be defined. The transparent areas of the image don't capture mouse events.
 
Background
 
To draw the background, Custom Chrome extends the Chrome base class and adds a draw() method to perform the vector drawing. To call the draw() method, the Background class overrides the base class layout() method.
 
Allowing cancellation of window operations
 
When a window uses system chrome, user interaction with the window can be cancelled by listening for and canceling the default behavior of the relevant event. For example, when a user clicks the system chrome close button, the closing event is dispatched. If any registered listener calls the preventDefault() method of the event, then the window will not close.
 
When a window does not use system chrome, notification events for window changes like resizing or closing are not automatically dispatched. Hence, if you call the methods for closing a window, changing the window display state, or set any of the window bounds properties, the change cannot be cancelled. To notify components in your application before a window change is actually made, your application logic can dispatch the appropriate notification event using the dispatchEvent() method of the window. For example, the following logic implements a cancelable event handler for a window close button:
 
public function onCloseCommand(event:MouseEvent):void{ var closing:Event = new Event(Event.CLOSING,true,true); dispatchEvent(closing); if(!closing.isDefaultPrevented()){ win.close(); } }
Note: Although the dispatchEvent() method returns false if the event preventDefault() method is called by a listener, it can also return false for other reasons. It is better to explicitly use the isDefaultPrevented() method to test whether the window change should be canceled.
 
 
Resizing and moving a window
Calling the startResize() or startMove() methods of a window starts a system-mediated window change. The user's mouse or keyboard actions are used to change the window bounds. In this case, each incremental change is preceded by an automatically generated moving or resizing event. If any listeners cancel these events, the move or resize sequence is terminated and no further changes are made to the window.
 
Custom Chrome uses the following functions to start a resize and move:
 
public function onMoveCommand(event:MouseEvent):void{ win.startMove(); } public function onResizeCommand(event:MouseEvent):void{ win.startResize(NativeWindowResize.BOTTOM_RIGHT); }
When a window is moved, its contents move with it (as you would expect), but when a window is resized, the situation is more complicated. The default behavior of a window depends on the stage scale mode. If the scale mode is anything except, noScale, then the stage and its contents are scaled when the window is resized. This means that any buttons or other chrome you add to your window is also scaled, which may not be a desirable effect. On the other hand, if you do use the noScale mode, your chrome won't be scaled, but then it won't be moved to conform to the resized window borders either. If you resize a window smaller, your chrome could be clipped. If you resize the window larger from the top edge, your title bar could be left in the middle.
 
The Custom Chrome example solves this problem by drawing its chrome relative to the window corners, and redrawing whenever the window dispatches a resize event. Each chrome element listens for the resize event through an event listener added by the base Chrome class:
 
stage.nativeWindow.addEventListener(NativeWindowBoundsEvent.RESIZE, onWindowResize);
When the event is received, the chrome element sets its position and size based on the afterBounds property of the event object:
 
private function onWindowResize(boundsEvent:NativeWindowBoundsEvent):void{ layout(boundsEvent.afterBounds); } public function layout(bounds:Rectangle):void{ setPosition(bounds); if(resizable){ setSize(bounds); } }
 
Controlling the window display state
The window display state can be controlled using the maximize(), minimize(), and restore() methods of the NativeWindow object. To allow other components to prepare for the change, Custom Chrome dispatches a displayStateChanging event before changing the display state:
 
public function onMaximizeCommand(event:MouseEvent):void{ var displayStateChanging:NativeWindowDisplayStateEvent = new NativeWindowDisplayStateEvent( NativeWindowDisplayStateEvent.DISPLAY_STATE_CHANGING, true, true); dispatchEvent(displayStateChanging); if(!displayStateChanging.isDefaultPrevented()){ win.maximize(); } }
The command to change the display state and the process of updating the size and position of the chrome elements is decoupled. Custom Chrome listens for the displayStateChange event to detect a change in display state and the resize event to detect a change in window size.
 
 
Closing the window
CustomChrome closes the window using the close() method of the NativeWindow object. To allow other components to prepare for the change, Custom Chrome dispatches a closing event before closing the window:
 
public function onCloseCommand(event:MouseEvent):void{ var closing:Event = new Event(Event.CLOSING,true,true); dispatchEvent(closing); if(!closing.isDefaultPrevented()){ win.close(); } }