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