7ff84ab9c569aee4a0305f380a2391b14dda5c3a
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / scrollable / scroll-view / scroll-view-impl.cpp
1 /*
2  * Copyright (c) 2015 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   UpdatePropertyDomain();
658   SetInternalConstraints();
659 }
660
661 void ScrollView::OnControlStageConnection()
662 {
663   DALI_LOG_SCROLL_STATE("[0x%X]", this);
664
665   if ( mSensitive )
666   {
667     SetScrollSensitive( false );
668     SetScrollSensitive( true );
669   }
670   if(IsOvershootEnabled())
671   {
672     // try and make sure property notifications are set
673     EnableScrollOvershoot(true);
674   }
675 }
676
677 void ScrollView::OnControlStageDisconnection()
678 {
679   DALI_LOG_SCROLL_STATE("[0x%X]", this);
680
681   StopAnimation();
682 }
683
684 ScrollView::~ScrollView()
685 {
686   DALI_LOG_SCROLL_STATE("[0x%X]", this);
687 }
688
689 AlphaFunction ScrollView::GetScrollSnapAlphaFunction() const
690 {
691   return mSnapAlphaFunction;
692 }
693
694 void ScrollView::SetScrollSnapAlphaFunction(AlphaFunction alpha)
695 {
696   mSnapAlphaFunction = alpha;
697 }
698
699 AlphaFunction ScrollView::GetScrollFlickAlphaFunction() const
700 {
701   return mFlickAlphaFunction;
702 }
703
704 void ScrollView::SetScrollFlickAlphaFunction(AlphaFunction alpha)
705 {
706   mFlickAlphaFunction = alpha;
707 }
708
709 float ScrollView::GetScrollSnapDuration() const
710 {
711   return mSnapDuration;
712 }
713
714 void ScrollView::SetScrollSnapDuration(float time)
715 {
716   mSnapDuration = time;
717 }
718
719 float ScrollView::GetScrollFlickDuration() const
720 {
721   return mFlickDuration;
722 }
723
724 void ScrollView::SetScrollFlickDuration(float time)
725 {
726   mFlickDuration = time;
727 }
728
729 void ScrollView::ApplyEffect(Toolkit::ScrollViewEffect effect)
730 {
731   Dali::Toolkit::ScrollView self = Dali::Toolkit::ScrollView::DownCast(Self());
732
733   // Assertion check to ensure effect doesn't already exist in this scrollview
734   bool effectAlreadyExistsInScrollView(false);
735   for (ScrollViewEffectIter iter = mEffects.begin(); iter != mEffects.end(); ++iter)
736   {
737     if(*iter==effect)
738     {
739       effectAlreadyExistsInScrollView = true;
740       break;
741     }
742   }
743
744   DALI_ASSERT_ALWAYS(!effectAlreadyExistsInScrollView);
745
746   // add effect to effects list
747   mEffects.push_back(effect);
748
749   // invoke Attachment request to ScrollView first
750   GetImpl(effect).Attach(self);
751 }
752
753 void ScrollView::RemoveEffect(Toolkit::ScrollViewEffect effect)
754 {
755   Dali::Toolkit::ScrollView self = Dali::Toolkit::ScrollView::DownCast(Self());
756
757   // remove effect from effects list
758   bool effectExistedInScrollView(false);
759   for (ScrollViewEffectIter iter = mEffects.begin(); iter != mEffects.end(); ++iter)
760   {
761     if(*iter==effect)
762     {
763       mEffects.erase(iter);
764       effectExistedInScrollView = true;
765       break;
766     }
767   }
768
769   // Assertion check to ensure effect existed.
770   DALI_ASSERT_ALWAYS(effectExistedInScrollView);
771
772   // invoke Detachment request to ScrollView last
773   GetImpl(effect).Detach(self);
774 }
775
776 void ScrollView::RemoveAllEffects()
777 {
778   Dali::Toolkit::ScrollView self = Dali::Toolkit::ScrollView::DownCast(Self());
779
780   for (ScrollViewEffectIter effectIter = mEffects.begin(); effectIter != mEffects.end(); ++effectIter)
781   {
782     Toolkit::ScrollViewEffect effect = *effectIter;
783
784     // invoke Detachment request to ScrollView last
785     GetImpl(effect).Detach(self);
786   }
787
788   mEffects.clear();
789 }
790
791 void ScrollView::ApplyConstraintToChildren(Constraint constraint)
792 {
793   ApplyConstraintToBoundActors(constraint);
794 }
795
796 void ScrollView::RemoveConstraintsFromChildren()
797 {
798   RemoveConstraintsFromBoundActors();
799 }
800
801 const RulerPtr ScrollView::GetRulerX() const
802 {
803   return mRulerX;
804 }
805
806 const RulerPtr ScrollView::GetRulerY() const
807 {
808   return mRulerY;
809 }
810
811 void ScrollView::SetRulerX(RulerPtr ruler)
812 {
813   mRulerX = ruler;
814
815   UpdatePropertyDomain();
816   UpdateMainInternalConstraint();
817 }
818
819 void ScrollView::SetRulerY(RulerPtr ruler)
820 {
821   mRulerY = ruler;
822
823   UpdatePropertyDomain();
824   UpdateMainInternalConstraint();
825 }
826
827 void ScrollView::UpdatePropertyDomain()
828 {
829   Actor self = Self();
830   Vector3 size = self.GetTargetSize();
831   Vector2 min = mMinScroll;
832   Vector2 max = mMaxScroll;
833   bool scrollPositionChanged = false;
834   bool domainChanged = false;
835
836   bool canScrollVertical = false;
837   bool canScrollHorizontal = false;
838   UpdateLocalScrollProperties();
839   if(mRulerX->IsEnabled())
840   {
841     const Toolkit::RulerDomain& rulerDomain = mRulerX->GetDomain();
842     if( fabsf(min.x - rulerDomain.min) > Math::MACHINE_EPSILON_100
843         || fabsf(max.x - rulerDomain.max) > Math::MACHINE_EPSILON_100 )
844     {
845       domainChanged = true;
846       min.x = rulerDomain.min;
847       max.x = rulerDomain.max;
848
849       // make sure new scroll value is within new domain
850       if( mScrollPrePosition.x < min.x
851           || mScrollPrePosition.x > max.x )
852       {
853         scrollPositionChanged = true;
854         mScrollPrePosition.x = Clamp(mScrollPrePosition.x, -(max.x - size.x), -min.x);
855       }
856     }
857     if( (fabsf(rulerDomain.max - rulerDomain.min) - size.x) > Math::MACHINE_EPSILON_100 )
858     {
859       canScrollHorizontal = true;
860     }
861   }
862   else if( fabs(min.x) > Math::MACHINE_EPSILON_100
863            || fabs(max.x) > Math::MACHINE_EPSILON_100 )
864   {
865     // need to reset to 0
866     domainChanged = true;
867     min.x = 0.0f;
868     max.x = 0.0f;
869     canScrollHorizontal = false;
870   }
871
872   if(mRulerY->IsEnabled())
873   {
874     const Toolkit::RulerDomain& rulerDomain = mRulerY->GetDomain();
875     if( fabsf(min.y - rulerDomain.min) > Math::MACHINE_EPSILON_100
876         || fabsf(max.y - rulerDomain.max) > Math::MACHINE_EPSILON_100 )
877     {
878       domainChanged = true;
879       min.y = rulerDomain.min;
880       max.y = rulerDomain.max;
881
882       // make sure new scroll value is within new domain
883       if( mScrollPrePosition.y < min.y
884           || mScrollPrePosition.y > max.y )
885       {
886         scrollPositionChanged = true;
887         mScrollPrePosition.y = Clamp(mScrollPrePosition.y, -(max.y - size.y), -min.y);
888       }
889     }
890     if( (fabsf(rulerDomain.max - rulerDomain.min) - size.y) > Math::MACHINE_EPSILON_100 )
891     {
892       canScrollVertical = true;
893     }
894   }
895   else if( fabs(min.y) > Math::MACHINE_EPSILON_100
896            || fabs(max.y) > Math::MACHINE_EPSILON_100 )
897   {
898     // need to reset to 0
899     domainChanged = true;
900     min.y = 0.0f;
901     max.y = 0.0f;
902     canScrollVertical = false;
903   }
904
905   // avoid setting properties if possible, otherwise this will cause an entire update as well as triggering constraints using each property we update
906   if( mCanScrollVertical != canScrollVertical )
907   {
908     mCanScrollVertical = canScrollVertical;
909     self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_VERTICAL, canScrollVertical);
910   }
911   if( mCanScrollHorizontal != canScrollHorizontal )
912   {
913     mCanScrollHorizontal = canScrollHorizontal;
914     self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_HORIZONTAL, canScrollHorizontal);
915   }
916   if( scrollPositionChanged )
917   {
918     DALI_LOG_SCROLL_STATE("[0x%X] Domain Changed, setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y );
919     self.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPrePosition);
920   }
921   if( domainChanged )
922   {
923     mMinScroll = min;
924     mMaxScroll = max;
925     self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MIN, mMinScroll );
926     self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MAX, mMaxScroll );
927   }
928 }
929
930 void ScrollView::SetScrollSensitive(bool sensitive)
931 {
932   Actor self = Self();
933   PanGestureDetector panGesture( GetPanGestureDetector() );
934
935   DALI_LOG_SCROLL_STATE("[0x%X] sensitive: before:[%d] setting[%d]", this, int(mSensitive), int(sensitive));
936
937   if((!mSensitive) && (sensitive))
938   {
939     mSensitive = sensitive;
940     panGesture.Attach(self);
941   }
942   else if((mSensitive) && (!sensitive))
943   {
944     DALI_LOG_SCROLL_STATE("[0x%X] BEFORE: panning:[%d]", this, int(mPanning));
945
946     // while the scroll view is panning, the state needs to be reset.
947     if ( mPanning )
948     {
949       PanGesture cancelGesture( Gesture::Cancelled );
950       OnPan( cancelGesture );
951     }
952
953     panGesture.Detach(self);
954     mSensitive = sensitive;
955
956     mGestureStackDepth = 0;
957     DALI_LOG_SCROLL_STATE("[0x%X] AFTER: panning:[%d]", this, int(mPanning));
958   }
959 }
960
961 void ScrollView::SetMaxOvershoot(float overshootX, float overshootY)
962 {
963   mMaxOvershoot.x = overshootX;
964   mMaxOvershoot.y = overshootY;
965   mUserMaxOvershoot = mMaxOvershoot;
966   mDefaultMaxOvershoot = false;
967   UpdateMainInternalConstraint();
968 }
969
970 void ScrollView::SetSnapOvershootAlphaFunction(AlphaFunction alpha)
971 {
972   mSnapOvershootAlphaFunction = alpha;
973 }
974
975 void ScrollView::SetSnapOvershootDuration(float duration)
976 {
977   mSnapOvershootDuration = duration;
978 }
979
980 void ScrollView::SetActorAutoSnap(bool enable)
981 {
982   mActorAutoSnapEnabled = enable;
983 }
984
985 void ScrollView::SetAutoResize(bool enable)
986 {
987   mAutoResizeContainerEnabled = enable;
988   // TODO: This needs a lot of issues to be addressed before working.
989 }
990
991 bool ScrollView::GetWrapMode() const
992 {
993   return mWrapMode;
994 }
995
996 void ScrollView::SetWrapMode(bool enable)
997 {
998   mWrapMode = enable;
999   Self().SetProperty(Toolkit::ScrollView::Property::WRAP, enable);
1000 }
1001
1002 int ScrollView::GetScrollUpdateDistance() const
1003 {
1004   return mScrollUpdateDistance;
1005 }
1006
1007 void ScrollView::SetScrollUpdateDistance(int distance)
1008 {
1009   mScrollUpdateDistance = distance;
1010 }
1011
1012 bool ScrollView::GetAxisAutoLock() const
1013 {
1014   return mAxisAutoLock;
1015 }
1016
1017 void ScrollView::SetAxisAutoLock(bool enable)
1018 {
1019   mAxisAutoLock = enable;
1020   UpdateMainInternalConstraint();
1021 }
1022
1023 float ScrollView::GetAxisAutoLockGradient() const
1024 {
1025   return mAxisAutoLockGradient;
1026 }
1027
1028 void ScrollView::SetAxisAutoLockGradient(float gradient)
1029 {
1030   DALI_ASSERT_DEBUG( gradient >= 0.0f && gradient <= 1.0f );
1031   mAxisAutoLockGradient = gradient;
1032   UpdateMainInternalConstraint();
1033 }
1034
1035 float ScrollView::GetFrictionCoefficient() const
1036 {
1037   return mFrictionCoefficient;
1038 }
1039
1040 void ScrollView::SetFrictionCoefficient(float friction)
1041 {
1042   DALI_ASSERT_DEBUG( friction > 0.0f );
1043   mFrictionCoefficient = friction;
1044 }
1045
1046 float ScrollView::GetFlickSpeedCoefficient() const
1047 {
1048   return mFlickSpeedCoefficient;
1049 }
1050
1051 void ScrollView::SetFlickSpeedCoefficient(float speed)
1052 {
1053   mFlickSpeedCoefficient = speed;
1054 }
1055
1056 Vector2 ScrollView::GetMinimumDistanceForFlick() const
1057 {
1058   return mMinFlickDistance;
1059 }
1060
1061 void ScrollView::SetMinimumDistanceForFlick( const Vector2& distance )
1062 {
1063   mMinFlickDistance = distance;
1064 }
1065
1066 float ScrollView::GetMinimumSpeedForFlick() const
1067 {
1068   return mFlickSpeedThreshold;
1069 }
1070
1071 void ScrollView::SetMinimumSpeedForFlick( float speed )
1072 {
1073   mFlickSpeedThreshold = speed;
1074 }
1075
1076 float ScrollView::GetMaxFlickSpeed() const
1077 {
1078   return mMaxFlickSpeed;
1079 }
1080
1081 void ScrollView::SetMaxFlickSpeed(float speed)
1082 {
1083   mMaxFlickSpeed = speed;
1084 }
1085
1086 void ScrollView::SetMouseWheelScrollDistanceStep(Vector2 step)
1087 {
1088   mMouseWheelScrollDistanceStep = step;
1089 }
1090
1091 Vector2 ScrollView::GetMouseWheelScrollDistanceStep() const
1092 {
1093   return mMouseWheelScrollDistanceStep;
1094 }
1095
1096 unsigned int ScrollView::GetCurrentPage() const
1097 {
1098   // in case animation is currently taking place.
1099   Vector2 position = GetPropertyPosition();
1100
1101   Actor self = Self();
1102   unsigned int page = 0;
1103   unsigned int pagesPerVolume = 1;
1104   unsigned int volume = 0;
1105
1106   // if rulerX is enabled, then get page count (columns)
1107   page = mRulerX->GetPageFromPosition(-position.x, mWrapMode);
1108   volume = mRulerY->GetPageFromPosition(-position.y, mWrapMode);
1109   pagesPerVolume = mRulerX->GetTotalPages();
1110
1111   return volume * pagesPerVolume + page;
1112 }
1113
1114 Vector2 ScrollView::GetCurrentScrollPosition() const
1115 {
1116   return -GetPropertyPosition();
1117 }
1118
1119 void ScrollView::SetScrollPosition(const Vector2& position)
1120 {
1121   mScrollPrePosition = position;
1122 }
1123
1124 Vector2 ScrollView::GetDomainSize() const
1125 {
1126   Vector3 size = Self().GetCurrentSize();
1127
1128   const RulerDomain& xDomain = GetRulerX()->GetDomain();
1129   const RulerDomain& yDomain = GetRulerY()->GetDomain();
1130
1131   Vector2 domainSize;
1132   domainSize.x = xDomain.max - xDomain.min - size.x;
1133   domainSize.y = yDomain.max - yDomain.min - size.y;
1134   return domainSize;
1135 }
1136
1137 void ScrollView::TransformTo(const Vector2& position,
1138                              DirectionBias horizontalBias, DirectionBias verticalBias)
1139 {
1140   TransformTo(position, mSnapDuration, mSnapAlphaFunction, horizontalBias, verticalBias);
1141 }
1142
1143 void ScrollView::TransformTo(const Vector2& position, float duration, AlphaFunction alpha,
1144                              DirectionBias horizontalBias, DirectionBias verticalBias)
1145 {
1146   // If this is called while the timer is running, then cancel it
1147   StopTouchDownTimer();
1148
1149   Actor self( Self() );
1150
1151   // Guard against destruction during signal emission
1152   // Note that Emit() methods are called indirectly e.g. from within ScrollView::AnimateTo()
1153   Toolkit::ScrollView handle( GetOwner() );
1154
1155   DALI_LOG_SCROLL_STATE("[0x%X] pos[%.2f,%.2f], duration[%.2f] bias[%d, %d]",
1156     this, position.x, position.y, duration, int(horizontalBias), int(verticalBias));
1157
1158   Vector2 currentScrollPosition = GetCurrentScrollPosition();
1159   self.SetProperty( Toolkit::ScrollView::Property::START_PAGE_POSITION, Vector3(currentScrollPosition) );
1160
1161   if( mScrolling ) // are we interrupting a current scroll?
1162   {
1163     // set mScrolling to false, in case user has code that interrogates mScrolling Getter() in complete.
1164     mScrolling = false;
1165     DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 1 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
1166     mScrollCompletedSignal.Emit( currentScrollPosition );
1167   }
1168
1169   if( mPanning ) // are we interrupting a current pan?
1170   {
1171     DALI_LOG_SCROLL_STATE("[0x%X] Interrupting Pan, set to false", this );
1172     mPanning = false;
1173     mGestureStackDepth = 0;
1174     self.SetProperty( Toolkit::ScrollView::Property::PANNING, false );
1175
1176     if( mScrollMainInternalPrePositionConstraint )
1177     {
1178       mScrollMainInternalPrePositionConstraint.Remove();
1179     }
1180   }
1181
1182   self.SetProperty(Toolkit::ScrollView::Property::SCROLLING, true);
1183   mScrolling = true;
1184
1185   DALI_LOG_SCROLL_STATE("[0x%X] mScrollStartedSignal 1 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
1186   mScrollStartedSignal.Emit( currentScrollPosition );
1187   bool animating = AnimateTo(-position,
1188                              Vector2::ONE * duration,
1189                              alpha,
1190                              true,
1191                              horizontalBias,
1192                              verticalBias,
1193                              Snap);
1194
1195   if(!animating)
1196   {
1197     // if not animating, then this pan has completed right now.
1198     self.SetProperty(Toolkit::ScrollView::Property::SCROLLING, false);
1199     mScrolling = false;
1200
1201     // If we have no duration, then in the next update frame, we will be at the position specified as we just set.
1202     // In this scenario, we cannot return the currentScrollPosition as this is out-of-date and should instead return the requested final position
1203     Vector2 completedPosition( currentScrollPosition );
1204     if( duration <= Math::MACHINE_EPSILON_10 )
1205     {
1206       completedPosition = position;
1207     }
1208
1209     DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 2 [%.2f, %.2f]", this, completedPosition.x, completedPosition.y);
1210     SetScrollUpdateNotification(false);
1211     mScrollCompletedSignal.Emit( completedPosition );
1212   }
1213 }
1214
1215 void ScrollView::ScrollTo(const Vector2& position)
1216 {
1217   ScrollTo(position, mSnapDuration );
1218 }
1219
1220 void ScrollView::ScrollTo(const Vector2& position, float duration)
1221 {
1222   ScrollTo(position, duration, DirectionBiasNone, DirectionBiasNone);
1223 }
1224
1225 void ScrollView::ScrollTo(const Vector2& position, float duration, AlphaFunction alpha)
1226 {
1227   ScrollTo(position, duration, alpha, DirectionBiasNone, DirectionBiasNone);
1228 }
1229
1230 void ScrollView::ScrollTo(const Vector2& position, float duration,
1231                           DirectionBias horizontalBias, DirectionBias verticalBias)
1232 {
1233   ScrollTo(position, duration, mSnapAlphaFunction, horizontalBias, verticalBias);
1234 }
1235
1236 void ScrollView::ScrollTo(const Vector2& position, float duration, AlphaFunction alpha,
1237                 DirectionBias horizontalBias, DirectionBias verticalBias)
1238 {
1239   DALI_LOG_SCROLL_STATE("[0x%X] position[%.2f, %.2f] duration[%.2f], bias[%d, %d]", this, position.x, position.y, duration, int(horizontalBias), int(verticalBias));
1240   TransformTo(position, duration, alpha, horizontalBias, verticalBias);
1241 }
1242
1243 void ScrollView::ScrollTo(unsigned int page)
1244 {
1245   ScrollTo(page, mSnapDuration);
1246 }
1247
1248 void ScrollView::ScrollTo(unsigned int page, float duration, DirectionBias bias)
1249 {
1250   Vector2 position;
1251   unsigned int volume;
1252   unsigned int libraries;
1253
1254   // The position to scroll to is continuous and linear
1255   // unless a domain has been enabled on the X axis.
1256   // or if WrapMode has been enabled.
1257   bool carryX = mRulerX->GetDomain().enabled | mWrapMode;
1258   bool carryY = mRulerY->GetDomain().enabled | mWrapMode;
1259
1260   position.x = mRulerX->GetPositionFromPage(page, volume, carryX);
1261   position.y = mRulerY->GetPositionFromPage(volume, libraries, carryY);
1262
1263   ScrollTo(position, duration, bias, bias);
1264 }
1265
1266 void ScrollView::ScrollTo(Actor &actor)
1267 {
1268   ScrollTo(actor, mSnapDuration);
1269 }
1270
1271 void ScrollView::ScrollTo(Actor &actor, float duration)
1272 {
1273   DALI_ASSERT_ALWAYS(actor.GetParent() == Self());
1274
1275   Actor self = Self();
1276   Vector3 size = self.GetCurrentSize();
1277   Vector3 position = actor.GetCurrentPosition();
1278   Vector2 prePosition = GetPropertyPrePosition();
1279   position.GetVectorXY() -= prePosition;
1280
1281   ScrollTo(Vector2(position.x - size.width * 0.5f, position.y - size.height * 0.5f), duration);
1282 }
1283
1284 Actor ScrollView::FindClosestActor()
1285 {
1286   Actor self = Self();
1287   Vector3 size = self.GetCurrentSize();
1288
1289   return FindClosestActorToPosition(Vector3(size.width * 0.5f,size.height * 0.5f,0.0f));
1290 }
1291
1292 Actor ScrollView::FindClosestActorToPosition(const Vector3& position, FindDirection dirX, FindDirection dirY, FindDirection dirZ)
1293 {
1294   Actor closestChild;
1295   float closestDistance2 = 0.0f;
1296   Vector3 actualPosition = position;
1297
1298   unsigned int numChildren = Self().GetChildCount();
1299
1300   for(unsigned int i = 0; i < numChildren; ++i)
1301   {
1302     Actor child = Self().GetChildAt(i);
1303
1304     if(mInternalActor == child) // ignore internal actor.
1305     {
1306       continue;
1307     }
1308
1309     Vector3 childPosition = GetPositionOfAnchor(child, AnchorPoint::CENTER);
1310
1311     Vector3 delta = childPosition - actualPosition;
1312
1313     // X-axis checking (only find Actors to the [dirX] of actualPosition)
1314     if(dirX > All) // != All,None
1315     {
1316       FindDirection deltaH = delta.x > 0 ? Right : Left;
1317       if(dirX != deltaH)
1318       {
1319         continue;
1320       }
1321     }
1322
1323     // Y-axis checking (only find Actors to the [dirY] of actualPosition)
1324     if(dirY > All) // != All,None
1325     {
1326       FindDirection deltaV = delta.y > 0 ? Down : Up;
1327       if(dirY  != deltaV)
1328       {
1329         continue;
1330       }
1331     }
1332
1333     // Z-axis checking (only find Actors to the [dirZ] of actualPosition)
1334     if(dirZ > All) // != All,None
1335     {
1336       FindDirection deltaV = delta.y > 0 ? In : Out;
1337       if(dirZ  != deltaV)
1338       {
1339         continue;
1340       }
1341     }
1342
1343     // compare child to closest child in terms of distance.
1344     float distance2 = 0.0f;
1345
1346     // distance2 = the Square of the relevant dimensions of delta
1347     if(dirX != None)
1348     {
1349       distance2 += delta.x * delta.x;
1350     }
1351
1352     if(dirY != None)
1353     {
1354       distance2 += delta.y * delta.y;
1355     }
1356
1357     if(dirZ != None)
1358     {
1359       distance2 += delta.z * delta.z;
1360     }
1361
1362     if(closestChild) // Next time.
1363     {
1364       if(distance2 < closestDistance2)
1365       {
1366         closestChild = child;
1367         closestDistance2 = distance2;
1368       }
1369     }
1370     else // First time.
1371     {
1372       closestChild = child;
1373       closestDistance2 = distance2;
1374     }
1375   }
1376
1377   return closestChild;
1378 }
1379
1380 bool ScrollView::ScrollToSnapPoint()
1381 {
1382   DALI_LOG_SCROLL_STATE("[0x%X]", this );
1383   Vector2 stationaryVelocity = Vector2(0.0f, 0.0f);
1384   return SnapWithVelocity( stationaryVelocity );
1385 }
1386
1387 // TODO: In situations where axes are different (X snap, Y free)
1388 // Each axis should really have their own independent animation (time and equation)
1389 // Consider, X axis snapping to nearest grid point (EaseOut over fixed time)
1390 // Consider, Y axis simulating physics to arrive at a point (Physics equation over variable time)
1391 // Currently, the axes have been split however, they both use the same EaseOut equation.
1392 bool ScrollView::SnapWithVelocity(Vector2 velocity)
1393 {
1394   // Animator takes over now, touches are assumed not to interfere.
1395   // And if touches do interfere, then we'll stop animation, update PrePosition
1396   // to current mScroll's properties, and then resume.
1397   // Note: For Flicking this may work a bit different...
1398
1399   float angle = atan2(velocity.y, velocity.x);
1400   float speed2 = velocity.LengthSquared();
1401   AlphaFunction alphaFunction = mSnapAlphaFunction;
1402   Vector2 positionDuration = Vector2::ONE * mSnapDuration;
1403   float biasX = 0.5f;
1404   float biasY = 0.5f;
1405   FindDirection horizontal = None;
1406   FindDirection vertical = None;
1407
1408   // orthoAngleRange = Angle tolerance within the Exact N,E,S,W direction
1409   // that will be accepted as a general N,E,S,W flick direction.
1410
1411   const float orthoAngleRange = FLICK_ORTHO_ANGLE_RANGE * M_PI / 180.0f;
1412   const float flickSpeedThreshold2 = mFlickSpeedThreshold * mFlickSpeedThreshold;
1413
1414   Vector2 positionSnap = mScrollPrePosition;
1415
1416   // Flick logic X Axis
1417
1418   if(mRulerX->IsEnabled() && mLockAxis != LockHorizontal)
1419   {
1420     horizontal = All;
1421
1422     if( speed2 > flickSpeedThreshold2 || // exceeds flick threshold
1423         mInAccessibilityPan ) // With AccessibilityPan its easier to move between snap positions
1424     {
1425       if((angle >= -orthoAngleRange) && (angle < orthoAngleRange)) // Swiping East
1426       {
1427         biasX = 0.0f, horizontal = Left;
1428
1429         // This guards against an error where no movement occurs, due to the flick finishing
1430         // before the update-thread has advanced mScrollPostPosition past the the previous snap point.
1431         positionSnap.x += 1.0f;
1432       }
1433       else if((angle >= M_PI-orthoAngleRange) || (angle < -M_PI+orthoAngleRange)) // Swiping West
1434       {
1435         biasX = 1.0f, horizontal = Right;
1436
1437         // This guards against an error where no movement occurs, due to the flick finishing
1438         // before the update-thread has advanced mScrollPostPosition past the the previous snap point.
1439         positionSnap.x -= 1.0f;
1440       }
1441     }
1442   }
1443
1444   // Flick logic Y Axis
1445
1446   if(mRulerY->IsEnabled() && mLockAxis != LockVertical)
1447   {
1448     vertical = All;
1449
1450     if( speed2 > flickSpeedThreshold2 || // exceeds flick threshold
1451         mInAccessibilityPan ) // With AccessibilityPan its easier to move between snap positions
1452     {
1453       if((angle >= M_PI_2-orthoAngleRange) && (angle < M_PI_2+orthoAngleRange)) // Swiping South
1454       {
1455         biasY = 0.0f, vertical = Up;
1456       }
1457       else if((angle >= -M_PI_2-orthoAngleRange) && (angle < -M_PI_2+orthoAngleRange)) // Swiping North
1458       {
1459         biasY = 1.0f, vertical = Down;
1460       }
1461     }
1462   }
1463
1464   // isFlick: Whether this gesture is a flick or not.
1465   bool isFlick = (horizontal != All || vertical != All);
1466   // isFreeFlick: Whether this gesture is a flick under free panning criteria.
1467   bool isFreeFlick = velocity.LengthSquared() > (FREE_FLICK_SPEED_THRESHOLD*FREE_FLICK_SPEED_THRESHOLD);
1468
1469   if(isFlick || isFreeFlick)
1470   {
1471     positionDuration = Vector2::ONE * mFlickDuration;
1472     alphaFunction = mFlickAlphaFunction;
1473   }
1474
1475   // Calculate next positionSnap ////////////////////////////////////////////////////////////
1476
1477   if(mActorAutoSnapEnabled)
1478   {
1479     Vector3 size = Self().GetCurrentSize();
1480
1481     Actor child = FindClosestActorToPosition( Vector3(size.width * 0.5f,size.height * 0.5f,0.0f), horizontal, vertical );
1482
1483     if(!child && isFlick )
1484     {
1485       // If we conducted a direction limited search and found no actor, then just snap to the closest actor.
1486       child = FindClosestActorToPosition( Vector3(size.width * 0.5f,size.height * 0.5f,0.0f) );
1487     }
1488
1489     if(child)
1490     {
1491       Vector2 position = Self().GetProperty<Vector2>(Toolkit::ScrollView::Property::SCROLL_POSITION);
1492
1493       // Get center-point of the Actor.
1494       Vector3 childPosition = GetPositionOfAnchor(child, AnchorPoint::CENTER);
1495
1496       if(mRulerX->IsEnabled())
1497       {
1498         positionSnap.x = position.x - childPosition.x + size.width * 0.5f;
1499       }
1500       if(mRulerY->IsEnabled())
1501       {
1502         positionSnap.y = position.y - childPosition.y + size.height * 0.5f;
1503       }
1504     }
1505   }
1506
1507   Vector2 startPosition = positionSnap;
1508   positionSnap.x = -mRulerX->Snap(-positionSnap.x, biasX);  // NOTE: X & Y rulers think in -ve coordinate system.
1509   positionSnap.y = -mRulerY->Snap(-positionSnap.y, biasY);  // That is scrolling RIGHT (e.g. 100.0, 0.0) means moving LEFT.
1510
1511   Vector2 clampDelta(Vector2::ZERO);
1512   ClampPosition(positionSnap);
1513
1514   if( (mRulerX->GetType() == Ruler::Free || mRulerY->GetType() == Ruler::Free)
1515       && isFreeFlick && !mActorAutoSnapEnabled)
1516   {
1517     // Calculate target position based on velocity of flick.
1518
1519     // a = Deceleration (Set to diagonal stage length * friction coefficient)
1520     // u = Initial Velocity (Flick velocity)
1521     // v = 0 (Final Velocity)
1522     // t = Time (Velocity / Deceleration)
1523     Vector2 stageSize = Stage::GetCurrent().GetSize();
1524     float stageLength = Vector3(stageSize.x, stageSize.y, 0.0f).Length();
1525     float a = (stageLength * mFrictionCoefficient);
1526     Vector3 u = Vector3(velocity.x, velocity.y, 0.0f) * mFlickSpeedCoefficient;
1527     float speed = u.Length();
1528     u/= speed;
1529
1530     // TODO: Change this to a decay function. (faster you flick, the slower it should be)
1531     speed = std::min(speed, stageLength * mMaxFlickSpeed );
1532     u*= speed;
1533     alphaFunction = ConstantDecelerationAlphaFunction;
1534
1535     float t = speed / a;
1536
1537     if(mRulerX->IsEnabled() && mRulerX->GetType() == Ruler::Free)
1538     {
1539       positionSnap.x += t*u.x*0.5f;
1540     }
1541
1542     if(mRulerY->IsEnabled() && mRulerY->GetType() == Ruler::Free)
1543     {
1544       positionSnap.y += t*u.y*0.5f;
1545     }
1546
1547     clampDelta = positionSnap;
1548     ClampPosition(positionSnap);
1549     if((positionSnap - startPosition).LengthSquared() > Math::MACHINE_EPSILON_0)
1550     {
1551       clampDelta -= positionSnap;
1552       clampDelta.x = clampDelta.x > 0.0f ? std::min(clampDelta.x, mMaxOvershoot.x) : std::max(clampDelta.x, -mMaxOvershoot.x);
1553       clampDelta.y = clampDelta.y > 0.0f ? std::min(clampDelta.y, mMaxOvershoot.y) : std::max(clampDelta.y, -mMaxOvershoot.y);
1554     }
1555     else
1556     {
1557       clampDelta = Vector2::ZERO;
1558     }
1559
1560     // If Axis is Free and has velocity, then calculate time taken
1561     // to reach target based on velocity in axis.
1562     if(mRulerX->IsEnabled() && mRulerX->GetType() == Ruler::Free)
1563     {
1564       float deltaX = fabsf(startPosition.x - positionSnap.x);
1565
1566       if(fabsf(u.x) > Math::MACHINE_EPSILON_1)
1567       {
1568         positionDuration.x = fabsf(deltaX / u.x);
1569       }
1570       else
1571       {
1572         positionDuration.x = 0;
1573       }
1574     }
1575
1576     if(mRulerY->IsEnabled() && mRulerY->GetType() == Ruler::Free)
1577     {
1578       float deltaY = fabsf(startPosition.y - positionSnap.y);
1579
1580       if(fabsf(u.y) > Math::MACHINE_EPSILON_1)
1581       {
1582         positionDuration.y = fabsf(deltaY / u.y);
1583       }
1584       else
1585       {
1586         positionDuration.y = 0;
1587       }
1588     }
1589   }
1590
1591   if(IsOvershootEnabled())
1592   {
1593     // Scroll to the end of the overshoot only when overshoot is enabled.
1594     positionSnap += clampDelta;
1595   }
1596
1597   bool animating = AnimateTo(positionSnap, positionDuration,
1598                              alphaFunction, false,
1599                              DirectionBiasNone, DirectionBiasNone,
1600                              isFlick || isFreeFlick ? Flick : Snap);
1601
1602   return animating;
1603 }
1604
1605 void ScrollView::StopAnimation(void)
1606 {
1607   // Clear Snap animation if exists.
1608   StopAnimation(mInternalXAnimation);
1609   StopAnimation(mInternalYAnimation);
1610   mScrollStateFlags = 0;
1611   // remove scroll animation flags
1612   HandleStoppedAnimation();
1613 }
1614
1615 void ScrollView::StopAnimation(Animation& animation)
1616 {
1617   if(animation)
1618   {
1619     animation.Stop();
1620     animation.Reset();
1621   }
1622 }
1623
1624 bool ScrollView::AnimateTo(const Vector2& position, const Vector2& positionDuration,
1625                            AlphaFunction alpha, bool findShortcuts,
1626                            DirectionBias horizontalBias, DirectionBias verticalBias,
1627                            SnapType snapType)
1628 {
1629   // Here we perform an animation on a number of properties (depending on which have changed)
1630   // The animation is applied to all ScrollBases
1631   Actor self = Self();
1632   mScrollTargetPosition = position;
1633   float totalDuration = 0.0f;
1634
1635   bool positionChanged = (mScrollTargetPosition != mScrollPostPosition);
1636
1637   if(positionChanged)
1638   {
1639     totalDuration = std::max(totalDuration, positionDuration.x);
1640     totalDuration = std::max(totalDuration, positionDuration.y);
1641   }
1642   else
1643   {
1644     // try to animate for a frame, on some occasions update will be changing scroll value while event side thinks it hasnt changed
1645     totalDuration = 0.01f;
1646     positionChanged = true;
1647   }
1648
1649   StopAnimation();
1650
1651   // Position Delta ///////////////////////////////////////////////////////
1652   if(positionChanged)
1653   {
1654     if(mWrapMode && findShortcuts)
1655     {
1656       // In Wrap Mode, the shortest distance is a little less intuitive...
1657       const RulerDomain rulerDomainX = mRulerX->GetDomain();
1658       const RulerDomain rulerDomainY = mRulerY->GetDomain();
1659
1660       if(mRulerX->IsEnabled())
1661       {
1662         float dir = VectorInDomain(-mScrollPrePosition.x, -mScrollTargetPosition.x, rulerDomainX.min, rulerDomainX.max, horizontalBias);
1663         mScrollTargetPosition.x = mScrollPrePosition.x + -dir;
1664       }
1665
1666       if(mRulerY->IsEnabled())
1667       {
1668         float dir = VectorInDomain(-mScrollPrePosition.y, -mScrollTargetPosition.y, rulerDomainY.min, rulerDomainY.max, verticalBias);
1669         mScrollTargetPosition.y = mScrollPrePosition.y + -dir;
1670       }
1671     }
1672
1673     // note we have two separate animations for X & Y, this deals with sliding diagonally and hitting
1674     // a horizonal/vertical wall.delay
1675     AnimateInternalXTo(mScrollTargetPosition.x, positionDuration.x, alpha);
1676     AnimateInternalYTo(mScrollTargetPosition.y, positionDuration.y, alpha);
1677
1678     if( !(mScrollStateFlags & SCROLL_ANIMATION_FLAGS) )
1679     {
1680       DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollTargetPosition.x, mScrollTargetPosition.y );
1681       self.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollTargetPosition);
1682       mScrollPrePosition = mScrollTargetPosition;
1683       mScrollPostPosition = mScrollTargetPosition;
1684       WrapPosition(mScrollPostPosition);
1685     }
1686
1687     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 );
1688     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 );
1689   }
1690
1691   SetScrollUpdateNotification(true);
1692
1693   // Always send a snap event when AnimateTo is called.
1694   Toolkit::ScrollView::SnapEvent snapEvent;
1695   snapEvent.type = snapType;
1696   snapEvent.position = -mScrollTargetPosition;
1697   snapEvent.duration = totalDuration;
1698
1699   DALI_LOG_SCROLL_STATE("[0x%X] mSnapStartedSignal [%.2f, %.2f]", this, snapEvent.position.x, snapEvent.position.y);
1700   mSnapStartedSignal.Emit( snapEvent );
1701
1702   return (mScrollStateFlags & SCROLL_ANIMATION_FLAGS) != 0;
1703 }
1704
1705 void ScrollView::EnableScrollOvershoot(bool enable)
1706 {
1707   if(enable && !mOvershootIndicator)
1708   {
1709     mOvershootIndicator = ScrollOvershootIndicator::New();
1710   }
1711   if( enable )
1712   {
1713     mOvershootIndicator->AttachToScrollable(*this);
1714   }
1715   else
1716   {
1717     mMaxOvershoot = mUserMaxOvershoot;
1718     mOvershootIndicator->DetachFromScrollable(*this);
1719   }
1720   UpdateMainInternalConstraint();
1721 }
1722
1723 void ScrollView::AddOverlay(Actor actor)
1724 {
1725   mInternalActor.Add( actor );
1726 }
1727
1728 void ScrollView::RemoveOverlay(Actor actor)
1729 {
1730   mInternalActor.Remove( actor );
1731 }
1732
1733 void ScrollView::SetOvershootEffectColor( const Vector4& color )
1734 {
1735   mOvershootEffectColor = color;
1736   if( mOvershootIndicator )
1737   {
1738     mOvershootIndicator->SetOvershootEffectColor( color );
1739   }
1740 }
1741
1742 void ScrollView::SetScrollingDirection( Radian direction, Radian threshold )
1743 {
1744   PanGestureDetector panGesture( GetPanGestureDetector() );
1745
1746   // First remove just in case we have some set, then add.
1747   panGesture.RemoveDirection( direction );
1748   panGesture.AddDirection( direction, threshold );
1749 }
1750
1751 void ScrollView::RemoveScrollingDirection( Radian direction )
1752 {
1753   PanGestureDetector panGesture( GetPanGestureDetector() );
1754   panGesture.RemoveDirection( direction );
1755 }
1756
1757 Toolkit::ScrollView::SnapStartedSignalType& ScrollView::SnapStartedSignal()
1758 {
1759   return mSnapStartedSignal;
1760 }
1761
1762 void ScrollView::FindAndUnbindActor(Actor child)
1763 {
1764   UnbindActor(child);
1765 }
1766
1767 Vector2 ScrollView::GetPropertyPrePosition() const
1768 {
1769   Vector2 position = Self().GetProperty<Vector2>(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION);
1770   WrapPosition(position);
1771   return position;
1772 }
1773
1774 Vector2 ScrollView::GetPropertyPosition() const
1775 {
1776   Vector2 position = Self().GetProperty<Vector2>(Toolkit::ScrollView::Property::SCROLL_POSITION);
1777   WrapPosition(position);
1778
1779   return position;
1780 }
1781
1782 void ScrollView::HandleStoppedAnimation()
1783 {
1784   SetScrollUpdateNotification(false);
1785 }
1786
1787 void ScrollView::HandleSnapAnimationFinished()
1788 {
1789   // Emit Signal that scrolling has completed.
1790   mScrolling = false;
1791   Actor self = Self();
1792   self.SetProperty(Toolkit::ScrollView::Property::SCROLLING, false);
1793
1794   Vector2 deltaPosition(mScrollPrePosition);
1795
1796   UpdateLocalScrollProperties();
1797   WrapPosition(mScrollPrePosition);
1798   DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y );
1799   self.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPrePosition);
1800
1801   Vector2 currentScrollPosition = GetCurrentScrollPosition();
1802   DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 3 current[%.2f, %.2f], mScrollTargetPosition[%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y, -mScrollTargetPosition.x, -mScrollTargetPosition.y );
1803   mScrollCompletedSignal.Emit( currentScrollPosition );
1804
1805   mDomainOffset += deltaPosition - mScrollPostPosition;
1806   self.SetProperty(Toolkit::ScrollView::Property::SCROLL_DOMAIN_OFFSET, mDomainOffset);
1807   HandleStoppedAnimation();
1808 }
1809
1810 void ScrollView::SetScrollUpdateNotification( bool enabled )
1811 {
1812   Actor self = Self();
1813   if( mScrollXUpdateNotification )
1814   {
1815     // disconnect now to avoid a notification before removed from update thread
1816     mScrollXUpdateNotification.NotifySignal().Disconnect(this, &ScrollView::OnScrollUpdateNotification);
1817     self.RemovePropertyNotification(mScrollXUpdateNotification);
1818     mScrollXUpdateNotification.Reset();
1819   }
1820   if( enabled && !mScrollUpdatedSignal.Empty())
1821   {
1822     // Only set up the notification when the application has connected to the updated signal
1823     mScrollXUpdateNotification = self.AddPropertyNotification(Toolkit::ScrollView::Property::SCROLL_POSITION, 0, StepCondition(mScrollUpdateDistance, 0.0f));
1824     mScrollXUpdateNotification.NotifySignal().Connect( this, &ScrollView::OnScrollUpdateNotification );
1825   }
1826   if( mScrollYUpdateNotification )
1827   {
1828     // disconnect now to avoid a notification before removed from update thread
1829     mScrollYUpdateNotification.NotifySignal().Disconnect(this, &ScrollView::OnScrollUpdateNotification);
1830     self.RemovePropertyNotification(mScrollYUpdateNotification);
1831     mScrollYUpdateNotification.Reset();
1832   }
1833   if( enabled && !mScrollUpdatedSignal.Empty())
1834   {
1835     // Only set up the notification when the application has connected to the updated signal
1836     mScrollYUpdateNotification = self.AddPropertyNotification(Toolkit::ScrollView::Property::SCROLL_POSITION, 1, StepCondition(mScrollUpdateDistance, 0.0f));
1837     mScrollYUpdateNotification.NotifySignal().Connect( this, &ScrollView::OnScrollUpdateNotification );
1838   }
1839 }
1840
1841 void ScrollView::OnScrollUpdateNotification(Dali::PropertyNotification& source)
1842 {
1843   // Guard against destruction during signal emission
1844   Toolkit::ScrollView handle( GetOwner() );
1845
1846   Vector2 currentScrollPosition = GetCurrentScrollPosition();
1847   mScrollUpdatedSignal.Emit( currentScrollPosition );
1848 }
1849
1850 bool ScrollView::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
1851 {
1852   Dali::BaseHandle handle( object );
1853
1854   bool connected( true );
1855   Toolkit::ScrollView view = Toolkit::ScrollView::DownCast( handle );
1856
1857   if( 0 == strcmp( signalName.c_str(), SIGNAL_SNAP_STARTED ) )
1858   {
1859     view.SnapStartedSignal().Connect( tracker, functor );
1860   }
1861   else
1862   {
1863     // signalName does not match any signal
1864     connected = false;
1865   }
1866
1867   return connected;
1868 }
1869
1870 void ScrollView::OnSizeAnimation(Animation& animation, const Vector3& targetSize)
1871 {
1872   // need to update domain properties for new size
1873   UpdatePropertyDomain();
1874 }
1875
1876 void ScrollView::OnControlSizeSet( const Vector3& size )
1877 {
1878   // need to update domain properties for new size
1879   if( mDefaultMaxOvershoot )
1880   {
1881     mUserMaxOvershoot.x = size.x * 0.5f;
1882     mUserMaxOvershoot.y = size.y * 0.5f;
1883     if( !IsOvershootEnabled() )
1884     {
1885       mMaxOvershoot = mUserMaxOvershoot;
1886     }
1887   }
1888   UpdatePropertyDomain();
1889   UpdateMainInternalConstraint();
1890   if( IsOvershootEnabled() )
1891   {
1892     mOvershootIndicator->Reset();
1893   }
1894 }
1895
1896 void ScrollView::OnChildAdd(Actor& child)
1897 {
1898   Dali::Toolkit::ScrollBar scrollBar = Dali::Toolkit::ScrollBar::DownCast(child);
1899   if(scrollBar)
1900   {
1901     mInternalActor.Add(scrollBar);
1902     if(scrollBar.GetScrollDirection() == Toolkit::ScrollBar::Horizontal)
1903     {
1904       scrollBar.SetScrollPropertySource(Self(),
1905                                         Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_X,
1906                                         Toolkit::Scrollable::Property::SCROLL_POSITION_MIN_X,
1907                                         Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_MAX_X,
1908                                         Toolkit::ScrollView::Property::SCROLL_DOMAIN_SIZE_X);
1909     }
1910     else
1911     {
1912       scrollBar.SetScrollPropertySource(Self(),
1913                                         Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_Y,
1914                                         Toolkit::Scrollable::Property::SCROLL_POSITION_MIN_Y,
1915                                         Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_MAX_Y,
1916                                         Toolkit::ScrollView::Property::SCROLL_DOMAIN_SIZE_Y);
1917     }
1918   }
1919   else if(mAlterChild)
1920   {
1921     BindActor(child);
1922   }
1923 }
1924
1925 void ScrollView::OnChildRemove(Actor& child)
1926 {
1927   // TODO: Actor needs a RemoveConstraint method to take out an individual constraint.
1928   UnbindActor(child);
1929 }
1930
1931 void ScrollView::StartTouchDownTimer()
1932 {
1933   if ( !mTouchDownTimer )
1934   {
1935     mTouchDownTimer = Timer::New( TOUCH_DOWN_TIMER_INTERVAL );
1936     mTouchDownTimer.TickSignal().Connect( this, &ScrollView::OnTouchDownTimeout );
1937   }
1938
1939   mTouchDownTimer.Start();
1940 }
1941
1942 void ScrollView::StopTouchDownTimer()
1943 {
1944   if ( mTouchDownTimer )
1945   {
1946     mTouchDownTimer.Stop();
1947   }
1948 }
1949
1950 bool ScrollView::OnTouchDownTimeout()
1951 {
1952   DALI_LOG_SCROLL_STATE("[0x%X]", this);
1953
1954   mTouchDownTimeoutReached = true;
1955
1956   unsigned int currentScrollStateFlags( mScrollStateFlags ); // Cleared in StopAnimation so keep local copy for comparison
1957   if( currentScrollStateFlags & (SCROLL_ANIMATION_FLAGS | SNAP_ANIMATION_FLAGS) )
1958   {
1959     DALI_LOG_SCROLL_STATE("[0x%X] Scrolling Or snapping flags set, stopping animation", this);
1960
1961     StopAnimation();
1962     if( currentScrollStateFlags & SCROLL_ANIMATION_FLAGS )
1963     {
1964       DALI_LOG_SCROLL_STATE("[0x%X] Scrolling flags set, emitting signal", this);
1965
1966       mScrollInterrupted = true;
1967       // reset domain offset as scrolling from original plane.
1968       mDomainOffset = Vector2::ZERO;
1969       Self().SetProperty(Toolkit::ScrollView::Property::SCROLL_DOMAIN_OFFSET, Vector2::ZERO);
1970
1971       UpdateLocalScrollProperties();
1972       Vector2 currentScrollPosition = GetCurrentScrollPosition();
1973       DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 4 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
1974       mScrollCompletedSignal.Emit( currentScrollPosition );
1975     }
1976   }
1977
1978   return false;
1979 }
1980
1981 bool ScrollView::OnTouchEvent(const TouchEvent& event)
1982 {
1983   if(!mSensitive)
1984   {
1985     DALI_LOG_SCROLL_STATE("[0x%X], Not Sensitive, ignoring", this);
1986
1987     // Ignore this touch event, if scrollview is insensitive.
1988     return false;
1989   }
1990
1991   // Ignore events with multiple-touch points
1992   if (event.GetPointCount() != 1)
1993   {
1994     DALI_LOG_SCROLL_STATE("[0x%X], multiple touch, ignoring", this);
1995
1996     return false;
1997   }
1998
1999   const TouchPoint::State pointState = event.GetPoint(0).state;
2000   if( pointState == TouchPoint::Down )
2001   {
2002     DALI_LOG_SCROLL_STATE("[0x%X] Down", this);
2003
2004     if(mGestureStackDepth==0)
2005     {
2006       mTouchDownTime = event.time;
2007
2008       // This allows time for a pan-gesture to start, to avoid breaking snap-animation behavior with fast flicks.
2009       // If touch-down does not become a pan (after timeout interval), then snap-animation can be interrupted.
2010       mTouchDownTimeoutReached = false;
2011       mScrollInterrupted = false;
2012       StartTouchDownTimer();
2013     }
2014   }
2015   else if( ( pointState == TouchPoint::Up ) ||
2016            ( ( pointState == TouchPoint::Interrupted ) && ( event.GetPoint(0).hitActor == Self() ) ) )
2017   {
2018     DALI_LOG_SCROLL_STATE("[0x%X] %s", this, ( ( pointState == TouchPoint::Up ) ? "Up" : "Interrupted" ) );
2019
2020     StopTouchDownTimer();
2021
2022     // if the user touches and releases without enough movement to go
2023     // into a gesture state, then we should snap to nearest point.
2024     // otherwise our scroll could be stopped (interrupted) half way through an animation.
2025     if(mGestureStackDepth==0 && mTouchDownTimeoutReached)
2026     {
2027       if( ( event.GetPoint(0).state == TouchPoint::Interrupted ) ||
2028           ( ( event.time - mTouchDownTime ) >= MINIMUM_TIME_BETWEEN_DOWN_AND_UP_FOR_RESET ) )
2029       {
2030         // Reset the velocity only if down was received a while ago
2031         mLastVelocity = Vector2( 0.0f, 0.0f );
2032       }
2033
2034       UpdateLocalScrollProperties();
2035       // Only finish the transform if scrolling was interrupted on down or if we are scrolling
2036       if ( mScrollInterrupted || mScrolling )
2037       {
2038         DALI_LOG_SCROLL_STATE("[0x%X] Calling FinishTransform", this);
2039
2040         FinishTransform();
2041       }
2042     }
2043     mTouchDownTimeoutReached = false;
2044     mScrollInterrupted = false;
2045   }
2046
2047   return true;
2048 }
2049
2050 bool ScrollView::OnMouseWheelEvent(const MouseWheelEvent& event)
2051 {
2052   if(!mSensitive)
2053   {
2054     // Ignore this mouse wheel event, if scrollview is insensitive.
2055     return false;
2056   }
2057
2058   Vector2 targetScrollPosition = GetPropertyPosition();
2059
2060   if(mRulerX->IsEnabled() && !mRulerY->IsEnabled())
2061   {
2062     // If only the ruler in the X axis is enabled, scroll in the X axis.
2063     if(mRulerX->GetType() == Ruler::Free)
2064     {
2065       // Free panning mode
2066       targetScrollPosition.x += event.z * mMouseWheelScrollDistanceStep.x;
2067       ClampPosition(targetScrollPosition);
2068       ScrollTo(-targetScrollPosition);
2069     }
2070     else if(!mScrolling)
2071     {
2072       // Snap mode, only respond to the event when the previous snap animation is finished.
2073       ScrollTo(GetCurrentPage() - event.z);
2074     }
2075   }
2076   else
2077   {
2078     // If the ruler in the Y axis is enabled, scroll in the Y axis.
2079     if(mRulerY->GetType() == Ruler::Free)
2080     {
2081       // Free panning mode
2082       targetScrollPosition.y += event.z * mMouseWheelScrollDistanceStep.y;
2083       ClampPosition(targetScrollPosition);
2084       ScrollTo(-targetScrollPosition);
2085     }
2086     else if(!mScrolling)
2087     {
2088       // Snap mode, only respond to the event when the previous snap animation is finished.
2089       ScrollTo(GetCurrentPage() - event.z * mRulerX->GetTotalPages());
2090     }
2091   }
2092
2093   return true;
2094 }
2095
2096 void ScrollView::ResetScrolling()
2097 {
2098   Actor self = Self();
2099   self.GetProperty(Toolkit::ScrollView::Property::SCROLL_POSITION).Get(mScrollPostPosition);
2100   mScrollPrePosition = mScrollPostPosition;
2101   DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPostPosition.x, mScrollPostPosition.y );
2102   self.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPostPosition);
2103 }
2104
2105 void ScrollView::UpdateLocalScrollProperties()
2106 {
2107   Actor self = Self();
2108   self.GetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION).Get(mScrollPrePosition);
2109   self.GetProperty(Toolkit::ScrollView::Property::SCROLL_POSITION).Get(mScrollPostPosition);
2110 }
2111
2112 // private functions
2113
2114 void ScrollView::PreAnimatedScrollSetup()
2115 {
2116   // SCROLL_PRE_POSITION is our unclamped property with wrapping
2117   // SCROLL_POSITION is our final scroll position after clamping
2118
2119   Actor self = Self();
2120
2121   Vector2 deltaPosition(mScrollPostPosition);
2122   WrapPosition(mScrollPostPosition);
2123   mDomainOffset += deltaPosition - mScrollPostPosition;
2124   Self().SetProperty(Toolkit::ScrollView::Property::SCROLL_DOMAIN_OFFSET, mDomainOffset);
2125
2126   if( mScrollStateFlags & SCROLL_X_STATE_MASK )
2127   {
2128     // already performing animation on internal x position
2129     StopAnimation(mInternalXAnimation);
2130   }
2131
2132   if( mScrollStateFlags & SCROLL_Y_STATE_MASK )
2133   {
2134     // already performing animation on internal y position
2135     StopAnimation(mInternalYAnimation);
2136   }
2137
2138   mScrollStateFlags = 0;
2139
2140   // Update Actor position with this wrapped value.
2141 }
2142
2143 void ScrollView::FinaliseAnimatedScroll()
2144 {
2145   // TODO - common animation finishing code in here
2146 }
2147
2148 void ScrollView::AnimateInternalXTo( float position, float duration, AlphaFunction alpha )
2149 {
2150   StopAnimation(mInternalXAnimation);
2151
2152   if( duration > Math::MACHINE_EPSILON_10 )
2153   {
2154     Actor self = Self();
2155     DALI_LOG_SCROLL_STATE("[0x%X], Animating from[%.2f] to[%.2f]", this, self.GetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION).Get<Vector2>().x, position );
2156     mInternalXAnimation = Animation::New(duration);
2157     DALI_LOG_SCROLL_STATE("[0x%X], mInternalXAnimation[0x%X]", this, mInternalXAnimation.GetObjectPtr() );
2158     mInternalXAnimation.FinishedSignal().Connect(this, &ScrollView::OnScrollAnimationFinished);
2159     mInternalXAnimation.AnimateTo( Property(self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, 0), position, alpha, TimePeriod(duration));
2160     mInternalXAnimation.Play();
2161
2162     // erase current state flags
2163     mScrollStateFlags &= ~SCROLL_X_STATE_MASK;
2164     // add internal animation state flag
2165     mScrollStateFlags |= AnimatingInternalX;
2166   }
2167 }
2168
2169 void ScrollView::AnimateInternalYTo( float position, float duration, AlphaFunction alpha )
2170 {
2171   StopAnimation(mInternalYAnimation);
2172
2173   if( duration > Math::MACHINE_EPSILON_10 )
2174   {
2175     Actor self = Self();
2176     DALI_LOG_SCROLL_STATE("[0x%X], Animating from[%.2f] to[%.2f]", this, self.GetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION).Get<Vector2>().y, position );
2177     mInternalYAnimation = Animation::New(duration);
2178     DALI_LOG_SCROLL_STATE("[0x%X], mInternalYAnimation[0x%X]", this, mInternalYAnimation.GetObjectPtr() );
2179     mInternalYAnimation.FinishedSignal().Connect(this, &ScrollView::OnScrollAnimationFinished);
2180     mInternalYAnimation.AnimateTo( Property(self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, 1), position, alpha, TimePeriod(duration));
2181     mInternalYAnimation.Play();
2182
2183     // erase current state flags
2184     mScrollStateFlags &= ~SCROLL_Y_STATE_MASK;
2185     // add internal animation state flag
2186     mScrollStateFlags |= AnimatingInternalY;
2187   }
2188 }
2189
2190 void ScrollView::OnScrollAnimationFinished( Animation& source )
2191 {
2192   // Guard against destruction during signal emission
2193   // Note that ScrollCompletedSignal is emitted from HandleSnapAnimationFinished()
2194   Toolkit::ScrollView handle( GetOwner() );
2195
2196   bool scrollingFinished = false;
2197
2198   // update our local scroll positions
2199   UpdateLocalScrollProperties();
2200
2201   if( source == mInternalXAnimation )
2202   {
2203     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 );
2204
2205     if( !(mScrollStateFlags & AnimatingInternalY) )
2206     {
2207       scrollingFinished = true;
2208     }
2209     mInternalXAnimation.Reset();
2210     // wrap pre scroll x position and set it
2211     if( mWrapMode )
2212     {
2213       const RulerDomain rulerDomain = mRulerX->GetDomain();
2214       mScrollPrePosition.x = -WrapInDomain(-mScrollPrePosition.x, rulerDomain.min, rulerDomain.max);
2215       DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y );
2216       handle.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPrePosition);
2217     }
2218     SnapInternalXTo(mScrollPostPosition.x);
2219   }
2220
2221   if( source == mInternalYAnimation )
2222   {
2223     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 );
2224
2225     if( !(mScrollStateFlags & AnimatingInternalX) )
2226     {
2227       scrollingFinished = true;
2228     }
2229     mInternalYAnimation.Reset();
2230     if( mWrapMode )
2231     {
2232       // wrap pre scroll y position and set it
2233       const RulerDomain rulerDomain = mRulerY->GetDomain();
2234       mScrollPrePosition.y = -WrapInDomain(-mScrollPrePosition.y, rulerDomain.min, rulerDomain.max);
2235       DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y );
2236       handle.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPrePosition);
2237     }
2238     SnapInternalYTo(mScrollPostPosition.y);
2239   }
2240
2241   DALI_LOG_SCROLL_STATE("[0x%X] scrollingFinished[%d] Animation[0x%X]", this, scrollingFinished, source.GetObjectPtr());
2242
2243   if(scrollingFinished)
2244   {
2245     HandleSnapAnimationFinished();
2246   }
2247 }
2248
2249 void ScrollView::OnSnapInternalPositionFinished( Animation& source )
2250 {
2251   Actor self = Self();
2252   UpdateLocalScrollProperties();
2253   if( source == mInternalXAnimation )
2254   {
2255     DALI_LOG_SCROLL_STATE("[0x%X] Finished X PostPosition Animation", this );
2256
2257     // clear internal x animation flags
2258     mScrollStateFlags &= ~SCROLL_X_STATE_MASK;
2259     mInternalXAnimation.Reset();
2260     WrapPosition(mScrollPrePosition);
2261   }
2262   if( source == mInternalYAnimation )
2263   {
2264     DALI_LOG_SCROLL_STATE("[0x%X] Finished Y PostPosition Animation", this );
2265
2266     mScrollStateFlags &= ~SCROLL_Y_STATE_MASK;
2267     mInternalYAnimation.Reset();
2268     WrapPosition(mScrollPrePosition);
2269   }
2270 }
2271
2272 void ScrollView::SnapInternalXTo(float position)
2273 {
2274   Actor self = Self();
2275
2276   StopAnimation(mInternalXAnimation);
2277
2278   // erase current state flags
2279   mScrollStateFlags &= ~SCROLL_X_STATE_MASK;
2280
2281   // if internal x not equal to inputed parameter, animate it
2282   float duration = std::min(fabsf((position - mScrollPrePosition.x) / mMaxOvershoot.x) * mSnapOvershootDuration, mSnapOvershootDuration);
2283   DALI_LOG_SCROLL_STATE("[0x%X] duration[%.2f]", this, duration );
2284   if( duration > Math::MACHINE_EPSILON_1 )
2285   {
2286     DALI_LOG_SCROLL_STATE("[0x%X] Starting X Snap Animation to[%.2f]", this, position );
2287
2288     mInternalXAnimation = Animation::New(duration);
2289     mInternalXAnimation.FinishedSignal().Connect(this, &ScrollView::OnSnapInternalPositionFinished);
2290     mInternalXAnimation.AnimateTo(Property(self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, 0), position);
2291     mInternalXAnimation.Play();
2292
2293     // add internal animation state flag
2294     mScrollStateFlags |= SnappingInternalX;
2295   }
2296 }
2297
2298 void ScrollView::SnapInternalYTo(float position)
2299 {
2300   Actor self = Self();
2301
2302   StopAnimation(mInternalYAnimation);
2303
2304   // erase current state flags
2305   mScrollStateFlags &= ~SCROLL_Y_STATE_MASK;
2306
2307   // if internal y not equal to inputed parameter, animate it
2308   float duration = std::min(fabsf((position - mScrollPrePosition.y) / mMaxOvershoot.y) * mSnapOvershootDuration, mSnapOvershootDuration);
2309   DALI_LOG_SCROLL_STATE("[0x%X] duration[%.2f]", this, duration );
2310   if( duration > Math::MACHINE_EPSILON_1 )
2311   {
2312     DALI_LOG_SCROLL_STATE("[0x%X] Starting Y Snap Animation to[%.2f]", this, position );
2313
2314     mInternalYAnimation = Animation::New(duration);
2315     mInternalYAnimation.FinishedSignal().Connect(this, &ScrollView::OnSnapInternalPositionFinished);
2316     mInternalYAnimation.AnimateTo(Property(self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, 1), position);
2317     mInternalYAnimation.Play();
2318
2319     // add internal animation state flag
2320     mScrollStateFlags |= SnappingInternalY;
2321   }
2322 }
2323
2324 void ScrollView::GestureStarted()
2325 {
2326   // we handle the first gesture.
2327   // if we're currently doing a gesture and receive another
2328   // we continue and combine the effects of the gesture instead of reseting.
2329   if(mGestureStackDepth++==0)
2330   {
2331     Actor self = Self();
2332     StopTouchDownTimer();
2333     StopAnimation();
2334     mPanDelta = Vector2::ZERO;
2335     mLastVelocity = Vector2::ZERO;
2336     if( !mScrolling )
2337     {
2338       mLockAxis = LockPossible;
2339     }
2340
2341     if( mScrollStateFlags & SCROLL_X_STATE_MASK )
2342     {
2343       StopAnimation(mInternalXAnimation);
2344     }
2345     if( mScrollStateFlags & SCROLL_Y_STATE_MASK )
2346     {
2347       StopAnimation(mInternalYAnimation);
2348     }
2349     mScrollStateFlags = 0;
2350
2351     if(mScrolling) // are we interrupting a current scroll?
2352     {
2353       // set mScrolling to false, in case user has code that interrogates mScrolling Getter() in complete.
2354       mScrolling = false;
2355       // send negative scroll position since scroll internal scroll position works as an offset for actors,
2356       // give applications the position within the domain from the scroll view's anchor position
2357       DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 5 [%.2f, %.2f]", this, -mScrollPostPosition.x, -mScrollPostPosition.y);
2358       mScrollCompletedSignal.Emit( -mScrollPostPosition );
2359     }
2360   }
2361 }
2362
2363 void ScrollView::GestureContinuing(const Vector2& panDelta)
2364 {
2365   mPanDelta.x+= panDelta.x;
2366   mPanDelta.y+= panDelta.y;
2367
2368   // Save the velocity, there is a bug in PanGesture
2369   // Whereby the Gesture::Finished's velocity is either:
2370   // NaN (due to time delta of zero between the last two events)
2371   // or 0 (due to position being the same between the last two events)
2372
2373   // Axis Auto Lock - locks the panning to the horizontal or vertical axis if the pan
2374   // appears mostly horizontal or mostly vertical respectively.
2375   if(mAxisAutoLock)
2376   {
2377     mLockAxis = GetLockAxis(mPanDelta, mLockAxis, mAxisAutoLockGradient);
2378   } // end if mAxisAutoLock
2379 }
2380
2381 // TODO: Upgrade to use a more powerful gesture detector (one that supports multiple touches on pan - so works as pan and flick gesture)
2382 // BUG: Gesture::Finished doesn't always return velocity on release (due to
2383 // timeDelta between last two events being 0 sometimes, or posiiton being the same)
2384 void ScrollView::OnPan( const PanGesture& gesture )
2385 {
2386   // Guard against destruction during signal emission
2387   // Note that Emit() methods are called indirectly e.g. from within ScrollView::OnGestureEx()
2388   Actor self( Self() );
2389
2390   if(!mSensitive)
2391   {
2392     DALI_LOG_SCROLL_STATE("[0x%X] Pan Ignored, Insensitive", this);
2393
2394     // If another callback on the same original signal disables sensitivity,
2395     // this callback will still be called, so we must suppress it.
2396     return;
2397   }
2398
2399   // translate Gesture input to get useful data...
2400   switch(gesture.state)
2401   {
2402     case Gesture::Started:
2403     {
2404       DALI_LOG_SCROLL_STATE("[0x%X] Pan Started", this);
2405       mPanStartPosition = gesture.position - gesture.displacement;
2406       UpdateLocalScrollProperties();
2407       GestureStarted();
2408       mPanning = true;
2409       self.SetProperty( Toolkit::ScrollView::Property::PANNING, true );
2410       self.SetProperty( Toolkit::ScrollView::Property::START_PAGE_POSITION, Vector3(gesture.position.x, gesture.position.y, 0.0f) );
2411
2412       UpdateMainInternalConstraint();
2413       break;
2414     }
2415
2416     case Gesture::Continuing:
2417     {
2418       if ( mPanning )
2419       {
2420         DALI_LOG_SCROLL_STATE("[0x%X] Pan Continuing", this);
2421         GestureContinuing(gesture.screenDisplacement);
2422       }
2423       else
2424       {
2425         // If we do not think we are panning, then we should not do anything here
2426         return;
2427       }
2428       break;
2429     }
2430
2431     case Gesture::Finished:
2432     case Gesture::Cancelled:
2433     {
2434       if ( mPanning )
2435       {
2436         DALI_LOG_SCROLL_STATE("[0x%X] Pan %s", this, ( ( gesture.state == Gesture::Finished ) ? "Finished" : "Cancelled" ) );
2437
2438         UpdateLocalScrollProperties();
2439         mLastVelocity = gesture.velocity;
2440         mPanning = false;
2441         self.SetProperty( Toolkit::ScrollView::Property::PANNING, false );
2442
2443         if( mScrollMainInternalPrePositionConstraint )
2444         {
2445           mScrollMainInternalPrePositionConstraint.Remove();
2446         }
2447
2448         if( mOvershootIndicator )
2449         {
2450           mOvershootIndicator->ClearOvershoot();
2451         }
2452       }
2453       else
2454       {
2455         // If we do not think we are panning, then we should not do anything here
2456         return;
2457       }
2458       break;
2459     }
2460
2461     case Gesture::Possible:
2462     case Gesture::Clear:
2463     {
2464       // Nothing to do, not needed.
2465       break;
2466     }
2467
2468   } // end switch(gesture.state)
2469
2470   OnGestureEx(gesture.state);
2471 }
2472
2473 void ScrollView::OnGestureEx(Gesture::State state)
2474 {
2475   // call necessary signals for application developer
2476
2477   if(state == Gesture::Started)
2478   {
2479     Vector2 currentScrollPosition = GetCurrentScrollPosition();
2480     Self().SetProperty(Toolkit::ScrollView::Property::SCROLLING, true);
2481     mScrolling = true;
2482     DALI_LOG_SCROLL_STATE("[0x%X] mScrollStartedSignal 2 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
2483     mScrollStartedSignal.Emit( currentScrollPosition );
2484   }
2485   else if( (state == Gesture::Finished) ||
2486            (state == Gesture::Cancelled) ) // Finished/default
2487   {
2488     // when all the gestures have finished, we finish the transform.
2489     // so if a user decides to pan (1 gesture), and then pan+zoom (2 gestures)
2490     // then stop panning (back to 1 gesture), and then stop zooming (0 gestures).
2491     // this is the point we end, and perform necessary snapping.
2492     mGestureStackDepth--;
2493     if(mGestureStackDepth==0)
2494     {
2495       // no flick if we have not exceeded min flick distance
2496       if( (fabsf(mPanDelta.x) < mMinFlickDistance.x)
2497           && (fabsf(mPanDelta.y) < mMinFlickDistance.y) )
2498       {
2499         // reset flick velocity
2500         mLastVelocity = Vector2::ZERO;
2501       }
2502       FinishTransform();
2503     }
2504     else
2505     {
2506       DALI_LOG_SCROLL_STATE("[0x%X] mGestureStackDepth[%d]", this, mGestureStackDepth);
2507     }
2508   }
2509 }
2510
2511 void ScrollView::FinishTransform()
2512 {
2513   // at this stage internal x and x scroll position should have followed prescroll position exactly
2514   Actor self = Self();
2515
2516   PreAnimatedScrollSetup();
2517
2518   // convert pixels/millisecond to pixels per second
2519   bool animating = SnapWithVelocity(mLastVelocity * 1000.0f);
2520
2521   if(!animating)
2522   {
2523     // if not animating, then this pan has completed right now.
2524     SetScrollUpdateNotification(false);
2525     mScrolling = false;
2526     Self().SetProperty(Toolkit::ScrollView::Property::SCROLLING, false);
2527
2528     if( fabs(mScrollPrePosition.x - mScrollTargetPosition.x) > Math::MACHINE_EPSILON_10 )
2529     {
2530       SnapInternalXTo(mScrollTargetPosition.x);
2531     }
2532     if( fabs(mScrollPrePosition.y - mScrollTargetPosition.y) > Math::MACHINE_EPSILON_10 )
2533     {
2534       SnapInternalYTo(mScrollTargetPosition.y);
2535     }
2536     Vector2 currentScrollPosition = GetCurrentScrollPosition();
2537     DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 6 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
2538     mScrollCompletedSignal.Emit( currentScrollPosition );
2539   }
2540 }
2541
2542 Vector2 ScrollView::GetOvershoot(Vector2& position) const
2543 {
2544   Vector3 size = Self().GetCurrentSize();
2545   Vector2 overshoot;
2546
2547   const RulerDomain rulerDomainX = mRulerX->GetDomain();
2548   const RulerDomain rulerDomainY = mRulerY->GetDomain();
2549
2550   if(mRulerX->IsEnabled() && rulerDomainX.enabled)
2551   {
2552     const float left = rulerDomainX.min - position.x;
2553     const float right = size.width - rulerDomainX.max - position.x;
2554     if(left<0)
2555     {
2556       overshoot.x = left;
2557     }
2558     else if(right>0)
2559     {
2560       overshoot.x = right;
2561     }
2562   }
2563
2564   if(mRulerY->IsEnabled() && rulerDomainY.enabled)
2565   {
2566     const float top = rulerDomainY.min - position.y;
2567     const float bottom = size.height - rulerDomainY.max - position.y;
2568     if(top<0)
2569     {
2570       overshoot.y = top;
2571     }
2572     else if(bottom>0)
2573     {
2574       overshoot.y = bottom;
2575     }
2576   }
2577
2578   return overshoot;
2579 }
2580
2581 bool ScrollView::OnAccessibilityPan(PanGesture gesture)
2582 {
2583   // Keep track of whether this is an AccessibilityPan
2584   mInAccessibilityPan = true;
2585   OnPan(gesture);
2586   mInAccessibilityPan = false;
2587
2588   return true;
2589 }
2590
2591 void ScrollView::ClampPosition(Vector2& position) const
2592 {
2593   ClampState2D clamped;
2594   ClampPosition(position, clamped);
2595 }
2596
2597 void ScrollView::ClampPosition(Vector2& position, ClampState2D &clamped) const
2598 {
2599   Vector3 size = Self().GetCurrentSize();
2600
2601   position.x = -mRulerX->Clamp(-position.x, size.width, 1.0f, clamped.x);    // NOTE: X & Y rulers think in -ve coordinate system.
2602   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.
2603 }
2604
2605 void ScrollView::WrapPosition(Vector2& position) const
2606 {
2607   if(mWrapMode)
2608   {
2609     const RulerDomain rulerDomainX = mRulerX->GetDomain();
2610     const RulerDomain rulerDomainY = mRulerY->GetDomain();
2611
2612     if(mRulerX->IsEnabled())
2613     {
2614       position.x = -WrapInDomain(-position.x, rulerDomainX.min, rulerDomainX.max);
2615     }
2616
2617     if(mRulerY->IsEnabled())
2618     {
2619       position.y = -WrapInDomain(-position.y, rulerDomainY.min, rulerDomainY.max);
2620     }
2621   }
2622 }
2623
2624 void ScrollView::UpdateMainInternalConstraint()
2625 {
2626   // TODO: Only update the constraints which have changed, rather than remove all and add all again.
2627   // Requires a dali-core ApplyConstraintAt, or a ReplaceConstraint. The former is probably more flexible.
2628   Actor self = Self();
2629   PanGestureDetector detector( GetPanGestureDetector() );
2630
2631   if(mScrollMainInternalPositionConstraint)
2632   {
2633     mScrollMainInternalPositionConstraint.Remove();
2634     mScrollMainInternalDeltaConstraint.Remove();
2635     mScrollMainInternalFinalConstraint.Remove();
2636     mScrollMainInternalRelativeConstraint.Remove();
2637     mScrollMainInternalDomainConstraint.Remove();
2638     mScrollMainInternalPrePositionMaxConstraint.Remove();
2639   }
2640   if( mScrollMainInternalPrePositionConstraint )
2641   {
2642     mScrollMainInternalPrePositionConstraint.Remove();
2643   }
2644
2645   // TODO: It's probably better to use a local displacement value as this will give a displacement when scrolling just commences
2646   // but we need to make sure than the gesture system gives displacement since last frame (60Hz), not displacement since last touch event (90Hz).
2647
2648   // 1. First calculate the pre-position (this is the scroll position if no clamping has taken place)
2649   Vector2 initialPanMask = Vector2(mRulerX->IsEnabled() ? 1.0f : 0.0f, mRulerY->IsEnabled() ? 1.0f : 0.0f);
2650
2651   if( mLockAxis == LockVertical )
2652   {
2653     initialPanMask.y = 0.0f;
2654   }
2655   else if( mLockAxis == LockHorizontal )
2656   {
2657     initialPanMask.x = 0.0f;
2658   }
2659
2660   if( mPanning )
2661   {
2662     mScrollMainInternalPrePositionConstraint = Constraint::New<Vector2>( self,
2663                                                                          Toolkit::ScrollView::Property::SCROLL_PRE_POSITION,
2664                                                                          InternalPrePositionConstraint( mPanStartPosition,
2665                                                                                                         initialPanMask,
2666                                                                                                         mAxisAutoLock,
2667                                                                                                         mAxisAutoLockGradient,
2668                                                                                                         mLockAxis,
2669                                                                                                         mMaxOvershoot,
2670                                                                                                         mRulerX->GetDomain(),
2671                                                                                                         mRulerY->GetDomain() ) );
2672     mScrollMainInternalPrePositionConstraint.AddSource( Source( detector, PanGestureDetector::Property::LOCAL_POSITION ) );
2673     mScrollMainInternalPrePositionConstraint.AddSource( Source( self, Actor::Property::SIZE ) );
2674     mScrollMainInternalPrePositionConstraint.Apply();
2675   }
2676
2677   // 2. Second calculate the clamped position (actual position)
2678   mScrollMainInternalPositionConstraint = Constraint::New<Vector2>( self,
2679                                                                     Toolkit::ScrollView::Property::SCROLL_POSITION,
2680                                                                     InternalPositionConstraint( mRulerX->GetDomain(),
2681                                                                                                 mRulerY->GetDomain(),
2682                                                                                                 mWrapMode ) );
2683   mScrollMainInternalPositionConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_PRE_POSITION ) );
2684   mScrollMainInternalPositionConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::SCROLL_POSITION_MIN ) );
2685   mScrollMainInternalPositionConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::SCROLL_POSITION_MAX ) );
2686   mScrollMainInternalPositionConstraint.AddSource( Source( self, Actor::Property::SIZE ) );
2687   mScrollMainInternalPositionConstraint.Apply();
2688
2689   mScrollMainInternalDeltaConstraint = Constraint::New<Vector2>( self, Toolkit::ScrollView::Property::SCROLL_POSITION_DELTA, InternalPositionDeltaConstraint );
2690   mScrollMainInternalDeltaConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_POSITION ) );
2691   mScrollMainInternalDeltaConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_DOMAIN_OFFSET ) );
2692   mScrollMainInternalDeltaConstraint.Apply();
2693
2694   mScrollMainInternalFinalConstraint = Constraint::New<Vector2>( self, Toolkit::ScrollView::Property::SCROLL_FINAL,
2695                                                                  InternalFinalConstraint( FinalDefaultAlphaFunction,
2696                                                                                           FinalDefaultAlphaFunction ) );
2697   mScrollMainInternalFinalConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_POSITION ) );
2698   mScrollMainInternalFinalConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::OVERSHOOT_X ) );
2699   mScrollMainInternalFinalConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::OVERSHOOT_Y ) );
2700   mScrollMainInternalFinalConstraint.Apply();
2701
2702   mScrollMainInternalRelativeConstraint = Constraint::New<Vector2>( self, Toolkit::Scrollable::Property::SCROLL_RELATIVE_POSITION, InternalRelativePositionConstraint );
2703   mScrollMainInternalRelativeConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_POSITION ) );
2704   mScrollMainInternalRelativeConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::SCROLL_POSITION_MIN ) );
2705   mScrollMainInternalRelativeConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::SCROLL_POSITION_MAX ) );
2706   mScrollMainInternalRelativeConstraint.AddSource( LocalSource( Actor::Property::SIZE ) );
2707   mScrollMainInternalRelativeConstraint.Apply();
2708
2709   mScrollMainInternalDomainConstraint = Constraint::New<Vector2>( self, Toolkit::ScrollView::Property::SCROLL_DOMAIN_SIZE, InternalScrollDomainConstraint );
2710   mScrollMainInternalDomainConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::SCROLL_POSITION_MIN ) );
2711   mScrollMainInternalDomainConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::SCROLL_POSITION_MAX ) );
2712   mScrollMainInternalDomainConstraint.AddSource( LocalSource( Actor::Property::SIZE ) );
2713   mScrollMainInternalDomainConstraint.Apply();
2714
2715   mScrollMainInternalPrePositionMaxConstraint = Constraint::New<Vector2>( self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_MAX, InternalPrePositionMaxConstraint );
2716   mScrollMainInternalPrePositionMaxConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::SCROLL_POSITION_MAX ) );
2717   mScrollMainInternalPrePositionMaxConstraint.AddSource( LocalSource( Actor::Property::SIZE ) );
2718   mScrollMainInternalPrePositionMaxConstraint.Apply();
2719
2720   // When panning we want to make sure overshoot values are affected by pre position and post position
2721   SetOvershootConstraintsEnabled(!mWrapMode);
2722 }
2723
2724 void ScrollView::SetOvershootConstraintsEnabled(bool enabled)
2725 {
2726   Actor self( Self() );
2727   // remove and reset, it may now be in wrong order with the main internal constraints
2728   if( mScrollMainInternalOvershootXConstraint )
2729   {
2730     mScrollMainInternalOvershootXConstraint.Remove();
2731     mScrollMainInternalOvershootXConstraint.Reset();
2732     mScrollMainInternalOvershootYConstraint.Remove();
2733     mScrollMainInternalOvershootYConstraint.Reset();
2734   }
2735   if( enabled )
2736   {
2737     mScrollMainInternalOvershootXConstraint= Constraint::New<float>( self, Toolkit::ScrollView::Property::OVERSHOOT_X, OvershootXConstraint(mMaxOvershoot.x) );
2738     mScrollMainInternalOvershootXConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_PRE_POSITION ) );
2739     mScrollMainInternalOvershootXConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_POSITION ) );
2740     mScrollMainInternalOvershootXConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::CAN_SCROLL_HORIZONTAL ) );
2741     mScrollMainInternalOvershootXConstraint.Apply();
2742
2743     mScrollMainInternalOvershootYConstraint = Constraint::New<float>( self, Toolkit::ScrollView::Property::OVERSHOOT_Y, OvershootYConstraint(mMaxOvershoot.y) );
2744     mScrollMainInternalOvershootYConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_PRE_POSITION ) );
2745     mScrollMainInternalOvershootYConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_POSITION ) );
2746     mScrollMainInternalOvershootYConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::CAN_SCROLL_VERTICAL ) );
2747     mScrollMainInternalOvershootYConstraint.Apply();
2748   }
2749   else
2750   {
2751     self.SetProperty(Toolkit::ScrollView::Property::OVERSHOOT_X, 0.0f);
2752     self.SetProperty(Toolkit::ScrollView::Property::OVERSHOOT_Y, 0.0f);
2753   }
2754 }
2755
2756 void ScrollView::SetInternalConstraints()
2757 {
2758   // Internal constraints (applied to target ScrollBase Actor itself) /////////
2759   UpdateMainInternalConstraint();
2760
2761   // User definable constraints to apply to all child actors //////////////////
2762   Actor self = Self();
2763
2764   // Apply some default constraints to ScrollView & its bound actors
2765   // Movement + Wrap function
2766
2767   Constraint constraint;
2768
2769   // MoveActor (scrolling)
2770   constraint = Constraint::New<Vector3>( self, Actor::Property::POSITION, MoveActorConstraint );
2771   constraint.AddSource( Source( self, Toolkit::ScrollView::Property::SCROLL_POSITION ) );
2772   constraint.SetRemoveAction(Constraint::Discard);
2773   ApplyConstraintToBoundActors(constraint);
2774
2775   // WrapActor (wrap functionality)
2776   constraint = Constraint::New<Vector3>( self, Actor::Property::POSITION, WrapActorConstraint );
2777   constraint.AddSource( LocalSource( Actor::Property::SCALE ) );
2778   constraint.AddSource( LocalSource( Actor::Property::ANCHOR_POINT ) );
2779   constraint.AddSource( LocalSource( Actor::Property::SIZE ) );
2780   constraint.AddSource( Source( self, Toolkit::Scrollable::Property::SCROLL_POSITION_MIN ) );
2781   constraint.AddSource( Source( self, Toolkit::Scrollable::Property::SCROLL_POSITION_MAX ) );
2782   constraint.AddSource( Source( self, Toolkit::ScrollView::Property::WRAP ) );
2783   constraint.SetRemoveAction(Constraint::Discard);
2784   ApplyConstraintToBoundActors(constraint);
2785 }
2786
2787 } // namespace Internal
2788
2789 } // namespace Toolkit
2790
2791 } // namespace Dali