DGEUF-110: Limit ScrollView to scroll maximum 1 page on flick
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / scrollable / scroll-view / scroll-view-impl.cpp
index 7ff84ab..81804c2 100644 (file)
@@ -22,7 +22,7 @@
 #include <cstring> // for strcmp
 #include <dali/public-api/animation/constraints.h>
 #include <dali/public-api/common/stage.h>
-#include <dali/public-api/events/mouse-wheel-event.h>
+#include <dali/public-api/events/wheel-event.h>
 #include <dali/public-api/events/touch-event.h>
 #include <dali/public-api/object/type-registry.h>
 #include <dali/devel-api/object/type-registry-helper.h>
@@ -69,7 +69,7 @@ const float DEFAULT_MIN_FLICK_SPEED_THRESHOLD(500.0f);              ///< Minimum
 const float FREE_FLICK_SPEED_THRESHOLD = 200.0f;                    ///< Free-Flick threshold in pixels/ms
 const float AUTOLOCK_AXIS_MINIMUM_DISTANCE2 = 100.0f;               ///< Auto-lock axis after minimum distance squared.
 const float FLICK_ORTHO_ANGLE_RANGE = 75.0f;                        ///< degrees. (if >45, then supports diagonal flicking)
-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 Vector2 DEFAULT_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION = Vector2(0.17f, 0.1f); ///< The step of horizontal scroll distance in the proportion of stage size for each wheel event received.
 const unsigned long MINIMUM_TIME_BETWEEN_DOWN_AND_UP_FOR_RESET( 150u );
 const float TOUCH_DOWN_TIMER_INTERVAL = 100.0f;
 const float DEFAULT_SCROLL_UPDATE_DISTANCE( 30.0f );                ///< Default distance to travel in pixels for scroll update signal
@@ -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;
 };
 
 /**
@@ -578,7 +609,7 @@ Dali::Toolkit::ScrollView ScrollView::New()
 }
 
 ScrollView::ScrollView()
-: ScrollBase( ControlBehaviour( REQUIRES_MOUSE_WHEEL_EVENTS ) ),   // Enable size negotiation
+: ScrollBase( ControlBehaviour( REQUIRES_WHEEL_EVENTS ) ),   // Enable size negotiation
   mTouchDownTime(0u),
   mGestureStackDepth(0),
   mScrollStateFlags(0),
@@ -598,9 +629,8 @@ ScrollView::ScrollView()
   mFrictionCoefficient(DEFAULT_FRICTION_COEFFICIENT),
   mFlickSpeedCoefficient(DEFAULT_FLICK_SPEED_COEFFICIENT),
   mMaxFlickSpeed(DEFAULT_MAX_FLICK_SPEED),
-  mMouseWheelScrollDistanceStep(Vector2::ZERO),
+  mWheelScrollDistanceStep(Vector2::ZERO),
   mInAccessibilityPan(false),
-  mInitialized(false),
   mScrolling(false),
   mScrollInterrupted(false),
   mPanning(false),
@@ -624,7 +654,6 @@ void ScrollView::OnInitialize()
   // Internal Actor, used to hide actors from enumerations.
   // Also actors added to Internal actor appear as overlays e.g. ScrollBar components.
   mInternalActor = Actor::New();
-  mInternalActor.SetDrawMode(DrawMode::OVERLAY);
   self.Add(mInternalActor);
 
   mInternalActor.SetParentOrigin(ParentOrigin::CENTER);
@@ -635,9 +664,7 @@ void ScrollView::OnInitialize()
 
   mScrollPostPosition = mScrollPrePosition = Vector2::ZERO;
 
-  mMouseWheelScrollDistanceStep = Stage::GetCurrent().GetSize() * DEFAULT_MOUSE_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION;
-
-  mInitialized = true;
+  mWheelScrollDistanceStep = Stage::GetCurrent().GetSize() * DEFAULT_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION;
 
   mGestureStackDepth = 0;
 
@@ -649,8 +676,6 @@ void ScrollView::OnInitialize()
   mRulerX = ruler;
   mRulerY = ruler;
 
-  SetOvershootEnabled(true);
-
   self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_VERTICAL, mCanScrollVertical);
   self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_HORIZONTAL, mCanScrollHorizontal);
 
@@ -658,7 +683,7 @@ void ScrollView::OnInitialize()
   SetInternalConstraints();
 }
 
-void ScrollView::OnControlStageConnection()
+void ScrollView::OnStageConnection( int depth )
 {
   DALI_LOG_SCROLL_STATE("[0x%X]", this);
 
@@ -674,7 +699,7 @@ void ScrollView::OnControlStageConnection()
   }
 }
 
-void ScrollView::OnControlStageDisconnection()
+void ScrollView::OnStageDisconnection()
 {
   DALI_LOG_SCROLL_STATE("[0x%X]", this);
 
@@ -1083,14 +1108,14 @@ void ScrollView::SetMaxFlickSpeed(float speed)
   mMaxFlickSpeed = speed;
 }
 
-void ScrollView::SetMouseWheelScrollDistanceStep(Vector2 step)
+void ScrollView::SetWheelScrollDistanceStep(Vector2 step)
 {
-  mMouseWheelScrollDistanceStep = step;
+  mWheelScrollDistanceStep = step;
 }
 
-Vector2 ScrollView::GetMouseWheelScrollDistanceStep() const
+Vector2 ScrollView::GetWheelScrollDistanceStep() const
 {
-  return mMouseWheelScrollDistanceStep;
+  return mWheelScrollDistanceStep;
 }
 
 unsigned int ScrollView::GetCurrentPage() const
@@ -1116,11 +1141,6 @@ Vector2 ScrollView::GetCurrentScrollPosition() const
   return -GetPropertyPosition();
 }
 
-void ScrollView::SetScrollPosition(const Vector2& position)
-{
-  mScrollPrePosition = position;
-}
-
 Vector2 ScrollView::GetDomainSize() const
 {
   Vector3 size = Self().GetCurrentSize();
@@ -1704,24 +1724,31 @@ bool ScrollView::AnimateTo(const Vector2& position, const Vector2& positionDurat
 
 void ScrollView::EnableScrollOvershoot(bool enable)
 {
-  if(enable && !mOvershootIndicator)
-  {
-    mOvershootIndicator = ScrollOvershootIndicator::New();
-  }
-  if( enable )
+  if (enable)
   {
+    if (!mOvershootIndicator)
+    {
+      mOvershootIndicator = ScrollOvershootIndicator::New();
+    }
+
     mOvershootIndicator->AttachToScrollable(*this);
   }
   else
   {
     mMaxOvershoot = mUserMaxOvershoot;
-    mOvershootIndicator->DetachFromScrollable(*this);
+
+    if (mOvershootIndicator)
+    {
+      mOvershootIndicator->DetachFromScrollable(*this);
+    }
   }
+
   UpdateMainInternalConstraint();
 }
 
 void ScrollView::AddOverlay(Actor actor)
 {
+  actor.SetDrawMode( DrawMode::OVERLAY_2D );
   mInternalActor.Add( actor );
 }
 
@@ -1873,7 +1900,7 @@ void ScrollView::OnSizeAnimation(Animation& animation, const Vector3& targetSize
   UpdatePropertyDomain();
 }
 
-void ScrollView::OnControlSizeSet( const Vector3& size )
+void ScrollView::OnSizeSet( const Vector3& size )
 {
   // need to update domain properties for new size
   if( mDefaultMaxOvershoot )
@@ -2047,11 +2074,11 @@ bool ScrollView::OnTouchEvent(const TouchEvent& event)
   return true;
 }
 
-bool ScrollView::OnMouseWheelEvent(const MouseWheelEvent& event)
+bool ScrollView::OnWheelEvent(const WheelEvent& event)
 {
   if(!mSensitive)
   {
-    // Ignore this mouse wheel event, if scrollview is insensitive.
+    // Ignore this wheel event, if scrollview is insensitive.
     return false;
   }
 
@@ -2063,7 +2090,7 @@ bool ScrollView::OnMouseWheelEvent(const MouseWheelEvent& event)
     if(mRulerX->GetType() == Ruler::Free)
     {
       // Free panning mode
-      targetScrollPosition.x += event.z * mMouseWheelScrollDistanceStep.x;
+      targetScrollPosition.x += event.z * mWheelScrollDistanceStep.x;
       ClampPosition(targetScrollPosition);
       ScrollTo(-targetScrollPosition);
     }
@@ -2079,7 +2106,7 @@ bool ScrollView::OnMouseWheelEvent(const MouseWheelEvent& event)
     if(mRulerY->GetType() == Ruler::Free)
     {
       // Free panning mode
-      targetScrollPosition.y += event.z * mMouseWheelScrollDistanceStep.y;
+      targetScrollPosition.y += event.z * mWheelScrollDistanceStep.y;
       ClampPosition(targetScrollPosition);
       ScrollTo(-targetScrollPosition);
     }
@@ -2444,11 +2471,6 @@ void ScrollView::OnPan( const PanGesture& gesture )
         {
           mScrollMainInternalPrePositionConstraint.Remove();
         }
-
-        if( mOvershootIndicator )
-        {
-          mOvershootIndicator->ClearOvershoot();
-        }
       }
       else
       {
@@ -2667,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();