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