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