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