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