Merge "Append clipboard text after current pre-edit text" into devel/master
[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( "internal-max-position" );
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, "scroll-position", VECTOR2, SCROLL_POSITION)
248 DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "scroll-pre-position", VECTOR2, SCROLL_PRE_POSITION)
249 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, ScrollView, "scroll-pre-position-x", SCROLL_PRE_POSITION_X, SCROLL_PRE_POSITION, 0)
250 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, ScrollView, "scroll-pre-position-y", SCROLL_PRE_POSITION_Y, SCROLL_PRE_POSITION, 1)
251 DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "scroll-pre-position-max", VECTOR2, SCROLL_PRE_POSITION_MAX)
252 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, ScrollView, "scroll-pre-position-max-x", SCROLL_PRE_POSITION_MAX_X, SCROLL_PRE_POSITION_MAX, 0)
253 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, ScrollView, "scroll-pre-position-max-y", SCROLL_PRE_POSITION_MAX_Y, SCROLL_PRE_POSITION_MAX, 1)
254 DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "overshoot-x", FLOAT, OVERSHOOT_X)
255 DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "overshoot-y", FLOAT, OVERSHOOT_Y)
256 DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "scroll-final", VECTOR2, SCROLL_FINAL)
257 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, ScrollView, "scroll-final-x", SCROLL_FINAL_X, SCROLL_FINAL,0)
258 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, ScrollView, "scroll-final-y", 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, "scroll-domain-size", VECTOR2, SCROLL_DOMAIN_SIZE)
263 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, ScrollView, "scroll-domain-size-x", SCROLL_DOMAIN_SIZE_X, SCROLL_DOMAIN_SIZE, 0)
264 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, ScrollView, "scroll-domain-size-y", SCROLL_DOMAIN_SIZE_Y, SCROLL_DOMAIN_SIZE, 1)
265 DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "scroll-domain-offset", VECTOR2, SCROLL_DOMAIN_OFFSET)
266 DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "scroll-position-delta", VECTOR2, SCROLL_POSITION_DELTA)
267 DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "start-page-position", VECTOR3, START_PAGE_POSITION)
268
269 DALI_SIGNAL_REGISTRATION( Toolkit, ScrollView, "value-changed", 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   DALI_LOG_SCROLL_STATE("[0x%X]", this);
699
700   if ( mSensitive )
701   {
702     SetScrollSensitive( false );
703     SetScrollSensitive( true );
704   }
705   if(IsOvershootEnabled())
706   {
707     // try and make sure property notifications are set
708     EnableScrollOvershoot(true);
709   }
710 }
711
712 void ScrollView::OnStageDisconnection()
713 {
714   DALI_LOG_SCROLL_STATE("[0x%X]", this);
715
716   StopAnimation();
717 }
718
719 ScrollView::~ScrollView()
720 {
721   DALI_LOG_SCROLL_STATE("[0x%X]", this);
722 }
723
724 AlphaFunction ScrollView::GetScrollSnapAlphaFunction() const
725 {
726   return mSnapAlphaFunction;
727 }
728
729 void ScrollView::SetScrollSnapAlphaFunction(AlphaFunction alpha)
730 {
731   mSnapAlphaFunction = alpha;
732 }
733
734 AlphaFunction ScrollView::GetScrollFlickAlphaFunction() const
735 {
736   return mFlickAlphaFunction;
737 }
738
739 void ScrollView::SetScrollFlickAlphaFunction(AlphaFunction alpha)
740 {
741   mFlickAlphaFunction = alpha;
742 }
743
744 float ScrollView::GetScrollSnapDuration() const
745 {
746   return mSnapDuration;
747 }
748
749 void ScrollView::SetScrollSnapDuration(float time)
750 {
751   mSnapDuration = time;
752 }
753
754 float ScrollView::GetScrollFlickDuration() const
755 {
756   return mFlickDuration;
757 }
758
759 void ScrollView::SetScrollFlickDuration(float time)
760 {
761   mFlickDuration = time;
762 }
763
764 void ScrollView::ApplyEffect(Toolkit::ScrollViewEffect effect)
765 {
766   Dali::Toolkit::ScrollView self = Dali::Toolkit::ScrollView::DownCast(Self());
767
768   // Assertion check to ensure effect doesn't already exist in this scrollview
769   bool effectAlreadyExistsInScrollView(false);
770   for (ScrollViewEffectIter iter = mEffects.begin(); iter != mEffects.end(); ++iter)
771   {
772     if(*iter==effect)
773     {
774       effectAlreadyExistsInScrollView = true;
775       break;
776     }
777   }
778
779   DALI_ASSERT_ALWAYS(!effectAlreadyExistsInScrollView);
780
781   // add effect to effects list
782   mEffects.push_back(effect);
783
784   // invoke Attachment request to ScrollView first
785   GetImpl(effect).Attach(self);
786 }
787
788 void ScrollView::RemoveEffect(Toolkit::ScrollViewEffect effect)
789 {
790   Dali::Toolkit::ScrollView self = Dali::Toolkit::ScrollView::DownCast(Self());
791
792   // remove effect from effects list
793   bool effectExistedInScrollView(false);
794   for (ScrollViewEffectIter iter = mEffects.begin(); iter != mEffects.end(); ++iter)
795   {
796     if(*iter==effect)
797     {
798       mEffects.erase(iter);
799       effectExistedInScrollView = true;
800       break;
801     }
802   }
803
804   // Assertion check to ensure effect existed.
805   DALI_ASSERT_ALWAYS(effectExistedInScrollView);
806
807   // invoke Detachment request to ScrollView last
808   GetImpl(effect).Detach(self);
809 }
810
811 void ScrollView::RemoveAllEffects()
812 {
813   Dali::Toolkit::ScrollView self = Dali::Toolkit::ScrollView::DownCast(Self());
814
815   for (ScrollViewEffectIter effectIter = mEffects.begin(); effectIter != mEffects.end(); ++effectIter)
816   {
817     Toolkit::ScrollViewEffect effect = *effectIter;
818
819     // invoke Detachment request to ScrollView last
820     GetImpl(effect).Detach(self);
821   }
822
823   mEffects.clear();
824 }
825
826 void ScrollView::ApplyConstraintToChildren(Constraint constraint)
827 {
828   ApplyConstraintToBoundActors(constraint);
829 }
830
831 void ScrollView::RemoveConstraintsFromChildren()
832 {
833   RemoveConstraintsFromBoundActors();
834 }
835
836 const RulerPtr ScrollView::GetRulerX() const
837 {
838   return mRulerX;
839 }
840
841 const RulerPtr ScrollView::GetRulerY() const
842 {
843   return mRulerY;
844 }
845
846 void ScrollView::SetRulerX(RulerPtr ruler)
847 {
848   mRulerX = ruler;
849
850   UpdatePropertyDomain();
851   UpdateMainInternalConstraint();
852 }
853
854 void ScrollView::SetRulerY(RulerPtr ruler)
855 {
856   mRulerY = ruler;
857
858   UpdatePropertyDomain();
859   UpdateMainInternalConstraint();
860 }
861
862 void ScrollView::UpdatePropertyDomain()
863 {
864   Actor self = Self();
865   Vector3 size = self.GetTargetSize();
866   Vector2 min = mMinScroll;
867   Vector2 max = mMaxScroll;
868   bool scrollPositionChanged = false;
869   bool domainChanged = false;
870
871   bool canScrollVertical = false;
872   bool canScrollHorizontal = false;
873   UpdateLocalScrollProperties();
874   if(mRulerX->IsEnabled())
875   {
876     const Toolkit::RulerDomain& rulerDomain = mRulerX->GetDomain();
877     if( fabsf(min.x - rulerDomain.min) > Math::MACHINE_EPSILON_100
878         || fabsf(max.x - rulerDomain.max) > Math::MACHINE_EPSILON_100 )
879     {
880       domainChanged = true;
881       min.x = rulerDomain.min;
882       max.x = rulerDomain.max;
883
884       // make sure new scroll value is within new domain
885       if( mScrollPrePosition.x < min.x
886           || mScrollPrePosition.x > max.x )
887       {
888         scrollPositionChanged = true;
889         mScrollPrePosition.x = Clamp(mScrollPrePosition.x, -(max.x - size.x), -min.x);
890       }
891     }
892     if( (fabsf(rulerDomain.max - rulerDomain.min) - size.x) > Math::MACHINE_EPSILON_100 )
893     {
894       canScrollHorizontal = true;
895     }
896   }
897   else if( fabs(min.x) > Math::MACHINE_EPSILON_100
898            || fabs(max.x) > Math::MACHINE_EPSILON_100 )
899   {
900     // need to reset to 0
901     domainChanged = true;
902     min.x = 0.0f;
903     max.x = 0.0f;
904     canScrollHorizontal = false;
905   }
906
907   if(mRulerY->IsEnabled())
908   {
909     const Toolkit::RulerDomain& rulerDomain = mRulerY->GetDomain();
910     if( fabsf(min.y - rulerDomain.min) > Math::MACHINE_EPSILON_100
911         || fabsf(max.y - rulerDomain.max) > Math::MACHINE_EPSILON_100 )
912     {
913       domainChanged = true;
914       min.y = rulerDomain.min;
915       max.y = rulerDomain.max;
916
917       // make sure new scroll value is within new domain
918       if( mScrollPrePosition.y < min.y
919           || mScrollPrePosition.y > max.y )
920       {
921         scrollPositionChanged = true;
922         mScrollPrePosition.y = Clamp(mScrollPrePosition.y, -(max.y - size.y), -min.y);
923       }
924     }
925     if( (fabsf(rulerDomain.max - rulerDomain.min) - size.y) > Math::MACHINE_EPSILON_100 )
926     {
927       canScrollVertical = true;
928     }
929   }
930   else if( fabs(min.y) > Math::MACHINE_EPSILON_100
931            || fabs(max.y) > Math::MACHINE_EPSILON_100 )
932   {
933     // need to reset to 0
934     domainChanged = true;
935     min.y = 0.0f;
936     max.y = 0.0f;
937     canScrollVertical = false;
938   }
939
940   // avoid setting properties if possible, otherwise this will cause an entire update as well as triggering constraints using each property we update
941   if( mCanScrollVertical != canScrollVertical )
942   {
943     mCanScrollVertical = canScrollVertical;
944     self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_VERTICAL, canScrollVertical);
945   }
946   if( mCanScrollHorizontal != canScrollHorizontal )
947   {
948     mCanScrollHorizontal = canScrollHorizontal;
949     self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_HORIZONTAL, canScrollHorizontal);
950   }
951   if( scrollPositionChanged )
952   {
953     DALI_LOG_SCROLL_STATE("[0x%X] Domain Changed, setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y );
954     self.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPrePosition);
955   }
956   if( domainChanged )
957   {
958     mMinScroll = min;
959     mMaxScroll = max;
960     self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MIN, mMinScroll );
961     self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MAX, mMaxScroll );
962   }
963 }
964
965 void ScrollView::SetScrollSensitive(bool sensitive)
966 {
967   Actor self = Self();
968   PanGestureDetector panGesture( GetPanGestureDetector() );
969
970   DALI_LOG_SCROLL_STATE("[0x%X] sensitive: before:[%d] setting[%d]", this, int(mSensitive), int(sensitive));
971
972   if((!mSensitive) && (sensitive))
973   {
974     mSensitive = sensitive;
975     panGesture.Attach(self);
976   }
977   else if((mSensitive) && (!sensitive))
978   {
979     DALI_LOG_SCROLL_STATE("[0x%X] BEFORE: panning:[%d]", this, int(mPanning));
980
981     // while the scroll view is panning, the state needs to be reset.
982     if ( mPanning )
983     {
984       PanGesture cancelGesture( Gesture::Cancelled );
985       OnPan( cancelGesture );
986     }
987
988     panGesture.Detach(self);
989     mSensitive = sensitive;
990
991     mGestureStackDepth = 0;
992     DALI_LOG_SCROLL_STATE("[0x%X] AFTER: panning:[%d]", this, int(mPanning));
993   }
994 }
995
996 void ScrollView::SetMaxOvershoot(float overshootX, float overshootY)
997 {
998   mMaxOvershoot.x = overshootX;
999   mMaxOvershoot.y = overshootY;
1000   mUserMaxOvershoot = mMaxOvershoot;
1001   mDefaultMaxOvershoot = false;
1002   UpdateMainInternalConstraint();
1003 }
1004
1005 void ScrollView::SetSnapOvershootAlphaFunction(AlphaFunction alpha)
1006 {
1007   mSnapOvershootAlphaFunction = alpha;
1008 }
1009
1010 void ScrollView::SetSnapOvershootDuration(float duration)
1011 {
1012   mSnapOvershootDuration = duration;
1013 }
1014
1015 void ScrollView::SetActorAutoSnap(bool enable)
1016 {
1017   mActorAutoSnapEnabled = enable;
1018 }
1019
1020 void ScrollView::SetAutoResize(bool enable)
1021 {
1022   mAutoResizeContainerEnabled = enable;
1023   // TODO: This needs a lot of issues to be addressed before working.
1024 }
1025
1026 bool ScrollView::GetWrapMode() const
1027 {
1028   return mWrapMode;
1029 }
1030
1031 void ScrollView::SetWrapMode(bool enable)
1032 {
1033   mWrapMode = enable;
1034   Self().SetProperty(Toolkit::ScrollView::Property::WRAP, enable);
1035 }
1036
1037 int ScrollView::GetScrollUpdateDistance() const
1038 {
1039   return mScrollUpdateDistance;
1040 }
1041
1042 void ScrollView::SetScrollUpdateDistance(int distance)
1043 {
1044   mScrollUpdateDistance = distance;
1045 }
1046
1047 bool ScrollView::GetAxisAutoLock() const
1048 {
1049   return mAxisAutoLock;
1050 }
1051
1052 void ScrollView::SetAxisAutoLock(bool enable)
1053 {
1054   mAxisAutoLock = enable;
1055   UpdateMainInternalConstraint();
1056 }
1057
1058 float ScrollView::GetAxisAutoLockGradient() const
1059 {
1060   return mAxisAutoLockGradient;
1061 }
1062
1063 void ScrollView::SetAxisAutoLockGradient(float gradient)
1064 {
1065   DALI_ASSERT_DEBUG( gradient >= 0.0f && gradient <= 1.0f );
1066   mAxisAutoLockGradient = gradient;
1067   UpdateMainInternalConstraint();
1068 }
1069
1070 float ScrollView::GetFrictionCoefficient() const
1071 {
1072   return mFrictionCoefficient;
1073 }
1074
1075 void ScrollView::SetFrictionCoefficient(float friction)
1076 {
1077   DALI_ASSERT_DEBUG( friction > 0.0f );
1078   mFrictionCoefficient = friction;
1079 }
1080
1081 float ScrollView::GetFlickSpeedCoefficient() const
1082 {
1083   return mFlickSpeedCoefficient;
1084 }
1085
1086 void ScrollView::SetFlickSpeedCoefficient(float speed)
1087 {
1088   mFlickSpeedCoefficient = speed;
1089 }
1090
1091 Vector2 ScrollView::GetMinimumDistanceForFlick() const
1092 {
1093   return mMinFlickDistance;
1094 }
1095
1096 void ScrollView::SetMinimumDistanceForFlick( const Vector2& distance )
1097 {
1098   mMinFlickDistance = distance;
1099 }
1100
1101 float ScrollView::GetMinimumSpeedForFlick() const
1102 {
1103   return mFlickSpeedThreshold;
1104 }
1105
1106 void ScrollView::SetMinimumSpeedForFlick( float speed )
1107 {
1108   mFlickSpeedThreshold = speed;
1109 }
1110
1111 float ScrollView::GetMaxFlickSpeed() const
1112 {
1113   return mMaxFlickSpeed;
1114 }
1115
1116 void ScrollView::SetMaxFlickSpeed(float speed)
1117 {
1118   mMaxFlickSpeed = speed;
1119 }
1120
1121 void ScrollView::SetWheelScrollDistanceStep(Vector2 step)
1122 {
1123   mWheelScrollDistanceStep = step;
1124 }
1125
1126 Vector2 ScrollView::GetWheelScrollDistanceStep() const
1127 {
1128   return mWheelScrollDistanceStep;
1129 }
1130
1131 unsigned int ScrollView::GetCurrentPage() const
1132 {
1133   // in case animation is currently taking place.
1134   Vector2 position = GetPropertyPosition();
1135
1136   Actor self = Self();
1137   unsigned int page = 0;
1138   unsigned int pagesPerVolume = 1;
1139   unsigned int volume = 0;
1140
1141   // if rulerX is enabled, then get page count (columns)
1142   page = mRulerX->GetPageFromPosition(-position.x, mWrapMode);
1143   volume = mRulerY->GetPageFromPosition(-position.y, mWrapMode);
1144   pagesPerVolume = mRulerX->GetTotalPages();
1145
1146   return volume * pagesPerVolume + page;
1147 }
1148
1149 Vector2 ScrollView::GetCurrentScrollPosition() const
1150 {
1151   return -GetPropertyPosition();
1152 }
1153
1154 Vector2 ScrollView::GetDomainSize() const
1155 {
1156   Vector3 size = Self().GetCurrentSize();
1157
1158   const RulerDomain& xDomain = GetRulerX()->GetDomain();
1159   const RulerDomain& yDomain = GetRulerY()->GetDomain();
1160
1161   Vector2 domainSize;
1162   domainSize.x = xDomain.max - xDomain.min - size.x;
1163   domainSize.y = yDomain.max - yDomain.min - size.y;
1164   return domainSize;
1165 }
1166
1167 void ScrollView::TransformTo(const Vector2& position,
1168                              DirectionBias horizontalBias, DirectionBias verticalBias)
1169 {
1170   TransformTo(position, mSnapDuration, mSnapAlphaFunction, horizontalBias, verticalBias);
1171 }
1172
1173 void ScrollView::TransformTo(const Vector2& position, float duration, AlphaFunction alpha,
1174                              DirectionBias horizontalBias, DirectionBias verticalBias)
1175 {
1176   // If this is called while the timer is running, then cancel it
1177   StopTouchDownTimer();
1178
1179   Actor self( Self() );
1180
1181   // Guard against destruction during signal emission
1182   // Note that Emit() methods are called indirectly e.g. from within ScrollView::AnimateTo()
1183   Toolkit::ScrollView handle( GetOwner() );
1184
1185   DALI_LOG_SCROLL_STATE("[0x%X] pos[%.2f,%.2f], duration[%.2f] bias[%d, %d]",
1186     this, position.x, position.y, duration, int(horizontalBias), int(verticalBias));
1187
1188   Vector2 currentScrollPosition = GetCurrentScrollPosition();
1189   self.SetProperty( Toolkit::ScrollView::Property::START_PAGE_POSITION, Vector3(currentScrollPosition) );
1190
1191   if( mScrolling ) // are we interrupting a current scroll?
1192   {
1193     // set mScrolling to false, in case user has code that interrogates mScrolling Getter() in complete.
1194     mScrolling = false;
1195     DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 1 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
1196     mScrollCompletedSignal.Emit( currentScrollPosition );
1197   }
1198
1199   if( mPanning ) // are we interrupting a current pan?
1200   {
1201     DALI_LOG_SCROLL_STATE("[0x%X] Interrupting Pan, set to false", this );
1202     mPanning = false;
1203     mGestureStackDepth = 0;
1204     self.SetProperty( Toolkit::ScrollView::Property::PANNING, false );
1205
1206     if( mScrollMainInternalPrePositionConstraint )
1207     {
1208       mScrollMainInternalPrePositionConstraint.Remove();
1209     }
1210   }
1211
1212   self.SetProperty(Toolkit::ScrollView::Property::SCROLLING, true);
1213   mScrolling = true;
1214
1215   DALI_LOG_SCROLL_STATE("[0x%X] mScrollStartedSignal 1 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
1216   mScrollStartedSignal.Emit( currentScrollPosition );
1217   bool animating = AnimateTo(-position,
1218                              Vector2::ONE * duration,
1219                              alpha,
1220                              true,
1221                              horizontalBias,
1222                              verticalBias,
1223                              Snap);
1224
1225   if(!animating)
1226   {
1227     // if not animating, then this pan has completed right now.
1228     self.SetProperty(Toolkit::ScrollView::Property::SCROLLING, false);
1229     mScrolling = false;
1230
1231     // If we have no duration, then in the next update frame, we will be at the position specified as we just set.
1232     // In this scenario, we cannot return the currentScrollPosition as this is out-of-date and should instead return the requested final position
1233     Vector2 completedPosition( currentScrollPosition );
1234     if( duration <= Math::MACHINE_EPSILON_10 )
1235     {
1236       completedPosition = position;
1237     }
1238
1239     DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 2 [%.2f, %.2f]", this, completedPosition.x, completedPosition.y);
1240     SetScrollUpdateNotification(false);
1241     mScrollCompletedSignal.Emit( completedPosition );
1242   }
1243 }
1244
1245 void ScrollView::ScrollTo(const Vector2& position)
1246 {
1247   ScrollTo(position, mSnapDuration );
1248 }
1249
1250 void ScrollView::ScrollTo(const Vector2& position, float duration)
1251 {
1252   ScrollTo(position, duration, DirectionBiasNone, DirectionBiasNone);
1253 }
1254
1255 void ScrollView::ScrollTo(const Vector2& position, float duration, AlphaFunction alpha)
1256 {
1257   ScrollTo(position, duration, alpha, DirectionBiasNone, DirectionBiasNone);
1258 }
1259
1260 void ScrollView::ScrollTo(const Vector2& position, float duration,
1261                           DirectionBias horizontalBias, DirectionBias verticalBias)
1262 {
1263   ScrollTo(position, duration, mSnapAlphaFunction, horizontalBias, verticalBias);
1264 }
1265
1266 void ScrollView::ScrollTo(const Vector2& position, float duration, AlphaFunction alpha,
1267                 DirectionBias horizontalBias, DirectionBias verticalBias)
1268 {
1269   DALI_LOG_SCROLL_STATE("[0x%X] position[%.2f, %.2f] duration[%.2f], bias[%d, %d]", this, position.x, position.y, duration, int(horizontalBias), int(verticalBias));
1270   TransformTo(position, duration, alpha, horizontalBias, verticalBias);
1271 }
1272
1273 void ScrollView::ScrollTo(unsigned int page)
1274 {
1275   ScrollTo(page, mSnapDuration);
1276 }
1277
1278 void ScrollView::ScrollTo(unsigned int page, float duration, DirectionBias bias)
1279 {
1280   Vector2 position;
1281   unsigned int volume;
1282   unsigned int libraries;
1283
1284   // The position to scroll to is continuous and linear
1285   // unless a domain has been enabled on the X axis.
1286   // or if WrapMode has been enabled.
1287   bool carryX = mRulerX->GetDomain().enabled | mWrapMode;
1288   bool carryY = mRulerY->GetDomain().enabled | mWrapMode;
1289
1290   position.x = mRulerX->GetPositionFromPage(page, volume, carryX);
1291   position.y = mRulerY->GetPositionFromPage(volume, libraries, carryY);
1292
1293   ScrollTo(position, duration, bias, bias);
1294 }
1295
1296 void ScrollView::ScrollTo(Actor &actor)
1297 {
1298   ScrollTo(actor, mSnapDuration);
1299 }
1300
1301 void ScrollView::ScrollTo(Actor &actor, float duration)
1302 {
1303   DALI_ASSERT_ALWAYS(actor.GetParent() == Self());
1304
1305   Actor self = Self();
1306   Vector3 size = self.GetCurrentSize();
1307   Vector3 position = actor.GetCurrentPosition();
1308   Vector2 prePosition = GetPropertyPrePosition();
1309   position.GetVectorXY() -= prePosition;
1310
1311   ScrollTo(Vector2(position.x - size.width * 0.5f, position.y - size.height * 0.5f), duration);
1312 }
1313
1314 Actor ScrollView::FindClosestActor()
1315 {
1316   Actor self = Self();
1317   Vector3 size = self.GetCurrentSize();
1318
1319   return FindClosestActorToPosition(Vector3(size.width * 0.5f,size.height * 0.5f,0.0f));
1320 }
1321
1322 Actor ScrollView::FindClosestActorToPosition(const Vector3& position, FindDirection dirX, FindDirection dirY, FindDirection dirZ)
1323 {
1324   Actor closestChild;
1325   float closestDistance2 = 0.0f;
1326   Vector3 actualPosition = position;
1327
1328   unsigned int numChildren = Self().GetChildCount();
1329
1330   for(unsigned int i = 0; i < numChildren; ++i)
1331   {
1332     Actor child = Self().GetChildAt(i);
1333
1334     if(mInternalActor == child) // ignore internal actor.
1335     {
1336       continue;
1337     }
1338
1339     Vector3 childPosition = GetPositionOfAnchor(child, AnchorPoint::CENTER);
1340
1341     Vector3 delta = childPosition - actualPosition;
1342
1343     // X-axis checking (only find Actors to the [dirX] of actualPosition)
1344     if(dirX > All) // != All,None
1345     {
1346       FindDirection deltaH = delta.x > 0 ? Right : Left;
1347       if(dirX != deltaH)
1348       {
1349         continue;
1350       }
1351     }
1352
1353     // Y-axis checking (only find Actors to the [dirY] of actualPosition)
1354     if(dirY > All) // != All,None
1355     {
1356       FindDirection deltaV = delta.y > 0 ? Down : Up;
1357       if(dirY  != deltaV)
1358       {
1359         continue;
1360       }
1361     }
1362
1363     // Z-axis checking (only find Actors to the [dirZ] of actualPosition)
1364     if(dirZ > All) // != All,None
1365     {
1366       FindDirection deltaV = delta.y > 0 ? In : Out;
1367       if(dirZ  != deltaV)
1368       {
1369         continue;
1370       }
1371     }
1372
1373     // compare child to closest child in terms of distance.
1374     float distance2 = 0.0f;
1375
1376     // distance2 = the Square of the relevant dimensions of delta
1377     if(dirX != None)
1378     {
1379       distance2 += delta.x * delta.x;
1380     }
1381
1382     if(dirY != None)
1383     {
1384       distance2 += delta.y * delta.y;
1385     }
1386
1387     if(dirZ != None)
1388     {
1389       distance2 += delta.z * delta.z;
1390     }
1391
1392     if(closestChild) // Next time.
1393     {
1394       if(distance2 < closestDistance2)
1395       {
1396         closestChild = child;
1397         closestDistance2 = distance2;
1398       }
1399     }
1400     else // First time.
1401     {
1402       closestChild = child;
1403       closestDistance2 = distance2;
1404     }
1405   }
1406
1407   return closestChild;
1408 }
1409
1410 bool ScrollView::ScrollToSnapPoint()
1411 {
1412   DALI_LOG_SCROLL_STATE("[0x%X]", this );
1413   Vector2 stationaryVelocity = Vector2(0.0f, 0.0f);
1414   return SnapWithVelocity( stationaryVelocity );
1415 }
1416
1417 // TODO: In situations where axes are different (X snap, Y free)
1418 // Each axis should really have their own independent animation (time and equation)
1419 // Consider, X axis snapping to nearest grid point (EaseOut over fixed time)
1420 // Consider, Y axis simulating physics to arrive at a point (Physics equation over variable time)
1421 // Currently, the axes have been split however, they both use the same EaseOut equation.
1422 bool ScrollView::SnapWithVelocity(Vector2 velocity)
1423 {
1424   // Animator takes over now, touches are assumed not to interfere.
1425   // And if touches do interfere, then we'll stop animation, update PrePosition
1426   // to current mScroll's properties, and then resume.
1427   // Note: For Flicking this may work a bit different...
1428
1429   float angle = atan2(velocity.y, velocity.x);
1430   float speed2 = velocity.LengthSquared();
1431   AlphaFunction alphaFunction = mSnapAlphaFunction;
1432   Vector2 positionDuration = Vector2::ONE * mSnapDuration;
1433   float biasX = 0.5f;
1434   float biasY = 0.5f;
1435   FindDirection horizontal = None;
1436   FindDirection vertical = None;
1437
1438   // orthoAngleRange = Angle tolerance within the Exact N,E,S,W direction
1439   // that will be accepted as a general N,E,S,W flick direction.
1440
1441   const float orthoAngleRange = FLICK_ORTHO_ANGLE_RANGE * M_PI / 180.0f;
1442   const float flickSpeedThreshold2 = mFlickSpeedThreshold * mFlickSpeedThreshold;
1443
1444   Vector2 positionSnap = mScrollPrePosition;
1445
1446   // Flick logic X Axis
1447
1448   if(mRulerX->IsEnabled() && mLockAxis != LockHorizontal)
1449   {
1450     horizontal = All;
1451
1452     if( speed2 > flickSpeedThreshold2 || // exceeds flick threshold
1453         mInAccessibilityPan ) // With AccessibilityPan its easier to move between snap positions
1454     {
1455       if((angle >= -orthoAngleRange) && (angle < orthoAngleRange)) // Swiping East
1456       {
1457         biasX = 0.0f, horizontal = Left;
1458
1459         // This guards against an error where no movement occurs, due to the flick finishing
1460         // before the update-thread has advanced mScrollPostPosition past the the previous snap point.
1461         positionSnap.x += 1.0f;
1462       }
1463       else if((angle >= M_PI-orthoAngleRange) || (angle < -M_PI+orthoAngleRange)) // Swiping West
1464       {
1465         biasX = 1.0f, horizontal = Right;
1466
1467         // This guards against an error where no movement occurs, due to the flick finishing
1468         // before the update-thread has advanced mScrollPostPosition past the the previous snap point.
1469         positionSnap.x -= 1.0f;
1470       }
1471     }
1472   }
1473
1474   // Flick logic Y Axis
1475
1476   if(mRulerY->IsEnabled() && mLockAxis != LockVertical)
1477   {
1478     vertical = All;
1479
1480     if( speed2 > flickSpeedThreshold2 || // exceeds flick threshold
1481         mInAccessibilityPan ) // With AccessibilityPan its easier to move between snap positions
1482     {
1483       if((angle >= M_PI_2-orthoAngleRange) && (angle < M_PI_2+orthoAngleRange)) // Swiping South
1484       {
1485         biasY = 0.0f, vertical = Up;
1486       }
1487       else if((angle >= -M_PI_2-orthoAngleRange) && (angle < -M_PI_2+orthoAngleRange)) // Swiping North
1488       {
1489         biasY = 1.0f, vertical = Down;
1490       }
1491     }
1492   }
1493
1494   // isFlick: Whether this gesture is a flick or not.
1495   bool isFlick = (horizontal != All || vertical != All);
1496   // isFreeFlick: Whether this gesture is a flick under free panning criteria.
1497   bool isFreeFlick = velocity.LengthSquared() > (FREE_FLICK_SPEED_THRESHOLD*FREE_FLICK_SPEED_THRESHOLD);
1498
1499   if(isFlick || isFreeFlick)
1500   {
1501     positionDuration = Vector2::ONE * mFlickDuration;
1502     alphaFunction = mFlickAlphaFunction;
1503   }
1504
1505   // Calculate next positionSnap ////////////////////////////////////////////////////////////
1506
1507   if(mActorAutoSnapEnabled)
1508   {
1509     Vector3 size = Self().GetCurrentSize();
1510
1511     Actor child = FindClosestActorToPosition( Vector3(size.width * 0.5f,size.height * 0.5f,0.0f), horizontal, vertical );
1512
1513     if(!child && isFlick )
1514     {
1515       // If we conducted a direction limited search and found no actor, then just snap to the closest actor.
1516       child = FindClosestActorToPosition( Vector3(size.width * 0.5f,size.height * 0.5f,0.0f) );
1517     }
1518
1519     if(child)
1520     {
1521       Vector2 position = Self().GetProperty<Vector2>(Toolkit::ScrollView::Property::SCROLL_POSITION);
1522
1523       // Get center-point of the Actor.
1524       Vector3 childPosition = GetPositionOfAnchor(child, AnchorPoint::CENTER);
1525
1526       if(mRulerX->IsEnabled())
1527       {
1528         positionSnap.x = position.x - childPosition.x + size.width * 0.5f;
1529       }
1530       if(mRulerY->IsEnabled())
1531       {
1532         positionSnap.y = position.y - childPosition.y + size.height * 0.5f;
1533       }
1534     }
1535   }
1536
1537   Vector2 startPosition = positionSnap;
1538   positionSnap.x = -mRulerX->Snap(-positionSnap.x, biasX);  // NOTE: X & Y rulers think in -ve coordinate system.
1539   positionSnap.y = -mRulerY->Snap(-positionSnap.y, biasY);  // That is scrolling RIGHT (e.g. 100.0, 0.0) means moving LEFT.
1540
1541   Vector2 clampDelta(Vector2::ZERO);
1542   ClampPosition(positionSnap);
1543
1544   if( (mRulerX->GetType() == Ruler::Free || mRulerY->GetType() == Ruler::Free)
1545       && isFreeFlick && !mActorAutoSnapEnabled)
1546   {
1547     // Calculate target position based on velocity of flick.
1548
1549     // a = Deceleration (Set to diagonal stage length * friction coefficient)
1550     // u = Initial Velocity (Flick velocity)
1551     // v = 0 (Final Velocity)
1552     // t = Time (Velocity / Deceleration)
1553     Vector2 stageSize = Stage::GetCurrent().GetSize();
1554     float stageLength = Vector3(stageSize.x, stageSize.y, 0.0f).Length();
1555     float a = (stageLength * mFrictionCoefficient);
1556     Vector3 u = Vector3(velocity.x, velocity.y, 0.0f) * mFlickSpeedCoefficient;
1557     float speed = u.Length();
1558     u/= speed;
1559
1560     // TODO: Change this to a decay function. (faster you flick, the slower it should be)
1561     speed = std::min(speed, stageLength * mMaxFlickSpeed );
1562     u*= speed;
1563     alphaFunction = ConstantDecelerationAlphaFunction;
1564
1565     float t = speed / a;
1566
1567     if(mRulerX->IsEnabled() && mRulerX->GetType() == Ruler::Free)
1568     {
1569       positionSnap.x += t*u.x*0.5f;
1570     }
1571
1572     if(mRulerY->IsEnabled() && mRulerY->GetType() == Ruler::Free)
1573     {
1574       positionSnap.y += t*u.y*0.5f;
1575     }
1576
1577     clampDelta = positionSnap;
1578     ClampPosition(positionSnap);
1579     if((positionSnap - startPosition).LengthSquared() > Math::MACHINE_EPSILON_0)
1580     {
1581       clampDelta -= positionSnap;
1582       clampDelta.x = clampDelta.x > 0.0f ? std::min(clampDelta.x, mMaxOvershoot.x) : std::max(clampDelta.x, -mMaxOvershoot.x);
1583       clampDelta.y = clampDelta.y > 0.0f ? std::min(clampDelta.y, mMaxOvershoot.y) : std::max(clampDelta.y, -mMaxOvershoot.y);
1584     }
1585     else
1586     {
1587       clampDelta = Vector2::ZERO;
1588     }
1589
1590     // If Axis is Free and has velocity, then calculate time taken
1591     // to reach target based on velocity in axis.
1592     if(mRulerX->IsEnabled() && mRulerX->GetType() == Ruler::Free)
1593     {
1594       float deltaX = fabsf(startPosition.x - positionSnap.x);
1595
1596       if(fabsf(u.x) > Math::MACHINE_EPSILON_1)
1597       {
1598         positionDuration.x = fabsf(deltaX / u.x);
1599       }
1600       else
1601       {
1602         positionDuration.x = 0;
1603       }
1604     }
1605
1606     if(mRulerY->IsEnabled() && mRulerY->GetType() == Ruler::Free)
1607     {
1608       float deltaY = fabsf(startPosition.y - positionSnap.y);
1609
1610       if(fabsf(u.y) > Math::MACHINE_EPSILON_1)
1611       {
1612         positionDuration.y = fabsf(deltaY / u.y);
1613       }
1614       else
1615       {
1616         positionDuration.y = 0;
1617       }
1618     }
1619   }
1620
1621   if(IsOvershootEnabled())
1622   {
1623     // Scroll to the end of the overshoot only when overshoot is enabled.
1624     positionSnap += clampDelta;
1625   }
1626
1627   bool animating = AnimateTo(positionSnap, positionDuration,
1628                              alphaFunction, false,
1629                              DirectionBiasNone, DirectionBiasNone,
1630                              isFlick || isFreeFlick ? Flick : Snap);
1631
1632   return animating;
1633 }
1634
1635 void ScrollView::StopAnimation(void)
1636 {
1637   // Clear Snap animation if exists.
1638   StopAnimation(mInternalXAnimation);
1639   StopAnimation(mInternalYAnimation);
1640   mScrollStateFlags = 0;
1641   // remove scroll animation flags
1642   HandleStoppedAnimation();
1643 }
1644
1645 void ScrollView::StopAnimation(Animation& animation)
1646 {
1647   if(animation)
1648   {
1649     animation.Stop();
1650     animation.Reset();
1651   }
1652 }
1653
1654 bool ScrollView::AnimateTo(const Vector2& position, const Vector2& positionDuration,
1655                            AlphaFunction alpha, bool findShortcuts,
1656                            DirectionBias horizontalBias, DirectionBias verticalBias,
1657                            SnapType snapType)
1658 {
1659   // Here we perform an animation on a number of properties (depending on which have changed)
1660   // The animation is applied to all ScrollBases
1661   Actor self = Self();
1662   mScrollTargetPosition = position;
1663   float totalDuration = 0.0f;
1664
1665   bool positionChanged = (mScrollTargetPosition != mScrollPostPosition);
1666
1667   if(positionChanged)
1668   {
1669     totalDuration = std::max(totalDuration, positionDuration.x);
1670     totalDuration = std::max(totalDuration, positionDuration.y);
1671   }
1672   else
1673   {
1674     // try to animate for a frame, on some occasions update will be changing scroll value while event side thinks it hasnt changed
1675     totalDuration = 0.01f;
1676     positionChanged = true;
1677   }
1678
1679   StopAnimation();
1680
1681   // Position Delta ///////////////////////////////////////////////////////
1682   if(positionChanged)
1683   {
1684     if(mWrapMode && findShortcuts)
1685     {
1686       // In Wrap Mode, the shortest distance is a little less intuitive...
1687       const RulerDomain rulerDomainX = mRulerX->GetDomain();
1688       const RulerDomain rulerDomainY = mRulerY->GetDomain();
1689
1690       if(mRulerX->IsEnabled())
1691       {
1692         float dir = VectorInDomain(-mScrollPrePosition.x, -mScrollTargetPosition.x, rulerDomainX.min, rulerDomainX.max, horizontalBias);
1693         mScrollTargetPosition.x = mScrollPrePosition.x + -dir;
1694       }
1695
1696       if(mRulerY->IsEnabled())
1697       {
1698         float dir = VectorInDomain(-mScrollPrePosition.y, -mScrollTargetPosition.y, rulerDomainY.min, rulerDomainY.max, verticalBias);
1699         mScrollTargetPosition.y = mScrollPrePosition.y + -dir;
1700       }
1701     }
1702
1703     // note we have two separate animations for X & Y, this deals with sliding diagonally and hitting
1704     // a horizonal/vertical wall.delay
1705     AnimateInternalXTo(mScrollTargetPosition.x, positionDuration.x, alpha);
1706     AnimateInternalYTo(mScrollTargetPosition.y, positionDuration.y, alpha);
1707
1708     if( !(mScrollStateFlags & SCROLL_ANIMATION_FLAGS) )
1709     {
1710       DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollTargetPosition.x, mScrollTargetPosition.y );
1711       self.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollTargetPosition);
1712       mScrollPrePosition = mScrollTargetPosition;
1713       mScrollPostPosition = mScrollTargetPosition;
1714       WrapPosition(mScrollPostPosition);
1715     }
1716
1717     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 );
1718     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 );
1719   }
1720
1721   SetScrollUpdateNotification(true);
1722
1723   // Always send a snap event when AnimateTo is called.
1724   Toolkit::ScrollView::SnapEvent snapEvent;
1725   snapEvent.type = snapType;
1726   snapEvent.position = -mScrollTargetPosition;
1727   snapEvent.duration = totalDuration;
1728
1729   DALI_LOG_SCROLL_STATE("[0x%X] mSnapStartedSignal [%.2f, %.2f]", this, snapEvent.position.x, snapEvent.position.y);
1730   mSnapStartedSignal.Emit( snapEvent );
1731
1732   return (mScrollStateFlags & SCROLL_ANIMATION_FLAGS) != 0;
1733 }
1734
1735 void ScrollView::EnableScrollOvershoot(bool enable)
1736 {
1737   if (enable)
1738   {
1739     if (!mOvershootIndicator)
1740     {
1741       mOvershootIndicator = ScrollOvershootIndicator::New();
1742     }
1743
1744     mOvershootIndicator->AttachToScrollable(*this);
1745   }
1746   else
1747   {
1748     mMaxOvershoot = mUserMaxOvershoot;
1749
1750     if (mOvershootIndicator)
1751     {
1752       mOvershootIndicator->DetachFromScrollable(*this);
1753     }
1754   }
1755
1756   UpdateMainInternalConstraint();
1757 }
1758
1759 void ScrollView::AddOverlay(Actor actor)
1760 {
1761   actor.SetDrawMode( DrawMode::OVERLAY_2D );
1762   mInternalActor.Add( actor );
1763 }
1764
1765 void ScrollView::RemoveOverlay(Actor actor)
1766 {
1767   mInternalActor.Remove( actor );
1768 }
1769
1770 void ScrollView::SetOvershootEffectColor( const Vector4& color )
1771 {
1772   mOvershootEffectColor = color;
1773   if( mOvershootIndicator )
1774   {
1775     mOvershootIndicator->SetOvershootEffectColor( color );
1776   }
1777 }
1778
1779 void ScrollView::SetScrollingDirection( Radian direction, Radian threshold )
1780 {
1781   PanGestureDetector panGesture( GetPanGestureDetector() );
1782
1783   // First remove just in case we have some set, then add.
1784   panGesture.RemoveDirection( direction );
1785   panGesture.AddDirection( direction, threshold );
1786 }
1787
1788 void ScrollView::RemoveScrollingDirection( Radian direction )
1789 {
1790   PanGestureDetector panGesture( GetPanGestureDetector() );
1791   panGesture.RemoveDirection( direction );
1792 }
1793
1794 Toolkit::ScrollView::SnapStartedSignalType& ScrollView::SnapStartedSignal()
1795 {
1796   return mSnapStartedSignal;
1797 }
1798
1799 void ScrollView::FindAndUnbindActor(Actor child)
1800 {
1801   UnbindActor(child);
1802 }
1803
1804 Vector2 ScrollView::GetPropertyPrePosition() const
1805 {
1806   Vector2 position = Self().GetProperty<Vector2>(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION);
1807   WrapPosition(position);
1808   return position;
1809 }
1810
1811 Vector2 ScrollView::GetPropertyPosition() const
1812 {
1813   Vector2 position = Self().GetProperty<Vector2>(Toolkit::ScrollView::Property::SCROLL_POSITION);
1814   WrapPosition(position);
1815
1816   return position;
1817 }
1818
1819 void ScrollView::HandleStoppedAnimation()
1820 {
1821   SetScrollUpdateNotification(false);
1822 }
1823
1824 void ScrollView::HandleSnapAnimationFinished()
1825 {
1826   // Emit Signal that scrolling has completed.
1827   mScrolling = false;
1828   Actor self = Self();
1829   self.SetProperty(Toolkit::ScrollView::Property::SCROLLING, false);
1830
1831   Vector2 deltaPosition(mScrollPrePosition);
1832
1833   UpdateLocalScrollProperties();
1834   WrapPosition(mScrollPrePosition);
1835   DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y );
1836   self.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPrePosition);
1837
1838   Vector2 currentScrollPosition = GetCurrentScrollPosition();
1839   DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 3 current[%.2f, %.2f], mScrollTargetPosition[%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y, -mScrollTargetPosition.x, -mScrollTargetPosition.y );
1840   mScrollCompletedSignal.Emit( currentScrollPosition );
1841
1842   mDomainOffset += deltaPosition - mScrollPostPosition;
1843   self.SetProperty(Toolkit::ScrollView::Property::SCROLL_DOMAIN_OFFSET, mDomainOffset);
1844   HandleStoppedAnimation();
1845 }
1846
1847 void ScrollView::SetScrollUpdateNotification( bool enabled )
1848 {
1849   Actor self = Self();
1850   if( mScrollXUpdateNotification )
1851   {
1852     // disconnect now to avoid a notification before removed from update thread
1853     mScrollXUpdateNotification.NotifySignal().Disconnect(this, &ScrollView::OnScrollUpdateNotification);
1854     self.RemovePropertyNotification(mScrollXUpdateNotification);
1855     mScrollXUpdateNotification.Reset();
1856   }
1857   if( enabled && !mScrollUpdatedSignal.Empty())
1858   {
1859     // Only set up the notification when the application has connected to the updated signal
1860     mScrollXUpdateNotification = self.AddPropertyNotification(Toolkit::ScrollView::Property::SCROLL_POSITION, 0, StepCondition(mScrollUpdateDistance, 0.0f));
1861     mScrollXUpdateNotification.NotifySignal().Connect( this, &ScrollView::OnScrollUpdateNotification );
1862   }
1863   if( mScrollYUpdateNotification )
1864   {
1865     // disconnect now to avoid a notification before removed from update thread
1866     mScrollYUpdateNotification.NotifySignal().Disconnect(this, &ScrollView::OnScrollUpdateNotification);
1867     self.RemovePropertyNotification(mScrollYUpdateNotification);
1868     mScrollYUpdateNotification.Reset();
1869   }
1870   if( enabled && !mScrollUpdatedSignal.Empty())
1871   {
1872     // Only set up the notification when the application has connected to the updated signal
1873     mScrollYUpdateNotification = self.AddPropertyNotification(Toolkit::ScrollView::Property::SCROLL_POSITION, 1, StepCondition(mScrollUpdateDistance, 0.0f));
1874     mScrollYUpdateNotification.NotifySignal().Connect( this, &ScrollView::OnScrollUpdateNotification );
1875   }
1876 }
1877
1878 void ScrollView::OnScrollUpdateNotification(Dali::PropertyNotification& source)
1879 {
1880   // Guard against destruction during signal emission
1881   Toolkit::ScrollView handle( GetOwner() );
1882
1883   Vector2 currentScrollPosition = GetCurrentScrollPosition();
1884   mScrollUpdatedSignal.Emit( currentScrollPosition );
1885 }
1886
1887 bool ScrollView::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
1888 {
1889   Dali::BaseHandle handle( object );
1890
1891   bool connected( true );
1892   Toolkit::ScrollView view = Toolkit::ScrollView::DownCast( handle );
1893
1894   if( 0 == strcmp( signalName.c_str(), SIGNAL_SNAP_STARTED ) )
1895   {
1896     view.SnapStartedSignal().Connect( tracker, functor );
1897   }
1898   else
1899   {
1900     // signalName does not match any signal
1901     connected = false;
1902   }
1903
1904   return connected;
1905 }
1906
1907 void ScrollView::OnSizeAnimation(Animation& animation, const Vector3& targetSize)
1908 {
1909   // need to update domain properties for new size
1910   UpdatePropertyDomain();
1911 }
1912
1913 void ScrollView::OnSizeSet( const Vector3& size )
1914 {
1915   // need to update domain properties for new size
1916   if( mDefaultMaxOvershoot )
1917   {
1918     mUserMaxOvershoot.x = size.x * 0.5f;
1919     mUserMaxOvershoot.y = size.y * 0.5f;
1920     if( !IsOvershootEnabled() )
1921     {
1922       mMaxOvershoot = mUserMaxOvershoot;
1923     }
1924   }
1925   UpdatePropertyDomain();
1926   UpdateMainInternalConstraint();
1927   if( IsOvershootEnabled() )
1928   {
1929     mOvershootIndicator->Reset();
1930   }
1931 }
1932
1933 void ScrollView::OnChildAdd(Actor& child)
1934 {
1935   Dali::Toolkit::ScrollBar scrollBar = Dali::Toolkit::ScrollBar::DownCast(child);
1936   if(scrollBar)
1937   {
1938     mInternalActor.Add(scrollBar);
1939     if(scrollBar.GetScrollDirection() == Toolkit::ScrollBar::Horizontal)
1940     {
1941       scrollBar.SetScrollPropertySource(Self(),
1942                                         Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_X,
1943                                         Toolkit::Scrollable::Property::SCROLL_POSITION_MIN_X,
1944                                         Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_MAX_X,
1945                                         Toolkit::ScrollView::Property::SCROLL_DOMAIN_SIZE_X);
1946     }
1947     else
1948     {
1949       scrollBar.SetScrollPropertySource(Self(),
1950                                         Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_Y,
1951                                         Toolkit::Scrollable::Property::SCROLL_POSITION_MIN_Y,
1952                                         Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_MAX_Y,
1953                                         Toolkit::ScrollView::Property::SCROLL_DOMAIN_SIZE_Y);
1954     }
1955   }
1956   else if(mAlterChild)
1957   {
1958     BindActor(child);
1959   }
1960 }
1961
1962 void ScrollView::OnChildRemove(Actor& child)
1963 {
1964   // TODO: Actor needs a RemoveConstraint method to take out an individual constraint.
1965   UnbindActor(child);
1966 }
1967
1968 void ScrollView::StartTouchDownTimer()
1969 {
1970   if ( !mTouchDownTimer )
1971   {
1972     mTouchDownTimer = Timer::New( TOUCH_DOWN_TIMER_INTERVAL );
1973     mTouchDownTimer.TickSignal().Connect( this, &ScrollView::OnTouchDownTimeout );
1974   }
1975
1976   mTouchDownTimer.Start();
1977 }
1978
1979 void ScrollView::StopTouchDownTimer()
1980 {
1981   if ( mTouchDownTimer )
1982   {
1983     mTouchDownTimer.Stop();
1984   }
1985 }
1986
1987 bool ScrollView::OnTouchDownTimeout()
1988 {
1989   DALI_LOG_SCROLL_STATE("[0x%X]", this);
1990
1991   mTouchDownTimeoutReached = true;
1992
1993   unsigned int currentScrollStateFlags( mScrollStateFlags ); // Cleared in StopAnimation so keep local copy for comparison
1994   if( currentScrollStateFlags & (SCROLL_ANIMATION_FLAGS | SNAP_ANIMATION_FLAGS) )
1995   {
1996     DALI_LOG_SCROLL_STATE("[0x%X] Scrolling Or snapping flags set, stopping animation", this);
1997
1998     StopAnimation();
1999     if( currentScrollStateFlags & SCROLL_ANIMATION_FLAGS )
2000     {
2001       DALI_LOG_SCROLL_STATE("[0x%X] Scrolling flags set, emitting signal", this);
2002
2003       mScrollInterrupted = true;
2004       // reset domain offset as scrolling from original plane.
2005       mDomainOffset = Vector2::ZERO;
2006       Self().SetProperty(Toolkit::ScrollView::Property::SCROLL_DOMAIN_OFFSET, Vector2::ZERO);
2007
2008       UpdateLocalScrollProperties();
2009       Vector2 currentScrollPosition = GetCurrentScrollPosition();
2010       DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 4 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
2011       mScrollCompletedSignal.Emit( currentScrollPosition );
2012     }
2013   }
2014
2015   return false;
2016 }
2017
2018 bool ScrollView::OnTouchEvent(const TouchEvent& event)
2019 {
2020   if(!mSensitive)
2021   {
2022     DALI_LOG_SCROLL_STATE("[0x%X], Not Sensitive, ignoring", this);
2023
2024     // Ignore this touch event, if scrollview is insensitive.
2025     return false;
2026   }
2027
2028   // Ignore events with multiple-touch points
2029   if (event.GetPointCount() != 1)
2030   {
2031     DALI_LOG_SCROLL_STATE("[0x%X], multiple touch, ignoring", this);
2032
2033     return false;
2034   }
2035
2036   const TouchPoint::State pointState = event.GetPoint(0).state;
2037   if( pointState == TouchPoint::Down )
2038   {
2039     DALI_LOG_SCROLL_STATE("[0x%X] Down", this);
2040
2041     if(mGestureStackDepth==0)
2042     {
2043       mTouchDownTime = event.time;
2044
2045       // This allows time for a pan-gesture to start, to avoid breaking snap-animation behavior with fast flicks.
2046       // If touch-down does not become a pan (after timeout interval), then snap-animation can be interrupted.
2047       mTouchDownTimeoutReached = false;
2048       mScrollInterrupted = false;
2049       StartTouchDownTimer();
2050     }
2051   }
2052   else if( ( pointState == TouchPoint::Up ) ||
2053            ( ( pointState == TouchPoint::Interrupted ) && ( event.GetPoint(0).hitActor == Self() ) ) )
2054   {
2055     DALI_LOG_SCROLL_STATE("[0x%X] %s", this, ( ( pointState == TouchPoint::Up ) ? "Up" : "Interrupted" ) );
2056
2057     StopTouchDownTimer();
2058
2059     // if the user touches and releases without enough movement to go
2060     // into a gesture state, then we should snap to nearest point.
2061     // otherwise our scroll could be stopped (interrupted) half way through an animation.
2062     if(mGestureStackDepth==0 && mTouchDownTimeoutReached)
2063     {
2064       if( ( event.GetPoint(0).state == TouchPoint::Interrupted ) ||
2065           ( ( event.time - mTouchDownTime ) >= MINIMUM_TIME_BETWEEN_DOWN_AND_UP_FOR_RESET ) )
2066       {
2067         // Reset the velocity only if down was received a while ago
2068         mLastVelocity = Vector2( 0.0f, 0.0f );
2069       }
2070
2071       UpdateLocalScrollProperties();
2072       // Only finish the transform if scrolling was interrupted on down or if we are scrolling
2073       if ( mScrollInterrupted || mScrolling )
2074       {
2075         DALI_LOG_SCROLL_STATE("[0x%X] Calling FinishTransform", this);
2076
2077         FinishTransform();
2078       }
2079     }
2080     mTouchDownTimeoutReached = false;
2081     mScrollInterrupted = false;
2082   }
2083
2084   return true;
2085 }
2086
2087 bool ScrollView::OnWheelEvent(const WheelEvent& event)
2088 {
2089   if(!mSensitive)
2090   {
2091     // Ignore this wheel event, if scrollview is insensitive.
2092     return false;
2093   }
2094
2095   Vector2 targetScrollPosition = GetPropertyPosition();
2096
2097   if(mRulerX->IsEnabled() && !mRulerY->IsEnabled())
2098   {
2099     // If only the ruler in the X axis is enabled, scroll in the X axis.
2100     if(mRulerX->GetType() == Ruler::Free)
2101     {
2102       // Free panning mode
2103       targetScrollPosition.x += event.z * mWheelScrollDistanceStep.x;
2104       ClampPosition(targetScrollPosition);
2105       ScrollTo(-targetScrollPosition);
2106     }
2107     else if(!mScrolling)
2108     {
2109       // Snap mode, only respond to the event when the previous snap animation is finished.
2110       ScrollTo(GetCurrentPage() - event.z);
2111     }
2112   }
2113   else
2114   {
2115     // If the ruler in the Y axis is enabled, scroll in the Y axis.
2116     if(mRulerY->GetType() == Ruler::Free)
2117     {
2118       // Free panning mode
2119       targetScrollPosition.y += event.z * mWheelScrollDistanceStep.y;
2120       ClampPosition(targetScrollPosition);
2121       ScrollTo(-targetScrollPosition);
2122     }
2123     else if(!mScrolling)
2124     {
2125       // Snap mode, only respond to the event when the previous snap animation is finished.
2126       ScrollTo(GetCurrentPage() - event.z * mRulerX->GetTotalPages());
2127     }
2128   }
2129
2130   return true;
2131 }
2132
2133 void ScrollView::ResetScrolling()
2134 {
2135   Actor self = Self();
2136   self.GetProperty(Toolkit::ScrollView::Property::SCROLL_POSITION).Get(mScrollPostPosition);
2137   mScrollPrePosition = mScrollPostPosition;
2138   DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPostPosition.x, mScrollPostPosition.y );
2139   self.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPostPosition);
2140 }
2141
2142 void ScrollView::UpdateLocalScrollProperties()
2143 {
2144   Actor self = Self();
2145   self.GetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION).Get(mScrollPrePosition);
2146   self.GetProperty(Toolkit::ScrollView::Property::SCROLL_POSITION).Get(mScrollPostPosition);
2147 }
2148
2149 // private functions
2150
2151 void ScrollView::PreAnimatedScrollSetup()
2152 {
2153   // SCROLL_PRE_POSITION is our unclamped property with wrapping
2154   // SCROLL_POSITION is our final scroll position after clamping
2155
2156   Actor self = Self();
2157
2158   Vector2 deltaPosition(mScrollPostPosition);
2159   WrapPosition(mScrollPostPosition);
2160   mDomainOffset += deltaPosition - mScrollPostPosition;
2161   Self().SetProperty(Toolkit::ScrollView::Property::SCROLL_DOMAIN_OFFSET, mDomainOffset);
2162
2163   if( mScrollStateFlags & SCROLL_X_STATE_MASK )
2164   {
2165     // already performing animation on internal x position
2166     StopAnimation(mInternalXAnimation);
2167   }
2168
2169   if( mScrollStateFlags & SCROLL_Y_STATE_MASK )
2170   {
2171     // already performing animation on internal y position
2172     StopAnimation(mInternalYAnimation);
2173   }
2174
2175   mScrollStateFlags = 0;
2176
2177   // Update Actor position with this wrapped value.
2178 }
2179
2180 void ScrollView::FinaliseAnimatedScroll()
2181 {
2182   // TODO - common animation finishing code in here
2183 }
2184
2185 void ScrollView::AnimateInternalXTo( float position, float duration, AlphaFunction alpha )
2186 {
2187   StopAnimation(mInternalXAnimation);
2188
2189   if( duration > Math::MACHINE_EPSILON_10 )
2190   {
2191     Actor self = Self();
2192     DALI_LOG_SCROLL_STATE("[0x%X], Animating from[%.2f] to[%.2f]", this, self.GetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION).Get<Vector2>().x, position );
2193     mInternalXAnimation = Animation::New(duration);
2194     DALI_LOG_SCROLL_STATE("[0x%X], mInternalXAnimation[0x%X]", this, mInternalXAnimation.GetObjectPtr() );
2195     mInternalXAnimation.FinishedSignal().Connect(this, &ScrollView::OnScrollAnimationFinished);
2196     mInternalXAnimation.AnimateTo( Property(self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, 0), position, alpha, TimePeriod(duration));
2197     mInternalXAnimation.Play();
2198
2199     // erase current state flags
2200     mScrollStateFlags &= ~SCROLL_X_STATE_MASK;
2201     // add internal animation state flag
2202     mScrollStateFlags |= AnimatingInternalX;
2203   }
2204 }
2205
2206 void ScrollView::AnimateInternalYTo( float position, float duration, AlphaFunction alpha )
2207 {
2208   StopAnimation(mInternalYAnimation);
2209
2210   if( duration > Math::MACHINE_EPSILON_10 )
2211   {
2212     Actor self = Self();
2213     DALI_LOG_SCROLL_STATE("[0x%X], Animating from[%.2f] to[%.2f]", this, self.GetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION).Get<Vector2>().y, position );
2214     mInternalYAnimation = Animation::New(duration);
2215     DALI_LOG_SCROLL_STATE("[0x%X], mInternalYAnimation[0x%X]", this, mInternalYAnimation.GetObjectPtr() );
2216     mInternalYAnimation.FinishedSignal().Connect(this, &ScrollView::OnScrollAnimationFinished);
2217     mInternalYAnimation.AnimateTo( Property(self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, 1), position, alpha, TimePeriod(duration));
2218     mInternalYAnimation.Play();
2219
2220     // erase current state flags
2221     mScrollStateFlags &= ~SCROLL_Y_STATE_MASK;
2222     // add internal animation state flag
2223     mScrollStateFlags |= AnimatingInternalY;
2224   }
2225 }
2226
2227 void ScrollView::OnScrollAnimationFinished( Animation& source )
2228 {
2229   // Guard against destruction during signal emission
2230   // Note that ScrollCompletedSignal is emitted from HandleSnapAnimationFinished()
2231   Toolkit::ScrollView handle( GetOwner() );
2232
2233   bool scrollingFinished = false;
2234
2235   // update our local scroll positions
2236   UpdateLocalScrollProperties();
2237
2238   if( source == mInternalXAnimation )
2239   {
2240     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 );
2241
2242     if( !(mScrollStateFlags & AnimatingInternalY) )
2243     {
2244       scrollingFinished = true;
2245     }
2246     mInternalXAnimation.Reset();
2247     // wrap pre scroll x position and set it
2248     if( mWrapMode )
2249     {
2250       const RulerDomain rulerDomain = mRulerX->GetDomain();
2251       mScrollPrePosition.x = -WrapInDomain(-mScrollPrePosition.x, rulerDomain.min, rulerDomain.max);
2252       DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y );
2253       handle.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPrePosition);
2254     }
2255     SnapInternalXTo(mScrollPostPosition.x);
2256   }
2257
2258   if( source == mInternalYAnimation )
2259   {
2260     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 );
2261
2262     if( !(mScrollStateFlags & AnimatingInternalX) )
2263     {
2264       scrollingFinished = true;
2265     }
2266     mInternalYAnimation.Reset();
2267     if( mWrapMode )
2268     {
2269       // wrap pre scroll y position and set it
2270       const RulerDomain rulerDomain = mRulerY->GetDomain();
2271       mScrollPrePosition.y = -WrapInDomain(-mScrollPrePosition.y, rulerDomain.min, rulerDomain.max);
2272       DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y );
2273       handle.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPrePosition);
2274     }
2275     SnapInternalYTo(mScrollPostPosition.y);
2276   }
2277
2278   DALI_LOG_SCROLL_STATE("[0x%X] scrollingFinished[%d] Animation[0x%X]", this, scrollingFinished, source.GetObjectPtr());
2279
2280   if(scrollingFinished)
2281   {
2282     HandleSnapAnimationFinished();
2283   }
2284 }
2285
2286 void ScrollView::OnSnapInternalPositionFinished( Animation& source )
2287 {
2288   Actor self = Self();
2289   UpdateLocalScrollProperties();
2290   if( source == mInternalXAnimation )
2291   {
2292     DALI_LOG_SCROLL_STATE("[0x%X] Finished X PostPosition Animation", this );
2293
2294     // clear internal x animation flags
2295     mScrollStateFlags &= ~SCROLL_X_STATE_MASK;
2296     mInternalXAnimation.Reset();
2297     WrapPosition(mScrollPrePosition);
2298   }
2299   if( source == mInternalYAnimation )
2300   {
2301     DALI_LOG_SCROLL_STATE("[0x%X] Finished Y PostPosition Animation", this );
2302
2303     mScrollStateFlags &= ~SCROLL_Y_STATE_MASK;
2304     mInternalYAnimation.Reset();
2305     WrapPosition(mScrollPrePosition);
2306   }
2307 }
2308
2309 void ScrollView::SnapInternalXTo(float position)
2310 {
2311   Actor self = Self();
2312
2313   StopAnimation(mInternalXAnimation);
2314
2315   // erase current state flags
2316   mScrollStateFlags &= ~SCROLL_X_STATE_MASK;
2317
2318   // if internal x not equal to inputed parameter, animate it
2319   float duration = std::min(fabsf((position - mScrollPrePosition.x) / mMaxOvershoot.x) * mSnapOvershootDuration, mSnapOvershootDuration);
2320   DALI_LOG_SCROLL_STATE("[0x%X] duration[%.2f]", this, duration );
2321   if( duration > Math::MACHINE_EPSILON_1 )
2322   {
2323     DALI_LOG_SCROLL_STATE("[0x%X] Starting X Snap Animation to[%.2f]", this, position );
2324
2325     mInternalXAnimation = Animation::New(duration);
2326     mInternalXAnimation.FinishedSignal().Connect(this, &ScrollView::OnSnapInternalPositionFinished);
2327     mInternalXAnimation.AnimateTo(Property(self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, 0), position);
2328     mInternalXAnimation.Play();
2329
2330     // add internal animation state flag
2331     mScrollStateFlags |= SnappingInternalX;
2332   }
2333 }
2334
2335 void ScrollView::SnapInternalYTo(float position)
2336 {
2337   Actor self = Self();
2338
2339   StopAnimation(mInternalYAnimation);
2340
2341   // erase current state flags
2342   mScrollStateFlags &= ~SCROLL_Y_STATE_MASK;
2343
2344   // if internal y not equal to inputed parameter, animate it
2345   float duration = std::min(fabsf((position - mScrollPrePosition.y) / mMaxOvershoot.y) * mSnapOvershootDuration, mSnapOvershootDuration);
2346   DALI_LOG_SCROLL_STATE("[0x%X] duration[%.2f]", this, duration );
2347   if( duration > Math::MACHINE_EPSILON_1 )
2348   {
2349     DALI_LOG_SCROLL_STATE("[0x%X] Starting Y Snap Animation to[%.2f]", this, position );
2350
2351     mInternalYAnimation = Animation::New(duration);
2352     mInternalYAnimation.FinishedSignal().Connect(this, &ScrollView::OnSnapInternalPositionFinished);
2353     mInternalYAnimation.AnimateTo(Property(self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, 1), position);
2354     mInternalYAnimation.Play();
2355
2356     // add internal animation state flag
2357     mScrollStateFlags |= SnappingInternalY;
2358   }
2359 }
2360
2361 void ScrollView::GestureStarted()
2362 {
2363   // we handle the first gesture.
2364   // if we're currently doing a gesture and receive another
2365   // we continue and combine the effects of the gesture instead of reseting.
2366   if(mGestureStackDepth++==0)
2367   {
2368     Actor self = Self();
2369     StopTouchDownTimer();
2370     StopAnimation();
2371     mPanDelta = Vector2::ZERO;
2372     mLastVelocity = Vector2::ZERO;
2373     if( !mScrolling )
2374     {
2375       mLockAxis = LockPossible;
2376     }
2377
2378     if( mScrollStateFlags & SCROLL_X_STATE_MASK )
2379     {
2380       StopAnimation(mInternalXAnimation);
2381     }
2382     if( mScrollStateFlags & SCROLL_Y_STATE_MASK )
2383     {
2384       StopAnimation(mInternalYAnimation);
2385     }
2386     mScrollStateFlags = 0;
2387
2388     if(mScrolling) // are we interrupting a current scroll?
2389     {
2390       // set mScrolling to false, in case user has code that interrogates mScrolling Getter() in complete.
2391       mScrolling = false;
2392       // send negative scroll position since scroll internal scroll position works as an offset for actors,
2393       // give applications the position within the domain from the scroll view's anchor position
2394       DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 5 [%.2f, %.2f]", this, -mScrollPostPosition.x, -mScrollPostPosition.y);
2395       mScrollCompletedSignal.Emit( -mScrollPostPosition );
2396     }
2397   }
2398 }
2399
2400 void ScrollView::GestureContinuing(const Vector2& panDelta)
2401 {
2402   mPanDelta.x+= panDelta.x;
2403   mPanDelta.y+= panDelta.y;
2404
2405   // Save the velocity, there is a bug in PanGesture
2406   // Whereby the Gesture::Finished's velocity is either:
2407   // NaN (due to time delta of zero between the last two events)
2408   // or 0 (due to position being the same between the last two events)
2409
2410   // Axis Auto Lock - locks the panning to the horizontal or vertical axis if the pan
2411   // appears mostly horizontal or mostly vertical respectively.
2412   if(mAxisAutoLock)
2413   {
2414     mLockAxis = GetLockAxis(mPanDelta, mLockAxis, mAxisAutoLockGradient);
2415   } // end if mAxisAutoLock
2416 }
2417
2418 // TODO: Upgrade to use a more powerful gesture detector (one that supports multiple touches on pan - so works as pan and flick gesture)
2419 // BUG: Gesture::Finished doesn't always return velocity on release (due to
2420 // timeDelta between last two events being 0 sometimes, or posiiton being the same)
2421 void ScrollView::OnPan( const PanGesture& gesture )
2422 {
2423   // Guard against destruction during signal emission
2424   // Note that Emit() methods are called indirectly e.g. from within ScrollView::OnGestureEx()
2425   Actor self( Self() );
2426
2427   if(!mSensitive)
2428   {
2429     DALI_LOG_SCROLL_STATE("[0x%X] Pan Ignored, Insensitive", this);
2430
2431     // If another callback on the same original signal disables sensitivity,
2432     // this callback will still be called, so we must suppress it.
2433     return;
2434   }
2435
2436   // translate Gesture input to get useful data...
2437   switch(gesture.state)
2438   {
2439     case Gesture::Started:
2440     {
2441       DALI_LOG_SCROLL_STATE("[0x%X] Pan Started", this);
2442       mPanStartPosition = gesture.position - gesture.displacement;
2443       UpdateLocalScrollProperties();
2444       GestureStarted();
2445       mPanning = true;
2446       self.SetProperty( Toolkit::ScrollView::Property::PANNING, true );
2447       self.SetProperty( Toolkit::ScrollView::Property::START_PAGE_POSITION, Vector3(gesture.position.x, gesture.position.y, 0.0f) );
2448
2449       UpdateMainInternalConstraint();
2450       break;
2451     }
2452
2453     case Gesture::Continuing:
2454     {
2455       if ( mPanning )
2456       {
2457         DALI_LOG_SCROLL_STATE("[0x%X] Pan Continuing", this);
2458         GestureContinuing(gesture.screenDisplacement);
2459       }
2460       else
2461       {
2462         // If we do not think we are panning, then we should not do anything here
2463         return;
2464       }
2465       break;
2466     }
2467
2468     case Gesture::Finished:
2469     case Gesture::Cancelled:
2470     {
2471       if ( mPanning )
2472       {
2473         DALI_LOG_SCROLL_STATE("[0x%X] Pan %s", this, ( ( gesture.state == Gesture::Finished ) ? "Finished" : "Cancelled" ) );
2474
2475         UpdateLocalScrollProperties();
2476         mLastVelocity = gesture.velocity;
2477         mPanning = false;
2478         self.SetProperty( Toolkit::ScrollView::Property::PANNING, false );
2479
2480         if( mScrollMainInternalPrePositionConstraint )
2481         {
2482           mScrollMainInternalPrePositionConstraint.Remove();
2483         }
2484       }
2485       else
2486       {
2487         // If we do not think we are panning, then we should not do anything here
2488         return;
2489       }
2490       break;
2491     }
2492
2493     case Gesture::Possible:
2494     case Gesture::Clear:
2495     {
2496       // Nothing to do, not needed.
2497       break;
2498     }
2499
2500   } // end switch(gesture.state)
2501
2502   OnGestureEx(gesture.state);
2503 }
2504
2505 void ScrollView::OnGestureEx(Gesture::State state)
2506 {
2507   // call necessary signals for application developer
2508
2509   if(state == Gesture::Started)
2510   {
2511     Vector2 currentScrollPosition = GetCurrentScrollPosition();
2512     Self().SetProperty(Toolkit::ScrollView::Property::SCROLLING, true);
2513     mScrolling = true;
2514     DALI_LOG_SCROLL_STATE("[0x%X] mScrollStartedSignal 2 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
2515     mScrollStartedSignal.Emit( currentScrollPosition );
2516   }
2517   else if( (state == Gesture::Finished) ||
2518            (state == Gesture::Cancelled) ) // Finished/default
2519   {
2520     // when all the gestures have finished, we finish the transform.
2521     // so if a user decides to pan (1 gesture), and then pan+zoom (2 gestures)
2522     // then stop panning (back to 1 gesture), and then stop zooming (0 gestures).
2523     // this is the point we end, and perform necessary snapping.
2524     mGestureStackDepth--;
2525     if(mGestureStackDepth==0)
2526     {
2527       // no flick if we have not exceeded min flick distance
2528       if( (fabsf(mPanDelta.x) < mMinFlickDistance.x)
2529           && (fabsf(mPanDelta.y) < mMinFlickDistance.y) )
2530       {
2531         // reset flick velocity
2532         mLastVelocity = Vector2::ZERO;
2533       }
2534       FinishTransform();
2535     }
2536     else
2537     {
2538       DALI_LOG_SCROLL_STATE("[0x%X] mGestureStackDepth[%d]", this, mGestureStackDepth);
2539     }
2540   }
2541 }
2542
2543 void ScrollView::FinishTransform()
2544 {
2545   // at this stage internal x and x scroll position should have followed prescroll position exactly
2546   Actor self = Self();
2547
2548   PreAnimatedScrollSetup();
2549
2550   // convert pixels/millisecond to pixels per second
2551   bool animating = SnapWithVelocity(mLastVelocity * 1000.0f);
2552
2553   if(!animating)
2554   {
2555     // if not animating, then this pan has completed right now.
2556     SetScrollUpdateNotification(false);
2557     mScrolling = false;
2558     Self().SetProperty(Toolkit::ScrollView::Property::SCROLLING, false);
2559
2560     if( fabs(mScrollPrePosition.x - mScrollTargetPosition.x) > Math::MACHINE_EPSILON_10 )
2561     {
2562       SnapInternalXTo(mScrollTargetPosition.x);
2563     }
2564     if( fabs(mScrollPrePosition.y - mScrollTargetPosition.y) > Math::MACHINE_EPSILON_10 )
2565     {
2566       SnapInternalYTo(mScrollTargetPosition.y);
2567     }
2568     Vector2 currentScrollPosition = GetCurrentScrollPosition();
2569     DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 6 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
2570     mScrollCompletedSignal.Emit( currentScrollPosition );
2571   }
2572 }
2573
2574 Vector2 ScrollView::GetOvershoot(Vector2& position) const
2575 {
2576   Vector3 size = Self().GetCurrentSize();
2577   Vector2 overshoot;
2578
2579   const RulerDomain rulerDomainX = mRulerX->GetDomain();
2580   const RulerDomain rulerDomainY = mRulerY->GetDomain();
2581
2582   if(mRulerX->IsEnabled() && rulerDomainX.enabled)
2583   {
2584     const float left = rulerDomainX.min - position.x;
2585     const float right = size.width - rulerDomainX.max - position.x;
2586     if(left<0)
2587     {
2588       overshoot.x = left;
2589     }
2590     else if(right>0)
2591     {
2592       overshoot.x = right;
2593     }
2594   }
2595
2596   if(mRulerY->IsEnabled() && rulerDomainY.enabled)
2597   {
2598     const float top = rulerDomainY.min - position.y;
2599     const float bottom = size.height - rulerDomainY.max - position.y;
2600     if(top<0)
2601     {
2602       overshoot.y = top;
2603     }
2604     else if(bottom>0)
2605     {
2606       overshoot.y = bottom;
2607     }
2608   }
2609
2610   return overshoot;
2611 }
2612
2613 bool ScrollView::OnAccessibilityPan(PanGesture gesture)
2614 {
2615   // Keep track of whether this is an AccessibilityPan
2616   mInAccessibilityPan = true;
2617   OnPan(gesture);
2618   mInAccessibilityPan = false;
2619
2620   return true;
2621 }
2622
2623 void ScrollView::ClampPosition(Vector2& position) const
2624 {
2625   ClampState2D clamped;
2626   ClampPosition(position, clamped);
2627 }
2628
2629 void ScrollView::ClampPosition(Vector2& position, ClampState2D &clamped) const
2630 {
2631   Vector3 size = Self().GetCurrentSize();
2632
2633   position.x = -mRulerX->Clamp(-position.x, size.width, 1.0f, clamped.x);    // NOTE: X & Y rulers think in -ve coordinate system.
2634   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.
2635 }
2636
2637 void ScrollView::WrapPosition(Vector2& position) const
2638 {
2639   if(mWrapMode)
2640   {
2641     const RulerDomain rulerDomainX = mRulerX->GetDomain();
2642     const RulerDomain rulerDomainY = mRulerY->GetDomain();
2643
2644     if(mRulerX->IsEnabled())
2645     {
2646       position.x = -WrapInDomain(-position.x, rulerDomainX.min, rulerDomainX.max);
2647     }
2648
2649     if(mRulerY->IsEnabled())
2650     {
2651       position.y = -WrapInDomain(-position.y, rulerDomainY.min, rulerDomainY.max);
2652     }
2653   }
2654 }
2655
2656 void ScrollView::UpdateMainInternalConstraint()
2657 {
2658   // TODO: Only update the constraints which have changed, rather than remove all and add all again.
2659   // Requires a dali-core ApplyConstraintAt, or a ReplaceConstraint. The former is probably more flexible.
2660   Actor self = Self();
2661   PanGestureDetector detector( GetPanGestureDetector() );
2662
2663   if(mScrollMainInternalPositionConstraint)
2664   {
2665     mScrollMainInternalPositionConstraint.Remove();
2666     mScrollMainInternalDeltaConstraint.Remove();
2667     mScrollMainInternalFinalConstraint.Remove();
2668     mScrollMainInternalRelativeConstraint.Remove();
2669     mScrollMainInternalDomainConstraint.Remove();
2670     mScrollMainInternalPrePositionMaxConstraint.Remove();
2671   }
2672   if( mScrollMainInternalPrePositionConstraint )
2673   {
2674     mScrollMainInternalPrePositionConstraint.Remove();
2675   }
2676
2677   // TODO: It's probably better to use a local displacement value as this will give a displacement when scrolling just commences
2678   // but we need to make sure than the gesture system gives displacement since last frame (60Hz), not displacement since last touch event (90Hz).
2679
2680   // 1. First calculate the pre-position (this is the scroll position if no clamping has taken place)
2681   Vector2 initialPanMask = Vector2(mRulerX->IsEnabled() ? 1.0f : 0.0f, mRulerY->IsEnabled() ? 1.0f : 0.0f);
2682
2683   if( mLockAxis == LockVertical )
2684   {
2685     initialPanMask.y = 0.0f;
2686   }
2687   else if( mLockAxis == LockHorizontal )
2688   {
2689     initialPanMask.x = 0.0f;
2690   }
2691
2692   if( mPanning )
2693   {
2694     mScrollMainInternalPrePositionConstraint = Constraint::New<Vector2>( self,
2695                                                                          Toolkit::ScrollView::Property::SCROLL_PRE_POSITION,
2696                                                                          InternalPrePositionConstraint( mPanStartPosition,
2697                                                                                                         initialPanMask,
2698                                                                                                         mAxisAutoLock,
2699                                                                                                         mAxisAutoLockGradient,
2700                                                                                                         mLockAxis,
2701                                                                                                         mMaxOvershoot,
2702                                                                                                         mRulerX,
2703                                                                                                         mRulerY ) );
2704     mScrollMainInternalPrePositionConstraint.AddSource( Source( detector, PanGestureDetector::Property::LOCAL_POSITION ) );
2705     mScrollMainInternalPrePositionConstraint.AddSource( Source( detector, PanGestureDetector::Property::PANNING ) );
2706     mScrollMainInternalPrePositionConstraint.AddSource( Source( self, Actor::Property::SIZE ) );
2707     mScrollMainInternalPrePositionConstraint.Apply();
2708   }
2709
2710   // 2. Second calculate the clamped position (actual position)
2711   mScrollMainInternalPositionConstraint = Constraint::New<Vector2>( self,
2712                                                                     Toolkit::ScrollView::Property::SCROLL_POSITION,
2713                                                                     InternalPositionConstraint( mRulerX->GetDomain(),
2714                                                                                                 mRulerY->GetDomain(),
2715                                                                                                 mWrapMode ) );
2716   mScrollMainInternalPositionConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_PRE_POSITION ) );
2717   mScrollMainInternalPositionConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::SCROLL_POSITION_MIN ) );
2718   mScrollMainInternalPositionConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::SCROLL_POSITION_MAX ) );
2719   mScrollMainInternalPositionConstraint.AddSource( Source( self, Actor::Property::SIZE ) );
2720   mScrollMainInternalPositionConstraint.Apply();
2721
2722   mScrollMainInternalDeltaConstraint = Constraint::New<Vector2>( self, Toolkit::ScrollView::Property::SCROLL_POSITION_DELTA, InternalPositionDeltaConstraint );
2723   mScrollMainInternalDeltaConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_POSITION ) );
2724   mScrollMainInternalDeltaConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_DOMAIN_OFFSET ) );
2725   mScrollMainInternalDeltaConstraint.Apply();
2726
2727   mScrollMainInternalFinalConstraint = Constraint::New<Vector2>( self, Toolkit::ScrollView::Property::SCROLL_FINAL,
2728                                                                  InternalFinalConstraint( FinalDefaultAlphaFunction,
2729                                                                                           FinalDefaultAlphaFunction ) );
2730   mScrollMainInternalFinalConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_POSITION ) );
2731   mScrollMainInternalFinalConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::OVERSHOOT_X ) );
2732   mScrollMainInternalFinalConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::OVERSHOOT_Y ) );
2733   mScrollMainInternalFinalConstraint.Apply();
2734
2735   mScrollMainInternalRelativeConstraint = Constraint::New<Vector2>( self, Toolkit::Scrollable::Property::SCROLL_RELATIVE_POSITION, InternalRelativePositionConstraint );
2736   mScrollMainInternalRelativeConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_POSITION ) );
2737   mScrollMainInternalRelativeConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::SCROLL_POSITION_MIN ) );
2738   mScrollMainInternalRelativeConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::SCROLL_POSITION_MAX ) );
2739   mScrollMainInternalRelativeConstraint.AddSource( LocalSource( Actor::Property::SIZE ) );
2740   mScrollMainInternalRelativeConstraint.Apply();
2741
2742   mScrollMainInternalDomainConstraint = Constraint::New<Vector2>( self, Toolkit::ScrollView::Property::SCROLL_DOMAIN_SIZE, InternalScrollDomainConstraint );
2743   mScrollMainInternalDomainConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::SCROLL_POSITION_MIN ) );
2744   mScrollMainInternalDomainConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::SCROLL_POSITION_MAX ) );
2745   mScrollMainInternalDomainConstraint.AddSource( LocalSource( Actor::Property::SIZE ) );
2746   mScrollMainInternalDomainConstraint.Apply();
2747
2748   mScrollMainInternalPrePositionMaxConstraint = Constraint::New<Vector2>( self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_MAX, InternalPrePositionMaxConstraint );
2749   mScrollMainInternalPrePositionMaxConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::SCROLL_POSITION_MAX ) );
2750   mScrollMainInternalPrePositionMaxConstraint.AddSource( LocalSource( Actor::Property::SIZE ) );
2751   mScrollMainInternalPrePositionMaxConstraint.Apply();
2752
2753   // When panning we want to make sure overshoot values are affected by pre position and post position
2754   SetOvershootConstraintsEnabled(!mWrapMode);
2755 }
2756
2757 void ScrollView::SetOvershootConstraintsEnabled(bool enabled)
2758 {
2759   Actor self( Self() );
2760   // remove and reset, it may now be in wrong order with the main internal constraints
2761   if( mScrollMainInternalOvershootXConstraint )
2762   {
2763     mScrollMainInternalOvershootXConstraint.Remove();
2764     mScrollMainInternalOvershootXConstraint.Reset();
2765     mScrollMainInternalOvershootYConstraint.Remove();
2766     mScrollMainInternalOvershootYConstraint.Reset();
2767   }
2768   if( enabled )
2769   {
2770     mScrollMainInternalOvershootXConstraint= Constraint::New<float>( self, Toolkit::ScrollView::Property::OVERSHOOT_X, OvershootXConstraint(mMaxOvershoot.x) );
2771     mScrollMainInternalOvershootXConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_PRE_POSITION ) );
2772     mScrollMainInternalOvershootXConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_POSITION ) );
2773     mScrollMainInternalOvershootXConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::CAN_SCROLL_HORIZONTAL ) );
2774     mScrollMainInternalOvershootXConstraint.Apply();
2775
2776     mScrollMainInternalOvershootYConstraint = Constraint::New<float>( self, Toolkit::ScrollView::Property::OVERSHOOT_Y, OvershootYConstraint(mMaxOvershoot.y) );
2777     mScrollMainInternalOvershootYConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_PRE_POSITION ) );
2778     mScrollMainInternalOvershootYConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_POSITION ) );
2779     mScrollMainInternalOvershootYConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::CAN_SCROLL_VERTICAL ) );
2780     mScrollMainInternalOvershootYConstraint.Apply();
2781   }
2782   else
2783   {
2784     self.SetProperty(Toolkit::ScrollView::Property::OVERSHOOT_X, 0.0f);
2785     self.SetProperty(Toolkit::ScrollView::Property::OVERSHOOT_Y, 0.0f);
2786   }
2787 }
2788
2789 void ScrollView::SetInternalConstraints()
2790 {
2791   // Internal constraints (applied to target ScrollBase Actor itself) /////////
2792   UpdateMainInternalConstraint();
2793
2794   // User definable constraints to apply to all child actors //////////////////
2795   Actor self = Self();
2796
2797   // Apply some default constraints to ScrollView & its bound actors
2798   // Movement + Wrap function
2799
2800   Constraint constraint;
2801
2802   // MoveActor (scrolling)
2803   constraint = Constraint::New<Vector3>( self, Actor::Property::POSITION, MoveActorConstraint );
2804   constraint.AddSource( Source( self, Toolkit::ScrollView::Property::SCROLL_POSITION ) );
2805   constraint.SetRemoveAction(Constraint::Discard);
2806   ApplyConstraintToBoundActors(constraint);
2807
2808   // WrapActor (wrap functionality)
2809   constraint = Constraint::New<Vector3>( self, Actor::Property::POSITION, WrapActorConstraint );
2810   constraint.AddSource( LocalSource( Actor::Property::SCALE ) );
2811   constraint.AddSource( LocalSource( Actor::Property::ANCHOR_POINT ) );
2812   constraint.AddSource( LocalSource( Actor::Property::SIZE ) );
2813   constraint.AddSource( Source( self, Toolkit::Scrollable::Property::SCROLL_POSITION_MIN ) );
2814   constraint.AddSource( Source( self, Toolkit::Scrollable::Property::SCROLL_POSITION_MAX ) );
2815   constraint.AddSource( Source( self, Toolkit::ScrollView::Property::WRAP ) );
2816   constraint.SetRemoveAction(Constraint::Discard);
2817   ApplyConstraintToBoundActors(constraint);
2818 }
2819
2820 } // namespace Internal
2821
2822 } // namespace Toolkit
2823
2824 } // namespace Dali