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