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