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 "ui/wm/core/focus_controller.h"
7 #include "base/auto_reset.h"
8 #include "ui/aura/client/aura_constants.h"
9 #include "ui/aura/client/capture_client.h"
10 #include "ui/aura/client/focus_change_observer.h"
11 #include "ui/aura/env.h"
12 #include "ui/aura/window_tracker.h"
13 #include "ui/base/ime/text_input_focus_manager.h"
14 #include "ui/events/event.h"
15 #include "ui/wm/core/focus_rules.h"
16 #include "ui/wm/core/window_util.h"
17 #include "ui/wm/public/activation_change_observer.h"
22 // When a modal window is activated, we bring its entire transient parent chain
23 // to the front. This function must be called before the modal transient is
24 // stacked at the top to ensure correct stacking order.
25 void StackTransientParentsBelowModalWindow(aura::Window
* window
) {
26 if (window
->GetProperty(aura::client::kModalKey
) != ui::MODAL_TYPE_WINDOW
)
29 aura::Window
* transient_parent
= wm::GetTransientParent(window
);
30 while (transient_parent
) {
31 transient_parent
->parent()->StackChildAtTop(transient_parent
);
32 transient_parent
= wm::GetTransientParent(transient_parent
);
38 ////////////////////////////////////////////////////////////////////////////////
39 // FocusController, public:
41 FocusController::FocusController(FocusRules
* rules
)
42 : active_window_(NULL
),
43 focused_window_(NULL
),
44 updating_focus_(false),
45 updating_activation_(false),
47 observer_manager_(this) {
51 FocusController::~FocusController() {
54 ////////////////////////////////////////////////////////////////////////////////
55 // FocusController, aura::client::ActivationClient implementation:
57 void FocusController::AddObserver(
58 aura::client::ActivationChangeObserver
* observer
) {
59 activation_observers_
.AddObserver(observer
);
62 void FocusController::RemoveObserver(
63 aura::client::ActivationChangeObserver
* observer
) {
64 activation_observers_
.RemoveObserver(observer
);
67 void FocusController::ActivateWindow(aura::Window
* window
) {
71 void FocusController::DeactivateWindow(aura::Window
* window
) {
73 FocusWindow(rules_
->GetNextActivatableWindow(window
));
76 aura::Window
* FocusController::GetActiveWindow() {
77 return active_window_
;
80 aura::Window
* FocusController::GetActivatableWindow(aura::Window
* window
) {
81 return rules_
->GetActivatableWindow(window
);
84 aura::Window
* FocusController::GetToplevelWindow(aura::Window
* window
) {
85 return rules_
->GetToplevelWindow(window
);
88 bool FocusController::CanActivateWindow(aura::Window
* window
) const {
89 return rules_
->CanActivateWindow(window
);
92 ////////////////////////////////////////////////////////////////////////////////
93 // FocusController, aura::client::FocusClient implementation:
95 void FocusController::AddObserver(
96 aura::client::FocusChangeObserver
* observer
) {
97 focus_observers_
.AddObserver(observer
);
100 void FocusController::RemoveObserver(
101 aura::client::FocusChangeObserver
* observer
) {
102 focus_observers_
.RemoveObserver(observer
);
105 void FocusController::FocusWindow(aura::Window
* window
) {
107 (window
->Contains(focused_window_
) || window
->Contains(active_window_
))) {
111 // Focusing a window also activates its containing activatable window. Note
112 // that the rules could redirect activation activation and/or focus.
113 aura::Window
* focusable
= rules_
->GetFocusableWindow(window
);
114 aura::Window
* activatable
=
115 focusable
? rules_
->GetActivatableWindow(focusable
) : NULL
;
117 // We need valid focusable/activatable windows in the event we're not clearing
118 // focus. "Clearing focus" is inferred by whether or not |window| passed to
119 // this function is non-NULL.
120 if (window
&& (!focusable
|| !activatable
))
122 DCHECK((focusable
&& activatable
) || !window
);
124 // Activation change observers may change the focused window. If this happens
125 // we must not adjust the focus below since this will clobber that change.
126 aura::Window
* last_focused_window
= focused_window_
;
127 if (!updating_activation_
)
128 SetActiveWindow(window
, activatable
);
130 // If the window's ActivationChangeObserver shifted focus to a valid window,
131 // we don't want to focus the window we thought would be focused by default.
132 bool activation_changed_focus
= last_focused_window
!= focused_window_
;
133 if (!updating_focus_
&& (!activation_changed_focus
|| !focused_window_
)) {
134 if (active_window_
&& focusable
)
135 DCHECK(active_window_
->Contains(focusable
));
136 SetFocusedWindow(focusable
);
140 void FocusController::ResetFocusWithinActiveWindow(aura::Window
* window
) {
144 if (!active_window_
->Contains(window
))
146 SetFocusedWindow(window
);
149 aura::Window
* FocusController::GetFocusedWindow() {
150 return focused_window_
;
153 ////////////////////////////////////////////////////////////////////////////////
154 // FocusController, ui::EventHandler implementation:
155 void FocusController::OnKeyEvent(ui::KeyEvent
* event
) {
158 void FocusController::OnMouseEvent(ui::MouseEvent
* event
) {
159 if (event
->type() == ui::ET_MOUSE_PRESSED
&& !event
->handled())
160 WindowFocusedFromInputEvent(static_cast<aura::Window
*>(event
->target()));
163 void FocusController::OnScrollEvent(ui::ScrollEvent
* event
) {
166 void FocusController::OnTouchEvent(ui::TouchEvent
* event
) {
169 void FocusController::OnGestureEvent(ui::GestureEvent
* event
) {
170 if (event
->type() == ui::ET_GESTURE_BEGIN
&&
171 event
->details().touch_points() == 1 &&
173 WindowFocusedFromInputEvent(static_cast<aura::Window
*>(event
->target()));
177 ////////////////////////////////////////////////////////////////////////////////
178 // FocusController, aura::WindowObserver implementation:
180 void FocusController::OnWindowVisibilityChanged(aura::Window
* window
,
183 WindowLostFocusFromDispositionChange(window
, window
->parent());
186 void FocusController::OnWindowDestroying(aura::Window
* window
) {
187 // A window's modality state will interfere with focus restoration during its
189 window
->ClearProperty(aura::client::kModalKey
);
190 WindowLostFocusFromDispositionChange(window
, window
->parent());
193 void FocusController::OnWindowHierarchyChanging(
194 const HierarchyChangeParams
& params
) {
195 if (params
.receiver
== active_window_
&&
196 params
.target
->Contains(params
.receiver
) && (!params
.new_parent
||
197 aura::client::GetFocusClient(params
.new_parent
) !=
198 aura::client::GetFocusClient(params
.receiver
))) {
199 WindowLostFocusFromDispositionChange(params
.receiver
, params
.old_parent
);
203 void FocusController::OnWindowHierarchyChanged(
204 const HierarchyChangeParams
& params
) {
205 if (params
.receiver
== focused_window_
&&
206 params
.target
->Contains(params
.receiver
) && (!params
.new_parent
||
207 aura::client::GetFocusClient(params
.new_parent
) !=
208 aura::client::GetFocusClient(params
.receiver
))) {
209 WindowLostFocusFromDispositionChange(params
.receiver
, params
.old_parent
);
213 ////////////////////////////////////////////////////////////////////////////////
214 // FocusController, private:
216 void FocusController::SetFocusedWindow(aura::Window
* window
) {
217 if (updating_focus_
|| window
== focused_window_
)
219 DCHECK(rules_
->CanFocusWindow(window
));
221 DCHECK_EQ(window
, rules_
->GetFocusableWindow(window
));
223 base::AutoReset
<bool> updating_focus(&updating_focus_
, true);
224 aura::Window
* lost_focus
= focused_window_
;
226 // |window| is going to get the focus, so reset the text input client.
227 // OnWindowFocused() may set a proper text input client if the implementation
228 // supports text input.
229 ui::TextInputFocusManager
* text_input_focus_manager
=
230 ui::TextInputFocusManager::GetInstance();
232 text_input_focus_manager
->FocusTextInputClient(NULL
);
234 // Allow for the window losing focus to be deleted during dispatch. If it is
235 // deleted pass NULL to observers instead of a deleted window.
236 aura::WindowTracker window_tracker
;
238 window_tracker
.Add(lost_focus
);
239 if (focused_window_
&& observer_manager_
.IsObserving(focused_window_
) &&
240 focused_window_
!= active_window_
) {
241 observer_manager_
.Remove(focused_window_
);
243 focused_window_
= window
;
244 if (focused_window_
&& !observer_manager_
.IsObserving(focused_window_
))
245 observer_manager_
.Add(focused_window_
);
247 FOR_EACH_OBSERVER(aura::client::FocusChangeObserver
,
249 OnWindowFocused(focused_window_
,
250 window_tracker
.Contains(lost_focus
) ?
252 if (window_tracker
.Contains(lost_focus
)) {
253 aura::client::FocusChangeObserver
* observer
=
254 aura::client::GetFocusChangeObserver(lost_focus
);
256 observer
->OnWindowFocused(focused_window_
, lost_focus
);
258 aura::client::FocusChangeObserver
* observer
=
259 aura::client::GetFocusChangeObserver(focused_window_
);
261 observer
->OnWindowFocused(
263 window_tracker
.Contains(lost_focus
) ? lost_focus
: NULL
);
266 // Ensure that the text input client is reset when the window loses the focus.
268 text_input_focus_manager
->FocusTextInputClient(NULL
);
271 void FocusController::SetActiveWindow(aura::Window
* requested_window
,
272 aura::Window
* window
) {
273 if (updating_activation_
)
276 if (window
== active_window_
) {
277 if (requested_window
) {
278 FOR_EACH_OBSERVER(aura::client::ActivationChangeObserver
,
279 activation_observers_
,
280 OnAttemptToReactivateWindow(requested_window
,
286 DCHECK(rules_
->CanActivateWindow(window
));
288 DCHECK_EQ(window
, rules_
->GetActivatableWindow(window
));
290 base::AutoReset
<bool> updating_activation(&updating_activation_
, true);
291 aura::Window
* lost_activation
= active_window_
;
292 // Allow for the window losing activation to be deleted during dispatch. If
293 // it is deleted pass NULL to observers instead of a deleted window.
294 aura::WindowTracker window_tracker
;
296 window_tracker
.Add(lost_activation
);
297 if (active_window_
&& observer_manager_
.IsObserving(active_window_
) &&
298 focused_window_
!= active_window_
) {
299 observer_manager_
.Remove(active_window_
);
301 active_window_
= window
;
302 if (active_window_
&& !observer_manager_
.IsObserving(active_window_
))
303 observer_manager_
.Add(active_window_
);
304 if (active_window_
) {
305 StackTransientParentsBelowModalWindow(active_window_
);
306 active_window_
->parent()->StackChildAtTop(active_window_
);
309 aura::client::ActivationChangeObserver
* observer
= NULL
;
310 if (window_tracker
.Contains(lost_activation
)) {
311 observer
= aura::client::GetActivationChangeObserver(lost_activation
);
313 observer
->OnWindowActivated(active_window_
, lost_activation
);
315 observer
= aura::client::GetActivationChangeObserver(active_window_
);
317 observer
->OnWindowActivated(
319 window_tracker
.Contains(lost_activation
) ? lost_activation
: NULL
);
321 FOR_EACH_OBSERVER(aura::client::ActivationChangeObserver
,
322 activation_observers_
,
323 OnWindowActivated(active_window_
,
324 window_tracker
.Contains(lost_activation
) ?
325 lost_activation
: NULL
));
328 void FocusController::WindowLostFocusFromDispositionChange(
329 aura::Window
* window
,
330 aura::Window
* next
) {
331 // TODO(beng): See if this function can be replaced by a call to
333 // Activation adjustments are handled first in the event of a disposition
334 // changed. If an activation change is necessary, focus is reset as part of
335 // that process so there's no point in updating focus independently.
336 if (window
== active_window_
) {
337 aura::Window
* next_activatable
= rules_
->GetNextActivatableWindow(window
);
338 SetActiveWindow(NULL
, next_activatable
);
339 if (!(active_window_
&& active_window_
->Contains(focused_window_
)))
340 SetFocusedWindow(next_activatable
);
341 } else if (window
->Contains(focused_window_
)) {
342 // Active window isn't changing, but focused window might be.
343 SetFocusedWindow(rules_
->GetFocusableWindow(next
));
347 void FocusController::WindowFocusedFromInputEvent(aura::Window
* window
) {
348 // Only focus |window| if it or any of its parents can be focused. Otherwise
349 // FocusWindow() will focus the topmost window, which may not be the
350 // currently focused one.
351 if (rules_
->CanFocusWindow(GetToplevelWindow(window
)))