Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Win32 backend: can't hover over nonclient areas when window does not have focus #6045

Closed
thundercarrot opened this issue Jan 5, 2023 · 5 comments
Labels
Milestone

Comments

@thundercarrot
Copy link

Version: 1.88
Branch: master/viewport/docking

Back-ends: imgui_impl_win32.cpp
Compiler: vs2022
Operating System: Windows 11

imgui buttons that are part of the nonclient area of the window behave correctly only when the window has focus:

When creating a frameless window with owner draw captions you can use imgui buttons for min/max/close buttons, by appropriately processing WM_NCHITTEST on the button regions. This works fine when the window has focus (is foreground). In this case AddMousePosEvent is not called from ImGui_ImplWin32_WndProcHandler's WM_MOUSEMOVE handler since they are considered nonclient areas. Instead, AddMousePosEvent is called from
ImGui_ImplWin32_UpdateMouseData(). However, UpdateMouseData checks first that the window has focus, so that when the window does not have focus AddMousePosEvent is never called and hover tests fail.

ImGui_ImplWin32_WndProcHandler should probably handle WM_NCMOUSEMOVE as well as WM_MOUSEMOVE.

@ocornut ocornut changed the title can't hover over nonclient areas when window does not have focus Win32 backend: can't hover over nonclient areas when window does not have focus Jan 5, 2023
@ocornut
Copy link
Owner

ocornut commented Jan 5, 2023

ImGui_ImplWin32_WndProcHandler should probably handle WM_NCMOUSEMOVE as well as WM_MOUSEMOVE.

It doesn't seem trivial to handle seems how WM_MOUSELEAVE are occasionally triggered after WM_NCMOUSEMOVE.

I came up with this:

Add bool MouseInClientArea; in ImGui_ImplWin32_Data.

    case WM_MOUSEMOVE:
    case WM_NCMOUSEMOVE:
    {
        // We need to call TrackMouseEvent in order to receive WM_MOUSELEAVE events
        bd->MouseHwnd = hwnd;
        bd->MouseInClientArea = (msg == WM_MOUSEMOVE);
        if (!bd->MouseTracked)
        {
            TRACKMOUSEEVENT tme = { sizeof(tme), TME_LEAVE, hwnd, 0 };
            ::TrackMouseEvent(&tme);
            bd->MouseTracked = true;
        }
        POINT pos = { (LONG)GET_X_LPARAM(lParam), (LONG)GET_Y_LPARAM(lParam) };
        if (msg == WM_NCMOUSEMOVE && ::ScreenToClient(hwnd, &pos) == FALSE) // WM_NCMOUSEMOVE are provided in screen coordinates.
            break;
        io.AddMousePosEvent((float)pos.x, (float)pos.y);
        break;
    }
    case WM_MOUSELEAVE:
    case WM_NCMOUSELEAVE:
        if (bd->MouseInClientArea == (msg == WM_MOUSELEAVE))
        {
            if (bd->MouseHwnd == hwnd)
                bd->MouseHwnd = nullptr;
            bd->MouseTracked = false;
            io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
        }
        break;

I would however need to test it more thoroughly, and with multi-viewports.
Could you test on your side?
That stuff tends to be tricky and fragile so if it comes with other undesirable side-effects we might need to back-track.

@thundercarrot
Copy link
Author

thundercarrot commented Jan 5, 2023

In handling WM_NCMOUSEMOVE I think you want TRACKMOUSEEVENT tme = { sizeof(tme), TME_LEAVE | TME_NONCLIENT, hwnd, 0 };

Getting WM_MOUSELEAVE after the first WM_NCMOUSEMOVE is unfortunate but not the end of the world. Otherwise I believe this works. One idea is to cancel the outstanding track mouse event (TME_CANCEL | TME_LEAVE) and issue a new one.

@thundercarrot
Copy link
Author

@ocornut your code above works perfectly with the addition of

if (msg == WM_NCMOUSEMOVE) tme.dwFlags |= TME_NONCLIENT;

@ocornut ocornut added this to the v1.90 milestone Feb 14, 2023
ocornut added a commit that referenced this issue Feb 15, 2023
…positions over non-client area (OS decorations) when app is not focused. (#6045, #6162)
@ocornut
Copy link
Owner

ocornut commented Feb 15, 2023

your code above works perfectly with the addition of

That didn't look right. When unfocused we don't always get leave events there.

I think I worked it our right always sending a cancel request when changing area.
Pushed 5d74891
Merged in docking via e25e452

cc @BobbyAnguelov would be good if you can confirm it works with vanilla backend and without your custom msg handler.

@ocornut ocornut closed this as completed Feb 15, 2023
@BobbyAnguelov
Copy link

Works in both the minimal example I created and in Esoterica.

Intrets pushed a commit to Intrets/imgui that referenced this issue Jun 14, 2024
…positions over non-client area (OS decorations) when app is not focused. (ocornut#6045, ocornut#6162)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants