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