| 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 |