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