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