Revert of Roll src/third_party/skia 1340039:e942bee (patchset #1 id:1 of https:/...
[chromium-blink-merge.git] / ui / touch_selection / touch_handle.cc
blobe2faff1f382cf4b85ea2892dbb1f4e7965ed03ad
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 // TODO(AviD): Remove this once logging(DCHECK) supports enum class.
48 static std::ostream& operator<<(std::ostream& os,
49 const TouchHandleOrientation& orientation) {
50 switch (orientation) {
51 case TouchHandleOrientation::LEFT:
52 return os << "LEFT";
53 case TouchHandleOrientation::RIGHT:
54 return os << "RIGHT";
55 case TouchHandleOrientation::CENTER:
56 return os << "CENTER";
57 case TouchHandleOrientation::UNDEFINED:
58 return os << "UNDEFINED";
59 default:
60 return os << "INVALID: " << static_cast<int>(orientation);
64 // Responsible for rendering a selection or insertion handle for text editing.
65 TouchHandle::TouchHandle(TouchHandleClient* client,
66 TouchHandleOrientation orientation)
67 : drawable_(client->CreateDrawable()),
68 client_(client),
69 orientation_(orientation),
70 deferred_orientation_(TouchHandleOrientation::UNDEFINED),
71 alpha_(0.f),
72 animate_deferred_fade_(false),
73 enabled_(true),
74 is_visible_(false),
75 is_dragging_(false),
76 is_drag_within_tap_region_(false) {
77 DCHECK_NE(orientation, TouchHandleOrientation::UNDEFINED);
78 drawable_->SetEnabled(enabled_);
79 drawable_->SetOrientation(orientation_);
80 drawable_->SetAlpha(alpha_);
81 drawable_->SetFocus(position_);
84 TouchHandle::~TouchHandle() {
87 void TouchHandle::SetEnabled(bool enabled) {
88 if (enabled_ == enabled)
89 return;
90 if (!enabled) {
91 EndDrag();
92 EndFade();
94 enabled_ = enabled;
95 drawable_->SetEnabled(enabled);
98 void TouchHandle::SetVisible(bool visible, AnimationStyle animation_style) {
99 DCHECK(enabled_);
100 if (is_visible_ == visible)
101 return;
103 is_visible_ = visible;
105 // Handle repositioning may have been deferred while previously invisible.
106 if (visible)
107 drawable_->SetFocus(position_);
109 bool animate = animation_style != ANIMATION_NONE;
110 if (is_dragging_) {
111 animate_deferred_fade_ = animate;
112 return;
115 if (animate)
116 BeginFade();
117 else
118 EndFade();
121 void TouchHandle::SetPosition(const gfx::PointF& position) {
122 DCHECK(enabled_);
123 if (position_ == position)
124 return;
125 position_ = position;
126 // Suppress repositioning a handle while invisible or fading out to prevent it
127 // from "ghosting" outside the visible bounds. The position will be pushed to
128 // the drawable when the handle regains visibility (see |SetVisible()|).
129 if (is_visible_)
130 drawable_->SetFocus(position_);
133 void TouchHandle::SetOrientation(TouchHandleOrientation orientation) {
134 DCHECK(enabled_);
135 DCHECK_NE(orientation, TouchHandleOrientation::UNDEFINED);
136 if (is_dragging_) {
137 deferred_orientation_ = orientation;
138 return;
140 DCHECK_EQ(deferred_orientation_, TouchHandleOrientation::UNDEFINED);
141 if (orientation_ == orientation)
142 return;
144 orientation_ = orientation;
145 drawable_->SetOrientation(orientation);
148 bool TouchHandle::WillHandleTouchEvent(const MotionEvent& event) {
149 if (!enabled_)
150 return false;
152 if (!is_dragging_ && event.GetAction() != MotionEvent::ACTION_DOWN)
153 return false;
155 switch (event.GetAction()) {
156 case MotionEvent::ACTION_DOWN: {
157 if (!is_visible_)
158 return false;
159 const gfx::PointF touch_point(event.GetX(), event.GetY());
160 const float touch_radius = std::max(
161 kMinTouchMajorForHitTesting,
162 std::min(kMaxTouchMajorForHitTesting, event.GetTouchMajor())) * 0.5f;
163 if (!RectIntersectsCircle(drawable_->GetVisibleBounds(),
164 touch_point,
165 touch_radius)) {
166 EndDrag();
167 return false;
169 touch_down_position_ = touch_point;
170 touch_drag_offset_ = position_ - touch_down_position_;
171 touch_down_time_ = event.GetEventTime();
172 BeginDrag();
173 } break;
175 case MotionEvent::ACTION_MOVE: {
176 gfx::PointF touch_move_position(event.GetX(), event.GetY());
177 is_drag_within_tap_region_ &=
178 client_->IsWithinTapSlop(touch_down_position_ - touch_move_position);
180 // Note that we signal drag update even if we're inside the tap region,
181 // as there are cases where characters are narrower than the slop length.
182 client_->OnDragUpdate(*this, touch_move_position + touch_drag_offset_);
183 } break;
185 case MotionEvent::ACTION_UP: {
186 if (is_drag_within_tap_region_ &&
187 (event.GetEventTime() - touch_down_time_) <
188 client_->GetTapTimeout()) {
189 client_->OnHandleTapped(*this);
192 EndDrag();
193 } break;
195 case MotionEvent::ACTION_CANCEL:
196 EndDrag();
197 break;
199 default:
200 break;
202 return true;
205 bool TouchHandle::IsActive() const {
206 return is_dragging_;
209 bool TouchHandle::Animate(base::TimeTicks frame_time) {
210 if (fade_end_time_ == base::TimeTicks())
211 return false;
213 DCHECK(enabled_);
215 float time_u =
216 1.f - (fade_end_time_ - frame_time).InMillisecondsF() / kFadeDurationMs;
217 float position_u =
218 (position_ - fade_start_position_).LengthSquared() / kFadeDistanceSquared;
219 float u = std::max(time_u, position_u);
220 SetAlpha(is_visible_ ? u : 1.f - u);
222 if (u >= 1.f) {
223 EndFade();
224 return false;
227 return true;
230 gfx::RectF TouchHandle::GetVisibleBounds() const {
231 if (!is_visible_ || !enabled_)
232 return gfx::RectF();
234 return drawable_->GetVisibleBounds();
237 void TouchHandle::BeginDrag() {
238 DCHECK(enabled_);
239 if (is_dragging_)
240 return;
241 EndFade();
242 is_dragging_ = true;
243 is_drag_within_tap_region_ = true;
244 client_->OnDragBegin(*this, position());
247 void TouchHandle::EndDrag() {
248 DCHECK(enabled_);
249 if (!is_dragging_)
250 return;
252 is_dragging_ = false;
253 is_drag_within_tap_region_ = false;
254 client_->OnDragEnd(*this);
256 if (deferred_orientation_ != TouchHandleOrientation::UNDEFINED) {
257 TouchHandleOrientation deferred_orientation = deferred_orientation_;
258 deferred_orientation_ = TouchHandleOrientation::UNDEFINED;
259 SetOrientation(deferred_orientation);
262 if (animate_deferred_fade_) {
263 BeginFade();
264 } else {
265 // As drawable visibility assignment is deferred while dragging, push the
266 // change by forcing fade completion.
267 EndFade();
271 void TouchHandle::BeginFade() {
272 DCHECK(enabled_);
273 DCHECK(!is_dragging_);
274 animate_deferred_fade_ = false;
275 const float target_alpha = is_visible_ ? 1.f : 0.f;
276 if (target_alpha == alpha_) {
277 EndFade();
278 return;
281 fade_end_time_ = base::TimeTicks::Now() +
282 base::TimeDelta::FromMillisecondsD(
283 kFadeDurationMs * std::abs(target_alpha - alpha_));
284 fade_start_position_ = position_;
285 client_->SetNeedsAnimate();
288 void TouchHandle::EndFade() {
289 DCHECK(enabled_);
290 animate_deferred_fade_ = false;
291 fade_end_time_ = base::TimeTicks();
292 SetAlpha(is_visible_ ? 1.f : 0.f);
295 void TouchHandle::SetAlpha(float alpha) {
296 alpha = std::max(0.f, std::min(1.f, alpha));
297 if (alpha_ == alpha)
298 return;
299 alpha_ = alpha;
300 drawable_->SetAlpha(alpha);
303 } // namespace ui