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