0bda5ea518993306a207f1ca0d9276b902a6b0ca
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / scrollable / scroll-view / scroll-view-impl.cpp
1 /*
2  * Copyright (c) 2015 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/scroll-view/scroll-view-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <cstring> // for strcmp
23 #include <dali/public-api/animation/constraints.h>
24 #include <dali/public-api/common/stage.h>
25 #include <dali/public-api/events/wheel-event.h>
26 #include <dali/public-api/events/touch-event.h>
27 #include <dali/public-api/object/type-registry.h>
28 #include <dali/devel-api/object/type-registry-helper.h>
29 #include <dali/integration-api/debug.h>
30
31 // INTERNAL INCLUDES
32 #include <dali-toolkit/public-api/controls/scroll-bar/scroll-bar.h>
33 #include <dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view.h>
34 #include <dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-constraints.h>
35 #include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-overshoot-indicator-impl.h>
36 #include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-effect-impl.h>
37
38 //#define ENABLED_SCROLL_STATE_LOGGING
39
40 #ifdef ENABLED_SCROLL_STATE_LOGGING
41 #define DALI_LOG_SCROLL_STATE(format, args...) Dali::Integration::Log::LogMessage(Dali::Integration::Log::DebugInfo, "%s:%d " format "\n", __PRETTY_FUNCTION__, __LINE__, ## args)
42 #else
43 #define DALI_LOG_SCROLL_STATE(format, args...)
44 #endif
45
46 // TODO: Change to two class system:
47 // 1. DraggableActor (is an actor which can be dragged anywhere, can be set to range using the ruler)
48 // 2. ScrollView (contains a draggable actor that can a) be dragged in the negative X, and Y domain, b) has a hitArea for touches)
49 // TODO: external components (page and status overlays).
50 // TODO: Orientation.
51 // TODO: upgrade Vector2/3 to support returning Unit vectors, normals, & cross product (dot product is already provided)
52
53 using namespace Dali;
54
55 namespace
56 {
57 const float DEFAULT_SLOW_SNAP_ANIMATION_DURATION(0.5f);             ///< Default Drag-Release animation time.
58 const float DEFAULT_FAST_SNAP_ANIMATION_DURATION(0.25f);            ///< Default Drag-Flick animation time.
59 const float DEFAULT_SNAP_OVERSHOOT_DURATION(0.5f);                  ///< Default Overshoot snapping animation time.
60 const float DEFAULT_MAX_OVERSHOOT(100.0f);                          ///< Default maximum allowed overshoot in pixels
61
62 const float DEFAULT_AXIS_AUTO_LOCK_GRADIENT(0.36f);                 ///< Default Axis-AutoLock gradient threshold. default is 0.36:1 (20 degrees)
63 const float DEFAULT_FRICTION_COEFFICIENT(1.0f);                     ///< Default Friction Co-efficient. (in stage diagonals per second)
64 const float DEFAULT_FLICK_SPEED_COEFFICIENT(1.0f);                  ///< Default Flick speed coefficient (multiples input touch velocity)
65 const float DEFAULT_MAX_FLICK_SPEED(3.0f);                          ///< Default Maximum flick speed. (in stage diagonals per second)
66
67 const Vector2 DEFAULT_MIN_FLICK_DISTANCE(30.0f, 30.0f);              ///< minimum distance for pan before flick allowed
68 const float DEFAULT_MIN_FLICK_SPEED_THRESHOLD(500.0f);              ///< Minimum pan speed required for flick in pixels/s
69 const float FREE_FLICK_SPEED_THRESHOLD = 200.0f;                    ///< Free-Flick threshold in pixels/ms
70 const float AUTOLOCK_AXIS_MINIMUM_DISTANCE2 = 100.0f;               ///< Auto-lock axis after minimum distance squared.
71 const float FLICK_ORTHO_ANGLE_RANGE = 75.0f;                        ///< degrees. (if >45, then supports diagonal flicking)
72 const Vector2 DEFAULT_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION = Vector2(0.17f, 0.1f); ///< The step of horizontal scroll distance in the proportion of stage size for each wheel event received.
73 const unsigned long MINIMUM_TIME_BETWEEN_DOWN_AND_UP_FOR_RESET( 150u );
74 const float TOUCH_DOWN_TIMER_INTERVAL = 100.0f;
75 const float DEFAULT_SCROLL_UPDATE_DISTANCE( 30.0f );                ///< Default distance to travel in pixels for scroll update signal
76
77 const std::string INTERNAL_MAX_POSITION_PROPERTY_NAME( "internalMaxPosition" );
78
79 // Helpers ////////////////////////////////////////////////////////////////////////////////////////
80
81 /**
82  * Find the vector (distance) from (a) to (b)
83  * in domain (start) to (end)
84  * (\ / start)               (\ / end)
85  *   |-a                 b<----|
86  *
87  * @note assumes both (a) and (b) are already with the domain
88  * (start) to (end)
89  *
90  * @param[in] a the current point
91  * @param[in] b the target point
92  * @param[in] start the start of the domain
93  * @param[in] end the end of the domain
94  * @param[in] bias whether to only take the right direction or the left direction,
95  * or the shortest direction.
96  * @return the shortest direction and distance
97  */
98 float VectorInDomain(float a, float b, float start, float end, Dali::Toolkit::DirectionBias bias)
99 {
100   if(bias == Dali::Toolkit::DirectionBiasNone)
101   {
102     return ShortestDistanceInDomain( a, b, start, end );
103   }
104   //  (a-start + end-b)
105   float size = end-start;
106   float vect = b-a;
107
108   if(vect > 0)
109   {
110     // +ve vector
111     if(bias == Dali::Toolkit::DirectionBiasRight) // going right, take the vector.
112     {
113       return vect;
114     }
115     else
116     {
117       float aRight = a+size;
118       return b-aRight;
119     }
120   }
121   else
122   {
123     // -ve vector
124     if(bias == Dali::Toolkit::DirectionBiasLeft) // going left, take the vector.
125     {
126       return vect;
127     }
128     else
129     {
130       float aLeft = a-size;
131       return b-aLeft;
132     }
133   }
134 }
135
136 /**
137  * Returns the position of the anchor within actor
138  *
139  * @param actor The Actor
140  * @param anchor The Anchor point of interest.
141  * @return The position of the Anchor
142  */
143 Vector3 GetPositionOfAnchor(Actor &actor, const Vector3 &anchor)
144 {
145   Vector3 childPosition = actor.GetCurrentPosition();
146   Vector3 childAnchor = - actor.GetCurrentAnchorPoint() + anchor;
147   Vector3 childSize = actor.GetCurrentSize();
148
149   return childPosition + childAnchor * childSize;
150 }
151
152 // AlphaFunctions /////////////////////////////////////////////////////////////////////////////////
153
154 float FinalDefaultAlphaFunction(float offset)
155 {
156   return offset * 0.5f;
157 }
158
159 /**
160  * ConstantDecelerationAlphaFunction
161  * Newtoninan distance for constant deceleration
162  * v = 1 - t, s = t - 1/2 t^2
163  * when t = 0, s = 0.0 (min distance)
164  * when t = 1, s = 0.5 (max distance)
165  * progress = s / (max-min) = 2t - t^2
166  *
167  * @param[in] offset The input progress
168  * @return The output progress
169  */
170 float ConstantDecelerationAlphaFunction(float progress)
171 {
172   return progress * 2.0f - progress * progress;
173 }
174
175 // Internal Constraints ///////////////////////////////////////////////////////////////////////////
176
177 /**
178  * Internal Relative position Constraint
179  * Generates the relative position value of the scroll view
180  * based on the absolute position, and it's relation to the
181  * scroll domain. This is a value from 0.0f to 1.0f in each
182  * scroll position axis.
183  */
184 void InternalRelativePositionConstraint( Vector2& relativePosition, const PropertyInputContainer& inputs)
185 {
186   Vector2 position = -inputs[0]->GetVector2();
187   const Vector2& min = inputs[1]->GetVector2();
188   const Vector2& max = inputs[2]->GetVector2();
189   const Vector3& size = inputs[3]->GetVector3();
190
191   position.x = WrapInDomain(position.x, min.x, max.x);
192   position.y = WrapInDomain(position.y, min.y, max.y);
193
194   Vector2 domainSize = (max - min) - size.GetVectorXY();
195
196   relativePosition.x = domainSize.x > Math::MACHINE_EPSILON_1 ? fabsf((position.x - min.x) / domainSize.x) : 0.0f;
197   relativePosition.y = domainSize.y > Math::MACHINE_EPSILON_1 ? fabsf((position.y - min.y) / domainSize.y) : 0.0f;
198 }
199
200 /**
201  * Internal scroll domain Constraint
202  * Generates the scroll domain of the scroll view.
203  */
204 void InternalScrollDomainConstraint( Vector2& scrollDomain, const PropertyInputContainer& inputs)
205 {
206   const Vector2& min = inputs[0]->GetVector2();
207   const Vector2& max = inputs[1]->GetVector2();
208   const Vector3& size = inputs[2]->GetVector3();
209
210   scrollDomain = (max - min) - size.GetVectorXY();
211 }
212
213 /**
214  * Internal maximum scroll position Constraint
215  * Generates the maximum scroll position of the scroll view.
216  */
217 void InternalPrePositionMaxConstraint( Vector2& scrollMax, const PropertyInputContainer& inputs)
218 {
219   const Vector2& max = inputs[0]->GetVector2();
220   const Vector3& size = inputs[1]->GetVector3();
221
222   scrollMax = max - size.GetVectorXY();
223 }
224
225 } // unnamed namespace
226
227 namespace Dali
228 {
229
230 namespace Toolkit
231 {
232
233 namespace Internal
234 {
235
236 namespace
237 {
238
239 BaseHandle Create()
240 {
241   return Toolkit::ScrollView::New();
242 }
243
244 // Setup properties, signals and actions using the type-registry.
245 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::ScrollView, Toolkit::Scrollable, Create )
246
247 DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "scrollPosition",  VECTOR2, SCROLL_POSITION)
248 DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "scrollPrePosition",   VECTOR2, SCROLL_PRE_POSITION)
249 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, ScrollView, "scrollPrePositionX",    SCROLL_PRE_POSITION_X, SCROLL_PRE_POSITION, 0)
250 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, ScrollView, "scrollPrePositionY",    SCROLL_PRE_POSITION_Y, SCROLL_PRE_POSITION, 1)
251 DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "scrollPrePositionMax",    VECTOR2, SCROLL_PRE_POSITION_MAX)
252 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, ScrollView, "scrollPrePositionMaxX",     SCROLL_PRE_POSITION_MAX_X, SCROLL_PRE_POSITION_MAX, 0)
253 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, ScrollView, "scrollPrePositionMaxY",     SCROLL_PRE_POSITION_MAX_Y, SCROLL_PRE_POSITION_MAX, 1)
254 DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "overshootX",  FLOAT, OVERSHOOT_X)
255 DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "overshootY",  FLOAT, OVERSHOOT_Y)
256 DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "scrollFinal",  VECTOR2, SCROLL_FINAL)
257 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, ScrollView, "scrollFinalX",   SCROLL_FINAL_X, SCROLL_FINAL,0)
258 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, ScrollView, "scrollFinalY",   SCROLL_FINAL_Y, SCROLL_FINAL,1)
259 DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "wrap", BOOLEAN, WRAP)
260 DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "panning", BOOLEAN, PANNING)
261 DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "scrolling", BOOLEAN, SCROLLING)
262 DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "scrollDomainSize",   VECTOR2, SCROLL_DOMAIN_SIZE)
263 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, ScrollView, "scrollDomainSizeX",    SCROLL_DOMAIN_SIZE_X, SCROLL_DOMAIN_SIZE, 0)
264 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, ScrollView, "scrollDomainSizeY",    SCROLL_DOMAIN_SIZE_Y, SCROLL_DOMAIN_SIZE, 1)
265 DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "scrollDomainOffset",   VECTOR2, SCROLL_DOMAIN_OFFSET)
266 DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "scrollPositionDelta",   VECTOR2, SCROLL_POSITION_DELTA)
267 DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "startPagePosition",   VECTOR3, START_PAGE_POSITION)
268
269 DALI_SIGNAL_REGISTRATION( Toolkit, ScrollView, "valueChanged",  SIGNAL_SNAP_STARTED )
270
271 DALI_TYPE_REGISTRATION_END()
272
273 /**
274  * Returns whether to lock scrolling to a particular axis
275  *
276  * @param[in] panDelta Distance panned since gesture started
277  * @param[in] currentLockAxis The current lock axis value
278  * @param[in] lockGradient How quickly to lock to a particular axis
279  *
280  * @return The new axis lock state
281  */
282 ScrollView::LockAxis GetLockAxis(const Vector2& panDelta, ScrollView::LockAxis currentLockAxis, float lockGradient)
283 {
284   if(panDelta.LengthSquared() > AUTOLOCK_AXIS_MINIMUM_DISTANCE2 &&
285       currentLockAxis == ScrollView::LockPossible)
286   {
287     float dx = fabsf(panDelta.x);
288     float dy = fabsf(panDelta.y);
289     if(dx * lockGradient >= dy)
290     {
291       // 0.36:1 gradient to the horizontal (deviate < 20 degrees)
292       currentLockAxis = ScrollView::LockVertical;
293     }
294     else if(dy * lockGradient > dx)
295     {
296       // 0.36:1 gradient to the vertical (deviate < 20 degrees)
297       currentLockAxis = ScrollView::LockHorizontal;
298     }
299     else
300     {
301       currentLockAxis = ScrollView::LockNone;
302     }
303   }
304   return currentLockAxis;
305 }
306
307 /**
308  * Internal Pre-Position Property Constraint.
309  *
310  * Generates position property based on current position + gesture displacement.
311  * Or generates position property based on positionX/Y.
312  * Note: This is the position prior to any clamping at scroll boundaries.
313  */
314 struct InternalPrePositionConstraint
315 {
316   InternalPrePositionConstraint( const Vector2& initialPanPosition,
317                                  const Vector2& initialPanMask,
318                                  bool axisAutoLock,
319                                  float axisAutoLockGradient,
320                                  ScrollView::LockAxis initialLockAxis,
321                                  const Vector2& maxOvershoot,
322                                  const RulerPtr& rulerX, const RulerPtr& rulerY )
323   : mLocalStart( initialPanPosition ),
324     mInitialPanMask( initialPanMask ),
325     mMaxOvershoot( maxOvershoot ),
326     mAxisAutoLockGradient( axisAutoLockGradient ),
327     mLockAxis( initialLockAxis ),
328     mAxisAutoLock( axisAutoLock ),
329     mWasPanning( false )
330   {
331     const RulerDomain& rulerDomainX = rulerX->GetDomain();
332     const RulerDomain& rulerDomainY = rulerY->GetDomain();
333     mDomainMin = Vector2( rulerDomainX.min, -rulerDomainY.min );
334     mDomainMax = Vector2( -rulerDomainX.max, -rulerDomainY.max );
335     mClampX = rulerDomainX.enabled;
336     mClampY = rulerDomainY.enabled;
337     mFixedRulerX = rulerX->GetType() == Ruler::Fixed;
338     mFixedRulerY = rulerY->GetType() == Ruler::Fixed;
339   }
340
341   void operator()( Vector2& scrollPostPosition, const PropertyInputContainer& inputs )
342   {
343     const Vector2& panPosition = inputs[0]->GetVector2();
344     const bool& inGesture = inputs[1]->GetBoolean();
345
346     // First check if we are within a gesture.
347     // The ScrollView may have received a start gesture from ::OnPan()
348     // while the finish gesture is received now in this constraint.
349     // This gesture must then be rejected as the value will be "old".
350     // Typically the last value from the end of the last gesture.
351     // If we are rejecting the gesture, we simply don't modify the constraint target.
352     if( inGesture )
353     {
354       if( !mWasPanning )
355       {
356         mPrePosition = scrollPostPosition;
357         mStartPosition = mPrePosition;
358         mCurrentPanMask = mInitialPanMask;
359         mWasPanning = true;
360       }
361
362       // Calculate Deltas...
363       const Vector2& currentPosition = panPosition;
364       Vector2 panDelta( currentPosition - mLocalStart );
365
366       // Axis Auto Lock - locks the panning to the horizontal or vertical axis if the pan
367       // appears mostly horizontal or mostly vertical respectively...
368       if( mAxisAutoLock )
369       {
370         mLockAxis = GetLockAxis( panDelta, mLockAxis, mAxisAutoLockGradient );
371         if( mLockAxis == ScrollView::LockVertical )
372         {
373           mCurrentPanMask.y = 0.0f;
374         }
375         else if( mLockAxis == ScrollView::LockHorizontal )
376         {
377           mCurrentPanMask.x = 0.0f;
378         }
379       }
380
381       // Restrict deltas based on ruler enable/disable and axis-lock state...
382       panDelta *= mCurrentPanMask;
383
384       // Perform Position transform based on input deltas...
385       scrollPostPosition = mPrePosition;
386       scrollPostPosition += panDelta;
387
388       // if no wrapping then clamp preposition to maximum overshoot amount
389       const Vector3& size = inputs[2]->GetVector3();
390       if( mClampX )
391       {
392         float newXPosition = Clamp( scrollPostPosition.x, ( mDomainMax.x + size.x ) - mMaxOvershoot.x, mDomainMin.x + mMaxOvershoot.x );
393         if( (newXPosition < scrollPostPosition.x - Math::MACHINE_EPSILON_1)
394           || (newXPosition > scrollPostPosition.x + Math::MACHINE_EPSILON_1) )
395         {
396           mPrePosition.x = newXPosition;
397           mLocalStart.x = panPosition.x;
398         }
399         scrollPostPosition.x = newXPosition;
400       }
401       if( mClampY )
402       {
403         float newYPosition = Clamp( scrollPostPosition.y, ( mDomainMax.y + size.y ) - mMaxOvershoot.y, mDomainMin.y + mMaxOvershoot.y );
404         if( ( newYPosition < scrollPostPosition.y - Math::MACHINE_EPSILON_1 )
405           || ( newYPosition > scrollPostPosition.y + Math::MACHINE_EPSILON_1 ) )
406         {
407           mPrePosition.y = newYPosition;
408           mLocalStart.y = panPosition.y;
409         }
410         scrollPostPosition.y = newYPosition;
411       }
412
413       // If we are using a fixed ruler in a particular axis, limit the maximum pages scrolled on that axis.
414       if( mFixedRulerX || mFixedRulerY )
415       {
416         // Here we limit the maximum amount that can be moved from the starting position of the gesture to one page.
417         // We do this only if we have a fixed ruler (on that axis) and the mode is enabled.
418         // Note: 1.0f is subtracted to keep the value within one page size (otherwise we stray on to the page after).
419         // Note: A further 1.0f is subtracted to handle a compensation that happens later within the flick handling code in SnapWithVelocity().
420         //       When a flick is completed, an adjustment of 1.0f is sometimes made to allow for the scenario where:
421         //       A flick finishes before the update thread has advanced the scroll position past the previous snap point.
422         Vector2 pageSizeLimit( size.x - ( 1.0f + 1.0f ), size.y - ( 1.0f - 1.0f ) );
423         Vector2 minPosition( mStartPosition.x - pageSizeLimit.x, mStartPosition.y - pageSizeLimit.y );
424         Vector2 maxPosition( mStartPosition.x + pageSizeLimit.x, mStartPosition.y + pageSizeLimit.y );
425
426         if( mFixedRulerX )
427         {
428           scrollPostPosition.x = Clamp( scrollPostPosition.x, minPosition.x, maxPosition.x );
429         }
430         if( mFixedRulerY )
431         {
432           scrollPostPosition.y = Clamp( scrollPostPosition.y, minPosition.y, maxPosition.y );
433         }
434       }
435     }
436   }
437
438   Vector2 mPrePosition;
439   Vector2 mLocalStart;
440   Vector2 mStartPosition;               ///< The start position of the gesture - used to limit scroll amount (not modified by clamping).
441   Vector2 mInitialPanMask;              ///< Initial pan mask (based on ruler settings).
442   Vector2 mCurrentPanMask;              ///< Current pan mask that can be altered by axis lock mode.
443   Vector2 mDomainMin;
444   Vector2 mDomainMax;
445   Vector2 mMaxOvershoot;
446
447   float mAxisAutoLockGradient;          ///< Set by ScrollView
448   ScrollView::LockAxis mLockAxis;
449
450   bool mAxisAutoLock:1;                 ///< Set by ScrollView
451   bool mWasPanning:1;
452   bool mClampX:1;
453   bool mClampY:1;
454   bool mFixedRulerX:1;
455   bool mFixedRulerY:1;
456 };
457
458 /**
459  * Internal Position Property Constraint.
460  *
461  * Generates position property based on pre-position
462  * Note: This is the position after clamping.
463  * (uses result of InternalPrePositionConstraint)
464  */
465 struct InternalPositionConstraint
466 {
467   InternalPositionConstraint(const RulerDomain& domainX, const RulerDomain& domainY, bool wrap)
468   : mDomainMin( -domainX.min, -domainY.min ),
469     mDomainMax( -domainX.max, -domainY.max ),
470     mClampX( domainX.enabled ),
471     mClampY( domainY.enabled ),
472     mWrap( wrap )
473   {
474   }
475
476   void operator()( Vector2& position, const PropertyInputContainer& inputs )
477   {
478     position = inputs[0]->GetVector2();
479     const Vector2& size = inputs[3]->GetVector3().GetVectorXY();
480     const Vector2& min = inputs[1]->GetVector2();
481     const Vector2& max = inputs[2]->GetVector2();
482
483     if( mWrap )
484     {
485       position.x = -WrapInDomain(-position.x, min.x, max.x);
486       position.y = -WrapInDomain(-position.y, min.y, max.y);
487     }
488     else
489     {
490       // clamp post position to domain
491       position.x = mClampX ? Clamp(position.x, mDomainMax.x + size.x, mDomainMin.x ) : position.x;
492       position.y = mClampY ? Clamp(position.y, mDomainMax.y + size.y, mDomainMin.y ) : position.y;
493     }
494   }
495
496   Vector2 mDomainMin;
497   Vector2 mDomainMax;
498   bool mClampX;
499   bool mClampY;
500   bool mWrap;
501
502 };
503
504 /**
505  * This constraint updates the X overshoot property using the difference
506  * SCROLL_PRE_POSITION.x and SCROLL_POSITION.x, returning a relative value between 0.0f and 1.0f
507  */
508 struct OvershootXConstraint
509 {
510   OvershootXConstraint(float maxOvershoot) : mMaxOvershoot(maxOvershoot) {}
511
512   void operator()( float& current, const PropertyInputContainer& inputs )
513   {
514     if( inputs[2]->GetBoolean() )
515     {
516       const Vector2& scrollPrePosition = inputs[0]->GetVector2();
517       const Vector2& scrollPostPosition = inputs[1]->GetVector2();
518       float newOvershoot = scrollPrePosition.x - scrollPostPosition.x;
519       current = (newOvershoot > 0.0f ? std::min(newOvershoot, mMaxOvershoot) : std::max(newOvershoot, -mMaxOvershoot)) / mMaxOvershoot;
520     }
521     else
522     {
523       current = 0.0f;
524     }
525   }
526
527   float mMaxOvershoot;
528 };
529
530 /**
531  * This constraint updates the Y overshoot property using the difference
532  * SCROLL_PRE_POSITION.y and SCROLL_POSITION.y, returning a relative value between 0.0f and 1.0f
533  */
534 struct OvershootYConstraint
535 {
536   OvershootYConstraint(float maxOvershoot) : mMaxOvershoot(maxOvershoot) {}
537
538   void operator()( float& current, const PropertyInputContainer& inputs )
539   {
540     if( inputs[2]->GetBoolean() )
541     {
542       const Vector2& scrollPrePosition = inputs[0]->GetVector2();
543       const Vector2& scrollPostPosition = inputs[1]->GetVector2();
544       float newOvershoot = scrollPrePosition.y - scrollPostPosition.y;
545       current = (newOvershoot > 0.0f ? std::min(newOvershoot, mMaxOvershoot) : std::max(newOvershoot, -mMaxOvershoot)) / mMaxOvershoot;
546     }
547     else
548     {
549       current = 0.0f;
550     }
551   }
552
553   float mMaxOvershoot;
554 };
555
556 /**
557  * Internal Position-Delta Property Constraint.
558  *
559  * Generates position-delta property based on scroll-position + scroll-offset properties.
560  */
561 void InternalPositionDeltaConstraint( Vector2& current, const PropertyInputContainer& inputs )
562 {
563   const Vector2& scrollPosition = inputs[0]->GetVector2();
564   const Vector2& scrollOffset = inputs[1]->GetVector2();
565
566   current = scrollPosition + scrollOffset;
567 }
568
569 /**
570  * Internal Final Position Constraint
571  * The position of content is:
572  * of scroll-position + f(scroll-overshoot)
573  * where f(...) function defines how overshoot
574  * should affect final-position.
575  */
576 struct InternalFinalConstraint
577 {
578   InternalFinalConstraint(AlphaFunctionPrototype functionX,
579                           AlphaFunctionPrototype functionY)
580   : mFunctionX(functionX),
581     mFunctionY(functionY)
582   {
583   }
584
585   void operator()( Vector2& current, const PropertyInputContainer& inputs )
586   {
587     const float& overshootx = inputs[1]->GetFloat();
588     const float& overshooty = inputs[2]->GetFloat();
589     Vector2 offset( mFunctionX(overshootx),
590                     mFunctionY(overshooty) );
591
592     current = inputs[0]->GetVector2() - offset;
593   }
594
595   AlphaFunctionPrototype mFunctionX;
596   AlphaFunctionPrototype mFunctionY;
597 };
598
599 }
600
601
602 ///////////////////////////////////////////////////////////////////////////////////////////////////
603 // ScrollView
604 ///////////////////////////////////////////////////////////////////////////////////////////////////
605
606 Dali::Toolkit::ScrollView ScrollView::New()
607 {
608   // Create the implementation
609   ScrollViewPtr scrollView(new ScrollView());
610
611   // Pass ownership to CustomActor via derived handle
612   Dali::Toolkit::ScrollView handle(*scrollView);
613
614   // Second-phase init of the implementation
615   // This can only be done after the CustomActor connection has been made...
616   scrollView->Initialize();
617
618   return handle;
619 }
620
621 ScrollView::ScrollView()
622 : ScrollBase( ControlBehaviour( REQUIRES_WHEEL_EVENTS ) ),   // Enable size negotiation
623   mTouchDownTime(0u),
624   mGestureStackDepth(0),
625   mScrollStateFlags(0),
626   mLockAxis(LockPossible),
627   mScrollUpdateDistance(DEFAULT_SCROLL_UPDATE_DISTANCE),
628   mMaxOvershoot(DEFAULT_MAX_OVERSHOOT, DEFAULT_MAX_OVERSHOOT),
629   mUserMaxOvershoot(DEFAULT_MAX_OVERSHOOT, DEFAULT_MAX_OVERSHOOT),
630   mSnapOvershootDuration(DEFAULT_SNAP_OVERSHOOT_DURATION),
631   mSnapOvershootAlphaFunction(AlphaFunction::EASE_OUT),
632   mSnapDuration(DEFAULT_SLOW_SNAP_ANIMATION_DURATION),
633   mSnapAlphaFunction(AlphaFunction::EASE_OUT),
634   mMinFlickDistance(DEFAULT_MIN_FLICK_DISTANCE),
635   mFlickSpeedThreshold(DEFAULT_MIN_FLICK_SPEED_THRESHOLD),
636   mFlickDuration(DEFAULT_FAST_SNAP_ANIMATION_DURATION),
637   mFlickAlphaFunction(AlphaFunction::EASE_OUT),
638   mAxisAutoLockGradient(DEFAULT_AXIS_AUTO_LOCK_GRADIENT),
639   mFrictionCoefficient(DEFAULT_FRICTION_COEFFICIENT),
640   mFlickSpeedCoefficient(DEFAULT_FLICK_SPEED_COEFFICIENT),
641   mMaxFlickSpeed(DEFAULT_MAX_FLICK_SPEED),
642   mWheelScrollDistanceStep(Vector2::ZERO),
643   mInAccessibilityPan(false),
644   mScrolling(false),
645   mScrollInterrupted(false),
646   mPanning(false),
647   mSensitive(true),
648   mTouchDownTimeoutReached(false),
649   mActorAutoSnapEnabled(false),
650   mAutoResizeContainerEnabled(false),
651   mWrapMode(false),
652   mAxisAutoLock(false),
653   mAlterChild(false),
654   mDefaultMaxOvershoot(true),
655   mCanScrollHorizontal(true),
656   mCanScrollVertical(true)
657 {
658 }
659
660 void ScrollView::OnInitialize()
661 {
662   Actor self = Self();
663
664   // Internal Actor, used to hide actors from enumerations.
665   // Also actors added to Internal actor appear as overlays e.g. ScrollBar components.
666   mInternalActor = Actor::New();
667   self.Add(mInternalActor);
668
669   mInternalActor.SetParentOrigin(ParentOrigin::CENTER);
670   mInternalActor.SetAnchorPoint(AnchorPoint::CENTER);
671   mInternalActor.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
672
673   mAlterChild = true;
674
675   mScrollPostPosition = mScrollPrePosition = Vector2::ZERO;
676
677   mWheelScrollDistanceStep = Stage::GetCurrent().GetSize() * DEFAULT_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION;
678
679   mGestureStackDepth = 0;
680
681   EnableGestureDetection( Gesture::Type( Gesture::Pan ) );
682
683   // By default we'll allow the user to freely drag the scroll view,
684   // while disabling the other rulers.
685   RulerPtr ruler = new DefaultRuler();
686   mRulerX = ruler;
687   mRulerY = ruler;
688
689   self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_VERTICAL, mCanScrollVertical);
690   self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_HORIZONTAL, mCanScrollHorizontal);
691
692   UpdatePropertyDomain();
693   SetInternalConstraints();
694 }
695
696 void ScrollView::OnStageConnection( int depth )
697 {
698   ScrollBase::OnStageConnection( depth );
699
700   DALI_LOG_SCROLL_STATE("[0x%X]", this);
701
702   if ( mSensitive )
703   {
704     SetScrollSensitive( false );
705     SetScrollSensitive( true );
706   }
707   if(IsOvershootEnabled())
708   {
709     // try and make sure property notifications are set
710     EnableScrollOvershoot(true);
711   }
712 }
713
714 void ScrollView::OnStageDisconnection()
715 {
716   DALI_LOG_SCROLL_STATE("[0x%X]", this);
717
718   StopAnimation();
719
720   ScrollBase::OnStageDisconnection();
721 }
722
723 ScrollView::~ScrollView()
724 {
725   DALI_LOG_SCROLL_STATE("[0x%X]", this);
726 }
727
728 AlphaFunction ScrollView::GetScrollSnapAlphaFunction() const
729 {
730   return mSnapAlphaFunction;
731 }
732
733 void ScrollView::SetScrollSnapAlphaFunction(AlphaFunction alpha)
734 {
735   mSnapAlphaFunction = alpha;
736 }
737
738 AlphaFunction ScrollView::GetScrollFlickAlphaFunction() const
739 {
740   return mFlickAlphaFunction;
741 }
742
743 void ScrollView::SetScrollFlickAlphaFunction(AlphaFunction alpha)
744 {
745   mFlickAlphaFunction = alpha;
746 }
747
748 float ScrollView::GetScrollSnapDuration() const
749 {
750   return mSnapDuration;
751 }
752
753 void ScrollView::SetScrollSnapDuration(float time)
754 {
755   mSnapDuration = time;
756 }
757
758 float ScrollView::GetScrollFlickDuration() const
759 {
760   return mFlickDuration;
761 }
762
763 void ScrollView::SetScrollFlickDuration(float time)
764 {
765   mFlickDuration = time;
766 }
767
768 void ScrollView::ApplyEffect(Toolkit::ScrollViewEffect effect)
769 {
770   Dali::Toolkit::ScrollView self = Dali::Toolkit::ScrollView::DownCast(Self());
771
772   // Assertion check to ensure effect doesn't already exist in this scrollview
773   bool effectAlreadyExistsInScrollView(false);
774   for (ScrollViewEffectIter iter = mEffects.begin(); iter != mEffects.end(); ++iter)
775   {
776     if(*iter==effect)
777     {
778       effectAlreadyExistsInScrollView = true;
779       break;
780     }
781   }
782
783   DALI_ASSERT_ALWAYS(!effectAlreadyExistsInScrollView);
784
785   // add effect to effects list
786   mEffects.push_back(effect);
787
788   // invoke Attachment request to ScrollView first
789   GetImpl(effect).Attach(self);
790 }
791
792 void ScrollView::RemoveEffect(Toolkit::ScrollViewEffect effect)
793 {
794   Dali::Toolkit::ScrollView self = Dali::Toolkit::ScrollView::DownCast(Self());
795
796   // remove effect from effects list
797   bool effectExistedInScrollView(false);
798   for (ScrollViewEffectIter iter = mEffects.begin(); iter != mEffects.end(); ++iter)
799   {
800     if(*iter==effect)
801     {
802       mEffects.erase(iter);
803       effectExistedInScrollView = true;
804       break;
805     }
806   }
807
808   // Assertion check to ensure effect existed.
809   DALI_ASSERT_ALWAYS(effectExistedInScrollView);
810
811   // invoke Detachment request to ScrollView last
812   GetImpl(effect).Detach(self);
813 }
814
815 void ScrollView::RemoveAllEffects()
816 {
817   Dali::Toolkit::ScrollView self = Dali::Toolkit::ScrollView::DownCast(Self());
818
819   for (ScrollViewEffectIter effectIter = mEffects.begin(); effectIter != mEffects.end(); ++effectIter)
820   {
821     Toolkit::ScrollViewEffect effect = *effectIter;
822
823     // invoke Detachment request to ScrollView last
824     GetImpl(effect).Detach(self);
825   }
826
827   mEffects.clear();
828 }
829
830 void ScrollView::ApplyConstraintToChildren(Constraint constraint)
831 {
832   ApplyConstraintToBoundActors(constraint);
833 }
834
835 void ScrollView::RemoveConstraintsFromChildren()
836 {
837   RemoveConstraintsFromBoundActors();
838 }
839
840 const RulerPtr ScrollView::GetRulerX() const
841 {
842   return mRulerX;
843 }
844
845 const RulerPtr ScrollView::GetRulerY() const
846 {
847   return mRulerY;
848 }
849
850 void ScrollView::SetRulerX(RulerPtr ruler)
851 {
852   mRulerX = ruler;
853
854   UpdatePropertyDomain();
855   UpdateMainInternalConstraint();
856 }
857
858 void ScrollView::SetRulerY(RulerPtr ruler)
859 {
860   mRulerY = ruler;
861
862   UpdatePropertyDomain();
863   UpdateMainInternalConstraint();
864 }
865
866 void ScrollView::UpdatePropertyDomain()
867 {
868   Actor self = Self();
869   Vector3 size = self.GetTargetSize();
870   Vector2 min = mMinScroll;
871   Vector2 max = mMaxScroll;
872   bool scrollPositionChanged = false;
873   bool domainChanged = false;
874
875   bool canScrollVertical = false;
876   bool canScrollHorizontal = false;
877   UpdateLocalScrollProperties();
878   if(mRulerX->IsEnabled())
879   {
880     const Toolkit::RulerDomain& rulerDomain = mRulerX->GetDomain();
881     if( fabsf(min.x - rulerDomain.min) > Math::MACHINE_EPSILON_100
882         || fabsf(max.x - rulerDomain.max) > Math::MACHINE_EPSILON_100 )
883     {
884       domainChanged = true;
885       min.x = rulerDomain.min;
886       max.x = rulerDomain.max;
887
888       // make sure new scroll value is within new domain
889       if( mScrollPrePosition.x < min.x
890           || mScrollPrePosition.x > max.x )
891       {
892         scrollPositionChanged = true;
893         mScrollPrePosition.x = Clamp(mScrollPrePosition.x, -(max.x - size.x), -min.x);
894       }
895     }
896     if( (fabsf(rulerDomain.max - rulerDomain.min) - size.x) > Math::MACHINE_EPSILON_100 )
897     {
898       canScrollHorizontal = true;
899     }
900   }
901   else if( fabs(min.x) > Math::MACHINE_EPSILON_100
902            || fabs(max.x) > Math::MACHINE_EPSILON_100 )
903   {
904     // need to reset to 0
905     domainChanged = true;
906     min.x = 0.0f;
907     max.x = 0.0f;
908     canScrollHorizontal = false;
909   }
910
911   if(mRulerY->IsEnabled())
912   {
913     const Toolkit::RulerDomain& rulerDomain = mRulerY->GetDomain();
914     if( fabsf(min.y - rulerDomain.min) > Math::MACHINE_EPSILON_100
915         || fabsf(max.y - rulerDomain.max) > Math::MACHINE_EPSILON_100 )
916     {
917       domainChanged = true;
918       min.y = rulerDomain.min;
919       max.y = rulerDomain.max;
920
921       // make sure new scroll value is within new domain
922       if( mScrollPrePosition.y < min.y
923           || mScrollPrePosition.y > max.y )
924       {
925         scrollPositionChanged = true;
926         mScrollPrePosition.y = Clamp(mScrollPrePosition.y, -(max.y - size.y), -min.y);
927       }
928     }
929     if( (fabsf(rulerDomain.max - rulerDomain.min) - size.y) > Math::MACHINE_EPSILON_100 )
930     {
931       canScrollVertical = true;
932     }
933   }
934   else if( fabs(min.y) > Math::MACHINE_EPSILON_100
935            || fabs(max.y) > Math::MACHINE_EPSILON_100 )
936   {
937     // need to reset to 0
938     domainChanged = true;
939     min.y = 0.0f;
940     max.y = 0.0f;
941     canScrollVertical = false;
942   }
943
944   // avoid setting properties if possible, otherwise this will cause an entire update as well as triggering constraints using each property we update
945   if( mCanScrollVertical != canScrollVertical )
946   {
947     mCanScrollVertical = canScrollVertical;
948     self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_VERTICAL, canScrollVertical);
949   }
950   if( mCanScrollHorizontal != canScrollHorizontal )
951   {
952     mCanScrollHorizontal = canScrollHorizontal;
953     self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_HORIZONTAL, canScrollHorizontal);
954   }
955   if( scrollPositionChanged )
956   {
957     DALI_LOG_SCROLL_STATE("[0x%X] Domain Changed, setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y );
958     self.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPrePosition);
959   }
960   if( domainChanged )
961   {
962     mMinScroll = min;
963     mMaxScroll = max;
964     self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MIN, mMinScroll );
965     self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MAX, mMaxScroll );
966   }
967 }
968
969 void ScrollView::SetScrollSensitive(bool sensitive)
970 {
971   Actor self = Self();
972   PanGestureDetector panGesture( GetPanGestureDetector() );
973
974   DALI_LOG_SCROLL_STATE("[0x%X] sensitive: before:[%d] setting[%d]", this, int(mSensitive), int(sensitive));
975
976   if((!mSensitive) && (sensitive))
977   {
978     mSensitive = sensitive;
979     panGesture.Attach(self);
980   }
981   else if((mSensitive) && (!sensitive))
982   {
983     DALI_LOG_SCROLL_STATE("[0x%X] BEFORE: panning:[%d]", this, int(mPanning));
984
985     // while the scroll view is panning, the state needs to be reset.
986     if ( mPanning )
987     {
988       PanGesture cancelGesture( Gesture::Cancelled );
989       OnPan( cancelGesture );
990     }
991
992     panGesture.Detach(self);
993     mSensitive = sensitive;
994
995     mGestureStackDepth = 0;
996     DALI_LOG_SCROLL_STATE("[0x%X] AFTER: panning:[%d]", this, int(mPanning));
997   }
998 }
999
1000 void ScrollView::SetMaxOvershoot(float overshootX, float overshootY)
1001 {
1002   mMaxOvershoot.x = overshootX;
1003   mMaxOvershoot.y = overshootY;
1004   mUserMaxOvershoot = mMaxOvershoot;
1005   mDefaultMaxOvershoot = false;
1006   UpdateMainInternalConstraint();
1007 }
1008
1009 void ScrollView::SetSnapOvershootAlphaFunction(AlphaFunction alpha)
1010 {
1011   mSnapOvershootAlphaFunction = alpha;
1012 }
1013
1014 void ScrollView::SetSnapOvershootDuration(float duration)
1015 {
1016   mSnapOvershootDuration = duration;
1017 }
1018
1019 void ScrollView::SetActorAutoSnap(bool enable)
1020 {
1021   mActorAutoSnapEnabled = enable;
1022 }
1023
1024 void ScrollView::SetAutoResize(bool enable)
1025 {
1026   mAutoResizeContainerEnabled = enable;
1027   // TODO: This needs a lot of issues to be addressed before working.
1028 }
1029
1030 bool ScrollView::GetWrapMode() const
1031 {
1032   return mWrapMode;
1033 }
1034
1035 void ScrollView::SetWrapMode(bool enable)
1036 {
1037   mWrapMode = enable;
1038   Self().SetProperty(Toolkit::ScrollView::Property::WRAP, enable);
1039 }
1040
1041 int ScrollView::GetScrollUpdateDistance() const
1042 {
1043   return mScrollUpdateDistance;
1044 }
1045
1046 void ScrollView::SetScrollUpdateDistance(int distance)
1047 {
1048   mScrollUpdateDistance = distance;
1049 }
1050
1051 bool ScrollView::GetAxisAutoLock() const
1052 {
1053   return mAxisAutoLock;
1054 }
1055
1056 void ScrollView::SetAxisAutoLock(bool enable)
1057 {
1058   mAxisAutoLock = enable;
1059   UpdateMainInternalConstraint();
1060 }
1061
1062 float ScrollView::GetAxisAutoLockGradient() const
1063 {
1064   return mAxisAutoLockGradient;
1065 }
1066
1067 void ScrollView::SetAxisAutoLockGradient(float gradient)
1068 {
1069   DALI_ASSERT_DEBUG( gradient >= 0.0f && gradient <= 1.0f );
1070   mAxisAutoLockGradient = gradient;
1071   UpdateMainInternalConstraint();
1072 }
1073
1074 float ScrollView::GetFrictionCoefficient() const
1075 {
1076   return mFrictionCoefficient;
1077 }
1078
1079 void ScrollView::SetFrictionCoefficient(float friction)
1080 {
1081   DALI_ASSERT_DEBUG( friction > 0.0f );
1082   mFrictionCoefficient = friction;
1083 }
1084
1085 float ScrollView::GetFlickSpeedCoefficient() const
1086 {
1087   return mFlickSpeedCoefficient;
1088 }
1089
1090 void ScrollView::SetFlickSpeedCoefficient(float speed)
1091 {
1092   mFlickSpeedCoefficient = speed;
1093 }
1094
1095 Vector2 ScrollView::GetMinimumDistanceForFlick() const
1096 {
1097   return mMinFlickDistance;
1098 }
1099
1100 void ScrollView::SetMinimumDistanceForFlick( const Vector2& distance )
1101 {
1102   mMinFlickDistance = distance;
1103 }
1104
1105 float ScrollView::GetMinimumSpeedForFlick() const
1106 {
1107   return mFlickSpeedThreshold;
1108 }
1109
1110 void ScrollView::SetMinimumSpeedForFlick( float speed )
1111 {
1112   mFlickSpeedThreshold = speed;
1113 }
1114
1115 float ScrollView::GetMaxFlickSpeed() const
1116 {
1117   return mMaxFlickSpeed;
1118 }
1119
1120 void ScrollView::SetMaxFlickSpeed(float speed)
1121 {
1122   mMaxFlickSpeed = speed;
1123 }
1124
1125 void ScrollView::SetWheelScrollDistanceStep(Vector2 step)
1126 {
1127   mWheelScrollDistanceStep = step;
1128 }
1129
1130 Vector2 ScrollView::GetWheelScrollDistanceStep() const
1131 {
1132   return mWheelScrollDistanceStep;
1133 }
1134
1135 unsigned int ScrollView::GetCurrentPage() const
1136 {
1137   // in case animation is currently taking place.
1138   Vector2 position = GetPropertyPosition();
1139
1140   Actor self = Self();
1141   unsigned int page = 0;
1142   unsigned int pagesPerVolume = 1;
1143   unsigned int volume = 0;
1144
1145   // if rulerX is enabled, then get page count (columns)
1146   page = mRulerX->GetPageFromPosition(-position.x, mWrapMode);
1147   volume = mRulerY->GetPageFromPosition(-position.y, mWrapMode);
1148   pagesPerVolume = mRulerX->GetTotalPages();
1149
1150   return volume * pagesPerVolume + page;
1151 }
1152
1153 Vector2 ScrollView::GetCurrentScrollPosition() const
1154 {
1155   return -GetPropertyPosition();
1156 }
1157
1158 Vector2 ScrollView::GetDomainSize() const
1159 {
1160   Vector3 size = Self().GetCurrentSize();
1161
1162   const RulerDomain& xDomain = GetRulerX()->GetDomain();
1163   const RulerDomain& yDomain = GetRulerY()->GetDomain();
1164
1165   Vector2 domainSize;
1166   domainSize.x = xDomain.max - xDomain.min - size.x;
1167   domainSize.y = yDomain.max - yDomain.min - size.y;
1168   return domainSize;
1169 }
1170
1171 void ScrollView::TransformTo(const Vector2& position,
1172                              DirectionBias horizontalBias, DirectionBias verticalBias)
1173 {
1174   TransformTo(position, mSnapDuration, mSnapAlphaFunction, horizontalBias, verticalBias);
1175 }
1176
1177 void ScrollView::TransformTo(const Vector2& position, float duration, AlphaFunction alpha,
1178                              DirectionBias horizontalBias, DirectionBias verticalBias)
1179 {
1180   // If this is called while the timer is running, then cancel it
1181   StopTouchDownTimer();
1182
1183   Actor self( Self() );
1184
1185   // Guard against destruction during signal emission
1186   // Note that Emit() methods are called indirectly e.g. from within ScrollView::AnimateTo()
1187   Toolkit::ScrollView handle( GetOwner() );
1188
1189   DALI_LOG_SCROLL_STATE("[0x%X] pos[%.2f,%.2f], duration[%.2f] bias[%d, %d]",
1190     this, position.x, position.y, duration, int(horizontalBias), int(verticalBias));
1191
1192   Vector2 currentScrollPosition = GetCurrentScrollPosition();
1193   self.SetProperty( Toolkit::ScrollView::Property::START_PAGE_POSITION, Vector3(currentScrollPosition) );
1194
1195   if( mScrolling ) // are we interrupting a current scroll?
1196   {
1197     // set mScrolling to false, in case user has code that interrogates mScrolling Getter() in complete.
1198     mScrolling = false;
1199     DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 1 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
1200     mScrollCompletedSignal.Emit( currentScrollPosition );
1201   }
1202
1203   if( mPanning ) // are we interrupting a current pan?
1204   {
1205     DALI_LOG_SCROLL_STATE("[0x%X] Interrupting Pan, set to false", this );
1206     mPanning = false;
1207     mGestureStackDepth = 0;
1208     self.SetProperty( Toolkit::ScrollView::Property::PANNING, false );
1209
1210     if( mScrollMainInternalPrePositionConstraint )
1211     {
1212       mScrollMainInternalPrePositionConstraint.Remove();
1213     }
1214   }
1215
1216   self.SetProperty(Toolkit::ScrollView::Property::SCROLLING, true);
1217   mScrolling = true;
1218
1219   DALI_LOG_SCROLL_STATE("[0x%X] mScrollStartedSignal 1 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
1220   mScrollStartedSignal.Emit( currentScrollPosition );
1221   bool animating = AnimateTo(-position,
1222                              Vector2::ONE * duration,
1223                              alpha,
1224                              true,
1225                              horizontalBias,
1226                              verticalBias,
1227                              Snap);
1228
1229   if(!animating)
1230   {
1231     // if not animating, then this pan has completed right now.
1232     self.SetProperty(Toolkit::ScrollView::Property::SCROLLING, false);
1233     mScrolling = false;
1234
1235     // If we have no duration, then in the next update frame, we will be at the position specified as we just set.
1236     // In this scenario, we cannot return the currentScrollPosition as this is out-of-date and should instead return the requested final position
1237     Vector2 completedPosition( currentScrollPosition );
1238     if( duration <= Math::MACHINE_EPSILON_10 )
1239     {
1240       completedPosition = position;
1241     }
1242
1243     DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 2 [%.2f, %.2f]", this, completedPosition.x, completedPosition.y);
1244     SetScrollUpdateNotification(false);
1245     mScrollCompletedSignal.Emit( completedPosition );
1246   }
1247 }
1248
1249 void ScrollView::ScrollTo(const Vector2& position)
1250 {
1251   ScrollTo(position, mSnapDuration );
1252 }
1253
1254 void ScrollView::ScrollTo(const Vector2& position, float duration)
1255 {
1256   ScrollTo(position, duration, DirectionBiasNone, DirectionBiasNone);
1257 }
1258
1259 void ScrollView::ScrollTo(const Vector2& position, float duration, AlphaFunction alpha)
1260 {
1261   ScrollTo(position, duration, alpha, DirectionBiasNone, DirectionBiasNone);
1262 }
1263
1264 void ScrollView::ScrollTo(const Vector2& position, float duration,
1265                           DirectionBias horizontalBias, DirectionBias verticalBias)
1266 {
1267   ScrollTo(position, duration, mSnapAlphaFunction, horizontalBias, verticalBias);
1268 }
1269
1270 void ScrollView::ScrollTo(const Vector2& position, float duration, AlphaFunction alpha,
1271                 DirectionBias horizontalBias, DirectionBias verticalBias)
1272 {
1273   DALI_LOG_SCROLL_STATE("[0x%X] position[%.2f, %.2f] duration[%.2f], bias[%d, %d]", this, position.x, position.y, duration, int(horizontalBias), int(verticalBias));
1274   TransformTo(position, duration, alpha, horizontalBias, verticalBias);
1275 }
1276
1277 void ScrollView::ScrollTo(unsigned int page)
1278 {
1279   ScrollTo(page, mSnapDuration);
1280 }
1281
1282 void ScrollView::ScrollTo(unsigned int page, float duration, DirectionBias bias)
1283 {
1284   Vector2 position;
1285   unsigned int volume;
1286   unsigned int libraries;
1287
1288   // The position to scroll to is continuous and linear
1289   // unless a domain has been enabled on the X axis.
1290   // or if WrapMode has been enabled.
1291   bool carryX = mRulerX->GetDomain().enabled | mWrapMode;
1292   bool carryY = mRulerY->GetDomain().enabled | mWrapMode;
1293
1294   position.x = mRulerX->GetPositionFromPage(page, volume, carryX);
1295   position.y = mRulerY->GetPositionFromPage(volume, libraries, carryY);
1296
1297   ScrollTo(position, duration, bias, bias);
1298 }
1299
1300 void ScrollView::ScrollTo(Actor &actor)
1301 {
1302   ScrollTo(actor, mSnapDuration);
1303 }
1304
1305 void ScrollView::ScrollTo(Actor &actor, float duration)
1306 {
1307   DALI_ASSERT_ALWAYS(actor.GetParent() == Self());
1308
1309   Actor self = Self();
1310   Vector3 size = self.GetCurrentSize();
1311   Vector3 position = actor.GetCurrentPosition();
1312   Vector2 prePosition = GetPropertyPrePosition();
1313   position.GetVectorXY() -= prePosition;
1314
1315   ScrollTo(Vector2(position.x - size.width * 0.5f, position.y - size.height * 0.5f), duration);
1316 }
1317
1318 Actor ScrollView::FindClosestActor()
1319 {
1320   Actor self = Self();
1321   Vector3 size = self.GetCurrentSize();
1322
1323   return FindClosestActorToPosition(Vector3(size.width * 0.5f,size.height * 0.5f,0.0f));
1324 }
1325
1326 Actor ScrollView::FindClosestActorToPosition(const Vector3& position, FindDirection dirX, FindDirection dirY, FindDirection dirZ)
1327 {
1328   Actor closestChild;
1329   float closestDistance2 = 0.0f;
1330   Vector3 actualPosition = position;
1331
1332   unsigned int numChildren = Self().GetChildCount();
1333
1334   for(unsigned int i = 0; i < numChildren; ++i)
1335   {
1336     Actor child = Self().GetChildAt(i);
1337
1338     if(mInternalActor == child) // ignore internal actor.
1339     {
1340       continue;
1341     }
1342
1343     Vector3 childPosition = GetPositionOfAnchor(child, AnchorPoint::CENTER);
1344
1345     Vector3 delta = childPosition - actualPosition;
1346
1347     // X-axis checking (only find Actors to the [dirX] of actualPosition)
1348     if(dirX > All) // != All,None
1349     {
1350       FindDirection deltaH = delta.x > 0 ? Right : Left;
1351       if(dirX != deltaH)
1352       {
1353         continue;
1354       }
1355     }
1356
1357     // Y-axis checking (only find Actors to the [dirY] of actualPosition)
1358     if(dirY > All) // != All,None
1359     {
1360       FindDirection deltaV = delta.y > 0 ? Down : Up;
1361       if(dirY  != deltaV)
1362       {
1363         continue;
1364       }
1365     }
1366
1367     // Z-axis checking (only find Actors to the [dirZ] of actualPosition)
1368     if(dirZ > All) // != All,None
1369     {
1370       FindDirection deltaV = delta.y > 0 ? In : Out;
1371       if(dirZ  != deltaV)
1372       {
1373         continue;
1374       }
1375     }
1376
1377     // compare child to closest child in terms of distance.
1378     float distance2 = 0.0f;
1379
1380     // distance2 = the Square of the relevant dimensions of delta
1381     if(dirX != None)
1382     {
1383       distance2 += delta.x * delta.x;
1384     }
1385
1386     if(dirY != None)
1387     {
1388       distance2 += delta.y * delta.y;
1389     }
1390
1391     if(dirZ != None)
1392     {
1393       distance2 += delta.z * delta.z;
1394     }
1395
1396     if(closestChild) // Next time.
1397     {
1398       if(distance2 < closestDistance2)
1399       {
1400         closestChild = child;
1401         closestDistance2 = distance2;
1402       }
1403     }
1404     else // First time.
1405     {
1406       closestChild = child;
1407       closestDistance2 = distance2;
1408     }
1409   }
1410
1411   return closestChild;
1412 }
1413
1414 bool ScrollView::ScrollToSnapPoint()
1415 {
1416   DALI_LOG_SCROLL_STATE("[0x%X]", this );
1417   Vector2 stationaryVelocity = Vector2(0.0f, 0.0f);
1418   return SnapWithVelocity( stationaryVelocity );
1419 }
1420
1421 // TODO: In situations where axes are different (X snap, Y free)
1422 // Each axis should really have their own independent animation (time and equation)
1423 // Consider, X axis snapping to nearest grid point (EaseOut over fixed time)
1424 // Consider, Y axis simulating physics to arrive at a point (Physics equation over variable time)
1425 // Currently, the axes have been split however, they both use the same EaseOut equation.
1426 bool ScrollView::SnapWithVelocity(Vector2 velocity)
1427 {
1428   // Animator takes over now, touches are assumed not to interfere.
1429   // And if touches do interfere, then we'll stop animation, update PrePosition
1430   // to current mScroll's properties, and then resume.
1431   // Note: For Flicking this may work a bit different...
1432
1433   float angle = atan2(velocity.y, velocity.x);
1434   float speed2 = velocity.LengthSquared();
1435   AlphaFunction alphaFunction = mSnapAlphaFunction;
1436   Vector2 positionDuration = Vector2::ONE * mSnapDuration;
1437   float biasX = 0.5f;
1438   float biasY = 0.5f;
1439   FindDirection horizontal = None;
1440   FindDirection vertical = None;
1441
1442   // orthoAngleRange = Angle tolerance within the Exact N,E,S,W direction
1443   // that will be accepted as a general N,E,S,W flick direction.
1444
1445   const float orthoAngleRange = FLICK_ORTHO_ANGLE_RANGE * M_PI / 180.0f;
1446   const float flickSpeedThreshold2 = mFlickSpeedThreshold * mFlickSpeedThreshold;
1447
1448   Vector2 positionSnap = mScrollPrePosition;
1449
1450   // Flick logic X Axis
1451
1452   if(mRulerX->IsEnabled() && mLockAxis != LockHorizontal)
1453   {
1454     horizontal = All;
1455
1456     if( speed2 > flickSpeedThreshold2 || // exceeds flick threshold
1457         mInAccessibilityPan ) // With AccessibilityPan its easier to move between snap positions
1458     {
1459       if((angle >= -orthoAngleRange) && (angle < orthoAngleRange)) // Swiping East
1460       {
1461         biasX = 0.0f, horizontal = Left;
1462
1463         // This guards against an error where no movement occurs, due to the flick finishing
1464         // before the update-thread has advanced mScrollPostPosition past the the previous snap point.
1465         positionSnap.x += 1.0f;
1466       }
1467       else if((angle >= M_PI-orthoAngleRange) || (angle < -M_PI+orthoAngleRange)) // Swiping West
1468       {
1469         biasX = 1.0f, horizontal = Right;
1470
1471         // This guards against an error where no movement occurs, due to the flick finishing
1472         // before the update-thread has advanced mScrollPostPosition past the the previous snap point.
1473         positionSnap.x -= 1.0f;
1474       }
1475     }
1476   }
1477
1478   // Flick logic Y Axis
1479
1480   if(mRulerY->IsEnabled() && mLockAxis != LockVertical)
1481   {
1482     vertical = All;
1483
1484     if( speed2 > flickSpeedThreshold2 || // exceeds flick threshold
1485         mInAccessibilityPan ) // With AccessibilityPan its easier to move between snap positions
1486     {
1487       if((angle >= M_PI_2-orthoAngleRange) && (angle < M_PI_2+orthoAngleRange)) // Swiping South
1488       {
1489         biasY = 0.0f, vertical = Up;
1490       }
1491       else if((angle >= -M_PI_2-orthoAngleRange) && (angle < -M_PI_2+orthoAngleRange)) // Swiping North
1492       {
1493         biasY = 1.0f, vertical = Down;
1494       }
1495     }
1496   }
1497
1498   // isFlick: Whether this gesture is a flick or not.
1499   bool isFlick = (horizontal != All || vertical != All);
1500   // isFreeFlick: Whether this gesture is a flick under free panning criteria.
1501   bool isFreeFlick = velocity.LengthSquared() > (FREE_FLICK_SPEED_THRESHOLD*FREE_FLICK_SPEED_THRESHOLD);
1502
1503   if(isFlick || isFreeFlick)
1504   {
1505     positionDuration = Vector2::ONE * mFlickDuration;
1506     alphaFunction = mFlickAlphaFunction;
1507   }
1508
1509   // Calculate next positionSnap ////////////////////////////////////////////////////////////
1510
1511   if(mActorAutoSnapEnabled)
1512   {
1513     Vector3 size = Self().GetCurrentSize();
1514
1515     Actor child = FindClosestActorToPosition( Vector3(size.width * 0.5f,size.height * 0.5f,0.0f), horizontal, vertical );
1516
1517     if(!child && isFlick )
1518     {
1519       // If we conducted a direction limited search and found no actor, then just snap to the closest actor.
1520       child = FindClosestActorToPosition( Vector3(size.width * 0.5f,size.height * 0.5f,0.0f) );
1521     }
1522
1523     if(child)
1524     {
1525       Vector2 position = Self().GetProperty<Vector2>(Toolkit::ScrollView::Property::SCROLL_POSITION);
1526
1527       // Get center-point of the Actor.
1528       Vector3 childPosition = GetPositionOfAnchor(child, AnchorPoint::CENTER);
1529
1530       if(mRulerX->IsEnabled())
1531       {
1532         positionSnap.x = position.x - childPosition.x + size.width * 0.5f;
1533       }
1534       if(mRulerY->IsEnabled())
1535       {
1536         positionSnap.y = position.y - childPosition.y + size.height * 0.5f;
1537       }
1538     }
1539   }
1540
1541   Vector2 startPosition = positionSnap;
1542   positionSnap.x = -mRulerX->Snap(-positionSnap.x, biasX);  // NOTE: X & Y rulers think in -ve coordinate system.
1543   positionSnap.y = -mRulerY->Snap(-positionSnap.y, biasY);  // That is scrolling RIGHT (e.g. 100.0, 0.0) means moving LEFT.
1544
1545   Vector2 clampDelta(Vector2::ZERO);
1546   ClampPosition(positionSnap);
1547
1548   if( (mRulerX->GetType() == Ruler::Free || mRulerY->GetType() == Ruler::Free)
1549       && isFreeFlick && !mActorAutoSnapEnabled)
1550   {
1551     // Calculate target position based on velocity of flick.
1552
1553     // a = Deceleration (Set to diagonal stage length * friction coefficient)
1554     // u = Initial Velocity (Flick velocity)
1555     // v = 0 (Final Velocity)
1556     // t = Time (Velocity / Deceleration)
1557     Vector2 stageSize = Stage::GetCurrent().GetSize();
1558     float stageLength = Vector3(stageSize.x, stageSize.y, 0.0f).Length();
1559     float a = (stageLength * mFrictionCoefficient);
1560     Vector3 u = Vector3(velocity.x, velocity.y, 0.0f) * mFlickSpeedCoefficient;
1561     float speed = u.Length();
1562     u/= speed;
1563
1564     // TODO: Change this to a decay function. (faster you flick, the slower it should be)
1565     speed = std::min(speed, stageLength * mMaxFlickSpeed );
1566     u*= speed;
1567     alphaFunction = ConstantDecelerationAlphaFunction;
1568
1569     float t = speed / a;
1570
1571     if(mRulerX->IsEnabled() && mRulerX->GetType() == Ruler::Free)
1572     {
1573       positionSnap.x += t*u.x*0.5f;
1574     }
1575
1576     if(mRulerY->IsEnabled() && mRulerY->GetType() == Ruler::Free)
1577     {
1578       positionSnap.y += t*u.y*0.5f;
1579     }
1580
1581     clampDelta = positionSnap;
1582     ClampPosition(positionSnap);
1583     if((positionSnap - startPosition).LengthSquared() > Math::MACHINE_EPSILON_0)
1584     {
1585       clampDelta -= positionSnap;
1586       clampDelta.x = clampDelta.x > 0.0f ? std::min(clampDelta.x, mMaxOvershoot.x) : std::max(clampDelta.x, -mMaxOvershoot.x);
1587       clampDelta.y = clampDelta.y > 0.0f ? std::min(clampDelta.y, mMaxOvershoot.y) : std::max(clampDelta.y, -mMaxOvershoot.y);
1588     }
1589     else
1590     {
1591       clampDelta = Vector2::ZERO;
1592     }
1593
1594     // If Axis is Free and has velocity, then calculate time taken
1595     // to reach target based on velocity in axis.
1596     if(mRulerX->IsEnabled() && mRulerX->GetType() == Ruler::Free)
1597     {
1598       float deltaX = fabsf(startPosition.x - positionSnap.x);
1599
1600       if(fabsf(u.x) > Math::MACHINE_EPSILON_1)
1601       {
1602         positionDuration.x = fabsf(deltaX / u.x);
1603       }
1604       else
1605       {
1606         positionDuration.x = 0;
1607       }
1608     }
1609
1610     if(mRulerY->IsEnabled() && mRulerY->GetType() == Ruler::Free)
1611     {
1612       float deltaY = fabsf(startPosition.y - positionSnap.y);
1613
1614       if(fabsf(u.y) > Math::MACHINE_EPSILON_1)
1615       {
1616         positionDuration.y = fabsf(deltaY / u.y);
1617       }
1618       else
1619       {
1620         positionDuration.y = 0;
1621       }
1622     }
1623   }
1624
1625   if(IsOvershootEnabled())
1626   {
1627     // Scroll to the end of the overshoot only when overshoot is enabled.
1628     positionSnap += clampDelta;
1629   }
1630
1631   bool animating = AnimateTo(positionSnap, positionDuration,
1632                              alphaFunction, false,
1633                              DirectionBiasNone, DirectionBiasNone,
1634                              isFlick || isFreeFlick ? Flick : Snap);
1635
1636   return animating;
1637 }
1638
1639 void ScrollView::StopAnimation(void)
1640 {
1641   // Clear Snap animation if exists.
1642   StopAnimation(mInternalXAnimation);
1643   StopAnimation(mInternalYAnimation);
1644   mScrollStateFlags = 0;
1645   // remove scroll animation flags
1646   HandleStoppedAnimation();
1647 }
1648
1649 void ScrollView::StopAnimation(Animation& animation)
1650 {
1651   if(animation)
1652   {
1653     animation.Stop();
1654     animation.Reset();
1655   }
1656 }
1657
1658 bool ScrollView::AnimateTo(const Vector2& position, const Vector2& positionDuration,
1659                            AlphaFunction alpha, bool findShortcuts,
1660                            DirectionBias horizontalBias, DirectionBias verticalBias,
1661                            SnapType snapType)
1662 {
1663   // Here we perform an animation on a number of properties (depending on which have changed)
1664   // The animation is applied to all ScrollBases
1665   Actor self = Self();
1666   mScrollTargetPosition = position;
1667   float totalDuration = 0.0f;
1668
1669   bool positionChanged = (mScrollTargetPosition != mScrollPostPosition);
1670
1671   if(positionChanged)
1672   {
1673     totalDuration = std::max(totalDuration, positionDuration.x);
1674     totalDuration = std::max(totalDuration, positionDuration.y);
1675   }
1676   else
1677   {
1678     // try to animate for a frame, on some occasions update will be changing scroll value while event side thinks it hasnt changed
1679     totalDuration = 0.01f;
1680     positionChanged = true;
1681   }
1682
1683   StopAnimation();
1684
1685   // Position Delta ///////////////////////////////////////////////////////
1686   if(positionChanged)
1687   {
1688     if(mWrapMode && findShortcuts)
1689     {
1690       // In Wrap Mode, the shortest distance is a little less intuitive...
1691       const RulerDomain rulerDomainX = mRulerX->GetDomain();
1692       const RulerDomain rulerDomainY = mRulerY->GetDomain();
1693
1694       if(mRulerX->IsEnabled())
1695       {
1696         float dir = VectorInDomain(-mScrollPrePosition.x, -mScrollTargetPosition.x, rulerDomainX.min, rulerDomainX.max, horizontalBias);
1697         mScrollTargetPosition.x = mScrollPrePosition.x + -dir;
1698       }
1699
1700       if(mRulerY->IsEnabled())
1701       {
1702         float dir = VectorInDomain(-mScrollPrePosition.y, -mScrollTargetPosition.y, rulerDomainY.min, rulerDomainY.max, verticalBias);
1703         mScrollTargetPosition.y = mScrollPrePosition.y + -dir;
1704       }
1705     }
1706
1707     // note we have two separate animations for X & Y, this deals with sliding diagonally and hitting
1708     // a horizonal/vertical wall.delay
1709     AnimateInternalXTo(mScrollTargetPosition.x, positionDuration.x, alpha);
1710     AnimateInternalYTo(mScrollTargetPosition.y, positionDuration.y, alpha);
1711
1712     if( !(mScrollStateFlags & SCROLL_ANIMATION_FLAGS) )
1713     {
1714       DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollTargetPosition.x, mScrollTargetPosition.y );
1715       self.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollTargetPosition);
1716       mScrollPrePosition = mScrollTargetPosition;
1717       mScrollPostPosition = mScrollTargetPosition;
1718       WrapPosition(mScrollPostPosition);
1719     }
1720
1721     DALI_LOG_SCROLL_STATE("[0x%X] position-changed, mScrollTargetPosition[%.2f, %.2f], mScrollPrePosition[%.2f, %.2f], mScrollPostPosition[%.2f, %.2f]", this, mScrollTargetPosition.x, mScrollTargetPosition.y, mScrollPrePosition.x, mScrollPrePosition.y, mScrollPostPosition.x, mScrollPostPosition.y );
1722     DALI_LOG_SCROLL_STATE("[0x%X] SCROLL_PRE_POSITION[%.2f, %.2f], SCROLL_POSITION[%.2f, %.2f]", this, self.GetProperty( Toolkit::ScrollView::Property::SCROLL_PRE_POSITION ).Get<Vector2>().x, self.GetProperty( Toolkit::ScrollView::Property::SCROLL_PRE_POSITION ).Get<Vector2>().y, self.GetProperty( Toolkit::ScrollView::Property::SCROLL_POSITION ).Get<Vector2>().x, self.GetProperty( Toolkit::ScrollView::Property::SCROLL_POSITION ).Get<Vector2>().y );
1723   }
1724
1725   SetScrollUpdateNotification(true);
1726
1727   // Always send a snap event when AnimateTo is called.
1728   Toolkit::ScrollView::SnapEvent snapEvent;
1729   snapEvent.type = snapType;
1730   snapEvent.position = -mScrollTargetPosition;
1731   snapEvent.duration = totalDuration;
1732
1733   DALI_LOG_SCROLL_STATE("[0x%X] mSnapStartedSignal [%.2f, %.2f]", this, snapEvent.position.x, snapEvent.position.y);
1734   mSnapStartedSignal.Emit( snapEvent );
1735
1736   return (mScrollStateFlags & SCROLL_ANIMATION_FLAGS) != 0;
1737 }
1738
1739 void ScrollView::EnableScrollOvershoot(bool enable)
1740 {
1741   if (enable)
1742   {
1743     if (!mOvershootIndicator)
1744     {
1745       mOvershootIndicator = ScrollOvershootIndicator::New();
1746     }
1747
1748     mOvershootIndicator->AttachToScrollable(*this);
1749   }
1750   else
1751   {
1752     mMaxOvershoot = mUserMaxOvershoot;
1753
1754     if (mOvershootIndicator)
1755     {
1756       mOvershootIndicator->DetachFromScrollable(*this);
1757     }
1758   }
1759
1760   UpdateMainInternalConstraint();
1761 }
1762
1763 void ScrollView::AddOverlay(Actor actor)
1764 {
1765   actor.SetDrawMode( DrawMode::OVERLAY_2D );
1766   mInternalActor.Add( actor );
1767 }
1768
1769 void ScrollView::RemoveOverlay(Actor actor)
1770 {
1771   mInternalActor.Remove( actor );
1772 }
1773
1774 void ScrollView::SetOvershootEffectColor( const Vector4& color )
1775 {
1776   mOvershootEffectColor = color;
1777   if( mOvershootIndicator )
1778   {
1779     mOvershootIndicator->SetOvershootEffectColor( color );
1780   }
1781 }
1782
1783 void ScrollView::SetScrollingDirection( Radian direction, Radian threshold )
1784 {
1785   PanGestureDetector panGesture( GetPanGestureDetector() );
1786
1787   // First remove just in case we have some set, then add.
1788   panGesture.RemoveDirection( direction );
1789   panGesture.AddDirection( direction, threshold );
1790 }
1791
1792 void ScrollView::RemoveScrollingDirection( Radian direction )
1793 {
1794   PanGestureDetector panGesture( GetPanGestureDetector() );
1795   panGesture.RemoveDirection( direction );
1796 }
1797
1798 Toolkit::ScrollView::SnapStartedSignalType& ScrollView::SnapStartedSignal()
1799 {
1800   return mSnapStartedSignal;
1801 }
1802
1803 void ScrollView::FindAndUnbindActor(Actor child)
1804 {
1805   UnbindActor(child);
1806 }
1807
1808 Vector2 ScrollView::GetPropertyPrePosition() const
1809 {
1810   Vector2 position = Self().GetProperty<Vector2>(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION);
1811   WrapPosition(position);
1812   return position;
1813 }
1814
1815 Vector2 ScrollView::GetPropertyPosition() const
1816 {
1817   Vector2 position = Self().GetProperty<Vector2>(Toolkit::ScrollView::Property::SCROLL_POSITION);
1818   WrapPosition(position);
1819
1820   return position;
1821 }
1822
1823 void ScrollView::HandleStoppedAnimation()
1824 {
1825   SetScrollUpdateNotification(false);
1826 }
1827
1828 void ScrollView::HandleSnapAnimationFinished()
1829 {
1830   // Emit Signal that scrolling has completed.
1831   mScrolling = false;
1832   Actor self = Self();
1833   self.SetProperty(Toolkit::ScrollView::Property::SCROLLING, false);
1834
1835   Vector2 deltaPosition(mScrollPrePosition);
1836
1837   UpdateLocalScrollProperties();
1838   WrapPosition(mScrollPrePosition);
1839   DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y );
1840   self.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPrePosition);
1841
1842   Vector2 currentScrollPosition = GetCurrentScrollPosition();
1843   DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 3 current[%.2f, %.2f], mScrollTargetPosition[%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y, -mScrollTargetPosition.x, -mScrollTargetPosition.y );
1844   mScrollCompletedSignal.Emit( currentScrollPosition );
1845
1846   mDomainOffset += deltaPosition - mScrollPostPosition;
1847   self.SetProperty(Toolkit::ScrollView::Property::SCROLL_DOMAIN_OFFSET, mDomainOffset);
1848   HandleStoppedAnimation();
1849 }
1850
1851 void ScrollView::SetScrollUpdateNotification( bool enabled )
1852 {
1853   Actor self = Self();
1854   if( mScrollXUpdateNotification )
1855   {
1856     // disconnect now to avoid a notification before removed from update thread
1857     mScrollXUpdateNotification.NotifySignal().Disconnect(this, &ScrollView::OnScrollUpdateNotification);
1858     self.RemovePropertyNotification(mScrollXUpdateNotification);
1859     mScrollXUpdateNotification.Reset();
1860   }
1861   if( enabled && !mScrollUpdatedSignal.Empty())
1862   {
1863     // Only set up the notification when the application has connected to the updated signal
1864     mScrollXUpdateNotification = self.AddPropertyNotification(Toolkit::ScrollView::Property::SCROLL_POSITION, 0, StepCondition(mScrollUpdateDistance, 0.0f));
1865     mScrollXUpdateNotification.NotifySignal().Connect( this, &ScrollView::OnScrollUpdateNotification );
1866   }
1867   if( mScrollYUpdateNotification )
1868   {
1869     // disconnect now to avoid a notification before removed from update thread
1870     mScrollYUpdateNotification.NotifySignal().Disconnect(this, &ScrollView::OnScrollUpdateNotification);
1871     self.RemovePropertyNotification(mScrollYUpdateNotification);
1872     mScrollYUpdateNotification.Reset();
1873   }
1874   if( enabled && !mScrollUpdatedSignal.Empty())
1875   {
1876     // Only set up the notification when the application has connected to the updated signal
1877     mScrollYUpdateNotification = self.AddPropertyNotification(Toolkit::ScrollView::Property::SCROLL_POSITION, 1, StepCondition(mScrollUpdateDistance, 0.0f));
1878     mScrollYUpdateNotification.NotifySignal().Connect( this, &ScrollView::OnScrollUpdateNotification );
1879   }
1880 }
1881
1882 void ScrollView::OnScrollUpdateNotification(Dali::PropertyNotification& source)
1883 {
1884   // Guard against destruction during signal emission
1885   Toolkit::ScrollView handle( GetOwner() );
1886
1887   Vector2 currentScrollPosition = GetCurrentScrollPosition();
1888   mScrollUpdatedSignal.Emit( currentScrollPosition );
1889 }
1890
1891 bool ScrollView::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
1892 {
1893   Dali::BaseHandle handle( object );
1894
1895   bool connected( true );
1896   Toolkit::ScrollView view = Toolkit::ScrollView::DownCast( handle );
1897
1898   if( 0 == strcmp( signalName.c_str(), SIGNAL_SNAP_STARTED ) )
1899   {
1900     view.SnapStartedSignal().Connect( tracker, functor );
1901   }
1902   else
1903   {
1904     // signalName does not match any signal
1905     connected = false;
1906   }
1907
1908   return connected;
1909 }
1910
1911 void ScrollView::OnSizeAnimation(Animation& animation, const Vector3& targetSize)
1912 {
1913   // need to update domain properties for new size
1914   UpdatePropertyDomain();
1915 }
1916
1917 void ScrollView::OnSizeSet( const Vector3& size )
1918 {
1919   // need to update domain properties for new size
1920   if( mDefaultMaxOvershoot )
1921   {
1922     mUserMaxOvershoot.x = size.x * 0.5f;
1923     mUserMaxOvershoot.y = size.y * 0.5f;
1924     if( !IsOvershootEnabled() )
1925     {
1926       mMaxOvershoot = mUserMaxOvershoot;
1927     }
1928   }
1929   UpdatePropertyDomain();
1930   UpdateMainInternalConstraint();
1931   if( IsOvershootEnabled() )
1932   {
1933     mOvershootIndicator->Reset();
1934   }
1935 }
1936
1937 void ScrollView::OnChildAdd(Actor& child)
1938 {
1939   Dali::Toolkit::ScrollBar scrollBar = Dali::Toolkit::ScrollBar::DownCast(child);
1940   if(scrollBar)
1941   {
1942     mInternalActor.Add(scrollBar);
1943     if(scrollBar.GetScrollDirection() == Toolkit::ScrollBar::Horizontal)
1944     {
1945       scrollBar.SetScrollPropertySource(Self(),
1946                                         Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_X,
1947                                         Toolkit::Scrollable::Property::SCROLL_POSITION_MIN_X,
1948                                         Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_MAX_X,
1949                                         Toolkit::ScrollView::Property::SCROLL_DOMAIN_SIZE_X);
1950     }
1951     else
1952     {
1953       scrollBar.SetScrollPropertySource(Self(),
1954                                         Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_Y,
1955                                         Toolkit::Scrollable::Property::SCROLL_POSITION_MIN_Y,
1956                                         Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_MAX_Y,
1957                                         Toolkit::ScrollView::Property::SCROLL_DOMAIN_SIZE_Y);
1958     }
1959   }
1960   else if(mAlterChild)
1961   {
1962     BindActor(child);
1963   }
1964 }
1965
1966 void ScrollView::OnChildRemove(Actor& child)
1967 {
1968   // TODO: Actor needs a RemoveConstraint method to take out an individual constraint.
1969   UnbindActor(child);
1970 }
1971
1972 void ScrollView::StartTouchDownTimer()
1973 {
1974   if ( !mTouchDownTimer )
1975   {
1976     mTouchDownTimer = Timer::New( TOUCH_DOWN_TIMER_INTERVAL );
1977     mTouchDownTimer.TickSignal().Connect( this, &ScrollView::OnTouchDownTimeout );
1978   }
1979
1980   mTouchDownTimer.Start();
1981 }
1982
1983 void ScrollView::StopTouchDownTimer()
1984 {
1985   if ( mTouchDownTimer )
1986   {
1987     mTouchDownTimer.Stop();
1988   }
1989 }
1990
1991 bool ScrollView::OnTouchDownTimeout()
1992 {
1993   DALI_LOG_SCROLL_STATE("[0x%X]", this);
1994
1995   mTouchDownTimeoutReached = true;
1996
1997   unsigned int currentScrollStateFlags( mScrollStateFlags ); // Cleared in StopAnimation so keep local copy for comparison
1998   if( currentScrollStateFlags & (SCROLL_ANIMATION_FLAGS | SNAP_ANIMATION_FLAGS) )
1999   {
2000     DALI_LOG_SCROLL_STATE("[0x%X] Scrolling Or snapping flags set, stopping animation", this);
2001
2002     StopAnimation();
2003     if( currentScrollStateFlags & SCROLL_ANIMATION_FLAGS )
2004     {
2005       DALI_LOG_SCROLL_STATE("[0x%X] Scrolling flags set, emitting signal", this);
2006
2007       mScrollInterrupted = true;
2008       // reset domain offset as scrolling from original plane.
2009       mDomainOffset = Vector2::ZERO;
2010       Self().SetProperty(Toolkit::ScrollView::Property::SCROLL_DOMAIN_OFFSET, Vector2::ZERO);
2011
2012       UpdateLocalScrollProperties();
2013       Vector2 currentScrollPosition = GetCurrentScrollPosition();
2014       DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 4 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
2015       mScrollCompletedSignal.Emit( currentScrollPosition );
2016     }
2017   }
2018
2019   return false;
2020 }
2021
2022 bool ScrollView::OnTouchEvent(const TouchEvent& event)
2023 {
2024   if(!mSensitive)
2025   {
2026     DALI_LOG_SCROLL_STATE("[0x%X], Not Sensitive, ignoring", this);
2027
2028     // Ignore this touch event, if scrollview is insensitive.
2029     return false;
2030   }
2031
2032   // Ignore events with multiple-touch points
2033   if (event.GetPointCount() != 1)
2034   {
2035     DALI_LOG_SCROLL_STATE("[0x%X], multiple touch, ignoring", this);
2036
2037     return false;
2038   }
2039
2040   const TouchPoint::State pointState = event.GetPoint(0).state;
2041   if( pointState == TouchPoint::Down )
2042   {
2043     DALI_LOG_SCROLL_STATE("[0x%X] Down", this);
2044
2045     if(mGestureStackDepth==0)
2046     {
2047       mTouchDownTime = event.time;
2048
2049       // This allows time for a pan-gesture to start, to avoid breaking snap-animation behavior with fast flicks.
2050       // If touch-down does not become a pan (after timeout interval), then snap-animation can be interrupted.
2051       mTouchDownTimeoutReached = false;
2052       mScrollInterrupted = false;
2053       StartTouchDownTimer();
2054     }
2055   }
2056   else if( ( pointState == TouchPoint::Up ) ||
2057            ( ( pointState == TouchPoint::Interrupted ) && ( event.GetPoint(0).hitActor == Self() ) ) )
2058   {
2059     DALI_LOG_SCROLL_STATE("[0x%X] %s", this, ( ( pointState == TouchPoint::Up ) ? "Up" : "Interrupted" ) );
2060
2061     StopTouchDownTimer();
2062
2063     // if the user touches and releases without enough movement to go
2064     // into a gesture state, then we should snap to nearest point.
2065     // otherwise our scroll could be stopped (interrupted) half way through an animation.
2066     if(mGestureStackDepth==0 && mTouchDownTimeoutReached)
2067     {
2068       if( ( event.GetPoint(0).state == TouchPoint::Interrupted ) ||
2069           ( ( event.time - mTouchDownTime ) >= MINIMUM_TIME_BETWEEN_DOWN_AND_UP_FOR_RESET ) )
2070       {
2071         // Reset the velocity only if down was received a while ago
2072         mLastVelocity = Vector2( 0.0f, 0.0f );
2073       }
2074
2075       UpdateLocalScrollProperties();
2076       // Only finish the transform if scrolling was interrupted on down or if we are scrolling
2077       if ( mScrollInterrupted || mScrolling )
2078       {
2079         DALI_LOG_SCROLL_STATE("[0x%X] Calling FinishTransform", this);
2080
2081         FinishTransform();
2082       }
2083     }
2084     mTouchDownTimeoutReached = false;
2085     mScrollInterrupted = false;
2086   }
2087
2088   return true;
2089 }
2090
2091 bool ScrollView::OnWheelEvent(const WheelEvent& event)
2092 {
2093   if(!mSensitive)
2094   {
2095     // Ignore this wheel event, if scrollview is insensitive.
2096     return false;
2097   }
2098
2099   Vector2 targetScrollPosition = GetPropertyPosition();
2100
2101   if(mRulerX->IsEnabled() && !mRulerY->IsEnabled())
2102   {
2103     // If only the ruler in the X axis is enabled, scroll in the X axis.
2104     if(mRulerX->GetType() == Ruler::Free)
2105     {
2106       // Free panning mode
2107       targetScrollPosition.x += event.z * mWheelScrollDistanceStep.x;
2108       ClampPosition(targetScrollPosition);
2109       ScrollTo(-targetScrollPosition);
2110     }
2111     else if(!mScrolling)
2112     {
2113       // Snap mode, only respond to the event when the previous snap animation is finished.
2114       ScrollTo(GetCurrentPage() - event.z);
2115     }
2116   }
2117   else
2118   {
2119     // If the ruler in the Y axis is enabled, scroll in the Y axis.
2120     if(mRulerY->GetType() == Ruler::Free)
2121     {
2122       // Free panning mode
2123       targetScrollPosition.y += event.z * mWheelScrollDistanceStep.y;
2124       ClampPosition(targetScrollPosition);
2125       ScrollTo(-targetScrollPosition);
2126     }
2127     else if(!mScrolling)
2128     {
2129       // Snap mode, only respond to the event when the previous snap animation is finished.
2130       ScrollTo(GetCurrentPage() - event.z * mRulerX->GetTotalPages());
2131     }
2132   }
2133
2134   return true;
2135 }
2136
2137 void ScrollView::ResetScrolling()
2138 {
2139   Actor self = Self();
2140   self.GetProperty(Toolkit::ScrollView::Property::SCROLL_POSITION).Get(mScrollPostPosition);
2141   mScrollPrePosition = mScrollPostPosition;
2142   DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPostPosition.x, mScrollPostPosition.y );
2143   self.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPostPosition);
2144 }
2145
2146 void ScrollView::UpdateLocalScrollProperties()
2147 {
2148   Actor self = Self();
2149   self.GetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION).Get(mScrollPrePosition);
2150   self.GetProperty(Toolkit::ScrollView::Property::SCROLL_POSITION).Get(mScrollPostPosition);
2151 }
2152
2153 // private functions
2154
2155 void ScrollView::PreAnimatedScrollSetup()
2156 {
2157   // SCROLL_PRE_POSITION is our unclamped property with wrapping
2158   // SCROLL_POSITION is our final scroll position after clamping
2159
2160   Actor self = Self();
2161
2162   Vector2 deltaPosition(mScrollPostPosition);
2163   WrapPosition(mScrollPostPosition);
2164   mDomainOffset += deltaPosition - mScrollPostPosition;
2165   Self().SetProperty(Toolkit::ScrollView::Property::SCROLL_DOMAIN_OFFSET, mDomainOffset);
2166
2167   if( mScrollStateFlags & SCROLL_X_STATE_MASK )
2168   {
2169     // already performing animation on internal x position
2170     StopAnimation(mInternalXAnimation);
2171   }
2172
2173   if( mScrollStateFlags & SCROLL_Y_STATE_MASK )
2174   {
2175     // already performing animation on internal y position
2176     StopAnimation(mInternalYAnimation);
2177   }
2178
2179   mScrollStateFlags = 0;
2180
2181   // Update Actor position with this wrapped value.
2182 }
2183
2184 void ScrollView::FinaliseAnimatedScroll()
2185 {
2186   // TODO - common animation finishing code in here
2187 }
2188
2189 void ScrollView::AnimateInternalXTo( float position, float duration, AlphaFunction alpha )
2190 {
2191   StopAnimation(mInternalXAnimation);
2192
2193   if( duration > Math::MACHINE_EPSILON_10 )
2194   {
2195     Actor self = Self();
2196     DALI_LOG_SCROLL_STATE("[0x%X], Animating from[%.2f] to[%.2f]", this, self.GetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION).Get<Vector2>().x, position );
2197     mInternalXAnimation = Animation::New(duration);
2198     DALI_LOG_SCROLL_STATE("[0x%X], mInternalXAnimation[0x%X]", this, mInternalXAnimation.GetObjectPtr() );
2199     mInternalXAnimation.FinishedSignal().Connect(this, &ScrollView::OnScrollAnimationFinished);
2200     mInternalXAnimation.AnimateTo( Property(self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, 0), position, alpha, TimePeriod(duration));
2201     mInternalXAnimation.Play();
2202
2203     // erase current state flags
2204     mScrollStateFlags &= ~SCROLL_X_STATE_MASK;
2205     // add internal animation state flag
2206     mScrollStateFlags |= AnimatingInternalX;
2207   }
2208 }
2209
2210 void ScrollView::AnimateInternalYTo( float position, float duration, AlphaFunction alpha )
2211 {
2212   StopAnimation(mInternalYAnimation);
2213
2214   if( duration > Math::MACHINE_EPSILON_10 )
2215   {
2216     Actor self = Self();
2217     DALI_LOG_SCROLL_STATE("[0x%X], Animating from[%.2f] to[%.2f]", this, self.GetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION).Get<Vector2>().y, position );
2218     mInternalYAnimation = Animation::New(duration);
2219     DALI_LOG_SCROLL_STATE("[0x%X], mInternalYAnimation[0x%X]", this, mInternalYAnimation.GetObjectPtr() );
2220     mInternalYAnimation.FinishedSignal().Connect(this, &ScrollView::OnScrollAnimationFinished);
2221     mInternalYAnimation.AnimateTo( Property(self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, 1), position, alpha, TimePeriod(duration));
2222     mInternalYAnimation.Play();
2223
2224     // erase current state flags
2225     mScrollStateFlags &= ~SCROLL_Y_STATE_MASK;
2226     // add internal animation state flag
2227     mScrollStateFlags |= AnimatingInternalY;
2228   }
2229 }
2230
2231 void ScrollView::OnScrollAnimationFinished( Animation& source )
2232 {
2233   // Guard against destruction during signal emission
2234   // Note that ScrollCompletedSignal is emitted from HandleSnapAnimationFinished()
2235   Toolkit::ScrollView handle( GetOwner() );
2236
2237   bool scrollingFinished = false;
2238
2239   // update our local scroll positions
2240   UpdateLocalScrollProperties();
2241
2242   if( source == mInternalXAnimation )
2243   {
2244     DALI_LOG_SCROLL_STATE("[0x%X] mInternalXAnimation[0x%X], expected[%.2f], actual[%.2f], post[%.2f]", this, mInternalXAnimation.GetObjectPtr(), mScrollTargetPosition.x, Self().GetProperty(SCROLL_PRE_POSITION).Get<Vector2>().x, mScrollPostPosition.x );
2245
2246     if( !(mScrollStateFlags & AnimatingInternalY) )
2247     {
2248       scrollingFinished = true;
2249     }
2250     mInternalXAnimation.Reset();
2251     // wrap pre scroll x position and set it
2252     if( mWrapMode )
2253     {
2254       const RulerDomain rulerDomain = mRulerX->GetDomain();
2255       mScrollPrePosition.x = -WrapInDomain(-mScrollPrePosition.x, rulerDomain.min, rulerDomain.max);
2256       DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y );
2257       handle.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPrePosition);
2258     }
2259     SnapInternalXTo(mScrollPostPosition.x);
2260   }
2261
2262   if( source == mInternalYAnimation )
2263   {
2264     DALI_LOG_SCROLL_STATE("[0x%X] mInternalYAnimation[0x%X], expected[%.2f], actual[%.2f], post[%.2f]", this, mInternalYAnimation.GetObjectPtr(), mScrollTargetPosition.y, Self().GetProperty(SCROLL_PRE_POSITION).Get<Vector2>().y, mScrollPostPosition.y );
2265
2266     if( !(mScrollStateFlags & AnimatingInternalX) )
2267     {
2268       scrollingFinished = true;
2269     }
2270     mInternalYAnimation.Reset();
2271     if( mWrapMode )
2272     {
2273       // wrap pre scroll y position and set it
2274       const RulerDomain rulerDomain = mRulerY->GetDomain();
2275       mScrollPrePosition.y = -WrapInDomain(-mScrollPrePosition.y, rulerDomain.min, rulerDomain.max);
2276       DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y );
2277       handle.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPrePosition);
2278     }
2279     SnapInternalYTo(mScrollPostPosition.y);
2280   }
2281
2282   DALI_LOG_SCROLL_STATE("[0x%X] scrollingFinished[%d] Animation[0x%X]", this, scrollingFinished, source.GetObjectPtr());
2283
2284   if(scrollingFinished)
2285   {
2286     HandleSnapAnimationFinished();
2287   }
2288 }
2289
2290 void ScrollView::OnSnapInternalPositionFinished( Animation& source )
2291 {
2292   Actor self = Self();
2293   UpdateLocalScrollProperties();
2294   if( source == mInternalXAnimation )
2295   {
2296     DALI_LOG_SCROLL_STATE("[0x%X] Finished X PostPosition Animation", this );
2297
2298     // clear internal x animation flags
2299     mScrollStateFlags &= ~SCROLL_X_STATE_MASK;
2300     mInternalXAnimation.Reset();
2301     WrapPosition(mScrollPrePosition);
2302   }
2303   if( source == mInternalYAnimation )
2304   {
2305     DALI_LOG_SCROLL_STATE("[0x%X] Finished Y PostPosition Animation", this );
2306
2307     mScrollStateFlags &= ~SCROLL_Y_STATE_MASK;
2308     mInternalYAnimation.Reset();
2309     WrapPosition(mScrollPrePosition);
2310   }
2311 }
2312
2313 void ScrollView::SnapInternalXTo(float position)
2314 {
2315   Actor self = Self();
2316
2317   StopAnimation(mInternalXAnimation);
2318
2319   // erase current state flags
2320   mScrollStateFlags &= ~SCROLL_X_STATE_MASK;
2321
2322   // if internal x not equal to inputed parameter, animate it
2323   float duration = std::min(fabsf((position - mScrollPrePosition.x) / mMaxOvershoot.x) * mSnapOvershootDuration, mSnapOvershootDuration);
2324   DALI_LOG_SCROLL_STATE("[0x%X] duration[%.2f]", this, duration );
2325   if( duration > Math::MACHINE_EPSILON_1 )
2326   {
2327     DALI_LOG_SCROLL_STATE("[0x%X] Starting X Snap Animation to[%.2f]", this, position );
2328
2329     mInternalXAnimation = Animation::New(duration);
2330     mInternalXAnimation.FinishedSignal().Connect(this, &ScrollView::OnSnapInternalPositionFinished);
2331     mInternalXAnimation.AnimateTo(Property(self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, 0), position);
2332     mInternalXAnimation.Play();
2333
2334     // add internal animation state flag
2335     mScrollStateFlags |= SnappingInternalX;
2336   }
2337 }
2338
2339 void ScrollView::SnapInternalYTo(float position)
2340 {
2341   Actor self = Self();
2342
2343   StopAnimation(mInternalYAnimation);
2344
2345   // erase current state flags
2346   mScrollStateFlags &= ~SCROLL_Y_STATE_MASK;
2347
2348   // if internal y not equal to inputed parameter, animate it
2349   float duration = std::min(fabsf((position - mScrollPrePosition.y) / mMaxOvershoot.y) * mSnapOvershootDuration, mSnapOvershootDuration);
2350   DALI_LOG_SCROLL_STATE("[0x%X] duration[%.2f]", this, duration );
2351   if( duration > Math::MACHINE_EPSILON_1 )
2352   {
2353     DALI_LOG_SCROLL_STATE("[0x%X] Starting Y Snap Animation to[%.2f]", this, position );
2354
2355     mInternalYAnimation = Animation::New(duration);
2356     mInternalYAnimation.FinishedSignal().Connect(this, &ScrollView::OnSnapInternalPositionFinished);
2357     mInternalYAnimation.AnimateTo(Property(self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, 1), position);
2358     mInternalYAnimation.Play();
2359
2360     // add internal animation state flag
2361     mScrollStateFlags |= SnappingInternalY;
2362   }
2363 }
2364
2365 void ScrollView::GestureStarted()
2366 {
2367   // we handle the first gesture.
2368   // if we're currently doing a gesture and receive another
2369   // we continue and combine the effects of the gesture instead of reseting.
2370   if(mGestureStackDepth++==0)
2371   {
2372     Actor self = Self();
2373     StopTouchDownTimer();
2374     StopAnimation();
2375     mPanDelta = Vector2::ZERO;
2376     mLastVelocity = Vector2::ZERO;
2377     if( !mScrolling )
2378     {
2379       mLockAxis = LockPossible;
2380     }
2381
2382     if( mScrollStateFlags & SCROLL_X_STATE_MASK )
2383     {
2384       StopAnimation(mInternalXAnimation);
2385     }
2386     if( mScrollStateFlags & SCROLL_Y_STATE_MASK )
2387     {
2388       StopAnimation(mInternalYAnimation);
2389     }
2390     mScrollStateFlags = 0;
2391
2392     if(mScrolling) // are we interrupting a current scroll?
2393     {
2394       // set mScrolling to false, in case user has code that interrogates mScrolling Getter() in complete.
2395       mScrolling = false;
2396       // send negative scroll position since scroll internal scroll position works as an offset for actors,
2397       // give applications the position within the domain from the scroll view's anchor position
2398       DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 5 [%.2f, %.2f]", this, -mScrollPostPosition.x, -mScrollPostPosition.y);
2399       mScrollCompletedSignal.Emit( -mScrollPostPosition );
2400     }
2401   }
2402 }
2403
2404 void ScrollView::GestureContinuing(const Vector2& panDelta)
2405 {
2406   mPanDelta.x+= panDelta.x;
2407   mPanDelta.y+= panDelta.y;
2408
2409   // Save the velocity, there is a bug in PanGesture
2410   // Whereby the Gesture::Finished's velocity is either:
2411   // NaN (due to time delta of zero between the last two events)
2412   // or 0 (due to position being the same between the last two events)
2413
2414   // Axis Auto Lock - locks the panning to the horizontal or vertical axis if the pan
2415   // appears mostly horizontal or mostly vertical respectively.
2416   if(mAxisAutoLock)
2417   {
2418     mLockAxis = GetLockAxis(mPanDelta, mLockAxis, mAxisAutoLockGradient);
2419   } // end if mAxisAutoLock
2420 }
2421
2422 // TODO: Upgrade to use a more powerful gesture detector (one that supports multiple touches on pan - so works as pan and flick gesture)
2423 // BUG: Gesture::Finished doesn't always return velocity on release (due to
2424 // timeDelta between last two events being 0 sometimes, or posiiton being the same)
2425 void ScrollView::OnPan( const PanGesture& gesture )
2426 {
2427   // Guard against destruction during signal emission
2428   // Note that Emit() methods are called indirectly e.g. from within ScrollView::OnGestureEx()
2429   Actor self( Self() );
2430
2431   if(!mSensitive)
2432   {
2433     DALI_LOG_SCROLL_STATE("[0x%X] Pan Ignored, Insensitive", this);
2434
2435     // If another callback on the same original signal disables sensitivity,
2436     // this callback will still be called, so we must suppress it.
2437     return;
2438   }
2439
2440   // translate Gesture input to get useful data...
2441   switch(gesture.state)
2442   {
2443     case Gesture::Started:
2444     {
2445       DALI_LOG_SCROLL_STATE("[0x%X] Pan Started", this);
2446       mPanStartPosition = gesture.position - gesture.displacement;
2447       UpdateLocalScrollProperties();
2448       GestureStarted();
2449       mPanning = true;
2450       self.SetProperty( Toolkit::ScrollView::Property::PANNING, true );
2451       self.SetProperty( Toolkit::ScrollView::Property::START_PAGE_POSITION, Vector3(gesture.position.x, gesture.position.y, 0.0f) );
2452
2453       UpdateMainInternalConstraint();
2454       break;
2455     }
2456
2457     case Gesture::Continuing:
2458     {
2459       if ( mPanning )
2460       {
2461         DALI_LOG_SCROLL_STATE("[0x%X] Pan Continuing", this);
2462         GestureContinuing(gesture.screenDisplacement);
2463       }
2464       else
2465       {
2466         // If we do not think we are panning, then we should not do anything here
2467         return;
2468       }
2469       break;
2470     }
2471
2472     case Gesture::Finished:
2473     case Gesture::Cancelled:
2474     {
2475       if ( mPanning )
2476       {
2477         DALI_LOG_SCROLL_STATE("[0x%X] Pan %s", this, ( ( gesture.state == Gesture::Finished ) ? "Finished" : "Cancelled" ) );
2478
2479         UpdateLocalScrollProperties();
2480         mLastVelocity = gesture.velocity;
2481         mPanning = false;
2482         self.SetProperty( Toolkit::ScrollView::Property::PANNING, false );
2483
2484         if( mScrollMainInternalPrePositionConstraint )
2485         {
2486           mScrollMainInternalPrePositionConstraint.Remove();
2487         }
2488       }
2489       else
2490       {
2491         // If we do not think we are panning, then we should not do anything here
2492         return;
2493       }
2494       break;
2495     }
2496
2497     case Gesture::Possible:
2498     case Gesture::Clear:
2499     {
2500       // Nothing to do, not needed.
2501       break;
2502     }
2503
2504   } // end switch(gesture.state)
2505
2506   OnGestureEx(gesture.state);
2507 }
2508
2509 void ScrollView::OnGestureEx(Gesture::State state)
2510 {
2511   // call necessary signals for application developer
2512
2513   if(state == Gesture::Started)
2514   {
2515     Vector2 currentScrollPosition = GetCurrentScrollPosition();
2516     Self().SetProperty(Toolkit::ScrollView::Property::SCROLLING, true);
2517     mScrolling = true;
2518     DALI_LOG_SCROLL_STATE("[0x%X] mScrollStartedSignal 2 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
2519     mScrollStartedSignal.Emit( currentScrollPosition );
2520   }
2521   else if( (state == Gesture::Finished) ||
2522            (state == Gesture::Cancelled) ) // Finished/default
2523   {
2524     // when all the gestures have finished, we finish the transform.
2525     // so if a user decides to pan (1 gesture), and then pan+zoom (2 gestures)
2526     // then stop panning (back to 1 gesture), and then stop zooming (0 gestures).
2527     // this is the point we end, and perform necessary snapping.
2528     mGestureStackDepth--;
2529     if(mGestureStackDepth==0)
2530     {
2531       // no flick if we have not exceeded min flick distance
2532       if( (fabsf(mPanDelta.x) < mMinFlickDistance.x)
2533           && (fabsf(mPanDelta.y) < mMinFlickDistance.y) )
2534       {
2535         // reset flick velocity
2536         mLastVelocity = Vector2::ZERO;
2537       }
2538       FinishTransform();
2539     }
2540     else
2541     {
2542       DALI_LOG_SCROLL_STATE("[0x%X] mGestureStackDepth[%d]", this, mGestureStackDepth);
2543     }
2544   }
2545 }
2546
2547 void ScrollView::FinishTransform()
2548 {
2549   // at this stage internal x and x scroll position should have followed prescroll position exactly
2550   Actor self = Self();
2551
2552   PreAnimatedScrollSetup();
2553
2554   // convert pixels/millisecond to pixels per second
2555   bool animating = SnapWithVelocity(mLastVelocity * 1000.0f);
2556
2557   if(!animating)
2558   {
2559     // if not animating, then this pan has completed right now.
2560     SetScrollUpdateNotification(false);
2561     mScrolling = false;
2562     Self().SetProperty(Toolkit::ScrollView::Property::SCROLLING, false);
2563
2564     if( fabs(mScrollPrePosition.x - mScrollTargetPosition.x) > Math::MACHINE_EPSILON_10 )
2565     {
2566       SnapInternalXTo(mScrollTargetPosition.x);
2567     }
2568     if( fabs(mScrollPrePosition.y - mScrollTargetPosition.y) > Math::MACHINE_EPSILON_10 )
2569     {
2570       SnapInternalYTo(mScrollTargetPosition.y);
2571     }
2572     Vector2 currentScrollPosition = GetCurrentScrollPosition();
2573     DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 6 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
2574     mScrollCompletedSignal.Emit( currentScrollPosition );
2575   }
2576 }
2577
2578 Vector2 ScrollView::GetOvershoot(Vector2& position) const
2579 {
2580   Vector3 size = Self().GetCurrentSize();
2581   Vector2 overshoot;
2582
2583   const RulerDomain rulerDomainX = mRulerX->GetDomain();
2584   const RulerDomain rulerDomainY = mRulerY->GetDomain();
2585
2586   if(mRulerX->IsEnabled() && rulerDomainX.enabled)
2587   {
2588     const float left = rulerDomainX.min - position.x;
2589     const float right = size.width - rulerDomainX.max - position.x;
2590     if(left<0)
2591     {
2592       overshoot.x = left;
2593     }
2594     else if(right>0)
2595     {
2596       overshoot.x = right;
2597     }
2598   }
2599
2600   if(mRulerY->IsEnabled() && rulerDomainY.enabled)
2601   {
2602     const float top = rulerDomainY.min - position.y;
2603     const float bottom = size.height - rulerDomainY.max - position.y;
2604     if(top<0)
2605     {
2606       overshoot.y = top;
2607     }
2608     else if(bottom>0)
2609     {
2610       overshoot.y = bottom;
2611     }
2612   }
2613
2614   return overshoot;
2615 }
2616
2617 bool ScrollView::OnAccessibilityPan(PanGesture gesture)
2618 {
2619   // Keep track of whether this is an AccessibilityPan
2620   mInAccessibilityPan = true;
2621   OnPan(gesture);
2622   mInAccessibilityPan = false;
2623
2624   return true;
2625 }
2626
2627 void ScrollView::ClampPosition(Vector2& position) const
2628 {
2629   ClampState2D clamped;
2630   ClampPosition(position, clamped);
2631 }
2632
2633 void ScrollView::ClampPosition(Vector2& position, ClampState2D &clamped) const
2634 {
2635   Vector3 size = Self().GetCurrentSize();
2636
2637   position.x = -mRulerX->Clamp(-position.x, size.width, 1.0f, clamped.x);    // NOTE: X & Y rulers think in -ve coordinate system.
2638   position.y = -mRulerY->Clamp(-position.y, size.height, 1.0f, clamped.y);   // That is scrolling RIGHT (e.g. 100.0, 0.0) means moving LEFT.
2639 }
2640
2641 void ScrollView::WrapPosition(Vector2& position) const
2642 {
2643   if(mWrapMode)
2644   {
2645     const RulerDomain rulerDomainX = mRulerX->GetDomain();
2646     const RulerDomain rulerDomainY = mRulerY->GetDomain();
2647
2648     if(mRulerX->IsEnabled())
2649     {
2650       position.x = -WrapInDomain(-position.x, rulerDomainX.min, rulerDomainX.max);
2651     }
2652
2653     if(mRulerY->IsEnabled())
2654     {
2655       position.y = -WrapInDomain(-position.y, rulerDomainY.min, rulerDomainY.max);
2656     }
2657   }
2658 }
2659
2660 void ScrollView::UpdateMainInternalConstraint()
2661 {
2662   // TODO: Only update the constraints which have changed, rather than remove all and add all again.
2663   // Requires a dali-core ApplyConstraintAt, or a ReplaceConstraint. The former is probably more flexible.
2664   Actor self = Self();
2665   PanGestureDetector detector( GetPanGestureDetector() );
2666
2667   if(mScrollMainInternalPositionConstraint)
2668   {
2669     mScrollMainInternalPositionConstraint.Remove();
2670     mScrollMainInternalDeltaConstraint.Remove();
2671     mScrollMainInternalFinalConstraint.Remove();
2672     mScrollMainInternalRelativeConstraint.Remove();
2673     mScrollMainInternalDomainConstraint.Remove();
2674     mScrollMainInternalPrePositionMaxConstraint.Remove();
2675   }
2676   if( mScrollMainInternalPrePositionConstraint )
2677   {
2678     mScrollMainInternalPrePositionConstraint.Remove();
2679   }
2680
2681   // TODO: It's probably better to use a local displacement value as this will give a displacement when scrolling just commences
2682   // but we need to make sure than the gesture system gives displacement since last frame (60Hz), not displacement since last touch event (90Hz).
2683
2684   // 1. First calculate the pre-position (this is the scroll position if no clamping has taken place)
2685   Vector2 initialPanMask = Vector2(mRulerX->IsEnabled() ? 1.0f : 0.0f, mRulerY->IsEnabled() ? 1.0f : 0.0f);
2686
2687   if( mLockAxis == LockVertical )
2688   {
2689     initialPanMask.y = 0.0f;
2690   }
2691   else if( mLockAxis == LockHorizontal )
2692   {
2693     initialPanMask.x = 0.0f;
2694   }
2695
2696   if( mPanning )
2697   {
2698     mScrollMainInternalPrePositionConstraint = Constraint::New<Vector2>( self,
2699                                                                          Toolkit::ScrollView::Property::SCROLL_PRE_POSITION,
2700                                                                          InternalPrePositionConstraint( mPanStartPosition,
2701                                                                                                         initialPanMask,
2702                                                                                                         mAxisAutoLock,
2703                                                                                                         mAxisAutoLockGradient,
2704                                                                                                         mLockAxis,
2705                                                                                                         mMaxOvershoot,
2706                                                                                                         mRulerX,
2707                                                                                                         mRulerY ) );
2708     mScrollMainInternalPrePositionConstraint.AddSource( Source( detector, PanGestureDetector::Property::LOCAL_POSITION ) );
2709     mScrollMainInternalPrePositionConstraint.AddSource( Source( detector, PanGestureDetector::Property::PANNING ) );
2710     mScrollMainInternalPrePositionConstraint.AddSource( Source( self, Actor::Property::SIZE ) );
2711     mScrollMainInternalPrePositionConstraint.Apply();
2712   }
2713
2714   // 2. Second calculate the clamped position (actual position)
2715   mScrollMainInternalPositionConstraint = Constraint::New<Vector2>( self,
2716                                                                     Toolkit::ScrollView::Property::SCROLL_POSITION,
2717                                                                     InternalPositionConstraint( mRulerX->GetDomain(),
2718                                                                                                 mRulerY->GetDomain(),
2719                                                                                                 mWrapMode ) );
2720   mScrollMainInternalPositionConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_PRE_POSITION ) );
2721   mScrollMainInternalPositionConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::SCROLL_POSITION_MIN ) );
2722   mScrollMainInternalPositionConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::SCROLL_POSITION_MAX ) );
2723   mScrollMainInternalPositionConstraint.AddSource( Source( self, Actor::Property::SIZE ) );
2724   mScrollMainInternalPositionConstraint.Apply();
2725
2726   mScrollMainInternalDeltaConstraint = Constraint::New<Vector2>( self, Toolkit::ScrollView::Property::SCROLL_POSITION_DELTA, InternalPositionDeltaConstraint );
2727   mScrollMainInternalDeltaConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_POSITION ) );
2728   mScrollMainInternalDeltaConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_DOMAIN_OFFSET ) );
2729   mScrollMainInternalDeltaConstraint.Apply();
2730
2731   mScrollMainInternalFinalConstraint = Constraint::New<Vector2>( self, Toolkit::ScrollView::Property::SCROLL_FINAL,
2732                                                                  InternalFinalConstraint( FinalDefaultAlphaFunction,
2733                                                                                           FinalDefaultAlphaFunction ) );
2734   mScrollMainInternalFinalConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_POSITION ) );
2735   mScrollMainInternalFinalConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::OVERSHOOT_X ) );
2736   mScrollMainInternalFinalConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::OVERSHOOT_Y ) );
2737   mScrollMainInternalFinalConstraint.Apply();
2738
2739   mScrollMainInternalRelativeConstraint = Constraint::New<Vector2>( self, Toolkit::Scrollable::Property::SCROLL_RELATIVE_POSITION, InternalRelativePositionConstraint );
2740   mScrollMainInternalRelativeConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_POSITION ) );
2741   mScrollMainInternalRelativeConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::SCROLL_POSITION_MIN ) );
2742   mScrollMainInternalRelativeConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::SCROLL_POSITION_MAX ) );
2743   mScrollMainInternalRelativeConstraint.AddSource( LocalSource( Actor::Property::SIZE ) );
2744   mScrollMainInternalRelativeConstraint.Apply();
2745
2746   mScrollMainInternalDomainConstraint = Constraint::New<Vector2>( self, Toolkit::ScrollView::Property::SCROLL_DOMAIN_SIZE, InternalScrollDomainConstraint );
2747   mScrollMainInternalDomainConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::SCROLL_POSITION_MIN ) );
2748   mScrollMainInternalDomainConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::SCROLL_POSITION_MAX ) );
2749   mScrollMainInternalDomainConstraint.AddSource( LocalSource( Actor::Property::SIZE ) );
2750   mScrollMainInternalDomainConstraint.Apply();
2751
2752   mScrollMainInternalPrePositionMaxConstraint = Constraint::New<Vector2>( self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_MAX, InternalPrePositionMaxConstraint );
2753   mScrollMainInternalPrePositionMaxConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::SCROLL_POSITION_MAX ) );
2754   mScrollMainInternalPrePositionMaxConstraint.AddSource( LocalSource( Actor::Property::SIZE ) );
2755   mScrollMainInternalPrePositionMaxConstraint.Apply();
2756
2757   // When panning we want to make sure overshoot values are affected by pre position and post position
2758   SetOvershootConstraintsEnabled(!mWrapMode);
2759 }
2760
2761 void ScrollView::SetOvershootConstraintsEnabled(bool enabled)
2762 {
2763   Actor self( Self() );
2764   // remove and reset, it may now be in wrong order with the main internal constraints
2765   if( mScrollMainInternalOvershootXConstraint )
2766   {
2767     mScrollMainInternalOvershootXConstraint.Remove();
2768     mScrollMainInternalOvershootXConstraint.Reset();
2769     mScrollMainInternalOvershootYConstraint.Remove();
2770     mScrollMainInternalOvershootYConstraint.Reset();
2771   }
2772   if( enabled )
2773   {
2774     mScrollMainInternalOvershootXConstraint= Constraint::New<float>( self, Toolkit::ScrollView::Property::OVERSHOOT_X, OvershootXConstraint(mMaxOvershoot.x) );
2775     mScrollMainInternalOvershootXConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_PRE_POSITION ) );
2776     mScrollMainInternalOvershootXConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_POSITION ) );
2777     mScrollMainInternalOvershootXConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::CAN_SCROLL_HORIZONTAL ) );
2778     mScrollMainInternalOvershootXConstraint.Apply();
2779
2780     mScrollMainInternalOvershootYConstraint = Constraint::New<float>( self, Toolkit::ScrollView::Property::OVERSHOOT_Y, OvershootYConstraint(mMaxOvershoot.y) );
2781     mScrollMainInternalOvershootYConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_PRE_POSITION ) );
2782     mScrollMainInternalOvershootYConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_POSITION ) );
2783     mScrollMainInternalOvershootYConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::CAN_SCROLL_VERTICAL ) );
2784     mScrollMainInternalOvershootYConstraint.Apply();
2785   }
2786   else
2787   {
2788     self.SetProperty(Toolkit::ScrollView::Property::OVERSHOOT_X, 0.0f);
2789     self.SetProperty(Toolkit::ScrollView::Property::OVERSHOOT_Y, 0.0f);
2790   }
2791 }
2792
2793 void ScrollView::SetInternalConstraints()
2794 {
2795   // Internal constraints (applied to target ScrollBase Actor itself) /////////
2796   UpdateMainInternalConstraint();
2797
2798   // User definable constraints to apply to all child actors //////////////////
2799   Actor self = Self();
2800
2801   // Apply some default constraints to ScrollView & its bound actors
2802   // Movement + Wrap function
2803
2804   Constraint constraint;
2805
2806   // MoveActor (scrolling)
2807   constraint = Constraint::New<Vector3>( self, Actor::Property::POSITION, MoveActorConstraint );
2808   constraint.AddSource( Source( self, Toolkit::ScrollView::Property::SCROLL_POSITION ) );
2809   constraint.SetRemoveAction(Constraint::Discard);
2810   ApplyConstraintToBoundActors(constraint);
2811
2812   // WrapActor (wrap functionality)
2813   constraint = Constraint::New<Vector3>( self, Actor::Property::POSITION, WrapActorConstraint );
2814   constraint.AddSource( LocalSource( Actor::Property::SCALE ) );
2815   constraint.AddSource( LocalSource( Actor::Property::ANCHOR_POINT ) );
2816   constraint.AddSource( LocalSource( Actor::Property::SIZE ) );
2817   constraint.AddSource( Source( self, Toolkit::Scrollable::Property::SCROLL_POSITION_MIN ) );
2818   constraint.AddSource( Source( self, Toolkit::Scrollable::Property::SCROLL_POSITION_MAX ) );
2819   constraint.AddSource( Source( self, Toolkit::ScrollView::Property::WRAP ) );
2820   constraint.SetRemoveAction(Constraint::Discard);
2821   ApplyConstraintToBoundActors(constraint);
2822 }
2823
2824 } // namespace Internal
2825
2826 } // namespace Toolkit
2827
2828 } // namespace Dali