[AT-SPI] Add AccessibleImpl::ScrollToChild()
[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 bool ScrollView::AccessibleImpl::ScrollToChild(Actor child)
1827 {
1828   auto scrollView = Dali::Toolkit::ScrollView::DownCast(Self());
1829   if (Toolkit::GetImpl(scrollView).FindClosestActor() == child)
1830   {
1831     return false;
1832   }
1833
1834   // FIXME: ScrollTo does not work (snaps back to original position)
1835   scrollView.ScrollTo(child, scrollView.GetScrollFlickDuration());
1836   return true;
1837 }
1838
1839 void ScrollView::FindAndUnbindActor(Actor child)
1840 {
1841   UnbindActor(child);
1842 }
1843
1844 Vector2 ScrollView::GetPropertyPrePosition() const
1845 {
1846   Vector2 position = Self().GetCurrentProperty<Vector2>(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION);
1847   WrapPosition(position);
1848   return position;
1849 }
1850
1851 Vector2 ScrollView::GetPropertyPosition() const
1852 {
1853   Vector2 position = Self().GetCurrentProperty<Vector2>(Toolkit::ScrollView::Property::SCROLL_POSITION);
1854   WrapPosition(position);
1855
1856   return position;
1857 }
1858
1859 void ScrollView::HandleStoppedAnimation()
1860 {
1861   SetScrollUpdateNotification(false);
1862 }
1863
1864 void ScrollView::HandleSnapAnimationFinished()
1865 {
1866   // Emit Signal that scrolling has completed.
1867   mScrolling = false;
1868   Actor self = Self();
1869   self.SetProperty(Toolkit::ScrollView::Property::SCROLLING, false);
1870
1871   Vector2 deltaPosition(mScrollPrePosition);
1872
1873   UpdateLocalScrollProperties();
1874   WrapPosition(mScrollPrePosition);
1875   DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y);
1876   self.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPrePosition);
1877
1878   Vector2 currentScrollPosition = GetCurrentScrollPosition();
1879   DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 3 current[%.2f, %.2f], mScrollTargetPosition[%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y, -mScrollTargetPosition.x, -mScrollTargetPosition.y);
1880   mScrollCompletedSignal.Emit(currentScrollPosition);
1881
1882   mDomainOffset += deltaPosition - mScrollPostPosition;
1883   self.SetProperty(Toolkit::ScrollView::Property::SCROLL_DOMAIN_OFFSET, mDomainOffset);
1884   HandleStoppedAnimation();
1885 }
1886
1887 void ScrollView::SetScrollUpdateNotification(bool enabled)
1888 {
1889   Actor self = Self();
1890   if(mScrollXUpdateNotification)
1891   {
1892     // disconnect now to avoid a notification before removed from update thread
1893     mScrollXUpdateNotification.NotifySignal().Disconnect(this, &ScrollView::OnScrollUpdateNotification);
1894     self.RemovePropertyNotification(mScrollXUpdateNotification);
1895     mScrollXUpdateNotification.Reset();
1896   }
1897   if(enabled && !mScrollUpdatedSignal.Empty())
1898   {
1899     // Only set up the notification when the application has connected to the updated signal
1900     mScrollXUpdateNotification = self.AddPropertyNotification(Toolkit::ScrollView::Property::SCROLL_POSITION, 0, StepCondition(mScrollUpdateDistance, 0.0f));
1901     mScrollXUpdateNotification.NotifySignal().Connect(this, &ScrollView::OnScrollUpdateNotification);
1902   }
1903   if(mScrollYUpdateNotification)
1904   {
1905     // disconnect now to avoid a notification before removed from update thread
1906     mScrollYUpdateNotification.NotifySignal().Disconnect(this, &ScrollView::OnScrollUpdateNotification);
1907     self.RemovePropertyNotification(mScrollYUpdateNotification);
1908     mScrollYUpdateNotification.Reset();
1909   }
1910   if(enabled && !mScrollUpdatedSignal.Empty())
1911   {
1912     // Only set up the notification when the application has connected to the updated signal
1913     mScrollYUpdateNotification = self.AddPropertyNotification(Toolkit::ScrollView::Property::SCROLL_POSITION, 1, StepCondition(mScrollUpdateDistance, 0.0f));
1914     mScrollYUpdateNotification.NotifySignal().Connect(this, &ScrollView::OnScrollUpdateNotification);
1915   }
1916 }
1917
1918 void ScrollView::OnScrollUpdateNotification(Dali::PropertyNotification& source)
1919 {
1920   // Guard against destruction during signal emission
1921   Toolkit::ScrollView handle(GetOwner());
1922
1923   Vector2 currentScrollPosition = GetCurrentScrollPosition();
1924   mScrollUpdatedSignal.Emit(currentScrollPosition);
1925 }
1926
1927 bool ScrollView::DoConnectSignal(BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor)
1928 {
1929   Dali::BaseHandle handle(object);
1930
1931   bool                connected(true);
1932   Toolkit::ScrollView view = Toolkit::ScrollView::DownCast(handle);
1933
1934   if(0 == strcmp(signalName.c_str(), SIGNAL_SNAP_STARTED))
1935   {
1936     view.SnapStartedSignal().Connect(tracker, functor);
1937   }
1938   else
1939   {
1940     // signalName does not match any signal
1941     connected = false;
1942   }
1943
1944   return connected;
1945 }
1946
1947 void ScrollView::OnSizeAnimation(Animation& animation, const Vector3& targetSize)
1948 {
1949   // need to update domain properties for new size
1950   UpdatePropertyDomain();
1951 }
1952
1953 void ScrollView::OnSizeSet(const Vector3& size)
1954 {
1955   // need to update domain properties for new size
1956   if(mDefaultMaxOvershoot)
1957   {
1958     mUserMaxOvershoot.x = size.x * 0.5f;
1959     mUserMaxOvershoot.y = size.y * 0.5f;
1960     if(!IsOvershootEnabled())
1961     {
1962       mMaxOvershoot = mUserMaxOvershoot;
1963     }
1964   }
1965   UpdatePropertyDomain();
1966   UpdateMainInternalConstraint();
1967   if(IsOvershootEnabled())
1968   {
1969     mOvershootIndicator->Reset();
1970   }
1971
1972   ScrollBase::OnSizeSet(size);
1973 }
1974
1975 void ScrollView::OnChildAdd(Actor& child)
1976 {
1977   ScrollBase::OnChildAdd(child);
1978
1979   Dali::Toolkit::ScrollBar scrollBar = Dali::Toolkit::ScrollBar::DownCast(child);
1980   if(scrollBar)
1981   {
1982     mScrollBar = scrollBar;
1983     scrollBar.SetProperty(Dali::Actor::Property::NAME, "ScrollBar");
1984
1985     mInternalActor.Add(scrollBar);
1986     if(scrollBar.GetScrollDirection() == Toolkit::ScrollBar::HORIZONTAL)
1987     {
1988       scrollBar.SetScrollPropertySource(Self(),
1989                                         Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_X,
1990                                         Toolkit::Scrollable::Property::SCROLL_POSITION_MIN_X,
1991                                         Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_MAX_X,
1992                                         Toolkit::ScrollView::Property::SCROLL_DOMAIN_SIZE_X);
1993     }
1994     else
1995     {
1996       scrollBar.SetScrollPropertySource(Self(),
1997                                         Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_Y,
1998                                         Toolkit::Scrollable::Property::SCROLL_POSITION_MIN_Y,
1999                                         Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_MAX_Y,
2000                                         Toolkit::ScrollView::Property::SCROLL_DOMAIN_SIZE_Y);
2001     }
2002
2003     if(mTransientScrollBar)
2004     {
2005       // Show the scroll-indicator for a brief period
2006       Property::Map emptyMap;
2007       scrollBar.DoAction("ShowTransientIndicator", emptyMap);
2008     }
2009   }
2010   else if(mAlterChild)
2011   {
2012     BindActor(child);
2013   }
2014 }
2015
2016 void ScrollView::OnChildRemove(Actor& child)
2017 {
2018   // TODO: Actor needs a RemoveConstraint method to take out an individual constraint.
2019   UnbindActor(child);
2020
2021   ScrollBase::OnChildRemove(child);
2022 }
2023
2024 void ScrollView::StartTouchDownTimer()
2025 {
2026   if(!mTouchDownTimer)
2027   {
2028     mTouchDownTimer = Timer::New(TOUCH_DOWN_TIMER_INTERVAL);
2029     mTouchDownTimer.TickSignal().Connect(this, &ScrollView::OnTouchDownTimeout);
2030   }
2031
2032   mTouchDownTimer.Start();
2033 }
2034
2035 void ScrollView::StopTouchDownTimer()
2036 {
2037   if(mTouchDownTimer)
2038   {
2039     mTouchDownTimer.Stop();
2040   }
2041 }
2042
2043 bool ScrollView::OnTouchDownTimeout()
2044 {
2045   DALI_LOG_SCROLL_STATE("[0x%X]", this);
2046
2047   mTouchDownTimeoutReached = true;
2048
2049   unsigned int currentScrollStateFlags(mScrollStateFlags); // Cleared in StopAnimation so keep local copy for comparison
2050   if(currentScrollStateFlags & (SCROLL_ANIMATION_FLAGS | SNAP_ANIMATION_FLAGS))
2051   {
2052     DALI_LOG_SCROLL_STATE("[0x%X] Scrolling Or snapping flags set, stopping animation", this);
2053
2054     StopAnimation();
2055     if(currentScrollStateFlags & SCROLL_ANIMATION_FLAGS)
2056     {
2057       DALI_LOG_SCROLL_STATE("[0x%X] Scrolling flags set, emitting signal", this);
2058
2059       mScrollInterrupted = true;
2060       // reset domain offset as scrolling from original plane.
2061       mDomainOffset = Vector2::ZERO;
2062       Self().SetProperty(Toolkit::ScrollView::Property::SCROLL_DOMAIN_OFFSET, Vector2::ZERO);
2063
2064       UpdateLocalScrollProperties();
2065       Vector2 currentScrollPosition = GetCurrentScrollPosition();
2066       DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 4 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
2067       mScrollCompletedSignal.Emit(currentScrollPosition);
2068     }
2069   }
2070
2071   return false;
2072 }
2073
2074 bool ScrollView::OnTouch(Actor actor, const TouchEvent& touch)
2075 {
2076   if(!mSensitive)
2077   {
2078     DALI_LOG_SCROLL_STATE("[0x%X], Not Sensitive, ignoring", this);
2079
2080     // Ignore this touch event, if scrollview is insensitive.
2081     return false;
2082   }
2083
2084   // Ignore events with multiple-touch points
2085   if(touch.GetPointCount() != 1)
2086   {
2087     DALI_LOG_SCROLL_STATE("[0x%X], multiple touch, ignoring", this);
2088
2089     return false;
2090   }
2091
2092   const PointState::Type pointState = touch.GetState(0);
2093   if(pointState == PointState::DOWN)
2094   {
2095     DALI_LOG_SCROLL_STATE("[0x%X] Down", this);
2096
2097     if(mGestureStackDepth == 0)
2098     {
2099       mTouchDownTime = touch.GetTime();
2100
2101       // This allows time for a pan-gesture to start, to avoid breaking snap-animation behavior with fast flicks.
2102       // If touch-down does not become a pan (after timeout interval), then snap-animation can be interrupted.
2103       mTouchDownTimeoutReached = false;
2104       mScrollInterrupted       = false;
2105       StartTouchDownTimer();
2106     }
2107   }
2108   else if((pointState == PointState::UP) ||
2109           ((pointState == PointState::INTERRUPTED) && (touch.GetHitActor(0) == Self())))
2110   {
2111     DALI_LOG_SCROLL_STATE("[0x%X] %s", this, ((pointState == PointState::UP) ? "Up" : "Interrupted"));
2112
2113     StopTouchDownTimer();
2114
2115     // if the user touches and releases without enough movement to go
2116     // into a gesture state, then we should snap to nearest point.
2117     // otherwise our scroll could be stopped (interrupted) half way through an animation.
2118     if(mGestureStackDepth == 0 && mTouchDownTimeoutReached)
2119     {
2120       if((pointState == PointState::INTERRUPTED) ||
2121          ((touch.GetTime() - mTouchDownTime) >= MINIMUM_TIME_BETWEEN_DOWN_AND_UP_FOR_RESET))
2122       {
2123         // Reset the velocity only if down was received a while ago
2124         mLastVelocity = Vector2(0.0f, 0.0f);
2125       }
2126
2127       UpdateLocalScrollProperties();
2128       // Only finish the transform if scrolling was interrupted on down or if we are scrolling
2129       if(mScrollInterrupted || mScrolling)
2130       {
2131         DALI_LOG_SCROLL_STATE("[0x%X] Calling FinishTransform", this);
2132
2133         FinishTransform();
2134       }
2135     }
2136     mTouchDownTimeoutReached = false;
2137     mScrollInterrupted       = false;
2138   }
2139
2140   return false;
2141 }
2142
2143 bool ScrollView::OnWheelEvent(Actor actor, const WheelEvent& event)
2144 {
2145   if(!mSensitive)
2146   {
2147     // Ignore this wheel event, if scrollview is insensitive.
2148     return false;
2149   }
2150
2151   Vector2 targetScrollPosition = GetPropertyPosition();
2152
2153   if(mRulerX->IsEnabled() && !mRulerY->IsEnabled())
2154   {
2155     // If only the ruler in the X axis is enabled, scroll in the X axis.
2156     if(mRulerX->GetType() == Ruler::FREE)
2157     {
2158       // Free panning mode
2159       targetScrollPosition.x += event.GetDelta() * mWheelScrollDistanceStep.x;
2160       ClampPosition(targetScrollPosition);
2161       ScrollTo(-targetScrollPosition);
2162     }
2163     else if(!mScrolling)
2164     {
2165       // Snap mode, only respond to the event when the previous snap animation is finished.
2166       ScrollTo(GetCurrentPage() - event.GetDelta());
2167     }
2168   }
2169   else
2170   {
2171     // If the ruler in the Y axis is enabled, scroll in the Y axis.
2172     if(mRulerY->GetType() == Ruler::FREE)
2173     {
2174       // Free panning mode
2175       targetScrollPosition.y += event.GetDelta() * mWheelScrollDistanceStep.y;
2176       ClampPosition(targetScrollPosition);
2177       ScrollTo(-targetScrollPosition);
2178     }
2179     else if(!mScrolling)
2180     {
2181       // Snap mode, only respond to the event when the previous snap animation is finished.
2182       ScrollTo(GetCurrentPage() - event.GetDelta() * mRulerX->GetTotalPages());
2183     }
2184   }
2185
2186   return true;
2187 }
2188
2189 void ScrollView::ResetScrolling()
2190 {
2191   Actor self = Self();
2192   self.GetCurrentProperty(Toolkit::ScrollView::Property::SCROLL_POSITION).Get(mScrollPostPosition);
2193   mScrollPrePosition = mScrollPostPosition;
2194   DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPostPosition.x, mScrollPostPosition.y);
2195   self.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPostPosition);
2196 }
2197
2198 void ScrollView::UpdateLocalScrollProperties()
2199 {
2200   Actor self = Self();
2201   self.GetCurrentProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION).Get(mScrollPrePosition);
2202   self.GetCurrentProperty(Toolkit::ScrollView::Property::SCROLL_POSITION).Get(mScrollPostPosition);
2203 }
2204
2205 // private functions
2206
2207 void ScrollView::PreAnimatedScrollSetup()
2208 {
2209   // SCROLL_PRE_POSITION is our unclamped property with wrapping
2210   // SCROLL_POSITION is our final scroll position after clamping
2211
2212   Actor self = Self();
2213
2214   Vector2 deltaPosition(mScrollPostPosition);
2215   WrapPosition(mScrollPostPosition);
2216   mDomainOffset += deltaPosition - mScrollPostPosition;
2217   Self().SetProperty(Toolkit::ScrollView::Property::SCROLL_DOMAIN_OFFSET, mDomainOffset);
2218
2219   if(mScrollStateFlags & SCROLL_X_STATE_MASK)
2220   {
2221     // already performing animation on internal x position
2222     StopAnimation(mInternalXAnimation);
2223   }
2224
2225   if(mScrollStateFlags & SCROLL_Y_STATE_MASK)
2226   {
2227     // already performing animation on internal y position
2228     StopAnimation(mInternalYAnimation);
2229   }
2230
2231   mScrollStateFlags = 0;
2232
2233   // Update Actor position with this wrapped value.
2234 }
2235
2236 void ScrollView::FinaliseAnimatedScroll()
2237 {
2238   // TODO - common animation finishing code in here
2239 }
2240
2241 void ScrollView::AnimateInternalXTo(float position, float duration, AlphaFunction alpha)
2242 {
2243   StopAnimation(mInternalXAnimation);
2244
2245   if(duration > Math::MACHINE_EPSILON_10)
2246   {
2247     Actor self = Self();
2248     DALI_LOG_SCROLL_STATE("[0x%X], Animating from[%.2f] to[%.2f]", this, self.GetCurrentProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION).Get<Vector2>().x, position);
2249     mInternalXAnimation = Animation::New(duration);
2250     DALI_LOG_SCROLL_STATE("[0x%X], mInternalXAnimation[0x%X]", this, mInternalXAnimation.GetObjectPtr());
2251     mInternalXAnimation.FinishedSignal().Connect(this, &ScrollView::OnScrollAnimationFinished);
2252     mInternalXAnimation.AnimateTo(Property(self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, 0), position, alpha, TimePeriod(duration));
2253     mInternalXAnimation.Play();
2254
2255     // erase current state flags
2256     mScrollStateFlags &= ~SCROLL_X_STATE_MASK;
2257     // add internal animation state flag
2258     mScrollStateFlags |= AnimatingInternalX;
2259   }
2260 }
2261
2262 void ScrollView::AnimateInternalYTo(float position, float duration, AlphaFunction alpha)
2263 {
2264   StopAnimation(mInternalYAnimation);
2265
2266   if(duration > Math::MACHINE_EPSILON_10)
2267   {
2268     Actor self = Self();
2269     DALI_LOG_SCROLL_STATE("[0x%X], Animating from[%.2f] to[%.2f]", this, self.GetCurrentProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION).Get<Vector2>().y, position);
2270     mInternalYAnimation = Animation::New(duration);
2271     DALI_LOG_SCROLL_STATE("[0x%X], mInternalYAnimation[0x%X]", this, mInternalYAnimation.GetObjectPtr());
2272     mInternalYAnimation.FinishedSignal().Connect(this, &ScrollView::OnScrollAnimationFinished);
2273     mInternalYAnimation.AnimateTo(Property(self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, 1), position, alpha, TimePeriod(duration));
2274     mInternalYAnimation.Play();
2275
2276     // erase current state flags
2277     mScrollStateFlags &= ~SCROLL_Y_STATE_MASK;
2278     // add internal animation state flag
2279     mScrollStateFlags |= AnimatingInternalY;
2280   }
2281 }
2282
2283 void ScrollView::OnScrollAnimationFinished(Animation& source)
2284 {
2285   // Guard against destruction during signal emission
2286   // Note that ScrollCompletedSignal is emitted from HandleSnapAnimationFinished()
2287   Toolkit::ScrollView handle(GetOwner());
2288
2289   bool scrollingFinished = false;
2290
2291   // update our local scroll positions
2292   UpdateLocalScrollProperties();
2293
2294   if(source == mInternalXAnimation)
2295   {
2296     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);
2297
2298     if(!(mScrollStateFlags & AnimatingInternalY))
2299     {
2300       scrollingFinished = true;
2301     }
2302     mInternalXAnimation.Reset();
2303     // wrap pre scroll x position and set it
2304     if(mWrapMode)
2305     {
2306       const RulerDomain rulerDomain = mRulerX->GetDomain();
2307       mScrollPrePosition.x          = -WrapInDomain(-mScrollPrePosition.x, rulerDomain.min, rulerDomain.max);
2308       DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y);
2309       handle.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPrePosition);
2310     }
2311     SnapInternalXTo(mScrollPostPosition.x);
2312   }
2313
2314   if(source == mInternalYAnimation)
2315   {
2316     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);
2317
2318     if(!(mScrollStateFlags & AnimatingInternalX))
2319     {
2320       scrollingFinished = true;
2321     }
2322     mInternalYAnimation.Reset();
2323     if(mWrapMode)
2324     {
2325       // wrap pre scroll y position and set it
2326       const RulerDomain rulerDomain = mRulerY->GetDomain();
2327       mScrollPrePosition.y          = -WrapInDomain(-mScrollPrePosition.y, rulerDomain.min, rulerDomain.max);
2328       DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y);
2329       handle.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPrePosition);
2330     }
2331     SnapInternalYTo(mScrollPostPosition.y);
2332   }
2333
2334   DALI_LOG_SCROLL_STATE("[0x%X] scrollingFinished[%d] Animation[0x%X]", this, scrollingFinished, source.GetObjectPtr());
2335
2336   if(scrollingFinished)
2337   {
2338     HandleSnapAnimationFinished();
2339   }
2340 }
2341
2342 void ScrollView::OnSnapInternalPositionFinished(Animation& source)
2343 {
2344   Actor self = Self();
2345   UpdateLocalScrollProperties();
2346   if(source == mInternalXAnimation)
2347   {
2348     DALI_LOG_SCROLL_STATE("[0x%X] Finished X PostPosition Animation", this);
2349
2350     // clear internal x animation flags
2351     mScrollStateFlags &= ~SCROLL_X_STATE_MASK;
2352     mInternalXAnimation.Reset();
2353     WrapPosition(mScrollPrePosition);
2354   }
2355   if(source == mInternalYAnimation)
2356   {
2357     DALI_LOG_SCROLL_STATE("[0x%X] Finished Y PostPosition Animation", this);
2358
2359     mScrollStateFlags &= ~SCROLL_Y_STATE_MASK;
2360     mInternalYAnimation.Reset();
2361     WrapPosition(mScrollPrePosition);
2362   }
2363 }
2364
2365 void ScrollView::SnapInternalXTo(float position)
2366 {
2367   Actor self = Self();
2368
2369   StopAnimation(mInternalXAnimation);
2370
2371   // erase current state flags
2372   mScrollStateFlags &= ~SCROLL_X_STATE_MASK;
2373
2374   // if internal x not equal to inputed parameter, animate it
2375   float duration = std::min(fabsf((position - mScrollPrePosition.x) / mMaxOvershoot.x) * mSnapOvershootDuration, mSnapOvershootDuration);
2376   DALI_LOG_SCROLL_STATE("[0x%X] duration[%.2f]", this, duration);
2377   if(duration > Math::MACHINE_EPSILON_1)
2378   {
2379     DALI_LOG_SCROLL_STATE("[0x%X] Starting X Snap Animation to[%.2f]", this, position);
2380
2381     mInternalXAnimation = Animation::New(duration);
2382     mInternalXAnimation.FinishedSignal().Connect(this, &ScrollView::OnSnapInternalPositionFinished);
2383     mInternalXAnimation.AnimateTo(Property(self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, 0), position);
2384     mInternalXAnimation.Play();
2385
2386     // add internal animation state flag
2387     mScrollStateFlags |= SnappingInternalX;
2388   }
2389 }
2390
2391 void ScrollView::SnapInternalYTo(float position)
2392 {
2393   Actor self = Self();
2394
2395   StopAnimation(mInternalYAnimation);
2396
2397   // erase current state flags
2398   mScrollStateFlags &= ~SCROLL_Y_STATE_MASK;
2399
2400   // if internal y not equal to inputed parameter, animate it
2401   float duration = std::min(fabsf((position - mScrollPrePosition.y) / mMaxOvershoot.y) * mSnapOvershootDuration, mSnapOvershootDuration);
2402   DALI_LOG_SCROLL_STATE("[0x%X] duration[%.2f]", this, duration);
2403   if(duration > Math::MACHINE_EPSILON_1)
2404   {
2405     DALI_LOG_SCROLL_STATE("[0x%X] Starting Y Snap Animation to[%.2f]", this, position);
2406
2407     mInternalYAnimation = Animation::New(duration);
2408     mInternalYAnimation.FinishedSignal().Connect(this, &ScrollView::OnSnapInternalPositionFinished);
2409     mInternalYAnimation.AnimateTo(Property(self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, 1), position);
2410     mInternalYAnimation.Play();
2411
2412     // add internal animation state flag
2413     mScrollStateFlags |= SnappingInternalY;
2414   }
2415 }
2416
2417 void ScrollView::GestureStarted()
2418 {
2419   // we handle the first gesture.
2420   // if we're currently doing a gesture and receive another
2421   // we continue and combine the effects of the gesture instead of reseting.
2422   if(mGestureStackDepth++ == 0)
2423   {
2424     Actor self = Self();
2425     StopTouchDownTimer();
2426     StopAnimation();
2427     mPanDelta     = Vector2::ZERO;
2428     mLastVelocity = Vector2::ZERO;
2429     if(!mScrolling)
2430     {
2431       mLockAxis = LockPossible;
2432     }
2433
2434     if(mScrollStateFlags & SCROLL_X_STATE_MASK)
2435     {
2436       StopAnimation(mInternalXAnimation);
2437     }
2438     if(mScrollStateFlags & SCROLL_Y_STATE_MASK)
2439     {
2440       StopAnimation(mInternalYAnimation);
2441     }
2442     mScrollStateFlags = 0;
2443
2444     if(mScrolling) // are we interrupting a current scroll?
2445     {
2446       // set mScrolling to false, in case user has code that interrogates mScrolling Getter() in complete.
2447       mScrolling = false;
2448       // send negative scroll position since scroll internal scroll position works as an offset for actors,
2449       // give applications the position within the domain from the scroll view's anchor position
2450       DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 5 [%.2f, %.2f]", this, -mScrollPostPosition.x, -mScrollPostPosition.y);
2451       mScrollCompletedSignal.Emit(-mScrollPostPosition);
2452     }
2453   }
2454 }
2455
2456 void ScrollView::GestureContinuing(const Vector2& panDelta)
2457 {
2458   mPanDelta.x += panDelta.x;
2459   mPanDelta.y += panDelta.y;
2460
2461   // Save the velocity, there is a bug in PanGesture
2462   // Whereby the GestureState::FINISHED's velocity is either:
2463   // NaN (due to time delta of zero between the last two events)
2464   // or 0 (due to position being the same between the last two events)
2465
2466   // Axis Auto Lock - locks the panning to the horizontal or vertical axis if the pan
2467   // appears mostly horizontal or mostly vertical respectively.
2468   if(mAxisAutoLock)
2469   {
2470     mLockAxis = GetLockAxis(mPanDelta, mLockAxis, mAxisAutoLockGradient);
2471   } // end if mAxisAutoLock
2472 }
2473
2474 // TODO: Upgrade to use a more powerful gesture detector (one that supports multiple touches on pan - so works as pan and flick gesture)
2475 // BUG: GestureState::FINISHED doesn't always return velocity on release (due to
2476 // timeDelta between last two events being 0 sometimes, or posiiton being the same)
2477 void ScrollView::OnPan(const PanGesture& gesture)
2478 {
2479   // Guard against destruction during signal emission
2480   // Note that Emit() methods are called indirectly e.g. from within ScrollView::OnGestureEx()
2481   Actor self(Self());
2482
2483   if(!mSensitive)
2484   {
2485     DALI_LOG_SCROLL_STATE("[0x%X] Pan Ignored, Insensitive", this);
2486
2487     // If another callback on the same original signal disables sensitivity,
2488     // this callback will still be called, so we must suppress it.
2489     return;
2490   }
2491
2492   // translate Gesture input to get useful data...
2493   switch(gesture.GetState())
2494   {
2495     case GestureState::STARTED:
2496     {
2497       DALI_LOG_SCROLL_STATE("[0x%X] Pan Started", this);
2498       const Vector2& position = gesture.GetPosition();
2499       mPanStartPosition       = position - gesture.GetDisplacement();
2500       UpdateLocalScrollProperties();
2501       GestureStarted();
2502       mPanning = true;
2503       self.SetProperty(Toolkit::ScrollView::Property::PANNING, true);
2504       self.SetProperty(Toolkit::ScrollView::Property::START_PAGE_POSITION, Vector3(position.x, position.y, 0.0f));
2505
2506       UpdateMainInternalConstraint();
2507       Toolkit::ScrollBar scrollBar = mScrollBar.GetHandle();
2508       if(scrollBar && mTransientScrollBar)
2509       {
2510         Vector3                     size         = Self().GetCurrentProperty<Vector3>(Actor::Property::SIZE);
2511         const Toolkit::RulerDomain& rulerDomainX = mRulerX->GetDomain();
2512         const Toolkit::RulerDomain& rulerDomainY = mRulerY->GetDomain();
2513
2514         if((rulerDomainX.max > size.width) || (rulerDomainY.max > size.height))
2515         {
2516           scrollBar.ShowIndicator();
2517         }
2518       }
2519       break;
2520     }
2521
2522     case GestureState::CONTINUING:
2523     {
2524       if(mPanning)
2525       {
2526         DALI_LOG_SCROLL_STATE("[0x%X] Pan Continuing", this);
2527         GestureContinuing(gesture.GetScreenDisplacement());
2528       }
2529       else
2530       {
2531         // If we do not think we are panning, then we should not do anything here
2532         return;
2533       }
2534       break;
2535     }
2536
2537     case GestureState::FINISHED:
2538     case GestureState::CANCELLED:
2539     {
2540       if(mPanning)
2541       {
2542         DALI_LOG_SCROLL_STATE("[0x%X] Pan %s", this, ((gesture.GetState() == GestureState::FINISHED) ? "Finished" : "Cancelled"));
2543
2544         UpdateLocalScrollProperties();
2545         mLastVelocity = gesture.GetVelocity();
2546         mPanning      = false;
2547         self.SetProperty(Toolkit::ScrollView::Property::PANNING, false);
2548
2549         if(mScrollMainInternalPrePositionConstraint)
2550         {
2551           mScrollMainInternalPrePositionConstraint.Remove();
2552         }
2553
2554         Toolkit::ScrollBar scrollBar = mScrollBar.GetHandle();
2555         if(scrollBar && mTransientScrollBar)
2556         {
2557           scrollBar.HideIndicator();
2558         }
2559       }
2560       else
2561       {
2562         // If we do not think we are panning, then we should not do anything here
2563         return;
2564       }
2565       break;
2566     }
2567
2568     case GestureState::POSSIBLE:
2569     case GestureState::CLEAR:
2570     {
2571       // Nothing to do, not needed.
2572       break;
2573     }
2574
2575   } // end switch(gesture.state)
2576
2577   OnGestureEx(gesture.GetState());
2578 }
2579
2580 void ScrollView::OnGestureEx(GestureState state)
2581 {
2582   // call necessary signals for application developer
2583
2584   if(state == GestureState::STARTED)
2585   {
2586     Vector2 currentScrollPosition = GetCurrentScrollPosition();
2587     Self().SetProperty(Toolkit::ScrollView::Property::SCROLLING, true);
2588     mScrolling = true;
2589     DALI_LOG_SCROLL_STATE("[0x%X] mScrollStartedSignal 2 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
2590     mScrollStartedSignal.Emit(currentScrollPosition);
2591   }
2592   else if((state == GestureState::FINISHED) ||
2593           (state == GestureState::CANCELLED)) // Finished/default
2594   {
2595     // when all the gestures have finished, we finish the transform.
2596     // so if a user decides to pan (1 gesture), and then pan+zoom (2 gestures)
2597     // then stop panning (back to 1 gesture), and then stop zooming (0 gestures).
2598     // this is the point we end, and perform necessary snapping.
2599     mGestureStackDepth--;
2600     if(mGestureStackDepth == 0)
2601     {
2602       // no flick if we have not exceeded min flick distance
2603       if((fabsf(mPanDelta.x) < mMinFlickDistance.x) && (fabsf(mPanDelta.y) < mMinFlickDistance.y))
2604       {
2605         // reset flick velocity
2606         mLastVelocity = Vector2::ZERO;
2607       }
2608       FinishTransform();
2609     }
2610     else
2611     {
2612       DALI_LOG_SCROLL_STATE("[0x%X] mGestureStackDepth[%d]", this, mGestureStackDepth);
2613     }
2614   }
2615 }
2616
2617 void ScrollView::FinishTransform()
2618 {
2619   // at this stage internal x and x scroll position should have followed prescroll position exactly
2620   Actor self = Self();
2621
2622   PreAnimatedScrollSetup();
2623
2624   // convert pixels/millisecond to pixels per second
2625   bool animating = SnapWithVelocity(mLastVelocity * 1000.0f);
2626
2627   if(!animating)
2628   {
2629     // if not animating, then this pan has completed right now.
2630     SetScrollUpdateNotification(false);
2631     mScrolling = false;
2632     Self().SetProperty(Toolkit::ScrollView::Property::SCROLLING, false);
2633
2634     if(fabs(mScrollPrePosition.x - mScrollTargetPosition.x) > Math::MACHINE_EPSILON_10)
2635     {
2636       SnapInternalXTo(mScrollTargetPosition.x);
2637     }
2638     if(fabs(mScrollPrePosition.y - mScrollTargetPosition.y) > Math::MACHINE_EPSILON_10)
2639     {
2640       SnapInternalYTo(mScrollTargetPosition.y);
2641     }
2642     Vector2 currentScrollPosition = GetCurrentScrollPosition();
2643     DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 6 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
2644     mScrollCompletedSignal.Emit(currentScrollPosition);
2645   }
2646 }
2647
2648 Vector2 ScrollView::GetOvershoot(Vector2& position) const
2649 {
2650   Vector3 size = Self().GetCurrentProperty<Vector3>(Actor::Property::SIZE);
2651   Vector2 overshoot;
2652
2653   const RulerDomain rulerDomainX = mRulerX->GetDomain();
2654   const RulerDomain rulerDomainY = mRulerY->GetDomain();
2655
2656   if(mRulerX->IsEnabled() && rulerDomainX.enabled)
2657   {
2658     const float left  = rulerDomainX.min - position.x;
2659     const float right = size.width - rulerDomainX.max - position.x;
2660     if(left < 0)
2661     {
2662       overshoot.x = left;
2663     }
2664     else if(right > 0)
2665     {
2666       overshoot.x = right;
2667     }
2668   }
2669
2670   if(mRulerY->IsEnabled() && rulerDomainY.enabled)
2671   {
2672     const float top    = rulerDomainY.min - position.y;
2673     const float bottom = size.height - rulerDomainY.max - position.y;
2674     if(top < 0)
2675     {
2676       overshoot.y = top;
2677     }
2678     else if(bottom > 0)
2679     {
2680       overshoot.y = bottom;
2681     }
2682   }
2683
2684   return overshoot;
2685 }
2686
2687 bool ScrollView::OnAccessibilityPan(PanGesture gesture)
2688 {
2689   // Keep track of whether this is an AccessibilityPan
2690   mInAccessibilityPan = true;
2691   OnPan(gesture);
2692   mInAccessibilityPan = false;
2693
2694   return true;
2695 }
2696
2697 void ScrollView::ClampPosition(Vector2& position) const
2698 {
2699   ClampState2D clamped;
2700   ClampPosition(position, clamped);
2701 }
2702
2703 void ScrollView::ClampPosition(Vector2& position, ClampState2D& clamped) const
2704 {
2705   Vector3 size = Self().GetCurrentProperty<Vector3>(Actor::Property::SIZE);
2706
2707   position.x = -mRulerX->Clamp(-position.x, size.width, 1.0f, clamped.x);  // NOTE: X & Y rulers think in -ve coordinate system.
2708   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.
2709 }
2710
2711 void ScrollView::WrapPosition(Vector2& position) const
2712 {
2713   if(mWrapMode)
2714   {
2715     const RulerDomain rulerDomainX = mRulerX->GetDomain();
2716     const RulerDomain rulerDomainY = mRulerY->GetDomain();
2717
2718     if(mRulerX->IsEnabled())
2719     {
2720       position.x = -WrapInDomain(-position.x, rulerDomainX.min, rulerDomainX.max);
2721     }
2722
2723     if(mRulerY->IsEnabled())
2724     {
2725       position.y = -WrapInDomain(-position.y, rulerDomainY.min, rulerDomainY.max);
2726     }
2727   }
2728 }
2729
2730 void ScrollView::UpdateMainInternalConstraint()
2731 {
2732   // TODO: Only update the constraints which have changed, rather than remove all and add all again.
2733   // Requires a dali-core ApplyConstraintAt, or a ReplaceConstraint. The former is probably more flexible.
2734   Actor              self = Self();
2735   PanGestureDetector detector(GetPanGestureDetector());
2736
2737   if(mScrollMainInternalPositionConstraint)
2738   {
2739     mScrollMainInternalPositionConstraint.Remove();
2740     mScrollMainInternalDeltaConstraint.Remove();
2741     mScrollMainInternalFinalConstraint.Remove();
2742     mScrollMainInternalRelativeConstraint.Remove();
2743     mScrollMainInternalDomainConstraint.Remove();
2744     mScrollMainInternalPrePositionMaxConstraint.Remove();
2745   }
2746   if(mScrollMainInternalPrePositionConstraint)
2747   {
2748     mScrollMainInternalPrePositionConstraint.Remove();
2749   }
2750
2751   // TODO: It's probably better to use a local displacement value as this will give a displacement when scrolling just commences
2752   // but we need to make sure than the gesture system gives displacement since last frame (60Hz), not displacement since last touch event (90Hz).
2753
2754   // 1. First calculate the pre-position (this is the scroll position if no clamping has taken place)
2755   Vector2 initialPanMask = Vector2(mRulerX->IsEnabled() ? 1.0f : 0.0f, mRulerY->IsEnabled() ? 1.0f : 0.0f);
2756
2757   if(mLockAxis == LockVertical)
2758   {
2759     initialPanMask.y = 0.0f;
2760   }
2761   else if(mLockAxis == LockHorizontal)
2762   {
2763     initialPanMask.x = 0.0f;
2764   }
2765
2766   if(mPanning)
2767   {
2768     mScrollMainInternalPrePositionConstraint = Constraint::New<Vector2>(self,
2769                                                                         Toolkit::ScrollView::Property::SCROLL_PRE_POSITION,
2770                                                                         InternalPrePositionConstraint(mPanStartPosition,
2771                                                                                                       initialPanMask,
2772                                                                                                       mAxisAutoLock,
2773                                                                                                       mAxisAutoLockGradient,
2774                                                                                                       mLockAxis,
2775                                                                                                       mMaxOvershoot,
2776                                                                                                       mRulerX,
2777                                                                                                       mRulerY));
2778     mScrollMainInternalPrePositionConstraint.AddSource(Source(detector, PanGestureDetector::Property::LOCAL_POSITION));
2779     mScrollMainInternalPrePositionConstraint.AddSource(Source(detector, PanGestureDetector::Property::PANNING));
2780     mScrollMainInternalPrePositionConstraint.AddSource(Source(self, Actor::Property::SIZE));
2781     mScrollMainInternalPrePositionConstraint.Apply();
2782   }
2783
2784   // 2. Second calculate the clamped position (actual position)
2785   mScrollMainInternalPositionConstraint = Constraint::New<Vector2>(self,
2786                                                                    Toolkit::ScrollView::Property::SCROLL_POSITION,
2787                                                                    InternalPositionConstraint(mRulerX->GetDomain(),
2788                                                                                               mRulerY->GetDomain(),
2789                                                                                               mWrapMode));
2790   mScrollMainInternalPositionConstraint.AddSource(LocalSource(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION));
2791   mScrollMainInternalPositionConstraint.AddSource(LocalSource(Toolkit::Scrollable::Property::SCROLL_POSITION_MIN));
2792   mScrollMainInternalPositionConstraint.AddSource(LocalSource(Toolkit::Scrollable::Property::SCROLL_POSITION_MAX));
2793   mScrollMainInternalPositionConstraint.AddSource(Source(self, Actor::Property::SIZE));
2794   mScrollMainInternalPositionConstraint.Apply();
2795
2796   mScrollMainInternalDeltaConstraint = Constraint::New<Vector2>(self, Toolkit::ScrollView::Property::SCROLL_POSITION_DELTA, InternalPositionDeltaConstraint);
2797   mScrollMainInternalDeltaConstraint.AddSource(LocalSource(Toolkit::ScrollView::Property::SCROLL_POSITION));
2798   mScrollMainInternalDeltaConstraint.AddSource(LocalSource(Toolkit::ScrollView::Property::SCROLL_DOMAIN_OFFSET));
2799   mScrollMainInternalDeltaConstraint.Apply();
2800
2801   mScrollMainInternalFinalConstraint = Constraint::New<Vector2>(self, Toolkit::ScrollView::Property::SCROLL_FINAL, InternalFinalConstraint(FinalDefaultAlphaFunction, FinalDefaultAlphaFunction));
2802   mScrollMainInternalFinalConstraint.AddSource(LocalSource(Toolkit::ScrollView::Property::SCROLL_POSITION));
2803   mScrollMainInternalFinalConstraint.AddSource(LocalSource(Toolkit::ScrollView::Property::OVERSHOOT_X));
2804   mScrollMainInternalFinalConstraint.AddSource(LocalSource(Toolkit::ScrollView::Property::OVERSHOOT_Y));
2805   mScrollMainInternalFinalConstraint.Apply();
2806
2807   mScrollMainInternalRelativeConstraint = Constraint::New<Vector2>(self, Toolkit::Scrollable::Property::SCROLL_RELATIVE_POSITION, InternalRelativePositionConstraint);
2808   mScrollMainInternalRelativeConstraint.AddSource(LocalSource(Toolkit::ScrollView::Property::SCROLL_POSITION));
2809   mScrollMainInternalRelativeConstraint.AddSource(LocalSource(Toolkit::Scrollable::Property::SCROLL_POSITION_MIN));
2810   mScrollMainInternalRelativeConstraint.AddSource(LocalSource(Toolkit::Scrollable::Property::SCROLL_POSITION_MAX));
2811   mScrollMainInternalRelativeConstraint.AddSource(LocalSource(Actor::Property::SIZE));
2812   mScrollMainInternalRelativeConstraint.Apply();
2813
2814   mScrollMainInternalDomainConstraint = Constraint::New<Vector2>(self, Toolkit::ScrollView::Property::SCROLL_DOMAIN_SIZE, InternalScrollDomainConstraint);
2815   mScrollMainInternalDomainConstraint.AddSource(LocalSource(Toolkit::Scrollable::Property::SCROLL_POSITION_MIN));
2816   mScrollMainInternalDomainConstraint.AddSource(LocalSource(Toolkit::Scrollable::Property::SCROLL_POSITION_MAX));
2817   mScrollMainInternalDomainConstraint.AddSource(LocalSource(Actor::Property::SIZE));
2818   mScrollMainInternalDomainConstraint.Apply();
2819
2820   mScrollMainInternalPrePositionMaxConstraint = Constraint::New<Vector2>(self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_MAX, InternalPrePositionMaxConstraint);
2821   mScrollMainInternalPrePositionMaxConstraint.AddSource(LocalSource(Toolkit::Scrollable::Property::SCROLL_POSITION_MAX));
2822   mScrollMainInternalPrePositionMaxConstraint.AddSource(LocalSource(Actor::Property::SIZE));
2823   mScrollMainInternalPrePositionMaxConstraint.Apply();
2824
2825   // When panning we want to make sure overshoot values are affected by pre position and post position
2826   SetOvershootConstraintsEnabled(!mWrapMode);
2827 }
2828
2829 void ScrollView::SetOvershootConstraintsEnabled(bool enabled)
2830 {
2831   Actor self(Self());
2832   // remove and reset, it may now be in wrong order with the main internal constraints
2833   if(mScrollMainInternalOvershootXConstraint)
2834   {
2835     mScrollMainInternalOvershootXConstraint.Remove();
2836     mScrollMainInternalOvershootXConstraint.Reset();
2837     mScrollMainInternalOvershootYConstraint.Remove();
2838     mScrollMainInternalOvershootYConstraint.Reset();
2839   }
2840   if(enabled)
2841   {
2842     mScrollMainInternalOvershootXConstraint = Constraint::New<float>(self, Toolkit::ScrollView::Property::OVERSHOOT_X, OvershootXConstraint(mMaxOvershoot.x));
2843     mScrollMainInternalOvershootXConstraint.AddSource(LocalSource(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION));
2844     mScrollMainInternalOvershootXConstraint.AddSource(LocalSource(Toolkit::ScrollView::Property::SCROLL_POSITION));
2845     mScrollMainInternalOvershootXConstraint.AddSource(LocalSource(Toolkit::Scrollable::Property::CAN_SCROLL_HORIZONTAL));
2846     mScrollMainInternalOvershootXConstraint.Apply();
2847
2848     mScrollMainInternalOvershootYConstraint = Constraint::New<float>(self, Toolkit::ScrollView::Property::OVERSHOOT_Y, OvershootYConstraint(mMaxOvershoot.y));
2849     mScrollMainInternalOvershootYConstraint.AddSource(LocalSource(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION));
2850     mScrollMainInternalOvershootYConstraint.AddSource(LocalSource(Toolkit::ScrollView::Property::SCROLL_POSITION));
2851     mScrollMainInternalOvershootYConstraint.AddSource(LocalSource(Toolkit::Scrollable::Property::CAN_SCROLL_VERTICAL));
2852     mScrollMainInternalOvershootYConstraint.Apply();
2853   }
2854   else
2855   {
2856     self.SetProperty(Toolkit::ScrollView::Property::OVERSHOOT_X, 0.0f);
2857     self.SetProperty(Toolkit::ScrollView::Property::OVERSHOOT_Y, 0.0f);
2858   }
2859 }
2860
2861 void ScrollView::SetInternalConstraints()
2862 {
2863   // Internal constraints (applied to target ScrollBase Actor itself) /////////
2864   UpdateMainInternalConstraint();
2865
2866   // User definable constraints to apply to all child actors //////////////////
2867   Actor self = Self();
2868
2869   // Apply some default constraints to ScrollView & its bound actors
2870   // Movement + Wrap function
2871
2872   Constraint constraint;
2873
2874   // MoveActor (scrolling)
2875   constraint = Constraint::New<Vector3>(self, Actor::Property::POSITION, MoveActorConstraint);
2876   constraint.AddSource(Source(self, Toolkit::ScrollView::Property::SCROLL_POSITION));
2877   constraint.SetRemoveAction(Constraint::DISCARD);
2878   ApplyConstraintToBoundActors(constraint);
2879
2880   // WrapActor (wrap functionality)
2881   constraint = Constraint::New<Vector3>(self, Actor::Property::POSITION, WrapActorConstraint);
2882   constraint.AddSource(LocalSource(Actor::Property::SCALE));
2883   constraint.AddSource(LocalSource(Actor::Property::ANCHOR_POINT));
2884   constraint.AddSource(LocalSource(Actor::Property::SIZE));
2885   constraint.AddSource(Source(self, Toolkit::Scrollable::Property::SCROLL_POSITION_MIN));
2886   constraint.AddSource(Source(self, Toolkit::Scrollable::Property::SCROLL_POSITION_MAX));
2887   constraint.AddSource(Source(self, Toolkit::ScrollView::Property::WRAP));
2888   constraint.SetRemoveAction(Constraint::DISCARD);
2889   ApplyConstraintToBoundActors(constraint);
2890 }
2891
2892 void ScrollView::SetProperty(BaseObject* object, Property::Index index, const Property::Value& value)
2893 {
2894   Toolkit::ScrollView scrollView = Toolkit::ScrollView::DownCast(Dali::BaseHandle(object));
2895
2896   if(scrollView)
2897   {
2898     ScrollView& scrollViewImpl(GetImpl(scrollView));
2899     switch(index)
2900     {
2901       case Toolkit::ScrollView::Property::WRAP_ENABLED:
2902       {
2903         scrollViewImpl.SetWrapMode(value.Get<bool>());
2904         break;
2905       }
2906       case Toolkit::ScrollView::Property::PANNING_ENABLED:
2907       {
2908         scrollViewImpl.SetScrollSensitive(value.Get<bool>());
2909         break;
2910       }
2911       case Toolkit::ScrollView::Property::AXIS_AUTO_LOCK_ENABLED:
2912       {
2913         scrollViewImpl.SetAxisAutoLock(value.Get<bool>());
2914         break;
2915       }
2916       case Toolkit::ScrollView::Property::WHEEL_SCROLL_DISTANCE_STEP:
2917       {
2918         scrollViewImpl.SetWheelScrollDistanceStep(value.Get<Vector2>());
2919         break;
2920       }
2921       case Toolkit::ScrollView::Property::SCROLL_MODE:
2922       {
2923         const Property::Map* map = value.GetMap();
2924         if(map)
2925         {
2926           scrollViewImpl.SetScrollMode(*map);
2927         }
2928       }
2929     }
2930   }
2931 }
2932
2933 Property::Value ScrollView::GetProperty(BaseObject* object, Property::Index index)
2934 {
2935   Property::Value value;
2936
2937   Toolkit::ScrollView scrollView = Toolkit::ScrollView::DownCast(Dali::BaseHandle(object));
2938
2939   if(scrollView)
2940   {
2941     ScrollView& scrollViewImpl(GetImpl(scrollView));
2942     switch(index)
2943     {
2944       case Toolkit::ScrollView::Property::WRAP_ENABLED:
2945       {
2946         value = scrollViewImpl.GetWrapMode();
2947         break;
2948       }
2949       case Toolkit::ScrollView::Property::PANNING_ENABLED:
2950       {
2951         value = scrollViewImpl.GetScrollSensitive();
2952         break;
2953       }
2954       case Toolkit::ScrollView::Property::AXIS_AUTO_LOCK_ENABLED:
2955       {
2956         value = scrollViewImpl.GetAxisAutoLock();
2957         break;
2958       }
2959       case Toolkit::ScrollView::Property::WHEEL_SCROLL_DISTANCE_STEP:
2960       {
2961         value = scrollViewImpl.GetWheelScrollDistanceStep();
2962         break;
2963       }
2964     }
2965   }
2966
2967   return value;
2968 }
2969
2970 void ScrollView::SetScrollMode(const Property::Map& scrollModeMap)
2971 {
2972   Toolkit::RulerPtr rulerX, rulerY;
2973
2974   // Check the scroll mode in the X axis
2975   bool             xAxisScrollEnabled = true;
2976   Property::Value* valuePtr           = scrollModeMap.Find(Toolkit::ScrollMode::X_AXIS_SCROLL_ENABLED, "xAxisScrollEnabled");
2977   if(valuePtr && valuePtr->GetType() == Property::BOOLEAN)
2978   {
2979     valuePtr->Get(xAxisScrollEnabled);
2980   }
2981
2982   if(!xAxisScrollEnabled)
2983   {
2984     // Default ruler and disabled
2985     rulerX = new Toolkit::DefaultRuler();
2986     rulerX->Disable();
2987   }
2988   else
2989   {
2990     valuePtr                  = scrollModeMap.Find(Toolkit::ScrollMode::X_AXIS_SNAP_TO_INTERVAL, "xAxisSnapToInterval");
2991     float xAxisSnapToInterval = 0.0f;
2992     if(valuePtr && valuePtr->Get(xAxisSnapToInterval))
2993     {
2994       // Fixed ruler and enabled
2995       rulerX = new Toolkit::FixedRuler(xAxisSnapToInterval);
2996     }
2997     else
2998     {
2999       // Default ruler and enabled
3000       rulerX = new Toolkit::DefaultRuler();
3001     }
3002
3003     valuePtr                  = scrollModeMap.Find(Toolkit::ScrollMode::X_AXIS_SCROLL_BOUNDARY, "xAxisScrollBoundary");
3004     float xAxisScrollBoundary = 0.0f;
3005     if(valuePtr && valuePtr->Get(xAxisScrollBoundary))
3006     {
3007       // By default ruler domain is disabled unless set
3008       rulerX->SetDomain(Toolkit::RulerDomain(0, xAxisScrollBoundary, true));
3009     }
3010   }
3011
3012   // Check the scroll mode in the Y axis
3013   bool yAxisScrollEnabled = true;
3014   valuePtr                = scrollModeMap.Find(Toolkit::ScrollMode::Y_AXIS_SCROLL_ENABLED, "yAxisScrollEnabled");
3015   if(valuePtr && valuePtr->GetType() == Property::BOOLEAN)
3016   {
3017     valuePtr->Get(yAxisScrollEnabled);
3018   }
3019
3020   if(!yAxisScrollEnabled)
3021   {
3022     // Default ruler and disabled
3023     rulerY = new Toolkit::DefaultRuler();
3024     rulerY->Disable();
3025   }
3026   else
3027   {
3028     valuePtr                  = scrollModeMap.Find(Toolkit::ScrollMode::Y_AXIS_SNAP_TO_INTERVAL, "yAxisSnapToInterval");
3029     float yAxisSnapToInterval = 0.0f;
3030     if(valuePtr && valuePtr->Get(yAxisSnapToInterval))
3031     {
3032       // Fixed ruler and enabled
3033       rulerY = new Toolkit::FixedRuler(yAxisSnapToInterval);
3034     }
3035     else
3036     {
3037       // Default ruler and enabled
3038       rulerY = new Toolkit::DefaultRuler();
3039     }
3040
3041     valuePtr                  = scrollModeMap.Find(Toolkit::ScrollMode::Y_AXIS_SCROLL_BOUNDARY, "yAxisScrollBoundary");
3042     float yAxisScrollBoundary = 0.0f;
3043     if(valuePtr && valuePtr->Get(yAxisScrollBoundary))
3044     {
3045       // By default ruler domain is disabled unless set
3046       rulerY->SetDomain(Toolkit::RulerDomain(0, yAxisScrollBoundary, true));
3047     }
3048   }
3049
3050   SetRulerX(rulerX);
3051   SetRulerY(rulerY);
3052 }
3053
3054 } // namespace Internal
3055
3056 } // namespace Toolkit
3057
3058 } // namespace Dali