| OLD | NEW |
| (Empty) |
| 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 "ui/views/focus/focus_manager.h" | |
| 7 #include "ui/views/focus/focus_search.h" | |
| 8 #include "ui/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(Contains(root_, starting_view)); | |
| 48 } | |
| 49 | |
| 50 View* v = NULL; | |
| 51 if (!reverse) { | |
| 52 v = FindNextFocusableViewImpl(starting_view, check_starting_view, | |
| 53 true, | |
| 54 (direction == DOWN), | |
| 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_ && !Contains(root_, 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->IsAccessibilityFocusable(); | |
| 104 return v && v->IsFocusable(); | |
| 105 } | |
| 106 | |
| 107 View* FocusSearch::FindSelectedViewForGroup(View* view) { | |
| 108 if (view->IsGroupFocusTraversable() || | |
| 109 view->GetGroup() == -1) // No group for that view. | |
| 110 return view; | |
| 111 | |
| 112 View* selected_view = view->GetSelectedViewForGroup(view->GetGroup()); | |
| 113 if (selected_view) | |
| 114 return selected_view; | |
| 115 | |
| 116 // No view selected for that group, default to the specified view. | |
| 117 return view; | |
| 118 } | |
| 119 | |
| 120 View* FocusSearch::GetParent(View* v) { | |
| 121 return Contains(root_, v) ? v->parent() : NULL; | |
| 122 } | |
| 123 | |
| 124 bool FocusSearch::Contains(View* root, const View* v) { | |
| 125 return root->Contains(v); | |
| 126 } | |
| 127 | |
| 128 // Strategy for finding the next focusable view: | |
| 129 // - keep going down the first child, stop when you find a focusable view or | |
| 130 // a focus traversable view (in that case return it) or when you reach a view | |
| 131 // with no children. | |
| 132 // - go to the right sibling and start the search from there (by invoking | |
| 133 // FindNextFocusableViewImpl on that view). | |
| 134 // - if the view has no right sibling, go up the parents until you find a parent | |
| 135 // with a right sibling and start the search from there. | |
| 136 View* FocusSearch::FindNextFocusableViewImpl( | |
| 137 View* starting_view, | |
| 138 bool check_starting_view, | |
| 139 bool can_go_up, | |
| 140 bool can_go_down, | |
| 141 int skip_group_id, | |
| 142 FocusTraversable** focus_traversable, | |
| 143 View** focus_traversable_view) { | |
| 144 if (check_starting_view) { | |
| 145 if (IsViewFocusableCandidate(starting_view, skip_group_id)) { | |
| 146 View* v = FindSelectedViewForGroup(starting_view); | |
| 147 // The selected view might not be focusable (if it is disabled for | |
| 148 // example). | |
| 149 if (IsFocusable(v)) | |
| 150 return v; | |
| 151 } | |
| 152 | |
| 153 *focus_traversable = starting_view->GetFocusTraversable(); | |
| 154 if (*focus_traversable) { | |
| 155 *focus_traversable_view = starting_view; | |
| 156 return NULL; | |
| 157 } | |
| 158 } | |
| 159 | |
| 160 // First let's try the left child. | |
| 161 if (can_go_down) { | |
| 162 if (starting_view->has_children()) { | |
| 163 View* v = FindNextFocusableViewImpl(starting_view->child_at(0), | |
| 164 true, false, true, skip_group_id, | |
| 165 focus_traversable, | |
| 166 focus_traversable_view); | |
| 167 if (v || *focus_traversable) | |
| 168 return v; | |
| 169 } | |
| 170 } | |
| 171 | |
| 172 // Then try the right sibling. | |
| 173 View* sibling = starting_view->GetNextFocusableView(); | |
| 174 if (sibling) { | |
| 175 View* v = FindNextFocusableViewImpl(sibling, | |
| 176 true, false, true, skip_group_id, | |
| 177 focus_traversable, | |
| 178 focus_traversable_view); | |
| 179 if (v || *focus_traversable) | |
| 180 return v; | |
| 181 } | |
| 182 | |
| 183 // Then go up to the parent sibling. | |
| 184 if (can_go_up) { | |
| 185 View* parent = GetParent(starting_view); | |
| 186 while (parent && parent != root_) { | |
| 187 sibling = parent->GetNextFocusableView(); | |
| 188 if (sibling) { | |
| 189 return FindNextFocusableViewImpl(sibling, | |
| 190 true, true, true, | |
| 191 skip_group_id, | |
| 192 focus_traversable, | |
| 193 focus_traversable_view); | |
| 194 } | |
| 195 parent = GetParent(parent); | |
| 196 } | |
| 197 } | |
| 198 | |
| 199 // We found nothing. | |
| 200 return NULL; | |
| 201 } | |
| 202 | |
| 203 // Strategy for finding the previous focusable view: | |
| 204 // - keep going down on the right until you reach a view with no children, if it | |
| 205 // it is a good candidate return it. | |
| 206 // - start the search on the left sibling. | |
| 207 // - if there are no left sibling, start the search on the parent (without going | |
| 208 // down). | |
| 209 View* FocusSearch::FindPreviousFocusableViewImpl( | |
| 210 View* starting_view, | |
| 211 bool check_starting_view, | |
| 212 bool can_go_up, | |
| 213 bool can_go_down, | |
| 214 int skip_group_id, | |
| 215 FocusTraversable** focus_traversable, | |
| 216 View** focus_traversable_view) { | |
| 217 // Let's go down and right as much as we can. | |
| 218 if (can_go_down) { | |
| 219 // Before we go into the direct children, we have to check if this view has | |
| 220 // a FocusTraversable. | |
| 221 *focus_traversable = starting_view->GetFocusTraversable(); | |
| 222 if (*focus_traversable) { | |
| 223 *focus_traversable_view = starting_view; | |
| 224 return NULL; | |
| 225 } | |
| 226 | |
| 227 if (starting_view->has_children()) { | |
| 228 View* view = | |
| 229 starting_view->child_at(starting_view->child_count() - 1); | |
| 230 View* v = FindPreviousFocusableViewImpl(view, true, false, true, | |
| 231 skip_group_id, | |
| 232 focus_traversable, | |
| 233 focus_traversable_view); | |
| 234 if (v || *focus_traversable) | |
| 235 return v; | |
| 236 } | |
| 237 } | |
| 238 | |
| 239 // Then look at this view. Here, we do not need to see if the view has | |
| 240 // a FocusTraversable, since we do not want to go down any more. | |
| 241 if (check_starting_view && | |
| 242 IsViewFocusableCandidate(starting_view, skip_group_id)) { | |
| 243 View* v = FindSelectedViewForGroup(starting_view); | |
| 244 // The selected view might not be focusable (if it is disabled for | |
| 245 // example). | |
| 246 if (IsFocusable(v)) | |
| 247 return v; | |
| 248 } | |
| 249 | |
| 250 // Then try the left sibling. | |
| 251 View* sibling = starting_view->GetPreviousFocusableView(); | |
| 252 if (sibling) { | |
| 253 return FindPreviousFocusableViewImpl(sibling, | |
| 254 true, true, true, | |
| 255 skip_group_id, | |
| 256 focus_traversable, | |
| 257 focus_traversable_view); | |
| 258 } | |
| 259 | |
| 260 // Then go up the parent. | |
| 261 if (can_go_up) { | |
| 262 View* parent = GetParent(starting_view); | |
| 263 if (parent) | |
| 264 return FindPreviousFocusableViewImpl(parent, | |
| 265 true, true, false, | |
| 266 skip_group_id, | |
| 267 focus_traversable, | |
| 268 focus_traversable_view); | |
| 269 } | |
| 270 | |
| 271 // We found nothing. | |
| 272 return NULL; | |
| 273 } | |
| 274 | |
| 275 } // namespace views | |
| OLD | NEW |