289290c248c3787f47b0ca8e8fdfcf9b71b404e0
[platform/core/uifw/dali-toolkit.git] / 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 Flora License, Version 1.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://floralicense.org/license/
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 // CLASS HEADER
18 #include <dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.h>
19
20 // EXTERNAL INCLUDES
21 #include <algorithm>
22
23 // INTERNAL INCLUDES
24 #include <dali/public-api/events/mouse-wheel-event.h>
25 #include <dali-toolkit/public-api/controls/scrollable/item-view/item-factory.h>
26 #include <dali-toolkit/internal/controls/scroll-bar/scroll-bar-impl.h>
27 #include <dali-toolkit/internal/controls/scrollable/scroll-connector-impl.h>
28
29 using namespace std;
30 using namespace Dali;
31
32 namespace // unnamed namespace
33 {
34
35 //Type registration
36 TypeRegistration mType( typeid(Toolkit::ItemView), typeid(Toolkit::Scrollable), NULL );
37
38 const float DEFAULT_MINIMUM_SWIPE_SPEED = 1.0f;
39 const float DEFAULT_MINIMUM_SWIPE_DISTANCE = 3.0f;
40 const float DEFAULT_MOUSE_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION = 0.1f;
41
42 const float DEFAULT_REFRESH_INTERVAL_LAYOUT_POSITIONS = 20.0f; // 1 updates per 20 items
43 const int MOUSE_WHEEL_EVENT_FINISHED_TIME_OUT = 500;  // 0.5 second
44
45 const float DEFAULT_ANCHORING_DURATION = 1.0f;  // 1 second
46 const float DEFAULT_COLOR_VISIBILITY_REMOVE_TIME = 0.5f; // 0.5 second
47
48 const float MILLISECONDS_PER_SECONDS = 1000.0f;
49
50 const char* OVERSHOOT_OVERLAY_RIPPLE_IMAGE_PATH = DALI_IMAGE_DIR "overshoot_ripple.png";
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 {
416   SetRequiresMouseWheelEvents(true);
417   SetKeyboardNavigationSupport(true);
418 }
419
420 void ItemView::OnInitialize()
421 {
422   SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
423
424   RegisterCommonProperties();
425
426   Actor self = Self();
427
428   mOvershootEffect = OvershootRippleEffect::New();
429   Image overshootImage = Image::New( OVERSHOOT_OVERLAY_RIPPLE_IMAGE_PATH );
430   mOvershootOverlay = ImageActor::New( overshootImage );
431   mOvershootOverlay.SetParentOrigin(ParentOrigin::TOP_LEFT);
432   mOvershootOverlay.SetAnchorPoint(AnchorPoint::TOP_LEFT);
433   mOvershootOverlay.SetDrawMode(DrawMode::OVERLAY);
434   mOvershootOverlay.SetShaderEffect(mOvershootEffect);
435   mOvershootOverlay.SetPixelArea(OVERSHOOT_BOUNCE_IMAGE_1_PIXEL_AREA);
436   self.Add(mOvershootOverlay);
437
438   mScrollConnector = Dali::Toolkit::ScrollConnector::New();
439   mScrollPositionObject = mScrollConnector.GetScrollPositionObject();
440
441   mPropertyMinimumLayoutPosition = self.RegisterProperty(MINIMUM_LAYOUT_POSITION_PROPERTY_NAME, 0.0f);
442   mPropertyPosition = self.RegisterProperty(POSITION_PROPERTY_NAME, 0.0f);
443   mPropertyScrollSpeed = self.RegisterProperty(SCROLL_SPEED_PROPERTY_NAME, 0.0f);
444
445   ApplyOvershootOverlayConstraints();
446
447   Constraint constraint = Constraint::New<Vector3>(mPropertyRelativePosition,
448                                                    LocalSource(mPropertyPosition),
449                                                    LocalSource(mPropertyPositionMin),
450                                                    LocalSource(mPropertyPositionMax),
451                                                    LocalSource(Actor::SIZE),
452                                                    RelativePositionConstraint);
453   self.ApplyConstraint(constraint);
454
455   Vector2 stageSize = Stage::GetCurrent().GetSize();
456   mMouseWheelScrollDistanceStep = stageSize.y * DEFAULT_MOUSE_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION;
457
458   EnableGestureDetection(Gesture::Type(Gesture::Pan));
459
460   mMouseWheelEventFinishedTimer = Timer::New( MOUSE_WHEEL_EVENT_FINISHED_TIME_OUT );
461   mMouseWheelEventFinishedTimer.TickSignal().Connect( this, &ItemView::OnMouseWheelEventFinished );
462
463   SetRefreshInterval(mRefreshIntervalLayoutPositions);
464 }
465
466 ItemView::~ItemView()
467 {
468 }
469
470 Dali::Toolkit::ScrollConnector ItemView::GetScrollConnector() const
471 {
472   return mScrollConnector;
473 }
474
475 unsigned int ItemView::GetLayoutCount() const
476 {
477   return mLayouts.size();
478 }
479
480 void ItemView::AddLayout(ItemLayout& layout)
481 {
482   mLayouts.push_back(ItemLayoutPtr(&layout));
483 }
484
485 void ItemView::RemoveLayout(unsigned int layoutIndex)
486 {
487   DALI_ASSERT_ALWAYS(layoutIndex < mLayouts.size());
488
489   if (mActiveLayout == mLayouts[layoutIndex].Get())
490   {
491     mActiveLayout = NULL;
492   }
493
494   mLayouts.erase(mLayouts.begin() + layoutIndex);
495 }
496
497 ItemLayoutPtr ItemView::GetLayout(unsigned int layoutIndex) const
498 {
499   return mLayouts[layoutIndex];
500 }
501
502 ItemLayoutPtr ItemView::GetActiveLayout() const
503 {
504   return ItemLayoutPtr(mActiveLayout);
505 }
506
507 float ItemView::GetCurrentLayoutPosition(unsigned int itemId) const
508 {
509   return mScrollPositionObject.GetProperty<float>( ScrollConnector::SCROLL_POSITION ) + static_cast<float>( itemId );
510 }
511
512 void ItemView::ActivateLayout(unsigned int layoutIndex, const Vector3& targetSize, float durationSeconds)
513 {
514   DALI_ASSERT_ALWAYS(layoutIndex < mLayouts.size());
515
516   Actor self = Self();
517
518   // The ItemView size should match the active layout size
519   self.SetSize(targetSize);
520   mActiveLayoutTargetSize = targetSize;
521
522   // Switch to the new layout
523   mActiveLayout = mLayouts[layoutIndex].Get();
524
525   // Move the items to the new layout positions...
526
527   bool resizeAnimationNeeded(false);
528
529   for (ConstItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
530   {
531     unsigned int itemId = iter->first;
532     Actor actor = iter->second;
533
534     // Remove constraints from previous layout
535     actor.RemoveConstraints();
536
537     Vector3 size;
538     if(mActiveLayout->GetItemSize(itemId, targetSize, size))
539     {
540       if( durationSeconds > 0.0f )
541       {
542         // Use a size animation
543         if (!resizeAnimationNeeded)
544         {
545           resizeAnimationNeeded = true;
546           RemoveAnimation(mResizeAnimation);
547           mResizeAnimation = Animation::New(durationSeconds);
548         }
549
550         // The layout provides its own resize animation
551         mActiveLayout->GetResizeAnimation(mResizeAnimation, actor, size, durationSeconds);
552       }
553       else
554       {
555         // resize immediately
556         actor.SetSize(size);
557       }
558     }
559
560     ApplyConstraints(actor, *mActiveLayout, itemId, durationSeconds);
561   }
562
563   if (resizeAnimationNeeded)
564   {
565     mResizeAnimation.Play();
566   }
567
568   // Refresh the new layout
569   ItemRange range = GetItemRange(*mActiveLayout, targetSize, true/*reserve extra*/);
570   AddActorsWithinRange( range, durationSeconds );
571
572   // Scroll to an appropriate layout position
573
574   bool scrollAnimationNeeded(false);
575   float firstItemScrollPosition(0.0f);
576
577   float current = GetCurrentLayoutPosition(0);
578   float minimum = ClampFirstItemPosition(current, targetSize, *mActiveLayout);
579   self.SetProperty(mPropertyPosition, GetScrollPosition(current, targetSize));
580
581   if (current < minimum)
582   {
583     scrollAnimationNeeded = true;
584     firstItemScrollPosition = minimum;
585   }
586   else if (mAnchoringEnabled)
587   {
588     scrollAnimationNeeded = true;
589     firstItemScrollPosition = mActiveLayout->GetClosestAnchorPosition(current);
590   }
591
592   if (scrollAnimationNeeded)
593   {
594     RemoveAnimation(mScrollAnimation);
595     mScrollAnimation = Animation::New(mAnchoringDuration);
596     mScrollAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), firstItemScrollPosition, AlphaFunctions::EaseOut );
597     mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, targetSize), AlphaFunctions::EaseOut );
598     mScrollAnimation.Play();
599   }
600
601   self.SetProperty(mPropertyMinimumLayoutPosition, mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), targetSize));
602   AnimateScrollOvershoot(0.0f);
603   mScrollOvershoot = 0.0f;
604
605   Radian scrollDirection(mActiveLayout->GetScrollDirection());
606   float orientation = static_cast<float>(mActiveLayout->GetOrientation());
607   self.SetProperty(mPropertyScrollDirection, Vector3(sinf(scrollDirection), cosf(scrollDirection), orientation));
608
609   self.SetProperty(mPropertyScrollSpeed, mScrollSpeed);
610
611   CalculateDomainSize(targetSize);
612 }
613
614 void ItemView::DeactivateCurrentLayout()
615 {
616   if (mActiveLayout)
617   {
618     for (ConstItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
619     {
620       Actor actor = iter->second;
621       actor.RemoveConstraints();
622     }
623
624     mActiveLayout = NULL;
625   }
626 }
627
628 void ItemView::SetDefaultAlphaFunction(AlphaFunction func)
629 {
630   mDefaultAlphaFunction = func;
631 }
632
633 AlphaFunction ItemView::GetDefaultAlphaFunction() const
634 {
635   return mDefaultAlphaFunction;
636 }
637
638 void ItemView::OnRefreshNotification(PropertyNotification& source)
639 {
640   if (mActiveLayout)
641   {
642     ItemRange range = GetItemRange(*mActiveLayout, mActiveLayoutTargetSize, true/*reserve extra*/);
643     RemoveActorsOutsideRange( range );
644     AddActorsWithinRange( range, 0.0f/*immediate*/ );
645
646     Vector3 currentScrollPosition = GetCurrentScrollPosition();
647     mScrollUpdatedSignalV2.Emit( currentScrollPosition );
648   }
649 }
650
651 void ItemView::SetMinimumSwipeSpeed(float speed)
652 {
653   mMinimumSwipeSpeed = speed;
654 }
655
656 float ItemView::GetMinimumSwipeSpeed() const
657 {
658   return mMinimumSwipeSpeed;
659 }
660
661 void ItemView::SetMinimumSwipeDistance(float distance)
662 {
663   mMinimumSwipeDistance = distance;
664 }
665
666 float ItemView::GetMinimumSwipeDistance() const
667 {
668   return mMinimumSwipeDistance;
669 }
670
671 void ItemView::SetMouseWheelScrollDistanceStep(float step)
672 {
673   mMouseWheelScrollDistanceStep = step;
674 }
675
676 float ItemView::GetMouseWheelScrollDistanceStep() const
677 {
678   return mMouseWheelScrollDistanceStep;
679 }
680
681 void ItemView::SetAnchoring(bool enabled)
682 {
683   mAnchoringEnabled = enabled;
684 }
685
686 bool ItemView::GetAnchoring() const
687 {
688   return mAnchoringEnabled;
689 }
690
691 void ItemView::SetAnchoringDuration(float durationSeconds)
692 {
693   mAnchoringDuration = durationSeconds;
694 }
695
696 float ItemView::GetAnchoringDuration() const
697 {
698   return mAnchoringDuration;
699 }
700
701 void ItemView::SetRefreshInterval(float intervalLayoutPositions)
702 {
703   mRefreshIntervalLayoutPositions = intervalLayoutPositions;
704
705   if(mRefreshNotification)
706   {
707     mScrollPositionObject.RemovePropertyNotification(mRefreshNotification);
708   }
709   mRefreshNotification = mScrollPositionObject.AddPropertyNotification( ScrollConnector::SCROLL_POSITION, StepCondition(mRefreshIntervalLayoutPositions, 0.0f) );
710   mRefreshNotification.NotifySignal().Connect( this, &ItemView::OnRefreshNotification );
711 }
712
713 float ItemView::GetRefreshInterval() const
714 {
715   return mRefreshIntervalLayoutPositions;
716 }
717
718 Actor ItemView::GetItem(unsigned int itemId) const
719 {
720   Actor actor;
721
722   ConstItemPoolIter iter = mItemPool.find( itemId );
723   if( iter != mItemPool.end() )
724   {
725     actor = iter->second;
726   }
727
728   return actor;
729 }
730
731 unsigned int ItemView::GetItemId( Actor actor ) const
732 {
733   unsigned int itemId( 0 );
734
735   for ( ConstItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter )
736   {
737     if( iter->second == actor )
738     {
739       itemId = iter->first;
740       break;
741     }
742   }
743
744   return itemId;
745 }
746
747 void ItemView::InsertItem( Item newItem, float durationSeconds )
748 {
749   mAddingItems = true;
750
751   SetupActor( newItem, durationSeconds );
752   Self().Add( newItem.second );
753
754   ItemPoolIter foundIter = mItemPool.find( newItem.first );
755   if( mItemPool.end() != foundIter )
756   {
757     Actor moveMe = foundIter->second;
758     foundIter->second = newItem.second;
759
760     // Move the existing actors to make room
761     for( ItemPoolIter iter = ++foundIter; mItemPool.end() != iter; ++iter )
762     {
763       Actor temp = iter->second;
764       iter->second = moveMe;
765       moveMe = temp;
766
767       iter->second.RemoveConstraints();
768       ApplyConstraints( iter->second, *mActiveLayout, iter->first, durationSeconds );
769     }
770
771     // Create last item
772     ItemId lastId = mItemPool.rbegin()->first;
773     Item lastItem( lastId + 1, moveMe );
774     mItemPool.insert( lastItem );
775
776     lastItem.second.RemoveConstraints();
777     ApplyConstraints( lastItem.second, *mActiveLayout, lastItem.first, durationSeconds );
778   }
779   else
780   {
781     mItemPool.insert( newItem );
782   }
783
784   CalculateDomainSize(Self().GetCurrentSize());
785
786   mAddingItems = false;
787 }
788
789 void ItemView::InsertItems( const ItemContainer& newItems, float durationSeconds )
790 {
791   mAddingItems = true;
792
793   // Insert from lowest id to highest
794   set<Item> sortedItems;
795   for( ConstItemIter iter = newItems.begin(); newItems.end() != iter; ++iter )
796   {
797     sortedItems.insert( *iter );
798   }
799
800   for( set<Item>::iterator iter = sortedItems.begin(); sortedItems.end() != iter; ++iter )
801   {
802     Self().Add( iter->second );
803
804     cout << "inserting item: " << iter->first << endl;
805
806     ItemPoolIter foundIter = mItemPool.find( iter->first );
807     if( mItemPool.end() != foundIter )
808     {
809       Actor moveMe = foundIter->second;
810       foundIter->second = iter->second;
811
812       // Move the existing actors to make room
813       for( ItemPoolIter iter = ++foundIter; mItemPool.end() != iter; ++iter )
814       {
815         Actor temp = iter->second;
816         iter->second = moveMe;
817         moveMe = temp;
818       }
819
820       // Create last item
821       ItemId lastId = mItemPool.rbegin()->first;
822       Item lastItem( lastId + 1, moveMe );
823       mItemPool.insert( lastItem );
824     }
825     else
826     {
827       mItemPool.insert( *iter );
828     }
829   }
830
831   // Relayout everything
832   for (ItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
833   {
834     // If newly inserted
835     if( FindById( newItems, iter->first ) )
836     {
837       SetupActor( *iter, durationSeconds );
838     }
839     else
840     {
841       iter->second.RemoveConstraints();
842       ApplyConstraints( iter->second, *mActiveLayout, iter->first, durationSeconds );
843     }
844   }
845
846   CalculateDomainSize(Self().GetCurrentSize());
847
848   mAddingItems = false;
849 }
850
851 void ItemView::RemoveItem( unsigned int itemId, float durationSeconds )
852 {
853   bool actorRemoved = RemoveActor( itemId );
854   if( actorRemoved )
855   {
856     ReapplyAllConstraints( durationSeconds );
857   }
858 }
859
860 void ItemView::RemoveItems( const ItemIdContainer& itemIds, float durationSeconds )
861 {
862   bool actorRemoved( false );
863
864   // Remove from highest id to lowest
865   set<ItemId> sortedItems;
866   for( ConstItemIdIter iter = itemIds.begin(); itemIds.end() != iter; ++iter )
867   {
868     sortedItems.insert( *iter );
869   }
870
871   for( set<ItemId>::reverse_iterator iter = sortedItems.rbegin(); sortedItems.rend() != iter; ++iter )
872   {
873     if( RemoveActor( *iter ) )
874     {
875       actorRemoved = true;
876     }
877   }
878
879   if( actorRemoved )
880   {
881     ReapplyAllConstraints( durationSeconds );
882   }
883 }
884
885 bool ItemView::RemoveActor(unsigned int itemId)
886 {
887   bool removed( false );
888
889   const ItemPoolIter removeIter = mItemPool.find( itemId );
890
891   if( removeIter != mItemPool.end() )
892   {
893     Self().Remove( removeIter->second );
894     removed = true;
895
896     // Adjust the remaining item IDs, for example if item 2 is removed:
897     //   Initial actors:     After insert:
898     //     ID 1 - ActorA       ID 1 - ActorA
899     //     ID 2 - ActorB       ID 2 - ActorC (previously ID 3)
900     //     ID 3 - ActorC       ID 3 - ActorB (previously ID 4)
901     //     ID 4 - ActorD
902     for (ItemPoolIter iter = removeIter; iter != mItemPool.end(); ++iter)
903     {
904       if( iter->first < mItemPool.rbegin()->first )
905       {
906         iter->second = mItemPool[ iter->first + 1 ];
907       }
908       else
909       {
910         mItemPool.erase( iter );
911         break;
912       }
913     }
914   }
915
916   return removed;
917 }
918
919 void ItemView::ReplaceItem( Item replacementItem, float durationSeconds )
920 {
921   mAddingItems = true;
922
923   SetupActor( replacementItem, durationSeconds );
924   Self().Add( replacementItem.second );
925
926   const ItemPoolIter iter = mItemPool.find( replacementItem.first );
927   if( mItemPool.end() != iter )
928   {
929     Self().Remove( iter->second );
930     iter->second = replacementItem.second;
931   }
932   else
933   {
934     mItemPool.insert( replacementItem );
935   }
936
937   CalculateDomainSize(Self().GetCurrentSize());
938
939   mAddingItems = false;
940 }
941
942 void ItemView::ReplaceItems( const ItemContainer& replacementItems, float durationSeconds )
943 {
944   for( ConstItemIter iter = replacementItems.begin(); replacementItems.end() != iter; ++iter )
945   {
946     ReplaceItem( *iter, durationSeconds );
947   }
948 }
949
950 void ItemView::RemoveActorsOutsideRange( ItemRange range )
951 {
952   // Remove unwanted actors from the ItemView & ItemPool
953   for (ItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); )
954   {
955     unsigned int current = iter->first;
956
957     if( ! range.Within( current ) )
958     {
959       Self().Remove( iter->second );
960
961       mItemPool.erase( iter++ ); // erase invalidates the return value of post-increment; iter remains valid
962     }
963     else
964     {
965       ++iter;
966     }
967   }
968 }
969
970 void ItemView::AddActorsWithinRange( ItemRange range, float durationSeconds )
971 {
972   range.end = min(mItemFactory.GetNumberOfItems(), range.end);
973
974   // The order of addition depends on the scroll direction.
975   if (mRefreshOrderHint)
976   {
977     for (unsigned int itemId = range.begin; itemId < range.end; ++itemId)
978     {
979       AddNewActor( itemId, durationSeconds );
980     }
981   }
982   else
983   {
984     for (unsigned int itemId = range.end; itemId > range.begin; --itemId)
985     {
986       AddNewActor( itemId-1, durationSeconds );
987     }
988   }
989
990   // Total number of items may change dynamically.
991   // Always recalculate the domain size to reflect that.
992   CalculateDomainSize(Self().GetCurrentSize());
993 }
994
995 void ItemView::AddNewActor( unsigned int itemId, float durationSeconds )
996 {
997   mAddingItems = true;
998
999   if( mItemPool.end() == mItemPool.find( itemId ) )
1000   {
1001     Actor actor = mItemFactory.NewItem( itemId );
1002
1003     if( actor )
1004     {
1005       Item newItem( itemId, actor );
1006
1007       mItemPool.insert( newItem );
1008
1009       SetupActor( newItem, durationSeconds );
1010       Self().Add( actor );
1011     }
1012   }
1013
1014   mAddingItems = false;
1015 }
1016
1017 void ItemView::SetupActor( Item item, float durationSeconds )
1018 {
1019   item.second.SetParentOrigin( ParentOrigin::CENTER );
1020   item.second.SetAnchorPoint( AnchorPoint::CENTER );
1021
1022   if( mActiveLayout )
1023   {
1024     Vector3 size;
1025     if( mActiveLayout->GetItemSize( item.first, mActiveLayoutTargetSize, size ) )
1026     {
1027       item.second.SetSize( size );
1028     }
1029
1030     ApplyConstraints( item.second, *mActiveLayout, item.first, durationSeconds );
1031   }
1032 }
1033
1034 ItemRange ItemView::GetItemRange(ItemLayout& layout, const Vector3& layoutSize, bool reserveExtra)
1035 {
1036   unsigned int itemCount = mItemFactory.GetNumberOfItems();
1037
1038   ItemRange available(0u, itemCount);
1039
1040   ItemRange range = layout.GetItemsWithinArea( GetCurrentLayoutPosition(0), layoutSize );
1041
1042   if (reserveExtra)
1043   {
1044     // Add the reserve items for scrolling
1045     unsigned int extra = layout.GetReserveItemCount(layoutSize);
1046     range.begin = (range.begin >= extra) ? (range.begin - extra) : 0u;
1047     range.end += extra;
1048   }
1049
1050   return range.Intersection(available);
1051 }
1052
1053 void ItemView::OnChildAdd(Actor& child)
1054 {
1055   if(!mAddingItems)
1056   {
1057     // We don't want to do this downcast check for any item added by ItemView itself.
1058     Dali::Toolkit::ScrollBar scrollBar = Dali::Toolkit::ScrollBar::DownCast(child);
1059     if(scrollBar)
1060     {
1061       // Set the scroll connector when scroll bar is being added
1062       scrollBar.SetScrollConnector(mScrollConnector);
1063     }
1064   }
1065 }
1066
1067 bool ItemView::OnTouchEvent(const TouchEvent& event)
1068 {
1069   // Ignore events with multiple-touch points
1070   if (event.GetPointCount() != 1)
1071   {
1072     return false;
1073   }
1074
1075   if (event.GetPoint(0).state == TouchPoint::Down)
1076   {
1077     // Cancel ongoing scrolling etc.
1078     mGestureState = Gesture::Clear;
1079
1080     mScrollDistance = 0.0f;
1081     mScrollSpeed = 0.0f;
1082     Self().SetProperty(mPropertyScrollSpeed, mScrollSpeed);
1083
1084     mScrollOvershoot = 0.0f;
1085     AnimateScrollOvershoot(0.0f);
1086
1087     mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition());
1088
1089     RemoveAnimation(mScrollAnimation);
1090   }
1091
1092   return true; // consume since we're potentially scrolling
1093 }
1094
1095 bool ItemView::OnMouseWheelEvent(const MouseWheelEvent& event)
1096 {
1097   // Respond the mouse wheel event to scroll
1098   if (mActiveLayout)
1099   {
1100     Actor self = Self();
1101     const Vector3 layoutSize = Self().GetCurrentSize();
1102     float layoutPositionDelta = GetCurrentLayoutPosition(0) + (event.z * mMouseWheelScrollDistanceStep * mActiveLayout->GetScrollSpeedFactor());
1103     float firstItemScrollPosition = ClampFirstItemPosition(layoutPositionDelta, layoutSize, *mActiveLayout);
1104
1105     mScrollPositionObject.SetProperty( ScrollConnector::SCROLL_POSITION, firstItemScrollPosition );
1106     self.SetProperty(mPropertyPosition, GetScrollPosition(firstItemScrollPosition, layoutSize));
1107     mScrollStartedSignalV2.Emit(GetCurrentScrollPosition());
1108   }
1109
1110   if (mMouseWheelEventFinishedTimer.IsRunning())
1111   {
1112     mMouseWheelEventFinishedTimer.Stop();
1113   }
1114
1115   mMouseWheelEventFinishedTimer.Start();
1116
1117   return true;
1118 }
1119
1120 bool ItemView::OnMouseWheelEventFinished()
1121 {
1122   if (mActiveLayout)
1123   {
1124     RemoveAnimation(mScrollAnimation);
1125
1126     // No more mouse wheel events coming. Do the anchoring if enabled.
1127     mScrollAnimation = DoAnchoring();
1128     if (mScrollAnimation)
1129     {
1130       mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1131       mScrollAnimation.Play();
1132     }
1133     else
1134     {
1135       mScrollOvershoot = 0.0f;
1136       AnimateScrollOvershoot(0.0f);
1137
1138       mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition());
1139     }
1140   }
1141
1142   return false;
1143 }
1144
1145 void ItemView::ApplyConstraints(Actor& actor, ItemLayout& layout, unsigned int itemId, float duration)
1146 {
1147   ItemLayout::Vector3Function positionConstraint;
1148   if (layout.GetPositionConstraint(itemId, positionConstraint))
1149   {
1150     WrappedVector3Constraint wrapped(positionConstraint, itemId);
1151
1152     Constraint constraint = Constraint::New<Vector3>( Actor::POSITION,
1153                                                       Source( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ),
1154                                                       ParentSource( mPropertyScrollSpeed ),
1155                                                       ParentSource( Actor::SIZE ),
1156                                                       wrapped );
1157     constraint.SetApplyTime(duration);
1158     constraint.SetAlphaFunction(mDefaultAlphaFunction);
1159
1160     actor.ApplyConstraint(constraint);
1161   }
1162
1163   ItemLayout::QuaternionFunction rotationConstraint;
1164   if (layout.GetRotationConstraint(itemId, rotationConstraint))
1165   {
1166     WrappedQuaternionConstraint wrapped(rotationConstraint, itemId);
1167
1168     Constraint constraint = Constraint::New<Quaternion>( Actor::ROTATION,
1169                                                          Source( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ),
1170                                                          ParentSource( mPropertyScrollSpeed ),
1171                                                          ParentSource( Actor::SIZE ),
1172                                                          wrapped );
1173     constraint.SetApplyTime(duration);
1174     constraint.SetAlphaFunction(mDefaultAlphaFunction);
1175
1176     actor.ApplyConstraint(constraint);
1177   }
1178
1179   ItemLayout::Vector3Function scaleConstraint;
1180   if (layout.GetScaleConstraint(itemId, scaleConstraint))
1181   {
1182     WrappedVector3Constraint wrapped(scaleConstraint, itemId);
1183
1184     Constraint constraint = Constraint::New<Vector3>( Actor::SCALE,
1185                                                       Source( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ),
1186                                                       ParentSource( mPropertyScrollSpeed ),
1187                                                       ParentSource( Actor::SIZE ),
1188                                                       wrapped );
1189     constraint.SetApplyTime(duration);
1190     constraint.SetAlphaFunction(mDefaultAlphaFunction);
1191
1192     actor.ApplyConstraint(constraint);
1193   }
1194
1195   ItemLayout::Vector4Function colorConstraint;
1196   if (layout.GetColorConstraint(itemId, colorConstraint))
1197   {
1198     WrappedVector4Constraint wrapped(colorConstraint, itemId);
1199
1200     Constraint constraint = Constraint::New<Vector4>( Actor::COLOR,
1201                                                       Source( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ),
1202                                                       ParentSource( mPropertyScrollSpeed ),
1203                                                       ParentSource( Actor::SIZE ),
1204                                                       wrapped );
1205     constraint.SetApplyTime(duration);
1206     constraint.SetAlphaFunction(mDefaultAlphaFunction);
1207
1208     // Release color constraints slowly; this allows ItemView to co-exist with ImageActor fade-in
1209     constraint.SetRemoveTime(DEFAULT_COLOR_VISIBILITY_REMOVE_TIME);
1210     constraint.SetRemoveAction(Dali::Constraint::Discard);
1211
1212     actor.ApplyConstraint(constraint);
1213   }
1214
1215   ItemLayout::BoolFunction visibilityConstraint;
1216   if (layout.GetVisibilityConstraint(itemId, visibilityConstraint))
1217   {
1218     WrappedBoolConstraint wrapped(visibilityConstraint, itemId);
1219
1220     Constraint constraint = Constraint::New<bool>( Actor::VISIBLE,
1221                                                    Source( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ),
1222                                                    ParentSource( mPropertyScrollSpeed ),
1223                                                    ParentSource( Actor::SIZE ),
1224                                                    wrapped );
1225     constraint.SetApplyTime(duration);
1226     constraint.SetAlphaFunction(mDefaultAlphaFunction);
1227
1228     // Release visibility constraints the same time as the color constraint
1229     constraint.SetRemoveTime(DEFAULT_COLOR_VISIBILITY_REMOVE_TIME);
1230     constraint.SetRemoveAction(Dali::Constraint::Discard);
1231
1232     actor.ApplyConstraint(constraint);
1233   }
1234 }
1235
1236 void ItemView::ReapplyAllConstraints( float durationSeconds )
1237 {
1238   for (ConstItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
1239   {
1240     unsigned int id = iter->first;
1241     Actor actor = iter->second;
1242
1243     actor.RemoveConstraints();
1244     ApplyConstraints(actor, *mActiveLayout, id, durationSeconds);
1245   }
1246
1247   CalculateDomainSize(Self().GetCurrentSize());
1248 }
1249
1250 float ItemView::ClampFirstItemPosition(float targetPosition, const Vector3& targetSize, ItemLayout& layout)
1251 {
1252   Actor self = Self();
1253   float minLayoutPosition = layout.GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), targetSize);
1254   float clamppedPosition = min(0.0f, max(minLayoutPosition, targetPosition));
1255   mScrollOvershoot = targetPosition - clamppedPosition;
1256   self.SetProperty(mPropertyMinimumLayoutPosition, minLayoutPosition);
1257
1258   return clamppedPosition;
1259 }
1260
1261 void ItemView::OnPan(PanGesture gesture)
1262 {
1263   Actor self = Self();
1264   const Vector3 layoutSize = Self().GetCurrentSize();
1265
1266   RemoveAnimation(mScrollAnimation);
1267
1268   // Short-circuit if there is no active layout
1269   if (!mActiveLayout)
1270   {
1271     mGestureState = Gesture::Clear;
1272     return;
1273   }
1274
1275   mGestureState = gesture.state;
1276
1277   switch (mGestureState)
1278   {
1279     case Gesture::Finished:
1280     {
1281       // Swipe Detection
1282       if (fabsf(mScrollDistance) > mMinimumSwipeDistance &&
1283           mScrollSpeed > mMinimumSwipeSpeed)
1284       {
1285         float direction = (mScrollDistance < 0.0f) ? -1.0f : 1.0f;
1286
1287         mRefreshOrderHint = true;
1288
1289         float currentLayoutPosition = GetCurrentLayoutPosition(0);
1290         float firstItemScrollPosition = ClampFirstItemPosition(currentLayoutPosition + mScrollSpeed * direction,
1291                                                                layoutSize,
1292                                                                *mActiveLayout);
1293
1294         if (mAnchoringEnabled)
1295         {
1296           firstItemScrollPosition = mActiveLayout->GetClosestAnchorPosition(firstItemScrollPosition);
1297         }
1298
1299         RemoveAnimation(mScrollAnimation);
1300
1301         float flickAnimationDuration = mActiveLayout->GetItemFlickAnimationDuration() * max(1.0f, fabsf(firstItemScrollPosition - GetCurrentLayoutPosition(0)));
1302         mScrollAnimation = Animation::New(flickAnimationDuration);
1303         mScrollAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), firstItemScrollPosition, AlphaFunctions::EaseOut );
1304         mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, layoutSize), AlphaFunctions::EaseOut );
1305         mScrollAnimation.AnimateTo( Property(self, mPropertyScrollSpeed), 0.0f, AlphaFunctions::EaseOut );
1306
1307         mIsFlicking = true;
1308         // Check whether it has already scrolled to the end
1309         if(fabs(currentLayoutPosition - firstItemScrollPosition) > Math::MACHINE_EPSILON_0)
1310         {
1311           AnimateScrollOvershoot(0.0f);
1312         }
1313       }
1314
1315       // Anchoring may be triggered when there was no swipe
1316       if (!mScrollAnimation)
1317       {
1318         mScrollAnimation = DoAnchoring();
1319       }
1320
1321       // Reset the overshoot if no scroll animation.
1322       if (!mScrollAnimation)
1323       {
1324         mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition());
1325
1326         AnimateScrollOvershoot(0.0f, false);
1327       }
1328     }
1329     break;
1330
1331     case Gesture::Started: // Fall through
1332     {
1333       mTotalPanDisplacement = Vector2::ZERO;
1334     }
1335
1336     case Gesture::Continuing:
1337     {
1338       mScrollDistance = CalculateScrollDistance(gesture.displacement, *mActiveLayout);
1339       mScrollSpeed = Clamp((gesture.GetSpeed() * mActiveLayout->GetScrollSpeedFactor() * MILLISECONDS_PER_SECONDS), 0.0f, mActiveLayout->GetMaximumSwipeSpeed());
1340
1341       // Refresh order depends on the direction of the scroll; negative is towards the last item.
1342       mRefreshOrderHint = mScrollDistance < 0.0f;
1343
1344       RemoveAnimation(mScrollSpeedAnimation);
1345       mScrollSpeedAnimation = Animation::New(0.3f);
1346       mScrollSpeedAnimation.AnimateTo( Property(self, mPropertyScrollSpeed), mScrollSpeed, AlphaFunctions::Linear );
1347       mScrollSpeedAnimation.Play();
1348
1349       float layoutPositionDelta = GetCurrentLayoutPosition(0) + (mScrollDistance * mActiveLayout->GetScrollSpeedFactor());
1350
1351       float firstItemScrollPosition = ClampFirstItemPosition(layoutPositionDelta, layoutSize, *mActiveLayout);
1352
1353       mScrollPositionObject.SetProperty( ScrollConnector::SCROLL_POSITION, firstItemScrollPosition );
1354       self.SetProperty(mPropertyPosition, GetScrollPosition(firstItemScrollPosition, layoutSize));
1355       mScrollStartedSignalV2.Emit(GetCurrentScrollPosition());
1356
1357       mTotalPanDisplacement += gesture.displacement;
1358       mScrollOvershoot = layoutPositionDelta - firstItemScrollPosition;
1359       if( mScrollOvershoot > Math::MACHINE_EPSILON_1 )
1360       {
1361         AnimateScrollOvershoot(1.0f);
1362       }
1363       else if( mScrollOvershoot < -Math::MACHINE_EPSILON_1 )
1364       {
1365         AnimateScrollOvershoot(-1.0f);
1366       }
1367       else
1368       {
1369         AnimateScrollOvershoot(0.0f);
1370       }
1371     }
1372     break;
1373
1374     case Gesture::Cancelled:
1375     {
1376       mScrollAnimation = DoAnchoring();
1377     }
1378     break;
1379
1380     default:
1381       break;
1382   }
1383
1384   if (mScrollAnimation)
1385   {
1386     mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1387     mScrollAnimation.Play();
1388   }
1389 }
1390
1391 bool ItemView::OnAccessibilityPan(PanGesture gesture)
1392 {
1393   OnPan(gesture);
1394   return true;
1395 }
1396
1397 Actor ItemView::GetNextKeyboardFocusableActor(Actor actor, Control::KeyboardFocusNavigationDirection direction, bool loopEnabled)
1398 {
1399   Actor nextFocusActor;
1400   if(mActiveLayout)
1401   {
1402     int nextItemID = 0;
1403     if(!actor || actor == this->Self())
1404     {
1405       nextFocusActor = GetItem(nextItemID);
1406     }
1407     else if(actor && actor.GetParent() == this->Self())
1408     {
1409       int itemID = GetItemId(actor);
1410       nextItemID = mActiveLayout->GetNextFocusItemID(itemID, mItemFactory.GetNumberOfItems(), direction, loopEnabled);
1411       nextFocusActor = GetItem(nextItemID);
1412       if(nextFocusActor == actor)
1413       {
1414         // need to pass NULL actor back to focus manager
1415         nextFocusActor.Reset();
1416         return nextFocusActor;
1417       }
1418     }
1419     float layoutPosition = mActiveLayout->GetClosestAnchorPosition( GetCurrentLayoutPosition(0) );
1420     Vector3 layoutSize = Self().GetCurrentSize();
1421     if(!nextFocusActor)
1422     {
1423       // likely the current item is not buffered, so not in our item pool, probably best to get first viewable item
1424       ItemRange viewableItems = mActiveLayout->GetItemsWithinArea(layoutPosition, layoutSize);
1425       nextItemID = viewableItems.begin;
1426       nextFocusActor = GetItem(nextItemID);
1427     }
1428   }
1429   return nextFocusActor;
1430 }
1431
1432 void ItemView::OnKeyboardFocusChangeCommitted(Actor commitedFocusableActor)
1433 {
1434   // only in this function if our chosen focus actor was actually used
1435   if(commitedFocusableActor)
1436   {
1437     int nextItemID = GetItemId(commitedFocusableActor);
1438     float layoutPosition = GetCurrentLayoutPosition(0);
1439     Vector3 layoutSize = Self().GetCurrentSize();
1440     Vector3 focusItemPosition = Vector3::ZERO;
1441     ItemLayout::Vector3Function itemPositionConstraint;
1442     if (mActiveLayout->GetPositionConstraint(nextItemID, itemPositionConstraint))
1443     {
1444       focusItemPosition = itemPositionConstraint(Vector3::ZERO, layoutPosition + nextItemID, 0.0f, layoutSize);
1445     }
1446
1447     float scrollTo = mActiveLayout->GetClosestOnScreenLayoutPosition(nextItemID, layoutPosition, layoutSize);
1448     ScrollTo(Vector3(0.0f, scrollTo, 0.0f), DEFAULT_KEYBOARD_FOCUS_SCROLL_DURATION);
1449   }
1450 }
1451
1452 Animation ItemView::DoAnchoring()
1453 {
1454   Animation anchoringAnimation;
1455   Actor self = Self();
1456
1457   if (mActiveLayout && mAnchoringEnabled)
1458   {
1459     float anchorPosition = mActiveLayout->GetClosestAnchorPosition( GetCurrentLayoutPosition(0) );
1460
1461     anchoringAnimation = Animation::New(mAnchoringDuration);
1462     anchoringAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), anchorPosition, AlphaFunctions::EaseOut );
1463     anchoringAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(anchorPosition, self.GetCurrentSize()), AlphaFunctions::EaseOut );
1464     anchoringAnimation.AnimateTo( Property(self, mPropertyScrollSpeed), 0.0f, AlphaFunctions::EaseOut );
1465     if(!mIsFlicking)
1466     {
1467       AnimateScrollOvershoot(0.0f);
1468     }
1469   }
1470
1471   return anchoringAnimation;
1472 }
1473
1474 void ItemView::OnScrollFinished(Animation& source)
1475 {
1476   Actor self = Self();
1477
1478   RemoveAnimation(mScrollAnimation); // mScrollAnimation is used to query whether we're scrolling
1479
1480   mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition());
1481
1482   if(mIsFlicking && fabsf(mScrollOvershoot) > Math::MACHINE_EPSILON_1)
1483   {
1484     AnimateScrollOvershoot( mScrollOvershoot > 0.0f ? 1.0f : -1.0f, true);
1485   }
1486   else
1487   {
1488     // Reset the overshoot
1489     AnimateScrollOvershoot( 0.0f );
1490   }
1491   mIsFlicking = false;
1492
1493   mScrollOvershoot = 0.0f;
1494 }
1495
1496 void ItemView::OnOvershootOnFinished(Animation& animation)
1497 {
1498   mAnimatingOvershootOn = false;
1499   mScrollOvershootAnimation.FinishedSignal().Disconnect(this, &ItemView::OnOvershootOnFinished);
1500   RemoveAnimation(mScrollOvershootAnimation);
1501   if(mAnimateOvershootOff)
1502   {
1503     AnimateScrollOvershoot(0.0f);
1504   }
1505 }
1506
1507 void ItemView::ScrollToItem(unsigned int itemId, float durationSeconds)
1508 {
1509   Actor self = Self();
1510   const Vector3 layoutSize = Self().GetCurrentSize();
1511   float firstItemScrollPosition = ClampFirstItemPosition(mActiveLayout->GetItemScrollToPosition(itemId), layoutSize, *mActiveLayout);
1512
1513   if(durationSeconds > 0.0f)
1514   {
1515     RemoveAnimation(mScrollAnimation);
1516     mScrollAnimation = Animation::New(durationSeconds);
1517     mScrollAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), firstItemScrollPosition, AlphaFunctions::EaseOut );
1518     mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, layoutSize), AlphaFunctions::EaseOut );
1519     mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1520     mScrollAnimation.Play();
1521   }
1522   else
1523   {
1524     mScrollPositionObject.SetProperty( ScrollConnector::SCROLL_POSITION, firstItemScrollPosition );
1525     AnimateScrollOvershoot(0.0f);
1526   }
1527
1528   mScrollStartedSignalV2.Emit(GetCurrentScrollPosition());
1529 }
1530
1531 void ItemView::RemoveAnimation(Animation& animation)
1532 {
1533   if(animation)
1534   {
1535     // Cease animating, and reset handle.
1536     animation.Clear();
1537     animation.Reset();
1538   }
1539 }
1540
1541 void ItemView::CalculateDomainSize(const Vector3& layoutSize)
1542 {
1543   Actor self = Self();
1544
1545   Vector3 firstItemPosition(Vector3::ZERO);
1546   Vector3 lastItemPosition(Vector3::ZERO);
1547
1548   if(mActiveLayout)
1549   {
1550     ItemLayout::Vector3Function firstItemPositionConstraint;
1551     if (mActiveLayout->GetPositionConstraint(0, firstItemPositionConstraint))
1552     {
1553       firstItemPosition = firstItemPositionConstraint(Vector3::ZERO, 0, 0.0f, layoutSize);
1554     }
1555
1556     float minLayoutPosition = mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), layoutSize);
1557     self.SetProperty(mPropertyMinimumLayoutPosition, minLayoutPosition);
1558
1559     ItemLayout::Vector3Function lastItemPositionConstraint;
1560     if (mActiveLayout->GetPositionConstraint(fabs(minLayoutPosition), lastItemPositionConstraint))
1561     {
1562       lastItemPosition = lastItemPositionConstraint(Vector3::ZERO, fabs(minLayoutPosition), 0.0f, layoutSize);
1563     }
1564
1565     float domainSize;
1566
1567     if(IsHorizontal(mActiveLayout->GetOrientation()))
1568     {
1569       self.SetProperty(mPropertyPositionMin, Vector3(0.0f, firstItemPosition.x, 0.0f));
1570       self.SetProperty(mPropertyPositionMax, Vector3(0.0f, lastItemPosition.x, 0.0f));
1571       domainSize = fabs(firstItemPosition.x - lastItemPosition.x);
1572     }
1573     else
1574     {
1575       self.SetProperty(mPropertyPositionMin, Vector3(0.0f, firstItemPosition.y, 0.0f));
1576       self.SetProperty(mPropertyPositionMax, Vector3(0.0f, lastItemPosition.y, 0.0f));
1577       domainSize = fabs(firstItemPosition.y - lastItemPosition.y);
1578     }
1579
1580     mScrollConnector.SetScrollDomain(minLayoutPosition, 0.0f, domainSize);
1581
1582     bool isLayoutScrollable = IsLayoutScrollable(layoutSize);
1583     self.SetProperty(mPropertyCanScrollVertical, isLayoutScrollable);
1584     self.SetProperty(mPropertyCanScrollHorizontal, false);
1585   }
1586 }
1587
1588 Vector3 ItemView::GetDomainSize() const
1589 {
1590   Actor self = Self();
1591
1592   float minScrollPosition = self.GetProperty<float>(mPropertyPositionMin);
1593   float maxScrollPosition = self.GetProperty<float>(mPropertyPositionMax);
1594
1595   return Vector3(0.0f, fabs(maxScrollPosition - minScrollPosition), 0.0f);
1596 }
1597
1598 bool ItemView::IsLayoutScrollable(const Vector3& layoutSize)
1599 {
1600   Actor self = Self();
1601
1602   float currentLayoutPosition = ClampFirstItemPosition( GetCurrentLayoutPosition(0), layoutSize, *mActiveLayout );
1603   float forwardClampedPosition = ClampFirstItemPosition(currentLayoutPosition + 1.0, layoutSize, *mActiveLayout);
1604   float backwardClampedPosition = ClampFirstItemPosition(currentLayoutPosition - 1.0, layoutSize, *mActiveLayout);
1605
1606   return (fabs(forwardClampedPosition - backwardClampedPosition) > Math::MACHINE_EPSILON_0);
1607 }
1608
1609 float ItemView::GetScrollPosition(float layoutPosition, const Vector3& layoutSize) const
1610 {
1611   Vector3 firstItemPosition(Vector3::ZERO);
1612   ItemLayout::Vector3Function firstItemPositionConstraint;
1613   if (mActiveLayout->GetPositionConstraint(0, firstItemPositionConstraint))
1614   {
1615     firstItemPosition = firstItemPositionConstraint(Vector3::ZERO, layoutPosition, 0.0f, layoutSize);
1616   }
1617
1618   return IsHorizontal(mActiveLayout->GetOrientation()) ? firstItemPosition.x: firstItemPosition.y;
1619 }
1620
1621 Vector3 ItemView::GetCurrentScrollPosition() const
1622 {
1623   float currentLayoutPosition = GetCurrentLayoutPosition(0);
1624   return Vector3(0.0f, GetScrollPosition(currentLayoutPosition, Self().GetCurrentSize()), 0.0f);
1625 }
1626
1627 void ItemView::AddOverlay(Actor actor)
1628 {
1629   Self().Add(actor);
1630 }
1631
1632 void ItemView::RemoveOverlay(Actor actor)
1633 {
1634   Self().Remove(actor);
1635 }
1636
1637 void ItemView::ScrollTo(const Vector3& position, float duration)
1638 {
1639   Actor self = Self();
1640   const Vector3 layoutSize = Self().GetCurrentSize();
1641
1642   float firstItemScrollPosition = ClampFirstItemPosition(position.y, layoutSize, *mActiveLayout);
1643
1644   if(duration > 0.0f)
1645   {
1646     RemoveAnimation(mScrollAnimation);
1647     mScrollAnimation = Animation::New(duration);
1648     mScrollAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), firstItemScrollPosition, AlphaFunctions::EaseOut );
1649     mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, layoutSize), AlphaFunctions::EaseOut );
1650     mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1651     mScrollAnimation.Play();
1652   }
1653   else
1654   {
1655     mScrollPositionObject.SetProperty( ScrollConnector::SCROLL_POSITION, firstItemScrollPosition );
1656     AnimateScrollOvershoot(0.0f);
1657   }
1658
1659   mScrollStartedSignalV2.Emit(GetCurrentScrollPosition());
1660 }
1661
1662 void ItemView::ApplyOvershootOverlayConstraints()
1663 {
1664   Constraint constraint = Constraint::New<float>( Actor::SIZE_WIDTH,
1665                                                     ParentSource( mPropertyScrollDirection ),
1666                                                     Source( mScrollPositionObject, ScrollConnector::OVERSHOOT ),
1667                                                     ParentSource( Actor::SIZE ),
1668                                                     OvershootOverlaySizeConstraint() );
1669   mOvershootOverlay.ApplyConstraint(constraint);
1670   mOvershootOverlay.SetSize(OVERSHOOT_BOUNCE_IMAGE_1_PIXEL_AREA.width, OVERSHOOT_BOUNCE_IMAGE_1_PIXEL_AREA.height);
1671
1672   constraint = Constraint::New<Quaternion>( Actor::ROTATION,
1673                                             ParentSource( mPropertyScrollDirection ),
1674                                             Source( mScrollPositionObject, ScrollConnector::OVERSHOOT ),
1675                                             OvershootOverlayRotationConstraint() );
1676   mOvershootOverlay.ApplyConstraint(constraint);
1677
1678   constraint = Constraint::New<Vector3>( Actor::POSITION,
1679                                          ParentSource( Actor::SIZE ),
1680                                          ParentSource( mPropertyScrollDirection ),
1681                                          Source( mScrollPositionObject, ScrollConnector::OVERSHOOT ),
1682                                          OvershootOverlayPositionConstraint() );
1683   mOvershootOverlay.ApplyConstraint(constraint);
1684
1685   constraint = Constraint::New<bool>( Actor::VISIBLE,
1686                                       ParentSource( mPropertyCanScrollVertical ),
1687                                       OvershootOverlayVisibilityConstraint() );
1688   mOvershootOverlay.ApplyConstraint(constraint);
1689
1690   int effectOvershootPropertyIndex = mOvershootEffect.GetPropertyIndex(mOvershootEffect.GetOvershootPropertyName());
1691   Actor self = Self();
1692   constraint = Constraint::New<float>( effectOvershootPropertyIndex,
1693                                        Source( mScrollPositionObject, ScrollConnector::OVERSHOOT ),
1694                                        EqualToConstraint() );
1695   mOvershootEffect.ApplyConstraint(constraint);
1696 }
1697
1698 float ItemView::CalculateScrollOvershoot()
1699 {
1700   float overshoot = 0.0f;
1701
1702   if(mActiveLayout)
1703   {
1704     // The overshoot must be calculated from the accumulated pan gesture displacement
1705     // since the pan gesture starts.
1706     Actor self = Self();
1707     float scrollDistance = CalculateScrollDistance(mTotalPanDisplacement, *mActiveLayout) * mActiveLayout->GetScrollSpeedFactor();
1708     float positionDelta = GetCurrentLayoutPosition(0) + scrollDistance;
1709     float minLayoutPosition = mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), Self().GetCurrentSize());
1710     self.SetProperty(mPropertyMinimumLayoutPosition, minLayoutPosition);
1711     float clamppedPosition = min(0.0f, max(minLayoutPosition, positionDelta));
1712     overshoot = positionDelta - clamppedPosition;
1713   }
1714
1715   return overshoot;
1716 }
1717
1718 void ItemView::AnimateScrollOvershoot(float overshootAmount, bool animateBack)
1719 {
1720   bool animatingOn = fabsf(overshootAmount) > Math::MACHINE_EPSILON_1;
1721
1722   // make sure we animate back if needed
1723   mAnimateOvershootOff = animateBack || (!animatingOn && mAnimatingOvershootOn);
1724
1725   if( mAnimatingOvershootOn )
1726   {
1727     // animating on, do not allow animate off
1728     return;
1729   }
1730
1731   Actor self = Self();
1732   float currentOvershoot = mScrollPositionObject.GetProperty<float>(ScrollConnector::OVERSHOOT);
1733   float duration = DEFAULT_OVERSHOOT_ANIMATION_DURATION * (animatingOn ? (1.0f - fabsf(currentOvershoot)) : fabsf(currentOvershoot));
1734
1735   RemoveAnimation(mScrollOvershootAnimation);
1736   mScrollOvershootAnimation = Animation::New(duration);
1737   mScrollOvershootAnimation.FinishedSignal().Connect(this, &ItemView::OnOvershootOnFinished);
1738   mScrollOvershootAnimation.AnimateTo( Property(mScrollPositionObject, ScrollConnector::OVERSHOOT), overshootAmount, TimePeriod(0.0f, duration) );
1739   mScrollOvershootAnimation.Play();
1740
1741   mAnimatingOvershootOn = animatingOn;
1742 }
1743
1744 } // namespace Internal
1745
1746 } // namespace Toolkit
1747
1748 } // namespace Dali