diff --git a/athena/athena.gyp b/athena/athena.gyp index 1cbeaad7f3bac..659844332d0ce 100644 --- a/athena/athena.gyp +++ b/athena/athena.gyp @@ -15,6 +15,7 @@ '../ui/accessibility/accessibility.gyp:ax_gen', '../ui/app_list/app_list.gyp:app_list', '../ui/aura/aura.gyp:aura', + '../ui/events/events.gyp:events_base', '../ui/strings/ui_strings.gyp:ui_strings', '../ui/views/views.gyp:views', ], diff --git a/athena/wm/window_overview_mode.cc b/athena/wm/window_overview_mode.cc index b1dabcf954e47..a294cdec6e66d 100644 --- a/athena/wm/window_overview_mode.cc +++ b/athena/wm/window_overview_mode.cc @@ -14,8 +14,13 @@ #include "ui/aura/window_delegate.h" #include "ui/aura/window_property.h" #include "ui/aura/window_targeter.h" +#include "ui/aura/window_tree_host.h" +#include "ui/compositor/compositor.h" +#include "ui/compositor/compositor_animation_observer.h" #include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/events/event_handler.h" +#include "ui/events/gestures/fling_curve.h" +#include "ui/gfx/frame_time.h" #include "ui/gfx/transform.h" #include "ui/wm/core/shadow.h" @@ -93,7 +98,8 @@ class StaticWindowTargeter : public aura::WindowTargeter { }; class WindowOverviewModeImpl : public WindowOverviewMode, - public ui::EventHandler { + public ui::EventHandler, + public ui::CompositorAnimationObserver { public: WindowOverviewModeImpl(aura::Window* container, WindowOverviewModeDelegate* delegate) @@ -113,7 +119,7 @@ class WindowOverviewModeImpl : public WindowOverviewMode, virtual ~WindowOverviewModeImpl() { container_->set_target_handler(container_->delegate()); - + RemoveAnimationObserver(); const aura::Window::Windows& windows = container_->children(); for (aura::Window::Windows::const_iterator iter = windows.begin(); iter != windows.end(); @@ -287,6 +293,24 @@ class WindowOverviewModeImpl : public WindowOverviewMode, return container_->bounds().height() * kScrollableFraction; } + void CreateFlingerFor(const ui::GestureEvent& event) { + gfx::Vector2dF velocity(event.details().velocity_x(), + event.details().velocity_y()); + fling_.reset(new ui::FlingCurve(velocity, gfx::FrameTime::Now())); + } + + void AddAnimationObserver() { + ui::Compositor* compositor = container_->GetHost()->compositor(); + if (!compositor->HasAnimationObserver(this)) + compositor->AddAnimationObserver(this); + } + + void RemoveAnimationObserver() { + ui::Compositor* compositor = container_->GetHost()->compositor(); + if (compositor->HasAnimationObserver(this)) + compositor->RemoveAnimationObserver(this); + } + // ui::EventHandler: virtual void OnMouseEvent(ui::MouseEvent* mouse) OVERRIDE { if (mouse->type() == ui::ET_MOUSE_PRESSED) { @@ -314,12 +338,36 @@ class WindowOverviewModeImpl : public WindowOverviewMode, } } else if (gesture->type() == ui::ET_GESTURE_SCROLL_UPDATE) { DoScroll(gesture->details().scroll_y()); + gesture->SetHandled(); + } else if (gesture->type() == ui::ET_SCROLL_FLING_START) { + CreateFlingerFor(*gesture); + AddAnimationObserver(); + gesture->SetHandled(); + } else if (gesture->type() == ui::ET_GESTURE_TAP_DOWN && fling_) { + fling_.reset(); + RemoveAnimationObserver(); + gesture->SetHandled(); + } + } + + // ui::CompositorAnimationObserver: + virtual void OnAnimationStep(base::TimeTicks timestamp) OVERRIDE { + CHECK(fling_); + if (fling_->start_timestamp() > timestamp) + return; + gfx::Vector2dF scroll = fling_->GetScrollAmountAtTime(timestamp); + if (scroll.IsZero()) { + fling_.reset(); + RemoveAnimationObserver(); + } else { + DoScroll(scroll.y()); } } aura::Window* container_; WindowOverviewModeDelegate* delegate_; scoped_ptr<aura::ScopedWindowTargeter> scoped_targeter_; + scoped_ptr<ui::FlingCurve> fling_; DISALLOW_COPY_AND_ASSIGN(WindowOverviewModeImpl); }; diff --git a/ui/events/events.gyp b/ui/events/events.gyp index ec7288a5021f9..b65b33bec9fb6 100644 --- a/ui/events/events.gyp +++ b/ui/events/events.gyp @@ -46,6 +46,8 @@ 'events_base_export.h', 'gesture_event_details.cc', 'gesture_event_details.h', + 'gestures/fling_curve.cc', + 'gestures/fling_curve.h', 'gestures/gesture_configuration.cc', 'gestures/gesture_configuration.h', 'keycodes/keyboard_code_conversion.cc', @@ -316,6 +318,7 @@ 'event_processor_unittest.cc', 'event_rewriter_unittest.cc', 'event_unittest.cc', + 'gestures/fling_curve_unittest.cc', 'gestures/motion_event_aura_unittest.cc', 'gestures/velocity_calculator_unittest.cc', 'gesture_detection/bitset_32_unittest.cc', diff --git a/ui/events/gestures/fling_curve.cc b/ui/events/gestures/fling_curve.cc new file mode 100644 index 0000000000000..6f63019510ac7 --- /dev/null +++ b/ui/events/gestures/fling_curve.cc @@ -0,0 +1,80 @@ +// 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 "ui/events/gestures/fling_curve.h" + +#include <algorithm> +#include <cmath> + +#include "base/logging.h" + +namespace { + +const float kDefaultAlpha = -5.70762e+03f; +const float kDefaultBeta = 1.72e+02f; +const float kDefaultGamma = 3.7e+00f; + +inline double GetPositionAtTime(double t) { + return kDefaultAlpha * exp(-kDefaultGamma * t) - kDefaultBeta * t - + kDefaultAlpha; +} + +inline double GetVelocityAtTime(double t) { + return -kDefaultAlpha * kDefaultGamma * exp(-kDefaultGamma * t) - + kDefaultBeta; +} + +inline double GetTimeAtVelocity(double v) { + return -log((v + kDefaultBeta) / (-kDefaultAlpha * kDefaultGamma)) / + kDefaultGamma; +} + +} // namespace + +namespace ui { + +FlingCurve::FlingCurve(const gfx::Vector2dF& velocity, + base::TimeTicks start_timestamp) + : curve_duration_(GetTimeAtVelocity(0)), + start_timestamp_(start_timestamp), + time_offset_(0), + position_offset_(0) { + float max_start_velocity = std::max(fabs(velocity.x()), fabs(velocity.y())); + if (max_start_velocity > GetVelocityAtTime(0)) + max_start_velocity = GetVelocityAtTime(0); + CHECK_GT(max_start_velocity, 0); + + displacement_ratio_ = gfx::Vector2dF(velocity.x() / max_start_velocity, + velocity.y() / max_start_velocity); + time_offset_ = GetTimeAtVelocity(max_start_velocity); + position_offset_ = GetPositionAtTime(time_offset_); + last_timestamp_ = start_timestamp_ + base::TimeDelta::FromSecondsD( + curve_duration_ - time_offset_); +} + +FlingCurve::~FlingCurve() { +} + +gfx::Vector2dF FlingCurve::GetScrollAmountAtTime(base::TimeTicks current) { + if (current < start_timestamp_) + return gfx::Vector2dF(); + + float displacement = 0; + if (current < last_timestamp_) { + float time = time_offset_ + (current - start_timestamp_).InSecondsF(); + CHECK_LT(time, curve_duration_); + displacement = GetPositionAtTime(time) - position_offset_; + } else { + displacement = GetPositionAtTime(curve_duration_) - position_offset_; + } + + gfx::Vector2dF scroll(displacement * displacement_ratio_.x(), + displacement * displacement_ratio_.y()); + gfx::Vector2dF scroll_increment(scroll.x() - cumulative_scroll_.x(), + scroll.y() - cumulative_scroll_.y()); + cumulative_scroll_ = scroll; + return scroll_increment; +} + +} // namespace ui diff --git a/ui/events/gestures/fling_curve.h b/ui/events/gestures/fling_curve.h new file mode 100644 index 0000000000000..583e172c999bb --- /dev/null +++ b/ui/events/gestures/fling_curve.h @@ -0,0 +1,40 @@ +// 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_GESTURES_FLING_CURVE_H_ +#define UI_EVENTS_GESTURES_FLING_CURVE_H_ + +#include "base/time/time.h" +#include "ui/events/events_base_export.h" +#include "ui/gfx/geometry/point_f.h" +#include "ui/gfx/geometry/vector2d_f.h" + +namespace ui { + +// FlingCurve can be used to scroll a UI element suitable for touch screen-based +// flings. +class EVENTS_BASE_EXPORT FlingCurve { + public: + FlingCurve(const gfx::Vector2dF& velocity, base::TimeTicks start_timestamp); + ~FlingCurve(); + + gfx::Vector2dF GetScrollAmountAtTime(base::TimeTicks current_timestamp); + base::TimeTicks start_timestamp() const { return start_timestamp_; } + + private: + const float curve_duration_; + const base::TimeTicks start_timestamp_; + + gfx::Vector2dF displacement_ratio_; + gfx::Vector2dF cumulative_scroll_; + base::TimeTicks last_timestamp_; + float time_offset_; + float position_offset_; + + DISALLOW_COPY_AND_ASSIGN(FlingCurve); +}; + +} // namespace ui + +#endif // UI_EVENTS_GESTURES_FLING_CURVE_H_ diff --git a/ui/events/gestures/fling_curve_unittest.cc b/ui/events/gestures/fling_curve_unittest.cc new file mode 100644 index 0000000000000..653a5effff8c9 --- /dev/null +++ b/ui/events/gestures/fling_curve_unittest.cc @@ -0,0 +1,36 @@ +// 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 "ui/events/gestures/fling_curve.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/frame_time.h" + +namespace ui { + +TEST(FlingCurveTest, Basic) { + const gfx::Vector2dF velocity(0, 5000); + base::TimeTicks now = gfx::FrameTime::Now(); + FlingCurve curve(velocity, now); + + gfx::Vector2dF scroll = + curve.GetScrollAmountAtTime(now + base::TimeDelta::FromMilliseconds(20)); + EXPECT_EQ(0, scroll.x()); + EXPECT_NEAR(scroll.y(), 96, 1); + + scroll = + curve.GetScrollAmountAtTime(now + base::TimeDelta::FromMilliseconds(250)); + EXPECT_EQ(0, scroll.x()); + EXPECT_NEAR(scroll.y(), 705, 1); + + scroll = + curve.GetScrollAmountAtTime(now + base::TimeDelta::FromSeconds(10)); + EXPECT_EQ(0, scroll.x()); + EXPECT_NEAR(scroll.y(), 392, 1); + + EXPECT_TRUE(curve.GetScrollAmountAtTime( + now + base::TimeDelta::FromSeconds(20)).IsZero()); +} + +} // namespace ui