Date: | October 15, 2004 / year-entry #368 |
Tags: | code |
Orig Link: | https://blogs.msdn.microsoft.com/oldnewthing/20041015-00/?p=37553 |
Comments: | 39 |
Summary: | First, I'm going to refer you to the MSDN documentation on mouse clicks, since that's the starting point. I'm going to assume that you know the mechanics of how single-clicks are converted to double-clicks. Okay, now that you've read it, let's talk about some logical consequences of that article and what it means for the... |
First, I'm going to refer you to the MSDN documentation on mouse clicks, since that's the starting point. I'm going to assume that you know the mechanics of how single-clicks are converted to double-clicks. Okay, now that you've read it, let's talk about some logical consequences of that article and what it means for the way you design your user interface. First, some people design their double-click action to be something unrelated to the single-click action. They want to know if they can suppress the initial WM_LBUTTONDOWN of the double-click sequence. Of course, you realize that that would require clairevoyance. When the mouse button goes down for the first time, the window manager doesn't know whether another click will come or not. (Heck, often the user doesn't know either!) So it spits out a WM_LBUTTONDOWN and waits for more. Now suppose you're a program that nevertheless wants to continue with the dubious design of having the double-click action be unrelated to the single-click action. What do you do?
Well, one thing you could do is to do nothing on receipt
of the WM_LBUTTONDOWN message aside from set a timer to fire
in
This "wait a tick" technique is also necessary if you don't have a double-click action, but the second click causes trouble in conjunction with the first click. Why is this necessary? Because many users double-click everything. Here are some examples of where the "delayed action to avoid the second click" can be seen:
Let's demonstrate how you might implement click delay. Start with the scratch program and add the following: void CALLBACK DelayedSingleClick(HWND hwnd, UINT, UINT_PTR id, DWORD) { KillTimer(hwnd, id); MessageBeep(MB_OK); } void OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags) { if (fDoubleClick) { KillTimer(hwnd, 1); MessageBeep(MB_ICONASTERISK); } else { SetTimer(hwnd, 1, GetDoubleClickTime(), DelayedSingleClick); } } HANDLE_MSG(hwnd, WM_LBUTTONDOWN, OnLButtonDown); HANDLE_MSG(hwnd, WM_LBUTTONDBLCLK, OnLButtonDown); Also, since we're messing with double clicks, we should turn them on: wc.style = CS_DBLCLKS; When you run this program, click and double-click in the client area. Notice that the program doesn't react to the single click until after your double-click timeout has elapsed, because it's waiting to see if you are going to continue to click a second time (and therefore double-click instead of single-click). Next time, we'll look at clicks beyond two. |
Comments (39)
Comments are closed. |
I totally agree with Moishe. Although, there are certainly some notification icons that exhibit different behavior for left or right click. The Safely Remove Hardware icon, for example, gives you the list of removeable hardware for left click, and the wizard for right click.
Now, I loathe a wizard when a single menu click will do. But I hate the left click delay! Why couldn’t it do as Moishe suggests?
Raymond, in the main text of your post you say to use GetTickCount(), but I guess you mean GetDoubleClickTime()
"The XP style Start button ignores the second click. Otherwise, when people double-click the Start button, the first click would open the Start menu and the second click would dismiss it!"
Huh? One click opens my Start menu, and a double click dismisses it again, on XP. Is this not supposed to happen?
"The XP style Start button ignores the second click. Otherwise, when people double-click the Start button, the first click would open the Start menu and the second click would dismiss it!"
Windows 2000 seems to act this way. I don’t have access to Windows XP at this moment. But certainly seems like a useful feature.
"Huh? One click opens my Start menu, and a double click dismisses it again, on XP. Is this not supposed to happen?"
Depends, are you clicking once to bring it up and then double-clicking? That’ll send it away.
The notification area icon for Outlook 2003 is different – if I single click on it (brings up menu), click on a blank space in the taskbar, and then double-click the icon, Outlook pops up instead of giving me the menu.
Delay showing context menus is bad. This is because just prior to showing a context menu over a notification icon, you must activate your hidden window. This way, the menu will correctly disappear when you click away from it on some other window.
MSN Messenger is an example of this. Click once with the left button, and it shows the menu after a short delay. Go to some other window during this delay, and the menu will still show, but you won’t be able to dismiss it without clicking inside it then out again.
If you’re going to display a menu or otherwise do something that messes with focus, then certainly check that you still have focus when the timeout expires – to avoid the problem you describe with Messenger.
…And I’ve just discovered a new strange behaviour — for some reason single clicking on the clock seems to cause the tool-tip for the left-most icon to appear. I’ve never noticed it before, so it may be something odd that’s happened due to the recent excessive clicking in the NA…
[ I think I might as well give up work for the day — sitting at my desk randomly clicking on icons is not really an efficient use of a Friday afternoon! ]
Mat: it does something similar for me under 2kPro. If I single click on the clock, it puts an ‘active’ border (dashed line) around the first icon in the system tray. You can then arrow around to move the border from one to another. Basically, the single click set focus on the tray, but the clock refuses to grab it. Hitting enter then opens that one.
The tooltip might be an artifact of the particular app? Mine is volume control.
The user experience for notification icons is down to the application itself. We have a server application written in VB whose UI is typically just a notification icon. The traditional way of handling an icon click (and I’ve just noticed that this app only responds to right-click, not left-click) is to call TrackPopupMenuEx using TPM_RETURNCMD to make it run a modal loop. However, doing this in our server stops the events arriving from the Winsock control! As a result, we create a modeless popup menu, but then it has the same problem as Messenger – it isn’t closed by clicking away from it. So there’s a Cancel option on the menu.
Yep, I’m as disgusted as you are, but until we get the actual operations on a separate thread (and a separate process!) from the UI, that’s the best I can do.
Kerbtray, from the Windows Server 2003 Resource Kit, does the same thing (we’re having a few problems with machine passwords at present, so I’m running it to check the tickets).
Another problem is that, when an icon is clicked, the messages you get back don’t contain the location of the pointer at the time of the click. There may be a delay between the event and the time that the window procedure is called. The code typically creates the context menu at the current cursor location – which may be a long way from the icon. I’m not sure if GetMessagePos would help here.
"The tooltip might be an artifact of the particular app? Mine is volume control."
I thought that, but in the interests of "science" I did futher experimentation: initially it was the network icon tooltip (which is probably how I noticed it as it’s a pretty big tooltip), so I started a few more apps that sit in the NA and tried again; no matter what app was on the far left, its tooltip pops up when I click on the clock. I’ll try and remember to play next time I restart and see what happens…
An intersting side note that I’ve seen of this. I use a touchpad quite often with my laptop and sometimes singleclicks followed by my finger moving to the pad registers as a double click. (I know it’s a double click as opposed to 2 single clicks because I’ve seen this action open shortcuts placed on my standard configuration desktop.) However when I go to the Quick Launch bar and open IE using that button, quite often I am presented with 2 IE windows. Does the Quicklauch bar not contain the same delay written into the other systems. (I’m on XP BTW) If so, any thoughts as to why not?.
It appears that QuickLaunch does not have debouncing code. Debouncing is comparatively rare. The purpose of this article was in part to raise awareness of it.
I dunno the XP start menu works exactly as described here. Assuming that your using the XP version of the menu and don’t have it in ‘classic mode’.
On the Start Menu in XP issue, double-clicking the Start button was dismissing my Start Menu too until I realized that the OP said the Windows XP STYLE start menu. If you have it set to Classic mode, the menu will be dismissed when you double-click it. If you have it set to "non-classic", the second click is ignored.
oops, what anon said
It’s interesting that the "debouncing" would only apply to a certian style of the start menu in XP. I keep mine set to Classic just because I like the look, but I’m prone to Double clicking alot. I guess it’s time I weigh my options. Thank for raising my awarness.
Bad tab orders drive me insane. It should be a requirement for the windows logo that software be tested without a mouse :)
"Huh? One click opens my Start menu, and a double click dismisses it again, on XP. Is this not supposed to happen?"
I just tried it about 50 times (it’s a slow day at the office), and sometimes it works the way Raymond described, and other times it opens and closes. I didn’t keep score, but I’d say it’s a ratio of about 1:3 in favour of open-close…
WRT to notification area icons, there doesn’t seem to be an agreed way of handling the various clicks, even across different MSFT products. For example, a single left click on the MSN Messenger displays the menu (after the brief delay), but a single left click on the Network Status icon performs the default menu action (displaying the status window). If MSFT can’t agree on what to do, what hope is there for the rest of us?
Further experimentation (still a slow day at wrok!) reveals that amongst the icons in my NA* some do nothing at all on a single left click, others display the menu (some with a delay, some not), and yet others perform some action (normally the default menu action, but not always). A final possibility, such as with the clock, as that double-clickng performs a "default" action, even though the context menu doesn’t appear to indicate that anything is default).
It’s only when I sit back and play with things like this that I begin to appreciate how "n00bs" can get so frustrated with PCs. Just when you think you’ve got the hang of it, something comes along and behaves in an unexpected way…
* 22 of them — I don’t "hide inactive icons" as I find that Sod’s Law takes hold and just before I WANT to use one of them it decides it’s not needed and hides, and I like to keep an eye on what things are up to. Has the icon gone because the app has quit, or is it just hidden? As I have plenty of screen real estate (2 21" monitors running at 1920×1440) it doesn’t bother me, but people always tend to comment on it…
I love the following paragraph from the <a href="http://msdn.microsoft.com/library/en-us/winui/winui/windowsuserinterface/userinput/mouseinput/aboutmouseinput.asp">About Mouse Input</a> article in MSDN Library
—cut—
The mouse is an important, but optional, user-input device for applications. A well-written application should include a mouse interface, but it should not depend solely on the mouse for acquiring user input. The application should provide full keyboard support as well.
—cut—
A vendor use Microsoft Access to write an application for my company to use and his application relies ***heavily*** on mouse input such that it is almost (if not impossible) to use the keyboard to run the application at all. Even worse, most of the dialog boxes have inconsistent keyboard tab order, you can go to item 1 in the first tab, and then you will find the next tab goes to item 3, or item 4 instead of item 2.
It is really a big nusiance !
Raymond: what’s your stance on SM_C(X|Y)DOUBLECLK in regards to people treating it as a radius:
const int w = GetSystemMetrics(SM_CXDOUBLECLK);
const int h = GetSystemMetrics(SM_CYDOUBLECLK);
const bool doubleClicked =
labs(oldp.x – newp.x) < w/2 &&
labs(oldp.y – newp.y) < h/2;
I really think it should be like:
const bool doubleClicked =
newp.x >= oldp.x – w/2 &&
newp.x < (oldp.x – w/2)+w &&
newp.y >= oldp.y – h/2 &&
newp.y < (oldp.y – h/2)+h;
because it’s "more correct" and if GetSystemMetrics returns 1 it will actually work unlike the other one.
Mike: I ran into the same problem for a program of mine. After spending quite some time trying to get a modeless menu to work properly with notify icon clicks (and failing; that annoying focus problem just won’t go away), I finally just gave up and made a new thread for it (ugh). Some menus just don’t want to be modeless.
Speaking of that, menus attached to windows don’t like being made modeless very much either. They tend to get their focus stolen from them (but don’t "realize" it), resulting in some weird condition that makes them impossible to operate without using the mouse (until you dismiss the menu). I’ve also seen window menus get opened *twice* when made modeless, on occasion.
I was able to eliminate most all of the modal things about my UI except the menus. Oh well.
"Assuming that your using the XP version of the menu and don’t have it in ‘classic mode’."
That would explain it, although it now raises the question of why it only applies to the new (and slightly irritating) style of Start Menu. I suppose it does more accurately emulate the behaviour of the classic menu, but I think that’s probably taking backwards compatability too far! :) (And it doesn’t explain why occasionally it seems to debounce even in classic mode… Then again, who cares?)
Even more annoying that the MSN Messenger menu is the NA volume icon – it does the same, but the master-volume-and-Mute things comes up where the mouse pointer is AFTER the delay. You also have to click in it, then out of it to dismiss it.
It also seems that:
Single-click – brings the master-volume-and-Mute UI
Double-click – brings the master-volume-and-Mute UI
Triple-click (?) – brings the full "Volume Control" app.
Right-click – brings a context menu, the default option is Volume Control.
Possibly displaying my ignorance here, but is it really OK to just wait GetDoubleClickTime() before deciding it is not a doubleclick?
On a heavily loaded system, delivery of messages can be delayed. If the WM_LBUTTONDBLCLK message is delayed more than the WM_LBUTTONDOWN message, then the time would expire and the program would incorrectly interpret the action as a single click, before subsequently receiving a double click message.
Is there anything in the internals of the message handling to prevent this?
2. As you said, many users double-click everything. It’s a shame that Explorer’s toolbars don’t take that into consideration. If only I had a nickel for every time a friend of mine says: "Why did 2 Internet Explorer windows just opened?"
Here is one of my favorite Windows bugs:
(from MSKB)
"Double-Clicking the Mouse Button Acts as a Single Click
Article ID: 170510 (Q170510)
SYMPTOMS
Double-clicking the mouse in your application behaves as if you clicked the mouse button once. This behavior has been observed in the following applications running in Windows NT 4.0 Service Pack 3: • Microsoft Visio Professional 4.x.
CAUSE
Windows NT 4.0 Service Pack 3 adds a code path where mouse movement does not update the timestamp for a WM_MOUSEMOVE event. This behavior can potentially occur with any application that relies on WM_MOUSEMOVE
timestamps. For example, Visio uses a custom event handler to detect double-clicks rather than checking for the WM_LBUTTONDBLCLK message."
My comment: Why on earth was Visio even doing that at all! The native Windows LBUTTONDBLCLK support was not good enough for them? Every feature introduces the risk of a bug.
Michael: See the documentation for GetMessage. Timer messages have a lower priority than the mouse messages so Raymond’s code is safe.
Paul: They probably felt the native code didn’t offer fine grained enough control. Not really sure what the specific Visio problem was, but it’s easier to write your own double-clicking code than dealing with the double click messages if you’re writing your own widgets not backed by an HWND (mozilla/IE/word/Qt), want to support things like triple-clicking in the same manner as double-clicking (any rich text-like widget), or want to support some of the fancy double-clicking behavior like the listview control does. In the Visio case, I’d imagine it does the double clicking code for the shape editor. The reason for that (for a really lame edge case example) is if you have a large click threshold and a large double click timeout value, you could click on one of the shapes, move the mouse over and click on another shape and it would register as a double click on the second shape so you need to keep track of the first shape that was clicked on. This doesn’t help against clicking on a shape, moving the mouse outside of the window, clicking on something, and moving the mouse back in and clicking on the original shape (CS_DBLCLKS is smart enough to handle cases of the mouse moving outside the window though).
The default double clicking stuff (arguably) botches some things if you’re not careful. If you single click, hold down the ctrl key and click again, it will get a double-click message. If you’re writing your own listbox code and aren’t careful about these sort of things it would register as double clicking on an item instead of selecting and then deselecting it (all listbox/listview controls I’ve come across gets this wrong).
I’m glad to see apps writing their own high level widget code instead of using the stuff already available, it helps find bugs in the implementation of the winapi that otherwise wouldn’t be found as the Visio case demonstrated.
Amit: Tweak UI lets you disable the double-click-debouncing of single-click-mode.
I’ve seen other cases of double-clicks being treated as single-clicks or being ignored entirely, and single-clicks being ignored entirely. Usually the application involved is Outlook Express. I don’t know what it’s doing at the time to lose clicks.
Sometimes a right-click on a notification icon brings up two context menus. If the user clicks entries in both context menus then they both go away after taking actions. If the user clicks an entry in one context menu (as surely intended) then that menu goes away after taking action, but whether the other one goes away or not seems to be random. Usually the offending application is the power management icon. Sometimes both context menus relate to power management, but sometimes one is the taskbar’s main properties. Windows XP SP2 hasn’t stopped it but fortunately does seem to have stopped blue screens from following some of these occurences.
Te "About Mouse Input" page, linked to from the base note, is not found in the MSDN Library’s table of contents. No wonder its information is not well known.
I think this delayed mode of catching accidental double clicks should be turned off. If users don’t learn that they don’t need to double click then they will continue to double click. As it is, even in single click mode, it looks as if a double click is required to run a program, because that is what works. If a user accidently lauched two instances of an application by double clicking then Windows could pop up a message or tip to say something about not needing to double click, and give an option to turn on double click mode. Users are like children. If you never tell them off they’ll never learn.
By the way, speaking of debouncing, Office’s toolbars could really do with some. Right new they don’t seem to do any, and as a result if you are using a tablet PC pen to (say) delete emails using the toolbar, sometimes it’ll erroneously delete 3 or 4 of them at once.
Kind of frustrating!
The "clairvoyant" mouse input model is getting pretty common these days. I’ve implemented this model for the in-place editors in Planners, and even though Windows has no internal support for it, it is implemented by parts of the Windows user interfac…
On 10/15/2004 7:58 PM, asdf asked "Raymond: what’s your stance on SM_C(X|Y)DOUBLECLK in regards to people treating it as a radius".
I think asdf meant "diameter" not "radius".
FWIW, I liked to treat it as an actual radius of a circle in MacOS programming. My version might be something like:
const int dx = oldp.x – newp.x;
const int dy = oldp.y – newp.y;
const bool doubleClicked = (dx*dx + dy*dy < w*h);
Though to be correct, it should be an ellipse (I hope I haven’t blown the math):
const int w2 = w*w;
const int h2 = h*h;
const bool doubleClicked = (h2*dx*dx + w2*dy*dy < w2*h2);
If the mouse has moved a giant distance there might be a problem with overflow. This may be able to happen with optical scan mice.
PingBack from http://www.zuschlogin.com/?p=33
PingBack from http://www.redmountainsw.com/wordpress/archives/clairvoyant-interaction
There’s a logic behind it, although not everybody follows it.