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