Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reset cursor in WM_SETCURSOR handler properly

Tags:

c++

winapi

INTRODUCTION AND RELEVANT INFORMATION:

I have made an application that needs to change the look of a cursor into hand when mouse hovers above the static control, but resets it to a normal cursor otherwise.

My initial application was in full screen mode, but recently terms have changed and it must have a resizable window.

This means that my handler for WM_SETCURSOR must be rewritten to reflect newly introduced changes.

Cursors are loaded in WM_CREATE, and I have defined class cursor, like this:

   // cursors 
   case WM_CREATE:
      hCursorHand = LoadCursor( NULL, IDC_HAND );
      hCursorArrow = LoadCursor( NULL, IDC_ARROW );
      // other stuff

In my class:

   WNDCLASSEX wc;
   // ...
   wc.hCursor = hCursorArrow;
   //...

This is my old WM_CURSOR handler ( code is simplified for clarity purposes ):

   case WM_SETCURSOR:
        if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) ) 
             SetCursor(hCursorHand);
        else
             SetCursor(hCursorArrow);
        return TRUE;

If cursor hovers above static control, then my handler changes it to hand, else sets it to default cursor ( arrow ).

Bellow is the picture I have sketched in Paint that displays the desired look of the cursor when it hovers above static control, it is on the client area, and when user resizes window.

enter image description here

If additional code snippets are required, ask and I will edit my post, but for now, they are omitted to keep the post short and concise.

I work on Windows XP, using MS Visual Studio C++ and pure Win32 API.

MY EFFORTS TO SOLVE PROBLEM:

Bellow are the code snippets that I have tried, but they all failed:

First snippet:

   case WM_SETCURSOR:
        if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) ) 
        {
             SetCursor(hCursorHand); 
             return TRUE; 
        }
        else
             return DefWindowProc( hWnd, msg, lParam, wParam );

Second Snippet:

   case WM_SETCURSOR:
        if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) ) 
        {
             SetCursor(hCursorHand); 
             return TRUE; 
        }
        break; // based on MSDN example

Third snippet:

   case WM_SETCURSOR:
        if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) ) 
        {
             SetCursor(hCursorHand); 
             return TRUE; 
        }
        else
             return FALSE;

These set cursor to hand no matter where it is.

If I leave my WM_SETCURSOR handler unchanged, the only problem I get is that instead of sizing arrows, I get regular arrow ( as the cursor’s look ) when I hover over the border, but window can be sized.

If I comment out my WM_SETCURSOR handler, sizing arrows and cursor arrow appear properly, but cursor doesn’t change into hand when hovers above static control ( which is logical, since there is no WM_SETCURSOR handler to change it ).

I have browsed through SO archive, looked on MSDN, CodeProject , DaniWeb, Cprogramming and CodeGuru, but had no success.

Looking through those, I have found an example where people compare low word of the lParam against hit test code.

Looking through MSDN I have found link for hit test values ( http://msdn.microsoft.com/en-us/library/windows/desktop/ms645618%28v=vs.85%29.aspx ) and I have found link for cursor types (http://msdn.microsoft.com/en-us/library/windows/desktop/ms648391%28v=vs.85%29.aspx ).

Currently I am reading them, because I think that I will have to load additional cursor resources, take several comparisons of hit test values, and then use those resources to set cursor look accordingly.

QUESTION:

I really would like my WM_SETCURSOR handler to look like this:

   case WM_SETCURSOR:
        if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) ) 
        {
             SetCursor(hCursorHand); 
             return TRUE;
        }
        else
             // reset cursor's look to default

so I ask the community to instruct me on how to do this.

If this isn’t possible, then I will consider using multiple if statements to check the hit test code, and set cursor’s look accordingly.

Of course, if there is better solution for my problem, please suggest it, I will consider it as well.

Thank you.

Regards.

like image 911
AlwaysLearningNewStuff Avatar asked Oct 08 '13 20:10

AlwaysLearningNewStuff


2 Answers

In general, if you handle the WM_SETCURSOR message you must either

  • Call SetCursor() to set the cursor, and return TRUE, or
  • If the message came from a child window, return FALSE for default processing, or
  • If the message is from your own window, pass the message through to DefWindowProc()

I think the last two points aren't made quite clear by the MSDN docs.

The window under the mouse pointer gets the first WM_SETCURSOR message. If it handles it and returns at that point, nothing else happens. If however it calls DefWindowProc(), then DWP forwards the message to the window's parent to handle. If the parent chooses not to handle it, it can return FALSE and the DefWindowProc processing will continue.

But this only applies if the message came from a previous call to DWP. If the message originated with the window itself, rather than a child, returning TRUE or FALSE without setting the cursor means the cursor won't be set at all.

Another thing: although your question didn't specify, I'm assuming from your use of GetDlgItem() that your top-level window is a dialog. If that's true, you can't just return TRUE or FALSE for a message - you need to return the value using SetWindowLongPtr() and store the return value in DWLP_MSGRESULT. Returning FALSE from a dialog procedure indicates that you didn't handle the message at all - this is equivalent to passing a message through to DefWindowProc().

So I think the proper handling for your situation is, in your top-level window:

case WM_SETCURSOR:
    if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) ) 
    {
        SetCursor(hCursorHand); 
        SetWindowLongPtr(hwnd, DWLP_MSGRESULT, TRUE);
        return TRUE;
    }
    return FALSE;

If your top-level window isn't in fact a dialog, you would do this:

case WM_SETCURSOR:
    if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) ) 
    {
        SetCursor(hCursorHand); 
        return TRUE;
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
like image 58
Jonathan Potter Avatar answered Oct 10 '22 06:10

Jonathan Potter


Here's my first example, if cursor go to menubar, cursor changes to cursor hand:

HCURSOR cursorHand = LoadCursor(NULL, IDC_HAND);
case WM_SETCURSOR:
    if(LOWORD(lParam) == HTMENU)
    {
          SetCursor(cursorHand);
    }
 break;

Here's my second example, if cursor goes to button, cursor changes to cursorHand:

HCURSOR cursorHand =  LoadCursor(NULL, IDC_HAND);

case WM_SETCURSOR:
      if(LOWORD(lParam) == buttonId)//declare you
      {
            SetCursor(cursorHand);
       }
   break;

Warning:menubar and button is not created! Create you and please, check my answer example.

like image 35
GobeRadJem32 Avatar answered Oct 10 '22 05:10

GobeRadJem32