Wednesday, September 11, 2013

Pre-Supporting an API


I got the tests passing last night in ctrl-alt-foo, keyboard shortcut library. That does not necessarily mean that the entire library works however. Much of the code and original functionality in that library was originally extracted from the ICE Code Editor. So I try the code out in there.

For the most part, I seem to have this working. When I smoke test ICE, I am able to create new projects and open existing projects using keyboard shortcuts. Oddly, focusing of the INPUT fields seems to have broken. I can get it working again with a Timer.run() around the focus() calls:
    // ...
    parent.append(dialog);
    // Allow event to finish propagating so that another element can be active
    Timer.run(()=> dialog.query('input').focus());
    // ...
A Timer.run() waits one event loop before performing the action. My initial thought is that last night's crazy JavaScript eval'd event listener combined with a Dart event stream has a minor impedance mismatch. Perhaps the event has not quite finished bubbling which triggers the dialog to be added to the parent element before the browser is ready to perform other actions.

My supposition turns out to be incorrect. I can add data and manipulate the DOM elements. I just cannot focus them. I suspect that this is because I am not preventing the default event actions from taking place when shortcuts are handled. I could invest time in doing this, but it seems like a lot of work especially since it seems likely that Dart will soon support creating keyboard events.

I hate to leave a problem half solved, but I am reasonably sure that I will just end up exploring new ways to abuse js-interop. So instead I shift focus to getting ready for the new keyboard event API. If I can get that close, then it should make it easier to switch once the changes land in Dart.

The proposed API will be:
var stream = KeyEvent.keyDownEvent.forTarget(document.body);
var subscription = stream.listen((keyEvent) {
  print(keyEvent.charCode);
  print(keyEvent.keyCode);
});

var k = new KeyEvent('keydown', keyCode: 65, charCode: 97);
stream.add(k);
I already have an “X” version of KeyEvent (KeyEventX). To conform to the new API, I need to convert my existing constructor into a wrapper constructor:
class KeyEventX implements KeyEvent {
  int keyCode;
  bool ctrlKey, metaKey, shiftKey;
  var _parent;

  KeyEventX.wrap(KeyboardEvent parent) {
    keyCode = parent.keyCode;
    ctrlKey = parent.ctrlKey;
    metaKey = parent.metaKey;
    shiftKey = parent.shiftKey;
  }
  // ...
}
The wrap() named constructor mimics the change in the proposed update to Dart's KeyEvent, though the implementation is slightly different owing to my use of JavaScript. Still, the exposed API should be the same:
    // ...
    var dartCallback = new js.Callback.many((event) {
      _controller.add(new KeyEventX.wrap(event));
    });
    // ...
That particular bit of code can probably go away once KeyEvent has its own nice streams, but if it needs to remain for any reason, I can make the quick change of removing the “X” from the class name.

As for dispatching the event, my use of JavaScript complicates supporting the same API. For now, I add the same constructor:
KeyEventX(String typem
      {Window view, bool canBubble: true, bool cancelable: true, int keyCode: 0,
      int charCode: 0, int keyLocation: 1, bool ctrlKey: false,
      bool altKey: false, bool shiftKey: false, bool metaKey: false,
      bool altGraphKey: false}) {
    // js-interop here
  }

But build my JavaScript event in there. I cut my effort there, however. Adding an event directly to a stream is non-standard Dart (though it certainly makes sense in this context). To get that working, I think I would have to duplicate a good part of the forthcoming patch. Rather than duplicating effort, I stop here with a reasonable chunk of the proposed API supported. Hopefully this will translate into a quicker adoption when the change lands.

/me waits patiently...


Day #871

No comments:

Post a Comment