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