1 // Copyright (c) 2012 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 "ui/views/accessible_pane_view.h" 6 7 #include "base/message_loop/message_loop.h" 8 #include "ui/accessibility/ax_view_state.h" 9 #include "ui/views/focus/focus_search.h" 10 #include "ui/views/focus/view_storage.h" 11 #include "ui/views/widget/widget.h" 12 13 namespace views { 14 15 // Create tiny subclass of FocusSearch that overrides GetParent and Contains, 16 // delegating these to methods in AccessiblePaneView. This is needed so that 17 // subclasses of AccessiblePaneView can customize the focus search logic and 18 // include views that aren't part of the AccessiblePaneView's view 19 // hierarchy in the focus order. 20 class AccessiblePaneViewFocusSearch : public FocusSearch { 21 public: 22 explicit AccessiblePaneViewFocusSearch(AccessiblePaneView* pane_view) 23 : FocusSearch(pane_view, true, true), 24 accessible_pane_view_(pane_view) {} 25 26 protected: 27 virtual View* GetParent(View* v) OVERRIDE { 28 return accessible_pane_view_->ContainsForFocusSearch(root(), v) ? 29 accessible_pane_view_->GetParentForFocusSearch(v) : NULL; 30 } 31 32 // Returns true if |v| is contained within the hierarchy rooted at |root|. 33 // Subclasses can override this if they need custom focus search behavior. 34 virtual bool Contains(View* root, const View* v) OVERRIDE { 35 return accessible_pane_view_->ContainsForFocusSearch(root, v); 36 } 37 38 private: 39 AccessiblePaneView* accessible_pane_view_; 40 DISALLOW_COPY_AND_ASSIGN(AccessiblePaneViewFocusSearch); 41 }; 42 43 AccessiblePaneView::AccessiblePaneView() 44 : pane_has_focus_(false), 45 allow_deactivate_on_esc_(false), 46 focus_manager_(NULL), 47 home_key_(ui::VKEY_HOME, ui::EF_NONE), 48 end_key_(ui::VKEY_END, ui::EF_NONE), 49 escape_key_(ui::VKEY_ESCAPE, ui::EF_NONE), 50 left_key_(ui::VKEY_LEFT, ui::EF_NONE), 51 right_key_(ui::VKEY_RIGHT, ui::EF_NONE), 52 method_factory_(this) { 53 focus_search_.reset(new AccessiblePaneViewFocusSearch(this)); 54 last_focused_view_storage_id_ = ViewStorage::GetInstance()->CreateStorageID(); 55 } 56 57 AccessiblePaneView::~AccessiblePaneView() { 58 if (pane_has_focus_) { 59 focus_manager_->RemoveFocusChangeListener(this); 60 } 61 } 62 63 bool AccessiblePaneView::SetPaneFocus(views::View* initial_focus) { 64 if (!visible()) 65 return false; 66 67 if (!focus_manager_) 68 focus_manager_ = GetFocusManager(); 69 70 View* focused_view = focus_manager_->GetFocusedView(); 71 if (focused_view && !ContainsForFocusSearch(this, focused_view)) { 72 ViewStorage* view_storage = ViewStorage::GetInstance(); 73 view_storage->RemoveView(last_focused_view_storage_id_); 74 view_storage->StoreView(last_focused_view_storage_id_, focused_view); 75 } 76 77 // Use the provided initial focus if it's visible and enabled, otherwise 78 // use the first focusable child. 79 if (!initial_focus || 80 !ContainsForFocusSearch(this, initial_focus) || 81 !initial_focus->visible() || 82 !initial_focus->enabled()) { 83 initial_focus = GetFirstFocusableChild(); 84 } 85 86 // Return false if there are no focusable children. 87 if (!initial_focus) 88 return false; 89 90 focus_manager_->SetFocusedView(initial_focus); 91 92 // If we already have pane focus, we're done. 93 if (pane_has_focus_) 94 return true; 95 96 // Otherwise, set accelerators and start listening for focus change events. 97 pane_has_focus_ = true; 98 ui::AcceleratorManager::HandlerPriority normal = 99 ui::AcceleratorManager::kNormalPriority; 100 focus_manager_->RegisterAccelerator(home_key_, normal, this); 101 focus_manager_->RegisterAccelerator(end_key_, normal, this); 102 focus_manager_->RegisterAccelerator(escape_key_, normal, this); 103 focus_manager_->RegisterAccelerator(left_key_, normal, this); 104 focus_manager_->RegisterAccelerator(right_key_, normal, this); 105 focus_manager_->AddFocusChangeListener(this); 106 107 return true; 108 } 109 110 bool AccessiblePaneView::SetPaneFocusAndFocusDefault() { 111 return SetPaneFocus(GetDefaultFocusableChild()); 112 } 113 114 views::View* AccessiblePaneView::GetDefaultFocusableChild() { 115 return NULL; 116 } 117 118 View* AccessiblePaneView::GetParentForFocusSearch(View* v) { 119 return v->parent(); 120 } 121 122 bool AccessiblePaneView::ContainsForFocusSearch(View* root, const View* v) { 123 return root->Contains(v); 124 } 125 126 void AccessiblePaneView::RemovePaneFocus() { 127 focus_manager_->RemoveFocusChangeListener(this); 128 pane_has_focus_ = false; 129 130 focus_manager_->UnregisterAccelerator(home_key_, this); 131 focus_manager_->UnregisterAccelerator(end_key_, this); 132 focus_manager_->UnregisterAccelerator(escape_key_, this); 133 focus_manager_->UnregisterAccelerator(left_key_, this); 134 focus_manager_->UnregisterAccelerator(right_key_, this); 135 } 136 137 views::View* AccessiblePaneView::GetFirstFocusableChild() { 138 FocusTraversable* dummy_focus_traversable; 139 views::View* dummy_focus_traversable_view; 140 return focus_search_->FindNextFocusableView( 141 NULL, false, views::FocusSearch::DOWN, false, 142 &dummy_focus_traversable, &dummy_focus_traversable_view); 143 } 144 145 views::View* AccessiblePaneView::GetLastFocusableChild() { 146 FocusTraversable* dummy_focus_traversable; 147 views::View* dummy_focus_traversable_view; 148 return focus_search_->FindNextFocusableView( 149 this, true, views::FocusSearch::DOWN, false, 150 &dummy_focus_traversable, &dummy_focus_traversable_view); 151 } 152 153 //////////////////////////////////////////////////////////////////////////////// 154 // View overrides: 155 156 views::FocusTraversable* AccessiblePaneView::GetPaneFocusTraversable() { 157 if (pane_has_focus_) 158 return this; 159 else 160 return NULL; 161 } 162 163 bool AccessiblePaneView::AcceleratorPressed( 164 const ui::Accelerator& accelerator) { 165 166 views::View* focused_view = focus_manager_->GetFocusedView(); 167 if (!ContainsForFocusSearch(this, focused_view)) 168 return false; 169 170 switch (accelerator.key_code()) { 171 case ui::VKEY_ESCAPE: { 172 RemovePaneFocus(); 173 View* last_focused_view = ViewStorage::GetInstance()->RetrieveView( 174 last_focused_view_storage_id_); 175 if (last_focused_view) { 176 focus_manager_->SetFocusedViewWithReason( 177 last_focused_view, FocusManager::kReasonFocusRestore); 178 } else if (allow_deactivate_on_esc_) { 179 focused_view->GetWidget()->Deactivate(); 180 } 181 return true; 182 } 183 case ui::VKEY_LEFT: 184 focus_manager_->AdvanceFocus(true); 185 return true; 186 case ui::VKEY_RIGHT: 187 focus_manager_->AdvanceFocus(false); 188 return true; 189 case ui::VKEY_HOME: 190 focus_manager_->SetFocusedViewWithReason( 191 GetFirstFocusableChild(), views::FocusManager::kReasonFocusTraversal); 192 return true; 193 case ui::VKEY_END: 194 focus_manager_->SetFocusedViewWithReason( 195 GetLastFocusableChild(), views::FocusManager::kReasonFocusTraversal); 196 return true; 197 default: 198 return false; 199 } 200 } 201 202 void AccessiblePaneView::SetVisible(bool flag) { 203 if (visible() && !flag && pane_has_focus_) { 204 RemovePaneFocus(); 205 focus_manager_->RestoreFocusedView(); 206 } 207 View::SetVisible(flag); 208 } 209 210 void AccessiblePaneView::GetAccessibleState(ui::AXViewState* state) { 211 state->role = ui::AX_ROLE_PANE; 212 } 213 214 void AccessiblePaneView::RequestFocus() { 215 SetPaneFocusAndFocusDefault(); 216 } 217 218 //////////////////////////////////////////////////////////////////////////////// 219 // FocusChangeListener overrides: 220 221 void AccessiblePaneView::OnWillChangeFocus(views::View* focused_before, 222 views::View* focused_now) { 223 // Act when focus has changed. 224 } 225 226 void AccessiblePaneView::OnDidChangeFocus(views::View* focused_before, 227 views::View* focused_now) { 228 if (!focused_now) 229 return; 230 231 views::FocusManager::FocusChangeReason reason = 232 focus_manager_->focus_change_reason(); 233 234 if (!ContainsForFocusSearch(this, focused_now) || 235 reason == views::FocusManager::kReasonDirectFocusChange) { 236 // We should remove pane focus (i.e. make most of the controls 237 // not focusable again) because the focus has left the pane, 238 // or because the focus changed within the pane due to the user 239 // directly focusing to a specific view (e.g., clicking on it). 240 RemovePaneFocus(); 241 } 242 } 243 244 //////////////////////////////////////////////////////////////////////////////// 245 // FocusTraversable overrides: 246 247 views::FocusSearch* AccessiblePaneView::GetFocusSearch() { 248 DCHECK(pane_has_focus_); 249 return focus_search_.get(); 250 } 251 252 views::FocusTraversable* AccessiblePaneView::GetFocusTraversableParent() { 253 DCHECK(pane_has_focus_); 254 return NULL; 255 } 256 257 views::View* AccessiblePaneView::GetFocusTraversableParentView() { 258 DCHECK(pane_has_focus_); 259 return NULL; 260 } 261 262 } // namespace views 263