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