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