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/activation_controller.h" 6 7 #include "ash/root_window_controller.h" 8 #include "ash/shell.h" 9 #include "ash/shell_window_ids.h" 10 #include "ash/wm/activation_controller_delegate.h" 11 #include "ash/wm/property_util.h" 12 #include "ash/wm/window_util.h" 13 #include "base/auto_reset.h" 14 #include "ui/aura/client/activation_change_observer.h" 15 #include "ui/aura/client/activation_delegate.h" 16 #include "ui/aura/client/aura_constants.h" 17 #include "ui/aura/client/focus_client.h" 18 #include "ui/aura/env.h" 19 #include "ui/aura/root_window.h" 20 #include "ui/aura/window.h" 21 #include "ui/aura/window_delegate.h" 22 #include "ui/base/ui_base_types.h" 23 #include "ui/compositor/layer.h" 24 #include "ui/views/corewm/window_modality_controller.h" 25 26 namespace ash { 27 namespace internal { 28 namespace { 29 30 // These are the list of container ids of containers which may contain windows 31 // that need to be activated in the order that they should be activated. 32 const int kWindowContainerIds[] = { 33 kShellWindowId_LockSystemModalContainer, 34 kShellWindowId_SettingBubbleContainer, 35 kShellWindowId_LockScreenContainer, 36 kShellWindowId_SystemModalContainer, 37 kShellWindowId_AlwaysOnTopContainer, 38 kShellWindowId_AppListContainer, 39 kShellWindowId_DefaultContainer, 40 41 // Docked, panel, launcher and status are intentionally checked after other 42 // containers even though these layers are higher. The user expects their 43 // windows to be focused before these elements. 44 kShellWindowId_DockedContainer, 45 kShellWindowId_PanelContainer, 46 kShellWindowId_ShelfContainer, 47 kShellWindowId_StatusContainer, 48 }; 49 50 bool BelongsToContainerWithEqualOrGreaterId(const aura::Window* window, 51 int container_id) { 52 for (; window; window = window->parent()) { 53 if (window->id() >= container_id) 54 return true; 55 } 56 return false; 57 } 58 59 // Returns true if children of |window| can be activated. 60 // These are the only containers in which windows can receive focus. 61 bool SupportsChildActivation(aura::Window* window) { 62 for (size_t i = 0; i < arraysize(kWindowContainerIds); i++) { 63 if (window->id() == kWindowContainerIds[i]) 64 return true; 65 } 66 return false; 67 } 68 69 bool HasModalTransientChild(aura::Window* window) { 70 aura::Window::Windows::const_iterator it; 71 for (it = window->transient_children().begin(); 72 it != window->transient_children().end(); 73 ++it) { 74 if ((*it)->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_WINDOW) 75 return true; 76 } 77 return false; 78 } 79 80 // See description in VisibilityMatches. 81 enum ActivateVisibilityType { 82 TARGET_VISIBILITY, 83 CURRENT_VISIBILITY, 84 }; 85 86 // Used by CanActivateWindowWithEvent() to test the visibility of a window. 87 // This is used by two distinct code paths: 88 // . when activating from an event we only care about the actual visibility. 89 // . when activating because of a keyboard accelerator, in which case we 90 // care about the TargetVisibility. 91 bool VisibilityMatches(aura::Window* window, ActivateVisibilityType type) { 92 bool visible = (type == CURRENT_VISIBILITY) ? window->IsVisible() : 93 window->TargetVisibility(); 94 return visible || wm::IsWindowMinimized(window) || 95 (window->TargetVisibility() && 96 (window->parent()->id() == kShellWindowId_DefaultContainer || 97 window->parent()->id() == kShellWindowId_LockScreenContainer)); 98 } 99 100 // Returns true if |window| can be activated or deactivated. 101 // A window manager typically defines some notion of "top level window" that 102 // supports activation/deactivation. 103 bool CanActivateWindowWithEvent(aura::Window* window, 104 const ui::Event* event, 105 ActivateVisibilityType visibility_type) { 106 return window && 107 VisibilityMatches(window, visibility_type) && 108 (!aura::client::GetActivationDelegate(window) || 109 aura::client::GetActivationDelegate(window)->ShouldActivate()) && 110 SupportsChildActivation(window->parent()) && 111 (BelongsToContainerWithEqualOrGreaterId( 112 window, kShellWindowId_SystemModalContainer) || 113 !Shell::GetInstance()->IsSystemModalWindowOpen()); 114 } 115 116 // When a modal window is activated, we bring its entire transient parent chain 117 // to the front. This function must be called before the modal transient is 118 // stacked at the top to ensure correct stacking order. 119 void StackTransientParentsBelowModalWindow(aura::Window* window) { 120 if (window->GetProperty(aura::client::kModalKey) != ui::MODAL_TYPE_WINDOW) 121 return; 122 123 aura::Window* transient_parent = window->transient_parent(); 124 while (transient_parent) { 125 transient_parent->parent()->StackChildAtTop(transient_parent); 126 transient_parent = transient_parent->transient_parent(); 127 } 128 } 129 130 aura::Window* FindFocusableWindowFor(aura::Window* window) { 131 while (window && !window->CanFocus()) 132 window = window->parent(); 133 return window; 134 } 135 136 } // namespace 137 138 //////////////////////////////////////////////////////////////////////////////// 139 // ActivationController, public: 140 141 ActivationController::ActivationController( 142 aura::client::FocusClient* focus_client, 143 ActivationControllerDelegate* delegate) 144 : focus_client_(focus_client), 145 updating_activation_(false), 146 active_window_(NULL), 147 observer_manager_(this), 148 delegate_(delegate) { 149 aura::Env::GetInstance()->AddObserver(this); 150 focus_client_->AddObserver(this); 151 } 152 153 ActivationController::~ActivationController() { 154 aura::Env::GetInstance()->RemoveObserver(this); 155 focus_client_->RemoveObserver(this); 156 } 157 158 // static 159 aura::Window* ActivationController::GetActivatableWindow( 160 aura::Window* window, 161 const ui::Event* event) { 162 aura::Window* parent = window->parent(); 163 aura::Window* child = window; 164 while (parent) { 165 if (CanActivateWindowWithEvent(child, event, CURRENT_VISIBILITY)) 166 return child; 167 // If |child| isn't activatable, but has transient parent, trace 168 // that path instead. 169 if (child->transient_parent()) 170 return GetActivatableWindow(child->transient_parent(), event); 171 parent = parent->parent(); 172 child = child->parent(); 173 } 174 return NULL; 175 } 176 177 bool ActivationController::CanActivateWindow(aura::Window* window) const { 178 return CanActivateWindowWithEvent(window, NULL, TARGET_VISIBILITY) && 179 !HasModalTransientChild(window); 180 } 181 182 //////////////////////////////////////////////////////////////////////////////// 183 // ActivationController, aura::client::ActivationClient implementation: 184 185 void ActivationController::AddObserver( 186 aura::client::ActivationChangeObserver* observer) { 187 observers_.AddObserver(observer); 188 } 189 190 void ActivationController::RemoveObserver( 191 aura::client::ActivationChangeObserver* observer) { 192 observers_.RemoveObserver(observer); 193 } 194 195 void ActivationController::ActivateWindow(aura::Window* window) { 196 ActivateWindowWithEvent(window, NULL); 197 } 198 199 void ActivationController::DeactivateWindow(aura::Window* window) { 200 if (window) 201 ActivateNextWindow(window); 202 } 203 204 aura::Window* ActivationController::GetActiveWindow() { 205 return active_window_; 206 } 207 208 aura::Window* ActivationController::GetActivatableWindow(aura::Window* window) { 209 return GetActivatableWindow(window, NULL); 210 } 211 212 aura::Window* ActivationController::GetToplevelWindow(aura::Window* window) { 213 return GetActivatableWindow(window, NULL); 214 } 215 216 bool ActivationController::OnWillFocusWindow(aura::Window* window, 217 const ui::Event* event) { 218 return CanActivateWindowWithEvent( 219 GetActivatableWindow(window, event), event, CURRENT_VISIBILITY); 220 } 221 222 //////////////////////////////////////////////////////////////////////////////// 223 // ActivationController, aura::WindowObserver implementation: 224 225 void ActivationController::OnWindowVisibilityChanged(aura::Window* window, 226 bool visible) { 227 if (!visible) { 228 aura::Window* next_window = ActivateNextWindow(window); 229 if (next_window && next_window->parent() == window->parent()) { 230 // Despite the activation change, we need to keep the window being hidden 231 // stacked above the new window so it stays on top as it animates away. 232 window->layer()->parent()->StackAbove(window->layer(), 233 next_window->layer()); 234 } 235 } 236 } 237 238 void ActivationController::OnWindowDestroying(aura::Window* window) { 239 // Don't use wm::IsActiveWidnow in case the |window| has already been 240 // removed from the root tree. 241 if (active_window_ == window) { 242 active_window_ = NULL; 243 FOR_EACH_OBSERVER(aura::client::ActivationChangeObserver, 244 observers_, 245 OnWindowActivated(NULL, window)); 246 ActivateWindow(GetTopmostWindowToActivate(window)); 247 } 248 observer_manager_.Remove(window); 249 } 250 251 //////////////////////////////////////////////////////////////////////////////// 252 // ActivationController, aura::EnvObserver implementation: 253 254 void ActivationController::OnWindowInitialized(aura::Window* window) { 255 observer_manager_.Add(window); 256 } 257 258 //////////////////////////////////////////////////////////////////////////////// 259 // ActivationController, aura::RootWindowObserver implementation: 260 261 void ActivationController::OnWindowFocused(aura::Window* gained_focus, 262 aura::Window* lost_focus) { 263 if (gained_focus) 264 ActivateWindow(GetActivatableWindow(gained_focus, NULL)); 265 } 266 267 //////////////////////////////////////////////////////////////////////////////// 268 // ActivationController, ui::EventHandler implementation: 269 270 void ActivationController::OnKeyEvent(ui::KeyEvent* event) { 271 } 272 273 void ActivationController::OnMouseEvent(ui::MouseEvent* event) { 274 if (event->type() == ui::ET_MOUSE_PRESSED) 275 FocusWindowWithEvent(event); 276 } 277 278 void ActivationController::OnScrollEvent(ui::ScrollEvent* event) { 279 } 280 281 void ActivationController::OnTouchEvent(ui::TouchEvent* event) { 282 if (event->type() == ui::ET_TOUCH_PRESSED) 283 FocusWindowWithEvent(event); 284 } 285 286 void ActivationController::OnGestureEvent(ui::GestureEvent* event) { 287 if (event->type() == ui::ET_GESTURE_BEGIN && 288 event->details().touch_points() == 1) { 289 FocusWindowWithEvent(event); 290 } 291 } 292 293 //////////////////////////////////////////////////////////////////////////////// 294 // ActivationController, private: 295 296 void ActivationController::ActivateWindowWithEvent(aura::Window* window, 297 const ui::Event* event) { 298 // Prevent recursion when called from focus. 299 if (updating_activation_) 300 return; 301 base::AutoReset<bool> in_activate_window(&updating_activation_, true); 302 303 // We allow the delegate to change which window gets activated, or to prevent 304 // activation changes. 305 aura::Window* original_active_window = window; 306 window = delegate_->WillActivateWindow(window); 307 // TODO(beng): note that this breaks the previous behavior where an activation 308 // attempt by a window behind the lock screen would at least 309 // restack that window frontmost within its container. fix this. 310 if (!window && original_active_window != window) 311 return; 312 313 // TODO(beng): This encapsulates additional Ash-specific restrictions on 314 // whether activation can change. Should move to the delegate. 315 if (window && !CanActivateWindowWithEvent(window, event, CURRENT_VISIBILITY)) 316 return; 317 318 if (active_window_ == window) 319 return; 320 321 aura::Window* old_active = active_window_; 322 active_window_ = window; 323 324 if (window && 325 !window->Contains(aura::client::GetFocusClient(window)-> 326 GetFocusedWindow())) { 327 aura::client::GetFocusClient(window)->FocusWindow(window); 328 } 329 330 if (window) { 331 StackTransientParentsBelowModalWindow(window); 332 window->parent()->StackChildAtTop(window); 333 } 334 335 FOR_EACH_OBSERVER(aura::client::ActivationChangeObserver, 336 observers_, 337 OnWindowActivated(window, old_active)); 338 if (aura::client::GetActivationChangeObserver(old_active)) { 339 aura::client::GetActivationChangeObserver(old_active)->OnWindowActivated( 340 window, old_active); 341 } 342 if (aura::client::GetActivationChangeObserver(window)) { 343 aura::client::GetActivationChangeObserver(window)->OnWindowActivated( 344 window, old_active); 345 } 346 } 347 348 aura::Window* ActivationController::ActivateNextWindow(aura::Window* window) { 349 aura::Window* next_window = NULL; 350 if (wm::IsActiveWindow(window)) { 351 next_window = GetTopmostWindowToActivate(window); 352 ActivateWindow(next_window); 353 } 354 return next_window; 355 } 356 357 aura::Window* ActivationController::GetTopmostWindowToActivate( 358 aura::Window* ignore) const { 359 size_t current_container_index = 0; 360 // If the container of the window losing focus is in the list, start from that 361 // container. 362 aura::RootWindow* root = ignore->GetRootWindow(); 363 if (!root) 364 root = Shell::GetActiveRootWindow(); 365 for (size_t i = 0; ignore && i < arraysize(kWindowContainerIds); i++) { 366 aura::Window* container = Shell::GetContainer(root, kWindowContainerIds[i]); 367 if (container && container->Contains(ignore)) { 368 current_container_index = i; 369 break; 370 } 371 } 372 373 // Look for windows to focus in that container and below. 374 aura::Window* window = NULL; 375 for (; !window && current_container_index < arraysize(kWindowContainerIds); 376 current_container_index++) { 377 aura::Window::Windows containers = Shell::GetContainersFromAllRootWindows( 378 kWindowContainerIds[current_container_index], 379 root); 380 for (aura::Window::Windows::const_iterator iter = containers.begin(); 381 iter != containers.end() && !window; ++iter) { 382 window = GetTopmostWindowToActivateInContainer((*iter), ignore); 383 } 384 } 385 return window; 386 } 387 388 aura::Window* ActivationController::GetTopmostWindowToActivateInContainer( 389 aura::Window* container, 390 aura::Window* ignore) const { 391 for (aura::Window::Windows::const_reverse_iterator i = 392 container->children().rbegin(); 393 i != container->children().rend(); 394 ++i) { 395 if (*i != ignore && 396 CanActivateWindowWithEvent(*i, NULL, CURRENT_VISIBILITY) && 397 !wm::IsWindowMinimized(*i)) 398 return *i; 399 } 400 return NULL; 401 } 402 403 void ActivationController::FocusWindowWithEvent(const ui::Event* event) { 404 aura::Window* window = static_cast<aura::Window*>(event->target()); 405 window = delegate_->WillFocusWindow(window); 406 if (GetActiveWindow() != window) { 407 aura::client::GetFocusClient(window)->FocusWindow( 408 FindFocusableWindowFor(window)); 409 } 410 } 411 412 } // namespace internal 413 } // namespace ash 414