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

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

Powered by Google App Engine
This is Rietveld 408576698