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