If you create a Windows classic Win32 tooltip control, you get to specify whether you want the control to subclass the window with which is associated, or whether you promise to forward mouse messages with the TTM_
message. The tooltip control uses these mouse messages to help decide when the tooltip should be displayed and hidden.RELAYEVENT
If you agree to forward the messages yourself, then you may find that the tooltip control runs a one-second periodic timer for as long as the tooltip is visible. What’s that for?
The timer is there so the tooltip can detect when the mouse has left the associated window. When that happens, the tooltip hides itself because the mouse is no longer on the tooltip target.
Why does it need a timer to do this?
Rewind to 1994. The tooltip control is being developed, and the TTM_
message gives the tooltip control insight into what the mouse is doing in the associated window. It uses this information to detect that the mouse has dwelled inside the tooltip target for the required amount of time, which causes the tooltip to appear. It also uses this information to detect that the mouse has moved to another part of the associated window that is not part of the tooltip target, at which point it can remove the tooltip.RELAYEVENT
But there’s another case that isn’t covered by this: The tooltip needs to know when the mouse has left the tooltip target due to the mouse leaving the associated window entirely.
Since mouse messages are delivered to the window under the mouse cursor,¹ moving the mouse out of the associated window entirely means that the associated window has nothing to forward to the control via the TTM_
message The only way for the tooltip to know that the mouse has left the window entirely is for it to run a timer and poll the mouse position.RELAYEVENT
That’s how things were in Windows 95.
The documentation for the TTM_
message saysRELAYEVENT
A tooltip control processes only the following messages passed to it by the TTM_
RELAYEVENT message:
- WM_
LBUTTONDOWN
- WM_
LBUTTONUP
- WM_
MBUTTONDOWN
- WM_
MBUTTONUP
- WM_
MOUSEMOVE
- WM_
RBUTTONDOWN
- WM_
RBUTTONUP
All other messages are ignored.
Move forward to 1998. The TrackMouseEvent
function was added. Among other things, this allows a window to be notified when the mouse leaves the window outright. Great, the tooltip control can take advantage of this so that it doesn’t need to poll the mouse to find out whether it left the window. It can just wait for the WM_
message.MOUSELEAVE
Except that it can’t.
Because the TTM_
message already had documentation that said “All other messages are ignored.” Programs were written based on the fact that only the messages given in the documentation need to be forwarded to the tooltip control. If they got any other message, they “optimized” their code by not bothering to forward it to the tooltip control.RELAYEVENT
This meant that the tooltip control would never get the WM_
message, since the documentation told people that the tooltip control ignored the message.MOUSELEAVE
So despite the availability of an efficient and battery-friendly way of detecting whether the mouse has left a window, the tooltip control cannot use it because the documentation revealed too much information, and people came to rely on that extra information.
If the documentation had merely said, “Forward all messages between WM_
and MOUSEFIRST
WM_
to the tooltip control,” without enumerating which mouse messages the tooltip control actually cares about, then it would have been possible to use the efficient version, because everybody would be forwarding all mouse messages, which includes the new MOUSELAST
WM_
message.MOUSELEAVE
So things are bad because we wrote too much documentation. The documentation described the implementation rather than the contract.
All is not lost, however.
If you set the TTF_
flag when you create a tooltip target, then you are telling the tooltip control to subclass the window in order to grab the mouse messages. In this case, you don’t need to (and shouldn’t) use the SUBCLASS
TTM_
message. And if the tooltip control is subclassing the window, it can see all the messages, and that includes the RELAYEVENT
WM_
message.MOUSELEAVE
So use the TTF_
flag when you create your tooltip targets. Your tooltip will respond more promptly to the user moving out of the window, and you won’t burn up the user’s battery.SUBCLASS
¹ Assuming that mouse capture is not in effect. Tooltips do not capture the mouse, because that would prevent the user from using the mouse to do normal mouse things.
As I always say: “Timers are the developer’s duck tape”
Heh, I always assumed TrackMouseEvent was implemented internally using a timer.
(The atom feeds seems to be broken again. But hey, it at least it made it to this decade… looks like the RSS feed last updated on New Year’s Eve…)
Edit: Fixed again, thanks!
Oh no, it’s too late to suggest a ‘British Monarchy Succession” tree-traversal article 🙁
I think that’s the same as preorder, isn’t it?
I think there is a post-traversal removal of any nodes that are Roman Catholic 🙂 It would have been more complicated before the removal of male-preference primogeniture in 2015
How do you evaluate the gender of a tree node?
Same way you distinguish a mail plane 🙂
Maybe the tooltip control can offer optimization that as soon as it see a WM_MOUSELEAVE message got forwarded, it can silently disable the timer it created.
So even if the control isn’t subclassed, you can avoid burning user battery after (maybe) a few minutes. (And add a new tips in category of “weird Windows workaround with good reason”.)
CCM_SETVERSION could be used to allow different behavior for new comctl32 versions, that is why this message exists. Comctl v6 was also a new opportunity to change behaviors.
“Hey, I think four years ago, there was this thing we wanted to do but couldn’t. Do you remember what that was?” (This assumes that there’s even a person from the old team that is still on the new team.)
Shouldn’t it have been documented in a bug database somewhere?
“Hey, go look in our 24-million-entry database to see if anything applies here.”
Ah, that would be the problem. I had assumed the old bug databases had been migrated. I suppose that doesn’t really make sense given that Windows 98 is a completely different architecture, but I hadn’t thought about it that way.
I’m not saying scroll through the whole thing; that is obviously a bad plan. It’s a database. Filter on status (open or on hold or wontfix-compatibility or whatever) and component (comctl32) and you’d have a to-do list of things to consider for v6. Maybe I don’t understand the quirks of the system in question but it seems like you should be able to do something along those lines.
That's still a lot of bugs to have to read through to see if any of them have a comment of the form "In the future, we might choose to..." And the bugs might be in databases that have been retired, so you will never find them. In this case, the "In the future, we might choose to..." would be a comment in the Windows 98 bug database, but comctl32 v6 didn't arrive until Windows...