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