Fix search results being clipped in app list.
[chromium-blink-merge.git] / ui / touch_selection / touch_handle.cc
blob0f28c3fedf8d69068acba4969db54e13cbd60bda
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_to_focus_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 if (is_drag_within_tap_region_) {
178 const float tap_slop = client_->GetTapSlop();
179 is_drag_within_tap_region_ =
180 (touch_move_position - touch_down_position_).LengthSquared() <
181 tap_slop * tap_slop;
184 // Note that we signal drag update even if we're inside the tap region,
185 // as there are cases where characters are narrower than the slop length.
186 client_->OnHandleDragUpdate(*this,
187 touch_move_position + touch_to_focus_offset_);
188 } break;
190 case MotionEvent::ACTION_UP: {
191 if (is_drag_within_tap_region_ &&
192 (event.GetEventTime() - touch_down_time_) <
193 client_->GetTapTimeout()) {
194 client_->OnHandleTapped(*this);
197 EndDrag();
198 } break;
200 case MotionEvent::ACTION_CANCEL:
201 EndDrag();
202 break;
204 default:
205 break;
207 return true;
210 bool TouchHandle::Animate(base::TimeTicks frame_time) {
211 if (fade_end_time_ == base::TimeTicks())
212 return false;
214 DCHECK(enabled_);
216 float time_u =
217 1.f - (fade_end_time_ - frame_time).InMillisecondsF() / kFadeDurationMs;
218 float position_u =
219 (position_ - fade_start_position_).LengthSquared() / kFadeDistanceSquared;
220 float u = std::max(time_u, position_u);
221 SetAlpha(is_visible_ ? u : 1.f - u);
223 if (u >= 1.f) {
224 EndFade();
225 return false;
228 return true;
231 void TouchHandle::BeginDrag() {
232 DCHECK(enabled_);
233 if (is_dragging_)
234 return;
235 EndFade();
236 is_dragging_ = true;
237 is_drag_within_tap_region_ = true;
238 client_->OnHandleDragBegin(*this);
241 void TouchHandle::EndDrag() {
242 DCHECK(enabled_);
243 if (!is_dragging_)
244 return;
246 is_dragging_ = false;
247 is_drag_within_tap_region_ = false;
248 client_->OnHandleDragEnd(*this);
250 if (deferred_orientation_ != TouchHandleOrientation::UNDEFINED) {
251 TouchHandleOrientation deferred_orientation = deferred_orientation_;
252 deferred_orientation_ = TouchHandleOrientation::UNDEFINED;
253 SetOrientation(deferred_orientation);
256 if (animate_deferred_fade_) {
257 BeginFade();
258 } else {
259 // As drawable visibility assignment is deferred while dragging, push the
260 // change by forcing fade completion.
261 EndFade();
265 void TouchHandle::BeginFade() {
266 DCHECK(enabled_);
267 DCHECK(!is_dragging_);
268 animate_deferred_fade_ = false;
269 const float target_alpha = is_visible_ ? 1.f : 0.f;
270 if (target_alpha == alpha_) {
271 EndFade();
272 return;
275 fade_end_time_ = base::TimeTicks::Now() +
276 base::TimeDelta::FromMillisecondsD(
277 kFadeDurationMs * std::abs(target_alpha - alpha_));
278 fade_start_position_ = position_;
279 client_->SetNeedsAnimate();
282 void TouchHandle::EndFade() {
283 DCHECK(enabled_);
284 animate_deferred_fade_ = false;
285 fade_end_time_ = base::TimeTicks();
286 SetAlpha(is_visible_ ? 1.f : 0.f);
289 void TouchHandle::SetAlpha(float alpha) {
290 alpha = std::max(0.f, std::min(1.f, alpha));
291 if (alpha_ == alpha)
292 return;
293 alpha_ = alpha;
294 drawable_->SetAlpha(alpha);
297 } // namespace ui