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