1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Windows: Fix bug where mouse events would not reach background windows blocked by modal windows

This commit is contained in:
reuk 2025-06-19 17:29:31 +01:00
parent b0a3be2bb4
commit 2d243486c9
No known key found for this signature in database

View file

@ -2813,6 +2813,11 @@ private:
doMouseEvent (getPointFromLocalLParam (lParam), MouseInputSource::defaultPressure); doMouseEvent (getPointFromLocalLParam (lParam), MouseInputSource::defaultPressure);
} }
// If this is the first event after receiving both a MOUSEACTIVATE and a SETFOCUS, then
// process the postponed focus update.
if (std::exchange (mouseActivateFlags, (uint8_t) 0) == (gotMouseActivate | gotSetFocus))
handleSetFocus();
} }
void doMouseUp (Point<float> position, const WPARAM wParam, bool adjustCapture = true) void doMouseUp (Point<float> position, const WPARAM wParam, bool adjustCapture = true)
@ -3820,6 +3825,27 @@ private:
return {}; return {};
} }
void handleSetFocus()
{
/* When the HWND receives Focus from the system it sends a
UIA_AutomationFocusChangedEventId notification redirecting the focus to the HWND
itself. This is a built-in behaviour of the HWND.
This means that whichever JUCE managed provider was active before the entire
window lost and then regained the focus, loses its focused state, and the
window's root element will become focused under which all JUCE managed providers
can be found.
This needs to be reflected on currentlyFocusedHandler so that the JUCE
accessibility mechanisms can detect that the root window got the focus, and send
another FocusChanged event to the system to redirect focus to a JUCE managed
provider if necessary.
*/
AccessibilityHandler::clearCurrentlyFocusedHandler();
updateKeyModifiers();
handleFocusGain();
}
LRESULT peerWindowProc (HWND h, UINT message, WPARAM wParam, LPARAM lParam) LRESULT peerWindowProc (HWND h, UINT message, WPARAM wParam, LPARAM lParam)
{ {
switch (message) switch (message)
@ -4120,23 +4146,14 @@ private:
//============================================================================== //==============================================================================
case WM_SETFOCUS: case WM_SETFOCUS:
/* When the HWND receives Focus from the system it sends a mouseActivateFlags |= gotSetFocus;
UIA_AutomationFocusChangedEventId notification redirecting the focus to the HWND
itself. This is a built-in behaviour of the HWND.
This means that whichever JUCE managed provider was active before the entire // If we've received a MOUSEACTIVATE, wait until we've seen the relevant mouse event
window lost and then regained the focus, loses its focused state, and the // before updating the focus.
window's root element will become focused under which all JUCE managed providers if ((mouseActivateFlags & gotMouseActivate) != 0)
can be found. break;
This needs to be reflected on currentlyFocusedHandler so that the JUCE handleSetFocus();
accessibility mechanisms can detect that the root window got the focus, and send
another FocusChanged event to the system to redirect focus to a JUCE managed
provider if necessary.
*/
AccessibilityHandler::clearCurrentlyFocusedHandler();
updateKeyModifiers();
handleFocusGain();
break; break;
case WM_KILLFOCUS: case WM_KILLFOCUS:
@ -4186,10 +4203,15 @@ private:
case WM_POINTERACTIVATE: case WM_POINTERACTIVATE:
case WM_MOUSEACTIVATE: case WM_MOUSEACTIVATE:
{
mouseActivateFlags = 0;
if (! component.getMouseClickGrabsKeyboardFocus()) if (! component.getMouseClickGrabsKeyboardFocus())
return MA_NOACTIVATE; return MA_NOACTIVATE;
mouseActivateFlags |= gotMouseActivate;
break; break;
}
case WM_SHOWWINDOW: case WM_SHOWWINDOW:
if (wParam != 0) if (wParam != 0)
@ -4731,6 +4753,26 @@ private:
IMEHandler imeHandler; IMEHandler imeHandler;
bool shouldIgnoreModalDismiss = false; bool shouldIgnoreModalDismiss = false;
/* When the user clicks on a window, the window gets sent WM_MOUSEACTIVATE, WM_ACTIVATE,
and WM_SETFOCUS, before sending a WM_LBUTTONDOWN or other pointer event.
However, if the WM_SETFOCUS message immediately calls SetFocus to move the focus to a
different window (e.g. a foreground modal window), then no mouse event will be sent to the
initially-activated window. This differs from the behaviour on other platforms, where the
mouse event always reaches the activated window. Failing to emit a mouse event breaks user
interaction: in the Toolbars pane of the WidgetsDemo, we create a foreground modal
customisation dialog. The window containing the toolbar is not modal, but still expects to
receive mouse events so that the toolbar buttons can be dragged around.
To avoid the system eating the mouse event sent to the initially-activated window, we
postpone processing the WM_SETFOCUS until *after* the activation mouse event.
*/
enum MouseActivateFlags : uint8_t
{
gotMouseActivate = 1 << 0,
gotSetFocus = 1 << 1,
};
uint8_t mouseActivateFlags = 0;
ScopedSuspendResumeNotificationRegistration suspendResumeRegistration; ScopedSuspendResumeNotificationRegistration suspendResumeRegistration;
std::optional<TimedCallback> monitorUpdateTimer; std::optional<TimedCallback> monitorUpdateTimer;