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