ScrollView - Increased the angle range for a flick to allow snapping to next snap...
[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 = 75.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     mGestureStackDepth = 0;
1300     self.SetProperty( mPropertyPanning, false );
1301
1302     if( mScrollMainInternalPrePositionConstraint )
1303     {
1304       self.RemoveConstraint(mScrollMainInternalPrePositionConstraint);
1305     }
1306   }
1307
1308   self.SetProperty(mPropertyScrolling, true);
1309   mScrolling = true;
1310
1311   DALI_LOG_SCROLL_STATE("[0x%X] mScrollStartedSignalV2 1 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
1312   mScrollStartedSignalV2.Emit( currentScrollPosition );
1313   bool animating = AnimateTo(-position,
1314                              Vector3::ONE * duration,
1315                              scale,
1316                              Vector3::ONE * duration,
1317                              rotation,
1318                              duration,
1319                              mSnapAlphaFunction,
1320                              true,
1321                              horizontalBias,
1322                              verticalBias,
1323                              Snap);
1324
1325   if(!animating)
1326   {
1327     // if not animating, then this pan has completed right now.
1328     self.SetProperty(mPropertyScrolling, false);
1329     mScrolling = false;
1330
1331     // If we have no duration, then in the next update frame, we will be at the position specified as we just set.
1332     // In this scenario, we cannot return the currentScrollPosition as this is out-of-date and should instead return the requested final position
1333     Vector3 completedPosition( currentScrollPosition );
1334     if( duration <= Math::MACHINE_EPSILON_10 )
1335     {
1336       completedPosition = position;
1337     }
1338
1339     DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignalV2 2 [%.2f, %.2f]", this, completedPosition.x, completedPosition.y);
1340     SetScrollUpdateNotification(false);
1341     mScrollCompletedSignalV2.Emit( completedPosition );
1342   }
1343 }
1344
1345 void ScrollView::ScrollTo(const Vector3& position)
1346 {
1347   ScrollTo(position, mSnapDuration );
1348 }
1349
1350 void ScrollView::ScrollTo(const Vector3& position, float duration)
1351 {
1352   ScrollTo(position, duration, DirectionBiasNone, DirectionBiasNone);
1353 }
1354
1355 void ScrollView::ScrollTo(const Vector3& position, float duration,
1356                           DirectionBias horizontalBias, DirectionBias verticalBias)
1357 {
1358   DALI_LOG_SCROLL_STATE("[0x%X] position[%.2f, %.2f] duration[%.2f]",
1359     this, position.x, position.y, duration, int(horizontalBias), int(verticalBias));
1360
1361   TransformTo(position, mScrollPostScale, mScrollPostRotation, duration, horizontalBias, verticalBias);
1362 }
1363
1364 void ScrollView::ScrollTo(unsigned int page)
1365 {
1366   ScrollTo(page, mSnapDuration);
1367 }
1368
1369 void ScrollView::ScrollTo(unsigned int page, float duration, DirectionBias bias)
1370 {
1371   Vector3 position;
1372   unsigned int volume;
1373   unsigned int libraries;
1374
1375   // The position to scroll to is continuous and linear
1376   // unless a domain has been enabled on the X axis.
1377   // or if WrapMode has been enabled.
1378   bool carryX = mRulerX->GetDomain().enabled | mWrapMode;
1379   bool carryY = mRulerY->GetDomain().enabled | mWrapMode;
1380
1381   position.x = mRulerX->GetPositionFromPage(page, volume, carryX);
1382   position.y = mRulerY->GetPositionFromPage(volume, libraries, carryY);
1383
1384   ScrollTo(position, duration, bias, bias);
1385 }
1386
1387 void ScrollView::ScrollTo(Actor &actor)
1388 {
1389   ScrollTo(actor, mSnapDuration);
1390 }
1391
1392 void ScrollView::ScrollTo(Actor &actor, float duration)
1393 {
1394   DALI_ASSERT_ALWAYS(actor.GetParent() == Self());
1395
1396   Actor self = Self();
1397   Vector3 size = self.GetCurrentSize();
1398   Vector3 position = actor.GetCurrentPosition();
1399   position -= GetPropertyPrePosition();
1400
1401   ScrollTo(Vector3(position.x - size.width * 0.5f, position.y - size.height * 0.5f, 0.0f), duration);
1402 }
1403
1404 Actor ScrollView::FindClosestActor()
1405 {
1406   Actor self = Self();
1407   Vector3 size = self.GetCurrentSize();
1408
1409   return FindClosestActorToPosition(Vector3(size.width * 0.5f,size.height * 0.5f,0.0f));
1410 }
1411
1412 Actor ScrollView::FindClosestActorToPosition(const Vector3& position, FindDirection dirX, FindDirection dirY, FindDirection dirZ)
1413 {
1414   Actor closestChild;
1415   float closestDistance2 = 0.0f;
1416   Vector3 actualPosition = position;
1417
1418   unsigned int numChildren = Self().GetChildCount();
1419
1420   for(unsigned int i = 0; i < numChildren; ++i)
1421   {
1422     Actor child = Self().GetChildAt(i);
1423
1424     if(mInternalActor == child) // ignore internal actor.
1425     {
1426       continue;
1427     }
1428
1429     Vector3 childPosition = GetPositionOfAnchor(child, AnchorPoint::CENTER);
1430
1431     Vector3 delta = childPosition - actualPosition;
1432
1433     // X-axis checking (only find Actors to the [dirX] of actualPosition)
1434     if(dirX > All) // != All,None
1435     {
1436       FindDirection deltaH = delta.x > 0 ? Right : Left;
1437       if(dirX != deltaH)
1438       {
1439         continue;
1440       }
1441     }
1442
1443     // Y-axis checking (only find Actors to the [dirY] of actualPosition)
1444     if(dirY > All) // != All,None
1445     {
1446       FindDirection deltaV = delta.y > 0 ? Down : Up;
1447       if(dirY  != deltaV)
1448       {
1449         continue;
1450       }
1451     }
1452
1453     // Z-axis checking (only find Actors to the [dirZ] of actualPosition)
1454     if(dirZ > All) // != All,None
1455     {
1456       FindDirection deltaV = delta.y > 0 ? In : Out;
1457       if(dirZ  != deltaV)
1458       {
1459         continue;
1460       }
1461     }
1462
1463     // compare child to closest child in terms of distance.
1464     float distance2 = 0.0f;
1465
1466     // distance2 = the Square of the relevant dimensions of delta
1467     if(dirX != None)
1468     {
1469       distance2 += delta.x * delta.x;
1470     }
1471
1472     if(dirY != None)
1473     {
1474       distance2 += delta.y * delta.y;
1475     }
1476
1477     if(dirZ != None)
1478     {
1479       distance2 += delta.z * delta.z;
1480     }
1481
1482     if(closestChild) // Next time.
1483     {
1484       if(distance2 < closestDistance2)
1485       {
1486         closestChild = child;
1487         closestDistance2 = distance2;
1488       }
1489     }
1490     else // First time.
1491     {
1492       closestChild = child;
1493       closestDistance2 = distance2;
1494     }
1495   }
1496
1497   return closestChild;
1498 }
1499
1500 bool ScrollView::ScrollToSnapPoint()
1501 {
1502   Vector2 stationaryVelocity = Vector2(0.0f, 0.0f);
1503   return SnapWithVelocity( stationaryVelocity );
1504 }
1505
1506 void ScrollView::ScaleTo(const Vector3& scale)
1507 {
1508   ScaleTo(scale, mSnapDuration);
1509 }
1510
1511 void ScrollView::ScaleTo(const Vector3& scale, float duration)
1512 {
1513   TransformTo(mScrollPostPosition, scale, mScrollPostRotation, duration);
1514 }
1515
1516
1517 // TODO: In situations where axes are different (X snap, Y free)
1518 // Each axis should really have their own independent animation (time and equation)
1519 // Consider, X axis snapping to nearest grid point (EaseOut over fixed time)
1520 // Consider, Y axis simulating physics to arrive at a point (Physics equation over variable time)
1521 // Currently, the axes have been split however, they both use the same EaseOut equation.
1522 bool ScrollView::SnapWithVelocity(Vector2 velocity)
1523 {
1524   // Animator takes over now, touches are assumed not to interfere.
1525   // And if touches do interfere, then we'll stop animation, update PrePosition
1526   // to current mScroll's properties, and then resume.
1527   // Note: For Flicking this may work a bit different...
1528
1529   float angle = atan2(velocity.y, velocity.x);
1530   float speed2 = velocity.LengthSquared();
1531   AlphaFunction alphaFunction = mSnapAlphaFunction;
1532   Vector3 positionDuration = Vector3::ONE * mSnapDuration;
1533   Vector3 scaleDuration = Vector3::ONE * mSnapDuration;
1534   float rotationDuration = mSnapDuration;
1535   float biasX = 0.5f;
1536   float biasY = 0.5f;
1537   FindDirection horizontal = None;
1538   FindDirection vertical = None;
1539
1540   // orthoAngleRange = Angle tolerance within the Exact N,E,S,W direction
1541   // that will be accepted as a general N,E,S,W flick direction.
1542
1543   const float orthoAngleRange = FLICK_ORTHO_ANGLE_RANGE * M_PI / 180.0f;
1544   const float flickSpeedThreshold2 = FLICK_SPEED_THRESHOLD*FLICK_SPEED_THRESHOLD;
1545
1546   Vector3 positionSnap = mScrollPrePosition;
1547
1548   // Flick logic X Axis
1549
1550   if(mRulerX->IsEnabled() && mLockAxis != LockHorizontal)
1551   {
1552     horizontal = All;
1553
1554     if( speed2 > flickSpeedThreshold2 || // exceeds flick threshold
1555         mInAccessibilityPan ) // With AccessibilityPan its easier to move between snap positions
1556     {
1557       if((angle >= -orthoAngleRange) && (angle < orthoAngleRange)) // Swiping East
1558       {
1559         biasX = 0.0f, horizontal = Left;
1560
1561         // This guards against an error where no movement occurs, due to the flick finishing
1562         // before the update-thread has advanced mScrollPostPosition past the the previous snap point.
1563         positionSnap.x += 1.0f;
1564       }
1565       else if((angle >= M_PI-orthoAngleRange) || (angle < -M_PI+orthoAngleRange)) // Swiping West
1566       {
1567         biasX = 1.0f, horizontal = Right;
1568
1569         // This guards against an error where no movement occurs, due to the flick finishing
1570         // before the update-thread has advanced mScrollPostPosition past the the previous snap point.
1571         positionSnap.x -= 1.0f;
1572       }
1573     }
1574   }
1575
1576   // Flick logic Y Axis
1577
1578   if(mRulerY->IsEnabled() && mLockAxis != LockVertical)
1579   {
1580     vertical = All;
1581
1582     if( speed2 > flickSpeedThreshold2 || // exceeds flick threshold
1583         mInAccessibilityPan ) // With AccessibilityPan its easier to move between snap positions
1584     {
1585       if((angle >= M_PI_2-orthoAngleRange) && (angle < M_PI_2+orthoAngleRange)) // Swiping South
1586       {
1587         biasY = 0.0f, vertical = Up;
1588       }
1589       else if((angle >= -M_PI_2-orthoAngleRange) && (angle < -M_PI_2+orthoAngleRange)) // Swiping North
1590       {
1591         biasY = 1.0f, vertical = Down;
1592       }
1593     }
1594   }
1595
1596   // isFlick: Whether this gesture is a flick or not.
1597   bool isFlick = (horizontal != All || vertical != All);
1598   // isFreeFlick: Whether this gesture is a flick under free panning criteria.
1599   bool isFreeFlick = velocity.LengthSquared() > (FREE_FLICK_SPEED_THRESHOLD*FREE_FLICK_SPEED_THRESHOLD);
1600
1601   if(isFlick || isFreeFlick)
1602   {
1603     positionDuration = Vector3::ONE * mFlickDuration;
1604     alphaFunction = mFlickAlphaFunction;
1605   }
1606
1607   // Calculate next positionSnap ////////////////////////////////////////////////////////////
1608
1609   if(mActorAutoSnapEnabled)
1610   {
1611     Vector3 size = Self().GetCurrentSize();
1612
1613     Actor child = FindClosestActorToPosition( Vector3(size.width * 0.5f,size.height * 0.5f,0.0f), horizontal, vertical );
1614
1615     if(!child && isFlick )
1616     {
1617       // If we conducted a direction limited search and found no actor, then just snap to the closest actor.
1618       child = FindClosestActorToPosition( Vector3(size.width * 0.5f,size.height * 0.5f,0.0f) );
1619     }
1620
1621     if(child)
1622     {
1623       Vector3 position = Self().GetProperty<Vector3>(mPropertyPosition);
1624
1625       // Get center-point of the Actor.
1626       Vector3 childPosition = GetPositionOfAnchor(child, AnchorPoint::CENTER);
1627
1628       if(mRulerX->IsEnabled())
1629       {
1630         positionSnap.x = position.x - childPosition.x + size.width * 0.5f;
1631       }
1632       if(mRulerY->IsEnabled())
1633       {
1634         positionSnap.y = position.y - childPosition.y + size.height * 0.5f;
1635       }
1636     }
1637   }
1638
1639   Vector3 startPosition = positionSnap;
1640   positionSnap.x = -mRulerX->Snap(-positionSnap.x, biasX);  // NOTE: X & Y rulers think in -ve coordinate system.
1641   positionSnap.y = -mRulerY->Snap(-positionSnap.y, biasY);  // That is scrolling RIGHT (e.g. 100.0, 0.0) means moving LEFT.
1642
1643   Vector3 clampDelta(Vector3::ZERO);
1644   ClampPosition(positionSnap);
1645
1646   if( (mRulerX->GetType() == Ruler::Free || mRulerY->GetType() == Ruler::Free)
1647       && isFreeFlick && !mActorAutoSnapEnabled)
1648   {
1649     // Calculate target position based on velocity of flick.
1650
1651     // a = Deceleration (Set to diagonal stage length * friction coefficient)
1652     // u = Initial Velocity (Flick velocity)
1653     // v = 0 (Final Velocity)
1654     // t = Time (Velocity / Deceleration)
1655     Vector2 stageSize = Stage::GetCurrent().GetSize();
1656     float stageLength = Vector3(stageSize.x, stageSize.y, 0.0f).Length();
1657     float a = (stageLength * mFrictionCoefficient);
1658     Vector3 u = Vector3(velocity.x, velocity.y, 0.0f) * mFlickSpeedCoefficient;
1659     float speed = u.Length();
1660     u/= speed;
1661
1662     // TODO: Change this to a decay function. (faster you flick, the slower it should be)
1663     speed = std::min(speed, stageLength * mMaxFlickSpeed );
1664     u*= speed;
1665     alphaFunction = ConstantDecelerationAlphaFunction;
1666
1667     float t = speed / a;
1668
1669     if(mRulerX->IsEnabled() && mRulerX->GetType() == Ruler::Free)
1670     {
1671       positionSnap.x += t*u.x*0.5f;
1672     }
1673
1674     if(mRulerY->IsEnabled() && mRulerY->GetType() == Ruler::Free)
1675     {
1676       positionSnap.y += t*u.y*0.5f;
1677     }
1678
1679     clampDelta = positionSnap;
1680     ClampPosition(positionSnap);
1681     if((positionSnap - startPosition).LengthSquared() > Math::MACHINE_EPSILON_0)
1682     {
1683       clampDelta -= positionSnap;
1684       clampDelta.x = clampDelta.x > 0.0f ? std::min(clampDelta.x, mMaxOvershoot.x) : std::max(clampDelta.x, -mMaxOvershoot.x);
1685       clampDelta.y = clampDelta.y > 0.0f ? std::min(clampDelta.y, mMaxOvershoot.y) : std::max(clampDelta.y, -mMaxOvershoot.y);
1686     }
1687     else
1688     {
1689       clampDelta = Vector3::ZERO;
1690     }
1691
1692     // If Axis is Free and has velocity, then calculate time taken
1693     // to reach target based on velocity in axis.
1694     if(mRulerX->IsEnabled() && mRulerX->GetType() == Ruler::Free)
1695     {
1696       float deltaX = fabsf(startPosition.x - positionSnap.x);
1697
1698       if(fabsf(u.x) > Math::MACHINE_EPSILON_1)
1699       {
1700         positionDuration.x = fabsf(deltaX / u.x);
1701       }
1702       else
1703       {
1704         positionDuration.x = 0;
1705       }
1706     }
1707
1708     if(mRulerY->IsEnabled() && mRulerY->GetType() == Ruler::Free)
1709     {
1710       float deltaY = fabsf(startPosition.y - positionSnap.y);
1711
1712       if(fabsf(u.y) > Math::MACHINE_EPSILON_1)
1713       {
1714         positionDuration.y = fabsf(deltaY / u.y);
1715       }
1716       else
1717       {
1718         positionDuration.y = 0;
1719       }
1720     }
1721   }
1722   positionSnap += clampDelta;
1723
1724   // Scale Snap ///////////////////////////////////////////////////////////////
1725   Vector3 scaleSnap = mScrollPostScale;
1726
1727   scaleSnap.x = mRulerScaleX->Snap(scaleSnap.x);
1728   scaleSnap.y = mRulerScaleY->Snap(scaleSnap.y);
1729
1730   ClampScale(scaleSnap);
1731
1732   // Rotation Snap ////////////////////////////////////////////////////////////
1733   float rotationSnap = mScrollPostRotation;
1734   // TODO: implement rotation snap
1735
1736   bool animating = AnimateTo(positionSnap, positionDuration,
1737                              scaleSnap, scaleDuration,
1738                              rotationSnap, rotationDuration,
1739                              alphaFunction, false,
1740                              DirectionBiasNone, DirectionBiasNone,
1741                              isFlick || isFreeFlick ? Flick : Snap);
1742
1743   return animating;
1744 }
1745
1746 void ScrollView::StopAnimation(void)
1747 {
1748   // Clear Snap animation if exists.
1749   StopAnimation(mSnapAnimation);
1750   StopAnimation(mInternalXAnimation);
1751   StopAnimation(mInternalYAnimation);
1752   mScrollStateFlags = 0;
1753   // remove scroll animation flags
1754   HandleStoppedAnimation();
1755 }
1756
1757 void ScrollView::StopAnimation(Animation& animation)
1758 {
1759   if(animation)
1760   {
1761     animation.Stop();
1762     animation.Reset();
1763   }
1764 }
1765
1766 bool ScrollView::AnimateTo(const Vector3& position, const Vector3& positionDuration,
1767                            const Vector3& scale, const Vector3& scaleDuration,
1768                            float rotation, float rotationDuration,
1769                            AlphaFunction alpha, bool findShortcuts,
1770                            DirectionBias horizontalBias, DirectionBias verticalBias,
1771                            SnapType snapType)
1772 {
1773   // Here we perform an animation on a number of properties (depending on which have changed)
1774   // The animation is applied to all ScrollBases
1775   Actor self = Self();
1776   mScrollTargetPosition = position;
1777   float totalDuration = 0.0f;
1778
1779   bool positionChanged = (mScrollTargetPosition != mScrollPostPosition);
1780   bool scaleChanged = (scale != mScrollPostScale);
1781   bool rotationChanged = fabsf(rotation - mScrollPostRotation) > Math::MACHINE_EPSILON_0;
1782
1783   if(positionChanged)
1784   {
1785     totalDuration = std::max(totalDuration, positionDuration.x);
1786     totalDuration = std::max(totalDuration, positionDuration.y);
1787   }
1788   else
1789   {
1790     // try to animate for a frame, on some occasions update will be changing scroll value while event side thinks it hasnt changed
1791     totalDuration = 0.01f;
1792     positionChanged = true;
1793   }
1794
1795   if(scaleChanged)
1796   {
1797     totalDuration = std::max(totalDuration, scaleDuration.x);
1798     totalDuration = std::max(totalDuration, scaleDuration.y);
1799   }
1800
1801   if(rotationChanged)
1802   {
1803     totalDuration = std::max(totalDuration, rotationDuration);
1804   }
1805   StopAnimation();
1806
1807   // Position Delta ///////////////////////////////////////////////////////
1808   if(positionChanged)
1809   {
1810     if(mWrapMode && findShortcuts)
1811     {
1812       // In Wrap Mode, the shortest distance is a little less intuitive...
1813       const RulerDomain rulerDomainX = mRulerX->GetDomain();
1814       const RulerDomain rulerDomainY = mRulerY->GetDomain();
1815
1816       if(mRulerX->IsEnabled())
1817       {
1818         float dir = VectorInDomain(-mScrollPrePosition.x, -mScrollTargetPosition.x, rulerDomainX.min, rulerDomainX.max, horizontalBias);
1819         mScrollTargetPosition.x = mScrollPrePosition.x + -dir;
1820       }
1821
1822       if(mRulerY->IsEnabled())
1823       {
1824         float dir = VectorInDomain(-mScrollPrePosition.y, -mScrollTargetPosition.y, rulerDomainY.min, rulerDomainY.max, verticalBias);
1825         mScrollTargetPosition.y = mScrollPrePosition.y + -dir;
1826       }
1827     }
1828
1829     // note we have two separate animations for X & Y, this deals with sliding diagonally and hitting
1830     // a horizonal/vertical wall.delay
1831     AnimateInternalXTo(mScrollTargetPosition.x, positionDuration.x, alpha);
1832     AnimateInternalYTo(mScrollTargetPosition.y, positionDuration.y, alpha);
1833
1834     if( !(mScrollStateFlags & SCROLL_ANIMATION_FLAGS) )
1835     {
1836       self.SetProperty(mPropertyPrePosition, mScrollTargetPosition);
1837       mScrollPrePosition = mScrollTargetPosition;
1838       mScrollPostPosition = mScrollTargetPosition;
1839       WrapPosition(mScrollPostPosition);
1840     }
1841   }
1842
1843   // Scale Delta ///////////////////////////////////////////////////////
1844   if(scaleChanged)
1845   {
1846     if(totalDuration > Math::MACHINE_EPSILON_1)
1847     {
1848       mSnapAnimation = Animation::New(totalDuration);
1849       mSnapAnimation.FinishedSignal().Connect(this, &ScrollView::OnScrollAnimationFinished);
1850       // TODO: for non-uniform scaling to different bounds e.g. scaling a square to a 4:3 aspect ratio screen with a velocity
1851       // the height will hit first, and then the width, so that would require two different animation times just like position.
1852       mSnapAnimation.AnimateTo( Property(self, mPropertyScale), scale, alpha, TimePeriod(0.0f, scaleDuration.x));
1853
1854       mSnapAnimation.AnimateTo( Property(self, mPropertyTime), totalDuration, AlphaFunctions::Linear );
1855       mSnapAnimation.Play();
1856     }
1857     else
1858     {
1859       self.SetProperty(mPropertyScale, scale);
1860
1861       mScrollPreScale = mScrollPostScale = scale;
1862     }
1863   }
1864   SetScrollUpdateNotification(true);
1865
1866   // Always send a snap event when AnimateTo is called.
1867   Toolkit::ScrollView::SnapEvent snapEvent;
1868   snapEvent.type = snapType;
1869   snapEvent.position = -mScrollTargetPosition;
1870   snapEvent.scale = scale;
1871   snapEvent.rotation = rotation;
1872   snapEvent.duration = totalDuration;
1873
1874   DALI_LOG_SCROLL_STATE("[0x%X] mSnapStartedSignalV2 [%.2f, %.2f]", this, snapEvent.position.x, snapEvent.position.y);
1875   mSnapStartedSignalV2.Emit( snapEvent );
1876
1877   return (mScrollStateFlags & SCROLL_ANIMATION_FLAGS) != 0;
1878 }
1879
1880 void ScrollView::SetOvershootEnabled(bool enabled)
1881 {
1882   if(enabled && !mOvershootIndicator)
1883   {
1884     mOvershootIndicator = ScrollOvershootIndicator::New();
1885   }
1886   if( enabled )
1887   {
1888     mMaxOvershoot = OVERSCROLL_CLAMP;
1889     mOvershootIndicator->AttachToScrollable(*this);
1890   }
1891   else
1892   {
1893     mMaxOvershoot = mUserMaxOvershoot;
1894     mOvershootIndicator->DetachFromScrollable(*this);
1895   }
1896   UpdateMainInternalConstraint();
1897 }
1898
1899 void ScrollView::AddOverlay(Actor actor)
1900 {
1901   mInternalActor.Add( actor );
1902 }
1903
1904 void ScrollView::RemoveOverlay(Actor actor)
1905 {
1906   mInternalActor.Remove( actor );
1907 }
1908
1909 void ScrollView::SetOvershootEffectColor( const Vector4& color )
1910 {
1911   mOvershootEffectColor = color;
1912   if( mOvershootIndicator )
1913   {
1914     mOvershootIndicator->SetOvershootEffectColor( color );
1915   }
1916 }
1917
1918 void ScrollView::SetScrollingDirection( Radian direction, Radian threshold )
1919 {
1920   PanGestureDetector panGesture( GetPanGestureDetector() );
1921
1922   // First remove just in case we have some set, then add.
1923   panGesture.RemoveDirection( direction );
1924   panGesture.AddDirection( direction, threshold );
1925 }
1926
1927 void ScrollView::RemoveScrollingDirection( Radian direction )
1928 {
1929   PanGestureDetector panGesture( GetPanGestureDetector() );
1930   panGesture.RemoveDirection( direction );
1931 }
1932
1933 Toolkit::ScrollView::SnapStartedSignalV2& ScrollView::SnapStartedSignal()
1934 {
1935   return mSnapStartedSignalV2;
1936 }
1937
1938 void ScrollView::FindAndUnbindActor(Actor child)
1939 {
1940   UnbindActor(child);
1941 }
1942
1943 Vector3 ScrollView::GetPropertyPrePosition() const
1944 {
1945   Vector3 position = Self().GetProperty<Vector3>(mPropertyPrePosition);
1946   WrapPosition(position);
1947   return position;
1948 }
1949
1950 Vector3 ScrollView::GetPropertyPosition() const
1951 {
1952   Vector3 position = Self().GetProperty<Vector3>(mPropertyPosition);
1953   WrapPosition(position);
1954
1955   return position;
1956 }
1957
1958 Vector3 ScrollView::GetPropertyScale() const
1959 {
1960   return Self().GetProperty<Vector3>(mPropertyScale);
1961 }
1962
1963 void ScrollView::HandleStoppedAnimation()
1964 {
1965   SetScrollUpdateNotification(false);
1966 }
1967
1968 void ScrollView::HandleSnapAnimationFinished()
1969 {
1970   // Emit Signal that scrolling has completed.
1971   mScrolling = false;
1972   Actor self = Self();
1973   self.SetProperty(mPropertyScrolling, false);
1974
1975   Vector3 deltaPosition(mScrollPrePosition);
1976
1977   UpdateLocalScrollProperties();
1978   WrapPosition(mScrollPrePosition);
1979   self.SetProperty(mPropertyPrePosition, mScrollPrePosition);
1980
1981   Vector3 currentScrollPosition = GetCurrentScrollPosition();
1982   DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignalV2 3 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
1983   mScrollCompletedSignalV2.Emit( currentScrollPosition );
1984
1985   mDomainOffset += deltaPosition - mScrollPostPosition;
1986   self.SetProperty(mPropertyDomainOffset, mDomainOffset);
1987   HandleStoppedAnimation();
1988 }
1989
1990 void ScrollView::SetScrollUpdateNotification( bool enabled )
1991 {
1992   Actor self = Self();
1993   if( mScrollXUpdateNotification )
1994   {
1995     // disconnect now to avoid a notification before removed from update thread
1996     mScrollXUpdateNotification.NotifySignal().Disconnect(this, &ScrollView::OnScrollUpdateNotification);
1997     self.RemovePropertyNotification(mScrollXUpdateNotification);
1998     mScrollXUpdateNotification.Reset();
1999   }
2000   if( enabled )
2001   {
2002     mScrollXUpdateNotification = self.AddPropertyNotification(mPropertyPosition, 0, StepCondition(mScrollUpdateDistance, 0.0f));
2003     mScrollXUpdateNotification.NotifySignal().Connect( this, &ScrollView::OnScrollUpdateNotification );
2004   }
2005   if( mScrollYUpdateNotification )
2006   {
2007     // disconnect now to avoid a notification before removed from update thread
2008     mScrollYUpdateNotification.NotifySignal().Disconnect(this, &ScrollView::OnScrollUpdateNotification);
2009     self.RemovePropertyNotification(mScrollYUpdateNotification);
2010     mScrollYUpdateNotification.Reset();
2011   }
2012   if( enabled )
2013   {
2014     mScrollYUpdateNotification = self.AddPropertyNotification(mPropertyPosition, 1, StepCondition(mScrollUpdateDistance, 0.0f));
2015     mScrollYUpdateNotification.NotifySignal().Connect( this, &ScrollView::OnScrollUpdateNotification );
2016   }
2017 }
2018
2019 void ScrollView::OnScrollUpdateNotification(Dali::PropertyNotification& source)
2020 {
2021   // Guard against destruction during signal emission
2022   Toolkit::ScrollView handle( GetOwner() );
2023
2024   Vector3 currentScrollPosition = GetCurrentScrollPosition();
2025   mScrollUpdatedSignalV2.Emit( currentScrollPosition );
2026 }
2027
2028 bool ScrollView::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
2029 {
2030   Dali::BaseHandle handle( object );
2031
2032   bool connected( true );
2033   Toolkit::ScrollView view = Toolkit::ScrollView::DownCast( handle );
2034
2035   if( Toolkit::ScrollView::SIGNAL_SNAP_STARTED == signalName )
2036   {
2037     view.SnapStartedSignal().Connect( tracker, functor );
2038   }
2039   else
2040   {
2041     // signalName does not match any signal
2042     connected = false;
2043   }
2044
2045   return connected;
2046 }
2047
2048 void ScrollView::OnSizeAnimation(Animation& animation, const Vector3& targetSize)
2049 {
2050   // need to update domain properties for new size
2051   UpdatePropertyDomain(targetSize);
2052 }
2053
2054 void ScrollView::OnControlSizeSet( const Vector3& size )
2055 {
2056   // need to update domain properties for new size
2057   if( mDefaultMaxOvershoot )
2058   {
2059     mUserMaxOvershoot.x = size.x * 0.5f;
2060     mUserMaxOvershoot.y = size.y * 0.5f;
2061     if( !IsScrollComponentEnabled(Toolkit::Scrollable::OvershootIndicator) )
2062     {
2063       mMaxOvershoot = mUserMaxOvershoot;
2064     }
2065   }
2066   UpdatePropertyDomain(size);
2067   UpdateMainInternalConstraint();
2068   if( IsScrollComponentEnabled(Toolkit::Scrollable::OvershootIndicator) )
2069   {
2070     mOvershootIndicator->Reset();
2071   }
2072 }
2073
2074 void ScrollView::OnChildAdd(Actor& child)
2075 {
2076   if(mAlterChild)
2077   {
2078     BindActor(child);
2079   }
2080 }
2081
2082 void ScrollView::OnChildRemove(Actor& child)
2083 {
2084   // TODO: Actor needs a RemoveConstraint method to take out an individual constraint.
2085   UnbindActor(child);
2086 }
2087
2088 void ScrollView::OnPropertySet( Property::Index index, Property::Value propertyValue )
2089 {
2090   Actor self = Self();
2091   if( index == mPropertyX )
2092   {
2093     self.GetProperty(mPropertyPrePosition).Get(mScrollPrePosition);
2094     propertyValue.Get(mScrollPrePosition.x);
2095     self.SetProperty(mPropertyPrePosition, mScrollPrePosition);
2096   }
2097   else if( index == mPropertyY )
2098   {
2099     self.GetProperty(mPropertyPrePosition).Get(mScrollPrePosition);
2100     propertyValue.Get(mScrollPrePosition.y);
2101     self.SetProperty(mPropertyPrePosition, mScrollPrePosition);
2102   }
2103   else if( index == mPropertyPrePosition )
2104   {
2105     DALI_LOG_SCROLL_STATE("[0x%X]", this);
2106     propertyValue.Get(mScrollPrePosition);
2107   }
2108 }
2109
2110 void ScrollView::StartTouchDownTimer()
2111 {
2112   if ( !mTouchDownTimer )
2113   {
2114     mTouchDownTimer = Timer::New( TOUCH_DOWN_TIMER_INTERVAL );
2115     mTouchDownTimer.TickSignal().Connect( this, &ScrollView::OnTouchDownTimeout );
2116   }
2117
2118   mTouchDownTimer.Start();
2119 }
2120
2121 void ScrollView::StopTouchDownTimer()
2122 {
2123   if ( mTouchDownTimer )
2124   {
2125     mTouchDownTimer.Stop();
2126   }
2127 }
2128
2129 bool ScrollView::OnTouchDownTimeout()
2130 {
2131   mTouchDownTimeoutReached = true;
2132
2133   if( mScrollStateFlags & (SCROLL_ANIMATION_FLAGS | SNAP_ANIMATION_FLAGS) )
2134   {
2135     StopAnimation();
2136     if( mScrollStateFlags & SCROLL_ANIMATION_FLAGS )
2137     {
2138       mScrollInterrupted = true;
2139       // reset domain offset as scrolling from original plane.
2140       mDomainOffset = Vector3::ZERO;
2141       Self().SetProperty(mPropertyDomainOffset, Vector3::ZERO);
2142
2143       UpdateLocalScrollProperties();
2144       Vector3 currentScrollPosition = GetCurrentScrollPosition();
2145       DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignalV2 4 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
2146       mScrollCompletedSignalV2.Emit( currentScrollPosition );
2147     }
2148   }
2149
2150   return false;
2151 }
2152
2153 bool ScrollView::OnTouchEvent(const TouchEvent& event)
2154 {
2155   if(!mSensitive)
2156   {
2157     // Ignore this touch event, if scrollview is insensitive.
2158     return false;
2159   }
2160
2161   // Ignore events with multiple-touch points
2162   if (event.GetPointCount() != 1)
2163   {
2164     return false;
2165   }
2166
2167   if( event.GetPoint(0).state == TouchPoint::Down )
2168   {
2169     if(mGestureStackDepth==0)
2170     {
2171       mTouchDownTime = event.time;
2172
2173       // This allows time for a pan-gesture to start, to avoid breaking snap-animation behavior with fast flicks.
2174       // If touch-down does not become a pan (after timeout interval), then snap-animation can be interrupted.
2175       StartTouchDownTimer();
2176     }
2177   }
2178   else if( event.GetPoint(0).state == TouchPoint::Up )
2179   {
2180     StopTouchDownTimer();
2181
2182     // if the user touches and releases without enough movement to go
2183     // into a gesture state, then we should snap to nearest point.
2184     // otherwise our scroll could be stopped (interrupted) half way through an animation.
2185     if(mGestureStackDepth==0 && mTouchDownTimeoutReached)
2186     {
2187       unsigned timeDelta( event.time - mTouchDownTime );
2188       if ( timeDelta >= MINIMUM_TIME_BETWEEN_DOWN_AND_UP_FOR_RESET )
2189       {
2190         // Reset the velocity only if down was received a while ago
2191         mLastVelocity = Vector2( 0.0f, 0.0f );
2192       }
2193
2194       UpdateLocalScrollProperties();
2195       // Only finish the transform if scrolling was interrupted on down or if we are scrolling
2196       if ( mScrollInterrupted || mScrolling )
2197       {
2198         FinishTransform();
2199       }
2200     }
2201     mTouchDownTimeoutReached = false;
2202     mScrollInterrupted = false;
2203   }
2204
2205   return true;
2206 }
2207
2208 bool ScrollView::OnMouseWheelEvent(const MouseWheelEvent& event)
2209 {
2210   if(!mSensitive)
2211   {
2212     // Ignore this mouse wheel event, if scrollview is insensitive.
2213     return false;
2214   }
2215
2216   Vector3 targetScrollPosition = GetPropertyPosition();
2217
2218   if(mRulerX->IsEnabled() && !mRulerY->IsEnabled())
2219   {
2220     // If only the ruler in the X axis is enabled, scroll in the X axis.
2221     if(mRulerX->GetType() == Ruler::Free)
2222     {
2223       // Free panning mode
2224       targetScrollPosition.x += event.z * mMouseWheelScrollDistanceStep.x;
2225       ClampPosition(targetScrollPosition);
2226       ScrollTo(-targetScrollPosition);
2227     }
2228     else if(!mScrolling)
2229     {
2230       // Snap mode, only respond to the event when the previous snap animation is finished.
2231       ScrollTo(GetCurrentPage() - event.z);
2232     }
2233   }
2234   else
2235   {
2236     // If the ruler in the Y axis is enabled, scroll in the Y axis.
2237     if(mRulerY->GetType() == Ruler::Free)
2238     {
2239       // Free panning mode
2240       targetScrollPosition.y += event.z * mMouseWheelScrollDistanceStep.y;
2241       ClampPosition(targetScrollPosition);
2242       ScrollTo(-targetScrollPosition);
2243     }
2244     else if(!mScrolling)
2245     {
2246       // Snap mode, only respond to the event when the previous snap animation is finished.
2247       ScrollTo(GetCurrentPage() - event.z * mRulerX->GetTotalPages());
2248     }
2249   }
2250
2251   return true;
2252 }
2253
2254 void ScrollView::ResetScrolling()
2255 {
2256   Actor self = Self();
2257   self.GetProperty(mPropertyPosition).Get(mScrollPostPosition);
2258   mScrollPrePosition = mScrollPostPosition;
2259   self.SetProperty(mPropertyPrePosition, mScrollPostPosition);
2260 }
2261
2262 void ScrollView::UpdateLocalScrollProperties()
2263 {
2264   Actor self = Self();
2265   self.GetProperty(mPropertyPrePosition).Get(mScrollPrePosition);
2266   self.GetProperty(mPropertyPosition).Get(mScrollPostPosition);
2267 }
2268
2269 // private functions
2270
2271 void ScrollView::PreAnimatedScrollSetup()
2272 {
2273   // mPropertyPrePosition is our unclamped property with wrapping
2274   // mPropertyPosition is our final scroll position after clamping
2275
2276   Actor self = Self();
2277
2278   Vector3 deltaPosition(mScrollPostPosition);
2279   WrapPosition(mScrollPostPosition);
2280   mDomainOffset += deltaPosition - mScrollPostPosition;
2281   Self().SetProperty(mPropertyDomainOffset, mDomainOffset);
2282
2283   if( mScrollStateFlags & SCROLL_X_STATE_MASK )
2284   {
2285     // already performing animation on internal x position
2286     StopAnimation(mInternalXAnimation);
2287   }
2288
2289   if( mScrollStateFlags & SCROLL_Y_STATE_MASK )
2290   {
2291     // already performing animation on internal y position
2292     StopAnimation(mInternalYAnimation);
2293   }
2294
2295   mScrollStateFlags = 0;
2296
2297   mScrollPostScale = GetPropertyScale();
2298
2299   // Update Actor position with this wrapped value.
2300   // TODO Rotation
2301
2302   mScrollPreScale = mScrollPostScale;
2303   mScrollPreRotation = mScrollPostRotation;
2304 }
2305
2306 void ScrollView::FinaliseAnimatedScroll()
2307 {
2308   // TODO - common animation finishing code in here
2309 }
2310
2311 void ScrollView::AnimateInternalXTo( float position, float duration, AlphaFunction alpha )
2312 {
2313   StopAnimation(mInternalXAnimation);
2314
2315   if( duration > Math::MACHINE_EPSILON_10 )
2316   {
2317     Actor self = Self();
2318     mInternalXAnimation = Animation::New(duration);
2319     mInternalXAnimation.FinishedSignal().Connect(this, &ScrollView::OnScrollAnimationFinished);
2320     mInternalXAnimation.AnimateTo( Property(self, mPropertyPrePosition, 0), position, alpha, duration);
2321     mInternalXAnimation.Play();
2322
2323     // erase current state flags
2324     mScrollStateFlags &= ~SCROLL_X_STATE_MASK;
2325     // add internal animation state flag
2326     mScrollStateFlags |= AnimatingInternalX;
2327   }
2328 }
2329
2330 void ScrollView::AnimateInternalYTo( float position, float duration, AlphaFunction alpha )
2331 {
2332   StopAnimation(mInternalYAnimation);
2333
2334   if( duration > Math::MACHINE_EPSILON_10 )
2335   {
2336     Actor self = Self();
2337     mInternalYAnimation = Animation::New(duration);
2338     mInternalYAnimation.FinishedSignal().Connect(this, &ScrollView::OnScrollAnimationFinished);
2339     mInternalYAnimation.AnimateTo( Property(self, mPropertyPrePosition, 1), position, alpha, TimePeriod(duration));
2340     mInternalYAnimation.Play();
2341
2342     // erase current state flags
2343     mScrollStateFlags &= ~SCROLL_Y_STATE_MASK;
2344     // add internal animation state flag
2345     mScrollStateFlags |= AnimatingInternalY;
2346   }
2347 }
2348
2349 void ScrollView::OnScrollAnimationFinished( Animation& source )
2350 {
2351   // Guard against destruction during signal emission
2352   // Note that ScrollCompletedSignal is emitted from HandleSnapAnimationFinished()
2353   Toolkit::ScrollView handle( GetOwner() );
2354
2355   bool scrollingFinished = false;
2356
2357   // update our local scroll positions
2358   UpdateLocalScrollProperties();
2359
2360   if( source == mSnapAnimation )
2361   {
2362     // generic snap animation used for scaling and rotation
2363     mSnapAnimation.Reset();
2364   }
2365
2366   if( source == mInternalXAnimation )
2367   {
2368     if( !(mScrollStateFlags & AnimatingInternalY) )
2369     {
2370       scrollingFinished = true;
2371     }
2372     mInternalXAnimation.Reset();
2373     // wrap pre scroll x position and set it
2374     if( mWrapMode )
2375     {
2376       const RulerDomain rulerDomain = mRulerX->GetDomain();
2377       mScrollPrePosition.x = -WrapInDomain(-mScrollPrePosition.x, rulerDomain.min, rulerDomain.max);
2378       handle.SetProperty(mPropertyPrePosition, mScrollPrePosition);
2379     }
2380     SnapInternalXTo(mScrollPostPosition.x);
2381   }
2382
2383   if( source == mInternalYAnimation )
2384   {
2385     if( !(mScrollStateFlags & AnimatingInternalX) )
2386     {
2387       scrollingFinished = true;
2388     }
2389     mInternalYAnimation.Reset();
2390     if( mWrapMode )
2391     {
2392       // wrap pre scroll y position and set it
2393       const RulerDomain rulerDomain = mRulerY->GetDomain();
2394       mScrollPrePosition.y = -WrapInDomain(-mScrollPrePosition.y, rulerDomain.min, rulerDomain.max);
2395       handle.SetProperty(mPropertyPrePosition, mScrollPrePosition);
2396     }
2397     SnapInternalYTo(mScrollPostPosition.y);
2398   }
2399
2400   if(scrollingFinished)
2401   {
2402     HandleSnapAnimationFinished();
2403   }
2404 }
2405
2406 void ScrollView::OnSnapInternalPositionFinished( Animation& source )
2407 {
2408   Actor self = Self();
2409   UpdateLocalScrollProperties();
2410   if( source == mInternalXAnimation )
2411   {
2412     // clear internal x animation flags
2413     mScrollStateFlags &= ~SCROLL_X_STATE_MASK;
2414     mInternalXAnimation.Reset();
2415     WrapPosition(mScrollPrePosition);
2416   }
2417   if( source == mInternalYAnimation )
2418   {
2419     mScrollStateFlags &= ~SCROLL_Y_STATE_MASK;
2420     mInternalYAnimation.Reset();
2421     WrapPosition(mScrollPrePosition);
2422   }
2423 }
2424
2425 void ScrollView::SnapInternalXTo(float position)
2426 {
2427   Actor self = Self();
2428
2429   StopAnimation(mInternalXAnimation);
2430
2431   // erase current state flags
2432   mScrollStateFlags &= ~SCROLL_X_STATE_MASK;
2433
2434   // if internal x not equal to inputed parameter, animate it
2435   float duration = std::min(fabsf((position - mScrollPrePosition.x) / mMaxOvershoot.x) * mSnapOvershootDuration, mSnapOvershootDuration);
2436   if( duration > Math::MACHINE_EPSILON_1 )
2437   {
2438     mInternalXAnimation = Animation::New(duration);
2439     mInternalXAnimation.FinishedSignal().Connect(this, &ScrollView::OnSnapInternalPositionFinished);
2440     mInternalXAnimation.AnimateTo(Property(self, mPropertyPrePosition, 0), position);
2441     mInternalXAnimation.Play();
2442
2443     // add internal animation state flag
2444     mScrollStateFlags |= SnappingInternalX;
2445   }
2446 }
2447
2448 void ScrollView::SnapInternalYTo(float position)
2449 {
2450   Actor self = Self();
2451
2452   StopAnimation(mInternalYAnimation);
2453
2454   // erase current state flags
2455   mScrollStateFlags &= ~SCROLL_Y_STATE_MASK;
2456
2457   // if internal y not equal to inputed parameter, animate it
2458   float duration = std::min(fabsf((position - mScrollPrePosition.y) / mMaxOvershoot.y) * mSnapOvershootDuration, mSnapOvershootDuration);
2459   if( duration > Math::MACHINE_EPSILON_1 )
2460   {
2461     mInternalYAnimation = Animation::New(duration);
2462     mInternalYAnimation.FinishedSignal().Connect(this, &ScrollView::OnSnapInternalPositionFinished);
2463     mInternalYAnimation.AnimateTo(Property(self, mPropertyPrePosition, 1), position);
2464     mInternalYAnimation.Play();
2465
2466     // add internal animation state flag
2467     mScrollStateFlags |= SnappingInternalY;
2468   }
2469 }
2470
2471 void ScrollView::GestureStarted()
2472 {
2473   // we handle the first gesture.
2474   // if we're currently doing a gesture and receive another
2475   // we continue and combine the effects of the gesture instead of reseting.
2476   if(mGestureStackDepth++==0)
2477   {
2478     Actor self = Self();
2479     StopTouchDownTimer();
2480     StopAnimation();
2481     mPanDelta = Vector3::ZERO;
2482     mScaleDelta = Vector3::ONE;
2483     mRotationDelta = 0.0f;
2484     mLastVelocity = Vector2(0.0f, 0.0f);
2485     if( !mScrolling )
2486     {
2487       mLockAxis = LockPossible;
2488     }
2489
2490     if( mScrollStateFlags & SCROLL_X_STATE_MASK )
2491     {
2492       StopAnimation(mInternalXAnimation);
2493     }
2494     if( mScrollStateFlags & SCROLL_Y_STATE_MASK )
2495     {
2496       StopAnimation(mInternalYAnimation);
2497     }
2498     mScrollStateFlags = 0;
2499
2500     if(mScrolling) // are we interrupting a current scroll?
2501     {
2502       // set mScrolling to false, in case user has code that interrogates mScrolling Getter() in complete.
2503       mScrolling = false;
2504       // send negative scroll position since scroll internal scroll position works as an offset for actors,
2505       // give applications the position within the domain from the scroll view's anchor position
2506       DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignalV2 5 [%.2f, %.2f]", this, -mScrollPostPosition.x, -mScrollPostPosition.y);
2507       mScrollCompletedSignalV2.Emit( -mScrollPostPosition );
2508     }
2509   }
2510 }
2511
2512 void ScrollView::GestureContinuing(const Vector2& panDelta, const Vector2& scaleDelta, float rotationDelta)
2513 {
2514   mPanDelta.x+= panDelta.x;
2515   mPanDelta.y+= panDelta.y;
2516   mScaleDelta.x*= scaleDelta.x;
2517   mScaleDelta.y*= scaleDelta.y;
2518   mRotationDelta+= rotationDelta;
2519
2520   // Save the velocity, there is a bug in PanGesture
2521   // Whereby the Gesture::Finished's velocity is either:
2522   // NaN (due to time delta of zero between the last two events)
2523   // or 0 (due to position being the same between the last two events)
2524
2525   // Axis Auto Lock - locks the panning to the horizontal or vertical axis if the pan
2526   // appears mostly horizontal or mostly vertical respectively.
2527   if(mAxisAutoLock)
2528   {
2529     mLockAxis = GetLockAxis(mPanDelta.GetVectorXY(), mLockAxis, mAxisAutoLockGradient);
2530   } // end if mAxisAutoLock
2531 }
2532
2533 // TODO: Upgrade to use a more powerful gesture detector (one that supports multiple touches on pan - so works as pan and flick gesture)
2534 // TODO: Reimplement Scaling (pinching 2+ points)
2535 // TODO: Reimplment Rotation (pinching 2+ points)
2536 // BUG: Gesture::Finished doesn't always return velocity on release (due to
2537 // timeDelta between last two events being 0 sometimes, or posiiton being the same)
2538 void ScrollView::OnPan(PanGesture gesture)
2539 {
2540   // Guard against destruction during signal emission
2541   // Note that Emit() methods are called indirectly e.g. from within ScrollView::OnGestureEx()
2542   Actor self( Self() );
2543
2544   if(!mSensitive)
2545   {
2546     DALI_LOG_SCROLL_STATE("[0x%X] Pan Ignored, Insensitive", this);
2547
2548     // If another callback on the same original signal disables sensitivity,
2549     // this callback will still be called, so we must suppress it.
2550     return;
2551   }
2552
2553   // translate Gesture input to get useful data...
2554   switch(gesture.state)
2555   {
2556     case Gesture::Started:
2557     {
2558       DALI_LOG_SCROLL_STATE("[0x%X] Pan Started", this);
2559       UpdateLocalScrollProperties();
2560       GestureStarted();
2561       mPanning = true;
2562       self.SetProperty( mPropertyPanning, true );
2563       self.SetProperty( mPropertyScrollStartPagePosition, Vector3(gesture.position.x, gesture.position.y, 0.0f) );
2564
2565       UpdateMainInternalConstraint();
2566       break;
2567     }
2568
2569     case Gesture::Continuing:
2570     {
2571       if ( mPanning )
2572       {
2573         DALI_LOG_SCROLL_STATE("[0x%X] Pan Continuing", this);
2574         GestureContinuing(gesture.screenDisplacement, Vector2::ZERO, 0.0f);
2575       }
2576       else
2577       {
2578         // If we do not think we are panning, then we should not do anything here
2579         return;
2580       }
2581       break;
2582     }
2583
2584     case Gesture::Finished:
2585     case Gesture::Cancelled:
2586     {
2587       if ( mPanning )
2588       {
2589         DALI_LOG_SCROLL_STATE("[0x%X] Pan %s", this, ( ( gesture.state == Gesture::Finished ) ? "Finished" : "Cancelled" ) );
2590
2591         UpdateLocalScrollProperties();
2592         mLastVelocity = gesture.velocity;
2593         mPanning = false;
2594         self.SetProperty( mPropertyPanning, false );
2595
2596         if( mScrollMainInternalPrePositionConstraint )
2597         {
2598           self.RemoveConstraint(mScrollMainInternalPrePositionConstraint);
2599         }
2600
2601         if( mOvershootIndicator )
2602         {
2603           mOvershootIndicator->ClearOvershoot();
2604         }
2605       }
2606       else
2607       {
2608         // If we do not think we are panning, then we should not do anything here
2609         return;
2610       }
2611       break;
2612     }
2613
2614     case Gesture::Possible:
2615     case Gesture::Clear:
2616     {
2617       // Nothing to do, not needed.
2618       break;
2619     }
2620
2621   } // end switch(gesture.state)
2622
2623   OnGestureEx(gesture.state);
2624 }
2625
2626 void ScrollView::OnGestureEx(Gesture::State state)
2627 {
2628   // call necessary signals for application developer
2629
2630   if(state == Gesture::Started)
2631   {
2632     Vector3 currentScrollPosition = GetCurrentScrollPosition();
2633     Self().SetProperty(mPropertyScrolling, true);
2634     mScrolling = true;
2635     DALI_LOG_SCROLL_STATE("[0x%X] mScrollStartedSignalV2 2 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
2636     mScrollStartedSignalV2.Emit( currentScrollPosition );
2637   }
2638   else if( (state == Gesture::Finished) ||
2639            (state == Gesture::Cancelled) ) // Finished/default
2640   {
2641     // when all the gestures have finished, we finish the transform.
2642     // so if a user decides to pan (1 gesture), and then pan+zoom (2 gestures)
2643     // then stop panning (back to 1 gesture), and then stop zooming (0 gestures).
2644     // this is the point we end, and perform necessary snapping.
2645     mGestureStackDepth--;
2646     if(mGestureStackDepth==0)
2647     {
2648       FinishTransform();
2649     }
2650     else
2651     {
2652       DALI_LOG_SCROLL_STATE("[0x%X] mGestureStackDepth[%d]", this, mGestureStackDepth);
2653     }
2654   }
2655 }
2656
2657 void ScrollView::UpdateTransform()
2658 {
2659 // TODO: notify clamps using property notifications (or see if we need this, can deprecate it)
2660 }
2661
2662 void ScrollView::FinishTransform()
2663 {
2664   // at this stage internal x and x scroll position should have followed prescroll position exactly
2665   Actor self = Self();
2666
2667   PreAnimatedScrollSetup();
2668
2669   bool animating = SnapWithVelocity(mLastVelocity * 1000.0f);
2670
2671   if(!animating)
2672   {
2673     // if not animating, then this pan has completed right now.
2674     SetScrollUpdateNotification(false);
2675     mScrolling = false;
2676     Self().SetProperty(mPropertyScrolling, false);
2677
2678     if( fabs(mScrollPrePosition.x - mScrollTargetPosition.x) > Math::MACHINE_EPSILON_10 )
2679     {
2680       SnapInternalXTo(mScrollTargetPosition.x);
2681     }
2682     if( fabs(mScrollPrePosition.y - mScrollTargetPosition.y) > Math::MACHINE_EPSILON_10 )
2683     {
2684       SnapInternalYTo(mScrollTargetPosition.y);
2685     }
2686     Vector3 currentScrollPosition = GetCurrentScrollPosition();
2687     DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignalV2 6 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
2688     mScrollCompletedSignalV2.Emit( currentScrollPosition );
2689   }
2690 }
2691
2692 Vector3 ScrollView::GetOvershoot(Vector3& position) const
2693 {
2694   Vector3 size = Self().GetCurrentSize();
2695   Vector3 overshoot;
2696
2697   const RulerDomain rulerDomainX = mRulerX->GetDomain();
2698   const RulerDomain rulerDomainY = mRulerY->GetDomain();
2699
2700   if(mRulerX->IsEnabled() && rulerDomainX.enabled)
2701   {
2702     const float left = rulerDomainX.min - position.x;
2703     const float right = size.width - rulerDomainX.max - position.x;
2704     if(left<0)
2705     {
2706       overshoot.x = left;
2707     }
2708     else if(right>0)
2709     {
2710       overshoot.x = right;
2711     }
2712   }
2713
2714   if(mRulerY->IsEnabled() && rulerDomainY.enabled)
2715   {
2716     const float top = rulerDomainY.min - position.y;
2717     const float bottom = size.height - rulerDomainY.max - position.y;
2718     if(top<0)
2719     {
2720       overshoot.y = top;
2721     }
2722     else if(bottom>0)
2723     {
2724       overshoot.y = bottom;
2725     }
2726   }
2727
2728   return overshoot;
2729 }
2730
2731 bool ScrollView::OnAccessibilityPan(PanGesture gesture)
2732 {
2733   // Keep track of whether this is an AccessibilityPan
2734   mInAccessibilityPan = true;
2735   OnPan(gesture);
2736   mInAccessibilityPan = false;
2737
2738   return true;
2739 }
2740
2741 void ScrollView::ClampPosition(Vector3& position) const
2742 {
2743   ClampState3 clamped;
2744   ClampPosition(position, clamped);
2745 }
2746
2747 void ScrollView::ClampPosition(Vector3& position, ClampState3 &clamped) const
2748 {
2749   Vector3 size = Self().GetCurrentSize();
2750
2751   // determine size of viewport relative to current scaled size.
2752   // e.g. if you're zoomed in 200%, then each pixel on screen is only 0.5 pixels on subject.
2753   if(fabsf(mScrollPostScale.x) > Math::MACHINE_EPSILON_0)
2754   {
2755     size.x /= mScrollPostScale.x;
2756   }
2757
2758   if(fabsf(mScrollPostScale.y) > Math::MACHINE_EPSILON_0)
2759   {
2760     size.y /= mScrollPostScale.y;
2761   }
2762
2763   position.x = -mRulerX->Clamp(-position.x, size.width, 1.0f, clamped.x);    // NOTE: X & Y rulers think in -ve coordinate system.
2764   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.
2765
2766   clamped.z = NotClamped;
2767 }
2768
2769 void ScrollView::WrapPosition(Vector3& position) const
2770 {
2771   if(mWrapMode)
2772   {
2773     const RulerDomain rulerDomainX = mRulerX->GetDomain();
2774     const RulerDomain rulerDomainY = mRulerY->GetDomain();
2775
2776     if(mRulerX->IsEnabled())
2777     {
2778       position.x = -WrapInDomain(-position.x, rulerDomainX.min, rulerDomainX.max);
2779     }
2780
2781     if(mRulerY->IsEnabled())
2782     {
2783       position.y = -WrapInDomain(-position.y, rulerDomainY.min, rulerDomainY.max);
2784     }
2785   }
2786 }
2787
2788 void ScrollView::ClampScale(Vector3& scale) const
2789 {
2790   ClampState3 clamped;
2791   ClampScale(scale, clamped);
2792 }
2793
2794 void ScrollView::ClampScale(Vector3& scale, ClampState3 &clamped) const
2795 {
2796   scale.x = mRulerScaleX->Clamp(scale.x, 0.0f, 1.0f, clamped.x);
2797   scale.y = mRulerScaleY->Clamp(scale.y, 0.0f, 1.0f, clamped.y);
2798   clamped.z = NotClamped;
2799 }
2800
2801 void ScrollView::UpdateMainInternalConstraint()
2802 {
2803   // TODO: Only update the constraints which have changed, rather than remove all and add all again.
2804   // Requires a dali-core ApplyConstraintAt, or a ReplaceConstraint. The former is probably more flexible.
2805   Actor self = Self();
2806   PanGestureDetector detector( GetPanGestureDetector() );
2807
2808   if(mScrollMainInternalPositionConstraint)
2809   {
2810     self.RemoveConstraint(mScrollMainInternalPositionConstraint);
2811     self.RemoveConstraint(mScrollMainInternalDeltaConstraint);
2812     self.RemoveConstraint(mScrollMainInternalFinalConstraint);
2813     self.RemoveConstraint(mScrollMainInternalRelativeConstraint);
2814     self.RemoveConstraint(mScrollMainInternalXConstraint);
2815     self.RemoveConstraint(mScrollMainInternalYConstraint);
2816   }
2817   if( mScrollMainInternalPrePositionConstraint )
2818   {
2819     self.RemoveConstraint(mScrollMainInternalPrePositionConstraint);
2820   }
2821
2822   // TODO: It's probably better to use a local displacement value as this will give a displacement when scrolling just commences
2823   // but we need to make sure than the gesture system gives displacement since last frame (60Hz), not displacement since last touch event (90Hz).
2824
2825   // 1. First calculate the pre-position (this is the scroll position if no clamping has taken place)
2826   Vector2 initialPanMask = Vector2(mRulerX->IsEnabled() ? 1.0f : 0.0f, mRulerY->IsEnabled() ? 1.0f : 0.0f);
2827
2828   if( mLockAxis == LockVertical )
2829   {
2830     initialPanMask.y = 0.0f;
2831   }
2832   else if( mLockAxis == LockHorizontal )
2833   {
2834     initialPanMask.x = 0.0f;
2835   }
2836   Constraint constraint;
2837
2838   if( mPanning )
2839   {
2840     constraint = Constraint::New<Vector3>( mPropertyPrePosition,
2841                                                       Source( detector, PanGestureDetector::LOCAL_POSITION ),
2842                                                       Source( detector, PanGestureDetector::LOCAL_DISPLACEMENT ),
2843                                                       Source( self, Actor::SIZE ),
2844                                                       InternalPrePositionConstraint( initialPanMask, mAxisAutoLock, mAxisAutoLockGradient, mLockAxis, mMaxOvershoot, mRulerX->GetDomain(), mRulerY->GetDomain() ) );
2845     mScrollMainInternalPrePositionConstraint = self.ApplyConstraint( constraint );
2846   }
2847
2848   // 2. Second calculate the clamped position (actual position)
2849   constraint = Constraint::New<Vector3>( mPropertyPosition,
2850                                          LocalSource( mPropertyPrePosition ),
2851                                          LocalSource( mPropertyPositionMin ),
2852                                          LocalSource( mPropertyPositionMax ),
2853                                          Source( self, Actor::SIZE ),
2854                                          InternalPositionConstraint( mRulerX->GetDomain(),
2855                                                                      mRulerY->GetDomain(), mWrapMode ) );
2856   mScrollMainInternalPositionConstraint = self.ApplyConstraint( constraint );
2857
2858   constraint = Constraint::New<Vector3>( mPropertyPositionDelta,
2859                                          LocalSource( mPropertyPosition ),
2860                                          LocalSource( mPropertyDomainOffset ),
2861                                          InternalPositionDeltaConstraint );
2862   mScrollMainInternalDeltaConstraint = self.ApplyConstraint( constraint );
2863
2864   constraint = Constraint::New<Vector3>( mPropertyFinal,
2865                                          LocalSource( mPropertyPosition ),
2866                                          LocalSource( mPropertyOvershootX ),
2867                                          LocalSource( mPropertyOvershootY ),
2868                                          InternalFinalConstraint( FinalDefaultAlphaFunction,
2869                                                                   FinalDefaultAlphaFunction ) );
2870   mScrollMainInternalFinalConstraint = self.ApplyConstraint( constraint );
2871
2872   constraint = Constraint::New<Vector3>( mPropertyRelativePosition,
2873                                          LocalSource( mPropertyPosition ),
2874                                          LocalSource( mPropertyPositionMin ),
2875                                          LocalSource( mPropertyPositionMax ),
2876                                          LocalSource( Actor::SIZE ),
2877                                          InternalRelativePositionConstraint );
2878   mScrollMainInternalRelativeConstraint = self.ApplyConstraint( constraint );
2879
2880   constraint = Constraint::New<float>( mPropertyX,
2881                                          LocalSource( mPropertyPrePosition ),
2882                                          InternalXConstraint );
2883   mScrollMainInternalXConstraint = self.ApplyConstraint( constraint );
2884
2885   constraint = Constraint::New<float>( mPropertyY,
2886                                          LocalSource( mPropertyPrePosition ),
2887                                          InternalYConstraint );
2888   mScrollMainInternalYConstraint = self.ApplyConstraint( constraint );
2889
2890   // When panning we want to make sure overshoot values are affected by pre position and post position
2891   SetOvershootConstraintsEnabled(!mWrapMode);
2892 }
2893
2894 void ScrollView::SetOvershootConstraintsEnabled(bool enabled)
2895 {
2896   Actor self( Self() );
2897   // remove and reset, it may now be in wrong order with the main internal constraints
2898   if( mScrollMainInternalOvershootXConstraint )
2899   {
2900     self.RemoveConstraint(mScrollMainInternalOvershootXConstraint);
2901     mScrollMainInternalOvershootXConstraint.Reset();
2902     self.RemoveConstraint(mScrollMainInternalOvershootYConstraint);
2903     mScrollMainInternalOvershootYConstraint.Reset();
2904   }
2905   if( enabled )
2906   {
2907     Constraint constraint = Constraint::New<float>( mPropertyOvershootX,
2908                                            LocalSource( mPropertyPrePosition ),
2909                                            LocalSource( mPropertyPosition ),
2910                                            LocalSource( mPropertyCanScrollHorizontal ),
2911                                            OvershootXConstraint(mMaxOvershoot.x) );
2912     mScrollMainInternalOvershootXConstraint = self.ApplyConstraint( constraint );
2913
2914     constraint = Constraint::New<float>( mPropertyOvershootY,
2915                                            LocalSource( mPropertyPrePosition ),
2916                                            LocalSource( mPropertyPosition ),
2917                                            LocalSource( mPropertyCanScrollVertical ),
2918                                            OvershootYConstraint(mMaxOvershoot.y) );
2919     mScrollMainInternalOvershootYConstraint = self.ApplyConstraint( constraint );
2920   }
2921   else
2922   {
2923     self.SetProperty(mPropertyOvershootX, 0.0f);
2924     self.SetProperty(mPropertyOvershootY, 0.0f);
2925   }
2926 }
2927
2928 void ScrollView::SetInternalConstraints()
2929 {
2930   // Internal constraints (applied to target ScrollBase Actor itself) /////////
2931   UpdateMainInternalConstraint();
2932
2933   // User definable constraints to apply to all child actors //////////////////
2934   Actor self = Self();
2935
2936   // LocalSource - The Actors to be moved.
2937   // self - The ScrollView
2938
2939   // Apply some default constraints to ScrollView.
2940   // Movement + Scaling + Wrap function
2941
2942   Constraint constraint;
2943
2944   // MoveScaledActor (scrolling/zooming)
2945   constraint = Constraint::New<Vector3>( Actor::POSITION,
2946                                          Source( self, mPropertyPosition ),
2947                                          Source( self, mPropertyScale ),
2948                                          MoveScaledActorConstraint );
2949   constraint.SetRemoveAction(Constraint::Discard);
2950   ApplyConstraintToBoundActors(constraint);
2951
2952   // ScaleActor (scrolling/zooming)
2953   constraint = Constraint::New<Vector3>( Actor::SCALE,
2954                                          Source( self, mPropertyScale ),
2955                                          ScaleActorConstraint );
2956   constraint.SetRemoveAction(Constraint::Discard);
2957   ApplyConstraintToBoundActors(constraint);
2958
2959   // WrapActor (wrap functionality)
2960   constraint = Constraint::New<Vector3>( Actor::POSITION,
2961                                                  LocalSource( Actor::SCALE ),
2962                                                  LocalSource( Actor::ANCHOR_POINT ),
2963                                                  LocalSource( Actor::SIZE ),
2964                                                  Source( self, mPropertyPositionMin ),
2965                                                  Source( self, mPropertyPositionMax ),
2966                                                  Source( self, mPropertyWrap ),
2967                                                  WrapActorConstraint );
2968   constraint.SetRemoveAction(Constraint::Discard);
2969   ApplyConstraintToBoundActors(constraint);
2970 }
2971
2972 } // namespace Internal
2973
2974 } // namespace Toolkit
2975
2976 } // namespace Dali