window_cycle_controller.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
1// Copyright (c) 2012 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/window_cycle_controller.h" 6 7#include <algorithm> 8 9#include "ash/session_state_delegate.h" 10#include "ash/shell.h" 11#include "ash/shell_window_ids.h" 12#include "ash/wm/activation_controller.h" 13#include "ash/wm/window_cycle_list.h" 14#include "ash/wm/window_util.h" 15#include "ash/wm/workspace_controller.h" 16#include "ui/aura/root_window.h" 17#include "ui/base/events/event.h" 18#include "ui/base/events/event_handler.h" 19 20namespace ash { 21 22namespace { 23 24// List of containers whose children we will cycle through. 25const int kContainerIds[] = { 26 internal::kShellWindowId_DefaultContainer, 27 internal::kShellWindowId_AlwaysOnTopContainer 28}; 29 30// Filter to watch for the termination of a keyboard gesture to cycle through 31// multiple windows. 32class WindowCycleEventFilter : public ui::EventHandler { 33 public: 34 WindowCycleEventFilter(); 35 virtual ~WindowCycleEventFilter(); 36 37 // Overridden from ui::EventHandler: 38 virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE; 39 private: 40 DISALLOW_COPY_AND_ASSIGN(WindowCycleEventFilter); 41}; 42 43// Watch for all keyboard events by filtering the root window. 44WindowCycleEventFilter::WindowCycleEventFilter() { 45} 46 47WindowCycleEventFilter::~WindowCycleEventFilter() { 48} 49 50void WindowCycleEventFilter::OnKeyEvent(ui::KeyEvent* event) { 51 // Views uses VKEY_MENU for both left and right Alt keys. 52 if (event->key_code() == ui::VKEY_MENU && 53 event->type() == ui::ET_KEY_RELEASED) { 54 Shell::GetInstance()->window_cycle_controller()->AltKeyReleased(); 55 // Warning: |this| will be deleted from here on. 56 } 57} 58 59// Adds all the children of |window| to |windows|. 60void AddAllChildren(aura::Window* window, 61 WindowCycleList::WindowList* windows) { 62 const WindowCycleList::WindowList& children(window->children()); 63 windows->insert(windows->end(), children.begin(), children.end()); 64} 65 66// Adds all the children of all of |window|s children to |windows|. 67void AddWorkspaceChildren(aura::Window* window, 68 WindowCycleList::WindowList* windows) { 69 for (size_t i = 0; i < window->children().size(); ++i) 70 AddAllChildren(window->children()[i], windows); 71} 72 73// Adds the windows that can be cycled through for the specified window id to 74// |windows|. 75void AddCycleWindows(aura::RootWindow* root, 76 int container_id, 77 WindowCycleList::WindowList* windows) { 78 aura::Window* container = Shell::GetContainer(root, container_id); 79 if (container_id == internal::kShellWindowId_DefaultContainer) 80 AddWorkspaceChildren(container, windows); 81 else 82 AddAllChildren(container, windows); 83} 84 85} // namespace 86 87////////////////////////////////////////////////////////////////////////////// 88// WindowCycleController, public: 89 90WindowCycleController::WindowCycleController( 91 aura::client::ActivationClient* activation_client) 92 : activation_client_(activation_client) { 93 activation_client_->AddObserver(this); 94} 95 96WindowCycleController::~WindowCycleController() { 97 Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); 98 for (Shell::RootWindowList::const_iterator iter = root_windows.begin(); 99 iter != root_windows.end(); ++iter) { 100 for (size_t i = 0; i < arraysize(kContainerIds); ++i) { 101 aura::Window* container = Shell::GetContainer(*iter, kContainerIds[i]); 102 if (container) 103 container->RemoveObserver(this); 104 } 105 aura::Window* default_container = 106 Shell::GetContainer(*iter, internal::kShellWindowId_DefaultContainer); 107 if (default_container) { 108 for (size_t i = 0; i < default_container->children().size(); ++i) { 109 aura::Window* workspace_window = default_container->children()[i]; 110 DCHECK_EQ(internal::kShellWindowId_WorkspaceContainer, 111 workspace_window->id()); 112 workspace_window->RemoveObserver(this); 113 } 114 } 115 } 116 117 activation_client_->RemoveObserver(this); 118 StopCycling(); 119} 120 121// static 122bool WindowCycleController::CanCycle() { 123 // Don't allow window cycling if the screen is locked or a modal dialog is 124 // open. 125 return !Shell::GetInstance()->session_state_delegate()->IsScreenLocked() && 126 !Shell::GetInstance()->IsSystemModalWindowOpen(); 127} 128 129void WindowCycleController::HandleCycleWindow(Direction direction, 130 bool is_alt_down) { 131 if (!CanCycle()) 132 return; 133 134 if (is_alt_down) { 135 if (!IsCycling()) { 136 // This is the start of an alt-tab cycle through multiple windows, so 137 // listen for the alt key being released to stop cycling. 138 StartCycling(); 139 Step(direction); 140 InstallEventFilter(); 141 } else { 142 // We're in the middle of an alt-tab cycle, just step forward. 143 Step(direction); 144 } 145 } else { 146 // This is a simple, single-step window cycle. 147 StartCycling(); 148 Step(direction); 149 StopCycling(); 150 } 151} 152 153void WindowCycleController::HandleLinearCycleWindow() { 154 if (!CanCycle() || IsCycling()) 155 return; 156 157 // Use the reversed list of windows to prevent a 2-cycle of the most recent 158 // windows occurring. 159 WindowCycleList cycle_list(BuildWindowList(NULL,true)); 160 cycle_list.Step(WindowCycleList::FORWARD); 161} 162 163void WindowCycleController::AltKeyReleased() { 164 StopCycling(); 165} 166 167// static 168std::vector<aura::Window*> WindowCycleController::BuildWindowList( 169 const std::list<aura::Window*>* mru_windows, 170 bool top_most_at_end) { 171 WindowCycleList::WindowList windows; 172 Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); 173 174 aura::RootWindow* active_root = Shell::GetActiveRootWindow(); 175 for (Shell::RootWindowList::const_iterator iter = root_windows.begin(); 176 iter != root_windows.end(); ++iter) { 177 if (*iter == active_root) 178 continue; 179 for (size_t i = 0; i < arraysize(kContainerIds); ++i) 180 AddCycleWindows(*iter, kContainerIds[i], &windows); 181 } 182 183 // Add windows in the active root windows last so that the topmost window 184 // in the active root window becomes the front of the list. 185 for (size_t i = 0; i < arraysize(kContainerIds); ++i) 186 AddCycleWindows(active_root, kContainerIds[i], &windows); 187 188 // Removes unfocusable windows. 189 WindowCycleList::WindowList::iterator last = 190 std::remove_if( 191 windows.begin(), 192 windows.end(), 193 std::not1(std::ptr_fun(ash::wm::CanActivateWindow))); 194 windows.erase(last, windows.end()); 195 196 // Put the windows in the mru_windows list at the head, if it's available. 197 if (mru_windows) { 198 // Iterate through the list backwards, so that we can move each window to 199 // the front of the windows list as we find them. 200 for (std::list<aura::Window*>::const_reverse_iterator ix = 201 mru_windows->rbegin(); 202 ix != mru_windows->rend(); ++ix) { 203 WindowCycleList::WindowList::iterator window = 204 std::find(windows.begin(), windows.end(), *ix); 205 if (window != windows.end()) { 206 windows.erase(window); 207 windows.push_back(*ix); 208 } 209 } 210 } 211 212 // Window cycling expects the topmost window at the front of the list. 213 if (!top_most_at_end) 214 std::reverse(windows.begin(), windows.end()); 215 216 return windows; 217} 218 219void WindowCycleController::OnRootWindowAdded(aura::RootWindow* root_window) { 220 for (size_t i = 0; i < arraysize(kContainerIds); ++i) { 221 aura::Window* container = 222 Shell::GetContainer(root_window, kContainerIds[i]); 223 container->AddObserver(this); 224 } 225 226 aura::Window* default_container = 227 Shell::GetContainer(root_window, 228 internal::kShellWindowId_DefaultContainer); 229 for (size_t i = 0; i < default_container->children().size(); ++i) { 230 aura::Window* workspace_window = default_container->children()[i]; 231 DCHECK_EQ(internal::kShellWindowId_WorkspaceContainer, 232 workspace_window->id()); 233 workspace_window->AddObserver(this); 234 } 235} 236 237////////////////////////////////////////////////////////////////////////////// 238// WindowCycleController, private: 239 240void WindowCycleController::StartCycling() { 241 windows_.reset(new WindowCycleList(BuildWindowList(&mru_windows_, false))); 242} 243 244void WindowCycleController::Step(Direction direction) { 245 DCHECK(windows_.get()); 246 windows_->Step(direction == FORWARD ? WindowCycleList::FORWARD : 247 WindowCycleList::BACKWARD); 248} 249 250void WindowCycleController::StopCycling() { 251 windows_.reset(); 252 // Remove our key event filter. 253 if (event_handler_) { 254 Shell::GetInstance()->RemovePreTargetHandler(event_handler_.get()); 255 event_handler_.reset(); 256 } 257 258 // Add the currently focused window to the MRU list 259 aura::Window* active_window = wm::GetActiveWindow(); 260 mru_windows_.remove(active_window); 261 mru_windows_.push_front(active_window); 262} 263 264// static 265bool WindowCycleController::IsTrackedContainer(aura::Window* window) { 266 if (!window) 267 return false; 268 for (size_t i = 0; i < arraysize(kContainerIds); ++i) { 269 if (window->id() == kContainerIds[i]) { 270 return true; 271 } 272 } 273 return window->id() == internal::kShellWindowId_WorkspaceContainer; 274} 275 276void WindowCycleController::InstallEventFilter() { 277 event_handler_.reset(new WindowCycleEventFilter()); 278 Shell::GetInstance()->AddPreTargetHandler(event_handler_.get()); 279} 280 281void WindowCycleController::OnWindowActivated(aura::Window* gained_active, 282 aura::Window* lost_active) { 283 if (gained_active && !IsCycling() && 284 IsTrackedContainer(gained_active->parent())) { 285 mru_windows_.remove(gained_active); 286 mru_windows_.push_front(gained_active); 287 } 288} 289 290void WindowCycleController::OnWindowAdded(aura::Window* window) { 291 if (window->id() == internal::kShellWindowId_WorkspaceContainer) 292 window->AddObserver(this); 293} 294 295void WindowCycleController::OnWillRemoveWindow(aura::Window* window) { 296 mru_windows_.remove(window); 297 if (window->id() == internal::kShellWindowId_WorkspaceContainer) 298 window->RemoveObserver(this); 299} 300 301void WindowCycleController::OnWindowDestroying(aura::Window* window) { 302 window->RemoveObserver(this); 303} 304 305} // namespace ash 306