DGEUF-110: Limit ScrollView to scroll maximum 1 page on flick 48/49448/5
authorTom Robinson <tom.robinson@samsung.com>
Tue, 13 Oct 2015 09:50:49 +0000 (10:50 +0100)
committerTom Robinson <tom.robinson@samsung.com>
Tue, 13 Oct 2015 16:16:31 +0000 (17:16 +0100)
Change-Id: I92b4c665c7c771b5ddb0667ebdf024bd640ae81b

automated-tests/src/dali-toolkit/utc-Dali-ScrollView.cpp
dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.cpp
dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.h

index dd631c6..3f9879e 100644 (file)
@@ -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;
+}
index 4681474..81804c2 100644 (file)
@@ -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();
index bbd87bc..c2b62bf 100644 (file)
@@ -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