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