Showing posts with label 13. Show all posts
Showing posts with label 13. Show all posts
Monday, January 26, 2015
Creating EasyKeyboard class Part 13
Today well further improve the abilities to remove listeners.
The removeListener() function works fine now, but theres a slight problem with removing TimedListeners. The issue is in the Timer, which doesnt stop working even after the listener was "removed". Aside from just removing it from the listeners array, we also need to reset and nullify the timer inside the listener.
You can witness the bug yourself if you create a timed listener and a key listener which removes the timed listener, then try to hold the key which triggers the timed event. If you remove the listener during the delay, youll see that the code dispatches the timed event one more time before stopping.
Lets fix this. Update the removeListener() function:
Now well add a function which removes all the listeners at once. Right now we have one called kill(), but it didnt actually remove all the listeners from the array - it just stops listening to the keyboard as a whole.
The removeAllListeners() method will loop through all listeners and remove them one by one, also taking care of timers the same way like in removeListener:
We should also call this function in kill():
So, to remove all the listeners, the user simply writes:
Full EasyKeyboard.as class code so far:
Thanks for reading!
Read more »
The removeListener() function works fine now, but theres a slight problem with removing TimedListeners. The issue is in the Timer, which doesnt stop working even after the listener was "removed". Aside from just removing it from the listeners array, we also need to reset and nullify the timer inside the listener.
You can witness the bug yourself if you create a timed listener and a key listener which removes the timed listener, then try to hold the key which triggers the timed event. If you remove the listener during the delay, youll see that the code dispatches the timed event one more time before stopping.
keyboard = new EasyKeyboard(stage);
keyboard.addEasyTimedListener("A", 1000, function() { trace("Key A is pressed!!") }, true, "key1");
keyboard.addEasyListener("S", function () { keyboard.removeListener("key1"); trace("Listener for A key removed!"); } );
Lets fix this. Update the removeListener() function:
/**
* 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) {
if (listeners[i] is TimedListener) {
listeners[i].delayTimer.reset();
listeners[i].delayTimer = null;
}
listeners.splice(i, 1);
break;
}
}
}
Now well add a function which removes all the listeners at once. Right now we have one called kill(), but it didnt actually remove all the listeners from the array - it just stops listening to the keyboard as a whole.
The removeAllListeners() method will loop through all listeners and remove them one by one, also taking care of timers the same way like in removeListener:
/**
* Remove all listeners. IMPORATNT: If you are removing the EasyKeyboard object as a whole, use the kill() method.
*/
public function removeAllListeners():void {
for (var i:int = listeners.length - 1; i >= 0; i--) {
if (listeners[i] is TimedListener) {
listeners[i].delayTimer.reset();
listeners[i].delayTimer = null;
}
listeners.splice(i, 1);
}
}
We should also call this function in kill():
/**
* Call this before nullifying the class instance to remove all the listeners.
*/
public function kill():void {
removeAllListeners();
st.removeEventListener(KeyboardEvent.KEY_DOWN, kDown);
st.removeEventListener(KeyboardEvent.KEY_UP, kUp);
st.addEventListener(Event.ENTER_FRAME, frame);
}
So, to remove all the listeners, the user simply writes:
keyboard.removeAllListeners();
Full EasyKeyboard.as class code so far:
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 {
removeAllListeners();
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) {
if (listeners[i] is TimedListener) {
listeners[i].delayTimer.reset();
listeners[i].delayTimer = null;
}
listeners.splice(i, 1);
break;
}
}
}
/**
* Remove all listeners. IMPORATNT: If you are removing the EasyKeyboard object as a whole, use the kill() method.
*/
public function removeAllListeners():void {
for (var i:int = listeners.length - 1; i >= 0; i--) {
if (listeners[i] is TimedListener) {
listeners[i].delayTimer.reset();
listeners[i].delayTimer = null;
}
listeners.splice(i, 1);
}
}
}
}
Thanks for reading!
Friday, January 23, 2015
Creating EasyTooltip class Part 13
In this tutorial we will add the ability to set a tooltip invoke delay - the tooltip will only be displayed after the user has held their mouse over the object for a specific period of time.
First of all, go to the EasyTooltip class and add a new parameter in the constructor - spawnDelay. Declare a variable in the class called "delay", and in the constructor apply spawnDelays value to delay. Then, in the addListener() function, pass the delay value as third parameter of the TooltipListener constructor:
Go to TooltipListener class, declare a Timer:
Receive the delay in the constructor, apply its value as the delay of the timer when creating it. Add a TIMER_COMPLETE event listener to the timer.
In the onOver() function, instead of simply setting display property of the tooltip to true, reset and start the timer:
In the onOut() function, set display to false and stop the timer:
And create the timer handler function onTimer(), use it to set display property of the tooltip to true:
This way the tooltip will be displayed after the timer dispatches the TIMER_COMPLETE event. Full TooltipListener class code:
In the main.as file, you can now pass a value in milliseconds to the EasyTooltip constructor to set the delay:
Thanks for reading!
Read more »
First of all, go to the EasyTooltip class and add a new parameter in the constructor - spawnDelay. Declare a variable in the class called "delay", and in the constructor apply spawnDelays value to delay. Then, in the addListener() function, pass the delay value as third parameter of the TooltipListener constructor:
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;
/**
* 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.
* @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, spawnDelay:int = 0)
{
delay = spawnDelay;
par = parent;
listeners = [];
parW = parentWidth;
parH = parentHeight;
cursor = new TooltipCursor(parentWidth, parentHeight);
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;
}
private function onFrame(evt:Event):void {
cursor.x = par.mouseX;
cursor.y = par.mouseY;
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);
}
}
}
}
Go to TooltipListener class, declare a Timer:
private var timer:Timer;
Receive the delay in the constructor, apply its value as the delay of the timer when creating it. Add a TIMER_COMPLETE event listener to the 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);
}
In the onOver() function, instead of simply setting display property of the tooltip to true, reset and start the timer:
private function onOver(evt:MouseEvent):void {
timer.reset();
timer.start();
}
In the onOut() function, set display to false and stop the timer:
private function onOut(evt:MouseEvent):void {
tip.display = false;
timer.stop();
}
And create the timer handler function onTimer(), use it to set display property of the tooltip to true:
private function onTimer(evt:TimerEvent):void {
tip.display = true;
}
This way the tooltip will be displayed after the timer dispatches the TIMER_COMPLETE event. Full TooltipListener class code:
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);
}
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;
}
}
}
In the main.as file, you can now pass a value in milliseconds to the EasyTooltip constructor to set the delay:
var myStyle:TooltipStyle = new TooltipStyle();
myStyle.minWidth = 240;
myStyle.maxWidth = 300;
myStyle.offsetX = -10;
myStyle.offsetY = -10;
tooltip = new EasyTooltip(stage, stage.stageWidth, stage.stageHeight, 500);
tooltip.setStyle(myStyle);
tooltip.addListener(object1, "This is a piece of a longer text. It should fit in the visible area.");
Thanks for reading!
Subscribe to:
Posts (Atom)