From: Paul Wisbey
Date: Tue, 3 Jun 2014 10:42:21 +0000 (+0900)
Subject: ScrollView: Avoid animating in wrong direction during fast flick
X-Git-Tag: dali_1.0.0~71
X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=commitdiff_plain;h=45e0dfb55809cde34b3913b0b16f18e79c14775c;hp=f4d5b3ffc194fc1b1c14f4a79e3efa435a31bc32
ScrollView: Avoid animating in wrong direction during fast flick
[problem] When flicking pages quickly, it sometimes jumps backwards
[cause] Incorrect (infinite) velocity calculation in ScrollView::OnTouchEvent()
[solution] Don't interrupt snap-animation immediately during TouchPoint::Down i.e. allow
time for pan gesture to occur.
---
diff --git a/base/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.cpp b/base/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.cpp
index 6cd1a37..dca4aab 100644
--- a/base/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.cpp
+++ b/base/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.cpp
@@ -44,7 +44,8 @@ const float AUTOLOCK_AXIS_MINIMUM_DISTANCE2 = 100.0f;
const float FLICK_ORTHO_ANGLE_RANGE = 60.0f; ///< degrees. (if >45, then supports diagonal flicking)
const unsigned int MAXIMUM_NUMBER_OF_VALUES = 5; ///< Number of values to use for weighted pan calculation.
const Vector2 DEFAULT_MOUSE_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION = Vector2(0.17f, 0.1f); ///< The step of horizontal scroll distance in the proportion of stage size for each mouse wheel event received.
-const unsigned long MINIMUM_TIME_BETWEEN_DOWN_AND_UP_FOR_RESET( 150u );
+const unsigned long MINIMUM_TIME_BETWEEN_DOWN_AND_UP_FOR_RESET( 150u ); ///< Determines whether velocity is reset after tap interrupts snap animation
+const float TOUCH_DOWN_TIMER_INTERVAL = 100.0f; ///< After this time interval with touch-down, the snap animation will be interrupted (if no gesture has started).
// predefined effect values
const Vector3 ANGLE_CAROUSEL_ROTATE(Math::PI * 0.5f, Math::PI * 0.5f, 0.0f);
@@ -518,7 +519,7 @@ ScrollView::ScrollView()
mRotationDelta(0.0f),
mScrollPreRotation(0.0f),
mScrollPostRotation(0.0f),
- mTouchDownReceived(false),
+ mTouchDownTimeoutReached(false),
mActorAutoSnapEnabled(false),
mAutoResizeContainerEnabled(false),
mWrapMode(false),
@@ -1878,6 +1879,49 @@ void ScrollView::OnChildRemove(Actor& child)
UnbindActor(child);
}
+void ScrollView::StartTouchDownTimer()
+{
+ if ( !mTouchDownTimer )
+ {
+ mTouchDownTimer = Timer::New( TOUCH_DOWN_TIMER_INTERVAL );
+ mTouchDownTimer.TickSignal().Connect( this, &ScrollView::OnTouchDownTimeout );
+ }
+
+ mTouchDownTimer.Start();
+}
+
+void ScrollView::StopTouchDownTimer()
+{
+ if ( mTouchDownTimer )
+ {
+ mTouchDownTimer.Stop();
+ }
+}
+
+bool ScrollView::OnTouchDownTimeout()
+{
+ mTouchDownTimeoutReached = true;
+
+ if( mSnapAnimation || mSnapXAnimation || mSnapYAnimation || mSnapOvershootAnimation )
+ {
+ mScrollInterrupted = true;
+ StopAnimation();
+ }
+
+ if(mScrolling) // are we interrupting a current scroll?
+ {
+ // reset domain offset as scrolling from original plane.
+ mDomainOffset = Vector3::ZERO;
+ Self().SetProperty(mPropertyDomainOffset, Vector3::ZERO);
+
+ mScrolling = false;
+ Vector3 currentScrollPosition = GetCurrentScrollPosition();
+ mScrollCompletedSignalV2.Emit( currentScrollPosition );
+ }
+
+ return false;
+}
+
bool ScrollView::OnTouchEvent(const TouchEvent& event)
{
if(!mSensitive)
@@ -1894,33 +1938,23 @@ bool ScrollView::OnTouchEvent(const TouchEvent& event)
if (event.GetPoint(0).state == TouchPoint::Down)
{
- mTouchDownTime = event.time;
- mTouchDownReceived = true;
- mTouchDownPosition = event.GetPoint(0).local;
-
- if( mSnapAnimation || mSnapXAnimation || mSnapYAnimation || mSnapOvershootAnimation )
- {
- mScrollInterrupted = true;
- StopAnimation();
- }
-
- if(mScrolling) // are we interrupting a current scroll?
+ if(mGestureStackDepth==0)
{
- // reset domain offset as scrolling from original plane.
- mDomainOffset = Vector3::ZERO;
- Self().SetProperty(mPropertyDomainOffset, Vector3::ZERO);
+ mTouchDownTime = event.time;
- mScrolling = false;
- Vector3 currentScrollPosition = GetCurrentScrollPosition();
- mScrollCompletedSignalV2.Emit( currentScrollPosition );
+ // This allows time for a pan-gesture to start, to avoid breaking snap-animation behavior with fast flicks.
+ // If touch-down does not become a pan (after timeout interval), then snap-animation can be interrupted.
+ StartTouchDownTimer();
}
}
else if(event.GetPoint(0).state == TouchPoint::Up)
{
+ StopTouchDownTimer();
+
// if the user touches and releases without enough movement to go
// into a gesture state, then we should snap to nearest point.
// otherwise our scroll could be stopped (interrupted) half way through an animation.
- if(mGestureStackDepth==0 && mTouchDownReceived)
+ if(mGestureStackDepth==0 && mTouchDownTimeoutReached)
{
unsigned timeDelta( event.time - mTouchDownTime );
if ( timeDelta >= MINIMUM_TIME_BETWEEN_DOWN_AND_UP_FOR_RESET )
@@ -1928,11 +1962,6 @@ bool ScrollView::OnTouchEvent(const TouchEvent& event)
// Reset the velocity only if down was received a while ago
mLastVelocity = Vector2( 0.0f, 0.0f );
}
- else
- {
- Vector2 positionDelta( mTouchDownPosition - event.GetPoint(0).local );
- mLastVelocity = positionDelta / timeDelta;
- }
// Only finish the transform if scrolling was interrupted on down or if we are scrolling
if ( mSnapAnimation || mSnapXAnimation || mSnapYAnimation || mSnapOvershootAnimation || mScrollInterrupted || mScrolling )
@@ -1940,7 +1969,7 @@ bool ScrollView::OnTouchEvent(const TouchEvent& event)
FinishTransform();
}
}
- mTouchDownReceived = false;
+ mTouchDownTimeoutReached = false;
mScrollInterrupted = false;
}
@@ -2058,6 +2087,7 @@ void ScrollView::GestureStarted()
// we continue and combine the effects of the gesture instead of reseting.
if(mGestureStackDepth++==0)
{
+ StopTouchDownTimer();
StopAnimation();
mPanDelta = Vector3::ZERO;
mScaleDelta = Vector3::ONE;
diff --git a/base/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.h b/base/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.h
index 4c3dc8e..8f2be58 100644
--- a/base/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.h
+++ b/base/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.h
@@ -556,6 +556,21 @@ private: // private overriden functions from CustomActorImpl and Controls
private:
/**
+ * Start a timer which calls OnTouchDownTimeout()
+ */
+ void StartTouchDownTimer();
+
+ /**
+ * Stop a timer which calls OnTouchDownTimeout()
+ */
+ void StopTouchDownTimer();
+
+ /**
+ * Helper to detect when touch-point has been down (outside of pan gesture)
+ */
+ bool OnTouchDownTimeout();
+
+ /**
* Called whenever a snap animation has completed
* @param[in] source the Animation instance that has completed.
*/
@@ -806,7 +821,6 @@ private:
bool mScrolling; ///< Flag indicating whether the scroll view is being scrolled (by user or animation)
bool mScrollInterrupted; ///< Flag set for when a down event interrupts a scroll
unsigned long mTouchDownTime; ///< The touch down time
- Vector2 mTouchDownPosition; ///< The touch down position
bool mSensitive; ///< Scroll Sensitivity Flag.
@@ -834,7 +848,7 @@ private:
RulerPtr mRulerScaleX;
RulerPtr mRulerScaleY;
RulerPtr mRulerRotation;
- bool mTouchDownReceived;
+ bool mTouchDownTimeoutReached;
bool mActorAutoSnapEnabled; ///< Whether to automatically snap to closest actor.
bool mAutoResizeContainerEnabled; ///< Whether to automatically resize container (affects RulerDomain's on X/Y axes)
bool mWrapMode; ///< Whether to wrap contents based on container size.
@@ -851,6 +865,7 @@ private:
Vector2 mLastVelocity; ///< Record the last velocity from PanGesture (Finish event doesn't have correct velocity)
LockAxis mLockAxis;
+ Timer mTouchDownTimer; ///< Used to interrupt snap-animation. This cannot be done in OnTouchEvent without breaking fast flick behavior.
Timer mOvershootRefreshTimer;
Timer mRefreshTimer; ///< Refresh timer is used to provide the Application developer with updates as animations run.
int mRefreshIntervalMilliseconds; ///< Refresh timer interval.