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