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