tags:

views:

426

answers:

3

In my Win32 app, I don't get WM_MOUSELEAVE messages when I hold down the left mouse button and quickly move the mouse pointer out of the window. But If I, holding down the left mouse button, start from the inside of the window and move slowly past the window edge, it'll generate a WM_MOUSELEAVE.

If I don't hold the left mouse button, I get WM_MOUSELEAVE messages every time no matter how fast the mouse pointer moves off the window.

What's the difference? What can I do to handle both cases properly?

EDIT: If I left click and hold, move out of the window and then let go of the left mouse button I get the WM_MOUSELEAVE message. But it's way too late.

A: 

As waiting for a WM_MOUSELEAVE is unreliable, the best solution I've found is to directly look at the mouse position during a WM_MOUSEMOVE. I compare the mouse position against the client area and if the position is outside then I handle that as a mouse leave.

I also make sure to call SetCapture when the mouse position is within the client area and ReleaseCapture when it leaves.

Steve the Plant
Remember, SetCapture *only* works for this when the user presses a mouse button within your window. Were you trying to use SetCapture and TrackMouseEvent together? That could explain what you were seeing...
Shog9
+1  A: 

WM_MOUSELEAVE is so that you can detect the mouse leaving your window when you don't have capture. When you have capture, you are responsible for detecting that yourself (if you care).

so It doesn't make any sense to SetCapture AND TrackMouseEvent at the same time, you would use one or the other.

Now, if it would be more convenient for you to see the WM_MOUSELEAVE messages while you have capture, it's a relatively simple matter to do that by yourself in your message pump.

You would just add code that looks something like this between the GetMessage() and the DispatchMessage() calls in your message pump.

  GetMessage(pmsg, ...);

  .....

  if ((IS_WITHIN(pmsg->message, WM_MOUSEFIRST, WM_MOUSELAST) ||
       IS_WITHIN(pmsg->message, WM_NCMOUSEMOVE, WM_NCMBUTTONDBLCLK)) &&
       MyMouseLeaveDetection(pmsg, g_hwndNotifyMouseLeave))
     {
     MSG msg = *pmsg;
     msg.message = WM_MOUSELEAVE;
     msg.hwnd    = g_hwndNotifyMouseLeave; // window that want's 
     msg.lParam  = 0xFFFFFFFF;
     g_hwndNotifyMouseLeave = NULL;

     DispatchMessage (&msg);
     }

 .....
 TranslateMessage(pmsg);
 DispatchMessage(pmsg);
John Knoeller
A: 

On Windows 7, I was trying to make an owner-drawn button. I subclassed the button in order to get more accurate mouseenter/mouseleave events, essentially. When doing this, I used TrackMouseEvent when I got a WM_MOUSEMOVE because this is only posted when the mouse is over the button. If not already set, I would then set a boolean to specify that the mouse is over the button as well as call TrackMouseEvent so that whenever the mouse left, I could unset my boolean. However, like you, I was not getting the WM_MOUSELEAVE when I pressed and held the left mouse button on the owner drawn button, then dragging the mouse out. Upon releasing the mouse outside of the button, I suddenly get the WM_MOUSELEAVE message - way too late.

I determined that the reason for this behavior was that the default button proc's handling for WM_LBUTTONDOWN calls SetCapture, and releases it later. The usage of SetCapture is what is breaking our reception of the WM_MOUSELEAVE event. However, as a side-effect of SetCapture being called, we will get WM_MOUSEMOVE events even if the control is not under the mouse. Thus, my workaround is duplicating the logic in WM_MOUSELEAVE in the WM_MOUSEMOVE handler to unset my boolean that indicates the mouse is over the button if I get a mousemove event that is outside of my button's area. If SetCapture isn't actually used inside WM_LBUTTONDOWN for the default button proc, then we'll already be getting our WM_MOUSELEAVE message and the code will still work... so this workaround works in both cases.

Your problem sounds like it is likely identical to mine, so hopefully this helps you.

Jacob McIntosh