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