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