After complaining yesterday about the lack of usability in VW under a one button mouse, I thought about it for awhile. And then I went home and did some more coding on my iMac under VW. And at one point, I just stopped, and I stared out that control in the lower left corner for a while. I felt like Lady MacBeth: "Out damned ctrl key!!" There's a reason I don't do too much with emacs, ya know? I hope to have not worn my pinky to the bone before my kids are grown.
Some Mac apps have done this thing, where you if you click and hold for a second, the context menu pops up then. So I set out to do that. Why would I do that? To quote Avi Bryant, who quoted somebody else: "Well, because I could."
The amazing thing is how easy this was. Somewhere a couple of VisualWorks versions back, InputState had this subtle change made to it that allows cool tricks. Inputs come up from the VM in a relatively platform neutral array of integers format. The first slot is what kind of event it is. There used to be a big old case statement in InputState>>process: that branched on this first slot. Now, there's a shared variable called EventDispatchTable which is an array of selectors. Just index the slot and perform it. So this is really cool, because you can add your own variants of event handling and update that table on the fly. All without doing a single override/patch/base change!
The idea in this case is to add some augmented button press/release event over and above the normal one. So on a red button press (red is the button 1), we archive a copy of the event (this is our "latch") and spin off a little process to wait and do something. Then it just calls the old method. The loop waits for an amount of time, and then checks to see if the latched value is the same as when it started, if it is, it sends a red button release event followed by a yellow button release. This is easy, we just munge the slots of the original array (change the type and time) and use the original methods. Sending these two faked events may cause a menu to come up. The augmented version of button release always clears the latch. Turns out you may want to drag too, so you use the same trick on the mouse moved event to cause it to clear the latch too. A couple of methods, a couple of lines of code, no overrides, and my Mac has become much more enjoyable to do VisualWorks with. You can find this fun little experiment as the OneButtonRules package in the Open Repository.
This is not the first time I've exploited this InputState possibility. I've used it with an application where the user presses a button which causes some external hardware to do something. The application polls said external hardware to figure out when it's done. It can terminate the polled wait early via a timeout or a click anywhere on the screen. By temporarily replacing the button pressed event selector with one that sets a flag the polling loop can check, this was easily solved. Said variant just sets the flag, and then puts the original back in. So a click anywhere on the screen becomes a cancel.
I was also struck by the thought that a problem posted in the vwdev list recently, that of noting when the last event had happened, so the app could know the last time the user had interacted with it, could be solved with this same technique. The nice thing is that it would be non-intrusive (i.e require no base method changes). Said app would simply implement replacement methods for events of interest which would log the time and then dispatch to the original method.
At the end of the day, I don't think I'd do the OneButtonRules behavior this way long term though. There are some quirky side affects of doing this. For example, the repository red/yellow button menu in the launcher doesn't work very well with the click and hold idea. Said widget would just be done different in a Mac environment in the first place. This trick exploited an easy fix in one place at the cost of making some global assumptions. A more involved, and much more intrusive approach, would create specialized versions of all the list view variants and all the text view variants, to do the waiting and pseudo yellow events there, based on whether they had menus or not. Quirks or no though, VW will be exerting less ctrl over me.
This marks day three of continuous blogging. Maybe I shoulda titled it "Return of the BloGuy"