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