Home | History | Annotate | Download | only in cocoa
      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 
      9 namespace ui {
     10 
     11 namespace {
     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.
     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         [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 
     58 void FocusWindowSet(const std::set<NSWindow*>& windows) {
     59   FocusWindowSetHelper(windows, true, true);
     60 }
     61 
     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);
     89 }
     90 
     91 }  // namespace ui
     92