Refactoring scroll-view-impl.cpp to reduce loc 46/265546/1
authorDavid Steele <david.steele@samsung.com>
Thu, 21 Oct 2021 16:53:19 +0000 (17:53 +0100)
committerDavid Steele <david.steele@samsung.com>
Thu, 21 Oct 2021 16:53:19 +0000 (17:53 +0100)
Change-Id: I7699976711d4a9ab24092ca8f07106e524409b63

dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.cpp

index 8ca6855..708830f 100644 (file)
@@ -154,6 +154,107 @@ Vector3 GetPositionOfAnchor(Actor& actor, const Vector3& anchor)
   return childPosition + childAnchor * childSize;
 }
 
+/**
+ * Returns the closest actor to the given position
+ * @param[in] actor The scrollview actor
+ * @param[in] internalActor The internal actor (to ignore)
+ * @param[in] position The given position
+ * @param[in] dirX Direction to search in
+ * @param[in] dirY Direction to search in
+ * @param[in] dirZ Direction to search in
+ * @return the closest child actor
+ */
+using FindDirection = Dali::Toolkit::Internal::ScrollView::FindDirection;
+
+Actor FindClosestActorToPosition(
+  CustomActor actor, Actor internalActor, const Vector3& position, FindDirection dirX, FindDirection dirY, FindDirection dirZ)
+{
+  Actor   closestChild;
+  float   closestDistance2 = 0.0f;
+  Vector3 actualPosition   = position;
+
+  unsigned int numChildren = actor.GetChildCount();
+
+  for(unsigned int i = 0; i < numChildren; ++i)
+  {
+    Actor child = actor.GetChildAt(i);
+
+    if(internalActor == child) // ignore internal actor.
+    {
+      continue;
+    }
+
+    Vector3 childPosition = GetPositionOfAnchor(child, AnchorPoint::CENTER);
+
+    Vector3 delta = childPosition - actualPosition;
+
+    // X-axis checking (only find Actors to the [dirX] of actualPosition)
+    if(dirX > FindDirection::All) // != All,None
+    {
+      FindDirection deltaH = delta.x > 0 ? FindDirection::Right : FindDirection::Left;
+      if(dirX != deltaH)
+      {
+        continue;
+      }
+    }
+
+    // Y-axis checking (only find Actors to the [dirY] of actualPosition)
+    if(dirY > FindDirection::All) // != All,None
+    {
+      FindDirection deltaV = delta.y > 0 ? FindDirection::Down : FindDirection::Up;
+      if(dirY != deltaV)
+      {
+        continue;
+      }
+    }
+
+    // Z-axis checking (only find Actors to the [dirZ] of actualPosition)
+    if(dirZ > FindDirection::All) // != All,None
+    {
+      FindDirection deltaV = delta.y > 0 ? FindDirection::In : FindDirection::Out;
+      if(dirZ != deltaV)
+      {
+        continue;
+      }
+    }
+
+    // compare child to closest child in terms of distance.
+    float distance2 = 0.0f;
+
+    // distance2 = the Square of the relevant dimensions of delta
+    if(dirX != FindDirection::None)
+    {
+      distance2 += delta.x * delta.x;
+    }
+
+    if(dirY != FindDirection::None)
+    {
+      distance2 += delta.y * delta.y;
+    }
+
+    if(dirZ != FindDirection::None)
+    {
+      distance2 += delta.z * delta.z;
+    }
+
+    if(closestChild) // Next time.
+    {
+      if(distance2 < closestDistance2)
+      {
+        closestChild     = child;
+        closestDistance2 = distance2;
+      }
+    }
+    else // First time.
+    {
+      closestChild     = child;
+      closestDistance2 = distance2;
+    }
+  }
+
+  return closestChild;
+}
+
 // AlphaFunctions /////////////////////////////////////////////////////////////////////////////////
 
 float FinalDefaultAlphaFunction(float offset)
@@ -227,6 +328,259 @@ void InternalPrePositionMaxConstraint(Vector2& scrollMax, const PropertyInputCon
   scrollMax = max - size.GetVectorXY();
 }
 
+/**
+ * Clamp a position
+ * @param[in] size The size to clamp to
+ * @param[in] rulerX The horizontal ruler
+ * @param[in] rulerY The vertical ruler
+ * @param[in,out] position The position to clamp
+ * @param[out] clamped the clamped state
+ */
+void ClampPosition(const Vector3& size, Dali::Toolkit::RulerPtr rulerX, Dali::Toolkit::RulerPtr rulerY, Vector2& position, Dali::Toolkit::ClampState2D& clamped)
+{
+  position.x = -rulerX->Clamp(-position.x, size.width, 1.0f, clamped.x);  // NOTE: X & Y rulers think in -ve coordinate system.
+  position.y = -rulerY->Clamp(-position.y, size.height, 1.0f, clamped.y); // That is scrolling RIGHT (e.g. 100.0, 0.0) means moving LEFT.
+}
+
+/**
+ * TODO: In situations where axes are different (X snap, Y free)
+ * Each axis should really have their own independent animation (time and equation)
+ * Consider, X axis snapping to nearest grid point (EaseOut over fixed time)
+ * Consider, Y axis simulating physics to arrive at a point (Physics equation over variable time)
+ * Currently, the axes have been split however, they both use the same EaseOut equation.
+ *
+ * @param[in] scrollView The main scrollview
+ * @param[in] rulerX The X ruler
+ * @param[in] rulerY The Y ruler
+ * @param[in] lockAxis Which axis (if any) is locked.
+ * @param[in] velocity Current pan velocity
+ * @param[in] maxOvershoot Maximum overshoot
+ * @param[in] inAcessibilityPan True if we are currently panning with accessibility
+ * @param[out] positionSnap The target position of snap animation
+ * @param[out] positionDuration The duration of the snap animation
+ * @param[out] alphaFunction The snap animation alpha function
+ * @param[out] isFlick if we are flicking or not
+ * @param[out] isFreeFlick if we are free flicking or not
+ */
+void SnapWithVelocity(
+  Dali::Toolkit::Internal::ScrollView&          scrollView,
+  Dali::Toolkit::RulerPtr                       rulerX,
+  Dali::Toolkit::RulerPtr                       rulerY,
+  Dali::Toolkit::Internal::ScrollView::LockAxis lockAxis,
+  Vector2                                       velocity,
+  Vector2                                       maxOvershoot,
+  Vector2&                                      positionSnap,
+  Vector2&                                      positionDuration,
+  AlphaFunction&                                alphaFunction,
+  bool                                          inAccessibilityPan,
+  bool&                                         isFlick,
+  bool&                                         isFreeFlick)
+{
+  // Animator takes over now, touches are assumed not to interfere.
+  // And if touches do interfere, then we'll stop animation, update PrePosition
+  // to current mScroll's properties, and then resume.
+  // Note: For Flicking this may work a bit different...
+
+  float         angle      = atan2(velocity.y, velocity.x);
+  float         speed2     = velocity.LengthSquared();
+  float         biasX      = 0.5f;
+  float         biasY      = 0.5f;
+  FindDirection horizontal = FindDirection::None;
+  FindDirection vertical   = FindDirection::None;
+
+  using LockAxis = Dali::Toolkit::Internal::ScrollView::LockAxis;
+
+  // orthoAngleRange = Angle tolerance within the Exact N,E,S,W direction
+  // that will be accepted as a general N,E,S,W flick direction.
+
+  const float orthoAngleRange      = FLICK_ORTHO_ANGLE_RANGE * M_PI / 180.0f;
+  const float flickSpeedThreshold2 = scrollView.GetMinimumSpeedForFlick() * scrollView.GetMinimumSpeedForFlick();
+
+  // Flick logic X Axis
+
+  if(rulerX->IsEnabled() && lockAxis != LockAxis::LockHorizontal)
+  {
+    horizontal = FindDirection::All;
+
+    if(speed2 > flickSpeedThreshold2 || // exceeds flick threshold
+       inAccessibilityPan)              // With AccessibilityPan its easier to move between snap positions
+    {
+      if((angle >= -orthoAngleRange) && (angle < orthoAngleRange)) // Swiping East
+      {
+        biasX = 0.0f, horizontal = FindDirection::Left;
+
+        // This guards against an error where no movement occurs, due to the flick finishing
+        // before the update-thread has advanced mScrollPostPosition past the the previous snap point.
+        positionSnap.x += 1.0f;
+      }
+      else if((angle >= M_PI - orthoAngleRange) || (angle < -M_PI + orthoAngleRange)) // Swiping West
+      {
+        biasX = 1.0f, horizontal = FindDirection::Right;
+
+        // This guards against an error where no movement occurs, due to the flick finishing
+        // before the update-thread has advanced mScrollPostPosition past the the previous snap point.
+        positionSnap.x -= 1.0f;
+      }
+    }
+  }
+
+  // Flick logic Y Axis
+
+  if(rulerY->IsEnabled() && lockAxis != LockAxis::LockVertical)
+  {
+    vertical = FindDirection::All;
+
+    if(speed2 > flickSpeedThreshold2 || // exceeds flick threshold
+       inAccessibilityPan)              // With AccessibilityPan its easier to move between snap positions
+    {
+      if((angle >= M_PI_2 - orthoAngleRange) && (angle < M_PI_2 + orthoAngleRange)) // Swiping South
+      {
+        biasY = 0.0f, vertical = FindDirection::Up;
+      }
+      else if((angle >= -M_PI_2 - orthoAngleRange) && (angle < -M_PI_2 + orthoAngleRange)) // Swiping North
+      {
+        biasY = 1.0f, vertical = FindDirection::Down;
+      }
+    }
+  }
+
+  // isFlick: Whether this gesture is a flick or not.
+  isFlick = (horizontal != FindDirection::All || vertical != FindDirection::All);
+  // isFreeFlick: Whether this gesture is a flick under free panning criteria.
+  isFreeFlick = velocity.LengthSquared() > (FREE_FLICK_SPEED_THRESHOLD * FREE_FLICK_SPEED_THRESHOLD);
+
+  if(isFlick || isFreeFlick)
+  {
+    positionDuration = Vector2::ONE * scrollView.GetScrollFlickDuration();
+    alphaFunction    = scrollView.GetScrollFlickAlphaFunction();
+  }
+
+  // Calculate next positionSnap ////////////////////////////////////////////////////////////
+
+  if(scrollView.GetActorAutoSnap())
+  {
+    Vector3 size = scrollView.Self().GetCurrentProperty<Vector3>(Actor::Property::SIZE);
+
+    Actor child = scrollView.FindClosestActorToPosition(Vector3(size.width * 0.5f, size.height * 0.5f, 0.0f), horizontal, vertical);
+
+    if(!child && isFlick)
+    {
+      // If we conducted a direction limited search and found no actor, then just snap to the closest actor.
+      child = scrollView.FindClosestActorToPosition(Vector3(size.width * 0.5f, size.height * 0.5f, 0.0f));
+    }
+
+    if(child)
+    {
+      Vector2 position = scrollView.Self().GetCurrentProperty<Vector2>(Toolkit::ScrollView::Property::SCROLL_POSITION);
+
+      // Get center-point of the Actor.
+      Vector3 childPosition = GetPositionOfAnchor(child, AnchorPoint::CENTER);
+
+      if(rulerX->IsEnabled())
+      {
+        positionSnap.x = position.x - childPosition.x + size.width * 0.5f;
+      }
+      if(rulerY->IsEnabled())
+      {
+        positionSnap.y = position.y - childPosition.y + size.height * 0.5f;
+      }
+    }
+  }
+
+  Vector2 startPosition = positionSnap;
+  positionSnap.x        = -rulerX->Snap(-positionSnap.x, biasX); // NOTE: X & Y rulers think in -ve coordinate system.
+  positionSnap.y        = -rulerY->Snap(-positionSnap.y, biasY); // That is scrolling RIGHT (e.g. 100.0, 0.0) means moving LEFT.
+
+  Dali::Toolkit::ClampState2D clamped;
+  Vector3                     size = scrollView.Self().GetCurrentProperty<Vector3>(Actor::Property::SIZE);
+  Vector2                     clampDelta(Vector2::ZERO);
+  ClampPosition(size, rulerX, rulerY, positionSnap, clamped);
+
+  if((rulerX->GetType() == Dali::Toolkit::Ruler::FREE || rulerY->GetType() == Dali::Toolkit::Ruler::FREE) &&
+     isFreeFlick && !scrollView.GetActorAutoSnap())
+  {
+    // Calculate target position based on velocity of flick.
+
+    // a = Deceleration (Set to diagonal stage length * friction coefficient)
+    // u = Initial Velocity (Flick velocity)
+    // v = 0 (Final Velocity)
+    // t = Time (Velocity / Deceleration)
+    Vector2 stageSize   = Stage::GetCurrent().GetSize();
+    float   stageLength = Vector3(stageSize.x, stageSize.y, 0.0f).Length();
+    float   a           = (stageLength * scrollView.GetFrictionCoefficient());
+    Vector3 u           = Vector3(velocity.x, velocity.y, 0.0f) * scrollView.GetFlickSpeedCoefficient();
+    float   speed       = u.Length();
+    u /= speed;
+
+    // TODO: Change this to a decay function. (faster you flick, the slower it should be)
+    speed = std::min(speed, stageLength * scrollView.GetMaxFlickSpeed());
+    u *= speed;
+    alphaFunction = ConstantDecelerationAlphaFunction;
+
+    float t = speed / a;
+
+    if(rulerX->IsEnabled() && rulerX->GetType() == Dali::Toolkit::Ruler::FREE)
+    {
+      positionSnap.x += t * u.x * 0.5f;
+    }
+
+    if(rulerY->IsEnabled() && rulerY->GetType() == Dali::Toolkit::Ruler::FREE)
+    {
+      positionSnap.y += t * u.y * 0.5f;
+    }
+
+    clampDelta = positionSnap;
+    ClampPosition(size, rulerX, rulerY, positionSnap, clamped);
+
+    if((positionSnap - startPosition).LengthSquared() > Math::MACHINE_EPSILON_0)
+    {
+      clampDelta -= positionSnap;
+      clampDelta.x = clampDelta.x > 0.0f ? std::min(clampDelta.x, maxOvershoot.x) : std::max(clampDelta.x, -maxOvershoot.x);
+      clampDelta.y = clampDelta.y > 0.0f ? std::min(clampDelta.y, maxOvershoot.y) : std::max(clampDelta.y, -maxOvershoot.y);
+    }
+    else
+    {
+      clampDelta = Vector2::ZERO;
+    }
+
+    // If Axis is Free and has velocity, then calculate time taken
+    // to reach target based on velocity in axis.
+    if(rulerX->IsEnabled() && rulerX->GetType() == Dali::Toolkit::Ruler::FREE)
+    {
+      float deltaX = fabsf(startPosition.x - positionSnap.x);
+
+      if(fabsf(u.x) > Math::MACHINE_EPSILON_1)
+      {
+        positionDuration.x = fabsf(deltaX / u.x);
+      }
+      else
+      {
+        positionDuration.x = 0;
+      }
+    }
+
+    if(rulerY->IsEnabled() && rulerY->GetType() == Dali::Toolkit::Ruler::FREE)
+    {
+      float deltaY = fabsf(startPosition.y - positionSnap.y);
+
+      if(fabsf(u.y) > Math::MACHINE_EPSILON_1)
+      {
+        positionDuration.y = fabsf(deltaY / u.y);
+      }
+      else
+      {
+        positionDuration.y = 0;
+      }
+    }
+  }
+
+  if(scrollView.IsOvershootEnabled())
+  {
+    // Scroll to the end of the overshoot only when overshoot is enabled.
+    positionSnap += clampDelta;
+  }
+}
+
 } // unnamed namespace
 
 namespace Dali
@@ -1345,90 +1699,7 @@ Actor ScrollView::FindClosestActor()
 
 Actor ScrollView::FindClosestActorToPosition(const Vector3& position, FindDirection dirX, FindDirection dirY, FindDirection dirZ)
 {
-  Actor   closestChild;
-  float   closestDistance2 = 0.0f;
-  Vector3 actualPosition   = position;
-
-  unsigned int numChildren = Self().GetChildCount();
-
-  for(unsigned int i = 0; i < numChildren; ++i)
-  {
-    Actor child = Self().GetChildAt(i);
-
-    if(mInternalActor == child) // ignore internal actor.
-    {
-      continue;
-    }
-
-    Vector3 childPosition = GetPositionOfAnchor(child, AnchorPoint::CENTER);
-
-    Vector3 delta = childPosition - actualPosition;
-
-    // X-axis checking (only find Actors to the [dirX] of actualPosition)
-    if(dirX > All) // != All,None
-    {
-      FindDirection deltaH = delta.x > 0 ? Right : Left;
-      if(dirX != deltaH)
-      {
-        continue;
-      }
-    }
-
-    // Y-axis checking (only find Actors to the [dirY] of actualPosition)
-    if(dirY > All) // != All,None
-    {
-      FindDirection deltaV = delta.y > 0 ? Down : Up;
-      if(dirY != deltaV)
-      {
-        continue;
-      }
-    }
-
-    // Z-axis checking (only find Actors to the [dirZ] of actualPosition)
-    if(dirZ > All) // != All,None
-    {
-      FindDirection deltaV = delta.y > 0 ? In : Out;
-      if(dirZ != deltaV)
-      {
-        continue;
-      }
-    }
-
-    // compare child to closest child in terms of distance.
-    float distance2 = 0.0f;
-
-    // distance2 = the Square of the relevant dimensions of delta
-    if(dirX != None)
-    {
-      distance2 += delta.x * delta.x;
-    }
-
-    if(dirY != None)
-    {
-      distance2 += delta.y * delta.y;
-    }
-
-    if(dirZ != None)
-    {
-      distance2 += delta.z * delta.z;
-    }
-
-    if(closestChild) // Next time.
-    {
-      if(distance2 < closestDistance2)
-      {
-        closestChild     = child;
-        closestDistance2 = distance2;
-      }
-    }
-    else // First time.
-    {
-      closestChild     = child;
-      closestDistance2 = distance2;
-    }
-  }
-
-  return closestChild;
+  return ::FindClosestActorToPosition(Self(), mInternalActor, position, dirX, dirY, dirZ);
 }
 
 bool ScrollView::ScrollToSnapPoint()
@@ -1438,214 +1709,15 @@ bool ScrollView::ScrollToSnapPoint()
   return SnapWithVelocity(stationaryVelocity);
 }
 
-// TODO: In situations where axes are different (X snap, Y free)
-// Each axis should really have their own independent animation (time and equation)
-// Consider, X axis snapping to nearest grid point (EaseOut over fixed time)
-// Consider, Y axis simulating physics to arrive at a point (Physics equation over variable time)
-// Currently, the axes have been split however, they both use the same EaseOut equation.
 bool ScrollView::SnapWithVelocity(Vector2 velocity)
 {
-  // Animator takes over now, touches are assumed not to interfere.
-  // And if touches do interfere, then we'll stop animation, update PrePosition
-  // to current mScroll's properties, and then resume.
-  // Note: For Flicking this may work a bit different...
-
-  float         angle            = atan2(velocity.y, velocity.x);
-  float         speed2           = velocity.LengthSquared();
-  AlphaFunction alphaFunction    = mSnapAlphaFunction;
+  Vector2       positionSnap     = mScrollPrePosition;
   Vector2       positionDuration = Vector2::ONE * mSnapDuration;
-  float         biasX            = 0.5f;
-  float         biasY            = 0.5f;
-  FindDirection horizontal       = None;
-  FindDirection vertical         = None;
-
-  // orthoAngleRange = Angle tolerance within the Exact N,E,S,W direction
-  // that will be accepted as a general N,E,S,W flick direction.
-
-  const float orthoAngleRange      = FLICK_ORTHO_ANGLE_RANGE * M_PI / 180.0f;
-  const float flickSpeedThreshold2 = mFlickSpeedThreshold * mFlickSpeedThreshold;
-
-  Vector2 positionSnap = mScrollPrePosition;
-
-  // Flick logic X Axis
-
-  if(mRulerX->IsEnabled() && mLockAxis != LockHorizontal)
-  {
-    horizontal = All;
-
-    if(speed2 > flickSpeedThreshold2 || // exceeds flick threshold
-       mInAccessibilityPan)             // With AccessibilityPan its easier to move between snap positions
-    {
-      if((angle >= -orthoAngleRange) && (angle < orthoAngleRange)) // Swiping East
-      {
-        biasX = 0.0f, horizontal = Left;
-
-        // This guards against an error where no movement occurs, due to the flick finishing
-        // before the update-thread has advanced mScrollPostPosition past the the previous snap point.
-        positionSnap.x += 1.0f;
-      }
-      else if((angle >= M_PI - orthoAngleRange) || (angle < -M_PI + orthoAngleRange)) // Swiping West
-      {
-        biasX = 1.0f, horizontal = Right;
-
-        // This guards against an error where no movement occurs, due to the flick finishing
-        // before the update-thread has advanced mScrollPostPosition past the the previous snap point.
-        positionSnap.x -= 1.0f;
-      }
-    }
-  }
-
-  // Flick logic Y Axis
-
-  if(mRulerY->IsEnabled() && mLockAxis != LockVertical)
-  {
-    vertical = All;
-
-    if(speed2 > flickSpeedThreshold2 || // exceeds flick threshold
-       mInAccessibilityPan)             // With AccessibilityPan its easier to move between snap positions
-    {
-      if((angle >= M_PI_2 - orthoAngleRange) && (angle < M_PI_2 + orthoAngleRange)) // Swiping South
-      {
-        biasY = 0.0f, vertical = Up;
-      }
-      else if((angle >= -M_PI_2 - orthoAngleRange) && (angle < -M_PI_2 + orthoAngleRange)) // Swiping North
-      {
-        biasY = 1.0f, vertical = Down;
-      }
-    }
-  }
-
-  // isFlick: Whether this gesture is a flick or not.
-  bool isFlick = (horizontal != All || vertical != All);
-  // isFreeFlick: Whether this gesture is a flick under free panning criteria.
-  bool isFreeFlick = velocity.LengthSquared() > (FREE_FLICK_SPEED_THRESHOLD * FREE_FLICK_SPEED_THRESHOLD);
-
-  if(isFlick || isFreeFlick)
-  {
-    positionDuration = Vector2::ONE * mFlickDuration;
-    alphaFunction    = mFlickAlphaFunction;
-  }
-
-  // Calculate next positionSnap ////////////////////////////////////////////////////////////
-
-  if(mActorAutoSnapEnabled)
-  {
-    Vector3 size = Self().GetCurrentProperty<Vector3>(Actor::Property::SIZE);
-
-    Actor child = FindClosestActorToPosition(Vector3(size.width * 0.5f, size.height * 0.5f, 0.0f), horizontal, vertical);
-
-    if(!child && isFlick)
-    {
-      // If we conducted a direction limited search and found no actor, then just snap to the closest actor.
-      child = FindClosestActorToPosition(Vector3(size.width * 0.5f, size.height * 0.5f, 0.0f));
-    }
-
-    if(child)
-    {
-      Vector2 position = Self().GetCurrentProperty<Vector2>(Toolkit::ScrollView::Property::SCROLL_POSITION);
-
-      // Get center-point of the Actor.
-      Vector3 childPosition = GetPositionOfAnchor(child, AnchorPoint::CENTER);
-
-      if(mRulerX->IsEnabled())
-      {
-        positionSnap.x = position.x - childPosition.x + size.width * 0.5f;
-      }
-      if(mRulerY->IsEnabled())
-      {
-        positionSnap.y = position.y - childPosition.y + size.height * 0.5f;
-      }
-    }
-  }
-
-  Vector2 startPosition = positionSnap;
-  positionSnap.x        = -mRulerX->Snap(-positionSnap.x, biasX); // NOTE: X & Y rulers think in -ve coordinate system.
-  positionSnap.y        = -mRulerY->Snap(-positionSnap.y, biasY); // That is scrolling RIGHT (e.g. 100.0, 0.0) means moving LEFT.
-
-  Vector2 clampDelta(Vector2::ZERO);
-  ClampPosition(positionSnap);
-
-  if((mRulerX->GetType() == Ruler::FREE || mRulerY->GetType() == Ruler::FREE) && isFreeFlick && !mActorAutoSnapEnabled)
-  {
-    // Calculate target position based on velocity of flick.
-
-    // a = Deceleration (Set to diagonal stage length * friction coefficient)
-    // u = Initial Velocity (Flick velocity)
-    // v = 0 (Final Velocity)
-    // t = Time (Velocity / Deceleration)
-    Vector2 stageSize   = Stage::GetCurrent().GetSize();
-    float   stageLength = Vector3(stageSize.x, stageSize.y, 0.0f).Length();
-    float   a           = (stageLength * mFrictionCoefficient);
-    Vector3 u           = Vector3(velocity.x, velocity.y, 0.0f) * mFlickSpeedCoefficient;
-    float   speed       = u.Length();
-    u /= speed;
-
-    // TODO: Change this to a decay function. (faster you flick, the slower it should be)
-    speed = std::min(speed, stageLength * mMaxFlickSpeed);
-    u *= speed;
-    alphaFunction = ConstantDecelerationAlphaFunction;
-
-    float t = speed / a;
-
-    if(mRulerX->IsEnabled() && mRulerX->GetType() == Ruler::FREE)
-    {
-      positionSnap.x += t * u.x * 0.5f;
-    }
-
-    if(mRulerY->IsEnabled() && mRulerY->GetType() == Ruler::FREE)
-    {
-      positionSnap.y += t * u.y * 0.5f;
-    }
-
-    clampDelta = positionSnap;
-    ClampPosition(positionSnap);
-    if((positionSnap - startPosition).LengthSquared() > Math::MACHINE_EPSILON_0)
-    {
-      clampDelta -= positionSnap;
-      clampDelta.x = clampDelta.x > 0.0f ? std::min(clampDelta.x, mMaxOvershoot.x) : std::max(clampDelta.x, -mMaxOvershoot.x);
-      clampDelta.y = clampDelta.y > 0.0f ? std::min(clampDelta.y, mMaxOvershoot.y) : std::max(clampDelta.y, -mMaxOvershoot.y);
-    }
-    else
-    {
-      clampDelta = Vector2::ZERO;
-    }
-
-    // If Axis is Free and has velocity, then calculate time taken
-    // to reach target based on velocity in axis.
-    if(mRulerX->IsEnabled() && mRulerX->GetType() == Ruler::FREE)
-    {
-      float deltaX = fabsf(startPosition.x - positionSnap.x);
-
-      if(fabsf(u.x) > Math::MACHINE_EPSILON_1)
-      {
-        positionDuration.x = fabsf(deltaX / u.x);
-      }
-      else
-      {
-        positionDuration.x = 0;
-      }
-    }
-
-    if(mRulerY->IsEnabled() && mRulerY->GetType() == Ruler::FREE)
-    {
-      float deltaY = fabsf(startPosition.y - positionSnap.y);
-
-      if(fabsf(u.y) > Math::MACHINE_EPSILON_1)
-      {
-        positionDuration.y = fabsf(deltaY / u.y);
-      }
-      else
-      {
-        positionDuration.y = 0;
-      }
-    }
-  }
+  AlphaFunction alphaFunction    = mSnapAlphaFunction;
+  bool          isFlick;
+  bool          isFreeFlick;
 
-  if(IsOvershootEnabled())
-  {
-    // Scroll to the end of the overshoot only when overshoot is enabled.
-    positionSnap += clampDelta;
-  }
+  ::SnapWithVelocity(*this, mRulerX, mRulerY, mLockAxis, velocity, mMaxOvershoot, positionSnap, positionDuration, alphaFunction, mInAccessibilityPan, isFlick, isFreeFlick);
 
   bool animating = AnimateTo(positionSnap, positionDuration, alphaFunction, false, DIRECTION_BIAS_NONE, DIRECTION_BIAS_NONE, isFlick || isFreeFlick ? FLICK : SNAP);
 
@@ -1826,7 +1898,7 @@ Toolkit::ScrollView::SnapStartedSignalType& ScrollView::SnapStartedSignal()
 bool ScrollView::AccessibleImpl::ScrollToChild(Actor child)
 {
   auto scrollView = Dali::Toolkit::ScrollView::DownCast(Self());
-  if (Toolkit::GetImpl(scrollView).FindClosestActor() == child)
+  if(Toolkit::GetImpl(scrollView).FindClosestActor() == child)
   {
     return false;
   }
@@ -2704,8 +2776,7 @@ void ScrollView::ClampPosition(Vector2& position, ClampState2D& clamped) const
 {
   Vector3 size = Self().GetCurrentProperty<Vector3>(Actor::Property::SIZE);
 
-  position.x = -mRulerX->Clamp(-position.x, size.width, 1.0f, clamped.x);  // NOTE: X & Y rulers think in -ve coordinate system.
-  position.y = -mRulerY->Clamp(-position.y, size.height, 1.0f, clamped.y); // That is scrolling RIGHT (e.g. 100.0, 0.0) means moving LEFT.
+  ::ClampPosition(size, mRulerX, mRulerY, position, clamped);
 }
 
 void ScrollView::WrapPosition(Vector2& position) const