Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(73)

Side by Side Diff: views/focus/focus_manager.cc

Issue 8588064: views: Move bubble, events, focus and layout to ui/views/. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « views/focus/focus_manager.h ('k') | views/focus/focus_manager_factory.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2011 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 "views/focus/focus_manager.h"
6
7 #include <algorithm>
8
9 #include "base/auto_reset.h"
10 #include "base/logging.h"
11 #include "build/build_config.h"
12 #include "ui/base/accelerator_manager.h"
13 #include "ui/base/keycodes/keyboard_codes.h"
14 #include "ui/base/models/accelerator.h"
15 #include "views/focus/focus_search.h"
16 #include "views/focus/view_storage.h"
17 #include "views/focus/widget_focus_manager.h"
18 #include "views/view.h"
19 #include "views/widget/root_view.h"
20 #include "views/widget/widget.h"
21
22 namespace views {
23
24 FocusManager::FocusManager(Widget* widget)
25 : widget_(widget),
26 focused_view_(NULL),
27 accelerator_manager_(new ui::AcceleratorManager),
28 focus_change_reason_(kReasonDirectFocusChange),
29 is_changing_focus_(false) {
30 DCHECK(widget_);
31 stored_focused_view_storage_id_ =
32 ViewStorage::GetInstance()->CreateStorageID();
33 }
34
35 FocusManager::~FocusManager() {
36 }
37
38 bool FocusManager::OnKeyEvent(const KeyEvent& event) {
39 #if defined(OS_WIN)
40 // If the focused view wants to process the key event as is, let it be.
41 // On Linux we always dispatch key events to the focused view first, so
42 // we should not do this check here. See also NativeWidgetGtk::OnKeyEvent().
43 if (focused_view_ && focused_view_->SkipDefaultKeyEventProcessing(event))
44 return true;
45 #endif
46
47 // Intercept Tab related messages for focus traversal.
48 // Note that we don't do focus traversal if the root window is not part of the
49 // active window hierarchy as this would mean we have no focused view and
50 // would focus the first focusable view.
51 #if defined(OS_WIN) && !defined(USE_AURA)
52 HWND top_window = widget_->GetNativeView();
53 HWND active_window = ::GetActiveWindow();
54 if ((active_window == top_window || ::IsChild(active_window, top_window)) &&
55 IsTabTraversalKeyEvent(event)) {
56 AdvanceFocus(event.IsShiftDown());
57 return false;
58 }
59 #else
60 if (IsTabTraversalKeyEvent(event)) {
61 AdvanceFocus(event.IsShiftDown());
62 return false;
63 }
64 #endif
65
66 // Intercept arrow key messages to switch between grouped views.
67 ui::KeyboardCode key_code = event.key_code();
68 if (focused_view_ && focused_view_->GetGroup() != -1 &&
69 (key_code == ui::VKEY_UP || key_code == ui::VKEY_DOWN ||
70 key_code == ui::VKEY_LEFT || key_code == ui::VKEY_RIGHT)) {
71 bool next = (key_code == ui::VKEY_RIGHT || key_code == ui::VKEY_DOWN);
72 View::Views views;
73 focused_view_->parent()->GetViewsInGroup(focused_view_->GetGroup(), &views);
74 View::Views::const_iterator i(
75 std::find(views.begin(), views.end(), focused_view_));
76 DCHECK(i != views.end());
77 int index = static_cast<int>(i - views.begin());
78 index += next ? 1 : -1;
79 if (index < 0) {
80 index = static_cast<int>(views.size()) - 1;
81 } else if (index >= static_cast<int>(views.size())) {
82 index = 0;
83 }
84 SetFocusedViewWithReason(views[index], kReasonFocusTraversal);
85 return false;
86 }
87
88 // Process keyboard accelerators.
89 // If the key combination matches an accelerator, the accelerator is
90 // triggered, otherwise the key event is processed as usual.
91 ui::Accelerator accelerator(event.key_code(),
92 event.IsShiftDown(),
93 event.IsControlDown(),
94 event.IsAltDown());
95 if (ProcessAccelerator(accelerator)) {
96 // If a shortcut was activated for this keydown message, do not propagate
97 // the event further.
98 return false;
99 }
100 return true;
101 }
102
103 void FocusManager::ValidateFocusedView() {
104 if (focused_view_) {
105 if (!ContainsView(focused_view_))
106 ClearFocus();
107 }
108 }
109
110 // Tests whether a view is valid, whether it still belongs to the window
111 // hierarchy of the FocusManager.
112 bool FocusManager::ContainsView(View* view) {
113 Widget* widget = view->GetWidget();
114 return widget ? widget->GetFocusManager() == this : false;
115 }
116
117 void FocusManager::AdvanceFocus(bool reverse) {
118 View* v = GetNextFocusableView(focused_view_, reverse, false);
119 // Note: Do not skip this next block when v == focused_view_. If the user
120 // tabs past the last focusable element in a webpage, we'll get here, and if
121 // the TabContentsContainerView is the only focusable view (possible in
122 // fullscreen mode), we need to run this block in order to cycle around to the
123 // first element on the page.
124 if (v) {
125 v->AboutToRequestFocusFromTabTraversal(reverse);
126 SetFocusedViewWithReason(v, kReasonFocusTraversal);
127 }
128 }
129
130 void FocusManager::ClearNativeFocus() {
131 // Keep the top root window focused so we get keyboard events.
132 widget_->ClearNativeFocus();
133 }
134
135 View* FocusManager::GetNextFocusableView(View* original_starting_view,
136 bool reverse,
137 bool dont_loop) {
138 FocusTraversable* focus_traversable = NULL;
139
140 // Let's revalidate the focused view.
141 ValidateFocusedView();
142
143 View* starting_view = NULL;
144 if (original_starting_view) {
145 // Search up the containment hierarchy to see if a view is acting as
146 // a pane, and wants to implement its own focus traversable to keep
147 // the focus trapped within that pane.
148 View* pane_search = original_starting_view;
149 while (pane_search) {
150 focus_traversable = pane_search->GetPaneFocusTraversable();
151 if (focus_traversable) {
152 starting_view = original_starting_view;
153 break;
154 }
155 pane_search = pane_search->parent();
156 }
157
158 if (!focus_traversable) {
159 if (!reverse) {
160 // If the starting view has a focus traversable, use it.
161 // This is the case with NativeWidgetWins for example.
162 focus_traversable = original_starting_view->GetFocusTraversable();
163
164 // Otherwise default to the root view.
165 if (!focus_traversable) {
166 focus_traversable =
167 original_starting_view->GetWidget()->GetFocusTraversable();
168 starting_view = original_starting_view;
169 }
170 } else {
171 // When you are going back, starting view's FocusTraversable
172 // should not be used.
173 focus_traversable =
174 original_starting_view->GetWidget()->GetFocusTraversable();
175 starting_view = original_starting_view;
176 }
177 }
178 } else {
179 focus_traversable = widget_->GetFocusTraversable();
180 }
181
182 // Traverse the FocusTraversable tree down to find the focusable view.
183 View* v = FindFocusableView(focus_traversable, starting_view, reverse);
184 if (v) {
185 return v;
186 } else {
187 // Let's go up in the FocusTraversable tree.
188 FocusTraversable* parent_focus_traversable =
189 focus_traversable->GetFocusTraversableParent();
190 starting_view = focus_traversable->GetFocusTraversableParentView();
191 while (parent_focus_traversable) {
192 FocusTraversable* new_focus_traversable = NULL;
193 View* new_starting_view = NULL;
194 // When we are going backward, the parent view might gain the next focus.
195 bool check_starting_view = reverse;
196 v = parent_focus_traversable->GetFocusSearch()->FindNextFocusableView(
197 starting_view, reverse, FocusSearch::UP,
198 check_starting_view, &new_focus_traversable, &new_starting_view);
199
200 if (new_focus_traversable) {
201 DCHECK(!v);
202
203 // There is a FocusTraversable, traverse it down.
204 v = FindFocusableView(new_focus_traversable, NULL, reverse);
205 }
206
207 if (v)
208 return v;
209
210 starting_view = focus_traversable->GetFocusTraversableParentView();
211 parent_focus_traversable =
212 parent_focus_traversable->GetFocusTraversableParent();
213 }
214
215 // If we get here, we have reached the end of the focus hierarchy, let's
216 // loop. Make sure there was at least a view to start with, to prevent
217 // infinitely looping in empty windows.
218 if (!dont_loop && original_starting_view) {
219 // Easy, just clear the selection and press tab again.
220 // By calling with NULL as the starting view, we'll start from the
221 // top_root_view.
222 return GetNextFocusableView(NULL, reverse, true);
223 }
224 }
225 return NULL;
226 }
227
228 void FocusManager::SetFocusedViewWithReason(
229 View* view, FocusChangeReason reason) {
230 if (focused_view_ == view)
231 return;
232
233 AutoReset<bool> auto_changing_focus(&is_changing_focus_, true);
234 // Update the reason for the focus change (since this is checked by
235 // some listeners), then notify all listeners.
236 focus_change_reason_ = reason;
237 FOR_EACH_OBSERVER(FocusChangeListener, focus_change_listeners_,
238 OnWillChangeFocus(focused_view_, view));
239
240 View* old_focused_view = focused_view_;
241 focused_view_ = view;
242 if (old_focused_view)
243 old_focused_view->Blur();
244 if (focused_view_)
245 focused_view_->Focus();
246
247 FOR_EACH_OBSERVER(FocusChangeListener, focus_change_listeners_,
248 OnDidChangeFocus(old_focused_view, focused_view_));
249 }
250
251 void FocusManager::ClearFocus() {
252 SetFocusedView(NULL);
253 ClearNativeFocus();
254 }
255
256 void FocusManager::StoreFocusedView() {
257 ViewStorage* view_storage = ViewStorage::GetInstance();
258 if (!view_storage) {
259 // This should never happen but bug 981648 seems to indicate it could.
260 NOTREACHED();
261 return;
262 }
263
264 // TODO (jcampan): when a TabContents containing a popup is closed, the focus
265 // is stored twice causing an assert. We should find a better alternative than
266 // removing the view from the storage explicitly.
267 view_storage->RemoveView(stored_focused_view_storage_id_);
268
269 if (!focused_view_)
270 return;
271
272 view_storage->StoreView(stored_focused_view_storage_id_, focused_view_);
273
274 View* v = focused_view_;
275
276 {
277 // Temporarily disable notification. ClearFocus() will set the focus to the
278 // main browser window. This extra focus bounce which happens during
279 // deactivation can confuse registered WidgetFocusListeners, as the focus
280 // is not changing due to a user-initiated event.
281 AutoNativeNotificationDisabler local_notification_disabler;
282 ClearFocus();
283 }
284
285 if (v)
286 v->SchedulePaint(); // Remove focus border.
287 }
288
289 void FocusManager::RestoreFocusedView() {
290 ViewStorage* view_storage = ViewStorage::GetInstance();
291 if (!view_storage) {
292 // This should never happen but bug 981648 seems to indicate it could.
293 NOTREACHED();
294 return;
295 }
296
297 View* view = view_storage->RetrieveView(stored_focused_view_storage_id_);
298 if (view) {
299 if (ContainsView(view)) {
300 if (!view->IsFocusableInRootView() &&
301 view->IsAccessibilityFocusableInRootView()) {
302 // RequestFocus would fail, but we want to restore focus to controls
303 // that had focus in accessibility mode.
304 SetFocusedViewWithReason(view, kReasonFocusRestore);
305 } else {
306 // This usually just sets the focus if this view is focusable, but
307 // let the view override RequestFocus if necessary.
308 view->RequestFocus();
309
310 // If it succeeded, the reason would be incorrect; set it to
311 // focus restore.
312 if (focused_view_ == view)
313 focus_change_reason_ = kReasonFocusRestore;
314 }
315 }
316 }
317 }
318
319 void FocusManager::ClearStoredFocusedView() {
320 ViewStorage* view_storage = ViewStorage::GetInstance();
321 if (!view_storage) {
322 // This should never happen but bug 981648 seems to indicate it could.
323 NOTREACHED();
324 return;
325 }
326 view_storage->RemoveView(stored_focused_view_storage_id_);
327 }
328
329 // Find the next (previous if reverse is true) focusable view for the specified
330 // FocusTraversable, starting at the specified view, traversing down the
331 // FocusTraversable hierarchy.
332 View* FocusManager::FindFocusableView(FocusTraversable* focus_traversable,
333 View* starting_view,
334 bool reverse) {
335 FocusTraversable* new_focus_traversable = NULL;
336 View* new_starting_view = NULL;
337 View* v = focus_traversable->GetFocusSearch()->FindNextFocusableView(
338 starting_view,
339 reverse,
340 FocusSearch::DOWN,
341 false,
342 &new_focus_traversable,
343 &new_starting_view);
344
345 // Let's go down the FocusTraversable tree as much as we can.
346 while (new_focus_traversable) {
347 DCHECK(!v);
348 focus_traversable = new_focus_traversable;
349 starting_view = new_starting_view;
350 new_focus_traversable = NULL;
351 starting_view = NULL;
352 v = focus_traversable->GetFocusSearch()->FindNextFocusableView(
353 starting_view,
354 reverse,
355 FocusSearch::DOWN,
356 false,
357 &new_focus_traversable,
358 &new_starting_view);
359 }
360 return v;
361 }
362
363 void FocusManager::RegisterAccelerator(
364 const ui::Accelerator& accelerator,
365 ui::AcceleratorTarget* target) {
366 accelerator_manager_->Register(accelerator, target);
367 }
368
369 void FocusManager::UnregisterAccelerator(const ui::Accelerator& accelerator,
370 ui::AcceleratorTarget* target) {
371 accelerator_manager_->Unregister(accelerator, target);
372 }
373
374 void FocusManager::UnregisterAccelerators(ui::AcceleratorTarget* target) {
375 accelerator_manager_->UnregisterAll(target);
376 }
377
378 bool FocusManager::ProcessAccelerator(const ui::Accelerator& accelerator) {
379 return accelerator_manager_->Process(accelerator);
380 }
381
382 ui::AcceleratorTarget* FocusManager::GetCurrentTargetForAccelerator(
383 const ui::Accelerator& accelerator) const {
384 return accelerator_manager_->GetCurrentTarget(accelerator);
385 }
386
387 void FocusManager::FocusNativeView(gfx::NativeView native_view) {
388 widget_->FocusNativeView(native_view);
389 }
390
391 // static
392 bool FocusManager::IsTabTraversalKeyEvent(const KeyEvent& key_event) {
393 return key_event.key_code() == ui::VKEY_TAB && !key_event.IsControlDown();
394 }
395
396 void FocusManager::ViewRemoved(View* removed) {
397 // If the view being removed contains (or is) the focused view,
398 // clear the focus. However, it's not safe to call ClearFocus()
399 // (and in turn ClearNativeFocus()) here because ViewRemoved() can
400 // be called while the top level widget is being destroyed.
401 if (focused_view_ && removed && removed->Contains(focused_view_))
402 SetFocusedView(NULL);
403 }
404
405 void FocusManager::AddFocusChangeListener(FocusChangeListener* listener) {
406 focus_change_listeners_.AddObserver(listener);
407 }
408
409 void FocusManager::RemoveFocusChangeListener(FocusChangeListener* listener) {
410 focus_change_listeners_.RemoveObserver(listener);
411 }
412
413 } // namespace views
OLDNEW
« no previous file with comments | « views/focus/focus_manager.h ('k') | views/focus/focus_manager_factory.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698