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#include "ash/wm/mru_window_tracker.h" 6 7#include <algorithm> 8 9#include "ash/session/session_state_delegate.h" 10#include "ash/shell.h" 11#include "ash/shell_window_ids.h" 12#include "ash/switchable_windows.h" 13#include "ash/wm/window_state.h" 14#include "ash/wm/window_util.h" 15#include "ash/wm/workspace_controller.h" 16#include "ui/aura/window_event_dispatcher.h" 17#include "ui/events/event.h" 18#include "ui/events/event_handler.h" 19#include "ui/wm/public/activation_client.h" 20 21namespace ash { 22 23namespace { 24 25// Adds the windows that can be cycled through for the specified window id to 26// |windows|. 27void AddTrackedWindows(aura::Window* root, 28 int container_id, 29 MruWindowTracker::WindowList* windows) { 30 aura::Window* container = Shell::GetContainer(root, container_id); 31 const MruWindowTracker::WindowList& children(container->children()); 32 windows->insert(windows->end(), children.begin(), children.end()); 33} 34 35// Adds windows being dragged in the docked container to |windows| list. 36void AddDraggedWindows(aura::Window* root, 37 MruWindowTracker::WindowList* windows) { 38 aura::Window* container = 39 Shell::GetContainer(root, kShellWindowId_DockedContainer); 40 const MruWindowTracker::WindowList& children = container->children(); 41 for (MruWindowTracker::WindowList::const_iterator iter = children.begin(); 42 iter != children.end(); ++iter) { 43 if (wm::GetWindowState(*iter)->is_dragged()) 44 windows->insert(windows->end(), *iter); 45 } 46} 47 48// Returns whether |w1| should be considered less recently used than |w2|. This 49// is used for a stable sort to move minimized windows to the LRU end of the 50// list. 51bool CompareWindowState(aura::Window* w1, aura::Window* w2) { 52 return ash::wm::IsWindowMinimized(w1) && !ash::wm::IsWindowMinimized(w2); 53} 54 55// Returns a list of windows ordered by their stacking order. 56// If |mru_windows| is passed, these windows are moved to the front of the list. 57// If |top_most_at_end|, the list is returned in descending (bottom-most / least 58// recently used) order. 59MruWindowTracker::WindowList BuildWindowListInternal( 60 const std::list<aura::Window*>* mru_windows, 61 bool top_most_at_end) { 62 MruWindowTracker::WindowList windows; 63 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 64 65 aura::Window* active_root = Shell::GetTargetRootWindow(); 66 for (aura::Window::Windows::const_iterator iter = root_windows.begin(); 67 iter != root_windows.end(); ++iter) { 68 if (*iter == active_root) 69 continue; 70 for (size_t i = 0; i < kSwitchableWindowContainerIdsLength; ++i) 71 AddTrackedWindows(*iter, kSwitchableWindowContainerIds[i], &windows); 72 } 73 74 // Add windows in the active root windows last so that the topmost window 75 // in the active root window becomes the front of the list. 76 for (size_t i = 0; i < kSwitchableWindowContainerIdsLength; ++i) 77 AddTrackedWindows(active_root, kSwitchableWindowContainerIds[i], &windows); 78 79 // Dragged windows are temporarily parented in docked container. Include them 80 // in the in tracked list. 81 AddDraggedWindows(active_root, &windows); 82 83 // Removes unfocusable windows. 84 MruWindowTracker::WindowList::iterator last = 85 std::remove_if( 86 windows.begin(), 87 windows.end(), 88 std::not1(std::ptr_fun(ash::wm::CanActivateWindow))); 89 windows.erase(last, windows.end()); 90 91 // Put the windows in the mru_windows list at the head, if it's available. 92 if (mru_windows) { 93 // Iterate through the list backwards, so that we can move each window to 94 // the front of the windows list as we find them. 95 for (std::list<aura::Window*>::const_reverse_iterator ix = 96 mru_windows->rbegin(); 97 ix != mru_windows->rend(); ++ix) { 98 // Exclude windows in non-switchable containers and those which cannot 99 // be activated. 100 if (!IsSwitchableContainer((*ix)->parent()) || 101 !ash::wm::CanActivateWindow(*ix)) { 102 continue; 103 } 104 105 MruWindowTracker::WindowList::iterator window = 106 std::find(windows.begin(), windows.end(), *ix); 107 if (window != windows.end()) { 108 windows.erase(window); 109 windows.push_back(*ix); 110 } 111 } 112 } 113 114 // Move minimized windows to the beginning (LRU end) of the list. 115 std::stable_sort(windows.begin(), windows.end(), CompareWindowState); 116 117 // Window cycling expects the topmost window at the front of the list. 118 if (!top_most_at_end) 119 std::reverse(windows.begin(), windows.end()); 120 121 return windows; 122} 123 124} // namespace 125 126////////////////////////////////////////////////////////////////////////////// 127// MruWindowTracker, public: 128 129MruWindowTracker::MruWindowTracker( 130 aura::client::ActivationClient* activation_client) 131 : activation_client_(activation_client), 132 ignore_window_activations_(false) { 133 activation_client_->AddObserver(this); 134} 135 136MruWindowTracker::~MruWindowTracker() { 137 for (std::list<aura::Window*>::iterator iter = mru_windows_.begin(); 138 iter != mru_windows_.end(); ++iter) { 139 (*iter)->RemoveObserver(this); 140 } 141 142 activation_client_->RemoveObserver(this); 143} 144 145// static 146MruWindowTracker::WindowList MruWindowTracker::BuildWindowList( 147 bool top_most_at_end) { 148 return BuildWindowListInternal(NULL, top_most_at_end); 149} 150 151MruWindowTracker::WindowList MruWindowTracker::BuildMruWindowList() { 152 return BuildWindowListInternal(&mru_windows_, false); 153} 154 155void MruWindowTracker::SetIgnoreActivations(bool ignore) { 156 ignore_window_activations_ = ignore; 157 158 // If no longer ignoring window activations, move currently active window 159 // to front. 160 if (!ignore) 161 SetActiveWindow(wm::GetActiveWindow()); 162} 163 164////////////////////////////////////////////////////////////////////////////// 165// MruWindowTracker, private: 166 167void MruWindowTracker::SetActiveWindow(aura::Window* active_window) { 168 if (!active_window) 169 return; 170 171 std::list<aura::Window*>::iterator iter = 172 std::find(mru_windows_.begin(), mru_windows_.end(), active_window); 173 // Observe all newly tracked windows. 174 if (iter == mru_windows_.end()) 175 active_window->AddObserver(this); 176 else 177 mru_windows_.erase(iter); 178 // TODO(flackr): Remove this check if this doesn't fire for a while. This 179 // should verify that all tracked windows start with a layer, see 180 // http://crbug.com/291354. 181 CHECK(active_window->layer()); 182 mru_windows_.push_front(active_window); 183} 184 185void MruWindowTracker::OnWindowActivated(aura::Window* gained_active, 186 aura::Window* lost_active) { 187 if (!ignore_window_activations_) 188 SetActiveWindow(gained_active); 189} 190 191void MruWindowTracker::OnWindowDestroyed(aura::Window* window) { 192 // It's possible for OnWindowActivated() to be called after 193 // OnWindowDestroying(). This means we need to override OnWindowDestroyed() 194 // else we may end up with a deleted window in |mru_windows_|. 195 mru_windows_.remove(window); 196 window->RemoveObserver(this); 197} 198 199} // namespace ash 200