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