#include "ui/touch_selection/longpress_drag_selector.h"
#include "ui/events/velocity_tracker/motion_event.h"
namespace ui {
namespace {
gfx::Vector2dF SafeNormalize(const gfx::Vector2dF& v) {
return v.IsZero() ? v : ScaleVector2d(v, 1.f / v.Length());
}
}
LongPressDragSelector::LongPressDragSelector(
LongPressDragSelectorClient* client)
: client_(client),
state_(INACTIVE),
has_longpress_drag_start_anchor_(false) {
#if BUILDFLAG(IS_ARKWEB)
utils_ = std::make_unique<LongPressDragSelectorUtils>(this);
#endif
}
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(INITIATING_GESTURE_PENDING);
return false;
case MotionEvent::Action::UP:
case MotionEvent::Action::CANCEL:
SetState(INACTIVE);
#if BUILDFLAG(ARKWEB_MENU)
client_->UpdateSelectionChanged(*this);
#endif
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_;
#if BUILDFLAG(ARKWEB_MENU)
drag_position = position;
#endif
client_->OnDragUpdate(*this, drag_position);
return true;
}
if (!has_longpress_drag_start_anchor_) {
has_longpress_drag_start_anchor_ = true;
longpress_drag_start_anchor_ = position;
return true;
}
#if BUILDFLAG(ARKWEB_MENU)
if (utils_ && utils_->PositionInSelection(position, client_->GetSelectionTop(), client_->GetSelectionEnd())) {
return true;
}
#endif
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())) {
extend_selection_start = delta.y() < 0;
} else {
gfx::Vector2dF start_delta = selection_start - longpress_drag_start_anchor_;
gfx::Vector2dF end_delta = selection_end - longpress_drag_start_anchor_;
gfx::Vector2dF normalized_start_delta = SafeNormalize(start_delta);
gfx::Vector2dF normalized_end_delta = SafeNormalize(end_delta);
double start_dot_product = gfx::DotProduct(normalized_start_delta, delta);
double end_dot_product = gfx::DotProduct(normalized_end_delta, delta);
if (start_dot_product >= 0 || end_dot_product >= 0) {
extend_selection_start = start_dot_product > end_dot_product;
} else {
extend_selection_start =
start_delta.LengthSquared() < end_delta.LengthSquared();
}
}
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_ == DRAG_PENDING || state_ == DRAGGING;
}
void LongPressDragSelector::OnLongPressEvent(base::TimeTicks event_time,
const gfx::PointF& position) {
if (state_ == INITIATING_GESTURE_PENDING &&
(touch_down_time_ < event_time + base::Microseconds(10)) &&
client_->IsWithinTapSlop(touch_down_position_ - position)) {
SetState(SELECTION_PENDING);
}
}
void LongPressDragSelector::OnDoublePressEvent(base::TimeTicks event_time,
const gfx::PointF& position) {
if (state_ == INITIATING_GESTURE_PENDING &&
touch_down_time_ == event_time && touch_down_position_ == position) {
SetState(SELECTION_PENDING);
}
}
void LongPressDragSelector::OnScrollBeginEvent() {
SetState(INACTIVE);
}
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;
if (was_dragging)
client_->OnDragEnd(*this);
if (was_active != IsActive())
client_->OnLongPressDragActiveStateChanged();
}
}