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 "content/browser/renderer_host/input/touch_handle.h"
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;
32 // Responsible for rendering a selection or insertion handle for text editing.
33 TouchHandle::TouchHandle(TouchHandleClient* client,
34 TouchHandleOrientation orientation)
35 : drawable_(client->CreateDrawable()),
37 orientation_(orientation),
38 deferred_orientation_(TOUCH_HANDLE_ORIENTATION_UNDEFINED),
40 animate_deferred_fade_(false),
44 is_drag_within_tap_region_(false) {
45 DCHECK_NE(orientation, TOUCH_HANDLE_ORIENTATION_UNDEFINED);
46 drawable_->SetEnabled(enabled_);
47 drawable_->SetOrientation(orientation_);
48 drawable_->SetAlpha(alpha_);
49 drawable_->SetVisible(is_visible_);
50 drawable_->SetFocus(position_);
53 TouchHandle::~TouchHandle() {
56 void TouchHandle::SetEnabled(bool enabled) {
57 if (enabled_ == enabled)
64 drawable_->SetEnabled(enabled);
67 void TouchHandle::SetVisible(bool visible, AnimationStyle animation_style) {
69 if (is_visible_ == visible)
72 is_visible_ = visible;
74 // Handle repositioning may have been deferred while previously invisible.
76 drawable_->SetFocus(position_);
78 bool animate = animation_style != ANIMATION_NONE;
80 animate_deferred_fade_ = animate;
90 void TouchHandle::SetPosition(const gfx::PointF& position) {
92 if (position_ == position)
95 // Suppress repositioning a handle while invisible or fading out to prevent it
96 // from "ghosting" outside the visible bounds. The position will be pushed to
97 // the drawable when the handle regains visibility (see |SetVisible()|).
99 drawable_->SetFocus(position_);
102 void TouchHandle::SetOrientation(TouchHandleOrientation orientation) {
104 DCHECK_NE(orientation, TOUCH_HANDLE_ORIENTATION_UNDEFINED);
106 deferred_orientation_ = orientation;
109 DCHECK_EQ(deferred_orientation_, TOUCH_HANDLE_ORIENTATION_UNDEFINED);
110 if (orientation_ == orientation)
113 orientation_ = orientation;
114 drawable_->SetOrientation(orientation);
117 bool TouchHandle::WillHandleTouchEvent(const ui::MotionEvent& event) {
121 if (!is_dragging_ && event.GetAction() != ui::MotionEvent::ACTION_DOWN)
124 switch (event.GetAction()) {
125 case ui::MotionEvent::ACTION_DOWN: {
128 const float touch_size = std::max(
129 kMinTouchMajorForHitTesting,
130 std::min(kMaxTouchMajorForHitTesting, event.GetTouchMajor()));
131 const gfx::RectF touch_rect(event.GetX() - touch_size * .5f,
132 event.GetY() - touch_size * .5f,
135 if (!drawable_->IntersectsWith(touch_rect))
137 touch_down_position_ = gfx::PointF(event.GetX(), event.GetY());
138 touch_to_focus_offset_ = position_ - touch_down_position_;
139 touch_down_time_ = event.GetEventTime();
143 case ui::MotionEvent::ACTION_MOVE: {
144 gfx::PointF touch_move_position(event.GetX(), event.GetY());
145 if (is_drag_within_tap_region_) {
146 const float tap_slop = client_->GetTapSlop();
147 is_drag_within_tap_region_ =
148 (touch_move_position - touch_down_position_).LengthSquared() <
152 // Note that we signal drag update even if we're inside the tap region,
153 // as there are cases where characters are narrower than the slop length.
154 client_->OnHandleDragUpdate(*this,
155 touch_move_position + touch_to_focus_offset_);
158 case ui::MotionEvent::ACTION_UP: {
159 if (is_drag_within_tap_region_ &&
160 (event.GetEventTime() - touch_down_time_) <
161 client_->GetTapTimeout()) {
162 client_->OnHandleTapped(*this);
168 case ui::MotionEvent::ACTION_CANCEL:
178 bool TouchHandle::Animate(base::TimeTicks frame_time) {
179 if (fade_end_time_ == base::TimeTicks())
185 1.f - (fade_end_time_ - frame_time).InMillisecondsF() / kFadeDurationMs;
187 (position_ - fade_start_position_).LengthSquared() / kFadeDistanceSquared;
188 float u = std::max(time_u, position_u);
189 SetAlpha(is_visible_ ? u : 1.f - u);
199 void TouchHandle::BeginDrag() {
205 is_drag_within_tap_region_ = true;
206 client_->OnHandleDragBegin(*this);
209 void TouchHandle::EndDrag() {
214 is_dragging_ = false;
215 is_drag_within_tap_region_ = false;
216 client_->OnHandleDragEnd(*this);
218 if (deferred_orientation_ != TOUCH_HANDLE_ORIENTATION_UNDEFINED) {
219 TouchHandleOrientation deferred_orientation = deferred_orientation_;
220 deferred_orientation_ = TOUCH_HANDLE_ORIENTATION_UNDEFINED;
221 SetOrientation(deferred_orientation);
224 if (animate_deferred_fade_) {
227 // As drawable visibility assignment is deferred while dragging, push the
228 // change by forcing fade completion.
233 void TouchHandle::BeginFade() {
235 DCHECK(!is_dragging_);
236 animate_deferred_fade_ = false;
237 const float target_alpha = is_visible_ ? 1.f : 0.f;
238 if (target_alpha == alpha_) {
243 drawable_->SetVisible(true);
244 fade_end_time_ = base::TimeTicks::Now() +
245 base::TimeDelta::FromMillisecondsD(
246 kFadeDurationMs * std::abs(target_alpha - alpha_));
247 fade_start_position_ = position_;
248 client_->SetNeedsAnimate();
251 void TouchHandle::EndFade() {
253 animate_deferred_fade_ = false;
254 fade_end_time_ = base::TimeTicks();
255 SetAlpha(is_visible_ ? 1.f : 0.f);
256 drawable_->SetVisible(is_visible_);
259 void TouchHandle::SetAlpha(float alpha) {
260 alpha = std::max(0.f, std::min(1.f, alpha));
264 drawable_->SetAlpha(alpha);
267 } // namespace content