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