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