Commit 1d6fc870 authored by jdduke's avatar jdduke Committed by Commit bot

Consolidate content fling implementations

Provide a single implementation of WebGestureCurve, instead relying on
platform-specific ui::GestureCurve implementations for core
functionality. This eliminates the duplicated curve code used for
content and view-targetted flings on Aura.

BUG=420214

Review URL: https://codereview.chromium.org/634373003

Cr-Commit-Position: refs/heads/master@{#299136}
parent bee8d3d4
...@@ -777,14 +777,15 @@ class WindowOverviewModeImpl : public WindowOverviewMode, ...@@ -777,14 +777,15 @@ class WindowOverviewModeImpl : public WindowOverviewMode,
// ui::CompositorAnimationObserver: // ui::CompositorAnimationObserver:
virtual void OnAnimationStep(base::TimeTicks timestamp) override { virtual void OnAnimationStep(base::TimeTicks timestamp) override {
CHECK(fling_); CHECK(fling_);
if (fling_->start_timestamp() > timestamp) gfx::Vector2dF delta;
return; bool fling_active = fling_->ComputeScrollDeltaAtTime(timestamp, &delta);
gfx::Vector2dF scroll = fling_->GetScrollAmountAtTime(timestamp);
if (scroll.IsZero()) { if (!delta.IsZero())
DoScroll(delta.y());
if (!fling_active) {
fling_.reset(); fling_.reset();
RemoveAnimationObserver(); RemoveAnimationObserver();
} else {
DoScroll(scroll.y());
} }
} }
......
per-file appcache*=michaeln@chromium.org per-file appcache*=michaeln@chromium.org
per-file *fling*=jdduke@chromium.org per-file *curve*=jdduke@chromium.org
# WebSocket # WebSocket
per-file *websocket*=ricea@chromium.org per-file *websocket*=ricea@chromium.org
......
...@@ -33,8 +33,8 @@ ...@@ -33,8 +33,8 @@
#include "content/child/child_thread.h" #include "content/child/child_thread.h"
#include "content/child/content_child_helpers.h" #include "content/child/content_child_helpers.h"
#include "content/child/geofencing/web_geofencing_provider_impl.h" #include "content/child/geofencing/web_geofencing_provider_impl.h"
#include "content/child/touch_fling_gesture_curve.h"
#include "content/child/web_discardable_memory_impl.h" #include "content/child/web_discardable_memory_impl.h"
#include "content/child/web_gesture_curve_impl.h"
#include "content/child/web_socket_stream_handle_impl.h" #include "content/child/web_socket_stream_handle_impl.h"
#include "content/child/web_url_loader_impl.h" #include "content/child/web_url_loader_impl.h"
#include "content/child/websocket_bridge.h" #include "content/child/websocket_bridge.h"
...@@ -47,16 +47,13 @@ ...@@ -47,16 +47,13 @@
#include "net/base/net_util.h" #include "net/base/net_util.h"
#include "third_party/WebKit/public/platform/WebConvertableToTraceFormat.h" #include "third_party/WebKit/public/platform/WebConvertableToTraceFormat.h"
#include "third_party/WebKit/public/platform/WebData.h" #include "third_party/WebKit/public/platform/WebData.h"
#include "third_party/WebKit/public/platform/WebFloatPoint.h"
#include "third_party/WebKit/public/platform/WebString.h" #include "third_party/WebKit/public/platform/WebString.h"
#include "third_party/WebKit/public/platform/WebURL.h" #include "third_party/WebKit/public/platform/WebURL.h"
#include "third_party/WebKit/public/platform/WebWaitableEvent.h" #include "third_party/WebKit/public/platform/WebWaitableEvent.h"
#include "third_party/WebKit/public/web/WebSecurityOrigin.h" #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
#include "ui/base/layout.h" #include "ui/base/layout.h"
#if defined(OS_ANDROID)
#include "content/child/fling_animator_impl_android.h"
#endif
#if !defined(NO_TCMALLOC) && defined(USE_TCMALLOC) && !defined(OS_WIN) #if !defined(NO_TCMALLOC) && defined(USE_TCMALLOC) && !defined(OS_WIN)
#include "third_party/tcmalloc/chromium/src/gperftools/heap-profiler.h" #include "third_party/tcmalloc/chromium/src/gperftools/heap-profiler.h"
#endif #endif
...@@ -1002,13 +999,10 @@ blink::WebGestureCurve* BlinkPlatformImpl::createFlingAnimationCurve( ...@@ -1002,13 +999,10 @@ blink::WebGestureCurve* BlinkPlatformImpl::createFlingAnimationCurve(
blink::WebGestureDevice device_source, blink::WebGestureDevice device_source,
const blink::WebFloatPoint& velocity, const blink::WebFloatPoint& velocity,
const blink::WebSize& cumulative_scroll) { const blink::WebSize& cumulative_scroll) {
#if defined(OS_ANDROID) auto curve = WebGestureCurveImpl::CreateFromDefaultPlatformCurve(
return FlingAnimatorImpl::CreateAndroidGestureCurve( gfx::Vector2dF(velocity.x, velocity.y),
velocity, gfx::Vector2dF(cumulative_scroll.width, cumulative_scroll.height));
cumulative_scroll); return curve.release();
#endif
return TouchFlingGestureCurve::Create(velocity, cumulative_scroll);
} }
void BlinkPlatformImpl::didStartWorkerRunLoop( void BlinkPlatformImpl::didStartWorkerRunLoop(
......
// 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/child/fling_animator_impl_android.h"
#include "base/logging.h"
#include "third_party/WebKit/public/platform/WebFloatSize.h"
#include "third_party/WebKit/public/platform/WebGestureCurveTarget.h"
#include "ui/gfx/frame_time.h"
#include "ui/gfx/vector2d.h"
namespace content {
namespace {
// Value taken directly from Android's ViewConfiguration. As the value has not
// changed in 4+ years, and does not depend on any device-specific configuration
// parameters, copy it directly to avoid potential JNI interop issues in the
// render process (see crbug.com/362614).
const float kDefaultAndroidPlatformScrollFriction = 0.015f;
gfx::Scroller::Config GetScrollerConfig() {
gfx::Scroller::Config config;
config.flywheel_enabled = false;
config.fling_friction = kDefaultAndroidPlatformScrollFriction;
return config;
}
} // namespace
FlingAnimatorImpl::FlingAnimatorImpl()
: is_active_(false),
scroller_(GetScrollerConfig()) {}
FlingAnimatorImpl::~FlingAnimatorImpl() {}
void FlingAnimatorImpl::StartFling(const gfx::PointF& velocity) {
// No bounds on the fling. See http://webkit.org/b/96403
// Instead, use the largest possible bounds for minX/maxX/minY/maxY. The
// compositor will ignore any attempt to scroll beyond the end of the page.
DCHECK(velocity.x() || velocity.y());
if (is_active_)
CancelFling();
is_active_ = true;
scroller_.Fling(0,
0,
velocity.x(),
velocity.y(),
INT_MIN,
INT_MAX,
INT_MIN,
INT_MAX,
base::TimeTicks());
}
void FlingAnimatorImpl::CancelFling() {
if (!is_active_)
return;
is_active_ = false;
scroller_.AbortAnimation();
}
bool FlingAnimatorImpl::apply(double time,
blink::WebGestureCurveTarget* target) {
// If the fling has yet to start, simply return and report true to prevent
// fling termination.
if (time <= 0)
return true;
const base::TimeTicks time_ticks =
base::TimeTicks() + base::TimeDelta::FromMicroseconds(
time * base::Time::kMicrosecondsPerSecond);
if (!scroller_.ComputeScrollOffset(time_ticks)) {
is_active_ = false;
return false;
}
gfx::PointF current_position(scroller_.GetCurrX(), scroller_.GetCurrY());
gfx::Vector2dF scroll_amount(current_position - last_position_);
last_position_ = current_position;
// scrollBy() could delete this curve if the animation is over, so don't touch
// any member variables after making that call.
return target->scrollBy(blink::WebFloatSize(scroll_amount),
blink::WebFloatSize(scroller_.GetCurrVelocityX(),
scroller_.GetCurrVelocityY()));
}
FlingAnimatorImpl* FlingAnimatorImpl::CreateAndroidGestureCurve(
const blink::WebFloatPoint& velocity,
const blink::WebSize&) {
FlingAnimatorImpl* gesture_curve = new FlingAnimatorImpl();
gesture_curve->StartFling(velocity);
return gesture_curve;
}
} // namespace content
// 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.
#ifndef CONTENT_CHILD_FLING_ANIMATOR_IMPL_ANDROID_H_
#define CONTENT_CHILD_FLING_ANIMATOR_IMPL_ANDROID_H_
#include "third_party/WebKit/public/platform/WebFloatPoint.h"
#include "third_party/WebKit/public/platform/WebGestureCurve.h"
#include "third_party/WebKit/public/platform/WebSize.h"
#include "ui/gfx/android/scroller.h"
#include "ui/gfx/point_f.h"
namespace blink {
class WebGestureCurveTarget;
}
namespace content {
class FlingAnimatorImpl : public blink::WebGestureCurve {
public:
FlingAnimatorImpl();
virtual ~FlingAnimatorImpl();
static FlingAnimatorImpl* CreateAndroidGestureCurve(
const blink::WebFloatPoint& velocity,
const blink::WebSize&);
virtual bool apply(double time, blink::WebGestureCurveTarget* target);
private:
void StartFling(const gfx::PointF& velocity);
void CancelFling();
bool is_active_;
gfx::Scroller scroller_;
gfx::PointF last_position_;
DISALLOW_COPY_AND_ASSIGN(FlingAnimatorImpl);
};
} // namespace content
#endif // CONTENT_CHILD_FLING_ANIMATOR_IMPL_ANDROID_H_
// 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/child/touch_fling_gesture_curve.h"
#include <cmath>
#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "third_party/WebKit/public/platform/WebFloatPoint.h"
#include "third_party/WebKit/public/platform/WebFloatSize.h"
#include "third_party/WebKit/public/platform/WebGestureCurve.h"
#include "third_party/WebKit/public/platform/WebGestureCurveTarget.h"
#include "third_party/WebKit/public/platform/WebSize.h"
using blink::WebFloatPoint;
using blink::WebFloatSize;
using blink::WebGestureCurve;
using blink::WebGestureCurveTarget;
using blink::WebSize;
namespace {
const char* kCurveName = "TouchFlingGestureCurve";
// The touchpad / touchscreen fling profiles are a matched set
// determined via UX experimentation. Do not modify without
// first discussing with rjkroege@chromium.org or
// wjmaclean@chromium.org.
const float kDefaultAlpha = -5.70762e+03f;
const float kDefaultBeta = 1.72e+02f;
const float kDefaultGamma = 3.7e+00f;
inline double position(double t) {
return kDefaultAlpha * exp(-kDefaultGamma * t) - kDefaultBeta * t -
kDefaultAlpha;
}
inline double velocity(double t) {
return -kDefaultAlpha * kDefaultGamma * exp(-kDefaultGamma * t) -
kDefaultBeta;
}
inline double timeAtVelocity(double v) {
return -log((v + kDefaultBeta) / (-kDefaultAlpha * kDefaultGamma)) /
kDefaultGamma;
}
} // namespace
namespace content {
// This curve implementation is based on the notion of a single, absolute
// curve, which starts at a large velocity and smoothly decreases to
// zero. For a given input velocity, we find where on the curve this
// velocity occurs, and start the animation at this point---denoted by
// (time_offset_, position_offset_).
//
// This has the effect of automatically determining an animation duration
// that scales with input velocity, as faster initial velocities start
// earlier on the curve and thus take longer to reach the end. No
// complicated time scaling is required.
//
// Since the starting velocity is implicitly determined by our starting
// point, we only store the relative magnitude and direction of both
// initial x- and y-velocities, and use this to scale the computed
// displacement at any point in time. This guarantees that fling
// trajectories are straight lines when viewed in x-y space. Initial
// velocities that lie outside the max velocity are constrained to start
// at zero (and thus are implicitly scaled).
//
// The curve is modelled as a 4th order polynomial, starting at t = 0,
// and ending at t = curve_duration_. Attempts to generate
// position/velocity estimates outside this range are undefined.
WebGestureCurve* TouchFlingGestureCurve::Create(
const WebFloatPoint& initial_velocity,
const WebSize& cumulative_scroll) {
return new TouchFlingGestureCurve(initial_velocity, cumulative_scroll);
}
TouchFlingGestureCurve::TouchFlingGestureCurve(
const WebFloatPoint& initial_velocity,
const WebSize& cumulative_scroll)
: cumulative_scroll_(WebFloatSize(cumulative_scroll.width,
cumulative_scroll.height)) {
DCHECK(initial_velocity != WebFloatPoint());
// Curve ends when velocity reaches zero.
curve_duration_ = timeAtVelocity(0);
DCHECK(curve_duration_ > 0);
float max_start_velocity = std::max(fabs(initial_velocity.x),
fabs(initial_velocity.y));
// Force max_start_velocity to lie in the range v(0) to v(curve_duration),
// and assume that the curve parameters define a monotonically decreasing
// velocity, or else bisection search may fail.
if (max_start_velocity > velocity(0))
max_start_velocity = velocity(0);
if (max_start_velocity < 0)
max_start_velocity = 0;
// We keep track of relative magnitudes and directions of the
// velocity/displacement components here.
displacement_ratio_ = WebFloatPoint(initial_velocity.x / max_start_velocity,
initial_velocity.y / max_start_velocity);
// Compute time-offset for start velocity.
time_offset_ = timeAtVelocity(max_start_velocity);
// Compute curve position at offset time
position_offset_ = position(time_offset_);
TRACE_EVENT_ASYNC_BEGIN1("input", "GestureAnimation", this, "curve",
kCurveName);
}
TouchFlingGestureCurve::~TouchFlingGestureCurve() {
TRACE_EVENT_ASYNC_END0("input", "GestureAnimation", this);
}
bool TouchFlingGestureCurve::apply(double time, WebGestureCurveTarget* target) {
// If the fling has yet to start, simply return and report true to prevent
// fling termination.
if (time <= 0)
return true;
float displacement;
float speed;
if (time + time_offset_ < curve_duration_) {
displacement = position(time + time_offset_) - position_offset_;
speed = velocity(time + time_offset_);
} else {
displacement = position(curve_duration_) - position_offset_;
speed = 0.f;
}
// Keep track of integer portion of scroll thus far, and prepare increment.
WebFloatSize scroll(displacement * displacement_ratio_.x,
displacement * displacement_ratio_.y);
WebFloatSize scroll_increment(scroll.width - cumulative_scroll_.width,
scroll.height - cumulative_scroll_.height);
WebFloatSize scroll_velocity(speed * displacement_ratio_.x,
speed * displacement_ratio_.y);
cumulative_scroll_ = scroll;
if (time + time_offset_ < curve_duration_ ||
scroll_increment != WebFloatSize()) {
// scrollBy() could delete this curve if the animation is over, so don't
// touch any member variables after making that call.
return target->scrollBy(scroll_increment, scroll_velocity);
}
return false;
}
} // namespace content
// 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.
#ifndef CONTENT_CHILD_TOUCH_FLING_GESTURE_CURVE_H_
#define CONTENT_CHILD_TOUCH_FLING_GESTURE_CURVE_H_
#include "content/common/content_export.h"
#include "third_party/WebKit/public/platform/WebFloatPoint.h"
#include "third_party/WebKit/public/platform/WebFloatSize.h"
#include "third_party/WebKit/public/platform/WebGestureCurve.h"
#include "third_party/WebKit/public/platform/WebSize.h"
namespace blink {
class WebGestureCurveTarget;
}
namespace content {
// Implementation of WebGestureCurve suitable for touch pad/screen-based
// fling scroll. Starts with a flat velocity profile based on 'velocity', which
// tails off to zero. Time is scaled to that duration of the fling is
// proportional to the initial velocity.
class TouchFlingGestureCurve : public blink::WebGestureCurve {
public:
static CONTENT_EXPORT WebGestureCurve* Create(
const blink::WebFloatPoint& initial_velocity,
const blink::WebSize& cumulativeScroll);
virtual bool apply(double monotonicTime,
blink::WebGestureCurveTarget*) override;
private:
TouchFlingGestureCurve(const blink::WebFloatPoint& initial_velocity,
const blink::WebSize& cumulativeScroll);
virtual ~TouchFlingGestureCurve();
blink::WebFloatPoint displacement_ratio_;
blink::WebFloatSize cumulative_scroll_;
float time_offset_;
float curve_duration_;
float position_offset_;
DISALLOW_COPY_AND_ASSIGN(TouchFlingGestureCurve);
};
} // namespace content
#endif // CONTENT_CHILD_TOUCH_FLING_GESTURE_CURVE_H_
// 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/child/web_gesture_curve_impl.h"
#include "base/logging.h"
#include "third_party/WebKit/public/platform/WebFloatSize.h"
#include "third_party/WebKit/public/platform/WebGestureCurveTarget.h"
#include "ui/events/gestures/fling_curve.h"
#include "ui/gfx/vector2d.h"
#if defined(OS_ANDROID)
#include "ui/events/android/scroller.h"
#endif
using blink::WebGestureCurve;
namespace content {
namespace {
scoped_ptr<ui::GestureCurve> CreateDefaultPlatformCurve(
const gfx::Vector2dF& initial_velocity) {
DCHECK(!initial_velocity.IsZero());
#if defined(OS_ANDROID)
auto scroller = make_scoped_ptr(new ui::Scroller(ui::Scroller::Config()));
scroller->Fling(0,
0,
initial_velocity.x(),
initial_velocity.y(),
INT_MIN,
INT_MAX,
INT_MIN,
INT_MAX,
base::TimeTicks());
return scroller.PassAs<ui::GestureCurve>();
#else
return scoped_ptr<ui::GestureCurve>(
new ui::FlingCurve(initial_velocity, base::TimeTicks()));
#endif
}
} // namespace
// static
scoped_ptr<WebGestureCurve> WebGestureCurveImpl::CreateFromDefaultPlatformCurve(
const gfx::Vector2dF& initial_velocity,
const gfx::Vector2dF& initial_offset) {
return CreateFrom(CreateDefaultPlatformCurve(initial_velocity),
initial_offset);
}
// static
scoped_ptr<WebGestureCurve> WebGestureCurveImpl::CreateFrom(
scoped_ptr<ui::GestureCurve> curve,
const gfx::Vector2dF& initial_offset) {
return scoped_ptr<WebGestureCurve>(
new WebGestureCurveImpl(curve.Pass(), initial_offset));
}
WebGestureCurveImpl::WebGestureCurveImpl(scoped_ptr<ui::GestureCurve> curve,
const gfx::Vector2dF& initial_offset)
: curve_(curve.Pass()), last_offset_(initial_offset) {
}
WebGestureCurveImpl::~WebGestureCurveImpl() {
}
bool WebGestureCurveImpl::apply(double time,
blink::WebGestureCurveTarget* target) {
// If the fling has yet to start, simply return and report true to prevent
// fling termination.
if (time <= 0)
return true;
const base::TimeTicks time_ticks =
base::TimeTicks() + base::TimeDelta::FromSecondsD(time);
gfx::Vector2dF offset, velocity;
bool still_active =
curve_->ComputeScrollOffset(time_ticks, &offset, &velocity);
gfx::Vector2dF delta = offset - last_offset_;
last_offset_ = offset;
// As successive timestamps can be arbitrarily close (but monotonic!), don't
// assume that a zero delta means the curve has terminated.
if (delta.IsZero())
return still_active;
// scrollBy() could delete this curve if the animation is over, so don't touch
// any member variables after making that call.
bool did_scroll = target->scrollBy(blink::WebFloatSize(delta),
blink::WebFloatSize(velocity));
return did_scroll && still_active;
}
} // namespace content
// 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.
#ifndef CONTENT_CHILD_WEB_GESTURE_CURVE_IMPL_H_
#define CONTENT_CHILD_WEB_GESTURE_CURVE_IMPL_H_
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
#include "content/common/content_export.h"
#include "third_party/WebKit/public/platform/WebGestureCurve.h"
#include "ui/gfx/geometry/vector2d_f.h"
namespace blink {
class WebGestureCurveTarget;
}
namespace ui {
class GestureCurve;
}
namespace content {
class CONTENT_EXPORT WebGestureCurveImpl
: public NON_EXPORTED_BASE(blink::WebGestureCurve) {
public:
static scoped_ptr<blink::WebGestureCurve> CreateFromDefaultPlatformCurve(
const gfx::Vector2dF& initial_velocity,
const gfx::Vector2dF& initial_offset);
static scoped_ptr<blink::WebGestureCurve> CreateFrom(
scoped_ptr<ui::GestureCurve> curve,
const gfx::Vector2dF& initial_offset);
virtual ~WebGestureCurveImpl();
// WebGestureCurve implementation.
virtual bool apply(double time,
blink::WebGestureCurveTarget* target) override;
private:
WebGestureCurveImpl(scoped_ptr<ui::GestureCurve> curve,
const gfx::Vector2dF& initial_offset);
scoped_ptr<ui::GestureCurve> curve_;
gfx::Vector2dF last_offset_;
DISALLOW_COPY_AND_ASSIGN(WebGestureCurveImpl);
};
} // namespace content
#endif // CONTENT_CHILD_WEB_GESTURE_CURVE_IMPL_H_
// Copyright 2014 The Chromium Authors. All rights reserved. // Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// Tests for the TouchFlingGestureCurve. #include "content/child/web_gesture_curve_impl.h"
#include "content/child/touch_fling_gesture_curve.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/public/platform/WebFloatPoint.h"
#include "third_party/WebKit/public/platform/WebFloatSize.h" #include "third_party/WebKit/public/platform/WebFloatSize.h"
#include "third_party/WebKit/public/platform/WebGestureCurve.h" #include "third_party/WebKit/public/platform/WebGestureCurve.h"
#include "third_party/WebKit/public/platform/WebGestureCurveTarget.h" #include "third_party/WebKit/public/platform/WebGestureCurveTarget.h"
#include "third_party/WebKit/public/platform/WebSize.h" #include "ui/events/gestures/fling_curve.h"
using blink::WebFloatPoint;
using blink::WebFloatSize; using blink::WebFloatSize;
using blink::WebGestureCurve; using blink::WebGestureCurve;
using blink::WebGestureCurveTarget; using blink::WebGestureCurveTarget;
using blink::WebSize;
namespace content {
namespace { namespace {
class MockGestureCurveTarget : public WebGestureCurveTarget { class MockGestureCurveTarget : public WebGestureCurveTarget {
...@@ -32,34 +28,40 @@ class MockGestureCurveTarget : public WebGestureCurveTarget { ...@@ -32,34 +28,40 @@ class MockGestureCurveTarget : public WebGestureCurveTarget {
return true; return true;
} }
WebFloatSize cumulative_delta() const { return cumulative_delta_; } const WebFloatSize& cumulative_delta() const { return cumulative_delta_; }
void resetCumulativeDelta() { cumulative_delta_ = WebFloatSize(); } const WebFloatSize& current_velocity() const { return current_velocity_; }
WebFloatSize current_velocity() const { return current_velocity_; }
private: private:
WebFloatSize cumulative_delta_; WebFloatSize cumulative_delta_;
WebFloatSize current_velocity_; WebFloatSize current_velocity_;
}; };
} // namespace anonymous } // namespace anonymous
TEST(TouchFlingGestureCurve, flingCurveTouch)
{
double initialVelocity = 5000;
MockGestureCurveTarget target;
scoped_ptr<WebGestureCurve> curve(content::TouchFlingGestureCurve::Create( TEST(WebGestureCurveImplTest, Basic) {
WebFloatPoint(initialVelocity, 0), WebSize())); gfx::Vector2dF velocity(5000, 0);
gfx::Vector2dF offset;
base::TimeTicks time;
auto curve = WebGestureCurveImpl::CreateFrom(
scoped_ptr<ui::GestureCurve>(new ui::FlingCurve(velocity, time)), offset);
// Note: the expectations below are dependent on the curve parameters hard
// coded into the create call above. // coded into the create call above.
MockGestureCurveTarget target;
EXPECT_TRUE(curve->apply(0, &target)); EXPECT_TRUE(curve->apply(0, &target));
EXPECT_TRUE(curve->apply(0.25, &target)); EXPECT_TRUE(curve->apply(0.25, &target));
EXPECT_NEAR(target.current_velocity().width, 1878, 1); EXPECT_NEAR(target.current_velocity().width, 1878, 1);
EXPECT_EQ(target.current_velocity().height, 0); EXPECT_EQ(target.current_velocity().height, 0);
EXPECT_TRUE(curve->apply(0.45f, &target)); // Use non-uniform tick spacing. EXPECT_GT(target.cumulative_delta().width, 0);
EXPECT_TRUE(curve->apply(1, &target)); EXPECT_TRUE(curve->apply(0.45, &target)); // Use non-uniform tick spacing.
// Ensure fling persists even if successive timestamps are identical.
gfx::Vector2dF cumulative_delta = target.cumulative_delta();
gfx::Vector2dF current_velocity = target.current_velocity();
EXPECT_TRUE(curve->apply(0.45, &target));
EXPECT_EQ(cumulative_delta, gfx::Vector2dF(target.cumulative_delta()));
EXPECT_EQ(current_velocity, gfx::Vector2dF(target.current_velocity()));
EXPECT_TRUE(curve->apply(0.75, &target));
EXPECT_FALSE(curve->apply(1.5, &target)); EXPECT_FALSE(curve->apply(1.5, &target));
EXPECT_NEAR(target.cumulative_delta().width, 1193, 1); EXPECT_NEAR(target.cumulative_delta().width, 1193, 1);
EXPECT_EQ(target.cumulative_delta().height, 0); EXPECT_EQ(target.cumulative_delta().height, 0);
...@@ -67,3 +69,4 @@ TEST(TouchFlingGestureCurve, flingCurveTouch) ...@@ -67,3 +69,4 @@ TEST(TouchFlingGestureCurve, flingCurveTouch)
EXPECT_EQ(target.current_velocity().height, 0); EXPECT_EQ(target.current_velocity().height, 0);
} }
} // namespace content
...@@ -68,8 +68,6 @@ ...@@ -68,8 +68,6 @@
'child/fileapi/webfilewriter_base.h', 'child/fileapi/webfilewriter_base.h',
'child/fileapi/webfilewriter_impl.cc', 'child/fileapi/webfilewriter_impl.cc',
'child/fileapi/webfilewriter_impl.h', 'child/fileapi/webfilewriter_impl.h',
'child/fling_animator_impl_android.cc',
'child/fling_animator_impl_android.h',
'child/ftp_directory_listing_response_delegate.cc', 'child/ftp_directory_listing_response_delegate.cc',
'child/ftp_directory_listing_response_delegate.h', 'child/ftp_directory_listing_response_delegate.h',
'child/geofencing/geofencing_dispatcher.cc', 'child/geofencing/geofencing_dispatcher.cc',
...@@ -193,12 +191,12 @@ ...@@ -193,12 +191,12 @@
'child/thread_safe_sender.h', 'child/thread_safe_sender.h',
'child/threaded_data_provider.cc', 'child/threaded_data_provider.cc',
'child/threaded_data_provider.h', 'child/threaded_data_provider.h',
'child/touch_fling_gesture_curve.cc',
'child/touch_fling_gesture_curve.h',
'child/web_database_observer_impl.cc', 'child/web_database_observer_impl.cc',
'child/web_database_observer_impl.h', 'child/web_database_observer_impl.h',
'child/web_discardable_memory_impl.cc', 'child/web_discardable_memory_impl.cc',
'child/web_discardable_memory_impl.h', 'child/web_discardable_memory_impl.h',
'child/web_gesture_curve_impl.cc',
'child/web_gesture_curve_impl.h',
'child/web_socket_stream_handle_bridge.h', 'child/web_socket_stream_handle_bridge.h',
'child/web_socket_stream_handle_delegate.h', 'child/web_socket_stream_handle_delegate.h',
'child/web_socket_stream_handle_impl.cc', 'child/web_socket_stream_handle_impl.cc',
......
...@@ -642,7 +642,7 @@ ...@@ -642,7 +642,7 @@
'child/resource_dispatcher_unittest.cc', 'child/resource_dispatcher_unittest.cc',
'child/simple_webmimeregistry_impl_unittest.cc', 'child/simple_webmimeregistry_impl_unittest.cc',
'child/site_isolation_policy_unittest.cc', 'child/site_isolation_policy_unittest.cc',
'child/touch_fling_gesture_curve_unittest.cc', 'child/web_gesture_curve_impl_unittest.cc',
'child/web_url_loader_impl_unittest.cc', 'child/web_url_loader_impl_unittest.cc',
'child/webcrypto/test/aes_cbc_unittest.cc', 'child/webcrypto/test/aes_cbc_unittest.cc',
'child/webcrypto/test/aes_ctr_unittest.cc', 'child/webcrypto/test/aes_ctr_unittest.cc',
......
...@@ -16,6 +16,8 @@ static_library("dom4_keycode_converter") { ...@@ -16,6 +16,8 @@ static_library("dom4_keycode_converter") {
component("events_base") { component("events_base") {
sources = [ sources = [
"android/scroller.cc",
"android/scroller.h",
"device_data_manager.cc", "device_data_manager.cc",
"device_data_manager.h", "device_data_manager.h",
"device_hotplug_event_observer.h", "device_hotplug_event_observer.h",
...@@ -23,6 +25,7 @@ component("events_base") { ...@@ -23,6 +25,7 @@ component("events_base") {
"event_switches.cc", "event_switches.cc",
"event_switches.h", "event_switches.h",
"events_base_export.h", "events_base_export.h",
"gesture_curve.h",
"gesture_event_details.cc", "gesture_event_details.cc",
"gesture_event_details.h", "gesture_event_details.h",
"gestures/fling_curve.cc", "gestures/fling_curve.cc",
...@@ -273,6 +276,7 @@ source_set("test_support") { ...@@ -273,6 +276,7 @@ source_set("test_support") {
test("events_unittests") { test("events_unittests") {
sources = [ sources = [
"android/scroller_unittest.cc",
"cocoa/events_mac_unittest.mm", "cocoa/events_mac_unittest.mm",
"event_dispatcher_unittest.cc", "event_dispatcher_unittest.cc",
"event_processor_unittest.cc", "event_processor_unittest.cc",
......
jdduke@chromium.org
...@@ -2,13 +2,13 @@ ...@@ -2,13 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "ui/gfx/android/scroller.h" #include "ui/events/android/scroller.h"
#include <cmath> #include <cmath>
#include "base/lazy_instance.h" #include "base/lazy_instance.h"
namespace gfx { namespace ui {
namespace { namespace {
// Default scroll duration from android.widget.Scroller. // Default scroll duration from android.widget.Scroller.
...@@ -122,9 +122,7 @@ struct SplineConstants { ...@@ -122,9 +122,7 @@ struct SplineConstants {
} }
private: private:
enum { enum { NUM_SAMPLES = 100 };
NUM_SAMPLES = 100
};
float spline_position_[NUM_SAMPLES + 1]; float spline_position_[NUM_SAMPLES + 1];
float spline_time_[NUM_SAMPLES + 1]; float spline_time_[NUM_SAMPLES + 1];
...@@ -160,8 +158,8 @@ base::LazyInstance<SplineConstants>::Leaky g_spline_constants = ...@@ -160,8 +158,8 @@ base::LazyInstance<SplineConstants>::Leaky g_spline_constants =
} // namespace } // namespace
Scroller::Config::Config() Scroller::Config::Config()
: fling_friction(kDefaultFriction), : fling_friction(kDefaultFriction), flywheel_enabled(false) {
flywheel_enabled(false) {} }
Scroller::Scroller(const Config& config) Scroller::Scroller(const Config& config)
: mode_(UNDEFINED), : mode_(UNDEFINED),
...@@ -187,9 +185,27 @@ Scroller::Scroller(const Config& config) ...@@ -187,9 +185,27 @@ Scroller::Scroller(const Config& config)
distance_(0), distance_(0),
fling_friction_(config.fling_friction), fling_friction_(config.fling_friction),
deceleration_(ComputeDeceleration(fling_friction_)), deceleration_(ComputeDeceleration(fling_friction_)),
tuning_coeff_(ComputeDeceleration(0.84f)) {} tuning_coeff_(ComputeDeceleration(0.84f)) {
}
Scroller::~Scroller() {
}
Scroller::~Scroller() {} bool Scroller::ComputeScrollOffset(base::TimeTicks time,
gfx::Vector2dF* offset,
gfx::Vector2dF* velocity) {
DCHECK(offset);
DCHECK(velocity);
if (!ComputeScrollOffsetInternal(time)) {
*offset = gfx::Vector2dF(GetFinalX(), GetFinalY());
*velocity = gfx::Vector2dF();
return false;
}
*offset = gfx::Vector2dF(GetCurrX(), GetCurrY());
*velocity = gfx::Vector2dF(GetCurrVelocityX(), GetCurrVelocityY());
return true;
}
void Scroller::StartScroll(float start_x, void Scroller::StartScroll(float start_x,
float start_y, float start_y,
...@@ -210,6 +226,7 @@ void Scroller::StartScroll(float start_x, ...@@ -210,6 +226,7 @@ void Scroller::StartScroll(float start_x,
float dy, float dy,
base::TimeTicks start_time, base::TimeTicks start_time,
base::TimeDelta duration) { base::TimeDelta duration) {
DCHECK_GT(duration.ToInternalValue(), 0);
mode_ = SCROLL_MODE; mode_ = SCROLL_MODE;
finished_ = false; finished_ = false;
duration_ = duration; duration_ = duration;
...@@ -232,6 +249,8 @@ void Scroller::Fling(float start_x, ...@@ -232,6 +249,8 @@ void Scroller::Fling(float start_x,
float min_y, float min_y,
float max_y, float max_y,
base::TimeTicks start_time) { base::TimeTicks start_time) {
DCHECK(velocity_x || velocity_y);
// Continue a scroll or fling in progress. // Continue a scroll or fling in progress.
if (flywheel_enabled_ && !finished_) { if (flywheel_enabled_ && !finished_) {
float old_velocity_x = GetCurrVelocityX(); float old_velocity_x = GetCurrVelocityX();
...@@ -250,6 +269,7 @@ void Scroller::Fling(float start_x, ...@@ -250,6 +269,7 @@ void Scroller::Fling(float start_x,
velocity_ = velocity; velocity_ = velocity;
duration_ = GetSplineFlingDuration(velocity); duration_ = GetSplineFlingDuration(velocity);
DCHECK_GT(duration_.ToInternalValue(), 0);
duration_seconds_reciprocal_ = 1.0 / duration_.InSecondsF(); duration_seconds_reciprocal_ = 1.0 / duration_.InSecondsF();
start_time_ = start_time; start_time_ = start_time;
curr_time_ = start_time_; curr_time_ = start_time_;
...@@ -276,68 +296,6 @@ void Scroller::Fling(float start_x, ...@@ -276,68 +296,6 @@ void Scroller::Fling(float start_x,
RecomputeDeltas(); RecomputeDeltas();
} }
bool Scroller::ComputeScrollOffset(base::TimeTicks time) {
if (finished_)
return false;
if (time == curr_time_)
return true;
base::TimeDelta time_passed = time - start_time_;
if (time_passed < base::TimeDelta()) {
time_passed = base::TimeDelta();
}
if (time_passed >= duration_) {
curr_x_ = final_x_;
curr_y_ = final_y_;
curr_time_ = start_time_ + duration_;
finished_ = true;
return true;
}
curr_time_ = time;
const float t = time_passed.InSecondsF() * duration_seconds_reciprocal_;
switch (mode_) {
case UNDEFINED:
NOTREACHED() << "|StartScroll()| or |Fling()| must be called prior to "
"scroll offset computation.";
return false;
case SCROLL_MODE: {
float x = g_viscosity_constants.Get().ApplyViscosity(t);
curr_x_ = start_x_ + x * delta_x_;
curr_y_ = start_y_ + x * delta_y_;
} break;
case FLING_MODE: {
float distance_coef = 1.f;
float velocity_coef = 0.f;
g_spline_constants.Get().CalculateCoefficients(
t, &distance_coef, &velocity_coef);
curr_velocity_ = velocity_coef * distance_ * duration_seconds_reciprocal_;
curr_x_ = start_x_ + distance_coef * delta_x_;
curr_x_ = Clamped(curr_x_, min_x_, max_x_);
curr_y_ = start_y_ + distance_coef * delta_y_;
curr_y_ = Clamped(curr_y_, min_y_, max_y_);
float diff_x = std::abs(curr_x_ - final_x_);
float diff_y = std::abs(curr_y_ - final_y_);
if (diff_x < kThresholdForFlingEnd && diff_y < kThresholdForFlingEnd)
finished_ = true;
} break;
}
return true;
}
void Scroller::ExtendDuration(base::TimeDelta extend) { void Scroller::ExtendDuration(base::TimeDelta extend) {
base::TimeDelta passed = GetTimePassed(); base::TimeDelta passed = GetTimePassed();
duration_ = passed + extend; duration_ = passed + extend;
...@@ -365,19 +323,29 @@ void Scroller::AbortAnimation() { ...@@ -365,19 +323,29 @@ void Scroller::AbortAnimation() {
finished_ = true; finished_ = true;
} }
void Scroller::ForceFinished(bool finished) { finished_ = finished; } void Scroller::ForceFinished(bool finished) {
finished_ = finished;
}
bool Scroller::IsFinished() const { return finished_; } bool Scroller::IsFinished() const {
return finished_;
}
base::TimeDelta Scroller::GetTimePassed() const { base::TimeDelta Scroller::GetTimePassed() const {
return curr_time_ - start_time_; return curr_time_ - start_time_;
} }
base::TimeDelta Scroller::GetDuration() const { return duration_; } base::TimeDelta Scroller::GetDuration() const {
return duration_;
}
float Scroller::GetCurrX() const { return curr_x_; } float Scroller::GetCurrX() const {
return curr_x_;
}
float Scroller::GetCurrY() const { return curr_y_; } float Scroller::GetCurrY() const {
return curr_y_;
}
float Scroller::GetCurrVelocity() const { float Scroller::GetCurrVelocity() const {
if (finished_) if (finished_)
...@@ -395,19 +363,83 @@ float Scroller::GetCurrVelocityY() const { ...@@ -395,19 +363,83 @@ float Scroller::GetCurrVelocityY() const {
return delta_y_norm_ * GetCurrVelocity(); return delta_y_norm_ * GetCurrVelocity();
} }
float Scroller::GetStartX() const { return start_x_; } float Scroller::GetStartX() const {
return start_x_;
}
float Scroller::GetStartY() const { return start_y_; } float Scroller::GetStartY() const {
return start_y_;
}
float Scroller::GetFinalX() const { return final_x_; } float Scroller::GetFinalX() const {
return final_x_;
}
float Scroller::GetFinalY() const { return final_y_; } float Scroller::GetFinalY() const {
return final_y_;
}
bool Scroller::IsScrollingInDirection(float xvel, float yvel) const { bool Scroller::IsScrollingInDirection(float xvel, float yvel) const {
return !finished_ && Signum(xvel) == Signum(delta_x_) && return !finished_ && Signum(xvel) == Signum(delta_x_) &&
Signum(yvel) == Signum(delta_y_); Signum(yvel) == Signum(delta_y_);
} }
bool Scroller::ComputeScrollOffsetInternal(base::TimeTicks time) {
if (finished_)
return false;
if (time <= start_time_)
return true;
if (time == curr_time_)
return true;
base::TimeDelta time_passed = time - start_time_;
if (time_passed >= duration_) {
AbortAnimation();
return false;
}
curr_time_ = time;
const float u = time_passed.InSecondsF() * duration_seconds_reciprocal_;
switch (mode_) {
case UNDEFINED:
NOTREACHED() << "|StartScroll()| or |Fling()| must be called prior to "
"scroll offset computation.";
return false;
case SCROLL_MODE: {
float x = g_viscosity_constants.Get().ApplyViscosity(u);
curr_x_ = start_x_ + x * delta_x_;
curr_y_ = start_y_ + x * delta_y_;
} break;
case FLING_MODE: {
float distance_coef = 1.f;
float velocity_coef = 0.f;
g_spline_constants.Get().CalculateCoefficients(
u, &distance_coef, &velocity_coef);
curr_velocity_ = velocity_coef * distance_ * duration_seconds_reciprocal_;
curr_x_ = start_x_ + distance_coef * delta_x_;
curr_x_ = Clamped(curr_x_, min_x_, max_x_);
curr_y_ = start_y_ + distance_coef * delta_y_;
curr_y_ = Clamped(curr_y_, min_y_, max_y_);
float diff_x = std::abs(curr_x_ - final_x_);
float diff_y = std::abs(curr_y_ - final_y_);
if (diff_x < kThresholdForFlingEnd && diff_y < kThresholdForFlingEnd)
AbortAnimation();
} break;
}
return !finished_;
}
void Scroller::RecomputeDeltas() { void Scroller::RecomputeDeltas() {
delta_x_ = final_x_ - start_x_; delta_x_ = final_x_ - start_x_;
delta_y_ = final_y_ - start_y_; delta_y_ = final_y_ - start_y_;
...@@ -441,4 +473,4 @@ double Scroller::GetSplineFlingDistance(float velocity) const { ...@@ -441,4 +473,4 @@ double Scroller::GetSplineFlingDistance(float velocity) const {
std::exp(kDecelerationRate / decel_minus_one * l); std::exp(kDecelerationRate / decel_minus_one * l);
} }
} // namespace gfx } // namespace ui
...@@ -2,18 +2,20 @@ ...@@ -2,18 +2,20 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef UI_GFX_ANDROID_SCROLLER_H_ #ifndef UI_EVENTS_ANDROID_SCROLLER_H_
#define UI_GFX_ANDROID_SCROLLER_H_ #define UI_EVENTS_ANDROID_SCROLLER_H_
#include "base/time/time.h" #include "base/time/time.h"
#include "ui/gfx/gfx_export.h" #include "ui/events/events_base_export.h"
#include "ui/events/gesture_curve.h"
#include "ui/gfx/geometry/vector2d_f.h"
namespace gfx { namespace ui {
// Native port of android.widget.Scroller. // Native port of android.widget.Scroller.
// * Change-Id: I4365946f890a76fcfa78ca9d69f2a8e0848095a9 // * Change-Id: I4365946f890a76fcfa78ca9d69f2a8e0848095a9
// * Please update the Change-Id as upstream Android changes are pulled. // * Please update the Change-Id as upstream Android changes are pulled.
class GFX_EXPORT Scroller { class EVENTS_BASE_EXPORT Scroller : public GestureCurve {
public: public:
struct Config { struct Config {
Config(); Config();
...@@ -26,7 +28,12 @@ class GFX_EXPORT Scroller { ...@@ -26,7 +28,12 @@ class GFX_EXPORT Scroller {
}; };
explicit Scroller(const Config& config); explicit Scroller(const Config& config);
~Scroller(); virtual ~Scroller();
// GestureCurve implementation.
virtual bool ComputeScrollOffset(base::TimeTicks time,
gfx::Vector2dF* offset,
gfx::Vector2dF* velocity) override;
// Start scrolling by providing a starting point and the distance to travel. // Start scrolling by providing a starting point and the distance to travel.
// The default value of 250 milliseconds will be used for the duration. // The default value of 250 milliseconds will be used for the duration.
...@@ -57,10 +64,6 @@ class GFX_EXPORT Scroller { ...@@ -57,10 +64,6 @@ class GFX_EXPORT Scroller {
float max_y, float max_y,
base::TimeTicks start_time); base::TimeTicks start_time);
// Call this when you want to know the new location. If it returns true,
// the animation is not yet finished.
bool ComputeScrollOffset(base::TimeTicks time);
// Extend the scroll animation by |extend|. This allows a running animation // Extend the scroll animation by |extend|. This allows a running animation
// to scroll further and longer when used with |SetFinalX()| or |SetFinalY()|. // to scroll further and longer when used with |SetFinalX()| or |SetFinalY()|.
void ExtendDuration(base::TimeDelta extend); void ExtendDuration(base::TimeDelta extend);
...@@ -102,7 +105,7 @@ class GFX_EXPORT Scroller { ...@@ -102,7 +105,7 @@ class GFX_EXPORT Scroller {
FLING_MODE, FLING_MODE,
}; };
void OnDurationChanged(); bool ComputeScrollOffsetInternal(base::TimeTicks time);
void RecomputeDeltas(); void RecomputeDeltas();
double GetSplineDeceleration(float velocity) const; double GetSplineDeceleration(float velocity) const;
...@@ -143,6 +146,6 @@ class GFX_EXPORT Scroller { ...@@ -143,6 +146,6 @@ class GFX_EXPORT Scroller {
float tuning_coeff_; float tuning_coeff_;
}; };
} // namespace gfx } // namespace ui
#endif // UI_GFX_ANDROID_SCROLLER_H_ #endif // UI_EVENTS_ANDROID_SCROLLER_H_
...@@ -4,10 +4,9 @@ ...@@ -4,10 +4,9 @@
#include "base/time/time.h" #include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/android/scroller.h" #include "ui/events/android/scroller.h"
namespace gfx {
namespace ui {
namespace { namespace {
const float kDefaultStartX = 7.f; const float kDefaultStartX = 7.f;
...@@ -48,46 +47,51 @@ TEST_F(ScrollerTest, Scroll) { ...@@ -48,46 +47,51 @@ TEST_F(ScrollerTest, Scroll) {
// Advance halfway through the scroll. // Advance halfway through the scroll.
const base::TimeDelta scroll_duration = scroller.GetDuration(); const base::TimeDelta scroll_duration = scroller.GetDuration();
scroller.ComputeScrollOffset(start_time + scroll_duration / 2); gfx::Vector2dF offset, velocity;
EXPECT_TRUE(scroller.ComputeScrollOffset(
start_time + scroll_duration / 2, &offset, &velocity));
// Ensure we've moved in the direction of the delta, but have yet to reach // Ensure we've moved in the direction of the delta, but have yet to reach
// the target. // the target.
EXPECT_GT(kDefaultStartX, scroller.GetCurrX()); EXPECT_GT(kDefaultStartX, offset.x());
EXPECT_LT(kDefaultStartY, scroller.GetCurrY()); EXPECT_LT(kDefaultStartY, offset.y());
EXPECT_LT(scroller.GetFinalX(), scroller.GetCurrX()); EXPECT_LT(scroller.GetFinalX(), offset.x());
EXPECT_GT(scroller.GetFinalY(), scroller.GetCurrY()); EXPECT_GT(scroller.GetFinalY(), offset.y());
EXPECT_FALSE(scroller.IsFinished()); EXPECT_FALSE(scroller.IsFinished());
// Ensure our velocity is non-zero and in the same direction as the delta. // Ensure our velocity is non-zero and in the same direction as the delta.
EXPECT_GT(0.f, scroller.GetCurrVelocityX() * kDefaultDeltaX); EXPECT_GT(0.f, velocity.x() * kDefaultDeltaX);
EXPECT_GT(0.f, scroller.GetCurrVelocityY() * kDefaultDeltaY); EXPECT_GT(0.f, velocity.y() * kDefaultDeltaY);
EXPECT_TRUE(scroller.IsScrollingInDirection(kDefaultDeltaX, kDefaultDeltaY)); EXPECT_TRUE(scroller.IsScrollingInDirection(kDefaultDeltaX, kDefaultDeltaY));
// Repeated offset computations at the same timestamp should yield identical // Repeated offset computations at the same timestamp should yield identical
// results. // results.
float curr_x = scroller.GetCurrX(); float curr_x = offset.x();
float curr_y = scroller.GetCurrY(); float curr_y = offset.y();
float curr_velocity_x = scroller.GetCurrVelocityX(); float curr_velocity_x = velocity.x();
float curr_velocity_y = scroller.GetCurrVelocityY(); float curr_velocity_y = velocity.y();
scroller.ComputeScrollOffset(start_time + scroll_duration / 2); EXPECT_TRUE(scroller.ComputeScrollOffset(
EXPECT_EQ(curr_x, scroller.GetCurrX()); start_time + scroll_duration / 2, &offset, &velocity));
EXPECT_EQ(curr_y, scroller.GetCurrY()); EXPECT_EQ(curr_x, offset.x());
EXPECT_EQ(curr_velocity_x, scroller.GetCurrVelocityX()); EXPECT_EQ(curr_y, offset.y());
EXPECT_EQ(curr_velocity_y, scroller.GetCurrVelocityY()); EXPECT_EQ(curr_velocity_x, velocity.x());
EXPECT_EQ(curr_velocity_y, velocity.y());
// Advance to the end. // Advance to the end.
scroller.ComputeScrollOffset(start_time + scroll_duration); EXPECT_FALSE(scroller.ComputeScrollOffset(
EXPECT_EQ(scroller.GetFinalX(), scroller.GetCurrX()); start_time + scroll_duration, &offset, &velocity));
EXPECT_EQ(scroller.GetFinalY(), scroller.GetCurrY()); EXPECT_EQ(scroller.GetFinalX(), offset.x());
EXPECT_EQ(scroller.GetFinalY(), offset.y());
EXPECT_TRUE(scroller.IsFinished()); EXPECT_TRUE(scroller.IsFinished());
EXPECT_EQ(scroll_duration, scroller.GetTimePassed()); EXPECT_EQ(scroll_duration, scroller.GetTimePassed());
EXPECT_NEAR(0.f, scroller.GetCurrVelocityX(), kEpsilon); EXPECT_NEAR(0.f, velocity.x(), kEpsilon);
EXPECT_NEAR(0.f, scroller.GetCurrVelocityY(), kEpsilon); EXPECT_NEAR(0.f, velocity.y(), kEpsilon);
// Try to advance further; nothing should change. // Try to advance further; nothing should change.
scroller.ComputeScrollOffset(start_time + scroll_duration * 2); EXPECT_FALSE(scroller.ComputeScrollOffset(
EXPECT_EQ(scroller.GetFinalX(), scroller.GetCurrX()); start_time + scroll_duration * 2, &offset, &velocity));
EXPECT_EQ(scroller.GetFinalY(), scroller.GetCurrY()); EXPECT_EQ(scroller.GetFinalX(), offset.x());
EXPECT_EQ(scroller.GetFinalY(), offset.y());
EXPECT_TRUE(scroller.IsFinished()); EXPECT_TRUE(scroller.IsFinished());
EXPECT_EQ(scroll_duration, scroller.GetTimePassed()); EXPECT_EQ(scroll_duration, scroller.GetTimePassed());
} }
...@@ -118,50 +122,55 @@ TEST_F(ScrollerTest, Fling) { ...@@ -118,50 +122,55 @@ TEST_F(ScrollerTest, Fling) {
// Advance halfway through the fling. // Advance halfway through the fling.
const base::TimeDelta scroll_duration = scroller.GetDuration(); const base::TimeDelta scroll_duration = scroller.GetDuration();
scroller.ComputeScrollOffset(start_time + scroll_duration / 2); gfx::Vector2dF offset, velocity;
scroller.ComputeScrollOffset(
start_time + scroll_duration / 2, &offset, &velocity);
// Ensure we've moved in the direction of the velocity, but have yet to reach // Ensure we've moved in the direction of the velocity, but have yet to reach
// the target. // the target.
EXPECT_GT(kDefaultStartX, scroller.GetCurrX()); EXPECT_GT(kDefaultStartX, offset.x());
EXPECT_LT(kDefaultStartY, scroller.GetCurrY()); EXPECT_LT(kDefaultStartY, offset.y());
EXPECT_LT(scroller.GetFinalX(), scroller.GetCurrX()); EXPECT_LT(scroller.GetFinalX(), offset.x());
EXPECT_GT(scroller.GetFinalY(), scroller.GetCurrY()); EXPECT_GT(scroller.GetFinalY(), offset.y());
EXPECT_FALSE(scroller.IsFinished()); EXPECT_FALSE(scroller.IsFinished());
// Ensure our velocity is non-zero and in the same direction as the original // Ensure our velocity is non-zero and in the same direction as the original
// velocity. // velocity.
EXPECT_LT(0.f, scroller.GetCurrVelocityX() * kDefaultVelocityX); EXPECT_LT(0.f, velocity.x() * kDefaultVelocityX);
EXPECT_LT(0.f, scroller.GetCurrVelocityY() * kDefaultVelocityY); EXPECT_LT(0.f, velocity.y() * kDefaultVelocityY);
EXPECT_TRUE( EXPECT_TRUE(
scroller.IsScrollingInDirection(kDefaultVelocityX, kDefaultVelocityY)); scroller.IsScrollingInDirection(kDefaultVelocityX, kDefaultVelocityY));
// Repeated offset computations at the same timestamp should yield identical // Repeated offset computations at the same timestamp should yield identical
// results. // results.
float curr_x = scroller.GetCurrX(); float curr_x = offset.x();
float curr_y = scroller.GetCurrY(); float curr_y = offset.y();
float curr_velocity_x = scroller.GetCurrVelocityX(); float curr_velocity_x = velocity.x();
float curr_velocity_y = scroller.GetCurrVelocityY(); float curr_velocity_y = velocity.y();
scroller.ComputeScrollOffset(start_time + scroll_duration / 2); EXPECT_TRUE(scroller.ComputeScrollOffset(
EXPECT_EQ(curr_x, scroller.GetCurrX()); start_time + scroll_duration / 2, &offset, &velocity));
EXPECT_EQ(curr_y, scroller.GetCurrY()); EXPECT_EQ(curr_x, offset.x());
EXPECT_EQ(curr_velocity_x, scroller.GetCurrVelocityX()); EXPECT_EQ(curr_y, offset.y());
EXPECT_EQ(curr_velocity_y, scroller.GetCurrVelocityY()); EXPECT_EQ(curr_velocity_x, velocity.x());
EXPECT_EQ(curr_velocity_y, velocity.y());
// Advance to the end. // Advance to the end.
scroller.ComputeScrollOffset(start_time + scroll_duration); EXPECT_FALSE(scroller.ComputeScrollOffset(
EXPECT_EQ(scroller.GetFinalX(), scroller.GetCurrX()); start_time + scroll_duration, &offset, &velocity));
EXPECT_EQ(scroller.GetFinalY(), scroller.GetCurrY()); EXPECT_EQ(scroller.GetFinalX(), offset.x());
EXPECT_EQ(scroller.GetFinalY(), offset.y());
EXPECT_TRUE(scroller.IsFinished()); EXPECT_TRUE(scroller.IsFinished());
EXPECT_EQ(scroll_duration, scroller.GetTimePassed()); EXPECT_EQ(scroll_duration, scroller.GetTimePassed());
EXPECT_NEAR(0.f, scroller.GetCurrVelocityX(), kEpsilon); EXPECT_NEAR(0.f, velocity.x(), kEpsilon);
EXPECT_NEAR(0.f, scroller.GetCurrVelocityY(), kEpsilon); EXPECT_NEAR(0.f, velocity.y(), kEpsilon);
// Try to advance further; nothing should change. // Try to advance further; nothing should change.
scroller.ComputeScrollOffset(start_time + scroll_duration * 2); EXPECT_FALSE(scroller.ComputeScrollOffset(
EXPECT_EQ(scroller.GetFinalX(), scroller.GetCurrX()); start_time + scroll_duration * 2, &offset, &velocity));
EXPECT_EQ(scroller.GetFinalY(), scroller.GetCurrY()); EXPECT_EQ(scroller.GetFinalX(), offset.x());
EXPECT_EQ(scroller.GetFinalY(), offset.y());
EXPECT_TRUE(scroller.IsFinished()); EXPECT_TRUE(scroller.IsFinished());
EXPECT_EQ(scroll_duration, scroller.GetTimePassed()); EXPECT_EQ(scroll_duration, scroller.GetTimePassed());
} }
} // namespace gfx } // namespace ui
...@@ -38,6 +38,8 @@ ...@@ -38,6 +38,8 @@
], ],
'sources': [ 'sources': [
# Note: sources list duplicated in GN build. # Note: sources list duplicated in GN build.
'android/scroller.cc',
'android/scroller.h',
'device_data_manager.cc', 'device_data_manager.cc',
'device_data_manager.h', 'device_data_manager.h',
'device_hotplug_event_observer.h', 'device_hotplug_event_observer.h',
...@@ -45,6 +47,7 @@ ...@@ -45,6 +47,7 @@
'event_switches.cc', 'event_switches.cc',
'event_switches.h', 'event_switches.h',
'events_base_export.h', 'events_base_export.h',
'gesture_curve.h',
'gesture_event_details.cc', 'gesture_event_details.cc',
'gesture_event_details.h', 'gesture_event_details.h',
'gestures/fling_curve.cc', 'gestures/fling_curve.cc',
...@@ -307,6 +310,7 @@ ...@@ -307,6 +310,7 @@
], ],
'sources': [ 'sources': [
# Note: sources list duplicated in GN build. # Note: sources list duplicated in GN build.
'android/scroller_unittest.cc',
'cocoa/events_mac_unittest.mm', 'cocoa/events_mac_unittest.mm',
'event_dispatcher_unittest.cc', 'event_dispatcher_unittest.cc',
'event_processor_unittest.cc', 'event_processor_unittest.cc',
......
// 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.
#ifndef UI_EVENTS_GESTURE_CURVE_H_
#define UI_EVENTS_GESTURE_CURVE_H_
#include "base/time/time.h"
#include "ui/gfx/geometry/vector2d_f.h"
namespace ui {
// An abstraction of curve-based gesture motion, allowing platform-specific
// motion tailoring.
class EVENTS_BASE_EXPORT GestureCurve {
public:
virtual ~GestureCurve() {}
// The output |offset| represents the total movement of the curve from its
// start until |time|.
// The output |velocity| represents the instantenous velocity at |time|.
// Returns false if |time| exceeds the fling duration, in which case
// the terminal offset will be reported.
virtual bool ComputeScrollOffset(base::TimeTicks time,
gfx::Vector2dF* offset,
gfx::Vector2dF* velocity) = 0;
};
} // namespace ui
#endif // UI_EVENTS_GESTURE_CURVE_H_
...@@ -38,8 +38,10 @@ FlingCurve::FlingCurve(const gfx::Vector2dF& velocity, ...@@ -38,8 +38,10 @@ FlingCurve::FlingCurve(const gfx::Vector2dF& velocity,
base::TimeTicks start_timestamp) base::TimeTicks start_timestamp)
: curve_duration_(GetTimeAtVelocity(0)), : curve_duration_(GetTimeAtVelocity(0)),
start_timestamp_(start_timestamp), start_timestamp_(start_timestamp),
previous_timestamp_(start_timestamp_),
time_offset_(0), time_offset_(0),
position_offset_(0) { position_offset_(0) {
DCHECK(!velocity.IsZero());
float max_start_velocity = std::max(fabs(velocity.x()), fabs(velocity.y())); float max_start_velocity = std::max(fabs(velocity.x()), fabs(velocity.y()));
if (max_start_velocity > GetVelocityAtTime(0)) if (max_start_velocity > GetVelocityAtTime(0))
max_start_velocity = GetVelocityAtTime(0); max_start_velocity = GetVelocityAtTime(0);
...@@ -49,32 +51,57 @@ FlingCurve::FlingCurve(const gfx::Vector2dF& velocity, ...@@ -49,32 +51,57 @@ FlingCurve::FlingCurve(const gfx::Vector2dF& velocity,
velocity.y() / max_start_velocity); velocity.y() / max_start_velocity);
time_offset_ = GetTimeAtVelocity(max_start_velocity); time_offset_ = GetTimeAtVelocity(max_start_velocity);
position_offset_ = GetPositionAtTime(time_offset_); position_offset_ = GetPositionAtTime(time_offset_);
last_timestamp_ = start_timestamp_ + base::TimeDelta::FromSecondsD(
curve_duration_ - time_offset_);
} }
FlingCurve::~FlingCurve() { FlingCurve::~FlingCurve() {
} }
gfx::Vector2dF FlingCurve::GetScrollAmountAtTime(base::TimeTicks current) { bool FlingCurve::ComputeScrollOffset(base::TimeTicks time,
if (current < start_timestamp_) gfx::Vector2dF* offset,
return gfx::Vector2dF(); gfx::Vector2dF* velocity) {
DCHECK(offset);
DCHECK(velocity);
base::TimeDelta elapsed_time = time - start_timestamp_;
if (elapsed_time < base::TimeDelta()) {
*offset = gfx::Vector2dF();
*velocity = gfx::Vector2dF();
return true;
}
float displacement = 0; bool still_active = true;
if (current < last_timestamp_) { float scalar_offset;
float time = time_offset_ + (current - start_timestamp_).InSecondsF(); float scalar_velocity;
CHECK_LT(time, curve_duration_); double offset_time = elapsed_time.InSecondsF() + time_offset_;
displacement = GetPositionAtTime(time) - position_offset_; if (offset_time < curve_duration_) {
scalar_offset = GetPositionAtTime(offset_time) - position_offset_;
scalar_velocity = GetVelocityAtTime(offset_time);
} else { } else {
displacement = GetPositionAtTime(curve_duration_) - position_offset_; scalar_offset = GetPositionAtTime(curve_duration_) - position_offset_;
scalar_velocity = 0;
still_active = false;
} }
gfx::Vector2dF scroll(displacement * displacement_ratio_.x(), *offset = gfx::ScaleVector2d(displacement_ratio_, scalar_offset);
displacement * displacement_ratio_.y()); *velocity = gfx::ScaleVector2d(displacement_ratio_, scalar_velocity);
gfx::Vector2dF scroll_increment(scroll.x() - cumulative_scroll_.x(), return still_active;
scroll.y() - cumulative_scroll_.y()); }
cumulative_scroll_ = scroll;
return scroll_increment; bool FlingCurve::ComputeScrollDeltaAtTime(base::TimeTicks current,
gfx::Vector2dF* delta) {
DCHECK(delta);
if (current <= previous_timestamp_) {
*delta = gfx::Vector2dF();
return true;
}
previous_timestamp_ = current;
gfx::Vector2dF offset, velocity;
bool still_active = ComputeScrollOffset(current, &offset, &velocity);
*delta = offset - cumulative_scroll_;
cumulative_scroll_ = offset;
return still_active;
} }
} // namespace ui } // namespace ui
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "base/time/time.h" #include "base/time/time.h"
#include "ui/events/events_base_export.h" #include "ui/events/events_base_export.h"
#include "ui/events/gesture_curve.h"
#include "ui/gfx/geometry/point_f.h" #include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/vector2d_f.h" #include "ui/gfx/geometry/vector2d_f.h"
...@@ -14,13 +15,21 @@ namespace ui { ...@@ -14,13 +15,21 @@ namespace ui {
// FlingCurve can be used to scroll a UI element suitable for touch screen-based // FlingCurve can be used to scroll a UI element suitable for touch screen-based
// flings. // flings.
class EVENTS_BASE_EXPORT FlingCurve { class EVENTS_BASE_EXPORT FlingCurve : public GestureCurve {
public: public:
FlingCurve(const gfx::Vector2dF& velocity, base::TimeTicks start_timestamp); FlingCurve(const gfx::Vector2dF& velocity, base::TimeTicks start_timestamp);
~FlingCurve(); virtual ~FlingCurve();
gfx::Vector2dF GetScrollAmountAtTime(base::TimeTicks current_timestamp); // GestureCurve implementation.
base::TimeTicks start_timestamp() const { return start_timestamp_; } virtual bool ComputeScrollOffset(base::TimeTicks time,
gfx::Vector2dF* offset,
gfx::Vector2dF* velocity) override;
// In contrast to |ComputeScrollOffset()|, this method is stateful and
// returns the *change* in scroll offset between successive calls.
// Returns true as long as the curve is still active and requires additional
// animation ticks.
bool ComputeScrollDeltaAtTime(base::TimeTicks current, gfx::Vector2dF* delta);
private: private:
const float curve_duration_; const float curve_duration_;
...@@ -28,7 +37,7 @@ class EVENTS_BASE_EXPORT FlingCurve { ...@@ -28,7 +37,7 @@ class EVENTS_BASE_EXPORT FlingCurve {
gfx::Vector2dF displacement_ratio_; gfx::Vector2dF displacement_ratio_;
gfx::Vector2dF cumulative_scroll_; gfx::Vector2dF cumulative_scroll_;
base::TimeTicks last_timestamp_; base::TimeTicks previous_timestamp_;
float time_offset_; float time_offset_;
float position_offset_; float position_offset_;
......
...@@ -14,23 +14,25 @@ TEST(FlingCurveTest, Basic) { ...@@ -14,23 +14,25 @@ TEST(FlingCurveTest, Basic) {
base::TimeTicks now = gfx::FrameTime::Now(); base::TimeTicks now = gfx::FrameTime::Now();
FlingCurve curve(velocity, now); FlingCurve curve(velocity, now);
gfx::Vector2dF scroll = gfx::Vector2dF delta;
curve.GetScrollAmountAtTime(now + base::TimeDelta::FromMilliseconds(20)); EXPECT_TRUE(curve.ComputeScrollDeltaAtTime(
EXPECT_EQ(0, scroll.x()); now + base::TimeDelta::FromMilliseconds(20), &delta));
EXPECT_NEAR(scroll.y(), 96, 1); EXPECT_EQ(0, delta.x());
EXPECT_NEAR(delta.y(), 96, 1);
scroll =
curve.GetScrollAmountAtTime(now + base::TimeDelta::FromMilliseconds(250)); EXPECT_TRUE(curve.ComputeScrollDeltaAtTime(
EXPECT_EQ(0, scroll.x()); now + base::TimeDelta::FromMilliseconds(250), &delta));
EXPECT_NEAR(scroll.y(), 705, 1); EXPECT_EQ(0, delta.x());
EXPECT_NEAR(delta.y(), 705, 1);
scroll =
curve.GetScrollAmountAtTime(now + base::TimeDelta::FromSeconds(10)); EXPECT_FALSE(curve.ComputeScrollDeltaAtTime(
EXPECT_EQ(0, scroll.x()); now + base::TimeDelta::FromSeconds(10), &delta));
EXPECT_NEAR(scroll.y(), 392, 1); EXPECT_EQ(0, delta.x());
EXPECT_NEAR(delta.y(), 392, 1);
EXPECT_TRUE(curve.GetScrollAmountAtTime(
now + base::TimeDelta::FromSeconds(20)).IsZero()); EXPECT_FALSE(curve.ComputeScrollDeltaAtTime(
now + base::TimeDelta::FromSeconds(20), &delta));
EXPECT_TRUE(delta.IsZero());
} }
} // namespace ui } // namespace ui
...@@ -27,8 +27,6 @@ component("gfx") { ...@@ -27,8 +27,6 @@ component("gfx") {
"android/gfx_jni_registrar.h", "android/gfx_jni_registrar.h",
"android/java_bitmap.cc", "android/java_bitmap.cc",
"android/java_bitmap.h", "android/java_bitmap.h",
"android/scroller.cc",
"android/scroller.h",
"android/shared_device_display_info.cc", "android/shared_device_display_info.cc",
"android/shared_device_display_info.h", "android/shared_device_display_info.h",
"android/view_configuration.cc", "android/view_configuration.cc",
......
...@@ -109,8 +109,6 @@ ...@@ -109,8 +109,6 @@
'android/gfx_jni_registrar.h', 'android/gfx_jni_registrar.h',
'android/java_bitmap.cc', 'android/java_bitmap.cc',
'android/java_bitmap.h', 'android/java_bitmap.h',
'android/scroller.cc',
'android/scroller.h',
'android/shared_device_display_info.cc', 'android/shared_device_display_info.cc',
'android/shared_device_display_info.h', 'android/shared_device_display_info.h',
'android/view_configuration.cc', 'android/view_configuration.cc',
......
...@@ -25,7 +25,6 @@ ...@@ -25,7 +25,6 @@
], ],
'all_sources': [ 'all_sources': [
'<@(_common_sources)', '<@(_common_sources)',
'android/scroller_unittest.cc',
'animation/animation_container_unittest.cc', 'animation/animation_container_unittest.cc',
'animation/animation_unittest.cc', 'animation/animation_unittest.cc',
'animation/multi_animation_unittest.cc', 'animation/multi_animation_unittest.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment