summaryrefslogtreecommitdiff
path: root/chromium/ui/views/focus/focus_search.h
blob: ca05b86eab8a618ab6d4d1447e3192b05e2fc895 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef UI_VIEWS_FOCUS_FOCUS_SEARCH_H_
#define UI_VIEWS_FOCUS_FOCUS_SEARCH_H_

#include "base/containers/flat_set.h"
#include "base/macros.h"
#include "ui/views/view.h"

namespace views {

class FocusTraversable;

// FocusSearch is an object that implements the algorithm to find the
// next view to focus.
class VIEWS_EXPORT FocusSearch {
 public:
  // The direction in which the focus traversal is going.
  // TODO (jcampan): add support for lateral (left, right) focus traversal. The
  // goal is to switch to focusable views on the same level when using the arrow
  // keys (ala Windows: in a dialog box, arrow keys typically move between the
  // dialog OK, Cancel buttons).
  enum class TraversalDirection {
    kUp,
    kDown,
  };

  enum class SearchDirection {
    kForwards,
    kBackwards,
  };

  enum class StartingViewPolicy {
    kSkipStartingView,
    kCheckStartingView,
  };

  enum class AnchoredDialogPolicy {
    kSkipAnchoredDialog,
    kCanGoIntoAnchoredDialog,
  };

  // Constructor.
  // - |root| is the root of the view hierarchy to traverse. Focus will be
  //   trapped inside.
  // - |cycle| should be true if you want FindNextFocusableView to cycle back
  //           to the first view within this root when the traversal reaches
  //           the end. If this is true, then if you pass a valid starting
  //           view to FindNextFocusableView you will always get a valid view
  //           out, even if it's the same view.
  // - |accessibility_mode| should be true if full keyboard accessibility is
  //   needed and you want to check IsAccessibilityFocusable(), rather than
  //   IsFocusable().
  FocusSearch(View* root, bool cycle, bool accessibility_mode);
  virtual ~FocusSearch() {}

  // Finds the next view that should be focused and returns it. If a
  // FocusTraversable is found while searching for the focusable view,
  // returns NULL and sets |focus_traversable| to the FocusTraversable
  // and |focus_traversable_view| to the view associated with the
  // FocusTraversable.
  //
  // Return NULL if the end of the focus loop is reached, unless this object
  // was initialized with |cycle|=true, in which case it goes back to the
  // beginning when it reaches the end of the traversal.
  // - |starting_view| is the view that should be used as the starting point
  //   when looking for the previous/next view. It may be NULL (in which case
  //   the first/last view should be used depending if normal/reverse).
  // - |search_direction| whether we should find the next (kForwards) or
  //   previous (kReverse) view.
  // - |traversal_direction| specifies whether we are traversing down (meaning
  //   we should look into child views) or traversing up (don't look at
  //   child views).
  // - |check_starting_view| indicated if starting_view may obtain the next
  //   focus.
  // - |can_go_into_anchored_dialog| controls if focus is allowed to jump
  //   into a dialog anchored at one of the views being traversed.
  // - |focus_traversable| is set to the focus traversable that should be
  //   traversed if one is found (in which case the call returns NULL).
  // - |focus_traversable_view| is set to the view associated with the
  //   FocusTraversable set in the previous parameter (it is used as the
  //   starting view when looking for the next focusable view).
  virtual View* FindNextFocusableView(
      View* starting_view,
      SearchDirection search_direction,
      TraversalDirection traversal_direction,
      StartingViewPolicy check_starting_view,
      AnchoredDialogPolicy can_go_into_anchored_dialog,
      FocusTraversable** focus_traversable,
      View** focus_traversable_view);

 protected:
  // Get the parent, but stay within the root. Returns NULL if asked for
  // the parent of |root_|. Subclasses can override this if they need custom
  // focus search behavior.
  virtual View* GetParent(View* v);

  // Returns true if |v| is contained within the hierarchy rooted at |root|.
  // Subclasses can override this if they need custom focus search behavior.
  virtual bool Contains(View* root, const View* v);

  View* root() const { return root_; }

 private:
  // Convenience method that returns true if a view is focusable and does not
  // belong to the specified group.
  bool IsViewFocusableCandidate(View* v, int skip_group_id);

  // Convenience method; returns true if a view is not NULL and is focusable
  // (checking IsAccessibilityFocusable() if |accessibility_mode_| is true or
  // the associated FocusManager has keyboard accessibility enabled).
  bool IsFocusable(View* v);

  // Returns the view selected for the group of the selected view. If the view
  // does not belong to a group or if no view is selected in the group, the
  // specified view is returned.
  View* FindSelectedViewForGroup(View* view);

  // Returns the next focusable view or view containing a FocusTraversable
  // (NULL if none was found), starting at the starting_view.
  // |check_starting_view|, |can_go_up| and |can_go_down| controls the
  // traversal of the views hierarchy. |skip_group_id| specifies a group_id,
  // -1 means no group. All views from a group are traversed in one pass.
  View* FindNextFocusableViewImpl(
      View* starting_view,
      StartingViewPolicy check_starting_view,
      bool can_go_up,
      bool can_go_down,
      AnchoredDialogPolicy can_go_into_anchored_dialog,
      int skip_group_id,
      base::flat_set<View*>* seen_views,
      FocusTraversable** focus_traversable,
      View** focus_traversable_view);

  // Same as FindNextFocusableViewImpl but returns the previous focusable view.
  View* FindPreviousFocusableViewImpl(
      View* starting_view,
      StartingViewPolicy check_starting_view,
      bool can_go_up,
      bool can_go_down,
      AnchoredDialogPolicy can_go_into_anchored_dialog,
      int skip_group_id,
      base::flat_set<View*>* seen_views,
      FocusTraversable** focus_traversable,
      View** focus_traversable_view);

  View* root_;
  bool cycle_;
  bool accessibility_mode_;

  DISALLOW_COPY_AND_ASSIGN(FocusSearch);
};

}  // namespace views

#endif  // UI_VIEWS_FOCUS_FOCUS_SEARCH_H_