Mouse Placeholders for when Programs Lose Focus

Programmers typically assume that a MouseUp() message won’t happen unless they had previously received a MouseDown()… and that these signals will come in precise pairs. Yet in almost every modern system that runs multiple applications at once, you will hit edge cases that send your program a MouseUp() when no MouseDown() ever happened—or two MouseDown() messages in a row.

There is no well-studied example of how to write your mouse handling code in a way that accounts for these cases. As a consequence, merely task-switching while still in the middle of a mouse operation will send a lot of applications into unexpected conditions! Many will crash or assert when you return… and those that don’t crash often do something bizarre. So I thought I would make a screencast of a prototype I made in 2002 which insulates programmers from these concerns. Even if you’re not a programmer and don’t want to read the whole article about the implementation, you might think the feature itself is unique, so check it out!

(Note: I use alt-tab to take the focus away in mid-mouse stroke, because that was easy to choreograph. But of course the technique is more compelling when an application in the background jumps forward and “steals” the focus. )

As you can see, my library took control of the mouse message pump and reduced the concerns of the programmer. If a mouse gesture was interrupted somehow I didn’t cancel the operation (nor did I just pretend the mouse button had been released and commit it). Instead, the library put the application into a suspended state with a placeholder icon at the last known mouse position. Clicking inside the placeholder restores your cursor to the previous coordinates and resumes the mouse operation, while pressing escape lets you abort.

Use Command objects that only run on MouseUp

In the past I’ve written about the importance of designing your program’s command processor in such a way that undo and redo operate consistently. One of the rules I mentioned was that your command processor shouldn’t be modifying the user’s document on MouseDown() or MouseMove(), but should accumulate the state in a Command object that is only submitted to your Undo/Redo queue when the MouseUp() is finally reached.

The good news is that if you’ve done that part right, then most of the necessary support work for this feature is already done!

A Command object not only makes things clearer for undo and redo—it also gives you a fantastic way of holding a mouse operation in “suspended animation”. The counterintuitive aspect is that pending commands must participate in the rendering process—since the document’s state alone is not enough to draw the view.

Build on Drag&Drop APIs, not mouse messages

One issue that I really had to grapple with was how to retain some control of the mouse cursor even when it had left my application. Sadly, running SetCapture() on Windows still means that the cursor will turn into the default arrow after the focus is lost. The way I found to work around this on Windows was with the Drag&Drop APIs—which turned out to be “tighter” than the default mouse API.

On Windows and other platforms, the Drag&Drop methods are precise analogues to the mouse messages we are familiar with:

  1. DragStart() = MouseDown()
  2. DragOver() = MouseMove()
  3. Drop() = MouseUp()

To use Drag&Drop for all your mouse handling, you merely need to invent a data type that only your application understands. If an object of that type is used, then it is an internal message and you should treat it as the corresponding ordinary mouse event. That may sound like a convoluted way of doing things, but the way Drag&Drop is built into the operating system gives it fundamental powers you won’t get from ordinary messages.

One limitation to consider is that while a drag is in progress, your program will not receive typed text input. I considered this an acceptable loss—no mainstream application I can think of works that way. Plus, any operation you can define by:

[Mouse Down] + typing + [Mouse Up]

…can be transformed into an equivalent (and probably better) sequence of operations like:

[Mouse Down] + [Mouse Up] + typing + [ENTER]

Do note that the keyboard can still affect mouse operations under the Drag&Drop API. You can read the status of whether keys on the keyboard are pressed, most typically Ctrl, Alt and Shift.

Jump the cursor back to its lost position

I wanted the placeholder icon to be a big target to hit, so it would stand out visually and be easy to click on. But once the user clicks anywhere inside of the placeholder, their cursor should be jumped back to the last known position. This means you need to save the coordinates of the cursor when the focus was lost—which isn’t necessarily where you’re drawing the exact center of the placeholder.

Though I talk about this in the video, you may have noticed that I clicked directly in the center of the icon. The reason I did that rather than make the cursor “jump” is because I’m running this old program in a virtual machine, and SetCursorPos() doesn’t work in VMWare at the moment! That’s a bug which will hopefully be fixed, and until it is then VMWare users will just have to be a little more careful when they click the placeholder.

Final thoughts

It may seem small in the scheme of things, but applications can suddenly lose focus for lots of random reasons (not just Alt-Tab!). Losing one’s in-progress work is always frustrating… drawing is the obvious case, but I get annoyed even if I am interrupted while merely making a selection of text! Plus it can be nice to be able to interrupt yourself sometimes and trust that an application will let you resume what you were doing without penalty.

I hope this demonstration inspires someone—especially people who write application frameworks and operating systems—to implement this feature!

Tags: , ,

9 Responses to “Mouse Placeholders for when Programs Lose Focus”

  1. Trill42 Says:

    That is very cool, and very well explained, both in the video and in the post. I’ve never seen this before, and it’s such a useful feature.

  2. Ted Young Says:

    This is a unique idea, I like the thought that went into it. I’m going to see if I can implement it in Java+Swing and see how it “feels”. I’m hoping that it’s helpful and not annoying like the “move cursor to default button on dialog” options that Windows, et al, have.

  3. pete Says:

    I can see how this technique allows recovery after focus is lost, and you explain the solution very well, but I think your example is not very good. If I hit alt-tab, and I know what that will do, then I know it will cause me to lose focus, so I don’t do it until I don’t need focus. Alt-tab is actually an activity I focus on, so it’s not part of the problem. The real problem is egotistical background applications which jerk themselves to the front and STEAL my focus. In these cases, the truly annoying data and time loss occurs IN MY HEAD, and there’s nothing software can do about that. Rather than complicate mousing code, I would savagely punish programmers who steal focus. On prime-time major network television. With a large studio audience. Every night. And I get to throw the switch. (I might share it with you occasionally if you ask nicely.)

  4. hostilefork Says:

    pete– you are quite right to say that the worse case and where this really becomes important are when other apps steal the focus without user initiation. Unfortunately that is hard to choreograph when producing a video like this one, it was just easier to hit alt-tab. Whether you think suspending your own operation is an interesting example or not, a behavior has to be defined for that case…and as I point out I don’t think committing or throwing away are good answers. So it’s not an *entirely* frivolous case, even if it’s not the most important one. Same code either way.

    The real truth of the matter with this feature (and how I came up with the idea) is that it sort of “fell out” of a design which I had created for other good reasons. If you check out my post on undo/redo tied to a single operation you’ll get an idea of what those “synergistic” motivations are. I already had to create an object that holds state for a mouse operation in progress until the button is released, and the moment of release is when the object is finally given the necessary privileges to edit the document and add items to the undo/redo queue. That mouse object is really what makes this an easy feature to implement:

    http://hostilefork.com/2007/11/25/undo-single-user-event/

    In terms of OS support for stopping focus stealing: Windows did add an interesting feature, which was to “flash” the task bar icon rather than allow the focus to be stolen, but it seems to only work in a few situations. Some of the remaining offenders are message boxes coming from OLE DLLs or other random pieces of the OS or its extensions. :( I agree that it would be ideal if there was no API giving apps the power to jump to the front of the screen without user initiation, but it’s kind of a fact of life, until we can edit the code for the shell.

  5. jim Says:

    excellent article and excellent replies.

  6. imadoofus123 Says:

    good idea for things like drawing programs but not so good for everything else. otherwise this could be added in by the window manager and programs wouldn’t have to do anything.

    (Note: This comment auto-syndicated from StumbleUpon)

  7. nekta Says:

    great ideas!

  8. Wayne Says:

    Fortunately, for web development, we don’t have to worry about such nuances. From the browser’s point of view you don’t need to be aware of the position and layering of other windows, unlike windows programming.

    Programming Language Share and Review:
    http://www.codesplunk.com

  9. Hostile Fork Says:

    Hi Wayne, yes… you’d think so. :) However, the evolution of browser apps is such that everyone keeps reinventing more-or-less the windows API on any system where it isn’t explicitly disallowed. You’ve probably seen the various “HTML desktops”, and they’re doing everything from “start” buttons to Z-ordered windows:

    http://hostilefork.com/2007/11/06/workaround-firefox-scrollbar-bug-on-mac/

    It’s nearly inevitable that this kind of app will keep getting popular. So it is good to realize that GUI programs need formal answers for all the various event orderings that come in (and not crash or misbehave). This is just one example of such a situation, but I think my solution illustrates the kind of thinking that would really help avoid bugs or errant program states…

Leave a Reply


Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported
Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported