Mailbox support for texture layers.
[chromium-blink-merge.git] / ash / wm / activation_controller.cc
blobccdc238cb1cffd3d3254b27c6cfb7e645d68c56c
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.
5 #include "ash/wm/activation_controller.h"
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 "ash/wm/workspace_controller.h"
14 #include "base/auto_reset.h"
15 #include "ui/aura/client/activation_change_observer.h"
16 #include "ui/aura/client/activation_delegate.h"
17 #include "ui/aura/client/aura_constants.h"
18 #include "ui/aura/client/focus_client.h"
19 #include "ui/aura/env.h"
20 #include "ui/aura/root_window.h"
21 #include "ui/aura/window.h"
22 #include "ui/aura/window_delegate.h"
23 #include "ui/base/ui_base_types.h"
24 #include "ui/compositor/layer.h"
25 #include "ui/views/corewm/window_modality_controller.h"
27 namespace ash {
28 namespace internal {
29 namespace {
31 // These are the list of container ids of containers which may contain windows
32 // that need to be activated in the order that they should be activated.
33 const int kWindowContainerIds[] = {
34 kShellWindowId_LockSystemModalContainer,
35 kShellWindowId_SettingBubbleContainer,
36 kShellWindowId_LockScreenContainer,
37 kShellWindowId_SystemModalContainer,
38 kShellWindowId_AlwaysOnTopContainer,
39 kShellWindowId_AppListContainer,
40 kShellWindowId_DefaultContainer,
42 // Panel, launcher and status are intentionally checked after other
43 // containers even though these layers are higher. The user expects their
44 // windows to be focused before these elements.
45 kShellWindowId_PanelContainer,
46 kShellWindowId_LauncherContainer,
47 kShellWindowId_StatusContainer,
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;
56 return false;
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 if (window->id() == kShellWindowId_WorkspaceContainer)
63 return true;
65 for (size_t i = 0; i < arraysize(kWindowContainerIds); i++) {
66 if (window->id() == kWindowContainerIds[i] &&
67 window->id() != kShellWindowId_DefaultContainer) {
68 return true;
71 return false;
74 bool HasModalTransientChild(aura::Window* window) {
75 aura::Window::Windows::const_iterator it;
76 for (it = window->transient_children().begin();
77 it != window->transient_children().end();
78 ++it) {
79 if ((*it)->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_WINDOW)
80 return true;
82 return false;
85 // See description in VisibilityMatches.
86 enum ActivateVisibilityType {
87 TARGET_VISIBILITY,
88 CURRENT_VISIBILITY,
91 // Used by CanActivateWindowWithEvent() to test the visibility of a window.
92 // This is used by two distinct code paths:
93 // . when activating from an event we only care about the actual visibility.
94 // . when activating because of a keyboard accelerator, in which case we
95 // care about the TargetVisibility.
96 bool VisibilityMatches(aura::Window* window, ActivateVisibilityType type) {
97 bool visible = (type == CURRENT_VISIBILITY) ? window->IsVisible() :
98 window->TargetVisibility();
99 return visible || wm::IsWindowMinimized(window) ||
100 (window->TargetVisibility() &&
101 (window->parent()->id() == kShellWindowId_WorkspaceContainer ||
102 window->parent()->id() == kShellWindowId_LockScreenContainer));
105 // Returns true if |window| can be activated or deactivated.
106 // A window manager typically defines some notion of "top level window" that
107 // supports activation/deactivation.
108 bool CanActivateWindowWithEvent(aura::Window* window,
109 const ui::Event* event,
110 ActivateVisibilityType visibility_type) {
111 return window &&
112 VisibilityMatches(window, visibility_type) &&
113 (!aura::client::GetActivationDelegate(window) ||
114 aura::client::GetActivationDelegate(window)->ShouldActivate()) &&
115 SupportsChildActivation(window->parent()) &&
116 (BelongsToContainerWithEqualOrGreaterId(
117 window, kShellWindowId_SystemModalContainer) ||
118 !Shell::GetInstance()->IsSystemModalWindowOpen());
121 // When a modal window is activated, we bring its entire transient parent chain
122 // to the front. This function must be called before the modal transient is
123 // stacked at the top to ensure correct stacking order.
124 void StackTransientParentsBelowModalWindow(aura::Window* window) {
125 if (window->GetProperty(aura::client::kModalKey) != ui::MODAL_TYPE_WINDOW)
126 return;
128 aura::Window* transient_parent = window->transient_parent();
129 while (transient_parent) {
130 transient_parent->parent()->StackChildAtTop(transient_parent);
131 transient_parent = transient_parent->transient_parent();
135 aura::Window* FindFocusableWindowFor(aura::Window* window) {
136 while (window && !window->CanFocus())
137 window = window->parent();
138 return window;
141 } // namespace
143 ////////////////////////////////////////////////////////////////////////////////
144 // ActivationController, public:
146 ActivationController::ActivationController(
147 aura::client::FocusClient* focus_client,
148 ActivationControllerDelegate* delegate)
149 : focus_client_(focus_client),
150 updating_activation_(false),
151 active_window_(NULL),
152 ALLOW_THIS_IN_INITIALIZER_LIST(observer_manager_(this)),
153 delegate_(delegate) {
154 aura::Env::GetInstance()->AddObserver(this);
155 focus_client_->AddObserver(this);
158 ActivationController::~ActivationController() {
159 aura::Env::GetInstance()->RemoveObserver(this);
160 focus_client_->RemoveObserver(this);
163 // static
164 aura::Window* ActivationController::GetActivatableWindow(
165 aura::Window* window,
166 const ui::Event* event) {
167 aura::Window* parent = window->parent();
168 aura::Window* child = window;
169 while (parent) {
170 if (CanActivateWindowWithEvent(child, event, CURRENT_VISIBILITY))
171 return child;
172 // If |child| isn't activatable, but has transient parent, trace
173 // that path instead.
174 if (child->transient_parent())
175 return GetActivatableWindow(child->transient_parent(), event);
176 parent = parent->parent();
177 child = child->parent();
179 return NULL;
182 bool ActivationController::CanActivateWindow(aura::Window* window) const {
183 return CanActivateWindowWithEvent(window, NULL, TARGET_VISIBILITY) &&
184 !HasModalTransientChild(window);
187 ////////////////////////////////////////////////////////////////////////////////
188 // ActivationController, aura::client::ActivationClient implementation:
190 void ActivationController::AddObserver(
191 aura::client::ActivationChangeObserver* observer) {
192 observers_.AddObserver(observer);
195 void ActivationController::RemoveObserver(
196 aura::client::ActivationChangeObserver* observer) {
197 observers_.RemoveObserver(observer);
200 void ActivationController::ActivateWindow(aura::Window* window) {
201 ActivateWindowWithEvent(window, NULL);
204 void ActivationController::DeactivateWindow(aura::Window* window) {
205 if (window)
206 ActivateNextWindow(window);
209 aura::Window* ActivationController::GetActiveWindow() {
210 return active_window_;
213 aura::Window* ActivationController::GetActivatableWindow(aura::Window* window) {
214 return GetActivatableWindow(window, NULL);
217 aura::Window* ActivationController::GetToplevelWindow(aura::Window* window) {
218 return GetActivatableWindow(window, NULL);
221 bool ActivationController::OnWillFocusWindow(aura::Window* window,
222 const ui::Event* event) {
223 return CanActivateWindowWithEvent(
224 GetActivatableWindow(window, event), event, CURRENT_VISIBILITY);
227 ////////////////////////////////////////////////////////////////////////////////
228 // ActivationController, aura::WindowObserver implementation:
230 void ActivationController::OnWindowVisibilityChanged(aura::Window* window,
231 bool visible) {
232 if (!visible) {
233 aura::Window* next_window = ActivateNextWindow(window);
234 if (next_window && next_window->parent() == window->parent()) {
235 // Despite the activation change, we need to keep the window being hidden
236 // stacked above the new window so it stays on top as it animates away.
237 window->layer()->parent()->StackAbove(window->layer(),
238 next_window->layer());
243 void ActivationController::OnWindowDestroying(aura::Window* window) {
244 // Don't use wm::IsActiveWidnow in case the |window| has already been
245 // removed from the root tree.
246 if (active_window_ == window) {
247 active_window_ = NULL;
248 FOR_EACH_OBSERVER(aura::client::ActivationChangeObserver,
249 observers_,
250 OnWindowActivated(NULL, window));
251 ActivateWindow(GetTopmostWindowToActivate(window));
253 observer_manager_.Remove(window);
256 ////////////////////////////////////////////////////////////////////////////////
257 // ActivationController, aura::EnvObserver implementation:
259 void ActivationController::OnWindowInitialized(aura::Window* window) {
260 observer_manager_.Add(window);
263 ////////////////////////////////////////////////////////////////////////////////
264 // ActivationController, aura::RootWindowObserver implementation:
266 void ActivationController::OnWindowFocused(aura::Window* gained_focus,
267 aura::Window* lost_focus) {
268 if (gained_focus)
269 ActivateWindow(GetActivatableWindow(gained_focus, NULL));
272 ////////////////////////////////////////////////////////////////////////////////
273 // ActivationController, ui::EventHandler implementation:
275 void ActivationController::OnKeyEvent(ui::KeyEvent* event) {
278 void ActivationController::OnMouseEvent(ui::MouseEvent* event) {
279 if (event->type() == ui::ET_MOUSE_PRESSED)
280 FocusWindowWithEvent(event);
283 void ActivationController::OnScrollEvent(ui::ScrollEvent* event) {
286 void ActivationController::OnTouchEvent(ui::TouchEvent* event) {
287 if (event->type() == ui::ET_TOUCH_PRESSED)
288 FocusWindowWithEvent(event);
291 void ActivationController::OnGestureEvent(ui::GestureEvent* event) {
292 if (event->type() == ui::ET_GESTURE_BEGIN &&
293 event->details().touch_points() == 1) {
294 FocusWindowWithEvent(event);
298 ////////////////////////////////////////////////////////////////////////////////
299 // ActivationController, private:
301 void ActivationController::ActivateWindowWithEvent(aura::Window* window,
302 const ui::Event* event) {
303 // Prevent recursion when called from focus.
304 if (updating_activation_)
305 return;
306 base::AutoReset<bool> in_activate_window(&updating_activation_, true);
308 // We allow the delegate to change which window gets activated, or to prevent
309 // activation changes.
310 aura::Window* original_active_window = window;
311 window = delegate_->WillActivateWindow(window);
312 // TODO(beng): note that this breaks the previous behavior where an activation
313 // attempt by a window behind the lock screen would at least
314 // restack that window frontmost within its container. fix this.
315 if (!window && original_active_window != window)
316 return;
318 // TODO(beng): This encapsulates additional Ash-specific restrictions on
319 // whether activation can change. Should move to the delegate.
320 if (window && !CanActivateWindowWithEvent(window, event, CURRENT_VISIBILITY))
321 return;
323 if (active_window_ == window)
324 return;
326 aura::Window* old_active = active_window_;
327 active_window_ = window;
329 if (window &&
330 !window->Contains(aura::client::GetFocusClient(window)->
331 GetFocusedWindow())) {
332 aura::client::GetFocusClient(window)->FocusWindow(window, event);
335 if (window) {
336 StackTransientParentsBelowModalWindow(window);
337 window->parent()->StackChildAtTop(window);
340 FOR_EACH_OBSERVER(aura::client::ActivationChangeObserver,
341 observers_,
342 OnWindowActivated(window, old_active));
343 if (aura::client::GetActivationChangeObserver(old_active)) {
344 aura::client::GetActivationChangeObserver(old_active)->OnWindowActivated(
345 window, old_active);
347 if (aura::client::GetActivationChangeObserver(window)) {
348 aura::client::GetActivationChangeObserver(window)->OnWindowActivated(
349 window, old_active);
353 aura::Window* ActivationController::ActivateNextWindow(aura::Window* window) {
354 aura::Window* next_window = NULL;
355 if (wm::IsActiveWindow(window)) {
356 next_window = GetTopmostWindowToActivate(window);
357 ActivateWindow(next_window);
359 return next_window;
362 aura::Window* ActivationController::GetTopmostWindowToActivate(
363 aura::Window* ignore) const {
364 size_t current_container_index = 0;
365 // If the container of the window losing focus is in the list, start from that
366 // container.
367 aura::RootWindow* root = ignore->GetRootWindow();
368 if (!root)
369 root = Shell::GetActiveRootWindow();
370 for (size_t i = 0; ignore && i < arraysize(kWindowContainerIds); i++) {
371 aura::Window* container = Shell::GetContainer(root, kWindowContainerIds[i]);
372 if (container && container->Contains(ignore)) {
373 current_container_index = i;
374 break;
378 // Look for windows to focus in that container and below.
379 aura::Window* window = NULL;
380 for (; !window && current_container_index < arraysize(kWindowContainerIds);
381 current_container_index++) {
382 aura::Window::Windows containers = Shell::GetContainersFromAllRootWindows(
383 kWindowContainerIds[current_container_index],
384 root);
385 for (aura::Window::Windows::const_iterator iter = containers.begin();
386 iter != containers.end() && !window; ++iter) {
387 window = GetTopmostWindowToActivateInContainer((*iter), ignore);
390 return window;
393 aura::Window* ActivationController::GetTopmostWindowToActivateInContainer(
394 aura::Window* container,
395 aura::Window* ignore) const {
396 // Workspace has an extra level of windows that needs to be special cased.
397 if (container->id() == kShellWindowId_DefaultContainer) {
398 for (aura::Window::Windows::const_reverse_iterator i =
399 container->children().rbegin();
400 i != container->children().rend(); ++i) {
401 if ((*i)->IsVisible()) {
402 aura::Window* window = GetTopmostWindowToActivateInContainer(
403 *i, ignore);
404 if (window)
405 return window;
408 return NULL;
410 for (aura::Window::Windows::const_reverse_iterator i =
411 container->children().rbegin();
412 i != container->children().rend();
413 ++i) {
414 if (*i != ignore &&
415 CanActivateWindowWithEvent(*i, NULL, CURRENT_VISIBILITY) &&
416 !wm::IsWindowMinimized(*i))
417 return *i;
419 return NULL;
422 void ActivationController::FocusWindowWithEvent(const ui::Event* event) {
423 aura::Window* window = static_cast<aura::Window*>(event->target());
424 window = delegate_->WillFocusWindow(window);
425 if (GetActiveWindow() != window) {
426 aura::client::GetFocusClient(window)->FocusWindow(
427 FindFocusableWindowFor(window), event);
431 } // namespace internal
432 } // namespace ash