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