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 "ui/wm/core/base_focus_rules.h"
6
7 #include "ui/aura/client/focus_client.h"
8 #include "ui/aura/window.h"
9 #include "ui/aura/window_event_dispatcher.h"
10 #include "ui/wm/core/window_modality_controller.h"
11 #include "ui/wm/core/window_util.h"
12 #include "ui/wm/public/activation_delegate.h"
13
14 namespace wm {
15 namespace {
16
GetFocusedWindow(aura::Window * context)17 aura::Window* GetFocusedWindow(aura::Window* context) {
18 aura::client::FocusClient* focus_client =
19 aura::client::GetFocusClient(context);
20 return focus_client ? focus_client->GetFocusedWindow() : NULL;
21 }
22
23 } // namespace
24
25 ////////////////////////////////////////////////////////////////////////////////
26 // BaseFocusRules, protected:
27
BaseFocusRules()28 BaseFocusRules::BaseFocusRules() {
29 }
30
~BaseFocusRules()31 BaseFocusRules::~BaseFocusRules() {
32 }
33
IsWindowConsideredVisibleForActivation(aura::Window * window) const34 bool BaseFocusRules::IsWindowConsideredVisibleForActivation(
35 aura::Window* window) const {
36 return window->IsVisible();
37 }
38
39 ////////////////////////////////////////////////////////////////////////////////
40 // BaseFocusRules, FocusRules implementation:
41
IsToplevelWindow(aura::Window * window) const42 bool BaseFocusRules::IsToplevelWindow(aura::Window* window) const {
43 // The window must in a valid hierarchy.
44 if (!window->GetRootWindow())
45 return false;
46
47 // The window must exist within a container that supports activation.
48 // The window cannot be blocked by a modal transient.
49 return SupportsChildActivation(window->parent());
50 }
51
CanActivateWindow(aura::Window * window) const52 bool BaseFocusRules::CanActivateWindow(aura::Window* window) const {
53 // It is possible to activate a NULL window, it is equivalent to clearing
54 // activation.
55 if (!window)
56 return true;
57
58 // Only toplevel windows can be activated.
59 if (!IsToplevelWindow(window))
60 return false;
61
62 // The window must be visible.
63 if (!IsWindowConsideredVisibleForActivation(window))
64 return false;
65
66 // The window's activation delegate must allow this window to be activated.
67 if (aura::client::GetActivationDelegate(window) &&
68 !aura::client::GetActivationDelegate(window)->ShouldActivate()) {
69 return false;
70 }
71
72 // A window must be focusable to be activatable. We don't call
73 // CanFocusWindow() from here because it will call back to us via
74 // GetActivatableWindow().
75 if (!window->CanFocus())
76 return false;
77
78 // The window cannot be blocked by a modal transient.
79 return !GetModalTransient(window);
80 }
81
CanFocusWindow(aura::Window * window) const82 bool BaseFocusRules::CanFocusWindow(aura::Window* window) const {
83 // It is possible to focus a NULL window, it is equivalent to clearing focus.
84 if (!window)
85 return true;
86
87 // The focused window is always inside the active window, so windows that
88 // aren't activatable can't contain the focused window.
89 aura::Window* activatable = GetActivatableWindow(window);
90 if (!activatable || !activatable->Contains(window))
91 return false;
92 return window->CanFocus();
93 }
94
GetToplevelWindow(aura::Window * window) const95 aura::Window* BaseFocusRules::GetToplevelWindow(aura::Window* window) const {
96 aura::Window* parent = window->parent();
97 aura::Window* child = window;
98 while (parent) {
99 if (IsToplevelWindow(child))
100 return child;
101
102 parent = parent->parent();
103 child = child->parent();
104 }
105 return NULL;
106 }
107
GetActivatableWindow(aura::Window * window) const108 aura::Window* BaseFocusRules::GetActivatableWindow(aura::Window* window) const {
109 aura::Window* parent = window->parent();
110 aura::Window* child = window;
111 while (parent) {
112 if (CanActivateWindow(child))
113 return child;
114
115 // CanActivateWindow() above will return false if |child| is blocked by a
116 // modal transient. In this case the modal is or contains the activatable
117 // window. We recurse because the modal may itself be blocked by a modal
118 // transient.
119 aura::Window* modal_transient = GetModalTransient(child);
120 if (modal_transient)
121 return GetActivatableWindow(modal_transient);
122
123 if (wm::GetTransientParent(child)) {
124 // To avoid infinite recursion, if |child| has a transient parent
125 // whose own modal transient is |child| itself, just return |child|.
126 aura::Window* parent_modal_transient =
127 GetModalTransient(wm::GetTransientParent(child));
128 if (parent_modal_transient == child)
129 return child;
130
131 return GetActivatableWindow(wm::GetTransientParent(child));
132 }
133
134 parent = parent->parent();
135 child = child->parent();
136 }
137 return NULL;
138 }
139
GetFocusableWindow(aura::Window * window) const140 aura::Window* BaseFocusRules::GetFocusableWindow(aura::Window* window) const {
141 if (CanFocusWindow(window))
142 return window;
143
144 // |window| may be in a hierarchy that is non-activatable, in which case we
145 // need to cut over to the activatable hierarchy.
146 aura::Window* activatable = GetActivatableWindow(window);
147 if (!activatable) {
148 // There may not be a related activatable hierarchy to cut over to, in which
149 // case we try an unrelated one.
150 aura::Window* toplevel = GetToplevelWindow(window);
151 if (toplevel)
152 activatable = GetNextActivatableWindow(toplevel);
153 if (!activatable)
154 return NULL;
155 }
156
157 if (!activatable->Contains(window)) {
158 // If there's already a child window focused in the activatable hierarchy,
159 // just use that (i.e. don't shift focus), otherwise we need to at least cut
160 // over to the activatable hierarchy.
161 aura::Window* focused = GetFocusedWindow(activatable);
162 return activatable->Contains(focused) ? focused : activatable;
163 }
164
165 while (window && !CanFocusWindow(window))
166 window = window->parent();
167 return window;
168 }
169
GetNextActivatableWindow(aura::Window * ignore) const170 aura::Window* BaseFocusRules::GetNextActivatableWindow(
171 aura::Window* ignore) const {
172 DCHECK(ignore);
173
174 // Can be called from the RootWindow's destruction, which has a NULL parent.
175 if (!ignore->parent())
176 return NULL;
177
178 // In the basic scenarios handled by BasicFocusRules, the pool of activatable
179 // windows is limited to the |ignore|'s siblings.
180 const aura::Window::Windows& siblings = ignore->parent()->children();
181 DCHECK(!siblings.empty());
182
183 for (aura::Window::Windows::const_reverse_iterator rit = siblings.rbegin();
184 rit != siblings.rend();
185 ++rit) {
186 aura::Window* cur = *rit;
187 if (cur == ignore)
188 continue;
189 if (CanActivateWindow(cur))
190 return cur;
191 }
192 return NULL;
193 }
194
195 } // namespace wm
196