Changes after Set/Get synchronous behaviour of registered animatable & custom properties
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / scrollable / scroll-view / scroll-overshoot-indicator-impl.cpp
index 4df873c..8652ac4 100644 (file)
-//
-// Copyright (c) 2014 Samsung Electronics Co., Ltd.
-//
-// Licensed under the Flora License, Version 1.0 (the License);
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://floralicense.org/license/
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an AS IS BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
 #include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-overshoot-indicator-impl.h>
 
 // EXTERNAL INCLUDES
-#include <boost/bind.hpp>
+#include <dali/devel-api/object/handle-devel.h>
 
+// INTERNAL INCLUDES
 #include <dali-toolkit/internal/controls/scrollable/scrollable-impl.h>
+#include <dali-toolkit/internal/controls/scrollable/bouncing-effect-actor.h>
 #include <dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view.h>
 
 using namespace Dali;
 
-namespace Dali
-{
-
-namespace Toolkit
-{
-
-namespace Internal
+namespace
 {
 
-const char* OVERSHOOT_OVERLAY_IMAGE_PATH = DALI_IMAGE_DIR "scroll_overshoot.png";
-const char* OVERSHOOT_OVERLAY_RIPPLE_IMAGE_PATH = DALI_IMAGE_DIR "overshoot_ripple.png";
-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 OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD = 180.0f;
 
-ScrollOvershootIndicator::ScrollOvershootIndicator(Scrollable& scrollable) :
-  mScrollable(scrollable),
-  mEffectX(NULL),
-  mEffectY(NULL)
+// local helper function to resize the height of the bounce actor
+float GetBounceActorHeight( float width, float defaultHeight )
 {
+  return (width > OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD) ? defaultHeight : defaultHeight * 0.5f;
 }
 
-ScrollOvershootIndicator::~ScrollOvershootIndicator()
-{
+const float MAX_OVERSHOOT_NOTIFY_AMOUNT = 0.99f;                     // 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_0;  // minimum amount to set notification for reduced overshoot, beyond this we just wait for it to increase again
+const float OVERSHOOT_NOTIFY_STEP = 0.01f;                          // amount to set notifications beyond current overshoot value
 
 }
 
-ScrollOvershootIndicator* ScrollOvershootIndicator::New(Scrollable& scrollable)
+namespace Dali
 {
-  ScrollOvershootIndicator* scrollOvershootPtr = new ScrollOvershootIndicator(scrollable);
-  return scrollOvershootPtr;
-}
 
-void ScrollOvershootIndicator::Enable(bool enable)
-{
-  if(enable)
-  {
-    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);
-  }
-  else
-  {
-    if(mEffectX)
-    {
-      mEffectX->Remove(mScrollable);
-    }
-    if(mEffectY)
-    {
-      mEffectY->Remove(mScrollable);
-    }
-  }
-}
-
-void ScrollOvershootIndicator::Reset()
+namespace Toolkit
 {
-  mEffectX->Reset();
-  mEffectY->Reset();
-}
 
-ScrollOvershootEffect::ScrollOvershootEffect(bool vertical) :
-    mVertical(vertical)
+namespace Internal
 {
 
-}
-
-ScrollOvershootEffectGradient::ScrollOvershootEffectGradient(bool vertical) :
-    ScrollOvershootEffect(vertical),
-    mMaxOvershootImageSize(DEFAULT_MAX_OVERSHOOT_HEIGHT)
+ScrollOvershootIndicator::ScrollOvershootIndicator() :
+  mEffectX(NULL),
+  mEffectY(NULL)
 {
-  Image overshootImage = Image::New( OVERSHOOT_OVERLAY_IMAGE_PATH );
-  mOvershootImage = ImageActor::New( overshootImage );
-  mOvershootImage.SetParentOrigin(ParentOrigin::TOP_LEFT);
-  mOvershootImage.SetAnchorPoint(AnchorPoint::TOP_LEFT);
-  mOvershootImage.SetDrawMode(DrawMode::OVERLAY);
 }
 
-void ScrollOvershootEffectGradient::Apply(Scrollable& scrollable)
+ScrollOvershootIndicator::~ScrollOvershootIndicator()
 {
-  Actor scrollableActor = scrollable.Self();
-  int overshootXPropertyIndex = scrollableActor.GetPropertyIndex(Toolkit::ScrollView::SCROLL_OVERSHOOT_X_PROPERTY_NAME);
-  int overshootYPropertyIndex = scrollableActor.GetPropertyIndex(Toolkit::ScrollView::SCROLL_OVERSHOOT_Y_PROPERTY_NAME);
-
-  Constraint constraint = Constraint::New<Vector3>( Actor::SIZE,
-                                                    Source( scrollableActor, overshootXPropertyIndex ),
-                                                    Source( scrollableActor, overshootYPropertyIndex ),
-                                                    Source( scrollableActor, Actor::SIZE ),
-                                                    boost::bind( &ScrollOvershootEffectGradient::SizeConstraint, this, _1, _2, _3, _4) );
-  mSizeConstraint = mOvershootImage.ApplyConstraint(constraint);
-
-  constraint = Constraint::New<Quaternion>( Actor::ROTATION,
-                                            Source( scrollableActor, overshootXPropertyIndex ),
-                                            Source( scrollableActor, overshootYPropertyIndex ),
-                                            boost::bind( &ScrollOvershootEffectGradient::RotationConstraint, this, _1, _2, _3) );
-  mRotationConstraint = mOvershootImage.ApplyConstraint(constraint);
-
-  constraint = Constraint::New<Vector3>( Actor::POSITION,
-                                         Source( scrollableActor, Actor::SIZE ),
-                                         Source( scrollableActor, overshootXPropertyIndex ),
-                                         Source( scrollableActor, overshootYPropertyIndex ),
-                                         boost::bind( &ScrollOvershootEffectGradient::PositionConstraint, this, _1, _2, _3, _4) );
-  mPositionConstraint = mOvershootImage.ApplyConstraint(constraint);
-
-  constraint = Constraint::New<bool>( Actor::VISIBLE,
-                                      Source( scrollableActor, IsVertical() ? scrollableActor.GetPropertyIndex(Scrollable::SCROLLABLE_CAN_SCROLL_VERTICAL) : scrollableActor.GetPropertyIndex(Scrollable::SCROLLABLE_CAN_SCROLL_HORIZONTAL)),
-                                      boost::bind( &ScrollOvershootEffectGradient::VisibilityConstraint, this, _1, _2) );
-  mVisibilityConstraint = mOvershootImage.ApplyConstraint(constraint);
-  scrollable.AddOverlay(mOvershootImage);
-  SetPropertyNotifications(scrollableActor);
-}
 
-void ScrollOvershootEffectGradient::Remove(Scrollable& scrollable)
-{
-  if(mOvershootImage)
-  {
-    if(mSizeConstraint)
-    {
-      mOvershootImage.RemoveConstraint(mSizeConstraint);
-      mSizeConstraint = NULL;
-    }
-    if(mRotationConstraint)
-    {
-      mOvershootImage.RemoveConstraint(mRotationConstraint);
-      mRotationConstraint = NULL;
-    }
-    if(mPositionConstraint)
-    {
-      mOvershootImage.RemoveConstraint(mPositionConstraint);
-      mPositionConstraint = NULL;
-    }
-    if(mVisibilityConstraint)
-    {
-      mOvershootImage.RemoveConstraint(mVisibilityConstraint);
-      mVisibilityConstraint = NULL;
-    }
-    scrollable.RemoveOverlay(mOvershootImage);
-  }
 }
 
-Vector3 ScrollOvershootEffectGradient::SizeConstraint(const Vector3& current,
-    const PropertyInput& overshootPropertyX, const PropertyInput& overshootPropertyY,
-    const PropertyInput& parentSizeProperty)
+ScrollOvershootIndicator* ScrollOvershootIndicator::New()
 {
-  float overshootx = overshootPropertyX.GetFloat();
-  float overshooty = overshootPropertyY.GetFloat();
-  const Vector3 parentSize = parentSizeProperty.GetVector3();
-
-  float overlayWidth = IsVertical() ? parentSize.x : parentSize.y;
-  float overlayHeight = mMaxOvershootImageSize * fabsf(IsVertical() ? overshooty : overshootx);
-
-  return Vector3(overlayWidth, overlayHeight, current.z);
+  ScrollOvershootIndicator* scrollOvershootPtr = new ScrollOvershootIndicator();
+  return scrollOvershootPtr;
 }
 
-Quaternion ScrollOvershootEffectGradient::RotationConstraint(const Quaternion& current,
-    const PropertyInput& overshootPropertyX, const PropertyInput& overshootPropertyY)
+void ScrollOvershootIndicator::AttachToScrollable(Scrollable& scrollable)
 {
-  float overshootx = overshootPropertyX.GetFloat();
-  float overshooty = overshootPropertyY.GetFloat();
-
-  Quaternion rotation;
-
-  if(IsVertical())
+  if(!mEffectX)
   {
-    if(overshooty < -Math::MACHINE_EPSILON_0)
-    {
-      rotation = Quaternion(Math::PI, Vector3::ZAXIS);
-    }
-    else if(overshooty > Math::MACHINE_EPSILON_0)
-    {
-      rotation = Quaternion(0.0f, Vector3::ZAXIS);
-    }
+    mEffectX = ScrollOvershootEffectRipple::New(false, scrollable);
   }
-  else
+  mEffectX->Apply();
+  if(!mEffectY)
   {
-    if(overshootx < -Math::MACHINE_EPSILON_0)
-    {
-      rotation = Quaternion(0.5f * Math::PI, Vector3::ZAXIS);
-    }
-    else if(overshootx > Math::MACHINE_EPSILON_0)
-    {
-      rotation = Quaternion(1.5f * Math::PI, Vector3::ZAXIS);
-    }
+    mEffectY = ScrollOvershootEffectRipple::New(true, scrollable);
   }
-
-  return rotation;
+  mEffectY->Apply();
 }
 
-Vector3 ScrollOvershootEffectGradient::PositionConstraint(const Vector3&    current,
-    const PropertyInput& parentSizeProperty,
-    const PropertyInput& overshootPropertyX, const PropertyInput& overshootPropertyY)
+void ScrollOvershootIndicator::DetachFromScrollable(Scrollable& scrollable)
 {
-  float overshootx = overshootPropertyX.GetFloat();
-  float overshooty = overshootPropertyY.GetFloat();
-  const Vector3 parentSize = parentSizeProperty.GetVector3();
-
-  Vector3 relativeOffset = Vector3::ZERO;
-
-  if(IsVertical())
+  if(mEffectX)
   {
-    if(overshooty > Math::MACHINE_EPSILON_0)
-    {
-      relativeOffset = Vector3(0.0f, 0.0f, 0.0f);
-    }
-    else if (overshooty < -Math::MACHINE_EPSILON_0)
-    {
-      relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
-    }
+    mEffectX->Remove(scrollable);
   }
-  else
+  if(mEffectY)
   {
-    if(overshootx > Math::MACHINE_EPSILON_0)
-    {
-      relativeOffset = Vector3(0.0f, 1.0f, 0.0f);
-    }
-    else if (overshootx < -Math::MACHINE_EPSILON_0)
-    {
-      relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
-    }
+    mEffectY->Remove(scrollable);
   }
-
-  return relativeOffset * parentSize;
 }
 
-bool ScrollOvershootEffectGradient::VisibilityConstraint(const bool& current,
-    const PropertyInput& canScrollProperty)
-{
-  return canScrollProperty.GetBoolean();
-}
-
-ScrollOvershootEffectGradientPtr ScrollOvershootEffectGradient::New(bool vertical)
-{
-  return new ScrollOvershootEffectGradient(vertical);
-}
-
-namespace
-{
-
-const std::string OVERSHOOT_PROPERTY_NAME( "uOvershoot" );
-const std::string OVERSHOOT_IMAGE_COUNT_PROPERTY_NAME( "uOvershootImageCount" );
-
-} // namespace
-
-OvershootRippleEffect::OvershootRippleEffect()
-{
-}
-
-//Call the Parent copy constructor to add reference to the implementation for this object
-OvershootRippleEffect::OvershootRippleEffect(ShaderEffect handle)
-:ShaderEffect(handle)
-{
-}
-
-OvershootRippleEffect::~OvershootRippleEffect()
+void ScrollOvershootIndicator::Reset()
 {
+  mEffectX->Reset();
+  mEffectY->Reset();
 }
 
-OvershootRippleEffect OvershootRippleEffect::New()
+void ScrollOvershootIndicator::SetOvershootEffectColor( const Vector4& color )
 {
-  std::string vertextShader(
-      "precision mediump float;                     \n"
-      "uniform  float  uOvershoot;                  \n"
-      "uniform  float  uOvershootImageCount;        \n"
-      "void main()                                  \n"
-      "{                                            \n"
-      "  gl_Position = uProjection * uModelView * vec4(aPosition, 1.0); \n"
-      "  vTexCoord = aTexCoord;                     \n"
-      "  vTexCoord.y += (1.0 / uOvershootImageCount) * min(floor((abs(uOvershoot) * (uOvershootImageCount - 1.0)) + 0.5), (uOvershootImageCount - 1.0)); \n"
-      "}                                            \n" );
-
-  std::string fragmentShader(
-      "void main()                                  \n"
-      "{                                            \n"
-      "  gl_FragColor = texture2D(sTexture, vTexCoord);    \n"
-      "}                                            \n" );
-
-  // Create the implementation, temporarily owned on stack,
-  Dali::ShaderEffect shaderEffectCustom =  Dali::ShaderEffect::New(
-      vertextShader,
-      fragmentShader);
-
-  /* Pass ownership to OvershootRippleEffect through overloaded constructor, So that it now has access to the
-     Dali::ShaderEffect implementation */
-  OvershootRippleEffect handle( shaderEffectCustom );
-  handle.SetUniform(OVERSHOOT_PROPERTY_NAME, 0.0f);
-  handle.SetUniform(OVERSHOOT_IMAGE_COUNT_PROPERTY_NAME, 10.0f);
-  return handle;
+  if(mEffectX)
+  {
+    mEffectX->SetOvershootEffectColor(color);
+  }
+  if(mEffectY)
+  {
+    mEffectY->SetOvershootEffectColor(color);
+  }
 }
 
-void OvershootRippleEffect::SetOvershoot( float overshoot )
+ScrollOvershootEffect::ScrollOvershootEffect( bool vertical ) :
+    mVertical(vertical)
 {
-  SetUniform( OVERSHOOT_PROPERTY_NAME, overshoot );
-}
 
-void OvershootRippleEffect::SetOvershootImageCount( float imageCount )
-{
-  SetUniform( OVERSHOOT_IMAGE_COUNT_PROPERTY_NAME, imageCount );
 }
 
-const std::string& OvershootRippleEffect::GetOvershootPropertyName() const
+bool ScrollOvershootEffect::IsVertical() const
 {
-  return OVERSHOOT_PROPERTY_NAME;
+  return mVertical;
 }
 
-const std::string& OvershootRippleEffect::GetOvershootImageCountPropertyName() const
+ScrollOvershootEffectRipple::ScrollOvershootEffectRipple( bool vertical, Scrollable& scrollable ) :
+    ScrollOvershootEffect( vertical ),
+    mAttachedScrollView(scrollable),
+    mOvershootProperty(Property::INVALID_INDEX),
+    mEffectOvershootProperty(Property::INVALID_INDEX),
+    mOvershoot(0.0f),
+    mOvershootSize( scrollable.GetOvershootSize() ),
+    mAnimationStateFlags(0)
 {
-  return OVERSHOOT_IMAGE_COUNT_PROPERTY_NAME;
-}
+  mOvershootOverlay = CreateBouncingEffectActor(mEffectOvershootProperty);
+  mOvershootOverlay.SetColor(mAttachedScrollView.GetOvershootEffectColor());
+  mOvershootOverlay.SetParentOrigin(ParentOrigin::TOP_LEFT);
+  mOvershootOverlay.SetAnchorPoint(AnchorPoint::TOP_LEFT);
+  mOvershootOverlay.SetVisible(false);
 
-ScrollOvershootEffectRipple::ScrollOvershootEffectRipple(bool vertical) :
-    ScrollOvershootEffect(vertical),
-    mMaxOvershootImageSize(DEFAULT_MAX_OVERSHOOT_HEIGHT)
-{
-  mRippleEffect = OvershootRippleEffect::New();
-  Image overshootImage = Image::New( OVERSHOOT_OVERLAY_RIPPLE_IMAGE_PATH );
-  mOvershootImage = ImageActor::New( overshootImage );
-  mOvershootImage.SetParentOrigin(ParentOrigin::TOP_LEFT);
-  mOvershootImage.SetAnchorPoint(AnchorPoint::TOP_LEFT);
-  mOvershootImage.SetDrawMode(DrawMode::OVERLAY);
-  mOvershootImage.SetShaderEffect(mRippleEffect);
-  mOvershootImage.SetPixelArea(OVERSHOOT_RIPPLE_IMAGE_1_PIXEL_AREA);
-  mOvershootImage.SetVisible(false);
-  mAnimatingOvershootOn = false;
-  mAnimateOvershootOff = false;
 }
 
-void ScrollOvershootEffectRipple::Apply(Scrollable& scrollable)
+void ScrollOvershootEffectRipple::Apply()
 {
-  Actor scrollableActor = scrollable.Self();
+  Actor self = mAttachedScrollView.Self();
+  mOvershootProperty = IsVertical() ? Toolkit::ScrollView::Property::OVERSHOOT_Y : Toolkit::ScrollView::Property::OVERSHOOT_X;
 
   // 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);
+  mOvershootSize = mAttachedScrollView.GetOvershootSize();
+  mOvershootOverlay.SetSize( mOvershootSize );
 
-  UpdateConstraints(scrollableActor);
-  scrollable.AddOverlay(mOvershootImage);
+  mAttachedScrollView.AddOverlay(mOvershootOverlay);
 
-  SetPropertyNotifications(scrollableActor);
+  UpdatePropertyNotifications();
 }
 
-void ScrollOvershootEffectRipple::Remove(Scrollable& scrollable)
+void ScrollOvershootEffectRipple::Remove( Scrollable& scrollable )
 {
-  if(mOvershootImage)
+  if(mOvershootOverlay)
   {
-    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(mOvershootOverlay);
   }
 }
 
 void ScrollOvershootEffectRipple::Reset()
 {
-  mAnimatingOvershootOn = false;
-  mAnimateOvershootOff = false;
-  mOvershootImage.SetVisible(false);
-  mRippleEffect.SetUniform(mRippleEffect.GetOvershootPropertyName(), 0.0f);
-  if(mScrollOvershootAnimation)
-  {
-    mScrollOvershootAnimation.Clear();
-    mScrollOvershootAnimation.Reset();
-  }
+  mOvershootOverlay.SetVisible(false);
+  mOvershootOverlay.SetProperty( mEffectOvershootProperty, 0.f);
 }
 
-void ScrollOvershootEffectRipple::UpdateConstraints(Actor& scrollable)
+void ScrollOvershootEffectRipple::UpdatePropertyNotifications()
 {
-  int overshootPropertyIndex = mRippleEffect.GetPropertyIndex(mRippleEffect.GetOvershootPropertyName());
-  Constraint constraint;
-  if(!mSizeConstraint)
-  {
-    constraint = Constraint::New<float>( Actor::SIZE_WIDTH,
-                                                      Source( scrollable, IsVertical() ? Actor::SIZE_WIDTH : Actor::SIZE_HEIGHT),
-                                                      EqualToConstraint() );
-    mSizeConstraint = mOvershootImage.ApplyConstraint(constraint);
-  }
+  float absOvershoot = fabsf(mOvershoot);
 
-  if(!mPositionConstraint)
+  Actor self = mAttachedScrollView.Self();
+  // update overshoot increase notify
+  if( mOvershootIncreaseNotification )
   {
-    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);
+    self.RemovePropertyNotification( mOvershootIncreaseNotification );
+    mOvershootIncreaseNotification.Reset();
   }
-}
-
-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(scrollable.OnStage())
+  if( absOvershoot < MAX_OVERSHOOT_NOTIFY_AMOUNT )
   {
-    if(!mOvershootNegativeNotification)
-    {
-      mOvershootNegativeNotification = scrollable.AddPropertyNotification(IsVertical() ? overshootYPropertyIndex : overshootXPropertyIndex, LessThanCondition(-Math::MACHINE_EPSILON_1));
-      mOvershootNegativeNotification.SetNotifyMode(PropertyNotification::NotifyOnChanged);
-      mOvershootNegativeNotification.NotifySignal().Connect(this, &ScrollOvershootEffectRipple::OnNegativeOvershootNotification);
-    }
-
-    if(!mOvershootPositiveNotification)
+    float increaseStep = absOvershoot + OVERSHOOT_NOTIFY_STEP;
+    if( increaseStep > MAX_OVERSHOOT_NOTIFY_AMOUNT )
     {
-      mOvershootPositiveNotification = scrollable.AddPropertyNotification(IsVertical() ? overshootYPropertyIndex : overshootXPropertyIndex, GreaterThanCondition(Math::MACHINE_EPSILON_1));
-      mOvershootPositiveNotification.SetNotifyMode(PropertyNotification::NotifyOnChanged);
-      mOvershootPositiveNotification.NotifySignal().Connect(this, &ScrollOvershootEffectRipple::OnPositiveOvershootNotification);
+      increaseStep = MAX_OVERSHOOT_NOTIFY_AMOUNT;
     }
+    mOvershootIncreaseNotification = self.AddPropertyNotification( mOvershootProperty, OutsideCondition(-increaseStep, increaseStep) );
+    mOvershootIncreaseNotification.SetNotifyMode(PropertyNotification::NotifyOnTrue);
+    mOvershootIncreaseNotification.NotifySignal().Connect(this, &ScrollOvershootEffectRipple::OnOvershootNotification);
   }
-}
-
-Vector3 ScrollOvershootEffectRipple::PositionConstraint(const Vector3& current,
-    const PropertyInput& parentSizeProperty, const PropertyInput& overshootProperty)
-{
-  float overshoot = overshootProperty.GetFloat();
-  const Vector3 parentSize = parentSizeProperty.GetVector3();
 
-  Vector3 relativeOffset = Vector3::ZERO;
-
-  if(IsVertical())
+  // update overshoot decrease notify
+  if( mOvershootDecreaseNotification )
   {
-    if(overshoot > Math::MACHINE_EPSILON_0)
-    {
-      relativeOffset = Vector3(0.0f, 0.0f, 0.0f);
-    }
-    else if (overshoot < -Math::MACHINE_EPSILON_0)
-    {
-      relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
-    }
+    self.RemovePropertyNotification( mOvershootDecreaseNotification );
+    mOvershootDecreaseNotification.Reset();
   }
-  else
+  if( absOvershoot > MIN_OVERSHOOT_NOTIFY_AMOUNT )
   {
-    if(overshoot > Math::MACHINE_EPSILON_0)
-    {
-      relativeOffset = Vector3(0.0f, 1.0f, 0.0f);
-    }
-    else if (overshoot < -Math::MACHINE_EPSILON_0)
+    float reduceStep = absOvershoot - OVERSHOOT_NOTIFY_STEP;
+    if( reduceStep < MIN_OVERSHOOT_NOTIFY_AMOUNT )
     {
-      relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
+      reduceStep = MIN_OVERSHOOT_NOTIFY_AMOUNT;
     }
+    mOvershootDecreaseNotification = self.AddPropertyNotification( mOvershootProperty, InsideCondition(-reduceStep, reduceStep) );
+    mOvershootDecreaseNotification.SetNotifyMode(PropertyNotification::NotifyOnTrue);
+    mOvershootDecreaseNotification.NotifySignal().Connect(this, &ScrollOvershootEffectRipple::OnOvershootNotification);
   }
-
-  return relativeOffset * parentSize;
 }
 
-void ScrollOvershootEffectRipple::OnPositiveOvershootNotification(PropertyNotification& source)
+void ScrollOvershootEffectRipple::SetOvershootEffectColor( const Vector4& color )
 {
-  Actor delegate = Actor::DownCast(source.GetTarget());
-  float overshoot = delegate.GetProperty<float>(source.GetTargetProperty());
-  bool canScroll = delegate.GetProperty<bool>(mCanScrollPropertyIndex);
-  if(!canScroll)
+  if(mOvershootOverlay)
   {
-    mOvershootImage.SetVisible(false);
-    return;
+    mOvershootOverlay.SetColor(color);
   }
-  mOvershootImage.SetVisible(true);
+}
 
-  if (fabsf(overshoot) < Math::MACHINE_EPSILON_1)
-  {
-    AnimateScrollOvershoot(0.0f);
-    return;
-  }
-  if(overshoot > 0.0f)
+void ScrollOvershootEffectRipple::UpdateVisibility( bool visible )
+{
+  mOvershootOverlay.SetVisible(visible);
+  // make sure overshoot image is correctly placed
+  if( visible )
   {
-    const Vector3 imageSize = mOvershootImage.GetCurrentSize();
-    Vector3 relativeOffset = Vector3::ZERO;
-    const Vector3 parentSize = delegate.GetCurrentSize();
-    AnimateScrollOvershoot(1.0f);
-    if(IsVertical())
+    Actor self = mAttachedScrollView.Self();
+    if(mOvershoot > 0.0f)
     {
-      mOvershootImage.SetRotation(Quaternion(0.0f, Vector3::ZAXIS));
-      mOvershootImage.SetSize(parentSize.width, imageSize.height, imageSize.depth);
-      relativeOffset = Vector3(0.0f, 0.0f, 0.0f);
+      // positive overshoot
+      const Vector3 size = mOvershootOverlay.GetCurrentSize();
+      Vector3 relativeOffset;
+      const Vector3 parentSize = self.GetCurrentSize();
+      if(IsVertical())
+      {
+        mOvershootOverlay.SetOrientation( Quaternion( Radian( 0.0f ), Vector3::ZAXIS ) );
+        mOvershootOverlay.SetSize(parentSize.width, GetBounceActorHeight(parentSize.width, mOvershootSize.height), size.depth);
+      }
+      else
+      {
+        mOvershootOverlay.SetOrientation( Quaternion( Radian( 1.5f * Math::PI ), Vector3::ZAXIS ) );
+        mOvershootOverlay.SetSize(parentSize.height, GetBounceActorHeight(parentSize.height, mOvershootSize.height), size.depth);
+        relativeOffset = Vector3(0.0f, 1.0f, 0.0f);
+      }
+      mOvershootOverlay.SetPosition(relativeOffset * parentSize);
     }
     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);
+      // negative overshoot
+      const Vector3 size = mOvershootOverlay.GetCurrentSize();
+      Vector3 relativeOffset;
+      const Vector3 parentSize = self.GetCurrentSize();
+      if(IsVertical())
+      {
+        mOvershootOverlay.SetOrientation( Quaternion( Radian( Math::PI ), Vector3::ZAXIS ) );
+        mOvershootOverlay.SetSize(parentSize.width, GetBounceActorHeight(parentSize.width, mOvershootSize.height), size.depth);
+        relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
+      }
+      else
+      {
+        mOvershootOverlay.SetOrientation( Quaternion( Radian( 0.5f * Math::PI ), Vector3::ZAXIS ) );
+        mOvershootOverlay.SetSize(parentSize.height, GetBounceActorHeight(parentSize.height, mOvershootSize.height), size.depth);
+        relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
+      }
+      mOvershootOverlay.SetPosition(relativeOffset * parentSize);
     }
-    mOvershootImage.SetPosition(relativeOffset * parentSize);
   }
 }
 
-void ScrollOvershootEffectRipple::OnNegativeOvershootNotification(PropertyNotification& source)
+void ScrollOvershootEffectRipple::OnOvershootNotification(PropertyNotification& source)
 {
-  Actor delegate = Actor::DownCast(source.GetTarget());
-  float overshoot = delegate.GetProperty<float>(source.GetTargetProperty());
-  bool canScroll = delegate.GetProperty<bool>(mCanScrollPropertyIndex);
-  if(!canScroll)
-  {
-    mOvershootImage.SetVisible(false);
-    return;
-  }
-  mOvershootImage.SetVisible(true);
-
-  if (fabsf(overshoot) < Math::MACHINE_EPSILON_1)
-  {
-    AnimateScrollOvershoot(0.0f);
-    return;
-  }
+  Actor self = mAttachedScrollView.Self();
+  mOvershoot = DevelHandle::GetCurrentProperty< float >( self, mOvershootProperty );
+  SetOvershoot(mOvershoot, false);
+  UpdatePropertyNotifications();
+}
 
-  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())
-    {
-      mOvershootImage.SetRotation(Quaternion(Math::PI, Vector3::ZAXIS));
-      mOvershootImage.SetSize(parentSize.width, imageSize.height, imageSize.depth);
-      relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
-    }
-    else
+    // trying to do what we are already doing
+    if( mAnimationStateFlags & AnimateBack )
     {
-      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);
+      mAnimationStateFlags &= ~AnimateBack;
     }
-    mOvershootImage.SetPosition(relativeOffset * parentSize);
+    return;
   }
-}
-
-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.GetOvershootPropertyName());
-  float currentOvershoot = mRippleEffect.GetProperty<float>(overShootProperty);
-  if(((currentOvershoot < 0.0f && overshootAmount > 0.0f)
-      || (currentOvershoot > 0.0f && overshootAmount < 0.0f)))
+  if( (!animatingOn && (mAnimationStateFlags & AnimatingOut)) )
   {
-    // cancel current animation
-    mAnimatingOvershootOn = false;
-    // reset currentOvershoot to 0.0f
-    mRippleEffect.SetProperty(overShootProperty, 0.0f);
-    currentOvershoot = 0.0f;
+    // trying to do what we are already doing
+    return;
   }
-  if( mAnimatingOvershootOn )
+  if( !animatingOn && (mAnimationStateFlags & AnimatingIn) )
   {
-    // animating on in same direction, do not allow animate off
+    // dont interrupt while animating on
+    mAnimationStateFlags |= AnimateBack;
     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);
+  float overshootAnimationSpeed = mAttachedScrollView.Self().GetProperty<float>(Toolkit::Scrollable::Property::OVERSHOOT_ANIMATION_SPEED);
 
-  mAnimatingOvershootOn = animatingOn;
+  if( animate && overshootAnimationSpeed > Math::MACHINE_EPSILON_0 )
+  {
+    float currentOvershoot = fabsf( mOvershootOverlay.GetProperty( mEffectOvershootProperty ).Get<float>() );
+    float duration = mOvershootOverlay.GetCurrentSize().height * (animatingOn ? (1.0f - currentOvershoot) : currentOvershoot) / overshootAnimationSpeed;
+
+    if( duration > Math::MACHINE_EPSILON_0 )
+    {
+      if(mScrollOvershootAnimation)
+      {
+        mScrollOvershootAnimation.FinishedSignal().Disconnect( this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished );
+        mScrollOvershootAnimation.Stop();
+        mScrollOvershootAnimation.Reset();
+      }
+      mScrollOvershootAnimation = Animation::New(duration);
+      mScrollOvershootAnimation.FinishedSignal().Connect( this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished );
+      mScrollOvershootAnimation.AnimateTo( Property(mOvershootOverlay, mEffectOvershootProperty), amount, TimePeriod(duration) );
+      mScrollOvershootAnimation.Play();
+      mAnimationStateFlags = animatingOn ? AnimatingIn : AnimatingOut;
+    }
+  }
+  else
+  {
+    mOvershootOverlay.SetProperty( mEffectOvershootProperty, amount);
+  }
 }
 
 void ScrollOvershootEffectRipple::OnOvershootAnimFinished(Animation& animation)
 {
-  if(!mAnimatingOvershootOn && !mAnimateOvershootOff)
+  bool animateOff = false;
+  if( mAnimationStateFlags & AnimatingOut )
+  {
+    // should now be offscreen
+    mOvershootOverlay.SetVisible(false);
+  }
+  if( (mAnimationStateFlags & AnimateBack) )
   {
-    // just finished animating overshoot to 0
-    mOvershootImage.SetVisible(false);
+    animateOff = true;
   }
-  mAnimatingOvershootOn = false;
-  mScrollOvershootAnimation.FinishedSignal().Disconnect(this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished);
-  mScrollOvershootAnimation.Clear();
+  mScrollOvershootAnimation.FinishedSignal().Disconnect( this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished );
+  mScrollOvershootAnimation.Stop();
   mScrollOvershootAnimation.Reset();
-  if(mAnimateOvershootOff)
+  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