Monday, June 17, 2013

Triggering Not-So-Custom UI Events in Dart

‹prev | My Chain | next›

I still have not decided if I want to keep last night's focus tests. I wrote a bunch of tests for the ICE Code Editor, but only ended up verifying that most everything worked already. Admittedly this was more of a noticeable problem in the old JavaScript version of ICE, but I had expected more trouble than I found.

I do not think it is so much Dart that made things better as being more consistent in the approach that I took towards buttons, menus and dialogs. Of course, Dart's static typing encourages consistency, but then again so does a strong #pairwithme partner and I have been blessed with many.

At any rate, I did leave off last night with a single failing focus test:
ERROR: Focus- editor has focus after closing a dialog
  Focus- editor has focus after closing a dialog: Test setup failed: Expected: TextAreaElement:<textarea>
       But: was BodyElement:<body>.
Unlike most of the other tests from yesterday, this was a “good” one in that it reproduced buggy behavior that I can see in the application.

My initial instinct for fixing the bug was to add an ice.focus() call in the hideDialog method:
_hideDialog() {
  queryAll('.ice-menu').forEach((e)=> e.remove());
  queryAll('.ice-dialog').forEach((e)=> e.remove());
  ice.focus();
}
If that was all that was necessary to fix it, I would have been done with it last night. The trouble is that the _hideDialog() method is not so much a method on the full-screen version of ICE as it is a helper function. In other words, it does not have access to an instance of ICE nor do the calling contexts always have access.

So to make this pass, I think that I have to have _hideDialog() trigger an event on the main DOM element that holds the editor. The constructor for the full-screen version of the editor creates an #ice <div>:
Full({enable_javascript_mode: true}) {
    el = new Element.html('<div id=ice>');
    document.body.nodes.add(el);

    ice = new Editor('#ice', enable_javascript_mode: enable_javascript_mode);
    store = new Store();
    // ...
  }
So sending a focus event to the #ice element seems the best course of action:
_hideDialog() {
  queryAll('.ice-menu').forEach((e)=> e.remove());
  queryAll('.ice-dialog').forEach((e)=> e.remove());
  query('#ice').focus();
}
A listener on the onFocus event stream should then do the trick:
      el.onFocus.listen((e)=> ice.focus());
It ought to work (at least in my mind), but it does not. The on-focus event is never fired. I could rail against the focus() method failing to generate a focus event, but that will get me no closer to solving my problem.

Instead of telling the element to focus, perhaps I can send a custom event? Or better yet, I try sending a focus event since that is really what I am trying to do:
_hideDialog() {
  queryAll('.ice-menu').forEach((e)=> e.remove());
  queryAll('.ice-dialog').forEach((e)=> e.remove());
  query('#ice').dispatchEvent(new UIEvent('focus'));
}
That is an uglyish constructor signature, but it does the trick. The event is received by the <div id=ice> element, which in turns tells the underlying ICE editor that it has focus. Yesterday's work, which turns out to not be a complete waste after all, ensures that the proper part of ICE—the code editor or the preview layer—gets focus. The bottom line is that I have a passing test:
PASS: Focus- editor has focus after closing a dialog
PASS: Focus- hiding code after update preview has focus
I do believe that closes another issues on the ICE Code Editor tracker, which is a fine stopping point for tonight.


Day #785

No comments:

Post a Comment