Showing posts with label class. Show all posts
Showing posts with label class. Show all posts

Thursday, February 5, 2015

Creating EasyTooltip class Part 21

In this tutorial well work on removing listeners and tooltips.

First of all, go to TooltipListener class and update the kill() function. Right now it only removes the 2 mouse event listeners. It should also stop the timer, remove its listener, set it to null and set the tooltip to null.

public function kill():void {
list.removeEventListener(MouseEvent.ROLL_OVER, onOver);
list.removeEventListener(MouseEvent.ROLL_OUT, onOut);
timer.removeEventListener(TimerEvent.TIMER_COMPLETE, onTimer);
timer.stop();
timer = null;
tip = null;
}

Full TooltipListener class:

package com.kircode.EasyTooltip 
{
import flash.display.DisplayObject;
import flash.events.MouseEvent;
import flash.events.TimerEvent;
import flash.utils.Timer;

/**
* Manages a single visible object that displays a tooltip when rolled over.
* @author Kirill Poletaev
*/
public class TooltipListener
{

public var tip:Tooltip;
public var list:DisplayObject;
private var timer:Timer;

public function TooltipListener(listener:DisplayObject, tooltip:Tooltip, delay:int)
{
list = listener;
tip = tooltip;
timer = new Timer(delay, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimer);
listener.addEventListener(MouseEvent.ROLL_OVER, onOver);
listener.addEventListener(MouseEvent.ROLL_OUT, onOut);
}

public function kill():void {
list.removeEventListener(MouseEvent.ROLL_OVER, onOver);
list.removeEventListener(MouseEvent.ROLL_OUT, onOut);
timer.removeEventListener(TimerEvent.TIMER_COMPLETE, onTimer);
timer.stop();
timer = null;
tip = null;
}

private function onOver(evt:MouseEvent):void {
timer.reset();
timer.start();
}

private function onOut(evt:MouseEvent):void {
tip.display = false;
timer.stop();
}

private function onTimer(evt:TimerEvent):void {
tip.display = true;
}

}

}

Now go to EasyTooltip class and add a function called removeListener(). Give it a parameter "listener", which is the DisplayObject instance that has a tooltip listener. In the function, we loop through the listeners and compare each "list" property of the listener in the array to the listener in the parameter. If they match, remove that listener using kill() method of the TooltipListener instance, set it to null, and remove the item from the array.

/**
* Removes a Tooltip listener.
* @paramlistener The object that invokes tooltip on roll over.
*/

public function removeListener(listener:DisplayObject):void {
for (var i:int = listeners.length-1; i >= 0; i--) {
if (listener == listeners[i].list) {
listeners[i].kill();
listeners[i] = null;
listeners.splice(i, 1);
}
}
}

Add a new function called kill(), which loops through the listeners and kills each one. Then remove the TooltipCursor, the ENTER_FRAME listener and set the cursor to null.

/**
* Completely removes the EasyTooltip manager, all the listeners and tooltip cursor.
*/

public function kill():void {
for (var i:int = listeners.length-1; i >= 0; i--) {
listeners[i].kill();
listeners[i] = null;
listeners.splice(i, 1);
}
(par as DisplayObjectContainer).removeChild(cursor);
cursor.removeEventListener(Event.ENTER_FRAME, onFrame);
cursor = null;
}

Full EasyTooltip code:

package com.kircode.EasyTooltip 
{
import flash.display.DisplayObjectContainer;
import flash.display.DisplayObject;
import flash.events.Event;
/**
* Utility for creation of tooltips.
* @author Kirill Poletaev
*/
public class EasyTooltip
{

private var par:DisplayObject;
public var listeners:Array;
private var cursor:TooltipCursor;
private var parW:int;
private var parH:int;
private var delay:int;
private var smooth:Number;

/**
* Create a Tooltip manager. Needed for creating and managing tooltips.
* @paramparent Reference to the parent of tooltips - the container, which will contain the objects that will be rolled over.
* @paramparentWidth Width of the parent.
* @paramparentHeight Height of the parent.
* @paramsmoothMovement The movement smoothener - the bigger the value, the smoother the movement. 1 = no smoothment.
* @paramspawnDelay Delay in milliseconds of how long the user has to hold their mouse over the object to invoke a tooltip.
*/

public function EasyTooltip(parent:DisplayObjectContainer, parentWidth:int, parentHeight:int, smoothMovement:Number = 3, spawnDelay:int = 0)
{
smooth = smoothMovement;
delay = spawnDelay;
par = parent;
listeners = [];
parW = parentWidth;
parH = parentHeight;
cursor = new TooltipCursor(parentWidth, parentHeight, smoothMovement);
cursor.mouseChildren = false;
cursor.mouseEnabled = false;
parent.addChild(cursor);
cursor.addEventListener(Event.ENTER_FRAME, onFrame);
setStyle(new TooltipStyle());
trace("Tooltip manager created!");
}

/**
* Set a new style for the tooltips.
* @paramstyle TooltipStyle object that defines the style.
*/

public function setStyle(style:TooltipStyle):void {
cursor.setStyle(style);
}

/**
* Add a Tooltip listener.
* @paramlistener Object, which invokes the tooltip on roll over.
* @paramtooltip Message to be displayed.
* @returnNewly created Tooltip object. Can be used to dynamically change its properties in real-time.
*/

public function addListener(listener:DisplayObject, tooltip:String):Tooltip {
var tip:Tooltip = new Tooltip(tooltip);
var list:TooltipListener = new TooltipListener(listener, tip, delay);
listeners.push(list);
return tip;
}

/**
* Removes a Tooltip listener.
* @paramlistener The object that invokes tooltip on roll over.
*/

public function removeListener(listener:DisplayObject):void {
for (var i:int = listeners.length-1; i >= 0; i--) {
if (listener == listeners[i].list) {
listeners[i].kill();
listeners[i] = null;
listeners.splice(i, 1);
}
}
}

/**
* Completely removes the EasyTooltip manager, all the listeners and tooltip cursor.
*/

public function kill():void {
for (var i:int = listeners.length-1; i >= 0; i--) {
listeners[i].kill();
listeners[i] = null;
listeners.splice(i, 1);
}
(par as DisplayObjectContainer).removeChild(cursor);
cursor.removeEventListener(Event.ENTER_FRAME, onFrame);
cursor = null;
}

private function onFrame(evt:Event):void {
cursor.x += (par.mouseX - cursor.x)/smooth;
cursor.y += (par.mouseY - cursor.y)/smooth;
var displayInd:int = -1;
for (var i:int = 0; i < listeners.length; i++) {
if (listeners[i].tip.display) {
displayInd = i;
break;
}
}
if (displayInd == -1) {
cursor.alpha = 0;
}else {
cursor.alpha = 1;
cursor.setText(listeners[displayInd].tip.msg);
}
}

}

}

Thats all for today.

Thanks for reading!
Read more »

Wednesday, February 4, 2015

Creating Advanced Alert Window class Part 18

Today well improve the skinning capabilities for AdvAlertButton objects.

Start by going to AdvAlertButton.as and declare 5 new variables:

private var textFormat:TextFormat;
private var bgStroke:*;
private var bgRect:*;
private var pos:Point;
private var textPadding:TextPadding;

Modify the constructor so that it doesnt erceive width and height values anymore, and doesnt set their values to w and h variables. Instead, call a setSkin() method.

/**
* Button object placed in the alert window.
* @paramlabel Text label of the button.
*/

public function AdvAlertButton(label:String)
{
txt = label;
textField = new TextField();
addChild(textField);

setSkin();

updateDraw();
}

In setSkin(), apply values to all the skinning variables we have.

public function setSkin():void {
w = 80;
h = 30;
textFormat = new TextFormat("Arial", 16, 0xffffff);
textFormat.align = "center";
bgStroke = new SolidColorStroke(2, 0x333399, 1);
bgRect = new SolidColorRect(0x6666ff, [3, 3, 3, 3], 1);
pos = new Point(0, 0);
textPadding = new TextPadding(3, 0, 3, 0);
}

Now go to updateDraw() function. Here, add the piece of code from AdvAlertWindows which draws background box based on the values:

// bg stroke
if (bgStroke is SolidColorStroke) {
this.graphics.lineStyle(bgStroke._lineThickness, bgStroke._lineColor, bgStroke._lineAlpha);
}
if (bgStroke is GradientColorStroke) {
this.graphics.lineStyle(bgStroke._lineThickness);
this.graphics.lineGradientStyle(bgStroke._gradientType, bgStroke._colors, bgStroke._alphas, bgStroke._ratios, bgStroke._matrix, bgStroke._spreadMethod, bgStroke._interpolationMethod, bgStroke._focalPointRatio);
}
if (bgStroke is BitmapStroke) {
this.graphics.lineStyle(bgStroke._lineThickness);
this.graphics.lineBitmapStyle(bgStroke._bitmap, bgStroke._matrix, bgStroke._repeat, bgStroke._smooth);
}

// bg fill
if (bgRect is SolidColorRect) this.graphics.beginFill(bgRect._backgroundColor, bgRect._alpha);
if (bgRect is GradientColorRect) this.graphics.beginGradientFill(bgRect._gradientType, bgRect._colors, bgRect._alphas, bgRect._ratios, bgRect._matrix, bgRect._spreadMethod, bgRect._interpolationMethod, bgRect._focalPointRatio);
if (bgRect is BitmapRect) this.graphics.beginBitmapFill(bgRect._bitmap, bgRect._matrix, bgRect._repeat, bgRect._smooth);

this.graphics.drawRoundRectComplex(pos.x, pos.y, w, h,
bgRect._radius[0], bgRect._radius[1], bgRect._radius[2], bgRect._radius[3]);
this.graphics.endFill();

Apply values to textField as well:

// text field
textField.width = w;
textField.height = h;
textField.text = txt;
textField.setTextFormat(textFormat);
textField.selectable = false;
textField.x = pos.x + textPadding.right;
textField.y = pos.y + textPadding.top;

Full class:

package com.kircode.AdvAlert 
{
import flash.display.MovieClip;
import flash.geom.Point;
import flash.text.TextField;
import flash.text.TextFormat;

/**
* Advanced Alert window object.
* @author Kirill Poletaev
*/
public class AdvAlertButton extends MovieClip
{

public var w:Number;
public var h:Number;
private var textFormat:TextFormat;
private var bgStroke:*;
private var bgRect:*;
private var pos:Point;
private var textPadding:TextPadding;

private var textField:TextField;
private var txt;

/**
* Button object placed in the alert window.
* @paramlabel Text label of the button.
*/

public function AdvAlertButton(label:String)
{
txt = label;
textField = new TextField();
addChild(textField);

setSkin();

updateDraw();
}

public function setSkin():void {
w = 80;
h = 30;
textFormat = new TextFormat("Arial", 16, 0xffffff);
textFormat.align = "center";
bgStroke = new SolidColorStroke(2, 0x333399, 1);
bgRect = new SolidColorRect(0x6666ff, [3, 3, 3, 3], 1);
pos = new Point(0, 0);
textPadding = new TextPadding(3, 0, 3, 0);
}

public function updateDraw():void {
this.graphics.clear();

// bg stroke
if (bgStroke is SolidColorStroke) {
this.graphics.lineStyle(bgStroke._lineThickness, bgStroke._lineColor, bgStroke._lineAlpha);
}
if (bgStroke is GradientColorStroke) {
this.graphics.lineStyle(bgStroke._lineThickness);
this.graphics.lineGradientStyle(bgStroke._gradientType, bgStroke._colors, bgStroke._alphas, bgStroke._ratios, bgStroke._matrix, bgStroke._spreadMethod, bgStroke._interpolationMethod, bgStroke._focalPointRatio);
}
if (bgStroke is BitmapStroke) {
this.graphics.lineStyle(bgStroke._lineThickness);
this.graphics.lineBitmapStyle(bgStroke._bitmap, bgStroke._matrix, bgStroke._repeat, bgStroke._smooth);
}

// bg fill
if (bgRect is SolidColorRect) this.graphics.beginFill(bgRect._backgroundColor, bgRect._alpha);
if (bgRect is GradientColorRect) this.graphics.beginGradientFill(bgRect._gradientType, bgRect._colors, bgRect._alphas, bgRect._ratios, bgRect._matrix, bgRect._spreadMethod, bgRect._interpolationMethod, bgRect._focalPointRatio);
if (bgRect is BitmapRect) this.graphics.beginBitmapFill(bgRect._bitmap, bgRect._matrix, bgRect._repeat, bgRect._smooth);

this.graphics.drawRoundRectComplex(pos.x, pos.y, w, h,
bgRect._radius[0], bgRect._radius[1], bgRect._radius[2], bgRect._radius[3]);
this.graphics.endFill();

// text field
textField.width = w;
textField.height = h;
textField.text = txt;
textField.setTextFormat(textFormat);
textField.selectable = false;
textField.x = pos.x + textPadding.right;
textField.y = pos.y + textPadding.top;
}


}

}

We now have skinnable buttons! Only default skins work right now, though. Well work on implementing custom skinning next time!

For now, you can test out the buttons with two simple lines of code in main.as:

AlertManager = new AdvAlertManager(this, stage.stageWidth, stage.stageHeight);
AlertManager.alert("This is an alert window with the default skin.", "Example alert!", [new AdvAlertButton("OK"), new AdvAlertButton("Cancel"), new AdvAlertButton("Back")]);

Thanks for reading!
Read more »

Creating Advanced Alert Window class Part 27

In this tutorial we will polish our tab navigation functionality, fix some bugs, add a default button, and add a feature that auto-sizes the window (in height) depending on the size of the text provided, if provided window height is 0.

First well fix some tab navigation code problems.

Go to AdvAlertManager and find the buttonHandler function inside alert(), which is executed when a button is pressed.

Here, add a line that sets focusedButton back to -1. Also, in the line that sets topWindow value, were currently referring to a "w" property, when it should be "window":

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);
focusedButton = -1;
if (windows.length > 0) topWindow = windows[windows.length - 1].window;
if (windows.length == 0) topWindow = null;
if (tabDisable && windows.length==0) {
defaultContainer.tabChildren = true;
defaultContainer.tabEnabled = true;
}
}

Now go to kDown() function and find the second if...statement. Here, also add the focusedButton = -1 line and fix the topWindow line.

Moreover, change the parameters that are passed to closeHandler function when its called - send topWindow.buttons[focusedButton] and topWindow.buttons[focusedButton].txt.

Also, in the beginning of the if... statement, add another conditional that checks if focusedButton is -1. If so, set it to 0.

if (evt.keyCode == 13 && topWindow != null && topWindow.buttons.length > 0) {
if (focusedButton == -1) {
focusedButton = 0;
}
var currentIndex:int = windows.length - 1;
var closeHandler:Function = windows[currentIndex].onclose;
if (closeHandler != null) closeHandler.call(topWindow.buttons[focusedButton], topWindow.buttons[focusedButton].txt);
windows[currentIndex].window.parent.removeChild(windows[currentIndex].window);
windows[currentIndex].blur.parent.removeChild(windows[currentIndex].blur);
windows.splice(currentIndex, 1);
focusedButton = -1;
if (windows.length > 0) topWindow = windows[windows.length - 1].window;
if (windows.length == 0) topWindow = null;
if (tabDisable && windows.length==0) {
defaultContainer.tabChildren = true;
defaultContainer.tabEnabled = true;
}
}

Full kDown() function:

private function kDown(evt:KeyboardEvent):void {
if (evt.keyCode == 9 && topWindow!=null && topWindow.buttons.length>0) {
focusedButton++;
if (focusedButton == topWindow.buttons.length) {
focusedButton = 0;
}
for (var i:int = 0; i < topWindow.buttons.length; i++) {
topWindow.buttons[i].over = false;
topWindow.buttons[i].updateDraw();
}
topWindow.buttons[focusedButton].over = true;
topWindow.buttons[focusedButton].updateDraw();
}
if (evt.keyCode == 13 && topWindow != null && topWindow.buttons.length > 0) {
if (focusedButton == -1) {
focusedButton = 0;
}
var currentIndex:int = windows.length - 1;
var closeHandler:Function = windows[currentIndex].onclose;
if (closeHandler != null) closeHandler.call(topWindow.buttons[focusedButton], topWindow.buttons[focusedButton].txt);
windows[currentIndex].window.parent.removeChild(windows[currentIndex].window);
windows[currentIndex].blur.parent.removeChild(windows[currentIndex].blur);
windows.splice(currentIndex, 1);
focusedButton = -1;
if (windows.length > 0) topWindow = windows[windows.length - 1].window;
if (windows.length == 0) topWindow = null;
if (tabDisable && windows.length==0) {
defaultContainer.tabChildren = true;
defaultContainer.tabEnabled = true;
}
}
}

Now, we need to add the ability to apply 0 value as window height, and autosize the window if 0 is specified. We will also have 0 as the default value. Well need to do a few changes to achieve this.

First of all, go to alert() function and set default value of the height parameter to 0:

public function alert(text:String, title:String = "", buttons:Array = null, closeHandler:Function = null, width:int = 300, height:int = 0, position:Point = null):void {

Then find the line that declares AdvAlertWindow object, pass 2 more parameters to the constructor - pWidth and pHeight.

var w:AdvAlertWindow = new AdvAlertWindow(text, title, width, height, position, skin, buttons, bSkin, pWidth, pHeight);

Also, when checking of buttons array is null, instead of setting it to an empty array, set it to an array of 1 default button "OK":

if (buttons == null) buttons = [new AdvAlertButton("OK")];

Full AdvAlertManager code so far:

package com.kircode.AdvAlert 
{
import flash.display.DisplayObjectContainer;
import flash.display.MovieClip;
import flash.display.Stage;
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;
private var focusedButton:int;
private var stg:Stage;

/**
* AdvAlertManager constructor.
* @paramdefaultWindowContainer Parent container of alert windows.
* @paramstage Stage reference.
* @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 windows buttons.
*/

public function AdvAlertManager(defaultWindowContainer:DisplayObjectContainer, stage:Stage, 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 = stage.stageWidth;
pHeight = stage.stageHeight;
stg = stage;
windows = [];
if (tabDisable) {
stg.addEventListener(KeyboardEvent.KEY_DOWN, kDown);
}
}

private function kDown(evt:KeyboardEvent):void {
if (evt.keyCode == 9 && topWindow!=null && topWindow.buttons.length>0) {
focusedButton++;
if (focusedButton == topWindow.buttons.length) {
focusedButton = 0;
}
for (var i:int = 0; i < topWindow.buttons.length; i++) {
topWindow.buttons[i].over = false;
topWindow.buttons[i].updateDraw();
}
topWindow.buttons[focusedButton].over = true;
topWindow.buttons[focusedButton].updateDraw();
}
if (evt.keyCode == 13 && topWindow != null && topWindow.buttons.length > 0) {
if (focusedButton == -1) {
focusedButton = 0;
}
var currentIndex:int = windows.length - 1;
var closeHandler:Function = windows[currentIndex].onclose;
if (closeHandler != null) closeHandler.call(topWindow.buttons[focusedButton], topWindow.buttons[focusedButton].txt);
windows[currentIndex].window.parent.removeChild(windows[currentIndex].window);
windows[currentIndex].blur.parent.removeChild(windows[currentIndex].blur);
windows.splice(currentIndex, 1);
focusedButton = -1;
if (windows.length > 0) topWindow = windows[windows.length - 1].window;
if (windows.length == 0) topWindow = null;
if (tabDisable && windows.length==0) {
defaultContainer.tabChildren = true;
defaultContainer.tabEnabled = true;
}
}
}

/**
* 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. If 0, window will be auto sized.
* @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 = 0, position:Point = null):void {
if (position == null) position = new Point((pWidth / 2) - (width / 2), (pHeight / 2) - (height / 2));
if (buttons == null) buttons = [new AdvAlertButton("OK")];
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, pWidth, pHeight);
var b:AdvAlertBlur = new AdvAlertBlur(pWidth, pHeight, skin);
var currentIndex:int = windows.length;
windows.push( {window:w, blur:b, onclose:closeHandler} );
defaultContainer.addChild(b);
defaultContainer.addChild(w);

topWindow = w;
focusedButton = -1;
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);
focusedButton = -1;
if (windows.length > 0) topWindow = windows[windows.length - 1].window;
if (windows.length == 0) topWindow = null;
if (tabDisable && windows.length==0) {
defaultContainer.tabChildren = true;
defaultContainer.tabEnabled = true;
}
}
}

}

}

Now go to AdvAlertWindow class.

Here, declare 2 new variables - parw and parh (to store the width and height of the parent):

private var parw:int;
private var parh:int;

Receive the 2 new parameters in the constructor:

public function AdvAlertWindow(pt:String, ptt:String, pw:int, ph:int, ppos:Point, sk:AdvAlertSkin, bt:Array, defaultButtonSkin:AdvAlertButtonSkin, parentw:int, parenth:int) 

Apply the values in constructor too:

parw = parentw;
parh = parenth;

In the end of the constructor, check if ph (the height of the window) equals 0. If so, call a function autoSize():

if (ph == 0) autoSize();

Full constructor:

public function AdvAlertWindow(pt:String, ptt:String, pw:int, ph:int, ppos:Point, sk:AdvAlertSkin, bt:Array, defaultButtonSkin:AdvAlertButtonSkin, parentw:int, parenth:int) 
{
t = pt;
tt = ptt;
w = pw;
h = ph;
pos = ppos;
buttons = bt;
parw = parentw;
parh = parenth;

setSkin(sk);

textField = new TextField();
addChild(textField);

titleField = new TextField();
addChild(titleField);
for (var i:int = 0; i < buttons.length; i++) {
if (buttons[i].currentSkin == null) {
buttons[i].setSkin(defaultButtonSkin);
buttons[i].updateDraw();
}
addChild(buttons[i]);
}

updateDraw();

if (ph == 0) autoSize();
}

Create the autoSize() function, calculate the new height and y position of the window, then call updateDraw(). Use the textFields textHeight property in the calculations to find out how big the text is.

private function autoSize():void {
var textHeight:int = textField.textHeight;
h = headerHeight + headerPadding.top + headerPadding.bottom + buttonbarHeight + buttonbarPadding.top + buttonbarPadding.bottom + textPadding.top + textPadding.bottom + textHeight;
pos.y = parh / 2 - h / 2;
updateDraw();
}

Full AdvAlertWindow.as code:

package com.kircode.AdvAlert 
{
import flash.display.MovieClip;
import flash.events.Event;
import flash.geom.Point;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.display.GradientType;

/**
* Advanced Alert window object.
* @author Kirill Poletaev
*/
public class AdvAlertWindow extends MovieClip
{
private var t:String;
private var tt:String;
private var w:int;
private var h:int;
private var pos:Point;
private var parw:int;
private var parh:int;
public var buttons:Array;

private var textField:TextField;
private var titleField:TextField;

private var textPadding:TextPadding;
private var headerPadding:TextPadding;
private var titlePadding:TextPadding;
private var headerHeight:Number;
private var buttonbarPadding:TextPadding;
private var buttonbarHeight:Number;
private var titleFormat:TextFormat;
private var textFormat:TextFormat;
private var selectable:Boolean;
private var headerRect:*;
private var bgRect:*;
private var headerStroke:*;
private var bgStroke:*;
private var skinFilters:Array;
private var buttonInterval:Number;
private var defaultButtonSkin:AdvAlertButtonSkin;
private var buttonbarAlign:String;

public function AdvAlertWindow(pt:String, ptt:String, pw:int, ph:int, ppos:Point, sk:AdvAlertSkin, bt:Array, defaultButtonSkin:AdvAlertButtonSkin, parentw:int, parenth:int)
{
t = pt;
tt = ptt;
w = pw;
h = ph;
pos = ppos;
buttons = bt;
parw = parentw;
parh = parenth;

setSkin(sk);

textField = new TextField();
addChild(textField);

titleField = new TextField();
addChild(titleField);
for (var i:int = 0; i < buttons.length; i++) {
if (buttons[i].currentSkin == null) {
buttons[i].setSkin(defaultButtonSkin);
buttons[i].updateDraw();
}
addChild(buttons[i]);
}

updateDraw();

if (ph == 0) autoSize();
}

public function setSkin(skin:AdvAlertSkin):void {
textPadding = skin.textPadding;
headerPadding = skin.headerPadding;
titlePadding = skin.titlePadding;
headerHeight = skin.headerHeight;
titleFormat = skin.titleFormat;
textFormat = skin.textFormat;
selectable = skin.selectable;
bgRect = skin.bgRect;
headerRect = skin.headerRect;
bgStroke = skin.bgStroke;
headerStroke = skin.headerStroke;
skinFilters = skin.filters;
buttonbarHeight = skin.buttonbarHeight;
buttonbarPadding = skin.buttonbarPadding;
buttonInterval = skin.buttonInterval;
buttonbarAlign = skin.buttonbarAlign;
}

private function autoSize():void {
var textHeight:int = textField.textHeight;
h = headerHeight + headerPadding.top + headerPadding.bottom + buttonbarHeight + buttonbarPadding.top + buttonbarPadding.bottom + textPadding.top + textPadding.bottom + textHeight;
pos.y = parh / 2 - h / 2;
updateDraw();
}

public function updateDraw():void {
this.graphics.clear();
// filters
this.filters = skinFilters;

// bg stroke
if (bgStroke is SolidColorStroke) {
this.graphics.lineStyle(bgStroke._lineThickness, bgStroke._lineColor, bgStroke._lineAlpha);
}
if (bgStroke is GradientColorStroke) {
this.graphics.lineStyle(bgStroke._lineThickness);
this.graphics.lineGradientStyle(bgStroke._gradientType, bgStroke._colors, bgStroke._alphas, bgStroke._ratios, bgStroke._matrix, bgStroke._spreadMethod, bgStroke._interpolationMethod, bgStroke._focalPointRatio);
}
if (bgStroke is BitmapStroke) {
this.graphics.lineStyle(bgStroke._lineThickness);
this.graphics.lineBitmapStyle(bgStroke._bitmap, bgStroke._matrix, bgStroke._repeat, bgStroke._smooth);
}

// bg fill
if (bgRect is SolidColorRect) this.graphics.beginFill(bgRect._backgroundColor, bgRect._alpha);
if (bgRect is GradientColorRect) this.graphics.beginGradientFill(bgRect._gradientType, bgRect._colors, bgRect._alphas, bgRect._ratios, bgRect._matrix, bgRect._spreadMethod, bgRect._interpolationMethod, bgRect._focalPointRatio);
if (bgRect is BitmapRect) this.graphics.beginBitmapFill(bgRect._bitmap, bgRect._matrix, bgRect._repeat, bgRect._smooth);

this.graphics.drawRoundRectComplex(pos.x, pos.y, w, h,
bgRect._radius[0], bgRect._radius[1], bgRect._radius[2], bgRect._radius[3]);
this.graphics.endFill();

// header stroke
if (headerStroke is SolidColorStroke) {
this.graphics.lineStyle(headerStroke._lineThickness, headerStroke._lineColor, headerStroke._lineAlpha);
}
if (headerStroke is GradientColorStroke) {
this.graphics.lineStyle(headerStroke._lineThickness);
this.graphics.lineGradientStyle(headerStroke._gradientType, headerStroke._colors, headerStroke._alphas, headerStroke._ratios, headerStroke._matrix, headerStroke._spreadMethod, headerStroke._interpolationMethod, headerStroke._focalPointRatio);
}
if (headerStroke is BitmapStroke) {
this.graphics.lineStyle(headerStroke._lineThickness);
this.graphics.lineBitmapStyle(headerStroke._bitmap, headerStroke._matrix, headerStroke._repeat, headerStroke._smooth);
}

// header fill
if (headerRect is SolidColorRect) this.graphics.beginFill(headerRect._backgroundColor, headerRect._alpha);
if (headerRect is GradientColorRect) this.graphics.beginGradientFill(headerRect._gradientType, headerRect._colors, headerRect._alphas, headerRect._ratios, headerRect._matrix, headerRect._spreadMethod, headerRect._interpolationMethod, headerRect._focalPointRatio);
if (headerRect is BitmapRect) this.graphics.beginBitmapFill(headerRect._bitmap, headerRect._matrix, headerRect._repeat, headerRect._smooth);

this.graphics.drawRoundRectComplex(pos.x + headerPadding.left, pos.y + headerPadding.top, w - (headerPadding.left + headerPadding.right), headerHeight,
headerRect._radius[0], headerRect._radius[1], headerRect._radius[2], headerRect._radius[3]);
this.graphics.endFill();

// title
titleField.width = w - (headerPadding.left + headerPadding.right);
titleField.text = tt;
titleField.height = headerHeight;
titleField.x = pos.x + headerPadding.left + titlePadding.left;
titleField.y = pos.y + headerPadding.top + titlePadding.top;

// text
textField.width = w - (textPadding.right + textPadding.left);
textField.height = h - (textPadding.top + textPadding.bottom + headerPadding.top + headerPadding.bottom + headerHeight + buttonbarHeight + buttonbarPadding.top + buttonbarPadding.bottom);
textField.text = t;
textField.x = pos.x + textPadding.right;
textField.y = pos.y + textPadding.top + headerHeight + headerPadding.bottom + headerPadding.top;

// formats
textField.setTextFormat(textFormat);
titleField.setTextFormat(titleFormat);
textField.selectable = selectable;
titleField.selectable = selectable;
textField.multiline = true;
textField.wordWrap = true;

// buttons
var xCoord:Number;
var i:int

// right align
if(buttonbarAlign=="right"){
if (buttons.length > 0) xCoord = pos.x + w - buttons[buttons.length-1].w - buttonbarPadding.right;
for (i = buttons.length-1; i >= 0; i--) {
if (i != buttons.length-1) xCoord -= buttons[i].w + buttonInterval;
buttons[i].x = xCoord;
buttons[i].y = pos.y + h - buttons[i].h - buttonbarPadding.bottom;
}
}

// left align
if(buttonbarAlign=="left"){
if (buttons.length > 0) xCoord = pos.x + buttonbarPadding.left;
for (i = 0; i < buttons.length; i++) {
if (i > 0) xCoord += buttons[i].w + buttonInterval;
buttons[i].x = xCoord;
buttons[i].y = pos.y + h - buttons[i].h - buttonbarPadding.bottom;
}
}

// center align
if(buttonbarAlign=="center"){
if (buttons.length > 0) {
var buttonbarWidth:Number = 0;
for (i = 0; i < buttons.length; i++) {
buttonbarWidth += buttons[i].w + buttonInterval;
}
buttonbarWidth -= buttonInterval;
xCoord = pos.x + (w/2) - buttonbarWidth/2;
}
for (i = 0; i < buttons.length; i++) {
if (i > 0) xCoord += buttons[i].w + buttonInterval;
buttons[i].x = xCoord;
buttons[i].y = pos.y + h - buttons[i].h - buttonbarPadding.bottom;
}
}
}


}

}

Now you can go to main.as and see the code in action by creating alert windows of different text size, with unspecified height and buttons:

AlertManager = new AdvAlertManager(this, stage);
AlertManager.alert("This is an alert window with the default skin.", "Example alert!");
AlertManager.alert("This is an alert window with the default skin. More text here! More text here! More text here! More text here! More text here!More text here! More text here!", "Example alert 2!");

You can see that they are both navigateable with tab/enter, both are automatically sized to fit the text, both have default OK button.

Thats all for now, thanks for reading!
Read more »

Tuesday, February 3, 2015

Creating Advanced Alert Window class Part 2

In this tutorial well add basic window drawing code, as well as the ability to set size and position of the window.

Start by opening AdvAlertManager.as class and adding 4 new variables:

private var windows:Array;
private var defaultContainer:DisplayObjectContainer;
private var pWidth:int;
private var pHeight:int;

The windows array will hold references to all the windows. The defaultContainer value will be passed to the object through constructor - this will be the reference to the parent of the windows. The pWidth and pHeight values are also passed through constructor. These represent the width and height of the parent.

Now, receive these values and set them to respective variables in constructor:

public function AdvAlertManager(defaultWindowContainer:DisplayObjectContainer, parentWidth:int, parentHeight:int) 
{
trace(": AdvAlertManager instance created.");

defaultContainer = defaultWindowContainer;
pWidth = parentWidth;
pHeight = parentHeight;
windows = [];
}

Now well add 3 new parameters to the alert() method. All of them are optional. These are: width, height and position. The first two values are integers, and the third one is a Point. Set their defaults to 300, 200 and null. In the function, we check if position is null (was not specified by the user), and calculate position to center the window.

Then create a new instance of AdvAlertWindow class, pass 4 values to the constructor - text, width, height and position.

Push this object to windows array and add it to defaultContainer.

public function alert(text:String, width:int = 300, height:int = 200, position:Point = null):void {
if (position == null) position = new Point((pWidth / 2) - (width / 2), (pHeight / 2) - (height / 2));
var w:AdvAlertWindow = new AdvAlertWindow(text, width, height, position);
windows.push(w);
defaultContainer.addChild(w);

trace("Message: " + text);
}

Full code so far:

package com.kircode 
{
import flash.display.DisplayObjectContainer;
import flash.display.MovieClip;
import flash.geom.Point;
/**
* 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;

/**
* AdvAlertManager constructor.
* @paramdefaultWindowContainer Parent container of alert windows.
* @paramparentWidth Containers width.
* @paramparentHeight Containers height.
*/


public function AdvAlertManager(defaultWindowContainer:DisplayObjectContainer, parentWidth:int, parentHeight:int)
{
trace(": AdvAlertManager instance created.");

defaultContainer = defaultWindowContainer;
pWidth = parentWidth;
pHeight = parentHeight;
windows = [];
}

/**
* Create an alert window.
* @paramtext Text value of the alert window.
* @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, width:int = 300, height:int = 200, position:Point = null):void {
if (position == null) position = new Point((pWidth / 2) - (width / 2), (pHeight / 2) - (height / 2));
var w:AdvAlertWindow = new AdvAlertWindow(text, width, height, position);
windows.push(w);
defaultContainer.addChild(w);

trace("Message: " + text);
}

}

}

Now create a new file in the same directory as AdvAlertManager.as - AdvAlertWindow.as.

Make this class extend MovieClip. Declare 4 private variables, which will hold text, width, height and position values:

private var t:String;
private var w:int;
private var h:int;
private var pos:Point;

Set values to these variables in the constructor. In the end of the constructor code, call updateDraw():

public function AdvAlertWindow(pt:String, pw:int, ph:int, ppos:Point) 
{
t = pt;
w = pw;
h = ph;
pos = ppos;
updateDraw();
}

The updateDraw() function currently draws a gray rectangle based on the data:

public function updateDraw():void {
this.graphics.clear();
this.graphics.beginFill(0xcccccc, 1);
this.graphics.drawRect(pos.x, pos.y, w, h);
this.graphics.endFill();
}

Full AdvAlertWindow:

package com.kircode 
{
import flash.display.MovieClip;
import flash.geom.Point;
/**
* Advanced Alert window object.
* @author Kirill Poletaev
*/
public class AdvAlertWindow extends MovieClip
{
private var t:String;
private var w:int;
private var h:int;
private var pos:Point;

public function AdvAlertWindow(pt:String, pw:int, ph:int, ppos:Point)
{
t = pt;
w = pw;
h = ph;
pos = ppos;
updateDraw();
}

public function updateDraw():void {
this.graphics.clear();
this.graphics.beginFill(0xcccccc, 1);
this.graphics.drawRect(pos.x, pos.y, w, h);
this.graphics.endFill();
}

}

}

Now return to main.as.

Here I set up a simple example of using the features we added today. Take a look at the code:

package  
{
import com.kircode.AdvAlertManager;
import flash.display.MovieClip;
import flash.events.Event;
import flash.geom.Point;
/**
* ...
* @author Kirill Poletaev
*/
public class main extends MovieClip
{
private var AlertManager:AdvAlertManager;

public function main()
{
addEventListener(Event.ADDED_TO_STAGE, init);
}

private function init(evt:Event):void {
AlertManager = new AdvAlertManager(this, stage.stageWidth, stage.stageHeight);
AlertManager.alert("This is a test message!");
// Same results as:
// AlertManager.alert("This is a test message!", 300, 200, new Point(125,100));
}

}

}

Thats all for today!

Thanks for reading!
Read more »

Creating Advanced Alert Window class Part 26

Today we will add the ability to use the Enter key to "click" buttons in the alert window.

When we will handle the enter key, were going to need know the close handler function for that alert window to call it. We can do this by storing the handler reference in a variable and then read from that variable, or we could put it as one of the attributes of an element in windows array, next to the window and blur references.

Open AdvAlertManager.as class, go to alert() function and find the line that adds elements to the windows array, where we can add an onclose attribute and store the close handler reference there.

windows.push( {window:w, blur:b, onclose:closeHandler} );

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, onclose:closeHandler} );
defaultContainer.addChild(b);
defaultContainer.addChild(w);

topWindow = w;
focusedButton = -1;
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 go to the kDown() function, add an if..statemenet that listens for Enter key (keyCode 13). Declare 2 variables - currentIndex (set to windows.length-1 to reference the top window) and closeHandler (set to the onclose attribute of the top window in the windows array).

Then execute the same code that is executed when a button is clicked.

Full kDown() function:

private function kDown(evt:KeyboardEvent):void {
if (evt.keyCode == 9 && topWindow!=null && topWindow.buttons.length>0) {
focusedButton++;
if (focusedButton == topWindow.buttons.length) {
focusedButton = 0;
}
for (var i:int = 0; i < topWindow.buttons.length; i++) {
topWindow.buttons[i].over = false;
topWindow.buttons[i].updateDraw();
}
topWindow.buttons[focusedButton].over = true;
topWindow.buttons[focusedButton].updateDraw();
}
if (evt.keyCode == 13 && topWindow != null && topWindow.buttons.length > 0) {
var currentIndex:int = windows.length - 1;
var closeHandler:Function = windows[currentIndex].onclose;
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;
}
}
}

And full AdvAlertManager class:

package com.kircode.AdvAlert 
{
import flash.display.DisplayObjectContainer;
import flash.display.MovieClip;
import flash.display.Stage;
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;
private var focusedButton:int;
private var stg:Stage;

/**
* AdvAlertManager constructor.
* @paramdefaultWindowContainer Parent container of alert windows.
* @paramstage Stage reference.
* @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 windows buttons.
*/

public function AdvAlertManager(defaultWindowContainer:DisplayObjectContainer, stage:Stage, 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 = stage.stageWidth;
pHeight = stage.stageHeight;
stg = stage;
windows = [];
if (tabDisable) {
stg.addEventListener(KeyboardEvent.KEY_DOWN, kDown);
}
}

private function kDown(evt:KeyboardEvent):void {
if (evt.keyCode == 9 && topWindow!=null && topWindow.buttons.length>0) {
focusedButton++;
if (focusedButton == topWindow.buttons.length) {
focusedButton = 0;
}
for (var i:int = 0; i < topWindow.buttons.length; i++) {
topWindow.buttons[i].over = false;
topWindow.buttons[i].updateDraw();
}
topWindow.buttons[focusedButton].over = true;
topWindow.buttons[focusedButton].updateDraw();
}
if (evt.keyCode == 13 && topWindow != null && topWindow.buttons.length > 0) {
var currentIndex:int = windows.length - 1;
var closeHandler:Function = windows[currentIndex].onclose;
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;
}
}
}

/**
* 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, onclose:closeHandler} );
defaultContainer.addChild(b);
defaultContainer.addChild(w);

topWindow = w;
focusedButton = -1;
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 we can navigate through the alert buttons using tab and press the buttons using enter.

Thanks for reading!
Read more »