acfe3bd70f87f1716272adbdccca88de11269f9e
[platform/framework/web/crosswalk.git] / src / content / browser / renderer_host / input / touch_handle.cc
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.
4
5 #include "content/browser/renderer_host/input/touch_handle.h"
6
7 #include <cmath>
8
9 namespace content {
10
11 namespace {
12
13 // Maximum duration of a fade sequence.
14 const double kFadeDurationMs = 200;
15
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;
19
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;
23
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;
29
30 }  // namespace
31
32 // Responsible for rendering a selection or insertion handle for text editing.
33 TouchHandle::TouchHandle(TouchHandleClient* client,
34                          TouchHandleOrientation orientation)
35     : drawable_(client->CreateDrawable()),
36       client_(client),
37       orientation_(orientation),
38       deferred_orientation_(TOUCH_HANDLE_ORIENTATION_UNDEFINED),
39       alpha_(0.f),
40       animate_deferred_fade_(false),
41       enabled_(true),
42       is_visible_(false),
43       is_dragging_(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_);
51 }
52
53 TouchHandle::~TouchHandle() {
54 }
55
56 void TouchHandle::SetEnabled(bool enabled) {
57   if (enabled_ == enabled)
58     return;
59   if (!enabled) {
60     EndDrag();
61     EndFade();
62   }
63   enabled_ = enabled;
64   drawable_->SetEnabled(enabled);
65 }
66
67 void TouchHandle::SetVisible(bool visible, AnimationStyle animation_style) {
68   DCHECK(enabled_);
69   if (is_visible_ == visible)
70     return;
71
72   is_visible_ = visible;
73
74   // Handle repositioning may have been deferred while previously invisible.
75   if (visible)
76     drawable_->SetFocus(position_);
77
78   bool animate = animation_style != ANIMATION_NONE;
79   if (is_dragging_) {
80     animate_deferred_fade_ = animate;
81     return;
82   }
83
84   if (animate)
85     BeginFade();
86   else
87     EndFade();
88 }
89
90 void TouchHandle::SetPosition(const gfx::PointF& position) {
91   DCHECK(enabled_);
92   if (position_ == position)
93     return;
94   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()|).
98   if (is_visible_)
99     drawable_->SetFocus(position_);
100 }
101
102 void TouchHandle::SetOrientation(TouchHandleOrientation orientation) {
103   DCHECK(enabled_);
104   DCHECK_NE(orientation, TOUCH_HANDLE_ORIENTATION_UNDEFINED);
105   if (is_dragging_) {
106     deferred_orientation_ = orientation;
107     return;
108   }
109   DCHECK_EQ(deferred_orientation_, TOUCH_HANDLE_ORIENTATION_UNDEFINED);
110   if (orientation_ == orientation)
111     return;
112
113   orientation_ = orientation;
114   drawable_->SetOrientation(orientation);
115 }
116
117 bool TouchHandle::WillHandleTouchEvent(const ui::MotionEvent& event) {
118   if (!enabled_)
119     return false;
120
121   if (!is_dragging_ && event.GetAction() != ui::MotionEvent::ACTION_DOWN)
122     return false;
123
124   switch (event.GetAction()) {
125     case ui::MotionEvent::ACTION_DOWN: {
126       if (!is_visible_)
127         return false;
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,
133                                   touch_size,
134                                   touch_size);
135       if (!drawable_->IntersectsWith(touch_rect))
136         return false;
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();
140       BeginDrag();
141     } break;
142
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() <
149             tap_slop * tap_slop;
150       }
151
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_);
156     } break;
157
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);
163       }
164
165       EndDrag();
166     } break;
167
168     case ui::MotionEvent::ACTION_CANCEL:
169       EndDrag();
170       break;
171
172     default:
173       break;
174   };
175   return true;
176 }
177
178 bool TouchHandle::Animate(base::TimeTicks frame_time) {
179   if (fade_end_time_ == base::TimeTicks())
180     return false;
181
182   DCHECK(enabled_);
183
184   float time_u =
185       1.f - (fade_end_time_ - frame_time).InMillisecondsF() / kFadeDurationMs;
186   float position_u =
187       (position_ - fade_start_position_).LengthSquared() / kFadeDistanceSquared;
188   float u = std::max(time_u, position_u);
189   SetAlpha(is_visible_ ? u : 1.f - u);
190
191   if (u >= 1.f) {
192     EndFade();
193     return false;
194   }
195
196   return true;
197 }
198
199 void TouchHandle::BeginDrag() {
200   DCHECK(enabled_);
201   if (is_dragging_)
202     return;
203   EndFade();
204   is_dragging_ = true;
205   is_drag_within_tap_region_ = true;
206   client_->OnHandleDragBegin(*this);
207 }
208
209 void TouchHandle::EndDrag() {
210   DCHECK(enabled_);
211   if (!is_dragging_)
212     return;
213
214   is_dragging_ = false;
215   is_drag_within_tap_region_ = false;
216   client_->OnHandleDragEnd(*this);
217
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);
222   }
223
224   if (animate_deferred_fade_) {
225     BeginFade();
226   } else {
227     // As drawable visibility assignment is deferred while dragging, push the
228     // change by forcing fade completion.
229     EndFade();
230   }
231 }
232
233 void TouchHandle::BeginFade() {
234   DCHECK(enabled_);
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_) {
239     EndFade();
240     return;
241   }
242
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();
249 }
250
251 void TouchHandle::EndFade() {
252   DCHECK(enabled_);
253   animate_deferred_fade_ = false;
254   fade_end_time_ = base::TimeTicks();
255   SetAlpha(is_visible_ ? 1.f : 0.f);
256   drawable_->SetVisible(is_visible_);
257 }
258
259 void TouchHandle::SetAlpha(float alpha) {
260   alpha = std::max(0.f, std::min(1.f, alpha));
261   if (alpha_ == alpha)
262     return;
263   alpha_ = alpha;
264   drawable_->SetAlpha(alpha);
265 }
266
267 }  // namespace content