Caps lock breaking keyboard shortcuts handling in Adobe Air
Hello everybody, I’m Florian and currently the newest member of the balsamiq team. So what is my job here? I hunt bugs and add automated tests so once bugs are eradicated we do not run into them again.
Today I would like to discuss an ancient bug. Certain keyboard shortcuts were disabled when caps lock was activated. Instead of saving or exporting your mockups as png nothing happened at all. The bug occured on Windows and Linux even though Windows and Mac are handled in the same way and Linux had its own solution.
So what did we do and what did we do wrong?
At this point I want to dig a bit deeper into technical details of the Flex API of the KeyboardEvent. The KeyboardEvent has several nice public properties which are set on the basis of the keys you press. These are:
• keyCode which is the numeric value of the key pressed
• shiftKey is a Boolean which tells us if shift is pressed, likewise
• altKey for alt
• controlKey for control by Linux and Windows
• commandKey by Mac
• and now the bad bad charCode which is the computed charCode by the all the keys pressed above.
So why is the charCode the baddy? Because it is affected by caps lock on Linux and Windows! So if your key handler works on the basis of the charCode and checks if a key modifier has been pressed you might end up either in a keyboard shortcut which should not be triggered, or with no result at all.
Windows and Mac
For Windows and Mac OS we used the nice NativeMenu from Flex.
We use the NativeMenu and the NativeMenuItems to add all the entries you see in the menu. NativeMenuItem is an entry e.g. “Save As” and in our case it should listen to the key combination "ctrl + shift + s". To create this "Save As" item we use following code sniplet:
var item:NativeMenuItem = new NativeMenuItem(“Save As”);
item.name = “saveas”;
item.keyEquivalent = “S”;
item.mnemonicIndex = 5;
This works great on Windows until we hit caps lock. Before I tell you why I want to refer to a blog post of Brooks Andrus. He added another line to the item above:
item.keyEquivalentModifiers = Keyboard.SHIFT
This meant that he had to have caps lock on to trigger the shortcut.
The reason is the that the NativeMenu and the NativeMenuItems work with the charCode. As we know the charCode is affected by caps lock on Windows. Though it is interesting that the charCode stays unaffected by Mac, so we do not need a special handling here.
Now how do we solve this issue on Windows? The NativeMenu only bubbles the event up if a listener triggers and even if it would still bubble it up the select event has absolutely no knowledge any more of which keys were pressed.
This is how we solved it:
We added a listener in the creation of the menu for a KeyboardEvent.KEY_UP.
And this is the function we call:
protected var _capsLockOn:String;
//Only used on Windows
protected function onKeyUp(p_evt:KeyboardEvent = null):void
{
trace("onKeyUp "+Keyboard.capsLock);
var newCapsLock:String = (Keyboard.capsLock) ? "ON" : "OFF"
if (_capsLockOn == null || newCapsLock != _capsLockOn) {
_capsLockOn = newCapsLock;
Application.application.stage.removeEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
if (_capsLockOn == "ON") {
Application.application.stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
}
}
}
In onKeyDown we again handle all the events that should have been handled in the NativeMenu.
Linux
For Linux we wrote our own menu. We designed it as we wanted, added all the items and added event listeners to handle keyboard events for specific item. The event handler worked with the charCode.
So for Linux the solution is straightforward. Instead of mapping just to the charCode we map to the lower case of the charCode and check if shift is pressed. It would probably be even better to map to the keyCode and the key modifiers.
So the mystery is solved why caps lock disables shortcuts. We have learned our lesson here.
I hope this helps a lot of you to solve the problem of shortcut issues vanishing when caps lock is pressed.
(P.S. If anybody knows how to simulate a keyboard event so the NativeMenu triggers please share this information with me. I want to add some Unit Tests for this. (Yes, I know that is no longer unit level!))



