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