New API to get the range of items in the current ItemView cache
[platform/core/uifw/dali-toolkit.git] / base / dali-toolkit / internal / controls / scrollable / item-view / item-view-impl.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <algorithm>
23
24 // INTERNAL INCLUDES
25 #include <dali/public-api/events/mouse-wheel-event.h>
26 #include <dali-toolkit/public-api/controls/scrollable/item-view/item-factory.h>
27 #include <dali-toolkit/internal/controls/scrollable/scroll-connector-impl.h>
28 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
29
30 using namespace std;
31 using namespace Dali;
32
33 namespace // unnamed namespace
34 {
35
36 //Type registration
37 TypeRegistration mType( typeid(Toolkit::ItemView), typeid(Toolkit::Scrollable), NULL );
38
39 const float DEFAULT_MINIMUM_SWIPE_SPEED = 1.0f;
40 const float DEFAULT_MINIMUM_SWIPE_DISTANCE = 3.0f;
41 const float DEFAULT_MOUSE_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION = 0.1f;
42
43 const float DEFAULT_REFRESH_INTERVAL_LAYOUT_POSITIONS = 20.0f; // 1 updates per 20 items
44 const int MOUSE_WHEEL_EVENT_FINISHED_TIME_OUT = 500;  // 0.5 second
45
46 const float DEFAULT_ANCHORING_DURATION = 1.0f;  // 1 second
47 const float DEFAULT_COLOR_VISIBILITY_REMOVE_TIME = 0.5f; // 0.5 second
48
49 const float MILLISECONDS_PER_SECONDS = 1000.0f;
50
51 const Rect<int> OVERSHOOT_BOUNCE_IMAGE_1_PIXEL_AREA( 0, 0, 720, 58 );
52 const Vector4 OVERSHOOT_OVERLAY_NINE_PATCH_BORDER(0.0f, 0.0f, 1.0f, 12.0f);
53 const float MAXIMUM_OVERSHOOT_HEIGHT = 36.0f;  // 36 pixels
54 const float DEFAULT_OVERSHOOT_ANIMATION_DURATION = 0.5f;  // 0.5 second
55 const float DEFAULT_KEYBOARD_FOCUS_SCROLL_DURATION = 0.2f;
56
57 const string LAYOUT_POSITION_PROPERTY_NAME( "item-view-layout-position" );
58 const string POSITION_PROPERTY_NAME( "item-view-position" );
59 const string MINIMUM_LAYOUT_POSITION_PROPERTY_NAME( "item-view-minimum-layout-position" );
60 const string SCROLL_SPEED_PROPERTY_NAME( "item-view-scroll-speed" );
61 const string SCROLL_DIRECTION_PROPERTY_NAME( "item-view-scroll-direction" );
62 const string OVERSHOOT_PROPERTY_NAME( "item-view-overshoot" );
63
64 // Functors which wrap constraint functions with stored item IDs
65
66 struct WrappedVector3Constraint
67 {
68   WrappedVector3Constraint(Toolkit::ItemLayout::Vector3Function wrapMe, unsigned int itemId)
69   : mWrapMe(wrapMe),
70     mItemId(itemId)
71   {
72   }
73
74   Vector3 operator()(const Vector3& current, const PropertyInput& layoutPosition, const PropertyInput& scrollSpeed, const PropertyInput& layoutSize)
75   {
76     float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast<float>(mItemId);
77
78     return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3());
79   }
80
81   Toolkit::ItemLayout::Vector3Function mWrapMe;
82   unsigned int mItemId;
83 };
84
85 struct WrappedQuaternionConstraint
86 {
87   WrappedQuaternionConstraint(Toolkit::ItemLayout::QuaternionFunction wrapMe, unsigned int itemId)
88   : mWrapMe(wrapMe),
89     mItemId(itemId)
90   {
91   }
92
93   Quaternion operator()(const Quaternion& current, const PropertyInput& layoutPosition, const PropertyInput& scrollSpeed, const PropertyInput& layoutSize)
94   {
95     float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast<float>(mItemId);
96
97     return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3());
98   }
99
100   Toolkit::ItemLayout::QuaternionFunction mWrapMe;
101   unsigned int mItemId;
102 };
103
104 struct WrappedVector4Constraint
105 {
106   WrappedVector4Constraint(Toolkit::ItemLayout::Vector4Function wrapMe, unsigned int itemId)
107   : mWrapMe(wrapMe),
108     mItemId(itemId)
109   {
110   }
111
112   Vector4 operator()(const Vector4& current, const PropertyInput& layoutPosition, const PropertyInput& scrollSpeed, const PropertyInput& layoutSize)
113   {
114     float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast<float>(mItemId);
115
116     return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3());
117   }
118
119   Toolkit::ItemLayout::Vector4Function mWrapMe;
120   unsigned int mItemId;
121 };
122
123 struct WrappedBoolConstraint
124 {
125   WrappedBoolConstraint(Toolkit::ItemLayout::BoolFunction wrapMe, unsigned int itemId)
126   : mWrapMe(wrapMe),
127     mItemId(itemId)
128   {
129   }
130
131   bool operator()(const bool& current, const PropertyInput& layoutPosition, const PropertyInput& scrollSpeed, const PropertyInput& layoutSize)
132   {
133     float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast<float>(mItemId);
134
135     return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3());
136   }
137
138   Toolkit::ItemLayout::BoolFunction mWrapMe;
139   unsigned int mItemId;
140 };
141
142 /**
143  * Local helper to convert pan distance (in actor coordinates) to the layout-specific scrolling direction
144  */
145 float CalculateScrollDistance(Vector2 panDistance, Toolkit::ItemLayout& layout)
146 {
147   Radian scrollDirection(layout.GetScrollDirection());
148
149   float cosTheta = cosf(scrollDirection);
150   float sinTheta = sinf(scrollDirection);
151
152   return panDistance.x * sinTheta + panDistance.y * cosTheta;
153 }
154
155 // Overshoot overlay constraints
156
157 struct OvershootOverlaySizeConstraint
158 {
159   float operator()(const float& current,
160                      const PropertyInput& parentScrollDirectionProperty,
161                      const PropertyInput& parentOvershootProperty,
162                      const PropertyInput& parentSizeProperty)
163   {
164     const Vector3 parentScrollDirection = parentScrollDirectionProperty.GetVector3();
165     const Vector3 parentSize = parentSizeProperty.GetVector3();
166     const Toolkit::ControlOrientation::Type& parentOrientation = static_cast<Toolkit::ControlOrientation::Type>(parentScrollDirection.z);
167
168     float overlayWidth;
169
170     if(Toolkit::IsVertical(parentOrientation))
171     {
172       overlayWidth = fabsf(parentScrollDirection.y) > Math::MACHINE_EPSILON_1 ? parentSize.x : parentSize.y;
173     }
174     else
175     {
176       overlayWidth = fabsf(parentScrollDirection.x) > Math::MACHINE_EPSILON_1 ? parentSize.y : parentSize.x;
177     }
178
179     return overlayWidth;
180   }
181 };
182
183 struct OvershootOverlayRotationConstraint
184 {
185   Quaternion operator()(const Quaternion& current,
186                         const PropertyInput& parentScrollDirectionProperty,
187                         const PropertyInput& parentOvershootProperty)
188   {
189     const Vector3 parentScrollDirection = parentScrollDirectionProperty.GetVector3();
190     const float parentOvershoot = parentOvershootProperty.GetFloat();
191     const Toolkit::ControlOrientation::Type& parentOrientation = static_cast<Toolkit::ControlOrientation::Type>(parentScrollDirection.z);
192
193     Quaternion rotation;
194
195     if(Toolkit::IsVertical(parentOrientation))
196     {
197       if(fabsf(parentScrollDirection.y) <= Math::MACHINE_EPSILON_1)
198       {
199         if( (parentOrientation == Toolkit::ControlOrientation::Up && parentOvershoot < Math::MACHINE_EPSILON_0)
200             || (parentOrientation == Toolkit::ControlOrientation::Down && parentOvershoot > Math::MACHINE_EPSILON_0) )
201         {
202           rotation = Quaternion(0.5f * Math::PI, Vector3::ZAXIS);
203         }
204         else
205         {
206           rotation = Quaternion(1.5f * Math::PI, Vector3::ZAXIS);
207         }
208       }
209       else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.y > Math::MACHINE_EPSILON_0)
210             || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.y < Math::MACHINE_EPSILON_0) )
211       {
212         rotation = Quaternion(0.0f, Vector3::ZAXIS);
213       }
214       else
215       {
216         rotation = Quaternion(Math::PI, Vector3::ZAXIS);
217       }
218     }
219     else
220     {
221       if(fabsf(parentScrollDirection.x) <= Math::MACHINE_EPSILON_1)
222       {
223         if( (parentOrientation == Toolkit::ControlOrientation::Left && parentOvershoot > Math::MACHINE_EPSILON_0)
224             ||(parentOrientation == Toolkit::ControlOrientation::Right && parentOvershoot < Math::MACHINE_EPSILON_0) )
225         {
226           rotation = Quaternion(Math::PI, Vector3::ZAXIS);
227         }
228         else
229         {
230           rotation = Quaternion(0.0f, Vector3::ZAXIS);
231         }
232       }
233       else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.x > Math::MACHINE_EPSILON_0)
234             || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.x < Math::MACHINE_EPSILON_0) )
235       {
236         rotation = Quaternion(1.5f * Math::PI, Vector3::ZAXIS);
237       }
238       else
239       {
240         rotation = Quaternion(0.5f * Math::PI, Vector3::ZAXIS);
241       }
242     }
243
244     return rotation;
245   }
246 };
247
248 struct OvershootOverlayPositionConstraint
249 {
250   Vector3 operator()(const Vector3&    current,
251                      const PropertyInput& parentSizeProperty,
252                      const PropertyInput& parentScrollDirectionProperty,
253                      const PropertyInput& parentOvershootProperty)
254   {
255     const Vector3 parentScrollDirection = parentScrollDirectionProperty.GetVector3();
256     const float parentOvershoot = parentOvershootProperty.GetFloat();
257     const Vector3 parentSize = parentSizeProperty.GetVector3();
258     const Toolkit::ControlOrientation::Type& parentOrientation = static_cast<Toolkit::ControlOrientation::Type>(parentScrollDirection.z);
259
260     Vector3 relativeOffset;
261
262     if(Toolkit::IsVertical(parentOrientation))
263     {
264       if(fabsf(parentScrollDirection.y) <= Math::MACHINE_EPSILON_1)
265       {
266         if( (parentOrientation == Toolkit::ControlOrientation::Up && parentOvershoot < Math::MACHINE_EPSILON_0)
267             || (parentOrientation == Toolkit::ControlOrientation::Down && parentOvershoot > Math::MACHINE_EPSILON_0) )
268         {
269           relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
270         }
271         else
272         {
273           relativeOffset =Vector3(0.0f, 1.0f, 0.0f);
274         }
275       }
276       else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.y > Math::MACHINE_EPSILON_0)
277             || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.y < Math::MACHINE_EPSILON_0) )
278       {
279         relativeOffset = Vector3(0.0f, 0.0f, 0.0f);
280       }
281       else
282       {
283         relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
284       }
285     }
286     else
287     {
288       if(fabsf(parentScrollDirection.x) <= Math::MACHINE_EPSILON_1)
289       {
290         if( (parentOrientation == Toolkit::ControlOrientation::Left && parentOvershoot < Math::MACHINE_EPSILON_0)
291             || (parentOrientation == Toolkit::ControlOrientation::Right && parentOvershoot > Math::MACHINE_EPSILON_0) )
292         {
293           relativeOffset = Vector3(0.0f, 0.0f, 0.0f);
294         }
295         else
296         {
297           relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
298         }
299       }
300       else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.x > Math::MACHINE_EPSILON_0)
301             || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.x < Math::MACHINE_EPSILON_0) )
302       {
303         relativeOffset = Vector3(0.0f, 1.0f, 0.0f);
304       }
305       else
306       {
307         relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
308       }
309     }
310
311     return relativeOffset * parentSize;
312
313   }
314 };
315
316 struct OvershootOverlayVisibilityConstraint
317 {
318   bool operator()(const bool& current,
319                   const PropertyInput& parentLayoutScrollableProperty)
320   {
321     const bool parentLayoutScrollable = parentLayoutScrollableProperty.GetBoolean();
322
323     return parentLayoutScrollable;
324   }
325 };
326
327 /**
328  * Relative position Constraint
329  * Generates the relative position value of the item view based on the layout position,
330  * and it's relation to the layout domain. This is a value from 0.0f to 1.0f in each axis.
331  */
332 Vector3 RelativePositionConstraint(const Vector3& current,
333                                    const PropertyInput& scrollPositionProperty,
334                                    const PropertyInput& scrollMinProperty,
335                                    const PropertyInput& scrollMaxProperty,
336                                    const PropertyInput& layoutSizeProperty)
337 {
338   const Vector3& position = Vector3(0.0f, scrollPositionProperty.GetFloat(), 0.0f);
339   const Vector3& min = scrollMinProperty.GetVector3();
340   const Vector3& max = scrollMaxProperty.GetVector3();
341
342   Vector3 relativePosition;
343   Vector3 domainSize = max - min;
344
345   relativePosition.x = fabsf(domainSize.x) > Math::MACHINE_EPSILON_1 ? ((min.x - position.x) / fabsf(domainSize.x)) : 0.0f;
346   relativePosition.y = fabsf(domainSize.y) > Math::MACHINE_EPSILON_1 ? ((min.y - position.y) / fabsf(domainSize.y)) : 0.0f;
347
348   return relativePosition;
349 }
350
351 } // unnamed namespace
352
353 namespace Dali
354 {
355
356 namespace Toolkit
357 {
358
359 namespace Internal
360 {
361
362 namespace // unnamed namespace
363 {
364
365 bool FindById( const ItemContainer& items, ItemId id )
366 {
367   for( ConstItemIter iter = items.begin(); items.end() != iter; ++iter )
368   {
369     if( iter->first == id )
370     {
371       return true;
372     }
373   }
374
375   return false;
376 }
377
378 } // unnamed namespace
379
380 Dali::Toolkit::ItemView ItemView::New(ItemFactory& factory)
381 {
382   // Create the implementation
383   ItemViewPtr itemView(new ItemView(factory));
384
385   // Pass ownership to CustomActor via derived handle
386   Dali::Toolkit::ItemView handle(*itemView);
387
388   // Second-phase init of the implementation
389   // This can only be done after the CustomActor connection has been made...
390   itemView->Initialize();
391
392   return handle;
393 }
394
395 ItemView::ItemView(ItemFactory& factory)
396 : Scrollable(),
397   mItemFactory(factory),
398   mActiveLayout(NULL),
399   mDefaultAlphaFunction(Dali::Constraint::DEFAULT_ALPHA_FUNCTION),
400   mAnimatingOvershootOn(false),
401   mAnimateOvershootOff(false),
402   mAnchoringEnabled(true),
403   mAnchoringDuration(DEFAULT_ANCHORING_DURATION),
404   mRefreshIntervalLayoutPositions(DEFAULT_REFRESH_INTERVAL_LAYOUT_POSITIONS),
405   mRefreshOrderHint(true/*Refresh item 0 first*/),
406   mMinimumSwipeSpeed(DEFAULT_MINIMUM_SWIPE_SPEED),
407   mMinimumSwipeDistance(DEFAULT_MINIMUM_SWIPE_DISTANCE),
408   mScrollDistance(0.0f),
409   mScrollSpeed(0.0f),
410   mTotalPanDisplacement(Vector2::ZERO),
411   mScrollOvershoot(0.0f),
412   mIsFlicking(false),
413   mGestureState(Gesture::Clear),
414   mAddingItems(false),
415   mRefreshEnabled(true),
416   mItemsParentOrigin( ParentOrigin::CENTER),
417   mItemsAnchorPoint( AnchorPoint::CENTER)
418 {
419   SetRequiresMouseWheelEvents(true);
420   SetKeyboardNavigationSupport(true);
421 }
422
423 void ItemView::OnInitialize()
424 {
425   SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
426
427   RegisterCommonProperties();
428
429   Actor self = Self();
430
431   mScrollConnector = Dali::Toolkit::ScrollConnector::New();
432   mScrollPositionObject = mScrollConnector.GetScrollPositionObject();
433
434   mPropertyMinimumLayoutPosition = self.RegisterProperty(MINIMUM_LAYOUT_POSITION_PROPERTY_NAME, 0.0f);
435   mPropertyPosition = self.RegisterProperty(POSITION_PROPERTY_NAME, 0.0f);
436   mPropertyScrollSpeed = self.RegisterProperty(SCROLL_SPEED_PROPERTY_NAME, 0.0f);
437
438   EnableScrollComponent(Toolkit::Scrollable::OvershootIndicator);
439
440   Constraint constraint = Constraint::New<Vector3>(mPropertyRelativePosition,
441                                                    LocalSource(mPropertyPosition),
442                                                    LocalSource(mPropertyPositionMin),
443                                                    LocalSource(mPropertyPositionMax),
444                                                    LocalSource(Actor::SIZE),
445                                                    RelativePositionConstraint);
446   self.ApplyConstraint(constraint);
447
448   Vector2 stageSize = Stage::GetCurrent().GetSize();
449   mMouseWheelScrollDistanceStep = stageSize.y * DEFAULT_MOUSE_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION;
450
451   EnableGestureDetection(Gesture::Type(Gesture::Pan));
452
453   mMouseWheelEventFinishedTimer = Timer::New( MOUSE_WHEEL_EVENT_FINISHED_TIME_OUT );
454   mMouseWheelEventFinishedTimer.TickSignal().Connect( this, &ItemView::OnMouseWheelEventFinished );
455
456   SetRefreshInterval(mRefreshIntervalLayoutPositions);
457 }
458
459 ItemView::~ItemView()
460 {
461 }
462
463 Dali::Toolkit::ScrollConnector ItemView::GetScrollConnector() const
464 {
465   return mScrollConnector;
466 }
467
468 unsigned int ItemView::GetLayoutCount() const
469 {
470   return mLayouts.size();
471 }
472
473 void ItemView::AddLayout(ItemLayout& layout)
474 {
475   mLayouts.push_back(ItemLayoutPtr(&layout));
476 }
477
478 void ItemView::RemoveLayout(unsigned int layoutIndex)
479 {
480   DALI_ASSERT_ALWAYS(layoutIndex < mLayouts.size());
481
482   if (mActiveLayout == mLayouts[layoutIndex].Get())
483   {
484     mActiveLayout = NULL;
485   }
486
487   mLayouts.erase(mLayouts.begin() + layoutIndex);
488 }
489
490 ItemLayoutPtr ItemView::GetLayout(unsigned int layoutIndex) const
491 {
492   return mLayouts[layoutIndex];
493 }
494
495 ItemLayoutPtr ItemView::GetActiveLayout() const
496 {
497   return ItemLayoutPtr(mActiveLayout);
498 }
499
500 float ItemView::GetCurrentLayoutPosition(unsigned int itemId) const
501 {
502   return mScrollPositionObject.GetProperty<float>( ScrollConnector::SCROLL_POSITION ) + static_cast<float>( itemId );
503 }
504
505 void ItemView::ActivateLayout(unsigned int layoutIndex, const Vector3& targetSize, float durationSeconds)
506 {
507   DALI_ASSERT_ALWAYS(layoutIndex < mLayouts.size());
508
509   Actor self = Self();
510
511   // The ItemView size should match the active layout size
512   self.SetSize(targetSize);
513   mActiveLayoutTargetSize = targetSize;
514
515   // Switch to the new layout
516   mActiveLayout = mLayouts[layoutIndex].Get();
517
518   // Move the items to the new layout positions...
519
520   bool resizeAnimationNeeded(false);
521
522   for (ConstItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
523   {
524     unsigned int itemId = iter->first;
525     Actor actor = iter->second;
526
527     // Remove constraints from previous layout
528     actor.RemoveConstraints();
529
530     Vector3 size;
531     if(mActiveLayout->GetItemSize(itemId, targetSize, size))
532     {
533       if( durationSeconds > 0.0f )
534       {
535         // Use a size animation
536         if (!resizeAnimationNeeded)
537         {
538           resizeAnimationNeeded = true;
539           RemoveAnimation(mResizeAnimation);
540           mResizeAnimation = Animation::New(durationSeconds);
541         }
542
543         // The layout provides its own resize animation
544         mActiveLayout->GetResizeAnimation(mResizeAnimation, actor, size, durationSeconds);
545       }
546       else
547       {
548         // resize immediately
549         actor.SetSize(size);
550       }
551     }
552
553     ApplyConstraints(actor, *mActiveLayout, itemId, durationSeconds);
554   }
555
556   if (resizeAnimationNeeded)
557   {
558     mResizeAnimation.Play();
559   }
560
561   // Refresh the new layout
562   ItemRange range = GetItemRange(*mActiveLayout, targetSize, GetCurrentLayoutPosition(0), true/*reserve extra*/);
563   AddActorsWithinRange( range, durationSeconds );
564
565   // Scroll to an appropriate layout position
566
567   bool scrollAnimationNeeded(false);
568   float firstItemScrollPosition(0.0f);
569
570   float current = GetCurrentLayoutPosition(0);
571   float minimum = ClampFirstItemPosition(current, targetSize, *mActiveLayout);
572   self.SetProperty(mPropertyPosition, GetScrollPosition(current, targetSize));
573
574   if (current < minimum)
575   {
576     scrollAnimationNeeded = true;
577     firstItemScrollPosition = minimum;
578   }
579   else if (mAnchoringEnabled)
580   {
581     scrollAnimationNeeded = true;
582     firstItemScrollPosition = mActiveLayout->GetClosestAnchorPosition(current);
583   }
584
585   if (scrollAnimationNeeded)
586   {
587     RemoveAnimation(mScrollAnimation);
588     mScrollAnimation = Animation::New(mAnchoringDuration);
589     mScrollAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), firstItemScrollPosition, AlphaFunctions::EaseOut );
590     mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, targetSize), AlphaFunctions::EaseOut );
591     mScrollAnimation.Play();
592   }
593
594   self.SetProperty(mPropertyMinimumLayoutPosition, mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), targetSize));
595   AnimateScrollOvershoot(0.0f);
596   mScrollOvershoot = 0.0f;
597
598   Radian scrollDirection(mActiveLayout->GetScrollDirection());
599   float orientation = static_cast<float>(mActiveLayout->GetOrientation());
600   self.SetProperty(mPropertyScrollDirection, Vector3(sinf(scrollDirection), cosf(scrollDirection), orientation));
601
602   self.SetProperty(mPropertyScrollSpeed, mScrollSpeed);
603
604   CalculateDomainSize(targetSize);
605 }
606
607 void ItemView::DeactivateCurrentLayout()
608 {
609   if (mActiveLayout)
610   {
611     for (ConstItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
612     {
613       Actor actor = iter->second;
614       actor.RemoveConstraints();
615     }
616
617     mActiveLayout = NULL;
618   }
619 }
620
621 void ItemView::SetDefaultAlphaFunction(AlphaFunction func)
622 {
623   mDefaultAlphaFunction = func;
624 }
625
626 AlphaFunction ItemView::GetDefaultAlphaFunction() const
627 {
628   return mDefaultAlphaFunction;
629 }
630
631 void ItemView::OnRefreshNotification(PropertyNotification& source)
632 {
633   if(mRefreshEnabled)
634   {
635     // Only refresh the cache during normal scrolling
636     DoRefresh(GetCurrentLayoutPosition(0), true);
637   }
638 }
639
640 void ItemView::DoRefresh(float currentLayoutPosition, bool cacheExtra)
641 {
642   if (mActiveLayout)
643   {
644     ItemRange range = GetItemRange(*mActiveLayout, mActiveLayoutTargetSize, currentLayoutPosition, cacheExtra/*reserve extra*/);
645     RemoveActorsOutsideRange( range );
646     AddActorsWithinRange( range, 0.0f/*immediate*/ );
647
648     mScrollUpdatedSignalV2.Emit( Vector3(0.0f, currentLayoutPosition, 0.0f) );
649   }
650 }
651
652 void ItemView::SetMinimumSwipeSpeed(float speed)
653 {
654   mMinimumSwipeSpeed = speed;
655 }
656
657 float ItemView::GetMinimumSwipeSpeed() const
658 {
659   return mMinimumSwipeSpeed;
660 }
661
662 void ItemView::SetMinimumSwipeDistance(float distance)
663 {
664   mMinimumSwipeDistance = distance;
665 }
666
667 float ItemView::GetMinimumSwipeDistance() const
668 {
669   return mMinimumSwipeDistance;
670 }
671
672 void ItemView::SetMouseWheelScrollDistanceStep(float step)
673 {
674   mMouseWheelScrollDistanceStep = step;
675 }
676
677 float ItemView::GetMouseWheelScrollDistanceStep() const
678 {
679   return mMouseWheelScrollDistanceStep;
680 }
681
682 void ItemView::SetAnchoring(bool enabled)
683 {
684   mAnchoringEnabled = enabled;
685 }
686
687 bool ItemView::GetAnchoring() const
688 {
689   return mAnchoringEnabled;
690 }
691
692 void ItemView::SetAnchoringDuration(float durationSeconds)
693 {
694   mAnchoringDuration = durationSeconds;
695 }
696
697 float ItemView::GetAnchoringDuration() const
698 {
699   return mAnchoringDuration;
700 }
701
702 void ItemView::SetRefreshInterval(float intervalLayoutPositions)
703 {
704   mRefreshIntervalLayoutPositions = intervalLayoutPositions;
705
706   if(mRefreshNotification)
707   {
708     mScrollPositionObject.RemovePropertyNotification(mRefreshNotification);
709   }
710   mRefreshNotification = mScrollPositionObject.AddPropertyNotification( ScrollConnector::SCROLL_POSITION, StepCondition(mRefreshIntervalLayoutPositions, 0.0f) );
711   mRefreshNotification.NotifySignal().Connect( this, &ItemView::OnRefreshNotification );
712 }
713
714 float ItemView::GetRefreshInterval() const
715 {
716   return mRefreshIntervalLayoutPositions;
717 }
718
719 void ItemView::SetRefreshEnabled(bool enabled)
720 {
721   mRefreshEnabled = enabled;
722 }
723
724 Actor ItemView::GetItem(unsigned int itemId) const
725 {
726   Actor actor;
727
728   ConstItemPoolIter iter = mItemPool.find( itemId );
729   if( iter != mItemPool.end() )
730   {
731     actor = iter->second;
732   }
733
734   return actor;
735 }
736
737 unsigned int ItemView::GetItemId( Actor actor ) const
738 {
739   unsigned int itemId( 0 );
740
741   for ( ConstItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter )
742   {
743     if( iter->second == actor )
744     {
745       itemId = iter->first;
746       break;
747     }
748   }
749
750   return itemId;
751 }
752
753 void ItemView::InsertItem( Item newItem, float durationSeconds )
754 {
755   mAddingItems = true;
756
757   SetupActor( newItem, durationSeconds );
758   Self().Add( newItem.second );
759
760   ItemPoolIter foundIter = mItemPool.find( newItem.first );
761   if( mItemPool.end() != foundIter )
762   {
763     Actor moveMe = foundIter->second;
764     foundIter->second = newItem.second;
765
766     // Move the existing actors to make room
767     for( ItemPoolIter iter = ++foundIter; mItemPool.end() != iter; ++iter )
768     {
769       Actor temp = iter->second;
770       iter->second = moveMe;
771       moveMe = temp;
772
773       iter->second.RemoveConstraints();
774       ApplyConstraints( iter->second, *mActiveLayout, iter->first, durationSeconds );
775     }
776
777     // Create last item
778     ItemId lastId = mItemPool.rbegin()->first;
779     Item lastItem( lastId + 1, moveMe );
780     mItemPool.insert( lastItem );
781
782     lastItem.second.RemoveConstraints();
783     ApplyConstraints( lastItem.second, *mActiveLayout, lastItem.first, durationSeconds );
784   }
785   else
786   {
787     mItemPool.insert( newItem );
788   }
789
790   CalculateDomainSize(Self().GetCurrentSize());
791
792   mAddingItems = false;
793 }
794
795 void ItemView::InsertItems( const ItemContainer& newItems, float durationSeconds )
796 {
797   mAddingItems = true;
798
799   // Insert from lowest id to highest
800   set<Item> sortedItems;
801   for( ConstItemIter iter = newItems.begin(); newItems.end() != iter; ++iter )
802   {
803     sortedItems.insert( *iter );
804   }
805
806   for( set<Item>::iterator iter = sortedItems.begin(); sortedItems.end() != iter; ++iter )
807   {
808     Self().Add( iter->second );
809
810     cout << "inserting item: " << iter->first << endl;
811
812     ItemPoolIter foundIter = mItemPool.find( iter->first );
813     if( mItemPool.end() != foundIter )
814     {
815       Actor moveMe = foundIter->second;
816       foundIter->second = iter->second;
817
818       // Move the existing actors to make room
819       for( ItemPoolIter iter = ++foundIter; mItemPool.end() != iter; ++iter )
820       {
821         Actor temp = iter->second;
822         iter->second = moveMe;
823         moveMe = temp;
824       }
825
826       // Create last item
827       ItemId lastId = mItemPool.rbegin()->first;
828       Item lastItem( lastId + 1, moveMe );
829       mItemPool.insert( lastItem );
830     }
831     else
832     {
833       mItemPool.insert( *iter );
834     }
835   }
836
837   // Relayout everything
838   for (ItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
839   {
840     // If newly inserted
841     if( FindById( newItems, iter->first ) )
842     {
843       SetupActor( *iter, durationSeconds );
844     }
845     else
846     {
847       iter->second.RemoveConstraints();
848       ApplyConstraints( iter->second, *mActiveLayout, iter->first, durationSeconds );
849     }
850   }
851
852   CalculateDomainSize(Self().GetCurrentSize());
853
854   mAddingItems = false;
855 }
856
857 void ItemView::RemoveItem( unsigned int itemId, float durationSeconds )
858 {
859   bool actorRemoved = RemoveActor( itemId );
860   if( actorRemoved )
861   {
862     ReapplyAllConstraints( durationSeconds );
863   }
864 }
865
866 void ItemView::RemoveItems( const ItemIdContainer& itemIds, float durationSeconds )
867 {
868   bool actorRemoved( false );
869
870   // Remove from highest id to lowest
871   set<ItemId> sortedItems;
872   for( ConstItemIdIter iter = itemIds.begin(); itemIds.end() != iter; ++iter )
873   {
874     sortedItems.insert( *iter );
875   }
876
877   for( set<ItemId>::reverse_iterator iter = sortedItems.rbegin(); sortedItems.rend() != iter; ++iter )
878   {
879     if( RemoveActor( *iter ) )
880     {
881       actorRemoved = true;
882     }
883   }
884
885   if( actorRemoved )
886   {
887     ReapplyAllConstraints( durationSeconds );
888   }
889 }
890
891 bool ItemView::RemoveActor(unsigned int itemId)
892 {
893   bool removed( false );
894
895   const ItemPoolIter removeIter = mItemPool.find( itemId );
896
897   if( removeIter != mItemPool.end() )
898   {
899     Self().Remove( removeIter->second );
900     removed = true;
901
902     // Adjust the remaining item IDs, for example if item 2 is removed:
903     //   Initial actors:     After insert:
904     //     ID 1 - ActorA       ID 1 - ActorA
905     //     ID 2 - ActorB       ID 2 - ActorC (previously ID 3)
906     //     ID 3 - ActorC       ID 3 - ActorB (previously ID 4)
907     //     ID 4 - ActorD
908     for (ItemPoolIter iter = removeIter; iter != mItemPool.end(); ++iter)
909     {
910       if( iter->first < mItemPool.rbegin()->first )
911       {
912         iter->second = mItemPool[ iter->first + 1 ];
913       }
914       else
915       {
916         mItemPool.erase( iter );
917         break;
918       }
919     }
920   }
921
922   return removed;
923 }
924
925 void ItemView::ReplaceItem( Item replacementItem, float durationSeconds )
926 {
927   mAddingItems = true;
928
929   SetupActor( replacementItem, durationSeconds );
930   Self().Add( replacementItem.second );
931
932   const ItemPoolIter iter = mItemPool.find( replacementItem.first );
933   if( mItemPool.end() != iter )
934   {
935     Self().Remove( iter->second );
936     iter->second = replacementItem.second;
937   }
938   else
939   {
940     mItemPool.insert( replacementItem );
941   }
942
943   CalculateDomainSize(Self().GetCurrentSize());
944
945   mAddingItems = false;
946 }
947
948 void ItemView::ReplaceItems( const ItemContainer& replacementItems, float durationSeconds )
949 {
950   for( ConstItemIter iter = replacementItems.begin(); replacementItems.end() != iter; ++iter )
951   {
952     ReplaceItem( *iter, durationSeconds );
953   }
954 }
955
956 void ItemView::RemoveActorsOutsideRange( ItemRange range )
957 {
958   // Remove unwanted actors from the ItemView & ItemPool
959   for (ItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); )
960   {
961     unsigned int current = iter->first;
962
963     if( ! range.Within( current ) )
964     {
965       Self().Remove( iter->second );
966
967       mItemPool.erase( iter++ ); // erase invalidates the return value of post-increment; iter remains valid
968     }
969     else
970     {
971       ++iter;
972     }
973   }
974 }
975
976 void ItemView::AddActorsWithinRange( ItemRange range, float durationSeconds )
977 {
978   range.end = min(mItemFactory.GetNumberOfItems(), range.end);
979
980   // The order of addition depends on the scroll direction.
981   if (mRefreshOrderHint)
982   {
983     for (unsigned int itemId = range.begin; itemId < range.end; ++itemId)
984     {
985       AddNewActor( itemId, durationSeconds );
986     }
987   }
988   else
989   {
990     for (unsigned int itemId = range.end; itemId > range.begin; --itemId)
991     {
992       AddNewActor( itemId-1, durationSeconds );
993     }
994   }
995
996   // Total number of items may change dynamically.
997   // Always recalculate the domain size to reflect that.
998   CalculateDomainSize(Self().GetCurrentSize());
999 }
1000
1001 void ItemView::AddNewActor( unsigned int itemId, float durationSeconds )
1002 {
1003   mAddingItems = true;
1004
1005   if( mItemPool.end() == mItemPool.find( itemId ) )
1006   {
1007     Actor actor = mItemFactory.NewItem( itemId );
1008
1009     if( actor )
1010     {
1011       Item newItem( itemId, actor );
1012
1013       mItemPool.insert( newItem );
1014
1015       SetupActor( newItem, durationSeconds );
1016       Self().Add( actor );
1017     }
1018   }
1019
1020   mAddingItems = false;
1021 }
1022
1023 void ItemView::SetupActor( Item item, float durationSeconds )
1024 {
1025   item.second.SetParentOrigin( mItemsParentOrigin );
1026   item.second.SetAnchorPoint( mItemsAnchorPoint );
1027
1028   if( mActiveLayout )
1029   {
1030     Vector3 size;
1031     if( mActiveLayout->GetItemSize( item.first, mActiveLayoutTargetSize, size ) )
1032     {
1033       item.second.SetSize( size );
1034     }
1035
1036     ApplyConstraints( item.second, *mActiveLayout, item.first, durationSeconds );
1037   }
1038 }
1039
1040 ItemRange ItemView::GetItemRange(ItemLayout& layout, const Vector3& layoutSize, float layoutPosition, bool reserveExtra)
1041 {
1042   unsigned int itemCount = mItemFactory.GetNumberOfItems();
1043
1044   ItemRange available(0u, itemCount);
1045
1046   ItemRange range = layout.GetItemsWithinArea( layoutPosition, layoutSize );
1047
1048   if (reserveExtra)
1049   {
1050     // Add the reserve items for scrolling
1051     unsigned int extra = layout.GetReserveItemCount(layoutSize);
1052     range.begin = (range.begin >= extra) ? (range.begin - extra) : 0u;
1053     range.end += extra;
1054   }
1055
1056   return range.Intersection(available);
1057 }
1058
1059 void ItemView::OnChildAdd(Actor& child)
1060 {
1061   if(!mAddingItems)
1062   {
1063     // We don't want to do this downcast check for any item added by ItemView itself.
1064     Dali::Toolkit::ScrollComponent scrollComponent = Dali::Toolkit::ScrollComponent::DownCast(child);
1065     if(scrollComponent)
1066     {
1067       // Set the scroll connector when scroll bar is being added
1068       scrollComponent.SetScrollConnector(mScrollConnector);
1069     }
1070   }
1071 }
1072
1073 bool ItemView::OnTouchEvent(const TouchEvent& event)
1074 {
1075   // Ignore events with multiple-touch points
1076   if (event.GetPointCount() != 1)
1077   {
1078     return false;
1079   }
1080
1081   if (event.GetPoint(0).state == TouchPoint::Down)
1082   {
1083     // Cancel ongoing scrolling etc.
1084     mGestureState = Gesture::Clear;
1085
1086     mScrollDistance = 0.0f;
1087     mScrollSpeed = 0.0f;
1088     Self().SetProperty(mPropertyScrollSpeed, mScrollSpeed);
1089
1090     mScrollOvershoot = 0.0f;
1091     AnimateScrollOvershoot(0.0f);
1092
1093     mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition());
1094
1095     RemoveAnimation(mScrollAnimation);
1096   }
1097
1098   return true; // consume since we're potentially scrolling
1099 }
1100
1101 bool ItemView::OnMouseWheelEvent(const MouseWheelEvent& event)
1102 {
1103   // Respond the mouse wheel event to scroll
1104   if (mActiveLayout)
1105   {
1106     Actor self = Self();
1107     const Vector3 layoutSize = Self().GetCurrentSize();
1108     float layoutPositionDelta = GetCurrentLayoutPosition(0) - (event.z * mMouseWheelScrollDistanceStep * mActiveLayout->GetScrollSpeedFactor());
1109     float firstItemScrollPosition = ClampFirstItemPosition(layoutPositionDelta, layoutSize, *mActiveLayout);
1110
1111     mScrollPositionObject.SetProperty( ScrollConnector::SCROLL_POSITION, firstItemScrollPosition );
1112     self.SetProperty(mPropertyPosition, GetScrollPosition(firstItemScrollPosition, layoutSize));
1113     mScrollStartedSignalV2.Emit(GetCurrentScrollPosition());
1114   }
1115
1116   if (mMouseWheelEventFinishedTimer.IsRunning())
1117   {
1118     mMouseWheelEventFinishedTimer.Stop();
1119   }
1120
1121   mMouseWheelEventFinishedTimer.Start();
1122
1123   return true;
1124 }
1125
1126 bool ItemView::OnMouseWheelEventFinished()
1127 {
1128   if (mActiveLayout)
1129   {
1130     RemoveAnimation(mScrollAnimation);
1131
1132     // No more mouse wheel events coming. Do the anchoring if enabled.
1133     mScrollAnimation = DoAnchoring();
1134     if (mScrollAnimation)
1135     {
1136       mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1137       mScrollAnimation.Play();
1138     }
1139     else
1140     {
1141       mScrollOvershoot = 0.0f;
1142       AnimateScrollOvershoot(0.0f);
1143
1144       mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition());
1145     }
1146   }
1147
1148   return false;
1149 }
1150
1151 void ItemView::ApplyConstraints(Actor& actor, ItemLayout& layout, unsigned int itemId, float duration)
1152 {
1153   ItemLayout::Vector3Function positionConstraint;
1154   if (layout.GetPositionConstraint(itemId, positionConstraint))
1155   {
1156     WrappedVector3Constraint wrapped(positionConstraint, itemId);
1157
1158     Constraint constraint = Constraint::New<Vector3>( Actor::POSITION,
1159                                                       Source( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ),
1160                                                       ParentSource( mPropertyScrollSpeed ),
1161                                                       ParentSource( Actor::SIZE ),
1162                                                       wrapped );
1163     constraint.SetApplyTime(duration);
1164     constraint.SetAlphaFunction(mDefaultAlphaFunction);
1165
1166     actor.ApplyConstraint(constraint);
1167   }
1168
1169   ItemLayout::QuaternionFunction rotationConstraint;
1170   if (layout.GetRotationConstraint(itemId, rotationConstraint))
1171   {
1172     WrappedQuaternionConstraint wrapped(rotationConstraint, itemId);
1173
1174     Constraint constraint = Constraint::New<Quaternion>( Actor::ROTATION,
1175                                                          Source( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ),
1176                                                          ParentSource( mPropertyScrollSpeed ),
1177                                                          ParentSource( Actor::SIZE ),
1178                                                          wrapped );
1179     constraint.SetApplyTime(duration);
1180     constraint.SetAlphaFunction(mDefaultAlphaFunction);
1181
1182     actor.ApplyConstraint(constraint);
1183   }
1184
1185   ItemLayout::Vector3Function scaleConstraint;
1186   if (layout.GetScaleConstraint(itemId, scaleConstraint))
1187   {
1188     WrappedVector3Constraint wrapped(scaleConstraint, itemId);
1189
1190     Constraint constraint = Constraint::New<Vector3>( Actor::SCALE,
1191                                                       Source( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ),
1192                                                       ParentSource( mPropertyScrollSpeed ),
1193                                                       ParentSource( Actor::SIZE ),
1194                                                       wrapped );
1195     constraint.SetApplyTime(duration);
1196     constraint.SetAlphaFunction(mDefaultAlphaFunction);
1197
1198     actor.ApplyConstraint(constraint);
1199   }
1200
1201   ItemLayout::Vector4Function colorConstraint;
1202   if (layout.GetColorConstraint(itemId, colorConstraint))
1203   {
1204     WrappedVector4Constraint wrapped(colorConstraint, itemId);
1205
1206     Constraint constraint = Constraint::New<Vector4>( Actor::COLOR,
1207                                                       Source( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ),
1208                                                       ParentSource( mPropertyScrollSpeed ),
1209                                                       ParentSource( Actor::SIZE ),
1210                                                       wrapped );
1211     constraint.SetApplyTime(duration);
1212     constraint.SetAlphaFunction(mDefaultAlphaFunction);
1213
1214     // Release color constraints slowly; this allows ItemView to co-exist with ImageActor fade-in
1215     constraint.SetRemoveTime(DEFAULT_COLOR_VISIBILITY_REMOVE_TIME);
1216     constraint.SetRemoveAction(Dali::Constraint::Discard);
1217
1218     actor.ApplyConstraint(constraint);
1219   }
1220
1221   ItemLayout::BoolFunction visibilityConstraint;
1222   if (layout.GetVisibilityConstraint(itemId, visibilityConstraint))
1223   {
1224     WrappedBoolConstraint wrapped(visibilityConstraint, itemId);
1225
1226     Constraint constraint = Constraint::New<bool>( Actor::VISIBLE,
1227                                                    Source( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ),
1228                                                    ParentSource( mPropertyScrollSpeed ),
1229                                                    ParentSource( Actor::SIZE ),
1230                                                    wrapped );
1231     constraint.SetApplyTime(duration);
1232     constraint.SetAlphaFunction(mDefaultAlphaFunction);
1233
1234     // Release visibility constraints the same time as the color constraint
1235     constraint.SetRemoveTime(DEFAULT_COLOR_VISIBILITY_REMOVE_TIME);
1236     constraint.SetRemoveAction(Dali::Constraint::Discard);
1237
1238     actor.ApplyConstraint(constraint);
1239   }
1240 }
1241
1242 void ItemView::ReapplyAllConstraints( float durationSeconds )
1243 {
1244   for (ConstItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
1245   {
1246     unsigned int id = iter->first;
1247     Actor actor = iter->second;
1248
1249     actor.RemoveConstraints();
1250     ApplyConstraints(actor, *mActiveLayout, id, durationSeconds);
1251   }
1252
1253   CalculateDomainSize(Self().GetCurrentSize());
1254 }
1255
1256 float ItemView::ClampFirstItemPosition(float targetPosition, const Vector3& targetSize, ItemLayout& layout)
1257 {
1258   Actor self = Self();
1259   float minLayoutPosition = layout.GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), targetSize);
1260   float clamppedPosition = min(0.0f, max(minLayoutPosition, targetPosition));
1261   mScrollOvershoot = targetPosition - clamppedPosition;
1262   self.SetProperty(mPropertyMinimumLayoutPosition, minLayoutPosition);
1263
1264   return clamppedPosition;
1265 }
1266
1267 void ItemView::OnPan(PanGesture gesture)
1268 {
1269   Actor self = Self();
1270   const Vector3 layoutSize = Self().GetCurrentSize();
1271
1272   RemoveAnimation(mScrollAnimation);
1273
1274   // Short-circuit if there is no active layout
1275   if (!mActiveLayout)
1276   {
1277     mGestureState = Gesture::Clear;
1278     return;
1279   }
1280
1281   mGestureState = gesture.state;
1282
1283   switch (mGestureState)
1284   {
1285     case Gesture::Finished:
1286     {
1287       // Swipe Detection
1288       if (fabsf(mScrollDistance) > mMinimumSwipeDistance &&
1289           mScrollSpeed > mMinimumSwipeSpeed)
1290       {
1291         float direction = (mScrollDistance < 0.0f) ? -1.0f : 1.0f;
1292
1293         mRefreshOrderHint = true;
1294
1295         float currentLayoutPosition = GetCurrentLayoutPosition(0);
1296         float firstItemScrollPosition = ClampFirstItemPosition(currentLayoutPosition + mScrollSpeed * direction,
1297                                                                layoutSize,
1298                                                                *mActiveLayout);
1299
1300         if (mAnchoringEnabled)
1301         {
1302           firstItemScrollPosition = mActiveLayout->GetClosestAnchorPosition(firstItemScrollPosition);
1303         }
1304
1305         RemoveAnimation(mScrollAnimation);
1306
1307         float flickAnimationDuration = mActiveLayout->GetItemFlickAnimationDuration() * max(1.0f, fabsf(firstItemScrollPosition - GetCurrentLayoutPosition(0)));
1308         mScrollAnimation = Animation::New(flickAnimationDuration);
1309         mScrollAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), firstItemScrollPosition, AlphaFunctions::EaseOut );
1310         mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, layoutSize), AlphaFunctions::EaseOut );
1311         mScrollAnimation.AnimateTo( Property(self, mPropertyScrollSpeed), 0.0f, AlphaFunctions::EaseOut );
1312
1313         mIsFlicking = true;
1314         // Check whether it has already scrolled to the end
1315         if(fabs(currentLayoutPosition - firstItemScrollPosition) > Math::MACHINE_EPSILON_0)
1316         {
1317           AnimateScrollOvershoot(0.0f);
1318         }
1319       }
1320
1321       // Anchoring may be triggered when there was no swipe
1322       if (!mScrollAnimation)
1323       {
1324         mScrollAnimation = DoAnchoring();
1325       }
1326
1327       // Reset the overshoot if no scroll animation.
1328       if (!mScrollAnimation)
1329       {
1330         mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition());
1331
1332         AnimateScrollOvershoot(0.0f, false);
1333       }
1334     }
1335     break;
1336
1337     case Gesture::Started: // Fall through
1338     {
1339       mTotalPanDisplacement = Vector2::ZERO;
1340     }
1341
1342     case Gesture::Continuing:
1343     {
1344       mScrollDistance = CalculateScrollDistance(gesture.displacement, *mActiveLayout);
1345       mScrollSpeed = Clamp((gesture.GetSpeed() * mActiveLayout->GetScrollSpeedFactor() * MILLISECONDS_PER_SECONDS), 0.0f, mActiveLayout->GetMaximumSwipeSpeed());
1346
1347       // Refresh order depends on the direction of the scroll; negative is towards the last item.
1348       mRefreshOrderHint = mScrollDistance < 0.0f;
1349
1350       float layoutPositionDelta = GetCurrentLayoutPosition(0) + (mScrollDistance * mActiveLayout->GetScrollSpeedFactor());
1351
1352       float firstItemScrollPosition = ClampFirstItemPosition(layoutPositionDelta, layoutSize, *mActiveLayout);
1353
1354       mScrollPositionObject.SetProperty( ScrollConnector::SCROLL_POSITION, firstItemScrollPosition );
1355       self.SetProperty(mPropertyPosition, GetScrollPosition(firstItemScrollPosition, layoutSize));
1356       mScrollStartedSignalV2.Emit(GetCurrentScrollPosition());
1357
1358       mTotalPanDisplacement += gesture.displacement;
1359       mScrollOvershoot = layoutPositionDelta - firstItemScrollPosition;
1360       if( mScrollOvershoot > Math::MACHINE_EPSILON_1 )
1361       {
1362         AnimateScrollOvershoot(1.0f);
1363       }
1364       else if( mScrollOvershoot < -Math::MACHINE_EPSILON_1 )
1365       {
1366         AnimateScrollOvershoot(-1.0f);
1367       }
1368       else
1369       {
1370         AnimateScrollOvershoot(0.0f);
1371       }
1372     }
1373     break;
1374
1375     case Gesture::Cancelled:
1376     {
1377       mScrollAnimation = DoAnchoring();
1378     }
1379     break;
1380
1381     default:
1382       break;
1383   }
1384
1385   if (mScrollAnimation)
1386   {
1387     mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1388     mScrollAnimation.Play();
1389   }
1390 }
1391
1392 bool ItemView::OnAccessibilityPan(PanGesture gesture)
1393 {
1394   OnPan(gesture);
1395   return true;
1396 }
1397
1398 Actor ItemView::GetNextKeyboardFocusableActor(Actor actor, Toolkit::Control::KeyboardFocusNavigationDirection direction, bool loopEnabled)
1399 {
1400   Actor nextFocusActor;
1401   if(mActiveLayout)
1402   {
1403     int nextItemID = 0;
1404     if(!actor || actor == this->Self())
1405     {
1406       nextFocusActor = GetItem(nextItemID);
1407     }
1408     else if(actor && actor.GetParent() == this->Self())
1409     {
1410       int itemID = GetItemId(actor);
1411       nextItemID = mActiveLayout->GetNextFocusItemID(itemID, mItemFactory.GetNumberOfItems(), direction, loopEnabled);
1412       nextFocusActor = GetItem(nextItemID);
1413       if(nextFocusActor == actor)
1414       {
1415         // need to pass NULL actor back to focus manager
1416         nextFocusActor.Reset();
1417         return nextFocusActor;
1418       }
1419     }
1420     float layoutPosition = mActiveLayout->GetClosestAnchorPosition( GetCurrentLayoutPosition(0) );
1421     Vector3 layoutSize = Self().GetCurrentSize();
1422     if(!nextFocusActor)
1423     {
1424       // likely the current item is not buffered, so not in our item pool, probably best to get first viewable item
1425       ItemRange viewableItems = mActiveLayout->GetItemsWithinArea(layoutPosition, layoutSize);
1426       nextItemID = viewableItems.begin;
1427       nextFocusActor = GetItem(nextItemID);
1428     }
1429   }
1430   return nextFocusActor;
1431 }
1432
1433 void ItemView::OnKeyboardFocusChangeCommitted(Actor commitedFocusableActor)
1434 {
1435   // only in this function if our chosen focus actor was actually used
1436   if(commitedFocusableActor)
1437   {
1438     int nextItemID = GetItemId(commitedFocusableActor);
1439     float layoutPosition = GetCurrentLayoutPosition(0);
1440     Vector3 layoutSize = Self().GetCurrentSize();
1441     Vector3 focusItemPosition = Vector3::ZERO;
1442     ItemLayout::Vector3Function itemPositionConstraint;
1443     if (mActiveLayout->GetPositionConstraint(nextItemID, itemPositionConstraint))
1444     {
1445       focusItemPosition = itemPositionConstraint(Vector3::ZERO, layoutPosition + nextItemID, 0.0f, layoutSize);
1446     }
1447
1448     float scrollTo = mActiveLayout->GetClosestOnScreenLayoutPosition(nextItemID, layoutPosition, layoutSize);
1449     ScrollTo(Vector3(0.0f, scrollTo, 0.0f), DEFAULT_KEYBOARD_FOCUS_SCROLL_DURATION);
1450   }
1451 }
1452
1453 Animation ItemView::DoAnchoring()
1454 {
1455   Animation anchoringAnimation;
1456   Actor self = Self();
1457
1458   if (mActiveLayout && mAnchoringEnabled)
1459   {
1460     float anchorPosition = mActiveLayout->GetClosestAnchorPosition( GetCurrentLayoutPosition(0) );
1461
1462     anchoringAnimation = Animation::New(mAnchoringDuration);
1463     anchoringAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), anchorPosition, AlphaFunctions::EaseOut );
1464     anchoringAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(anchorPosition, self.GetCurrentSize()), AlphaFunctions::EaseOut );
1465     anchoringAnimation.AnimateTo( Property(self, mPropertyScrollSpeed), 0.0f, AlphaFunctions::EaseOut );
1466     if(!mIsFlicking)
1467     {
1468       AnimateScrollOvershoot(0.0f);
1469     }
1470   }
1471
1472   return anchoringAnimation;
1473 }
1474
1475 void ItemView::OnScrollFinished(Animation& source)
1476 {
1477   Actor self = Self();
1478
1479   RemoveAnimation(mScrollAnimation); // mScrollAnimation is used to query whether we're scrolling
1480
1481   mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition());
1482
1483   if(mIsFlicking && fabsf(mScrollOvershoot) > Math::MACHINE_EPSILON_1)
1484   {
1485     AnimateScrollOvershoot( mScrollOvershoot > 0.0f ? 1.0f : -1.0f, true);
1486   }
1487   else
1488   {
1489     // Reset the overshoot
1490     AnimateScrollOvershoot( 0.0f );
1491   }
1492   mIsFlicking = false;
1493
1494   mScrollOvershoot = 0.0f;
1495 }
1496
1497 void ItemView::OnOvershootOnFinished(Animation& animation)
1498 {
1499   mAnimatingOvershootOn = false;
1500   mScrollOvershootAnimation.FinishedSignal().Disconnect(this, &ItemView::OnOvershootOnFinished);
1501   RemoveAnimation(mScrollOvershootAnimation);
1502   if(mAnimateOvershootOff)
1503   {
1504     AnimateScrollOvershoot(0.0f);
1505   }
1506 }
1507
1508 void ItemView::ScrollToItem(unsigned int itemId, float durationSeconds)
1509 {
1510   Actor self = Self();
1511   const Vector3 layoutSize = Self().GetCurrentSize();
1512   float firstItemScrollPosition = ClampFirstItemPosition(mActiveLayout->GetItemScrollToPosition(itemId), layoutSize, *mActiveLayout);
1513
1514   if(durationSeconds > 0.0f)
1515   {
1516     RemoveAnimation(mScrollAnimation);
1517     mScrollAnimation = Animation::New(durationSeconds);
1518     mScrollAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), firstItemScrollPosition, AlphaFunctions::EaseOut );
1519     mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, layoutSize), AlphaFunctions::EaseOut );
1520     mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1521     mScrollAnimation.Play();
1522   }
1523   else
1524   {
1525     mScrollPositionObject.SetProperty( ScrollConnector::SCROLL_POSITION, firstItemScrollPosition );
1526     AnimateScrollOvershoot(0.0f);
1527   }
1528
1529   mScrollStartedSignalV2.Emit(GetCurrentScrollPosition());
1530 }
1531
1532 void ItemView::RemoveAnimation(Animation& animation)
1533 {
1534   if(animation)
1535   {
1536     // Cease animating, and reset handle.
1537     animation.Clear();
1538     animation.Reset();
1539   }
1540 }
1541
1542 void ItemView::CalculateDomainSize(const Vector3& layoutSize)
1543 {
1544   Actor self = Self();
1545
1546   Vector3 firstItemPosition(Vector3::ZERO);
1547   Vector3 lastItemPosition(Vector3::ZERO);
1548
1549   if(mActiveLayout)
1550   {
1551     ItemLayout::Vector3Function firstItemPositionConstraint;
1552     if (mActiveLayout->GetPositionConstraint(0, firstItemPositionConstraint))
1553     {
1554       firstItemPosition = firstItemPositionConstraint(Vector3::ZERO, 0, 0.0f, layoutSize);
1555     }
1556
1557     float minLayoutPosition = mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), layoutSize);
1558     self.SetProperty(mPropertyMinimumLayoutPosition, minLayoutPosition);
1559
1560     ItemLayout::Vector3Function lastItemPositionConstraint;
1561     if (mActiveLayout->GetPositionConstraint(fabs(minLayoutPosition), lastItemPositionConstraint))
1562     {
1563       lastItemPosition = lastItemPositionConstraint(Vector3::ZERO, fabs(minLayoutPosition), 0.0f, layoutSize);
1564     }
1565
1566     float domainSize;
1567
1568     if(IsHorizontal(mActiveLayout->GetOrientation()))
1569     {
1570       self.SetProperty(mPropertyPositionMin, Vector3(0.0f, firstItemPosition.x, 0.0f));
1571       self.SetProperty(mPropertyPositionMax, Vector3(0.0f, lastItemPosition.x, 0.0f));
1572       domainSize = fabs(firstItemPosition.x - lastItemPosition.x);
1573     }
1574     else
1575     {
1576       self.SetProperty(mPropertyPositionMin, Vector3(0.0f, firstItemPosition.y, 0.0f));
1577       self.SetProperty(mPropertyPositionMax, Vector3(0.0f, lastItemPosition.y, 0.0f));
1578       domainSize = fabs(firstItemPosition.y - lastItemPosition.y);
1579     }
1580
1581     mScrollConnector.SetScrollDomain(minLayoutPosition, 0.0f, domainSize);
1582
1583     bool isLayoutScrollable = IsLayoutScrollable(layoutSize);
1584     self.SetProperty(mPropertyCanScrollVertical, isLayoutScrollable);
1585     self.SetProperty(mPropertyCanScrollHorizontal, false);
1586   }
1587 }
1588
1589 Vector3 ItemView::GetDomainSize() const
1590 {
1591   Actor self = Self();
1592
1593   float minScrollPosition = self.GetProperty<float>(mPropertyPositionMin);
1594   float maxScrollPosition = self.GetProperty<float>(mPropertyPositionMax);
1595
1596   return Vector3(0.0f, fabs(maxScrollPosition - minScrollPosition), 0.0f);
1597 }
1598
1599 bool ItemView::IsLayoutScrollable(const Vector3& layoutSize)
1600 {
1601   Actor self = Self();
1602
1603   float currentLayoutPosition = ClampFirstItemPosition( GetCurrentLayoutPosition(0), layoutSize, *mActiveLayout );
1604   float forwardClampedPosition = ClampFirstItemPosition(currentLayoutPosition + 1.0, layoutSize, *mActiveLayout);
1605   float backwardClampedPosition = ClampFirstItemPosition(currentLayoutPosition - 1.0, layoutSize, *mActiveLayout);
1606
1607   return (fabs(forwardClampedPosition - backwardClampedPosition) > Math::MACHINE_EPSILON_0);
1608 }
1609
1610 float ItemView::GetScrollPosition(float layoutPosition, const Vector3& layoutSize) const
1611 {
1612   Vector3 firstItemPosition(Vector3::ZERO);
1613   ItemLayout::Vector3Function firstItemPositionConstraint;
1614   if (mActiveLayout->GetPositionConstraint(0, firstItemPositionConstraint))
1615   {
1616     firstItemPosition = firstItemPositionConstraint(Vector3::ZERO, layoutPosition, 0.0f, layoutSize);
1617   }
1618
1619   return IsHorizontal(mActiveLayout->GetOrientation()) ? firstItemPosition.x: firstItemPosition.y;
1620 }
1621
1622 Vector3 ItemView::GetCurrentScrollPosition() const
1623 {
1624   float currentLayoutPosition = GetCurrentLayoutPosition(0);
1625   return Vector3(0.0f, GetScrollPosition(currentLayoutPosition, Self().GetCurrentSize()), 0.0f);
1626 }
1627
1628 void ItemView::AddOverlay(Actor actor)
1629 {
1630   Self().Add(actor);
1631 }
1632
1633 void ItemView::RemoveOverlay(Actor actor)
1634 {
1635   Self().Remove(actor);
1636 }
1637
1638 void ItemView::ScrollTo(const Vector3& position, float duration)
1639 {
1640   Actor self = Self();
1641   const Vector3 layoutSize = Self().GetCurrentSize();
1642
1643   float firstItemScrollPosition = ClampFirstItemPosition(position.y, layoutSize, *mActiveLayout);
1644
1645   if(duration > 0.0f)
1646   {
1647     RemoveAnimation(mScrollAnimation);
1648     mScrollAnimation = Animation::New(duration);
1649     mScrollAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), firstItemScrollPosition, AlphaFunctions::EaseOut );
1650     mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, layoutSize), AlphaFunctions::EaseOut );
1651     mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1652     mScrollAnimation.Play();
1653   }
1654   else
1655   {
1656     mScrollPositionObject.SetProperty( ScrollConnector::SCROLL_POSITION, firstItemScrollPosition );
1657     AnimateScrollOvershoot(0.0f);
1658   }
1659
1660   mScrollStartedSignalV2.Emit(GetCurrentScrollPosition());
1661 }
1662
1663 void ItemView::SetOvershootEnabled( bool enable )
1664 {
1665   Actor self = Self();
1666   if( enable )
1667   {
1668     mOvershootEffect = BouncingEffect::New(Scrollable::DEFAULT_OVERSHOOT_COLOUR);
1669     mOvershootOverlay = CreateSolidColorActor(Vector4::ONE);
1670     mOvershootOverlay.SetParentOrigin(ParentOrigin::TOP_LEFT);
1671     mOvershootOverlay.SetAnchorPoint(AnchorPoint::TOP_LEFT);
1672     mOvershootOverlay.SetDrawMode(DrawMode::OVERLAY);
1673     mOvershootOverlay.SetShaderEffect(mOvershootEffect);
1674     self.Add(mOvershootOverlay);
1675     Constraint constraint = Constraint::New<float>( Actor::SIZE_WIDTH,
1676                                                       ParentSource( mPropertyScrollDirection ),
1677                                                       Source( mScrollPositionObject, ScrollConnector::OVERSHOOT ),
1678                                                       ParentSource( Actor::SIZE ),
1679                                                       OvershootOverlaySizeConstraint() );
1680     mOvershootOverlay.ApplyConstraint(constraint);
1681     mOvershootOverlay.SetSize(OVERSHOOT_BOUNCE_IMAGE_1_PIXEL_AREA.width, OVERSHOOT_BOUNCE_IMAGE_1_PIXEL_AREA.height);
1682
1683     constraint = Constraint::New<Quaternion>( Actor::ROTATION,
1684                                               ParentSource( mPropertyScrollDirection ),
1685                                               Source( mScrollPositionObject, ScrollConnector::OVERSHOOT ),
1686                                               OvershootOverlayRotationConstraint() );
1687     mOvershootOverlay.ApplyConstraint(constraint);
1688
1689     constraint = Constraint::New<Vector3>( Actor::POSITION,
1690                                            ParentSource( Actor::SIZE ),
1691                                            ParentSource( mPropertyScrollDirection ),
1692                                            Source( mScrollPositionObject, ScrollConnector::OVERSHOOT ),
1693                                            OvershootOverlayPositionConstraint() );
1694     mOvershootOverlay.ApplyConstraint(constraint);
1695
1696     constraint = Constraint::New<bool>( Actor::VISIBLE,
1697                                         ParentSource( mPropertyCanScrollVertical ),
1698                                         OvershootOverlayVisibilityConstraint() );
1699     mOvershootOverlay.ApplyConstraint(constraint);
1700
1701     int effectOvershootPropertyIndex = mOvershootEffect.GetPropertyIndex(mOvershootEffect.GetProgressRatePropertyName());
1702     Actor self = Self();
1703     constraint = Constraint::New<float>( effectOvershootPropertyIndex,
1704                                          Source( mScrollPositionObject, ScrollConnector::OVERSHOOT ),
1705                                          EqualToConstraint() );
1706     mOvershootEffect.ApplyConstraint(constraint);
1707   }
1708   else
1709   {
1710     if( mOvershootOverlay )
1711     {
1712       self.Remove(mOvershootOverlay);
1713       mOvershootOverlay.Reset();
1714     }
1715     mOvershootEffect.Reset();
1716   }
1717 }
1718
1719 float ItemView::CalculateScrollOvershoot()
1720 {
1721   float overshoot = 0.0f;
1722
1723   if(mActiveLayout)
1724   {
1725     // The overshoot must be calculated from the accumulated pan gesture displacement
1726     // since the pan gesture starts.
1727     Actor self = Self();
1728     float scrollDistance = CalculateScrollDistance(mTotalPanDisplacement, *mActiveLayout) * mActiveLayout->GetScrollSpeedFactor();
1729     float positionDelta = GetCurrentLayoutPosition(0) + scrollDistance;
1730     float minLayoutPosition = mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), Self().GetCurrentSize());
1731     self.SetProperty(mPropertyMinimumLayoutPosition, minLayoutPosition);
1732     float clamppedPosition = min(0.0f, max(minLayoutPosition, positionDelta));
1733     overshoot = positionDelta - clamppedPosition;
1734   }
1735
1736   return overshoot;
1737 }
1738
1739 void ItemView::AnimateScrollOvershoot(float overshootAmount, bool animateBack)
1740 {
1741   bool animatingOn = fabsf(overshootAmount) > Math::MACHINE_EPSILON_1;
1742
1743   // make sure we animate back if needed
1744   mAnimateOvershootOff = animateBack || (!animatingOn && mAnimatingOvershootOn);
1745
1746   if( mAnimatingOvershootOn )
1747   {
1748     // animating on, do not allow animate off
1749     return;
1750   }
1751
1752   Actor self = Self();
1753   float currentOvershoot = mScrollPositionObject.GetProperty<float>(ScrollConnector::OVERSHOOT);
1754   float duration = DEFAULT_OVERSHOOT_ANIMATION_DURATION * (animatingOn ? (1.0f - fabsf(currentOvershoot)) : fabsf(currentOvershoot));
1755
1756   RemoveAnimation(mScrollOvershootAnimation);
1757   mScrollOvershootAnimation = Animation::New(duration);
1758   mScrollOvershootAnimation.FinishedSignal().Connect(this, &ItemView::OnOvershootOnFinished);
1759   mScrollOvershootAnimation.AnimateTo( Property(mScrollPositionObject, ScrollConnector::OVERSHOOT), overshootAmount, TimePeriod(0.0f, duration) );
1760   mScrollOvershootAnimation.Play();
1761
1762   mAnimatingOvershootOn = animatingOn;
1763 }
1764
1765 void ItemView::SetItemsParentOrigin( const Vector3& parentOrigin )
1766 {
1767   if( parentOrigin != mItemsParentOrigin )
1768   {
1769     mItemsParentOrigin = parentOrigin;
1770     for (ItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
1771     {
1772       iter->second.SetParentOrigin(parentOrigin);
1773     }
1774   }
1775 }
1776
1777 Vector3 ItemView::GetItemsParentOrigin() const
1778 {
1779   return mItemsParentOrigin;
1780 }
1781
1782 void ItemView::SetItemsAnchorPoint( const Vector3& anchorPoint )
1783 {
1784   if( anchorPoint != mItemsAnchorPoint )
1785   {
1786     mItemsAnchorPoint = anchorPoint;
1787     for (ItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
1788     {
1789       iter->second.SetAnchorPoint(anchorPoint);
1790     }
1791   }
1792 }
1793
1794 Vector3 ItemView::GetItemsAnchorPoint() const
1795 {
1796   return mItemsAnchorPoint;
1797 }
1798
1799 void ItemView::GetItemsRange(ItemRange& range)
1800 {
1801   range.begin = mItemPool.begin()->first;
1802   range.end = mItemPool.rbegin()->first + 1;
1803 }
1804
1805 } // namespace Internal
1806
1807 } // namespace Toolkit
1808
1809 } // namespace Dali