accessible_pane_view.cc revision dc0f95d653279beabeb9817299e2902918ba123e
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 90views::View* AccessiblePaneView::GetDefaultFocusableChild() { 91 return NULL; 92} 93 94void AccessiblePaneView::RemovePaneFocus() { 95 focus_manager_->RemoveFocusChangeListener(this); 96 pane_has_focus_ = false; 97 98 focus_manager_->UnregisterAccelerator(home_key_, this); 99 focus_manager_->UnregisterAccelerator(end_key_, this); 100 focus_manager_->UnregisterAccelerator(escape_key_, this); 101 focus_manager_->UnregisterAccelerator(left_key_, this); 102 focus_manager_->UnregisterAccelerator(right_key_, this); 103} 104 105void AccessiblePaneView::LocationBarSelectAll() { 106 views::View* focused_view = GetFocusManager()->GetFocusedView(); 107 if (focused_view && 108 focused_view->GetClassName() == LocationBarView::kViewClassName) { 109 static_cast<LocationBarView*>(focused_view)->SelectAll(); 110 } 111} 112 113void AccessiblePaneView::RestoreLastFocusedView() { 114 views::ViewStorage* view_storage = views::ViewStorage::GetInstance(); 115 views::View* last_focused_view = 116 view_storage->RetrieveView(last_focused_view_storage_id_); 117 if (last_focused_view) { 118 focus_manager_->SetFocusedViewWithReason( 119 last_focused_view, views::FocusManager::kReasonFocusRestore); 120 } else { 121 // Focus the location bar 122 views::View* view = GetAncestorWithClassName(BrowserView::kViewClassName); 123 if (view) { 124 BrowserView* browser_view = static_cast<BrowserView*>(view); 125 browser_view->SetFocusToLocationBar(false); 126 } 127 } 128} 129 130views::View* AccessiblePaneView::GetFirstFocusableChild() { 131 FocusTraversable* dummy_focus_traversable; 132 views::View* dummy_focus_traversable_view; 133 return focus_search_->FindNextFocusableView( 134 NULL, false, views::FocusSearch::DOWN, false, 135 &dummy_focus_traversable, &dummy_focus_traversable_view); 136} 137 138views::View* AccessiblePaneView::GetLastFocusableChild() { 139 FocusTraversable* dummy_focus_traversable; 140 views::View* dummy_focus_traversable_view; 141 return focus_search_->FindNextFocusableView( 142 this, true, views::FocusSearch::DOWN, false, 143 &dummy_focus_traversable, &dummy_focus_traversable_view); 144} 145 146//////////////////////////////////////////////////////////////////////////////// 147// View overrides: 148 149views::FocusTraversable* AccessiblePaneView::GetPaneFocusTraversable() { 150 if (pane_has_focus_) 151 return this; 152 else 153 return NULL; 154} 155 156bool AccessiblePaneView::AcceleratorPressed( 157 const views::Accelerator& accelerator) { 158 // Special case: don't handle any accelerators for the location bar, 159 // so that it behaves exactly the same whether you focus it with Ctrl+L 160 // or F6 or Alt+D or Alt+Shift+T. 161 views::View* focused_view = focus_manager_->GetFocusedView(); 162 if ((focused_view->GetClassName() == LocationBarView::kViewClassName || 163 focused_view->GetClassName() == views::NativeViewHost::kViewClassName)) { 164 return false; 165 } 166 167 switch (accelerator.GetKeyCode()) { 168 case ui::VKEY_ESCAPE: 169 RemovePaneFocus(); 170 RestoreLastFocusedView(); 171 return true; 172 case ui::VKEY_LEFT: 173 focus_manager_->AdvanceFocus(true); 174 return true; 175 case ui::VKEY_RIGHT: 176 focus_manager_->AdvanceFocus(false); 177 return true; 178 case ui::VKEY_HOME: 179 focus_manager_->SetFocusedViewWithReason( 180 GetFirstFocusableChild(), views::FocusManager::kReasonFocusTraversal); 181 return true; 182 case ui::VKEY_END: 183 focus_manager_->SetFocusedViewWithReason( 184 GetLastFocusableChild(), views::FocusManager::kReasonFocusTraversal); 185 return true; 186 default: 187 return false; 188 } 189} 190 191void AccessiblePaneView::SetVisible(bool flag) { 192 if (IsVisible() && !flag && pane_has_focus_) { 193 RemovePaneFocus(); 194 RestoreLastFocusedView(); 195 } 196 View::SetVisible(flag); 197} 198 199AccessibilityTypes::Role AccessiblePaneView::GetAccessibleRole() { 200 return AccessibilityTypes::ROLE_PANE; 201} 202 203//////////////////////////////////////////////////////////////////////////////// 204// FocusChangeListener overrides: 205 206void AccessiblePaneView::FocusWillChange(views::View* focused_before, 207 views::View* focused_now) { 208 if (!focused_now) 209 return; 210 211 views::FocusManager::FocusChangeReason reason = 212 focus_manager_->focus_change_reason(); 213 214 if (focused_now->GetClassName() == LocationBarView::kViewClassName && 215 reason == views::FocusManager::kReasonFocusTraversal) { 216 // Tabbing to the location bar should select all. Defer so that it happens 217 // after the focus. 218 MessageLoop::current()->PostTask( 219 FROM_HERE, method_factory_.NewRunnableMethod( 220 &AccessiblePaneView::LocationBarSelectAll)); 221 } 222 223 if (!Contains(focused_now) || 224 reason == views::FocusManager::kReasonDirectFocusChange) { 225 // We should remove pane focus (i.e. make most of the controls 226 // not focusable again) either because the focus is leaving the pane, 227 // or because the focus changed within the pane due to the user 228 // directly focusing to a specific view (e.g., clicking on it). 229 // 230 // Defer rather than calling RemovePaneFocus right away, because we can't 231 // remove |this| as a focus change listener while FocusManager is in the 232 // middle of iterating over the list of listeners. 233 MessageLoop::current()->PostTask( 234 FROM_HERE, method_factory_.NewRunnableMethod( 235 &AccessiblePaneView::RemovePaneFocus)); 236 } 237} 238 239//////////////////////////////////////////////////////////////////////////////// 240// FocusTraversable overrides: 241 242views::FocusSearch* AccessiblePaneView::GetFocusSearch() { 243 DCHECK(pane_has_focus_); 244 return focus_search_.get(); 245} 246 247views::FocusTraversable* AccessiblePaneView::GetFocusTraversableParent() { 248 DCHECK(pane_has_focus_); 249 return NULL; 250} 251 252views::View* AccessiblePaneView::GetFocusTraversableParentView() { 253 DCHECK(pane_has_focus_); 254 return NULL; 255} 256