OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/touch_selection/touch_handle.h" | 5 #include "ui/touch_selection/touch_handle.h" |
6 | 6 |
7 #include <cmath> | 7 #include <cmath> |
8 | 8 |
9 namespace ui { | 9 namespace ui { |
10 | 10 |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
56 return os << "CENTER"; | 56 return os << "CENTER"; |
57 case TouchHandleOrientation::UNDEFINED: | 57 case TouchHandleOrientation::UNDEFINED: |
58 return os << "UNDEFINED"; | 58 return os << "UNDEFINED"; |
59 default: | 59 default: |
60 return os << "INVALID: " << static_cast<int>(orientation); | 60 return os << "INVALID: " << static_cast<int>(orientation); |
61 } | 61 } |
62 } | 62 } |
63 | 63 |
64 // Responsible for rendering a selection or insertion handle for text editing. | 64 // Responsible for rendering a selection or insertion handle for text editing. |
65 TouchHandle::TouchHandle(TouchHandleClient* client, | 65 TouchHandle::TouchHandle(TouchHandleClient* client, |
66 TouchHandleOrientation orientation) | 66 TouchHandleOrientation orientation, |
| 67 const gfx::RectF& viewport_rect) |
67 : drawable_(client->CreateDrawable()), | 68 : drawable_(client->CreateDrawable()), |
68 client_(client), | 69 client_(client), |
| 70 viewport_rect_(viewport_rect), |
69 orientation_(orientation), | 71 orientation_(orientation), |
70 deferred_orientation_(TouchHandleOrientation::UNDEFINED), | 72 deferred_orientation_(TouchHandleOrientation::UNDEFINED), |
71 alpha_(0.f), | 73 alpha_(0.f), |
72 animate_deferred_fade_(false), | 74 animate_deferred_fade_(false), |
73 enabled_(true), | 75 enabled_(true), |
74 is_visible_(false), | 76 is_visible_(false), |
75 is_dragging_(false), | 77 is_dragging_(false), |
76 is_drag_within_tap_region_(false) { | 78 is_drag_within_tap_region_(false), |
| 79 is_handle_layout_update_required_(false), |
| 80 mirror_vertical_(false), |
| 81 mirror_horizontal_(false) { |
77 DCHECK_NE(orientation, TouchHandleOrientation::UNDEFINED); | 82 DCHECK_NE(orientation, TouchHandleOrientation::UNDEFINED); |
78 drawable_->SetEnabled(enabled_); | 83 drawable_->SetEnabled(enabled_); |
79 drawable_->SetOrientation(orientation_); | 84 drawable_->SetOrientation(orientation_, false, false); |
| 85 drawable_->SetOrigin(focus_bottom_); |
80 drawable_->SetAlpha(alpha_); | 86 drawable_->SetAlpha(alpha_); |
81 drawable_->SetFocus(position_); | 87 handle_horizontal_padding_ = drawable_->GetDrawableHorizontalPaddingRatio(); |
82 } | 88 } |
83 | 89 |
84 TouchHandle::~TouchHandle() { | 90 TouchHandle::~TouchHandle() { |
85 } | 91 } |
86 | 92 |
87 void TouchHandle::SetEnabled(bool enabled) { | 93 void TouchHandle::SetEnabled(bool enabled) { |
88 if (enabled_ == enabled) | 94 if (enabled_ == enabled) |
89 return; | 95 return; |
90 if (!enabled) { | 96 if (!enabled) { |
91 EndDrag(); | 97 EndDrag(); |
92 EndFade(); | 98 EndFade(); |
93 } | 99 } |
94 enabled_ = enabled; | 100 enabled_ = enabled; |
95 drawable_->SetEnabled(enabled); | 101 drawable_->SetEnabled(enabled); |
96 } | 102 } |
97 | 103 |
98 void TouchHandle::SetVisible(bool visible, AnimationStyle animation_style) { | 104 void TouchHandle::SetVisible(bool visible, AnimationStyle animation_style) { |
99 DCHECK(enabled_); | 105 DCHECK(enabled_); |
100 if (is_visible_ == visible) | 106 if (is_visible_ == visible) |
101 return; | 107 return; |
102 | 108 |
103 is_visible_ = visible; | 109 is_visible_ = visible; |
104 | 110 |
105 // Handle repositioning may have been deferred while previously invisible. | 111 // Handle repositioning may have been deferred while previously invisible. |
106 if (visible) | 112 if (visible) |
107 drawable_->SetFocus(position_); | 113 SetUpdateLayoutRequired(); |
108 | 114 |
109 bool animate = animation_style != ANIMATION_NONE; | 115 bool animate = animation_style != ANIMATION_NONE; |
110 if (is_dragging_) { | 116 if (is_dragging_) { |
111 animate_deferred_fade_ = animate; | 117 animate_deferred_fade_ = animate; |
112 return; | 118 return; |
113 } | 119 } |
114 | 120 |
115 if (animate) | 121 if (animate) |
116 BeginFade(); | 122 BeginFade(); |
117 else | 123 else |
118 EndFade(); | 124 EndFade(); |
119 } | 125 } |
120 | 126 |
121 void TouchHandle::SetPosition(const gfx::PointF& position) { | 127 void TouchHandle::SetFocus(const gfx::PointF& top, const gfx::PointF& bottom) { |
122 DCHECK(enabled_); | 128 DCHECK(enabled_); |
123 if (position_ == position) | 129 if (focus_top_ == top && focus_bottom_ == bottom) |
124 return; | 130 return; |
125 position_ = position; | 131 |
126 // Suppress repositioning a handle while invisible or fading out to prevent it | 132 focus_top_ = top; |
127 // from "ghosting" outside the visible bounds. The position will be pushed to | 133 focus_bottom_ = bottom; |
128 // the drawable when the handle regains visibility (see |SetVisible()|). | 134 SetUpdateLayoutRequired(); |
129 if (is_visible_) | 135 } |
130 drawable_->SetFocus(position_); | 136 |
| 137 void TouchHandle::SetViewportRect(const gfx::RectF& viewport_rect) { |
| 138 DCHECK(enabled_); |
| 139 if (viewport_rect_ == viewport_rect) |
| 140 return; |
| 141 |
| 142 viewport_rect_ = viewport_rect; |
| 143 SetUpdateLayoutRequired(); |
131 } | 144 } |
132 | 145 |
133 void TouchHandle::SetOrientation(TouchHandleOrientation orientation) { | 146 void TouchHandle::SetOrientation(TouchHandleOrientation orientation) { |
134 DCHECK(enabled_); | 147 DCHECK(enabled_); |
135 DCHECK_NE(orientation, TouchHandleOrientation::UNDEFINED); | 148 DCHECK_NE(orientation, TouchHandleOrientation::UNDEFINED); |
136 if (is_dragging_) { | 149 if (is_dragging_) { |
137 deferred_orientation_ = orientation; | 150 deferred_orientation_ = orientation; |
138 return; | 151 return; |
139 } | 152 } |
140 DCHECK_EQ(deferred_orientation_, TouchHandleOrientation::UNDEFINED); | 153 DCHECK_EQ(deferred_orientation_, TouchHandleOrientation::UNDEFINED); |
141 if (orientation_ == orientation) | 154 if (orientation_ == orientation) |
142 return; | 155 return; |
143 | 156 |
144 orientation_ = orientation; | 157 orientation_ = orientation; |
145 drawable_->SetOrientation(orientation); | 158 SetUpdateLayoutRequired(); |
146 } | 159 } |
147 | 160 |
148 bool TouchHandle::WillHandleTouchEvent(const MotionEvent& event) { | 161 bool TouchHandle::WillHandleTouchEvent(const MotionEvent& event) { |
149 if (!enabled_) | 162 if (!enabled_) |
150 return false; | 163 return false; |
151 | 164 |
152 if (!is_dragging_ && event.GetAction() != MotionEvent::ACTION_DOWN) | 165 if (!is_dragging_ && event.GetAction() != MotionEvent::ACTION_DOWN) |
153 return false; | 166 return false; |
154 | 167 |
155 switch (event.GetAction()) { | 168 switch (event.GetAction()) { |
156 case MotionEvent::ACTION_DOWN: { | 169 case MotionEvent::ACTION_DOWN: { |
157 if (!is_visible_) | 170 if (!is_visible_) |
158 return false; | 171 return false; |
159 const gfx::PointF touch_point(event.GetX(), event.GetY()); | 172 const gfx::PointF touch_point(event.GetX(), event.GetY()); |
160 const float touch_radius = std::max( | 173 const float touch_radius = std::max( |
161 kMinTouchMajorForHitTesting, | 174 kMinTouchMajorForHitTesting, |
162 std::min(kMaxTouchMajorForHitTesting, event.GetTouchMajor())) * 0.5f; | 175 std::min(kMaxTouchMajorForHitTesting, event.GetTouchMajor())) * 0.5f; |
163 const gfx::RectF drawable_bounds = drawable_->GetVisibleBounds(); | 176 const gfx::RectF drawable_bounds = drawable_->GetVisibleBounds(); |
164 // Only use the touch radius for targetting if the touch is at or below | 177 // Only use the touch radius for targetting if the touch is at or below |
165 // the drawable area. This makes it easier to interact with the line of | 178 // the drawable area. This makes it easier to interact with the line of |
166 // text above the drawable. | 179 // text above the drawable. |
167 if (touch_point.y() < drawable_bounds.y() || | 180 if (touch_point.y() < drawable_bounds.y() || |
168 !RectIntersectsCircle(drawable_bounds, touch_point, touch_radius)) { | 181 !RectIntersectsCircle(drawable_bounds, touch_point, touch_radius)) { |
169 EndDrag(); | 182 EndDrag(); |
170 return false; | 183 return false; |
171 } | 184 } |
172 touch_down_position_ = touch_point; | 185 touch_down_position_ = touch_point; |
173 touch_drag_offset_ = position_ - touch_down_position_; | 186 touch_drag_offset_ = focus_bottom_ - touch_down_position_; |
174 touch_down_time_ = event.GetEventTime(); | 187 touch_down_time_ = event.GetEventTime(); |
175 BeginDrag(); | 188 BeginDrag(); |
176 } break; | 189 } break; |
177 | 190 |
178 case MotionEvent::ACTION_MOVE: { | 191 case MotionEvent::ACTION_MOVE: { |
179 gfx::PointF touch_move_position(event.GetX(), event.GetY()); | 192 gfx::PointF touch_move_position(event.GetX(), event.GetY()); |
180 is_drag_within_tap_region_ &= | 193 is_drag_within_tap_region_ &= |
181 client_->IsWithinTapSlop(touch_down_position_ - touch_move_position); | 194 client_->IsWithinTapSlop(touch_down_position_ - touch_move_position); |
182 | 195 |
183 // Note that we signal drag update even if we're inside the tap region, | 196 // Note that we signal drag update even if we're inside the tap region, |
(...skipping 26 matching lines...) Expand all Loading... |
210 } | 223 } |
211 | 224 |
212 bool TouchHandle::Animate(base::TimeTicks frame_time) { | 225 bool TouchHandle::Animate(base::TimeTicks frame_time) { |
213 if (fade_end_time_ == base::TimeTicks()) | 226 if (fade_end_time_ == base::TimeTicks()) |
214 return false; | 227 return false; |
215 | 228 |
216 DCHECK(enabled_); | 229 DCHECK(enabled_); |
217 | 230 |
218 float time_u = | 231 float time_u = |
219 1.f - (fade_end_time_ - frame_time).InMillisecondsF() / kFadeDurationMs; | 232 1.f - (fade_end_time_ - frame_time).InMillisecondsF() / kFadeDurationMs; |
220 float position_u = | 233 float position_u = (focus_bottom_ - fade_start_position_).LengthSquared() / |
221 (position_ - fade_start_position_).LengthSquared() / kFadeDistanceSquared; | 234 kFadeDistanceSquared; |
222 float u = std::max(time_u, position_u); | 235 float u = std::max(time_u, position_u); |
223 SetAlpha(is_visible_ ? u : 1.f - u); | 236 SetAlpha(is_visible_ ? u : 1.f - u); |
224 | 237 |
225 if (u >= 1.f) { | 238 if (u >= 1.f) { |
226 EndFade(); | 239 EndFade(); |
227 return false; | 240 return false; |
228 } | 241 } |
229 | 242 |
230 return true; | 243 return true; |
231 } | 244 } |
232 | 245 |
233 gfx::RectF TouchHandle::GetVisibleBounds() const { | 246 gfx::RectF TouchHandle::GetVisibleBounds() const { |
234 if (!is_visible_ || !enabled_) | 247 if (!is_visible_ || !enabled_) |
235 return gfx::RectF(); | 248 return gfx::RectF(); |
236 | 249 |
237 return drawable_->GetVisibleBounds(); | 250 return drawable_->GetVisibleBounds(); |
238 } | 251 } |
239 | 252 |
| 253 void TouchHandle::UpdateHandleLayout() { |
| 254 // Suppress repositioning a handle while invisible or fading out to prevent it |
| 255 // from "ghosting" outside the visible bounds. The position will be pushed to |
| 256 // the drawable when the handle regains visibility (see |SetVisible()|). |
| 257 if (!is_visible_ || !is_handle_layout_update_required_) |
| 258 return; |
| 259 |
| 260 is_handle_layout_update_required_ = false; |
| 261 |
| 262 // Update mirror values only when dragging has stopped to prevent unwanted |
| 263 // inversion while dragging of handles. |
| 264 if (client_->IsAdaptiveHandleOrientationEnabled() && !is_dragging_) { |
| 265 gfx::RectF handle_bounds = drawable_->GetVisibleBounds(); |
| 266 bool mirror_horizontal = false; |
| 267 bool mirror_vertical = false; |
| 268 |
| 269 const float handle_width = |
| 270 handle_bounds.width() * (1.0 - handle_horizontal_padding_); |
| 271 const float handle_height = handle_bounds.height(); |
| 272 |
| 273 const float bottom_y_unmirrored = |
| 274 focus_bottom_.y() + handle_height + viewport_rect_.y(); |
| 275 const float top_y_mirrored = |
| 276 focus_top_.y() - handle_height + viewport_rect_.y(); |
| 277 |
| 278 // In case the viewport height is small, like webview, avoid inversion. |
| 279 if (bottom_y_unmirrored > viewport_rect_.bottom() && |
| 280 top_y_mirrored > viewport_rect_.y()) { |
| 281 mirror_vertical = true; |
| 282 } |
| 283 |
| 284 if (orientation_ == TouchHandleOrientation::LEFT && |
| 285 focus_bottom_.x() - handle_width < viewport_rect_.x()) { |
| 286 mirror_horizontal = true; |
| 287 } else if (orientation_ == TouchHandleOrientation::RIGHT && |
| 288 focus_bottom_.x() + handle_width > viewport_rect_.right()) { |
| 289 mirror_horizontal = true; |
| 290 } |
| 291 |
| 292 mirror_horizontal_ = mirror_horizontal; |
| 293 mirror_vertical_ = mirror_vertical; |
| 294 } |
| 295 |
| 296 drawable_->SetOrientation(orientation_, mirror_vertical_, mirror_horizontal_); |
| 297 drawable_->SetOrigin(ComputeHandleOrigin()); |
| 298 } |
| 299 |
| 300 gfx::PointF TouchHandle::ComputeHandleOrigin() const { |
| 301 gfx::PointF focus = mirror_vertical_ ? focus_top_ : focus_bottom_; |
| 302 gfx::RectF drawable_bounds = drawable_->GetVisibleBounds(); |
| 303 float drawable_width = drawable_->GetVisibleBounds().width(); |
| 304 |
| 305 // Calculate the focal offsets from origin for the handle drawable |
| 306 // based on the orientation. |
| 307 int focal_offset_x = 0; |
| 308 int focal_offset_y = mirror_vertical_ ? drawable_bounds.height() : 0; |
| 309 switch (orientation_) { |
| 310 case ui::TouchHandleOrientation::LEFT: |
| 311 focal_offset_x = |
| 312 mirror_horizontal_ |
| 313 ? drawable_width * handle_horizontal_padding_ |
| 314 : drawable_width * (1.0f - handle_horizontal_padding_); |
| 315 break; |
| 316 case ui::TouchHandleOrientation::RIGHT: |
| 317 focal_offset_x = |
| 318 mirror_horizontal_ |
| 319 ? drawable_width * (1.0f - handle_horizontal_padding_) |
| 320 : drawable_width * handle_horizontal_padding_; |
| 321 break; |
| 322 case ui::TouchHandleOrientation::CENTER: |
| 323 focal_offset_x = drawable_width * 0.5f; |
| 324 break; |
| 325 case ui::TouchHandleOrientation::UNDEFINED: |
| 326 NOTREACHED() << "Invalid touch handle orientation."; |
| 327 break; |
| 328 }; |
| 329 |
| 330 return focus - gfx::Vector2dF(focal_offset_x, focal_offset_y); |
| 331 } |
| 332 |
240 void TouchHandle::BeginDrag() { | 333 void TouchHandle::BeginDrag() { |
241 DCHECK(enabled_); | 334 DCHECK(enabled_); |
242 if (is_dragging_) | 335 if (is_dragging_) |
243 return; | 336 return; |
244 EndFade(); | 337 EndFade(); |
245 is_dragging_ = true; | 338 is_dragging_ = true; |
246 is_drag_within_tap_region_ = true; | 339 is_drag_within_tap_region_ = true; |
247 client_->OnDragBegin(*this, position()); | 340 client_->OnDragBegin(*this, focus_bottom()); |
248 } | 341 } |
249 | 342 |
250 void TouchHandle::EndDrag() { | 343 void TouchHandle::EndDrag() { |
251 DCHECK(enabled_); | 344 DCHECK(enabled_); |
252 if (!is_dragging_) | 345 if (!is_dragging_) |
253 return; | 346 return; |
254 | 347 |
255 is_dragging_ = false; | 348 is_dragging_ = false; |
256 is_drag_within_tap_region_ = false; | 349 is_drag_within_tap_region_ = false; |
257 client_->OnDragEnd(*this); | 350 client_->OnDragEnd(*this); |
258 | 351 |
259 if (deferred_orientation_ != TouchHandleOrientation::UNDEFINED) { | 352 if (deferred_orientation_ != TouchHandleOrientation::UNDEFINED) { |
260 TouchHandleOrientation deferred_orientation = deferred_orientation_; | 353 TouchHandleOrientation deferred_orientation = deferred_orientation_; |
261 deferred_orientation_ = TouchHandleOrientation::UNDEFINED; | 354 deferred_orientation_ = TouchHandleOrientation::UNDEFINED; |
262 SetOrientation(deferred_orientation); | 355 SetOrientation(deferred_orientation); |
| 356 // Handle layout may be deferred while the handle is dragged. |
| 357 SetUpdateLayoutRequired(); |
| 358 UpdateHandleLayout(); |
263 } | 359 } |
264 | 360 |
265 if (animate_deferred_fade_) { | 361 if (animate_deferred_fade_) { |
266 BeginFade(); | 362 BeginFade(); |
267 } else { | 363 } else { |
268 // As drawable visibility assignment is deferred while dragging, push the | 364 // As drawable visibility assignment is deferred while dragging, push the |
269 // change by forcing fade completion. | 365 // change by forcing fade completion. |
270 EndFade(); | 366 EndFade(); |
271 } | 367 } |
272 } | 368 } |
273 | 369 |
274 void TouchHandle::BeginFade() { | 370 void TouchHandle::BeginFade() { |
275 DCHECK(enabled_); | 371 DCHECK(enabled_); |
276 DCHECK(!is_dragging_); | 372 DCHECK(!is_dragging_); |
277 animate_deferred_fade_ = false; | 373 animate_deferred_fade_ = false; |
278 const float target_alpha = is_visible_ ? 1.f : 0.f; | 374 const float target_alpha = is_visible_ ? 1.f : 0.f; |
279 if (target_alpha == alpha_) { | 375 if (target_alpha == alpha_) { |
280 EndFade(); | 376 EndFade(); |
281 return; | 377 return; |
282 } | 378 } |
283 | 379 |
284 fade_end_time_ = base::TimeTicks::Now() + | 380 fade_end_time_ = base::TimeTicks::Now() + |
285 base::TimeDelta::FromMillisecondsD( | 381 base::TimeDelta::FromMillisecondsD( |
286 kFadeDurationMs * std::abs(target_alpha - alpha_)); | 382 kFadeDurationMs * std::abs(target_alpha - alpha_)); |
287 fade_start_position_ = position_; | 383 fade_start_position_ = focus_bottom_; |
288 client_->SetNeedsAnimate(); | 384 client_->SetNeedsAnimate(); |
289 } | 385 } |
290 | 386 |
291 void TouchHandle::EndFade() { | 387 void TouchHandle::EndFade() { |
292 DCHECK(enabled_); | 388 DCHECK(enabled_); |
293 animate_deferred_fade_ = false; | 389 animate_deferred_fade_ = false; |
294 fade_end_time_ = base::TimeTicks(); | 390 fade_end_time_ = base::TimeTicks(); |
295 SetAlpha(is_visible_ ? 1.f : 0.f); | 391 SetAlpha(is_visible_ ? 1.f : 0.f); |
296 } | 392 } |
297 | 393 |
298 void TouchHandle::SetAlpha(float alpha) { | 394 void TouchHandle::SetAlpha(float alpha) { |
299 alpha = std::max(0.f, std::min(1.f, alpha)); | 395 alpha = std::max(0.f, std::min(1.f, alpha)); |
300 if (alpha_ == alpha) | 396 if (alpha_ == alpha) |
301 return; | 397 return; |
302 alpha_ = alpha; | 398 alpha_ = alpha; |
303 drawable_->SetAlpha(alpha); | 399 drawable_->SetAlpha(alpha); |
304 } | 400 } |
305 | 401 |
| 402 void TouchHandle::SetUpdateLayoutRequired() { |
| 403 // TODO(AviD): Make the layout call explicit to the caller by adding this in |
| 404 // TouchHandleClient. |
| 405 is_handle_layout_update_required_ = true; |
| 406 } |
| 407 |
306 } // namespace ui | 408 } // namespace ui |
OLD | NEW |