123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711 |
- // Copyright 2014 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/touch_selection_controller.h"
- #include <memory>
- #include "base/auto_reset.h"
- #include "base/check_op.h"
- #include "base/metrics/histogram_macros.h"
- #include "base/metrics/user_metrics.h"
- #include "base/notreached.h"
- namespace ui {
- namespace {
- gfx::Vector2dF ComputeLineOffsetFromBottom(const gfx::SelectionBound& bound) {
- gfx::Vector2dF line_offset =
- gfx::ScaleVector2d(bound.edge_start() - bound.edge_end(), 0.5f);
- // An offset of 8 DIPs is sufficient for most line sizes. For small lines,
- // using half the line height avoids synthesizing a point on a line above
- // (or below) the intended line.
- const gfx::Vector2dF kMaxLineOffset(8.f, 8.f);
- line_offset.SetToMin(kMaxLineOffset);
- line_offset.SetToMax(-kMaxLineOffset);
- return line_offset;
- }
- TouchHandleOrientation ToTouchHandleOrientation(
- gfx::SelectionBound::Type type) {
- switch (type) {
- case gfx::SelectionBound::LEFT:
- return TouchHandleOrientation::LEFT;
- case gfx::SelectionBound::RIGHT:
- return TouchHandleOrientation::RIGHT;
- case gfx::SelectionBound::CENTER:
- return TouchHandleOrientation::CENTER;
- case gfx::SelectionBound::EMPTY:
- return TouchHandleOrientation::UNDEFINED;
- }
- NOTREACHED() << "Invalid selection bound type: " << type;
- return TouchHandleOrientation::UNDEFINED;
- }
- } // namespace
- TouchSelectionController::Config::Config()
- : max_tap_duration(base::Milliseconds(300)),
- tap_slop(8),
- enable_adaptive_handle_orientation(false),
- enable_longpress_drag_selection(false),
- hide_active_handle(false) {}
- TouchSelectionController::Config::~Config() {
- }
- TouchSelectionController::TouchSelectionController(
- TouchSelectionControllerClient* client,
- const Config& config)
- : client_(client),
- config_(config),
- response_pending_input_event_(INPUT_EVENT_TYPE_NONE),
- start_orientation_(TouchHandleOrientation::UNDEFINED),
- end_orientation_(TouchHandleOrientation::UNDEFINED),
- active_status_(INACTIVE),
- temporarily_hidden_(false),
- anchor_drag_to_selection_start_(false),
- longpress_drag_selector_(this),
- selection_handle_dragged_(false),
- consume_touch_sequence_(false),
- show_touch_handles_(false) {
- DCHECK(client_);
- }
- TouchSelectionController::~TouchSelectionController() {
- }
- void TouchSelectionController::OnSelectionBoundsChanged(
- const gfx::SelectionBound& start,
- const gfx::SelectionBound& end) {
- if (start == start_ && end_ == end)
- return;
- if (start.type() == gfx::SelectionBound::EMPTY ||
- end.type() == gfx::SelectionBound::EMPTY ||
- !show_touch_handles_) {
- HideHandles();
- return;
- }
- // Swap the Handles when the start and end selection points cross each other.
- if (active_status_ == SELECTION_ACTIVE) {
- // Bounds have the same orientation.
- bool need_swap = (start_selection_handle_->IsActive() &&
- end_.edge_end() == start.edge_end()) ||
- (end_selection_handle_->IsActive() &&
- end.edge_end() == start_.edge_end());
- // Bounds have different orientation.
- // Specifically, for writing-mode: vertical-*, selection bounds are
- // horizontal.
- // When vertical-lr:
- // - start bound is from right to left,
- // - end bound is from left to right.
- // When vertical-rl:
- // - start bound is from left to right,
- // - end bound is from right to left.
- // So when previous start/end bound become current end/start bound,
- // edge_start() and edge_end() are swapped. Therefore, we are comparing
- // edge_end() with edge_start() here.
- need_swap |= (start_selection_handle_->IsActive() &&
- end_.edge_end() == start.edge_start()) ||
- (end_selection_handle_->IsActive() &&
- end.edge_end() == start_.edge_start());
- if (need_swap)
- start_selection_handle_.swap(end_selection_handle_);
- }
- // Update |anchor_drag_to_selection_start_| for long press drag selector.
- // Since selection can be updated with only one end at a time, if one end is
- // equal to the previous value, the updated end is the other.
- if (start_ == start)
- anchor_drag_to_selection_start_ = false;
- else if (end_ == end)
- anchor_drag_to_selection_start_ = true;
- start_ = start;
- end_ = end;
- start_orientation_ = ToTouchHandleOrientation(start_.type());
- end_orientation_ = ToTouchHandleOrientation(end_.type());
- // Ensure that |response_pending_input_event_| is cleared after the method
- // completes, while also making its current value available for the duration
- // of the call.
- InputEventType causal_input_event = response_pending_input_event_;
- response_pending_input_event_ = INPUT_EVENT_TYPE_NONE;
- base::AutoReset<InputEventType> auto_reset_response_pending_input_event(
- &response_pending_input_event_, causal_input_event);
- if ((start_orientation_ == TouchHandleOrientation::LEFT ||
- start_orientation_ == TouchHandleOrientation::RIGHT) &&
- (end_orientation_ == TouchHandleOrientation::RIGHT ||
- end_orientation_ == TouchHandleOrientation::LEFT)) {
- OnSelectionChanged();
- return;
- }
- if (start_orientation_ == TouchHandleOrientation::CENTER) {
- OnInsertionChanged();
- return;
- }
- HideHandles();
- }
- void TouchSelectionController::OnViewportChanged(
- const gfx::RectF viewport_rect) {
- // Trigger a force update if the viewport is changed, so that
- // it triggers a call to change the mirror values if required.
- if (viewport_rect_ == viewport_rect)
- return;
- viewport_rect_ = viewport_rect;
- if (active_status_ == INACTIVE)
- return;
- if (active_status_ == INSERTION_ACTIVE) {
- DCHECK(insertion_handle_);
- insertion_handle_->SetViewportRect(viewport_rect);
- } else if (active_status_ == SELECTION_ACTIVE) {
- DCHECK(start_selection_handle_);
- DCHECK(end_selection_handle_);
- start_selection_handle_->SetViewportRect(viewport_rect);
- end_selection_handle_->SetViewportRect(viewport_rect);
- }
- // Update handle layout after setting the new Viewport size.
- UpdateHandleLayoutIfNecessary();
- }
- bool TouchSelectionController::WillHandleTouchEvent(const MotionEvent& event) {
- bool handled = WillHandleTouchEventImpl(event);
- // If Action::DOWN is consumed, the rest of touch sequence should be consumed,
- // too, regardless of value of |handled|.
- // TODO(mohsen): This will consume touch events until the next Action::DOWN.
- // Ideally we should consume until the final Action::UP/Action::CANCEL.
- // But, apparently, we can't reliably determine the final Action::CANCEL in a
- // multi-touch scenario. See https://crbug.com/653212.
- if (event.GetAction() == MotionEvent::Action::DOWN)
- consume_touch_sequence_ = handled;
- return handled || consume_touch_sequence_;
- }
- void TouchSelectionController::HandleTapEvent(const gfx::PointF& location,
- int tap_count) {
- if (tap_count > 1) {
- response_pending_input_event_ = REPEATED_TAP;
- } else {
- response_pending_input_event_ = TAP;
- }
- }
- void TouchSelectionController::HandleLongPressEvent(
- base::TimeTicks event_time,
- const gfx::PointF& location) {
- longpress_drag_selector_.OnLongPressEvent(event_time, location);
- response_pending_input_event_ = LONG_PRESS;
- }
- void TouchSelectionController::OnScrollBeginEvent() {
- // When there is an active selection, if the user performs a long-press that
- // does not trigger a new selection (e.g. a long-press on an empty area) and
- // then scrolls, the scroll will move the selection. In this case we will
- // think incorrectly that the selection change was due to the long-press and
- // will activate touch selection and start long-press drag gesture (see
- // ActivateInsertionIfNecessary()). To prevent this, we need to reset the
- // state of touch selection controller and long-press drag selector.
- // TODO(mohsen): Remove this workaround when we have enough information about
- // the cause of a selection change (see https://crbug.com/571897).
- longpress_drag_selector_.OnScrollBeginEvent();
- response_pending_input_event_ = INPUT_EVENT_TYPE_NONE;
- }
- void TouchSelectionController::HideHandles() {
- response_pending_input_event_ = INPUT_EVENT_TYPE_NONE;
- DeactivateInsertion();
- DeactivateSelection();
- start_ = gfx::SelectionBound();
- end_ = gfx::SelectionBound();
- start_orientation_ = ToTouchHandleOrientation(start_.type());
- end_orientation_ = ToTouchHandleOrientation(end_.type());
- }
- void TouchSelectionController::HideAndDisallowShowingAutomatically() {
- HideHandles();
- show_touch_handles_ = false;
- }
- void TouchSelectionController::SetTemporarilyHidden(bool hidden) {
- if (temporarily_hidden_ == hidden)
- return;
- temporarily_hidden_ = hidden;
- RefreshHandleVisibility();
- }
- bool TouchSelectionController::Animate(base::TimeTicks frame_time) {
- if (active_status_ == INSERTION_ACTIVE)
- return insertion_handle_->Animate(frame_time);
- if (active_status_ == SELECTION_ACTIVE) {
- bool needs_animate = start_selection_handle_->Animate(frame_time);
- needs_animate |= end_selection_handle_->Animate(frame_time);
- return needs_animate;
- }
- return false;
- }
- gfx::RectF TouchSelectionController::GetRectBetweenBounds() const {
- // Short-circuit for efficiency.
- if (active_status_ == INACTIVE)
- return gfx::RectF();
- if (start_.visible() && !end_.visible()) {
- // This BoundingRect is actually a line unless the selection is rotated.
- return gfx::BoundingRect(start_.edge_start(), start_.edge_end());
- }
- if (end_.visible() && !start_.visible()) {
- // This BoundingRect is actually a line unless the selection is rotated.
- return gfx::BoundingRect(end_.edge_start(), end_.edge_end());
- }
- // If both handles are visible, or both are invisible, use the entire rect.
- // Specifically, if both handles are on the same horizontal line for
- // writing-mode: vertical-*, or both are on the same vertical line for
- // writing-mode: horizontal, the entire rect is actually a line unless the
- // selection is rotated.
- return RectFBetweenSelectionBounds(start_, end_);
- }
- gfx::RectF TouchSelectionController::GetVisibleRectBetweenBounds() const {
- // Short-circuit for efficiency.
- if (active_status_ == INACTIVE)
- return gfx::RectF();
- // Returns the rect of the entire visible selection rect.
- return RectFBetweenVisibleSelectionBounds(start_, end_);
- }
- gfx::RectF TouchSelectionController::GetStartHandleRect() const {
- if (active_status_ == INSERTION_ACTIVE)
- return insertion_handle_->GetVisibleBounds();
- if (active_status_ == SELECTION_ACTIVE)
- return start_selection_handle_->GetVisibleBounds();
- return gfx::RectF();
- }
- gfx::RectF TouchSelectionController::GetEndHandleRect() const {
- if (active_status_ == INSERTION_ACTIVE)
- return insertion_handle_->GetVisibleBounds();
- if (active_status_ == SELECTION_ACTIVE)
- return end_selection_handle_->GetVisibleBounds();
- return gfx::RectF();
- }
- float TouchSelectionController::GetTouchHandleHeight() const {
- if (active_status_ == INSERTION_ACTIVE)
- return insertion_handle_->GetVisibleBounds().height();
- if (active_status_ == SELECTION_ACTIVE) {
- if (GetStartVisible())
- return start_selection_handle_->GetVisibleBounds().height();
- if (GetEndVisible())
- return end_selection_handle_->GetVisibleBounds().height();
- }
- return 0.f;
- }
- float TouchSelectionController::GetActiveHandleMiddleY() const {
- const gfx::SelectionBound* bound = nullptr;
- if (active_status_ == INSERTION_ACTIVE && insertion_handle_->IsActive())
- bound = &start_;
- if (active_status_ == SELECTION_ACTIVE) {
- if (start_selection_handle_->IsActive())
- bound = &start_;
- else if (end_selection_handle_->IsActive())
- bound = &end_;
- }
- if (!bound)
- return 0.f;
- return (bound->edge_start().y() + bound->edge_end().y()) / 2.f;
- }
- const gfx::PointF& TouchSelectionController::GetStartPosition() const {
- return start_.edge_end();
- }
- const gfx::PointF& TouchSelectionController::GetEndPosition() const {
- return end_.edge_end();
- }
- bool TouchSelectionController::WillHandleTouchEventImpl(
- const MotionEvent& event) {
- show_touch_handles_ = true;
- 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);
- }
- if (active_status_ == SELECTION_ACTIVE) {
- DCHECK(start_selection_handle_);
- DCHECK(end_selection_handle_);
- if (start_selection_handle_->IsActive())
- return start_selection_handle_->WillHandleTouchEvent(event);
- if (end_selection_handle_->IsActive())
- return end_selection_handle_->WillHandleTouchEvent(event);
- const gfx::PointF event_pos(event.GetX(), event.GetY());
- if ((event_pos - GetStartPosition()).LengthSquared() <=
- (event_pos - GetEndPosition()).LengthSquared()) {
- return start_selection_handle_->WillHandleTouchEvent(event);
- }
- return end_selection_handle_->WillHandleTouchEvent(event);
- }
- return false;
- }
- void TouchSelectionController::OnSwipeToMoveCursorBegin() {
- if (config_.hide_active_handle) {
- // Hide the handle when magnifier is showing since it can confuse the user.
- SetTemporarilyHidden(true);
- // If the user has typed something, the insertion handle might be hidden.
- // Prepare to show touch handles on end.
- show_touch_handles_ = true;
- }
- }
- void TouchSelectionController::OnSwipeToMoveCursorEnd() {
- // Show the handle at the end if magnifier was showing.
- if (config_.hide_active_handle)
- SetTemporarilyHidden(false);
- }
- void TouchSelectionController::OnDragBegin(
- const TouchSelectionDraggable& draggable,
- const gfx::PointF& drag_position) {
- if (&draggable == insertion_handle_.get()) {
- DCHECK_EQ(active_status_, INSERTION_ACTIVE);
- if (config_.hide_active_handle)
- insertion_handle_->SetTransparent();
- client_->OnSelectionEvent(INSERTION_HANDLE_DRAG_STARTED);
- anchor_drag_to_selection_start_ = true;
- return;
- }
- 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 {
- DCHECK_EQ(&draggable, &longpress_drag_selector_);
- anchor_drag_to_selection_start_ =
- (drag_position - GetStartPosition()).LengthSquared() <
- (drag_position - GetEndPosition()).LengthSquared();
- }
- if (config_.hide_active_handle) {
- if (&draggable == start_selection_handle_.get()) {
- start_selection_handle_->SetTransparent();
- } else if (&draggable == end_selection_handle_.get()) {
- end_selection_handle_->SetTransparent();
- }
- }
- gfx::PointF base = GetStartPosition() + GetStartLineOffset();
- gfx::PointF extent = GetEndPosition() + GetEndLineOffset();
- if (anchor_drag_to_selection_start_)
- std::swap(base, extent);
- // If this is the first drag, log an action to allow user action sequencing.
- if (!selection_handle_dragged_)
- base::RecordAction(base::UserMetricsAction("SelectionChanged"));
- selection_handle_dragged_ = true;
- // When moving the handle we want to move only the extent point. Before doing
- // so we must make sure that the base point is set correctly.
- client_->SelectBetweenCoordinates(base, extent);
- client_->OnSelectionEvent(SELECTION_HANDLE_DRAG_STARTED);
- }
- 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 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 = drag_position + line_offset;
- if (&draggable == insertion_handle_.get())
- client_->MoveCaret(line_position);
- else
- client_->MoveRangeSelectionExtent(line_position);
- // We use the bound middle point to restrict the ability to move up and
- // down, but let user move it more freely in horizontal direction.
- if (&draggable == &longpress_drag_selector_) {
- // Show magnifier at the selection edge.
- const gfx::SelectionBound* bound =
- anchor_drag_to_selection_start_ ? &start_ : &end_;
- const float x = bound->edge_start().x();
- const float y = (bound->edge_start().y() + bound->edge_end().y()) / 2.f;
- client_->OnDragUpdate(TouchSelectionDraggable::Type::kLongpress,
- gfx::PointF(x, y));
- } else {
- const float y = GetActiveHandleMiddleY();
- client_->OnDragUpdate(TouchSelectionDraggable::Type::kTouchHandle,
- gfx::PointF(drag_position.x(), y));
- }
- }
- void TouchSelectionController::OnDragEnd(
- const TouchSelectionDraggable& draggable) {
- if (&draggable == insertion_handle_.get())
- client_->OnSelectionEvent(INSERTION_HANDLE_DRAG_STOPPED);
- else
- client_->OnSelectionEvent(SELECTION_HANDLE_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_HANDLE_TAPPED);
- }
- void TouchSelectionController::SetNeedsAnimate() {
- client_->SetNeedsAnimate();
- }
- std::unique_ptr<TouchHandleDrawable>
- TouchSelectionController::CreateDrawable() {
- return client_->CreateDrawable();
- }
- base::TimeDelta TouchSelectionController::GetMaxTapDuration() const {
- return config_.max_tap_duration;
- }
- bool TouchSelectionController::IsAdaptiveHandleOrientationEnabled() const {
- return config_.enable_adaptive_handle_orientation;
- }
- 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::OnInsertionChanged() {
- DeactivateSelection();
- const bool activated = ActivateInsertionIfNecessary();
- const TouchHandle::AnimationStyle animation = GetAnimationStyle(!activated);
- insertion_handle_->SetFocus(start_.edge_start(), start_.edge_end());
- insertion_handle_->SetVisible(GetStartVisible(), animation);
- UpdateHandleLayoutIfNecessary();
- client_->OnSelectionEvent(activated ? INSERTION_HANDLE_SHOWN
- : INSERTION_HANDLE_MOVED);
- }
- void TouchSelectionController::OnSelectionChanged() {
- DeactivateInsertion();
- const bool activated = ActivateSelectionIfNecessary();
- const TouchHandle::AnimationStyle animation = GetAnimationStyle(!activated);
- start_selection_handle_->SetFocus(start_.edge_start(), start_.edge_end());
- end_selection_handle_->SetFocus(end_.edge_start(), end_.edge_end());
- start_selection_handle_->SetOrientation(start_orientation_);
- end_selection_handle_->SetOrientation(end_orientation_);
- start_selection_handle_->SetVisible(GetStartVisible(), animation);
- end_selection_handle_->SetVisible(GetEndVisible(), animation);
- UpdateHandleLayoutIfNecessary();
- client_->OnSelectionEvent(activated ? SELECTION_HANDLES_SHOWN
- : SELECTION_HANDLES_MOVED);
- }
- bool TouchSelectionController::ActivateInsertionIfNecessary() {
- DCHECK_NE(SELECTION_ACTIVE, active_status_);
- if (!insertion_handle_) {
- insertion_handle_ = std::make_unique<TouchHandle>(
- this, TouchHandleOrientation::CENTER, viewport_rect_);
- }
- if (active_status_ == INACTIVE || response_pending_input_event_ == TAP ||
- response_pending_input_event_ == LONG_PRESS) {
- active_status_ = INSERTION_ACTIVE;
- insertion_handle_->SetEnabled(true);
- insertion_handle_->SetViewportRect(viewport_rect_);
- response_pending_input_event_ = INPUT_EVENT_TYPE_NONE;
- return true;
- }
- return false;
- }
- void TouchSelectionController::DeactivateInsertion() {
- if (active_status_ != INSERTION_ACTIVE)
- return;
- DCHECK(insertion_handle_);
- active_status_ = INACTIVE;
- insertion_handle_->SetEnabled(false);
- client_->OnSelectionEvent(INSERTION_HANDLE_CLEARED);
- }
- bool TouchSelectionController::ActivateSelectionIfNecessary() {
- DCHECK_NE(INSERTION_ACTIVE, active_status_);
- if (!start_selection_handle_) {
- start_selection_handle_ =
- std::make_unique<TouchHandle>(this, start_orientation_, viewport_rect_);
- } else {
- start_selection_handle_->SetEnabled(true);
- start_selection_handle_->SetViewportRect(viewport_rect_);
- }
- if (!end_selection_handle_) {
- end_selection_handle_ =
- std::make_unique<TouchHandle>(this, end_orientation_, viewport_rect_);
- } else {
- end_selection_handle_->SetEnabled(true);
- end_selection_handle_->SetViewportRect(viewport_rect_);
- }
- // As a long press received while a selection is already active may trigger
- // an entirely new selection, notify the client but avoid sending an
- // intervening SELECTION_HANDLES_CLEARED update to avoid unnecessary state
- // changes.
- if (active_status_ == INACTIVE ||
- response_pending_input_event_ == LONG_PRESS ||
- response_pending_input_event_ == REPEATED_TAP) {
- if (active_status_ == SELECTION_ACTIVE) {
- // The active selection session finishes with the start of the new one.
- LogSelectionEnd();
- }
- active_status_ = SELECTION_ACTIVE;
- 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;
- }
- void TouchSelectionController::DeactivateSelection() {
- if (active_status_ != SELECTION_ACTIVE)
- return;
- 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;
- client_->OnSelectionEvent(SELECTION_HANDLES_CLEARED);
- }
- void TouchSelectionController::UpdateHandleLayoutIfNecessary() {
- if (active_status_ == INSERTION_ACTIVE) {
- DCHECK(insertion_handle_);
- insertion_handle_->UpdateHandleLayout();
- } else if (active_status_ == SELECTION_ACTIVE) {
- DCHECK(start_selection_handle_);
- DCHECK(end_selection_handle_);
- start_selection_handle_->UpdateHandleLayout();
- end_selection_handle_->UpdateHandleLayout();
- }
- }
- 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);
- // Update handle layout if handle visibility is explicitly changed.
- UpdateHandleLayoutIfNecessary();
- }
- gfx::Vector2dF TouchSelectionController::GetStartLineOffset() const {
- return ComputeLineOffsetFromBottom(start_);
- }
- gfx::Vector2dF TouchSelectionController::GetEndLineOffset() const {
- return ComputeLineOffsetFromBottom(end_);
- }
- bool TouchSelectionController::GetStartVisible() const {
- if (!start_.visible())
- return false;
- return !temporarily_hidden_ && !longpress_drag_selector_.IsActive();
- }
- bool TouchSelectionController::GetEndVisible() const {
- if (!end_.visible())
- return false;
- return !temporarily_hidden_ && !longpress_drag_selector_.IsActive();
- }
- TouchHandle::AnimationStyle TouchSelectionController::GetAnimationStyle(
- bool was_active) const {
- return was_active && client_->SupportsAnimation()
- ? TouchHandle::ANIMATION_SMOOTH
- : TouchHandle::ANIMATION_NONE;
- }
- void TouchSelectionController::LogSelectionEnd() {
- // TODO(mfomitchev): Once we are able to tell the difference between
- // 'successful' and 'unsuccessful' selections - log
- // Event.TouchSelection.Duration instead and get rid of
- // Event.TouchSelectionD.WasDraggeduration.
- if (selection_handle_dragged_) {
- base::TimeDelta duration = base::TimeTicks::Now() - selection_start_time_;
- UMA_HISTOGRAM_CUSTOM_TIMES("Event.TouchSelection.WasDraggedDuration",
- duration, base::Milliseconds(500),
- base::Seconds(60), 60);
- }
- }
- } // namespace ui
|