| 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 |