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