1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #import <Cocoa/Cocoa.h>
7 #include "ui/base/cocoa/focus_window_set.h"
13 // This attempts to match OS X's native behavior, namely that a window
14 // is only ever deminiaturized if ALL windows on ALL workspaces are
16 void FocusWindowSetHelper(const std::set<NSWindow*>& windows,
17 bool allow_workspace_switch,
18 bool visible_windows_only) {
19 NSArray* ordered_windows = [NSApp orderedWindows];
20 NSWindow* frontmost_window = nil;
21 NSWindow* frontmost_window_all_spaces = nil;
22 NSWindow* frontmost_miniaturized_window = nil;
23 bool all_miniaturized = true;
24 for (int i = [ordered_windows count] - 1; i >= 0; i--) {
25 NSWindow* win = [ordered_windows objectAtIndex:i];
26 if (windows.find(win) == windows.end())
28 if ([win isMiniaturized]) {
29 frontmost_miniaturized_window = win;
30 } else if (!visible_windows_only || [win isVisible]) {
31 all_miniaturized = false;
32 frontmost_window_all_spaces = win;
33 if ([win isOnActiveSpace]) {
35 frontmost_window = win;
39 if (all_miniaturized && frontmost_miniaturized_window) {
40 [frontmost_miniaturized_window deminiaturize:nil];
41 frontmost_window = frontmost_miniaturized_window;
43 // If we couldn't find one on this window, consider all spaces.
44 if (allow_workspace_switch &&
45 !frontmost_window && frontmost_window_all_spaces) {
46 frontmost_window = frontmost_window_all_spaces;
47 [frontmost_window orderFront:nil];
49 if (frontmost_window) {
50 [NSApp activateIgnoringOtherApps:YES];
51 [frontmost_window makeMainWindow];
52 [frontmost_window makeKeyWindow];
58 void FocusWindowSet(const std::set<NSWindow*>& windows) {
59 FocusWindowSetHelper(windows, true, true);
62 void FocusWindowSetOnCurrentSpace(const std::set<NSWindow*>& windows) {
63 // This callback runs before AppKit picks its own window to
64 // deminiaturize, so we get to pick one from the right set. Limit to
65 // the windows on the current workspace. Otherwise we jump spaces
68 // Also consider both visible and hidden windows; this call races
69 // with the system unhiding the application. http://crbug.com/368238
71 // NOTE: If this is called in the
72 // applicationShouldHandleReopen:hasVisibleWindows: hook when
73 // clicking the dock icon, and that caused OS X to begin switch
74 // spaces, isOnActiveSpace gives the answer for the PREVIOUS
75 // space. This means that we actually raise and focus the wrong
76 // space's windows, leaving the new key window off-screen. To detect
77 // this, check if the key window is on the active space prior to
80 // Also, if we decide to deminiaturize a window during a space switch,
81 // that can switch spaces and then switch back. Fortunately, this only
82 // happens if, say, space 1 contains an app, space 2 contains a
83 // miniaturized browser. We click the icon, OS X switches to space 1,
84 // we deminiaturize the browser, and that triggers switching back.
86 // TODO(davidben): To limit those cases, consider preferentially
87 // deminiaturizing a window on the current space.
88 FocusWindowSetHelper(windows, false, false);