Commit 6c944694 authored by Daniel Bratell's avatar Daniel Bratell Committed by Commit Bot

Use MobileScroller instead of cloning it.

Chromecast wants to scroll exactly like Android and for that
the code cloned the mobile scrolling code, but simpler and
easier and with less code and fewer jumbo compilation errors
is to just use the existing code directly.

Only a minor code difference existed and that is controlled with
a conditional statement instead now.

Bug: 813911
Change-Id: I9d037da9b58c1ad0916dc22ee194328c113075cd
Reviewed-on: https://chromium-review.googlesource.com/1136541Reviewed-by: default avatarLuke Halliwell <halliwell@chromium.org>
Reviewed-by: default avatarSadrul Chowdhury <sadrul@chromium.org>
Reviewed-by: default avatarKevin Schoedel <kpschoedel@chromium.org>
Commit-Queue: Daniel Bratell <bratell@opera.com>
Cr-Commit-Position: refs/heads/master@{#576035}
parent ec63e94d
......@@ -151,13 +151,6 @@ component("events_base") {
"Carbon.framework",
]
}
if (is_chromecast && !is_android) {
sources += [
"chromecast/scroller.cc",
"chromecast/scroller.h",
]
}
}
component("events") {
......
// Copyright 2018 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 "ui/events/chromecast/scroller.h"
#include <cmath>
#include "base/lazy_instance.h"
#include "base/macros.h"
namespace ui {
namespace {
// Default scroll duration from android.widget.Scroller.
const int kDefaultDurationMs = 250;
// Default friction constant in android.view.ViewConfiguration.
const float kDefaultFriction = 0.015f;
// == std::log(0.78f) / std::log(0.9f)
const float kDecelerationRate = 2.3582018f;
// Tension lines cross at (kInflexion, 1).
const float kInflexion = 0.35f;
const float kEpsilon = 1e-5f;
// Fling scroll is stopped when the scroll position is |kThresholdForFlingEnd|
// pixels or closer from the end.
const float kThresholdForFlingEnd = 0.1f;
// Scale factor applied to incoming fling velocity.
const float kFlingVelocityAttenuationFactor = 1.0f;
bool ApproxEquals(float a, float b) {
return std::abs(a - b) < kEpsilon;
}
struct ViscosityConstants {
ViscosityConstants()
: viscous_fluid_scale_(8.f), viscous_fluid_normalize_(1.f) {
viscous_fluid_normalize_ = 1.0f / ApplyViscosity(1.0f);
}
float ApplyViscosity(float x) {
x *= viscous_fluid_scale_;
if (x < 1.0f) {
x -= (1.0f - std::exp(-x));
} else {
float start = 0.36787944117f; // 1/e == exp(-1)
x = 1.0f - std::exp(1.0f - x);
x = start + x * (1.0f - start);
}
x *= viscous_fluid_normalize_;
return x;
}
private:
// This controls the intensity of the viscous fluid effect.
float viscous_fluid_scale_;
float viscous_fluid_normalize_;
DISALLOW_COPY_AND_ASSIGN(ViscosityConstants);
};
struct SplineConstants {
SplineConstants() {
const float kStartTension = 0.5f;
const float kEndTension = 1.0f;
const float kP1 = kStartTension * kInflexion;
const float kP2 = 1.0f - kEndTension * (1.0f - kInflexion);
float x_min = 0.0f;
float y_min = 0.0f;
for (int i = 0; i < NUM_SAMPLES; i++) {
const float alpha = static_cast<float>(i) / NUM_SAMPLES;
float x_max = 1.0f;
float x, tx, coef;
while (true) {
x = x_min + (x_max - x_min) / 2.0f;
coef = 3.0f * x * (1.0f - x);
tx = coef * ((1.0f - x) * kP1 + x * kP2) + x * x * x;
if (ApproxEquals(tx, alpha))
break;
if (tx > alpha)
x_max = x;
else
x_min = x;
}
spline_position_[i] = coef * ((1.0f - x) * kStartTension + x) + x * x * x;
float y_max = 1.0f;
float y, dy;
while (true) {
y = y_min + (y_max - y_min) / 2.0f;
coef = 3.0f * y * (1.0f - y);
dy = coef * ((1.0f - y) * kStartTension + y) + y * y * y;
if (ApproxEquals(dy, alpha))
break;
if (dy > alpha)
y_max = y;
else
y_min = y;
}
spline_time_[i] = coef * ((1.0f - y) * kP1 + y * kP2) + y * y * y;
}
spline_position_[NUM_SAMPLES] = spline_time_[NUM_SAMPLES] = 1.0f;
}
void CalculateCoefficients(float t,
float* distance_coef,
float* velocity_coef) {
*distance_coef = 1.f;
*velocity_coef = 0.f;
const int index = static_cast<int>(NUM_SAMPLES * t);
if (index < NUM_SAMPLES) {
const float t_inf = static_cast<float>(index) / NUM_SAMPLES;
const float t_sup = static_cast<float>(index + 1) / NUM_SAMPLES;
const float d_inf = spline_position_[index];
const float d_sup = spline_position_[index + 1];
*velocity_coef = (d_sup - d_inf) / (t_sup - t_inf);
*distance_coef = d_inf + (t - t_inf) * *velocity_coef;
}
}
private:
enum { NUM_SAMPLES = 100 };
float spline_position_[NUM_SAMPLES + 1];
float spline_time_[NUM_SAMPLES + 1];
DISALLOW_COPY_AND_ASSIGN(SplineConstants);
};
float ComputeDeceleration(float friction) {
const float kGravityEarth = 9.80665f;
return kGravityEarth // g (m/s^2)
* 39.37f // inch/meter
* 160.f // pixels/inch
* friction;
}
template <typename T>
int Signum(T t) {
return (T(0) < t) - (t < T(0));
}
template <typename T>
T Clamped(T t, T a, T b) {
return t < a ? a : (t > b ? b : t);
}
// Leaky to allow access from the impl thread.
base::LazyInstance<ViscosityConstants>::Leaky g_viscosity_constants =
LAZY_INSTANCE_INITIALIZER;
base::LazyInstance<SplineConstants>::Leaky g_spline_constants =
LAZY_INSTANCE_INITIALIZER;
} // namespace
Scroller::Config::Config()
: fling_friction(kDefaultFriction), flywheel_enabled(false) {
}
Scroller::Scroller(const Config& config)
: mode_(UNDEFINED),
start_x_(0),
start_y_(0),
final_x_(0),
final_y_(0),
min_x_(0),
max_x_(0),
min_y_(0),
max_y_(0),
curr_x_(0),
curr_y_(0),
duration_seconds_reciprocal_(1),
delta_x_(0),
delta_x_norm_(1),
delta_y_(0),
delta_y_norm_(1),
finished_(true),
flywheel_enabled_(config.flywheel_enabled),
velocity_(0),
curr_velocity_(0),
distance_(0),
fling_friction_(config.fling_friction),
deceleration_(ComputeDeceleration(fling_friction_)),
tuning_coeff_(ComputeDeceleration(0.9f)) {
}
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,
float start_y,
float dx,
float dy,
base::TimeTicks start_time) {
StartScroll(start_x,
start_y,
dx,
dy,
start_time,
base::TimeDelta::FromMilliseconds(kDefaultDurationMs));
}
void Scroller::StartScroll(float start_x,
float start_y,
float dx,
float dy,
base::TimeTicks start_time,
base::TimeDelta duration) {
DCHECK_GT(duration, base::TimeDelta());
mode_ = SCROLL_MODE;
finished_ = false;
duration_ = duration;
duration_seconds_reciprocal_ = 1.0 / duration_.InSecondsF();
start_time_ = start_time;
curr_x_ = start_x_ = start_x;
curr_y_ = start_y_ = start_y;
final_x_ = start_x + dx;
final_y_ = start_y + dy;
RecomputeDeltas();
curr_time_ = start_time_;
}
void Scroller::Fling(float start_x,
float start_y,
float velocity_x,
float velocity_y,
float min_x,
float max_x,
float min_y,
float max_y,
base::TimeTicks start_time) {
DCHECK(velocity_x || velocity_y);
// Continue a scroll or fling in progress.
if (flywheel_enabled_ && !finished_) {
float old_velocity_x = GetCurrVelocityX();
float old_velocity_y = GetCurrVelocityY();
if (Signum(velocity_x) == Signum(old_velocity_x) &&
Signum(velocity_y) == Signum(old_velocity_y)) {
velocity_x += old_velocity_x;
velocity_y += old_velocity_y;
}
}
mode_ = FLING_MODE;
finished_ = false;
float velocity = std::sqrt(velocity_x * velocity_x + velocity_y * velocity_y);
velocity *= kFlingVelocityAttenuationFactor;
velocity_ = velocity;
duration_ = GetSplineFlingDuration(velocity);
DCHECK_GT(duration_, base::TimeDelta());
duration_seconds_reciprocal_ = 1.0 / duration_.InSecondsF();
start_time_ = start_time;
curr_time_ = start_time_;
curr_x_ = start_x_ = start_x;
curr_y_ = start_y_ = start_y;
float coeff_x = velocity == 0 ? 1.0f : velocity_x / velocity;
float coeff_y = velocity == 0 ? 1.0f : velocity_y / velocity;
double total_distance = GetSplineFlingDistance(velocity);
distance_ = total_distance * Signum(velocity);
min_x_ = min_x;
max_x_ = max_x;
min_y_ = min_y;
max_y_ = max_y;
final_x_ = start_x + total_distance * coeff_x;
final_x_ = Clamped(final_x_, min_x_, max_x_);
final_y_ = start_y + total_distance * coeff_y;
final_y_ = Clamped(final_y_, min_y_, max_y_);
RecomputeDeltas();
}
void Scroller::ExtendDuration(base::TimeDelta extend) {
base::TimeDelta passed = GetTimePassed();
duration_ = passed + extend;
duration_seconds_reciprocal_ = 1.0 / duration_.InSecondsF();
finished_ = false;
}
void Scroller::SetFinalX(float new_x) {
final_x_ = new_x;
finished_ = false;
RecomputeDeltas();
}
void Scroller::SetFinalY(float new_y) {
final_y_ = new_y;
finished_ = false;
RecomputeDeltas();
}
void Scroller::AbortAnimation() {
curr_x_ = final_x_;
curr_y_ = final_y_;
curr_velocity_ = 0;
curr_time_ = start_time_ + duration_;
finished_ = true;
}
void Scroller::ForceFinished(bool finished) {
finished_ = finished;
}
bool Scroller::IsFinished() const {
return finished_;
}
base::TimeDelta Scroller::GetTimePassed() const {
return curr_time_ - start_time_;
}
base::TimeDelta Scroller::GetDuration() const {
return duration_;
}
float Scroller::GetCurrX() const {
return curr_x_;
}
float Scroller::GetCurrY() const {
return curr_y_;
}
float Scroller::GetCurrVelocity() const {
if (finished_)
return 0;
if (mode_ == FLING_MODE)
return curr_velocity_;
return velocity_ - deceleration_ * GetTimePassed().InSecondsF() * 0.5f;
}
float Scroller::GetCurrVelocityX() const {
return delta_x_norm_ * GetCurrVelocity();
}
float Scroller::GetCurrVelocityY() const {
return delta_y_norm_ * GetCurrVelocity();
}
float Scroller::GetStartX() const {
return start_x_;
}
float Scroller::GetStartY() const {
return start_y_;
}
float Scroller::GetFinalX() const {
return final_x_;
}
float Scroller::GetFinalY() const {
return final_y_;
}
bool Scroller::IsScrollingInDirection(float xvel, float yvel) const {
return !finished_ && Signum(xvel) == Signum(delta_x_) &&
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() {
delta_x_ = final_x_ - start_x_;
delta_y_ = final_y_ - start_y_;
const float hyp = std::sqrt(delta_x_ * delta_x_ + delta_y_ * delta_y_);
if (hyp > kEpsilon) {
delta_x_norm_ = delta_x_ / hyp;
delta_y_norm_ = delta_y_ / hyp;
} else {
delta_x_norm_ = delta_y_norm_ = 1;
}
}
double Scroller::GetSplineDeceleration(float velocity) const {
return std::log(kInflexion * std::abs(velocity) /
(fling_friction_ * tuning_coeff_));
}
base::TimeDelta Scroller::GetSplineFlingDuration(float velocity) const {
const double l = GetSplineDeceleration(velocity);
const double decel_minus_one = kDecelerationRate - 1.0;
const double time_seconds = std::exp(l / decel_minus_one);
return base::TimeDelta::FromMicroseconds(time_seconds *
base::Time::kMicrosecondsPerSecond);
}
double Scroller::GetSplineFlingDistance(float velocity) const {
const double l = GetSplineDeceleration(velocity);
const double decel_minus_one = kDecelerationRate - 1.0;
return fling_friction_ * tuning_coeff_ *
std::exp(kDecelerationRate / decel_minus_one * l);
}
} // namespace ui
// Copyright 2018 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_CHROMECAST_SCROLLER_H_
#define UI_EVENTS_CHROMECAST_SCROLLER_H_
#include "base/time/time.h"
#include "ui/events/events_base_export.h"
#include "ui/events/gesture_curve.h"
#include "ui/gfx/geometry/vector2d_f.h"
namespace ui {
// Native port of android.widget.Scroller.
// * Change-Id: I4365946f890a76fcfa78ca9d69f2a8e0848095a9
// * Please update the Change-Id as upstream Android changes are pulled.
class EVENTS_BASE_EXPORT Scroller : public GestureCurve {
public:
struct Config {
Config();
// Controls fling deceleration. Defaults to 0.015f.
float fling_friction;
// Controls fling accumulation. Defaults to disabled.
bool flywheel_enabled;
};
explicit Scroller(const Config& config);
~Scroller() override;
// GestureCurve implementation.
bool ComputeScrollOffset(base::TimeTicks time,
gfx::Vector2dF* offset,
gfx::Vector2dF* velocity) override;
// Start scrolling by providing a starting point and the distance to travel.
// The default value of 250 milliseconds will be used for the duration.
void StartScroll(float start_x,
float start_y,
float dx,
float dy,
base::TimeTicks start_time);
// Start scrolling by providing a starting point, the distance to travel,
// and the duration of the scroll.
void StartScroll(float start_x,
float start_y,
float dx,
float dy,
base::TimeTicks start_time,
base::TimeDelta duration);
// Start scrolling based on a fling gesture. The distance travelled will
// depend on the initial velocity of the fling.
void Fling(float start_x,
float start_y,
float velocity_x,
float velocity_y,
float min_x,
float max_x,
float min_y,
float max_y,
base::TimeTicks start_time);
// Extend the scroll animation by |extend|. This allows a running animation
// to scroll further and longer when used with |SetFinalX()| or |SetFinalY()|.
void ExtendDuration(base::TimeDelta extend);
void SetFinalX(float new_x);
void SetFinalY(float new_y);
// Stops the animation. Contrary to |ForceFinished()|, aborting the animation
// causes the scroller to move to the final x and y position.
void AbortAnimation();
// Terminate the scroll without affecting the current x and y positions.
void ForceFinished(bool finished);
// Returns whether the scroller has finished scrolling.
bool IsFinished() const;
// Returns the time elapsed since the beginning of the scrolling.
base::TimeDelta GetTimePassed() const;
// Returns how long the scroll event will take.
base::TimeDelta GetDuration() const;
float GetStartX() const;
float GetStartY() const;
float GetCurrX() const;
float GetCurrY() const;
float GetCurrVelocity() const;
float GetCurrVelocityX() const;
float GetCurrVelocityY() const;
float GetFinalX() const;
float GetFinalY() const;
bool IsScrollingInDirection(float xvel, float yvel) const;
private:
enum Mode {
UNDEFINED,
SCROLL_MODE,
FLING_MODE,
};
bool ComputeScrollOffsetInternal(base::TimeTicks time);
void RecomputeDeltas();
double GetSplineDeceleration(float velocity) const;
base::TimeDelta GetSplineFlingDuration(float velocity) const;
double GetSplineFlingDistance(float velocity) const;
Mode mode_;
float start_x_;
float start_y_;
float final_x_;
float final_y_;
float min_x_;
float max_x_;
float min_y_;
float max_y_;
float curr_x_;
float curr_y_;
base::TimeTicks start_time_;
base::TimeTicks curr_time_;
base::TimeDelta duration_;
double duration_seconds_reciprocal_;
float delta_x_;
float delta_x_norm_;
float delta_y_;
float delta_y_norm_;
bool finished_;
bool flywheel_enabled_;
float velocity_;
float curr_velocity_;
float distance_;
float fling_friction_;
float deceleration_;
float tuning_coeff_;
};
} // namespace ui
#endif // UI_EVENTS_CHROMECAST_SCROLLER_H_
......@@ -6,7 +6,7 @@
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/chromecast/scroller.h"
#include "ui/events/mobile_scroller.h"
namespace ui {
namespace {
......@@ -19,8 +19,8 @@ const float kDefaultVelocityX = -350.f;
const float kDefaultVelocityY = 220.f;
const float kEpsilon = 1e-3f;
Scroller::Config DefaultConfig() {
return Scroller::Config();
MobileScroller::Config DefaultConfig() {
return MobileScroller::Config();
}
} // namespace
......@@ -28,7 +28,7 @@ Scroller::Config DefaultConfig() {
class ScrollerTest : public testing::Test {};
TEST_F(ScrollerTest, Scroll) {
Scroller scroller(DefaultConfig());
MobileScroller scroller(DefaultConfig());
base::TimeTicks start_time = base::TimeTicks::Now();
// Start a scroll and verify initialized values.
......@@ -99,7 +99,7 @@ TEST_F(ScrollerTest, Scroll) {
}
TEST_F(ScrollerTest, Fling) {
Scroller scroller(DefaultConfig());
MobileScroller scroller(DefaultConfig());
base::TimeTicks start_time = base::TimeTicks::Now();
// Start a fling and verify initialized values.
......
......@@ -2,12 +2,18 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//build/config/chromecast_build.gni")
source_set("blink") {
sources = [
"web_gesture_curve_impl.cc",
"web_gesture_curve_impl.h",
]
if (is_chromecast && !is_android) {
defines = [ "USE_MOBILE_FLING_CURVE" ]
}
deps = [
"//base",
"//third_party/blink/public:blink_headers",
......
......@@ -19,10 +19,6 @@
#include "ui/gfx/geometry/vector2d.h"
#include "ui/gfx/geometry/vector2d_f.h"
#if !defined(OS_ANDROID) && defined(CHROMECAST_BUILD)
#include "ui/events/chromecast/scroller.h"
#endif
using blink::WebGestureCurve;
namespace ui {
......@@ -37,21 +33,22 @@ std::unique_ptr<GestureCurve> CreateDefaultPlatformCurve(
base::TimeTicks());
}
#if !defined(OS_ANDROID) && defined(CHROMECAST_BUILD)
auto scroller = std::make_unique<Scroller>(Scroller::Config());
scroller->Fling(0, 0, initial_velocity.x(), initial_velocity.y(), INT_MIN,
INT_MAX, INT_MIN, INT_MAX, base::TimeTicks());
return std::move(scroller);
#else
#ifdef USE_MOBILE_FLING_CURVE
use_mobile_fling_curve = true;
#endif
if (use_mobile_fling_curve) {
auto scroller = std::make_unique<MobileScroller>(MobileScroller::Config());
MobileScroller::Config config;
#ifdef USE_MOBILE_FLING_CURVE
config.chromecast_optimized = true;
#endif
auto scroller = std::make_unique<MobileScroller>(config);
scroller->Fling(0, 0, initial_velocity.x(), initial_velocity.y(), INT_MIN,
INT_MAX, INT_MIN, INT_MAX, base::TimeTicks());
return std::move(scroller);
}
return std::make_unique<FlingCurve>(initial_velocity, base::TimeTicks());
#endif
}
} // namespace
......
......@@ -159,7 +159,9 @@ base::LazyInstance<SplineConstants>::Leaky g_spline_constants =
} // namespace
MobileScroller::Config::Config()
: fling_friction(kDefaultFriction), flywheel_enabled(false) {}
: fling_friction(kDefaultFriction),
flywheel_enabled(false),
chromecast_optimized(false) {}
MobileScroller::MobileScroller(const Config& config)
: mode_(UNDEFINED),
......@@ -185,7 +187,8 @@ MobileScroller::MobileScroller(const Config& config)
distance_(0),
fling_friction_(config.fling_friction),
deceleration_(ComputeDeceleration(fling_friction_)),
tuning_coeff_(ComputeDeceleration(0.84f)) {}
tuning_coeff_(
ComputeDeceleration(config.chromecast_optimized ? 0.9f : 0.84f)) {}
MobileScroller::~MobileScroller() {}
......
......@@ -25,6 +25,10 @@ class EVENTS_BASE_EXPORT MobileScroller : public GestureCurve {
// Controls fling accumulation. Defaults to disabled.
bool flywheel_enabled;
// Controls whether to use chromecast optimized
// scrolling. Defaults to false, mimic normal Android scrolling.
bool chromecast_optimized;
};
explicit MobileScroller(const Config& config);
......
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