Wednesday, September 26, 2012

Creating Advanced Alert Window class: Part 24

Today we will start working on tab navigation in the alert windows.

We will let the user pass 1 more value to the AdvAlertManager object - disableTab. When this is true (and it is true by default), if an alert window is currently visible, the tab navigation for everything behind it (inside the parent container) becomes unavailable for tab navigation. When all alert windows all closed, it once again becomes possible to navigate with tabs.

When the value is true, it will also be possible to navigate the top alert window's buttons using tab.

Start by going to AdvAlertManager.as class and declaring 2 new variables - tabDisable and topWindow:

private var tabDisable:Boolean;
private var topWindow:AdvAlertWindow;

Add a new parameter in AdvAlertManager constructor - disableTab:

public function AdvAlertManager(defaultWindowContainer:DisplayObjectContainer, parentWidth:int, parentHeight:int, windowSkin:AdvAlertSkin = null, buttonSkin:AdvAlertButtonSkin = null, disableTab:Boolean = true) 

In the constructor, apply disableTab's value to tabDisable:

tabDisable = disableTab;

As I said before, we will enable tab navigation in the alert window if tabDisable is true. This means we'll need to listen to keyboard events.

Check if tabDisable is true in the constructor, and then check if the parent container's stage value is null. If it isn't, add a KeyboardEvent.KEY_DOWN listener to stage. If it is, add an Event.ADDED_TO_STAGE to the parent, with handler function onAdd():

if (tabDisable) {
if (defaultWindowContainer.stage != null) defaultWindowContainer.stage.addEventListener(KeyboardEvent.KEY_DOWN, kDown);
if (defaultWindowContainer.stage == null) defaultWindowContainer.addEventListener(Event.ADDED_TO_STAGE, onAdd);
}

Full constructor:

public function AdvAlertManager(defaultWindowContainer:DisplayObjectContainer, parentWidth:int, parentHeight:int, windowSkin:AdvAlertSkin = null, buttonSkin:AdvAlertButtonSkin = null, disableTab:Boolean = true) 
{
trace(": AdvAlertManager instance created.");
skin = windowSkin;
bSkin = buttonSkin;
tabDisable = disableTab;
if (skin == null) skin = new AdvAlertSkin();
if (bSkin == null) bSkin = new AdvAlertButtonSkin();
defaultContainer = defaultWindowContainer;
pWidth = parentWidth;
pHeight = parentHeight;
windows = [];
if (tabDisable) {
if (defaultWindowContainer.stage != null) defaultWindowContainer.stage.addEventListener(KeyboardEvent.KEY_DOWN, kDown);
if (defaultWindowContainer.stage == null) defaultWindowContainer.addEventListener(Event.ADDED_TO_STAGE, onAdd);
}
}

When the parent is added, we add the keyboard listener:

private function onAdd(evt:Event):void {
defaultContainer.stage.addEventListener(KeyboardEvent.KEY_DOWN, kDown);
}

For now let's just display the pressed key's keycode:

private function kDown(evt:KeyboardEvent):void {
trace("Key code: " + evt.keyCode)
}

Now move to alert() function. After adding window and blur objects to the parent, set topWindow to the newly added window (since it will be put on top of everythin).

Then check if tabDisable is true, and disable tab navigation for the container if so.

topWindow = w;
if (tabDisable) {
defaultContainer.tabChildren = false;
defaultContainer.tabEnabled = false;
}

In the buttonHandler() function we will check if windows array is empty after deleting the top window. If yes, set topWindow to null. If not, set topWindow to the new top window.

If tabDisable is true, and the windows array is empty, enable tab navigation for parent.

function buttonHandler(evt:MouseEvent):void {
if (closeHandler != null) closeHandler.call(evt.currentTarget, evt.currentTarget.txt);
windows[currentIndex].window.parent.removeChild(windows[currentIndex].window);
windows[currentIndex].blur.parent.removeChild(windows[currentIndex].blur);
windows.splice(currentIndex, 1);
if (windows.length > 0) topWindow = windows[windows.length - 1].w;
if (windows.length == 0) topWindow = null;
if (tabDisable && windows.length==0) {
defaultContainer.tabChildren = true;
defaultContainer.tabEnabled = true;
}
}

Full alert() function:

public function alert(text:String, title:String = "", buttons:Array = null, closeHandler:Function = null, width:int = 300, height:int = 200, position:Point = null):void {
if (position == null) position = new Point((pWidth / 2) - (width / 2), (pHeight / 2) - (height / 2));
if (buttons == null) buttons = [];
for (var i:int = buttons.length - 1; i >= 0; i--) {
if (!buttons[i] is AdvAlertButton) {
throw new Error("An item in 'buttons' array is not an AdvAlertButton instance. Ignoring...");
buttons.splice(i, 1);
}else {
buttons[i].addEventListener(MouseEvent.CLICK, buttonHandler);
}
}
var w:AdvAlertWindow = new AdvAlertWindow(text, title, width, height, position, skin, buttons, bSkin);
var b:AdvAlertBlur = new AdvAlertBlur(pWidth, pHeight, skin);
var currentIndex:int = windows.length;
windows.push( { window:w, blur:b } );
defaultContainer.addChild(b);
defaultContainer.addChild(w);

topWindow = w;
if (tabDisable) {
defaultContainer.tabChildren = false;
defaultContainer.tabEnabled = false;
}

function buttonHandler(evt:MouseEvent):void {
if (closeHandler != null) closeHandler.call(evt.currentTarget, evt.currentTarget.txt);
windows[currentIndex].window.parent.removeChild(windows[currentIndex].window);
windows[currentIndex].blur.parent.removeChild(windows[currentIndex].blur);
windows.splice(currentIndex, 1);
if (windows.length > 0) topWindow = windows[windows.length - 1].w;
if (windows.length == 0) topWindow = null;
if (tabDisable && windows.length==0) {
defaultContainer.tabChildren = true;
defaultContainer.tabEnabled = true;
}
}
}

Full AdvAlertManager class:

package com.kircode.AdvAlert 
{
import flash.display.DisplayObjectContainer;
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.events.KeyboardEvent;
import flash.events.Event;
/**
 * Advanced Alert window manager.
 * @author Kirill Poletaev
 */
public class AdvAlertManager 
{
private var windows:Array;
private var defaultContainer:DisplayObjectContainer;
private var pWidth:int;
private var pHeight:int;
private var skin:AdvAlertSkin;
private var bSkin:AdvAlertButtonSkin;
private var tabDisable:Boolean;
private var topWindow:AdvAlertWindow;

/**
 * AdvAlertManager constructor.
 * @paramdefaultWindowContainer Parent container of alert windows.
 * @paramparentWidth Container's width.
 * @paramparentHeight Container's height.
 * @paramwindowSkin Default skin for alert windows.
 * @parambuttonSkin Default skin for buttons in this window.
 * @paramdisableTab Disable tab focusing on the parent container when an alert window is visible. This also enables tab navigation for the alert window's buttons.
 */

public function AdvAlertManager(defaultWindowContainer:DisplayObjectContainer, parentWidth:int, parentHeight:int, windowSkin:AdvAlertSkin = null, buttonSkin:AdvAlertButtonSkin = null, disableTab:Boolean = true) 
{
trace(": AdvAlertManager instance created.");
skin = windowSkin;
bSkin = buttonSkin;
tabDisable = disableTab;
if (skin == null) skin = new AdvAlertSkin();
if (bSkin == null) bSkin = new AdvAlertButtonSkin();
defaultContainer = defaultWindowContainer;
pWidth = parentWidth;
pHeight = parentHeight;
windows = [];
if (tabDisable) {
if (defaultWindowContainer.stage != null) defaultWindowContainer.stage.addEventListener(KeyboardEvent.KEY_DOWN, kDown);
if (defaultWindowContainer.stage == null) defaultWindowContainer.addEventListener(Event.ADDED_TO_STAGE, onAdd);
}
}

private function onAdd(evt:Event):void {
defaultContainer.stage.addEventListener(KeyboardEvent.KEY_DOWN, kDown);
}

private function kDown(evt:KeyboardEvent):void {
trace("Key code: " + evt.keyCode)
}

/**
 * Set skin of all future created alert windows.
 * @paramwindowSkin Reference to an AdvAlertSkin object.
 */

public function setSkin(windowSkin:AdvAlertSkin):void {
skin = windowSkin;
}

/**
 * Set skin of all buttons of future created alert windows.
 * @parambuttonSkin Reference to an AdvAlertButtonSkin object.
 */

public function setButtonSkin(buttonSkin:AdvAlertButtonSkin):void {
bSkin = buttonSkin;
}

/**
 * Create an alert window.
 * @paramtext Text value of the alert window.
 * @paramtitle Title value of the alert window.
 * @parambuttons (Optional) Array of AdvAlertButton objects that represent buttons in the alert window.
 * @paramcloseHandler (Optional) Close handling function. Must receive a string value as parameter (which will hold the label of the button that was clicked).
 * @paramwidth (Optional) Width of the alert window. 300 by default.
 * @paramheight (Optional) Height of the alert window. 200 by default.
 * @paramposition (Optional) Coordinates of top-left corner of the alert window. If not specified - the window is centered.
 */

public function alert(text:String, title:String = "", buttons:Array = null, closeHandler:Function = null, width:int = 300, height:int = 200, position:Point = null):void {
if (position == null) position = new Point((pWidth / 2) - (width / 2), (pHeight / 2) - (height / 2));
if (buttons == null) buttons = [];
for (var i:int = buttons.length - 1; i >= 0; i--) {
if (!buttons[i] is AdvAlertButton) {
throw new Error("An item in 'buttons' array is not an AdvAlertButton instance. Ignoring...");
buttons.splice(i, 1);
}else {
buttons[i].addEventListener(MouseEvent.CLICK, buttonHandler);
}
}
var w:AdvAlertWindow = new AdvAlertWindow(text, title, width, height, position, skin, buttons, bSkin);
var b:AdvAlertBlur = new AdvAlertBlur(pWidth, pHeight, skin);
var currentIndex:int = windows.length;
windows.push( { window:w, blur:b } );
defaultContainer.addChild(b);
defaultContainer.addChild(w);

topWindow = w;
if (tabDisable) {
defaultContainer.tabChildren = false;
defaultContainer.tabEnabled = false;
}

function buttonHandler(evt:MouseEvent):void {
if (closeHandler != null) closeHandler.call(evt.currentTarget, evt.currentTarget.txt);
windows[currentIndex].window.parent.removeChild(windows[currentIndex].window);
windows[currentIndex].blur.parent.removeChild(windows[currentIndex].blur);
windows.splice(currentIndex, 1);
if (windows.length > 0) topWindow = windows[windows.length - 1].w;
if (windows.length == 0) topWindow = null;
if (tabDisable && windows.length==0) {
defaultContainer.tabChildren = true;
defaultContainer.tabEnabled = true;
}
}
}

}

}

Now, if we put random buttons on stage and then test our alert windows on top of them, you'll find that you can't use tab navigation on the buttons when an alert window is up. But you can, if all alert windows all closed.

Thanks for reading!

No comments:

Post a Comment