OLD | NEW |
| (Empty) |
1 // Copyright (c) 2010 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 "views/focus/focus_manager.h" | |
7 #include "views/focus/focus_search.h" | |
8 #include "views/view.h" | |
9 | |
10 namespace views { | |
11 | |
12 FocusSearch::FocusSearch(View* root, bool cycle, bool accessibility_mode) | |
13 : root_(root), | |
14 cycle_(cycle), | |
15 accessibility_mode_(accessibility_mode) { | |
16 } | |
17 | |
18 View* FocusSearch::FindNextFocusableView(View* starting_view, | |
19 bool reverse, | |
20 Direction direction, | |
21 bool check_starting_view, | |
22 FocusTraversable** focus_traversable, | |
23 View** focus_traversable_view) { | |
24 *focus_traversable = NULL; | |
25 *focus_traversable_view = NULL; | |
26 | |
27 if (!root_->has_children()) { | |
28 NOTREACHED(); | |
29 // Nothing to focus on here. | |
30 return NULL; | |
31 } | |
32 | |
33 View* initial_starting_view = starting_view; | |
34 int starting_view_group = -1; | |
35 if (starting_view) | |
36 starting_view_group = starting_view->GetGroup(); | |
37 | |
38 if (!starting_view) { | |
39 // Default to the first/last child | |
40 starting_view = reverse ? root_->child_at(root_->child_count() - 1) : | |
41 root_->child_at(0); | |
42 // If there was no starting view, then the one we select is a potential | |
43 // focus candidate. | |
44 check_starting_view = true; | |
45 } else { | |
46 // The starting view should be a direct or indirect child of the root. | |
47 DCHECK(root_->Contains(starting_view)); | |
48 } | |
49 | |
50 View* v = NULL; | |
51 if (!reverse) { | |
52 v = FindNextFocusableViewImpl(starting_view, check_starting_view, | |
53 true, | |
54 (direction == DOWN) ? true : false, | |
55 starting_view_group, | |
56 focus_traversable, | |
57 focus_traversable_view); | |
58 } else { | |
59 // If the starting view is focusable, we don't want to go down, as we are | |
60 // traversing the view hierarchy tree bottom-up. | |
61 bool can_go_down = (direction == DOWN) && !IsFocusable(starting_view); | |
62 v = FindPreviousFocusableViewImpl(starting_view, check_starting_view, | |
63 true, | |
64 can_go_down, | |
65 starting_view_group, | |
66 focus_traversable, | |
67 focus_traversable_view); | |
68 } | |
69 | |
70 // Don't set the focus to something outside of this view hierarchy. | |
71 if (v && v != root_ && !root_->Contains(v)) | |
72 v = NULL; | |
73 | |
74 // If |cycle_| is true, prefer to keep cycling rather than returning NULL. | |
75 if (cycle_ && !v && initial_starting_view) { | |
76 v = FindNextFocusableView(NULL, reverse, direction, check_starting_view, | |
77 focus_traversable, focus_traversable_view); | |
78 DCHECK(IsFocusable(v)); | |
79 return v; | |
80 } | |
81 | |
82 // Doing some sanity checks. | |
83 if (v) { | |
84 DCHECK(IsFocusable(v)); | |
85 return v; | |
86 } | |
87 if (*focus_traversable) { | |
88 DCHECK(*focus_traversable_view); | |
89 return NULL; | |
90 } | |
91 // Nothing found. | |
92 return NULL; | |
93 } | |
94 | |
95 bool FocusSearch::IsViewFocusableCandidate(View* v, int skip_group_id) { | |
96 return IsFocusable(v) && | |
97 (v->IsGroupFocusTraversable() || skip_group_id == -1 || | |
98 v->GetGroup() != skip_group_id); | |
99 } | |
100 | |
101 bool FocusSearch::IsFocusable(View* v) { | |
102 if (accessibility_mode_) | |
103 return v && v->IsAccessibilityFocusableInRootView(); | |
104 | |
105 return v && v->IsFocusableInRootView(); | |
106 } | |
107 | |
108 View* FocusSearch::FindSelectedViewForGroup(View* view) { | |
109 if (view->IsGroupFocusTraversable() || | |
110 view->GetGroup() == -1) // No group for that view. | |
111 return view; | |
112 | |
113 View* selected_view = view->GetSelectedViewForGroup(view->GetGroup()); | |
114 if (selected_view) | |
115 return selected_view; | |
116 | |
117 // No view selected for that group, default to the specified view. | |
118 return view; | |
119 } | |
120 | |
121 View* FocusSearch::GetParent(View* v) { | |
122 return root_->Contains(v) ? v->parent() : NULL; | |
123 } | |
124 | |
125 // Strategy for finding the next focusable view: | |
126 // - keep going down the first child, stop when you find a focusable view or | |
127 // a focus traversable view (in that case return it) or when you reach a view | |
128 // with no children. | |
129 // - go to the right sibling and start the search from there (by invoking | |
130 // FindNextFocusableViewImpl on that view). | |
131 // - if the view has no right sibling, go up the parents until you find a parent | |
132 // with a right sibling and start the search from there. | |
133 View* FocusSearch::FindNextFocusableViewImpl( | |
134 View* starting_view, | |
135 bool check_starting_view, | |
136 bool can_go_up, | |
137 bool can_go_down, | |
138 int skip_group_id, | |
139 FocusTraversable** focus_traversable, | |
140 View** focus_traversable_view) { | |
141 if (check_starting_view) { | |
142 if (IsViewFocusableCandidate(starting_view, skip_group_id)) { | |
143 View* v = FindSelectedViewForGroup(starting_view); | |
144 // The selected view might not be focusable (if it is disabled for | |
145 // example). | |
146 if (IsFocusable(v)) | |
147 return v; | |
148 } | |
149 | |
150 *focus_traversable = starting_view->GetFocusTraversable(); | |
151 if (*focus_traversable) { | |
152 *focus_traversable_view = starting_view; | |
153 return NULL; | |
154 } | |
155 } | |
156 | |
157 // First let's try the left child. | |
158 if (can_go_down) { | |
159 if (starting_view->has_children()) { | |
160 View* v = FindNextFocusableViewImpl(starting_view->child_at(0), | |
161 true, false, true, skip_group_id, | |
162 focus_traversable, | |
163 focus_traversable_view); | |
164 if (v || *focus_traversable) | |
165 return v; | |
166 } | |
167 } | |
168 | |
169 // Then try the right sibling. | |
170 View* sibling = starting_view->GetNextFocusableView(); | |
171 if (sibling) { | |
172 View* v = FindNextFocusableViewImpl(sibling, | |
173 true, false, true, skip_group_id, | |
174 focus_traversable, | |
175 focus_traversable_view); | |
176 if (v || *focus_traversable) | |
177 return v; | |
178 } | |
179 | |
180 // Then go up to the parent sibling. | |
181 if (can_go_up) { | |
182 View* parent = GetParent(starting_view); | |
183 while (parent) { | |
184 sibling = parent->GetNextFocusableView(); | |
185 if (sibling) { | |
186 return FindNextFocusableViewImpl(sibling, | |
187 true, true, true, | |
188 skip_group_id, | |
189 focus_traversable, | |
190 focus_traversable_view); | |
191 } | |
192 parent = GetParent(parent); | |
193 } | |
194 } | |
195 | |
196 // We found nothing. | |
197 return NULL; | |
198 } | |
199 | |
200 // Strategy for finding the previous focusable view: | |
201 // - keep going down on the right until you reach a view with no children, if it | |
202 // it is a good candidate return it. | |
203 // - start the search on the left sibling. | |
204 // - if there are no left sibling, start the search on the parent (without going | |
205 // down). | |
206 View* FocusSearch::FindPreviousFocusableViewImpl( | |
207 View* starting_view, | |
208 bool check_starting_view, | |
209 bool can_go_up, | |
210 bool can_go_down, | |
211 int skip_group_id, | |
212 FocusTraversable** focus_traversable, | |
213 View** focus_traversable_view) { | |
214 // Let's go down and right as much as we can. | |
215 if (can_go_down) { | |
216 // Before we go into the direct children, we have to check if this view has | |
217 // a FocusTraversable. | |
218 *focus_traversable = starting_view->GetFocusTraversable(); | |
219 if (*focus_traversable) { | |
220 *focus_traversable_view = starting_view; | |
221 return NULL; | |
222 } | |
223 | |
224 if (starting_view->has_children()) { | |
225 View* view = | |
226 starting_view->child_at(starting_view->child_count() - 1); | |
227 View* v = FindPreviousFocusableViewImpl(view, true, false, true, | |
228 skip_group_id, | |
229 focus_traversable, | |
230 focus_traversable_view); | |
231 if (v || *focus_traversable) | |
232 return v; | |
233 } | |
234 } | |
235 | |
236 // Then look at this view. Here, we do not need to see if the view has | |
237 // a FocusTraversable, since we do not want to go down any more. | |
238 if (check_starting_view && | |
239 IsViewFocusableCandidate(starting_view, skip_group_id)) { | |
240 View* v = FindSelectedViewForGroup(starting_view); | |
241 // The selected view might not be focusable (if it is disabled for | |
242 // example). | |
243 if (IsFocusable(v)) | |
244 return v; | |
245 } | |
246 | |
247 // Then try the left sibling. | |
248 View* sibling = starting_view->GetPreviousFocusableView(); | |
249 if (sibling) { | |
250 return FindPreviousFocusableViewImpl(sibling, | |
251 true, true, true, | |
252 skip_group_id, | |
253 focus_traversable, | |
254 focus_traversable_view); | |
255 } | |
256 | |
257 // Then go up the parent. | |
258 if (can_go_up) { | |
259 View* parent = GetParent(starting_view); | |
260 if (parent) | |
261 return FindPreviousFocusableViewImpl(parent, | |
262 true, true, false, | |
263 skip_group_id, | |
264 focus_traversable, | |
265 focus_traversable_view); | |
266 } | |
267 | |
268 // We found nothing. | |
269 return NULL; | |
270 } | |
271 | |
272 } // namespace views | |
OLD | NEW |