accessible_pane_view.cc 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  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. #include "ui/views/accessible_pane_view.h"
  5. #include <memory>
  6. #include "base/memory/raw_ptr.h"
  7. #include "ui/accessibility/ax_enums.mojom.h"
  8. #include "ui/accessibility/ax_node_data.h"
  9. #include "ui/base/metadata/metadata_impl_macros.h"
  10. #include "ui/views/focus/focus_search.h"
  11. #include "ui/views/view_tracker.h"
  12. #include "ui/views/widget/widget.h"
  13. namespace views {
  14. // Create tiny subclass of FocusSearch that overrides GetParent and Contains,
  15. // delegating these to methods in AccessiblePaneView. This is needed so that
  16. // subclasses of AccessiblePaneView can customize the focus search logic and
  17. // include views that aren't part of the AccessiblePaneView's view
  18. // hierarchy in the focus order.
  19. class AccessiblePaneViewFocusSearch : public FocusSearch {
  20. public:
  21. explicit AccessiblePaneViewFocusSearch(AccessiblePaneView* pane_view)
  22. : FocusSearch(pane_view, true, true), accessible_pane_view_(pane_view) {}
  23. AccessiblePaneViewFocusSearch(const AccessiblePaneViewFocusSearch&) = delete;
  24. AccessiblePaneViewFocusSearch& operator=(
  25. const AccessiblePaneViewFocusSearch&) = delete;
  26. protected:
  27. View* GetParent(View* v) override {
  28. return accessible_pane_view_->ContainsForFocusSearch(root(), v)
  29. ? accessible_pane_view_->GetParentForFocusSearch(v)
  30. : nullptr;
  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. bool Contains(View* root, const View* v) override {
  35. return accessible_pane_view_->ContainsForFocusSearch(root, v);
  36. }
  37. private:
  38. raw_ptr<AccessiblePaneView> accessible_pane_view_;
  39. };
  40. AccessiblePaneView::AccessiblePaneView()
  41. : last_focused_view_tracker_(std::make_unique<ViewTracker>()) {
  42. focus_search_ = std::make_unique<AccessiblePaneViewFocusSearch>(this);
  43. }
  44. AccessiblePaneView::~AccessiblePaneView() {
  45. if (pane_has_focus_) {
  46. RemovePaneFocus();
  47. }
  48. }
  49. bool AccessiblePaneView::SetPaneFocus(views::View* initial_focus) {
  50. if (!GetVisible())
  51. return false;
  52. if (!focus_manager_)
  53. focus_manager_ = GetFocusManager();
  54. View* focused_view = focus_manager_->GetFocusedView();
  55. if (focused_view && !ContainsForFocusSearch(this, focused_view))
  56. last_focused_view_tracker_->SetView(focused_view);
  57. // Use the provided initial focus if it's visible and enabled, otherwise
  58. // use the first focusable child.
  59. if (!initial_focus || !ContainsForFocusSearch(this, initial_focus) ||
  60. !initial_focus->GetVisible() || !initial_focus->GetEnabled()) {
  61. initial_focus = GetFirstFocusableChild();
  62. }
  63. // Return false if there are no focusable children.
  64. if (!initial_focus)
  65. return false;
  66. focus_manager_->SetFocusedView(initial_focus);
  67. // TODO(pbos): Move this behavior into FocusManager. Focusing an unfocusable
  68. // view should do something smart (move focus to its children or clear focus).
  69. // DownloadItemView is an example (isn't focusable, has focusable children).
  70. // See https://crbug.com/1000998.
  71. // The initially-focused view may not be focusable (but one of its children
  72. // might). We may need to advance focus here to make sure focus is on
  73. // something focusable.
  74. focus_manager_->AdvanceFocusIfNecessary();
  75. // If we already have pane focus, we're done.
  76. if (pane_has_focus_)
  77. return true;
  78. // Otherwise, set accelerators and start listening for focus change events.
  79. pane_has_focus_ = true;
  80. ui::AcceleratorManager::HandlerPriority normal =
  81. ui::AcceleratorManager::kNormalPriority;
  82. focus_manager_->RegisterAccelerator(home_key_, normal, this);
  83. focus_manager_->RegisterAccelerator(end_key_, normal, this);
  84. focus_manager_->RegisterAccelerator(escape_key_, normal, this);
  85. focus_manager_->RegisterAccelerator(left_key_, normal, this);
  86. focus_manager_->RegisterAccelerator(right_key_, normal, this);
  87. focus_manager_->AddFocusChangeListener(this);
  88. return true;
  89. }
  90. bool AccessiblePaneView::SetPaneFocusAndFocusDefault() {
  91. return SetPaneFocus(GetDefaultFocusableChild());
  92. }
  93. views::View* AccessiblePaneView::GetDefaultFocusableChild() {
  94. return nullptr;
  95. }
  96. View* AccessiblePaneView::GetParentForFocusSearch(View* v) {
  97. return v->parent();
  98. }
  99. bool AccessiblePaneView::ContainsForFocusSearch(View* root, const View* v) {
  100. return root->Contains(v);
  101. }
  102. void AccessiblePaneView::RemovePaneFocus() {
  103. focus_manager_->RemoveFocusChangeListener(this);
  104. pane_has_focus_ = false;
  105. focus_manager_->UnregisterAccelerator(home_key_, this);
  106. focus_manager_->UnregisterAccelerator(end_key_, this);
  107. focus_manager_->UnregisterAccelerator(escape_key_, this);
  108. focus_manager_->UnregisterAccelerator(left_key_, this);
  109. focus_manager_->UnregisterAccelerator(right_key_, this);
  110. }
  111. views::View* AccessiblePaneView::GetFirstFocusableChild() {
  112. FocusTraversable* dummy_focus_traversable;
  113. views::View* dummy_focus_traversable_view;
  114. return focus_search_->FindNextFocusableView(
  115. nullptr, FocusSearch::SearchDirection::kForwards,
  116. FocusSearch::TraversalDirection::kDown,
  117. FocusSearch::StartingViewPolicy::kSkipStartingView,
  118. FocusSearch::AnchoredDialogPolicy::kCanGoIntoAnchoredDialog,
  119. &dummy_focus_traversable, &dummy_focus_traversable_view);
  120. }
  121. views::View* AccessiblePaneView::GetLastFocusableChild() {
  122. FocusTraversable* dummy_focus_traversable;
  123. views::View* dummy_focus_traversable_view;
  124. return focus_search_->FindNextFocusableView(
  125. this, FocusSearch::SearchDirection::kBackwards,
  126. FocusSearch::TraversalDirection::kDown,
  127. FocusSearch::StartingViewPolicy::kSkipStartingView,
  128. FocusSearch::AnchoredDialogPolicy::kCanGoIntoAnchoredDialog,
  129. &dummy_focus_traversable, &dummy_focus_traversable_view);
  130. }
  131. ////////////////////////////////////////////////////////////////////////////////
  132. // View overrides:
  133. views::FocusTraversable* AccessiblePaneView::GetPaneFocusTraversable() {
  134. if (pane_has_focus_)
  135. return this;
  136. else
  137. return nullptr;
  138. }
  139. bool AccessiblePaneView::AcceleratorPressed(
  140. const ui::Accelerator& accelerator) {
  141. views::View* focused_view = focus_manager_->GetFocusedView();
  142. if (!ContainsForFocusSearch(this, focused_view))
  143. return false;
  144. using FocusChangeReason = views::FocusManager::FocusChangeReason;
  145. switch (accelerator.key_code()) {
  146. case ui::VKEY_ESCAPE: {
  147. RemovePaneFocus();
  148. View* last_focused_view = last_focused_view_tracker_->view();
  149. // Ignore |last_focused_view| if it's no longer in the same widget.
  150. if (last_focused_view && GetWidget() != last_focused_view->GetWidget())
  151. last_focused_view = nullptr;
  152. if (last_focused_view) {
  153. focus_manager_->SetFocusedViewWithReason(
  154. last_focused_view, FocusChangeReason::kFocusRestore);
  155. } else if (allow_deactivate_on_esc_) {
  156. focused_view->GetWidget()->Deactivate();
  157. }
  158. return true;
  159. }
  160. case ui::VKEY_LEFT:
  161. focus_manager_->AdvanceFocus(true);
  162. return true;
  163. case ui::VKEY_RIGHT:
  164. focus_manager_->AdvanceFocus(false);
  165. return true;
  166. case ui::VKEY_HOME:
  167. focus_manager_->SetFocusedViewWithReason(
  168. GetFirstFocusableChild(), FocusChangeReason::kFocusTraversal);
  169. return true;
  170. case ui::VKEY_END:
  171. focus_manager_->SetFocusedViewWithReason(
  172. GetLastFocusableChild(), FocusChangeReason::kFocusTraversal);
  173. return true;
  174. default:
  175. return false;
  176. }
  177. }
  178. void AccessiblePaneView::SetVisible(bool flag) {
  179. if (GetVisible() && !flag && pane_has_focus_) {
  180. RemovePaneFocus();
  181. focus_manager_->RestoreFocusedView();
  182. }
  183. View::SetVisible(flag);
  184. }
  185. void AccessiblePaneView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
  186. node_data->role = ax::mojom::Role::kPane;
  187. }
  188. void AccessiblePaneView::RequestFocus() {
  189. SetPaneFocusAndFocusDefault();
  190. }
  191. ////////////////////////////////////////////////////////////////////////////////
  192. // FocusChangeListener overrides:
  193. void AccessiblePaneView::OnWillChangeFocus(views::View* focused_before,
  194. views::View* focused_now) {
  195. // Act when focus has changed.
  196. }
  197. void AccessiblePaneView::OnDidChangeFocus(views::View* focused_before,
  198. views::View* focused_now) {
  199. if (!focused_now)
  200. return;
  201. views::FocusManager::FocusChangeReason reason =
  202. focus_manager_->focus_change_reason();
  203. if (!ContainsForFocusSearch(this, focused_now) ||
  204. reason == views::FocusManager::FocusChangeReason::kDirectFocusChange) {
  205. // We should remove pane focus (i.e. make most of the controls
  206. // not focusable again) because the focus has left the pane,
  207. // or because the focus changed within the pane due to the user
  208. // directly focusing to a specific view (e.g., clicking on it).
  209. RemovePaneFocus();
  210. }
  211. }
  212. ////////////////////////////////////////////////////////////////////////////////
  213. // FocusTraversable overrides:
  214. views::FocusSearch* AccessiblePaneView::GetFocusSearch() {
  215. DCHECK(pane_has_focus_);
  216. return focus_search_.get();
  217. }
  218. views::FocusTraversable* AccessiblePaneView::GetFocusTraversableParent() {
  219. DCHECK(pane_has_focus_);
  220. return nullptr;
  221. }
  222. views::View* AccessiblePaneView::GetFocusTraversableParentView() {
  223. DCHECK(pane_has_focus_);
  224. return nullptr;
  225. }
  226. BEGIN_METADATA(AccessiblePaneView, View)
  227. END_METADATA
  228. } // namespace views