From 647305c7fc201b837f0643bf7994c46c4546801f Mon Sep 17 00:00:00 2001 From: Tom Robinson Date: Tue, 13 Oct 2015 10:50:49 +0100 Subject: [PATCH 1/1] DGEUF-110: Limit ScrollView to scroll maximum 1 page on flick Change-Id: I92b4c665c7c771b5ddb0667ebdf024bd640ae81b --- .../src/dali-toolkit/utc-Dali-ScrollView.cpp | 60 ++++++++++++++++ .../scrollable/scroll-view/scroll-view-impl.cpp | 80 +++++++++++++++------- .../scrollable/scroll-view/scroll-view-impl.h | 3 +- 3 files changed, 115 insertions(+), 28 deletions(-) diff --git a/automated-tests/src/dali-toolkit/utc-Dali-ScrollView.cpp b/automated-tests/src/dali-toolkit/utc-Dali-ScrollView.cpp index dd631c6..3f9879e 100644 --- a/automated-tests/src/dali-toolkit/utc-Dali-ScrollView.cpp +++ b/automated-tests/src/dali-toolkit/utc-Dali-ScrollView.cpp @@ -2287,3 +2287,63 @@ int UtcDaliToolkitScrollViewConstraintsWrap(void) END_TEST; } + +// Non-API test (so no P or N variant). +int UtcDaliToolkitScrollViewGesturePageLimit(void) +{ + ToolkitTestApplication application; + tet_infoline( " UtcDaliToolkitScrollViewGesturePageLimit" ); + + // Set up a scrollView. + ScrollView scrollView = ScrollView::New(); + + // Do not rely on stage size for UTC tests. + Vector2 pageSize( 720.0f, 1280.0f ); + scrollView.SetResizePolicy( ResizePolicy::FIXED, Dimension::ALL_DIMENSIONS ); + scrollView.SetSize( pageSize ); + scrollView.SetParentOrigin( ParentOrigin::CENTER ); + scrollView.SetAnchorPoint( AnchorPoint::CENTER ); + scrollView.SetPosition( 0.0f, 0.0f, 0.0f ); + + // Position rulers. + // We set the X ruler to fixed to give us pages to snap to. + Dali::Toolkit::FixedRuler* rulerX = new Dali::Toolkit::FixedRuler( pageSize.width ); + // Note: The 3x page width is arbitary, but we need enough to show that we are + // capping page movement by the page limiter, and not the domain. + rulerX->SetDomain( Dali::Toolkit::RulerDomain( 0.0f, pageSize.width * 3.0f, false ) ); + Dali::Toolkit::RulerPtr rulerY = new Dali::Toolkit::DefaultRuler(); + rulerY->Disable(); + scrollView.SetRulerX( rulerX ); + scrollView.SetRulerY( rulerY ); + + scrollView.SetWrapMode( false ); + scrollView.SetScrollSensitive( true ); + + Stage::GetCurrent().Add( scrollView ); + + // Set up a gesture to perform. + Vector2 startPos( 50.0f, 0.0f ); + Vector2 direction( -5.0f, 0.0f ); + int frames = 200; + + // Force starting position. + scrollView.ScrollTo( startPos, 0.0f ); + Wait( application ); + + // Deliberately skip the "Finished" part of the gesture, so we can read the coordinates before the snap begins. + Vector2 currentPos( PerformGestureDiagonalSwipe( application, startPos, direction, frames - 1, false ) ); + + // Confirm the final X coord has not moved more than one page from the start X position. + DALI_TEST_GREATER( ( startPos.x + pageSize.width ), scrollView.GetCurrentScrollPosition().x, TEST_LOCATION ); + + // Finish the gesture and wait for the snap. + currentPos += direction; + SendPan( application, Gesture::Finished, currentPos ); + // We add RENDER_FRAME_INTERVAL on to wait for an extra frame (for the last "finished" gesture to complete first. + Wait( application, RENDER_DELAY_SCROLL + RENDER_FRAME_INTERVAL ); + + // Confirm the final X coord has snapped to exactly one page ahead of the start page. + DALI_TEST_EQUALS( pageSize.width, scrollView.GetCurrentScrollPosition().x, Math::MACHINE_EPSILON_0, TEST_LOCATION ); + + END_TEST; +} diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.cpp b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.cpp index 4681474..81804c2 100644 --- a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.cpp +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.cpp @@ -313,25 +313,29 @@ ScrollView::LockAxis GetLockAxis(const Vector2& panDelta, ScrollView::LockAxis c */ struct InternalPrePositionConstraint { - InternalPrePositionConstraint(const Vector2& initialPanPosition, - const Vector2& initialPanMask, - bool axisAutoLock, - float axisAutoLockGradient, - ScrollView::LockAxis initialLockAxis, - const Vector2& maxOvershoot, - const RulerDomain& domainX, const RulerDomain& domainY) - : mLocalStart(initialPanPosition), - mInitialPanMask(initialPanMask), - mDomainMin( -domainX.min, -domainY.min ), - mDomainMax( -domainX.max, -domainY.max ), - mMaxOvershoot(maxOvershoot), - mAxisAutoLockGradient(axisAutoLockGradient), - mLockAxis(initialLockAxis), - mAxisAutoLock(axisAutoLock), - mWasPanning(false), - mClampX( domainX.enabled ), - mClampY( domainY.enabled ) - { + InternalPrePositionConstraint( const Vector2& initialPanPosition, + const Vector2& initialPanMask, + bool axisAutoLock, + float axisAutoLockGradient, + ScrollView::LockAxis initialLockAxis, + const Vector2& maxOvershoot, + const RulerPtr& rulerX, const RulerPtr& rulerY ) + : mLocalStart( initialPanPosition ), + mInitialPanMask( initialPanMask ), + mMaxOvershoot( maxOvershoot ), + mAxisAutoLockGradient( axisAutoLockGradient ), + mLockAxis( initialLockAxis ), + mAxisAutoLock( axisAutoLock ), + mWasPanning( false ) + { + const RulerDomain& rulerDomainX = rulerX->GetDomain(); + const RulerDomain& rulerDomainY = rulerY->GetDomain(); + mDomainMin = Vector2( rulerDomainX.min, -rulerDomainY.min ); + mDomainMax = Vector2( -rulerDomainX.max, -rulerDomainY.max ); + mClampX = rulerDomainX.enabled; + mClampY = rulerDomainY.enabled; + mFixedRulerX = rulerX->GetType() == Ruler::Fixed; + mFixedRulerY = rulerY->GetType() == Ruler::Fixed; } void operator()( Vector2& scrollPostPosition, const PropertyInputContainer& inputs ) @@ -341,6 +345,7 @@ struct InternalPrePositionConstraint if(!mWasPanning) { mPrePosition = scrollPostPosition; + mStartPosition = mPrePosition; mCurrentPanMask = mInitialPanMask; mWasPanning = true; } @@ -395,11 +400,35 @@ struct InternalPrePositionConstraint } scrollPostPosition.y = newYPosition; } + + // If we are using a fixed ruler in a particular axis, limit the maximum pages scrolled on that axis. + if( mFixedRulerX || mFixedRulerY ) + { + // Here we limit the maximum amount that can be moved from the starting position of the gesture to one page. + // We do this only if we have a fixed ruler (on that axis) and the mode is enabled. + // Note: 1.0f is subtracted to keep the value within one page size (otherwise we stray on to the page after). + // Note: A further 1.0f is subtracted to handle a compensation that happens later within the flick handling code in SnapWithVelocity(). + // When a flick is completed, an adjustment of 1.0f is sometimes made to allow for the scenario where: + // A flick finishes before the update thread has advanced the scroll position past the previous snap point. + Vector2 pageSizeLimit( size.x - ( 1.0f + 1.0f ), size.y - ( 1.0f - 1.0f ) ); + Vector2 minPosition( mStartPosition.x - pageSizeLimit.x, mStartPosition.y - pageSizeLimit.y ); + Vector2 maxPosition( mStartPosition.x + pageSizeLimit.x, mStartPosition.y + pageSizeLimit.y ); + + if( mFixedRulerX ) + { + scrollPostPosition.x = Clamp( scrollPostPosition.x, minPosition.x, maxPosition.x ); + } + if( mFixedRulerY ) + { + scrollPostPosition.y = Clamp( scrollPostPosition.y, minPosition.y, maxPosition.y ); + } + } } Vector2 mPrePosition; Vector2 mLocalStart; - Vector2 mInitialPanMask; ///< Initial pan mask (based on ruler settings) + Vector2 mStartPosition; ///< The start position of the gesture - used to limit scroll amount (not modified by clamping). + Vector2 mInitialPanMask; ///< Initial pan mask (based on ruler settings). Vector2 mCurrentPanMask; ///< Current pan mask that can be altered by axis lock mode. Vector2 mDomainMin; Vector2 mDomainMax; @@ -408,10 +437,12 @@ struct InternalPrePositionConstraint float mAxisAutoLockGradient; ///< Set by ScrollView ScrollView::LockAxis mLockAxis; - bool mAxisAutoLock:1; ///< Set by ScrollView + bool mAxisAutoLock:1; ///< Set by ScrollView bool mWasPanning:1; bool mClampX:1; bool mClampY:1; + bool mFixedRulerX:1; + bool mFixedRulerY:1; }; /** @@ -600,7 +631,6 @@ ScrollView::ScrollView() mMaxFlickSpeed(DEFAULT_MAX_FLICK_SPEED), mWheelScrollDistanceStep(Vector2::ZERO), mInAccessibilityPan(false), - mInitialized(false), mScrolling(false), mScrollInterrupted(false), mPanning(false), @@ -636,8 +666,6 @@ void ScrollView::OnInitialize() mWheelScrollDistanceStep = Stage::GetCurrent().GetSize() * DEFAULT_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION; - mInitialized = true; - mGestureStackDepth = 0; EnableGestureDetection( Gesture::Type( Gesture::Pan ) ); @@ -2661,8 +2689,8 @@ void ScrollView::UpdateMainInternalConstraint() mAxisAutoLockGradient, mLockAxis, mMaxOvershoot, - mRulerX->GetDomain(), - mRulerY->GetDomain() ) ); + mRulerX, + mRulerY ) ); mScrollMainInternalPrePositionConstraint.AddSource( Source( detector, PanGestureDetector::Property::LOCAL_POSITION ) ); mScrollMainInternalPrePositionConstraint.AddSource( Source( self, Actor::Property::SIZE ) ); mScrollMainInternalPrePositionConstraint.Apply(); diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.h b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.h index bbd87bc..c2b62bf 100644 --- a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.h +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.h @@ -900,8 +900,7 @@ private: Toolkit::ScrollView::SnapStartedSignalType mSnapStartedSignal; - bool mInAccessibilityPan : 1; ///< With AccessibilityPan its easier to move between snap positions - bool mInitialized:1; + bool mInAccessibilityPan:1; ///< With AccessibilityPan its easier to move between snap positions bool mScrolling:1; ///< Flag indicating whether the scroll view is being scrolled (by user or animation) bool mScrollInterrupted:1; ///< Flag set for when a down event interrupts a scroll bool mPanning:1; ///< Whether scroll view is currently panning or not -- 2.7.4