OLD | NEW |
| (Empty) |
1 // Copyright (c) 2009 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 "base/logging.h" | |
6 #include "chrome/browser/view_ids.h" | |
7 #include "chrome/browser/views/frame/browser_view.h" | |
8 #include "chrome/browser/views/accessible_toolbar_view.h" | |
9 #include "views/controls/button/menu_button.h" | |
10 #include "views/focus/view_storage.h" | |
11 #include "views/widget/root_view.h" | |
12 #include "views/widget/tooltip_manager.h" | |
13 #include "views/widget/widget.h" | |
14 | |
15 AccessibleToolbarView::AccessibleToolbarView() | |
16 : selected_focused_view_(NULL), | |
17 last_focused_view_storage_id_( | |
18 views::ViewStorage::GetSharedInstance()->CreateStorageID()) { | |
19 } | |
20 | |
21 AccessibleToolbarView::~AccessibleToolbarView() { | |
22 } | |
23 | |
24 void AccessibleToolbarView::InitiateTraversal() { | |
25 // We only traverse if accessibility is active. | |
26 if (selected_focused_view_ != NULL) | |
27 return; | |
28 | |
29 // Save the last focused view so that when the user presses ESC, it will | |
30 // return back to the last focus. | |
31 views::ViewStorage* view_storage = views::ViewStorage::GetSharedInstance(); | |
32 view_storage->StoreView(last_focused_view_storage_id_, | |
33 GetRootView()->GetFocusedView()); | |
34 | |
35 // Request focus to the toolbar. | |
36 RequestFocus(); | |
37 } | |
38 | |
39 int AccessibleToolbarView::GetNextAccessibleViewIndex(int view_index, | |
40 bool forward) { | |
41 int modifier = forward ? 1 : -1; | |
42 int current_view_index = view_index + modifier; | |
43 | |
44 while ((current_view_index >= 0) && | |
45 (current_view_index < GetChildViewCount())) { | |
46 // Try to find the next available view that can be interacted with. That | |
47 // view must be enabled, visible, and traversable. | |
48 views::View* current_view = GetChildViewAt(current_view_index); | |
49 if (current_view->IsEnabled() && current_view->IsVisible() && | |
50 IsAccessibleViewTraversable(current_view)) { | |
51 return current_view_index; | |
52 } | |
53 current_view_index += modifier; | |
54 } | |
55 | |
56 // No button is available in the specified direction. | |
57 return -1; | |
58 } | |
59 | |
60 bool AccessibleToolbarView::IsAccessibleViewTraversable(views::View* view) { | |
61 return true; | |
62 } | |
63 | |
64 void AccessibleToolbarView::DidGainFocus() { | |
65 // Check to see if the accessible focus should be restored to previously | |
66 // focused button. The must be enabled and visible in the toolbar. | |
67 if (!selected_focused_view_ || | |
68 !selected_focused_view_->IsEnabled() || | |
69 !selected_focused_view_->IsVisible()) { | |
70 // Find first accessible child (-1 to start search at parent). | |
71 int first_acc_child = GetNextAccessibleViewIndex(-1, true); | |
72 | |
73 // No buttons enabled or visible. | |
74 if (first_acc_child == -1) | |
75 return; | |
76 | |
77 selected_focused_view_ = GetChildViewAt(first_acc_child); | |
78 } | |
79 | |
80 // Set the focus to the current accessible view. | |
81 SetFocusToAccessibleView(); | |
82 } | |
83 | |
84 void AccessibleToolbarView::WillLoseFocus() { | |
85 // Any tooltips that are active should be hidden when toolbar loses focus. | |
86 if (GetWidget() && GetWidget()->GetTooltipManager()) | |
87 GetWidget()->GetTooltipManager()->HideKeyboardTooltip(); | |
88 | |
89 // Removes the child accessibility view's focus and the view from the | |
90 // ViewStorage, when toolbar loses focus. | |
91 if (selected_focused_view_) { | |
92 selected_focused_view_->SetHotTracked(false); | |
93 selected_focused_view_ = NULL; | |
94 views::ViewStorage* view_storage = views::ViewStorage::GetSharedInstance(); | |
95 view_storage->RemoveView(last_focused_view_storage_id_); | |
96 } | |
97 } | |
98 | |
99 void AccessibleToolbarView::ShowContextMenu(int x, int y, | |
100 bool is_mouse_gesture) { | |
101 if (selected_focused_view_) | |
102 selected_focused_view_->ShowContextMenu(x, y, is_mouse_gesture); | |
103 } | |
104 | |
105 void AccessibleToolbarView::RequestFocus() { | |
106 // When the toolbar needs to request focus, the default implementation of | |
107 // View::RequestFocus requires the View to be focusable. Since ToolbarView is | |
108 // not technically focused, we need to temporarily set and remove focus so | |
109 // that it can focus back to its focused state. |selected_focused_view_| is | |
110 // not necessarily set since it can be null if this view has already lost | |
111 // focus, such as traversing through the context menu. | |
112 SetFocusable(true); | |
113 View::RequestFocus(); | |
114 SetFocusable(false); | |
115 } | |
116 | |
117 bool AccessibleToolbarView::OnKeyPressed(const views::KeyEvent& e) { | |
118 // Paranoia check, button should be initialized upon toolbar gaining focus. | |
119 if (!selected_focused_view_) | |
120 return View::OnKeyPressed(e); | |
121 | |
122 int focused_view = GetChildIndex(selected_focused_view_); | |
123 int next_view = focused_view; | |
124 | |
125 switch (e.GetKeyCode()) { | |
126 case base::VKEY_LEFT: | |
127 next_view = GetNextAccessibleViewIndex(focused_view, false); | |
128 break; | |
129 case base::VKEY_RIGHT: | |
130 next_view = GetNextAccessibleViewIndex(focused_view, true); | |
131 break; | |
132 case base::VKEY_DOWN: | |
133 case base::VKEY_RETURN: | |
134 if (selected_focused_view_->GetClassName() == | |
135 views::MenuButton::kViewClassName) { | |
136 // If a menu button is activated and its menu is displayed, then the | |
137 // active tooltip should be hidden. | |
138 if (GetWidget()->GetTooltipManager()) | |
139 GetWidget()->GetTooltipManager()->HideKeyboardTooltip(); | |
140 | |
141 // Safe to cast, given to above check. | |
142 static_cast<views::MenuButton*>(selected_focused_view_)->Activate(); | |
143 | |
144 // If activate did not trigger a focus change, the menu button should | |
145 // remain hot tracked since the view is still focused. | |
146 if (selected_focused_view_) { | |
147 // Re-enable hot-tracking, as Activate() will disable it. | |
148 selected_focused_view_->SetHotTracked(true); | |
149 } | |
150 return true; | |
151 } | |
152 default: | |
153 // If key is not handled explicitly, pass it on to view. | |
154 return selected_focused_view_->OnKeyPressed(e); | |
155 } | |
156 | |
157 // No buttons enabled, visible, or focus hasn't moved. | |
158 if (next_view == -1) | |
159 return false; | |
160 | |
161 // Remove hot-tracking from old focused button. | |
162 selected_focused_view_->SetHotTracked(false); | |
163 | |
164 // All is well, update the focused child member variable. | |
165 selected_focused_view_ = GetChildViewAt(next_view); | |
166 | |
167 // Set the focus to the current accessible view. | |
168 SetFocusToAccessibleView(); | |
169 return true; | |
170 } | |
171 | |
172 bool AccessibleToolbarView::OnKeyReleased(const views::KeyEvent& e) { | |
173 // Paranoia check, button should be initialized upon toolbar gaining focus. | |
174 if (!selected_focused_view_) | |
175 return false; | |
176 | |
177 // Have keys be handled by the views themselves. | |
178 return selected_focused_view_->OnKeyReleased(e); | |
179 } | |
180 | |
181 bool AccessibleToolbarView::SkipDefaultKeyEventProcessing( | |
182 const views::KeyEvent& e) { | |
183 // Accessibility focus must be present in order to handle ESC and TAB related | |
184 // key events. | |
185 if (!selected_focused_view_) | |
186 return false; | |
187 | |
188 // The ancestor *must* be a BrowserView. | |
189 views::View* view = GetAncestorWithClassName(BrowserView::kViewClassName); | |
190 if (!view) | |
191 return false; | |
192 | |
193 // Given the check above, we can ensure its a BrowserView. | |
194 BrowserView* browser_view = static_cast<BrowserView*>(view); | |
195 | |
196 // Handle ESC and TAB events. | |
197 switch (e.GetKeyCode()) { | |
198 case base::VKEY_ESCAPE: { | |
199 // Retrieve the focused view from the storage so we can request focus back | |
200 // to it. If |focus_view| is null, we place focus on the default view. | |
201 // |selected_focused_view_| doesn't need to be reset here since it will be | |
202 // dealt within the WillLoseFocus method. | |
203 views::ViewStorage* view_storage = | |
204 views::ViewStorage::GetSharedInstance(); | |
205 views::View* focused_view = | |
206 view_storage->RetrieveView(last_focused_view_storage_id_); | |
207 if (focused_view) { | |
208 view_storage->RemoveView(last_focused_view_storage_id_); | |
209 focused_view->RequestFocus(); | |
210 } else { | |
211 browser_view->SetFocusToLocationBar(); | |
212 } | |
213 return true; | |
214 } | |
215 case base::VKEY_TAB: { | |
216 if (e.IsShiftDown()) { | |
217 browser_view->TraverseNextAccessibleToolbar(false); | |
218 } else { | |
219 browser_view->TraverseNextAccessibleToolbar(true); | |
220 } | |
221 return true; | |
222 } | |
223 default: return false; | |
224 } | |
225 } | |
226 | |
227 bool AccessibleToolbarView::GetAccessibleName(std::wstring* name) { | |
228 *name = accessible_name_; | |
229 return !accessible_name_.empty(); | |
230 } | |
231 | |
232 bool AccessibleToolbarView::GetAccessibleRole(AccessibilityTypes::Role* role) { | |
233 DCHECK(role); | |
234 | |
235 *role = AccessibilityTypes::ROLE_TOOLBAR; | |
236 return true; | |
237 } | |
238 | |
239 void AccessibleToolbarView::SetAccessibleName(const std::wstring& name) { | |
240 accessible_name_ = name; | |
241 } | |
242 | |
243 void AccessibleToolbarView::ViewHierarchyChanged(bool is_add, View* parent, | |
244 View* child) { | |
245 // When the toolbar is removed, traverse to the next accessible toolbar. | |
246 if (!is_add && parent->GetClassName() == BrowserView::kViewClassName) { | |
247 // Given the check above, we can ensure its a BrowserView. | |
248 BrowserView* browser_view = static_cast<BrowserView*>(parent); | |
249 browser_view->TraverseNextAccessibleToolbar(true); | |
250 } | |
251 } | |
252 | |
253 void AccessibleToolbarView::SetFocusToAccessibleView() { | |
254 // Hot-track new focused button. | |
255 selected_focused_view_->SetHotTracked(true); | |
256 | |
257 // Show the tooltip for the view that got the focus. | |
258 if (GetWidget()->GetTooltipManager()) { | |
259 GetWidget()->GetTooltipManager()->ShowKeyboardTooltip( | |
260 selected_focused_view_); | |
261 } | |
262 | |
263 #if defined(OS_WIN) | |
264 // Retrieve information to generate an accessible focus event. | |
265 gfx::NativeView wnd = GetWidget()->GetNativeView(); | |
266 int view_id = selected_focused_view_->GetID(); | |
267 // Notify Access Technology that there was a change in keyboard focus. | |
268 ::NotifyWinEvent(EVENT_OBJECT_FOCUS, wnd, OBJID_CLIENT, | |
269 static_cast<LONG>(view_id)); | |
270 #else | |
271 NOTIMPLEMENTED(); | |
272 #endif | |
273 } | |
OLD | NEW |