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 WindowLostFocusFromDispositionChange(window
, window
->parent());
190 void FocusController::OnWindowHierarchyChanging(
191 const HierarchyChangeParams
& params
) {
192 if (params
.receiver
== active_window_
&&
193 params
.target
->Contains(params
.receiver
) && (!params
.new_parent
||
194 aura::client::GetFocusClient(params
.new_parent
) !=
195 aura::client::GetFocusClient(params
.receiver
))) {
196 WindowLostFocusFromDispositionChange(params
.receiver
, params
.old_parent
);
200 void FocusController::OnWindowHierarchyChanged(
201 const HierarchyChangeParams
& params
) {
202 if (params
.receiver
== focused_window_
&&
203 params
.target
->Contains(params
.receiver
) && (!params
.new_parent
||
204 aura::client::GetFocusClient(params
.new_parent
) !=
205 aura::client::GetFocusClient(params
.receiver
))) {
206 WindowLostFocusFromDispositionChange(params
.receiver
, params
.old_parent
);
210 ////////////////////////////////////////////////////////////////////////////////
211 // FocusController, private:
213 void FocusController::SetFocusedWindow(aura::Window
* window
) {
214 if (updating_focus_
|| window
== focused_window_
)
216 DCHECK(rules_
->CanFocusWindow(window
));
218 DCHECK_EQ(window
, rules_
->GetFocusableWindow(window
));
220 base::AutoReset
<bool> updating_focus(&updating_focus_
, true);
221 aura::Window
* lost_focus
= focused_window_
;
223 // |window| is going to get the focus, so reset the text input client.
224 // OnWindowFocused() may set a proper text input client if the implementation
225 // supports text input.
226 ui::TextInputFocusManager
* text_input_focus_manager
=
227 ui::TextInputFocusManager::GetInstance();
229 text_input_focus_manager
->FocusTextInputClient(NULL
);
231 // Allow for the window losing focus to be deleted during dispatch. If it is
232 // deleted pass NULL to observers instead of a deleted window.
233 aura::WindowTracker window_tracker
;
235 window_tracker
.Add(lost_focus
);
236 if (focused_window_
&& observer_manager_
.IsObserving(focused_window_
) &&
237 focused_window_
!= active_window_
) {
238 observer_manager_
.Remove(focused_window_
);
240 focused_window_
= window
;
241 if (focused_window_
&& !observer_manager_
.IsObserving(focused_window_
))
242 observer_manager_
.Add(focused_window_
);
244 FOR_EACH_OBSERVER(aura::client::FocusChangeObserver
,
246 OnWindowFocused(focused_window_
,
247 window_tracker
.Contains(lost_focus
) ?
249 if (window_tracker
.Contains(lost_focus
)) {
250 aura::client::FocusChangeObserver
* observer
=
251 aura::client::GetFocusChangeObserver(lost_focus
);
253 observer
->OnWindowFocused(focused_window_
, lost_focus
);
255 aura::client::FocusChangeObserver
* observer
=
256 aura::client::GetFocusChangeObserver(focused_window_
);
258 observer
->OnWindowFocused(
260 window_tracker
.Contains(lost_focus
) ? lost_focus
: NULL
);
263 // Ensure that the text input client is reset when the window loses the focus.
265 text_input_focus_manager
->FocusTextInputClient(NULL
);
268 void FocusController::SetActiveWindow(aura::Window
* requested_window
,
269 aura::Window
* window
) {
270 if (updating_activation_
)
273 if (window
== active_window_
) {
274 if (requested_window
) {
275 FOR_EACH_OBSERVER(aura::client::ActivationChangeObserver
,
276 activation_observers_
,
277 OnAttemptToReactivateWindow(requested_window
,
283 DCHECK(rules_
->CanActivateWindow(window
));
285 DCHECK_EQ(window
, rules_
->GetActivatableWindow(window
));
287 base::AutoReset
<bool> updating_activation(&updating_activation_
, true);
288 aura::Window
* lost_activation
= active_window_
;
289 // Allow for the window losing activation to be deleted during dispatch. If
290 // it is deleted pass NULL to observers instead of a deleted window.
291 aura::WindowTracker window_tracker
;
293 window_tracker
.Add(lost_activation
);
294 if (active_window_
&& observer_manager_
.IsObserving(active_window_
) &&
295 focused_window_
!= active_window_
) {
296 observer_manager_
.Remove(active_window_
);
298 active_window_
= window
;
299 if (active_window_
&& !observer_manager_
.IsObserving(active_window_
))
300 observer_manager_
.Add(active_window_
);
301 if (active_window_
) {
302 StackTransientParentsBelowModalWindow(active_window_
);
303 active_window_
->parent()->StackChildAtTop(active_window_
);
306 aura::client::ActivationChangeObserver
* observer
= NULL
;
307 if (window_tracker
.Contains(lost_activation
)) {
308 observer
= aura::client::GetActivationChangeObserver(lost_activation
);
310 observer
->OnWindowActivated(active_window_
, lost_activation
);
312 observer
= aura::client::GetActivationChangeObserver(active_window_
);
314 observer
->OnWindowActivated(
316 window_tracker
.Contains(lost_activation
) ? lost_activation
: NULL
);
318 FOR_EACH_OBSERVER(aura::client::ActivationChangeObserver
,
319 activation_observers_
,
320 OnWindowActivated(active_window_
,
321 window_tracker
.Contains(lost_activation
) ?
322 lost_activation
: NULL
));
325 void FocusController::WindowLostFocusFromDispositionChange(
326 aura::Window
* window
,
327 aura::Window
* next
) {
328 // A window's modality state will interfere with focus restoration during its
330 window
->ClearProperty(aura::client::kModalKey
);
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
)))