Article
Take Your CSS to the Desktop with Adobe AIR!
Page: 1 2
Window Controls
Ignoring the application area in the middle for the moment, the next step will be to wire up all the window controls, like the minimize and quit buttons, window dragging, and the resizing handle. Here’s the plan:
![]()
As it turns out adding custom window controls is very easy.
The minimize and quit buttons are simply a couple of HTML buttons with some CSS added. Here’s the markup:
<div id="window_control">
<button id="minimise_control">-</button>
<button id="quit_control">x</button>
</div>
The CSS removes the borders and applies a background-color, and a few other properties:
#window_control button {
border: none;
background-color: #3378be;
color: #dae6f3;
font-weight: bold;
cursor: pointer;
}
The effect of the CSS can be seen in the figure below, with the finished button on the right:

While we can use jQuery to wire up the events that drive the button functions, we also need an AIR JavaScript file for the window API. AIR provides a file called AIRAliases.js that simply exposes a lot of AIR API functions. I’ve added the following to the HTML head of my ui.html file:
<script type="text/javascript" src="lib/air/AIRAliases.js"></script>
<script type="text/javascript" src="lib/jquery/jquery-1.3.2.min.js"></script>
<script type="text/javascript" src="scripts/harpoon.js"></script>
The harpoon.js file is where I keep all the custom JavaScript that drives the app.
First we create a function that’s called when the application starts. Within this function we add event listeners to the window control buttons that will call the appropriate AIR functions. I like to keep all my custom JavaScript neatly namespaced so that I can see where the code calls a custom function instead of a jQuery or AIR function. Actually, I do it mostly out of habit, but it’s a good habit to have. So here’s the Harpoon object and a function called init that will be called when the application starts; the standard jQuery method works perfectly for this:
var Harpoon = {
init : function() {
// application setup happens here
}
}
$(document).ready(function() {
Harpoon.init();
});
We then create a function called setupWindow and call it from our init function:
var Harpoon = {
init : function() {
Harpoon.setupWindow();
},
setupWindow : function() {
}
}
Now we’re ready to add the event listeners. In our setupWindow function we bind event listeners for the click event to each button. These will in turn call our custom functions: Harpoon.minimize and Harpoon.quit:
setupWindow : function() {
$('#minimise_control').bind('click', function(event){
event.preventDefault();
Harpoon.minimize();
});
$('#quit_control').bind('click', function(event){
event.preventDefault();
Harpoon.quit();
});
}
The custom functions call the approriate AIR functions. We use the nativeWindow.minimize function for the minimize control :
minimize : function() {
nativeWindow.minimize();
}
That’s all we need to do for the minimize button (did you think this was going to be hard?). The function for the quit button is a little more complex, but only a little:
quit : function() {
var exitingEvent = new air.Event(air.Event.EXITING, false, true);
air.NativeApplication.nativeApplication.dispatchEvent(exitingEvent);
if (!exitingEvent.isDefaultPrevented()) {
air.NativeApplication.nativeApplication.exit();
}
}
This is the standard approach recommended by Adobe. The air.NativeApplication.nativeApplication.exit function is the one that actually closes the application, but the risk of calling this function is that you may have unsaved data you can lose. So instead Adobe recommend that you first create a new event called an exiting event:
var exitingEvent = new air.Event(air.Event.EXITING, false, true);
Then you dispatch this event to your application:
air.NativeApplication.nativeApplication.dispatchEvent(exitingEvent);
In your application, if there’s any data you risk losing when the application quits, you create an event listener and listen for the exiting event. This event listener should cancel the exiting event and save any relevant data before the application exits. This is what our quit function is testing. If the exiting event is not cancelled then it’s okay to exit the application:
if(!exitingEvent.isDefaultPrevented()) {
air.NativeApplication.nativeApplication.exit();
}
We’ll make use of this facility in our application later on.
The window resize handle is a transparent PNG image placed in the bottom-right corner of the foot element:
<img id="resize_control" src="icons/resize.png">
#resize_control {
position: absolute;
right: -3px;
bottom: -3px;
width: 20px;
height: 20px;
}
Here’s the transparent PNG image used and its final appearance in the application:

It’s wired up in exactly the same way as the minimize and quit buttons. We add some code to our setupWindow function, this time listening for the mousedown event instead of the click event:
$('#resize_control').bind('mousedown', function(event){
event.preventDefault();
Harpoon.resize();
});
Then we add the custom function to the Harpoon object:
resize : function() {
nativeWindow.startResize(air.NativeWindowResize.BOTTOM_RIGHT);
}
You can add resize controls that resize the window from various directions. We’ve specified the bottom-right direction here.
Making our window draggable is just as easy as creating the other window controls. We’ll use the head element as the handle, as that seems to be the most intuitive for users. Once again we add some code for our event listener (again the mousedown event):
$('#head').bind('mousedown', function(event){
event.preventDefault();
Harpoon.move();
});
And the custom function:
move : function() {
nativeWindow.startMove();
}
Our custom JavaScript file now looks like this:
var Harpoon = {
init : function() {
Harpoon.setupWindow();
},
minimize : function() {
nativeWindow.minimize();
},
resize : function() {
nativeWindow.startResize(air.NativeWindowResize.BOTTOM_RIGHT);
},
move : function() {
nativeWindow.startMove();
},
quit : function() {
var exitingEvent = new air.Event(air.Event.EXITING, false, true);
air.NativeApplication.nativeApplication.dispatchEvent(exitingEvent);
if (!exitingEvent.isDefaultPrevented()) {
air.NativeApplication.nativeApplication.exit();
}
},
setupWindow : function() {
$('#minimise_control').bind('click', function(event){
event.preventDefault();
Harpoon.minimize();
});
$('#quit_control').bind('click', function(event){
event.preventDefault();
Harpoon.quit();
});
$('#resize_control').bind('mousedown', function(event){
event.preventDefault();
Harpoon.resize();
});
$('#head').bind('mousedown', function(event){
event.preventDefault();
Harpoon.move();
});
}
}
$(document).ready(function() {
Harpoon.init();
});
For all our efforts, we now have custom-designed and completely functional window chrome for our application.
As far as Harpoon is concerned there’s still a long way to go. The window design is unfinished and the application is functional but hardly complete. At the moment all you can do is perform a search and it’ll use the custom search feeds from flippa.com for the results. Some of the challenges ahead include storing data in a database, making use of the auction bid feeds on flippa.com, refreshing the results automatically, and alerting the user. I’ll keep you posted!
You can download all the Harpoon files and have a look yourself. Download Aptana and start your own AIR project; you can use the Harpoon window as a starting point if you like. It’s a lot of fun, once you’ve started.
If you’d like to install Harpoon, first grab Adobe AIR, and then download Harpoon.
Remember to have a crack at the quiz and win a great prize!