Chapter 4. Screens

Breaking out of the browser provides you with additional challenges when developing applications. In addition to positioning elements within your application, you now have control over where the application itself is positioned. In addition, you now have to expand your thinking beyond a browser window to a user’s desktop, which consists of one or more monitors.

The Screen class in Adobe AIR provides you with information about the current user’s monitor setup. This allows you to take advantage of the full capability of the user’s desktop, including multiple monitor configurations. By using the data contained in this class, you can get explicit information about the number of monitors, the positioning of these monitors, and even the capabilities of the monitors themselves.

Positioning Windows on the Desktop

Problem

You want to center your application on the user’s primary monitor.

Solution

Use the Screen class to determine the width and height of the primary monitor on the user’s computer. Using this information, you can position your application in the center of the screen.

Discussion

The Screen class has properties that reflect characteristics of the user’s primary monitor and provides an instance of itself in the static variable Screen.mainScreen. Included in these properties are two variables that define the width and height of the monitor: bounds and visibleBounds.

  • bounds: The bounds property of the Screen class is an instance of the Rectangle class, which provides the properties for the entire space on a screen that is available to the operating system.

  • visibleBounds: The visibleBounds property is an instance of the Rectangle class, which provides the properties for the available space on the screen. On a Windows system, this is the full size of the screen without the taskbar. On a Mac, this could be the full size of the screen without the menu bar. Also, note that, depending on system preferences, this could include or not include the Dock on a Mac.

Both the bounds and visibleBounds variables are instances of the Rectangle class, which provides the width, height, x, and y values for the screen as well as additional positioning values. With these variables, you can determine the center point of the screen as well as the application’s relative positioning.

After you have determined the center point, you can then calculate where the window will need to be positioned to be in the center. To accomplish this, you can adjust the x and y positions of the current NativeWindow instance in your application. To find these values, you subtract half the application’s width and half the application’s height from the center point and then position the NativeWindow instance at that point.

ActionScript

Within ActionScript, you first need to import the flash.display.Screen class. You then can determine the center x and y values by dividing the Screen.mainScreen.bounds.width and Screen.mainScreen.bounds.height values by 2. Finally, you can position the nativeWindow instance’s x and y values by subtracting half the window’s width and height from the center points.

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication
    xmlns:mx="http://www.adobe.com/2006/mxml"
    horizontalAlign="center" verticalAlign="middle">

    <mx:Script>
          <![CDATA[

               import flash.display.Screen;

               private function handleClick(event:MouseEvent):void {
                     var centerX:Number = Screen.mainScreen.bounds.width / 2;
                     var centerY:Number = Screen.mainScreen.bounds.height / 2
                     nativeWindow.x = centerX - (nativeWindow.width / 2);
                     nativeWindow.y = centerY - (nativeWindow.height / 2);
               }

          ]]>
    </mx:Script>

    <mx:Button id="centerButton"
          click="handleClick(event)"
          label="Center Window" />

</mx:WindowedApplication>

JavaScript

Within JavaScript, you can access the Screen class with air.Screen. To determine the width and height of the primary monitor, you use air.Screen.mainScreen.bounds.width and air.Screen.mainScreen.bounds.height. By dividing these two values in half, you have the center point of the user’s primary monitor.

Next, subtract half the application’s width and height from these center values to determine the point at which the application should be positioned to center it on the screen. The nativeWindow instance that refers to the current application window is located in window.nativeWindow. Set its x and y values to the predetermined point to center the window on the user’s screen.

<html>
<head>
    <title>Entry 4.1 - Center a Window</title>

    <script type="text/javascript" src="AIRAliases.js"></script>
    <script type="text/javascript">
        function handleClick(){
            var centerX = air.Screen.mainScreen.bounds.width / 2;
            var centerY = air.Screen.mainScreen.bounds.height / 2;
            window.nativeWindow.x = centerX - (window.nativeWindow.width / 2);
            window.nativeWindow.y = centerY - (window.nativeWindow.height / 2);
        }
    </script>
</head>

<body>
    <input id="centerButton" type="button" value="Center Window" 
onclick="handleClick()" />
</body>
</html>

Positioning Windows Across Multiple Monitors

Problem

You want to be able to center your application on any of the available monitors for the user’s current desktop.

Solution

Use the static Screen.screens array, which contains the properties of the available monitors.

Discussion

The process of centering the application on any available monitor is twofold. First, you need to loop through the array of screens to determine the monitors that are available. Second, you must calculate the center point of the monitor that is selected.

Looping through the available monitors requires looping through the Screen.screens array. To center your application in Positioning Windows on the Desktop, you used the Screen.mainScreen property, which held a reference to an instance of the Screen class. Each element in the Screen.screens array is an instance of the Screen class as well. Each of these instances corresponds to one of the user’s available screens. You can use the bounds rectangle of each Screen defined in the array to determine the width, height, x, and y values of the available monitors.

Calculating the center point now requires an additional step. In AIR, the primary monitor is always positioned at 0,0, and other monitors are positioned relative to their positioning on the user’s desktop. For example, if the user had a monitor above the primary monitor, its x and y values would be negative. If you are trying to center a window on a specific monitor, the starting point will be that window’s x and y positions as defined in its instance of the Screen class. From that point, you can add half the width and height to determine the center.

ActionScript

In this example, the handleCreationComplete method loops through each of the available monitors and populates a group of radio buttons. Each radio button is given a value that corresponds to the index of the screen in the Screen.screens array with which you can retrieve a reference to each screen instance and its properties.

When the centerButton is clicked, the handleClick method determines the monitor that the user selected with the radio buttons and calculates that screen’s center point. Finally, the application window is positioned at the center point of the selected monitor. If a screen is not selected, an Alert window is shown.

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication
    xmlns:mx="http://www.adobe.com/2006/mxml"
    layout="vertical"
    creationComplete="handleCreationComplete()">

    <mx:Script>
          <![CDATA[
               import mx.controls.Alert;
               import flash.display.Screen;
               import mx.collections.ArrayCollection;

               [Bindable]
               private var screens:ArrayCollection;

               private function handleCreationComplete():void {
                     screens = new ArrayCollection();
                     var output:String = "";
                     var screen:Screen;
                     for(var i:uint = 0; i < Screen.screens.length; i++) {
                          screen = Screen.screens[i];
                          output = "Screen " + i + " - ";
                          output += "Width: " + screen.bounds.width;
                          output += " Height: " + screen.bounds.height;
                          screens.addItem({ label: output });
                     }
               }

               private function handleClick(event:MouseEvent):void {
                     if(monitorButtonGroup.selectedValue != null) {
                          var selectedVal:Object = monitorButtonGroup.selectedValue;

                          var screen:Screen = Screen.screens[selectedVal] as Screen;
                          var centerX:Number = screen.bounds.x + screen.bounds.width / 2;
                          var centerY:Number = screen.bounds.y + screen.bounds.height / 2;
                          nativeWindow.x = centerX - (nativeWindow.width / 2);
                          nativeWindow.y = centerY - (nativeWindow.height / 2);
                     } else {
                          mx.controls.Alert.show("Please Select a Monitor","");
                     }
               }

          ]]>
    </mx:Script>

    <mx:RadioButtonGroup id="monitorButtonGroup" />

    <mx:Repeater id="screensRepeater" dataProvider="{screens}">
          <mx:RadioButton
               id="monitorsButton"
               group="{monitorButtonGroup}"
               value="{screensRepeater.currentIndex}"
               width="100%"
               label="{screensRepeater.currentItem.label}" />
    </mx:Repeater>

    <mx:Button id="centerButton"
          click="handleClick(event)"
          label="Center on Selected Monitor" />

</mx:WindowedApplication>

JavaScript

In this example, the function populateScreens is called in response to the onload event. This method loops through each of the screens available on the user’s computer. It adds a radio button for each of these screens along with a label for each button that gives the screen’s index in the Screen.screens array as well as its width and height. It is important to note that if you have only one monitor, you will see only one option here.

The page also contains a button that triggers the handleClick function when clicked. This method determines which radio button is selected and then calculates the center point of the referenced screen. The application window is positioned at this center point. If no screen is selected, an alert window is launched.

<html>
<head>
    <title>Entry 4.2 - Multiple Monitors</title>

    <script type="text/javascript" src="AIRAliases.js"></script>
    <script type="text/javascript">
      function populateScreens() {
               var form = document.getElementById('radioButtons');
               for(i=0; I < air.Screen.screens.length; i++) {
                     var screen = air.Screen.screens[i];
                     var rb = document.createElement('input');
                     rb.type='radio';
                     rb.name='screens';
                     rb.value=i;
                     form.appendChild(rb);
                     form.innerHTML += 'Screen ' + i + ' - '
                     form.innerHTML += 'Width: ' + screen.bounds.width;
                     form.innerHTML += ' Height: ' + screen.bounds.height;
                     form.innerHTML += '<br />';
               }
          }
          function handleClick() {
               var buttons = document.forms['screensForm'].elements['screens'];
               var selectedValue;
               for( i=0; i<buttons.length;i++) {
                     if( buttons[i].checked) {
                           selectedValue = buttons[i].value;
                     }
               }
               if (selectedValue) {
                     var screen = air.Screen.screens[selectedValue];
                     var centerX = screen.bounds.x + screen.bounds.width / 2;
                     var centerY = screen.bounds.y + screen.bounds.height / 2;
                     window.nativeWindow.x = centerX - (window.nativeWindow.width / 2);
                     window.nativeWindow.y = centerY - (window.nativeWindow.height / 2);
               } else {
                     alert('Please Select a Screen');
               }
          }
    </script>
</head>

<body onload="populateScreens()" style="text-align:center;">
    <form id="radioButtons" name="screensForm"></form>
    <br />
    <input type="button" value="Center on Selected Screen" onclick="handleClick()" />
</body>
</html>

Determining the Monitors on Which an Application Is Currently Displayed

Problem

You need to determine which screens currently display your AIR application.

Solution

Use the getScreensForRectangle, which is a static method of the Screen class, to determine which screens are being used to display a given region as defined by the given rectangle instance.

Discussion

In situations where the current screen needs to be calculated, you can use the getScreensForRectangle. This static method of the Screen class takes one argument: a rectangle. When you pass in an instance of the Rectangle class, it returns an array of the screens the rectangle occupies.

In this case, the bounds property of the NativeWindow class provides the instance of the Rectangle class that is needed for the getScreensForRectangle method. By passing this value into the method, you can determine which screens the referenced window occupies.

In the following examples, an event listener is attached to the native window’s moving event. This means that each time the application window is moved, the event listener method will be called. In the handleMove method, the currently occupied screens are calculated and then displayed in the application.

ActionScript

In this example, the list of the currently occupied screens is stored in an ArrayCollection, which is bound to a List and updated in the handleMove listener method for the moving event when the window is moved. This listener method is also added as a listener to the creationComplete event to ensure it is executed when the application is launched and rendered.

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication
    xmlns:mx="http://www.adobe.com/2006/mxml"
    layout="vertical"
    moving="handleMove(event)"
    creationComplete="handleMove(event)">
    width="320" height="240">

    <mx:Script>
          <![CDATA[
               import mx.collections.ArrayCollection;

               [Bindable]
               private var monitors:ArrayCollection = new ArrayCollection();

               private function handleMove(event:Event):void {
                     monitors.removeAll();
                     var windowBounds:Rectangle = this.nativeWindow.bounds;
                     var screens:Array = Screen.getScreensForRectangle(windowBounds);

                     var output:String = "";
                     var screen:Screen;
                     for( var i:uint = 0; i < screens.length; i++ ) {
                          screen = screens[i];
                          output = "Screen " + i + " - ";
                          output += "Width: " + screen.bounds.width;
                          output += " Height: " + screen.bounds.height;
                          monitors.addItem({ label: output });
                     }
               }
          ]]>
    </mx:Script>

    <mx:Label text="Occupied Screens" fontWeight="bold" />
    <mx:List id="monitorsList" dataProvider="{monitors}" width="100%" height="100%" />


</mx:WindowedApplication>

JavaScript

In this example, the event listener, handleMove, is registered for the AIR moving event and also for the JavaScript onload event of the page body. The method is called any time the window is moved and also when the page is loaded. It determines the currently occupied screens before adding a new div to the main listing div for each of the screens that is returned from the call to getScreensForRectangle.

<html>
<head>
    <title>Entry 4.3 - Get Current Application Screens</title>

    <script type="text/javascript" src="AIRAliases.js"></script>
    <script type="text/javascript">
          window.nativeWindow.addEventListener(air.NativeWindowBoundsEvent.MOVING, 
handleMove);

          function handleMove(event) {
               var listing = document.getElementById('listing');
               while(listing.hasChildNodes()) {
                     listing.removeChild(listing.firstChild);
               }
               var screens = 
air.Screen.getScreensForRectangle(window.nativeWindow.bounds);
               for(i=0;i < screens.length;i++) {
                     var newDiv = document.createElement('div');
                     newDiv.innerHTML += "Screen " + i;
                     newDiv.innerHTML += " WIDTH: " + screens[i].bounds.width;
                     newDiv.innerHTML += " HEIGHT: " + screens[i].bounds.height;
                     listing.appendChild(newDiv);
               }
          }
    </script>
</head>

<body onload="handleMove(event)" style="text-align:center;">
    <strong>Occupied Screens</strong>
    <div id="listing"></div>
</body>
</html>

Get Adobe AIR 1.5 Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.