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