(ScrollView) Updates to scroll view internals to improve responsiveness and smoothnes...
authorJulien Heanley <j.heanley@partner.samsung.com>
Fri, 21 Feb 2014 11:09:08 +0000 (11:09 +0000)
committerAdeel Kazmi <adeel.kazmi@samsung.com>
Wed, 11 Jun 2014 08:01:24 +0000 (09:01 +0100)
[Problem] Overshoot indicator could occasionally remain on screen, overshoot would prevent panning in opposite direction until pan returned to point where overshoot started
[Solution] Replaced numerous constraints with property notifications.
           Implemented the new vector component animation to remove need for separate internal x/y components.
           First stage of constraint reduction on ScrollView, more to follow
           Pre clamp scroll value is now snapped back to clamped position using animation and no longer needs a constraint to follow old internal x/y components
           Made functionality of overshoot more solid by making it directly dependant on pre clamped scroll and post clamp scroll values now that animation is done elsewhere.
           Overshoot no longer instantly flicks back to 0 if user releases and restarts a gesture before values have finished animating back
           If a pan has locked to an axis it now remains locked if user releases and starts another pan before scrolling has completed, preventing issue where a vertical page scroll could be started in the middle of a horizontal page scroll, which brakes the appearance of all page scroll effects.

Change-Id: I73e8ca71f5ffd8eb3c8a1fbf91823881ac4bb2ce
Signed-off-by: Julien Heanley <j.heanley@partner.samsung.com>
base/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-overshoot-indicator-impl.cpp
base/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-overshoot-indicator-impl.h
base/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.cpp
base/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.h
base/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-twist-effect-impl.cpp
base/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-twist-effect-impl.h
base/dali-toolkit/internal/controls/scrollable/scrollable-impl.cpp
base/dali-toolkit/internal/controls/scrollable/scrollable-impl.h
base/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view.cpp
capi/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view.h

index 70194d2..94aca48 100644 (file)
@@ -38,9 +38,11 @@ namespace Internal
 const float DEFAULT_MAX_OVERSHOOT_HEIGHT = 36.0f;  // 36 pixels
 const Rect<int> OVERSHOOT_RIPPLE_IMAGE_1_PIXEL_AREA( 0, 0, 720, 58 );
 const float DEFAULT_OVERSHOOT_ANIMATION_DURATION = 0.35f;  // time in seconds
 const float DEFAULT_MAX_OVERSHOOT_HEIGHT = 36.0f;  // 36 pixels
 const Rect<int> OVERSHOOT_RIPPLE_IMAGE_1_PIXEL_AREA( 0, 0, 720, 58 );
 const float DEFAULT_OVERSHOOT_ANIMATION_DURATION = 0.35f;  // time in seconds
+const float MAX_OVERSHOOT_NOTIFY_AMOUNT = 0.9f;                     // maximum amount to set notification for increased overshoot, beyond this we just wait for it to reduce again
+const float MIN_OVERSHOOT_NOTIFY_AMOUNT = Math::MACHINE_EPSILON_1;  // minimum amount to set notification for reduced overshoot, beyond this we just wait for it to increase again
+const float OVERSHOOT_NOTIFY_STEP = 0.1f;                           // amount to set notifications beyond current overshoot value
 
 
-ScrollOvershootIndicator::ScrollOvershootIndicator(Scrollable& scrollable) :
-  mScrollable(scrollable),
+ScrollOvershootIndicator::ScrollOvershootIndicator() :
   mEffectX(NULL),
   mEffectY(NULL)
 {
   mEffectX(NULL),
   mEffectY(NULL)
 {
@@ -51,39 +53,35 @@ ScrollOvershootIndicator::~ScrollOvershootIndicator()
 
 }
 
 
 }
 
-ScrollOvershootIndicator* ScrollOvershootIndicator::New(Scrollable& scrollable)
+ScrollOvershootIndicator* ScrollOvershootIndicator::New()
 {
 {
-  ScrollOvershootIndicator* scrollOvershootPtr = new ScrollOvershootIndicator(scrollable);
+  ScrollOvershootIndicator* scrollOvershootPtr = new ScrollOvershootIndicator();
   return scrollOvershootPtr;
 }
 
   return scrollOvershootPtr;
 }
 
-void ScrollOvershootIndicator::Enable(bool enable)
+void ScrollOvershootIndicator::AttachToScrollable(Scrollable& scrollable)
 {
 {
-  if(enable)
+  if(!mEffectX)
   {
   {
-    Actor scrollableActor = mScrollable.Self();
-    if(!mEffectX)
-    {
-      mEffectX = ScrollOvershootEffectRipple::New(false);
-    }
-    mEffectX->Apply(mScrollable);
-    if(!mEffectY)
-    {
-      mEffectY = ScrollOvershootEffectRipple::New(true);
-    }
-    mEffectY->Apply(mScrollable);
-    mEffectY->SetPropertyNotifications(scrollableActor);
+    mEffectX = ScrollOvershootEffectRipple::New(false, scrollable);
   }
   }
-  else
+  mEffectX->Apply();
+  if(!mEffectY)
   {
   {
-    if(mEffectX)
-    {
-      mEffectX->Remove(mScrollable);
-    }
-    if(mEffectY)
-    {
-      mEffectY->Remove(mScrollable);
-    }
+    mEffectY = ScrollOvershootEffectRipple::New(true, scrollable);
+  }
+  mEffectY->Apply();
+}
+
+void ScrollOvershootIndicator::DetachFromScrollable(Scrollable& scrollable)
+{
+  if(mEffectX)
+  {
+    mEffectX->Remove(scrollable);
+  }
+  if(mEffectY)
+  {
+    mEffectY->Remove(scrollable);
   }
 }
 
   }
 }
 
@@ -93,15 +91,27 @@ void ScrollOvershootIndicator::Reset()
   mEffectY->Reset();
 }
 
   mEffectY->Reset();
 }
 
-ScrollOvershootEffect::ScrollOvershootEffect(bool vertical) :
+ScrollOvershootEffect::ScrollOvershootEffect( bool vertical ) :
     mVertical(vertical)
 {
 
 }
 
     mVertical(vertical)
 {
 
 }
 
-ScrollOvershootEffectRipple::ScrollOvershootEffectRipple(bool vertical) :
-    ScrollOvershootEffect(vertical),
-    mMaxOvershootImageSize(DEFAULT_MAX_OVERSHOOT_HEIGHT)
+bool ScrollOvershootEffect::IsVertical() const
+{
+  return mVertical;
+}
+
+ScrollOvershootEffectRipple::ScrollOvershootEffectRipple( bool vertical, Scrollable& scrollable ) :
+    ScrollOvershootEffect( vertical ),
+    mAttachedScrollView(scrollable),
+    mCanScrollPropertyIndex(Property::INVALID_INDEX),
+    mOvershootProperty(Property::INVALID_INDEX),
+    mEffectOvershootProperty(Property::INVALID_INDEX),
+    mMaxOvershootImageSize(DEFAULT_MAX_OVERSHOOT_HEIGHT),
+    mOvershootAnimationDuration(DEFAULT_OVERSHOOT_ANIMATION_DURATION),
+    mOvershoot(0.0f),
+    mAnimationStateFlags(0)
 {
   mRippleEffect = BouncingEffect::New(Scrollable::DEFAULT_OVERSHOOT_COLOUR);
   mOvershootImage = CreateSolidColorActor(Vector4::ONE);
 {
   mRippleEffect = BouncingEffect::New(Scrollable::DEFAULT_OVERSHOOT_COLOUR);
   mOvershootImage = CreateSolidColorActor(Vector4::ONE);
@@ -110,36 +120,36 @@ ScrollOvershootEffectRipple::ScrollOvershootEffectRipple(bool vertical) :
   mOvershootImage.SetDrawMode(DrawMode::OVERLAY);
   mOvershootImage.SetShaderEffect(mRippleEffect);
   mOvershootImage.SetVisible(false);
   mOvershootImage.SetDrawMode(DrawMode::OVERLAY);
   mOvershootImage.SetShaderEffect(mRippleEffect);
   mOvershootImage.SetVisible(false);
-  mAnimatingOvershootOn = false;
-  mAnimateOvershootOff = false;
 }
 
 }
 
-void ScrollOvershootEffectRipple::Apply(Scrollable& scrollable)
+void ScrollOvershootEffectRipple::Apply()
 {
 {
-  Actor scrollableActor = scrollable.Self();
+  Actor self = mAttachedScrollView.Self();
+  mOvershootProperty = self.GetPropertyIndex(IsVertical() ? Toolkit::ScrollView::SCROLL_OVERSHOOT_Y_PROPERTY_NAME : Toolkit::ScrollView::SCROLL_OVERSHOOT_X_PROPERTY_NAME);
+  mCanScrollPropertyIndex = self.GetPropertyIndex(IsVertical() ? Scrollable::SCROLLABLE_CAN_SCROLL_VERTICAL : Scrollable::SCROLLABLE_CAN_SCROLL_HORIZONTAL);
+  mEffectOvershootProperty = mRippleEffect.GetPropertyIndex(mRippleEffect.GetProgressRatePropertyName());
 
   // make sure height is set, since we only create a constraint for image width
   mOvershootImage.SetSize(OVERSHOOT_RIPPLE_IMAGE_1_PIXEL_AREA.width, OVERSHOOT_RIPPLE_IMAGE_1_PIXEL_AREA.height);
 
 
   // make sure height is set, since we only create a constraint for image width
   mOvershootImage.SetSize(OVERSHOOT_RIPPLE_IMAGE_1_PIXEL_AREA.width, OVERSHOOT_RIPPLE_IMAGE_1_PIXEL_AREA.height);
 
-  UpdateConstraints(scrollableActor);
-  scrollable.AddOverlay(mOvershootImage);
+  mAttachedScrollView.AddOverlay(mOvershootImage);
 
 
-  SetPropertyNotifications(scrollableActor);
+  UpdatePropertyNotifications();
 }
 
 }
 
-void ScrollOvershootEffectRipple::Remove(Scrollable& scrollable)
+void ScrollOvershootEffectRipple::Remove( Scrollable& scrollable )
 {
   if(mOvershootImage)
   {
 {
   if(mOvershootImage)
   {
-    if(mSizeConstraint)
+    if(mOvershootIncreaseNotification)
     {
     {
-      mOvershootImage.RemoveConstraint(mSizeConstraint);
-      mSizeConstraint = NULL;
+      scrollable.Self().RemovePropertyNotification(mOvershootIncreaseNotification);
+      mOvershootIncreaseNotification.Reset();
     }
     }
-    if(mPositionConstraint)
+    if(mOvershootDecreaseNotification)
     {
     {
-      mOvershootImage.RemoveConstraint(mPositionConstraint);
-      mPositionConstraint = NULL;
+      scrollable.Self().RemovePropertyNotification(mOvershootDecreaseNotification);
+      mOvershootDecreaseNotification.Reset();
     }
     scrollable.RemoveOverlay(mOvershootImage);
   }
     }
     scrollable.RemoveOverlay(mOvershootImage);
   }
@@ -147,233 +157,207 @@ void ScrollOvershootEffectRipple::Remove(Scrollable& scrollable)
 
 void ScrollOvershootEffectRipple::Reset()
 {
 
 void ScrollOvershootEffectRipple::Reset()
 {
-  mAnimatingOvershootOn = false;
-  mAnimateOvershootOff = false;
   mOvershootImage.SetVisible(false);
   mOvershootImage.SetVisible(false);
-  mRippleEffect.SetProgressRate(0.0f);
-  if(mScrollOvershootAnimation)
-  {
-    mScrollOvershootAnimation.Clear();
-    mScrollOvershootAnimation.Reset();
-  }
+  mRippleEffect.SetUniform(mRippleEffect.GetProgressRatePropertyName(), 0.0f);
 }
 
 }
 
-void ScrollOvershootEffectRipple::UpdateConstraints(Actor& scrollable)
+void ScrollOvershootEffectRipple::UpdatePropertyNotifications()
 {
 {
-  int overshootPropertyIndex = mRippleEffect.GetPropertyIndex(mRippleEffect.GetProgressRatePropertyName());
-  Constraint constraint;
-  if(!mSizeConstraint)
+  float absOvershoot = fabsf(mOvershoot);
+
+  Actor self = mAttachedScrollView.Self();
+  // update overshoot increase notify
+  if( mOvershootIncreaseNotification )
   {
   {
-    constraint = Constraint::New<float>( Actor::SIZE_WIDTH,
-                                                      Source( scrollable, IsVertical() ? Actor::SIZE_WIDTH : Actor::SIZE_HEIGHT),
-                                                      EqualToConstraint() );
-    mSizeConstraint = mOvershootImage.ApplyConstraint(constraint);
+    self.RemovePropertyNotification( mOvershootIncreaseNotification );
+    mOvershootIncreaseNotification.Reset();
   }
   }
-
-  if(!mPositionConstraint)
+  if( absOvershoot < MAX_OVERSHOOT_NOTIFY_AMOUNT )
   {
   {
-    constraint = Constraint::New<Vector3>( Actor::POSITION,
-                                           Source( scrollable, Actor::SIZE ),
-                                           Source( mRippleEffect, overshootPropertyIndex ),
-                                           boost::bind( &ScrollOvershootEffectRipple::PositionConstraint, this, _1, _2, _3) );
-    mPositionConstraint = mOvershootImage.ApplyConstraint(constraint);
+    float increaseStep = absOvershoot + OVERSHOOT_NOTIFY_STEP;
+    if( increaseStep > MAX_OVERSHOOT_NOTIFY_AMOUNT )
+    {
+      increaseStep = MAX_OVERSHOOT_NOTIFY_AMOUNT;
+    }
+    mOvershootIncreaseNotification = self.AddPropertyNotification( mOvershootProperty, OutsideCondition(-increaseStep, increaseStep) );
+    mOvershootIncreaseNotification.SetNotifyMode(PropertyNotification::NotifyOnTrue);
+    mOvershootIncreaseNotification.NotifySignal().Connect(this, &ScrollOvershootEffectRipple::OnOvershootNotification);
   }
   }
-}
-
-void ScrollOvershootEffectRipple::SetPropertyNotifications(Actor& scrollable)
-{
-  int overshootXPropertyIndex = scrollable.GetPropertyIndex(Toolkit::ScrollView::SCROLL_OVERSHOOT_X_PROPERTY_NAME);
-  int overshootYPropertyIndex = scrollable.GetPropertyIndex(Toolkit::ScrollView::SCROLL_OVERSHOOT_Y_PROPERTY_NAME);
-  mCanScrollPropertyIndex = scrollable.GetPropertyIndex(IsVertical() ? Scrollable::SCROLLABLE_CAN_SCROLL_VERTICAL : Scrollable::SCROLLABLE_CAN_SCROLL_HORIZONTAL);
 
 
-  if(!mOvershootNegativeNotification)
+  // update overshoot decrease notify
+  if( mOvershootDecreaseNotification )
   {
   {
-    mOvershootNegativeNotification = scrollable.AddPropertyNotification(IsVertical() ? overshootYPropertyIndex : overshootXPropertyIndex, LessThanCondition(-Math::MACHINE_EPSILON_1));
-    mOvershootNegativeNotification.SetNotifyMode(PropertyNotification::NotifyOnChanged);
-    mOvershootNegativeNotification.NotifySignal().Connect(this, &ScrollOvershootEffectRipple::OnNegativeOvershootNotification);
+    self.RemovePropertyNotification( mOvershootDecreaseNotification );
+    mOvershootDecreaseNotification.Reset();
   }
   }
-
-  if(!mOvershootPositiveNotification)
+  if( absOvershoot > MIN_OVERSHOOT_NOTIFY_AMOUNT )
   {
   {
-    mOvershootPositiveNotification = scrollable.AddPropertyNotification(IsVertical() ? overshootYPropertyIndex : overshootXPropertyIndex, GreaterThanCondition(Math::MACHINE_EPSILON_1));
-    mOvershootPositiveNotification.SetNotifyMode(PropertyNotification::NotifyOnChanged);
-    mOvershootPositiveNotification.NotifySignal().Connect(this, &ScrollOvershootEffectRipple::OnPositiveOvershootNotification);
+    float reduceStep = absOvershoot - OVERSHOOT_NOTIFY_STEP;
+    if( reduceStep < MIN_OVERSHOOT_NOTIFY_AMOUNT )
+    {
+      reduceStep = MIN_OVERSHOOT_NOTIFY_AMOUNT;
+    }
+    mOvershootDecreaseNotification = self.AddPropertyNotification( mOvershootProperty, InsideCondition(-reduceStep, reduceStep) );
+    mOvershootDecreaseNotification.SetNotifyMode(PropertyNotification::NotifyOnTrue);
+    mOvershootDecreaseNotification.NotifySignal().Connect(this, &ScrollOvershootEffectRipple::OnOvershootNotification);
   }
 }
 
   }
 }
 
-Vector3 ScrollOvershootEffectRipple::PositionConstraint(const Vector3& current,
-    const PropertyInput& parentSizeProperty, const PropertyInput& overshootProperty)
+void ScrollOvershootEffectRipple::UpdateVisibility( bool visible )
 {
 {
-  float overshoot = overshootProperty.GetFloat();
-  const Vector3 parentSize = parentSizeProperty.GetVector3();
-
-  Vector3 relativeOffset = Vector3::ZERO;
-
-  if(IsVertical())
+  mOvershootImage.SetVisible(visible);
+  // make sure overshoot image is correctly placed
+  if( visible )
   {
   {
-    if(overshoot > Math::MACHINE_EPSILON_0)
+    Actor self = mAttachedScrollView.Self();
+    if(mOvershoot > 0.0f)
     {
     {
-      relativeOffset = Vector3(0.0f, 0.0f, 0.0f);
+      // positive overshoot
+      const Vector3 imageSize = mOvershootImage.GetCurrentSize();
+      Vector3 relativeOffset;
+      const Vector3 parentSize = self.GetCurrentSize();
+      if(IsVertical())
+      {
+        mOvershootImage.SetRotation(Quaternion(0.0f, Vector3::ZAXIS));
+        mOvershootImage.SetSize(parentSize.width, imageSize.height, imageSize.depth);
+      }
+      else
+      {
+        mOvershootImage.SetRotation(Quaternion(1.5f * Math::PI, Vector3::ZAXIS));
+        mOvershootImage.SetSize(parentSize.height, imageSize.height, imageSize.depth);
+        relativeOffset = Vector3(0.0f, 1.0f, 0.0f);
+      }
+      mOvershootImage.SetPosition(relativeOffset * parentSize);
     }
     }
-    else if (overshoot < -Math::MACHINE_EPSILON_0)
+    else
     {
     {
-      relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
+      // negative overshoot
+      const Vector3 imageSize = mOvershootImage.GetCurrentSize();
+      Vector3 relativeOffset;
+      const Vector3 parentSize = self.GetCurrentSize();
+      if(IsVertical())
+      {
+        mOvershootImage.SetRotation(Quaternion(Math::PI, Vector3::ZAXIS));
+        mOvershootImage.SetSize(parentSize.width, imageSize.height, imageSize.depth);
+        relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
+      }
+      else
+      {
+        mOvershootImage.SetRotation(Quaternion(0.5f * Math::PI, Vector3::ZAXIS));
+        mOvershootImage.SetSize(parentSize.height, imageSize.height, imageSize.depth);
+        relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
+      }
+      mOvershootImage.SetPosition(relativeOffset * parentSize);
     }
   }
     }
   }
-  else
+}
+
+void ScrollOvershootEffectRipple::OnOvershootNotification(PropertyNotification& source)
+{
+  Actor self = mAttachedScrollView.Self();
+  mOvershoot = self.GetProperty<float>(mOvershootProperty);
+  if( source == mOvershootIncreaseNotification )
   {
   {
-    if(overshoot > Math::MACHINE_EPSILON_0)
+    if( mOvershoot > Math::MACHINE_EPSILON_0 )
     {
     {
-      relativeOffset = Vector3(0.0f, 1.0f, 0.0f);
+      SetOvershoot(1.0f);
     }
     }
-    else if (overshoot < -Math::MACHINE_EPSILON_0)
+    else if ( mOvershoot < -Math::MACHINE_EPSILON_0 )
     {
     {
-      relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
+      SetOvershoot(-1.0f);
     }
   }
     }
   }
-
-  return relativeOffset * parentSize;
-}
-
-void ScrollOvershootEffectRipple::OnPositiveOvershootNotification(PropertyNotification& source)
-{
-  Actor delegate = Actor::DownCast(source.GetTarget());
-  float overshoot = delegate.GetProperty<float>(source.GetTargetProperty());
-  bool canScroll = delegate.GetProperty<bool>(mCanScrollPropertyIndex);
-  if(!canScroll)
+  else if( source == mOvershootDecreaseNotification )
   {
   {
-    mOvershootImage.SetVisible(false);
-    return;
+    SetOvershoot(0.0f);
+    // overshoot reducing
   }
   }
-  mOvershootImage.SetVisible(true);
+  UpdatePropertyNotifications();
+}
 
 
-  if (fabsf(overshoot) < Math::MACHINE_EPSILON_1)
-  {
-    AnimateScrollOvershoot(0.0f);
-    return;
-  }
-  if(overshoot > 0.0f)
+void ScrollOvershootEffectRipple::SetOvershoot(float amount, bool animate)
+{
+  float absAmount = fabsf(amount);
+  bool animatingOn = absAmount > Math::MACHINE_EPSILON_0;
+  if( (animatingOn && (mAnimationStateFlags & AnimatingIn)) )
   {
   {
-    const Vector3 imageSize = mOvershootImage.GetCurrentSize();
-    Vector3 relativeOffset = Vector3::ZERO;
-    const Vector3 parentSize = delegate.GetCurrentSize();
-    AnimateScrollOvershoot(1.0f);
-    if(IsVertical())
+    // trying to do what we are already doing
+    if( mAnimationStateFlags & AnimateBack )
     {
     {
-      mOvershootImage.SetRotation(Quaternion(0.0f, Vector3::ZAXIS));
-      mOvershootImage.SetSize(parentSize.width, imageSize.height, imageSize.depth);
-      relativeOffset = Vector3(0.0f, 0.0f, 0.0f);
+      mAnimationStateFlags &= ~AnimateBack;
     }
     }
-    else
-    {
-      mOvershootImage.SetRotation(Quaternion(1.5f * Math::PI, Vector3::ZAXIS));
-      mOvershootImage.SetSize(parentSize.height, imageSize.height, imageSize.depth);
-      relativeOffset = Vector3(0.0f, 1.0f, 0.0f);
-    }
-    mOvershootImage.SetPosition(relativeOffset * parentSize);
+    return;
   }
   }
-}
-
-void ScrollOvershootEffectRipple::OnNegativeOvershootNotification(PropertyNotification& source)
-{
-  Actor delegate = Actor::DownCast(source.GetTarget());
-  float overshoot = delegate.GetProperty<float>(source.GetTargetProperty());
-  bool canScroll = delegate.GetProperty<bool>(mCanScrollPropertyIndex);
-  if(!canScroll)
+  if( (!animatingOn && (mAnimationStateFlags & AnimatingOut)) )
   {
   {
-    mOvershootImage.SetVisible(false);
+    // trying to do what we are already doing
     return;
   }
     return;
   }
-  mOvershootImage.SetVisible(true);
-
-  if (fabsf(overshoot) < Math::MACHINE_EPSILON_1)
+  if( !animatingOn && (mAnimationStateFlags & AnimatingIn) )
   {
   {
-    AnimateScrollOvershoot(0.0f);
+    // dont interrupt while animating on
+    mAnimationStateFlags |= AnimateBack;
     return;
   }
     return;
   }
-
-  if(overshoot < 0.0f)
+  // When we need to animate overshoot to 0
+  if( mOvershootAnimationDuration > Math::MACHINE_EPSILON_1 )
   {
   {
-    const Vector3 imageSize = mOvershootImage.GetCurrentSize();
-    Vector3 relativeOffset = Vector3::ZERO;
-    const Vector3 parentSize = delegate.GetCurrentSize();
-    AnimateScrollOvershoot(-1.0f);
-    if(IsVertical())
-    {
-      mOvershootImage.SetRotation(Quaternion(Math::PI, Vector3::ZAXIS));
-      mOvershootImage.SetSize(parentSize.width, imageSize.height, imageSize.depth);
-      relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
-    }
-    else
+    // setup the new overshoot to 0 animation
+    float currentOvershoot = fabsf( mRippleEffect.GetProperty<float>( mEffectOvershootProperty ) );
+    float duration = mOvershootAnimationDuration * (animatingOn ? (1.0f - currentOvershoot) : currentOvershoot);
+
+    if( duration > Math::MACHINE_EPSILON_0 )
     {
     {
-      mOvershootImage.SetRotation(Quaternion(0.5f * Math::PI, Vector3::ZAXIS));
-      mOvershootImage.SetSize(parentSize.height, imageSize.height, imageSize.depth);
-      relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
+      if(mScrollOvershootAnimation)
+      {
+        mScrollOvershootAnimation.FinishedSignal().Disconnect( this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished );
+        mScrollOvershootAnimation.Stop();
+        mScrollOvershootAnimation.Clear();
+        mScrollOvershootAnimation = NULL;
+      }
+      mScrollOvershootAnimation = Animation::New(duration);
+      mScrollOvershootAnimation.FinishedSignal().Connect( this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished );
+      mScrollOvershootAnimation.AnimateTo( Property(mRippleEffect, mEffectOvershootProperty), amount, TimePeriod(0.0f, duration) );
+      mScrollOvershootAnimation.Play();
+      mAnimationStateFlags = animatingOn ? AnimatingIn : AnimatingOut;
     }
     }
-    mOvershootImage.SetPosition(relativeOffset * parentSize);
   }
   }
-}
-
-void ScrollOvershootEffectRipple::AnimateScrollOvershoot(float overshootAmount)
-{
-  bool animatingOn = fabsf(overshootAmount) > Math::MACHINE_EPSILON_1;
-
-  // make sure we animate back if needed
-  mAnimateOvershootOff = (!animatingOn && mAnimatingOvershootOn);
-
-  int overShootProperty = mRippleEffect.GetPropertyIndex(mRippleEffect.GetProgressRatePropertyName());
-  float currentOvershoot = mRippleEffect.GetProperty<float>(overShootProperty);
-  if(((currentOvershoot < 0.0f && overshootAmount > 0.0f)
-      || (currentOvershoot > 0.0f && overshootAmount < 0.0f)))
+  else
   {
   {
-    // cancel current animation
-    mAnimatingOvershootOn = false;
-    // reset currentOvershoot to 0.0f
-    mRippleEffect.SetProperty(overShootProperty, 0.0f);
-    currentOvershoot = 0.0f;
+    mRippleEffect.SetProgressRate(amount);
   }
   }
-  if( mAnimatingOvershootOn )
-  {
-    // animating on in same direction, do not allow animate off
-    return;
-  }
-  float duration = DEFAULT_OVERSHOOT_ANIMATION_DURATION * (animatingOn ? (1.0f - fabsf(currentOvershoot)) : fabsf(currentOvershoot));
-
-  if(mScrollOvershootAnimation)
+  if( absAmount > Math::MACHINE_EPSILON_1 )
   {
   {
-    mScrollOvershootAnimation.Clear();
-    mScrollOvershootAnimation.Reset();
+    UpdateVisibility(true);
   }
   }
-  mScrollOvershootAnimation = Animation::New(duration);
-  mScrollOvershootAnimation.FinishedSignal().Connect(this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished);
-  mScrollOvershootAnimation.AnimateTo( Property(mRippleEffect, overShootProperty), overshootAmount, TimePeriod(0.0f, duration) );
-  mScrollOvershootAnimation.Play();
-
-  mOvershootImage.SetVisible(true);
-
-  mAnimatingOvershootOn = animatingOn;
 }
 
 void ScrollOvershootEffectRipple::OnOvershootAnimFinished(Animation& animation)
 {
 }
 
 void ScrollOvershootEffectRipple::OnOvershootAnimFinished(Animation& animation)
 {
-  if(!mAnimatingOvershootOn && !mAnimateOvershootOff)
+  bool animateOff = false;
+  if( mAnimationStateFlags & AnimatingOut )
   {
   {
-    // just finished animating overshoot to 0
+    // should now be offscreen
     mOvershootImage.SetVisible(false);
   }
     mOvershootImage.SetVisible(false);
   }
-  mAnimatingOvershootOn = false;
-  mScrollOvershootAnimation.FinishedSignal().Disconnect(this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished);
+  if( (mAnimationStateFlags & AnimateBack) )
+  {
+    animateOff = true;
+  }
+  mScrollOvershootAnimation.FinishedSignal().Disconnect( this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished );
+  mScrollOvershootAnimation.Stop();
   mScrollOvershootAnimation.Clear();
   mScrollOvershootAnimation.Clear();
-  mScrollOvershootAnimation.Reset();
-  if(mAnimateOvershootOff)
+  mScrollOvershootAnimation = NULL;
+  mAnimationStateFlags = 0;
+  if( animateOff )
   {
   {
-    AnimateScrollOvershoot(0.0f);
+    SetOvershoot(0.0f, true);
   }
 }
 
   }
 }
 
-ScrollOvershootEffectRipplePtr ScrollOvershootEffectRipple::New(bool vertical)
+ScrollOvershootEffectRipplePtr ScrollOvershootEffectRipple::New( bool vertical, Scrollable& scrollable )
 {
 {
-  return new ScrollOvershootEffectRipple(vertical);
+  return new ScrollOvershootEffectRipple(vertical, scrollable);
 }
 
 } // namespace Internal
 }
 
 } // namespace Internal
index 04536c5..ce0b8d5 100644 (file)
@@ -43,10 +43,8 @@ public:
 
   /**
    * ScrollOvershootIndicator constructor.
 
   /**
    * ScrollOvershootIndicator constructor.
-   * @param[in] scrollView reference to ScrollView implementation
-   * or horizontally (false)
    */
    */
-  ScrollOvershootIndicator( Scrollable& scrollable);
+  ScrollOvershootIndicator();
 
   /**
    * Virtual destructor
 
   /**
    * Virtual destructor
@@ -54,10 +52,18 @@ public:
   virtual ~ScrollOvershootIndicator();
 
   /**
   virtual ~ScrollOvershootIndicator();
 
   /**
-   * Enables and disables the indicator
-   * &param[in] enable true to enable, false to disable
+   * Attaches the scroll indicator to a scrollable actor
+   *
+   * &param[in] scrollable The scrollable actor to attach to
    */
    */
-  void Enable(bool enable);
+  void AttachToScrollable(Scrollable& scrollable);
+
+  /**
+   * Detaches the scroll indicator from a scrollable actor
+   *
+   * &param[in] scrollable The scrollable actor to detach from
+   */
+  void DetachFromScrollable(Scrollable& scrollable);
 
   /**
    * Resets the indicator
 
   /**
    * Resets the indicator
@@ -66,14 +72,12 @@ public:
 
   /**
    * Create an initialized ScrollOvershootIndicator
 
   /**
    * Create an initialized ScrollOvershootIndicator
-   * @param[in] scrollView reference to ScrollView implementation
-   * or horizontally (false)
+   *
    * @return A pointer to the created ScrollOvershootIndicator.
    */
    * @return A pointer to the created ScrollOvershootIndicator.
    */
-  static ScrollOvershootIndicator* New( Scrollable& scrollable);
+  static ScrollOvershootIndicator* New();
 
 private:
 
 private:
-  Scrollable& mScrollable;                                ///< Internal::Scrollable object
   ScrollOvershootEffectPtr mEffectX;                      ///< effect used for x-axis/horizontal display
   ScrollOvershootEffectPtr mEffectY;                      ///< effect used for y-axis/vertical display
 };
   ScrollOvershootEffectPtr mEffectX;                      ///< effect used for x-axis/horizontal display
   ScrollOvershootEffectPtr mEffectY;                      ///< effect used for y-axis/vertical display
 };
@@ -90,7 +94,7 @@ public:
    *
    * @param[in] vertical whether this effect is a vertical or horizontal one
    */
    *
    * @param[in] vertical whether this effect is a vertical or horizontal one
    */
-  ScrollOvershootEffect(bool vertical);
+  ScrollOvershootEffect( bool vertical );
 
   /**
    * Virtual destructor
 
   /**
    * Virtual destructor
@@ -102,21 +106,21 @@ public:
    *
    * @return true or false
    */
    *
    * @return true or false
    */
-  inline bool IsVertical() { return mVertical; }
+  bool IsVertical() const;
 
   /**
    * Applies the indicator effect, all derived effects must implement this function
    *
    * @param[in] scrollable the scrollable object to apply this effect to
    */
 
   /**
    * Applies the indicator effect, all derived effects must implement this function
    *
    * @param[in] scrollable the scrollable object to apply this effect to
    */
-  virtual void Apply(Scrollable& scrollable) = 0;
+  virtual void Apply() = 0;
 
   /**
    * Removes the indicator effect, all derived effects must implement this function
    *
    * @param[in] scrollable the scrollable object to remove this effect from
    */
 
   /**
    * Removes the indicator effect, all derived effects must implement this function
    *
    * @param[in] scrollable the scrollable object to remove this effect from
    */
-  virtual void Remove(Scrollable& scrollable) = 0;
+  virtual void Remove( Scrollable& scrollable ) = 0;
 
   /**
    * Resets this overshoot effect
 
   /**
    * Resets this overshoot effect
@@ -124,18 +128,9 @@ public:
   virtual void Reset() = 0;
 
   /**
   virtual void Reset() = 0;
 
   /**
-   * Updates the constraints used for the overshoot effect
-   *
-   * @param[in] scrollable the container for the overshoot effect
-   */
-  virtual void UpdateConstraints(Actor& scrollable) {}
-
-  /**
    * Sets up property notifications for overshoot values
    * Sets up property notifications for overshoot values
-   *
-   * @param[in] scrollable the container for the overshoot effect
    */
    */
-  virtual void SetPropertyNotifications(Actor& scrollable) {}
+  virtual void UpdatePropertyNotifications() {}
 
 private:
   bool mVertical;                      ///< whether this is a vertical/horizontal effect
 
 private:
   bool mVertical;                      ///< whether this is a vertical/horizontal effect
@@ -147,21 +142,31 @@ private:
  */
 struct ScrollOvershootEffectRipple : public ScrollOvershootEffect, public ConnectionTracker
 {
  */
 struct ScrollOvershootEffectRipple : public ScrollOvershootEffect, public ConnectionTracker
 {
+  enum AnimationState
+  {
+    AnimatingIn  = 0x01,  ///< animating overshoot to 0
+    AnimatingOut = 0x02,  ///< animating overshoot to negative (overshoot image displays in +ve area of screen)
+    AnimateBack  = 0x04,  ///< indicates that we need to animate overshoot back to zero immediately after it has finished animating in
+  };
+
 public:
 public:
+
   /**
    * Create a new gradient overshoot effect, passing in whether it is vertical or horizontal
   /**
    * Create a new gradient overshoot effect, passing in whether it is vertical or horizontal
+   *
+   * @param[in] vertical Whether this indicator is vertical or horizontal
    */
    */
-  ScrollOvershootEffectRipple(bool vertical);
+  ScrollOvershootEffectRipple( bool vertical, Scrollable& scrollable );
 
   /**
    * @copydoc ScrollOvershootEffect::Apply
    */
 
   /**
    * @copydoc ScrollOvershootEffect::Apply
    */
-  virtual void Apply(Scrollable& scrollable);
+  virtual void Apply();
 
   /**
    * @copydoc ScrollOvershootEffect::Remove
    */
 
   /**
    * @copydoc ScrollOvershootEffect::Remove
    */
-  virtual void Remove(Scrollable& scrollable);
+  virtual void Remove( Scrollable& scrollable );
 
   /**
    * @copydoc ScrollOvershootEffect::Reset
 
   /**
    * @copydoc ScrollOvershootEffect::Reset
@@ -169,46 +174,33 @@ public:
   virtual void Reset();
 
   /**
   virtual void Reset();
 
   /**
-   * @copydoc ScrollOvershootEffect::UpdateConstraints(Actor& scrollable)
-   */
-  virtual void UpdateConstraints(Actor& scrollable);
-
-  /**
-   * @copydoc ScrollOvershootEffect::SetPropertyNotification
-   */
-  virtual void SetPropertyNotifications(Actor& scrollable);
-
-  /**
-   * Constrains the size of the gradient image
-   * @param[in] current current position of the image actor
-   * @param[in] parentSizeProperty size of the scrollable area so we can position image on the edge of it
-   * @param[in] overshootProperty current overshoot amount for this indicator's axis
-   * @return new position of the gradient image actor
+   * @copydoc ScrollOvershootEffect::UpdatePropertyNotifications
    */
    */
-  Vector3 PositionConstraint(const Vector3& current, const PropertyInput& parentSizeProperty, const PropertyInput& overshootProperty);
+  void UpdatePropertyNotifications();
 
   /**
 
   /**
-   * Informs overshoot effect to update image position and to animate effect overshoot value for a
-   * positive overshoot value from scrollview
+   * Updates the vibility of the overshoot image as well as updating its size, position and rotation
+   * This function is called when animation starts and finishes
    *
    *
-   * @param[in] source the property notification that triggered this callback
+   * @param[in] visible Whether to set the image visible or not
    */
    */
-  void OnPositiveOvershootNotification(PropertyNotification& source);
+  void UpdateVisibility( bool visible );
 
   /**
    * Informs overshoot effect to update image position and to animate effect overshoot value for a
 
   /**
    * Informs overshoot effect to update image position and to animate effect overshoot value for a
-   * negative overshoot value from scrollview
+   * positive overshoot value from scrollview
    *
    * @param[in] source the property notification that triggered this callback
    */
    *
    * @param[in] source the property notification that triggered this callback
    */
-  void OnNegativeOvershootNotification(PropertyNotification& source);
+  void OnOvershootNotification(PropertyNotification& source);
 
   /**
 
   /**
-   * Function to animate effect overshoot value either to -1.0f/1.0f or 0.0f
+   * Sets shader overshoot value, either immediately of by animating over time
    *
    *
-   * @param[in] overshootAmount the amount to animate overshoot to [-1.0f,0.0f,1.0f]
+   * @param[in] amount The amount to set overshoot to [-1.0f,1.0f]
+   * @param[in] animate Whether to animate or set immediately
    */
    */
-  void AnimateScrollOvershoot(float overshootAmount);
+  void SetOvershoot(float amount, bool animate = true);
 
   /**
    * Connects to the animation finished signal of our overshoot animation
 
   /**
    * Connects to the animation finished signal of our overshoot animation
@@ -219,24 +211,26 @@ public:
 
   /**
    * Creates a new ScrollOvershootEffectGradient objects and returns a pointer to it
 
   /**
    * Creates a new ScrollOvershootEffectGradient objects and returns a pointer to it
+   *
    * @param[in] vertical whether to create a vertical(true) or horizontal effect
    * @return a pointer to the new effect
    */
    * @param[in] vertical whether to create a vertical(true) or horizontal effect
    * @return a pointer to the new effect
    */
-  static ScrollOvershootEffectRipplePtr New( bool vertical );
+  static ScrollOvershootEffectRipplePtr New( bool vertical, Scrollable& scrollable );
 
 private:
 
 private:
-
-  float mMaxOvershootImageSize;            ///< maximum size of the image when overshoot value is 1.0f
-  ImageActor mOvershootImage;              ///< the overshoot image...
-  Animation mScrollOvershootAnimation;     ///< animation
-  bool      mAnimatingOvershootOn;         ///< whether we are currently animating overshoot to 1.0f/-1.0f (on) or to 0.0f (off)
-  bool      mAnimateOvershootOff;          ///< whether we are currently animating overshoot to 1.0f/-1.0f (on) or to 0.0f (off)
-  int       mCanScrollPropertyIndex;       ///< property index to a property that informs indicator if it is needed
-  BouncingEffect mRippleEffect;                 // the ripple vertex/fragment shader effect
-  PropertyNotification mOvershootPositiveNotification; // stores the property notification used for positive overshoot values
-  PropertyNotification mOvershootNegativeNotification; // stores the property notification used for negative overshoot values
-  ActiveConstraint    mSizeConstraint;                 // active constraint handle used to store the image width constraint
-  ActiveConstraint    mPositionConstraint;             // active constraint handle used to store the image position constraint
+  ImageActor            mOvershootImage;               ///< the overshoot image...
+  Scrollable&           mAttachedScrollView;           ///< the actor that this indicator has been attached to
+  BouncingEffect        mRippleEffect;                 ///< the ripple vertex/fragment shader effect
+  Animation             mScrollOvershootAnimation;     ///< overshoot animation
+  PropertyNotification  mOvershootIncreaseNotification;///< notification used to inform as overshoot increases
+  PropertyNotification  mOvershootDecreaseNotification;///< notification used to inform as overshoot decreases
+  Property::Index       mCanScrollPropertyIndex;       ///< property index to a property that informs indicator if it is needed
+  Property::Index       mOvershootProperty;            ///< index of the overshoot property in the scrollable actor
+  Property::Index       mEffectOvershootProperty;      ///< index of the effect's overshoot property
+  float                 mMaxOvershootImageSize;        ///< maximum height of the image when overshoot value is 1.0f
+  float                 mOvershootAnimationDuration;   ///< time taken for overshoot to go from fully offscreen to fully onscreen and vice versa
+  float                 mOvershoot;                    ///< last overshoot value as detected by notifications
+  unsigned short        mAnimationStateFlags;          ///< contains flags indicating the current state of the overshoot animation
 };
 
 } // namespace Internal
 };
 
 } // namespace Internal
index 2c5b61b..2779369 100644 (file)
@@ -23,6 +23,7 @@
 #include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.h>
 #include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-effect-impl.h>
 #include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-overshoot-indicator-impl.h>
 #include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.h>
 #include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-effect-impl.h>
 #include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-overshoot-indicator-impl.h>
+#include <dali/integration-api/debug.h>
 
 // TODO: Change to two class system:
 // 1. DraggableActor (is an actor which can be dragged anywhere/scaled/rotated, can be set to range using the ruler)
 
 // TODO: Change to two class system:
 // 1. DraggableActor (is an actor which can be dragged anywhere/scaled/rotated, can be set to range using the ruler)
@@ -45,8 +46,10 @@ 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 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 );                   ///< 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).
+const unsigned long MINIMUM_TIME_BETWEEN_DOWN_AND_UP_FOR_RESET( 150u );
+const float DEFAULT_OVERSHOOT_ANIMATION_DURATION = 0.35f;  // time in seconds
+const Vector2 OVERSCROLL_CLAMP(1.0f, 1.0f);                // maximum overscroll allowed in pixels when overshoot indicator is being used
+const float TOUCH_DOWN_TIMER_INTERVAL = 100.0f;
 
 // predefined effect values
 const Vector3 ANGLE_CAROUSEL_ROTATE(Math::PI * 0.5f, Math::PI * 0.5f, 0.0f);
 
 // predefined effect values
 const Vector3 ANGLE_CAROUSEL_ROTATE(Math::PI * 0.5f, Math::PI * 0.5f, 0.0f);
@@ -188,11 +191,14 @@ Vector3 InternalRelativePositionConstraint(const Vector3&    current,
                                            const PropertyInput& scrollMaxProperty,
                                            const PropertyInput& scrollSizeProperty)
 {
                                            const PropertyInput& scrollMaxProperty,
                                            const PropertyInput& scrollSizeProperty)
 {
-  const Vector3& position = -scrollPositionProperty.GetVector3();
+  Vector3 position = -scrollPositionProperty.GetVector3();
   const Vector3& min = scrollMinProperty.GetVector3();
   const Vector3& max = scrollMaxProperty.GetVector3();
   const Vector3& size = scrollSizeProperty.GetVector3();
 
   const Vector3& min = scrollMinProperty.GetVector3();
   const Vector3& max = scrollMaxProperty.GetVector3();
   const Vector3& size = scrollSizeProperty.GetVector3();
 
+  position.x = WrapInDomain(position.x, min.x, max.x);
+  position.y = WrapInDomain(position.y, min.y, max.y);
+
   Vector3 relativePosition;
   Vector3 domainSize = (max - min) - size;
 
   Vector3 relativePosition;
   Vector3 domainSize = (max - min) - size;
 
@@ -217,6 +223,40 @@ namespace
 {
 
 /**
 {
 
 /**
+ * Returns whether to lock scrolling to a particular axis
+ *
+ * @param[in] panDelta Distance panned since gesture started
+ * @param[in] currentLockAxis The current lock axis value
+ * @param[in] lockGradient How quickly to lock to a particular axis
+ *
+ * @return The new axis lock state
+ */
+ScrollView::LockAxis GetLockAxis(const Vector2& panDelta, ScrollView::LockAxis currentLockAxis, float lockGradient)
+{
+  if(panDelta.LengthSquared() > AUTOLOCK_AXIS_MINIMUM_DISTANCE2 &&
+      currentLockAxis == ScrollView::LockPossible)
+  {
+    float dx = fabsf(panDelta.x);
+    float dy = fabsf(panDelta.y);
+    if(dx * lockGradient >= dy)
+    {
+      // 0.36:1 gradient to the horizontal (deviate < 20 degrees)
+      currentLockAxis = ScrollView::LockVertical;
+    }
+    else if(dy * lockGradient > dx)
+    {
+      // 0.36:1 gradient to the vertical (deviate < 20 degrees)
+      currentLockAxis = ScrollView::LockHorizontal;
+    }
+    else
+    {
+      currentLockAxis = ScrollView::LockNone;
+    }
+  }
+  return currentLockAxis;
+}
+
+/**
  * Internal Pre-Position Property Constraint.
  *
  * Generates position property based on current position + gesture displacement.
  * Internal Pre-Position Property Constraint.
  *
  * Generates position property based on current position + gesture displacement.
@@ -228,101 +268,108 @@ struct InternalPrePositionConstraint
 {
   InternalPrePositionConstraint(const Vector2& initialPanMask,
                                 bool axisAutoLock,
 {
   InternalPrePositionConstraint(const Vector2& initialPanMask,
                                 bool axisAutoLock,
-                                float axisAutoLockGradient)
+                                float axisAutoLockGradient,
+                                ScrollView::LockAxis initialLockAxis,
+                                const Vector2& maxOvershoot,
+                                const RulerDomain& domainX, const RulerDomain& domainY)
   : mInitialPanMask(initialPanMask),
   : mInitialPanMask(initialPanMask),
-    mAxisAutoLock(axisAutoLock),
-    mLockAxis(ScrollView::LockPossible),
+    mDomainMin( -domainX.min, -domainY.min ),
+    mDomainMax( -domainX.max, -domainY.max ),
+    mMaxOvershoot(maxOvershoot),
     mAxisAutoLockGradient(axisAutoLockGradient),
     mAxisAutoLockGradient(axisAutoLockGradient),
-    mPrePosition(Vector3::ZERO),
-    mWasPanning(false)
+    mLockAxis(initialLockAxis),
+    mAxisAutoLock(axisAutoLock),
+    mWasPanning(false),
+    mClampX( domainX.enabled ),
+    mClampY( domainY.enabled )
   {
   }
 
   Vector3 operator()(const Vector3&    current,
                      const PropertyInput& gesturePositionProperty,
                      const PropertyInput& gestureDisplacementProperty,
   {
   }
 
   Vector3 operator()(const Vector3&    current,
                      const PropertyInput& gesturePositionProperty,
                      const PropertyInput& gestureDisplacementProperty,
-                     const PropertyInput& scrollPositionXProperty,
-                     const PropertyInput& scrollPositionYProperty,
-                     const PropertyInput& panningProperty)
+                     const PropertyInput& sizeProperty)
   {
   {
-    const bool panning = panningProperty.GetBoolean();
-    Vector3 scrollPostPosition;
+    Vector3 scrollPostPosition = current;
+    Vector2 panPosition = gesturePositionProperty.GetVector2();
 
 
-    if(panning)
+    if(!mWasPanning)
     {
     {
-      // Check if panning has just started...
-      if(!mWasPanning)
-      {
-        mLocalStart = gesturePositionProperty.GetVector2() - gestureDisplacementProperty.GetVector2();
-        mPrePosition = current;
-        mLockAxis = ScrollView::LockPossible;
+      mLocalStart = gesturePositionProperty.GetVector2() - gestureDisplacementProperty.GetVector2();
+      mPrePosition = current;
+      mCurrentPanMask = mInitialPanMask;
+      mWasPanning = true;
+    }
 
 
-        mCurrentPanMask = mInitialPanMask;
-      }
+    // Calculate Deltas...
+    Vector2 currentPosition = gesturePositionProperty.GetVector2();
+    Vector2 panDelta( currentPosition - mLocalStart );
 
 
-      // Calculate Deltas...
-      Vector2 currentPosition = gesturePositionProperty.GetVector2();
-      Vector2 panDelta( currentPosition - mLocalStart );
+    // Axis Auto Lock - locks the panning to the horizontal or vertical axis if the pan
+    // appears mostly horizontal or mostly vertical respectively...
+    if( mAxisAutoLock )
+    {
+      mLockAxis = GetLockAxis(panDelta, mLockAxis, mAxisAutoLockGradient);
+      if( mLockAxis == ScrollView::LockVertical )
+      {
+        mCurrentPanMask.y = 0.0f;
+      }
+      else if( mLockAxis == ScrollView::LockHorizontal )
+      {
+        mCurrentPanMask.x = 0.0f;
+      }
+    }
 
 
-      // Axis Auto Lock - locks the panning to the horizontal or vertical axis if the pan
-      // appears mostly horizontal or mostly vertical respectively...
-      AxisAutoLock(panDelta);
+    // Restrict deltas based on ruler enable/disable and axis-lock state...
+    panDelta *= mCurrentPanMask;
 
 
-      // Restrict deltas based on ruler enable/disable and axis-lock state...
-      panDelta *= mCurrentPanMask;
+    // Perform Position transform based on input deltas...
+    scrollPostPosition = mPrePosition;
+    scrollPostPosition.GetVectorXY() += panDelta;
 
 
-      // Perform Position transform based on input deltas...
-      scrollPostPosition = mPrePosition;
-      scrollPostPosition.GetVectorXY() += panDelta;
-    }
-    else
+    // if no wrapping then clamp preposition to maximum overshoot amount
+    const Vector3& size = sizeProperty.GetVector3();
+    if( mClampX )
     {
     {
-      scrollPostPosition.x = scrollPositionXProperty.GetFloat();
-      scrollPostPosition.y = scrollPositionYProperty.GetFloat();
+      float newXPosition = Clamp(scrollPostPosition.x, (mDomainMax.x + size.x) - mMaxOvershoot.x, mDomainMin.x + mMaxOvershoot.x );
+      if( (newXPosition < scrollPostPosition.x - Math::MACHINE_EPSILON_1)
+              || (newXPosition > scrollPostPosition.x + Math::MACHINE_EPSILON_1) )
+      {
+        mPrePosition.x = newXPosition;
+        mLocalStart.x = panPosition.x;
+      }
+      scrollPostPosition.x = newXPosition;
     }
     }
-
-    mWasPanning = panning;
-    return scrollPostPosition;
-  }
-
-  void AxisAutoLock(Vector2& panDelta)
-  {
-    if(mAxisAutoLock)
+    if( mClampY )
     {
     {
-      if(panDelta.LengthSquared() > AUTOLOCK_AXIS_MINIMUM_DISTANCE2 &&
-          mLockAxis == ScrollView::LockPossible)
+      float newYPosition = Clamp(scrollPostPosition.y, (mDomainMax.y + size.y) - mMaxOvershoot.y, mDomainMin.y + mMaxOvershoot.y );
+      if( (newYPosition < scrollPostPosition.y - Math::MACHINE_EPSILON_1)
+              || (newYPosition > scrollPostPosition.y + Math::MACHINE_EPSILON_1) )
       {
       {
-        float dx = fabsf(panDelta.x);
-        float dy = fabsf(panDelta.y);
-        if(dx * mAxisAutoLockGradient >= dy)
-        {
-          // 0.36:1 gradient to the horizontal (deviate < 20 degrees)
-          mLockAxis = ScrollView::LockVertical;
-          mCurrentPanMask.y = 0.0f;
-        }
-        else if(dy * mAxisAutoLockGradient > dx)
-        {
-          // 0.36:1 gradient to the vertical (deviate < 20 degrees)
-          mLockAxis = ScrollView::LockHorizontal;
-          mCurrentPanMask.x = 0.0f;
-        }
-        else
-        {
-          mLockAxis = ScrollView::LockNone;
-        }
+        mPrePosition.y = newYPosition;
+        mLocalStart.y = panPosition.y;
       }
       }
-    } // end if mAxisAutoLock
+      scrollPostPosition.y = newYPosition;
+    }
+
+    return scrollPostPosition;
   }
 
   }
 
+  Vector3 mPrePosition;
   Vector2 mLocalStart;
   Vector2 mInitialPanMask;              ///< Initial pan mask (based on ruler settings)
   Vector2 mCurrentPanMask;              ///< Current pan mask that can be altered by axis lock mode.
   Vector2 mLocalStart;
   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;
+  Vector2 mMaxOvershoot;
 
 
-  bool mAxisAutoLock;                   ///< Set by ScrollView
-  ScrollView::LockAxis mLockAxis;
   float mAxisAutoLockGradient;          ///< Set by ScrollView
   float mAxisAutoLockGradient;          ///< Set by ScrollView
-  Vector3 mPrePosition;
-  bool mWasPanning;
+  ScrollView::LockAxis mLockAxis;
+
+  bool mAxisAutoLock:1;                   ///< Set by ScrollView
+  bool mWasPanning:1;
+  bool mClampX:1;
+  bool mClampY:1;
 };
 
 /**
 };
 
 /**
@@ -334,23 +381,37 @@ struct InternalPrePositionConstraint
  */
 struct InternalPositionConstraint
 {
  */
 struct InternalPositionConstraint
 {
-  InternalPositionConstraint(const RulerDomain& domainX, const RulerDomain& domainY)
+  InternalPositionConstraint(const RulerDomain& domainX, const RulerDomain& domainY, bool wrap)
   : mDomainMin( -domainX.min, -domainY.min ),
     mDomainMax( -domainX.max, -domainY.max ),
     mClampX( domainX.enabled ),
   : mDomainMin( -domainX.min, -domainY.min ),
     mDomainMax( -domainX.max, -domainY.max ),
     mClampX( domainX.enabled ),
-    mClampY( domainY.enabled )
+    mClampY( domainY.enabled ),
+    mWrap( wrap )
   {
   }
 
   Vector3 operator()(const Vector3&    current,
                      const PropertyInput& scrollPositionProperty,
   {
   }
 
   Vector3 operator()(const Vector3&    current,
                      const PropertyInput& scrollPositionProperty,
+                     const PropertyInput& scrollMinProperty,
+                     const PropertyInput& scrollMaxProperty,
                      const PropertyInput& scrollSizeProperty)
   {
     Vector3 position = scrollPositionProperty.GetVector3();
     const Vector2& size = scrollSizeProperty.GetVector3().GetVectorXY();
                      const PropertyInput& scrollSizeProperty)
   {
     Vector3 position = scrollPositionProperty.GetVector3();
     const Vector2& size = scrollSizeProperty.GetVector3().GetVectorXY();
+    const Vector3& min = scrollMinProperty.GetVector3();
+    const Vector3& max = scrollMaxProperty.GetVector3();
 
 
-    position.x = mClampX ? Clamp(position.x, mDomainMax.x + size.x, mDomainMin.x ) : position.x;
-    position.y = mClampY ? Clamp(position.y, mDomainMax.y + size.y, mDomainMin.y ) : position.y;
+    if( mWrap )
+    {
+      position.x = -WrapInDomain(-position.x, min.x, max.x);
+      position.y = -WrapInDomain(-position.y, min.y, max.y);
+    }
+    else
+    {
+      // clamp post position to domain
+      position.x = mClampX ? Clamp(position.x, mDomainMax.x + size.x, mDomainMin.x ) : position.x;
+      position.y = mClampY ? Clamp(position.y, mDomainMax.y + size.y, mDomainMin.y ) : position.y;
+    }
 
     return position;
   }
 
     return position;
   }
@@ -359,6 +420,7 @@ struct InternalPositionConstraint
   Vector2 mDomainMax;
   bool mClampX;
   bool mClampY;
   Vector2 mDomainMax;
   bool mClampX;
   bool mClampY;
+  bool mWrap;
 
 };
 
 
 };
 
@@ -368,19 +430,18 @@ struct InternalPositionConstraint
  */
 struct OvershootXConstraint
 {
  */
 struct OvershootXConstraint
 {
-  OvershootXConstraint(float maxOvershoot) : mLastOvershoot(0.0f), mMaxOvershoot(maxOvershoot) {}
+  OvershootXConstraint(float maxOvershoot) : mMaxOvershoot(maxOvershoot) {}
 
   float operator()(const float&    current,
       const PropertyInput& scrollPrePositionProperty,
       const PropertyInput& scrollPostPositionProperty)
   {
 
   float operator()(const float&    current,
       const PropertyInput& scrollPrePositionProperty,
       const PropertyInput& scrollPostPositionProperty)
   {
-    Vector3 scrollPrePosition = scrollPrePositionProperty.GetVector3();
-    Vector3 scrollPostPosition = scrollPostPositionProperty.GetVector3();
+    const Vector3& scrollPrePosition = scrollPrePositionProperty.GetVector3();
+    const Vector3& scrollPostPosition = scrollPostPositionProperty.GetVector3();
     float newOvershoot = scrollPrePosition.x - scrollPostPosition.x;
     return (newOvershoot > 0.0f ? std::min(newOvershoot, mMaxOvershoot) : std::max(newOvershoot, -mMaxOvershoot)) / mMaxOvershoot;
   }
 
     float newOvershoot = scrollPrePosition.x - scrollPostPosition.x;
     return (newOvershoot > 0.0f ? std::min(newOvershoot, mMaxOvershoot) : std::max(newOvershoot, -mMaxOvershoot)) / mMaxOvershoot;
   }
 
-  float mLastOvershoot;
   float mMaxOvershoot;
 };
 
   float mMaxOvershoot;
 };
 
@@ -390,19 +451,18 @@ struct OvershootXConstraint
  */
 struct OvershootYConstraint
 {
  */
 struct OvershootYConstraint
 {
-  OvershootYConstraint(float maxOvershoot) : mLastOvershoot(0.0f), mMaxOvershoot(maxOvershoot) {}
+  OvershootYConstraint(float maxOvershoot) : mMaxOvershoot(maxOvershoot) {}
 
   float operator()(const float&    current,
       const PropertyInput& scrollPrePositionProperty,
       const PropertyInput& scrollPostPositionProperty)
   {
 
   float operator()(const float&    current,
       const PropertyInput& scrollPrePositionProperty,
       const PropertyInput& scrollPostPositionProperty)
   {
-    Vector3 scrollPrePosition = scrollPrePositionProperty.GetVector3();
-    Vector3 scrollPostPosition = scrollPostPositionProperty.GetVector3();
+    const Vector3& scrollPrePosition = scrollPrePositionProperty.GetVector3();
+    const Vector3& scrollPostPosition = scrollPostPositionProperty.GetVector3();
     float newOvershoot = scrollPrePosition.y - scrollPostPosition.y;
     return (newOvershoot > 0.0f ? std::min(newOvershoot, mMaxOvershoot) : std::max(newOvershoot, -mMaxOvershoot)) / mMaxOvershoot;
   }
 
     float newOvershoot = scrollPrePosition.y - scrollPostPosition.y;
     return (newOvershoot > 0.0f ? std::min(newOvershoot, mMaxOvershoot) : std::max(newOvershoot, -mMaxOvershoot)) / mMaxOvershoot;
   }
 
-  float mLastOvershoot;
   float mMaxOvershoot;
 };
 
   float mMaxOvershoot;
 };
 
@@ -411,8 +471,7 @@ struct OvershootYConstraint
  * it has no effect on the X property.
  */
 float InternalXConstraint(const float&    current,
  * it has no effect on the X property.
  */
 float InternalXConstraint(const float&    current,
-                          const PropertyInput& scrollPosition,
-                          const PropertyInput& panningProperty)
+                          const PropertyInput& scrollPosition)
 {
   return scrollPosition.GetVector3().x;
 }
 {
   return scrollPosition.GetVector3().x;
 }
@@ -422,8 +481,7 @@ float InternalXConstraint(const float&    current,
  * it has no effect on the Y property.
  */
 float InternalYConstraint(const float&    current,
  * it has no effect on the Y property.
  */
 float InternalYConstraint(const float&    current,
-                          const PropertyInput& scrollPosition,
-                          const PropertyInput& panningProperty)
+                          const PropertyInput& scrollPosition)
 {
   return scrollPosition.GetVector3().y;
 }
 {
   return scrollPosition.GetVector3().y;
 }
@@ -511,28 +569,19 @@ Dali::Toolkit::ScrollView ScrollView::New()
 
 ScrollView::ScrollView()
 : ScrollBase(),
 
 ScrollView::ScrollView()
 : ScrollBase(),
-  mInitialized(false),
-  mScrolling(false),
-  mScrollInterrupted(false),
   mTouchDownTime(0u),
   mTouchDownTime(0u),
-  mSensitive(true),
   mGestureStackDepth(0),
   mRotationDelta(0.0f),
   mGestureStackDepth(0),
   mRotationDelta(0.0f),
+  mScrollStateFlags(0),
   mScrollPreRotation(0.0f),
   mScrollPostRotation(0.0f),
   mScrollPreRotation(0.0f),
   mScrollPostRotation(0.0f),
-  mTouchDownTimeoutReached(false),
-  mActorAutoSnapEnabled(false),
-  mAutoResizeContainerEnabled(false),
-  mWrapMode(false),
-  mAxisAutoLock(false),
   mMinTouchesForPanning(1),
   mMaxTouchesForPanning(1),
   mLockAxis(LockPossible),
   mRefreshIntervalMilliseconds(DEFAULT_REFRESH_INTERVAL_MILLISECONDS),
   mMinTouchesForPanning(1),
   mMaxTouchesForPanning(1),
   mLockAxis(LockPossible),
   mRefreshIntervalMilliseconds(DEFAULT_REFRESH_INTERVAL_MILLISECONDS),
-  mAlterChild(false),
   mOvershootDelay(1.0f),
   mMaxOvershoot(Toolkit::ScrollView::DEFAULT_MAX_OVERSHOOT, Toolkit::ScrollView::DEFAULT_MAX_OVERSHOOT),
   mOvershootDelay(1.0f),
   mMaxOvershoot(Toolkit::ScrollView::DEFAULT_MAX_OVERSHOOT, Toolkit::ScrollView::DEFAULT_MAX_OVERSHOOT),
-  mDefaultMaxOvershoot(true),
+  mUserMaxOvershoot(Toolkit::ScrollView::DEFAULT_MAX_OVERSHOOT, Toolkit::ScrollView::DEFAULT_MAX_OVERSHOOT),
   mSnapOvershootDuration(Toolkit::ScrollView::DEFAULT_SNAP_OVERSHOOT_DURATION),
   mSnapOvershootAlphaFunction(AlphaFunctions::EaseOut),
   mSnapDuration(Toolkit::ScrollView::DEFAULT_SLOW_SNAP_ANIMATION_DURATION),
   mSnapOvershootDuration(Toolkit::ScrollView::DEFAULT_SNAP_OVERSHOOT_DURATION),
   mSnapOvershootAlphaFunction(AlphaFunctions::EaseOut),
   mSnapDuration(Toolkit::ScrollView::DEFAULT_SLOW_SNAP_ANIMATION_DURATION),
@@ -543,7 +592,19 @@ ScrollView::ScrollView()
   mFrictionCoefficient(Toolkit::ScrollView::DEFAULT_FRICTION_COEFFICIENT),
   mFlickSpeedCoefficient(Toolkit::ScrollView::DEFAULT_FLICK_SPEED_COEFFICIENT),
   mMaxFlickSpeed(Toolkit::ScrollView::DEFAULT_MAX_FLICK_SPEED),
   mFrictionCoefficient(Toolkit::ScrollView::DEFAULT_FRICTION_COEFFICIENT),
   mFlickSpeedCoefficient(Toolkit::ScrollView::DEFAULT_FLICK_SPEED_COEFFICIENT),
   mMaxFlickSpeed(Toolkit::ScrollView::DEFAULT_MAX_FLICK_SPEED),
-  mInAccessibilityPan(false)
+  mInAccessibilityPan(false),
+  mInitialized(false),
+  mScrolling(false),
+  mScrollInterrupted(false),
+  mPanning(false),
+  mSensitive(true),
+  mTouchDownTimeoutReached(false),
+  mActorAutoSnapEnabled(false),
+  mAutoResizeContainerEnabled(false),
+  mWrapMode(false),
+  mAxisAutoLock(false),
+  mAlterChild(false),
+  mDefaultMaxOvershoot(true)
 {
   SetRequiresMouseWheelEvents(true);
 }
 {
   SetRequiresMouseWheelEvents(true);
 }
@@ -551,7 +612,6 @@ ScrollView::ScrollView()
 void ScrollView::OnInitialize()
 {
   Actor self = Self();
 void ScrollView::OnInitialize()
 {
   Actor self = Self();
-  self.SetLeaveRequired(true);
 
   // Internal Actor, used to hide actors from enumerations.
   // Also actors added to Internal actor appear as overlays e.g. ScrollBar components.
 
   // Internal Actor, used to hide actors from enumerations.
   // Also actors added to Internal actor appear as overlays e.g. ScrollBar components.
@@ -616,11 +676,6 @@ void ScrollView::OnControlStageConnection()
 
 void ScrollView::OnControlStageDisconnection()
 {
 
 void ScrollView::OnControlStageDisconnection()
 {
-  if ( mSnapOvershootAnimation )
-  {
-    SetOvershootToOrigin();
-  }
-
   StopAnimation();
 }
 
   StopAnimation();
 }
 
@@ -848,52 +903,80 @@ void ScrollView::SetRulerY(RulerPtr ruler)
 
 void ScrollView::UpdatePropertyDomain(const Vector3& size)
 {
 
 void ScrollView::UpdatePropertyDomain(const Vector3& size)
 {
-  Vector3 min;
-  Vector3 max;
+  Actor self = Self();
+  Vector3 min = self.GetProperty<Vector3>(mPropertyPositionMin);
+  Vector3 max = self.GetProperty<Vector3>(mPropertyPositionMax);
+  bool scrollPositionChanged = false;
+  bool domainChanged = false;
 
   bool canScrollVertical = false;
   bool canScrollHorizontal = false;
 
   bool canScrollVertical = false;
   bool canScrollHorizontal = false;
-  Actor self = Self();
+  UpdateLocalScrollProperties();
   if(mRulerX->IsEnabled())
   {
     const Toolkit::RulerDomain& rulerDomain = mRulerX->GetDomain();
   if(mRulerX->IsEnabled())
   {
     const Toolkit::RulerDomain& rulerDomain = mRulerX->GetDomain();
-    min.x = rulerDomain.min;
-    max.x = rulerDomain.max;
-
-    // make sure new scroll value is within new domain
-    float newScroll = min.x;
-    int scrollXPropertyIndex = self.GetPropertyIndex(Toolkit::ScrollView::SCROLL_X_PROPERTY_NAME);
-    if((fabsf(max.x - min.x) - size.x) > Math::MACHINE_EPSILON_1)
+    if( fabsf(min.x - rulerDomain.min) > Math::MACHINE_EPSILON_10000
+        || fabsf(max.x - rulerDomain.max) > Math::MACHINE_EPSILON_10000 )
     {
     {
-      canScrollHorizontal = true;
-      float currentScroll = self.GetProperty<float>(scrollXPropertyIndex);
-      newScroll = Clamp(currentScroll, -(max.x - size.x), -min.x);
+      domainChanged = true;
+      min.x = rulerDomain.min;
+      max.x = rulerDomain.max;
+
+      // make sure new scroll value is within new domain
+      if( mScrollPrePosition.x < min.x
+          || mScrollPrePosition.x > max.x )
+      {
+        scrollPositionChanged = true;
+        mScrollPrePosition.x = Clamp(mScrollPrePosition.x, -(max.x - size.x), -min.x);
+      }
+      if((fabsf(max.x - min.x) - size.x) > Math::MACHINE_EPSILON_10000)
+      {
+        canScrollHorizontal = true;
+      }
     }
     }
-    self.SetProperty(scrollXPropertyIndex, newScroll);
   }
 
   if(mRulerY->IsEnabled())
   {
     const Toolkit::RulerDomain& rulerDomain = mRulerY->GetDomain();
   }
 
   if(mRulerY->IsEnabled())
   {
     const Toolkit::RulerDomain& rulerDomain = mRulerY->GetDomain();
-    min.y = rulerDomain.min;
-    max.y = rulerDomain.max;
-
-    // make sure new scroll value is within new domain
-    float newScroll = min.y;
-    int scrollYPropertyIndex = self.GetPropertyIndex(Toolkit::ScrollView::SCROLL_Y_PROPERTY_NAME);
-    if((fabsf(max.y - min.y) - size.y) > Math::MACHINE_EPSILON_1)
+    if( fabsf(min.y - rulerDomain.min) > Math::MACHINE_EPSILON_10000
+        || fabsf(max.y - rulerDomain.max) > Math::MACHINE_EPSILON_10000 )
     {
     {
-      canScrollVertical = true;
-      float currentScroll = self.GetProperty<float>(scrollYPropertyIndex);
-      newScroll = Clamp(currentScroll, -(max.y - size.y), -min.y);
+      domainChanged = true;
+      min.y = rulerDomain.min;
+      max.y = rulerDomain.max;
+
+      // make sure new scroll value is within new domain
+      if( mScrollPrePosition.y < min.y
+          || mScrollPrePosition.y > max.y )
+      {
+        scrollPositionChanged = true;
+        mScrollPrePosition.y = Clamp(mScrollPrePosition.y, -(max.y - size.y), -min.y);
+      }
+      if((fabsf(max.y - min.y) - size.y) > Math::MACHINE_EPSILON_10000)
+      {
+        canScrollVertical = true;
+      }
     }
     }
-    self.SetProperty(scrollYPropertyIndex, newScroll);
   }
   }
-  self.SetProperty(mPropertyCanScrollVertical, canScrollVertical);
-  self.SetProperty(mPropertyCanScrollHorizontal, canScrollHorizontal);
-
-  self.SetProperty(mPropertyPositionMin, min );
-  self.SetProperty(mPropertyPositionMax, max );
+  // avoid setting properties if possible, otherwise this will cause an entire update as well as triggering constraints using each property we update
+  if( self.GetProperty<bool>(mPropertyCanScrollVertical) != canScrollVertical )
+  {
+    self.SetProperty(mPropertyCanScrollVertical, canScrollVertical);
+  }
+  if( self.GetProperty<bool>(mPropertyCanScrollHorizontal) != canScrollHorizontal )
+  {
+    self.SetProperty(mPropertyCanScrollHorizontal, canScrollVertical);
+  }
+  if( scrollPositionChanged )
+  {
+    self.SetProperty(mPropertyPrePosition, mScrollPrePosition);
+  }
+  if( domainChanged )
+  {
+    self.SetProperty(mPropertyPositionMin, min );
+    self.SetProperty(mPropertyPositionMax, max );
+  }
 }
 
 void ScrollView::SetRulerScaleX(RulerPtr ruler)
 }
 
 void ScrollView::SetRulerScaleX(RulerPtr ruler)
@@ -945,6 +1028,7 @@ void ScrollView::SetMaxOvershoot(float overshootX, float overshootY)
 {
   mMaxOvershoot.x = overshootX;
   mMaxOvershoot.y = overshootY;
 {
   mMaxOvershoot.x = overshootX;
   mMaxOvershoot.y = overshootY;
+  mUserMaxOvershoot = mMaxOvershoot;
   mDefaultMaxOvershoot = false;
   UpdateMainInternalConstraint();
 }
   mDefaultMaxOvershoot = false;
   UpdateMainInternalConstraint();
 }
@@ -1077,7 +1161,7 @@ Vector2 ScrollView::GetMouseWheelScrollDistanceStep() const
 unsigned int ScrollView::GetCurrentPage() const
 {
   // in case animation is currently taking place.
 unsigned int ScrollView::GetCurrentPage() const
 {
   // in case animation is currently taking place.
-  Vector3 position = GetPropertyPrePosition();
+  Vector3 position = GetPropertyPosition();
 
   Actor self = Self();
   unsigned int page = 0;
 
   Actor self = Self();
   unsigned int page = 0;
@@ -1094,8 +1178,12 @@ unsigned int ScrollView::GetCurrentPage() const
 
 Vector3 ScrollView::GetCurrentScrollPosition() const
 {
 
 Vector3 ScrollView::GetCurrentScrollPosition() const
 {
-  // in case animation is currently taking place.
-  return -GetPropertyPrePosition();
+  return -GetPropertyPosition();
+}
+
+void ScrollView::SetScrollPosition(const Vector3& position)
+{
+  mScrollPrePosition = position;
 }
 
 Vector3 ScrollView::GetCurrentScrollScale() const
 }
 
 Vector3 ScrollView::GetCurrentScrollScale() const
@@ -1360,11 +1448,11 @@ bool ScrollView::SnapWithVelocity(Vector2 velocity)
   const float orthoAngleRange = FLICK_ORTHO_ANGLE_RANGE * M_PI / 180.0f;
   const float flickSpeedThreshold2 = FLICK_SPEED_THRESHOLD*FLICK_SPEED_THRESHOLD;
 
   const float orthoAngleRange = FLICK_ORTHO_ANGLE_RANGE * M_PI / 180.0f;
   const float flickSpeedThreshold2 = FLICK_SPEED_THRESHOLD*FLICK_SPEED_THRESHOLD;
 
-  Vector3 positionSnap = mScrollPostPosition;
+  Vector3 positionSnap = mScrollPrePosition;
 
   // Flick logic X Axis
 
 
   // Flick logic X Axis
 
-  if(mRulerX->IsEnabled())
+  if(mRulerX->IsEnabled() && mLockAxis != LockHorizontal)
   {
     horizontal = All;
 
   {
     horizontal = All;
 
@@ -1392,7 +1480,7 @@ bool ScrollView::SnapWithVelocity(Vector2 velocity)
 
   // Flick logic Y Axis
 
 
   // Flick logic Y Axis
 
-  if(mRulerY->IsEnabled())
+  if(mRulerY->IsEnabled() && mLockAxis != LockVertical)
   {
     vertical = All;
 
   {
     vertical = All;
 
@@ -1557,46 +1645,27 @@ bool ScrollView::SnapWithVelocity(Vector2 velocity)
                              DirectionBiasNone, DirectionBiasNone,
                              isFlick || isFreeFlick ? Flick : Snap);
 
                              DirectionBiasNone, DirectionBiasNone,
                              isFlick || isFreeFlick ? Flick : Snap);
 
-  if(animating)
-  {
-    AnimateOvershootToOrigin(positionDuration.x, positionDuration.y);
-  }
-
   return animating;
 }
 
 void ScrollView::StopAnimation(void)
 {
   // Clear Snap animation if exists.
   return animating;
 }
 
 void ScrollView::StopAnimation(void)
 {
   // Clear Snap animation if exists.
-  if(mSnapAnimation)
-  {
-    mSnapAnimation.Stop();
-    mSnapAnimation.FinishedSignal().Disconnect(this, &ScrollView::OnSnapAnimationFinished);
-    mSnapAnimation.Clear();
-    mSnapAnimation = NULL;
-  }
-  if(mSnapXAnimation)
-  {
-    mSnapXAnimation.Stop();
-    mSnapXAnimation.FinishedSignal().Disconnect(this, &ScrollView::OnSnapXAnimationFinished);
-    mSnapXAnimation.Clear();
-    mSnapXAnimation = NULL;
-  }
-  if(mSnapYAnimation)
-  {
-    mSnapYAnimation.Stop();
-    mSnapYAnimation.FinishedSignal().Disconnect(this, &ScrollView::OnSnapYAnimationFinished);
-    mSnapYAnimation.Clear();
-    mSnapYAnimation = NULL;
-  }
-  if(mSnapOvershootAnimation)
+  StopAnimation(mSnapAnimation);
+  StopAnimation(mInternalXAnimation);
+  StopAnimation(mInternalYAnimation);
+  mScrollStateFlags = 0;
+  // remove scroll animation flags
+  HandleStoppedAnimation();
+}
+
+void ScrollView::StopAnimation(Animation& animation)
+{
+  if(animation)
   {
   {
-    mSnapOvershootAnimation.FinishedSignal().Disconnect(this, &ScrollView::OnSnapOvershootAnimationFinished);
-    mSnapOvershootAnimation.Stop();
-    mSnapOvershootAnimation.Clear();
-    mSnapOvershootAnimation = NULL;
+    animation.Stop();
+    animation.Reset();
   }
   }
-  HandleStoppedAnimation();
 }
 
 bool ScrollView::AnimateTo(const Vector3& position, const Vector3& positionDuration,
 }
 
 bool ScrollView::AnimateTo(const Vector3& position, const Vector3& positionDuration,
@@ -1609,11 +1678,10 @@ bool ScrollView::AnimateTo(const Vector3& position, const Vector3& positionDurat
   // Here we perform an animation on a number of properties (depending on which have changed)
   // The animation is applied to all ScrollBases
   Actor self = Self();
   // Here we perform an animation on a number of properties (depending on which have changed)
   // The animation is applied to all ScrollBases
   Actor self = Self();
-  bool startAnimation = false;
-  Vector3 positionTransformed = position;
+  mScrollTargetPosition = position;
   float totalDuration = 0.0f;
 
   float totalDuration = 0.0f;
 
-  bool positionChanged = (positionTransformed != mScrollPostPosition);
+  bool positionChanged = (mScrollTargetPosition != mScrollPostPosition);
   bool scaleChanged = (scale != mScrollPostScale);
   bool rotationChanged = fabsf(rotation - mScrollPostRotation) > Math::MACHINE_EPSILON_0;
 
   bool scaleChanged = (scale != mScrollPostScale);
   bool rotationChanged = fabsf(rotation - mScrollPostRotation) > Math::MACHINE_EPSILON_0;
 
@@ -1622,6 +1690,12 @@ bool ScrollView::AnimateTo(const Vector3& position, const Vector3& positionDurat
     totalDuration = std::max(totalDuration, positionDuration.x);
     totalDuration = std::max(totalDuration, positionDuration.y);
   }
     totalDuration = std::max(totalDuration, positionDuration.x);
     totalDuration = std::max(totalDuration, positionDuration.y);
   }
+  else
+  {
+    // try to animate for a frame, on some occasions update will be changing scroll value while event side thinks it hasnt changed
+    totalDuration = 0.01f;
+    positionChanged = true;
+  }
 
   if(scaleChanged)
   {
 
   if(scaleChanged)
   {
@@ -1633,100 +1707,95 @@ bool ScrollView::AnimateTo(const Vector3& position, const Vector3& positionDurat
   {
     totalDuration = std::max(totalDuration, rotationDuration);
   }
   {
     totalDuration = std::max(totalDuration, rotationDuration);
   }
+  StopAnimation();
 
 
-  if(totalDuration > Math::MACHINE_EPSILON_1)
+  // Position Delta ///////////////////////////////////////////////////////
+  if(positionChanged)
   {
   {
-    StopAnimation();
-    mSnapAnimation = Animation::New(totalDuration);
-    mSnapAnimation.FinishedSignal().Connect(this, &ScrollView::OnSnapAnimationFinished);
-    mSnapXAnimation = Animation::New(positionDuration.x);
-    mSnapXAnimation.FinishedSignal().Connect(this, &ScrollView::OnSnapXAnimationFinished);
-    mSnapYAnimation = Animation::New(positionDuration.y);
-    mSnapYAnimation.FinishedSignal().Connect(this, &ScrollView::OnSnapYAnimationFinished);
-    startAnimation = true;
-
-    // Position Delta ///////////////////////////////////////////////////////
-    if(positionChanged)
-    {
-      if(mWrapMode && findShortcuts)
+    if(mWrapMode && findShortcuts)
+    {
+      // In Wrap Mode, the shortest distance is a little less intuitive...
+      const RulerDomain rulerDomainX = mRulerX->GetDomain();
+      const RulerDomain rulerDomainY = mRulerY->GetDomain();
+
+      if(mRulerX->IsEnabled())
       {
       {
-        // In Wrap Mode, the shortest distance is a little less intuitive...
-        const RulerDomain rulerDomainX = mRulerX->GetDomain();
-        const RulerDomain rulerDomainY = mRulerY->GetDomain();
-
-        if(mRulerX->IsEnabled())
-        {
-          float dir = VectorInDomain(-mScrollPostPosition.x, -positionTransformed.x, rulerDomainX.min, rulerDomainX.max, horizontalBias);
-          positionTransformed.x = mScrollPostPosition.x + -dir;
-        }
-
-        if(mRulerY->IsEnabled())
-        {
-          float dir = VectorInDomain(-mScrollPostPosition.y, -positionTransformed.y, rulerDomainY.min, rulerDomainY.max, verticalBias);
-          positionTransformed.y = mScrollPostPosition.y + -dir;
-        }
+        float dir = VectorInDomain(-mScrollPostPosition.x, -mScrollTargetPosition.x, rulerDomainX.min, rulerDomainX.max, horizontalBias);
+        mScrollTargetPosition.x = mScrollPostPosition.x + -dir;
       }
 
       }
 
-      // note we have two separate animations for X & Y, this deals with sliding diagonally and hitting
-      // a horizonal/vertical wall.delay
-      mSnapXAnimation.AnimateTo( Property(self, mPropertyX), positionTransformed.x, alpha, TimePeriod(0.0f, positionDuration.x));
-      mSnapYAnimation.AnimateTo( Property(self, mPropertyY), positionTransformed.y, alpha, TimePeriod(0.0f, positionDuration.y));
+      if(mRulerY->IsEnabled())
+      {
+        float dir = VectorInDomain(-mScrollPostPosition.y, -mScrollTargetPosition.y, rulerDomainY.min, rulerDomainY.max, verticalBias);
+        mScrollTargetPosition.y = mScrollPostPosition.y + -dir;
+      }
     }
 
     }
 
-    // Scale Delta ///////////////////////////////////////////////////////
-    if(scaleChanged)
+    // note we have two separate animations for X & Y, this deals with sliding diagonally and hitting
+    // a horizonal/vertical wall.delay
+    AnimateInternalXTo(mScrollTargetPosition.x, positionDuration.x, alpha);
+    AnimateInternalYTo(mScrollTargetPosition.y, positionDuration.y, alpha);
+
+    if( !(mScrollStateFlags & SCROLL_ANIMATION_FLAGS) )
     {
     {
-      // TODO: for non-uniform scaling to different bounds e.g. scaling a square to a 4:3 aspect ratio screen with a velocity
-      // the height will hit first, and then the width, so that would require two different animation times just like position.
-      mSnapAnimation.AnimateTo( Property(self, mPropertyScale), scale, alpha, TimePeriod(0.0f, scaleDuration.x));
+      self.SetProperty(mPropertyPrePosition, mScrollTargetPosition);
+      mScrollPrePosition = mScrollTargetPosition;
     }
     }
+  }
 
 
-    mSnapAnimation.AnimateTo( Property(self, mPropertyTime), totalDuration, AlphaFunctions::Linear );
-
-    mSnapAnimation.Play();
-    mSnapXAnimation.Play();
-    mSnapYAnimation.Play();
-    StartRefreshTimer();
-  } // end if(totalDuration > Math::MACHINE_EPSILON_1)
-  else // totalDuration == 0
+  // Scale Delta ///////////////////////////////////////////////////////
+  if(scaleChanged)
   {
   {
-    // instantly set transform.
-    if(positionChanged)
+    if(totalDuration > Math::MACHINE_EPSILON_1)
     {
     {
-      self.SetProperty(mPropertyX, positionTransformed.x);
-      self.SetProperty(mPropertyY, positionTransformed.y);
+      mSnapAnimation = Animation::New(totalDuration);
+      mSnapAnimation.FinishedSignal().Connect(this, &ScrollView::OnScrollAnimationFinished);
+      // TODO: for non-uniform scaling to different bounds e.g. scaling a square to a 4:3 aspect ratio screen with a velocity
+      // the height will hit first, and then the width, so that would require two different animation times just like position.
+      mSnapAnimation.AnimateTo( Property(self, mPropertyScale), scale, alpha, TimePeriod(0.0f, scaleDuration.x));
 
 
-      mScrollPrePosition = mScrollPostPosition = positionTransformed;
+      mSnapAnimation.AnimateTo( Property(self, mPropertyTime), totalDuration, AlphaFunctions::Linear );
+      mSnapAnimation.Play();
     }
     }
-
-    if(scaleChanged)
+    else
     {
       self.SetProperty(mPropertyScale, scale);
 
       mScrollPreScale = mScrollPostScale = scale;
     }
   }
     {
       self.SetProperty(mPropertyScale, scale);
 
       mScrollPreScale = mScrollPostScale = scale;
     }
   }
+  StartRefreshTimer();
 
   // Always send a snap event when AnimateTo is called.
   Toolkit::ScrollView::SnapEvent snapEvent;
   snapEvent.type = snapType;
 
   // Always send a snap event when AnimateTo is called.
   Toolkit::ScrollView::SnapEvent snapEvent;
   snapEvent.type = snapType;
-  snapEvent.position = positionTransformed;
+  snapEvent.position = -mScrollTargetPosition;
   snapEvent.scale = scale;
   snapEvent.rotation = rotation;
   snapEvent.duration = totalDuration;
 
   mSnapStartedSignalV2.Emit( snapEvent );
 
   snapEvent.scale = scale;
   snapEvent.rotation = rotation;
   snapEvent.duration = totalDuration;
 
   mSnapStartedSignalV2.Emit( snapEvent );
 
-  return startAnimation;
+  return (mScrollStateFlags & SCROLL_ANIMATION_FLAGS) != 0;
 }
 
 void ScrollView::SetOvershootEnabled(bool enabled)
 {
   if(enabled && !mOvershootIndicator)
   {
 }
 
 void ScrollView::SetOvershootEnabled(bool enabled)
 {
   if(enabled && !mOvershootIndicator)
   {
-    mOvershootIndicator = ScrollOvershootIndicator::New(*this);
+    mOvershootIndicator = ScrollOvershootIndicator::New();
+  }
+  if( enabled )
+  {
+    mMaxOvershoot = OVERSCROLL_CLAMP;
+    mOvershootIndicator->AttachToScrollable(*this);
+  }
+  else
+  {
+    mMaxOvershoot = mUserMaxOvershoot;
+    mOvershootIndicator->DetachFromScrollable(*this);
   }
   }
-  mOvershootIndicator->Enable(enabled);
+  UpdateMainInternalConstraint();
 }
 
 void ScrollView::AddOverlay(Actor actor)
 }
 
 void ScrollView::AddOverlay(Actor actor)
@@ -1766,9 +1835,8 @@ void ScrollView::FindAndUnbindActor(Actor child)
 
 Vector3 ScrollView::GetPropertyPrePosition() const
 {
 
 Vector3 ScrollView::GetPropertyPrePosition() const
 {
-  Vector3 position(Self().GetProperty<float>(mPropertyX), Self().GetProperty<float>(mPropertyY), 0.0f);
+  Vector3 position = Self().GetProperty<Vector3>(mPropertyPrePosition);
   WrapPosition(position);
   WrapPosition(position);
-
   return position;
 }
 
   return position;
 }
 
@@ -1789,38 +1857,26 @@ void ScrollView::HandleStoppedAnimation()
 {
   // Animation has stopped, so stop sending the scroll-update signal.
   CancelRefreshTimer();
 {
   // Animation has stopped, so stop sending the scroll-update signal.
   CancelRefreshTimer();
-
-  // cement transform now, and allow interactivity to resume.
-  mScrollPostPosition = GetPropertyPosition();
-
-  mScrollPostScale = GetPropertyScale();
-
-  // Update Actor position with this wrapped value.
-
-  Self().SetProperty(mPropertyX, mScrollPostPosition.x);
-  Self().SetProperty(mPropertyY, mScrollPostPosition.y);
-  // TODO Rotation
-
-  mScrollPrePosition = mScrollPostPosition;
-  mScrollPreScale = mScrollPostScale;
-  mScrollPreRotation = mScrollPostRotation;
 }
 
 void ScrollView::HandleSnapAnimationFinished()
 {
   // Emit Signal that scrolling has completed.
   mScrolling = false;
 }
 
 void ScrollView::HandleSnapAnimationFinished()
 {
   // Emit Signal that scrolling has completed.
   mScrolling = false;
-  Self().SetProperty(mPropertyScrolling, false);
+  Actor self = Self();
+  self.SetProperty(mPropertyScrolling, false);
 
 
-  Vector3 deltaPosition(Self().GetProperty<float>(mPropertyX),
-                        Self().GetProperty<float>(mPropertyY),
-                        0.0f);
+  Vector3 deltaPosition(mScrollPrePosition);
+
+  UpdateLocalScrollProperties();
+  WrapPosition(mScrollPrePosition);
+  self.SetProperty(mPropertyPrePosition, mScrollPrePosition);
 
   Vector3 currentScrollPosition = GetCurrentScrollPosition();
   mScrollCompletedSignalV2.Emit( currentScrollPosition );
 
   mDomainOffset += deltaPosition - mScrollPostPosition;
 
   Vector3 currentScrollPosition = GetCurrentScrollPosition();
   mScrollCompletedSignalV2.Emit( currentScrollPosition );
 
   mDomainOffset += deltaPosition - mScrollPostPosition;
-  Self().SetProperty(mPropertyDomainOffset, mDomainOffset);
+  self.SetProperty(mPropertyDomainOffset, mDomainOffset);
   HandleStoppedAnimation();
 }
 
   HandleStoppedAnimation();
 }
 
@@ -1855,8 +1911,12 @@ void ScrollView::OnControlSizeSet( const Vector3& size )
   // need to update domain properties for new size
   if( mDefaultMaxOvershoot )
   {
   // need to update domain properties for new size
   if( mDefaultMaxOvershoot )
   {
-    mMaxOvershoot.x = size.x * 0.5f;
-    mMaxOvershoot.y = size.y * 0.5f;
+    mUserMaxOvershoot.x = size.x * 0.5f;
+    mUserMaxOvershoot.y = size.y * 0.5f;
+    if( !IsScrollComponentEnabled(Toolkit::Scrollable::OvershootIndicator) )
+    {
+      mMaxOvershoot = mUserMaxOvershoot;
+    }
   }
   UpdatePropertyDomain(size);
   UpdateMainInternalConstraint();
   }
   UpdatePropertyDomain(size);
   UpdateMainInternalConstraint();
@@ -1880,6 +1940,27 @@ void ScrollView::OnChildRemove(Actor& child)
   UnbindActor(child);
 }
 
   UnbindActor(child);
 }
 
+void ScrollView::OnPropertySet( Property::Index index, Property::Value propertyValue )
+{
+  Actor self = Self();
+  if( index == mPropertyX )
+  {
+    self.GetProperty(mPropertyPrePosition).Get(mScrollPrePosition);
+    propertyValue.Get(mScrollPrePosition.x);
+    self.SetProperty(mPropertyPrePosition, mScrollPrePosition);
+  }
+  else if( index == mPropertyY )
+  {
+    self.GetProperty(mPropertyPrePosition).Get(mScrollPrePosition);
+    propertyValue.Get(mScrollPrePosition.y);
+    self.SetProperty(mPropertyPrePosition, mScrollPrePosition);
+  }
+  else if( index == mPropertyPrePosition )
+  {
+    propertyValue.Get(mScrollPrePosition);
+  }
+}
+
 void ScrollView::StartTouchDownTimer()
 {
   if ( !mTouchDownTimer )
 void ScrollView::StartTouchDownTimer()
 {
   if ( !mTouchDownTimer )
@@ -1903,21 +1984,20 @@ bool ScrollView::OnTouchDownTimeout()
 {
   mTouchDownTimeoutReached = true;
 
 {
   mTouchDownTimeoutReached = true;
 
-  if( mSnapAnimation || mSnapXAnimation || mSnapYAnimation || mSnapOvershootAnimation )
+  if( mScrollStateFlags & (SCROLL_ANIMATION_FLAGS | SNAP_ANIMATION_FLAGS) )
   {
   {
-    mScrollInterrupted = true;
     StopAnimation();
     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);
+    if( mScrollStateFlags & SCROLL_ANIMATION_FLAGS )
+    {
+      mScrollInterrupted = true;
+      // reset domain offset as scrolling from original plane.
+      mDomainOffset = Vector3::ZERO;
+      Self().SetProperty(mPropertyDomainOffset, Vector3::ZERO);
 
 
-    mScrolling = false;
-    Vector3 currentScrollPosition = GetCurrentScrollPosition();
-    mScrollCompletedSignalV2.Emit( currentScrollPosition );
+      UpdateLocalScrollProperties();
+      Vector3 currentScrollPosition = GetCurrentScrollPosition();
+      mScrollCompletedSignalV2.Emit( currentScrollPosition );
+    }
   }
 
   return false;
   }
 
   return false;
@@ -1937,7 +2017,7 @@ bool ScrollView::OnTouchEvent(const TouchEvent& event)
     return false;
   }
 
     return false;
   }
 
-  if (event.GetPoint(0).state == TouchPoint::Down)
+  if( event.GetPoint(0).state == TouchPoint::Down )
   {
     if(mGestureStackDepth==0)
     {
   {
     if(mGestureStackDepth==0)
     {
@@ -1948,7 +2028,7 @@ bool ScrollView::OnTouchEvent(const TouchEvent& event)
       StartTouchDownTimer();
     }
   }
       StartTouchDownTimer();
     }
   }
-  else if(event.GetPoint(0).state == TouchPoint::Up)
+  else if( event.GetPoint(0).state == TouchPoint::Up )
   {
     StopTouchDownTimer();
 
   {
     StopTouchDownTimer();
 
@@ -1964,8 +2044,9 @@ bool ScrollView::OnTouchEvent(const TouchEvent& event)
         mLastVelocity = Vector2( 0.0f, 0.0f );
       }
 
         mLastVelocity = Vector2( 0.0f, 0.0f );
       }
 
+      UpdateLocalScrollProperties();
       // Only finish the transform if scrolling was interrupted on down or if we are scrolling
       // Only finish the transform if scrolling was interrupted on down or if we are scrolling
-      if ( mSnapAnimation || mSnapXAnimation || mSnapYAnimation || mSnapOvershootAnimation || mScrollInterrupted || mScrolling )
+      if ( mScrollInterrupted || mScrolling )
       {
         FinishTransform();
       }
       {
         FinishTransform();
       }
@@ -1974,7 +2055,7 @@ bool ScrollView::OnTouchEvent(const TouchEvent& event)
     mScrollInterrupted = false;
   }
 
     mScrollInterrupted = false;
   }
 
-  return true; // consume since we're potentially scrolling
+  return true;
 }
 
 bool ScrollView::OnMouseWheelEvent(const MouseWheelEvent& event)
 }
 
 bool ScrollView::OnMouseWheelEvent(const MouseWheelEvent& event)
@@ -2023,64 +2104,205 @@ bool ScrollView::OnMouseWheelEvent(const MouseWheelEvent& event)
   return true;
 }
 
   return true;
 }
 
-void ScrollView::OnSnapAnimationFinished( Animation& source )
+void ScrollView::ResetScrolling()
 {
 {
-  mSnapAnimation.FinishedSignal().Disconnect( this, &ScrollView::OnSnapAnimationFinished );
-  mSnapAnimation = NULL;
+  Actor self = Self();
+  self.GetProperty(mPropertyPosition).Get(mScrollPostPosition);
+  mScrollPrePosition = mScrollPostPosition;
+  self.SetProperty(mPropertyPrePosition, mScrollPostPosition);
 }
 
 }
 
-void ScrollView::OnSnapXAnimationFinished( Animation& source )
+void ScrollView::UpdateLocalScrollProperties()
 {
 {
-  // Guard against destruction during signal emission
-  // Note that ScrollCompletedSignal is emitted from HandleSnapAnimationFinished()
-  Toolkit::ScrollView handle( GetOwner() );
+  Actor self = Self();
+  self.GetProperty(mPropertyPrePosition).Get(mScrollPrePosition);
+  self.GetProperty(mPropertyPosition).Get(mScrollPostPosition);
+}
+
+// private functions
+
+void ScrollView::PreAnimatedScrollSetup()
+{
+  // mPropertyPrePosition is our unclamped property with wrapping
+  // mPropertyPosition is our final scroll position after clamping
+
+  Actor self = Self();
+
+  Vector3 deltaPosition(mScrollPostPosition);
+  WrapPosition(mScrollPostPosition);
+  mDomainOffset += deltaPosition - mScrollPostPosition;
+  Self().SetProperty(mPropertyDomainOffset, mDomainOffset);
 
 
-  if(!mSnapYAnimation)
+  if( mScrollStateFlags & SCROLL_X_STATE_MASK )
   {
   {
-    HandleSnapAnimationFinished();
+    // already performing animation on internal x position
+    StopAnimation(mInternalXAnimation);
   }
   }
-  if(mScrollMainInternalOvershootXConstraint)
+
+  if( mScrollStateFlags & SCROLL_Y_STATE_MASK )
   {
   {
-    Self().RemoveConstraint(mScrollMainInternalOvershootXConstraint);
-    mScrollMainInternalOvershootXConstraint.Reset();
-    mScrollMainInternalOvershootXConstraint = 0;
+    // already performing animation on internal y position
+    StopAnimation(mInternalYAnimation);
   }
   }
-  mSnapXAnimation.FinishedSignal().Disconnect(this, &ScrollView::OnSnapXAnimationFinished);
-  mSnapXAnimation.Reset();
-  mSnapXAnimation = NULL;
-  if( IsScrollComponentEnabled(Toolkit::Scrollable::OvershootIndicator) )
+
+  mScrollStateFlags = 0;
+
+  mScrollPostScale = GetPropertyScale();
+
+  // Update Actor position with this wrapped value.
+  // TODO Rotation
+
+  mScrollPreScale = mScrollPostScale;
+  mScrollPreRotation = mScrollPostRotation;
+}
+
+void ScrollView::FinaliseAnimatedScroll()
+{
+  // TODO - common animation finishing code in here
+}
+
+void ScrollView::AnimateInternalXTo( float position, float duration, AlphaFunction alpha )
+{
+  StopAnimation(mInternalXAnimation);
+
+  if( duration > Math::MACHINE_EPSILON_10 )
   {
   {
-    // kick start animation to 0
-    Self().SetProperty(mPropertyOvershootX, 0.0f);
+    Actor self = Self();
+    mInternalXAnimation = Animation::New(duration);
+    mInternalXAnimation.FinishedSignal().Connect(this, &ScrollView::OnScrollAnimationFinished);
+    mInternalXAnimation.AnimateTo( Property(self, mPropertyPrePosition, 0), position, alpha, duration);
+    mInternalXAnimation.Play();
+
+    // erase current state flags
+    mScrollStateFlags &= ~SCROLL_X_STATE_MASK;
+    // add internal animation state flag
+    mScrollStateFlags |= AnimatingInternalX;
+  }
+}
+
+void ScrollView::AnimateInternalYTo( float position, float duration, AlphaFunction alpha )
+{
+  StopAnimation(mInternalYAnimation);
+
+  if( duration > Math::MACHINE_EPSILON_10 )
+  {
+    Actor self = Self();
+    mInternalYAnimation = Animation::New(duration);
+    mInternalYAnimation.FinishedSignal().Connect(this, &ScrollView::OnScrollAnimationFinished);
+    mInternalYAnimation.AnimateTo( Property(self, mPropertyPrePosition, 1), position, alpha, TimePeriod(duration));
+    mInternalYAnimation.Play();
+
+    // erase current state flags
+    mScrollStateFlags &= ~SCROLL_Y_STATE_MASK;
+    // add internal animation state flag
+    mScrollStateFlags |= AnimatingInternalY;
   }
 }
 
   }
 }
 
-void ScrollView::OnSnapYAnimationFinished( Animation& source )
+void ScrollView::OnScrollAnimationFinished( Animation& source )
 {
   // Guard against destruction during signal emission
   // Note that ScrollCompletedSignal is emitted from HandleSnapAnimationFinished()
   Toolkit::ScrollView handle( GetOwner() );
 
 {
   // Guard against destruction during signal emission
   // Note that ScrollCompletedSignal is emitted from HandleSnapAnimationFinished()
   Toolkit::ScrollView handle( GetOwner() );
 
-  if(!mSnapXAnimation)
+  bool scrollingFinished = false;
+
+  // update our local scroll positions
+  UpdateLocalScrollProperties();
+
+  if( source == mSnapAnimation )
+  {
+    // generic snap animation used for scaling and rotation
+    mSnapAnimation.Reset();
+  }
+
+  if( source == mInternalXAnimation )
+  {
+    if( !(mScrollStateFlags & AnimatingInternalY) )
+    {
+      scrollingFinished = true;
+    }
+    mInternalXAnimation.Reset();
+    SnapInternalXTo(mScrollPostPosition.x);
+  }
+
+  if( source == mInternalYAnimation )
+  {
+    if( !(mScrollStateFlags & AnimatingInternalX) )
+    {
+      scrollingFinished = true;
+    }
+    mInternalYAnimation.Reset();
+    SnapInternalYTo(mScrollPostPosition.y);
+  }
+
+  if(scrollingFinished)
   {
     HandleSnapAnimationFinished();
   }
   {
     HandleSnapAnimationFinished();
   }
-  if(mScrollMainInternalOvershootYConstraint)
+}
+
+void ScrollView::OnSnapInternalPositionFinished( Animation& source )
+{
+  Actor self = Self();
+  UpdateLocalScrollProperties();
+  if( source == mInternalXAnimation )
   {
   {
-    Self().RemoveConstraint(mScrollMainInternalOvershootYConstraint);
-    mScrollMainInternalOvershootYConstraint.Reset();
-    mScrollMainInternalOvershootYConstraint = 0;
+    // clear internal x animation flags
+    mScrollStateFlags &= ~SCROLL_X_STATE_MASK;
+    mInternalXAnimation.Reset();
+    WrapPosition(mScrollPrePosition);
   }
   }
-  mSnapYAnimation.FinishedSignal().Disconnect(this, &ScrollView::OnSnapYAnimationFinished);
-  mSnapYAnimation.Reset();
-  mSnapYAnimation = NULL;
-  if( IsScrollComponentEnabled(Toolkit::Scrollable::OvershootIndicator) )
+  if( source == mInternalYAnimation )
   {
   {
-    // kick start animation to 0
-    Self().SetProperty(mPropertyOvershootY, 0.0f);
+    mScrollStateFlags &= ~SCROLL_Y_STATE_MASK;
+    mInternalYAnimation.Reset();
+    WrapPosition(mScrollPrePosition);
   }
 }
 
   }
 }
 
+void ScrollView::SnapInternalXTo(float position)
+{
+  Actor self = Self();
+
+  StopAnimation(mInternalXAnimation);
+
+  // erase current state flags
+  mScrollStateFlags &= ~SCROLL_X_STATE_MASK;
+
+  // if internal x not equal to inputed parameter, animate it
+  float current = self.GetProperty<Vector3>(mPropertyPrePosition).x;
+  float duration = fabsf(position - current);
+  mInternalXAnimation = Animation::New(duration);
+  mInternalXAnimation.FinishedSignal().Connect(this, &ScrollView::OnSnapInternalPositionFinished);
+  mInternalXAnimation.AnimateTo(Property(self, mPropertyPrePosition, 0), position);
+  mInternalXAnimation.Play();
+
+  // add internal animation state flag
+  mScrollStateFlags |= SnappingInternalX;
+}
+
+void ScrollView::SnapInternalYTo(float position)
+{
+  Actor self = Self();
+
+  StopAnimation(mInternalYAnimation);
+
+  // erase current state flags
+  mScrollStateFlags &= ~SCROLL_Y_STATE_MASK;
+
+  // if internal y not equal to inputed parameter, animate it
+  float current = self.GetProperty<Vector3>(mPropertyPrePosition).y;
+  float duration = fabsf(position - current);
+  mInternalYAnimation = Animation::New(duration);
+  mInternalYAnimation.FinishedSignal().Connect(this, &ScrollView::OnSnapInternalPositionFinished);
+  mInternalYAnimation.AnimateTo(Property(self, mPropertyPrePosition, 1), position);
+  mInternalYAnimation.Play();
+
+  // add internal animation state flag
+  mScrollStateFlags |= SnappingInternalY;
+}
+
 void ScrollView::GestureStarted()
 {
   // we handle the first gesture.
 void ScrollView::GestureStarted()
 {
   // we handle the first gesture.
@@ -2088,25 +2310,40 @@ void ScrollView::GestureStarted()
   // we continue and combine the effects of the gesture instead of reseting.
   if(mGestureStackDepth++==0)
   {
   // we continue and combine the effects of the gesture instead of reseting.
   if(mGestureStackDepth++==0)
   {
+    Actor self = Self();
     StopTouchDownTimer();
     StopAnimation();
     mPanDelta = Vector3::ZERO;
     mScaleDelta = Vector3::ONE;
     mRotationDelta = 0.0f;
     mLastVelocity = Vector2(0.0f, 0.0f);
     StopTouchDownTimer();
     StopAnimation();
     mPanDelta = Vector3::ZERO;
     mScaleDelta = Vector3::ONE;
     mRotationDelta = 0.0f;
     mLastVelocity = Vector2(0.0f, 0.0f);
-    mLockAxis = LockPossible;
+    if( !mScrolling )
+    {
+      mLockAxis = LockPossible;
+    }
+
+    if( mScrollStateFlags & SCROLL_X_STATE_MASK )
+    {
+      StopAnimation(mInternalXAnimation);
+    }
+    if( mScrollStateFlags & SCROLL_Y_STATE_MASK )
+    {
+      StopAnimation(mInternalYAnimation);
+    }
+    mScrollStateFlags = 0;
 
     if(mScrolling) // are we interrupting a current scroll?
     {
       // set mScrolling to false, in case user has code that interrogates mScrolling Getter() in complete.
       mScrolling = false;
 
     if(mScrolling) // are we interrupting a current scroll?
     {
       // set mScrolling to false, in case user has code that interrogates mScrolling Getter() in complete.
       mScrolling = false;
-      Vector3 currentScrollPosition = GetCurrentScrollPosition();
-      mScrollCompletedSignalV2.Emit( currentScrollPosition );
+      // send negative scroll position since scroll internal scroll position works as an offset for actors,
+      // give applications the position within the domain from the scroll view's anchor position
+      mScrollCompletedSignalV2.Emit( -mScrollPostPosition );
     }
   }
 }
 
     }
   }
 }
 
-void ScrollView::GestureContinuing(Vector2 panDelta, Vector2 scaleDelta, float rotationDelta)
+void ScrollView::GestureContinuing(const Vector2& panDelta, const Vector2& scaleDelta, float rotationDelta)
 {
   mPanDelta.x+= panDelta.x;
   mPanDelta.y+= panDelta.y;
 {
   mPanDelta.x+= panDelta.x;
   mPanDelta.y+= panDelta.y;
@@ -2123,26 +2360,7 @@ void ScrollView::GestureContinuing(Vector2 panDelta, Vector2 scaleDelta, float r
   // appears mostly horizontal or mostly vertical respectively.
   if(mAxisAutoLock)
   {
   // appears mostly horizontal or mostly vertical respectively.
   if(mAxisAutoLock)
   {
-    if(mPanDelta.LengthSquared() > AUTOLOCK_AXIS_MINIMUM_DISTANCE2 &&
-        mLockAxis == LockPossible)
-    {
-      float dx = fabsf(mPanDelta.x);
-      float dy = fabsf(mPanDelta.y);
-      if(dx * mAxisAutoLockGradient >= dy)
-      {
-        // 0.36:1 gradient to the horizontal (deviate < 20 degrees)
-        mLockAxis = LockVertical;
-      }
-      else if(dy * mAxisAutoLockGradient > dx)
-      {
-        // 0.36:1 gradient to the vertical (deviate < 20 degrees)
-        mLockAxis = LockHorizontal;
-      }
-      else
-      {
-        mLockAxis = LockNone;
-      }
-    }
+    mLockAxis = GetLockAxis(mPanDelta.GetVectorXY(), mLockAxis, mAxisAutoLockGradient);
   } // end if mAxisAutoLock
 }
 
   } // end if mAxisAutoLock
 }
 
@@ -2169,50 +2387,34 @@ void ScrollView::OnPan(PanGesture gesture)
   {
     case Gesture::Started:
     {
   {
     case Gesture::Started:
     {
+      UpdateLocalScrollProperties();
       GestureStarted();
       GestureStarted();
+      mPanning = true;
       self.SetProperty( mPropertyPanning, true );
       self.SetProperty( mPropertyPanning, true );
-      self.SetProperty( mPropertyScrollStartPagePosition, GetCurrentScrollPosition() );
+      self.SetProperty( mPropertyScrollStartPagePosition, Vector3(gesture.position.x, gesture.position.y, 0.0f) );
 
 
-      //  Update property: X & Y = Position (only when in panning mode - in snapping mode, X & Y are animated).
-      if( ! mScrollMainInternalXConstraint )
-      {
-        Constraint constraint = Constraint::New<float>( mPropertyX,
-                                                        LocalSource( mPropertyPosition ),
-                                                        Source( self, mPropertyPanning ),
-                                                        InternalXConstraint );
-        mScrollMainInternalXConstraint = self.ApplyConstraint( constraint );
-      }
-      if( ! mScrollMainInternalYConstraint )
-      {
-        Constraint constraint = Constraint::New<float>( mPropertyY,
-                                                        LocalSource( mPropertyPosition ),
-                                                        Source( self, mPropertyPanning ),
-                                                        InternalYConstraint );
-        mScrollMainInternalYConstraint = self.ApplyConstraint( constraint );
-      }
-
-      // When panning we want to make sure overshoot values are affected by pre position and post position
-      SetOvershootConstraintsEnabled(true);
+      UpdateMainInternalConstraint();
       break;
     }
 
     case Gesture::Continuing:
     {
       break;
     }
 
     case Gesture::Continuing:
     {
-      // Nothing to do, handled in constraint.
+      GestureContinuing(gesture.screenDisplacement, Vector2::ZERO, 0.0f);
       break;
     }
 
     case Gesture::Finished:
     case Gesture::Cancelled:
     {
       break;
     }
 
     case Gesture::Finished:
     case Gesture::Cancelled:
     {
+      UpdateLocalScrollProperties();
       mLastVelocity = gesture.velocity;
       mLastVelocity = gesture.velocity;
+      mPanning = false;
       self.SetProperty( mPropertyPanning, false );
 
       self.SetProperty( mPropertyPanning, false );
 
-      // Remove X & Y position constraints as they are not required when we are not panning.
-      self.RemoveConstraint(mScrollMainInternalXConstraint);
-      self.RemoveConstraint(mScrollMainInternalYConstraint);
-      mScrollMainInternalXConstraint.Reset();
-      mScrollMainInternalYConstraint.Reset();
+      if( mScrollMainInternalPrePositionConstraint )
+      {
+        self.RemoveConstraint(mScrollMainInternalPrePositionConstraint);
+      }
       break;
     }
 
       break;
     }
 
@@ -2261,28 +2463,28 @@ void ScrollView::UpdateTransform()
 
 void ScrollView::FinishTransform()
 {
 
 void ScrollView::FinishTransform()
 {
-  const Vector3& scrollPosition = Self().GetProperty<Vector3>(mPropertyPosition);
-
-  mScrollPostPosition.x = scrollPosition.x;
-  mScrollPostPosition.y = scrollPosition.y;
-
-  Vector3 deltaPosition(mScrollPostPosition);
-  // Cement PRE transform (PRE = POST), and Begin Snap Animation if necessary.
-  WrapPosition(mScrollPostPosition);
+  // at this stage internal x and x scroll position should have followed prescroll position exactly
+  Actor self = Self();
 
 
-  mDomainOffset += deltaPosition - mScrollPostPosition;
-  Self().SetProperty(mPropertyDomainOffset, mDomainOffset);
+  PreAnimatedScrollSetup();
 
   bool animating = SnapWithVelocity(mLastVelocity * 1000.0f);
 
   if(!animating)
   {
 
   bool animating = SnapWithVelocity(mLastVelocity * 1000.0f);
 
   if(!animating)
   {
-    AnimateOvershootToOrigin(0.0f, 0.0f);
     // if not animating, then this pan has completed right now.
     mScrolling = false;
     Self().SetProperty(mPropertyScrolling, false);
     Vector3 currentScrollPosition = GetCurrentScrollPosition();
     mScrollCompletedSignalV2.Emit( currentScrollPosition );
     // if not animating, then this pan has completed right now.
     mScrolling = false;
     Self().SetProperty(mPropertyScrolling, false);
     Vector3 currentScrollPosition = GetCurrentScrollPosition();
     mScrollCompletedSignalV2.Emit( currentScrollPosition );
+    if( fabs(mScrollPrePosition.x - mScrollTargetPosition.x) > Math::MACHINE_EPSILON_10 )
+    {
+      SnapInternalXTo(mScrollTargetPosition.x);
+    }
+    if( fabs(mScrollPrePosition.y - mScrollTargetPosition.y) > Math::MACHINE_EPSILON_10 )
+    {
+      SnapInternalYTo(mScrollTargetPosition.y);
+    }
   }
 }
 
   }
 }
 
@@ -2402,13 +2604,18 @@ void ScrollView::UpdateMainInternalConstraint()
   Actor self = Self();
   PanGestureDetector detector( GetPanGestureDetector() );
 
   Actor self = Self();
   PanGestureDetector detector( GetPanGestureDetector() );
 
-  if(mScrollMainInternalPrePositionConstraint)
+  if(mScrollMainInternalPositionConstraint)
   {
   {
-    self.RemoveConstraint(mScrollMainInternalPrePositionConstraint);
     self.RemoveConstraint(mScrollMainInternalPositionConstraint);
     self.RemoveConstraint(mScrollMainInternalDeltaConstraint);
     self.RemoveConstraint(mScrollMainInternalFinalConstraint);
     self.RemoveConstraint(mScrollMainInternalRelativeConstraint);
     self.RemoveConstraint(mScrollMainInternalPositionConstraint);
     self.RemoveConstraint(mScrollMainInternalDeltaConstraint);
     self.RemoveConstraint(mScrollMainInternalFinalConstraint);
     self.RemoveConstraint(mScrollMainInternalRelativeConstraint);
+    self.RemoveConstraint(mScrollMainInternalXConstraint);
+    self.RemoveConstraint(mScrollMainInternalYConstraint);
+  }
+  if( mScrollMainInternalPrePositionConstraint )
+  {
+    self.RemoveConstraint(mScrollMainInternalPrePositionConstraint);
   }
 
   // TODO: It's probably better to use a local displacement value as this will give a displacement when scrolling just commences
   }
 
   // TODO: It's probably better to use a local displacement value as this will give a displacement when scrolling just commences
@@ -2417,28 +2624,41 @@ void ScrollView::UpdateMainInternalConstraint()
   // 1. First calculate the pre-position (this is the scroll position if no clamping has taken place)
   Vector2 initialPanMask = Vector2(mRulerX->IsEnabled() ? 1.0f : 0.0f, mRulerY->IsEnabled() ? 1.0f : 0.0f);
 
   // 1. First calculate the pre-position (this is the scroll position if no clamping has taken place)
   Vector2 initialPanMask = Vector2(mRulerX->IsEnabled() ? 1.0f : 0.0f, mRulerY->IsEnabled() ? 1.0f : 0.0f);
 
-  Constraint constraint = Constraint::New<Vector3>( mPropertyPrePosition,
-                                                    Source( detector, PanGestureDetector::LOCAL_POSITION ),
-                                                    Source( detector, PanGestureDetector::LOCAL_DISPLACEMENT ),
-                                                    LocalSource( mPropertyX ),
-                                                    LocalSource( mPropertyY ),
-                                                    Source( self, mPropertyPanning ),
-                                                    InternalPrePositionConstraint( initialPanMask, mAxisAutoLock, mAxisAutoLockGradient ) );
-  mScrollMainInternalPrePositionConstraint = self.ApplyConstraint(constraint);
+  if( mLockAxis == LockVertical )
+  {
+    initialPanMask.y = 0.0f;
+  }
+  else if( mLockAxis == LockHorizontal )
+  {
+    initialPanMask.x = 0.0f;
+  }
+  Constraint constraint;
+
+  if( mPanning )
+  {
+    constraint = Constraint::New<Vector3>( mPropertyPrePosition,
+                                                      Source( detector, PanGestureDetector::LOCAL_POSITION ),
+                                                      Source( detector, PanGestureDetector::LOCAL_DISPLACEMENT ),
+                                                      Source( self, Actor::SIZE ),
+                                                      InternalPrePositionConstraint( initialPanMask, mAxisAutoLock, mAxisAutoLockGradient, mLockAxis, mMaxOvershoot, mRulerX->GetDomain(), mRulerY->GetDomain() ) );
+    mScrollMainInternalPrePositionConstraint = self.ApplyConstraint( constraint );
+  }
 
   // 2. Second calculate the clamped position (actual position)
   constraint = Constraint::New<Vector3>( mPropertyPosition,
                                          LocalSource( mPropertyPrePosition ),
 
   // 2. Second calculate the clamped position (actual position)
   constraint = Constraint::New<Vector3>( mPropertyPosition,
                                          LocalSource( mPropertyPrePosition ),
+                                         LocalSource( mPropertyPositionMin ),
+                                         LocalSource( mPropertyPositionMax ),
                                          Source( self, Actor::SIZE ),
                                          InternalPositionConstraint( mRulerX->GetDomain(),
                                          Source( self, Actor::SIZE ),
                                          InternalPositionConstraint( mRulerX->GetDomain(),
-                                                                     mRulerY->GetDomain()) );
-  mScrollMainInternalPositionConstraint = self.ApplyConstraint(constraint);
+                                                                     mRulerY->GetDomain(), mWrapMode ) );
+  mScrollMainInternalPositionConstraint = self.ApplyConstraint( constraint );
 
   constraint = Constraint::New<Vector3>( mPropertyPositionDelta,
                                          LocalSource( mPropertyPosition ),
                                          LocalSource( mPropertyDomainOffset ),
                                          InternalPositionDeltaConstraint );
 
   constraint = Constraint::New<Vector3>( mPropertyPositionDelta,
                                          LocalSource( mPropertyPosition ),
                                          LocalSource( mPropertyDomainOffset ),
                                          InternalPositionDeltaConstraint );
-  mScrollMainInternalDeltaConstraint = self.ApplyConstraint(constraint);
+  mScrollMainInternalDeltaConstraint = self.ApplyConstraint( constraint );
 
   constraint = Constraint::New<Vector3>( mPropertyFinal,
                                          LocalSource( mPropertyPosition ),
 
   constraint = Constraint::New<Vector3>( mPropertyFinal,
                                          LocalSource( mPropertyPosition ),
@@ -2446,7 +2666,7 @@ void ScrollView::UpdateMainInternalConstraint()
                                          LocalSource( mPropertyOvershootY ),
                                          InternalFinalConstraint( FinalDefaultAlphaFunction,
                                                                   FinalDefaultAlphaFunction ) );
                                          LocalSource( mPropertyOvershootY ),
                                          InternalFinalConstraint( FinalDefaultAlphaFunction,
                                                                   FinalDefaultAlphaFunction ) );
-  mScrollMainInternalFinalConstraint = self.ApplyConstraint(constraint);
+  mScrollMainInternalFinalConstraint = self.ApplyConstraint( constraint );
 
   constraint = Constraint::New<Vector3>( mPropertyRelativePosition,
                                          LocalSource( mPropertyPosition ),
 
   constraint = Constraint::New<Vector3>( mPropertyRelativePosition,
                                          LocalSource( mPropertyPosition ),
@@ -2454,61 +2674,51 @@ void ScrollView::UpdateMainInternalConstraint()
                                          LocalSource( mPropertyPositionMax ),
                                          LocalSource( Actor::SIZE ),
                                          InternalRelativePositionConstraint );
                                          LocalSource( mPropertyPositionMax ),
                                          LocalSource( Actor::SIZE ),
                                          InternalRelativePositionConstraint );
-  mScrollMainInternalRelativeConstraint = self.ApplyConstraint(constraint);
-
-  if(mScrollMainInternalOvershootXConstraint)
-  {
-    // reset these constraints in correct order
-    self.RemoveConstraint(mScrollMainInternalOvershootXConstraint);
-    mScrollMainInternalOvershootXConstraint.Reset();
+  mScrollMainInternalRelativeConstraint = self.ApplyConstraint( constraint );
 
 
-    Constraint constraint = Constraint::New<float>( mPropertyOvershootX,
-                                           LocalSource( mPropertyPrePosition ),
-                                           LocalSource( mPropertyPosition ),
-                                           OvershootXConstraint(mMaxOvershoot.x) );
-    mScrollMainInternalOvershootXConstraint = self.ApplyConstraint(constraint);
-  }
+  constraint = Constraint::New<float>( mPropertyX,
+                                         LocalSource( mPropertyPrePosition ),
+                                         InternalXConstraint );
+  mScrollMainInternalXConstraint = self.ApplyConstraint( constraint );
 
 
-  if(mScrollMainInternalOvershootYConstraint)
-  {
-    // reset these constraints in correct order
-    self.RemoveConstraint(mScrollMainInternalOvershootYConstraint);
-    mScrollMainInternalOvershootYConstraint.Reset();
+  constraint = Constraint::New<float>( mPropertyY,
+                                         LocalSource( mPropertyPrePosition ),
+                                         InternalYConstraint );
+  mScrollMainInternalYConstraint = self.ApplyConstraint( constraint );
 
 
-    Constraint constraint = Constraint::New<float>( mPropertyOvershootY,
-                                           LocalSource( mPropertyPrePosition ),
-                                           LocalSource( mPropertyPosition ),
-                                           OvershootXConstraint(mMaxOvershoot.y) );
-    mScrollMainInternalOvershootYConstraint = self.ApplyConstraint(constraint);
-  }
+  // When panning we want to make sure overshoot values are affected by pre position and post position
+  SetOvershootConstraintsEnabled(!mWrapMode);
 }
 
 void ScrollView::SetOvershootConstraintsEnabled(bool enabled)
 {
   Actor self( Self() );
   // remove and reset, it may now be in wrong order with the main internal constraints
 }
 
 void ScrollView::SetOvershootConstraintsEnabled(bool enabled)
 {
   Actor self( Self() );
   // remove and reset, it may now be in wrong order with the main internal constraints
-  if(mScrollMainInternalOvershootXConstraint)
+  if( mScrollMainInternalOvershootXConstraint )
   {
     self.RemoveConstraint(mScrollMainInternalOvershootXConstraint);
     mScrollMainInternalOvershootXConstraint.Reset();
   {
     self.RemoveConstraint(mScrollMainInternalOvershootXConstraint);
     mScrollMainInternalOvershootXConstraint.Reset();
-  }
-  if(mScrollMainInternalOvershootYConstraint)
-  {
     self.RemoveConstraint(mScrollMainInternalOvershootYConstraint);
     mScrollMainInternalOvershootYConstraint.Reset();
   }
     self.RemoveConstraint(mScrollMainInternalOvershootYConstraint);
     mScrollMainInternalOvershootYConstraint.Reset();
   }
-  if(enabled)
+  if( enabled )
   {
     Constraint constraint = Constraint::New<float>( mPropertyOvershootX,
                                            LocalSource( mPropertyPrePosition ),
                                            LocalSource( mPropertyPosition ),
                                            OvershootXConstraint(mMaxOvershoot.x) );
   {
     Constraint constraint = Constraint::New<float>( mPropertyOvershootX,
                                            LocalSource( mPropertyPrePosition ),
                                            LocalSource( mPropertyPosition ),
                                            OvershootXConstraint(mMaxOvershoot.x) );
-    mScrollMainInternalOvershootXConstraint = self.ApplyConstraint(constraint);
+    mScrollMainInternalOvershootXConstraint = self.ApplyConstraint( constraint );
+
     constraint = Constraint::New<float>( mPropertyOvershootY,
                                            LocalSource( mPropertyPrePosition ),
                                            LocalSource( mPropertyPosition ),
                                            OvershootYConstraint(mMaxOvershoot.y) );
     constraint = Constraint::New<float>( mPropertyOvershootY,
                                            LocalSource( mPropertyPrePosition ),
                                            LocalSource( mPropertyPosition ),
                                            OvershootYConstraint(mMaxOvershoot.y) );
-    mScrollMainInternalOvershootYConstraint = self.ApplyConstraint(constraint);
+    mScrollMainInternalOvershootYConstraint = self.ApplyConstraint( constraint );
+  }
+  else
+  {
+    self.SetProperty(mPropertyOvershootX, 0.0f);
+    self.SetProperty(mPropertyOvershootY, 0.0f);
   }
 }
 
   }
 }
 
@@ -2556,85 +2766,6 @@ void ScrollView::SetInternalConstraints()
   ApplyConstraintToBoundActors(constraint);
 }
 
   ApplyConstraintToBoundActors(constraint);
 }
 
-void ScrollView::SetOvershootToOrigin()
-{
-  // Clear Snap animation if exists.
-  if(mSnapOvershootAnimation)
-  {
-    mSnapOvershootAnimation.FinishedSignal().Disconnect(this, &ScrollView::OnSnapOvershootAnimationFinished);
-    mSnapOvershootAnimation.Stop();
-    mSnapOvershootAnimation.Clear();
-    mSnapOvershootAnimation = NULL;
-  }
-  SetOvershootConstraintsEnabled(false);
-  Self().SetProperty(mPropertyOvershootX, 0.0f);
-  Self().SetProperty(mPropertyOvershootY, 0.0f);
-}
-
-void ScrollView::AnimateOvershootToOrigin(float xDelay, float yDelay)
-{
-  if( IsScrollComponentEnabled(Toolkit::Scrollable::OvershootIndicator) )
-  {
-    if(xDelay < Math::MACHINE_EPSILON_1)
-    {
-      // kick start animation to 0
-      Self().SetProperty(mPropertyOvershootX, 0.0f);
-    }
-    if(yDelay < Math::MACHINE_EPSILON_1)
-    {
-      // kick start animation to 0
-      Self().SetProperty(mPropertyOvershootY, 0.0f);
-    }
-    return;
-  }
-  // When we need to animate overshoot to 0
-  if(mSnapOvershootDuration > Math::MACHINE_EPSILON_1)
-  {
-    Actor self = Self();
-    // Clear Snap animation if exists.
-    if(mSnapOvershootAnimation)
-    {
-      mSnapOvershootAnimation.FinishedSignal().Disconnect( this, &ScrollView::OnSnapOvershootAnimationFinished );
-      mSnapOvershootAnimation.Stop();
-      mSnapOvershootAnimation.Clear();
-      mSnapOvershootAnimation = NULL;
-    }
-    if(!mSnapXAnimation && mScrollMainInternalOvershootXConstraint)
-    {
-      // need to remove the x overshoot constraint now or it will override animation to 0
-      Self().RemoveConstraint(mScrollMainInternalOvershootXConstraint);
-      mScrollMainInternalOvershootXConstraint.Reset();
-      mScrollMainInternalOvershootXConstraint = 0;
-    }
-    if(!mSnapYAnimation && mScrollMainInternalOvershootYConstraint)
-    {
-      // need to remove the y overshoot constraint now or it will override animation to 0
-      Self().RemoveConstraint(mScrollMainInternalOvershootYConstraint);
-      mScrollMainInternalOvershootYConstraint.Reset();
-      mScrollMainInternalOvershootYConstraint = 0;
-    }
-    // setup the new overshoot to 0 animation
-    float totalDuration = (xDelay > yDelay ? xDelay : yDelay) + mSnapOvershootDuration;
-    mSnapOvershootAnimation = Animation::New(totalDuration);
-    mSnapOvershootAnimation.FinishedSignal().Connect( this, &ScrollView::OnSnapOvershootAnimationFinished );
-
-    mSnapOvershootAnimation.AnimateTo( Property(self, mPropertyOvershootX), 0.0f, mSnapOvershootAlphaFunction, TimePeriod(xDelay, mSnapOvershootDuration) );
-    mSnapOvershootAnimation.AnimateTo( Property(self, mPropertyOvershootY), 0.0f, mSnapOvershootAlphaFunction, TimePeriod(yDelay, mSnapOvershootDuration) );
-
-    mSnapOvershootAnimation.SetDuration(totalDuration);
-    mSnapOvershootAnimation.Play();
-  }
-  else
-  {
-    SetOvershootToOrigin();
-  }
-}
-
-void ScrollView::OnSnapOvershootAnimationFinished( Animation& source )
-{
-  mSnapOvershootAnimation = NULL;
-}
-
 void ScrollView::StartRefreshTimer()
 {
   if(mRefreshIntervalMilliseconds > 0)
 void ScrollView::StartRefreshTimer()
 {
   if(mRefreshIntervalMilliseconds > 0)
index 89c4c48..1591fb6 100644 (file)
@@ -78,6 +78,19 @@ public:
     LockNone          ///< Locking is set to none (free panning).
   };
 
     LockNone          ///< Locking is set to none (free panning).
   };
 
+  enum ScrollStateFlag
+  {
+    AnimatingInternalX = 0x01, ///< animating mPropertyX due to externally requested ScrollTo or internal snapping operation
+    AnimatingInternalY = 0x02, ///< animating mPropertyY due to externally requested ScrollTo or internal snapping operation
+    SnappingInternalX  = 0x04, ///< snapping mPropertyX back to mPropertyPreScroll x value to remove x overshoot over time
+    SnappingInternalY  = 0x08, ///< snapping mPropertyY back to mPropertyPreScroll y value to remove y overshoot over time
+  };
+
+  static const unsigned int SCROLL_X_STATE_MASK = AnimatingInternalX | SnappingInternalX;
+  static const unsigned int SCROLL_Y_STATE_MASK = AnimatingInternalY | SnappingInternalY;
+  static const unsigned int SCROLL_ANIMATION_FLAGS = AnimatingInternalX | AnimatingInternalY;
+  static const unsigned int SNAP_ANIMATION_FLAGS = SnappingInternalX | SnappingInternalY;
+
 public:
 
   /**
 public:
 
   /**
@@ -333,6 +346,11 @@ public:
   Vector3 GetCurrentScrollPosition() const;
 
   /**
   Vector3 GetCurrentScrollPosition() const;
 
   /**
+   * @copydoc Toolkit::ScrollView::SetScrollPosition
+   */
+  void SetScrollPosition(const Vector3& position);
+
+  /**
    * @copydoc Toolkit::ScrollView::GetCurrentScrollScale
    */
   Vector3 GetCurrentScrollScale() const;
    * @copydoc Toolkit::ScrollView::GetCurrentScrollScale
    */
   Vector3 GetCurrentScrollScale() const;
@@ -439,6 +457,13 @@ public:
   void StopAnimation(void);
 
   /**
   void StopAnimation(void);
 
   /**
+   * Stops the input animation
+   *
+   * @param[in] the animation to stop
+   */
+  void StopAnimation(Animation& animation);
+
+  /**
    * Animates to position/scale/rotation transform.
    *
    * @param[in] position The position to animate to
    * Animates to position/scale/rotation transform.
    *
    * @param[in] position The position to animate to
@@ -513,6 +538,11 @@ private: // private overriden functions from CustomActorImpl and Controls
   virtual void OnChildRemove(Actor& child);
 
   /**
   virtual void OnChildRemove(Actor& child);
 
   /**
+   * @copydoc Dali::CustomActorImpl::OnPropertySet( Property::Index index, Property::Value propertyValue )
+   */
+  virtual void OnPropertySet( Property::Index index, Property::Value propertyValue );
+
+  /**
    * From CustomActorImpl; called after a touch-signal is received by the owning actor.
    *
    * We don't listen to these events as content within the contain may consume events.
    * From CustomActorImpl; called after a touch-signal is received by the owning actor.
    *
    * We don't listen to these events as content within the contain may consume events.
@@ -574,20 +604,70 @@ private:
   /**
    * Called whenever a snap animation has completed
    * @param[in] source the Animation instance that has completed.
   /**
    * Called whenever a snap animation has completed
    * @param[in] source the Animation instance that has completed.
+   * Resets all scrolling animations and states, leaving current scroll position at mPropertyPosition
+   */
+  void ResetScrolling();
+
+  /**
+   * Updates mScrollInternalPosition, mScrollPrePosition and mScrollPostPosition from their property counterparts
+   */
+  void UpdateLocalScrollProperties();
+
+  /**
+   * Makes sure scroll values are ready for animated scrolling
+   */
+  void PreAnimatedScrollSetup();
+
+  /**
+   * Finish an animated scroll, ensuring all scroll properties are updated
+   * and synchronised
+   */
+  void FinaliseAnimatedScroll();
+
+  /**
+   * Animates the internal x property to the given value
+   *
+   * @param[in] position The X position to animate to
+   * @param[in] duration The time in seconds for animation
+   * @param[in] alpha The alpha function to use for animating
    */
    */
-  void OnSnapAnimationFinished( Animation& source );
+  void AnimateInternalXTo( float position, float duration, AlphaFunction alpha );
+
+  /**
+   * Animates the internal y property to the given value
+   *
+   * @param[in] position The Y position to animate to
+   * @param[in] duration The time in seconds for animation
+   * @param[in] alpha The alpha function to use for animating
+   */
+  void AnimateInternalYTo( float position, float duration, AlphaFunction alpha );
 
   /**
    * Called whenever a snap animation on the x-axis has completed
    * @param[in] source the Animation instance that has completed.
    */
 
   /**
    * Called whenever a snap animation on the x-axis has completed
    * @param[in] source the Animation instance that has completed.
    */
-  void OnSnapXAnimationFinished( Animation& source );
+  void OnScrollAnimationFinished( Animation& source );
 
   /**
 
   /**
-   * Called whenever a snap animation on the y-axis has completed
+   * Called when either the X or Y internal scroll positions have finished snapping back to mPropertyPrePosition
+   *
    * @param[in] source the Animation instance that has completed.
    */
    * @param[in] source the Animation instance that has completed.
    */
-  void OnSnapYAnimationFinished( Animation& source );
+  void OnSnapInternalPositionFinished( Animation& source );
+
+  /**
+   * Called whenever a snap animation on the x-axis has completed and we need to snap pre scroll
+   * position to our clamped position
+   * @param[in] position The x position to snap pre scroll property to
+   */
+  void SnapInternalXTo( float position );
+
+  /**
+   * Called whenever a snap animation on the y-axis has completed and we need to snap pre scroll
+   * position to our clamped position
+   * @param[in] position The y position to snap pre scroll property to
+   */
+  void SnapInternalYTo( float position );
 
   /**
    * This is called internally whenever the Scroll Rulers are
 
   /**
    * This is called internally whenever the Scroll Rulers are
@@ -610,7 +690,7 @@ private:
    * @param[in] scaleDelta average scale delta from base scale (1)
    * @param[in] rotationDelta average rotation delta from base angle (0)
    */
    * @param[in] scaleDelta average scale delta from base scale (1)
    * @param[in] rotationDelta average rotation delta from base angle (0)
    */
-  void GestureContinuing(Vector2 panDelta, Vector2 scaleDelta, float rotationDelta);
+  void GestureContinuing(const Vector2& panDelta, const Vector2& scaleDelta, float rotationDelta);
 
   /**
    * Called upon pan gesture event.
 
   /**
    * Called upon pan gesture event.
@@ -647,22 +727,6 @@ private:
   void FinishTransform();
 
   /**
   void FinishTransform();
 
   /**
-   * Sets Overshoot to origin / cancels animation
-   */
-  void SetOvershootToOrigin();
-
-  /**
-   * Animates Overshoot to origin
-   */
-  void AnimateOvershootToOrigin(float xDelay, float yDelay);
-
-  /**
-   * Called whenever a snap overshoot animation has completed.
-   * @param[in] source the Animation instance that has completed.
-   */
-  void OnSnapOvershootAnimationFinished( Animation& source );
-
-  /**
    * Returns overshoot vector based on current position
    *
    * Overshoot vector is defined as how far outside of bounds
    * Returns overshoot vector based on current position
    *
    * Overshoot vector is defined as how far outside of bounds
@@ -818,13 +882,8 @@ private:
 
 private:
 
 
 private:
 
-  bool mInitialized;
-  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
 
   unsigned long mTouchDownTime;         ///< The touch down time
 
-  bool mSensitive;                      ///< Scroll Sensitivity Flag.
-
   int mGestureStackDepth;               ///< How many gestures are currently occuring.
   Vector2 mGestureReferencePosition;    ///< Point where scaling should occur from.
   Vector2 mPinchGestureLastPosition;
   int mGestureStackDepth;               ///< How many gestures are currently occuring.
   Vector2 mGestureReferencePosition;    ///< Point where scaling should occur from.
   Vector2 mPinchGestureLastPosition;
@@ -834,9 +893,11 @@ private:
   Vector3 mScaleDelta;                  ///< Amount currently scaled.
   float mRotationDelta;                 ///< Amount currently rotated.
 
   Vector3 mScaleDelta;                  ///< Amount currently scaled.
   float mRotationDelta;                 ///< Amount currently rotated.
 
+  unsigned int mScrollStateFlags;       ///< flags indicating current state of scrolling
   // Scroll delegate pre and post position/scale/rotation properties...
   // Scroll delegate pre and post position/scale/rotation properties...
-  Vector3 mScrollPrePosition;           ///< Scroll delegate pre-position
-  Vector3 mScrollPostPosition;          ///< Scroll delegate post-position (affected by current touch)
+  Vector3 mScrollPrePosition;           ///< Wrapped scroll position, but not clamped
+  Vector3 mScrollPostPosition;          ///< Wrapped and clamped, this is the final scroll position used
+  Vector3 mScrollTargetPosition;        ///< Final target position for an animated scroll
   Vector3 mScrollPreScale;              ///< Scroll delegate pre-scale
   Vector3 mScrollPostScale;             ///< Scroll delegate post-scale (affected by current touch)
   float mScrollPreRotation;             ///< Scroll delegate pre-rotation
   Vector3 mScrollPreScale;              ///< Scroll delegate pre-scale
   Vector3 mScrollPostScale;             ///< Scroll delegate post-scale (affected by current touch)
   float mScrollPreRotation;             ///< Scroll delegate pre-rotation
@@ -849,18 +910,13 @@ private:
   RulerPtr mRulerScaleX;
   RulerPtr mRulerScaleY;
   RulerPtr mRulerRotation;
   RulerPtr mRulerScaleX;
   RulerPtr mRulerScaleY;
   RulerPtr mRulerRotation;
-  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.
-  bool mAxisAutoLock;                   ///< Whether to automatically lock axis when panning.
+
   unsigned int mMinTouchesForPanning;   ///< Minimum number of touches for panning to be used.
   unsigned int mMaxTouchesForPanning;   ///< Maximum number of touches for panning to be used.
 
   unsigned int mMinTouchesForPanning;   ///< Minimum number of touches for panning to be used.
   unsigned int mMaxTouchesForPanning;   ///< Maximum number of touches for panning to be used.
 
-  Animation mSnapAnimation;
-  Animation mSnapXAnimation;             ///< Animates from current x-axis position to the snapped (or scrolled) x-axis position.
-  Animation mSnapYAnimation;             ///< Animates from current y-axis position to the snapped (or scrolled) y-axis position.
-  Animation mSnapOvershootAnimation;    ///< Animates scroll-overshoot from current position to 0,0 based on specified easing equation.
+  Animation mSnapAnimation;             ///< Used to animate rotation and scaling of scroll properties
+  Animation mInternalXAnimation;        ///< Animates mPropertyX to a snap position or application requested scroll position
+  Animation mInternalYAnimation;        ///< Animates mPropertyY to a snap position or application requested scroll position
 
 
   Vector2 mLastVelocity;                ///< Record the last velocity from PanGesture (Finish event doesn't have correct velocity)
 
 
   Vector2 mLastVelocity;                ///< Record the last velocity from PanGesture (Finish event doesn't have correct velocity)
@@ -871,14 +927,13 @@ private:
   Timer mRefreshTimer;                  ///< Refresh timer is used to provide the Application developer with updates as animations run.
   int mRefreshIntervalMilliseconds;     ///< Refresh timer interval.
 
   Timer mRefreshTimer;                  ///< Refresh timer is used to provide the Application developer with updates as animations run.
   int mRefreshIntervalMilliseconds;     ///< Refresh timer interval.
 
-  bool mAlterChild;                     ///< Internal flag to control behavior of OnChildAdd/OnChildRemove when Adding internal Actors.
   Actor mInternalActor;                 ///< Internal actor (we keep internal actors in here e.g. scrollbars, so we can ignore it in searches)
 
   ScrollViewEffectContainer mEffects;   ///< Container keeping track of all the applied effects.
 
   float     mOvershootDelay;                    ///< Time to wait for input before reducing overshoot back to 0
   Vector2   mMaxOvershoot;                      ///< Number of scrollable pixels that will take overshoot from 0.0f to 1.0f
   Actor mInternalActor;                 ///< Internal actor (we keep internal actors in here e.g. scrollbars, so we can ignore it in searches)
 
   ScrollViewEffectContainer mEffects;   ///< Container keeping track of all the applied effects.
 
   float     mOvershootDelay;                    ///< Time to wait for input before reducing overshoot back to 0
   Vector2   mMaxOvershoot;                      ///< Number of scrollable pixels that will take overshoot from 0.0f to 1.0f
-  bool      mDefaultMaxOvershoot;               ///< Whether to use default max overshoot or application defined one
+  Vector2   mUserMaxOvershoot;                  ///< Set by user, allows overriding of default max overshoot for the scroll indicator
   float     mSnapOvershootDuration;             ///< Duration for overshoot snapping back to Vector3::ZERO
   AlphaFunction mSnapOvershootAlphaFunction;    ///< AlphaFunction to be used for this overshoot.
 
   float     mSnapOvershootDuration;             ///< Duration for overshoot snapping back to Vector3::ZERO
   AlphaFunction mSnapOvershootAlphaFunction;    ///< AlphaFunction to be used for this overshoot.
 
@@ -910,7 +965,20 @@ private:
 
   Toolkit::ScrollView::SnapStartedSignalV2 mSnapStartedSignalV2;
 
 
   Toolkit::ScrollView::SnapStartedSignalV2 mSnapStartedSignalV2;
 
-  bool mInAccessibilityPan : 1; // With AccessibilityPan its easier to move between snap positions
+  bool mInAccessibilityPan : 1;           ///< With AccessibilityPan its easier to move between snap positions
+  bool mInitialized:1;
+  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
+  bool mSensitive:1;                      ///< Scroll Sensitivity Flag.
+  bool mTouchDownTimeoutReached:1;        ///< Indicates when down event timeout occured without corresponding up event (touch still down)
+  bool mActorAutoSnapEnabled:1;           ///< Whether to automatically snap to closest actor.
+  bool mAutoResizeContainerEnabled:1;     ///< Whether to automatically resize container (affects RulerDomain's on X/Y axes)
+  bool mWrapMode:1;                       ///< Whether to wrap contents based on container size.
+  bool mAxisAutoLock:1;                   ///< Whether to automatically lock axis when panning.
+  bool mAlterChild:1;                     ///< Internal flag to control behavior of OnChildAdd/OnChildRemove when Adding internal Actors.
+  bool mDefaultMaxOvershoot:1;            ///< Whether to use default max overshoot or application defined one
+  bool mUserSetPosition:1;                ///< SetScrollPosition has been called, return this position until internals get control of scroll position again
 };
 
 } // namespace Internal
 };
 
 } // namespace Internal
index 899b6e6..1a3b893 100755 (executable)
@@ -179,8 +179,7 @@ struct ScrollDropoffTwistRotationConstraint
                         const PropertyInput& scrollablePositionProperty,
                         const PropertyInput& scrollOvershootXProperty,
                         const PropertyInput& scrollOvershootYProperty,
                         const PropertyInput& scrollablePositionProperty,
                         const PropertyInput& scrollOvershootXProperty,
                         const PropertyInput& scrollOvershootYProperty,
-                        const PropertyInput& pageSizeProperty,
-                        const PropertyInput& activateProperty)
+                        const PropertyInput& pageSizeProperty)
   {
     const Vector3& position = actorPositionProperty.GetVector3();
     const Vector3& parentPosition = scrollablePositionProperty.GetVector3();
   {
     const Vector3& position = actorPositionProperty.GetVector3();
     const Vector3& parentPosition = scrollablePositionProperty.GetVector3();
@@ -192,13 +191,6 @@ struct ScrollDropoffTwistRotationConstraint
       return current;
     }
 
       return current;
     }
 
-    const float& activate = activateProperty.GetFloat();
-
-    if(activate < Math::MACHINE_EPSILON_0)
-    {
-      return current;
-    }
-
     // get distance from centre of scrollable container
     Vector2 distance = position.GetVectorXY() - parentPosition.GetVectorXY();
 
     // get distance from centre of scrollable container
     Vector2 distance = position.GetVectorXY() - parentPosition.GetVectorXY();
 
@@ -275,19 +267,11 @@ struct ScrollTwistRotationConstraint
    */
   Quaternion operator()(const Quaternion& current,
                         const PropertyInput& scrollOvershootXProperty,
    */
   Quaternion operator()(const Quaternion& current,
                         const PropertyInput& scrollOvershootXProperty,
-                        const PropertyInput& scrollOvershootYProperty,
-                        const PropertyInput& activateProperty)
+                        const PropertyInput& scrollOvershootYProperty)
   {
     const Vector2 overshoot(scrollOvershootXProperty.GetFloat(), scrollOvershootYProperty.GetFloat());
 
   {
     const Vector2 overshoot(scrollOvershootXProperty.GetFloat(), scrollOvershootYProperty.GetFloat());
 
-    if(fabsf(overshoot.x) < Math::MACHINE_EPSILON_0 && fabsf(overshoot.y) < Math::MACHINE_EPSILON_0)
-    {
-      return current;
-    }
-
-    const float& activate = activateProperty.GetFloat();
-
-    if(activate < Math::MACHINE_EPSILON_0)
+    if( fabsf(overshoot.x) < Math::MACHINE_EPSILON_0 && fabsf(overshoot.y) < Math::MACHINE_EPSILON_0 )
     {
       return current;
     }
     {
       return current;
     }
@@ -543,7 +527,6 @@ void ScrollViewTwistEffect::Apply(Actor child)
                                                 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_OVERSHOOT_X_PROPERTY_NAME ) ),
                                                 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_OVERSHOOT_Y_PROPERTY_NAME ) ),
                                                 Source(scrollView, Actor::SIZE ),
                                                 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_OVERSHOOT_X_PROPERTY_NAME ) ),
                                                 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_OVERSHOOT_Y_PROPERTY_NAME ) ),
                                                 Source(scrollView, Actor::SIZE ),
-                                                Source(scrollView, scrollView.GetPropertyIndex( EFFECT_ACTIVATE) ),
                                                 ScrollDropoffTwistRotationConstraint(mMaxSwingAngle, mDropOff, mDropOffDistance, mDropOffFunction) );
     }
     else
                                                 ScrollDropoffTwistRotationConstraint(mMaxSwingAngle, mDropOff, mDropOffDistance, mDropOffFunction) );
     }
     else
@@ -551,7 +534,6 @@ void ScrollViewTwistEffect::Apply(Actor child)
       constraint = Constraint::New<Quaternion>( Actor::ROTATION,
                                                 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_OVERSHOOT_X_PROPERTY_NAME ) ),
                                                 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_OVERSHOOT_Y_PROPERTY_NAME ) ),
       constraint = Constraint::New<Quaternion>( Actor::ROTATION,
                                                 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_OVERSHOOT_X_PROPERTY_NAME ) ),
                                                 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_OVERSHOOT_Y_PROPERTY_NAME ) ),
-                                                Source(scrollView, scrollView.GetPropertyIndex( EFFECT_ACTIVATE) ),
                                                 ScrollTwistRotationConstraint(mMaxSwingAngle) );
     }
     constraint.SetRemoveAction( Constraint::Discard );
                                                 ScrollTwistRotationConstraint(mMaxSwingAngle) );
     }
     constraint.SetRemoveAction( Constraint::Discard );
@@ -584,8 +566,10 @@ void ScrollViewTwistEffect::OnAttach(Toolkit::ScrollView& scrollView)
     mPropertyTime = SafeRegisterProperty( scrollView, EFFECT_TIME, 0.0f );
     mPropertyReference = SafeRegisterProperty( scrollView, EFFECT_REFERENCE, Vector3::ZERO );
     mPropertyDepth = SafeRegisterProperty( scrollView, EFFECT_DEPTH, 0.0f);
     mPropertyTime = SafeRegisterProperty( scrollView, EFFECT_TIME, 0.0f );
     mPropertyReference = SafeRegisterProperty( scrollView, EFFECT_REFERENCE, Vector3::ZERO );
     mPropertyDepth = SafeRegisterProperty( scrollView, EFFECT_DEPTH, 0.0f);
-    mPropertyActivate = SafeRegisterProperty(scrollView, EFFECT_ACTIVATE, 1.0f);
+    mPropertyActivate = SafeRegisterProperty(scrollView, EFFECT_ACTIVATE, 0.0f);
   }
   }
+  // currently cant change overshoot snap back duration, use the constant one from ScrollView
+  mActivationTime = Toolkit::ScrollView::DEFAULT_SNAP_OVERSHOOT_DURATION;
 
   // Connect to the scroll view signals
   scrollView.ScrollStartedSignal().Connect(this, &ScrollViewTwistEffect::OnScrollStart);
 
   // Connect to the scroll view signals
   scrollView.ScrollStartedSignal().Connect(this, &ScrollViewTwistEffect::OnScrollStart);
@@ -677,7 +661,7 @@ void ScrollViewTwistEffect::OnScrollComplete( const Vector3& position )
   }
   Actor scrollView = GetScrollView();
   scrollView.SetProperty(mPropertyActivate, 1.0f);
   }
   Actor scrollView = GetScrollView();
   scrollView.SetProperty(mPropertyActivate, 1.0f);
-  mActivateAnimation = Animation::New(DELAY);
+  mActivateAnimation = Animation::New(mActivationTime);
   mActivateAnimation.AnimateTo( Property(scrollView, mPropertyActivate), 0.0f, AlphaFunctions::Linear);
   mActivateAnimation.FinishedSignal().Connect(this, &ScrollViewTwistEffect::OnActivateAnimationFinished);
   mActivateAnimation.Play();
   mActivateAnimation.AnimateTo( Property(scrollView, mPropertyActivate), 0.0f, AlphaFunctions::Linear);
   mActivateAnimation.FinishedSignal().Connect(this, &ScrollViewTwistEffect::OnActivateAnimationFinished);
   mActivateAnimation.Play();
index 819c238..d98a086 100644 (file)
@@ -185,6 +185,7 @@ private:
 
   ushort mFlags;
   Animation mAnimation;                         ///< Animation Timer to drive the twist effect constraint.
 
   ushort mFlags;
   Animation mAnimation;                         ///< Animation Timer to drive the twist effect constraint.
+  float     mActivationTime;                    ///< Time taken for overshoot to reach zero, which is the time we need to allow effect to be active
   Animation mActivateAnimation;
   Property::Index mPropertyTime;                ///< Time property used by twist effect constraint to calculate timePassed.
   bool mEnableEffect;                           ///< flag that decide whether enable or disable the twist effect.
   Animation mActivateAnimation;
   Property::Index mPropertyTime;                ///< Time property used by twist effect constraint to calculate timePassed.
   bool mEnableEffect;                           ///< flag that decide whether enable or disable the twist effect.
index c130d96..71e8be8 100644 (file)
@@ -67,6 +67,8 @@ Scrollable::Scrollable()
   mPropertyPositionMin(Property::INVALID_INDEX),
   mPropertyPositionMax(Property::INVALID_INDEX),
   mPropertyScrollDirection(Property::INVALID_INDEX),
   mPropertyPositionMin(Property::INVALID_INDEX),
   mPropertyPositionMax(Property::INVALID_INDEX),
   mPropertyScrollDirection(Property::INVALID_INDEX),
+  mPropertyCanScrollVertical(Property::INVALID_INDEX),
+  mPropertyCanScrollHorizontal(Property::INVALID_INDEX),
   mOvershootEnabled(false)
 {
 }
   mOvershootEnabled(false)
 {
 }
index 83ff5ce..10d9f1d 100644 (file)
@@ -213,7 +213,7 @@ private:
   typedef ComponentContainer::iterator ComponentIter;
 
   ComponentContainer mComponents;  ///< ScrollComponent (such as a scrollbar/page indicator/status)
   typedef ComponentContainer::iterator ComponentIter;
 
   ComponentContainer mComponents;  ///< ScrollComponent (such as a scrollbar/page indicator/status)
-  bool mOvershootEnabled;
+  bool mOvershootEnabled:1;
 };
 
 } // namespace Internal
 };
 
 } // namespace Internal
index e333902..69db853 100644 (file)
@@ -280,7 +280,7 @@ const std::string ScrollView::SCROLL_START_PAGE_POSITION_PROPERTY_NAME( "scroll-
 
 const float ScrollView::DEFAULT_SLOW_SNAP_ANIMATION_DURATION(0.5f);
 const float ScrollView::DEFAULT_FAST_SNAP_ANIMATION_DURATION(0.25f);
 
 const float ScrollView::DEFAULT_SLOW_SNAP_ANIMATION_DURATION(0.5f);
 const float ScrollView::DEFAULT_FAST_SNAP_ANIMATION_DURATION(0.25f);
-const float ScrollView::DEFAULT_SNAP_OVERSHOOT_DURATION(1.0f);
+const float ScrollView::DEFAULT_SNAP_OVERSHOOT_DURATION(0.5f);
 const float ScrollView::DEFAULT_MAX_OVERSHOOT(100.0f);  // 100 pixels
 
 const float ScrollView::DEFAULT_AXIS_AUTO_LOCK_GRADIENT(0.36f);
 const float ScrollView::DEFAULT_MAX_OVERSHOOT(100.0f);  // 100 pixels
 
 const float ScrollView::DEFAULT_AXIS_AUTO_LOCK_GRADIENT(0.36f);
@@ -503,6 +503,11 @@ Vector3 ScrollView::GetCurrentScrollPosition() const
   return GetImpl(*this).GetCurrentScrollPosition();
 }
 
   return GetImpl(*this).GetCurrentScrollPosition();
 }
 
+void ScrollView::SetScrollPosition(const Vector3& position)
+{
+  GetImpl(*this).SetScrollPosition(position);
+}
+
 Vector3 ScrollView::GetCurrentScrollScale() const
 {
   return GetImpl(*this).GetCurrentScrollScale();
 Vector3 ScrollView::GetCurrentScrollScale() const
 {
   return GetImpl(*this).GetCurrentScrollScale();
index 8ef9fca..963955c 100644 (file)
@@ -859,6 +859,14 @@ public:
   Vector3 GetCurrentScrollPosition() const;
 
   /**
   Vector3 GetCurrentScrollPosition() const;
 
   /**
+   * @brief Sets the current scroll position, overriding current scroll animations. If panning is currently taking place
+   *        SetScrollPosition will have no effect. Try to ensure panning has stopped before calling this function.
+   *
+   * @param[in] position The new scroll position to set.
+   */
+  void SetScrollPosition(const Vector3& position);
+
+  /**
    * @brief Retrieves current scroll scale.
    *
    * @returns The current scroll scale.
    * @brief Retrieves current scroll scale.
    *
    * @returns The current scroll scale.