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 276