From 01000c73b0207e7e37d175a450224c5934e71707 Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Sat, 26 Apr 2025 03:31:22 -0400
Subject: [PATCH] cocoa: Immediately update the mouse focus when showing/hiding
a popup menu
When showing or hiding a popup menu, manually check and set the focus if the new topmost window under the cursor is an SDL window. Otherwise, the focus won't be updated until the cursor is actually moved.
(cherry picked from commit 6f5892e5436259357fe3befad74e55844d8fa02d)
---
src/video/cocoa/SDL_cocoawindow.m | 37 ++++++++++++++++++++++++++++++-
1 file changed, 36 insertions(+), 1 deletion(-)
diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m
index 9c047579f8039..3e9642a3c01e1 100644
--- a/src/video/cocoa/SDL_cocoawindow.m
+++ b/src/video/cocoa/SDL_cocoawindow.m
@@ -2115,6 +2115,36 @@ - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
@end
+static void Cocoa_UpdateMouseFocus()
+{
+ const NSPoint mouseLocation = [NSEvent mouseLocation];
+
+ // Find the topmost window under the pointer and send a motion event if it is an SDL window.
+ [NSApp enumerateWindowsWithOptions:NSWindowListOrderedFrontToBack
+ usingBlock:^(NSWindow *nswin, BOOL *stop) {
+ NSRect r = [nswin contentRectForFrameRect:[nswin frame]];
+ if (NSPointInRect(mouseLocation, r)) {
+ SDL_VideoDevice *vid = SDL_GetVideoDevice();
+ SDL_Window *sdlwindow;
+ for (sdlwindow = vid->windows; sdlwindow; sdlwindow = sdlwindow->next) {
+ if (nswin == ((__bridge SDL_CocoaWindowData *)sdlwindow->internal).nswindow) {
+ break;
+ }
+ }
+ *stop = YES;
+ if (sdlwindow) {
+ int wx, wy;
+ SDL_RelativeToGlobalForWindow(sdlwindow, sdlwindow->x, sdlwindow->y, &wx, &wy);
+
+ // Calculate the cursor coordinates relative to the window.
+ const float dx = mouseLocation.x - wx;
+ const float dy = (CGDisplayPixelsHigh(kCGDirectMainDisplay) - mouseLocation.y) - wy;
+ SDL_SendMouseMotion(0, sdlwindow, SDL_GLOBAL_MOUSE_ID, false, dx, dy);
+ }
+ }
+ }];
+}
+
static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, NSWindow *nswindow, NSView *nsview)
{
@autoreleasepool {
@@ -2213,8 +2243,9 @@ then immediately ordering out (removing) the window does work. */
if (window->flags & SDL_WINDOW_TOOLTIP) {
[nswindow setIgnoresMouseEvents:YES];
[nswindow setAcceptsMouseMovedEvents:NO];
- } else if (window->flags & SDL_WINDOW_POPUP_MENU) {
+ } else if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_HIDDEN)) {
Cocoa_SetKeyboardFocus(window, window->parent == SDL_GetKeyboardFocus());
+ Cocoa_UpdateMouseFocus();
}
}
@@ -2599,6 +2630,9 @@ void Cocoa_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
[nswindow orderWindow:NSWindowBelow relativeTo:[[NSApp keyWindow] windowNumber]];
}
}
+ } else if (window->flags & SDL_WINDOW_POPUP_MENU) {
+ Cocoa_SetKeyboardFocus(window, window->parent == SDL_GetKeyboardFocus());
+ Cocoa_UpdateMouseFocus();
}
}
[nswindow setIsVisible:YES];
@@ -2646,6 +2680,7 @@ void Cocoa_HideWindow(SDL_VideoDevice *_this, SDL_Window *window)
}
Cocoa_SetKeyboardFocus(new_focus, set_focus);
+ Cocoa_UpdateMouseFocus();
} else if (window->parent && waskey) {
/* Key status is not automatically set on the parent when a child is hidden. Check if the
* child window was key, and set the first visible parent to be key if so.