| // 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 |