summaryrefslogtreecommitdiffstats
path: root/ui/touch_selection/touch_selection_controller.h
blob: ae0161b7734e3658f822d9a8ebc534206dcc1db6 (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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
// Copyright 2014 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_TOUCH_SELECTION_TOUCH_SELECTION_CONTROLLER_H_
#define UI_TOUCH_SELECTION_TOUCH_SELECTION_CONTROLLER_H_

#include "base/time/time.h"
#include "ui/base/touch/selection_bound.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/vector2d_f.h"
#include "ui/touch_selection/longpress_drag_selector.h"
#include "ui/touch_selection/selection_event_type.h"
#include "ui/touch_selection/touch_handle.h"
#include "ui/touch_selection/touch_handle_orientation.h"
#include "ui/touch_selection/ui_touch_selection_export.h"

namespace ui {
class MotionEvent;

// Interface through which |TouchSelectionController| issues selection-related
// commands, notifications and requests.
class UI_TOUCH_SELECTION_EXPORT TouchSelectionControllerClient {
 public:
  virtual ~TouchSelectionControllerClient() {}

  virtual bool SupportsAnimation() const = 0;
  virtual void SetNeedsAnimate() = 0;
  virtual void MoveCaret(const gfx::PointF& position) = 0;
  virtual void MoveRangeSelectionExtent(const gfx::PointF& extent) = 0;
  virtual void SelectBetweenCoordinates(const gfx::PointF& base,
                                        const gfx::PointF& extent) = 0;
  virtual void OnSelectionEvent(SelectionEventType event) = 0;
  virtual scoped_ptr<TouchHandleDrawable> CreateDrawable() = 0;
};

// Controller for manipulating text selection via touch input.
class UI_TOUCH_SELECTION_EXPORT TouchSelectionController
    : public TouchHandleClient,
      public LongPressDragSelectorClient {
 public:
  enum ActiveStatus {
    INACTIVE,
    INSERTION_ACTIVE,
    SELECTION_ACTIVE,
  };

  struct UI_TOUCH_SELECTION_EXPORT Config {
    Config();
    ~Config();

    // Maximum allowed time for handle tap detection. Defaults to 300 ms.
    base::TimeDelta max_tap_duration;

    // Defaults to 8 DIPs.
    float tap_slop;

    // Controls whether adaptive orientation for selection handles is enabled.
    // Defaults to false.
    bool enable_adaptive_handle_orientation;

    // Controls whether drag selection after a longpress is enabled.
    // Defaults to false.
    bool enable_longpress_drag_selection;

    // Controls whether an insertion handle is shown on a tap for an empty
    // editable text. Defauls to false.
    // TODO(mohsen): This flag used to be set to |true| on Aura. That's not the
    // case anymore and it is always |false|. Consider removing it.
    bool show_on_tap_for_empty_editable;
  };

  TouchSelectionController(TouchSelectionControllerClient* client,
                           const Config& config);
  ~TouchSelectionController() override;

  // To be called when the selection bounds have changed.
  // Note that such updates will trigger handle updates only if preceded
  // by an appropriate call to allow automatic showing.
  void OnSelectionBoundsChanged(const SelectionBound& start,
                                const SelectionBound& end);

  // To be called when the viewport rect has been changed. This is used for
  // setting the state of the handles.
  void OnViewportChanged(const gfx::RectF viewport_rect);

  // Allows touch-dragging of the handle.
  // Returns true iff the event was consumed, in which case the caller should
  // cease further handling of the event.
  bool WillHandleTouchEvent(const MotionEvent& event);

  // To be called before forwarding a tap event. This allows automatically
  // showing the insertion handle from subsequent bounds changes.
  // |tap_count| is tap index in a repeated sequence, i.e., 1 for the first
  // tap, 2 for the second tap, etc...
  bool WillHandleTapEvent(const gfx::PointF& location, int tap_count);

  // To be called before forwarding a longpress event. This allows automatically
  // showing the selection or insertion handles from subsequent bounds changes.
  bool WillHandleLongPressEvent(base::TimeTicks event_time,
                                const gfx::PointF& location);

  // Allow showing the selection handles from the most recent selection bounds
  // update (if valid), or a future valid bounds update.
  void AllowShowingFromCurrentSelection();

  // Hide the handles and suppress bounds updates until the next explicit
  // showing allowance.
  void HideAndDisallowShowingAutomatically();

  // Override the handle visibility according to |hidden|.
  void SetTemporarilyHidden(bool hidden);

  // To be called when the editability of the focused region changes.
  void OnSelectionEditable(bool editable);

  // To be called when the contents of the focused region changes.
  void OnSelectionEmpty(bool empty);

  // Ticks an active animation, as requested to the client by |SetNeedsAnimate|.
  // Returns true if an animation is active and requires further ticking.
  bool Animate(base::TimeTicks animate_time);

  // Returns the rect between the two active selection bounds. If just one of
  // the bounds is visible, the rect is simply the (one-dimensional) rect of
  // that bound. If no selection is active, an empty rect will be returned.
  gfx::RectF GetRectBetweenBounds() const;

  // Returns the visible rect of specified touch handle. For an active insertion
  // these values will be identical.
  gfx::RectF GetStartHandleRect() const;
  gfx::RectF GetEndHandleRect() const;

  // Returns the focal point of the start and end bounds, as defined by
  // their bottom coordinate.
  const gfx::PointF& GetStartPosition() const;
  const gfx::PointF& GetEndPosition() const;

  const SelectionBound& start() const { return start_; }
  const SelectionBound& end() const { return end_; }

  ActiveStatus active_status() const { return active_status_; }

 private:
  friend class TouchSelectionControllerTestApi;

  enum InputEventType { TAP, REPEATED_TAP, LONG_PRESS, INPUT_EVENT_TYPE_NONE };

  // TouchHandleClient implementation.
  void OnDragBegin(const TouchSelectionDraggable& draggable,
                   const gfx::PointF& drag_position) override;
  void OnDragUpdate(const TouchSelectionDraggable& draggable,
                    const gfx::PointF& drag_position) override;
  void OnDragEnd(const TouchSelectionDraggable& draggable) override;
  bool IsWithinTapSlop(const gfx::Vector2dF& delta) const override;
  void OnHandleTapped(const TouchHandle& handle) override;
  void SetNeedsAnimate() override;
  scoped_ptr<TouchHandleDrawable> CreateDrawable() override;
  base::TimeDelta GetMaxTapDuration() const override;
  bool IsAdaptiveHandleOrientationEnabled() const override;

  // LongPressDragSelectorClient implementation.
  void OnLongPressDragActiveStateChanged() override;
  gfx::PointF GetSelectionStart() const override;
  gfx::PointF GetSelectionEnd() const override;

  void ShowInsertionHandleAutomatically();
  void ShowSelectionHandlesAutomatically();
  bool WillHandleTapOrLongPress(const gfx::PointF& location);

  void OnInsertionChanged();
  void OnSelectionChanged();

  // Returns true if insertion mode was newly (re)activated.
  bool ActivateInsertionIfNecessary();
  void DeactivateInsertion();
  // Returns true if selection mode was newly (re)activated.
  bool ActivateSelectionIfNecessary();
  void DeactivateSelection();
  void ForceNextUpdateIfInactive();
  void UpdateHandleLayoutIfNecessary();

  bool WillHandleTouchEventForLongPressDrag(const MotionEvent& event);
  void SetTemporarilyHiddenForLongPressDrag(bool hidden);
  void RefreshHandleVisibility();

  gfx::Vector2dF GetStartLineOffset() const;
  gfx::Vector2dF GetEndLineOffset() const;
  bool GetStartVisible() const;
  bool GetEndVisible() const;
  TouchHandle::AnimationStyle GetAnimationStyle(bool was_active) const;

  void LogSelectionEnd();

  TouchSelectionControllerClient* const client_;
  const Config config_;

  // Whether to force an update on the next selection event even if the
  // cached selection matches the new selection.
  bool force_next_update_;

  InputEventType response_pending_input_event_;

  SelectionBound start_;
  SelectionBound end_;
  TouchHandleOrientation start_orientation_;
  TouchHandleOrientation end_orientation_;

  ActiveStatus active_status_;

  scoped_ptr<TouchHandle> insertion_handle_;
  bool activate_insertion_automatically_;

  scoped_ptr<TouchHandle> start_selection_handle_;
  scoped_ptr<TouchHandle> end_selection_handle_;
  bool activate_selection_automatically_;

  bool selection_empty_;
  bool selection_editable_;

  bool temporarily_hidden_;

  // Whether to use the start bound (if false, the end bound) for computing the
  // appropriate text line offset when performing a selection drag. This helps
  // ensure that the initial selection induced by the drag doesn't "jump"
  // between lines.
  bool anchor_drag_to_selection_start_;

  // Longpress drag allows direct manipulation of longpress-initiated selection.
  LongPressDragSelector longpress_drag_selector_;

  gfx::RectF viewport_rect_;

  base::TimeTicks selection_start_time_;
  // Whether a selection handle was dragged during the current 'selection
  // session' - i.e. since the current selection has been activated.
  bool selection_handle_dragged_;

  DISALLOW_COPY_AND_ASSIGN(TouchSelectionController);
};

}  // namespace ui

#endif  // UI_TOUCH_SELECTION_TOUCH_SELECTION_CONTROLLER_H_