accessible_pane_view.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
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 "base/logging.h" 6#include "chrome/browser/ui/view_ids.h" 7#include "chrome/browser/ui/views/accessible_pane_view.h" 8#include "chrome/browser/ui/views/frame/browser_view.h" 9#include "chrome/browser/ui/views/location_bar/location_bar_view.h" 10#include "views/controls/button/menu_button.h" 11#include "views/controls/native/native_view_host.h" 12#include "views/focus/focus_search.h" 13#include "views/focus/view_storage.h" 14#include "views/widget/tooltip_manager.h" 15#include "views/widget/widget.h" 16 17AccessiblePaneView::AccessiblePaneView() 18 : pane_has_focus_(false), 19 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)), 20 focus_manager_(NULL), 21 home_key_(ui::VKEY_HOME, false, false, false), 22 end_key_(ui::VKEY_END, false, false, false), 23 escape_key_(ui::VKEY_ESCAPE, false, false, false), 24 left_key_(ui::VKEY_LEFT, false, false, false), 25 right_key_(ui::VKEY_RIGHT, false, false, false), 26 last_focused_view_storage_id_(-1) { 27 focus_search_.reset(new views::FocusSearch(this, true, true)); 28} 29 30AccessiblePaneView::~AccessiblePaneView() { 31 if (pane_has_focus_) { 32 focus_manager_->RemoveFocusChangeListener(this); 33 } 34} 35 36bool AccessiblePaneView::SetPaneFocus(int view_storage_id, 37 views::View* initial_focus) { 38 if (!IsVisible()) 39 return false; 40 41 // Save the storage id to the last focused view. This would be used to request 42 // focus to the view when the traversal is ended. 43 last_focused_view_storage_id_ = view_storage_id; 44 45 if (!focus_manager_) 46 focus_manager_ = GetFocusManager(); 47 48 // Use the provided initial focus if it's visible and enabled, otherwise 49 // use the first focusable child. 50 if (!initial_focus || 51 !Contains(initial_focus) || 52 !initial_focus->IsVisible() || 53 !initial_focus->IsEnabled()) { 54 initial_focus = GetFirstFocusableChild(); 55 } 56 57 // Return false if there are no focusable children. 58 if (!initial_focus) 59 return false; 60 61 // Set focus to the initial view. If it's a location bar, use a special 62 // method that tells it to select all, also. 63 if (initial_focus->GetClassName() == LocationBarView::kViewClassName) { 64 static_cast<LocationBarView*>(initial_focus)->FocusLocation(true); 65 } else { 66 focus_manager_->SetFocusedView(initial_focus); 67 } 68 69 // If we already have pane focus, we're done. 70 if (pane_has_focus_) 71 return true; 72 73 // Otherwise, set accelerators and start listening for focus change events. 74 pane_has_focus_ = true; 75 focus_manager_->RegisterAccelerator(home_key_, this); 76 focus_manager_->RegisterAccelerator(end_key_, this); 77 focus_manager_->RegisterAccelerator(escape_key_, this); 78 focus_manager_->RegisterAccelerator(left_key_, this); 79 focus_manager_->RegisterAccelerator(right_key_, this); 80 focus_manager_->AddFocusChangeListener(this); 81 82 return true; 83} 84 85bool AccessiblePaneView::SetPaneFocusAndFocusDefault( 86 int view_storage_id) { 87 return SetPaneFocus(view_storage_id, GetDefaultFocusableChild()); 88} 89 90void AccessiblePaneView::RemovePaneFocus() { 91 focus_manager_->RemoveFocusChangeListener(this); 92 pane_has_focus_ = false; 93 94 focus_manager_->UnregisterAccelerator(home_key_, this); 95 focus_manager_->UnregisterAccelerator(end_key_, this); 96 focus_manager_->UnregisterAccelerator(escape_key_, this); 97 focus_manager_->UnregisterAccelerator(left_key_, this); 98 focus_manager_->UnregisterAccelerator(right_key_, this); 99} 100 101void AccessiblePaneView::LocationBarSelectAll() { 102 views::View* focused_view = GetFocusManager()->GetFocusedView(); 103 if (focused_view && 104 focused_view->GetClassName() == LocationBarView::kViewClassName) { 105 static_cast<LocationBarView*>(focused_view)->SelectAll(); 106 } 107} 108 109void AccessiblePaneView::RestoreLastFocusedView() { 110 views::ViewStorage* view_storage = views::ViewStorage::GetInstance(); 111 views::View* last_focused_view = 112 view_storage->RetrieveView(last_focused_view_storage_id_); 113 if (last_focused_view) { 114 focus_manager_->SetFocusedViewWithReason( 115 last_focused_view, views::FocusManager::kReasonFocusRestore); 116 } else { 117 // Focus the location bar 118 views::View* view = GetAncestorWithClassName(BrowserView::kViewClassName); 119 if (view) { 120 BrowserView* browser_view = static_cast<BrowserView*>(view); 121 browser_view->SetFocusToLocationBar(false); 122 } 123 } 124} 125 126views::View* AccessiblePaneView::GetFirstFocusableChild() { 127 FocusTraversable* dummy_focus_traversable; 128 views::View* dummy_focus_traversable_view; 129 return focus_search_->FindNextFocusableView( 130 NULL, false, views::FocusSearch::DOWN, false, 131 &dummy_focus_traversable, &dummy_focus_traversable_view); 132} 133 134views::View* AccessiblePaneView::GetLastFocusableChild() { 135 FocusTraversable* dummy_focus_traversable; 136 views::View* dummy_focus_traversable_view; 137 return focus_search_->FindNextFocusableView( 138 this, true, views::FocusSearch::DOWN, false, 139 &dummy_focus_traversable, &dummy_focus_traversable_view); 140} 141 142//////////////////////////////////////////////////////////////////////////////// 143// View overrides: 144 145views::FocusTraversable* AccessiblePaneView::GetPaneFocusTraversable() { 146 if (pane_has_focus_) 147 return this; 148 else 149 return NULL; 150} 151 152bool AccessiblePaneView::AcceleratorPressed( 153 const views::Accelerator& accelerator) { 154 // Special case: don't handle any accelerators for the location bar, 155 // so that it behaves exactly the same whether you focus it with Ctrl+L 156 // or F6 or Alt+D or Alt+Shift+T. 157 views::View* focused_view = focus_manager_->GetFocusedView(); 158 if ((focused_view->GetClassName() == LocationBarView::kViewClassName || 159 focused_view->GetClassName() == views::NativeViewHost::kViewClassName)) { 160 return false; 161 } 162 163 switch (accelerator.GetKeyCode()) { 164 case ui::VKEY_ESCAPE: 165 RemovePaneFocus(); 166 RestoreLastFocusedView(); 167 return true; 168 case ui::VKEY_LEFT: 169 focus_manager_->AdvanceFocus(true); 170 return true; 171 case ui::VKEY_RIGHT: 172 focus_manager_->AdvanceFocus(false); 173 return true; 174 case ui::VKEY_HOME: 175 focus_manager_->SetFocusedViewWithReason( 176 GetFirstFocusableChild(), views::FocusManager::kReasonFocusTraversal); 177 return true; 178 case ui::VKEY_END: 179 focus_manager_->SetFocusedViewWithReason( 180 GetLastFocusableChild(), views::FocusManager::kReasonFocusTraversal); 181 return true; 182 default: 183 return false; 184 } 185} 186 187void AccessiblePaneView::SetVisible(bool flag) { 188 if (IsVisible() && !flag && pane_has_focus_) { 189 RemovePaneFocus(); 190 RestoreLastFocusedView(); 191 } 192 View::SetVisible(flag); 193} 194 195AccessibilityTypes::Role AccessiblePaneView::GetAccessibleRole() { 196 return AccessibilityTypes::ROLE_PANE; 197} 198 199//////////////////////////////////////////////////////////////////////////////// 200// FocusChangeListener overrides: 201 202void AccessiblePaneView::FocusWillChange(views::View* focused_before, 203 views::View* focused_now) { 204 if (!focused_now) 205 return; 206 207 views::FocusManager::FocusChangeReason reason = 208 focus_manager_->focus_change_reason(); 209 210 if (focused_now->GetClassName() == LocationBarView::kViewClassName && 211 reason == views::FocusManager::kReasonFocusTraversal) { 212 // Tabbing to the location bar should select all. Defer so that it happens 213 // after the focus. 214 MessageLoop::current()->PostTask( 215 FROM_HERE, method_factory_.NewRunnableMethod( 216 &AccessiblePaneView::LocationBarSelectAll)); 217 } 218 219 if (!Contains(focused_now) || 220 reason == views::FocusManager::kReasonDirectFocusChange) { 221 // We should remove pane focus (i.e. make most of the controls 222 // not focusable again) either because the focus is leaving the pane, 223 // or because the focus changed within the pane due to the user 224 // directly focusing to a specific view (e.g., clicking on it). 225 // 226 // Defer rather than calling RemovePaneFocus right away, because we can't 227 // remove |this| as a focus change listener while FocusManager is in the 228 // middle of iterating over the list of listeners. 229 MessageLoop::current()->PostTask( 230 FROM_HERE, method_factory_.NewRunnableMethod( 231 &AccessiblePaneView::RemovePaneFocus)); 232 } 233} 234 235//////////////////////////////////////////////////////////////////////////////// 236// FocusTraversable overrides: 237 238views::FocusSearch* AccessiblePaneView::GetFocusSearch() { 239 DCHECK(pane_has_focus_); 240 return focus_search_.get(); 241} 242 243views::FocusTraversable* AccessiblePaneView::GetFocusTraversableParent() { 244 DCHECK(pane_has_focus_); 245 return NULL; 246} 247 248views::View* AccessiblePaneView::GetFocusTraversableParentView() { 249 DCHECK(pane_has_focus_); 250 return NULL; 251} 252