Index: ui/touch_selection/touch_selection_controller.cc |
diff --git a/ui/touch_selection/touch_selection_controller.cc b/ui/touch_selection/touch_selection_controller.cc |
index 3dbaaa008ecd0da64d47dcae6cf703e032b85c4c..a5facdcba480a20084fc5b5cb08e7080aeffd779 100644 |
--- a/ui/touch_selection/touch_selection_controller.cc |
+++ b/ui/touch_selection/touch_selection_controller.cc |
@@ -40,16 +40,22 @@ TouchHandleOrientation ToTouchHandleOrientation(SelectionBound::Type type) { |
} // namespace |
+TouchSelectionController::Config::Config() |
+ : tap_timeout(base::TimeDelta::FromMilliseconds(100)), |
+ tap_slop(8), |
+ enable_longpress_drag_selection(false), |
+ show_on_tap_for_empty_editable(false) { |
+} |
+ |
+TouchSelectionController::Config::~Config() { |
+} |
+ |
TouchSelectionController::TouchSelectionController( |
TouchSelectionControllerClient* client, |
- base::TimeDelta tap_timeout, |
- float tap_slop, |
- bool show_on_tap_for_empty_editable) |
+ const Config& config) |
: client_(client), |
- tap_timeout_(tap_timeout), |
- tap_slop_(tap_slop), |
+ config_(config), |
force_next_update_(false), |
- show_on_tap_for_empty_editable_(show_on_tap_for_empty_editable), |
response_pending_input_event_(INPUT_EVENT_TYPE_NONE), |
start_orientation_(TouchHandleOrientation::UNDEFINED), |
end_orientation_(TouchHandleOrientation::UNDEFINED), |
@@ -59,6 +65,8 @@ TouchSelectionController::TouchSelectionController( |
selection_empty_(false), |
selection_editable_(false), |
temporarily_hidden_(false), |
+ anchor_drag_to_selection_start_(false), |
+ longpress_drag_selector_(this), |
selection_handle_dragged_(false) { |
DCHECK(client_); |
} |
@@ -94,8 +102,8 @@ void TouchSelectionController::OnSelectionBoundsChanged( |
&response_pending_input_event_, causal_input_event); |
const bool is_selection_dragging = active_status_ == SELECTION_ACTIVE && |
- (start_selection_handle_->is_dragging() || |
- end_selection_handle_->is_dragging()); |
+ (start_selection_handle_->IsActive() || |
+ end_selection_handle_->IsActive()); |
// It's possible that the bounds temporarily overlap while a selection handle |
// is being dragged, incorrectly reporting a CENTER orientation. |
@@ -128,6 +136,11 @@ void TouchSelectionController::OnSelectionBoundsChanged( |
} |
bool TouchSelectionController::WillHandleTouchEvent(const MotionEvent& event) { |
+ if (config_.enable_longpress_drag_selection && |
+ longpress_drag_selector_.WillHandleTouchEvent(event)) { |
+ return true; |
+ } |
+ |
if (active_status_ == INSERTION_ACTIVE) { |
DCHECK(insertion_handle_); |
return insertion_handle_->WillHandleTouchEvent(event); |
@@ -136,10 +149,10 @@ bool TouchSelectionController::WillHandleTouchEvent(const MotionEvent& event) { |
if (active_status_ == SELECTION_ACTIVE) { |
DCHECK(start_selection_handle_); |
DCHECK(end_selection_handle_); |
- if (start_selection_handle_->is_dragging()) |
+ if (start_selection_handle_->IsActive()) |
return start_selection_handle_->WillHandleTouchEvent(event); |
- if (end_selection_handle_->is_dragging()) |
+ if (end_selection_handle_->IsActive()) |
return end_selection_handle_->WillHandleTouchEvent(event); |
const gfx::PointF event_pos(event.GetX(), event.GetY()); |
@@ -161,17 +174,19 @@ bool TouchSelectionController::WillHandleTapEvent(const gfx::PointF& location) { |
if (active_status_ != SELECTION_ACTIVE) |
activate_selection_automatically_ = false; |
ShowInsertionHandleAutomatically(); |
- if (selection_empty_ && !show_on_tap_for_empty_editable_) |
+ if (selection_empty_ && !config_.show_on_tap_for_empty_editable) |
DeactivateInsertion(); |
ForceNextUpdateIfInactive(); |
return false; |
} |
bool TouchSelectionController::WillHandleLongPressEvent( |
+ base::TimeTicks event_time, |
const gfx::PointF& location) { |
if (WillHandleTapOrLongPress(location)) |
return true; |
+ longpress_drag_selector_.OnLongPressEvent(event_time, location); |
response_pending_input_event_ = LONG_PRESS; |
ShowSelectionHandlesAutomatically(); |
ShowInsertionHandleAutomatically(); |
@@ -205,14 +220,7 @@ void TouchSelectionController::SetTemporarilyHidden(bool hidden) { |
if (temporarily_hidden_ == hidden) |
return; |
temporarily_hidden_ = hidden; |
- |
- TouchHandle::AnimationStyle animation_style = GetAnimationStyle(true); |
- if (active_status_ == SELECTION_ACTIVE) { |
- start_selection_handle_->SetVisible(GetStartVisible(), animation_style); |
- end_selection_handle_->SetVisible(GetEndVisible(), animation_style); |
- } else if (active_status_ == INSERTION_ACTIVE) { |
- insertion_handle_->SetVisible(GetStartVisible(), animation_style); |
- } |
+ RefreshHandleVisibility(); |
} |
void TouchSelectionController::OnSelectionEditable(bool editable) { |
@@ -283,20 +291,34 @@ const gfx::PointF& TouchSelectionController::GetEndPosition() const { |
return end_.edge_bottom(); |
} |
-void TouchSelectionController::OnHandleDragBegin(const TouchHandle& handle) { |
- if (&handle == insertion_handle_.get()) { |
+void TouchSelectionController::OnDragBegin( |
+ const TouchSelectionDraggable& draggable, |
+ const gfx::PointF& drag_position) { |
+ if (&draggable == insertion_handle_.get()) { |
+ DCHECK_EQ(active_status_, INSERTION_ACTIVE); |
client_->OnSelectionEvent(INSERTION_DRAG_STARTED); |
+ anchor_drag_to_selection_start_ = true; |
return; |
} |
- gfx::PointF base, extent; |
- if (&handle == start_selection_handle_.get()) { |
- base = end_selection_handle_->position() + GetEndLineOffset(); |
- extent = start_selection_handle_->position() + GetStartLineOffset(); |
+ DCHECK_EQ(active_status_, SELECTION_ACTIVE); |
+ |
+ if (&draggable == start_selection_handle_.get()) { |
+ anchor_drag_to_selection_start_ = true; |
+ } else if (&draggable == end_selection_handle_.get()) { |
+ anchor_drag_to_selection_start_ = false; |
} else { |
- base = start_selection_handle_->position() + GetStartLineOffset(); |
- extent = end_selection_handle_->position() + GetEndLineOffset(); |
+ DCHECK_EQ(&draggable, &longpress_drag_selector_); |
+ anchor_drag_to_selection_start_ = |
+ (drag_position - GetStartPosition()).LengthSquared() < |
+ (drag_position - GetEndPosition()).LengthSquared(); |
} |
+ |
+ gfx::PointF base = GetStartPosition() + GetStartLineOffset(); |
+ gfx::PointF extent = GetEndPosition() + GetEndLineOffset(); |
+ if (anchor_drag_to_selection_start_) |
+ std::swap(base, extent); |
+ |
selection_handle_dragged_ = true; |
// When moving the handle we want to move only the extent point. Before doing |
@@ -305,27 +327,35 @@ void TouchSelectionController::OnHandleDragBegin(const TouchHandle& handle) { |
client_->OnSelectionEvent(SELECTION_DRAG_STARTED); |
} |
-void TouchSelectionController::OnHandleDragUpdate(const TouchHandle& handle, |
- const gfx::PointF& position) { |
+void TouchSelectionController::OnDragUpdate( |
+ const TouchSelectionDraggable& draggable, |
+ const gfx::PointF& drag_position) { |
// As the position corresponds to the bottom left point of the selection |
- // bound, offset it by half the corresponding line height. |
- gfx::Vector2dF line_offset = &handle == start_selection_handle_.get() |
+ // bound, offset it to some reasonable point on the current line of text. |
+ gfx::Vector2dF line_offset = anchor_drag_to_selection_start_ |
? GetStartLineOffset() |
: GetEndLineOffset(); |
- gfx::PointF line_position = position + line_offset; |
- if (&handle == insertion_handle_.get()) |
+ gfx::PointF line_position = drag_position + line_offset; |
+ if (&draggable == insertion_handle_.get()) |
client_->MoveCaret(line_position); |
else |
client_->MoveRangeSelectionExtent(line_position); |
} |
-void TouchSelectionController::OnHandleDragEnd(const TouchHandle& handle) { |
- if (&handle == insertion_handle_.get()) |
+void TouchSelectionController::OnDragEnd( |
+ const TouchSelectionDraggable& draggable) { |
+ if (&draggable == insertion_handle_.get()) |
client_->OnSelectionEvent(INSERTION_DRAG_STOPPED); |
else |
client_->OnSelectionEvent(SELECTION_DRAG_STOPPED); |
} |
+bool TouchSelectionController::IsWithinTapSlop( |
+ const gfx::Vector2dF& delta) const { |
+ return delta.LengthSquared() < |
+ (static_cast<double>(config_.tap_slop) * config_.tap_slop); |
+} |
+ |
void TouchSelectionController::OnHandleTapped(const TouchHandle& handle) { |
if (insertion_handle_ && &handle == insertion_handle_.get()) |
client_->OnSelectionEvent(INSERTION_TAPPED); |
@@ -340,11 +370,21 @@ scoped_ptr<TouchHandleDrawable> TouchSelectionController::CreateDrawable() { |
} |
base::TimeDelta TouchSelectionController::GetTapTimeout() const { |
- return tap_timeout_; |
+ return config_.tap_timeout; |
} |
-float TouchSelectionController::GetTapSlop() const { |
- return tap_slop_; |
+void TouchSelectionController::OnLongPressDragActiveStateChanged() { |
+ // The handles should remain hidden for the duration of a longpress drag, |
+ // including the time between a longpress and the start of drag motion. |
+ RefreshHandleVisibility(); |
+} |
+ |
+gfx::PointF TouchSelectionController::GetSelectionStart() const { |
+ return GetStartPosition(); |
+} |
+ |
+gfx::PointF TouchSelectionController::GetSelectionEnd() const { |
+ return GetEndPosition(); |
} |
void TouchSelectionController::ShowInsertionHandleAutomatically() { |
@@ -381,7 +421,7 @@ void TouchSelectionController::OnInsertionChanged() { |
DeactivateSelection(); |
if (response_pending_input_event_ == TAP && selection_empty_ && |
- !show_on_tap_for_empty_editable_) { |
+ !config_.show_on_tap_for_empty_editable) { |
HideAndDisallowShowingAutomatically(); |
return; |
} |
@@ -470,6 +510,7 @@ bool TouchSelectionController::ActivateSelectionIfNecessary() { |
selection_handle_dragged_ = false; |
selection_start_time_ = base::TimeTicks::Now(); |
response_pending_input_event_ = INPUT_EVENT_TYPE_NONE; |
+ longpress_drag_selector_.OnSelectionActivated(); |
return true; |
} |
return false; |
@@ -481,6 +522,7 @@ void TouchSelectionController::DeactivateSelection() { |
DCHECK(start_selection_handle_); |
DCHECK(end_selection_handle_); |
LogSelectionEnd(); |
+ longpress_drag_selector_.OnSelectionDeactivated(); |
start_selection_handle_->SetEnabled(false); |
end_selection_handle_->SetEnabled(false); |
active_status_ = INACTIVE; |
@@ -498,6 +540,16 @@ void TouchSelectionController::ForceNextUpdateIfInactive() { |
} |
} |
+void TouchSelectionController::RefreshHandleVisibility() { |
+ TouchHandle::AnimationStyle animation_style = GetAnimationStyle(true); |
+ if (active_status_ == SELECTION_ACTIVE) { |
+ start_selection_handle_->SetVisible(GetStartVisible(), animation_style); |
+ end_selection_handle_->SetVisible(GetEndVisible(), animation_style); |
+ } |
+ if (active_status_ == INSERTION_ACTIVE) |
+ insertion_handle_->SetVisible(GetStartVisible(), animation_style); |
+} |
+ |
gfx::Vector2dF TouchSelectionController::GetStartLineOffset() const { |
return ComputeLineOffsetFromBottom(start_); |
} |
@@ -507,11 +559,17 @@ gfx::Vector2dF TouchSelectionController::GetEndLineOffset() const { |
} |
bool TouchSelectionController::GetStartVisible() const { |
- return start_.visible() && !temporarily_hidden_; |
+ if (!start_.visible()) |
+ return false; |
+ |
+ return !temporarily_hidden_ && !longpress_drag_selector_.IsActive(); |
} |
bool TouchSelectionController::GetEndVisible() const { |
- return end_.visible() && !temporarily_hidden_; |
+ if (!end_.visible()) |
+ return false; |
+ |
+ return !temporarily_hidden_ && !longpress_drag_selector_.IsActive(); |
} |
TouchHandle::AnimationStyle TouchSelectionController::GetAnimationStyle( |