| OLD | NEW |
| (Empty) |
| 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 | |
| OLD | NEW |