[dali_2.3.20] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / scrollable / item-view / item-view-impl.cpp
1 /*
2  * Copyright (c) 2021 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 <dali/devel-api/actors/actor-devel.h>
23 #include <dali/devel-api/common/stage.h>
24 #include <dali/devel-api/object/property-helper-devel.h>
25 #include <dali/public-api/actors/layer.h>
26 #include <dali/public-api/animation/constraint.h>
27 #include <dali/public-api/animation/constraints.h>
28 #include <dali/public-api/events/touch-event.h>
29 #include <dali/public-api/events/wheel-event.h>
30 #include <dali/public-api/object/type-registry-helper.h>
31 #include <dali/public-api/object/type-registry.h>
32 #include <algorithm>
33 #include <cstring> // for strcmp
34
35 // INTERNAL INCLUDES
36 #include <dali-toolkit/devel-api/controls/scroll-bar/scroll-bar.h>
37 #include <dali-toolkit/internal/controls/scrollable/bouncing-effect-actor.h>
38 #include <dali-toolkit/internal/controls/scrollable/item-view/depth-layout.h>
39 #include <dali-toolkit/internal/controls/scrollable/item-view/grid-layout.h>
40 #include <dali-toolkit/internal/controls/scrollable/item-view/spiral-layout.h>
41 #include <dali-toolkit/public-api/controls/scrollable/item-view/default-item-layout-property.h>
42 #include <dali-toolkit/public-api/controls/scrollable/item-view/default-item-layout.h>
43 #include <dali-toolkit/public-api/controls/scrollable/item-view/item-factory.h>
44
45 using std::string;
46 using namespace Dali;
47
48 namespace // Unnamed namespace
49 {
50 const float DEFAULT_MINIMUM_SWIPE_SPEED                   = 1.0f;
51 const float DEFAULT_MINIMUM_SWIPE_DISTANCE                = 3.0f;
52 const float DEFAULT_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   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 float   OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD = 180.0f;
65 const Vector4 OVERSHOOT_OVERLAY_NINE_PATCH_BORDER(0.0f, 0.0f, 1.0f, 12.0f);
66 const float   DEFAULT_KEYBOARD_FOCUS_SCROLL_DURATION = 0.2f;
67
68 const unsigned int OVERSHOOT_SIZE_CONSTRAINT_TAG(42);
69
70 /**
71  * Local helper to convert pan distance (in actor coordinates) to the layout-specific scrolling direction
72  */
73 float CalculateScrollDistance(Vector2 panDistance, Toolkit::ItemLayout& layout)
74 {
75   Radian scrollDirection(layout.GetScrollDirection());
76
77   float cosTheta = cosf(scrollDirection);
78   float sinTheta = sinf(scrollDirection);
79
80   return panDistance.x * sinTheta + panDistance.y * cosTheta;
81 }
82
83 // Overshoot overlay constraints
84 struct OvershootOverlaySizeConstraint
85 {
86   OvershootOverlaySizeConstraint(float height)
87   : mOvershootHeight(height)
88   {
89   }
90
91   void operator()(Vector3& current, const PropertyInputContainer& inputs)
92   {
93     const Vector2&                           parentScrollDirection = inputs[0]->GetVector2();
94     const Toolkit::ControlOrientation::Type& layoutOrientation     = static_cast<Toolkit::ControlOrientation::Type>(inputs[1]->GetInteger());
95     const Vector3&                           parentSize            = inputs[2]->GetVector3();
96
97     if(Toolkit::IsVertical(layoutOrientation))
98     {
99       current.width = fabsf(parentScrollDirection.y) > Math::MACHINE_EPSILON_1 ? parentSize.x : parentSize.y;
100     }
101     else
102     {
103       current.width = fabsf(parentScrollDirection.x) > Math::MACHINE_EPSILON_1 ? parentSize.y : parentSize.x;
104     }
105
106     current.height = (current.width > OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD) ? mOvershootHeight : mOvershootHeight * 0.5f;
107   }
108
109   float mOvershootHeight;
110 };
111
112 void OvershootOverlayRotationConstraint(Quaternion& current, const PropertyInputContainer& inputs)
113 {
114   const Vector2&                           parentScrollDirection = inputs[0]->GetVector2();
115   const Toolkit::ControlOrientation::Type& layoutOrientation     = static_cast<Toolkit::ControlOrientation::Type>(inputs[1]->GetInteger());
116   const float                              parentOvershoot       = inputs[2]->GetFloat();
117
118   float multiplier = 0;
119   if(Toolkit::IsVertical(layoutOrientation))
120   {
121     if(fabsf(parentScrollDirection.y) <= Math::MACHINE_EPSILON_1)
122     {
123       if((layoutOrientation == Toolkit::ControlOrientation::Up && parentOvershoot < Math::MACHINE_EPSILON_0) || (layoutOrientation == Toolkit::ControlOrientation::Down && parentOvershoot > Math::MACHINE_EPSILON_0))
124       {
125         multiplier = 0.5f;
126       }
127       else
128       {
129         multiplier = 1.5f;
130       }
131     }
132     else if((parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.y > Math::MACHINE_EPSILON_0) || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.y < Math::MACHINE_EPSILON_0))
133     {
134       multiplier = 0.0f;
135     }
136     else
137     {
138       multiplier = 1.0f;
139     }
140   }
141   else
142   {
143     if(fabsf(parentScrollDirection.x) <= Math::MACHINE_EPSILON_1)
144     {
145       if((layoutOrientation == Toolkit::ControlOrientation::Left && parentOvershoot > Math::MACHINE_EPSILON_0) || (layoutOrientation == Toolkit::ControlOrientation::Right && parentOvershoot < Math::MACHINE_EPSILON_0))
146       {
147         multiplier = 1.0f;
148       }
149       else
150       {
151         multiplier = 0.0f;
152       }
153     }
154     else if((parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.x > Math::MACHINE_EPSILON_0) || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.x < Math::MACHINE_EPSILON_0))
155     {
156       multiplier = 1.5f;
157     }
158     else
159     {
160       multiplier = 0.5f;
161     }
162   }
163
164   current = Quaternion(Radian(multiplier * Math::PI), Vector3::ZAXIS);
165 }
166
167 void OvershootOverlayPositionConstraint(Vector3& current, const PropertyInputContainer& inputs)
168 {
169   const Vector3&                           parentSize            = inputs[0]->GetVector3();
170   const Vector2&                           parentScrollDirection = inputs[1]->GetVector2();
171   const Toolkit::ControlOrientation::Type& layoutOrientation     = static_cast<Toolkit::ControlOrientation::Type>(inputs[2]->GetInteger());
172   const float                              parentOvershoot       = inputs[3]->GetFloat();
173
174   Vector3 relativeOffset;
175
176   if(Toolkit::IsVertical(layoutOrientation))
177   {
178     if(fabsf(parentScrollDirection.y) <= Math::MACHINE_EPSILON_1)
179     {
180       if((layoutOrientation == Toolkit::ControlOrientation::Up && parentOvershoot < Math::MACHINE_EPSILON_0) || (layoutOrientation == Toolkit::ControlOrientation::Down && parentOvershoot > Math::MACHINE_EPSILON_0))
181       {
182         relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
183       }
184       else
185       {
186         relativeOffset = Vector3(0.0f, 1.0f, 0.0f);
187       }
188     }
189     else if((parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.y > Math::MACHINE_EPSILON_0) || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.y < Math::MACHINE_EPSILON_0))
190     {
191       relativeOffset = Vector3(0.0f, 0.0f, 0.0f);
192     }
193     else
194     {
195       relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
196     }
197   }
198   else
199   {
200     if(fabsf(parentScrollDirection.x) <= Math::MACHINE_EPSILON_1)
201     {
202       if((layoutOrientation == Toolkit::ControlOrientation::Left && parentOvershoot < Math::MACHINE_EPSILON_0) || (layoutOrientation == Toolkit::ControlOrientation::Right && parentOvershoot > Math::MACHINE_EPSILON_0))
203       {
204         relativeOffset = Vector3(0.0f, 0.0f, 0.0f);
205       }
206       else
207       {
208         relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
209       }
210     }
211     else if((parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.x > Math::MACHINE_EPSILON_0) || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.x < Math::MACHINE_EPSILON_0))
212     {
213       relativeOffset = Vector3(0.0f, 1.0f, 0.0f);
214     }
215     else
216     {
217       relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
218     }
219   }
220
221   current = relativeOffset * parentSize;
222 }
223
224 void OvershootOverlayVisibilityConstraint(bool& current, const PropertyInputContainer& inputs)
225 {
226   current = inputs[0]->GetBoolean();
227 }
228
229 } // unnamed namespace
230
231 namespace Dali
232 {
233 namespace Toolkit
234 {
235 namespace Internal
236 {
237 namespace // unnamed namespace
238 {
239 //Type registration
240 // clang-format off
241 DALI_TYPE_REGISTRATION_BEGIN(Toolkit::ItemView, Toolkit::Scrollable, nullptr)
242
243 DALI_PROPERTY_REGISTRATION(Toolkit, ItemView, "minimumSwipeSpeed",       FLOAT,   MINIMUM_SWIPE_SPEED       )
244 DALI_PROPERTY_REGISTRATION(Toolkit, ItemView, "minimumSwipeDistance",    FLOAT,   MINIMUM_SWIPE_DISTANCE    )
245 DALI_PROPERTY_REGISTRATION(Toolkit, ItemView, "wheelScrollDistanceStep", FLOAT,   WHEEL_SCROLL_DISTANCE_STEP)
246 DALI_PROPERTY_REGISTRATION(Toolkit, ItemView, "snapToItemEnabled",       BOOLEAN, SNAP_TO_ITEM_ENABLED      )
247 DALI_PROPERTY_REGISTRATION(Toolkit, ItemView, "refreshInterval",         FLOAT,   REFRESH_INTERVAL          )
248 DALI_PROPERTY_REGISTRATION(Toolkit, ItemView, "layout",                  ARRAY,   LAYOUT                    )
249
250
251 DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ItemView, "layoutPosition",    FLOAT,   LAYOUT_POSITION    )
252 DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ItemView, "scrollSpeed",       FLOAT,   SCROLL_SPEED       )
253 DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ItemView, "overshoot",         FLOAT,   OVERSHOOT          )
254 DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ItemView, "scrollDirection",   VECTOR2, SCROLL_DIRECTION   )
255 DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ItemView, "layoutOrientation", INTEGER, LAYOUT_ORIENTATION )
256 DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ItemView, "scrollContentSize", FLOAT,   SCROLL_CONTENT_SIZE)
257
258 DALI_SIGNAL_REGISTRATION(Toolkit, ItemView, "layoutActivated", LAYOUT_ACTIVATED_SIGNAL)
259
260 DALI_ACTION_REGISTRATION(Toolkit, ItemView, "stopScrolling", ACTION_STOP_SCROLLING)
261
262 DALI_ACTION_REGISTRATION(Toolkit, ItemView, "enableRefresh",  ACTION_ENABLE_REFRESH )
263 DALI_ACTION_REGISTRATION(Toolkit, ItemView, "disableRefresh", ACTION_DISABLE_REFRESH)
264
265 DALI_TYPE_REGISTRATION_END()
266 // clang-format on
267
268 const ItemIter FindItemById(ItemContainer& items, ItemId id)
269 {
270   for(ItemIter iter = items.begin(); items.end() != iter; ++iter)
271   {
272     if(iter->first == id)
273     {
274       return iter;
275     }
276   }
277
278   return items.end();
279 }
280
281 void InsertToItemContainer(ItemContainer& items, Item item)
282 {
283   if(items.end() == FindItemById(items, item.first))
284   {
285     ItemIter iterToInsert = std::lower_bound(items.begin(), items.end(), item);
286     items.insert(iterToInsert, item);
287   }
288 }
289
290 /**
291   * Helper to apply size constraint to mOvershootOverlay
292   * @param[in] overshootOverlay The overshootOverlay actor
293   * @param[in] The required height
294   */
295 void ApplyOvershootSizeConstraint(Actor overshootOverlay, float height)
296 {
297   Constraint constraint = Constraint::New<Vector3>(overshootOverlay, Actor::Property::SIZE, OvershootOverlaySizeConstraint(height));
298   constraint.AddSource(ParentSource(Dali::Toolkit::ItemView::Property::SCROLL_DIRECTION));
299   constraint.AddSource(ParentSource(Dali::Toolkit::ItemView::Property::LAYOUT_ORIENTATION));
300   constraint.AddSource(ParentSource(Dali::Actor::Property::SIZE));
301   constraint.SetTag(OVERSHOOT_SIZE_CONSTRAINT_TAG);
302   constraint.Apply();
303 }
304
305 } // unnamed namespace
306
307 Dali::Toolkit::ItemView ItemView::New(ItemFactory& factory)
308 {
309   // Create the implementation
310   ItemViewPtr itemView(new ItemView(factory));
311
312   // Pass ownership to CustomActor via derived handle
313   Dali::Toolkit::ItemView handle(*itemView);
314
315   // Second-phase init of the implementation
316   // This can only be done after the CustomActor connection has been made...
317   itemView->Initialize();
318
319   return handle;
320 }
321
322 ItemView::ItemView(ItemFactory& factory)
323 : Scrollable(ControlBehaviour(DISABLE_SIZE_NEGOTIATION | DISABLE_STYLE_CHANGE_SIGNALS | REQUIRES_KEYBOARD_NAVIGATION_SUPPORT)),
324   mItemFactory(factory),
325   mItemsParentOrigin(ParentOrigin::CENTER),
326   mItemsAnchorPoint(AnchorPoint::CENTER),
327   mTotalPanDisplacement(Vector2::ZERO),
328   mActiveLayout(NULL),
329   mAnchoringDuration(DEFAULT_ANCHORING_DURATION),
330   mRefreshIntervalLayoutPositions(0.0f),
331   mMinimumSwipeSpeed(DEFAULT_MINIMUM_SWIPE_SPEED),
332   mMinimumSwipeDistance(DEFAULT_MINIMUM_SWIPE_DISTANCE),
333   mWheelScrollDistanceStep(0.0f),
334   mScrollDistance(0.0f),
335   mScrollSpeed(0.0f),
336   mScrollOvershoot(0.0f),
337   mGestureState(GestureState::CLEAR),
338   mAnimatingOvershootOn(false),
339   mAnimateOvershootOff(false),
340   mAnchoringEnabled(false),
341   mRefreshOrderHint(true /*Refresh item 0 first*/),
342   mIsFlicking(false),
343   mAddingItems(false),
344   mRefreshEnabled(true),
345   mRefreshNotificationEnabled(true),
346   mInAnimation(false)
347 {
348 }
349
350 void ItemView::OnInitialize()
351 {
352   Scrollable::OnInitialize();
353
354   Actor self = Self();
355
356   Vector2 stageSize        = Stage::GetCurrent().GetSize();
357   mWheelScrollDistanceStep = stageSize.y * DEFAULT_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION;
358
359   self.TouchedSignal().Connect(this, &ItemView::OnTouch);
360   EnableGestureDetection(GestureType::Value(GestureType::PAN));
361
362   mWheelEventFinishedTimer = Timer::New(WHEEL_EVENT_FINISHED_TIME_OUT);
363   mWheelEventFinishedTimer.TickSignal().Connect(this, &ItemView::OnWheelEventFinished);
364
365   SetRefreshInterval(DEFAULT_REFRESH_INTERVAL_LAYOUT_POSITIONS);
366
367   // Connect wheel event
368   self.WheelEventSignal().Connect(this, &ItemView::OnWheelEvent);
369
370   self.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, Dali::Accessibility::Role::SCROLL_PANE);
371 }
372
373 DevelControl::ControlAccessible* ItemView::CreateAccessibleObject()
374 {
375   return new ItemViewAccessible(Self());
376 }
377
378 ItemView::~ItemView()
379 {
380 }
381
382 unsigned int ItemView::GetLayoutCount() const
383 {
384   return mLayouts.size();
385 }
386
387 void ItemView::AddLayout(ItemLayout& layout)
388 {
389   mLayouts.push_back(ItemLayoutPtr(&layout));
390 }
391
392 void ItemView::RemoveLayout(unsigned int layoutIndex)
393 {
394   DALI_ASSERT_ALWAYS(layoutIndex < mLayouts.size());
395
396   if(mActiveLayout == mLayouts[layoutIndex].Get())
397   {
398     mActiveLayout = NULL;
399   }
400
401   mLayouts.erase(mLayouts.begin() + layoutIndex);
402 }
403
404 ItemLayoutPtr ItemView::GetLayout(unsigned int layoutIndex) const
405 {
406   return mLayouts[layoutIndex];
407 }
408
409 ItemLayoutPtr ItemView::GetActiveLayout() const
410 {
411   return ItemLayoutPtr(mActiveLayout);
412 }
413
414 float ItemView::GetCurrentLayoutPosition(unsigned int itemId) const
415 {
416   return Self().GetCurrentProperty<float>(Toolkit::ItemView::Property::LAYOUT_POSITION) + static_cast<float>(itemId);
417 }
418
419 void ItemView::ActivateLayout(unsigned int layoutIndex, const Vector3& targetSize, float durationSeconds)
420 {
421   DALI_ASSERT_ALWAYS(layoutIndex < mLayouts.size());
422
423   mRefreshEnabled = false;
424
425   Actor self = Self();
426
427   // The ItemView size should match the active layout size
428   self.SetProperty(Actor::Property::SIZE, targetSize);
429   mActiveLayoutTargetSize = targetSize;
430
431   // Switch to the new layout
432   mActiveLayout = mLayouts[layoutIndex].Get();
433
434   // Move the items to the new layout positions...
435
436   for(ConstItemIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
437   {
438     unsigned int itemId = iter->first;
439     Actor        actor  = iter->second;
440
441     // Remove constraints from previous layout
442     actor.RemoveConstraints();
443
444     mActiveLayout->ApplyConstraints(actor, itemId, targetSize, Self());
445
446     Vector3 size;
447     mActiveLayout->GetItemSize(itemId, targetSize, size);
448     actor.SetProperty(Actor::Property::SIZE, size.GetVectorXY());
449   }
450
451   // Refresh the new layout
452   ItemRange range = GetItemRange(*mActiveLayout, targetSize, GetCurrentLayoutPosition(0), false /* don't reserve extra*/);
453   AddActorsWithinRange(range, targetSize);
454
455   // Scroll to an appropriate layout position
456
457   bool  scrollAnimationNeeded(false);
458   float firstItemScrollPosition(0.0f);
459
460   float current = GetCurrentLayoutPosition(0);
461   float minimum = ClampFirstItemPosition(current, targetSize, *mActiveLayout);
462
463   if(current < minimum)
464   {
465     scrollAnimationNeeded   = true;
466     firstItemScrollPosition = minimum;
467   }
468   else if(mAnchoringEnabled)
469   {
470     scrollAnimationNeeded   = true;
471     firstItemScrollPosition = mActiveLayout->GetClosestAnchorPosition(current);
472   }
473
474   if(scrollAnimationNeeded)
475   {
476     RemoveAnimation(mScrollAnimation);
477     mScrollAnimation = Animation::New(durationSeconds);
478     mScrollAnimation.AnimateTo(Property(self, Toolkit::ItemView::Property::LAYOUT_POSITION), firstItemScrollPosition, AlphaFunction::EASE_OUT);
479     mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnLayoutActivationScrollFinished);
480     mScrollAnimation.Play();
481   }
482   else
483   {
484     // Emit the layout activated signal
485     mLayoutActivatedSignal.Emit();
486   }
487
488   AnimateScrollOvershoot(0.0f);
489   mScrollOvershoot = 0.0f;
490
491   Radian scrollDirection(mActiveLayout->GetScrollDirection());
492   self.SetProperty(Toolkit::ItemView::Property::SCROLL_DIRECTION, Vector2(sinf(scrollDirection), cosf(scrollDirection)));
493   self.SetProperty(Toolkit::ItemView::Property::LAYOUT_ORIENTATION, static_cast<int>(mActiveLayout->GetOrientation()));
494   self.SetProperty(Toolkit::ItemView::Property::SCROLL_SPEED, mScrollSpeed);
495
496   CalculateDomainSize(targetSize);
497 }
498
499 void ItemView::DeactivateCurrentLayout()
500 {
501   if(mActiveLayout)
502   {
503     for(ConstItemIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
504     {
505       Actor actor = iter->second;
506       actor.RemoveConstraints();
507     }
508
509     mActiveLayout = NULL;
510   }
511 }
512
513 void ItemView::OnRefreshNotification(PropertyNotification& source)
514 {
515   if(mRefreshNotificationEnabled)
516   {
517     // Cancel scroll animation to prevent any fighting of setting the scroll position property by scroll bar during fast scroll.
518     if(!mRefreshEnabled && mScrollAnimation)
519     {
520       RemoveAnimation(mScrollAnimation);
521     }
522
523     // Only cache extra items when it is not a fast scroll
524     DoRefresh(GetCurrentLayoutPosition(0), mRefreshEnabled || mScrollAnimation);
525   }
526 }
527
528 void ItemView::Refresh()
529 {
530   for(ConstItemIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
531   {
532     ReleaseActor(iter->first, iter->second);
533   }
534   mItemPool.clear();
535
536   DoRefresh(GetCurrentLayoutPosition(0), true);
537 }
538
539 void ItemView::DoRefresh(float currentLayoutPosition, bool cacheExtra)
540 {
541   if(mActiveLayout)
542   {
543     ItemRange range = GetItemRange(*mActiveLayout, mActiveLayoutTargetSize, currentLayoutPosition, cacheExtra /*reserve extra*/);
544     RemoveActorsOutsideRange(range);
545     AddActorsWithinRange(range, Self().GetCurrentProperty<Vector3>(Actor::Property::SIZE));
546
547     mScrollUpdatedSignal.Emit(Vector2(0.0f, currentLayoutPosition));
548   }
549 }
550
551 void ItemView::SetMinimumSwipeSpeed(float speed)
552 {
553   mMinimumSwipeSpeed = speed;
554 }
555
556 float ItemView::GetMinimumSwipeSpeed() const
557 {
558   return mMinimumSwipeSpeed;
559 }
560
561 void ItemView::SetMinimumSwipeDistance(float distance)
562 {
563   mMinimumSwipeDistance = distance;
564 }
565
566 float ItemView::GetMinimumSwipeDistance() const
567 {
568   return mMinimumSwipeDistance;
569 }
570
571 void ItemView::SetWheelScrollDistanceStep(float step)
572 {
573   mWheelScrollDistanceStep = step;
574 }
575
576 float ItemView::GetWheelScrollDistanceStep() const
577 {
578   return mWheelScrollDistanceStep;
579 }
580
581 void ItemView::SetAnchoring(bool enabled)
582 {
583   mAnchoringEnabled = enabled;
584 }
585
586 bool ItemView::GetAnchoring() const
587 {
588   return mAnchoringEnabled;
589 }
590
591 void ItemView::SetAnchoringDuration(float durationSeconds)
592 {
593   mAnchoringDuration = durationSeconds;
594 }
595
596 float ItemView::GetAnchoringDuration() const
597 {
598   return mAnchoringDuration;
599 }
600
601 void ItemView::SetRefreshInterval(float intervalLayoutPositions)
602 {
603   if(!Equals(mRefreshIntervalLayoutPositions, intervalLayoutPositions))
604   {
605     mRefreshIntervalLayoutPositions = intervalLayoutPositions;
606
607     Actor self = Self();
608     if(mRefreshNotification)
609     {
610       self.RemovePropertyNotification(mRefreshNotification);
611     }
612     mRefreshNotification = self.AddPropertyNotification(Toolkit::ItemView::Property::LAYOUT_POSITION, StepCondition(mRefreshIntervalLayoutPositions, 0.0f));
613     mRefreshNotification.NotifySignal().Connect(this, &ItemView::OnRefreshNotification);
614   }
615 }
616
617 float ItemView::GetRefreshInterval() const
618 {
619   return mRefreshIntervalLayoutPositions;
620 }
621
622 void ItemView::SetRefreshEnabled(bool enabled)
623 {
624   mRefreshEnabled = enabled;
625 }
626
627 Actor ItemView::GetItem(unsigned int itemId) const
628 {
629   Actor actor;
630
631   for(ConstItemIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
632   {
633     if(iter->first == itemId)
634     {
635       actor = iter->second;
636       break;
637     }
638   }
639
640   return actor;
641 }
642
643 unsigned int ItemView::GetItemId(Actor actor) const
644 {
645   unsigned int itemId(0);
646
647   for(ConstItemIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
648   {
649     if(iter->second == actor)
650     {
651       itemId = iter->first;
652       break;
653     }
654   }
655
656   return itemId;
657 }
658
659 void ItemView::InsertItem(Item newItem, float durationSeconds)
660 {
661   mAddingItems       = true;
662   Vector3 layoutSize = Self().GetCurrentProperty<Vector3>(Actor::Property::SIZE);
663
664   Actor    displacedActor;
665   ItemIter afterDisplacedIter = mItemPool.end();
666
667   ItemIter foundIter = FindItemById(mItemPool, newItem.first);
668   if(mItemPool.end() != foundIter)
669   {
670     SetupActor(newItem, layoutSize);
671     Self().Add(newItem.second);
672
673     displacedActor    = foundIter->second;
674     foundIter->second = newItem.second;
675
676     afterDisplacedIter = ++foundIter;
677   }
678   else
679   {
680     // Inserting before the existing item range?
681     ItemIter iter = mItemPool.begin();
682     if(iter != mItemPool.end() &&
683        iter->first > newItem.first)
684     {
685       displacedActor = iter->second;
686       iter           = mItemPool.erase(iter); // iter is still valid after the erase
687
688       afterDisplacedIter = iter;
689     }
690   }
691
692   if(displacedActor)
693   {
694     // Move the existing actors to make room
695     for(ItemIter iter = afterDisplacedIter; mItemPool.end() != iter; ++iter)
696     {
697       Actor temp     = iter->second;
698       iter->second   = displacedActor;
699       displacedActor = temp;
700
701       iter->second.RemoveConstraints();
702       mActiveLayout->ApplyConstraints(iter->second, iter->first, layoutSize, Self());
703     }
704
705     // Create last item
706     ItemContainer::reverse_iterator lastIter = mItemPool.rbegin();
707     if(lastIter != mItemPool.rend())
708     {
709       ItemId lastId = lastIter->first;
710       Item   lastItem(lastId + 1, displacedActor);
711       InsertToItemContainer(mItemPool, lastItem);
712
713       lastItem.second.RemoveConstraints();
714       mActiveLayout->ApplyConstraints(lastItem.second, lastItem.first, layoutSize, Self());
715     }
716   }
717
718   CalculateDomainSize(layoutSize);
719
720   mAddingItems = false;
721 }
722
723 void ItemView::InsertItems(const ItemContainer& newItems, float durationSeconds)
724 {
725   mAddingItems       = true;
726   Vector3 layoutSize = Self().GetCurrentProperty<Vector3>(Actor::Property::SIZE);
727
728   // Insert from lowest id to highest
729   ItemContainer sortedItems(newItems);
730   std::sort(sortedItems.begin(), sortedItems.end());
731
732   for(ItemIter iter = sortedItems.begin(); sortedItems.end() != iter; ++iter)
733   {
734     Self().Add(iter->second);
735
736     ItemIter foundIter = FindItemById(mItemPool, iter->first);
737     if(mItemPool.end() != foundIter)
738     {
739       Actor moveMe      = foundIter->second;
740       foundIter->second = iter->second;
741
742       // Move the existing actors to make room
743       for(ItemIter iter = ++foundIter; mItemPool.end() != iter; ++iter)
744       {
745         Actor temp   = iter->second;
746         iter->second = moveMe;
747         moveMe       = temp;
748       }
749
750       // Create last item
751       ItemId lastId = mItemPool.rbegin()->first;
752       Item   lastItem(lastId + 1, moveMe);
753       InsertToItemContainer(mItemPool, lastItem);
754     }
755     else
756     {
757       InsertToItemContainer(mItemPool, *iter);
758     }
759   }
760
761   // Relayout everything
762   for(ItemIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
763   {
764     // If newly inserted
765     if(std::binary_search(sortedItems.begin(), sortedItems.end(), *iter))
766     {
767       SetupActor(*iter, layoutSize);
768     }
769     else
770     {
771       iter->second.RemoveConstraints();
772       mActiveLayout->ApplyConstraints(iter->second, iter->first, layoutSize, Self());
773     }
774   }
775
776   CalculateDomainSize(layoutSize);
777
778   mAddingItems = false;
779 }
780
781 void ItemView::RemoveItem(unsigned int itemId, float durationSeconds)
782 {
783   bool actorsReordered = RemoveActor(itemId);
784   if(actorsReordered)
785   {
786     ReapplyAllConstraints();
787
788     OnItemsRemoved();
789   }
790 }
791
792 void ItemView::RemoveItems(const ItemIdContainer& itemIds, float durationSeconds)
793 {
794   bool actorsReordered(false);
795
796   // Remove from highest id to lowest
797   ItemIdContainer sortedItems(itemIds);
798   std::sort(sortedItems.begin(), sortedItems.end());
799
800   for(ItemIdContainer::reverse_iterator iter = sortedItems.rbegin(); sortedItems.rend() != iter; ++iter)
801   {
802     if(RemoveActor(*iter))
803     {
804       actorsReordered = true;
805     }
806   }
807
808   if(actorsReordered)
809   {
810     ReapplyAllConstraints();
811
812     OnItemsRemoved();
813   }
814 }
815
816 bool ItemView::RemoveActor(unsigned int itemId)
817 {
818   bool reordered(false);
819
820   ItemIter removeIter = FindItemById(mItemPool, itemId);
821   if(removeIter != mItemPool.end())
822   {
823     ReleaseActor(itemId, removeIter->second);
824   }
825   else
826   {
827     // Removing before the existing item range?
828     ItemIter iter = mItemPool.begin();
829     if(iter != mItemPool.end() &&
830        iter->first > itemId)
831     {
832       // In order to decrement the first visible item ID
833       InsertToItemContainer(mItemPool, Item(iter->first - 1, Actor()));
834
835       removeIter = mItemPool.begin();
836     }
837   }
838
839   if(removeIter != mItemPool.end())
840   {
841     reordered = true;
842
843     // Adjust the remaining item IDs, for example if item 2 is removed:
844     //   Initial actors:     After insert:
845     //     ID 1 - ActorA       ID 1 - ActorA
846     //     ID 2 - ActorB       ID 2 - ActorC (previously ID 3)
847     //     ID 3 - ActorC       ID 3 - ActorB (previously ID 4)
848     //     ID 4 - ActorD
849     for(ItemIter iter = removeIter; iter != mItemPool.end(); ++iter)
850     {
851       if(iter->first < mItemPool.rbegin()->first)
852       {
853         iter->second = (iter + 1)->second;
854       }
855       else
856       {
857         mItemPool.erase(iter);
858         break;
859       }
860     }
861   }
862
863   return reordered;
864 }
865
866 void ItemView::ReplaceItem(Item replacementItem, float durationSeconds)
867 {
868   mAddingItems       = true;
869   Vector3 layoutSize = Self().GetCurrentProperty<Vector3>(Actor::Property::SIZE);
870
871   SetupActor(replacementItem, layoutSize);
872   Self().Add(replacementItem.second);
873
874   const ItemIter iter = FindItemById(mItemPool, replacementItem.first);
875   if(mItemPool.end() != iter)
876   {
877     ReleaseActor(iter->first, iter->second);
878     iter->second = replacementItem.second;
879   }
880   else
881   {
882     InsertToItemContainer(mItemPool, replacementItem);
883   }
884
885   CalculateDomainSize(layoutSize);
886
887   mAddingItems = false;
888 }
889
890 void ItemView::ReplaceItems(const ItemContainer& replacementItems, float durationSeconds)
891 {
892   for(ConstItemIter iter = replacementItems.begin(); replacementItems.end() != iter; ++iter)
893   {
894     ReplaceItem(*iter, durationSeconds);
895   }
896 }
897
898 void ItemView::RemoveActorsOutsideRange(ItemRange range)
899 {
900   // Remove unwanted actors from the ItemView & ItemPool
901   for(ItemIter iter = mItemPool.begin(); iter != mItemPool.end();)
902   {
903     unsigned int current = iter->first;
904
905     if(!range.Within(current))
906     {
907       ReleaseActor(iter->first, iter->second);
908
909       iter = mItemPool.erase(iter); // iter is still valid after the erase
910     }
911     else
912     {
913       ++iter;
914     }
915   }
916 }
917
918 void ItemView::AddActorsWithinRange(ItemRange range, const Vector3& layoutSize)
919 {
920   range.end = std::min(mItemFactory.GetNumberOfItems(), range.end);
921
922   // The order of addition depends on the scroll direction.
923   if(mRefreshOrderHint)
924   {
925     for(unsigned int itemId = range.begin; itemId < range.end; ++itemId)
926     {
927       AddNewActor(itemId, layoutSize);
928     }
929   }
930   else
931   {
932     for(unsigned int itemId = range.end; itemId > range.begin; --itemId)
933     {
934       AddNewActor(itemId - 1, layoutSize);
935     }
936   }
937
938   // Total number of items may change dynamically.
939   // Always recalculate the domain size to reflect that.
940   CalculateDomainSize(Self().GetCurrentProperty<Vector3>(Actor::Property::SIZE));
941 }
942
943 void ItemView::AddNewActor(unsigned int itemId, const Vector3& layoutSize)
944 {
945   mAddingItems = true;
946
947   if(mItemPool.end() == FindItemById(mItemPool, itemId))
948   {
949     Actor actor = mItemFactory.NewItem(itemId);
950
951     if(actor)
952     {
953       Item newItem(itemId, actor);
954
955       InsertToItemContainer(mItemPool, newItem);
956
957       SetupActor(newItem, layoutSize);
958       Self().Add(actor);
959     }
960   }
961
962   mAddingItems = false;
963 }
964
965 void ItemView::SetupActor(Item item, const Vector3& layoutSize)
966 {
967   item.second.SetProperty(Actor::Property::PARENT_ORIGIN, mItemsParentOrigin);
968   item.second.SetProperty(Actor::Property::ANCHOR_POINT, mItemsAnchorPoint);
969
970   if(mActiveLayout)
971   {
972     Vector3 size;
973     mActiveLayout->GetItemSize(item.first, mActiveLayoutTargetSize, size);
974     item.second.SetProperty(Actor::Property::SIZE, size.GetVectorXY());
975
976     mActiveLayout->ApplyConstraints(item.second, item.first, layoutSize, Self());
977   }
978 }
979
980 void ItemView::ReleaseActor(ItemId item, Actor actor)
981 {
982   Self().Remove(actor);
983   mItemFactory.ItemReleased(item, actor);
984 }
985
986 ItemRange ItemView::GetItemRange(ItemLayout& layout, const Vector3& layoutSize, float layoutPosition, bool reserveExtra)
987 {
988   unsigned int itemCount = mItemFactory.GetNumberOfItems();
989
990   ItemRange available(0u, itemCount);
991
992   ItemRange range = layout.GetItemsWithinArea(layoutPosition, layoutSize);
993
994   if(reserveExtra)
995   {
996     // Add the reserve items for scrolling
997     unsigned int extra = layout.GetReserveItemCount(layoutSize);
998     range.begin        = (range.begin >= extra) ? (range.begin - extra) : 0u;
999     range.end += extra;
1000   }
1001
1002   return range.Intersection(available);
1003 }
1004
1005 void ItemView::OnChildAdd(Actor& child)
1006 {
1007   if(!mAddingItems)
1008   {
1009     // We don't want to do this downcast check for any item added by ItemView itself.
1010     Dali::Toolkit::ScrollBar scrollBar = Dali::Toolkit::ScrollBar::DownCast(child);
1011     if(scrollBar)
1012     {
1013       scrollBar.SetScrollPropertySource(Self(),
1014                                         Toolkit::ItemView::Property::LAYOUT_POSITION,
1015                                         Toolkit::Scrollable::Property::SCROLL_POSITION_MIN_Y,
1016                                         Toolkit::Scrollable::Property::SCROLL_POSITION_MAX_Y,
1017                                         Toolkit::ItemView::Property::SCROLL_CONTENT_SIZE);
1018     }
1019   }
1020
1021   Scrollable::OnChildAdd(child);
1022 }
1023
1024 bool ItemView::OnWheelEvent(Actor actor, const WheelEvent& event)
1025 {
1026   // Respond the wheel event to scroll
1027   if(mActiveLayout)
1028   {
1029     Actor         self                    = Self();
1030     const Vector3 layoutSize              = Self().GetCurrentProperty<Vector3>(Actor::Property::SIZE);
1031     float         layoutPositionDelta     = GetCurrentLayoutPosition(0) - (event.GetDelta() * mWheelScrollDistanceStep * mActiveLayout->GetScrollSpeedFactor());
1032     float         firstItemScrollPosition = ClampFirstItemPosition(layoutPositionDelta, layoutSize, *mActiveLayout);
1033
1034     self.SetProperty(Toolkit::ItemView::Property::LAYOUT_POSITION, firstItemScrollPosition);
1035
1036     mScrollStartedSignal.Emit(GetCurrentScrollPosition());
1037     mRefreshEnabled = true;
1038   }
1039
1040   if(mWheelEventFinishedTimer.IsRunning())
1041   {
1042     mWheelEventFinishedTimer.Stop();
1043   }
1044
1045   mWheelEventFinishedTimer.Start();
1046
1047   return true;
1048 }
1049
1050 bool ItemView::OnWheelEventFinished()
1051 {
1052   if(mActiveLayout)
1053   {
1054     RemoveAnimation(mScrollAnimation);
1055
1056     // No more wheel events coming. Do the anchoring if enabled.
1057     mScrollAnimation = DoAnchoring();
1058     if(mScrollAnimation)
1059     {
1060       mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1061       mScrollAnimation.Play();
1062     }
1063     else
1064     {
1065       mScrollOvershoot = 0.0f;
1066       AnimateScrollOvershoot(0.0f);
1067
1068       mScrollCompletedSignal.Emit(GetCurrentScrollPosition());
1069     }
1070   }
1071
1072   return false;
1073 }
1074
1075 void ItemView::ReapplyAllConstraints()
1076 {
1077   Vector3 layoutSize = Self().GetCurrentProperty<Vector3>(Actor::Property::SIZE);
1078
1079   for(ConstItemIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
1080   {
1081     unsigned int id    = iter->first;
1082     Actor        actor = iter->second;
1083
1084     actor.RemoveConstraints();
1085     mActiveLayout->ApplyConstraints(actor, id, layoutSize, Self());
1086   }
1087 }
1088
1089 void ItemView::OnItemsRemoved()
1090 {
1091   CalculateDomainSize(Self().GetCurrentProperty<Vector3>(Actor::Property::SIZE));
1092
1093   // Adjust scroll-position after an item is removed
1094   if(mActiveLayout)
1095   {
1096     float firstItemScrollPosition = ClampFirstItemPosition(GetCurrentLayoutPosition(0), Self().GetCurrentProperty<Vector3>(Actor::Property::SIZE), *mActiveLayout);
1097     Self().SetProperty(Toolkit::ItemView::Property::LAYOUT_POSITION, firstItemScrollPosition);
1098   }
1099 }
1100
1101 float ItemView::ClampFirstItemPosition(float targetPosition, const Vector3& targetSize, ItemLayout& layout, bool updateOvershoot)
1102 {
1103   Actor self              = Self();
1104   float minLayoutPosition = layout.GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), targetSize);
1105   float clamppedPosition  = std::min(0.0f, std::max(minLayoutPosition, targetPosition));
1106   self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MAX, Vector2(0.0f, -minLayoutPosition));
1107
1108   if(updateOvershoot)
1109   {
1110     mScrollOvershoot = targetPosition - clamppedPosition;
1111   }
1112
1113   return clamppedPosition;
1114 }
1115
1116 bool ItemView::OnTouch(Actor actor, const TouchEvent& touch)
1117 {
1118   // Ignore events with multiple-touch points
1119   if(touch.GetPointCount() != 1)
1120   {
1121     return false;
1122   }
1123
1124   if(touch.GetState(0) == PointState::DOWN)
1125   {
1126     // Cancel ongoing scrolling etc.
1127     mGestureState = GestureState::CLEAR;
1128
1129     mScrollDistance = 0.0f;
1130     mScrollSpeed    = 0.0f;
1131     Self().SetProperty(Toolkit::ItemView::Property::SCROLL_SPEED, mScrollSpeed);
1132
1133     mScrollOvershoot = 0.0f;
1134     AnimateScrollOvershoot(0.0f);
1135
1136     if(mScrollAnimation)
1137     {
1138       mScrollCompletedSignal.Emit(GetCurrentScrollPosition());
1139     }
1140
1141     RemoveAnimation(mScrollAnimation);
1142   }
1143
1144   return false; // Do not consume as we're potentially scrolling (detecting pan gestures)
1145 }
1146
1147 void ItemView::OnPan(const PanGesture& gesture)
1148 {
1149   Actor         self       = Self();
1150   const Vector3 layoutSize = Self().GetCurrentProperty<Vector3>(Actor::Property::SIZE);
1151
1152   RemoveAnimation(mScrollAnimation);
1153
1154   // Short-circuit if there is no active layout
1155   if(!mActiveLayout)
1156   {
1157     mGestureState = GestureState::CLEAR;
1158     return;
1159   }
1160
1161   mGestureState = gesture.GetState();
1162
1163   switch(mGestureState)
1164   {
1165     case GestureState::FINISHED:
1166     {
1167       // Swipe Detection
1168       if(fabsf(mScrollDistance) > mMinimumSwipeDistance &&
1169          mScrollSpeed > mMinimumSwipeSpeed)
1170       {
1171         float direction = (mScrollDistance < 0.0f) ? -1.0f : 1.0f;
1172
1173         mRefreshOrderHint = true;
1174
1175         float currentLayoutPosition   = GetCurrentLayoutPosition(0);
1176         float firstItemScrollPosition = ClampFirstItemPosition(currentLayoutPosition + mScrollSpeed * direction,
1177                                                                layoutSize,
1178                                                                *mActiveLayout);
1179
1180         if(mAnchoringEnabled)
1181         {
1182           firstItemScrollPosition = mActiveLayout->GetClosestAnchorPosition(firstItemScrollPosition);
1183         }
1184
1185         RemoveAnimation(mScrollAnimation);
1186
1187         float flickAnimationDuration = Clamp(mActiveLayout->GetItemFlickAnimationDuration() * std::max(1.0f, fabsf(firstItemScrollPosition - GetCurrentLayoutPosition(0))), DEFAULT_MINIMUM_SWIPE_DURATION, DEFAULT_MAXIMUM_SWIPE_DURATION);
1188
1189         mScrollAnimation = Animation::New(flickAnimationDuration);
1190         mScrollAnimation.AnimateTo(Property(self, Toolkit::ItemView::Property::LAYOUT_POSITION), firstItemScrollPosition, AlphaFunction::EASE_OUT);
1191         mScrollAnimation.AnimateTo(Property(self, Toolkit::ItemView::Property::SCROLL_SPEED), 0.0f, AlphaFunction::EASE_OUT);
1192
1193         mIsFlicking = true;
1194
1195         // Check whether it has already scrolled to the end
1196         if(fabs(currentLayoutPosition - firstItemScrollPosition) < Math::MACHINE_EPSILON_0)
1197         {
1198           AnimateScrollOvershoot(0.0f);
1199           RemoveAnimation(mScrollAnimation);
1200         }
1201       }
1202
1203       // Anchoring may be triggered when there was no swipe
1204       if(!mScrollAnimation)
1205       {
1206         mScrollAnimation = DoAnchoring();
1207       }
1208
1209       // Reset the overshoot if no scroll animation.
1210       if(!mScrollAnimation)
1211       {
1212         mScrollCompletedSignal.Emit(GetCurrentScrollPosition());
1213
1214         AnimateScrollOvershoot(0.0f, false);
1215       }
1216     }
1217     break;
1218
1219     case GestureState::STARTED: // Fall through
1220     {
1221       mTotalPanDisplacement = Vector2::ZERO;
1222       mScrollStartedSignal.Emit(GetCurrentScrollPosition());
1223       mRefreshEnabled = true;
1224     }
1225
1226     case GestureState::CONTINUING:
1227     {
1228       const Vector2& displacement = gesture.GetDisplacement();
1229       mScrollDistance             = CalculateScrollDistance(displacement, *mActiveLayout);
1230       mScrollSpeed                = Clamp((gesture.GetSpeed() * gesture.GetSpeed() * mActiveLayout->GetFlickSpeedFactor() * MILLISECONDS_PER_SECONDS), 0.0f, mActiveLayout->GetMaximumSwipeSpeed());
1231
1232       // Refresh order depends on the direction of the scroll; negative is towards the last item.
1233       mRefreshOrderHint = mScrollDistance < 0.0f;
1234
1235       float layoutPositionDelta = GetCurrentLayoutPosition(0) + (mScrollDistance * mActiveLayout->GetScrollSpeedFactor());
1236
1237       float firstItemScrollPosition = ClampFirstItemPosition(layoutPositionDelta, layoutSize, *mActiveLayout);
1238
1239       float currentOvershoot = self.GetCurrentProperty<float>(Toolkit::ItemView::Property::OVERSHOOT);
1240
1241       self.SetProperty(Toolkit::ItemView::Property::LAYOUT_POSITION, firstItemScrollPosition);
1242
1243       if((firstItemScrollPosition >= 0.0f &&
1244           currentOvershoot < 1.0f) ||
1245          (firstItemScrollPosition <= mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), layoutSize) &&
1246           currentOvershoot > -1.0f))
1247       {
1248         mTotalPanDisplacement += displacement;
1249       }
1250
1251       mScrollOvershoot = CalculateScrollOvershoot();
1252
1253       // If the view is moved in a direction against the overshoot indicator, then the indicator should be animated off.
1254       // First make sure we are not in an animation, otherwise a previously started
1255       // off-animation will be overwritten as the user continues scrolling.
1256       if(!mInAnimation)
1257       {
1258         // Check if the movement is against the current overshoot amount (if we are currently displaying the indicator).
1259         if(((mScrollOvershoot > Math::MACHINE_EPSILON_0) && (mScrollDistance < -Math::MACHINE_EPSILON_0)) ||
1260            ((mScrollOvershoot < Math::MACHINE_EPSILON_0) && (mScrollDistance > Math::MACHINE_EPSILON_0)))
1261         {
1262           // The user has moved against the indicator direction.
1263           // First, we reset the total displacement. This means the overshoot amount will become zero the next frame,
1264           // and if the user starts dragging in the overshoot direction again, the indicator will appear once more.
1265           mTotalPanDisplacement = Vector2::ZERO;
1266           // Animate the overshoot indicator off.
1267           AnimateScrollOvershoot(0.0f, false);
1268         }
1269         else
1270         {
1271           // Only set the property directly if we are not animating the overshoot away,
1272           // as otherwise this will overwrite the animation generated value.
1273           self.SetProperty(Toolkit::ItemView::Property::OVERSHOOT, mScrollOvershoot);
1274         }
1275       }
1276     }
1277     break;
1278
1279     case GestureState::CANCELLED:
1280     {
1281       mScrollAnimation = DoAnchoring();
1282     }
1283     break;
1284
1285     default:
1286       break;
1287   }
1288
1289   if(mScrollAnimation)
1290   {
1291     mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1292     mScrollAnimation.Play();
1293   }
1294 }
1295
1296 bool ItemView::OnAccessibilityPan(PanGesture gesture)
1297 {
1298   OnPan(gesture);
1299   return true;
1300 }
1301
1302 Actor ItemView::GetNextKeyboardFocusableActor(Actor actor, Toolkit::Control::KeyboardFocus::Direction direction, bool loopEnabled)
1303 {
1304   Actor nextFocusActor;
1305   if(mActiveLayout)
1306   {
1307     int nextItemID = 0;
1308     if(!actor || actor == this->Self())
1309     {
1310       nextFocusActor = GetItem(nextItemID);
1311     }
1312     else if(actor && actor.GetParent() == this->Self())
1313     {
1314       int itemID     = GetItemId(actor);
1315       nextItemID     = mActiveLayout->GetNextFocusItemID(itemID, mItemFactory.GetNumberOfItems(), direction, loopEnabled);
1316       nextFocusActor = GetItem(nextItemID);
1317       if(nextFocusActor == actor)
1318       {
1319         // need to pass NULL actor back to focus manager
1320         nextFocusActor.Reset();
1321         return nextFocusActor;
1322       }
1323     }
1324     float   layoutPosition = mActiveLayout->GetClosestAnchorPosition(GetCurrentLayoutPosition(0));
1325     Vector3 layoutSize     = Self().GetCurrentProperty<Vector3>(Actor::Property::SIZE);
1326     if(!nextFocusActor)
1327     {
1328       // likely the current item is not buffered, so not in our item pool, probably best to get first viewable item
1329       ItemRange viewableItems = mActiveLayout->GetItemsWithinArea(layoutPosition, layoutSize);
1330       nextItemID              = viewableItems.begin;
1331       nextFocusActor          = GetItem(nextItemID);
1332     }
1333   }
1334   return nextFocusActor;
1335 }
1336
1337 void ItemView::OnKeyboardFocusChangeCommitted(Actor commitedFocusableActor)
1338 {
1339   // only in this function if our chosen focus actor was actually used
1340   if(commitedFocusableActor)
1341   {
1342     int     nextItemID     = GetItemId(commitedFocusableActor);
1343     float   layoutPosition = GetCurrentLayoutPosition(0);
1344     Vector3 layoutSize     = Self().GetCurrentProperty<Vector3>(Actor::Property::SIZE);
1345
1346     float scrollTo = mActiveLayout->GetClosestOnScreenLayoutPosition(nextItemID, layoutPosition, layoutSize);
1347     ScrollTo(Vector2(0.0f, scrollTo), DEFAULT_KEYBOARD_FOCUS_SCROLL_DURATION);
1348   }
1349 }
1350
1351 bool ItemView::ItemViewAccessible::ScrollToChild(Actor child)
1352 {
1353   auto itemView = Dali::Toolkit::ItemView::DownCast(Self());
1354   Toolkit::GetImpl(itemView).OnKeyboardFocusChangeCommitted(child);
1355   return true;
1356 }
1357
1358 Animation ItemView::DoAnchoring()
1359 {
1360   Animation anchoringAnimation;
1361   Actor     self = Self();
1362
1363   if(mActiveLayout && mAnchoringEnabled)
1364   {
1365     float anchorPosition = mActiveLayout->GetClosestAnchorPosition(GetCurrentLayoutPosition(0));
1366
1367     anchoringAnimation = Animation::New(mAnchoringDuration);
1368     anchoringAnimation.AnimateTo(Property(self, Toolkit::ItemView::Property::LAYOUT_POSITION), anchorPosition, AlphaFunction::EASE_OUT);
1369     anchoringAnimation.AnimateTo(Property(self, Toolkit::ItemView::Property::SCROLL_SPEED), 0.0f, AlphaFunction::EASE_OUT);
1370     if(!mIsFlicking)
1371     {
1372       AnimateScrollOvershoot(0.0f);
1373     }
1374   }
1375
1376   return anchoringAnimation;
1377 }
1378
1379 void ItemView::OnScrollFinished(Animation& source)
1380 {
1381   Actor self = Self();
1382
1383   RemoveAnimation(mScrollAnimation); // mScrollAnimation is used to query whether we're scrolling
1384
1385   mScrollCompletedSignal.Emit(GetCurrentScrollPosition());
1386
1387   if(mIsFlicking && fabsf(mScrollOvershoot) > Math::MACHINE_EPSILON_1)
1388   {
1389     AnimateScrollOvershoot(mScrollOvershoot > 0.0f ? 1.0f : -1.0f, true);
1390   }
1391   else
1392   {
1393     // Reset the overshoot
1394     AnimateScrollOvershoot(0.0f);
1395   }
1396   mIsFlicking = false;
1397
1398   mScrollOvershoot = 0.0f;
1399 }
1400
1401 void ItemView::OnLayoutActivationScrollFinished(Animation& source)
1402 {
1403   RemoveAnimation(mScrollAnimation);
1404   mRefreshEnabled = true;
1405   DoRefresh(GetCurrentLayoutPosition(0), true);
1406
1407   // Emit the layout activated signal
1408   mLayoutActivatedSignal.Emit();
1409 }
1410
1411 void ItemView::OnOvershootOnFinished(Animation& animation)
1412 {
1413   mAnimatingOvershootOn = false;
1414   mScrollOvershootAnimation.FinishedSignal().Disconnect(this, &ItemView::OnOvershootOnFinished);
1415   RemoveAnimation(mScrollOvershootAnimation);
1416   if(mAnimateOvershootOff)
1417   {
1418     AnimateScrollOvershoot(0.0f);
1419   }
1420   mInAnimation = false;
1421 }
1422
1423 void ItemView::ScrollToItem(unsigned int itemId, float durationSeconds)
1424 {
1425   Actor         self                    = Self();
1426   const Vector3 layoutSize              = Self().GetCurrentProperty<Vector3>(Actor::Property::SIZE);
1427   float         firstItemScrollPosition = ClampFirstItemPosition(mActiveLayout->GetItemScrollToPosition(itemId), layoutSize, *mActiveLayout);
1428
1429   if(durationSeconds > 0.0f)
1430   {
1431     RemoveAnimation(mScrollAnimation);
1432     mScrollAnimation = Animation::New(durationSeconds);
1433     mScrollAnimation.AnimateTo(Property(self, Toolkit::ItemView::Property::LAYOUT_POSITION), firstItemScrollPosition, mScrollToAlphaFunction);
1434     mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1435     mScrollAnimation.Play();
1436   }
1437   else
1438   {
1439     self.SetProperty(Toolkit::ItemView::Property::LAYOUT_POSITION, firstItemScrollPosition);
1440     AnimateScrollOvershoot(0.0f);
1441   }
1442
1443   mScrollStartedSignal.Emit(GetCurrentScrollPosition());
1444   mRefreshEnabled = true;
1445 }
1446
1447 void ItemView::RemoveAnimation(Animation& animation)
1448 {
1449   if(animation)
1450   {
1451     // Cease animating, and reset handle.
1452     animation.Clear();
1453     animation.Reset();
1454   }
1455 }
1456
1457 void ItemView::CalculateDomainSize(const Vector3& layoutSize)
1458 {
1459   Actor self = Self();
1460
1461   Vector3 firstItemPosition(Vector3::ZERO);
1462   Vector3 lastItemPosition(Vector3::ZERO);
1463
1464   if(mActiveLayout)
1465   {
1466     firstItemPosition = mActiveLayout->GetItemPosition(0, 0, layoutSize);
1467
1468     float minLayoutPosition = mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), layoutSize);
1469     lastItemPosition        = mActiveLayout->GetItemPosition(fabs(minLayoutPosition), fabs(minLayoutPosition), layoutSize);
1470
1471     float domainSize;
1472
1473     if(IsHorizontal(mActiveLayout->GetOrientation()))
1474     {
1475       domainSize = fabs(firstItemPosition.x - lastItemPosition.x);
1476     }
1477     else
1478     {
1479       domainSize = fabs(firstItemPosition.y - lastItemPosition.y);
1480     }
1481
1482     self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MIN, Vector2::ZERO);
1483     self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MAX, Vector2(0.0f, -minLayoutPosition));
1484
1485     self.SetProperty(Toolkit::ItemView::Property::SCROLL_CONTENT_SIZE, domainSize);
1486
1487     bool isLayoutScrollable = IsLayoutScrollable(layoutSize);
1488     self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_VERTICAL, isLayoutScrollable);
1489     self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_HORIZONTAL, false);
1490   }
1491 }
1492
1493 bool ItemView::IsLayoutScrollable(const Vector3& layoutSize)
1494 {
1495   Actor self = Self();
1496
1497   float currentLayoutPosition   = ClampFirstItemPosition(GetCurrentLayoutPosition(0), layoutSize, *mActiveLayout, false);
1498   float forwardClampedPosition  = ClampFirstItemPosition(currentLayoutPosition + 1.0, layoutSize, *mActiveLayout, false);
1499   float backwardClampedPosition = ClampFirstItemPosition(currentLayoutPosition - 1.0, layoutSize, *mActiveLayout, false);
1500
1501   return (fabs(forwardClampedPosition - backwardClampedPosition) > Math::MACHINE_EPSILON_0);
1502 }
1503
1504 float ItemView::GetScrollPosition(float layoutPosition, const Vector3& layoutSize) const
1505 {
1506   Vector3 firstItemPosition(mActiveLayout->GetItemPosition(0, layoutPosition, layoutSize));
1507   return IsHorizontal(mActiveLayout->GetOrientation()) ? firstItemPosition.x : firstItemPosition.y;
1508 }
1509
1510 Vector2 ItemView::GetCurrentScrollPosition() const
1511 {
1512   return Vector2(0.0f, GetScrollPosition(GetCurrentLayoutPosition(0), Self().GetCurrentProperty<Vector3>(Actor::Property::SIZE)));
1513 }
1514
1515 void ItemView::AddOverlay(Actor actor)
1516 {
1517   actor.SetProperty(Actor::Property::DRAW_MODE, DrawMode::OVERLAY_2D);
1518   Self().Add(actor);
1519 }
1520
1521 void ItemView::RemoveOverlay(Actor actor)
1522 {
1523   Self().Remove(actor);
1524 }
1525
1526 void ItemView::ScrollTo(const Vector2& position, float duration)
1527 {
1528   Actor         self       = Self();
1529   const Vector3 layoutSize = Self().GetCurrentProperty<Vector3>(Actor::Property::SIZE);
1530
1531   float firstItemScrollPosition = ClampFirstItemPosition(position.y, layoutSize, *mActiveLayout);
1532
1533   if(duration > 0.0f)
1534   {
1535     RemoveAnimation(mScrollAnimation);
1536     mScrollAnimation = Animation::New(duration);
1537     mScrollAnimation.AnimateTo(Property(self, Toolkit::ItemView::Property::LAYOUT_POSITION), firstItemScrollPosition, mScrollToAlphaFunction);
1538     mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1539     mScrollAnimation.Play();
1540   }
1541   else
1542   {
1543     self.SetProperty(Toolkit::ItemView::Property::LAYOUT_POSITION, firstItemScrollPosition);
1544     AnimateScrollOvershoot(0.0f);
1545   }
1546
1547   mScrollStartedSignal.Emit(GetCurrentScrollPosition());
1548   mRefreshEnabled = true;
1549 }
1550
1551 void ItemView::SetOvershootSize(const Vector2& size)
1552 {
1553   mOvershootSize = size;
1554
1555   if(mOvershootOverlay)
1556   {
1557     // Remove old & add new size constraint
1558     mOvershootOverlay.RemoveConstraints(OVERSHOOT_SIZE_CONSTRAINT_TAG);
1559     ApplyOvershootSizeConstraint(mOvershootOverlay, mOvershootSize.height);
1560   }
1561 }
1562
1563 void ItemView::SetOvershootEffectColor(const Vector4& color)
1564 {
1565   mOvershootEffectColor = color;
1566   if(mOvershootOverlay)
1567   {
1568     mOvershootOverlay.SetProperty(Actor::Property::COLOR, color);
1569   }
1570 }
1571
1572 void ItemView::EnableScrollOvershoot(bool enable)
1573 {
1574   Actor self = Self();
1575   if(enable)
1576   {
1577     if(!mOvershootOverlay)
1578     {
1579       Property::Index effectOvershootPropertyIndex = Property::INVALID_INDEX;
1580       mOvershootOverlay                            = CreateBouncingEffectActor(effectOvershootPropertyIndex);
1581       mOvershootOverlay.SetProperty(Actor::Property::COLOR, mOvershootEffectColor);
1582       mOvershootOverlay.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
1583       mOvershootOverlay.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
1584       mOvershootOverlay.SetProperty(Actor::Property::DRAW_MODE, DrawMode::OVERLAY_2D);
1585       self.Add(mOvershootOverlay);
1586
1587       ApplyOvershootSizeConstraint(mOvershootOverlay, mOvershootSize.height);
1588
1589       Constraint constraint = Constraint::New<Quaternion>(mOvershootOverlay, Actor::Property::ORIENTATION, OvershootOverlayRotationConstraint);
1590       constraint.AddSource(ParentSource(Toolkit::ItemView::Property::SCROLL_DIRECTION));
1591       constraint.AddSource(ParentSource(Toolkit::ItemView::Property::LAYOUT_ORIENTATION));
1592       constraint.AddSource(ParentSource(Toolkit::ItemView::Property::OVERSHOOT));
1593       constraint.Apply();
1594
1595       constraint = Constraint::New<Vector3>(mOvershootOverlay, Actor::Property::POSITION, OvershootOverlayPositionConstraint);
1596       constraint.AddSource(ParentSource(Actor::Property::SIZE));
1597       constraint.AddSource(ParentSource(Toolkit::ItemView::Property::SCROLL_DIRECTION));
1598       constraint.AddSource(ParentSource(Toolkit::ItemView::Property::LAYOUT_ORIENTATION));
1599       constraint.AddSource(ParentSource(Toolkit::ItemView::Property::OVERSHOOT));
1600       constraint.Apply();
1601
1602       constraint = Constraint::New<bool>(mOvershootOverlay, Actor::Property::VISIBLE, OvershootOverlayVisibilityConstraint);
1603       constraint.AddSource(ParentSource(Toolkit::Scrollable::Property::CAN_SCROLL_VERTICAL));
1604       constraint.Apply();
1605
1606       constraint = Constraint::New<float>(mOvershootOverlay, effectOvershootPropertyIndex, EqualToConstraint());
1607       constraint.AddSource(ParentSource(Toolkit::ItemView::Property::OVERSHOOT));
1608       constraint.Apply();
1609     }
1610   }
1611   else
1612   {
1613     if(mOvershootOverlay)
1614     {
1615       self.Remove(mOvershootOverlay);
1616       mOvershootOverlay.Reset();
1617     }
1618   }
1619 }
1620
1621 float ItemView::CalculateScrollOvershoot()
1622 {
1623   float overshoot = 0.0f;
1624
1625   if(mActiveLayout)
1626   {
1627     // The overshoot must be calculated from the accumulated pan gesture displacement
1628     // since the pan gesture starts.
1629     Actor self              = Self();
1630     float scrollDistance    = CalculateScrollDistance(mTotalPanDisplacement, *mActiveLayout) * mActiveLayout->GetScrollSpeedFactor();
1631     float positionDelta     = GetCurrentLayoutPosition(0) + scrollDistance;
1632     float minLayoutPosition = mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), Self().GetCurrentProperty<Vector3>(Actor::Property::SIZE));
1633     self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MAX, Vector2(0.0f, -minLayoutPosition));
1634     float clamppedPosition = std::min(0.0f, std::max(minLayoutPosition, positionDelta));
1635     overshoot              = positionDelta - clamppedPosition;
1636   }
1637
1638   return overshoot > 0.0f ? std::min(overshoot, 1.0f) : std::max(overshoot, -1.0f);
1639 }
1640
1641 void ItemView::AnimateScrollOvershoot(float overshootAmount, bool animateBack)
1642 {
1643   bool animatingOn = fabsf(overshootAmount) > Math::MACHINE_EPSILON_1;
1644
1645   // make sure we animate back if needed
1646   mAnimateOvershootOff = animateBack || (!animatingOn && mAnimatingOvershootOn);
1647
1648   if(mAnimatingOvershootOn)
1649   {
1650     // animating on, do not allow animate off
1651     return;
1652   }
1653
1654   Actor self = Self();
1655
1656   if(mOvershootAnimationSpeed > Math::MACHINE_EPSILON_0)
1657   {
1658     float currentOvershoot = self.GetCurrentProperty<float>(Toolkit::ItemView::Property::OVERSHOOT);
1659     float duration         = 0.0f;
1660
1661     if(mOvershootOverlay)
1662     {
1663       duration = mOvershootOverlay.GetCurrentProperty<Vector3>(Actor::Property::SIZE).height * (animatingOn ? (1.0f - fabsf(currentOvershoot)) : fabsf(currentOvershoot)) / mOvershootAnimationSpeed;
1664     }
1665
1666     // Mark the animation as in progress to prevent manual property sets overwriting it.
1667     mInAnimation          = true;
1668     mAnimatingOvershootOn = animatingOn;
1669     RemoveAnimation(mScrollOvershootAnimation);
1670     mScrollOvershootAnimation = Animation::New(duration);
1671     mScrollOvershootAnimation.FinishedSignal().Connect(this, &ItemView::OnOvershootOnFinished);
1672     mScrollOvershootAnimation.AnimateTo(Property(self, Toolkit::ItemView::Property::OVERSHOOT), overshootAmount, TimePeriod(0.0f, duration));
1673     mScrollOvershootAnimation.Play();
1674   }
1675   else
1676   {
1677     self.SetProperty(Toolkit::ItemView::Property::OVERSHOOT, overshootAmount);
1678   }
1679 }
1680
1681 void ItemView::SetItemsParentOrigin(const Vector3& parentOrigin)
1682 {
1683   if(parentOrigin != mItemsParentOrigin)
1684   {
1685     mItemsParentOrigin = parentOrigin;
1686     for(ItemIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
1687     {
1688       iter->second.SetProperty(Actor::Property::PARENT_ORIGIN, parentOrigin);
1689     }
1690   }
1691 }
1692
1693 Vector3 ItemView::GetItemsParentOrigin() const
1694 {
1695   return mItemsParentOrigin;
1696 }
1697
1698 void ItemView::SetItemsAnchorPoint(const Vector3& anchorPoint)
1699 {
1700   if(anchorPoint != mItemsAnchorPoint)
1701   {
1702     mItemsAnchorPoint = anchorPoint;
1703     for(ItemIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
1704     {
1705       iter->second.SetProperty(Actor::Property::ANCHOR_POINT, anchorPoint);
1706     }
1707   }
1708 }
1709
1710 Vector3 ItemView::GetItemsAnchorPoint() const
1711 {
1712   return mItemsAnchorPoint;
1713 }
1714
1715 void ItemView::GetItemsRange(ItemRange& range)
1716 {
1717   if(!mItemPool.empty())
1718   {
1719     range.begin = mItemPool.begin()->first;
1720     range.end   = mItemPool.rbegin()->first + 1;
1721   }
1722   else
1723   {
1724     range.begin = 0;
1725     range.end   = 0;
1726   }
1727 }
1728
1729 bool ItemView::DoConnectSignal(BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor)
1730 {
1731   Dali::BaseHandle handle(object);
1732
1733   bool              connected(true);
1734   Toolkit::ItemView itemView = Toolkit::ItemView::DownCast(handle);
1735
1736   if(0 == strcmp(signalName.c_str(), LAYOUT_ACTIVATED_SIGNAL))
1737   {
1738     itemView.LayoutActivatedSignal().Connect(tracker, functor);
1739   }
1740   else
1741   {
1742     // signalName does not match any signal
1743     connected = false;
1744   }
1745
1746   return connected;
1747 }
1748
1749 void ItemView::SetProperty(BaseObject* object, Property::Index index, const Property::Value& value)
1750 {
1751   Toolkit::ItemView itemView = Toolkit::ItemView::DownCast(Dali::BaseHandle(object));
1752
1753   if(itemView)
1754   {
1755     ItemView& itemViewImpl(GetImpl(itemView));
1756     switch(index)
1757     {
1758       case Toolkit::ItemView::Property::MINIMUM_SWIPE_SPEED:
1759       {
1760         itemViewImpl.SetMinimumSwipeSpeed(value.Get<float>());
1761         break;
1762       }
1763
1764       case Toolkit::ItemView::Property::MINIMUM_SWIPE_DISTANCE:
1765       {
1766         itemViewImpl.SetMinimumSwipeDistance(value.Get<float>());
1767         break;
1768       }
1769
1770       case Toolkit::ItemView::Property::WHEEL_SCROLL_DISTANCE_STEP:
1771       {
1772         itemViewImpl.SetWheelScrollDistanceStep(value.Get<float>());
1773         break;
1774       }
1775
1776       case Toolkit::ItemView::Property::SNAP_TO_ITEM_ENABLED:
1777       {
1778         itemViewImpl.SetAnchoring(value.Get<bool>());
1779         break;
1780       }
1781
1782       case Toolkit::ItemView::Property::REFRESH_INTERVAL:
1783       {
1784         itemViewImpl.SetRefreshInterval(value.Get<float>());
1785         break;
1786       }
1787
1788       case Toolkit::ItemView::Property::LAYOUT:
1789       {
1790         // Get a Property::Array from the property if possible.
1791         Property::Array layoutArray;
1792         if(value.Get(layoutArray))
1793         {
1794           itemViewImpl.SetLayoutArray(layoutArray);
1795         }
1796         break;
1797       }
1798     }
1799   }
1800 }
1801
1802 Property::Array ItemView::GetLayoutArray()
1803 {
1804   return mlayoutArray;
1805 }
1806
1807 void ItemView::SetLayoutArray(const Property::Array& layouts)
1808 {
1809   mlayoutArray          = layouts;
1810   const int layoutCount = GetLayoutCount();
1811   if(layoutCount > 0)
1812   {
1813     for(int index = layoutCount - 1; index >= 0; --index)
1814     {
1815       RemoveLayout(index);
1816       if(index == 0) break;
1817     }
1818   }
1819
1820   for(unsigned int arrayIdx = 0, arrayCount = layouts.Count(); arrayIdx < arrayCount; ++arrayIdx)
1821   {
1822     const Property::Value& element = layouts.GetElementAt(arrayIdx);
1823
1824     const Property::Map* layout = element.GetMap();
1825     if(layout != NULL)
1826     {
1827       for(unsigned int mapIdx = 0, mapCount = (*layout).Count(); mapIdx < mapCount; ++mapIdx)
1828       {
1829         KeyValuePair propertyPair((*layout).GetKeyValue(mapIdx));
1830
1831         if(propertyPair.first == DefaultItemLayoutProperty::TYPE)
1832         {
1833           int layoutType = propertyPair.second.Get<int>();
1834           if(layoutType <= DefaultItemLayout::SPIRAL && layoutType >= DefaultItemLayout::DEPTH)
1835           {
1836             //DEPTH, GRID, LIST, SPIRAL
1837             switch(DefaultItemLayout::Type(layoutType))
1838             {
1839               case DefaultItemLayout::DEPTH:
1840               {
1841                 Internal::DepthLayoutPtr depthLayout = Internal::DepthLayout::New();
1842                 (*depthLayout).SetLayoutProperties(*layout);
1843                 (*depthLayout).SetDepthLayoutProperties(*layout);
1844                 AddLayout(*depthLayout);
1845                 break;
1846               }
1847               case DefaultItemLayout::GRID:
1848               {
1849                 Internal::GridLayoutPtr gridLayout = Internal::GridLayout::New();
1850                 (*gridLayout).SetLayoutProperties(*layout);
1851                 (*gridLayout).SetGridLayoutProperties(*layout);
1852                 AddLayout(*gridLayout);
1853                 break;
1854               }
1855               case DefaultItemLayout::LIST:
1856               {
1857                 Internal::GridLayoutPtr listLayout = Internal::GridLayout::New();
1858                 listLayout->SetNumberOfColumns(1);
1859                 (*listLayout).SetLayoutProperties(*layout);
1860                 (*listLayout).SetGridLayoutProperties(*layout);
1861                 AddLayout(*listLayout);
1862                 break;
1863               }
1864               case DefaultItemLayout::SPIRAL:
1865               {
1866                 Internal::SpiralLayoutPtr spiralLayout = Internal::SpiralLayout::New();
1867                 (*spiralLayout).SetLayoutProperties(*layout);
1868                 (*spiralLayout).SetSpiralLayoutProperties(*layout);
1869                 AddLayout(*spiralLayout);
1870                 break;
1871               }
1872             }
1873           }
1874         }
1875       }
1876     }
1877   }
1878 }
1879
1880 Property::Value ItemView::GetProperty(BaseObject* object, Property::Index index)
1881 {
1882   Property::Value value;
1883
1884   Toolkit::ItemView itemView = Toolkit::ItemView::DownCast(Dali::BaseHandle(object));
1885
1886   if(itemView)
1887   {
1888     ItemView& itemViewImpl(GetImpl(itemView));
1889     switch(index)
1890     {
1891       case Toolkit::ItemView::Property::MINIMUM_SWIPE_SPEED:
1892       {
1893         value = itemViewImpl.GetMinimumSwipeSpeed();
1894         break;
1895       }
1896
1897       case Toolkit::ItemView::Property::MINIMUM_SWIPE_DISTANCE:
1898       {
1899         value = itemViewImpl.GetMinimumSwipeDistance();
1900         break;
1901       }
1902
1903       case Toolkit::ItemView::Property::WHEEL_SCROLL_DISTANCE_STEP:
1904       {
1905         value = itemViewImpl.GetWheelScrollDistanceStep();
1906         break;
1907       }
1908
1909       case Toolkit::ItemView::Property::SNAP_TO_ITEM_ENABLED:
1910       {
1911         value = itemViewImpl.GetAnchoring();
1912         break;
1913       }
1914
1915       case Toolkit::ItemView::Property::REFRESH_INTERVAL:
1916       {
1917         value = itemViewImpl.GetRefreshInterval();
1918         break;
1919       }
1920
1921       case Toolkit::ItemView::Property::LAYOUT:
1922       {
1923         value = itemViewImpl.GetLayoutArray();
1924         break;
1925       }
1926     }
1927   }
1928
1929   return value;
1930 }
1931
1932 bool ItemView::DoAction(BaseObject* object, const std::string& actionName, const Property::Map& attributes)
1933 {
1934   Dali::BaseHandle handle(object);
1935
1936   Toolkit::ItemView itemView = Toolkit::ItemView::DownCast(handle);
1937
1938   DALI_ASSERT_ALWAYS(itemView);
1939
1940   if(0 == strcmp(actionName.c_str(), ACTION_STOP_SCROLLING))
1941   {
1942     GetImpl(itemView).DoStopScrolling();
1943   }
1944   else if(0 == strcmp(actionName.c_str(), ACTION_ENABLE_REFRESH))
1945   {
1946     GetImpl(itemView).SetRefreshNotificationEnabled(true);
1947   }
1948   else if(0 == strcmp(actionName.c_str(), ACTION_DISABLE_REFRESH))
1949   {
1950     GetImpl(itemView).SetRefreshNotificationEnabled(false);
1951   }
1952
1953   return true;
1954 }
1955
1956 void ItemView::DoStopScrolling()
1957 {
1958   if(mScrollAnimation)
1959   {
1960     mScrollAnimation.Stop();
1961     mScrollAnimation.Reset();
1962   }
1963 }
1964
1965 void ItemView::SetRefreshNotificationEnabled(bool enabled)
1966 {
1967   mRefreshNotificationEnabled = enabled;
1968 }
1969
1970 } // namespace Internal
1971
1972 } // namespace Toolkit
1973
1974 } // namespace Dali