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