12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#import <Cocoa/Cocoa.h>
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/base/cocoa/focus_window_set.h"
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace ui {
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
11cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)namespace {
12cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
1358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// This attempts to match OS X's native behavior, namely that a window
1458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// is only ever deminiaturized if ALL windows on ALL workspaces are
15cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// miniaturized.
16cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void FocusWindowSetHelper(const std::set<NSWindow*>& windows,
17cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                          bool allow_workspace_switch,
18cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                          bool visible_windows_only) {
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  NSArray* ordered_windows = [NSApp orderedWindows];
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  NSWindow* frontmost_window = nil;
2158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  NSWindow* frontmost_window_all_spaces = nil;
22558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch  NSWindow* frontmost_miniaturized_window = nil;
2358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  bool all_miniaturized = true;
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (int i = [ordered_windows count] - 1; i >= 0; i--) {
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    NSWindow* win = [ordered_windows objectAtIndex:i];
26cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    if (windows.find(win) == windows.end())
27cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      continue;
28cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    if ([win isMiniaturized]) {
29cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      frontmost_miniaturized_window = win;
30cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    } else if (!visible_windows_only || [win isVisible]) {
31cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      all_miniaturized = false;
32cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      frontmost_window_all_spaces = win;
33cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      if ([win isOnActiveSpace]) {
34cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        [win orderFront:nil];
35cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        frontmost_window = win;
36558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch      }
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (all_miniaturized && frontmost_miniaturized_window) {
40558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    [frontmost_miniaturized_window deminiaturize:nil];
41558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    frontmost_window = frontmost_miniaturized_window;
42558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch  }
4358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // If we couldn't find one on this window, consider all spaces.
4458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (allow_workspace_switch &&
4558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      !frontmost_window && frontmost_window_all_spaces) {
4658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    frontmost_window = frontmost_window_all_spaces;
4758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    [frontmost_window orderFront:nil];
4858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
49558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch  if (frontmost_window) {
50558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    [NSApp activateIgnoringOtherApps:YES];
51558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    [frontmost_window makeMainWindow];
52558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    [frontmost_window makeKeyWindow];
53558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch  }
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
56cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}  // namespace
57cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
58cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void FocusWindowSet(const std::set<NSWindow*>& windows) {
59cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  FocusWindowSetHelper(windows, true, true);
60cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
61cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
62cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void FocusWindowSetOnCurrentSpace(const std::set<NSWindow*>& windows) {
63cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // This callback runs before AppKit picks its own window to
64cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // deminiaturize, so we get to pick one from the right set. Limit to
65cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // the windows on the current workspace. Otherwise we jump spaces
66cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // haphazardly.
67cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  //
68cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // Also consider both visible and hidden windows; this call races
69cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // with the system unhiding the application. http://crbug.com/368238
70cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  //
71cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // NOTE: If this is called in the
72cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // applicationShouldHandleReopen:hasVisibleWindows: hook when
73cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // clicking the dock icon, and that caused OS X to begin switch
74cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // spaces, isOnActiveSpace gives the answer for the PREVIOUS
75cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // space. This means that we actually raise and focus the wrong
76cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // space's windows, leaving the new key window off-screen. To detect
77cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // this, check if the key window is on the active space prior to
78cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // calling.
79cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  //
80cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // Also, if we decide to deminiaturize a window during a space switch,
81cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // that can switch spaces and then switch back. Fortunately, this only
82cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // happens if, say, space 1 contains an app, space 2 contains a
83cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // miniaturized browser. We click the icon, OS X switches to space 1,
84cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // we deminiaturize the browser, and that triggers switching back.
85cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  //
86cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // TODO(davidben): To limit those cases, consider preferentially
87cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // deminiaturizing a window on the current space.
88cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  FocusWindowSetHelper(windows, false, false);
89cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
90cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace ui
92