// Copyright (c) 2012 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/events/gestures/gesture_point.h" #include <cmath> #include "base/basictypes.h" #include "ui/events/event.h" #include "ui/events/event_constants.h" #include "ui/events/gestures/gesture_configuration.h" #include "ui/events/gestures/gesture_types.h" #include "ui/events/gestures/gesture_util.h" namespace ui { GesturePoint::GesturePoint() : first_touch_time_(0.0), second_last_touch_time_(0.0), last_touch_time_(0.0), second_last_tap_time_(0.0), last_tap_time_(0.0), velocity_calculator_( GestureConfiguration::points_buffered_for_velocity()), point_id_(-1), touch_id_(-1) { } GesturePoint::~GesturePoint() {} void GesturePoint::Reset() { first_touch_time_ = second_last_touch_time_ = last_touch_time_ = 0.0; ResetVelocity(); point_id_ = -1; clear_enclosing_rectangle(); } void GesturePoint::ResetVelocity() { velocity_calculator_.ClearHistory(); same_direction_count_ = gfx::Vector2d(); } gfx::Vector2d GesturePoint::ScrollDelta() { return last_touch_position_ - second_last_touch_position_; } void GesturePoint::UpdateValues(const TouchEvent& event) { const int64 event_timestamp_microseconds = event.time_stamp().InMicroseconds(); if (event.type() == ui::ET_TOUCH_MOVED) { velocity_calculator_.PointSeen(event.location().x(), event.location().y(), event_timestamp_microseconds); gfx::Vector2d sd(ScrollVelocityDirection(velocity_calculator_.XVelocity()), ScrollVelocityDirection(velocity_calculator_.YVelocity())); same_direction_count_ = same_direction_count_ + sd; } last_touch_time_ = event.time_stamp().InSecondsF(); last_touch_position_ = event.location(); if (event.type() == ui::ET_TOUCH_PRESSED) { ResetVelocity(); clear_enclosing_rectangle(); first_touch_time_ = last_touch_time_; first_touch_position_ = event.location(); second_last_touch_position_ = last_touch_position_; second_last_touch_time_ = last_touch_time_; velocity_calculator_.PointSeen(event.location().x(), event.location().y(), event_timestamp_microseconds); } UpdateEnclosingRectangle(event); } void GesturePoint::UpdateForTap() { // Update the tap-position and time, and reset every other state. second_last_tap_position_ = last_tap_position_; second_last_tap_time_ = last_tap_time_; last_tap_time_ = last_touch_time_; last_tap_position_ = last_touch_position_; } void GesturePoint::UpdateForScroll() { second_last_touch_position_ = last_touch_position_; second_last_touch_time_ = last_touch_time_; same_direction_count_ = gfx::Vector2d(); } bool GesturePoint::IsInClickWindow(const TouchEvent& event) const { return IsInClickTimeWindow() && IsInsideManhattanSquare(event); } bool GesturePoint::IsInDoubleClickWindow(const TouchEvent& event) const { return IsInClickAggregateTimeWindow(last_tap_time_, last_touch_time_) && IsPointInsideManhattanSquare(event.location(), last_tap_position_); } bool GesturePoint::IsInTripleClickWindow(const TouchEvent& event) const { return IsInClickAggregateTimeWindow(last_tap_time_, last_touch_time_) && IsInClickAggregateTimeWindow(second_last_tap_time_, last_tap_time_) && IsPointInsideManhattanSquare(event.location(), last_tap_position_) && IsPointInsideManhattanSquare(last_tap_position_, second_last_tap_position_); } bool GesturePoint::IsInScrollWindow(const TouchEvent& event) const { if (IsConsistentScrollingActionUnderway()) return true; return event.type() == ui::ET_TOUCH_MOVED && !IsInsideManhattanSquare(event); } bool GesturePoint::IsInFlickWindow(const TouchEvent& event) { return IsOverMinFlickSpeed() && event.type() != ui::ET_TOUCH_CANCELLED; } int GesturePoint::ScrollVelocityDirection(float v) { if (v < -GestureConfiguration::min_scroll_velocity()) return -1; else if (v > GestureConfiguration::min_scroll_velocity()) return 1; else return 0; } bool GesturePoint::DidScroll(const TouchEvent& event, int dist) const { gfx::Vector2d d = last_touch_position_ - second_last_touch_position_; return abs(d.x()) > dist || abs(d.y()) > dist; } bool GesturePoint::IsConsistentScrollingActionUnderway() const { int me = GestureConfiguration::min_scroll_successive_velocity_events(); if (abs(same_direction_count_.x()) >= me || abs(same_direction_count_.y()) >= me) return true; return false; } bool GesturePoint::IsInHorizontalRailWindow() const { gfx::Vector2d d = last_touch_position_ - second_last_touch_position_; return abs(d.x()) > GestureConfiguration::rail_start_proportion() * abs(d.y()); } bool GesturePoint::IsInVerticalRailWindow() const { gfx::Vector2d d = last_touch_position_ - second_last_touch_position_; return abs(d.y()) > GestureConfiguration::rail_start_proportion() * abs(d.x()); } bool GesturePoint::BreaksHorizontalRail() { float vx = XVelocity(); float vy = YVelocity(); return fabs(vy) > GestureConfiguration::rail_break_proportion() * fabs(vx) + GestureConfiguration::min_rail_break_velocity(); } bool GesturePoint::BreaksVerticalRail() { float vx = XVelocity(); float vy = YVelocity(); return fabs(vx) > GestureConfiguration::rail_break_proportion() * fabs(vy) + GestureConfiguration::min_rail_break_velocity(); } bool GesturePoint::IsInClickTimeWindow() const { double duration = last_touch_time_ - first_touch_time_; return duration >= GestureConfiguration::min_touch_down_duration_in_seconds_for_click() && duration < GestureConfiguration::max_touch_down_duration_in_seconds_for_click(); } bool GesturePoint::IsInClickAggregateTimeWindow(double before, double after) const { double duration = after - before; return duration < GestureConfiguration::max_seconds_between_double_click(); } bool GesturePoint::IsInsideManhattanSquare(const TouchEvent& event) const { return ui::gestures::IsInsideManhattanSquare(event.location(), first_touch_position_); } bool GesturePoint::IsPointInsideManhattanSquare(gfx::Point p1, gfx::Point p2) const { int manhattan_distance = abs(p1.x() - p2.x()) + abs(p1.y() - p2.y()); return manhattan_distance < GestureConfiguration::max_distance_between_taps_for_double_tap(); } bool GesturePoint::IsOverMinFlickSpeed() { return velocity_calculator_.VelocitySquared() > GestureConfiguration::min_flick_speed_squared(); } void GesturePoint::UpdateEnclosingRectangle(const TouchEvent& event) { int radius; // Ignore this TouchEvent if it has a radius larger than the maximum // allowed radius size. if (event.radius_x() > GestureConfiguration::max_radius() || event.radius_y() > GestureConfiguration::max_radius()) return; // If the device provides at least one of the radius values, take the larger // of the two and use this as both the x radius and the y radius of the // touch region. Otherwise use the default radius value. // TODO(tdanderson): Implement a more specific check for the exact // information provided by the device (0-2 radii values, force, angle) and // use this to compute a more representative rectangular touch region. if (event.radius_x() || event.radius_y()) radius = std::max(event.radius_x(), event.radius_y()); else radius = GestureConfiguration::default_radius(); gfx::Rect rect(event.location().x() - radius, event.location().y() - radius, radius * 2, radius * 2); if (IsInClickWindow(event)) enclosing_rect_.Union(rect); else enclosing_rect_ = rect; } } // namespace ui