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

Side by Side Diff: content/browser/renderer_host/input/touch_selection_controller.cc

Issue 759433002: Reland: Move TouchSelectionController from content to ui (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Excluded ui/touch_selection from Windows GN build Created 6 years 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 2014 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 "content/browser/renderer_host/input/touch_selection_controller.h"
6
7 #include "base/auto_reset.h"
8 #include "base/logging.h"
9 #include "third_party/WebKit/public/web/WebInputEvent.h"
10
11 namespace content {
12 namespace {
13
14 TouchHandleOrientation ToTouchHandleOrientation(cc::SelectionBoundType type) {
15 switch (type) {
16 case cc::SELECTION_BOUND_LEFT:
17 return TOUCH_HANDLE_LEFT;
18 case cc::SELECTION_BOUND_RIGHT:
19 return TOUCH_HANDLE_RIGHT;
20 case cc::SELECTION_BOUND_CENTER:
21 return TOUCH_HANDLE_CENTER;
22 case cc::SELECTION_BOUND_EMPTY:
23 return TOUCH_HANDLE_ORIENTATION_UNDEFINED;
24 }
25 NOTREACHED() << "Invalid selection bound type: " << type;
26 return TOUCH_HANDLE_ORIENTATION_UNDEFINED;
27 }
28
29 } // namespace
30
31 TouchSelectionController::TouchSelectionController(
32 TouchSelectionControllerClient* client,
33 base::TimeDelta tap_timeout,
34 float tap_slop)
35 : client_(client),
36 tap_timeout_(tap_timeout),
37 tap_slop_(tap_slop),
38 response_pending_input_event_(INPUT_EVENT_TYPE_NONE),
39 start_orientation_(TOUCH_HANDLE_ORIENTATION_UNDEFINED),
40 end_orientation_(TOUCH_HANDLE_ORIENTATION_UNDEFINED),
41 is_insertion_active_(false),
42 activate_insertion_automatically_(false),
43 is_selection_active_(false),
44 activate_selection_automatically_(false),
45 selection_empty_(false),
46 selection_editable_(false),
47 temporarily_hidden_(false) {
48 DCHECK(client_);
49 HideAndDisallowShowingAutomatically();
50 }
51
52 TouchSelectionController::~TouchSelectionController() {
53 }
54
55 void TouchSelectionController::OnSelectionBoundsChanged(
56 const cc::ViewportSelectionBound& start,
57 const cc::ViewportSelectionBound& end) {
58 if (start == start_ && end_ == end)
59 return;
60
61 start_ = start;
62 end_ = end;
63 start_orientation_ = ToTouchHandleOrientation(start_.type);
64 end_orientation_ = ToTouchHandleOrientation(end_.type);
65
66 if (!activate_selection_automatically_ &&
67 !activate_insertion_automatically_) {
68 DCHECK_EQ(INPUT_EVENT_TYPE_NONE, response_pending_input_event_);
69 return;
70 }
71
72 // Ensure that |response_pending_input_event_| is cleared after the method
73 // completes, while also making its current value available for the duration
74 // of the call.
75 InputEventType causal_input_event = response_pending_input_event_;
76 response_pending_input_event_ = INPUT_EVENT_TYPE_NONE;
77 base::AutoReset<InputEventType> auto_reset_response_pending_input_event(
78 &response_pending_input_event_, causal_input_event);
79
80 const bool is_selection_dragging =
81 is_selection_active_ && (start_selection_handle_->is_dragging() ||
82 end_selection_handle_->is_dragging());
83
84 // It's possible that the bounds temporarily overlap while a selection handle
85 // is being dragged, incorrectly reporting a CENTER orientation.
86 // TODO(jdduke): This safeguard is racy, as it's possible the delayed response
87 // from handle positioning occurs *after* the handle dragging has ceased.
88 // Instead, prevent selection -> insertion transitions without an intervening
89 // action or selection clearing of some sort, crbug.com/392696.
90 if (is_selection_dragging) {
91 if (start_orientation_ == TOUCH_HANDLE_CENTER)
92 start_orientation_ = start_selection_handle_->orientation();
93 if (end_orientation_ == TOUCH_HANDLE_CENTER)
94 end_orientation_ = end_selection_handle_->orientation();
95 }
96
97 if (GetStartPosition() != GetEndPosition() ||
98 (is_selection_dragging &&
99 start_orientation_ != TOUCH_HANDLE_ORIENTATION_UNDEFINED &&
100 end_orientation_ != TOUCH_HANDLE_ORIENTATION_UNDEFINED)) {
101 OnSelectionChanged();
102 return;
103 }
104
105 if (start_orientation_ == TOUCH_HANDLE_CENTER && selection_editable_) {
106 OnInsertionChanged();
107 return;
108 }
109
110 HideAndDisallowShowingAutomatically();
111 }
112
113 bool TouchSelectionController::WillHandleTouchEvent(
114 const ui::MotionEvent& event) {
115 if (is_insertion_active_) {
116 DCHECK(insertion_handle_);
117 return insertion_handle_->WillHandleTouchEvent(event);
118 }
119
120 if (is_selection_active_) {
121 DCHECK(start_selection_handle_);
122 DCHECK(end_selection_handle_);
123 if (start_selection_handle_->is_dragging())
124 return start_selection_handle_->WillHandleTouchEvent(event);
125
126 if (end_selection_handle_->is_dragging())
127 return end_selection_handle_->WillHandleTouchEvent(event);
128
129 const gfx::PointF event_pos(event.GetX(), event.GetY());
130 if ((event_pos - GetStartPosition()).LengthSquared() <=
131 (event_pos - GetEndPosition()).LengthSquared())
132 return start_selection_handle_->WillHandleTouchEvent(event);
133 else
134 return end_selection_handle_->WillHandleTouchEvent(event);
135 }
136
137 return false;
138 }
139
140 void TouchSelectionController::OnLongPressEvent() {
141 response_pending_input_event_ = LONG_PRESS;
142 ShowSelectionHandlesAutomatically();
143 ShowInsertionHandleAutomatically();
144 ResetCachedValuesIfInactive();
145 }
146
147 void TouchSelectionController::AllowShowingFromCurrentSelection() {
148 if (is_selection_active_ || is_insertion_active_)
149 return;
150
151 activate_selection_automatically_ = true;
152 activate_insertion_automatically_ = true;
153 if (GetStartPosition() != GetEndPosition())
154 OnSelectionChanged();
155 else if (start_orientation_ == TOUCH_HANDLE_CENTER && selection_editable_)
156 OnInsertionChanged();
157 }
158
159 void TouchSelectionController::OnTapEvent() {
160 response_pending_input_event_ = TAP;
161 ShowInsertionHandleAutomatically();
162 if (selection_empty_)
163 DeactivateInsertion();
164 ResetCachedValuesIfInactive();
165 }
166
167 void TouchSelectionController::HideAndDisallowShowingAutomatically() {
168 response_pending_input_event_ = INPUT_EVENT_TYPE_NONE;
169 DeactivateInsertion();
170 DeactivateSelection();
171 activate_insertion_automatically_ = false;
172 activate_selection_automatically_ = false;
173 }
174
175 void TouchSelectionController::SetTemporarilyHidden(bool hidden) {
176 if (temporarily_hidden_ == hidden)
177 return;
178 temporarily_hidden_ = hidden;
179
180 TouchHandle::AnimationStyle animation_style = GetAnimationStyle(true);
181 if (is_selection_active_) {
182 start_selection_handle_->SetVisible(GetStartVisible(), animation_style);
183 end_selection_handle_->SetVisible(GetEndVisible(), animation_style);
184 }
185 if (is_insertion_active_)
186 insertion_handle_->SetVisible(GetStartVisible(), animation_style);
187 }
188
189 void TouchSelectionController::OnSelectionEditable(bool editable) {
190 if (selection_editable_ == editable)
191 return;
192 selection_editable_ = editable;
193 ResetCachedValuesIfInactive();
194 if (!selection_editable_)
195 DeactivateInsertion();
196 }
197
198 void TouchSelectionController::OnSelectionEmpty(bool empty) {
199 if (selection_empty_ == empty)
200 return;
201 selection_empty_ = empty;
202 ResetCachedValuesIfInactive();
203 }
204
205 bool TouchSelectionController::Animate(base::TimeTicks frame_time) {
206 if (is_insertion_active_)
207 return insertion_handle_->Animate(frame_time);
208
209 if (is_selection_active_) {
210 bool needs_animate = start_selection_handle_->Animate(frame_time);
211 needs_animate |= end_selection_handle_->Animate(frame_time);
212 return needs_animate;
213 }
214
215 return false;
216 }
217
218 void TouchSelectionController::OnHandleDragBegin(const TouchHandle& handle) {
219 if (&handle == insertion_handle_.get()) {
220 client_->OnSelectionEvent(INSERTION_DRAG_STARTED, handle.position());
221 return;
222 }
223
224 gfx::PointF base, extent;
225 if (&handle == start_selection_handle_.get()) {
226 base = end_selection_handle_->position() + GetEndLineOffset();
227 extent = start_selection_handle_->position() + GetStartLineOffset();
228 } else {
229 base = start_selection_handle_->position() + GetStartLineOffset();
230 extent = end_selection_handle_->position() + GetEndLineOffset();
231 }
232
233 // When moving the handle we want to move only the extent point. Before doing
234 // so we must make sure that the base point is set correctly.
235 client_->SelectBetweenCoordinates(base, extent);
236
237 client_->OnSelectionEvent(SELECTION_DRAG_STARTED, handle.position());
238 }
239
240 void TouchSelectionController::OnHandleDragUpdate(const TouchHandle& handle,
241 const gfx::PointF& position) {
242 // As the position corresponds to the bottom left point of the selection
243 // bound, offset it by half the corresponding line height.
244 gfx::Vector2dF line_offset = &handle == end_selection_handle_.get()
245 ? GetStartLineOffset()
246 : GetEndLineOffset();
247 gfx::PointF line_position = position + line_offset;
248 if (&handle == insertion_handle_.get()) {
249 client_->MoveCaret(line_position);
250 } else {
251 client_->MoveRangeSelectionExtent(line_position);
252 }
253 }
254
255 void TouchSelectionController::OnHandleDragEnd(const TouchHandle& handle) {
256 if (&handle != insertion_handle_.get())
257 client_->OnSelectionEvent(SELECTION_DRAG_STOPPED, handle.position());
258 }
259
260 void TouchSelectionController::OnHandleTapped(const TouchHandle& handle) {
261 if (insertion_handle_ && &handle == insertion_handle_.get())
262 client_->OnSelectionEvent(INSERTION_TAPPED, handle.position());
263 }
264
265 void TouchSelectionController::SetNeedsAnimate() {
266 client_->SetNeedsAnimate();
267 }
268
269 scoped_ptr<TouchHandleDrawable> TouchSelectionController::CreateDrawable() {
270 return client_->CreateDrawable();
271 }
272
273 base::TimeDelta TouchSelectionController::GetTapTimeout() const {
274 return tap_timeout_;
275 }
276
277 float TouchSelectionController::GetTapSlop() const {
278 return tap_slop_;
279 }
280
281 void TouchSelectionController::ShowInsertionHandleAutomatically() {
282 if (activate_insertion_automatically_)
283 return;
284 activate_insertion_automatically_ = true;
285 ResetCachedValuesIfInactive();
286 }
287
288 void TouchSelectionController::ShowSelectionHandlesAutomatically() {
289 if (activate_selection_automatically_)
290 return;
291 activate_selection_automatically_ = true;
292 ResetCachedValuesIfInactive();
293 }
294
295 void TouchSelectionController::OnInsertionChanged() {
296 DeactivateSelection();
297
298 if (response_pending_input_event_ == TAP && selection_empty_) {
299 HideAndDisallowShowingAutomatically();
300 return;
301 }
302
303 if (!activate_insertion_automatically_)
304 return;
305
306 const bool was_active = is_insertion_active_;
307 const gfx::PointF position = GetStartPosition();
308 if (!is_insertion_active_)
309 ActivateInsertion();
310 else
311 client_->OnSelectionEvent(INSERTION_MOVED, position);
312
313 insertion_handle_->SetVisible(GetStartVisible(),
314 GetAnimationStyle(was_active));
315 insertion_handle_->SetPosition(position);
316 }
317
318 void TouchSelectionController::OnSelectionChanged() {
319 DeactivateInsertion();
320
321 if (!activate_selection_automatically_)
322 return;
323
324 const bool was_active = is_selection_active_;
325 ActivateSelection();
326
327 const TouchHandle::AnimationStyle animation = GetAnimationStyle(was_active);
328 start_selection_handle_->SetVisible(GetStartVisible(), animation);
329 end_selection_handle_->SetVisible(GetEndVisible(), animation);
330
331 start_selection_handle_->SetPosition(GetStartPosition());
332 end_selection_handle_->SetPosition(GetEndPosition());
333 }
334
335 void TouchSelectionController::ActivateInsertion() {
336 DCHECK(!is_selection_active_);
337
338 if (!insertion_handle_)
339 insertion_handle_.reset(new TouchHandle(this, TOUCH_HANDLE_CENTER));
340
341 if (!is_insertion_active_) {
342 is_insertion_active_ = true;
343 insertion_handle_->SetEnabled(true);
344 client_->OnSelectionEvent(INSERTION_SHOWN, GetStartPosition());
345 }
346 }
347
348 void TouchSelectionController::DeactivateInsertion() {
349 if (!is_insertion_active_)
350 return;
351 DCHECK(insertion_handle_);
352 is_insertion_active_ = false;
353 insertion_handle_->SetEnabled(false);
354 client_->OnSelectionEvent(INSERTION_CLEARED, gfx::PointF());
355 }
356
357 void TouchSelectionController::ActivateSelection() {
358 DCHECK(!is_insertion_active_);
359
360 if (!start_selection_handle_) {
361 start_selection_handle_.reset(new TouchHandle(this, start_orientation_));
362 } else {
363 start_selection_handle_->SetEnabled(true);
364 start_selection_handle_->SetOrientation(start_orientation_);
365 }
366
367 if (!end_selection_handle_) {
368 end_selection_handle_.reset(new TouchHandle(this, end_orientation_));
369 } else {
370 end_selection_handle_->SetEnabled(true);
371 end_selection_handle_->SetOrientation(end_orientation_);
372 }
373
374 // As a long press received while a selection is already active may trigger
375 // an entirely new selection, notify the client but avoid sending an
376 // intervening SELECTION_CLEARED update to avoid unnecessary state changes.
377 if (!is_selection_active_ || response_pending_input_event_ == LONG_PRESS) {
378 is_selection_active_ = true;
379 response_pending_input_event_ = INPUT_EVENT_TYPE_NONE;
380 client_->OnSelectionEvent(SELECTION_SHOWN, GetStartPosition());
381 }
382 }
383
384 void TouchSelectionController::DeactivateSelection() {
385 if (!is_selection_active_)
386 return;
387 DCHECK(start_selection_handle_);
388 DCHECK(end_selection_handle_);
389 start_selection_handle_->SetEnabled(false);
390 end_selection_handle_->SetEnabled(false);
391 is_selection_active_ = false;
392 client_->OnSelectionEvent(SELECTION_CLEARED, gfx::PointF());
393 }
394
395 void TouchSelectionController::ResetCachedValuesIfInactive() {
396 if (is_selection_active_ || is_insertion_active_)
397 return;
398 start_ = cc::ViewportSelectionBound();
399 end_ = cc::ViewportSelectionBound();
400 start_orientation_ = TOUCH_HANDLE_ORIENTATION_UNDEFINED;
401 end_orientation_ = TOUCH_HANDLE_ORIENTATION_UNDEFINED;
402 }
403
404 const gfx::PointF& TouchSelectionController::GetStartPosition() const {
405 return start_.edge_bottom;
406 }
407
408 const gfx::PointF& TouchSelectionController::GetEndPosition() const {
409 return end_.edge_bottom;
410 }
411
412 gfx::Vector2dF TouchSelectionController::GetStartLineOffset() const {
413 return gfx::ScaleVector2d(start_.edge_top - start_.edge_bottom, 0.5f);
414 }
415
416 gfx::Vector2dF TouchSelectionController::GetEndLineOffset() const {
417 return gfx::ScaleVector2d(end_.edge_top - end_.edge_bottom, 0.5f);
418 }
419
420 bool TouchSelectionController::GetStartVisible() const {
421 return start_.visible && !temporarily_hidden_;
422 }
423
424 bool TouchSelectionController::GetEndVisible() const {
425 return end_.visible && !temporarily_hidden_;
426 }
427
428 TouchHandle::AnimationStyle TouchSelectionController::GetAnimationStyle(
429 bool was_active) const {
430 return was_active && client_->SupportsAnimation()
431 ? TouchHandle::ANIMATION_SMOOTH
432 : TouchHandle::ANIMATION_NONE;
433 }
434
435 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698