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