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

Unified 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, 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 side-by-side diff with in-line comments
Download patch
Index: ui/touch_selection/longpress_drag_selector.cc
diff --git a/ui/touch_selection/longpress_drag_selector.cc b/ui/touch_selection/longpress_drag_selector.cc
new file mode 100644
index 0000000000000000000000000000000000000000..ad68171a61910f198ed457e0003466b8895cf967
--- /dev/null
+++ b/ui/touch_selection/longpress_drag_selector.cc
@@ -0,0 +1,141 @@
+// Copyright 2015 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.
+
+#include "ui/touch_selection/longpress_drag_selector.h"
+
+#include "base/auto_reset.h"
+#include "ui/events/gesture_detection/motion_event.h"
+
+namespace ui {
+
+LongPressDragSelector::LongPressDragSelector(
+ LongPressDragSelectorClient* client)
+ : client_(client),
+ state_(INACTIVE),
+ has_longpress_drag_start_anchor_(false) {
+}
+
+LongPressDragSelector::~LongPressDragSelector() {
+}
+
+bool LongPressDragSelector::WillHandleTouchEvent(const MotionEvent& event) {
+ switch (event.GetAction()) {
+ case MotionEvent::ACTION_DOWN:
+ touch_down_position_.SetPoint(event.GetX(), event.GetY());
+ touch_down_time_ = event.GetEventTime();
+ has_longpress_drag_start_anchor_ = false;
+ SetState(LONGPRESS_PENDING);
+ return false;
+
+ case MotionEvent::ACTION_UP:
+ case MotionEvent::ACTION_CANCEL:
+ SetState(INACTIVE);
+ return false;
+
+ case MotionEvent::ACTION_MOVE:
+ break;
+
+ default:
+ return false;
+ }
+
+ if (state_ != DRAG_PENDING && state_ != DRAGGING)
+ return false;
+
+ gfx::PointF position(event.GetX(), event.GetY());
+ if (state_ == DRAGGING) {
+ gfx::PointF drag_position = position + longpress_drag_selection_offset_;
+ client_->OnDragUpdate(*this, drag_position);
+ return true;
+ }
+
+ // We can't use |touch_down_position_| as the offset anchor, as
+ // showing the selection UI may have shifted the motion coordinates.
+ if (!has_longpress_drag_start_anchor_) {
+ has_longpress_drag_start_anchor_ = true;
+ longpress_drag_start_anchor_ = position;
+ return true;
+ }
+
+ // Allow an additional slop affordance after the longpress occurs.
+ gfx::Vector2dF delta = position - longpress_drag_start_anchor_;
+ if (client_->IsWithinTapSlop(delta))
+ return true;
+
+ gfx::PointF selection_start = client_->GetSelectionStart();
+ gfx::PointF selection_end = client_->GetSelectionEnd();
+ bool extend_selection_start = false;
+ if (std::abs(delta.y()) > std::abs(delta.x())) {
+ // If initial motion is up/down, extend the start/end selection bound.
+ extend_selection_start = delta.y() < 0;
+ } else {
+ // Otherwise extend the selection bound toward which we're moving.
+ // Note that, for mixed RTL text, or for multiline selections triggered
+ // by longpress, this may not pick the most suitable drag target
+ gfx::Vector2dF start_delta = selection_start - position;
+
+ // The vectors must be normalized to make dot product comparison meaningful.
+ if (!start_delta.IsZero())
+ start_delta.Scale(1.f / start_delta.Length());
+ gfx::Vector2dF end_delta = selection_end - position;
+ if (!end_delta.IsZero())
+ end_delta.Scale(1.f / start_delta.Length());
+
+ // The larger the dot product the more similar the direction.
+ extend_selection_start =
+ gfx::DotProduct(start_delta, delta) > gfx::DotProduct(end_delta, delta);
+ }
+
+ gfx::PointF extent = extend_selection_start ? selection_start : selection_end;
+ longpress_drag_selection_offset_ = extent - position;
+ client_->OnDragBegin(*this, extent);
+ SetState(DRAGGING);
+ return true;
+}
+
+bool LongPressDragSelector::IsActive() const {
+ return state_ != INACTIVE && state_ != LONGPRESS_PENDING;
+}
+
+void LongPressDragSelector::OnLongPressEvent(base::TimeTicks event_time,
+ const gfx::PointF& position) {
+ // We have no guarantees that the current gesture stream is aligned with the
+ // observed touch stream. We only know that the gesture sequence is downstream
+ // from the touch sequence. Using a time/distance heuristic helps ensure that
+ // the observed longpress corresponds to the active touch sequence.
+ if (state_ == LONGPRESS_PENDING &&
+ // Ensure the down event occurs *before* the longpress event. Use a
+ // small time epsilon to account for floating point time conversion.
+ (touch_down_time_ < event_time + base::TimeDelta::FromMicroseconds(10)) &&
+ client_->IsWithinTapSlop(touch_down_position_ - position)) {
+ SetState(SELECTION_PENDING);
+ }
+}
+
+void LongPressDragSelector::OnSelectionActivated() {
+ if (state_ == SELECTION_PENDING)
+ SetState(DRAG_PENDING);
+}
+
+void LongPressDragSelector::OnSelectionDeactivated() {
+ SetState(INACTIVE);
+}
+
+void LongPressDragSelector::SetState(SelectionState state) {
+ if (state_ == state)
+ return;
+
+ const bool was_dragging = state_ == DRAGGING;
+ const bool was_active = IsActive();
+ state_ = state;
+
+ // TODO(jdduke): Add UMA for tracking relative longpress drag frequency.
+ if (was_dragging)
+ client_->OnDragEnd(*this);
+
+ if (was_active != IsActive())
+ client_->OnLongPressDragActiveStateChanged();
+}
+
+} // namespace ui
« 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