This source file includes following definitions.
- min_scaling_span
- OnScale
- OnScaleBegin
- OnScaleEnd
- event_before_or_above_starting_gesture_event_
- OnTouchEvent
- SetQuickScaleEnabled
- IsQuickScaleEnabled
- IsInProgress
- InDoubleTapMode
- GetFocusX
- GetFocusY
- GetCurrentSpan
- GetCurrentSpanX
- GetCurrentSpanY
- GetPreviousSpan
- GetPreviousSpanX
- GetPreviousSpanY
- GetScaleFactor
- GetTimeDelta
- GetEventTime
- OnDoubleTap
- AddTouchHistory
- ClearTouchHistory
#include "ui/events/gesture_detection/scale_gesture_detector.h"
#include <limits.h>
#include <cmath>
#include "base/float_util.h"
#include "base/logging.h"
#include "ui/events/gesture_detection/motion_event.h"
using base::TimeDelta;
using base::TimeTicks;
namespace ui {
namespace {
const int kTouchStabilizeTimeMs = 128;
const float kScaleFactor = .5f;
}
ScaleGestureDetector::Config::Config()
: quick_scale_enabled(true),
min_scaling_touch_major(48),
min_scaling_span(200) {}
ScaleGestureDetector::Config::~Config() {}
bool ScaleGestureDetector::SimpleScaleGestureListener::OnScale(
const ScaleGestureDetector&, const MotionEvent&) {
return false;
}
bool ScaleGestureDetector::SimpleScaleGestureListener::OnScaleBegin(
const ScaleGestureDetector&, const MotionEvent&) {
return true;
}
void ScaleGestureDetector::SimpleScaleGestureListener::OnScaleEnd(
const ScaleGestureDetector&, const MotionEvent&) {}
ScaleGestureDetector::ScaleGestureDetector(const Config& config,
ScaleGestureListener* listener)
: listener_(listener),
config_(config),
focus_x_(0),
focus_y_(0),
quick_scale_enabled_(false),
curr_span_(0),
prev_span_(0),
initial_span_(0),
curr_span_x_(0),
curr_span_y_(0),
prev_span_x_(0),
prev_span_y_(0),
in_progress_(0),
span_slop_(0),
min_span_(0),
touch_upper_(0),
touch_lower_(0),
touch_history_last_accepted_(0),
touch_history_direction_(0),
touch_min_major_(0),
double_tap_focus_x_(0),
double_tap_focus_y_(0),
double_tap_mode_(DOUBLE_TAP_MODE_NONE),
event_before_or_above_starting_gesture_event_(false) {
DCHECK(listener_);
span_slop_ = config.gesture_detector_config.scaled_touch_slop * 2;
touch_min_major_ = config.min_scaling_touch_major;
min_span_ = config.min_scaling_span;
SetQuickScaleEnabled(config.quick_scale_enabled);
}
ScaleGestureDetector::~ScaleGestureDetector() {}
bool ScaleGestureDetector::OnTouchEvent(const MotionEvent& event) {
curr_time_ = event.GetEventTime();
const int action = event.GetAction();
if (quick_scale_enabled_) {
DCHECK(gesture_detector_);
gesture_detector_->OnTouchEvent(event);
}
const bool stream_complete =
action == MotionEvent::ACTION_UP || action == MotionEvent::ACTION_CANCEL;
if (action == MotionEvent::ACTION_DOWN || stream_complete) {
if (in_progress_) {
listener_->OnScaleEnd(*this, event);
in_progress_ = false;
initial_span_ = 0;
double_tap_mode_ = DOUBLE_TAP_MODE_NONE;
} else if (double_tap_mode_ == DOUBLE_TAP_MODE_IN_PROGRESS &&
stream_complete) {
in_progress_ = false;
initial_span_ = 0;
double_tap_mode_ = DOUBLE_TAP_MODE_NONE;
}
if (stream_complete) {
ClearTouchHistory();
return true;
}
}
const bool config_changed = action == MotionEvent::ACTION_DOWN ||
action == MotionEvent::ACTION_POINTER_UP ||
action == MotionEvent::ACTION_POINTER_DOWN;
const bool pointer_up = action == MotionEvent::ACTION_POINTER_UP;
const int skip_index = pointer_up ? event.GetActionIndex() : -1;
float sum_x = 0, sum_y = 0;
const int count = static_cast<int>(event.GetPointerCount());
const int div = pointer_up ? count - 1 : count;
float focus_x;
float focus_y;
if (double_tap_mode_ == DOUBLE_TAP_MODE_IN_PROGRESS) {
focus_x = double_tap_focus_x_;
focus_y = double_tap_focus_y_;
if (event.GetY() < focus_y) {
event_before_or_above_starting_gesture_event_ = true;
} else {
event_before_or_above_starting_gesture_event_ = false;
}
} else {
for (int i = 0; i < count; i++) {
if (skip_index == i)
continue;
sum_x += event.GetX(i);
sum_y += event.GetY(i);
}
focus_x = sum_x / div;
focus_y = sum_y / div;
}
AddTouchHistory(event);
float dev_sum_x = 0, dev_sum_y = 0;
for (int i = 0; i < count; i++) {
if (skip_index == i)
continue;
const float touch_size = touch_history_last_accepted_ / 2;
dev_sum_x += std::abs(event.GetX(i) - focus_x) + touch_size;
dev_sum_y += std::abs(event.GetY(i) - focus_y) + touch_size;
}
const float dev_x = dev_sum_x / div;
const float dev_y = dev_sum_y / div;
const float span_x = dev_x * 2;
const float span_y = dev_y * 2;
float span;
if (InDoubleTapMode()) {
span = span_y;
} else {
span = std::sqrt(span_x * span_x + span_y * span_y);
}
const bool was_in_progress = in_progress_;
focus_x_ = focus_x;
focus_y_ = focus_y;
if (!InDoubleTapMode() && in_progress_ &&
(span < min_span_ || config_changed)) {
listener_->OnScaleEnd(*this, event);
in_progress_ = false;
initial_span_ = span;
double_tap_mode_ = DOUBLE_TAP_MODE_NONE;
}
if (config_changed) {
prev_span_x_ = curr_span_x_ = span_x;
prev_span_y_ = curr_span_y_ = span_y;
initial_span_ = prev_span_ = curr_span_ = span;
}
const int min_span = InDoubleTapMode() ? span_slop_ : min_span_;
if (!in_progress_ && span >= min_span &&
(was_in_progress || std::abs(span - initial_span_) > span_slop_)) {
prev_span_x_ = curr_span_x_ = span_x;
prev_span_y_ = curr_span_y_ = span_y;
prev_span_ = curr_span_ = span;
prev_time_ = curr_time_;
in_progress_ = listener_->OnScaleBegin(*this, event);
}
if (action == MotionEvent::ACTION_MOVE) {
curr_span_x_ = span_x;
curr_span_y_ = span_y;
curr_span_ = span;
bool update_prev = true;
if (in_progress_) {
update_prev = listener_->OnScale(*this, event);
}
if (update_prev) {
prev_span_x_ = curr_span_x_;
prev_span_y_ = curr_span_y_;
prev_span_ = curr_span_;
prev_time_ = curr_time_;
}
}
return true;
}
void ScaleGestureDetector::SetQuickScaleEnabled(bool scales) {
quick_scale_enabled_ = scales;
if (quick_scale_enabled_ && !gesture_detector_) {
gesture_detector_.reset(
new GestureDetector(config_.gesture_detector_config, this, this));
}
}
bool ScaleGestureDetector::IsQuickScaleEnabled() const {
return quick_scale_enabled_;
}
bool ScaleGestureDetector::IsInProgress() const { return in_progress_; }
bool ScaleGestureDetector::InDoubleTapMode() const {
return double_tap_mode_ == DOUBLE_TAP_MODE_IN_PROGRESS;
}
float ScaleGestureDetector::GetFocusX() const { return focus_x_; }
float ScaleGestureDetector::GetFocusY() const { return focus_y_; }
float ScaleGestureDetector::GetCurrentSpan() const { return curr_span_; }
float ScaleGestureDetector::GetCurrentSpanX() const { return curr_span_x_; }
float ScaleGestureDetector::GetCurrentSpanY() const { return curr_span_y_; }
float ScaleGestureDetector::GetPreviousSpan() const { return prev_span_; }
float ScaleGestureDetector::GetPreviousSpanX() const { return prev_span_x_; }
float ScaleGestureDetector::GetPreviousSpanY() const { return prev_span_y_; }
float ScaleGestureDetector::GetScaleFactor() const {
if (InDoubleTapMode()) {
const bool scale_up = (event_before_or_above_starting_gesture_event_ &&
(curr_span_ < prev_span_)) ||
(!event_before_or_above_starting_gesture_event_ &&
(curr_span_ > prev_span_));
const float span_diff =
(std::abs(1.f - (curr_span_ / prev_span_)) * kScaleFactor);
return prev_span_ <= 0 ? 1.f
: (scale_up ? (1.f + span_diff) : (1.f - span_diff));
}
return prev_span_ > 0 ? curr_span_ / prev_span_ : 1;
}
base::TimeDelta ScaleGestureDetector::GetTimeDelta() const {
return curr_time_ - prev_time_;
}
base::TimeTicks ScaleGestureDetector::GetEventTime() const {
return curr_time_;
}
bool ScaleGestureDetector::OnDoubleTap(const MotionEvent& ev) {
double_tap_focus_x_ = ev.GetX();
double_tap_focus_y_ = ev.GetY();
double_tap_mode_ = DOUBLE_TAP_MODE_IN_PROGRESS;
return true;
}
void ScaleGestureDetector::AddTouchHistory(const MotionEvent& ev) {
const base::TimeTicks current_time = base::TimeTicks::Now();
const int count = static_cast<int>(ev.GetPointerCount());
bool accept = (current_time - touch_history_last_accepted_time_)
.InMilliseconds() >= kTouchStabilizeTimeMs;
float total = 0;
int sample_count = 0;
for (int i = 0; i < count; i++) {
const bool has_last_accepted = !base::IsNaN(touch_history_last_accepted_);
const int history_size = static_cast<int>(ev.GetHistorySize());
const int pointersample_count = history_size + 1;
for (int h = 0; h < pointersample_count; h++) {
float major;
if (h < history_size) {
major = ev.GetHistoricalTouchMajor(i, h);
} else {
major = ev.GetTouchMajor(i);
}
if (major < touch_min_major_)
major = touch_min_major_;
total += major;
if (base::IsNaN(touch_upper_) || major > touch_upper_) {
touch_upper_ = major;
}
if (base::IsNaN(touch_lower_) || major < touch_lower_) {
touch_lower_ = major;
}
if (has_last_accepted) {
const float major_delta = major - touch_history_last_accepted_;
const int direction_sig =
major_delta > 0 ? 1 : (major_delta < 0 ? -1 : 0);
if (direction_sig != touch_history_direction_ ||
(direction_sig == 0 && touch_history_direction_ == 0)) {
touch_history_direction_ = direction_sig;
touch_history_last_accepted_time_ = h < history_size
? ev.GetHistoricalEventTime(h)
: ev.GetEventTime();
accept = false;
}
}
}
sample_count += pointersample_count;
}
const float avg = total / sample_count;
if (accept) {
float new_accepted = (touch_upper_ + touch_lower_ + avg) / 3;
touch_upper_ = (touch_upper_ + new_accepted) / 2;
touch_lower_ = (touch_lower_ + new_accepted) / 2;
touch_history_last_accepted_ = new_accepted;
touch_history_direction_ = 0;
touch_history_last_accepted_time_ = ev.GetEventTime();
}
}
void ScaleGestureDetector::ClearTouchHistory() {
touch_upper_ = std::numeric_limits<float>::quiet_NaN();
touch_lower_ = std::numeric_limits<float>::quiet_NaN();
touch_history_last_accepted_ = std::numeric_limits<float>::quiet_NaN();
touch_history_direction_ = 0;
touch_history_last_accepted_time_ = base::TimeTicks();
}
}