Friday, August 16, 2013

OSX Keyboard Shortcuts with Dart

Tonight I venture into the land of OSX. Happily, I am armed with Dart, so I ought to be well-provisioned.

The specific goal is to support ⌘+N style shortcuts instead of Ctrl+N shortcuts that reasonable operating systems support. Did I just include Windows in the list of reasonable operating systems? I believe I did.

In many ways, I find this scarier than supporting Windows. At least with Windows I can fire up a VM for testing. With Apple testing, I actually have to boot into OSX to do work. Ugh.

There is no way that I am going to attempt to actually develop in OSX. I tried that once for about three months. I still remember the day I switched back to Linux: happiness. I could try booting my wife's Mac into OSX, but she'd kill me. Anyhow, I am going to make an educated guess of how things should work and use last night's test page ( for when my educated guess goes wrong.

My hypothesis is that OSX browsers will set the metaKey to true on keyboard events when the Command key is pressed. I have tests in the ctrl_alt_foo library that generate and check for Ctrl+A key events:
  test("can listen for Ctrl shortcuts", (){
    KeyboardEventStreamX.onKeyDown(document).listen(expectAsync1((e) {
      expect(e.isCtrl('A'), true);

I adapt that to my hypothetical OSX as:
  test("can listen for Command shortcuts (OSX)", (){
    KeyboardEventStreamX.onKeyDown(document).listen(expectAsync1((e) {
      expect(e.isCommand('A'), true);

(I do something similar for Command+Shift+A)

This fails because I lack the typeCommand() helper. So I add it as:
typeCommand(char) {
    new KeyboardEvent(
      keyIdentifier: keyIdentifierFor(char),
      metaKey: true
The only different between this and the typeCtrl() helper the metaKey property instead of ctrlKey.

The test still fails however, because I lack the isCommand predicate method on ctrl_alt_foo's specialized keyboard event class. I add it as:
class KeyEventX extends KeyEvent {
  // ...
  bool isCommand(String char) => metaKey && isKey(char);
  // ...
With that, I have my OSX tests passing… on Linux.

To test my hypothesis out in OSX, I convert the shortcuts in the ICE Code Editor to use the OSX version as well:
    // ...
    KeyboardEventStreamX.onKeyDown(document).listen((e) {
      if (e.isCtrl('N') || e.isCommand('N')) {
        new NewProjectDialog(this).open();
      // ...
    // ...
With that, I am ready to venture to the land of true hipsters.

First up in OSX, I try the test page. If the input field is active, ⌘-B produces a keydown events in both the INPUT and BODY:

As I found yesterday, Chrome does not register this as a keypress event since there is no printable character generated. Interestingly, there is no corresponding key up event.

Similarly, when I try it out with the BODY active (i.e. I click outside the INPUT field), there is only the key down event, this time only on the BODY:

Since I am in OSX, I give this a try in Safari as well. When the INPUT field is active, the keyup event is again not registered. Just to ensure that something is different, Safari does register a key press event:

Of course Safari had to be a little different. It would not be proper for any two browsers to behave the same—even with supposedly standard events.

Anyhow, when the BODY is active in Safari, the event is again seen by both key down and key press events:

In all cases, the meta key was set in the event, so it would seem that my ⌘-B hypothesis is confirmed. So I try it in the ICE Code Editor, which is using ctrl_alt_foo shortcuts to find… it does not work.

Well, it works, but only a little. Specifically, Chrome prevents ⌘-Q, ⌘-W, ⌘-T, and ⌘-N from reaching the browser at all. So the original goal of this exercise — supporting ⌘-N — is a no-go. On the other hand, ⌘-O does work. I have also managed to verify that my tests more or less approximate legitimate key press events in OSX, so I will count this effort in the win column.

Day #845

No comments:

Post a Comment