Fixed issue that ItemView overshoot appears before it reaches the end of the list
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / scrollable / item-view / item-view-impl.cpp
index 9ef9b3a..27054bd 100644 (file)
 #include <algorithm>
 #include <dali/public-api/animation/constraint.h>
 #include <dali/public-api/animation/constraints.h>
-#include <dali/public-api/common/set-wrapper.h>
+#include <dali/devel-api/common/set-wrapper.h>
 #include <dali/public-api/common/stage.h>
-#include <dali/public-api/events/mouse-wheel-event.h>
+#include <dali/public-api/events/wheel-event.h>
 #include <dali/public-api/events/touch-event.h>
 #include <dali/public-api/object/type-registry.h>
-#include <dali/public-api/object/type-registry-helper.h>
+#include <dali/devel-api/object/type-registry-helper.h>
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/public-api/controls/scroll-bar/scroll-bar.h>
@@ -53,23 +53,24 @@ DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ItemView, "scroll-direction",
 DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ItemView, "layout-orientation",  INTEGER,  LAYOUT_ORIENTATION)
 DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ItemView, "scroll-content-size", FLOAT,    SCROLL_CONTENT_SIZE)
 
+DALI_SIGNAL_REGISTRATION(              Toolkit, ItemView, "layout-activated",    LAYOUT_ACTIVATED_SIGNAL )
+
 DALI_TYPE_REGISTRATION_END()
 
 const float DEFAULT_MINIMUM_SWIPE_SPEED = 1.0f;
 const float DEFAULT_MINIMUM_SWIPE_DISTANCE = 3.0f;
-const float DEFAULT_MOUSE_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION = 0.1f;
+const float DEFAULT_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION = 0.1f;
 
 const float DEFAULT_MINIMUM_SWIPE_DURATION = 0.45f;
 const float DEFAULT_MAXIMUM_SWIPE_DURATION = 2.6f;
 
 const float DEFAULT_REFRESH_INTERVAL_LAYOUT_POSITIONS = 20.0f; // 1 updates per 20 items
-const int MOUSE_WHEEL_EVENT_FINISHED_TIME_OUT = 500;  // 0.5 second
+const int WHEEL_EVENT_FINISHED_TIME_OUT = 500;  // 0.5 second
 
 const float DEFAULT_ANCHORING_DURATION = 1.0f;  // 1 second
 
 const float MILLISECONDS_PER_SECONDS = 1000.0f;
 
-const Vector2 OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE( 720.0f, 42.0f );
 const float OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD = 180.0f;
 const Vector4 OVERSHOOT_OVERLAY_NINE_PATCH_BORDER(0.0f, 0.0f, 1.0f, 12.0f);
 const float DEFAULT_KEYBOARD_FOCUS_SCROLL_DURATION = 0.2f;
@@ -88,23 +89,33 @@ float CalculateScrollDistance(Vector2 panDistance, Toolkit::ItemLayout& layout)
 }
 
 // Overshoot overlay constraints
-void OvershootOverlaySizeConstraint( Vector3& current, const PropertyInputContainer& inputs )
+struct OvershootOverlaySizeConstraint
 {
-  const Vector2& parentScrollDirection = inputs[0]->GetVector2();
-  const Toolkit::ControlOrientation::Type& layoutOrientation = static_cast<Toolkit::ControlOrientation::Type>(inputs[1]->GetInteger());
-  const Vector3& parentSize = inputs[2]->GetVector3();
-
-  if(Toolkit::IsVertical(layoutOrientation))
+  OvershootOverlaySizeConstraint( float height )
+  : mOvershootHeight( height )
   {
-    current.width = fabsf(parentScrollDirection.y) > Math::MACHINE_EPSILON_1 ? parentSize.x : parentSize.y;
   }
-  else
+
+  void operator()( Vector3& current, const PropertyInputContainer& inputs )
   {
-    current.width = fabsf(parentScrollDirection.x) > Math::MACHINE_EPSILON_1 ? parentSize.y : parentSize.x;
+    const Vector2& parentScrollDirection = inputs[0]->GetVector2();
+    const Toolkit::ControlOrientation::Type& layoutOrientation = static_cast<Toolkit::ControlOrientation::Type>(inputs[1]->GetInteger());
+    const Vector3& parentSize = inputs[2]->GetVector3();
+
+    if(Toolkit::IsVertical(layoutOrientation))
+    {
+      current.width = fabsf(parentScrollDirection.y) > Math::MACHINE_EPSILON_1 ? parentSize.x : parentSize.y;
+    }
+    else
+    {
+      current.width = fabsf(parentScrollDirection.x) > Math::MACHINE_EPSILON_1 ? parentSize.y : parentSize.x;
+    }
+
+    current.height = ( current.width > OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD ) ? mOvershootHeight : mOvershootHeight*0.5f;
   }
 
-  current.height = ( current.width > OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD ) ? OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.height : OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.height*0.5f;
-}
+  float mOvershootHeight;
+};
 
 void OvershootOverlayRotationConstraint( Quaternion& current, const PropertyInputContainer& inputs )
 {
@@ -276,7 +287,7 @@ Dali::Toolkit::ItemView ItemView::New(ItemFactory& factory)
 }
 
 ItemView::ItemView(ItemFactory& factory)
-: Scrollable( ControlBehaviour( DISABLE_SIZE_NEGOTIATION | REQUIRES_MOUSE_WHEEL_EVENTS | REQUIRES_KEYBOARD_NAVIGATION_SUPPORT ) ),
+: Scrollable( ControlBehaviour( DISABLE_SIZE_NEGOTIATION | REQUIRES_WHEEL_EVENTS | REQUIRES_KEYBOARD_NAVIGATION_SUPPORT ) ),
   mItemFactory(factory),
   mActiveLayout(NULL),
   mAnimatingOvershootOn(false),
@@ -287,7 +298,7 @@ ItemView::ItemView(ItemFactory& factory)
   mRefreshOrderHint(true/*Refresh item 0 first*/),
   mMinimumSwipeSpeed(DEFAULT_MINIMUM_SWIPE_SPEED),
   mMinimumSwipeDistance(DEFAULT_MINIMUM_SWIPE_DISTANCE),
-  mMouseWheelScrollDistanceStep(0.0f),
+  mWheelScrollDistanceStep(0.0f),
   mScrollDistance(0.0f),
   mScrollSpeed(0.0f),
   mTotalPanDisplacement(Vector2::ZERO),
@@ -305,15 +316,13 @@ void ItemView::OnInitialize()
 {
   Actor self = Self();
 
-  SetOvershootEnabled(true);
-
   Vector2 stageSize = Stage::GetCurrent().GetSize();
-  mMouseWheelScrollDistanceStep = stageSize.y * DEFAULT_MOUSE_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION;
+  mWheelScrollDistanceStep = stageSize.y * DEFAULT_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION;
 
   EnableGestureDetection(Gesture::Type(Gesture::Pan));
 
-  mMouseWheelEventFinishedTimer = Timer::New( MOUSE_WHEEL_EVENT_FINISHED_TIME_OUT );
-  mMouseWheelEventFinishedTimer.TickSignal().Connect( this, &ItemView::OnMouseWheelEventFinished );
+  mWheelEventFinishedTimer = Timer::New( WHEEL_EVENT_FINISHED_TIME_OUT );
+  mWheelEventFinishedTimer.TickSignal().Connect( this, &ItemView::OnWheelEventFinished );
 
   SetRefreshInterval(DEFAULT_REFRESH_INTERVAL_LAYOUT_POSITIONS);
 }
@@ -422,6 +431,11 @@ void ItemView::ActivateLayout(unsigned int layoutIndex, const Vector3& targetSiz
     mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnLayoutActivationScrollFinished);
     mScrollAnimation.Play();
   }
+  else
+  {
+    // Emit the layout activated signal
+    mLayoutActivatedSignal.Emit();
+  }
 
   AnimateScrollOvershoot(0.0f);
   mScrollOvershoot = 0.0f;
@@ -450,11 +464,14 @@ void ItemView::DeactivateCurrentLayout()
 
 void ItemView::OnRefreshNotification(PropertyNotification& source)
 {
-  if(mRefreshEnabled || mScrollAnimation)
+  // Cancel scroll animation to prevent any fighting of setting the scroll position property by scroll bar during fast scroll.
+  if(!mRefreshEnabled && mScrollAnimation)
   {
-    // Only refresh the cache during normal scrolling
-    DoRefresh(GetCurrentLayoutPosition(0), true);
+    RemoveAnimation(mScrollAnimation);
   }
+
+  // Only cache extra items when it is not a fast scroll
+  DoRefresh(GetCurrentLayoutPosition(0), mRefreshEnabled || mScrollAnimation);
 }
 
 void ItemView::Refresh()
@@ -500,14 +517,14 @@ float ItemView::GetMinimumSwipeDistance() const
   return mMinimumSwipeDistance;
 }
 
-void ItemView::SetMouseWheelScrollDistanceStep(float step)
+void ItemView::SetWheelScrollDistanceStep(float step)
 {
-  mMouseWheelScrollDistanceStep = step;
+  mWheelScrollDistanceStep = step;
 }
 
-float ItemView::GetMouseWheelScrollDistanceStep() const
+float ItemView::GetWheelScrollDistanceStep() const
 {
-  return mMouseWheelScrollDistanceStep;
+  return mWheelScrollDistanceStep;
 }
 
 void ItemView::SetAnchoring(bool enabled)
@@ -950,7 +967,6 @@ void ItemView::OnChildAdd(Actor& child)
                                         Toolkit::Scrollable::Property::SCROLL_POSITION_MIN_Y,
                                         Toolkit::Scrollable::Property::SCROLL_POSITION_MAX_Y,
                                         Toolkit::ItemView::Property::SCROLL_CONTENT_SIZE);
-      scrollBar.ScrollPositionIntervalReachedSignal().Connect( this, &ItemView::OnScrollPositionChanged );
     }
   }
 }
@@ -986,14 +1002,14 @@ bool ItemView::OnTouchEvent(const TouchEvent& event)
   return true; // consume since we're potentially scrolling
 }
 
-bool ItemView::OnMouseWheelEvent(const MouseWheelEvent& event)
+bool ItemView::OnWheelEvent(const WheelEvent& event)
 {
-  // Respond the mouse wheel event to scroll
+  // Respond the wheel event to scroll
   if (mActiveLayout)
   {
     Actor self = Self();
     const Vector3 layoutSize = Self().GetCurrentSize();
-    float layoutPositionDelta = GetCurrentLayoutPosition(0) - (event.z * mMouseWheelScrollDistanceStep * mActiveLayout->GetScrollSpeedFactor());
+    float layoutPositionDelta = GetCurrentLayoutPosition(0) - (event.z * mWheelScrollDistanceStep * mActiveLayout->GetScrollSpeedFactor());
     float firstItemScrollPosition = ClampFirstItemPosition(layoutPositionDelta, layoutSize, *mActiveLayout);
 
     self.SetProperty(Toolkit::ItemView::Property::LAYOUT_POSITION, firstItemScrollPosition );
@@ -1002,23 +1018,23 @@ bool ItemView::OnMouseWheelEvent(const MouseWheelEvent& event)
     mRefreshEnabled = true;
   }
 
-  if (mMouseWheelEventFinishedTimer.IsRunning())
+  if (mWheelEventFinishedTimer.IsRunning())
   {
-    mMouseWheelEventFinishedTimer.Stop();
+    mWheelEventFinishedTimer.Stop();
   }
 
-  mMouseWheelEventFinishedTimer.Start();
+  mWheelEventFinishedTimer.Start();
 
   return true;
 }
 
-bool ItemView::OnMouseWheelEventFinished()
+bool ItemView::OnWheelEventFinished()
 {
   if (mActiveLayout)
   {
     RemoveAnimation(mScrollAnimation);
 
-    // No more mouse wheel events coming. Do the anchoring if enabled.
+    // No more wheel events coming. Do the anchoring if enabled.
     mScrollAnimation = DoAnchoring();
     if (mScrollAnimation)
     {
@@ -1168,7 +1184,10 @@ void ItemView::OnPan( const PanGesture& gesture )
 
       self.SetProperty(Toolkit::ItemView::Property::LAYOUT_POSITION, firstItemScrollPosition );
 
-      if( (firstItemScrollPosition >= 0.0f && currentOvershoot < 1.0f) || (firstItemScrollPosition >= mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), layoutSize) && currentOvershoot > -1.0f) )
+      if( ( firstItemScrollPosition >= 0.0f &&
+            currentOvershoot < 1.0f ) ||
+          ( firstItemScrollPosition <= mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), layoutSize) &&
+            currentOvershoot > -1.0f ) )
       {
         mTotalPanDisplacement += gesture.displacement;
       }
@@ -1201,7 +1220,7 @@ bool ItemView::OnAccessibilityPan(PanGesture gesture)
   return true;
 }
 
-Actor ItemView::GetNextKeyboardFocusableActor(Actor actor, Toolkit::Control::KeyboardFocusNavigationDirection direction, bool loopEnabled)
+Actor ItemView::GetNextKeyboardFocusableActor(Actor actor, Toolkit::Control::KeyboardFocus::Direction direction, bool loopEnabled)
 {
   Actor nextFocusActor;
   if(mActiveLayout)
@@ -1298,6 +1317,9 @@ void ItemView::OnLayoutActivationScrollFinished(Animation& source)
   RemoveAnimation(mScrollAnimation);
   mRefreshEnabled = true;
   DoRefresh(GetCurrentLayoutPosition(0), true);
+
+  // Emit the layout activated signal
+  mLayoutActivatedSignal.Emit();
 }
 
 void ItemView::OnOvershootOnFinished(Animation& animation)
@@ -1415,6 +1437,7 @@ Vector2 ItemView::GetCurrentScrollPosition() const
 
 void ItemView::AddOverlay(Actor actor)
 {
+  actor.SetDrawMode( DrawMode::OVERLAY_2D );
   Self().Add(actor);
 }
 
@@ -1467,16 +1490,16 @@ void ItemView::EnableScrollOvershoot( bool enable )
     mOvershootOverlay.SetColor(mOvershootEffectColor);
     mOvershootOverlay.SetParentOrigin(ParentOrigin::TOP_LEFT);
     mOvershootOverlay.SetAnchorPoint(AnchorPoint::TOP_LEFT);
-    mOvershootOverlay.SetDrawMode(DrawMode::OVERLAY);
+    mOvershootOverlay.SetDrawMode( DrawMode::OVERLAY_2D );
     self.Add(mOvershootOverlay);
 
-    Constraint constraint = Constraint::New<Vector3>( mOvershootOverlay, Actor::Property::SIZE, OvershootOverlaySizeConstraint );
+    Constraint constraint = Constraint::New<Vector3>( mOvershootOverlay, Actor::Property::SIZE, OvershootOverlaySizeConstraint(mOvershootSize.height) );
     constraint.AddSource( ParentSource( Toolkit::ItemView::Property::SCROLL_DIRECTION ) );
     constraint.AddSource( ParentSource( Toolkit::ItemView::Property::LAYOUT_ORIENTATION ) );
     constraint.AddSource( ParentSource( Actor::Property::SIZE ) );
     constraint.Apply();
 
-    mOvershootOverlay.SetSize(OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.width, OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.height);
+    mOvershootOverlay.SetSize(mOvershootSize.width, mOvershootSize.height);
 
     constraint = Constraint::New<Quaternion>( mOvershootOverlay, Actor::Property::ORIENTATION, OvershootOverlayRotationConstraint );
     constraint.AddSource( ParentSource( Toolkit::ItemView::Property::SCROLL_DIRECTION ) );
@@ -1616,13 +1639,24 @@ void ItemView::GetItemsRange(ItemRange& range)
   }
 }
 
-void ItemView::OnScrollPositionChanged( float position )
+bool ItemView::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
 {
-  // Cancel scroll animation to prevent any fighting of setting the scroll position property.
-  RemoveAnimation(mScrollAnimation);
+  Dali::BaseHandle handle( object );
+
+  bool connected( true );
+  Toolkit::ItemView itemView = Toolkit::ItemView::DownCast( handle );
+
+  if( 0 == strcmp( signalName.c_str(), LAYOUT_ACTIVATED_SIGNAL ) )
+  {
+    itemView.LayoutActivatedSignal().Connect( tracker, functor );
+  }
+  else
+  {
+    // signalName does not match any signal
+    connected = false;
+  }
 
-  // Refresh the cache immediately when the scroll position is changed.
-  DoRefresh(position, false); // No need to cache extra items.
+  return connected;
 }
 
 } // namespace Internal