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