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"
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"
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
,
52 for (; window
; window
= window
->parent()) {
53 if (window
->id() >= container_id
)
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
)
65 for (size_t i
= 0; i
< arraysize(kWindowContainerIds
); i
++) {
66 if (window
->id() == kWindowContainerIds
[i
] &&
67 window
->id() != kShellWindowId_DefaultContainer
) {
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();
79 if ((*it
)->GetProperty(aura::client::kModalKey
) == ui::MODAL_TYPE_WINDOW
)
85 // See description in VisibilityMatches.
86 enum ActivateVisibilityType
{
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
) {
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
)
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();
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);
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
;
170 if (CanActivateWindowWithEvent(child
, event
, CURRENT_VISIBILITY
))
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();
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
) {
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
,
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
,
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
) {
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_
)
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
)
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
))
323 if (active_window_
== window
)
326 aura::Window
* old_active
= active_window_
;
327 active_window_
= window
;
330 !window
->Contains(aura::client::GetFocusClient(window
)->
331 GetFocusedWindow())) {
332 aura::client::GetFocusClient(window
)->FocusWindow(window
, event
);
336 StackTransientParentsBelowModalWindow(window
);
337 window
->parent()->StackChildAtTop(window
);
340 FOR_EACH_OBSERVER(aura::client::ActivationChangeObserver
,
342 OnWindowActivated(window
, old_active
));
343 if (aura::client::GetActivationChangeObserver(old_active
)) {
344 aura::client::GetActivationChangeObserver(old_active
)->OnWindowActivated(
347 if (aura::client::GetActivationChangeObserver(window
)) {
348 aura::client::GetActivationChangeObserver(window
)->OnWindowActivated(
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
);
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
367 aura::RootWindow
* root
= ignore
->GetRootWindow();
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
;
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
],
385 for (aura::Window::Windows::const_iterator iter
= containers
.begin();
386 iter
!= containers
.end() && !window
; ++iter
) {
387 window
= GetTopmostWindowToActivateInContainer((*iter
), ignore
);
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(
410 for (aura::Window::Windows::const_reverse_iterator i
=
411 container
->children().rbegin();
412 i
!= container
->children().rend();
415 CanActivateWindowWithEvent(*i
, NULL
, CURRENT_VISIBILITY
) &&
416 !wm::IsWindowMinimized(*i
))
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