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 21 namespace ash { 22 23 namespace { 24 25 // Adds the windows that can be cycled through for the specified window id to 26 // |windows|. 27 void 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. 36 void 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. 51 bool 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. 59 MruWindowTracker::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 129 MruWindowTracker::MruWindowTracker( 130 aura::client::ActivationClient* activation_client) 131 : activation_client_(activation_client), 132 ignore_window_activations_(false) { 133 activation_client_->AddObserver(this); 134 } 135 136 MruWindowTracker::~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 146 MruWindowTracker::WindowList MruWindowTracker::BuildWindowList( 147 bool top_most_at_end) { 148 return BuildWindowListInternal(NULL, top_most_at_end); 149 } 150 151 MruWindowTracker::WindowList MruWindowTracker::BuildMruWindowList() { 152 return BuildWindowListInternal(&mru_windows_, false); 153 } 154 155 void 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 167 void 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 185 void MruWindowTracker::OnWindowActivated(aura::Window* gained_active, 186 aura::Window* lost_active) { 187 if (!ignore_window_activations_) 188 SetActiveWindow(gained_active); 189 } 190 191 void 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