focus_controller.cc revision 0de6073388f4e2780db8536178b129cd8f6ab386
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/focus_controller.h"
6
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"
18
19namespace wm {
20namespace {
21
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.
25void StackTransientParentsBelowModalWindow(aura::Window* window) {
26  if (window->GetProperty(aura::client::kModalKey) != ui::MODAL_TYPE_WINDOW)
27    return;
28
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);
33  }
34}
35
36}  // namespace
37
38////////////////////////////////////////////////////////////////////////////////
39// FocusController, public:
40
41FocusController::FocusController(FocusRules* rules)
42    : active_window_(NULL),
43      focused_window_(NULL),
44      updating_focus_(false),
45      updating_activation_(false),
46      rules_(rules),
47      observer_manager_(this) {
48  DCHECK(rules);
49}
50
51FocusController::~FocusController() {
52}
53
54////////////////////////////////////////////////////////////////////////////////
55// FocusController, aura::client::ActivationClient implementation:
56
57void FocusController::AddObserver(
58    aura::client::ActivationChangeObserver* observer) {
59  activation_observers_.AddObserver(observer);
60}
61
62void FocusController::RemoveObserver(
63    aura::client::ActivationChangeObserver* observer) {
64  activation_observers_.RemoveObserver(observer);
65}
66
67void FocusController::ActivateWindow(aura::Window* window) {
68  FocusWindow(window);
69}
70
71void FocusController::DeactivateWindow(aura::Window* window) {
72  if (window)
73    FocusWindow(rules_->GetNextActivatableWindow(window));
74}
75
76aura::Window* FocusController::GetActiveWindow() {
77  return active_window_;
78}
79
80aura::Window* FocusController::GetActivatableWindow(aura::Window* window) {
81  return rules_->GetActivatableWindow(window);
82}
83
84aura::Window* FocusController::GetToplevelWindow(aura::Window* window) {
85  return rules_->GetToplevelWindow(window);
86}
87
88bool FocusController::OnWillFocusWindow(aura::Window* window,
89                                        const ui::Event* event) {
90  NOTREACHED();
91  return false;
92}
93
94bool FocusController::CanActivateWindow(aura::Window* window) const {
95  return rules_->CanActivateWindow(window);
96}
97
98////////////////////////////////////////////////////////////////////////////////
99// FocusController, aura::client::FocusClient implementation:
100
101void FocusController::AddObserver(
102    aura::client::FocusChangeObserver* observer) {
103  focus_observers_.AddObserver(observer);
104}
105
106void FocusController::RemoveObserver(
107    aura::client::FocusChangeObserver* observer) {
108  focus_observers_.RemoveObserver(observer);
109}
110
111void FocusController::FocusWindow(aura::Window* window) {
112  if (window &&
113      (window->Contains(focused_window_) || window->Contains(active_window_))) {
114    return;
115  }
116
117  // We should not be messing with the focus if the window has capture, unless
118  // no has focus.
119  if (window && (aura::client::GetCaptureWindow(window) == window) &&
120      focused_window_) {
121    return;
122  }
123
124  // Focusing a window also activates its containing activatable window. Note
125  // that the rules could redirect activation activation and/or focus.
126  aura::Window* focusable = rules_->GetFocusableWindow(window);
127  aura::Window* activatable =
128      focusable ? rules_->GetActivatableWindow(focusable) : NULL;
129
130  // We need valid focusable/activatable windows in the event we're not clearing
131  // focus. "Clearing focus" is inferred by whether or not |window| passed to
132  // this function is non-NULL.
133  if (window && (!focusable || !activatable))
134    return;
135  DCHECK((focusable && activatable) || !window);
136
137  // Activation change observers may change the focused window. If this happens
138  // we must not adjust the focus below since this will clobber that change.
139  aura::Window* last_focused_window = focused_window_;
140  if (!updating_activation_)
141    SetActiveWindow(window, activatable);
142
143  // If the window's ActivationChangeObserver shifted focus to a valid window,
144  // we don't want to focus the window we thought would be focused by default.
145  bool activation_changed_focus = last_focused_window != focused_window_;
146  if (!updating_focus_ && (!activation_changed_focus || !focused_window_)) {
147    if (active_window_ && focusable)
148      DCHECK(active_window_->Contains(focusable));
149    SetFocusedWindow(focusable);
150  }
151}
152
153void FocusController::ResetFocusWithinActiveWindow(aura::Window* window) {
154  DCHECK(window);
155  if (!active_window_)
156    return;
157  if (!active_window_->Contains(window))
158    return;
159  SetFocusedWindow(window);
160}
161
162aura::Window* FocusController::GetFocusedWindow() {
163  return focused_window_;
164}
165
166////////////////////////////////////////////////////////////////////////////////
167// FocusController, ui::EventHandler implementation:
168void FocusController::OnKeyEvent(ui::KeyEvent* event) {
169}
170
171void FocusController::OnMouseEvent(ui::MouseEvent* event) {
172  if (event->type() == ui::ET_MOUSE_PRESSED && !event->handled())
173    WindowFocusedFromInputEvent(static_cast<aura::Window*>(event->target()));
174}
175
176void FocusController::OnScrollEvent(ui::ScrollEvent* event) {
177}
178
179void FocusController::OnTouchEvent(ui::TouchEvent* event) {
180}
181
182void FocusController::OnGestureEvent(ui::GestureEvent* event) {
183  if (event->type() == ui::ET_GESTURE_BEGIN &&
184      event->details().touch_points() == 1 &&
185      !event->handled()) {
186    WindowFocusedFromInputEvent(static_cast<aura::Window*>(event->target()));
187  }
188}
189
190////////////////////////////////////////////////////////////////////////////////
191// FocusController, aura::WindowObserver implementation:
192
193void FocusController::OnWindowVisibilityChanged(aura::Window* window,
194                                                bool visible) {
195  if (!visible)
196    WindowLostFocusFromDispositionChange(window, window->parent());
197}
198
199void FocusController::OnWindowDestroying(aura::Window* window) {
200  WindowLostFocusFromDispositionChange(window, window->parent());
201}
202
203void FocusController::OnWindowHierarchyChanging(
204    const HierarchyChangeParams& params) {
205  if (params.receiver == active_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);
210  }
211}
212
213void FocusController::OnWindowHierarchyChanged(
214    const HierarchyChangeParams& params) {
215  if (params.receiver == focused_window_ &&
216      params.target->Contains(params.receiver) && (!params.new_parent ||
217      aura::client::GetFocusClient(params.new_parent) !=
218          aura::client::GetFocusClient(params.receiver))) {
219    WindowLostFocusFromDispositionChange(params.receiver, params.old_parent);
220  }
221}
222
223////////////////////////////////////////////////////////////////////////////////
224// FocusController, private:
225
226void FocusController::SetFocusedWindow(aura::Window* window) {
227  if (updating_focus_ || window == focused_window_)
228    return;
229  DCHECK(rules_->CanFocusWindow(window));
230  if (window)
231    DCHECK_EQ(window, rules_->GetFocusableWindow(window));
232
233  base::AutoReset<bool> updating_focus(&updating_focus_, true);
234  aura::Window* lost_focus = focused_window_;
235
236  // |window| is going to get the focus, so reset the text input client.
237  // OnWindowFocused() may set a proper text input client if the implementation
238  // supports text input.
239  ui::TextInputFocusManager* text_input_focus_manager =
240      ui::TextInputFocusManager::GetInstance();
241  if (window)
242    text_input_focus_manager->FocusTextInputClient(NULL);
243
244  // Allow for the window losing focus to be deleted during dispatch. If it is
245  // deleted pass NULL to observers instead of a deleted window.
246  aura::WindowTracker window_tracker;
247  if (lost_focus)
248    window_tracker.Add(lost_focus);
249  if (focused_window_ && observer_manager_.IsObserving(focused_window_) &&
250      focused_window_ != active_window_) {
251    observer_manager_.Remove(focused_window_);
252  }
253  focused_window_ = window;
254  if (focused_window_ && !observer_manager_.IsObserving(focused_window_))
255    observer_manager_.Add(focused_window_);
256
257  FOR_EACH_OBSERVER(aura::client::FocusChangeObserver,
258                    focus_observers_,
259                    OnWindowFocused(focused_window_,
260                                    window_tracker.Contains(lost_focus) ?
261                                    lost_focus : NULL));
262  if (window_tracker.Contains(lost_focus)) {
263    aura::client::FocusChangeObserver* observer =
264        aura::client::GetFocusChangeObserver(lost_focus);
265    if (observer)
266      observer->OnWindowFocused(focused_window_, lost_focus);
267  }
268  aura::client::FocusChangeObserver* observer =
269      aura::client::GetFocusChangeObserver(focused_window_);
270  if (observer) {
271    observer->OnWindowFocused(
272        focused_window_,
273        window_tracker.Contains(lost_focus) ? lost_focus : NULL);
274  }
275
276  // Ensure that the text input client is reset when the window loses the focus.
277  if (!window)
278    text_input_focus_manager->FocusTextInputClient(NULL);
279}
280
281void FocusController::SetActiveWindow(aura::Window* requested_window,
282                                      aura::Window* window) {
283  if (updating_activation_)
284    return;
285
286  if (window == active_window_) {
287    if (requested_window) {
288      FOR_EACH_OBSERVER(aura::client::ActivationChangeObserver,
289                        activation_observers_,
290                        OnAttemptToReactivateWindow(requested_window,
291                                                    active_window_));
292    }
293    return;
294  }
295
296  DCHECK(rules_->CanActivateWindow(window));
297  if (window)
298    DCHECK_EQ(window, rules_->GetActivatableWindow(window));
299
300  base::AutoReset<bool> updating_activation(&updating_activation_, true);
301  aura::Window* lost_activation = active_window_;
302  // Allow for the window losing activation to be deleted during dispatch. If
303  // it is deleted pass NULL to observers instead of a deleted window.
304  aura::WindowTracker window_tracker;
305  if (lost_activation)
306    window_tracker.Add(lost_activation);
307  if (active_window_ && observer_manager_.IsObserving(active_window_) &&
308      focused_window_ != active_window_) {
309    observer_manager_.Remove(active_window_);
310  }
311  active_window_ = window;
312  if (active_window_ && !observer_manager_.IsObserving(active_window_))
313    observer_manager_.Add(active_window_);
314  if (active_window_) {
315    StackTransientParentsBelowModalWindow(active_window_);
316    active_window_->parent()->StackChildAtTop(active_window_);
317  }
318
319  aura::client::ActivationChangeObserver* observer = NULL;
320  if (window_tracker.Contains(lost_activation)) {
321    observer = aura::client::GetActivationChangeObserver(lost_activation);
322    if (observer)
323      observer->OnWindowActivated(active_window_, lost_activation);
324  }
325  observer = aura::client::GetActivationChangeObserver(active_window_);
326  if (observer) {
327    observer->OnWindowActivated(
328        active_window_,
329        window_tracker.Contains(lost_activation) ? lost_activation : NULL);
330  }
331  FOR_EACH_OBSERVER(aura::client::ActivationChangeObserver,
332                    activation_observers_,
333                    OnWindowActivated(active_window_,
334                                      window_tracker.Contains(lost_activation) ?
335                                      lost_activation : NULL));
336}
337
338void FocusController::WindowLostFocusFromDispositionChange(
339    aura::Window* window,
340    aura::Window* next) {
341  // A window's modality state will interfere with focus restoration during its
342  // destruction.
343  window->ClearProperty(aura::client::kModalKey);
344  // TODO(beng): See if this function can be replaced by a call to
345  //             FocusWindow().
346  // Activation adjustments are handled first in the event of a disposition
347  // changed. If an activation change is necessary, focus is reset as part of
348  // that process so there's no point in updating focus independently.
349  if (window == active_window_) {
350    aura::Window* next_activatable = rules_->GetNextActivatableWindow(window);
351    SetActiveWindow(NULL, next_activatable);
352    if (!(active_window_ && active_window_->Contains(focused_window_)))
353      SetFocusedWindow(next_activatable);
354  } else if (window->Contains(focused_window_)) {
355    // Active window isn't changing, but focused window might be.
356    SetFocusedWindow(rules_->GetFocusableWindow(next));
357  }
358}
359
360void FocusController::WindowFocusedFromInputEvent(aura::Window* window) {
361  // Only focus |window| if it or any of its parents can be focused. Otherwise
362  // FocusWindow() will focus the topmost window, which may not be the
363  // currently focused one.
364  if (rules_->CanFocusWindow(GetToplevelWindow(window)))
365    FocusWindow(window);
366}
367
368}  // namespace wm
369