Merge "[AT-SPI] fix a crash on scroll-view" into devel/master
[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/internal/controls/scrollable/scroll-view/scroll-view-impl-property-handler.h>
40 #include <dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-mode.h>
41 #include <dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-constraints.h>
42 #include <dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view.h>
43
44 //#define ENABLED_SCROLL_STATE_LOGGING
45
46 #ifdef ENABLED_SCROLL_STATE_LOGGING
47 #define DALI_LOG_SCROLL_STATE(format, ...) Dali::Integration::Log::LogMessageWithFunctionLine(Dali::Integration::Log::DebugInfo, "%s:%d " format "\n", __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
48 #else
49 #define DALI_LOG_SCROLL_STATE(format, ...)
50 #endif
51
52 // TODO: Change to two class system:
53 // 1. DraggableActor (is an actor which can be dragged anywhere, can be set to range using the ruler)
54 // 2. ScrollView (contains a draggable actor that can a) be dragged in the negative X, and Y domain, b) has a hitArea for touches)
55 // TODO: external components (page and status overlays).
56 // TODO: Orientation.
57 // TODO: upgrade Vector2/3 to support returning Unit vectors, normals, & cross product (dot product is already provided)
58
59 namespace
60 {
61 using namespace Dali;
62
63 constexpr float DEFAULT_SLOW_SNAP_ANIMATION_DURATION(0.5f);  ///< Default Drag-Release animation time.
64 constexpr float DEFAULT_FAST_SNAP_ANIMATION_DURATION(0.25f); ///< Default Drag-Flick animation time.
65 constexpr float DEFAULT_SNAP_OVERSHOOT_DURATION(0.5f);       ///< Default Overshoot snapping animation time.
66 constexpr float DEFAULT_MAX_OVERSHOOT(100.0f);               ///< Default maximum allowed overshoot in pixels
67
68 constexpr float DEFAULT_AXIS_AUTO_LOCK_GRADIENT(0.36f); ///< Default Axis-AutoLock gradient threshold. default is 0.36:1 (20 degrees)
69 constexpr float DEFAULT_FRICTION_COEFFICIENT(1.0f);     ///< Default Friction Co-efficient. (in stage diagonals per second)
70 constexpr float DEFAULT_FLICK_SPEED_COEFFICIENT(1.0f);  ///< Default Flick speed coefficient (multiples input touch velocity)
71 constexpr float DEFAULT_MAX_FLICK_SPEED(3.0f);          ///< Default Maximum flick speed. (in stage diagonals per second)
72
73 constexpr Dali::Vector2 DEFAULT_MIN_FLICK_DISTANCE(30.0f, 30.0f);  ///< minimum distance for pan before flick allowed
74 constexpr float         DEFAULT_MIN_FLICK_SPEED_THRESHOLD(500.0f); ///< Minimum pan speed required for flick in pixels/s
75
76 constexpr float FREE_FLICK_SPEED_THRESHOLD      = 200.0f; ///< Free-Flick threshold in pixels/ms
77 constexpr float AUTOLOCK_AXIS_MINIMUM_DISTANCE2 = 100.0f; ///< Auto-lock axis after minimum distance squared.
78 constexpr float FLICK_ORTHO_ANGLE_RANGE         = 75.0f;  ///< degrees. (if >45, then supports diagonal flicking)
79
80 constexpr Dali::Vector2 DEFAULT_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION(0.17f, 0.1f); ///< The step of horizontal scroll distance in the proportion of stage size for each wheel event received.
81
82 constexpr unsigned long MINIMUM_TIME_BETWEEN_DOWN_AND_UP_FOR_RESET(150u);
83 constexpr float         TOUCH_DOWN_TIMER_INTERVAL = 100.0f;
84 constexpr float         DEFAULT_SCROLL_UPDATE_DISTANCE(30.0f); ///< Default distance to travel in pixels for scroll update signal
85
86 const std::string INTERNAL_MAX_POSITION_PROPERTY_NAME("internalMaxPosition");
87
88 // Helpers ////////////////////////////////////////////////////////////////////////////////////////
89
90 /**
91  * Find the vector (distance) from (a) to (b)
92  * in domain (start) to (end)
93  * (\ / start)               (\ / end)
94  *   |-a                 b<----|
95  *
96  * @note assumes both (a) and (b) are already with the domain
97  * (start) to (end)
98  *
99  * @param[in] a the current point
100  * @param[in] b the target point
101  * @param[in] start the start of the domain
102  * @param[in] end the end of the domain
103  * @param[in] bias whether to only take the right direction or the left direction,
104  * or the shortest direction.
105  * @return the shortest direction and distance
106  */
107 float VectorInDomain(float a, float b, float start, float end, Dali::Toolkit::DirectionBias bias)
108 {
109   if(bias == Dali::Toolkit::DIRECTION_BIAS_NONE)
110   {
111     return Dali::ShortestDistanceInDomain(a, b, start, end);
112   }
113   //  (a-start + end-b)
114   float size = end - start;
115   float vect = b - a;
116
117   if(vect > 0)
118   {
119     // +ve vector
120     if(bias == Dali::Toolkit::DIRECTION_BIAS_RIGHT) // going right, take the vector.
121     {
122       return vect;
123     }
124     else
125     {
126       float aRight = a + size;
127       return b - aRight;
128     }
129   }
130   else
131   {
132     // -ve vector
133     if(bias == Dali::Toolkit::DIRECTION_BIAS_LEFT) // going left, take the vector.
134     {
135       return vect;
136     }
137     else
138     {
139       float aLeft = a - size;
140       return b - aLeft;
141     }
142   }
143 }
144
145 /**
146  * Returns the position of the anchor within actor
147  *
148  * @param actor The Actor
149  * @param anchor The Anchor point of interest.
150  * @return The position of the Anchor
151  */
152 Dali::Vector3 GetPositionOfAnchor(Dali::Actor& actor, const Dali::Vector3& anchor)
153 {
154   Dali::Vector3 childPosition = actor.GetCurrentProperty<Dali::Vector3>(Dali::Actor::Property::POSITION);
155   Dali::Vector3 childAnchor   = -actor.GetCurrentProperty<Dali::Vector3>(Dali::Actor::Property::ANCHOR_POINT) + anchor;
156   Dali::Vector3 childSize     = actor.GetCurrentProperty<Dali::Vector3>(Dali::Actor::Property::SIZE);
157
158   return childPosition + childAnchor * childSize;
159 }
160
161 /**
162  * Returns the closest actor to the given position
163  * @param[in] actor The scrollview actor
164  * @param[in] internalActor The internal actor (to ignore)
165  * @param[in] position The given position
166  * @param[in] dirX Direction to search in
167  * @param[in] dirY Direction to search in
168  * @param[in] dirZ Direction to search in
169  * @return the closest child actor
170  */
171 using FindDirection = Dali::Toolkit::Internal::ScrollView::FindDirection;
172
173 Actor FindClosestActorToPosition(
174   CustomActor actor, Actor internalActor, const Vector3& position, FindDirection dirX, FindDirection dirY, FindDirection dirZ)
175 {
176   Actor   closestChild;
177   float   closestDistance2 = 0.0f;
178   Vector3 actualPosition   = position;
179
180   unsigned int numChildren = actor.GetChildCount();
181
182   for(unsigned int i = 0; i < numChildren; ++i)
183   {
184     Actor child = actor.GetChildAt(i);
185
186     if(internalActor == child) // ignore internal actor.
187     {
188       continue;
189     }
190
191     Vector3 childPosition = GetPositionOfAnchor(child, AnchorPoint::CENTER);
192
193     Vector3 delta = childPosition - actualPosition;
194
195     // X-axis checking (only find Actors to the [dirX] of actualPosition)
196     if(dirX > FindDirection::All) // != All,None
197     {
198       FindDirection deltaH = delta.x > 0 ? FindDirection::Right : FindDirection::Left;
199       if(dirX != deltaH)
200       {
201         continue;
202       }
203     }
204
205     // Y-axis checking (only find Actors to the [dirY] of actualPosition)
206     if(dirY > FindDirection::All) // != All,None
207     {
208       FindDirection deltaV = delta.y > 0 ? FindDirection::Down : FindDirection::Up;
209       if(dirY != deltaV)
210       {
211         continue;
212       }
213     }
214
215     // Z-axis checking (only find Actors to the [dirZ] of actualPosition)
216     if(dirZ > FindDirection::All) // != All,None
217     {
218       FindDirection deltaV = delta.y > 0 ? FindDirection::In : FindDirection::Out;
219       if(dirZ != deltaV)
220       {
221         continue;
222       }
223     }
224
225     // compare child to closest child in terms of distance.
226     float distance2 = 0.0f;
227
228     // distance2 = the Square of the relevant dimensions of delta
229     if(dirX != FindDirection::None)
230     {
231       distance2 += delta.x * delta.x;
232     }
233
234     if(dirY != FindDirection::None)
235     {
236       distance2 += delta.y * delta.y;
237     }
238
239     if(dirZ != FindDirection::None)
240     {
241       distance2 += delta.z * delta.z;
242     }
243
244     if(closestChild) // Next time.
245     {
246       if(distance2 < closestDistance2)
247       {
248         closestChild     = child;
249         closestDistance2 = distance2;
250       }
251     }
252     else // First time.
253     {
254       closestChild     = child;
255       closestDistance2 = distance2;
256     }
257   }
258
259   return closestChild;
260 }
261
262 // AlphaFunctions /////////////////////////////////////////////////////////////////////////////////
263
264 /**
265  * ConstantDecelerationAlphaFunction
266  * Newtoninan distance for constant deceleration
267  * v = 1 - t, s = t - 1/2 t^2
268  * when t = 0, s = 0.0 (min distance)
269  * when t = 1, s = 0.5 (max distance)
270  * progress = s / (max-min) = 2t - t^2
271  *
272  * @param[in] offset The input progress
273  * @return The output progress
274  */
275 float ConstantDecelerationAlphaFunction(float progress)
276 {
277   return progress * 2.0f - progress * progress;
278 }
279
280 /**
281  * Clamp a position
282  * @param[in] size The size to clamp to
283  * @param[in] rulerX The horizontal ruler
284  * @param[in] rulerY The vertical ruler
285  * @param[in,out] position The position to clamp
286  * @param[out] clamped the clamped state
287  */
288 void ClampPosition(const Vector3& size, Dali::Toolkit::RulerPtr rulerX, Dali::Toolkit::RulerPtr rulerY, Vector2& position, Dali::Toolkit::ClampState2D& clamped)
289 {
290   position.x = -rulerX->Clamp(-position.x, size.width, 1.0f, clamped.x);  // NOTE: X & Y rulers think in -ve coordinate system.
291   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.
292 }
293
294 /**
295  * TODO: In situations where axes are different (X snap, Y free)
296  * Each axis should really have their own independent animation (time and equation)
297  * Consider, X axis snapping to nearest grid point (EaseOut over fixed time)
298  * Consider, Y axis simulating physics to arrive at a point (Physics equation over variable time)
299  * Currently, the axes have been split however, they both use the same EaseOut equation.
300  *
301  * @param[in] scrollView The main scrollview
302  * @param[in] rulerX The X ruler
303  * @param[in] rulerY The Y ruler
304  * @param[in] lockAxis Which axis (if any) is locked.
305  * @param[in] velocity Current pan velocity
306  * @param[in] maxOvershoot Maximum overshoot
307  * @param[in] inAcessibilityPan True if we are currently panning with accessibility
308  * @param[out] positionSnap The target position of snap animation
309  * @param[out] positionDuration The duration of the snap animation
310  * @param[out] alphaFunction The snap animation alpha function
311  * @param[out] isFlick if we are flicking or not
312  * @param[out] isFreeFlick if we are free flicking or not
313  */
314 void SnapWithVelocity(
315   Dali::Toolkit::Internal::ScrollView&          scrollView,
316   Dali::Toolkit::RulerPtr                       rulerX,
317   Dali::Toolkit::RulerPtr                       rulerY,
318   Dali::Toolkit::Internal::ScrollView::LockAxis lockAxis,
319   Vector2                                       velocity,
320   Vector2                                       maxOvershoot,
321   Vector2&                                      positionSnap,
322   Vector2&                                      positionDuration,
323   AlphaFunction&                                alphaFunction,
324   bool                                          inAccessibilityPan,
325   bool&                                         isFlick,
326   bool&                                         isFreeFlick)
327 {
328   // Animator takes over now, touches are assumed not to interfere.
329   // And if touches do interfere, then we'll stop animation, update PrePosition
330   // to current mScroll's properties, and then resume.
331   // Note: For Flicking this may work a bit different...
332
333   float         angle      = atan2(velocity.y, velocity.x);
334   float         speed2     = velocity.LengthSquared();
335   float         biasX      = 0.5f;
336   float         biasY      = 0.5f;
337   FindDirection horizontal = FindDirection::None;
338   FindDirection vertical   = FindDirection::None;
339
340   using LockAxis = Dali::Toolkit::Internal::ScrollView::LockAxis;
341
342   // orthoAngleRange = Angle tolerance within the Exact N,E,S,W direction
343   // that will be accepted as a general N,E,S,W flick direction.
344
345   const float orthoAngleRange      = FLICK_ORTHO_ANGLE_RANGE * M_PI / 180.0f;
346   const float flickSpeedThreshold2 = scrollView.GetMinimumSpeedForFlick() * scrollView.GetMinimumSpeedForFlick();
347
348   // Flick logic X Axis
349
350   if(rulerX->IsEnabled() && lockAxis != LockAxis::LockHorizontal)
351   {
352     horizontal = FindDirection::All;
353
354     if(speed2 > flickSpeedThreshold2 || // exceeds flick threshold
355        inAccessibilityPan)              // With AccessibilityPan its easier to move between snap positions
356     {
357       if((angle >= -orthoAngleRange) && (angle < orthoAngleRange)) // Swiping East
358       {
359         biasX = 0.0f, horizontal = FindDirection::Left;
360
361         // This guards against an error where no movement occurs, due to the flick finishing
362         // before the update-thread has advanced mScrollPostPosition past the the previous snap point.
363         positionSnap.x += 1.0f;
364       }
365       else if((angle >= M_PI - orthoAngleRange) || (angle < -M_PI + orthoAngleRange)) // Swiping West
366       {
367         biasX = 1.0f, horizontal = FindDirection::Right;
368
369         // This guards against an error where no movement occurs, due to the flick finishing
370         // before the update-thread has advanced mScrollPostPosition past the the previous snap point.
371         positionSnap.x -= 1.0f;
372       }
373     }
374   }
375
376   // Flick logic Y Axis
377
378   if(rulerY->IsEnabled() && lockAxis != LockAxis::LockVertical)
379   {
380     vertical = FindDirection::All;
381
382     if(speed2 > flickSpeedThreshold2 || // exceeds flick threshold
383        inAccessibilityPan)              // With AccessibilityPan its easier to move between snap positions
384     {
385       if((angle >= M_PI_2 - orthoAngleRange) && (angle < M_PI_2 + orthoAngleRange)) // Swiping South
386       {
387         biasY = 0.0f, vertical = FindDirection::Up;
388       }
389       else if((angle >= -M_PI_2 - orthoAngleRange) && (angle < -M_PI_2 + orthoAngleRange)) // Swiping North
390       {
391         biasY = 1.0f, vertical = FindDirection::Down;
392       }
393     }
394   }
395
396   // isFlick: Whether this gesture is a flick or not.
397   isFlick = (horizontal != FindDirection::All || vertical != FindDirection::All);
398   // isFreeFlick: Whether this gesture is a flick under free panning criteria.
399   isFreeFlick = velocity.LengthSquared() > (FREE_FLICK_SPEED_THRESHOLD * FREE_FLICK_SPEED_THRESHOLD);
400
401   if(isFlick || isFreeFlick)
402   {
403     positionDuration = Vector2::ONE * scrollView.GetScrollFlickDuration();
404     alphaFunction    = scrollView.GetScrollFlickAlphaFunction();
405   }
406
407   // Calculate next positionSnap ////////////////////////////////////////////////////////////
408
409   if(scrollView.GetActorAutoSnap())
410   {
411     Vector3 size = scrollView.Self().GetCurrentProperty<Vector3>(Actor::Property::SIZE);
412
413     Actor child = scrollView.FindClosestActorToPosition(Vector3(size.width * 0.5f, size.height * 0.5f, 0.0f), horizontal, vertical);
414
415     if(!child && isFlick)
416     {
417       // If we conducted a direction limited search and found no actor, then just snap to the closest actor.
418       child = scrollView.FindClosestActorToPosition(Vector3(size.width * 0.5f, size.height * 0.5f, 0.0f));
419     }
420
421     if(child)
422     {
423       Vector2 position = scrollView.Self().GetCurrentProperty<Vector2>(Toolkit::ScrollView::Property::SCROLL_POSITION);
424
425       // Get center-point of the Actor.
426       Vector3 childPosition = GetPositionOfAnchor(child, AnchorPoint::CENTER);
427
428       if(rulerX->IsEnabled())
429       {
430         positionSnap.x = position.x - childPosition.x + size.width * 0.5f;
431       }
432       if(rulerY->IsEnabled())
433       {
434         positionSnap.y = position.y - childPosition.y + size.height * 0.5f;
435       }
436     }
437   }
438
439   Vector2 startPosition = positionSnap;
440   positionSnap.x        = -rulerX->Snap(-positionSnap.x, biasX); // NOTE: X & Y rulers think in -ve coordinate system.
441   positionSnap.y        = -rulerY->Snap(-positionSnap.y, biasY); // That is scrolling RIGHT (e.g. 100.0, 0.0) means moving LEFT.
442
443   Dali::Toolkit::ClampState2D clamped;
444   Vector3                     size = scrollView.Self().GetCurrentProperty<Vector3>(Actor::Property::SIZE);
445   Vector2                     clampDelta(Vector2::ZERO);
446   ClampPosition(size, rulerX, rulerY, positionSnap, clamped);
447
448   if((rulerX->GetType() == Dali::Toolkit::Ruler::FREE || rulerY->GetType() == Dali::Toolkit::Ruler::FREE) &&
449      isFreeFlick && !scrollView.GetActorAutoSnap())
450   {
451     // Calculate target position based on velocity of flick.
452
453     // a = Deceleration (Set to diagonal stage length * friction coefficient)
454     // u = Initial Velocity (Flick velocity)
455     // v = 0 (Final Velocity)
456     // t = Time (Velocity / Deceleration)
457     Vector2 stageSize   = Stage::GetCurrent().GetSize();
458     float   stageLength = Vector3(stageSize.x, stageSize.y, 0.0f).Length();
459     float   a           = (stageLength * scrollView.GetFrictionCoefficient());
460     Vector3 u           = Vector3(velocity.x, velocity.y, 0.0f) * scrollView.GetFlickSpeedCoefficient();
461     float   speed       = u.Length();
462     u /= speed;
463
464     // TODO: Change this to a decay function. (faster you flick, the slower it should be)
465     speed = std::min(speed, stageLength * scrollView.GetMaxFlickSpeed());
466     u *= speed;
467     alphaFunction = ConstantDecelerationAlphaFunction;
468
469     float t = speed / a;
470
471     if(rulerX->IsEnabled() && rulerX->GetType() == Dali::Toolkit::Ruler::FREE)
472     {
473       positionSnap.x += t * u.x * 0.5f;
474     }
475
476     if(rulerY->IsEnabled() && rulerY->GetType() == Dali::Toolkit::Ruler::FREE)
477     {
478       positionSnap.y += t * u.y * 0.5f;
479     }
480
481     clampDelta = positionSnap;
482     ClampPosition(size, rulerX, rulerY, positionSnap, clamped);
483
484     if((positionSnap - startPosition).LengthSquared() > Math::MACHINE_EPSILON_0)
485     {
486       clampDelta -= positionSnap;
487       clampDelta.x = clampDelta.x > 0.0f ? std::min(clampDelta.x, maxOvershoot.x) : std::max(clampDelta.x, -maxOvershoot.x);
488       clampDelta.y = clampDelta.y > 0.0f ? std::min(clampDelta.y, maxOvershoot.y) : std::max(clampDelta.y, -maxOvershoot.y);
489     }
490     else
491     {
492       clampDelta = Vector2::ZERO;
493     }
494
495     // If Axis is Free and has velocity, then calculate time taken
496     // to reach target based on velocity in axis.
497     if(rulerX->IsEnabled() && rulerX->GetType() == Dali::Toolkit::Ruler::FREE)
498     {
499       float deltaX = fabsf(startPosition.x - positionSnap.x);
500
501       if(fabsf(u.x) > Math::MACHINE_EPSILON_1)
502       {
503         positionDuration.x = fabsf(deltaX / u.x);
504       }
505       else
506       {
507         positionDuration.x = 0;
508       }
509     }
510
511     if(rulerY->IsEnabled() && rulerY->GetType() == Dali::Toolkit::Ruler::FREE)
512     {
513       float deltaY = fabsf(startPosition.y - positionSnap.y);
514
515       if(fabsf(u.y) > Math::MACHINE_EPSILON_1)
516       {
517         positionDuration.y = fabsf(deltaY / u.y);
518       }
519       else
520       {
521         positionDuration.y = 0;
522       }
523     }
524   }
525
526   if(scrollView.IsOvershootEnabled())
527   {
528     // Scroll to the end of the overshoot only when overshoot is enabled.
529     positionSnap += clampDelta;
530   }
531 }
532
533 } // unnamed namespace
534
535 namespace Dali
536 {
537 namespace Toolkit
538 {
539 namespace Internal
540 {
541 namespace
542 {
543 BaseHandle Create()
544 {
545   return Toolkit::ScrollView::New();
546 }
547
548 // Setup properties, signals and actions using the type-registry.
549 DALI_TYPE_REGISTRATION_BEGIN(Toolkit::ScrollView, Toolkit::Scrollable, Create)
550
551 DALI_PROPERTY_REGISTRATION(Toolkit, ScrollView, "wrapEnabled", BOOLEAN, WRAP_ENABLED)
552 DALI_PROPERTY_REGISTRATION(Toolkit, ScrollView, "panningEnabled", BOOLEAN, PANNING_ENABLED)
553 DALI_PROPERTY_REGISTRATION(Toolkit, ScrollView, "axisAutoLockEnabled", BOOLEAN, AXIS_AUTO_LOCK_ENABLED)
554 DALI_PROPERTY_REGISTRATION(Toolkit, ScrollView, "wheelScrollDistanceStep", VECTOR2, WHEEL_SCROLL_DISTANCE_STEP)
555 DALI_PROPERTY_REGISTRATION(Toolkit, ScrollView, "scrollMode", MAP, SCROLL_MODE)
556
557 DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ScrollView, "scrollPosition", VECTOR2, SCROLL_POSITION)
558 DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ScrollView, "scrollPrePosition", VECTOR2, SCROLL_PRE_POSITION)
559 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION(Toolkit, ScrollView, "scrollPrePositionX", SCROLL_PRE_POSITION_X, SCROLL_PRE_POSITION, 0)
560 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION(Toolkit, ScrollView, "scrollPrePositionY", SCROLL_PRE_POSITION_Y, SCROLL_PRE_POSITION, 1)
561 DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ScrollView, "scrollPrePositionMax", VECTOR2, SCROLL_PRE_POSITION_MAX)
562 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION(Toolkit, ScrollView, "scrollPrePositionMaxX", SCROLL_PRE_POSITION_MAX_X, SCROLL_PRE_POSITION_MAX, 0)
563 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION(Toolkit, ScrollView, "scrollPrePositionMaxY", SCROLL_PRE_POSITION_MAX_Y, SCROLL_PRE_POSITION_MAX, 1)
564 DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ScrollView, "overshootX", FLOAT, OVERSHOOT_X)
565 DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ScrollView, "overshootY", FLOAT, OVERSHOOT_Y)
566 DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ScrollView, "scrollFinal", VECTOR2, SCROLL_FINAL)
567 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION(Toolkit, ScrollView, "scrollFinalX", SCROLL_FINAL_X, SCROLL_FINAL, 0)
568 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION(Toolkit, ScrollView, "scrollFinalY", SCROLL_FINAL_Y, SCROLL_FINAL, 1)
569 DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ScrollView, "wrap", BOOLEAN, WRAP)
570 DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ScrollView, "panning", BOOLEAN, PANNING)
571 DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ScrollView, "scrolling", BOOLEAN, SCROLLING)
572 DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ScrollView, "scrollDomainSize", VECTOR2, SCROLL_DOMAIN_SIZE)
573 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION(Toolkit, ScrollView, "scrollDomainSizeX", SCROLL_DOMAIN_SIZE_X, SCROLL_DOMAIN_SIZE, 0)
574 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION(Toolkit, ScrollView, "scrollDomainSizeY", SCROLL_DOMAIN_SIZE_Y, SCROLL_DOMAIN_SIZE, 1)
575 DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ScrollView, "scrollDomainOffset", VECTOR2, SCROLL_DOMAIN_OFFSET)
576 DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ScrollView, "scrollPositionDelta", VECTOR2, SCROLL_POSITION_DELTA)
577 DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ScrollView, "startPagePosition", VECTOR3, START_PAGE_POSITION)
578
579 DALI_SIGNAL_REGISTRATION(Toolkit, ScrollView, "valueChanged", SIGNAL_SNAP_STARTED)
580
581 DALI_TYPE_REGISTRATION_END()
582
583 } // namespace
584
585 ///////////////////////////////////////////////////////////////////////////////////////////////////
586 // ScrollView
587 ///////////////////////////////////////////////////////////////////////////////////////////////////
588
589 Dali::Toolkit::ScrollView ScrollView::New()
590 {
591   // Create the implementation
592   ScrollViewPtr scrollView(new ScrollView());
593
594   // Pass ownership to CustomActor via derived handle
595   Dali::Toolkit::ScrollView handle(*scrollView);
596
597   // Second-phase init of the implementation
598   // This can only be done after the CustomActor connection has been made...
599   scrollView->Initialize();
600
601   return handle;
602 }
603
604 ScrollView::ScrollView()
605 : ScrollBase(ControlBehaviour(DISABLE_STYLE_CHANGE_SIGNALS)), // Enable size negotiation
606   mTouchDownTime(0u),
607   mGestureStackDepth(0),
608   mScrollStateFlags(0),
609   mLockAxis(LockPossible),
610   mScrollUpdateDistance(DEFAULT_SCROLL_UPDATE_DISTANCE),
611   mMaxOvershoot(DEFAULT_MAX_OVERSHOOT, DEFAULT_MAX_OVERSHOOT),
612   mUserMaxOvershoot(DEFAULT_MAX_OVERSHOOT, DEFAULT_MAX_OVERSHOOT),
613   mSnapOvershootDuration(DEFAULT_SNAP_OVERSHOOT_DURATION),
614   mSnapOvershootAlphaFunction(AlphaFunction::EASE_OUT),
615   mSnapDuration(DEFAULT_SLOW_SNAP_ANIMATION_DURATION),
616   mSnapAlphaFunction(AlphaFunction::EASE_OUT),
617   mMinFlickDistance(DEFAULT_MIN_FLICK_DISTANCE),
618   mFlickSpeedThreshold(DEFAULT_MIN_FLICK_SPEED_THRESHOLD),
619   mFlickDuration(DEFAULT_FAST_SNAP_ANIMATION_DURATION),
620   mFlickAlphaFunction(AlphaFunction::EASE_OUT),
621   mAxisAutoLockGradient(DEFAULT_AXIS_AUTO_LOCK_GRADIENT),
622   mFrictionCoefficient(DEFAULT_FRICTION_COEFFICIENT),
623   mFlickSpeedCoefficient(DEFAULT_FLICK_SPEED_COEFFICIENT),
624   mMaxFlickSpeed(DEFAULT_MAX_FLICK_SPEED),
625   mWheelScrollDistanceStep(Vector2::ZERO),
626   mInAccessibilityPan(false),
627   mScrolling(false),
628   mScrollInterrupted(false),
629   mPanning(false),
630   mSensitive(true),
631   mTouchDownTimeoutReached(false),
632   mActorAutoSnapEnabled(false),
633   mAutoResizeContainerEnabled(false),
634   mWrapMode(false),
635   mAxisAutoLock(false),
636   mAlterChild(false),
637   mDefaultMaxOvershoot(true),
638   mCanScrollHorizontal(true),
639   mCanScrollVertical(true),
640   mTransientScrollBar(true)
641 {
642 }
643
644 void ScrollView::OnInitialize()
645 {
646   Actor self = Self();
647
648   // Internal Actor, used to hide actors from enumerations.
649   // Also actors added to Internal actor appear as overlays e.g. ScrollBar components.
650   mInternalActor = Actor::New();
651   self.Add(mInternalActor);
652
653   mInternalActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
654   mInternalActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
655   mInternalActor.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
656
657   mAlterChild = true;
658
659   mScrollPostPosition = mScrollPrePosition = Vector2::ZERO;
660
661   mWheelScrollDistanceStep = Stage::GetCurrent().GetSize() * DEFAULT_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION;
662
663   mGestureStackDepth = 0;
664
665   self.TouchedSignal().Connect(this, &ScrollView::OnTouch);
666   EnableGestureDetection(GestureType::Value(GestureType::PAN));
667
668   // By default we'll allow the user to freely drag the scroll view,
669   // while disabling the other rulers.
670   RulerPtr ruler = new DefaultRuler();
671   mRulerX        = ruler;
672   mRulerY        = ruler;
673
674   self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_VERTICAL, mCanScrollVertical);
675   self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_HORIZONTAL, mCanScrollHorizontal);
676
677   ScrollViewPropertyHandler::UpdatePropertyDomain(*this);
678   mConstraints.SetInternalConstraints(*this);
679
680   // Connect wheel event
681   self.WheelEventSignal().Connect(this, &ScrollView::OnWheelEvent);
682
683   self.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, Dali::Accessibility::Role::SCROLL_PANE);
684 }
685
686 DevelControl::ControlAccessible* ScrollView::CreateAccessibleObject()
687 {
688   return new ScrollViewAccessible(Self());
689 }
690
691 void ScrollView::OnSceneConnection(int depth)
692 {
693   DALI_LOG_SCROLL_STATE("[0x%X]", this);
694
695   if(mSensitive)
696   {
697     SetScrollSensitive(false);
698     SetScrollSensitive(true);
699   }
700
701   if(IsOvershootEnabled())
702   {
703     // try and make sure property notifications are set
704     EnableScrollOvershoot(true);
705   }
706
707   ScrollBase::OnSceneConnection(depth);
708 }
709
710 void ScrollView::OnSceneDisconnection()
711 {
712   DALI_LOG_SCROLL_STATE("[0x%X]", this);
713
714   StopAnimation();
715
716   ScrollBase::OnSceneDisconnection();
717 }
718
719 ScrollView::~ScrollView()
720 {
721   DALI_LOG_SCROLL_STATE("[0x%X]", this);
722 }
723
724 void ScrollView::ApplyEffect(Toolkit::ScrollViewEffect effect)
725 {
726   Dali::Toolkit::ScrollView self = Dali::Toolkit::ScrollView::DownCast(Self());
727
728   // Assertion check to ensure effect doesn't already exist in this scrollview
729   bool effectAlreadyExistsInScrollView(false);
730   for(ScrollViewEffectIter iter = mEffects.begin(); iter != mEffects.end(); ++iter)
731   {
732     if(*iter == effect)
733     {
734       effectAlreadyExistsInScrollView = true;
735       break;
736     }
737   }
738
739   DALI_ASSERT_ALWAYS(!effectAlreadyExistsInScrollView);
740
741   // add effect to effects list
742   mEffects.push_back(effect);
743
744   // invoke Attachment request to ScrollView first
745   GetImpl(effect).Attach(self);
746 }
747
748 void ScrollView::RemoveEffect(Toolkit::ScrollViewEffect effect)
749 {
750   Dali::Toolkit::ScrollView self = Dali::Toolkit::ScrollView::DownCast(Self());
751
752   // remove effect from effects list
753   bool effectExistedInScrollView(false);
754   for(ScrollViewEffectIter iter = mEffects.begin(); iter != mEffects.end(); ++iter)
755   {
756     if(*iter == effect)
757     {
758       mEffects.erase(iter);
759       effectExistedInScrollView = true;
760       break;
761     }
762   }
763
764   // Assertion check to ensure effect existed.
765   DALI_ASSERT_ALWAYS(effectExistedInScrollView);
766
767   // invoke Detachment request to ScrollView last
768   GetImpl(effect).Detach(self);
769 }
770
771 void ScrollView::RemoveAllEffects()
772 {
773   Dali::Toolkit::ScrollView self = Dali::Toolkit::ScrollView::DownCast(Self());
774
775   for(ScrollViewEffectIter effectIter = mEffects.begin(); effectIter != mEffects.end(); ++effectIter)
776   {
777     Toolkit::ScrollViewEffect effect = *effectIter;
778
779     // invoke Detachment request to ScrollView last
780     GetImpl(effect).Detach(self);
781   }
782
783   mEffects.clear();
784 }
785
786 void ScrollView::ApplyConstraintToChildren(Constraint constraint)
787 {
788   ApplyConstraintToBoundActors(constraint);
789 }
790
791 void ScrollView::RemoveConstraintsFromChildren()
792 {
793   RemoveConstraintsFromBoundActors();
794 }
795
796 void ScrollView::SetRulerX(RulerPtr ruler)
797 {
798   mRulerX = ruler;
799
800   ScrollViewPropertyHandler::UpdatePropertyDomain(*this);
801   mConstraints.UpdateMainInternalConstraint(*this);
802 }
803
804 void ScrollView::SetRulerY(RulerPtr ruler)
805 {
806   mRulerY = ruler;
807
808   ScrollViewPropertyHandler::UpdatePropertyDomain(*this);
809   mConstraints.UpdateMainInternalConstraint(*this);
810 }
811
812 void ScrollView::SetScrollSensitive(bool sensitive)
813 {
814   Actor              self = Self();
815   PanGestureDetector panGesture(GetPanGestureDetector());
816
817   DALI_LOG_SCROLL_STATE("[0x%X] sensitive: before:[%d] setting[%d]", this, int(mSensitive), int(sensitive));
818
819   if((!mSensitive) && (sensitive))
820   {
821     mSensitive = sensitive;
822     panGesture.Attach(self);
823   }
824   else if((mSensitive) && (!sensitive))
825   {
826     DALI_LOG_SCROLL_STATE("[0x%X] BEFORE: panning:[%d]", this, int(mPanning));
827
828     // while the scroll view is panning, the state needs to be reset.
829     if(mPanning)
830     {
831       PanGesture cancelGesture = DevelPanGesture::New(GestureState::CANCELLED);
832       OnPan(cancelGesture);
833     }
834
835     panGesture.Detach(self);
836     mSensitive = sensitive;
837
838     mGestureStackDepth = 0;
839     DALI_LOG_SCROLL_STATE("[0x%X] AFTER: panning:[%d]", this, int(mPanning));
840   }
841 }
842
843 void ScrollView::SetMaxOvershoot(float overshootX, float overshootY)
844 {
845   mMaxOvershoot.x      = overshootX;
846   mMaxOvershoot.y      = overshootY;
847   mUserMaxOvershoot    = mMaxOvershoot;
848   mDefaultMaxOvershoot = false;
849   mConstraints.UpdateMainInternalConstraint(*this);
850 }
851
852 bool ScrollView::GetActorAutoSnap()
853 {
854   return mActorAutoSnapEnabled;
855 }
856
857 void ScrollView::SetAutoResize(bool enable)
858 {
859   mAutoResizeContainerEnabled = enable;
860   // TODO: This needs a lot of issues to be addressed before working.
861 }
862
863 void ScrollView::SetWrapMode(bool enable)
864 {
865   mWrapMode = enable;
866   Self().SetProperty(Toolkit::ScrollView::Property::WRAP, enable);
867 }
868
869 void ScrollView::SetAxisAutoLock(bool enable)
870 {
871   mAxisAutoLock = enable;
872   mConstraints.UpdateMainInternalConstraint(*this);
873 }
874
875 void ScrollView::SetAxisAutoLockGradient(float gradient)
876 {
877   DALI_ASSERT_DEBUG(gradient >= 0.0f && gradient <= 1.0f);
878   mAxisAutoLockGradient = gradient;
879   mConstraints.UpdateMainInternalConstraint(*this);
880 }
881
882 void ScrollView::SetFrictionCoefficient(float friction)
883 {
884   DALI_ASSERT_DEBUG(friction > 0.0f);
885   mFrictionCoefficient = friction;
886 }
887
888 unsigned int ScrollView::GetCurrentPage() const
889 {
890   // in case animation is currently taking place.
891   Vector2 position = GetPropertyPosition();
892
893   Actor        self           = Self();
894   unsigned int page           = 0;
895   unsigned int pagesPerVolume = 1;
896   unsigned int volume         = 0;
897
898   // if rulerX is enabled, then get page count (columns)
899   page           = mRulerX->GetPageFromPosition(-position.x, mWrapMode);
900   volume         = mRulerY->GetPageFromPosition(-position.y, mWrapMode);
901   pagesPerVolume = mRulerX->GetTotalPages();
902
903   return volume * pagesPerVolume + page;
904 }
905
906 Vector2 ScrollView::GetCurrentScrollPosition() const
907 {
908   return -GetPropertyPosition();
909 }
910
911 void ScrollView::TransformTo(const Vector2& position,
912                              DirectionBias  horizontalBias,
913                              DirectionBias  verticalBias)
914 {
915   TransformTo(position, mSnapDuration, mSnapAlphaFunction, horizontalBias, verticalBias);
916 }
917
918 void ScrollView::TransformTo(const Vector2& position, float duration, AlphaFunction alpha, DirectionBias horizontalBias, DirectionBias verticalBias)
919 {
920   // If this is called while the timer is running, then cancel it
921   StopTouchDownTimer();
922
923   Actor self(Self());
924
925   // Guard against destruction during signal emission
926   // Note that Emit() methods are called indirectly e.g. from within ScrollView::AnimateTo()
927   Toolkit::ScrollView handle(GetOwner());
928
929   DALI_LOG_SCROLL_STATE("[0x%X] pos[%.2f,%.2f], duration[%.2f] bias[%d, %d]",
930                         this,
931                         position.x,
932                         position.y,
933                         duration,
934                         int(horizontalBias),
935                         int(verticalBias));
936
937   Vector2 currentScrollPosition = GetCurrentScrollPosition();
938   self.SetProperty(Toolkit::ScrollView::Property::START_PAGE_POSITION, Vector3(currentScrollPosition));
939
940   if(mScrolling) // are we interrupting a current scroll?
941   {
942     // set mScrolling to false, in case user has code that interrogates mScrolling Getter() in complete.
943     mScrolling = false;
944     DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 1 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
945     mScrollCompletedSignal.Emit(currentScrollPosition);
946   }
947
948   if(mPanning) // are we interrupting a current pan?
949   {
950     DALI_LOG_SCROLL_STATE("[0x%X] Interrupting Pan, set to false", this);
951     mPanning           = false;
952     mGestureStackDepth = 0;
953     self.SetProperty(Toolkit::ScrollView::Property::PANNING, false);
954
955     if(mConstraints.mScrollMainInternalPrePositionConstraint)
956     {
957       mConstraints.mScrollMainInternalPrePositionConstraint.Remove();
958     }
959   }
960
961   self.SetProperty(Toolkit::ScrollView::Property::SCROLLING, true);
962   mScrolling = true;
963
964   DALI_LOG_SCROLL_STATE("[0x%X] mScrollStartedSignal 1 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
965   mScrollStartedSignal.Emit(currentScrollPosition);
966   bool animating = AnimateTo(-position,
967                              Vector2::ONE * duration,
968                              alpha,
969                              true,
970                              horizontalBias,
971                              verticalBias,
972                              SNAP);
973
974   if(!animating)
975   {
976     // if not animating, then this pan has completed right now.
977     self.SetProperty(Toolkit::ScrollView::Property::SCROLLING, false);
978     mScrolling = false;
979
980     // If we have no duration, then in the next update frame, we will be at the position specified as we just set.
981     // In this scenario, we cannot return the currentScrollPosition as this is out-of-date and should instead return the requested final position
982     Vector2 completedPosition(currentScrollPosition);
983     if(duration <= Math::MACHINE_EPSILON_10)
984     {
985       completedPosition = position;
986     }
987
988     DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 2 [%.2f, %.2f]", this, completedPosition.x, completedPosition.y);
989     SetScrollUpdateNotification(false);
990     mScrollCompletedSignal.Emit(completedPosition);
991   }
992 }
993
994 void ScrollView::ScrollTo(const Vector2& position)
995 {
996   ScrollTo(position, mSnapDuration);
997 }
998
999 void ScrollView::ScrollTo(const Vector2& position, float duration)
1000 {
1001   ScrollTo(position, duration, DIRECTION_BIAS_NONE, DIRECTION_BIAS_NONE);
1002 }
1003
1004 void ScrollView::ScrollTo(const Vector2& position, float duration, AlphaFunction alpha)
1005 {
1006   ScrollTo(position, duration, alpha, DIRECTION_BIAS_NONE, DIRECTION_BIAS_NONE);
1007 }
1008
1009 void ScrollView::ScrollTo(const Vector2& position, float duration, DirectionBias horizontalBias, DirectionBias verticalBias)
1010 {
1011   ScrollTo(position, duration, mSnapAlphaFunction, horizontalBias, verticalBias);
1012 }
1013
1014 void ScrollView::ScrollTo(const Vector2& position, float duration, AlphaFunction alpha, DirectionBias horizontalBias, DirectionBias verticalBias)
1015 {
1016   DALI_LOG_SCROLL_STATE("[0x%X] position[%.2f, %.2f] duration[%.2f], bias[%d, %d]", this, position.x, position.y, duration, int(horizontalBias), int(verticalBias));
1017   TransformTo(position, duration, alpha, horizontalBias, verticalBias);
1018 }
1019
1020 void ScrollView::ScrollTo(unsigned int page)
1021 {
1022   ScrollTo(page, mSnapDuration);
1023 }
1024
1025 void ScrollView::ScrollTo(unsigned int page, float duration, DirectionBias bias)
1026 {
1027   Vector2      position;
1028   unsigned int volume;
1029   unsigned int libraries;
1030
1031   // The position to scroll to is continuous and linear
1032   // unless a domain has been enabled on the X axis.
1033   // or if WrapMode has been enabled.
1034   bool carryX = mRulerX->GetDomain().enabled | mWrapMode;
1035   bool carryY = mRulerY->GetDomain().enabled | mWrapMode;
1036
1037   position.x = mRulerX->GetPositionFromPage(page, volume, carryX);
1038   position.y = mRulerY->GetPositionFromPage(volume, libraries, carryY);
1039
1040   ScrollTo(position, duration, bias, bias);
1041 }
1042
1043 void ScrollView::ScrollTo(Actor& actor)
1044 {
1045   ScrollTo(actor, mSnapDuration);
1046 }
1047
1048 void ScrollView::ScrollTo(Actor& actor, float duration)
1049 {
1050   DALI_ASSERT_ALWAYS(actor.GetParent() == Self());
1051
1052   Actor   self        = Self();
1053   Vector3 size        = self.GetCurrentProperty<Vector3>(Actor::Property::SIZE);
1054   Vector3 position    = actor.GetCurrentProperty<Vector3>(Actor::Property::POSITION);
1055   Vector2 prePosition = GetPropertyPrePosition();
1056   position.GetVectorXY() -= prePosition;
1057
1058   ScrollTo(Vector2(position.x - size.width * 0.5f, position.y - size.height * 0.5f), duration);
1059 }
1060
1061 Actor ScrollView::FindClosestActor()
1062 {
1063   Actor   self = Self();
1064   Vector3 size = self.GetCurrentProperty<Vector3>(Actor::Property::SIZE);
1065
1066   return FindClosestActorToPosition(Vector3(size.width * 0.5f, size.height * 0.5f, 0.0f));
1067 }
1068
1069 Actor ScrollView::FindClosestActorToPosition(const Vector3& position, FindDirection dirX, FindDirection dirY, FindDirection dirZ)
1070 {
1071   return ::FindClosestActorToPosition(Self(), mInternalActor, position, dirX, dirY, dirZ);
1072 }
1073
1074 bool ScrollView::ScrollToSnapPoint()
1075 {
1076   DALI_LOG_SCROLL_STATE("[0x%X]", this);
1077   Vector2 stationaryVelocity = Vector2(0.0f, 0.0f);
1078   return SnapWithVelocity(stationaryVelocity);
1079 }
1080
1081 bool ScrollView::SnapWithVelocity(Vector2 velocity)
1082 {
1083   Vector2       positionSnap     = mScrollPrePosition;
1084   Vector2       positionDuration = Vector2::ONE * mSnapDuration;
1085   AlphaFunction alphaFunction    = mSnapAlphaFunction;
1086   bool          isFlick;
1087   bool          isFreeFlick;
1088
1089   ::SnapWithVelocity(*this, mRulerX, mRulerY, mLockAxis, velocity, mMaxOvershoot, positionSnap, positionDuration, alphaFunction, mInAccessibilityPan, isFlick, isFreeFlick);
1090
1091   bool animating = AnimateTo(positionSnap, positionDuration, alphaFunction, false, DIRECTION_BIAS_NONE, DIRECTION_BIAS_NONE, isFlick || isFreeFlick ? FLICK : SNAP);
1092
1093   return animating;
1094 }
1095
1096 void ScrollView::StopAnimation(void)
1097 {
1098   // Clear Snap animation if exists.
1099   StopAnimation(mInternalXAnimation);
1100   StopAnimation(mInternalYAnimation);
1101   mScrollStateFlags = 0;
1102   // remove scroll animation flags
1103   HandleStoppedAnimation();
1104 }
1105
1106 void ScrollView::StopAnimation(Animation& animation)
1107 {
1108   if(animation)
1109   {
1110     animation.Stop();
1111     animation.Reset();
1112   }
1113 }
1114
1115 bool ScrollView::AnimateTo(const Vector2& position, const Vector2& positionDuration, AlphaFunction alpha, bool findShortcuts, DirectionBias horizontalBias, DirectionBias verticalBias, SnapType snapType)
1116 {
1117   // Here we perform an animation on a number of properties (depending on which have changed)
1118   // The animation is applied to all ScrollBases
1119   Actor self            = Self();
1120   mScrollTargetPosition = position;
1121   float totalDuration   = 0.0f;
1122
1123   bool positionChanged = (mScrollTargetPosition != mScrollPostPosition);
1124
1125   if(positionChanged)
1126   {
1127     totalDuration = std::max(totalDuration, positionDuration.x);
1128     totalDuration = std::max(totalDuration, positionDuration.y);
1129   }
1130   else
1131   {
1132     // try to animate for a frame, on some occasions update will be changing scroll value while event side thinks it hasnt changed
1133     totalDuration   = 0.01f;
1134     positionChanged = true;
1135   }
1136
1137   StopAnimation();
1138
1139   // Position Delta ///////////////////////////////////////////////////////
1140   if(positionChanged)
1141   {
1142     mConstraints.UpdateMainInternalConstraint(*this);
1143     if(mWrapMode && findShortcuts)
1144     {
1145       // In Wrap Mode, the shortest distance is a little less intuitive...
1146       const RulerDomain rulerDomainX = mRulerX->GetDomain();
1147       const RulerDomain rulerDomainY = mRulerY->GetDomain();
1148
1149       if(mRulerX->IsEnabled())
1150       {
1151         float dir               = VectorInDomain(-mScrollPrePosition.x, -mScrollTargetPosition.x, rulerDomainX.min, rulerDomainX.max, horizontalBias);
1152         mScrollTargetPosition.x = mScrollPrePosition.x + -dir;
1153       }
1154
1155       if(mRulerY->IsEnabled())
1156       {
1157         float dir               = VectorInDomain(-mScrollPrePosition.y, -mScrollTargetPosition.y, rulerDomainY.min, rulerDomainY.max, verticalBias);
1158         mScrollTargetPosition.y = mScrollPrePosition.y + -dir;
1159       }
1160     }
1161
1162     // note we have two separate animations for X & Y, this deals with sliding diagonally and hitting
1163     // a horizonal/vertical wall.delay
1164     AnimateInternalXTo(mScrollTargetPosition.x, positionDuration.x, alpha);
1165     AnimateInternalYTo(mScrollTargetPosition.y, positionDuration.y, alpha);
1166
1167     if(!(mScrollStateFlags & SCROLL_ANIMATION_FLAGS))
1168     {
1169       DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollTargetPosition.x, mScrollTargetPosition.y);
1170       self.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollTargetPosition);
1171       mScrollPrePosition  = mScrollTargetPosition;
1172       mScrollPostPosition = mScrollTargetPosition;
1173       WrapPosition(mScrollPostPosition);
1174     }
1175
1176     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);
1177     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);
1178   }
1179
1180   SetScrollUpdateNotification(true);
1181
1182   // Always send a snap event when AnimateTo is called.
1183   Toolkit::ScrollView::SnapEvent snapEvent;
1184   snapEvent.type     = snapType;
1185   snapEvent.position = -mScrollTargetPosition;
1186   snapEvent.duration = totalDuration;
1187
1188   DALI_LOG_SCROLL_STATE("[0x%X] mSnapStartedSignal [%.2f, %.2f]", this, snapEvent.position.x, snapEvent.position.y);
1189   mSnapStartedSignal.Emit(snapEvent);
1190
1191   return (mScrollStateFlags & SCROLL_ANIMATION_FLAGS) != 0;
1192 }
1193
1194 void ScrollView::EnableScrollOvershoot(bool enable)
1195 {
1196   if(enable)
1197   {
1198     if(!mOvershootIndicator)
1199     {
1200       mOvershootIndicator = ScrollOvershootIndicator::New();
1201     }
1202
1203     mOvershootIndicator->AttachToScrollable(*this);
1204   }
1205   else
1206   {
1207     mMaxOvershoot = mUserMaxOvershoot;
1208
1209     if(mOvershootIndicator)
1210     {
1211       mOvershootIndicator->DetachFromScrollable(*this);
1212     }
1213   }
1214
1215   mConstraints.UpdateMainInternalConstraint(*this);
1216 }
1217
1218 void ScrollView::AddOverlay(Actor actor)
1219 {
1220   actor.SetProperty(Actor::Property::DRAW_MODE, DrawMode::OVERLAY_2D);
1221   mInternalActor.Add(actor);
1222 }
1223
1224 void ScrollView::RemoveOverlay(Actor actor)
1225 {
1226   mInternalActor.Remove(actor);
1227 }
1228
1229 void ScrollView::SetOvershootSize(const Vector2& size)
1230 {
1231   mOvershootSize = size;
1232   if(IsOvershootEnabled() && mOvershootIndicator)
1233   {
1234     mOvershootIndicator->AttachToScrollable(*this);
1235   }
1236 }
1237
1238 void ScrollView::SetOvershootEffectColor(const Vector4& color)
1239 {
1240   mOvershootEffectColor = color;
1241   if(mOvershootIndicator)
1242   {
1243     mOvershootIndicator->SetOvershootEffectColor(color);
1244   }
1245 }
1246
1247 void ScrollView::SetScrollingDirection(Radian direction, Radian threshold)
1248 {
1249   PanGestureDetector panGesture(GetPanGestureDetector());
1250
1251   // First remove just in case we have some set, then add.
1252   panGesture.RemoveDirection(direction);
1253   panGesture.AddDirection(direction, threshold);
1254 }
1255
1256 void ScrollView::RemoveScrollingDirection(Radian direction)
1257 {
1258   PanGestureDetector panGesture(GetPanGestureDetector());
1259   panGesture.RemoveDirection(direction);
1260 }
1261
1262 Toolkit::ScrollView::SnapStartedSignalType& ScrollView::SnapStartedSignal()
1263 {
1264   return mSnapStartedSignal;
1265 }
1266
1267 bool ScrollView::ScrollViewAccessible::ScrollToChild(Actor child)
1268 {
1269   auto scrollView = Dali::Toolkit::ScrollView::DownCast(Self());
1270   if(Toolkit::GetImpl(scrollView).FindClosestActor() == child)
1271   {
1272     return false;
1273   }
1274
1275   // child can be one of descendants
1276   // find direct child of ScrollView to avoid the ASSERT in ScrollTo
1277   auto parent = child.GetParent();
1278   while (parent && parent != Self())
1279   {
1280     child = parent;
1281     parent = child.GetParent();
1282   }
1283   if (!parent)
1284   {
1285     return false;
1286   }
1287
1288   // FIXME: ScrollTo does not work (snaps back to original position)
1289   scrollView.ScrollTo(child, scrollView.GetScrollFlickDuration());
1290   return true;
1291 }
1292
1293 void ScrollView::FindAndUnbindActor(Actor child)
1294 {
1295   UnbindActor(child);
1296 }
1297
1298 Vector2 ScrollView::GetPropertyPrePosition() const
1299 {
1300   Vector2 position = Self().GetCurrentProperty<Vector2>(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION);
1301   WrapPosition(position);
1302   return position;
1303 }
1304
1305 Vector2 ScrollView::GetPropertyPosition() const
1306 {
1307   Vector2 position = Self().GetCurrentProperty<Vector2>(Toolkit::ScrollView::Property::SCROLL_POSITION);
1308   WrapPosition(position);
1309
1310   return position;
1311 }
1312
1313 void ScrollView::HandleStoppedAnimation()
1314 {
1315   SetScrollUpdateNotification(false);
1316 }
1317
1318 void ScrollView::HandleSnapAnimationFinished()
1319 {
1320   // Emit Signal that scrolling has completed.
1321   mScrolling = false;
1322   Actor self = Self();
1323   self.SetProperty(Toolkit::ScrollView::Property::SCROLLING, false);
1324
1325   Vector2 deltaPosition(mScrollPrePosition);
1326
1327   UpdateLocalScrollProperties();
1328   WrapPosition(mScrollPrePosition);
1329   DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y);
1330   self.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPrePosition);
1331
1332   Vector2 currentScrollPosition = GetCurrentScrollPosition();
1333   DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 3 current[%.2f, %.2f], mScrollTargetPosition[%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y, -mScrollTargetPosition.x, -mScrollTargetPosition.y);
1334   mScrollCompletedSignal.Emit(currentScrollPosition);
1335
1336   mDomainOffset += deltaPosition - mScrollPostPosition;
1337   self.SetProperty(Toolkit::ScrollView::Property::SCROLL_DOMAIN_OFFSET, mDomainOffset);
1338   HandleStoppedAnimation();
1339 }
1340
1341 void ScrollView::SetScrollUpdateNotification(bool enabled)
1342 {
1343   Actor self = Self();
1344   if(mScrollXUpdateNotification)
1345   {
1346     // disconnect now to avoid a notification before removed from update thread
1347     mScrollXUpdateNotification.NotifySignal().Disconnect(this, &ScrollView::OnScrollUpdateNotification);
1348     self.RemovePropertyNotification(mScrollXUpdateNotification);
1349     mScrollXUpdateNotification.Reset();
1350   }
1351   if(enabled && !mScrollUpdatedSignal.Empty())
1352   {
1353     // Only set up the notification when the application has connected to the updated signal
1354     mScrollXUpdateNotification = self.AddPropertyNotification(Toolkit::ScrollView::Property::SCROLL_POSITION, 0, StepCondition(mScrollUpdateDistance, 0.0f));
1355     mScrollXUpdateNotification.NotifySignal().Connect(this, &ScrollView::OnScrollUpdateNotification);
1356   }
1357   if(mScrollYUpdateNotification)
1358   {
1359     // disconnect now to avoid a notification before removed from update thread
1360     mScrollYUpdateNotification.NotifySignal().Disconnect(this, &ScrollView::OnScrollUpdateNotification);
1361     self.RemovePropertyNotification(mScrollYUpdateNotification);
1362     mScrollYUpdateNotification.Reset();
1363   }
1364   if(enabled && !mScrollUpdatedSignal.Empty())
1365   {
1366     // Only set up the notification when the application has connected to the updated signal
1367     mScrollYUpdateNotification = self.AddPropertyNotification(Toolkit::ScrollView::Property::SCROLL_POSITION, 1, StepCondition(mScrollUpdateDistance, 0.0f));
1368     mScrollYUpdateNotification.NotifySignal().Connect(this, &ScrollView::OnScrollUpdateNotification);
1369   }
1370 }
1371
1372 void ScrollView::OnScrollUpdateNotification(Dali::PropertyNotification& source)
1373 {
1374   // Guard against destruction during signal emission
1375   Toolkit::ScrollView handle(GetOwner());
1376
1377   Vector2 currentScrollPosition = GetCurrentScrollPosition();
1378   mScrollUpdatedSignal.Emit(currentScrollPosition);
1379 }
1380
1381 bool ScrollView::DoConnectSignal(BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor)
1382 {
1383   Dali::BaseHandle handle(object);
1384
1385   bool                connected(true);
1386   Toolkit::ScrollView view = Toolkit::ScrollView::DownCast(handle);
1387
1388   if(0 == strcmp(signalName.c_str(), SIGNAL_SNAP_STARTED))
1389   {
1390     view.SnapStartedSignal().Connect(tracker, functor);
1391   }
1392   else
1393   {
1394     // signalName does not match any signal
1395     connected = false;
1396   }
1397
1398   return connected;
1399 }
1400
1401 void ScrollView::OnSizeAnimation(Animation& animation, const Vector3& targetSize)
1402 {
1403   // need to update domain properties for new size
1404   ScrollViewPropertyHandler::UpdatePropertyDomain(*this);
1405 }
1406
1407 void ScrollView::OnSizeSet(const Vector3& size)
1408 {
1409   // need to update domain properties for new size
1410   if(mDefaultMaxOvershoot)
1411   {
1412     mUserMaxOvershoot.x = size.x * 0.5f;
1413     mUserMaxOvershoot.y = size.y * 0.5f;
1414     if(!IsOvershootEnabled())
1415     {
1416       mMaxOvershoot = mUserMaxOvershoot;
1417     }
1418   }
1419   ScrollViewPropertyHandler::UpdatePropertyDomain(*this);
1420   mConstraints.UpdateMainInternalConstraint(*this);
1421   if(IsOvershootEnabled())
1422   {
1423     mOvershootIndicator->Reset();
1424   }
1425
1426   ScrollBase::OnSizeSet(size);
1427 }
1428
1429 void ScrollView::OnChildAdd(Actor& child)
1430 {
1431   ScrollBase::OnChildAdd(child);
1432
1433   Dali::Toolkit::ScrollBar scrollBar = Dali::Toolkit::ScrollBar::DownCast(child);
1434   if(scrollBar)
1435   {
1436     mScrollBar = scrollBar;
1437     scrollBar.SetProperty(Dali::Actor::Property::NAME, "ScrollBar");
1438
1439     mInternalActor.Add(scrollBar);
1440     if(scrollBar.GetScrollDirection() == Toolkit::ScrollBar::HORIZONTAL)
1441     {
1442       scrollBar.SetScrollPropertySource(Self(),
1443                                         Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_X,
1444                                         Toolkit::Scrollable::Property::SCROLL_POSITION_MIN_X,
1445                                         Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_MAX_X,
1446                                         Toolkit::ScrollView::Property::SCROLL_DOMAIN_SIZE_X);
1447     }
1448     else
1449     {
1450       scrollBar.SetScrollPropertySource(Self(),
1451                                         Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_Y,
1452                                         Toolkit::Scrollable::Property::SCROLL_POSITION_MIN_Y,
1453                                         Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_MAX_Y,
1454                                         Toolkit::ScrollView::Property::SCROLL_DOMAIN_SIZE_Y);
1455     }
1456
1457     if(mTransientScrollBar)
1458     {
1459       // Show the scroll-indicator for a brief period
1460       Property::Map emptyMap;
1461       scrollBar.DoAction("ShowTransientIndicator", emptyMap);
1462     }
1463   }
1464   else if(mAlterChild)
1465   {
1466     BindActor(child);
1467   }
1468 }
1469
1470 void ScrollView::OnChildRemove(Actor& child)
1471 {
1472   // TODO: Actor needs a RemoveConstraint method to take out an individual constraint.
1473   UnbindActor(child);
1474
1475   ScrollBase::OnChildRemove(child);
1476 }
1477
1478 void ScrollView::StartTouchDownTimer()
1479 {
1480   if(!mTouchDownTimer)
1481   {
1482     mTouchDownTimer = Timer::New(TOUCH_DOWN_TIMER_INTERVAL);
1483     mTouchDownTimer.TickSignal().Connect(this, &ScrollView::OnTouchDownTimeout);
1484   }
1485
1486   mTouchDownTimer.Start();
1487 }
1488
1489 void ScrollView::StopTouchDownTimer()
1490 {
1491   if(mTouchDownTimer)
1492   {
1493     mTouchDownTimer.Stop();
1494   }
1495 }
1496
1497 bool ScrollView::OnTouchDownTimeout()
1498 {
1499   DALI_LOG_SCROLL_STATE("[0x%X]", this);
1500
1501   mTouchDownTimeoutReached = true;
1502
1503   unsigned int currentScrollStateFlags(mScrollStateFlags); // Cleared in StopAnimation so keep local copy for comparison
1504   if(currentScrollStateFlags & (SCROLL_ANIMATION_FLAGS | SNAP_ANIMATION_FLAGS))
1505   {
1506     DALI_LOG_SCROLL_STATE("[0x%X] Scrolling Or snapping flags set, stopping animation", this);
1507
1508     StopAnimation();
1509     if(currentScrollStateFlags & SCROLL_ANIMATION_FLAGS)
1510     {
1511       DALI_LOG_SCROLL_STATE("[0x%X] Scrolling flags set, emitting signal", this);
1512
1513       mScrollInterrupted = true;
1514       // reset domain offset as scrolling from original plane.
1515       mDomainOffset = Vector2::ZERO;
1516       Self().SetProperty(Toolkit::ScrollView::Property::SCROLL_DOMAIN_OFFSET, Vector2::ZERO);
1517
1518       UpdateLocalScrollProperties();
1519       Vector2 currentScrollPosition = GetCurrentScrollPosition();
1520       DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 4 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
1521       mScrollCompletedSignal.Emit(currentScrollPosition);
1522     }
1523   }
1524
1525   return false;
1526 }
1527
1528 bool ScrollView::OnTouch(Actor actor, const TouchEvent& touch)
1529 {
1530   if(!mSensitive)
1531   {
1532     DALI_LOG_SCROLL_STATE("[0x%X], Not Sensitive, ignoring", this);
1533
1534     // Ignore this touch event, if scrollview is insensitive.
1535     return false;
1536   }
1537
1538   // Ignore events with multiple-touch points
1539   if(touch.GetPointCount() != 1)
1540   {
1541     DALI_LOG_SCROLL_STATE("[0x%X], multiple touch, ignoring", this);
1542
1543     return false;
1544   }
1545
1546   const PointState::Type pointState = touch.GetState(0);
1547   if(pointState == PointState::DOWN)
1548   {
1549     DALI_LOG_SCROLL_STATE("[0x%X] Down", this);
1550
1551     if(mGestureStackDepth == 0)
1552     {
1553       mTouchDownTime = touch.GetTime();
1554
1555       // This allows time for a pan-gesture to start, to avoid breaking snap-animation behavior with fast flicks.
1556       // If touch-down does not become a pan (after timeout interval), then snap-animation can be interrupted.
1557       mTouchDownTimeoutReached = false;
1558       mScrollInterrupted       = false;
1559       StartTouchDownTimer();
1560     }
1561   }
1562   else if((pointState == PointState::UP) ||
1563           ((pointState == PointState::INTERRUPTED) && (touch.GetHitActor(0) == Self())))
1564   {
1565     DALI_LOG_SCROLL_STATE("[0x%X] %s", this, ((pointState == PointState::UP) ? "Up" : "Interrupted"));
1566
1567     StopTouchDownTimer();
1568
1569     // if the user touches and releases without enough movement to go
1570     // into a gesture state, then we should snap to nearest point.
1571     // otherwise our scroll could be stopped (interrupted) half way through an animation.
1572     if(mGestureStackDepth == 0 && mTouchDownTimeoutReached)
1573     {
1574       if((pointState == PointState::INTERRUPTED) ||
1575          ((touch.GetTime() - mTouchDownTime) >= MINIMUM_TIME_BETWEEN_DOWN_AND_UP_FOR_RESET))
1576       {
1577         // Reset the velocity only if down was received a while ago
1578         mLastVelocity = Vector2(0.0f, 0.0f);
1579       }
1580
1581       UpdateLocalScrollProperties();
1582       // Only finish the transform if scrolling was interrupted on down or if we are scrolling
1583       if(mScrollInterrupted || mScrolling)
1584       {
1585         DALI_LOG_SCROLL_STATE("[0x%X] Calling FinishTransform", this);
1586
1587         FinishTransform();
1588       }
1589     }
1590     mTouchDownTimeoutReached = false;
1591     mScrollInterrupted       = false;
1592   }
1593
1594   return false;
1595 }
1596
1597 bool ScrollView::OnWheelEvent(Actor actor, const WheelEvent& event)
1598 {
1599   if(!mSensitive)
1600   {
1601     // Ignore this wheel event, if scrollview is insensitive.
1602     return false;
1603   }
1604
1605   Vector2 targetScrollPosition = GetPropertyPosition();
1606
1607   if(mRulerX->IsEnabled() && !mRulerY->IsEnabled())
1608   {
1609     // If only the ruler in the X axis is enabled, scroll in the X axis.
1610     if(mRulerX->GetType() == Ruler::FREE)
1611     {
1612       // Free panning mode
1613       targetScrollPosition.x += event.GetDelta() * mWheelScrollDistanceStep.x;
1614       ClampPosition(targetScrollPosition);
1615       ScrollTo(-targetScrollPosition);
1616     }
1617     else if(!mScrolling)
1618     {
1619       // Snap mode, only respond to the event when the previous snap animation is finished.
1620       ScrollTo(GetCurrentPage() - event.GetDelta());
1621     }
1622   }
1623   else
1624   {
1625     // If the ruler in the Y axis is enabled, scroll in the Y axis.
1626     if(mRulerY->GetType() == Ruler::FREE)
1627     {
1628       // Free panning mode
1629       targetScrollPosition.y += event.GetDelta() * mWheelScrollDistanceStep.y;
1630       ClampPosition(targetScrollPosition);
1631       ScrollTo(-targetScrollPosition);
1632     }
1633     else if(!mScrolling)
1634     {
1635       // Snap mode, only respond to the event when the previous snap animation is finished.
1636       ScrollTo(GetCurrentPage() - event.GetDelta() * mRulerX->GetTotalPages());
1637     }
1638   }
1639
1640   return true;
1641 }
1642
1643 void ScrollView::ResetScrolling()
1644 {
1645   Actor self = Self();
1646   self.GetCurrentProperty(Toolkit::ScrollView::Property::SCROLL_POSITION).Get(mScrollPostPosition);
1647   mScrollPrePosition = mScrollPostPosition;
1648   DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPostPosition.x, mScrollPostPosition.y);
1649   self.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPostPosition);
1650 }
1651
1652 void ScrollView::UpdateLocalScrollProperties()
1653 {
1654   Actor self = Self();
1655   self.GetCurrentProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION).Get(mScrollPrePosition);
1656   self.GetCurrentProperty(Toolkit::ScrollView::Property::SCROLL_POSITION).Get(mScrollPostPosition);
1657 }
1658
1659 // private functions
1660
1661 void ScrollView::PreAnimatedScrollSetup()
1662 {
1663   // SCROLL_PRE_POSITION is our unclamped property with wrapping
1664   // SCROLL_POSITION is our final scroll position after clamping
1665
1666   Actor self = Self();
1667
1668   Vector2 deltaPosition(mScrollPostPosition);
1669   WrapPosition(mScrollPostPosition);
1670   mDomainOffset += deltaPosition - mScrollPostPosition;
1671   Self().SetProperty(Toolkit::ScrollView::Property::SCROLL_DOMAIN_OFFSET, mDomainOffset);
1672
1673   if(mScrollStateFlags & SCROLL_X_STATE_MASK)
1674   {
1675     // already performing animation on internal x position
1676     StopAnimation(mInternalXAnimation);
1677   }
1678
1679   if(mScrollStateFlags & SCROLL_Y_STATE_MASK)
1680   {
1681     // already performing animation on internal y position
1682     StopAnimation(mInternalYAnimation);
1683   }
1684
1685   mScrollStateFlags = 0;
1686
1687   // Update Actor position with this wrapped value.
1688 }
1689
1690 void ScrollView::FinaliseAnimatedScroll()
1691 {
1692   // TODO - common animation finishing code in here
1693 }
1694
1695 void ScrollView::AnimateInternalXTo(float position, float duration, AlphaFunction alpha)
1696 {
1697   StopAnimation(mInternalXAnimation);
1698
1699   if(duration > Math::MACHINE_EPSILON_10)
1700   {
1701     Actor self = Self();
1702     DALI_LOG_SCROLL_STATE("[0x%X], Animating from[%.2f] to[%.2f]", this, self.GetCurrentProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION).Get<Vector2>().x, position);
1703     mInternalXAnimation = Animation::New(duration);
1704     DALI_LOG_SCROLL_STATE("[0x%X], mInternalXAnimation[0x%X]", this, mInternalXAnimation.GetObjectPtr());
1705     mInternalXAnimation.FinishedSignal().Connect(this, &ScrollView::OnScrollAnimationFinished);
1706     mInternalXAnimation.AnimateTo(Property(self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, 0), position, alpha, TimePeriod(duration));
1707     mInternalXAnimation.Play();
1708
1709     // erase current state flags
1710     mScrollStateFlags &= ~SCROLL_X_STATE_MASK;
1711     // add internal animation state flag
1712     mScrollStateFlags |= AnimatingInternalX;
1713   }
1714 }
1715
1716 void ScrollView::AnimateInternalYTo(float position, float duration, AlphaFunction alpha)
1717 {
1718   StopAnimation(mInternalYAnimation);
1719
1720   if(duration > Math::MACHINE_EPSILON_10)
1721   {
1722     Actor self = Self();
1723     DALI_LOG_SCROLL_STATE("[0x%X], Animating from[%.2f] to[%.2f]", this, self.GetCurrentProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION).Get<Vector2>().y, position);
1724     mInternalYAnimation = Animation::New(duration);
1725     DALI_LOG_SCROLL_STATE("[0x%X], mInternalYAnimation[0x%X]", this, mInternalYAnimation.GetObjectPtr());
1726     mInternalYAnimation.FinishedSignal().Connect(this, &ScrollView::OnScrollAnimationFinished);
1727     mInternalYAnimation.AnimateTo(Property(self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, 1), position, alpha, TimePeriod(duration));
1728     mInternalYAnimation.Play();
1729
1730     // erase current state flags
1731     mScrollStateFlags &= ~SCROLL_Y_STATE_MASK;
1732     // add internal animation state flag
1733     mScrollStateFlags |= AnimatingInternalY;
1734   }
1735 }
1736
1737 void ScrollView::OnScrollAnimationFinished(Animation& source)
1738 {
1739   // Guard against destruction during signal emission
1740   // Note that ScrollCompletedSignal is emitted from HandleSnapAnimationFinished()
1741   Toolkit::ScrollView handle(GetOwner());
1742
1743   bool scrollingFinished = false;
1744
1745   // update our local scroll positions
1746   UpdateLocalScrollProperties();
1747
1748   if(source == mInternalXAnimation)
1749   {
1750     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);
1751
1752     if(!(mScrollStateFlags & AnimatingInternalY))
1753     {
1754       scrollingFinished = true;
1755     }
1756     mInternalXAnimation.Reset();
1757     // wrap pre scroll x position and set it
1758     if(mWrapMode)
1759     {
1760       const RulerDomain rulerDomain = mRulerX->GetDomain();
1761       mScrollPrePosition.x          = -WrapInDomain(-mScrollPrePosition.x, rulerDomain.min, rulerDomain.max);
1762       DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y);
1763       handle.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPrePosition);
1764     }
1765     SnapInternalXTo(mScrollPostPosition.x);
1766   }
1767
1768   if(source == mInternalYAnimation)
1769   {
1770     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);
1771
1772     if(!(mScrollStateFlags & AnimatingInternalX))
1773     {
1774       scrollingFinished = true;
1775     }
1776     mInternalYAnimation.Reset();
1777     if(mWrapMode)
1778     {
1779       // wrap pre scroll y position and set it
1780       const RulerDomain rulerDomain = mRulerY->GetDomain();
1781       mScrollPrePosition.y          = -WrapInDomain(-mScrollPrePosition.y, rulerDomain.min, rulerDomain.max);
1782       DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y);
1783       handle.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPrePosition);
1784     }
1785     SnapInternalYTo(mScrollPostPosition.y);
1786   }
1787
1788   DALI_LOG_SCROLL_STATE("[0x%X] scrollingFinished[%d] Animation[0x%X]", this, scrollingFinished, source.GetObjectPtr());
1789
1790   if(scrollingFinished)
1791   {
1792     HandleSnapAnimationFinished();
1793   }
1794 }
1795
1796 void ScrollView::OnSnapInternalPositionFinished(Animation& source)
1797 {
1798   Actor self = Self();
1799   UpdateLocalScrollProperties();
1800   if(source == mInternalXAnimation)
1801   {
1802     DALI_LOG_SCROLL_STATE("[0x%X] Finished X PostPosition Animation", this);
1803
1804     // clear internal x animation flags
1805     mScrollStateFlags &= ~SCROLL_X_STATE_MASK;
1806     mInternalXAnimation.Reset();
1807     WrapPosition(mScrollPrePosition);
1808   }
1809   if(source == mInternalYAnimation)
1810   {
1811     DALI_LOG_SCROLL_STATE("[0x%X] Finished Y PostPosition Animation", this);
1812
1813     mScrollStateFlags &= ~SCROLL_Y_STATE_MASK;
1814     mInternalYAnimation.Reset();
1815     WrapPosition(mScrollPrePosition);
1816   }
1817 }
1818
1819 void ScrollView::SnapInternalXTo(float position)
1820 {
1821   Actor self = Self();
1822
1823   StopAnimation(mInternalXAnimation);
1824
1825   // erase current state flags
1826   mScrollStateFlags &= ~SCROLL_X_STATE_MASK;
1827
1828   // if internal x not equal to inputed parameter, animate it
1829   float duration = std::min(fabsf((position - mScrollPrePosition.x) / mMaxOvershoot.x) * mSnapOvershootDuration, mSnapOvershootDuration);
1830   DALI_LOG_SCROLL_STATE("[0x%X] duration[%.2f]", this, duration);
1831   if(duration > Math::MACHINE_EPSILON_1)
1832   {
1833     DALI_LOG_SCROLL_STATE("[0x%X] Starting X Snap Animation to[%.2f]", this, position);
1834
1835     mInternalXAnimation = Animation::New(duration);
1836     mInternalXAnimation.FinishedSignal().Connect(this, &ScrollView::OnSnapInternalPositionFinished);
1837     mInternalXAnimation.AnimateTo(Property(self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, 0), position);
1838     mInternalXAnimation.Play();
1839
1840     // add internal animation state flag
1841     mScrollStateFlags |= SnappingInternalX;
1842   }
1843 }
1844
1845 void ScrollView::SnapInternalYTo(float position)
1846 {
1847   Actor self = Self();
1848
1849   StopAnimation(mInternalYAnimation);
1850
1851   // erase current state flags
1852   mScrollStateFlags &= ~SCROLL_Y_STATE_MASK;
1853
1854   // if internal y not equal to inputed parameter, animate it
1855   float duration = std::min(fabsf((position - mScrollPrePosition.y) / mMaxOvershoot.y) * mSnapOvershootDuration, mSnapOvershootDuration);
1856   DALI_LOG_SCROLL_STATE("[0x%X] duration[%.2f]", this, duration);
1857   if(duration > Math::MACHINE_EPSILON_1)
1858   {
1859     DALI_LOG_SCROLL_STATE("[0x%X] Starting Y Snap Animation to[%.2f]", this, position);
1860
1861     mInternalYAnimation = Animation::New(duration);
1862     mInternalYAnimation.FinishedSignal().Connect(this, &ScrollView::OnSnapInternalPositionFinished);
1863     mInternalYAnimation.AnimateTo(Property(self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, 1), position);
1864     mInternalYAnimation.Play();
1865
1866     // add internal animation state flag
1867     mScrollStateFlags |= SnappingInternalY;
1868   }
1869 }
1870
1871 void ScrollView::GestureStarted()
1872 {
1873   // we handle the first gesture.
1874   // if we're currently doing a gesture and receive another
1875   // we continue and combine the effects of the gesture instead of reseting.
1876   if(mGestureStackDepth++ == 0)
1877   {
1878     Actor self = Self();
1879     StopTouchDownTimer();
1880     StopAnimation();
1881     mPanDelta     = Vector2::ZERO;
1882     mLastVelocity = Vector2::ZERO;
1883     if(!mScrolling)
1884     {
1885       mLockAxis = LockPossible;
1886     }
1887
1888     if(mScrollStateFlags & SCROLL_X_STATE_MASK)
1889     {
1890       StopAnimation(mInternalXAnimation);
1891     }
1892     if(mScrollStateFlags & SCROLL_Y_STATE_MASK)
1893     {
1894       StopAnimation(mInternalYAnimation);
1895     }
1896     mScrollStateFlags = 0;
1897
1898     if(mScrolling) // are we interrupting a current scroll?
1899     {
1900       // set mScrolling to false, in case user has code that interrogates mScrolling Getter() in complete.
1901       mScrolling = false;
1902       // send negative scroll position since scroll internal scroll position works as an offset for actors,
1903       // give applications the position within the domain from the scroll view's anchor position
1904       DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 5 [%.2f, %.2f]", this, -mScrollPostPosition.x, -mScrollPostPosition.y);
1905       mScrollCompletedSignal.Emit(-mScrollPostPosition);
1906     }
1907   }
1908 }
1909
1910 void ScrollView::GestureContinuing(const Vector2& panDelta)
1911 {
1912   mPanDelta.x += panDelta.x;
1913   mPanDelta.y += panDelta.y;
1914
1915   // Save the velocity, there is a bug in PanGesture
1916   // Whereby the GestureState::FINISHED's velocity is either:
1917   // NaN (due to time delta of zero between the last two events)
1918   // or 0 (due to position being the same between the last two events)
1919
1920   // Axis Auto Lock - locks the panning to the horizontal or vertical axis if the pan
1921   // appears mostly horizontal or mostly vertical respectively.
1922   if(mAxisAutoLock)
1923   {
1924     mLockAxis = GetLockAxis(mPanDelta, mLockAxis, mAxisAutoLockGradient);
1925   } // end if mAxisAutoLock
1926 }
1927
1928 // TODO: Upgrade to use a more powerful gesture detector (one that supports multiple touches on pan - so works as pan and flick gesture)
1929 // BUG: GestureState::FINISHED doesn't always return velocity on release (due to
1930 // timeDelta between last two events being 0 sometimes, or posiiton being the same)
1931 void ScrollView::OnPan(const PanGesture& gesture)
1932 {
1933   // Guard against destruction during signal emission
1934   // Note that Emit() methods are called indirectly e.g. from within ScrollView::OnGestureEx()
1935   Actor self(Self());
1936
1937   if(!mSensitive)
1938   {
1939     DALI_LOG_SCROLL_STATE("[0x%X] Pan Ignored, Insensitive", this);
1940
1941     // If another callback on the same original signal disables sensitivity,
1942     // this callback will still be called, so we must suppress it.
1943     return;
1944   }
1945
1946   // translate Gesture input to get useful data...
1947   switch(gesture.GetState())
1948   {
1949     case GestureState::STARTED:
1950     {
1951       DALI_LOG_SCROLL_STATE("[0x%X] Pan Started", this);
1952       const Vector2& position = gesture.GetPosition();
1953       mPanStartPosition       = position - gesture.GetDisplacement();
1954       UpdateLocalScrollProperties();
1955       GestureStarted();
1956       mPanning = true;
1957       self.SetProperty(Toolkit::ScrollView::Property::PANNING, true);
1958       self.SetProperty(Toolkit::ScrollView::Property::START_PAGE_POSITION, Vector3(position.x, position.y, 0.0f));
1959
1960       mConstraints.UpdateMainInternalConstraint(*this);
1961       Toolkit::ScrollBar scrollBar = mScrollBar.GetHandle();
1962       if(scrollBar && mTransientScrollBar)
1963       {
1964         Vector3                     size         = Self().GetCurrentProperty<Vector3>(Actor::Property::SIZE);
1965         const Toolkit::RulerDomain& rulerDomainX = mRulerX->GetDomain();
1966         const Toolkit::RulerDomain& rulerDomainY = mRulerY->GetDomain();
1967
1968         if((rulerDomainX.max > size.width) || (rulerDomainY.max > size.height))
1969         {
1970           scrollBar.ShowIndicator();
1971         }
1972       }
1973       break;
1974     }
1975
1976     case GestureState::CONTINUING:
1977     {
1978       if(mPanning)
1979       {
1980         DALI_LOG_SCROLL_STATE("[0x%X] Pan Continuing", this);
1981         GestureContinuing(gesture.GetScreenDisplacement());
1982       }
1983       else
1984       {
1985         // If we do not think we are panning, then we should not do anything here
1986         return;
1987       }
1988       break;
1989     }
1990
1991     case GestureState::FINISHED:
1992     case GestureState::CANCELLED:
1993     {
1994       if(mPanning)
1995       {
1996         DALI_LOG_SCROLL_STATE("[0x%X] Pan %s", this, ((gesture.GetState() == GestureState::FINISHED) ? "Finished" : "Cancelled"));
1997
1998         UpdateLocalScrollProperties();
1999         mLastVelocity = gesture.GetVelocity();
2000         mPanning      = false;
2001         self.SetProperty(Toolkit::ScrollView::Property::PANNING, false);
2002
2003         if(mConstraints.mScrollMainInternalPrePositionConstraint)
2004         {
2005           mConstraints.mScrollMainInternalPrePositionConstraint.Remove();
2006         }
2007
2008         Toolkit::ScrollBar scrollBar = mScrollBar.GetHandle();
2009         if(scrollBar && mTransientScrollBar)
2010         {
2011           scrollBar.HideIndicator();
2012         }
2013       }
2014       else
2015       {
2016         // If we do not think we are panning, then we should not do anything here
2017         return;
2018       }
2019       break;
2020     }
2021
2022     case GestureState::POSSIBLE:
2023     case GestureState::CLEAR:
2024     {
2025       // Nothing to do, not needed.
2026       break;
2027     }
2028
2029   } // end switch(gesture.state)
2030
2031   OnGestureEx(gesture.GetState());
2032 }
2033
2034 void ScrollView::OnGestureEx(GestureState state)
2035 {
2036   // call necessary signals for application developer
2037
2038   if(state == GestureState::STARTED)
2039   {
2040     Vector2 currentScrollPosition = GetCurrentScrollPosition();
2041     Self().SetProperty(Toolkit::ScrollView::Property::SCROLLING, true);
2042     mScrolling = true;
2043     DALI_LOG_SCROLL_STATE("[0x%X] mScrollStartedSignal 2 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
2044     mScrollStartedSignal.Emit(currentScrollPosition);
2045   }
2046   else if((state == GestureState::FINISHED) ||
2047           (state == GestureState::CANCELLED)) // Finished/default
2048   {
2049     // when all the gestures have finished, we finish the transform.
2050     // so if a user decides to pan (1 gesture), and then pan+zoom (2 gestures)
2051     // then stop panning (back to 1 gesture), and then stop zooming (0 gestures).
2052     // this is the point we end, and perform necessary snapping.
2053     mGestureStackDepth--;
2054     if(mGestureStackDepth == 0)
2055     {
2056       // no flick if we have not exceeded min flick distance
2057       if((fabsf(mPanDelta.x) < mMinFlickDistance.x) && (fabsf(mPanDelta.y) < mMinFlickDistance.y))
2058       {
2059         // reset flick velocity
2060         mLastVelocity = Vector2::ZERO;
2061       }
2062       FinishTransform();
2063     }
2064     else
2065     {
2066       DALI_LOG_SCROLL_STATE("[0x%X] mGestureStackDepth[%d]", this, mGestureStackDepth);
2067     }
2068   }
2069 }
2070
2071 void ScrollView::FinishTransform()
2072 {
2073   // at this stage internal x and x scroll position should have followed prescroll position exactly
2074   Actor self = Self();
2075
2076   PreAnimatedScrollSetup();
2077
2078   // convert pixels/millisecond to pixels per second
2079   bool animating = SnapWithVelocity(mLastVelocity * 1000.0f);
2080
2081   if(!animating)
2082   {
2083     // if not animating, then this pan has completed right now.
2084     SetScrollUpdateNotification(false);
2085     mScrolling = false;
2086     Self().SetProperty(Toolkit::ScrollView::Property::SCROLLING, false);
2087
2088     if(fabs(mScrollPrePosition.x - mScrollTargetPosition.x) > Math::MACHINE_EPSILON_10)
2089     {
2090       SnapInternalXTo(mScrollTargetPosition.x);
2091     }
2092     if(fabs(mScrollPrePosition.y - mScrollTargetPosition.y) > Math::MACHINE_EPSILON_10)
2093     {
2094       SnapInternalYTo(mScrollTargetPosition.y);
2095     }
2096     Vector2 currentScrollPosition = GetCurrentScrollPosition();
2097     DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 6 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
2098     mScrollCompletedSignal.Emit(currentScrollPosition);
2099   }
2100 }
2101
2102 bool ScrollView::OnAccessibilityPan(PanGesture gesture)
2103 {
2104   // Keep track of whether this is an AccessibilityPan
2105   mInAccessibilityPan = true;
2106   OnPan(gesture);
2107   mInAccessibilityPan = false;
2108
2109   return true;
2110 }
2111
2112 void ScrollView::ClampPosition(Vector2& position) const
2113 {
2114   ClampState2D clamped;
2115   ClampPosition(position, clamped);
2116 }
2117
2118 void ScrollView::ClampPosition(Vector2& position, ClampState2D& clamped) const
2119 {
2120   Vector3 size = Self().GetCurrentProperty<Vector3>(Actor::Property::SIZE);
2121
2122   ::ClampPosition(size, mRulerX, mRulerY, position, clamped);
2123 }
2124
2125 void ScrollView::WrapPosition(Vector2& position) const
2126 {
2127   if(mWrapMode)
2128   {
2129     const RulerDomain rulerDomainX = mRulerX->GetDomain();
2130     const RulerDomain rulerDomainY = mRulerY->GetDomain();
2131
2132     if(mRulerX->IsEnabled())
2133     {
2134       position.x = -WrapInDomain(-position.x, rulerDomainX.min, rulerDomainX.max);
2135     }
2136
2137     if(mRulerY->IsEnabled())
2138     {
2139       position.y = -WrapInDomain(-position.y, rulerDomainY.min, rulerDomainY.max);
2140     }
2141   }
2142 }
2143
2144 void ScrollView::SetProperty(BaseObject* object, Property::Index index, const Property::Value& value)
2145 {
2146   ScrollViewPropertyHandler::Set(object, index, value);
2147 }
2148
2149 Property::Value ScrollView::GetProperty(BaseObject* object, Property::Index index)
2150 {
2151   return ScrollViewPropertyHandler::Get(object, index);
2152 }
2153
2154 ScrollView::LockAxis GetLockAxis(const Vector2& panDelta, ScrollView::LockAxis currentLockAxis, float lockGradient)
2155 {
2156   if(panDelta.LengthSquared() > AUTOLOCK_AXIS_MINIMUM_DISTANCE2 &&
2157      currentLockAxis == ScrollView::LockPossible)
2158   {
2159     float dx = fabsf(panDelta.x);
2160     float dy = fabsf(panDelta.y);
2161     if(dx * lockGradient >= dy)
2162     {
2163       // 0.36:1 gradient to the horizontal (deviate < 20 degrees)
2164       currentLockAxis = ScrollView::LockVertical;
2165     }
2166     else if(dy * lockGradient > dx)
2167     {
2168       // 0.36:1 gradient to the vertical (deviate < 20 degrees)
2169       currentLockAxis = ScrollView::LockHorizontal;
2170     }
2171     else
2172     {
2173       currentLockAxis = ScrollView::LockNone;
2174     }
2175   }
2176   return currentLockAxis;
2177 }
2178
2179 } // namespace Internal
2180
2181 } // namespace Toolkit
2182
2183 } // namespace Dali