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