Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / ui / views / focus / focus_manager.cc
blobc84c3152734d2de2994673aee76b89365884d19d
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/views/focus/focus_manager.h"
7 #include <algorithm>
8 #include <vector>
10 #include "base/auto_reset.h"
11 #include "base/logging.h"
12 #include "build/build_config.h"
13 #include "ui/base/accelerators/accelerator.h"
14 #include "ui/base/ime/input_method.h"
15 #include "ui/base/ime/text_input_client.h"
16 #include "ui/events/event.h"
17 #include "ui/events/keycodes/keyboard_codes.h"
18 #include "ui/views/focus/focus_manager_delegate.h"
19 #include "ui/views/focus/focus_search.h"
20 #include "ui/views/focus/view_storage.h"
21 #include "ui/views/focus/widget_focus_manager.h"
22 #include "ui/views/view.h"
23 #include "ui/views/widget/root_view.h"
24 #include "ui/views/widget/widget.h"
25 #include "ui/views/widget/widget_delegate.h"
27 namespace views {
29 bool FocusManager::arrow_key_traversal_enabled_ = false;
31 FocusManager::FocusManager(Widget* widget, FocusManagerDelegate* delegate)
32 : widget_(widget),
33 delegate_(delegate),
34 focused_view_(NULL),
35 accelerator_manager_(new ui::AcceleratorManager),
36 shortcut_handling_suspended_(false),
37 focus_change_reason_(kReasonDirectFocusChange),
38 is_changing_focus_(false) {
39 DCHECK(widget_);
40 stored_focused_view_storage_id_ =
41 ViewStorage::GetInstance()->CreateStorageID();
44 FocusManager::~FocusManager() {
47 bool FocusManager::OnKeyEvent(const ui::KeyEvent& event) {
48 const int key_code = event.key_code();
50 if (event.type() != ui::ET_KEY_PRESSED && event.type() != ui::ET_KEY_RELEASED)
51 return false;
53 if (shortcut_handling_suspended())
54 return true;
56 ui::Accelerator accelerator(event);
58 if (event.type() == ui::ET_KEY_PRESSED) {
59 // If the focused view wants to process the key event as is, let it be.
60 if (focused_view_ && focused_view_->SkipDefaultKeyEventProcessing(event) &&
61 !accelerator_manager_->HasPriorityHandler(accelerator))
62 return true;
64 // Intercept Tab related messages for focus traversal.
65 // Note that we don't do focus traversal if the root window is not part of
66 // the active window hierarchy as this would mean we have no focused view
67 // and would focus the first focusable view.
68 if (IsTabTraversalKeyEvent(event)) {
69 AdvanceFocus(event.IsShiftDown());
70 return false;
73 if (arrow_key_traversal_enabled_ && ProcessArrowKeyTraversal(event))
74 return false;
76 // Intercept arrow key messages to switch between grouped views.
77 if (focused_view_ && focused_view_->GetGroup() != -1 &&
78 (key_code == ui::VKEY_UP || key_code == ui::VKEY_DOWN ||
79 key_code == ui::VKEY_LEFT || key_code == ui::VKEY_RIGHT)) {
80 bool next = (key_code == ui::VKEY_RIGHT || key_code == ui::VKEY_DOWN);
81 View::Views views;
82 focused_view_->parent()->GetViewsInGroup(focused_view_->GetGroup(),
83 &views);
84 View::Views::const_iterator i(
85 std::find(views.begin(), views.end(), focused_view_));
86 DCHECK(i != views.end());
87 int index = static_cast<int>(i - views.begin());
88 index += next ? 1 : -1;
89 if (index < 0) {
90 index = static_cast<int>(views.size()) - 1;
91 } else if (index >= static_cast<int>(views.size())) {
92 index = 0;
94 SetFocusedViewWithReason(views[index], kReasonFocusTraversal);
95 return false;
99 // Process keyboard accelerators.
100 // If the key combination matches an accelerator, the accelerator is
101 // triggered, otherwise the key event is processed as usual.
102 if (ProcessAccelerator(accelerator)) {
103 // If a shortcut was activated for this keydown message, do not propagate
104 // the event further.
105 return false;
107 return true;
110 void FocusManager::ValidateFocusedView() {
111 if (focused_view_ && !ContainsView(focused_view_))
112 ClearFocus();
115 // Tests whether a view is valid, whether it still belongs to the window
116 // hierarchy of the FocusManager.
117 bool FocusManager::ContainsView(View* view) {
118 Widget* widget = view->GetWidget();
119 return widget ? widget->GetFocusManager() == this : false;
122 void FocusManager::AdvanceFocus(bool reverse) {
123 View* v = GetNextFocusableView(focused_view_, NULL, reverse, false);
124 // Note: Do not skip this next block when v == focused_view_. If the user
125 // tabs past the last focusable element in a webpage, we'll get here, and if
126 // the TabContentsContainerView is the only focusable view (possible in
127 // fullscreen mode), we need to run this block in order to cycle around to the
128 // first element on the page.
129 if (v) {
130 views::View* focused_view = focused_view_;
131 v->AboutToRequestFocusFromTabTraversal(reverse);
132 // AboutToRequestFocusFromTabTraversal() may have changed focus. If it did,
133 // don't change focus again.
134 if (focused_view == focused_view_)
135 SetFocusedViewWithReason(v, kReasonFocusTraversal);
139 void FocusManager::ClearNativeFocus() {
140 // Keep the top root window focused so we get keyboard events.
141 widget_->ClearNativeFocus();
144 bool FocusManager::RotatePaneFocus(Direction direction,
145 FocusCycleWrappingBehavior wrap) {
146 // Get the list of all accessible panes.
147 std::vector<View*> panes;
148 widget_->widget_delegate()->GetAccessiblePanes(&panes);
150 // Count the number of panes and set the default index if no pane
151 // is initially focused.
152 int count = static_cast<int>(panes.size());
153 if (count == 0)
154 return false;
156 // Initialize |index| to an appropriate starting index if nothing is
157 // focused initially.
158 int index = direction == kBackward ? 0 : count - 1;
160 // Check to see if a pane already has focus and update the index accordingly.
161 const views::View* focused_view = GetFocusedView();
162 if (focused_view) {
163 for (int i = 0; i < count; i++) {
164 if (panes[i] && panes[i]->Contains(focused_view)) {
165 index = i;
166 break;
171 // Rotate focus.
172 int start_index = index;
173 for (;;) {
174 if (direction == kBackward)
175 index--;
176 else
177 index++;
179 if (wrap == kNoWrap && (index >= count || index < 0))
180 return false;
181 index = (index + count) % count;
183 // Ensure that we don't loop more than once.
184 if (index == start_index)
185 break;
187 views::View* pane = panes[index];
188 DCHECK(pane);
190 if (!pane->visible())
191 continue;
193 pane->RequestFocus();
194 focused_view = GetFocusedView();
195 if (pane == focused_view || pane->Contains(focused_view))
196 return true;
199 return false;
202 View* FocusManager::GetNextFocusableView(View* original_starting_view,
203 Widget* starting_widget,
204 bool reverse,
205 bool dont_loop) {
206 FocusTraversable* focus_traversable = NULL;
208 // Let's revalidate the focused view.
209 ValidateFocusedView();
211 View* starting_view = NULL;
212 if (original_starting_view) {
213 // Search up the containment hierarchy to see if a view is acting as
214 // a pane, and wants to implement its own focus traversable to keep
215 // the focus trapped within that pane.
216 View* pane_search = original_starting_view;
217 while (pane_search) {
218 focus_traversable = pane_search->GetPaneFocusTraversable();
219 if (focus_traversable) {
220 starting_view = original_starting_view;
221 break;
223 pane_search = pane_search->parent();
226 if (!focus_traversable) {
227 if (!reverse) {
228 // If the starting view has a focus traversable, use it.
229 // This is the case with NativeWidgetWins for example.
230 focus_traversable = original_starting_view->GetFocusTraversable();
232 // Otherwise default to the root view.
233 if (!focus_traversable) {
234 focus_traversable =
235 original_starting_view->GetWidget()->GetFocusTraversable();
236 starting_view = original_starting_view;
238 } else {
239 // When you are going back, starting view's FocusTraversable
240 // should not be used.
241 focus_traversable =
242 original_starting_view->GetWidget()->GetFocusTraversable();
243 starting_view = original_starting_view;
246 } else {
247 Widget* widget = starting_widget ? starting_widget : widget_;
248 focus_traversable = widget->GetFocusTraversable();
251 // Traverse the FocusTraversable tree down to find the focusable view.
252 View* v = FindFocusableView(focus_traversable, starting_view, reverse);
253 if (v) {
254 return v;
255 } else {
256 // Let's go up in the FocusTraversable tree.
257 FocusTraversable* parent_focus_traversable =
258 focus_traversable->GetFocusTraversableParent();
259 starting_view = focus_traversable->GetFocusTraversableParentView();
260 while (parent_focus_traversable) {
261 FocusTraversable* new_focus_traversable = NULL;
262 View* new_starting_view = NULL;
263 // When we are going backward, the parent view might gain the next focus.
264 bool check_starting_view = reverse;
265 v = parent_focus_traversable->GetFocusSearch()->FindNextFocusableView(
266 starting_view, reverse, FocusSearch::UP,
267 check_starting_view, &new_focus_traversable, &new_starting_view);
269 if (new_focus_traversable) {
270 DCHECK(!v);
272 // There is a FocusTraversable, traverse it down.
273 v = FindFocusableView(new_focus_traversable, NULL, reverse);
276 if (v)
277 return v;
279 starting_view = focus_traversable->GetFocusTraversableParentView();
280 parent_focus_traversable =
281 parent_focus_traversable->GetFocusTraversableParent();
284 // If we get here, we have reached the end of the focus hierarchy, let's
285 // loop. Make sure there was at least a view to start with, to prevent
286 // infinitely looping in empty windows.
287 if (!dont_loop && original_starting_view) {
288 // Easy, just clear the selection and press tab again.
289 // By calling with NULL as the starting view, we'll start from either
290 // the starting views widget or |widget_|.
291 Widget* widget = original_starting_view->GetWidget();
292 if (widget->widget_delegate()->ShouldAdvanceFocusToTopLevelWidget())
293 widget = widget_;
294 return GetNextFocusableView(NULL, widget, reverse, true);
297 return NULL;
300 void FocusManager::SetFocusedViewWithReason(
301 View* view, FocusChangeReason reason) {
302 if (focused_view_ == view)
303 return;
305 base::AutoReset<bool> auto_changing_focus(&is_changing_focus_, true);
306 // Update the reason for the focus change (since this is checked by
307 // some listeners), then notify all listeners.
308 focus_change_reason_ = reason;
309 FOR_EACH_OBSERVER(FocusChangeListener, focus_change_listeners_,
310 OnWillChangeFocus(focused_view_, view));
312 View* old_focused_view = focused_view_;
313 focused_view_ = view;
314 if (old_focused_view)
315 old_focused_view->Blur();
316 // Also make |focused_view_| the stored focus view. This way the stored focus
317 // view is remembered if focus changes are requested prior to a show or while
318 // hidden.
319 SetStoredFocusView(focused_view_);
320 if (focused_view_)
321 focused_view_->Focus();
323 FOR_EACH_OBSERVER(FocusChangeListener, focus_change_listeners_,
324 OnDidChangeFocus(old_focused_view, focused_view_));
327 void FocusManager::ClearFocus() {
328 // SetFocusedView(NULL) is going to clear out the stored view to. We need to
329 // persist it in this case.
330 views::View* focused_view = GetStoredFocusView();
331 SetFocusedView(NULL);
332 ClearNativeFocus();
333 SetStoredFocusView(focused_view);
336 void FocusManager::AdvanceFocusIfNecessary() {
337 // If widget is inactive, there is no focused view to check. The stored view
338 // will also be checked for focusability when it is being restored.
339 if (!widget_->IsActive())
340 return;
342 // If widget is active and focused view is not focusable, advance focus or,
343 // if not possible, clear focus.
344 if (focused_view_ && !focused_view_->IsAccessibilityFocusable()) {
345 AdvanceFocus(false);
346 if (focused_view_ && !focused_view_->IsAccessibilityFocusable())
347 ClearFocus();
351 void FocusManager::StoreFocusedView(bool clear_native_focus) {
352 View* focused_view = focused_view_;
353 // Don't do anything if no focused view. Storing the view (which is NULL), in
354 // this case, would clobber the view that was previously saved.
355 if (!focused_view_)
356 return;
358 View* v = focused_view_;
360 if (clear_native_focus) {
361 // Temporarily disable notification. ClearFocus() will set the focus to the
362 // main browser window. This extra focus bounce which happens during
363 // deactivation can confuse registered WidgetFocusListeners, as the focus
364 // is not changing due to a user-initiated event.
365 AutoNativeNotificationDisabler local_notification_disabler;
366 // ClearFocus() also stores the focused view.
367 ClearFocus();
368 } else {
369 SetFocusedView(NULL);
370 SetStoredFocusView(focused_view);
373 if (v)
374 v->SchedulePaint(); // Remove focus border.
377 bool FocusManager::RestoreFocusedView() {
378 View* view = GetStoredFocusView();
379 if (view) {
380 if (ContainsView(view)) {
381 if (!view->IsFocusable() && view->IsAccessibilityFocusable()) {
382 // RequestFocus would fail, but we want to restore focus to controls
383 // that had focus in accessibility mode.
384 SetFocusedViewWithReason(view, kReasonFocusRestore);
385 } else {
386 // This usually just sets the focus if this view is focusable, but
387 // let the view override RequestFocus if necessary.
388 view->RequestFocus();
390 // If it succeeded, the reason would be incorrect; set it to
391 // focus restore.
392 if (focused_view_ == view)
393 focus_change_reason_ = kReasonFocusRestore;
396 return true;
398 return false;
401 void FocusManager::SetStoredFocusView(View* focus_view) {
402 ViewStorage* view_storage = ViewStorage::GetInstance();
403 if (!view_storage) {
404 // This should never happen but bug 981648 seems to indicate it could.
405 NOTREACHED();
406 return;
409 // TODO(jcivelli): when a TabContents containing a popup is closed, the focus
410 // is stored twice causing an assert. We should find a better alternative than
411 // removing the view from the storage explicitly.
412 view_storage->RemoveView(stored_focused_view_storage_id_);
414 if (!focus_view)
415 return;
417 view_storage->StoreView(stored_focused_view_storage_id_, focus_view);
420 View* FocusManager::GetStoredFocusView() {
421 ViewStorage* view_storage = ViewStorage::GetInstance();
422 if (!view_storage) {
423 // This should never happen but bug 981648 seems to indicate it could.
424 NOTREACHED();
425 return NULL;
428 return view_storage->RetrieveView(stored_focused_view_storage_id_);
431 void FocusManager::ClearStoredFocusedView() {
432 SetStoredFocusView(NULL);
435 // Find the next (previous if reverse is true) focusable view for the specified
436 // FocusTraversable, starting at the specified view, traversing down the
437 // FocusTraversable hierarchy.
438 View* FocusManager::FindFocusableView(FocusTraversable* focus_traversable,
439 View* starting_view,
440 bool reverse) {
441 FocusTraversable* new_focus_traversable = NULL;
442 View* new_starting_view = NULL;
443 View* v = focus_traversable->GetFocusSearch()->FindNextFocusableView(
444 starting_view,
445 reverse,
446 FocusSearch::DOWN,
447 false,
448 &new_focus_traversable,
449 &new_starting_view);
451 // Let's go down the FocusTraversable tree as much as we can.
452 while (new_focus_traversable) {
453 DCHECK(!v);
454 focus_traversable = new_focus_traversable;
455 new_focus_traversable = NULL;
456 starting_view = NULL;
457 v = focus_traversable->GetFocusSearch()->FindNextFocusableView(
458 starting_view,
459 reverse,
460 FocusSearch::DOWN,
461 false,
462 &new_focus_traversable,
463 &new_starting_view);
465 return v;
468 void FocusManager::RegisterAccelerator(
469 const ui::Accelerator& accelerator,
470 ui::AcceleratorManager::HandlerPriority priority,
471 ui::AcceleratorTarget* target) {
472 accelerator_manager_->Register(accelerator, priority, target);
475 void FocusManager::UnregisterAccelerator(const ui::Accelerator& accelerator,
476 ui::AcceleratorTarget* target) {
477 accelerator_manager_->Unregister(accelerator, target);
480 void FocusManager::UnregisterAccelerators(ui::AcceleratorTarget* target) {
481 accelerator_manager_->UnregisterAll(target);
484 bool FocusManager::ProcessAccelerator(const ui::Accelerator& accelerator) {
485 if (accelerator_manager_->Process(accelerator))
486 return true;
487 if (delegate_.get())
488 return delegate_->ProcessAccelerator(accelerator);
489 return false;
492 ui::AcceleratorTarget* FocusManager::GetCurrentTargetForAccelerator(
493 const ui::Accelerator& accelerator) const {
494 ui::AcceleratorTarget* target =
495 accelerator_manager_->GetCurrentTarget(accelerator);
496 if (!target && delegate_.get())
497 target = delegate_->GetCurrentTargetForAccelerator(accelerator);
498 return target;
501 bool FocusManager::HasPriorityHandler(
502 const ui::Accelerator& accelerator) const {
503 return accelerator_manager_->HasPriorityHandler(accelerator);
506 // static
507 bool FocusManager::IsTabTraversalKeyEvent(const ui::KeyEvent& key_event) {
508 return key_event.key_code() == ui::VKEY_TAB && !key_event.IsControlDown();
511 void FocusManager::ViewRemoved(View* removed) {
512 // If the view being removed contains (or is) the focused view,
513 // clear the focus. However, it's not safe to call ClearFocus()
514 // (and in turn ClearNativeFocus()) here because ViewRemoved() can
515 // be called while the top level widget is being destroyed.
516 if (focused_view_ && removed->Contains(focused_view_))
517 SetFocusedView(NULL);
520 void FocusManager::AddFocusChangeListener(FocusChangeListener* listener) {
521 focus_change_listeners_.AddObserver(listener);
524 void FocusManager::RemoveFocusChangeListener(FocusChangeListener* listener) {
525 focus_change_listeners_.RemoveObserver(listener);
528 bool FocusManager::ProcessArrowKeyTraversal(const ui::KeyEvent& event) {
529 if (event.IsShiftDown() || event.IsControlDown() || event.IsAltDown())
530 return false;
532 const int key_code = event.key_code();
533 if (key_code == ui::VKEY_LEFT || key_code == ui::VKEY_UP) {
534 AdvanceFocus(true);
535 return true;
537 if (key_code == ui::VKEY_RIGHT || key_code == ui::VKEY_DOWN) {
538 AdvanceFocus(false);
539 return true;
542 return false;
545 } // namespace views