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