Backends: Win32: Use WM_NCMOUSEMOVE / WM_NCMOUSELEAVE to track mouse positions over non-client area (OS decorations) when app is not focused. (#6045, #6162)
diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp
index 7ebb58c..7cc5717 100644
--- a/backends/imgui_impl_win32.cpp
+++ b/backends/imgui_impl_win32.cpp
@@ -34,6 +34,7 @@
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
+// 2023-02-15: Inputs: Use WM_NCMOUSEMOVE / WM_NCMOUSELEAVE to track mouse position over non-client area (e.g. OS decorations) when app is not focused. (#6045, #6162)
// 2023-02-02: Inputs: Flipping WM_MOUSEHWHEEL (horizontal mouse-wheel) value to match other backends and offer consistent horizontal scrolling direction. (#4019, #6096, #1463)
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
// 2022-09-28: Inputs: Convert WM_CHAR values with MultiByteToWideChar() when window class was registered as MBCS (not Unicode).
@@ -84,7 +85,7 @@
{
HWND hWnd;
HWND MouseHwnd;
- bool MouseTracked;
+ int MouseTrackedArea; // 0: not tracked, 1: client are, 2: non-client area
int MouseButtonsDown;
INT64 Time;
INT64 TicksPerSecond;
@@ -267,7 +268,8 @@
}
// (Optional) Fallback to provide mouse position when focused (WM_MOUSEMOVE already provides this when hovered or captured)
- if (!io.WantSetMousePos && !bd->MouseTracked)
+ // This also fills a short gap when clicking non-client area: WM_NCMOUSELEAVE -> modal OS move -> gap -> WM_NCMOUSEMOVE
+ if (!io.WantSetMousePos && bd->MouseTrackedArea == 0)
{
POINT pos;
if (::GetCursorPos(&pos) && ::ScreenToClient(bd->hWnd, &pos))
@@ -515,25 +517,39 @@
switch (msg)
{
case WM_MOUSEMOVE:
+ case WM_NCMOUSEMOVE:
{
// We need to call TrackMouseEvent in order to receive WM_MOUSELEAVE events
+ const int area = (msg == WM_MOUSEMOVE) ? 1 : 2;
bd->MouseHwnd = hwnd;
- if (!bd->MouseTracked)
+ if (bd->MouseTrackedArea != area)
{
- TRACKMOUSEEVENT tme = { sizeof(tme), TME_LEAVE, hwnd, 0 };
- ::TrackMouseEvent(&tme);
- bd->MouseTracked = true;
+ TRACKMOUSEEVENT tme_cancel = { sizeof(tme_cancel), TME_CANCEL, hwnd, 0 };
+ TRACKMOUSEEVENT tme_track = { sizeof(tme_track), (DWORD)((area == 2) ? (TME_LEAVE | TME_NONCLIENT) : TME_LEAVE), hwnd, 0 };
+ if (bd->MouseTrackedArea != 0)
+ ::TrackMouseEvent(&tme_cancel);
+ ::TrackMouseEvent(&tme_track);
+ bd->MouseTrackedArea = area;
}
POINT mouse_pos = { (LONG)GET_X_LPARAM(lParam), (LONG)GET_Y_LPARAM(lParam) };
+ if (msg == WM_NCMOUSEMOVE && ::ScreenToClient(hwnd, &mouse_pos) == FALSE) // WM_NCMOUSEMOVE are provided in absolute coordinates.
+ break;
io.AddMousePosEvent((float)mouse_pos.x, (float)mouse_pos.y);
break;
}
case WM_MOUSELEAVE:
- if (bd->MouseHwnd == hwnd)
- bd->MouseHwnd = nullptr;
- bd->MouseTracked = false;
- io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
+ case WM_NCMOUSELEAVE:
+ {
+ const int area = (msg == WM_MOUSELEAVE) ? 1 : 2;
+ if (bd->MouseTrackedArea == area)
+ {
+ if (bd->MouseHwnd == hwnd)
+ bd->MouseHwnd = nullptr;
+ bd->MouseTrackedArea = 0;
+ io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
+ }
break;
+ }
case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK:
case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK:
case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK:
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 0973268..642a1ac 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -35,6 +35,14 @@
VERSION 1.89.4 WIP (In Progress)
-----------------------------------------------------------------------
+Breaking Changes:
+
+Other changes:
+
+- Backends: Win32: Use WM_NCMOUSEMOVE / WM_NCMOUSELEAVE to track mouse positions over
+ non-client area (e.g. OS decorations) when app is not focused. (#6045, #6162)
+
+
-----------------------------------------------------------------------
VERSION 1.89.3 (Released 2023-02-14)
@@ -50,7 +58,7 @@
- imgui_impl_sdl.h -> imgui_impl_sdl2.h
- example_sdl_xxxx/ -> example_sdl2_xxxx/ (folders and projects)
-All changes:
+Other changes:
- SeparatorText(): Added SeparatorText() widget. (#1643) [@phed, @ocornut]
- Added to style: float SeparatorTextBorderSize.