Cocoa: Fix missing key window after closing "About"
authorSergey Sharybin <sergey.vfx@gmail.com>
Sun, 17 Mar 2019 11:14:09 +0000 (12:14 +0100)
committerSergey Sharybin <sergey.vfx@gmail.com>
Sun, 17 Mar 2019 12:24:38 +0000 (13:24 +0100)
The cause of this issue goes deeper inside of the custom nature of
the event loop. In short, when not using [NSApp run] closing "About"
window does not make previous key window a key again.

The solution is quite similar to other projects, but we only force
key window from handler when closing one appears to be "About" window.
In all other cases we leave it up to Blender's window manager to make
decision and do not interfere with it.

Test plan:
- Open Blender application
- Go to Blender -> About Blender menu item
- Close About window
- Note that Blender's window does not become active again.

Reviewers: brecht

Reviewed By: brecht

Differential Revision: https://developer.blender.org/D4534

intern/ghost/intern/GHOST_SystemCocoa.mm

index b749587..52031e7 100644 (file)
@@ -282,6 +282,9 @@ extern "C" int GHOST_HACK_getFirstFile(char buf[FIRSTFILEBUFLG])
 
        GHOST_SystemCocoa *systemCocoa;
 }
+
+- (id)init;
+- (void)dealloc;
 - (void)setSystemCocoa:(GHOST_SystemCocoa *)sysCocoa;
 - (void)applicationDidFinishLaunching:(NSNotification *)aNotification;
 - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename;
@@ -289,9 +292,28 @@ extern "C" int GHOST_HACK_getFirstFile(char buf[FIRSTFILEBUFLG])
 - (void)applicationWillTerminate:(NSNotification *)aNotification;
 - (void)applicationWillBecomeActive:(NSNotification *)aNotification;
 - (void)toggleFullScreen:(NSNotification *)notification;
+- (void)windowWillClose:(NSNotification*)notification;
 @end
 
 @implementation CocoaAppDelegate : NSObject
+- (id)init {
+       self = [super init];
+       if (self) {
+               NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
+               [center addObserver:self
+                          selector:@selector(windowWillClose:)
+                              name:NSWindowWillCloseNotification
+                            object:nil];
+       }
+       return self;
+}
+
+- (void)dealloc {
+       NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
+       [center removeObserver:self name:NSWindowWillCloseNotification object:nil];
+       [super dealloc];
+}
+
 -(void)setSystemCocoa:(GHOST_SystemCocoa *)sysCocoa
 {
        systemCocoa = sysCocoa;
@@ -341,6 +363,53 @@ extern "C" int GHOST_HACK_getFirstFile(char buf[FIRSTFILEBUFLG])
 {
 }
 
+// The purpose of this function is to make sure closing "About" window does not
+// leave Blender with no key windows. This is needed due to a custom event loop
+// nature of the application: for some reason only using [NSApp run] will ensure
+// correct behavior in this case.
+//
+// This is similar to an issue solved in SDL:
+//   https://bugzilla.libsdl.org/show_bug.cgi?id=1825
+//
+// Our solution is different, since we want Blender to keep track of what is
+// the key window during normal operation. In order to do so we exploit the
+// fact that "About" window is never in the orderedWindows array: we only force
+// key window from here if the closing one is not in the orderedWindows. This
+// saves lack of key windows when closing "About", but does not interfere with
+// Blender's window manager when closing Blender's windows.
+- (void)windowWillClose:(NSNotification*)notification {
+       NSWindow* closing_window = (NSWindow*)[notification object];
+       NSInteger index = [[NSApp orderedWindows] indexOfObject:closing_window];
+       if (index != NSNotFound) {
+               return;
+       }
+       // Find first suitable window from the current space.
+       for (NSWindow* current_window in [NSApp orderedWindows]) {
+               if (current_window == closing_window) {
+                       continue;
+               }
+               if ([current_window isOnActiveSpace] &&
+                   [current_window canBecomeKeyWindow])
+               {
+                       [current_window makeKeyAndOrderFront:nil];
+                       return;
+               }
+       }
+       // If that didn't find any windows, we try to find any suitable window of
+       // the application.
+       for (NSNumber* window_number in [NSWindow windowNumbersWithOptions:0]) {
+               NSWindow* current_window =
+                         [NSApp windowWithWindowNumber:[window_number integerValue]];
+               if (current_window == closing_window) {
+                       continue;
+               }
+               if ([current_window canBecomeKeyWindow]) {
+                       [current_window makeKeyAndOrderFront:nil];
+                       return;
+               }
+       }
+}
+
 @end