Skip direct map from apk check for Samsung Mega.
[chromium-blink-merge.git] / ui / touch_selection / touch_handle.cc
blob0680e6db8afeca5bbe649bf07992391810777efe
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/touch_selection/touch_handle.h"
7 #include <cmath>
9 namespace ui {
11 namespace {
13 // Maximum duration of a fade sequence.
14 const double kFadeDurationMs = 200;
16 // Maximum amount of travel for a fade sequence. This avoids handle "ghosting"
17 // when the handle is moving rapidly while the fade is active.
18 const double kFadeDistanceSquared = 20.f * 20.f;
20 // Avoid using an empty touch rect, as it may fail the intersection test event
21 // if it lies within the other rect's bounds.
22 const float kMinTouchMajorForHitTesting = 1.f;
24 // The maximum touch size to use when computing whether a touch point is
25 // targetting a touch handle. This is necessary for devices that misreport
26 // touch radii, preventing inappropriately largely touch sizes from completely
27 // breaking handle dragging behavior.
28 const float kMaxTouchMajorForHitTesting = 36.f;
30 // Note that the intersection region is boundary *exclusive*.
31 bool RectIntersectsCircle(const gfx::RectF& rect,
32 const gfx::PointF& circle_center,
33 float circle_radius) {
34 DCHECK_GT(circle_radius, 0.f);
35 // An intersection occurs if the closest point between the rect and the
36 // circle's center is less than the circle's radius.
37 gfx::PointF closest_point_in_rect(circle_center);
38 closest_point_in_rect.SetToMax(rect.origin());
39 closest_point_in_rect.SetToMin(rect.bottom_right());
41 gfx::Vector2dF distance = circle_center - closest_point_in_rect;
42 return distance.LengthSquared() < (circle_radius * circle_radius);
45 } // namespace
47 // Responsible for rendering a selection or insertion handle for text editing.
48 TouchHandle::TouchHandle(TouchHandleClient* client,
49 TouchHandleOrientation orientation)
50 : drawable_(client->CreateDrawable()),
51 client_(client),
52 orientation_(orientation),
53 deferred_orientation_(TOUCH_HANDLE_ORIENTATION_UNDEFINED),
54 alpha_(0.f),
55 animate_deferred_fade_(false),
56 enabled_(true),
57 is_visible_(false),
58 is_dragging_(false),
59 is_drag_within_tap_region_(false) {
60 DCHECK_NE(orientation, TOUCH_HANDLE_ORIENTATION_UNDEFINED);
61 drawable_->SetEnabled(enabled_);
62 drawable_->SetOrientation(orientation_);
63 drawable_->SetAlpha(alpha_);
64 drawable_->SetFocus(position_);
67 TouchHandle::~TouchHandle() {
70 void TouchHandle::SetEnabled(bool enabled) {
71 if (enabled_ == enabled)
72 return;
73 if (!enabled) {
74 EndDrag();
75 EndFade();
77 enabled_ = enabled;
78 drawable_->SetEnabled(enabled);
81 void TouchHandle::SetVisible(bool visible, AnimationStyle animation_style) {
82 DCHECK(enabled_);
83 if (is_visible_ == visible)
84 return;
86 is_visible_ = visible;
88 // Handle repositioning may have been deferred while previously invisible.
89 if (visible)
90 drawable_->SetFocus(position_);
92 bool animate = animation_style != ANIMATION_NONE;
93 if (is_dragging_) {
94 animate_deferred_fade_ = animate;
95 return;
98 if (animate)
99 BeginFade();
100 else
101 EndFade();
104 void TouchHandle::SetPosition(const gfx::PointF& position) {
105 DCHECK(enabled_);
106 if (position_ == position)
107 return;
108 position_ = position;
109 // Suppress repositioning a handle while invisible or fading out to prevent it
110 // from "ghosting" outside the visible bounds. The position will be pushed to
111 // the drawable when the handle regains visibility (see |SetVisible()|).
112 if (is_visible_)
113 drawable_->SetFocus(position_);
116 void TouchHandle::SetOrientation(TouchHandleOrientation orientation) {
117 DCHECK(enabled_);
118 DCHECK_NE(orientation, TOUCH_HANDLE_ORIENTATION_UNDEFINED);
119 if (is_dragging_) {
120 deferred_orientation_ = orientation;
121 return;
123 DCHECK_EQ(deferred_orientation_, TOUCH_HANDLE_ORIENTATION_UNDEFINED);
124 if (orientation_ == orientation)
125 return;
127 orientation_ = orientation;
128 drawable_->SetOrientation(orientation);
131 bool TouchHandle::WillHandleTouchEvent(const MotionEvent& event) {
132 if (!enabled_)
133 return false;
135 if (!is_dragging_ && event.GetAction() != MotionEvent::ACTION_DOWN)
136 return false;
138 switch (event.GetAction()) {
139 case MotionEvent::ACTION_DOWN: {
140 if (!is_visible_)
141 return false;
142 const gfx::PointF touch_point(event.GetX(), event.GetY());
143 const float touch_radius = std::max(
144 kMinTouchMajorForHitTesting,
145 std::min(kMaxTouchMajorForHitTesting, event.GetTouchMajor())) * 0.5f;
146 if (!RectIntersectsCircle(drawable_->GetVisibleBounds(),
147 touch_point,
148 touch_radius)) {
149 EndDrag();
150 return false;
152 touch_down_position_ = touch_point;
153 touch_to_focus_offset_ = position_ - touch_down_position_;
154 touch_down_time_ = event.GetEventTime();
155 BeginDrag();
156 } break;
158 case MotionEvent::ACTION_MOVE: {
159 gfx::PointF touch_move_position(event.GetX(), event.GetY());
160 if (is_drag_within_tap_region_) {
161 const float tap_slop = client_->GetTapSlop();
162 is_drag_within_tap_region_ =
163 (touch_move_position - touch_down_position_).LengthSquared() <
164 tap_slop * tap_slop;
167 // Note that we signal drag update even if we're inside the tap region,
168 // as there are cases where characters are narrower than the slop length.
169 client_->OnHandleDragUpdate(*this,
170 touch_move_position + touch_to_focus_offset_);
171 } break;
173 case MotionEvent::ACTION_UP: {
174 if (is_drag_within_tap_region_ &&
175 (event.GetEventTime() - touch_down_time_) <
176 client_->GetTapTimeout()) {
177 client_->OnHandleTapped(*this);
180 EndDrag();
181 } break;
183 case MotionEvent::ACTION_CANCEL:
184 EndDrag();
185 break;
187 default:
188 break;
190 return true;
193 bool TouchHandle::Animate(base::TimeTicks frame_time) {
194 if (fade_end_time_ == base::TimeTicks())
195 return false;
197 DCHECK(enabled_);
199 float time_u =
200 1.f - (fade_end_time_ - frame_time).InMillisecondsF() / kFadeDurationMs;
201 float position_u =
202 (position_ - fade_start_position_).LengthSquared() / kFadeDistanceSquared;
203 float u = std::max(time_u, position_u);
204 SetAlpha(is_visible_ ? u : 1.f - u);
206 if (u >= 1.f) {
207 EndFade();
208 return false;
211 return true;
214 void TouchHandle::BeginDrag() {
215 DCHECK(enabled_);
216 if (is_dragging_)
217 return;
218 EndFade();
219 is_dragging_ = true;
220 is_drag_within_tap_region_ = true;
221 client_->OnHandleDragBegin(*this);
224 void TouchHandle::EndDrag() {
225 DCHECK(enabled_);
226 if (!is_dragging_)
227 return;
229 is_dragging_ = false;
230 is_drag_within_tap_region_ = false;
231 client_->OnHandleDragEnd(*this);
233 if (deferred_orientation_ != TOUCH_HANDLE_ORIENTATION_UNDEFINED) {
234 TouchHandleOrientation deferred_orientation = deferred_orientation_;
235 deferred_orientation_ = TOUCH_HANDLE_ORIENTATION_UNDEFINED;
236 SetOrientation(deferred_orientation);
239 if (animate_deferred_fade_) {
240 BeginFade();
241 } else {
242 // As drawable visibility assignment is deferred while dragging, push the
243 // change by forcing fade completion.
244 EndFade();
248 void TouchHandle::BeginFade() {
249 DCHECK(enabled_);
250 DCHECK(!is_dragging_);
251 animate_deferred_fade_ = false;
252 const float target_alpha = is_visible_ ? 1.f : 0.f;
253 if (target_alpha == alpha_) {
254 EndFade();
255 return;
258 fade_end_time_ = base::TimeTicks::Now() +
259 base::TimeDelta::FromMillisecondsD(
260 kFadeDurationMs * std::abs(target_alpha - alpha_));
261 fade_start_position_ = position_;
262 client_->SetNeedsAnimate();
265 void TouchHandle::EndFade() {
266 DCHECK(enabled_);
267 animate_deferred_fade_ = false;
268 fade_end_time_ = base::TimeTicks();
269 SetAlpha(is_visible_ ? 1.f : 0.f);
272 void TouchHandle::SetAlpha(float alpha) {
273 alpha = std::max(0.f, std::min(1.f, alpha));
274 if (alpha_ == alpha)
275 return;
276 alpha_ = alpha;
277 drawable_->SetAlpha(alpha);
280 } // namespace ui