Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / ui / base / cocoa / focus_window_set.mm
bloba12b5503ee02e926e03857129904c31ef1cb40bd
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"
9 namespace ui {
11 namespace {
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
15 // miniaturized.
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())
27       continue;
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]) {
34         // Raise the old |frontmost_window| (if any). The topmost |win| will be
35         // raised with makeKeyAndOrderFront: below.
36         [frontmost_window orderFront:nil];
37         frontmost_window = win;
38       }
39     }
40   }
41   if (all_miniaturized && frontmost_miniaturized_window) {
42     DCHECK(!frontmost_window);
43     // Note the call to makeKeyAndOrderFront: will deminiaturize the window.
44     frontmost_window = frontmost_miniaturized_window;
45   }
46   // If we couldn't find one on the active space, consider all spaces.
47   if (allow_workspace_switch && !frontmost_window)
48     frontmost_window = frontmost_window_all_spaces;
50   if (frontmost_window) {
51     [frontmost_window makeKeyAndOrderFront:nil];
52     [NSApp activateIgnoringOtherApps:YES];
53   }
56 }  // namespace
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
66   // haphazardly.
67   //
68   // Also consider both visible and hidden windows; this call races
69   // with the system unhiding the application. http://crbug.com/368238
70   //
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
78   // calling.
79   //
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.
85   //
86   // TODO(davidben): To limit those cases, consider preferentially
87   // deminiaturizing a window on the current space.
88   FocusWindowSetHelper(windows, false, false);
91 }  // namespace ui