Improve fast scrolling performance in ItemView 10/20310/1
authorRichard Huang <r.huang@samsung.com>
Thu, 24 Apr 2014 10:14:06 +0000 (11:14 +0100)
committerDavid Steele <david.steele@partner.samsung.com>
Thu, 1 May 2014 15:01:35 +0000 (16:01 +0100)
Change-Id: I4105319807124bc6c3a5218612eecae4f3685d10
Signed-off-by: David Steele <david.steele@partner.samsung.com>
dali-toolkit/internal/controls/scroll-bar/scroll-bar-impl.cpp
dali-toolkit/internal/controls/scroll-bar/scroll-bar-impl.h
dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.cpp
dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.h
dali-toolkit/public-api/controls/scrollable/item-view/grid-layout.cpp

index 7ae50af..21582a0 100755 (executable)
@@ -15,6 +15,7 @@
 //
 
 #include <dali-toolkit/internal/controls/scroll-bar/scroll-bar-impl.h>
+#include <dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.h>
 
 using namespace Dali;
 
@@ -26,6 +27,7 @@ const Vector4 DEFAULT_INDICATOR_NINE_PATCH_BORDER(0.0f, 12.0f, 14.0f, 14.0f);
 const float DEFAULT_SLIDER_DEPTH(1.0f);
 const float INDICATOR_SHOW_TIME(0.5f);
 const float INDICATOR_HIDE_TIME(0.5f);
+const float DEFAULT_PAN_GESTURE_PROCESS_TIME(16.7f); // 16.7 milliseconds, i.e. one frame
 
 /**
  * Indicator size constraint
@@ -126,7 +128,9 @@ TypeRegistration mType( typeid(Toolkit::ScrollBar), typeid(Toolkit::Control), Cr
 }
 
 ScrollBar::ScrollBar()
-: mScrollStart(0.0f)
+: mScrollStart(0.0f),
+  mIsPanning(false),
+  mCurrentScrollPosition(0.0f)
 {
 }
 
@@ -274,6 +278,21 @@ void ScrollBar::Hide()
   mAnimation.Play();
 }
 
+bool ScrollBar::OnPanGestureProcessTick()
+{
+  // Update the scroll position property.
+  mScrollPositionObject.SetProperty( Toolkit::ScrollConnector::SCROLL_POSITION, mCurrentScrollPosition );
+
+  Dali::Toolkit::ItemView itemView = Dali::Toolkit::ItemView::DownCast(Self().GetParent());
+  if(itemView)
+  {
+    // Refresh ItemView immediately when the scroll position is changed.
+    GetImpl(itemView).DoRefresh(mCurrentScrollPosition, false); // No need to cache extra items.
+  }
+
+  return true;
+}
+
 void ScrollBar::OnPan( PanGesture gesture )
 {
   if(mScrollPositionObject)
@@ -282,9 +301,19 @@ void ScrollBar::OnPan( PanGesture gesture )
     {
       case Gesture::Started:
       {
+        if( !mTimer )
+        {
+          // Make sure the pan gesture is only being processed once per frame.
+          mTimer = Timer::New( DEFAULT_PAN_GESTURE_PROCESS_TIME );
+          mTimer.TickSignal().Connect( this, &ScrollBar::OnPanGestureProcessTick );
+          mTimer.Start();
+        }
+
         Show();
         mScrollStart = mScrollPositionObject.GetProperty<float>( Toolkit::ScrollConnector::SCROLL_POSITION );
         mGestureDisplacement = Vector3::ZERO;
+        mIsPanning = true;
+
         break;
       }
       case Gesture::Continuing:
@@ -294,16 +323,33 @@ void ScrollBar::OnPan( PanGesture gesture )
 
         Vector3 span = Self().GetCurrentSize() - mIndicator.GetCurrentSize();
         const float domainSize = fabs(mScrollConnector.GetMaxLimit() - mScrollConnector.GetMinLimit());
-        float position = mScrollStart - mGestureDisplacement.y * domainSize / span.y;
-        position = std::min(mScrollConnector.GetMaxLimit(), std::max(position, mScrollConnector.GetMinLimit()));
-        mScrollPositionObject.SetProperty( Toolkit::ScrollConnector::SCROLL_POSITION, position );
+        mCurrentScrollPosition = mScrollStart - mGestureDisplacement.y * domainSize / span.y;
+        mCurrentScrollPosition = std::min(mScrollConnector.GetMaxLimit(), std::max(mCurrentScrollPosition, mScrollConnector.GetMinLimit()));
+
         break;
       }
       default:
       {
+        mIsPanning = false;
+
+        if( mTimer )
+        {
+          // Destroy the timer when pan gesture is finished.
+          mTimer.Stop();
+          mTimer.TickSignal().Disconnect( this, &ScrollBar::OnPanGestureProcessTick );
+          mTimer.Reset();
+        }
+
         break;
       }
     }
+
+    Dali::Toolkit::ItemView itemView = Dali::Toolkit::ItemView::DownCast(Self().GetParent());
+    if(itemView)
+    {
+      // Disable automatic refresh in ItemView during fast scrolling
+      GetImpl(itemView).SetRefreshEnabled(!mIsPanning);
+    }
   }
 }
 
index c938c7e..909e4fe 100755 (executable)
@@ -141,6 +141,12 @@ private:
    */
   void OnScrollPositionNotified(PropertyNotification& source);
 
+  /**
+   * Process the pan gesture per predefined timeout until the gesture is finished.
+   * @return True if the timer should be kept running.
+   */
+  bool OnPanGestureProcessTick();
+
 private:
 
   Constrainable mScrollPositionObject;                    ///< From mScrollConnector
@@ -149,9 +155,14 @@ private:
   ImageActor mIndicator;                                  ///< Image of scroll indicator.
   Animation mAnimation;                                   ///< Scroll indicator Show/Hide Animation.
 
-  float mScrollStart;                                     ///< Scroll Start position (start of drag)
+  float mScrollStart;                                    ///< Scroll Start position (start of drag)
   Vector3 mGestureDisplacement;                           ///< Gesture Displacement.
 
+  bool mIsPanning;                                       ///< Whether the scroll bar is being panned.
+  float mCurrentScrollPosition;                          ///< The current scroll position updated by the pan gesture
+
+  Timer mTimer;                                           ///< The timer to process the pan gesture after the gesture is started.
+
   Property::Index mPropertyIndicatorPosition;             ///< Indicatore Position ("indicator-position")
 
   PropertyNotification mPositionNotification;             ///< Stores the property notification used for scroll position changes
index 5caa208..001291f 100644 (file)
@@ -410,7 +410,8 @@ ItemView::ItemView(ItemFactory& factory)
   mScrollOvershoot(0.0f),
   mIsFlicking(false),
   mGestureState(Gesture::Clear),
-  mAddingItems(false)
+  mAddingItems(false),
+  mRefreshEnabled(true)
 {
   SetRequiresMouseWheelEvents(true);
   SetKeyboardNavigationSupport(true);
@@ -565,7 +566,7 @@ void ItemView::ActivateLayout(unsigned int layoutIndex, const Vector3& targetSiz
   }
 
   // Refresh the new layout
-  ItemRange range = GetItemRange(*mActiveLayout, targetSize, true/*reserve extra*/);
+  ItemRange range = GetItemRange(*mActiveLayout, targetSize, GetCurrentLayoutPosition(0), true/*reserve extra*/);
   AddActorsWithinRange( range, durationSeconds );
 
   // Scroll to an appropriate layout position
@@ -636,14 +637,22 @@ AlphaFunction ItemView::GetDefaultAlphaFunction() const
 
 void ItemView::OnRefreshNotification(PropertyNotification& source)
 {
+  if(mRefreshEnabled)
+  {
+    // Only refresh the cache during normal scrolling
+    DoRefresh(GetCurrentLayoutPosition(0), true);
+  }
+}
+
+void ItemView::DoRefresh(float currentLayoutPosition, bool cacheExtra)
+{
   if (mActiveLayout)
   {
-    ItemRange range = GetItemRange(*mActiveLayout, mActiveLayoutTargetSize, true/*reserve extra*/);
+    ItemRange range = GetItemRange(*mActiveLayout, mActiveLayoutTargetSize, currentLayoutPosition, cacheExtra/*reserve extra*/);
     RemoveActorsOutsideRange( range );
     AddActorsWithinRange( range, 0.0f/*immediate*/ );
 
-    Vector3 currentScrollPosition = GetCurrentScrollPosition();
-    mScrollUpdatedSignalV2.Emit( currentScrollPosition );
+    mScrollUpdatedSignalV2.Emit( Vector3(0.0f, currentLayoutPosition, 0.0f) );
   }
 }
 
@@ -714,6 +723,11 @@ float ItemView::GetRefreshInterval() const
   return mRefreshIntervalLayoutPositions;
 }
 
+void ItemView::SetRefreshEnabled(bool enabled)
+{
+  mRefreshEnabled = enabled;
+}
+
 Actor ItemView::GetItem(unsigned int itemId) const
 {
   Actor actor;
@@ -1030,13 +1044,13 @@ void ItemView::SetupActor( Item item, float durationSeconds )
   }
 }
 
-ItemRange ItemView::GetItemRange(ItemLayout& layout, const Vector3& layoutSize, bool reserveExtra)
+ItemRange ItemView::GetItemRange(ItemLayout& layout, const Vector3& layoutSize, float layoutPosition, bool reserveExtra)
 {
   unsigned int itemCount = mItemFactory.GetNumberOfItems();
 
   ItemRange available(0u, itemCount);
 
-  ItemRange range = layout.GetItemsWithinArea( GetCurrentLayoutPosition(0), layoutSize );
+  ItemRange range = layout.GetItemsWithinArea( layoutPosition, layoutSize );
 
   if (reserveExtra)
   {
index 038a567..5f42a61 100644 (file)
@@ -243,6 +243,25 @@ public:
    */
   void ScrollTo(const Vector3& position, float duration);
 
+  /**
+   * @brief Set whether to enable automatic refresh or not. When refresh is disabled,
+   * ItemView will not automatically refresh the cache in the given interval when the
+   * layout position is changed. This is useful in some cases, for example, automatic
+   * refresh is not needed during fast scrolling, otherwise it will cache unneeded
+   * items when the layout position changes quickly.
+   *
+   * @param[in] enabled True to enable automatic refresh or false to disable it.
+   */
+  void SetRefreshEnabled(bool enabled);
+
+  /**
+   * @brief Helper to perform the refresh.
+   *
+   * @param[in] currentLayoutPosition The current layout position.
+   * @param[in] cacheExtra Whether to cache extra items during refresh.
+   */
+  void DoRefresh(float currentLayoutPosition, bool cacheExtra);
+
 private:
 
   /**
@@ -379,7 +398,7 @@ private:
    * @param[in] range The range of items.
    * @param[in] reserveExtra True if reserve items should be included.
    */
-  ItemRange GetItemRange(ItemLayout& layout, const Vector3& layoutSize, bool reserveExtra);
+  ItemRange GetItemRange(ItemLayout& layout, const Vector3& layoutSize, float layoutPosition, bool reserveExtra);
 
   // Input Handling
 
@@ -543,6 +562,8 @@ private:
   Property::Index mPropertyPosition; ///< The physical position of the first item within the layout
   Property::Index mPropertyMinimumLayoutPosition; ///< The minimum valid layout position in the layout.
   Property::Index mPropertyScrollSpeed; ///< The current scroll speed of item view
+
+  bool mRefreshEnabled; ///< Whether to refresh the cache automatically
 };
 
 } // namespace Internal
index b10700b..eb3bfd8 100644 (file)
@@ -500,7 +500,7 @@ ItemRange GridLayout::GetItemsWithinArea(float firstItemPosition, Vector3 layout
   int firstVisibleItem = -(static_cast<int>(firstItemPosition / mImpl->mNumberOfColumns)) * mImpl->mNumberOfColumns;
 
   int firstItemIndex = std::max(0, firstVisibleItem - static_cast<int>(mImpl->mNumberOfColumns));
-  int lastItemIndex  = std::max(0, firstVisibleItem + (itemsPerPage - 1));
+  int lastItemIndex  = std::max(0, firstVisibleItem + itemsPerPage);
 
   return ItemRange(firstItemIndex, lastItemIndex);
 }