If you've ever made an app with custom UI that needs to respond to double clicks, then you've probably caught yourself wondering how to do it. And you've probably come up with a solution.
But was it the proper solution?
Here's the things you need to remember when testing whether something is a double click.
1) Are the two clicks close enough in time? That's a no-brainer that everyone gets right. You use system declares to find out what the OS thinks the proper time-between-clicks should be for a double click and you save the old click time to compare against the new one.
2) Are the two clicks from the same button? It's not a double click if it's a right/left click combination. It has to be two left or two right clicks.
3) Are the modifier keys in the same state? It's not a double click if the first one has shift held down and the second one has control. If the modifier key states aren't the same, it's not a double click.
4) Are the clicks in the proper location relative to one another. Here's where it gets really fun. On Windows, there's a declare into GetSystemMetrics (usually returns 4 or 5) -- if the clicks are further away than the cx and cy returned from this call, it's not a double click. This holds true for Linux as well (but I don't know if there's a declare or not). However, on the Mac, it's element-based. So if you are clicking on say a listbox, then the two clicks need to be on the same row of the listbox, but not within 5 pixels of one another. So the basic rule of thumb is, use the system declares if you have them, otherwise default to about a 5 by 5 area, and if you're on the Mac and using an element-based UI item, base it on the width and height of a single element.
5) As if that's not bad enough, if you process both single clicks and double clicks as different events, you're in for a whole world of hurt. You can't know when a double click has happened until a certain period of time has elapsed because a single click could really be the start of a double click. So if you want these events to be doing different operations (like the way the tray items do on Windows), then you need to throw a timer in there and only fire the SingleClick event if the timer's fired before a second click comes in. Btw, if you're UI is such that a single click is a selction and a double click is an operation on a selection (like in a listbox), then you don't have to worry about this sort of thing.
Phew! Ok, now by show of hands -- who here has gotten their double click code wrong before?
Uh, me!
Not me - I've never needed to do this :-P
:: raises hand :: don't believe me? Try ListBox.DoubleClick on Windows -- you'll see that it's ignoring the rectangle area and just basing it off the entire row (like the Mac should, and does, behave).
Not a good record
I think I always get (1) right with some assistance from the LR,
(2)I have to admit I never check it's the same button.
(3) Heaven only knows what complications I've caused with modifier keys.
(4) I wrongly use 5 pixels, though I do get it right for the mac (probably the only thing I get right for the mac) and
(5) I nearly always get right because I always consider it individually for the circumstances.
Heh. http://joneskatamari.ytmnd.com/
*raises hand*
Though I can talk myself out of it: It was on Classic MacOS, and I didn't go through the hassle of trying to detect mouse buttons, because back in the WaitNextEvent days, multi-button mice were a hack. Though I dont really remember whether I checked the modifiers, so I guess two half-mistakes makes one full one any day :-)
Thanks for posting this handy list!
Or you can use the appropriate system events. When I write non-Rb Windows code, I just listen for WM_LBUTTONDBLCLK and let the OS decide what a double-click is... That might be harder in the Rb framework (multi-platform, needs to get things at a pretty low-level to send them on, etc), but if you can use them I recommend letting the system do as much work as possible.
There is a Linux function (actually you need to call two) -- you figured out how to declare them for me for my book :)