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