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