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.
5 #include "ui/views/accessible_pane_view.h"
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"
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
{
22 explicit AccessiblePaneViewFocusSearch(AccessiblePaneView
* pane_view
)
23 : FocusSearch(pane_view
, true, true),
24 accessible_pane_view_(pane_view
) {}
27 View
* GetParent(View
* v
) override
{
28 return accessible_pane_view_
->ContainsForFocusSearch(root(), v
) ?
29 accessible_pane_view_
->GetParentForFocusSearch(v
) : NULL
;
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
);
39 AccessiblePaneView
* accessible_pane_view_
;
40 DISALLOW_COPY_AND_ASSIGN(AccessiblePaneViewFocusSearch
);
43 AccessiblePaneView::AccessiblePaneView()
44 : pane_has_focus_(false),
45 allow_deactivate_on_esc_(false),
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();
57 AccessiblePaneView::~AccessiblePaneView() {
58 if (pane_has_focus_
) {
59 focus_manager_
->RemoveFocusChangeListener(this);
63 bool AccessiblePaneView::SetPaneFocus(views::View
* initial_focus
) {
68 focus_manager_
= GetFocusManager();
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
);
77 // Use the provided initial focus if it's visible and enabled, otherwise
78 // use the first focusable child.
80 !ContainsForFocusSearch(this, initial_focus
) ||
81 !initial_focus
->visible() ||
82 !initial_focus
->enabled()) {
83 initial_focus
= GetFirstFocusableChild();
86 // Return false if there are no focusable children.
90 focus_manager_
->SetFocusedView(initial_focus
);
92 // If we already have pane focus, we're done.
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);
110 bool AccessiblePaneView::SetPaneFocusAndFocusDefault() {
111 return SetPaneFocus(GetDefaultFocusableChild());
114 views::View
* AccessiblePaneView::GetDefaultFocusableChild() {
118 View
* AccessiblePaneView::GetParentForFocusSearch(View
* v
) {
122 bool AccessiblePaneView::ContainsForFocusSearch(View
* root
, const View
* v
) {
123 return root
->Contains(v
);
126 void AccessiblePaneView::RemovePaneFocus() {
127 focus_manager_
->RemoveFocusChangeListener(this);
128 pane_has_focus_
= false;
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);
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
);
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
);
153 ////////////////////////////////////////////////////////////////////////////////
156 views::FocusTraversable
* AccessiblePaneView::GetPaneFocusTraversable() {
163 bool AccessiblePaneView::AcceleratorPressed(
164 const ui::Accelerator
& accelerator
) {
166 views::View
* focused_view
= focus_manager_
->GetFocusedView();
167 if (!ContainsForFocusSearch(this, focused_view
))
170 switch (accelerator
.key_code()) {
171 case ui::VKEY_ESCAPE
: {
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();
184 focus_manager_
->AdvanceFocus(true);
187 focus_manager_
->AdvanceFocus(false);
190 focus_manager_
->SetFocusedViewWithReason(
191 GetFirstFocusableChild(), views::FocusManager::kReasonFocusTraversal
);
194 focus_manager_
->SetFocusedViewWithReason(
195 GetLastFocusableChild(), views::FocusManager::kReasonFocusTraversal
);
202 void AccessiblePaneView::SetVisible(bool flag
) {
203 if (visible() && !flag
&& pane_has_focus_
) {
205 focus_manager_
->RestoreFocusedView();
207 View::SetVisible(flag
);
210 void AccessiblePaneView::GetAccessibleState(ui::AXViewState
* state
) {
211 state
->role
= ui::AX_ROLE_PANE
;
214 void AccessiblePaneView::RequestFocus() {
215 SetPaneFocusAndFocusDefault();
218 ////////////////////////////////////////////////////////////////////////////////
219 // FocusChangeListener overrides:
221 void AccessiblePaneView::OnWillChangeFocus(views::View
* focused_before
,
222 views::View
* focused_now
) {
223 // Act when focus has changed.
226 void AccessiblePaneView::OnDidChangeFocus(views::View
* focused_before
,
227 views::View
* focused_now
) {
231 views::FocusManager::FocusChangeReason reason
=
232 focus_manager_
->focus_change_reason();
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).
244 ////////////////////////////////////////////////////////////////////////////////
245 // FocusTraversable overrides:
247 views::FocusSearch
* AccessiblePaneView::GetFocusSearch() {
248 DCHECK(pane_has_focus_
);
249 return focus_search_
.get();
252 views::FocusTraversable
* AccessiblePaneView::GetFocusTraversableParent() {
253 DCHECK(pane_has_focus_
);
257 views::View
* AccessiblePaneView::GetFocusTraversableParentView() {
258 DCHECK(pane_has_focus_
);