Tuesday, October 30, 2012

Creating EasyKeyboard class: Part 12

Today we'll add the ability to remove individual keyboard listeners.

We will edit each listener to add an "id" property, which can be later used to delete the listener using this id. The id value is an optional String parameter, which will be optional in each add___Listener function in the class.

First of all, we need to edit KeyListener, HoldListener, ComboListener, TimedListener and SequenceListener classes to:

1) Add new public property "id"
2) Receive an additional parameter "listenerId"
3) Apply the "listenerId" value to "id" in the constructor.

The new KeyListener class:

package com.kircode.EasyKeyboard 
{
/**
 * ...
 * @author Kirill Poletaev
 */
public class KeyListener 
{

public var keyCode:int;
public var handlerD:Function;
public var handlerU:Function;
public var alt:Boolean;
public var ctrl:Boolean;
public var shift:Boolean;
public var id:String;

public function KeyListener(code:int, funcD:Function, funcU:Function, a:Boolean, c:Boolean, s:Boolean, listenerId:String) 
{
keyCode = code;
handlerD = funcD;
handlerU = funcU;
alt = a;
ctrl = c;
shift = s;
id = listenerId;
}

}

}

HoldListener:

package com.kircode.EasyKeyboard 
{
/**
 * ...
 * @author Kirill Poletaev
 */
public class HoldListener 
{

public var keyCode:int;
public var handler:Function;
public var flag:Boolean;
public var id:String;

public function HoldListener(code:int, func:Function, boolean:Boolean, listenerId:String) 
{
keyCode = code;
handler = func;
flag = boolean;
id = listenerId;
}

}

}

ComboListener:

package com.kircode.EasyKeyboard 
{
/**
 * ...
 * @author Kirill Poletaev
 */
public class ComboListener 
{

public var keyCodes:Array;
public var handler:Function;
public var flags:Array;
public var id:String;

public function ComboListener(codes:Array, func:Function, booleans:Array, listenerId:String) 
{
keyCodes = codes;
handler = func;
flags = booleans;
id = listenerId;
}

}

}

TimedListener:

package com.kircode.EasyKeyboard 
{
import flash.events.TimerEvent;
import flash.utils.Timer;
/**
 * ...
 * @author Kirill Poletaev
 */
public class TimedListener 
{

public var keyCode:int;
public var handler:Function;
public var delayTimer:Timer;
public var repeat:int;
public var id:String;

public function TimedListener(code:int, timer:Timer, func:Function, rep:int, listenerId:String) 
{
repeat = rep;
keyCode = code;
handler = func;
delayTimer = timer;
id = listenerId;
delayTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimer);
}

private function onTimer(evt:TimerEvent):void {
if (repeat==0 || repeat==1){
if (handler != null) handler.call();
if (repeat == 1) repeat = 2;
}
}

}

}

SequenceListener:

package com.kircode.EasyKeyboard 
{
/**
 * ...
 * @author Kirill Poletaev
 */
public class SequenceListener 
{

public var keyCodes:Array;
public var handler:Function;
public var sequenceIndex:int = 0;
public var id:String;

public function SequenceListener(codes:Array, func:Function, listenerId:String) 
{
keyCodes = codes;
handler = func;
id = listenerId;
}

}

}

Now go to EasyKeyboard class. Add a new function removeListener(), which compares the provided id with all listeners' ids and removes the needed listener.

/**
 * Remove a single listener.
 * @paramlistenerId Id of the listener, which is specified as String when the listener is created.
 */

public function removeListener(listenerId:String):void {
for (var i:int = listeners.length - 1; i >= 0; i--) {
if (listeners[i].id == listenerId) {
listeners.splice(i, 1);
break;
}
}
}

Now we need to go to each add____Listener method and add a new optional parameter "id" and apply that value to the object that's being passed to the listeners array.

Full EasyKeyboard.as code:

package com.kircode.EasyKeyboard 
{
import flash.display.Stage;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.utils.Timer;

/**
 * Utility for easy keyboard listener management.
 * @author Kirill Poletaev
 */
public class EasyKeyboard 
{
public var listeners:Array = [];
private var keyLabels:Array = ["0","1","2","3","4","5","6","7","Backspace","Tab","10","11","Center","Enter","14","15","Shift","Control","Alt","Pause","Caps Lock","21","22","23","24","25","26","27","28","29","30","31","Space","Page Up","Page Down","End","Home","Left","Up","Right","Down","41","42","43","44","Insert","Delete","47","0","1","2","3","4","5","6","7","8","9","58","59","60","61","62","63","64","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","Windows","Windows","Menu","94","95","Num 0","Num 1","Num 2","Num 3","Num 4","Num 5","Num 6","Num 7","Num 8","Num 9","Num *","Num +","108","Num -","Num .","Num /","F1","F2","F3","F4","F5","F6","F7","F8","F9","F10","F11","F12","124","125","126","127","128","129","130","131","132","133","134","135","136","137","138","139","140","141","142","143","Num Lock","Scroll Lock","146","147","148","149","150","151","152","153","154","155","156","157","158","159","160","161","162","163","164","165","166","167","168","169","170","171","172","173","174","175","176","177","178","179","180","181","182","183","184","185",";","+",",","-",".","/","~","193","194","195","196","197","198","199","200","201","202","203","204","205","206","207","208","209","210","211","212","213","214","215","216","217","218","[","\\","]","'","223","224","225","226","227","228","229","230","231","232","233","234","235","236","237","238","239","240","241","242","243","244","245","246","247","248","249","250","251","252","253","254","255"];
private var st:Stage;

public function EasyKeyboard(stage:Stage) 
{
stage.addEventListener(KeyboardEvent.KEY_DOWN, kDown);
stage.addEventListener(KeyboardEvent.KEY_UP, kUp);
stage.addEventListener(Event.ENTER_FRAME, frame);
st = stage;
}

private function frame(evt:Event):void {
for (var i:int = 0; i < listeners.length; i++) {
if (listeners[i] is HoldListener && listeners[i].flag) {
if (listeners[i].handler != null) listeners[i].handler.call();
}
}
}

/**
 * Call this before nullifying the class instance to remove all the listeners.
 */

public function kill():void {
st.removeEventListener(KeyboardEvent.KEY_DOWN, kDown);
st.removeEventListener(KeyboardEvent.KEY_UP, kUp);
st.addEventListener(Event.ENTER_FRAME, frame);
}

/**
 * Add event listener for a single key using a keycode.
 * @paramkeyCode Key code of the key.
 * @paramhandlerDown Function to be called when the key is pressed down.
 * @paramhandlerUp Function to be called when the key is released.
 * @paramalt Used in combination with the alt key.
 * @paramctrl Used in combination with the ctrl key.
 * @paramshift Used in combination with the shift key.
 * @paramid ID of the listener. Set this to be able to remove the listener dynamically later using the removeListener() method.
 */

public function addListener(keyCode:int, handlerDown:Function = null, handlerUp:Function = null, alt:Boolean = false, ctrl:Boolean = false, shift:Boolean = false, id:String = ""):void {
listeners.push(new KeyListener(keyCode, handlerDown, handlerUp, alt, ctrl, shift, id));
}

/**
 * Add event listener for a single key using a keycode.
 * @paramkeyCode Key code of the key.
 * @paramhandler Function to execute while the key is held every frame.
 * @paramid ID of the listener. Set this to be able to remove the listener dynamically later using the removeListener() method.
 */

public function addHoldListener(keyCode:int, handler:Function, id:String = ""):void {
listeners.push(new HoldListener(keyCode, handler, false, id));
}


/**
 * Add event listener for a combination of keys using key codes.
 * @paramkeyCodes Array of key codes for the combination.
 * @paramhandler Function to execute when the combination is held.
 * @paramid ID of the listener. Set this to be able to remove the listener dynamically later using the removeListener() method.
 */

public function addComboListener(keyCodes:Array, handler:Function, id:String = ""):void {
var flags:Array = [];
for (var i:int = 0; i < keyCodes.length; i++) {
if (isNaN(keyCodes[i])) {
throw new Error('Incorrect key code value specified - "' + keyCodes[i] + '" is not a number.');
return;
}
flags.push(false);
}
listeners.push(new ComboListener(keyCodes, handler, flags, id));
}

/**
 * Add event timed listener for a single key using a keycode.
 * @paramkeyCode Key code of the key.
 * @paramdelay Delay in milliseconds of how long the key needs to be held down for the event to be registered.
 * @paramhandler Function to execute when the key has been held for the specified amount of time.
 * @paramrepeat Set to true if you want to dispatch the event forever until the key is released. If you only want to dispatch it once, set to false.
 * @paramid ID of the listener. Set this to be able to remove the listener dynamically later using the removeListener() method.
 */

public function addTimedListener(keyCode:int, delay:int, handler:Function, repeat:Boolean = true, id:String = ""):void {
var rep:int = 1;
if (repeat == true) rep = 0;
var delayTimer:Timer = new Timer(delay, 1);
listeners.push(new TimedListener(keyCode, delayTimer, handler, rep, id));
}

/**
 * Add event listener for a sequence of keys to be pressed.
 * @paramkeyCodes Array of key codes in the sequence.
 * @paramhandler Function to execute when the keys are pressed in the specific order.
 * @paramid ID of the listener. Set this to be able to remove the listener dynamically later using the removeListener() method.
 */

public function addSequenceListener(keyCodes:Array, handler:Function, id:String = ""):void {
for (var i:int = 0; i < keyCodes.length; i++) {
if (isNaN(keyCodes[i])) {
throw new Error('Incorrect key code value specified - "' + keyCodes[i] + '" is not a number.');
return;
}
}
listeners.push(new SequenceListener(keyCodes, handler, id));
}

/**
 * Add event listener for a single key using key string value.
 * @paramkeyName String name of the key.
 * @paramhandlerDown Function to be called when the key is pressed down.
 * @paramhandlerUp Function to be called when the key is released.
 * @paramalt Used in combination with the alt key.
 * @paramctrl Used in combination with the ctrl key.
 * @paramshift Used in combination with the shift key.
 * @paramid ID of the listener. Set this to be able to remove the listener dynamically later using the removeListener() method.
 */

public function addEasyListener(keyName:String, handlerDown:Function = null, handlerUp:Function = null, alt:Boolean = false, ctrl:Boolean = false, shift:Boolean = false, id:String = ""):void {
var code:int = -1;
for (var i:int = 0; i < keyLabels.length; i++) {
if (keyLabels[i] == keyName) {
code = i;
break;
}
}
if (code == -1) {
throw new Error('Incorrect key string value specified - no "' + keyName + '" key found.');
return;
}
addListener(code, handlerDown, handlerUp, alt, ctrl, shift, id);
}

/**
 * Add hold listener for a single key using key string value.
 * @paramkeyName String name of the key.
 * @paramhandler Function to execute while the key is held every frame.
 * @paramid ID of the listener. Set this to be able to remove the listener dynamically later using the removeListener() method.
 */

public function addEasyHoldListener(keyName:String, handler:Function, id:String = ""):void {
var code:int = -1;
for (var i:int = 0; i < keyLabels.length; i++) {
if (keyLabels[i] == keyName) {
code = i;
break;
}
}
if (code == -1) {
throw new Error('Incorrect key string value specified - no "' + keyName + '" key found.');
return;
}
addHoldListener(code, handler, id);
}

/**
 * Add event listener for a combination of keys using key names.
 * @paramkeyNames Array of key names for the combination.
 * @paramhandler Function to execute when the combination is held.
 * @paramid ID of the listener. Set this to be able to remove the listener dynamically later using the removeListener() method.
 */

public function addEasyComboListener(keyNames:Array, handler:Function, id:String = ""):void {
var flags:Array = [];
var keyCodes:Array = [];
var u:int;
var i:int;
var code:int = -1;
var keyName:String;

for (u = 0; u < keyNames.length; u++) {
flags.push(false);
code = -1;
keyName = keyNames[u];
for (i = 0; i < keyLabels.length; i++) {
if (keyLabels[i] == keyName) {
code = i;
keyCodes.push(code);
break;
}
}
if (code == -1) {
throw new Error('Incorrect key string value specified - no "' + keyName + '" key found.');
return;
}
}

listeners.push(new ComboListener(keyCodes, handler, flags, id));
}

/**
 * Add event listener for a sequence of keys to be pressed using key names.
 * @paramkeyNames Array of key names in the sequence.
 * @paramhandler Function to execute when the keys are pressed in the specific order.
 * @paramid ID of the listener. Set this to be able to remove the listener dynamically later using the removeListener() method.
 */

public function addEasySequenceListener(keyNames:Array, handler:Function, id:String = ""):void {
var keyCodes:Array = [];
var u:int;
var i:int;
var code:int = -1;
var keyName:String;

for (u = 0; u < keyNames.length; u++) {
code = -1;
keyName = keyNames[u];
for (i = 0; i < keyLabels.length; i++) {
if (keyLabels[i] == keyName) {
code = i;
keyCodes.push(code);
break;
}
}
if (code == -1) {
throw new Error('Incorrect key string value specified - no "' + keyName + '" key found.');
return;
}
}
listeners.push(new SequenceListener(keyCodes, handler, id));
}

/**
 * Add event timed listener for a single key using a keyname.
 * @paramkeyName Name of the key.
 * @paramdelay Delay in milliseconds of how long the key needs to be held down for the event to be registered.
 * @paramhandler Function to execute when the key has been held for the specified amount of time.
 * @paramrepeat Set to true if you want to dispatch the event forever until the key is released. If you only want to dispatch it once, set to false.
 * @paramid ID of the listener. Set this to be able to remove the listener dynamically later using the removeListener() method.
 */

public function addEasyTimedListener(keyName:String, delay:int, handler:Function, repeat:Boolean = true, id:String = ""):void {
var rep:int = 1;
if (repeat == true) rep = 0;
var delayTimer:Timer = new Timer(delay, 1);
var code:int = -1;
for (var i:int = 0; i < keyLabels.length; i++) {
if (keyLabels[i] == keyName) {
code = i;
break;
}
}
if (code == -1) {
throw new Error('Incorrect key string value specified - no "' + keyName + '" key found.');
return;
}
listeners.push(new TimedListener(code, delayTimer, handler, rep, id));
}

private function kDown(evt:KeyboardEvent):void {
var u:int = 0;
var comboKeys:int = 0;
for (var i:int = 0; i < listeners.length; i++) {
if (listeners[i] is KeyListener && evt.keyCode == listeners[i].keyCode && evt.altKey == listeners[i].alt && evt.ctrlKey == listeners[i].ctrl && evt.shiftKey == listeners[i].shift) {
if (listeners[i].handlerD) listeners[i].handlerD.call();
}
if (listeners[i] is HoldListener && evt.keyCode == listeners[i].keyCode) {
listeners[i].flag = true;
}
if (listeners[i] is ComboListener) {
comboKeys = 0;
for (u = 0; u < listeners[i].keyCodes.length; u++) {
if (listeners[i].keyCodes[u] == evt.keyCode) {
listeners[i].flags[u] = true;
}
if (listeners[i].flags[u] == true) comboKeys++;
}
if (comboKeys == listeners[i].keyCodes.length) {
if (listeners[i].handler) listeners[i].handler.call();
}
}
if (listeners[i] is TimedListener && evt.keyCode == listeners[i].keyCode) {
if (!listeners[i].delayTimer.running) {
listeners[i].delayTimer.start();
}
}
if (listeners[i] is SequenceListener) {
if (evt.keyCode == listeners[i].keyCodes[listeners[i].sequenceIndex]) {
listeners[i].sequenceIndex++;
if (listeners[i].sequenceIndex == listeners[i].keyCodes.length) {
if (listeners[i].handler) listeners[i].handler.call();
listeners[i].sequenceIndex = 0;
}
}else {
listeners[i].sequenceIndex = 0;
}
}
}
}

private function kUp(evt:KeyboardEvent):void {
var u:int = 0;
for (var i:int = 0; i < listeners.length; i++) {
if (listeners[i] is KeyListener && evt.keyCode == listeners[i].keyCode && evt.altKey == listeners[i].alt && evt.ctrlKey == listeners[i].ctrl && evt.shiftKey == listeners[i].shift) {
if (listeners[i].handlerU) listeners[i].handlerU.call();
}
if (listeners[i] is HoldListener && evt.keyCode == listeners[i].keyCode) {
listeners[i].flag = false;
}
if (listeners[i] is ComboListener) {
for (u = 0; u < listeners[i].keyCodes.length; u++) {
if (listeners[i].keyCodes[u] == evt.keyCode) {
listeners[i].flags[u] = false;
}
}
}
if (listeners[i] is TimedListener && evt.keyCode == listeners[i].keyCode) {
if (listeners[i].delayTimer.running) {
listeners[i].delayTimer.reset();
if (listeners[i].repeat == 2) listeners[i].repeat = 1;
}
}
}
}

/**
 * Remove a single listener.
 * @paramlistenerId Id of the listener, which is specified as String when the listener is created.
 */

public function removeListener(listenerId:String):void {
for (var i:int = listeners.length - 1; i >= 0; i--) {
if (listeners[i].id == listenerId) {
listeners.splice(i, 1);
break;
}
}
}

}

}

Now you can go to main.as and try writing a simple example like this:

keyboard = new EasyKeyboard(stage);
keyboard.addEasyListener("A", function() { trace("Key A is pressed!!") }, null, false, false, false, "key1");
keyboard.addEasyListener("S", function () { keyboard.removeListener("key1"); trace("Listener for A key removed!"); } );

The A key listener works until you hit the S key - then the A key listener is removed and no longer works.

Thanks for reading!

No comments:

Post a Comment