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