Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(640)

Side by Side Diff: ui/touch_selection/longpress_drag_selector.cc

Issue 1087893003: Support longpress drag selection (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Code review Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 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 "ui/touch_selection/longpress_drag_selector.h"
6
7 #include "ui/events/gesture_detection/motion_event.h"
8
9 namespace ui {
10
11 LongPressDragSelector::LongPressDragSelector(
12 LongPressDragSelectorClient* client)
13 : client_(client), state_(INACTIVE) {
14 }
15
16 LongPressDragSelector::~LongPressDragSelector() {
17 }
18
19 bool LongPressDragSelector::WillHandleTouchEvent(const MotionEvent& event) {
20 switch (event.GetAction()) {
21 case MotionEvent::ACTION_DOWN:
22 touch_down_position_.SetPoint(event.GetX(), event.GetY());
23 touch_down_time_ = event.GetEventTime();
24 longpress_drag_start_position_ = gfx::PointF();
25 SetState(LONGPRESS_PENDING);
26 return false;
27
28 case MotionEvent::ACTION_UP:
29 case MotionEvent::ACTION_CANCEL:
30 SetState(INACTIVE);
31 return false;
32
33 case MotionEvent::ACTION_MOVE:
34 break;
35
36 default:
37 return false;
38 }
39
40 if (state_ != DRAG_PENDING && state_ != DRAGGING)
41 return false;
42
43 gfx::PointF position(event.GetX(), event.GetY());
44 if (state_ == DRAGGING) {
45 gfx::PointF drag_position = position + longpress_drag_selection_offset_;
46 client_->OnDragUpdate(*this, drag_position);
47 return true;
48 }
49
50 // We can't use |touch_down_position_| as the offset anchor, as
51 // showing the selection UI may have shifted the motion coordinates.
52 if (longpress_drag_start_position_.IsOrigin()) {
53 longpress_drag_start_position_ = position;
54 return true;
55 }
56
57 // Allow an additional slop affordance after the longpress occurs.
58 gfx::Vector2dF delta = position - longpress_drag_start_position_;
59 if (client_->IsWithinTapSlop(delta))
60 return true;
61
62 gfx::PointF selection_start = client_->GetSelectionStart();
63 gfx::PointF selection_end = client_->GetSelectionEnd();
64 bool extend_selection_start = false;
65 if (std::abs(delta.y()) > std::abs(delta.x())) {
66 // If initial motion is up/down, extend the start/end selection bound.
67 extend_selection_start = delta.y() < 0;
68 } else {
69 // Otherwise extend the selection bound toward which we're moving.
70 // Note that, for mixed RTL text, or for multiline selections triggered
71 // by longpress, this may not pick the most suitable drag target
72 gfx::Vector2dF start_delta = selection_start - position;
73 if (!start_delta.IsZero())
74 start_delta.Scale(1.f / start_delta.Length());
75 gfx::Vector2dF end_delta = selection_end - position;
76 if (!end_delta.IsZero())
77 end_delta.Scale(1.f / start_delta.Length());
78 extend_selection_start =
79 gfx::DotProduct(start_delta, delta) > gfx::DotProduct(end_delta, delta);
80 }
81
82 gfx::PointF extent =
83 extend_selection_start ? selection_start : selection_end;
84 longpress_drag_selection_offset_ = extent - position;
85 client_->OnDragBegin(*this, extent);
86 SetState(DRAGGING);
87 return true;
88 }
89
90 bool LongPressDragSelector::IsDragging() const {
91 return state_ == DRAGGING;
92 }
93
94 void LongPressDragSelector::OnLongPressEvent(base::TimeTicks event_time,
95 const gfx::PointF& position) {
96 // We have no guarantees that the current gesture stream is aligned with the
97 // observed touch stream. We only know that the gesture sequence is downstream
98 // from the touch sequence. Using a time/distance heuristic helps ensure that
99 // the observed longpress corresponds to the active touch sequence.
100 if (state_ == LONGPRESS_PENDING &&
101 // Ensure the down event occurs *before* the longpress event. Use a
102 // small time epsilon to account for floating point time conversion.
103 (touch_down_time_ < event_time + base::TimeDelta::FromMicroseconds(10)) &&
104 client_->IsWithinTapSlop(touch_down_position_ - position)) {
105 SetState(SELECTION_PENDING);
106 }
107 }
108
109 void LongPressDragSelector::OnSelectionActivated() {
110 if (state_ == SELECTION_PENDING)
111 SetState(DRAG_PENDING);
112 }
113
114 void LongPressDragSelector::OnSelectionDeactivated() {
115 SetState(INACTIVE);
116 }
117
118 bool LongPressDragSelector::IsDetectingDrag() const {
119 return state_ == DRAGGING || state_ == DRAG_PENDING;
120 }
121
122 void LongPressDragSelector::SetState(SelectionState state) {
123 if (state_ == state)
124 return;
125
126 const bool was_dragging = IsDragging();
127 const bool was_detecting_drag = IsDetectingDrag();
128 state_ = state;
129
130 // TODO(jdduke): Add UMA for tracking relative longpress drag frequency.
131 if (was_dragging)
132 client_->OnDragEnd(*this);
133
134 if (was_detecting_drag != IsDetectingDrag())
135 client_->OnLongPressDragDetectionStateChanged();
136 }
137
138 } // namespace ui
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698