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. 4 5#import <Cocoa/Cocoa.h> 6 7#include "ui/base/cocoa/focus_window_set.h" 8 9namespace ui { 10 11namespace { 12 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. 16void 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 [win orderFront:nil]; 35 frontmost_window = win; 36 } 37 } 38 } 39 if (all_miniaturized && frontmost_miniaturized_window) { 40 [frontmost_miniaturized_window deminiaturize:nil]; 41 frontmost_window = frontmost_miniaturized_window; 42 } 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]; 48 } 49 if (frontmost_window) { 50 [NSApp activateIgnoringOtherApps:YES]; 51 [frontmost_window makeMainWindow]; 52 [frontmost_window makeKeyWindow]; 53 } 54} 55 56} // namespace 57 58void FocusWindowSet(const std::set<NSWindow*>& windows) { 59 FocusWindowSetHelper(windows, true, true); 60} 61 62void 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); 89} 90 91} // namespace ui 92