Merge remote-tracking branch 'origin/tizen' into new_text
[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
1660   if(IsScrollComponentEnabled(Toolkit::Scrollable::OvershootIndicator))
1661   {
1662     // Scroll to the end of the overshoot only when overshoot is enabled.
1663     positionSnap += clampDelta;
1664   }
1665
1666   bool animating = AnimateTo(positionSnap, positionDuration,
1667                              alphaFunction, false,
1668                              DirectionBiasNone, DirectionBiasNone,
1669                              isFlick || isFreeFlick ? Flick : Snap);
1670
1671   return animating;
1672 }
1673
1674 void ScrollView::StopAnimation(void)
1675 {
1676   // Clear Snap animation if exists.
1677   StopAnimation(mInternalXAnimation);
1678   StopAnimation(mInternalYAnimation);
1679   mScrollStateFlags = 0;
1680   // remove scroll animation flags
1681   HandleStoppedAnimation();
1682 }
1683
1684 void ScrollView::StopAnimation(Animation& animation)
1685 {
1686   if(animation)
1687   {
1688     animation.Stop();
1689     animation.Reset();
1690   }
1691 }
1692
1693 bool ScrollView::AnimateTo(const Vector3& position, const Vector3& positionDuration,
1694                            AlphaFunction alpha, bool findShortcuts,
1695                            DirectionBias horizontalBias, DirectionBias verticalBias,
1696                            SnapType snapType)
1697 {
1698   // Here we perform an animation on a number of properties (depending on which have changed)
1699   // The animation is applied to all ScrollBases
1700   Actor self = Self();
1701   mScrollTargetPosition = position;
1702   float totalDuration = 0.0f;
1703
1704   bool positionChanged = (mScrollTargetPosition != mScrollPostPosition);
1705
1706   if(positionChanged)
1707   {
1708     totalDuration = std::max(totalDuration, positionDuration.x);
1709     totalDuration = std::max(totalDuration, positionDuration.y);
1710   }
1711   else
1712   {
1713     // try to animate for a frame, on some occasions update will be changing scroll value while event side thinks it hasnt changed
1714     totalDuration = 0.01f;
1715     positionChanged = true;
1716   }
1717
1718   StopAnimation();
1719
1720   // Position Delta ///////////////////////////////////////////////////////
1721   if(positionChanged)
1722   {
1723     if(mWrapMode && findShortcuts)
1724     {
1725       // In Wrap Mode, the shortest distance is a little less intuitive...
1726       const RulerDomain rulerDomainX = mRulerX->GetDomain();
1727       const RulerDomain rulerDomainY = mRulerY->GetDomain();
1728
1729       if(mRulerX->IsEnabled())
1730       {
1731         float dir = VectorInDomain(-mScrollPrePosition.x, -mScrollTargetPosition.x, rulerDomainX.min, rulerDomainX.max, horizontalBias);
1732         mScrollTargetPosition.x = mScrollPrePosition.x + -dir;
1733       }
1734
1735       if(mRulerY->IsEnabled())
1736       {
1737         float dir = VectorInDomain(-mScrollPrePosition.y, -mScrollTargetPosition.y, rulerDomainY.min, rulerDomainY.max, verticalBias);
1738         mScrollTargetPosition.y = mScrollPrePosition.y + -dir;
1739       }
1740     }
1741
1742     // note we have two separate animations for X & Y, this deals with sliding diagonally and hitting
1743     // a horizonal/vertical wall.delay
1744     AnimateInternalXTo(mScrollTargetPosition.x, positionDuration.x, alpha);
1745     AnimateInternalYTo(mScrollTargetPosition.y, positionDuration.y, alpha);
1746
1747     if( !(mScrollStateFlags & SCROLL_ANIMATION_FLAGS) )
1748     {
1749       DALI_LOG_SCROLL_STATE("[0x%X] Setting mPropertyPrePosition To[%.2f, %.2f]", this, mScrollTargetPosition.x, mScrollTargetPosition.y );
1750       self.SetProperty(mPropertyPrePosition, mScrollTargetPosition);
1751       mScrollPrePosition = mScrollTargetPosition;
1752       mScrollPostPosition = mScrollTargetPosition;
1753       WrapPosition(mScrollPostPosition);
1754     }
1755
1756     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 );
1757     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 );
1758   }
1759
1760   SetScrollUpdateNotification(true);
1761
1762   // Always send a snap event when AnimateTo is called.
1763   Toolkit::ScrollView::SnapEvent snapEvent;
1764   snapEvent.type = snapType;
1765   snapEvent.position = -mScrollTargetPosition;
1766   snapEvent.duration = totalDuration;
1767
1768   DALI_LOG_SCROLL_STATE("[0x%X] mSnapStartedSignalV2 [%.2f, %.2f]", this, snapEvent.position.x, snapEvent.position.y);
1769   mSnapStartedSignalV2.Emit( snapEvent );
1770
1771   return (mScrollStateFlags & SCROLL_ANIMATION_FLAGS) != 0;
1772 }
1773
1774 void ScrollView::SetOvershootEnabled(bool enabled)
1775 {
1776   if(enabled && !mOvershootIndicator)
1777   {
1778     mOvershootIndicator = ScrollOvershootIndicator::New();
1779   }
1780   if( enabled )
1781   {
1782     mOvershootIndicator->AttachToScrollable(*this);
1783   }
1784   else
1785   {
1786     mMaxOvershoot = mUserMaxOvershoot;
1787     mOvershootIndicator->DetachFromScrollable(*this);
1788   }
1789   UpdateMainInternalConstraint();
1790 }
1791
1792 void ScrollView::AddOverlay(Actor actor)
1793 {
1794   mInternalActor.Add( actor );
1795 }
1796
1797 void ScrollView::RemoveOverlay(Actor actor)
1798 {
1799   mInternalActor.Remove( actor );
1800 }
1801
1802 void ScrollView::SetOvershootEffectColor( const Vector4& color )
1803 {
1804   mOvershootEffectColor = color;
1805   if( mOvershootIndicator )
1806   {
1807     mOvershootIndicator->SetOvershootEffectColor( color );
1808   }
1809 }
1810
1811 void ScrollView::SetScrollingDirection( Radian direction, Radian threshold )
1812 {
1813   PanGestureDetector panGesture( GetPanGestureDetector() );
1814
1815   // First remove just in case we have some set, then add.
1816   panGesture.RemoveDirection( direction );
1817   panGesture.AddDirection( direction, threshold );
1818 }
1819
1820 void ScrollView::RemoveScrollingDirection( Radian direction )
1821 {
1822   PanGestureDetector panGesture( GetPanGestureDetector() );
1823   panGesture.RemoveDirection( direction );
1824 }
1825
1826 Toolkit::ScrollView::SnapStartedSignalV2& ScrollView::SnapStartedSignal()
1827 {
1828   return mSnapStartedSignalV2;
1829 }
1830
1831 void ScrollView::FindAndUnbindActor(Actor child)
1832 {
1833   UnbindActor(child);
1834 }
1835
1836 Vector3 ScrollView::GetPropertyPrePosition() const
1837 {
1838   Vector3 position = Self().GetProperty<Vector3>(mPropertyPrePosition);
1839   WrapPosition(position);
1840   return position;
1841 }
1842
1843 Vector3 ScrollView::GetPropertyPosition() const
1844 {
1845   Vector3 position = Self().GetProperty<Vector3>(mPropertyPosition);
1846   WrapPosition(position);
1847
1848   return position;
1849 }
1850
1851 void ScrollView::HandleStoppedAnimation()
1852 {
1853   SetScrollUpdateNotification(false);
1854 }
1855
1856 void ScrollView::HandleSnapAnimationFinished()
1857 {
1858   // Emit Signal that scrolling has completed.
1859   mScrolling = false;
1860   Actor self = Self();
1861   self.SetProperty(mPropertyScrolling, false);
1862
1863   Vector3 deltaPosition(mScrollPrePosition);
1864
1865   UpdateLocalScrollProperties();
1866   WrapPosition(mScrollPrePosition);
1867   DALI_LOG_SCROLL_STATE("[0x%X] Setting mPropertyPrePosition To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y );
1868   self.SetProperty(mPropertyPrePosition, mScrollPrePosition);
1869
1870   Vector3 currentScrollPosition = GetCurrentScrollPosition();
1871   DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignalV2 3 current[%.2f, %.2f], mScrollTargetPosition[%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y, -mScrollTargetPosition.x, -mScrollTargetPosition.y );
1872   mScrollCompletedSignalV2.Emit( currentScrollPosition );
1873
1874   mDomainOffset += deltaPosition - mScrollPostPosition;
1875   self.SetProperty(mPropertyDomainOffset, mDomainOffset);
1876   HandleStoppedAnimation();
1877 }
1878
1879 void ScrollView::SetScrollUpdateNotification( bool enabled )
1880 {
1881   Actor self = Self();
1882   if( mScrollXUpdateNotification )
1883   {
1884     // disconnect now to avoid a notification before removed from update thread
1885     mScrollXUpdateNotification.NotifySignal().Disconnect(this, &ScrollView::OnScrollUpdateNotification);
1886     self.RemovePropertyNotification(mScrollXUpdateNotification);
1887     mScrollXUpdateNotification.Reset();
1888   }
1889   if( enabled )
1890   {
1891     mScrollXUpdateNotification = self.AddPropertyNotification(mPropertyPosition, 0, StepCondition(mScrollUpdateDistance, 0.0f));
1892     mScrollXUpdateNotification.NotifySignal().Connect( this, &ScrollView::OnScrollUpdateNotification );
1893   }
1894   if( mScrollYUpdateNotification )
1895   {
1896     // disconnect now to avoid a notification before removed from update thread
1897     mScrollYUpdateNotification.NotifySignal().Disconnect(this, &ScrollView::OnScrollUpdateNotification);
1898     self.RemovePropertyNotification(mScrollYUpdateNotification);
1899     mScrollYUpdateNotification.Reset();
1900   }
1901   if( enabled )
1902   {
1903     mScrollYUpdateNotification = self.AddPropertyNotification(mPropertyPosition, 1, StepCondition(mScrollUpdateDistance, 0.0f));
1904     mScrollYUpdateNotification.NotifySignal().Connect( this, &ScrollView::OnScrollUpdateNotification );
1905   }
1906 }
1907
1908 void ScrollView::OnScrollUpdateNotification(Dali::PropertyNotification& source)
1909 {
1910   // Guard against destruction during signal emission
1911   Toolkit::ScrollView handle( GetOwner() );
1912
1913   Vector3 currentScrollPosition = GetCurrentScrollPosition();
1914   mScrollUpdatedSignalV2.Emit( currentScrollPosition );
1915 }
1916
1917 bool ScrollView::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
1918 {
1919   Dali::BaseHandle handle( object );
1920
1921   bool connected( true );
1922   Toolkit::ScrollView view = Toolkit::ScrollView::DownCast( handle );
1923
1924   if( Toolkit::ScrollView::SIGNAL_SNAP_STARTED == signalName )
1925   {
1926     view.SnapStartedSignal().Connect( tracker, functor );
1927   }
1928   else
1929   {
1930     // signalName does not match any signal
1931     connected = false;
1932   }
1933
1934   return connected;
1935 }
1936
1937 void ScrollView::OnSizeAnimation(Animation& animation, const Vector3& targetSize)
1938 {
1939   // need to update domain properties for new size
1940   UpdatePropertyDomain(targetSize);
1941 }
1942
1943 void ScrollView::OnControlSizeSet( const Vector3& size )
1944 {
1945   // need to update domain properties for new size
1946   if( mDefaultMaxOvershoot )
1947   {
1948     mUserMaxOvershoot.x = size.x * 0.5f;
1949     mUserMaxOvershoot.y = size.y * 0.5f;
1950     if( !IsScrollComponentEnabled(Toolkit::Scrollable::OvershootIndicator) )
1951     {
1952       mMaxOvershoot = mUserMaxOvershoot;
1953     }
1954   }
1955   UpdatePropertyDomain(size);
1956   UpdateMainInternalConstraint();
1957   if( IsScrollComponentEnabled(Toolkit::Scrollable::OvershootIndicator) )
1958   {
1959     mOvershootIndicator->Reset();
1960   }
1961 }
1962
1963 void ScrollView::OnChildAdd(Actor& child)
1964 {
1965   if(mAlterChild)
1966   {
1967     BindActor(child);
1968   }
1969 }
1970
1971 void ScrollView::OnChildRemove(Actor& child)
1972 {
1973   // TODO: Actor needs a RemoveConstraint method to take out an individual constraint.
1974   UnbindActor(child);
1975 }
1976
1977 void ScrollView::OnPropertySet( Property::Index index, Property::Value propertyValue )
1978 {
1979   Actor self = Self();
1980   if( index == mPropertyPrePosition )
1981   {
1982     DALI_LOG_SCROLL_STATE("[0x%X]: mPropertyPrePosition[%.2f, %.2f]", this, propertyValue.Get<Vector3>().x, propertyValue.Get<Vector3>().y);
1983     propertyValue.Get(mScrollPrePosition);
1984   }
1985 }
1986
1987 void ScrollView::StartTouchDownTimer()
1988 {
1989   if ( !mTouchDownTimer )
1990   {
1991     mTouchDownTimer = Timer::New( TOUCH_DOWN_TIMER_INTERVAL );
1992     mTouchDownTimer.TickSignal().Connect( this, &ScrollView::OnTouchDownTimeout );
1993   }
1994
1995   mTouchDownTimer.Start();
1996 }
1997
1998 void ScrollView::StopTouchDownTimer()
1999 {
2000   if ( mTouchDownTimer )
2001   {
2002     mTouchDownTimer.Stop();
2003   }
2004 }
2005
2006 bool ScrollView::OnTouchDownTimeout()
2007 {
2008   DALI_LOG_SCROLL_STATE("[0x%X]", this);
2009
2010   mTouchDownTimeoutReached = true;
2011
2012   unsigned int currentScrollStateFlags( mScrollStateFlags ); // Cleared in StopAnimation so keep local copy for comparison
2013   if( currentScrollStateFlags & (SCROLL_ANIMATION_FLAGS | SNAP_ANIMATION_FLAGS) )
2014   {
2015     DALI_LOG_SCROLL_STATE("[0x%X] Scrolling Or snapping flags set, stopping animation", this);
2016
2017     StopAnimation();
2018     if( currentScrollStateFlags & SCROLL_ANIMATION_FLAGS )
2019     {
2020       DALI_LOG_SCROLL_STATE("[0x%X] Scrolling flags set, emitting signal", this);
2021
2022       mScrollInterrupted = true;
2023       // reset domain offset as scrolling from original plane.
2024       mDomainOffset = Vector3::ZERO;
2025       Self().SetProperty(mPropertyDomainOffset, Vector3::ZERO);
2026
2027       UpdateLocalScrollProperties();
2028       Vector3 currentScrollPosition = GetCurrentScrollPosition();
2029       DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignalV2 4 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
2030       mScrollCompletedSignalV2.Emit( currentScrollPosition );
2031     }
2032   }
2033
2034   return false;
2035 }
2036
2037 bool ScrollView::OnTouchEvent(const TouchEvent& event)
2038 {
2039   if(!mSensitive)
2040   {
2041     DALI_LOG_SCROLL_STATE("[0x%X], Not Sensitive, ignoring", this);
2042
2043     // Ignore this touch event, if scrollview is insensitive.
2044     return false;
2045   }
2046
2047   // Ignore events with multiple-touch points
2048   if (event.GetPointCount() != 1)
2049   {
2050     DALI_LOG_SCROLL_STATE("[0x%X], multiple touch, ignoring", this);
2051
2052     return false;
2053   }
2054
2055   const TouchPoint::State pointState = event.GetPoint(0).state;
2056   if( pointState == TouchPoint::Down )
2057   {
2058     DALI_LOG_SCROLL_STATE("[0x%X] Down", this);
2059
2060     if(mGestureStackDepth==0)
2061     {
2062       mTouchDownTime = event.time;
2063
2064       // This allows time for a pan-gesture to start, to avoid breaking snap-animation behavior with fast flicks.
2065       // If touch-down does not become a pan (after timeout interval), then snap-animation can be interrupted.
2066       mTouchDownTimeoutReached = false;
2067       mScrollInterrupted = false;
2068       StartTouchDownTimer();
2069     }
2070   }
2071   else if( ( pointState == TouchPoint::Up ) ||
2072            ( ( pointState == TouchPoint::Interrupted ) && ( event.GetPoint(0).hitActor == Self() ) ) )
2073   {
2074     DALI_LOG_SCROLL_STATE("[0x%X] %s", this, ( ( pointState == TouchPoint::Up ) ? "Up" : "Interrupted" ) );
2075
2076     StopTouchDownTimer();
2077
2078     // if the user touches and releases without enough movement to go
2079     // into a gesture state, then we should snap to nearest point.
2080     // otherwise our scroll could be stopped (interrupted) half way through an animation.
2081     if(mGestureStackDepth==0 && mTouchDownTimeoutReached)
2082     {
2083       if( ( event.GetPoint(0).state == TouchPoint::Interrupted ) ||
2084           ( ( event.time - mTouchDownTime ) >= MINIMUM_TIME_BETWEEN_DOWN_AND_UP_FOR_RESET ) )
2085       {
2086         // Reset the velocity only if down was received a while ago
2087         mLastVelocity = Vector2( 0.0f, 0.0f );
2088       }
2089
2090       UpdateLocalScrollProperties();
2091       // Only finish the transform if scrolling was interrupted on down or if we are scrolling
2092       if ( mScrollInterrupted || mScrolling )
2093       {
2094         DALI_LOG_SCROLL_STATE("[0x%X] Calling FinishTransform", this);
2095
2096         FinishTransform();
2097       }
2098     }
2099     mTouchDownTimeoutReached = false;
2100     mScrollInterrupted = false;
2101   }
2102
2103   return true;
2104 }
2105
2106 bool ScrollView::OnMouseWheelEvent(const MouseWheelEvent& event)
2107 {
2108   if(!mSensitive)
2109   {
2110     // Ignore this mouse wheel event, if scrollview is insensitive.
2111     return false;
2112   }
2113
2114   Vector3 targetScrollPosition = GetPropertyPosition();
2115
2116   if(mRulerX->IsEnabled() && !mRulerY->IsEnabled())
2117   {
2118     // If only the ruler in the X axis is enabled, scroll in the X axis.
2119     if(mRulerX->GetType() == Ruler::Free)
2120     {
2121       // Free panning mode
2122       targetScrollPosition.x += event.z * mMouseWheelScrollDistanceStep.x;
2123       ClampPosition(targetScrollPosition);
2124       ScrollTo(-targetScrollPosition);
2125     }
2126     else if(!mScrolling)
2127     {
2128       // Snap mode, only respond to the event when the previous snap animation is finished.
2129       ScrollTo(GetCurrentPage() - event.z);
2130     }
2131   }
2132   else
2133   {
2134     // If the ruler in the Y axis is enabled, scroll in the Y axis.
2135     if(mRulerY->GetType() == Ruler::Free)
2136     {
2137       // Free panning mode
2138       targetScrollPosition.y += event.z * mMouseWheelScrollDistanceStep.y;
2139       ClampPosition(targetScrollPosition);
2140       ScrollTo(-targetScrollPosition);
2141     }
2142     else if(!mScrolling)
2143     {
2144       // Snap mode, only respond to the event when the previous snap animation is finished.
2145       ScrollTo(GetCurrentPage() - event.z * mRulerX->GetTotalPages());
2146     }
2147   }
2148
2149   return true;
2150 }
2151
2152 void ScrollView::ResetScrolling()
2153 {
2154   Actor self = Self();
2155   self.GetProperty(mPropertyPosition).Get(mScrollPostPosition);
2156   mScrollPrePosition = mScrollPostPosition;
2157   DALI_LOG_SCROLL_STATE("[0x%X] Setting mPropertyPrePosition To[%.2f, %.2f]", this, mScrollPostPosition.x, mScrollPostPosition.y );
2158   self.SetProperty(mPropertyPrePosition, mScrollPostPosition);
2159 }
2160
2161 void ScrollView::UpdateLocalScrollProperties()
2162 {
2163   Actor self = Self();
2164   self.GetProperty(mPropertyPrePosition).Get(mScrollPrePosition);
2165   self.GetProperty(mPropertyPosition).Get(mScrollPostPosition);
2166 }
2167
2168 // private functions
2169
2170 void ScrollView::PreAnimatedScrollSetup()
2171 {
2172   // mPropertyPrePosition is our unclamped property with wrapping
2173   // mPropertyPosition is our final scroll position after clamping
2174
2175   Actor self = Self();
2176
2177   Vector3 deltaPosition(mScrollPostPosition);
2178   WrapPosition(mScrollPostPosition);
2179   mDomainOffset += deltaPosition - mScrollPostPosition;
2180   Self().SetProperty(mPropertyDomainOffset, mDomainOffset);
2181
2182   if( mScrollStateFlags & SCROLL_X_STATE_MASK )
2183   {
2184     // already performing animation on internal x position
2185     StopAnimation(mInternalXAnimation);
2186   }
2187
2188   if( mScrollStateFlags & SCROLL_Y_STATE_MASK )
2189   {
2190     // already performing animation on internal y position
2191     StopAnimation(mInternalYAnimation);
2192   }
2193
2194   mScrollStateFlags = 0;
2195
2196   // Update Actor position with this wrapped value.
2197 }
2198
2199 void ScrollView::FinaliseAnimatedScroll()
2200 {
2201   // TODO - common animation finishing code in here
2202 }
2203
2204 void ScrollView::AnimateInternalXTo( float position, float duration, AlphaFunction alpha )
2205 {
2206   StopAnimation(mInternalXAnimation);
2207
2208   if( duration > Math::MACHINE_EPSILON_10 )
2209   {
2210     Actor self = Self();
2211     DALI_LOG_SCROLL_STATE("[0x%X], Animating from[%.2f] to[%.2f]", this, self.GetProperty(mPropertyPrePosition).Get<Vector3>().x, position );
2212     mInternalXAnimation = Animation::New(duration);
2213     DALI_LOG_SCROLL_STATE("[0x%X], mInternalXAnimation[0x%X]", this, mInternalXAnimation.GetObjectPtr() );
2214     mInternalXAnimation.FinishedSignal().Connect(this, &ScrollView::OnScrollAnimationFinished);
2215     mInternalXAnimation.AnimateTo( Property(self, mPropertyPrePosition, 0), position, alpha, duration);
2216     mInternalXAnimation.Play();
2217
2218     // erase current state flags
2219     mScrollStateFlags &= ~SCROLL_X_STATE_MASK;
2220     // add internal animation state flag
2221     mScrollStateFlags |= AnimatingInternalX;
2222   }
2223 }
2224
2225 void ScrollView::AnimateInternalYTo( float position, float duration, AlphaFunction alpha )
2226 {
2227   StopAnimation(mInternalYAnimation);
2228
2229   if( duration > Math::MACHINE_EPSILON_10 )
2230   {
2231     Actor self = Self();
2232     DALI_LOG_SCROLL_STATE("[0x%X], Animating from[%.2f] to[%.2f]", this, self.GetProperty(mPropertyPrePosition).Get<Vector3>().y, position );
2233     mInternalYAnimation = Animation::New(duration);
2234     DALI_LOG_SCROLL_STATE("[0x%X], mInternalYAnimation[0x%X]", this, mInternalYAnimation.GetObjectPtr() );
2235     mInternalYAnimation.FinishedSignal().Connect(this, &ScrollView::OnScrollAnimationFinished);
2236     mInternalYAnimation.AnimateTo( Property(self, mPropertyPrePosition, 1), position, alpha, TimePeriod(duration));
2237     mInternalYAnimation.Play();
2238
2239     // erase current state flags
2240     mScrollStateFlags &= ~SCROLL_Y_STATE_MASK;
2241     // add internal animation state flag
2242     mScrollStateFlags |= AnimatingInternalY;
2243   }
2244 }
2245
2246 void ScrollView::OnScrollAnimationFinished( Animation& source )
2247 {
2248   // Guard against destruction during signal emission
2249   // Note that ScrollCompletedSignal is emitted from HandleSnapAnimationFinished()
2250   Toolkit::ScrollView handle( GetOwner() );
2251
2252   bool scrollingFinished = false;
2253
2254   // update our local scroll positions
2255   UpdateLocalScrollProperties();
2256
2257   if( source == mInternalXAnimation )
2258   {
2259     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 );
2260
2261     if( !(mScrollStateFlags & AnimatingInternalY) )
2262     {
2263       scrollingFinished = true;
2264     }
2265     mInternalXAnimation.Reset();
2266     // wrap pre scroll x position and set it
2267     if( mWrapMode )
2268     {
2269       const RulerDomain rulerDomain = mRulerX->GetDomain();
2270       mScrollPrePosition.x = -WrapInDomain(-mScrollPrePosition.x, rulerDomain.min, rulerDomain.max);
2271       DALI_LOG_SCROLL_STATE("[0x%X] Setting mPropertyPrePosition To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y );
2272       handle.SetProperty(mPropertyPrePosition, mScrollPrePosition);
2273     }
2274     SnapInternalXTo(mScrollPostPosition.x);
2275   }
2276
2277   if( source == mInternalYAnimation )
2278   {
2279     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 );
2280
2281     if( !(mScrollStateFlags & AnimatingInternalX) )
2282     {
2283       scrollingFinished = true;
2284     }
2285     mInternalYAnimation.Reset();
2286     if( mWrapMode )
2287     {
2288       // wrap pre scroll y position and set it
2289       const RulerDomain rulerDomain = mRulerY->GetDomain();
2290       mScrollPrePosition.y = -WrapInDomain(-mScrollPrePosition.y, rulerDomain.min, rulerDomain.max);
2291       DALI_LOG_SCROLL_STATE("[0x%X] Setting mPropertyPrePosition To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y );
2292       handle.SetProperty(mPropertyPrePosition, mScrollPrePosition);
2293     }
2294     SnapInternalYTo(mScrollPostPosition.y);
2295   }
2296
2297   DALI_LOG_SCROLL_STATE("[0x%X] scrollingFinished[%d] Animation[0x%X]", this, scrollingFinished, source.GetObjectPtr());
2298
2299   if(scrollingFinished)
2300   {
2301     HandleSnapAnimationFinished();
2302   }
2303 }
2304
2305 void ScrollView::OnSnapInternalPositionFinished( Animation& source )
2306 {
2307   Actor self = Self();
2308   UpdateLocalScrollProperties();
2309   if( source == mInternalXAnimation )
2310   {
2311     DALI_LOG_SCROLL_STATE("[0x%X] Finished X PostPosition Animation", this );
2312
2313     // clear internal x animation flags
2314     mScrollStateFlags &= ~SCROLL_X_STATE_MASK;
2315     mInternalXAnimation.Reset();
2316     WrapPosition(mScrollPrePosition);
2317   }
2318   if( source == mInternalYAnimation )
2319   {
2320     DALI_LOG_SCROLL_STATE("[0x%X] Finished Y PostPosition Animation", this );
2321
2322     mScrollStateFlags &= ~SCROLL_Y_STATE_MASK;
2323     mInternalYAnimation.Reset();
2324     WrapPosition(mScrollPrePosition);
2325   }
2326 }
2327
2328 void ScrollView::SnapInternalXTo(float position)
2329 {
2330   Actor self = Self();
2331
2332   StopAnimation(mInternalXAnimation);
2333
2334   // erase current state flags
2335   mScrollStateFlags &= ~SCROLL_X_STATE_MASK;
2336
2337   // if internal x not equal to inputed parameter, animate it
2338   float duration = std::min(fabsf((position - mScrollPrePosition.x) / mMaxOvershoot.x) * mSnapOvershootDuration, mSnapOvershootDuration);
2339   DALI_LOG_SCROLL_STATE("[0x%X] duration[%.2f]", this, duration );
2340   if( duration > Math::MACHINE_EPSILON_1 )
2341   {
2342     DALI_LOG_SCROLL_STATE("[0x%X] Starting X Snap Animation to[%.2f]", this, position );
2343
2344     mInternalXAnimation = Animation::New(duration);
2345     mInternalXAnimation.FinishedSignal().Connect(this, &ScrollView::OnSnapInternalPositionFinished);
2346     mInternalXAnimation.AnimateTo(Property(self, mPropertyPrePosition, 0), position);
2347     mInternalXAnimation.Play();
2348
2349     // add internal animation state flag
2350     mScrollStateFlags |= SnappingInternalX;
2351   }
2352 }
2353
2354 void ScrollView::SnapInternalYTo(float position)
2355 {
2356   Actor self = Self();
2357
2358   StopAnimation(mInternalYAnimation);
2359
2360   // erase current state flags
2361   mScrollStateFlags &= ~SCROLL_Y_STATE_MASK;
2362
2363   // if internal y not equal to inputed parameter, animate it
2364   float duration = std::min(fabsf((position - mScrollPrePosition.y) / mMaxOvershoot.y) * mSnapOvershootDuration, mSnapOvershootDuration);
2365   DALI_LOG_SCROLL_STATE("[0x%X] duration[%.2f]", this, duration );
2366   if( duration > Math::MACHINE_EPSILON_1 )
2367   {
2368     DALI_LOG_SCROLL_STATE("[0x%X] Starting Y Snap Animation to[%.2f]", this, position );
2369
2370     mInternalYAnimation = Animation::New(duration);
2371     mInternalYAnimation.FinishedSignal().Connect(this, &ScrollView::OnSnapInternalPositionFinished);
2372     mInternalYAnimation.AnimateTo(Property(self, mPropertyPrePosition, 1), position);
2373     mInternalYAnimation.Play();
2374
2375     // add internal animation state flag
2376     mScrollStateFlags |= SnappingInternalY;
2377   }
2378 }
2379
2380 void ScrollView::GestureStarted()
2381 {
2382   // we handle the first gesture.
2383   // if we're currently doing a gesture and receive another
2384   // we continue and combine the effects of the gesture instead of reseting.
2385   if(mGestureStackDepth++==0)
2386   {
2387     Actor self = Self();
2388     StopTouchDownTimer();
2389     StopAnimation();
2390     mPanDelta = Vector3::ZERO;
2391     mLastVelocity = Vector2(0.0f, 0.0f);
2392     if( !mScrolling )
2393     {
2394       mLockAxis = LockPossible;
2395     }
2396
2397     if( mScrollStateFlags & SCROLL_X_STATE_MASK )
2398     {
2399       StopAnimation(mInternalXAnimation);
2400     }
2401     if( mScrollStateFlags & SCROLL_Y_STATE_MASK )
2402     {
2403       StopAnimation(mInternalYAnimation);
2404     }
2405     mScrollStateFlags = 0;
2406
2407     if(mScrolling) // are we interrupting a current scroll?
2408     {
2409       // set mScrolling to false, in case user has code that interrogates mScrolling Getter() in complete.
2410       mScrolling = false;
2411       // send negative scroll position since scroll internal scroll position works as an offset for actors,
2412       // give applications the position within the domain from the scroll view's anchor position
2413       DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignalV2 5 [%.2f, %.2f]", this, -mScrollPostPosition.x, -mScrollPostPosition.y);
2414       mScrollCompletedSignalV2.Emit( -mScrollPostPosition );
2415     }
2416   }
2417 }
2418
2419 void ScrollView::GestureContinuing(const Vector2& panDelta)
2420 {
2421   mPanDelta.x+= panDelta.x;
2422   mPanDelta.y+= panDelta.y;
2423
2424   // Save the velocity, there is a bug in PanGesture
2425   // Whereby the Gesture::Finished's velocity is either:
2426   // NaN (due to time delta of zero between the last two events)
2427   // or 0 (due to position being the same between the last two events)
2428
2429   // Axis Auto Lock - locks the panning to the horizontal or vertical axis if the pan
2430   // appears mostly horizontal or mostly vertical respectively.
2431   if(mAxisAutoLock)
2432   {
2433     mLockAxis = GetLockAxis(mPanDelta.GetVectorXY(), mLockAxis, mAxisAutoLockGradient);
2434   } // end if mAxisAutoLock
2435 }
2436
2437 // TODO: Upgrade to use a more powerful gesture detector (one that supports multiple touches on pan - so works as pan and flick gesture)
2438 // BUG: Gesture::Finished doesn't always return velocity on release (due to
2439 // timeDelta between last two events being 0 sometimes, or posiiton being the same)
2440 void ScrollView::OnPan( const PanGesture& gesture )
2441 {
2442   // Guard against destruction during signal emission
2443   // Note that Emit() methods are called indirectly e.g. from within ScrollView::OnGestureEx()
2444   Actor self( Self() );
2445
2446   if(!mSensitive)
2447   {
2448     DALI_LOG_SCROLL_STATE("[0x%X] Pan Ignored, Insensitive", this);
2449
2450     // If another callback on the same original signal disables sensitivity,
2451     // this callback will still be called, so we must suppress it.
2452     return;
2453   }
2454
2455   // translate Gesture input to get useful data...
2456   switch(gesture.state)
2457   {
2458     case Gesture::Started:
2459     {
2460       DALI_LOG_SCROLL_STATE("[0x%X] Pan Started", this);
2461       mPanStartPosition = gesture.position - gesture.displacement;
2462       UpdateLocalScrollProperties();
2463       GestureStarted();
2464       mPanning = true;
2465       self.SetProperty( mPropertyPanning, true );
2466       self.SetProperty( mPropertyScrollStartPagePosition, Vector3(gesture.position.x, gesture.position.y, 0.0f) );
2467
2468       UpdateMainInternalConstraint();
2469       break;
2470     }
2471
2472     case Gesture::Continuing:
2473     {
2474       if ( mPanning )
2475       {
2476         DALI_LOG_SCROLL_STATE("[0x%X] Pan Continuing", this);
2477         GestureContinuing(gesture.screenDisplacement);
2478       }
2479       else
2480       {
2481         // If we do not think we are panning, then we should not do anything here
2482         return;
2483       }
2484       break;
2485     }
2486
2487     case Gesture::Finished:
2488     case Gesture::Cancelled:
2489     {
2490       if ( mPanning )
2491       {
2492         DALI_LOG_SCROLL_STATE("[0x%X] Pan %s", this, ( ( gesture.state == Gesture::Finished ) ? "Finished" : "Cancelled" ) );
2493
2494         UpdateLocalScrollProperties();
2495         mLastVelocity = gesture.velocity;
2496         mPanning = false;
2497         self.SetProperty( mPropertyPanning, false );
2498
2499         if( mScrollMainInternalPrePositionConstraint )
2500         {
2501           self.RemoveConstraint(mScrollMainInternalPrePositionConstraint);
2502         }
2503
2504         if( mOvershootIndicator )
2505         {
2506           mOvershootIndicator->ClearOvershoot();
2507         }
2508       }
2509       else
2510       {
2511         // If we do not think we are panning, then we should not do anything here
2512         return;
2513       }
2514       break;
2515     }
2516
2517     case Gesture::Possible:
2518     case Gesture::Clear:
2519     {
2520       // Nothing to do, not needed.
2521       break;
2522     }
2523
2524   } // end switch(gesture.state)
2525
2526   OnGestureEx(gesture.state);
2527 }
2528
2529 void ScrollView::OnGestureEx(Gesture::State state)
2530 {
2531   // call necessary signals for application developer
2532
2533   if(state == Gesture::Started)
2534   {
2535     Vector3 currentScrollPosition = GetCurrentScrollPosition();
2536     Self().SetProperty(mPropertyScrolling, true);
2537     mScrolling = true;
2538     DALI_LOG_SCROLL_STATE("[0x%X] mScrollStartedSignalV2 2 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
2539     mScrollStartedSignalV2.Emit( currentScrollPosition );
2540   }
2541   else if( (state == Gesture::Finished) ||
2542            (state == Gesture::Cancelled) ) // Finished/default
2543   {
2544     // when all the gestures have finished, we finish the transform.
2545     // so if a user decides to pan (1 gesture), and then pan+zoom (2 gestures)
2546     // then stop panning (back to 1 gesture), and then stop zooming (0 gestures).
2547     // this is the point we end, and perform necessary snapping.
2548     mGestureStackDepth--;
2549     if(mGestureStackDepth==0)
2550     {
2551       // no flick if we have not exceeded min flick distance
2552       if( (fabsf(mPanDelta.x) < mMinFlickDistance.x)
2553           && (fabsf(mPanDelta.y) < mMinFlickDistance.y) )
2554       {
2555         // reset flick velocity
2556         mLastVelocity = Vector2::ZERO;
2557       }
2558       FinishTransform();
2559     }
2560     else
2561     {
2562       DALI_LOG_SCROLL_STATE("[0x%X] mGestureStackDepth[%d]", this, mGestureStackDepth);
2563     }
2564   }
2565 }
2566
2567 void ScrollView::FinishTransform()
2568 {
2569   // at this stage internal x and x scroll position should have followed prescroll position exactly
2570   Actor self = Self();
2571
2572   PreAnimatedScrollSetup();
2573
2574   // convert pixels/millisecond to pixels per second
2575   bool animating = SnapWithVelocity(mLastVelocity * 1000.0f);
2576
2577   if(!animating)
2578   {
2579     // if not animating, then this pan has completed right now.
2580     SetScrollUpdateNotification(false);
2581     mScrolling = false;
2582     Self().SetProperty(mPropertyScrolling, false);
2583
2584     if( fabs(mScrollPrePosition.x - mScrollTargetPosition.x) > Math::MACHINE_EPSILON_10 )
2585     {
2586       SnapInternalXTo(mScrollTargetPosition.x);
2587     }
2588     if( fabs(mScrollPrePosition.y - mScrollTargetPosition.y) > Math::MACHINE_EPSILON_10 )
2589     {
2590       SnapInternalYTo(mScrollTargetPosition.y);
2591     }
2592     Vector3 currentScrollPosition = GetCurrentScrollPosition();
2593     DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignalV2 6 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
2594     mScrollCompletedSignalV2.Emit( currentScrollPosition );
2595   }
2596 }
2597
2598 Vector3 ScrollView::GetOvershoot(Vector3& position) const
2599 {
2600   Vector3 size = Self().GetCurrentSize();
2601   Vector3 overshoot;
2602
2603   const RulerDomain rulerDomainX = mRulerX->GetDomain();
2604   const RulerDomain rulerDomainY = mRulerY->GetDomain();
2605
2606   if(mRulerX->IsEnabled() && rulerDomainX.enabled)
2607   {
2608     const float left = rulerDomainX.min - position.x;
2609     const float right = size.width - rulerDomainX.max - position.x;
2610     if(left<0)
2611     {
2612       overshoot.x = left;
2613     }
2614     else if(right>0)
2615     {
2616       overshoot.x = right;
2617     }
2618   }
2619
2620   if(mRulerY->IsEnabled() && rulerDomainY.enabled)
2621   {
2622     const float top = rulerDomainY.min - position.y;
2623     const float bottom = size.height - rulerDomainY.max - position.y;
2624     if(top<0)
2625     {
2626       overshoot.y = top;
2627     }
2628     else if(bottom>0)
2629     {
2630       overshoot.y = bottom;
2631     }
2632   }
2633
2634   return overshoot;
2635 }
2636
2637 bool ScrollView::OnAccessibilityPan(PanGesture gesture)
2638 {
2639   // Keep track of whether this is an AccessibilityPan
2640   mInAccessibilityPan = true;
2641   OnPan(gesture);
2642   mInAccessibilityPan = false;
2643
2644   return true;
2645 }
2646
2647 void ScrollView::ClampPosition(Vector3& position) const
2648 {
2649   ClampState3 clamped;
2650   ClampPosition(position, clamped);
2651 }
2652
2653 void ScrollView::ClampPosition(Vector3& position, ClampState3 &clamped) const
2654 {
2655   Vector3 size = Self().GetCurrentSize();
2656
2657   position.x = -mRulerX->Clamp(-position.x, size.width, 1.0f, clamped.x);    // NOTE: X & Y rulers think in -ve coordinate system.
2658   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.
2659
2660   clamped.z = NotClamped;
2661 }
2662
2663 void ScrollView::WrapPosition(Vector3& position) const
2664 {
2665   if(mWrapMode)
2666   {
2667     const RulerDomain rulerDomainX = mRulerX->GetDomain();
2668     const RulerDomain rulerDomainY = mRulerY->GetDomain();
2669
2670     if(mRulerX->IsEnabled())
2671     {
2672       position.x = -WrapInDomain(-position.x, rulerDomainX.min, rulerDomainX.max);
2673     }
2674
2675     if(mRulerY->IsEnabled())
2676     {
2677       position.y = -WrapInDomain(-position.y, rulerDomainY.min, rulerDomainY.max);
2678     }
2679   }
2680 }
2681
2682 void ScrollView::UpdateMainInternalConstraint()
2683 {
2684   // TODO: Only update the constraints which have changed, rather than remove all and add all again.
2685   // Requires a dali-core ApplyConstraintAt, or a ReplaceConstraint. The former is probably more flexible.
2686   Actor self = Self();
2687   PanGestureDetector detector( GetPanGestureDetector() );
2688
2689   if(mScrollMainInternalPositionConstraint)
2690   {
2691     self.RemoveConstraint(mScrollMainInternalPositionConstraint);
2692     self.RemoveConstraint(mScrollMainInternalDeltaConstraint);
2693     self.RemoveConstraint(mScrollMainInternalFinalConstraint);
2694     self.RemoveConstraint(mScrollMainInternalRelativeConstraint);
2695   }
2696   if( mScrollMainInternalPrePositionConstraint )
2697   {
2698     self.RemoveConstraint(mScrollMainInternalPrePositionConstraint);
2699   }
2700
2701   // TODO: It's probably better to use a local displacement value as this will give a displacement when scrolling just commences
2702   // but we need to make sure than the gesture system gives displacement since last frame (60Hz), not displacement since last touch event (90Hz).
2703
2704   // 1. First calculate the pre-position (this is the scroll position if no clamping has taken place)
2705   Vector2 initialPanMask = Vector2(mRulerX->IsEnabled() ? 1.0f : 0.0f, mRulerY->IsEnabled() ? 1.0f : 0.0f);
2706
2707   if( mLockAxis == LockVertical )
2708   {
2709     initialPanMask.y = 0.0f;
2710   }
2711   else if( mLockAxis == LockHorizontal )
2712   {
2713     initialPanMask.x = 0.0f;
2714   }
2715   Constraint constraint;
2716
2717   if( mPanning )
2718   {
2719     constraint = Constraint::New<Vector3>( mPropertyPrePosition,
2720                                                       Source( detector, PanGestureDetector::LOCAL_POSITION ),
2721                                                       Source( self, Actor::SIZE ),
2722                                                       InternalPrePositionConstraint( mPanStartPosition, initialPanMask, mAxisAutoLock, mAxisAutoLockGradient, mLockAxis, mMaxOvershoot, mRulerX->GetDomain(), mRulerY->GetDomain() ) );
2723     mScrollMainInternalPrePositionConstraint = self.ApplyConstraint( constraint );
2724   }
2725
2726   // 2. Second calculate the clamped position (actual position)
2727   constraint = Constraint::New<Vector3>( mPropertyPosition,
2728                                          LocalSource( mPropertyPrePosition ),
2729                                          LocalSource( mPropertyPositionMin ),
2730                                          LocalSource( mPropertyPositionMax ),
2731                                          Source( self, Actor::SIZE ),
2732                                          InternalPositionConstraint( mRulerX->GetDomain(),
2733                                                                      mRulerY->GetDomain(), mWrapMode ) );
2734   mScrollMainInternalPositionConstraint = self.ApplyConstraint( constraint );
2735
2736   constraint = Constraint::New<Vector3>( mPropertyPositionDelta,
2737                                          LocalSource( mPropertyPosition ),
2738                                          LocalSource( mPropertyDomainOffset ),
2739                                          InternalPositionDeltaConstraint );
2740   mScrollMainInternalDeltaConstraint = self.ApplyConstraint( constraint );
2741
2742   constraint = Constraint::New<Vector3>( mPropertyFinal,
2743                                          LocalSource( mPropertyPosition ),
2744                                          LocalSource( mPropertyOvershootX ),
2745                                          LocalSource( mPropertyOvershootY ),
2746                                          InternalFinalConstraint( FinalDefaultAlphaFunction,
2747                                                                   FinalDefaultAlphaFunction ) );
2748   mScrollMainInternalFinalConstraint = self.ApplyConstraint( constraint );
2749
2750   constraint = Constraint::New<Vector3>( mPropertyRelativePosition,
2751                                          LocalSource( mPropertyPosition ),
2752                                          LocalSource( mPropertyPositionMin ),
2753                                          LocalSource( mPropertyPositionMax ),
2754                                          LocalSource( Actor::SIZE ),
2755                                          InternalRelativePositionConstraint );
2756   mScrollMainInternalRelativeConstraint = self.ApplyConstraint( constraint );
2757
2758   // When panning we want to make sure overshoot values are affected by pre position and post position
2759   SetOvershootConstraintsEnabled(!mWrapMode);
2760 }
2761
2762 void ScrollView::SetOvershootConstraintsEnabled(bool enabled)
2763 {
2764   Actor self( Self() );
2765   // remove and reset, it may now be in wrong order with the main internal constraints
2766   if( mScrollMainInternalOvershootXConstraint )
2767   {
2768     self.RemoveConstraint(mScrollMainInternalOvershootXConstraint);
2769     mScrollMainInternalOvershootXConstraint.Reset();
2770     self.RemoveConstraint(mScrollMainInternalOvershootYConstraint);
2771     mScrollMainInternalOvershootYConstraint.Reset();
2772   }
2773   if( enabled )
2774   {
2775     Constraint constraint = Constraint::New<float>( mPropertyOvershootX,
2776                                            LocalSource( mPropertyPrePosition ),
2777                                            LocalSource( mPropertyPosition ),
2778                                            LocalSource( mPropertyCanScrollHorizontal ),
2779                                            OvershootXConstraint(mMaxOvershoot.x) );
2780     mScrollMainInternalOvershootXConstraint = self.ApplyConstraint( constraint );
2781
2782     constraint = Constraint::New<float>( mPropertyOvershootY,
2783                                            LocalSource( mPropertyPrePosition ),
2784                                            LocalSource( mPropertyPosition ),
2785                                            LocalSource( mPropertyCanScrollVertical ),
2786                                            OvershootYConstraint(mMaxOvershoot.y) );
2787     mScrollMainInternalOvershootYConstraint = self.ApplyConstraint( constraint );
2788   }
2789   else
2790   {
2791     self.SetProperty(mPropertyOvershootX, 0.0f);
2792     self.SetProperty(mPropertyOvershootY, 0.0f);
2793   }
2794 }
2795
2796 void ScrollView::SetInternalConstraints()
2797 {
2798   // Internal constraints (applied to target ScrollBase Actor itself) /////////
2799   UpdateMainInternalConstraint();
2800
2801   // User definable constraints to apply to all child actors //////////////////
2802   Actor self = Self();
2803
2804   // Apply some default constraints to ScrollView & its bound actors
2805   // Movement + Wrap function
2806
2807   Constraint constraint;
2808
2809   // MoveActor (scrolling)
2810   constraint = Constraint::New<Vector3>( Actor::POSITION,
2811                                          Source( self, mPropertyPosition ),
2812                                          MoveActorConstraint );
2813   constraint.SetRemoveAction(Constraint::Discard);
2814   ApplyConstraintToBoundActors(constraint);
2815
2816   // WrapActor (wrap functionality)
2817   constraint = Constraint::New<Vector3>( Actor::POSITION,
2818                                                  LocalSource( Actor::SCALE ),
2819                                                  LocalSource( Actor::ANCHOR_POINT ),
2820                                                  LocalSource( Actor::SIZE ),
2821                                                  Source( self, mPropertyPositionMin ),
2822                                                  Source( self, mPropertyPositionMax ),
2823                                                  Source( self, mPropertyWrap ),
2824                                                  WrapActorConstraint );
2825   constraint.SetRemoveAction(Constraint::Discard);
2826   ApplyConstraintToBoundActors(constraint);
2827 }
2828
2829 } // namespace Internal
2830
2831 } // namespace Toolkit
2832
2833 } // namespace Dali