Archive for shortcut manager
Contextual Shortcut Manager
Posted by: | CommentsI’m about ready to sleep on my ShortcutManager, I figured I might as well finish off the series. I added context to it, while still maintaining backward compatibility with the non-context enabled version. I don’t think I posted it, so it probably won’t matter so much to my readers. For those who haven’t read the previous posts on my ShortcutManager, the “context” I’m referring to is the context in which a keyboard shortcut might be used; such that a given shortcut might have several functions associated with it depending on its context.
In short I needed four things to implement the context aware shortcut manager.
1. The IKeyboardContext interface
This is a simple interface that a model or class can implement that allows a keyboardContext to be set and retrieved. A keyboardContext is simply a string that identifies the current context. These contexts will be associated with a shortcut in the manager. Here is the entire interface:
package com.googolflex.gflib.interfaces {
public interface IKeyboardContext {
[Bindable(event="propertyChange")]
function get keyboardContext() : String;
function set keyboardContext(v : String) : void;
}
}
2. A reference to an IKeyboardContext needed to be added to the ShortcutManager
As the keyboardContext changes in the model, the ShortcutManager needs to be able to access it, so it can retrieve the correct function for a given keycode-flags-context tuple. The IKeyboardContext was left null, which helps with the backward compatibility (the implementer will be forced to set it if she wants to use it). Here is the line I added, which is pretty trivial:
public static var model : IKeyboardContext = null;
3. Add the context to the addShortcut method
I gave it a default value, which incidentally was “default”, so that every function added to the Dictionary would have some kind of context. Here is the new addShortcut() method:
public static function addShortcut(keycode : uint, func : Function, flags : uint, context : String = "default") : void {
if (flags > 0 && flags < 8)
functionMap[keycode + "-" + flags + "-" + context] = func;
}
4. Finally, the shortcutHandler() needed to incorporate the context
I first check if the IKeyboardContext is null, if it is I know to use “default” as the context. If no context was ever specified, and no IKeyboardContext was ever set, then it still works. Here is the modified code:
public static function shortcutHandler(event : KeyboardEvent) : void {
var flags : uint = getFlags(event);
var context : String = (model!=null) ? model.keyboardContext : "default";
if ( functionMap[event.keyCode + "-" + flags + "-" + context] != null )
(functionMap[event.keyCode + "-" + flags + "-" + context] as Function).apply();
}
And was pretty much it. I will post the modified code, along with a demonstration application at the end of the post. I do want to make a couple points to be aware of when actually incorporating this into a project.
First, your model/class will need to implement IKeyboardContext. Second, you need to remember to set ShortcutManager.model = myIKeyboardContext somewhere. Third, you’re on your own when it comes to maintaining your context. Maybe the “SimpleContextManager” will be next, who knows? And fourth, you’ll have to specify a context when you call addShortcut(). Remember if you download the demo, it will need to have some kind of reference to the ShortcutManager code.
Simple Flex ShortcutManager (revisited)
Posted by: | CommentsI’ve put some more thought into my shortcut manager, and have decided on a way to implement the shortcut context, as well as curb the Dictionary explosion that my previous architecture would have had.
The new version uses one Dictionary to store all functions, the keyCode, combo keys, and context is all encoded into the dictionary key. To illustrate here is my new addShortcut() method:
public static function addShortcut(keycode : uint, func : Function, pair : String) : void {
var flags : int = getFlagsFromString(pair);
if (flags > 0)
functionMap[keycode + "-" + flags] = func;
}
It accepts the same parameters, the only difference is that the pair string is now a dash delimited string, containing the key combinations (i.e. “ctrl“, “ctrl-alt“, “shift-ctrl“, etc). Those are parsed by a method getFlagsFromString() which I will probably get rid of in favor of some named constants on the ShortcutManager class. That will eliminate the need for the additional method, and pair (which will be one of said constants) can be added to the key as-is.
The removeShortcut() is very similar. I haven’t implemented “contexts” yet, but you can visualize how it will be done:
functionMap[keycode + "-" + flags + "-" + context] = func;
Now that there is only one Dictionary, the shortcutHandler() method has become almost trivial. Here it is, in all of its 5-lined glory:
public static function shortcutHandler(event : KeyboardEvent) : void {
var flags : uint = getFlags(event);
if (functionMap[event.keyCode + "-" + flags] != null)
(functionMap[event.keyCode + "-" + flags] as Function).apply();
}
Like I said… trivial.
Simple Flex ShortcutManager
Posted by: | CommentsJust for fun I wrote a simple ShortcutManager this evening, it turned out to be an interesting little project and taught me a few things about Functions. It’s by no means finished, I’ll detail a few planned improvements after I go over it’s usage.
My goals were to have a static registry that the application could register a keyCode, along with a qualifying key combination (I refer to this as “pair” in the code) like CTRL, or CTRL-ALT. I left out ALT, as Windows seems to have issues with giving up control of the ALT key when used alone. Along with the keyCode, I needed to be able to register a function that would be called when the key combination was pressed. I also wanted to be able to remove a key combination at runtime.
My data structure was simple, I used a separate Dictionary for each of the key combinations I was supporting.
private static var ctrlFunctionMap : Dictionary = new Dictionary(); private static var ctrlAltFunctionMap : Dictionary = new Dictionary();
I wrote an addShortcut method that accepted the keyCode integer, the function to be called, and a string declaring the combination key(s). In it I simply associated the keycode and the function in the appropriate Dictionary. Here is the addShortcut():
public static function addShortcut(keycode : uint, func : Function, pair : String = "ctrl") : void {
if (pair == "ctrl") {
ctrlFunctionMap[keycode] = func;
}
else if (pair == "ctrlAlt") {
ctrlAltFunctionMap[keycode] = func;
}
else {
trace("Key combination not supported.");
}
}
The removeShortcut() method has a similar structure, with one less parameter since we don’t need to know the function. We simply set the appropriate function for the appropriate keycode and combination key to null. Here is the removeShortcut() function:
public static function removeShortcut(keycode : uint, pair : String = "ctrl") : void {
if (pair == "ctrl") {
ctrlFunctionMap[keycode] = null;
}
else if (pair == "ctrlAlt") {
ctrlAltFunctionMap[keycode] = null;
}
else {
trace("Key combination not supported.");
}
}
Finally we need a method that accepts a KeyboardEvent and determines if one of the functions needs to be executed, and then call it. Here is the shortcutHandler() function that does this:
public static function shortcutHandler(event : KeyboardEvent) : void {
if ( event.ctrlKey && !event.altKey && (ctrlFunctionMap[event.keyCode] != null) ) {
(ctrlFunctionMap[event.keyCode] as Function).apply();
}
else if (event.ctrlKey && event.altKey && (ctrlAltFunctionMap[event.keyCode] != null) ) {
(ctrlAltFunctionMap[event.keyCode] as Function).apply();
}
}
I learned an important lesson when I first tried to implement this method. I tried casting the value from the map as a Function, like this:
Function(ctrlFunctionMap[event.keyCode]).apply();
which didn’t work, and spat the error:
EvalError: Error #1066: The form function('function body') is not supported.
Fortunately, one of ActionScript’s more descriptive error messages (I could tell by the parentheses what was wrong) and was able to correct it using the as operator. I didn’t try it, but I might have been able to not bother casting it at all.
Here’s my ShortcutManagerDemo application, demonstrating how it can be used with a variety of functions and function literals.
import com.googolflex.gflib.managers.ShortcutManager;
private function onCreationComplete() : void {
ShortcutManager.addShortcut(89, onCtrlY, "ctrl");
ShortcutManager.addShortcut(89, onAltY, "ctrlAlt");
var f : Function = function():void { trace("Ctrl-U pressed."); }
ShortcutManager.addShortcut(85, f, "ctrl");
ShortcutManager.addShortcut(85,
function():void { trace("Ctrl-Alt-U pressed."); },
"ctrlAlt");
}
private function onAddedToStage() : void {
stage.addEventListener(KeyboardEvent.KEY_DOWN, globalShortcutHandler);
}
private function globalShortcutHandler(event : KeyboardEvent) : void {
ShortcutManager.shortcutHandler(event);
}
private function onCtrlY() : void {
trace("Control-Y pressed.");
}
private function onAltY() : void {
trace("Ctrl-Alt-Y pressed.");
}
I’ll post the code for the manager, and the sample application at the end of the post. Anyway, some improvements I’m planning…
I’d like to associate a context with each command, so that the same keyCodes could be used on different screens. Organizing the manager for such functionality doesn’t strike me as being that hard. I would have to make some changes to my logic to keep down the “conditional explosion”. The daunting part is where to get the context state… I’ll probably end up creating an interface for it, and then make my models implement it.
Of course I’d like to add more combination keys: SHIFT, SHIFT-ALT, CTRL-SHIFT-ALT, etc. The challenging part for that will be simplifying the conditional logic in shortcutHandler() to keep from having to check for a billion cases. I think I would start by pushing the (ctrlFunctionMap[event.keyCode] != null) check down a level, and using my first level of logic only to check the combination keys pressed.
Anyway, if this is useful for anyone I’m glad I could help. Here’s the source:
ShortcutManagerDemo.mxml
ShortcutManager.zip
Update (8/18/09): I added a few things to ShortcutManagerDemo.mxml, namely an interface that allows the user to add their own key commands at runtime. This has limited usefulness in it’s present form, as the functions themselves are still fixed at compile time. Here are some keycodes you can try it out on, anyhow.
a-65; b-66; c-67… 0-48; 1-49; 2-50…

