OLD | NEW |
(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 |
OLD | NEW |