Initially show scroll indicator for a brief period
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / scroll-bar / scroll-bar-impl.cpp
index 74450ae..5adc065 100755 (executable)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * 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.
 #include <dali/public-api/images/resource-image.h>
 #include <dali/public-api/object/type-registry.h>
 #include <dali/public-api/object/property-array.h>
-#include <dali/devel-api/object/type-registry-helper.h>
+#include <dali/public-api/object/type-registry-helper.h>
 #include <dali/integration-api/debug.h>
-
+#include <dali/devel-api/object/property-helper-devel.h>
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.h>
+#include <dali-toolkit/public-api/controls/image-view/image-view.h>
+#include <dali-toolkit/devel-api/controls/scroll-bar/scroll-bar-devel.h>
 
 using namespace Dali;
 
 namespace
 {
 
-const char* DEFAULT_INDICATOR_IMAGE_PATH = DALI_IMAGE_DIR "popup_scroll.png";
-const Vector4 DEFAULT_INDICATOR_NINE_PATCH_BORDER(4.0f, 9.0f, 7.0f, 11.0f);
-const float MINIMUM_INDICATOR_HEIGHT(20.0f); // The minimum indicator height for the nine patch border
+const char* DEFAULT_INDICATOR_IMAGE_PATH = DALI_IMAGE_DIR "popup_scroll.9.png";
 const float DEFAULT_SLIDER_DEPTH(1.0f);
 const float DEFAULT_INDICATOR_SHOW_DURATION(0.5f);
 const float DEFAULT_INDICATOR_HIDE_DURATION(0.5f);
 const float DEFAULT_PAN_GESTURE_PROCESS_TIME(16.7f); // 16.7 milliseconds, i.e. one frame
 const float DEFAULT_INDICATOR_FIXED_HEIGHT(80.0f);
+const float DEFAULT_INDICATOR_MINIMUM_HEIGHT(0.0f);
+const float DEFAULT_INDICATOR_START_PADDING(0.0f);
+const float DEFAULT_INDICATOR_END_PADDING(0.0f);
+const float DEFAULT_INDICATOR_TRANSIENT_DURATION(1.0f);
 
 /**
  * Indicator size constraint
@@ -52,7 +56,13 @@ const float DEFAULT_INDICATOR_FIXED_HEIGHT(80.0f);
  */
 struct IndicatorSizeConstraint
 {
-  IndicatorSizeConstraint()
+  /**
+   * @param[in] minimumHeight The minimum height for the indicator
+   * @param[in] padding The sum of the padding at the start & end of the indicator
+   */
+  IndicatorSizeConstraint( float minimumHeight, float padding )
+  : mMinimumHeight( minimumHeight ),
+    mPadding( padding )
   {
   }
 
@@ -62,17 +72,23 @@ struct IndicatorSizeConstraint
    * @param[in] parentSizeProperty The parent size of scroll indicator.
    * @return The new scroll indicator size.
    */
-  void operator()(Vector3& current, const PropertyInputContainer& inputs )
+  void operator()( Vector3& current, const PropertyInputContainer& inputs )
   {
     const Vector3& parentSize = inputs[0]->GetVector3();
     const float contentSize = inputs[1]->GetFloat();
 
-    float height = contentSize > parentSize.height ?
-                   parentSize.height * ( parentSize.height / contentSize ) :
-                   parentSize.height * ( (parentSize.height - contentSize * 0.5f) / parentSize.height);
+    // Take into account padding that may exist at the beginning and end of the indicator.
+    const float parentHeightMinusPadding = parentSize.height - mPadding;
+
+    float height = contentSize > parentHeightMinusPadding ?
+                   parentHeightMinusPadding * ( parentHeightMinusPadding / contentSize ) :
+                   parentHeightMinusPadding * ( ( parentHeightMinusPadding - contentSize * 0.5f ) / parentHeightMinusPadding );
 
-    current.y = std::max(MINIMUM_INDICATOR_HEIGHT, height);
+    current.y = std::max( mMinimumHeight, height );
   }
+
+  float mMinimumHeight;
+  float mPadding;
 };
 
 /**
@@ -82,10 +98,12 @@ struct IndicatorSizeConstraint
 struct IndicatorPositionConstraint
 {
   /**
-   * @param[in] minPosition The minimum limit of scroll position
-   * @param[in] maxPosition the maximum limit of scroll position
+   * @param[in] startPadding The padding at the start of the indicator
+   * @param[in] endPadding The padding at the end of the indicator
    */
-  IndicatorPositionConstraint()
+  IndicatorPositionConstraint( float startPadding, float endPadding )
+  : mStartPadding( startPadding ),
+    mEndPadding( endPadding )
   {
   }
 
@@ -100,13 +118,19 @@ struct IndicatorPositionConstraint
     const Vector3& indicatorSize = inputs[0]->GetVector3();
     const Vector3& parentSize = inputs[1]->GetVector3();
     const float scrollPosition = -inputs[2]->GetFloat();
-    const float minScrollPosition = inputs[3]->GetFloat();
-    const float maxScrollPosition = inputs[4]->GetFloat();
+    const float minimumScrollPosition = inputs[3]->GetFloat();
+    const float maximumScrollPosition = inputs[4]->GetFloat();
 
-    float relativePosition = std::max( 0.0f, std::min( 1.0f, (scrollPosition - minScrollPosition) / (maxScrollPosition - minScrollPosition) ) );
-    current.y = ( parentSize.height - indicatorSize.height ) * relativePosition;
+    // Take into account padding that may exist at the beginning and end of the indicator.
+    const float parentHeightMinusPadding = parentSize.height - ( mStartPadding + mEndPadding );
+
+    float relativePosition = std::max( 0.0f, std::min( 1.0f, ( scrollPosition - minimumScrollPosition ) / ( maximumScrollPosition - minimumScrollPosition ) ) );
+    current.y = mStartPadding + ( parentHeightMinusPadding - indicatorSize.height ) * relativePosition;
     current.z = DEFAULT_SLIDER_DEPTH;
   }
+
+  float mStartPadding;
+  float mEndPadding;
 };
 
 } // unnamed namespace
@@ -133,15 +157,24 @@ BaseHandle Create()
 // Setup properties, signals and actions using the type-registry.
 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::ScrollBar, Toolkit::Control, Create );
 
-DALI_PROPERTY_REGISTRATION( Toolkit, ScrollBar, "scroll-direction",                  STRING, SCROLL_DIRECTION          )
-DALI_PROPERTY_REGISTRATION( Toolkit, ScrollBar, "indicator-height-policy",           STRING, INDICATOR_HEIGHT_POLICY   )
-DALI_PROPERTY_REGISTRATION( Toolkit, ScrollBar, "indicator-fixed-height",            FLOAT,  INDICATOR_FIXED_HEIGHT    )
-DALI_PROPERTY_REGISTRATION( Toolkit, ScrollBar, "indicator-show-duration",           FLOAT,  INDICATOR_SHOW_DURATION   )
-DALI_PROPERTY_REGISTRATION( Toolkit, ScrollBar, "indicator-hide-duration",           FLOAT,  INDICATOR_HIDE_DURATION   )
-DALI_PROPERTY_REGISTRATION( Toolkit, ScrollBar, "scroll-position-intervals",         ARRAY,  SCROLL_POSITION_INTERVALS )
+DALI_PROPERTY_REGISTRATION( Toolkit, ScrollBar, "scrollDirection",                   STRING, SCROLL_DIRECTION             )
+DALI_PROPERTY_REGISTRATION( Toolkit, ScrollBar, "indicatorHeightPolicy",             STRING, INDICATOR_HEIGHT_POLICY      )
+DALI_PROPERTY_REGISTRATION( Toolkit, ScrollBar, "indicatorFixedHeight",              FLOAT,  INDICATOR_FIXED_HEIGHT       )
+DALI_PROPERTY_REGISTRATION( Toolkit, ScrollBar, "indicatorShowDuration",             FLOAT,  INDICATOR_SHOW_DURATION      )
+DALI_PROPERTY_REGISTRATION( Toolkit, ScrollBar, "indicatorHideDuration",             FLOAT,  INDICATOR_HIDE_DURATION      )
+DALI_PROPERTY_REGISTRATION( Toolkit, ScrollBar, "scrollPositionIntervals",           ARRAY,  SCROLL_POSITION_INTERVALS    )
+DALI_PROPERTY_REGISTRATION( Toolkit, ScrollBar, "indicatorMinimumHeight",            FLOAT,  INDICATOR_MINIMUM_HEIGHT     )
+DALI_PROPERTY_REGISTRATION( Toolkit, ScrollBar, "indicatorStartPadding",             FLOAT,  INDICATOR_START_PADDING      )
+DALI_PROPERTY_REGISTRATION( Toolkit, ScrollBar, "indicatorEndPadding",               FLOAT,  INDICATOR_END_PADDING        )
+
+DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, ScrollBar, "indicatorTransientDuration", FLOAT,  INDICATOR_TRANSIENT_DURATION )
 
-DALI_SIGNAL_REGISTRATION(   Toolkit, ScrollBar, "pan-finished",                      PAN_FINISHED_SIGNAL )
-DALI_SIGNAL_REGISTRATION(   Toolkit, ScrollBar, "scroll-position-interval-reached",  SCROLL_POSITION_INTERVAL_REACHED_SIGNAL )
+DALI_SIGNAL_REGISTRATION(   Toolkit, ScrollBar, "panFinished",                       PAN_FINISHED_SIGNAL                     )
+DALI_SIGNAL_REGISTRATION(   Toolkit, ScrollBar, "scrollPositionIntervalReached",     SCROLL_POSITION_INTERVAL_REACHED_SIGNAL )
+
+DALI_ACTION_REGISTRATION(   Toolkit, ScrollBar, "ShowIndicator",                     ACTION_SHOW_INDICATOR                   )
+DALI_ACTION_REGISTRATION(   Toolkit, ScrollBar, "HideIndicator",                     ACTION_HIDE_INDICATOR                   )
+DALI_ACTION_REGISTRATION(   Toolkit, ScrollBar, "ShowTransientIndicator",            ACTION_SHOW_TRANSIENT_INDICATOR         )
 
 DALI_TYPE_REGISTRATION_END()
 
@@ -151,7 +184,8 @@ const char* INDICATOR_HEIGHT_POLICY_NAME[] = {"Variable", "Fixed"};
 }
 
 ScrollBar::ScrollBar(Toolkit::ScrollBar::Direction direction)
-: Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
+: Control( ControlBehaviour( CONTROL_BEHAVIOUR_DEFAULT ) ),
+  mIndicatorShowAlpha(1.0f),
   mDirection(direction),
   mScrollableObject(WeakHandleBase()),
   mPropertyScrollPosition(Property::INVALID_INDEX),
@@ -160,11 +194,16 @@ ScrollBar::ScrollBar(Toolkit::ScrollBar::Direction direction)
   mPropertyScrollContentSize(Property::INVALID_INDEX),
   mIndicatorShowDuration(DEFAULT_INDICATOR_SHOW_DURATION),
   mIndicatorHideDuration(DEFAULT_INDICATOR_HIDE_DURATION),
+  mTransientIndicatorDuration(DEFAULT_INDICATOR_TRANSIENT_DURATION),
   mScrollStart(0.0f),
-  mIsPanning(false),
   mCurrentScrollPosition(0.0f),
   mIndicatorHeightPolicy(Toolkit::ScrollBar::Variable),
-  mIndicatorFixedHeight(DEFAULT_INDICATOR_FIXED_HEIGHT)
+  mIndicatorFixedHeight(DEFAULT_INDICATOR_FIXED_HEIGHT),
+  mIndicatorMinimumHeight(DEFAULT_INDICATOR_MINIMUM_HEIGHT),
+  mIndicatorStartPadding(DEFAULT_INDICATOR_START_PADDING),
+  mIndicatorEndPadding(DEFAULT_INDICATOR_END_PADDING),
+  mIsPanning(false),
+  mIndicatorFirstShow(true)
 {
 }
 
@@ -202,13 +241,11 @@ void ScrollBar::SetScrollPropertySource( Handle handle, Property::Index property
 
 void ScrollBar::CreateDefaultIndicatorActor()
 {
-  Image indicatorImage = ResourceImage::New( DEFAULT_INDICATOR_IMAGE_PATH );
-  ImageActor indicator = ImageActor::New( indicatorImage );
-  indicator.SetNinePatchBorder( DEFAULT_INDICATOR_NINE_PATCH_BORDER );
-  indicator.SetStyle( ImageActor::STYLE_NINE_PATCH );
+  Toolkit::ImageView indicator = Toolkit::ImageView::New( DEFAULT_INDICATOR_IMAGE_PATH );
   indicator.SetParentOrigin( ParentOrigin::TOP_LEFT );
   indicator.SetAnchorPoint( AnchorPoint::TOP_LEFT );
-
+  indicator.SetStyleName( "ScrollBarIndicator" );
+  indicator.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );
   SetScrollIndicator(indicator);
 }
 
@@ -217,10 +254,17 @@ void ScrollBar::SetScrollIndicator( Actor indicator )
   // Don't allow empty handle
   if( indicator )
   {
+    // Remove current Indicator
+    if( mIndicator )
+    {
+      Self().Remove( mIndicator );
+    }
     mIndicator = indicator;
-    Self().Add(mIndicator);
 
-    EnableGestureDetection(Gesture::Type(Gesture::Pan));
+    mIndicatorFirstShow = true;
+    Self().Add( mIndicator );
+
+    EnableGestureDetection( Gesture::Type( Gesture::Pan ) );
 
     PanGestureDetector detector( GetPanGestureDetector() );
     detector.DetachAll();
@@ -265,7 +309,8 @@ void ScrollBar::ApplyConstraints()
     }
     else
     {
-      mIndicatorSizeConstraint = Constraint::New<Vector3>( mIndicator, Actor::Property::SIZE, IndicatorSizeConstraint() );
+      mIndicatorSizeConstraint = Constraint::New<Vector3>( mIndicator, Actor::Property::SIZE,
+                                                           IndicatorSizeConstraint( mIndicatorMinimumHeight, mIndicatorStartPadding + mIndicatorEndPadding ) );
       mIndicatorSizeConstraint.AddSource( ParentSource( Actor::Property::SIZE ) );
       mIndicatorSizeConstraint.AddSource( Source( scrollableHandle, mPropertyScrollContentSize ) );
       mIndicatorSizeConstraint.Apply();
@@ -276,7 +321,8 @@ void ScrollBar::ApplyConstraints()
       mIndicatorPositionConstraint.Remove();
     }
 
-    mIndicatorPositionConstraint = Constraint::New<Vector3>( mIndicator, Actor::Property::POSITION, IndicatorPositionConstraint() );
+    mIndicatorPositionConstraint = Constraint::New<Vector3>( mIndicator, Actor::Property::POSITION,
+                                                             IndicatorPositionConstraint( mIndicatorStartPadding, mIndicatorEndPadding ) );
     mIndicatorPositionConstraint.AddSource( LocalSource( Actor::Property::SIZE ) );
     mIndicatorPositionConstraint.AddSource( ParentSource( Actor::Property::SIZE ) );
     mIndicatorPositionConstraint.AddSource( Source( scrollableHandle, mPropertyScrollPosition ) );
@@ -328,15 +374,22 @@ void ScrollBar::ShowIndicator()
     mAnimation.Reset();
   }
 
+  if( mIndicatorFirstShow )
+  {
+    // Preserve the alpha value from the stylesheet
+    mIndicatorShowAlpha = Self().GetCurrentColor().a;
+    mIndicatorFirstShow = false;
+  }
+
   if(mIndicatorShowDuration > 0.0f)
   {
     mAnimation = Animation::New( mIndicatorShowDuration );
-    mAnimation.AnimateTo( Property( mIndicator, Actor::Property::COLOR_ALPHA ), 1.0f, AlphaFunction::EASE_IN );
+    mAnimation.AnimateTo( Property( mIndicator, Actor::Property::COLOR_ALPHA ), mIndicatorShowAlpha, AlphaFunction::EASE_IN );
     mAnimation.Play();
   }
   else
   {
-    mIndicator.SetOpacity(1.0f);
+    mIndicator.SetOpacity(mIndicatorShowAlpha);
   }
 }
 
@@ -361,6 +414,30 @@ void ScrollBar::HideIndicator()
   }
 }
 
+void ScrollBar::ShowTransientIndicator()
+{
+  // Cancel any animation
+  if(mAnimation)
+  {
+    mAnimation.Clear();
+    mAnimation.Reset();
+  }
+
+  mAnimation = Animation::New( mIndicatorShowDuration + mTransientIndicatorDuration + mIndicatorHideDuration );
+  if(mIndicatorShowDuration > 0.0f)
+  {
+    mAnimation.AnimateTo( Property( mIndicator, Actor::Property::COLOR_ALPHA ),
+                          mIndicatorShowAlpha, AlphaFunction::EASE_IN, TimePeriod(0, mIndicatorShowDuration) );
+  }
+  else
+  {
+    mIndicator.SetOpacity(mIndicatorShowAlpha);
+  }
+  mAnimation.AnimateTo( Property( mIndicator, Actor::Property::COLOR_ALPHA ),
+                        0.0f, AlphaFunction::EASE_IN, TimePeriod((mIndicatorShowDuration + mTransientIndicatorDuration), mIndicatorHideDuration) );
+  mAnimation.Play();
+}
+
 bool ScrollBar::OnPanGestureProcessTick()
 {
   // Update the scroll position property.
@@ -402,16 +479,18 @@ void ScrollBar::OnPan( const PanGesture& gesture )
       }
       case Gesture::Continuing:
       {
-        Vector3 delta(gesture.displacement.x, gesture.displacement.y, 0.0f);
-        mGestureDisplacement+=delta;
+        mGestureDisplacement.x += gesture.displacement.x;
+        mGestureDisplacement.y += gesture.displacement.y;
+
+        float minScrollPosition = scrollableHandle.GetProperty<float>( mPropertyMinScrollPosition );
+        float maxScrollPosition = scrollableHandle.GetProperty<float>( mPropertyMaxScrollPosition );
 
-        Vector3 span = Self().GetCurrentSize() - mIndicator.GetCurrentSize();
-        float minScrollPosition = scrollableHandle.GetProperty<float>(mPropertyMinScrollPosition);
-        float maxScrollPosition = scrollableHandle.GetProperty<float>(mPropertyMaxScrollPosition);
+        // The domain size is the internal range
         float domainSize = maxScrollPosition - minScrollPosition;
+        float logicalSize = Self().GetCurrentSize().y - ( mIndicator.GetCurrentSize().y + mIndicatorStartPadding + mIndicatorEndPadding );
 
-        mCurrentScrollPosition = mScrollStart - mGestureDisplacement.y * domainSize / span.y;
-        mCurrentScrollPosition = 0.0f - std::min(maxScrollPosition, std::max(-mCurrentScrollPosition, minScrollPosition));
+        mCurrentScrollPosition = mScrollStart - ( ( mGestureDisplacement.y * domainSize ) / logicalSize );
+        mCurrentScrollPosition = -std::min( maxScrollPosition, std::max( -mCurrentScrollPosition, minScrollPosition ) );
 
         break;
       }
@@ -453,6 +532,8 @@ void ScrollBar::OnSizeSet( const Vector3& size )
   {
     mIndicator.SetSize(size.width, mIndicatorFixedHeight);
   }
+
+  Control::OnSizeSet( size );
 }
 
 void ScrollBar::SetScrollDirection( Toolkit::ScrollBar::Direction direction )
@@ -467,8 +548,11 @@ Toolkit::ScrollBar::Direction ScrollBar::GetScrollDirection() const
 
 void ScrollBar::SetIndicatorHeightPolicy( Toolkit::ScrollBar::IndicatorHeightPolicy policy )
 {
-  mIndicatorHeightPolicy = policy;
-  ApplyConstraints();
+  if( policy != mIndicatorHeightPolicy )
+  {
+    mIndicatorHeightPolicy = policy;
+    ApplyConstraints();
+  }
 }
 
 Toolkit::ScrollBar::IndicatorHeightPolicy ScrollBar::GetIndicatorHeightPolicy() const
@@ -620,6 +704,29 @@ void ScrollBar::SetProperty( BaseObject* object, Property::Index index, const Pr
         }
         break;
       }
+      case Toolkit::ScrollBar::Property::INDICATOR_MINIMUM_HEIGHT:
+      {
+        scrollBarImpl.mIndicatorMinimumHeight = value.Get<float>();
+        scrollBarImpl.ApplyConstraints();
+        break;
+      }
+      case Toolkit::ScrollBar::Property::INDICATOR_START_PADDING:
+      {
+        scrollBarImpl.mIndicatorStartPadding = value.Get<float>();
+        scrollBarImpl.ApplyConstraints();
+        break;
+      }
+      case Toolkit::ScrollBar::Property::INDICATOR_END_PADDING:
+      {
+        scrollBarImpl.mIndicatorEndPadding = value.Get<float>();
+        scrollBarImpl.ApplyConstraints();
+        break;
+      }
+      case Toolkit::DevelScrollBar::Property::INDICATOR_TRANSIENT_DURATION:
+      {
+        scrollBarImpl.mTransientIndicatorDuration = value.Get<float>();
+        break;
+      }
     }
   }
 }
@@ -664,19 +771,75 @@ Property::Value ScrollBar::GetProperty( BaseObject* object, Property::Index inde
       {
         Property::Value value( Property::ARRAY );
         Property::Array* array = value.GetArray();
-        Dali::Vector<float> positions = scrollBarImpl.GetScrollPositionIntervals();
-        size_t positionCount( array->Count() );
-        for( size_t i( 0 ); i != positionCount; ++i )
+
+        if( array )
         {
-          array->PushBack( positions[i] );
+          Dali::Vector<float> positions = scrollBarImpl.GetScrollPositionIntervals();
+          size_t positionCount( array->Count() );
+          for( size_t i( 0 ); i != positionCount; ++i )
+          {
+            array->PushBack( positions[i] );
+          }
         }
         break;
       }
+      case Toolkit::ScrollBar::Property::INDICATOR_MINIMUM_HEIGHT:
+      {
+        value = scrollBarImpl.mIndicatorMinimumHeight;
+        break;
+      }
+      case Toolkit::ScrollBar::Property::INDICATOR_START_PADDING:
+      {
+        value = scrollBarImpl.mIndicatorStartPadding;
+        break;
+      }
+      case Toolkit::ScrollBar::Property::INDICATOR_END_PADDING:
+      {
+        value = scrollBarImpl.mIndicatorEndPadding;
+        break;
+      }
+      case Toolkit::DevelScrollBar::Property::INDICATOR_TRANSIENT_DURATION:
+      {
+        value = scrollBarImpl.mTransientIndicatorDuration;
+        break;
+      }
     }
   }
   return value;
 }
 
+bool ScrollBar::DoAction( BaseObject* object, const std::string& actionName, const Property::Map& attributes )
+{
+  bool ret = false;
+
+  Dali::BaseHandle handle( object );
+
+  Toolkit::ScrollBar scrollBar = Toolkit::ScrollBar::DownCast( handle );
+
+  DALI_ASSERT_DEBUG( scrollBar );
+
+  if( scrollBar )
+  {
+    if( 0 == strcmp( actionName.c_str(), ACTION_SHOW_INDICATOR ) )
+    {
+      GetImpl( scrollBar ).ShowIndicator();
+      ret = true;
+    }
+    else if( 0 == strcmp( actionName.c_str(), ACTION_HIDE_INDICATOR ) )
+    {
+      GetImpl( scrollBar ).HideIndicator();
+      ret = true;
+    }
+    else if( 0 == strcmp( actionName.c_str(), ACTION_SHOW_TRANSIENT_INDICATOR ) )
+    {
+      GetImpl( scrollBar ).ShowTransientIndicator();
+      ret = true;
+    }
+  }
+
+  return ret;
+}
+
 Toolkit::ScrollBar ScrollBar::New(Toolkit::ScrollBar::Direction direction)
 {
   // Create the implementation, temporarily owned by this handle on stack