Cocoa: Handle more cases of lost focus when Key window closes (thanks, Alex!).
authorRyan C. Gordon <icculus@icculus.org>
Sun, 22 Mar 2015 01:25:12 -0400
changeset 9419 9763f689bced
parent 9418 eaafb42daa70
child 9420 5ffb5a958c43
Cocoa: Handle more cases of lost focus when Key window closes (thanks, Alex!). Sort of fixes Bugzilla #1825 a little more. It's an ongoing effort. :)
src/video/cocoa/SDL_cocoaevents.m
src/video/cocoa/SDL_cocoawindow.m
--- a/src/video/cocoa/SDL_cocoaevents.m	Sat Mar 21 22:42:53 2015 +0100
+++ b/src/video/cocoa/SDL_cocoaevents.m	Sun Mar 22 01:25:12 2015 -0400
@@ -66,11 +66,19 @@
 {
     self = [super init];
     if (self) {
+        NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+
         seenFirstActivate = NO;
-        [[NSNotificationCenter defaultCenter] addObserver:self
-                                                 selector:@selector(focusSomeWindow:)
-                                                     name:NSApplicationDidBecomeActiveNotification
-                                                   object:nil];
+
+        [center addObserver:self
+                   selector:@selector(windowWillClose:)
+                       name:NSWindowWillCloseNotification
+                     object:nil];
+
+        [center addObserver:self
+                   selector:@selector(focusSomeWindow:)
+                       name:NSApplicationDidBecomeActiveNotification
+                     object:nil];
     }
 
     return self;
@@ -78,16 +86,65 @@
 
 - (void)dealloc
 {
-    [[NSNotificationCenter defaultCenter] removeObserver:self];
+    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+
+    [center removeObserver:self name:NSWindowWillCloseNotification object:nil];
+    [center removeObserver:self name:NSApplicationDidBecomeActiveNotification object:nil];
+
     [super dealloc];
 }
 
+- (void)windowWillClose:(NSNotification *)notification;
+{
+    NSWindow *win = (NSWindow*)[notification object];
+
+    if (![win isKeyWindow]) {
+        return;
+    }
+
+    /* HACK: Make the next window in the z-order key when the key window is
+     * closed. The custom event loop and/or windowing code we have seems to
+     * prevent the normal behavior: https://bugzilla.libsdl.org/show_bug.cgi?id=1825
+     */
+
+    /* +[NSApp orderedWindows] never includes the 'About' window, but we still
+     * want to try its list first since the behavior in other apps is to only
+     * make the 'About' window key if no other windows are on-screen.
+     */
+    for (NSWindow *window in [NSApp orderedWindows]) {
+        if (window != win && [window canBecomeKeyWindow]) {
+            if ([window respondsToSelector:@selector(isOnActiveSpace)]) {
+                if (![window isOnActiveSpace]) {
+                    continue;
+                }
+            }
+            [window makeKeyAndOrderFront:self];
+            return;
+        }
+    }
+
+    /* If a window wasn't found above, iterate through all visible windows
+     * (including the 'About' window, if it's shown) and make the first one key.
+     * Note that +[NSWindow windowNumbersWithOptions:] was added in 10.6.
+     */
+    if ([NSWindow respondsToSelector:@selector(windowNumbersWithOptions:)]) {
+        /* Get all visible windows in the active Space, in z-order. */
+        for (NSNumber *num in [NSWindow windowNumbersWithOptions:0]) {
+            NSWindow *window = [NSApp windowWithWindowNumber:[num integerValue]];
+            if (window && window != win && [window canBecomeKeyWindow]) {
+                [window makeKeyAndOrderFront:self];
+                return;
+            }
+        }
+    }
+}
+
 - (void)focusSomeWindow:(NSNotification *)aNotification
 {
     /* HACK: Ignore the first call. The application gets a
      * applicationDidBecomeActive: a little bit after the first window is
      * created, and if we don't ignore it, a window that has been created with
-     * SDL_WINDOW_MINIZED will ~immediately be restored.
+     * SDL_WINDOW_MINIMIZED will ~immediately be restored.
      */
     if (!seenFirstActivate) {
         seenFirstActivate = YES;
--- a/src/video/cocoa/SDL_cocoawindow.m	Sat Mar 21 22:42:53 2015 +0100
+++ b/src/video/cocoa/SDL_cocoawindow.m	Sun Mar 22 01:25:12 2015 -0400
@@ -374,7 +374,6 @@
     NSNotificationCenter *center;
     NSWindow *window = _data->nswindow;
     NSView *view = [window contentView];
-    NSArray *windows = nil;
 
     center = [NSNotificationCenter defaultCenter];
 
@@ -402,25 +401,6 @@
     if ([view nextResponder] == self) {
         [view setNextResponder:nil];
     }
-
-    /* Make the next window in the z-order Key. If we weren't the foreground
-       when closed, this is a no-op.
-       !!! FIXME: Note that this is a hack, and there are corner cases where
-       !!! FIXME:  this fails (such as the About box). The typical nib+RunLoop
-       !!! FIXME:  handles this for Cocoa apps, but we bypass all that in SDL.
-       !!! FIXME:  We should remove this code when we find a better way to
-       !!! FIXME:  have the system do this for us. See discussion in
-       !!! FIXME:   http://bugzilla.libsdl.org/show_bug.cgi?id=1825
-    */
-    windows = [NSApp orderedWindows];
-    for (NSWindow *win in windows) {
-        if (win == window) {
-            continue;
-        }
-
-        [win makeKeyAndOrderFront:self];
-        break;
-    }
 }
 
 - (BOOL)isMoving