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"
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/base/ime/text_input_focus_manager.h"
17 #include "ui/base/ui_base_switches_util.h"
18 #include "ui/events/event.h"
19 #include "ui/events/keycodes/keyboard_codes.h"
20 #include "ui/views/focus/focus_manager_delegate.h"
21 #include "ui/views/focus/focus_search.h"
22 #include "ui/views/focus/view_storage.h"
23 #include "ui/views/focus/widget_focus_manager.h"
24 #include "ui/views/view.h"
25 #include "ui/views/widget/root_view.h"
26 #include "ui/views/widget/widget.h"
27 #include "ui/views/widget/widget_delegate.h"
35 bool FocusManager::shortcut_handling_suspended_
= false;
36 bool FocusManager::arrow_key_traversal_enabled_
= false;
38 FocusManager::FocusManager(Widget
* widget
, FocusManagerDelegate
* delegate
)
42 accelerator_manager_(new ui::AcceleratorManager
),
43 focus_change_reason_(kReasonDirectFocusChange
),
44 is_changing_focus_(false) {
46 stored_focused_view_storage_id_
=
47 ViewStorage::GetInstance()->CreateStorageID();
50 FocusManager::~FocusManager() {
53 bool FocusManager::OnKeyEvent(const ui::KeyEvent
& event
) {
54 const int key_code
= event
.key_code();
56 if (event
.type() != ui::ET_KEY_PRESSED
&& event
.type() != ui::ET_KEY_RELEASED
)
59 if (shortcut_handling_suspended())
62 int modifiers
= ui::EF_NONE
;
63 if (event
.IsShiftDown())
64 modifiers
|= ui::EF_SHIFT_DOWN
;
65 if (event
.IsControlDown())
66 modifiers
|= ui::EF_CONTROL_DOWN
;
67 if (event
.IsAltDown())
68 modifiers
|= ui::EF_ALT_DOWN
;
69 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
70 if (event
.IsCommandDown())
71 modifiers
|= ui::EF_COMMAND_DOWN
;
73 ui::Accelerator
accelerator(event
.key_code(), modifiers
);
74 accelerator
.set_type(event
.type());
75 accelerator
.set_is_repeat(event
.IsRepeat());
77 if (event
.type() == ui::ET_KEY_PRESSED
) {
78 // If the focused view wants to process the key event as is, let it be.
79 if (focused_view_
&& focused_view_
->SkipDefaultKeyEventProcessing(event
) &&
80 !accelerator_manager_
->HasPriorityHandler(accelerator
))
83 // Intercept Tab related messages for focus traversal.
84 // Note that we don't do focus traversal if the root window is not part of
85 // the active window hierarchy as this would mean we have no focused view
86 // and would focus the first focusable view.
87 if (IsTabTraversalKeyEvent(event
)) {
88 AdvanceFocus(event
.IsShiftDown());
92 if (arrow_key_traversal_enabled_
&& ProcessArrowKeyTraversal(event
))
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
);
101 focused_view_
->parent()->GetViewsInGroup(focused_view_
->GetGroup(),
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;
109 index
= static_cast<int>(views
.size()) - 1;
110 } else if (index
>= static_cast<int>(views
.size())) {
113 SetFocusedViewWithReason(views
[index
], kReasonFocusTraversal
);
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.
129 void FocusManager::ValidateFocusedView() {
130 if (focused_view_
&& !ContainsView(focused_view_
))
134 // Tests whether a view is valid, whether it still belongs to the window
135 // hierarchy of the FocusManager.
136 bool FocusManager::ContainsView(View
* view
) {
137 Widget
* widget
= view
->GetWidget();
138 return widget
? widget
->GetFocusManager() == this : false;
141 void 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.
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
);
158 void FocusManager::ClearNativeFocus() {
159 // Keep the top root window focused so we get keyboard events.
160 widget_
->ClearNativeFocus();
163 bool 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
);
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());
175 // Initialize |index| to an appropriate starting index if nothing is
176 // focused initially.
177 int index
= direction
== kBackward
? 0 : count
- 1;
179 // Check to see if a pane already has focus and update the index accordingly.
180 const views::View
* focused_view
= GetFocusedView();
182 for (int i
= 0; i
< count
; i
++) {
183 if (panes
[i
] && panes
[i
]->Contains(focused_view
)) {
191 int start_index
= index
;
193 if (direction
== kBackward
)
198 if (wrap
== kNoWrap
&& (index
>= count
|| index
< 0))
200 index
= (index
+ count
) % count
;
202 // Ensure that we don't loop more than once.
203 if (index
== start_index
)
206 views::View
* pane
= panes
[index
];
209 if (!pane
->visible())
212 pane
->RequestFocus();
213 focused_view
= GetFocusedView();
214 if (pane
== focused_view
|| pane
->Contains(focused_view
))
221 View
* FocusManager::GetNextFocusableView(View
* original_starting_view
,
222 Widget
* starting_widget
,
225 FocusTraversable
* focus_traversable
= NULL
;
227 // Let's revalidate the focused view.
228 ValidateFocusedView();
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
;
242 pane_search
= pane_search
->parent();
245 if (!focus_traversable
) {
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();
251 // Otherwise default to the root view.
252 if (!focus_traversable
) {
254 original_starting_view
->GetWidget()->GetFocusTraversable();
255 starting_view
= original_starting_view
;
258 // When you are going back, starting view's FocusTraversable
259 // should not be used.
261 original_starting_view
->GetWidget()->GetFocusTraversable();
262 starting_view
= original_starting_view
;
266 Widget
* widget
= starting_widget
? starting_widget
: widget_
;
267 focus_traversable
= widget
->GetFocusTraversable();
270 // Traverse the FocusTraversable tree down to find the focusable view.
271 View
* v
= FindFocusableView(focus_traversable
, starting_view
, reverse
);
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
);
288 if (new_focus_traversable
) {
291 // There is a FocusTraversable, traverse it down.
292 v
= FindFocusableView(new_focus_traversable
, NULL
, reverse
);
298 starting_view
= focus_traversable
->GetFocusTraversableParentView();
299 parent_focus_traversable
=
300 parent_focus_traversable
->GetFocusTraversableParent();
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())
313 return GetNextFocusableView(NULL
, widget
, reverse
, true);
319 void FocusManager::SetFocusedViewWithReason(
320 View
* view
, FocusChangeReason reason
) {
321 if (focused_view_
== view
) {
322 // In the case that the widget lost the focus and gained it back without
323 // changing the focused view, we have to make the text input client focused.
324 // TODO(yukishiino): Remove this hack once we fix http://crbug.com/383236
325 FocusTextInputClient(focused_view_
);
329 base::AutoReset
<bool> auto_changing_focus(&is_changing_focus_
, true);
330 // Update the reason for the focus change (since this is checked by
331 // some listeners), then notify all listeners.
332 focus_change_reason_
= reason
;
333 FOR_EACH_OBSERVER(FocusChangeListener
, focus_change_listeners_
,
334 OnWillChangeFocus(focused_view_
, view
));
336 View
* old_focused_view
= focused_view_
;
337 focused_view_
= view
;
338 if (old_focused_view
) {
339 old_focused_view
->Blur();
340 BlurTextInputClient(old_focused_view
);
342 // Also make |focused_view_| the stored focus view. This way the stored focus
343 // view is remembered if focus changes are requested prior to a show or while
345 SetStoredFocusView(focused_view_
);
347 FocusTextInputClient(focused_view_
);
348 focused_view_
->Focus();
351 FOR_EACH_OBSERVER(FocusChangeListener
, focus_change_listeners_
,
352 OnDidChangeFocus(old_focused_view
, focused_view_
));
355 void FocusManager::ClearFocus() {
356 // SetFocusedView(NULL) is going to clear out the stored view to. We need to
357 // persist it in this case.
358 views::View
* focused_view
= GetStoredFocusView();
359 SetFocusedView(NULL
);
361 SetStoredFocusView(focused_view
);
364 void FocusManager::AdvanceFocusIfNecessary() {
365 // If widget is inactive, there is no focused view to check. The stored view
366 // will also be checked for focusability when it is being restored.
367 if (!widget_
->IsActive())
370 // If widget is active and focused view is not focusable, advance focus or,
371 // if not possible, clear focus.
372 if (focused_view_
&& !focused_view_
->IsAccessibilityFocusable()) {
374 if (focused_view_
&& !focused_view_
->IsAccessibilityFocusable())
379 void FocusManager::StoreFocusedView(bool clear_native_focus
) {
380 View
* focused_view
= focused_view_
;
381 // Don't do anything if no focused view. Storing the view (which is NULL), in
382 // this case, would clobber the view that was previously saved.
386 View
* v
= focused_view_
;
388 if (clear_native_focus
) {
389 // Temporarily disable notification. ClearFocus() will set the focus to the
390 // main browser window. This extra focus bounce which happens during
391 // deactivation can confuse registered WidgetFocusListeners, as the focus
392 // is not changing due to a user-initiated event.
393 AutoNativeNotificationDisabler local_notification_disabler
;
394 // ClearFocus() also stores the focused view.
397 SetFocusedView(NULL
);
398 SetStoredFocusView(focused_view
);
402 v
->SchedulePaint(); // Remove focus border.
405 bool FocusManager::RestoreFocusedView() {
406 View
* view
= GetStoredFocusView();
408 if (ContainsView(view
)) {
409 if (!view
->IsFocusable() && view
->IsAccessibilityFocusable()) {
410 // RequestFocus would fail, but we want to restore focus to controls
411 // that had focus in accessibility mode.
412 SetFocusedViewWithReason(view
, kReasonFocusRestore
);
414 // This usually just sets the focus if this view is focusable, but
415 // let the view override RequestFocus if necessary.
416 view
->RequestFocus();
418 // If it succeeded, the reason would be incorrect; set it to
420 if (focused_view_
== view
)
421 focus_change_reason_
= kReasonFocusRestore
;
429 void FocusManager::SetStoredFocusView(View
* focus_view
) {
430 ViewStorage
* view_storage
= ViewStorage::GetInstance();
432 // This should never happen but bug 981648 seems to indicate it could.
437 // TODO(jcivelli): when a TabContents containing a popup is closed, the focus
438 // is stored twice causing an assert. We should find a better alternative than
439 // removing the view from the storage explicitly.
440 view_storage
->RemoveView(stored_focused_view_storage_id_
);
445 view_storage
->StoreView(stored_focused_view_storage_id_
, focus_view
);
448 View
* FocusManager::GetStoredFocusView() {
449 ViewStorage
* view_storage
= ViewStorage::GetInstance();
451 // This should never happen but bug 981648 seems to indicate it could.
456 return view_storage
->RetrieveView(stored_focused_view_storage_id_
);
459 void FocusManager::ClearStoredFocusedView() {
460 SetStoredFocusView(NULL
);
463 void FocusManager::OnTextInputClientChanged(View
* view
) {
464 if (view
== focused_view_
)
465 FocusTextInputClient(view
);
468 void FocusManager::FocusTextInputClient(View
* view
) {
469 if (!switches::IsTextInputFocusManagerEnabled())
472 // If the widget is not active, do not steal the text input focus.
473 if (!widget_
->IsActive())
476 ui::TextInputClient
* text_input_client
=
477 view
? view
->GetTextInputClient() : NULL
;
478 ui::TextInputFocusManager::GetInstance()->
479 FocusTextInputClient(text_input_client
);
480 ui::InputMethod
* input_method
= widget_
->GetHostInputMethod();
482 input_method
->OnTextInputTypeChanged(text_input_client
);
483 input_method
->OnCaretBoundsChanged(text_input_client
);
487 void FocusManager::BlurTextInputClient(View
* view
) {
488 if (!switches::IsTextInputFocusManagerEnabled())
491 ui::TextInputClient
* text_input_client
=
492 view
? view
->GetTextInputClient() : NULL
;
493 if (text_input_client
&& text_input_client
->HasCompositionText()) {
494 text_input_client
->ConfirmCompositionText();
495 ui::InputMethod
* input_method
= widget_
->GetHostInputMethod();
496 if (input_method
&& input_method
->GetTextInputClient() == text_input_client
)
497 input_method
->CancelComposition(text_input_client
);
499 ui::TextInputFocusManager::GetInstance()->
500 BlurTextInputClient(text_input_client
);
503 // Find the next (previous if reverse is true) focusable view for the specified
504 // FocusTraversable, starting at the specified view, traversing down the
505 // FocusTraversable hierarchy.
506 View
* FocusManager::FindFocusableView(FocusTraversable
* focus_traversable
,
509 FocusTraversable
* new_focus_traversable
= NULL
;
510 View
* new_starting_view
= NULL
;
511 View
* v
= focus_traversable
->GetFocusSearch()->FindNextFocusableView(
516 &new_focus_traversable
,
519 // Let's go down the FocusTraversable tree as much as we can.
520 while (new_focus_traversable
) {
522 focus_traversable
= new_focus_traversable
;
523 new_focus_traversable
= NULL
;
524 starting_view
= NULL
;
525 v
= focus_traversable
->GetFocusSearch()->FindNextFocusableView(
530 &new_focus_traversable
,
536 void FocusManager::RegisterAccelerator(
537 const ui::Accelerator
& accelerator
,
538 ui::AcceleratorManager::HandlerPriority priority
,
539 ui::AcceleratorTarget
* target
) {
540 accelerator_manager_
->Register(accelerator
, priority
, target
);
543 void FocusManager::UnregisterAccelerator(const ui::Accelerator
& accelerator
,
544 ui::AcceleratorTarget
* target
) {
545 accelerator_manager_
->Unregister(accelerator
, target
);
548 void FocusManager::UnregisterAccelerators(ui::AcceleratorTarget
* target
) {
549 accelerator_manager_
->UnregisterAll(target
);
552 bool FocusManager::ProcessAccelerator(const ui::Accelerator
& accelerator
) {
553 if (accelerator_manager_
->Process(accelerator
))
556 return delegate_
->ProcessAccelerator(accelerator
);
560 ui::AcceleratorTarget
* FocusManager::GetCurrentTargetForAccelerator(
561 const ui::Accelerator
& accelerator
) const {
562 ui::AcceleratorTarget
* target
=
563 accelerator_manager_
->GetCurrentTarget(accelerator
);
564 if (!target
&& delegate_
.get())
565 target
= delegate_
->GetCurrentTargetForAccelerator(accelerator
);
569 bool FocusManager::HasPriorityHandler(
570 const ui::Accelerator
& accelerator
) const {
571 return accelerator_manager_
->HasPriorityHandler(accelerator
);
575 bool FocusManager::IsTabTraversalKeyEvent(const ui::KeyEvent
& key_event
) {
576 return key_event
.key_code() == ui::VKEY_TAB
&& !key_event
.IsControlDown();
579 void FocusManager::ViewRemoved(View
* removed
) {
580 // If the view being removed contains (or is) the focused view,
581 // clear the focus. However, it's not safe to call ClearFocus()
582 // (and in turn ClearNativeFocus()) here because ViewRemoved() can
583 // be called while the top level widget is being destroyed.
584 if (focused_view_
&& removed
->Contains(focused_view_
))
585 SetFocusedView(NULL
);
588 void FocusManager::AddFocusChangeListener(FocusChangeListener
* listener
) {
589 focus_change_listeners_
.AddObserver(listener
);
592 void FocusManager::RemoveFocusChangeListener(FocusChangeListener
* listener
) {
593 focus_change_listeners_
.RemoveObserver(listener
);
596 bool FocusManager::ProcessArrowKeyTraversal(const ui::KeyEvent
& event
) {
597 if (event
.IsShiftDown() || event
.IsControlDown() || event
.IsAltDown())
600 const int key_code
= event
.key_code();
601 if (key_code
== ui::VKEY_LEFT
|| key_code
== ui::VKEY_UP
) {
605 if (key_code
== ui::VKEY_RIGHT
|| key_code
== ui::VKEY_DOWN
) {