 Chromium Code Reviews
 Chromium Code Reviews Issue 335943002:
  [Android] Composited selection handle rendering  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@input_native_handles_final
    
  
    Issue 335943002:
  [Android] Composited selection handle rendering  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@input_native_handles_final| Index: content/browser/renderer_host/input/touch_handle.cc | 
| diff --git a/content/browser/renderer_host/input/touch_handle.cc b/content/browser/renderer_host/input/touch_handle.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..63b6d961b1b66036533977a78d407304c3d7ec84 | 
| --- /dev/null | 
| +++ b/content/browser/renderer_host/input/touch_handle.cc | 
| @@ -0,0 +1,208 @@ | 
| +// Copyright 2014 The Chromium Authors. All rights reserved. | 
| +// Use of this source code is governed by a BSD-style license that can be | 
| +// found in the LICENSE file. | 
| + | 
| +#include "content/browser/renderer_host/input/touch_handle.h" | 
| + | 
| +namespace content { | 
| + | 
| +namespace { | 
| + | 
| +// Maximum duration of a fade sequence. | 
| +const double kFadeDurationMs = 200; | 
| + | 
| +// Maximum amount of travel for a fade sequence. This avoids handle "ghosting" | 
| +// when the handle is moving rapidly while the fade is active. | 
| +const double kFadeDistanceSquared = 20.f * 20.f; | 
| + | 
| +} // namespace | 
| + | 
| +// Responsible for rendering a selection or insertion handle for text editing. | 
| +TouchHandle::TouchHandle(TouchHandleClient* client, | 
| + TouchHandleOrientation orientation) | 
| + : drawable_(client->CreateDrawable()), | 
| + client_(client), | 
| + orientation_(orientation), | 
| + deferred_orientation_(TOUCH_HANDLE_ORIENTATION_UNDEFINED), | 
| + alpha_(0.f), | 
| + enabled_(true), | 
| + is_visible_(false), | 
| + is_dragging_(false) { | 
| + DCHECK_NE(orientation, TOUCH_HANDLE_ORIENTATION_UNDEFINED); | 
| + drawable_->SetEnabled(enabled_); | 
| + drawable_->SetOrientation(orientation_); | 
| + drawable_->SetAlpha(alpha_); | 
| + drawable_->SetVisible(is_visible_); | 
| + drawable_->SetFocus(position_); | 
| +} | 
| + | 
| +TouchHandle::~TouchHandle() { | 
| +} | 
| + | 
| +void TouchHandle::SetEnabled(bool enabled) { | 
| + if (enabled_ == enabled) | 
| + return; | 
| + enabled_ = enabled; | 
| + drawable_->SetEnabled(enabled); | 
| + if (!enabled_) { | 
| + EndFade(); | 
| 
cjhopman
2014/07/14 18:23:52
I think that, since EndDrag() can trigger a fade,
 
jdduke (slow)
2014/07/15 15:44:24
You're absolutely right, I've been running tests l
 | 
| + EndDrag(); | 
| + } | 
| +} | 
| + | 
| +void TouchHandle::SetVisible(bool visible, AnimationStyle animation_style) { | 
| + if (is_visible_ == visible) | 
| + return; | 
| + is_visible_ = visible; | 
| + if (!enabled_ || animation_style == ANIMATION_NONE) { | 
| + EndFade(); | 
| + } else { | 
| + // The fade will be rescheduled once dragging completes. | 
| + if (!is_dragging_) | 
| + BeginFade(); | 
| + } | 
| +} | 
| + | 
| +void TouchHandle::SetPosition(const gfx::PointF& position) { | 
| + if (position_ == position) | 
| + return; | 
| + position_ = position; | 
| + // Suppress repositioning a handle while invisible or fading to prevent it | 
| + // from "ghosting" outside the visible bounds. The position will be pushed to | 
| + // the drawable when the handle regains visibility (see |{Begin|End}Fade|). | 
| 
cjhopman
2014/07/14 18:23:52
For handling the deferred position while invisible
 
jdduke (slow)
2014/07/15 15:44:24
That would be a better place, but we still need th
 | 
| + if (is_visible_) | 
| 
cjhopman
2014/07/14 18:23:52
This allows changing the position while fading in
 
jdduke (slow)
2014/07/15 15:44:24
Hmm, yeah we only want to defer while fading *out*
 | 
| + drawable_->SetFocus(position_); | 
| +} | 
| + | 
| +void TouchHandle::SetOrientation(TouchHandleOrientation orientation) { | 
| + DCHECK_NE(orientation, TOUCH_HANDLE_ORIENTATION_UNDEFINED); | 
| + if (is_dragging_) { | 
| + deferred_orientation_ = orientation; | 
| + return; | 
| + } | 
| + deferred_orientation_ = TOUCH_HANDLE_ORIENTATION_UNDEFINED; | 
| + if (orientation_ == orientation) | 
| + return; | 
| + | 
| + orientation_ = orientation; | 
| + drawable_->SetOrientation(orientation); | 
| +} | 
| + | 
| +bool TouchHandle::WillHandleTouchEvent(const ui::MotionEvent& event) { | 
| + if (!enabled_) | 
| + return false; | 
| + | 
| + if (!is_dragging_ && event.GetAction() != ui::MotionEvent::ACTION_DOWN) | 
| + return false; | 
| + | 
| + switch (event.GetAction()) { | 
| + case ui::MotionEvent::ACTION_DOWN: { | 
| + gfx::PointF touch_position = gfx::PointF(event.GetX(), event.GetY()); | 
| + if (!is_visible_ || !drawable_->ContainsPoint(touch_position)) | 
| + return false; | 
| + touch_down_position_ = touch_position; | 
| + touch_to_focus_offset_ = position_ - touch_down_position_; | 
| + touch_down_time_ = event.GetEventTime(); | 
| + BeginDrag(); | 
| + } break; | 
| + | 
| + case ui::MotionEvent::ACTION_MOVE: { | 
| + gfx::PointF new_position = | 
| + gfx::PointF(event.GetX(), event.GetY()) + touch_to_focus_offset_; | 
| + client_->OnHandleDragUpdate(*this, new_position); | 
| + } break; | 
| + | 
| + case ui::MotionEvent::ACTION_UP: { | 
| + base::TimeDelta delay = event.GetEventTime() - touch_down_time_; | 
| + if (delay < base::TimeDelta::FromMilliseconds(180)) | 
| + client_->OnHandleTapped(*this); | 
| + | 
| + EndDrag(); | 
| + } break; | 
| + | 
| + case ui::MotionEvent::ACTION_CANCEL: | 
| + EndDrag(); | 
| + break; | 
| + | 
| + default: | 
| + break; | 
| + }; | 
| + return true; | 
| +} | 
| + | 
| +bool TouchHandle::Animate(base::TimeTicks frame_time) { | 
| + if (fade_end_time_ == base::TimeTicks()) | 
| + return false; | 
| + | 
| + float time_u = | 
| + 1.f - (fade_end_time_ - frame_time).InMillisecondsF() / kFadeDurationMs; | 
| + float position_u = | 
| + (position_ - fade_start_position_).LengthSquared() / kFadeDistanceSquared; | 
| + float u = std::max(time_u, position_u); | 
| + SetAlpha(is_visible_ ? u : 1.f - u); | 
| + | 
| + if (u >= 1.f) { | 
| + EndFade(); | 
| + return false; | 
| + } | 
| + | 
| + return true; | 
| +} | 
| + | 
| +void TouchHandle::BeginDrag() { | 
| + DCHECK(enabled_); | 
| + if (is_dragging_) | 
| + return; | 
| + is_dragging_ = true; | 
| + client_->OnHandleDragBegin(*this); | 
| +} | 
| + | 
| +void TouchHandle::EndDrag() { | 
| + if (!is_dragging_) | 
| + return; | 
| + | 
| + is_dragging_ = false; | 
| + client_->OnHandleDragEnd(*this); | 
| + | 
| + if (deferred_orientation_ != TOUCH_HANDLE_ORIENTATION_UNDEFINED) | 
| + SetOrientation(deferred_orientation_); | 
| + | 
| + BeginFade(); | 
| +} | 
| + | 
| +void TouchHandle::BeginFade() { | 
| + DCHECK(enabled_); | 
| + | 
| + if (is_visible_) | 
| + drawable_->SetFocus(position_); | 
| + | 
| + const float target_alpha = is_visible_ ? 1.f : 0.f; | 
| + if (target_alpha == alpha_) | 
| + return; | 
| + | 
| + drawable_->SetVisible(true); | 
| + fade_end_time_ = base::TimeTicks::Now() + | 
| + base::TimeDelta::FromMillisecondsD( | 
| + kFadeDurationMs * std::abs(target_alpha - alpha_)); | 
| + fade_start_position_ = position_; | 
| + client_->SetNeedsAnimate(); | 
| +} | 
| + | 
| +void TouchHandle::EndFade() { | 
| + if (is_visible_) | 
| + drawable_->SetFocus(position_); | 
| + | 
| + fade_end_time_ = base::TimeTicks(); | 
| + SetAlpha(is_visible_ ? 1.f : 0.f); | 
| + drawable_->SetVisible(is_visible_); | 
| +} | 
| + | 
| +void TouchHandle::SetAlpha(float alpha) { | 
| + alpha = std::max(0.f, std::min(1.f, alpha)); | 
| + if (alpha_ == alpha) | 
| + return; | 
| + alpha_ = alpha; | 
| + drawable_->SetAlpha(alpha); | 
| +} | 
| + | 
| +} // namespace content |