[AT-SPI] Remove SetAccessibilityConstructor()
[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   // FIXME: ScrollTo does not work (snaps back to original position)
1276   scrollView.ScrollTo(child, scrollView.GetScrollFlickDuration());
1277   return true;
1278 }
1279
1280 void ScrollView::FindAndUnbindActor(Actor child)
1281 {
1282   UnbindActor(child);
1283 }
1284
1285 Vector2 ScrollView::GetPropertyPrePosition() const
1286 {
1287   Vector2 position = Self().GetCurrentProperty<Vector2>(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION);
1288   WrapPosition(position);
1289   return position;
1290 }
1291
1292 Vector2 ScrollView::GetPropertyPosition() const
1293 {
1294   Vector2 position = Self().GetCurrentProperty<Vector2>(Toolkit::ScrollView::Property::SCROLL_POSITION);
1295   WrapPosition(position);
1296
1297   return position;
1298 }
1299
1300 void ScrollView::HandleStoppedAnimation()
1301 {
1302   SetScrollUpdateNotification(false);
1303 }
1304
1305 void ScrollView::HandleSnapAnimationFinished()
1306 {
1307   // Emit Signal that scrolling has completed.
1308   mScrolling = false;
1309   Actor self = Self();
1310   self.SetProperty(Toolkit::ScrollView::Property::SCROLLING, false);
1311
1312   Vector2 deltaPosition(mScrollPrePosition);
1313
1314   UpdateLocalScrollProperties();
1315   WrapPosition(mScrollPrePosition);
1316   DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y);
1317   self.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPrePosition);
1318
1319   Vector2 currentScrollPosition = GetCurrentScrollPosition();
1320   DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 3 current[%.2f, %.2f], mScrollTargetPosition[%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y, -mScrollTargetPosition.x, -mScrollTargetPosition.y);
1321   mScrollCompletedSignal.Emit(currentScrollPosition);
1322
1323   mDomainOffset += deltaPosition - mScrollPostPosition;
1324   self.SetProperty(Toolkit::ScrollView::Property::SCROLL_DOMAIN_OFFSET, mDomainOffset);
1325   HandleStoppedAnimation();
1326 }
1327
1328 void ScrollView::SetScrollUpdateNotification(bool enabled)
1329 {
1330   Actor self = Self();
1331   if(mScrollXUpdateNotification)
1332   {
1333     // disconnect now to avoid a notification before removed from update thread
1334     mScrollXUpdateNotification.NotifySignal().Disconnect(this, &ScrollView::OnScrollUpdateNotification);
1335     self.RemovePropertyNotification(mScrollXUpdateNotification);
1336     mScrollXUpdateNotification.Reset();
1337   }
1338   if(enabled && !mScrollUpdatedSignal.Empty())
1339   {
1340     // Only set up the notification when the application has connected to the updated signal
1341     mScrollXUpdateNotification = self.AddPropertyNotification(Toolkit::ScrollView::Property::SCROLL_POSITION, 0, StepCondition(mScrollUpdateDistance, 0.0f));
1342     mScrollXUpdateNotification.NotifySignal().Connect(this, &ScrollView::OnScrollUpdateNotification);
1343   }
1344   if(mScrollYUpdateNotification)
1345   {
1346     // disconnect now to avoid a notification before removed from update thread
1347     mScrollYUpdateNotification.NotifySignal().Disconnect(this, &ScrollView::OnScrollUpdateNotification);
1348     self.RemovePropertyNotification(mScrollYUpdateNotification);
1349     mScrollYUpdateNotification.Reset();
1350   }
1351   if(enabled && !mScrollUpdatedSignal.Empty())
1352   {
1353     // Only set up the notification when the application has connected to the updated signal
1354     mScrollYUpdateNotification = self.AddPropertyNotification(Toolkit::ScrollView::Property::SCROLL_POSITION, 1, StepCondition(mScrollUpdateDistance, 0.0f));
1355     mScrollYUpdateNotification.NotifySignal().Connect(this, &ScrollView::OnScrollUpdateNotification);
1356   }
1357 }
1358
1359 void ScrollView::OnScrollUpdateNotification(Dali::PropertyNotification& source)
1360 {
1361   // Guard against destruction during signal emission
1362   Toolkit::ScrollView handle(GetOwner());
1363
1364   Vector2 currentScrollPosition = GetCurrentScrollPosition();
1365   mScrollUpdatedSignal.Emit(currentScrollPosition);
1366 }
1367
1368 bool ScrollView::DoConnectSignal(BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor)
1369 {
1370   Dali::BaseHandle handle(object);
1371
1372   bool                connected(true);
1373   Toolkit::ScrollView view = Toolkit::ScrollView::DownCast(handle);
1374
1375   if(0 == strcmp(signalName.c_str(), SIGNAL_SNAP_STARTED))
1376   {
1377     view.SnapStartedSignal().Connect(tracker, functor);
1378   }
1379   else
1380   {
1381     // signalName does not match any signal
1382     connected = false;
1383   }
1384
1385   return connected;
1386 }
1387
1388 void ScrollView::OnSizeAnimation(Animation& animation, const Vector3& targetSize)
1389 {
1390   // need to update domain properties for new size
1391   ScrollViewPropertyHandler::UpdatePropertyDomain(*this);
1392 }
1393
1394 void ScrollView::OnSizeSet(const Vector3& size)
1395 {
1396   // need to update domain properties for new size
1397   if(mDefaultMaxOvershoot)
1398   {
1399     mUserMaxOvershoot.x = size.x * 0.5f;
1400     mUserMaxOvershoot.y = size.y * 0.5f;
1401     if(!IsOvershootEnabled())
1402     {
1403       mMaxOvershoot = mUserMaxOvershoot;
1404     }
1405   }
1406   ScrollViewPropertyHandler::UpdatePropertyDomain(*this);
1407   mConstraints.UpdateMainInternalConstraint(*this);
1408   if(IsOvershootEnabled())
1409   {
1410     mOvershootIndicator->Reset();
1411   }
1412
1413   ScrollBase::OnSizeSet(size);
1414 }
1415
1416 void ScrollView::OnChildAdd(Actor& child)
1417 {
1418   ScrollBase::OnChildAdd(child);
1419
1420   Dali::Toolkit::ScrollBar scrollBar = Dali::Toolkit::ScrollBar::DownCast(child);
1421   if(scrollBar)
1422   {
1423     mScrollBar = scrollBar;
1424     scrollBar.SetProperty(Dali::Actor::Property::NAME, "ScrollBar");
1425
1426     mInternalActor.Add(scrollBar);
1427     if(scrollBar.GetScrollDirection() == Toolkit::ScrollBar::HORIZONTAL)
1428     {
1429       scrollBar.SetScrollPropertySource(Self(),
1430                                         Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_X,
1431                                         Toolkit::Scrollable::Property::SCROLL_POSITION_MIN_X,
1432                                         Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_MAX_X,
1433                                         Toolkit::ScrollView::Property::SCROLL_DOMAIN_SIZE_X);
1434     }
1435     else
1436     {
1437       scrollBar.SetScrollPropertySource(Self(),
1438                                         Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_Y,
1439                                         Toolkit::Scrollable::Property::SCROLL_POSITION_MIN_Y,
1440                                         Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_MAX_Y,
1441                                         Toolkit::ScrollView::Property::SCROLL_DOMAIN_SIZE_Y);
1442     }
1443
1444     if(mTransientScrollBar)
1445     {
1446       // Show the scroll-indicator for a brief period
1447       Property::Map emptyMap;
1448       scrollBar.DoAction("ShowTransientIndicator", emptyMap);
1449     }
1450   }
1451   else if(mAlterChild)
1452   {
1453     BindActor(child);
1454   }
1455 }
1456
1457 void ScrollView::OnChildRemove(Actor& child)
1458 {
1459   // TODO: Actor needs a RemoveConstraint method to take out an individual constraint.
1460   UnbindActor(child);
1461
1462   ScrollBase::OnChildRemove(child);
1463 }
1464
1465 void ScrollView::StartTouchDownTimer()
1466 {
1467   if(!mTouchDownTimer)
1468   {
1469     mTouchDownTimer = Timer::New(TOUCH_DOWN_TIMER_INTERVAL);
1470     mTouchDownTimer.TickSignal().Connect(this, &ScrollView::OnTouchDownTimeout);
1471   }
1472
1473   mTouchDownTimer.Start();
1474 }
1475
1476 void ScrollView::StopTouchDownTimer()
1477 {
1478   if(mTouchDownTimer)
1479   {
1480     mTouchDownTimer.Stop();
1481   }
1482 }
1483
1484 bool ScrollView::OnTouchDownTimeout()
1485 {
1486   DALI_LOG_SCROLL_STATE("[0x%X]", this);
1487
1488   mTouchDownTimeoutReached = true;
1489
1490   unsigned int currentScrollStateFlags(mScrollStateFlags); // Cleared in StopAnimation so keep local copy for comparison
1491   if(currentScrollStateFlags & (SCROLL_ANIMATION_FLAGS | SNAP_ANIMATION_FLAGS))
1492   {
1493     DALI_LOG_SCROLL_STATE("[0x%X] Scrolling Or snapping flags set, stopping animation", this);
1494
1495     StopAnimation();
1496     if(currentScrollStateFlags & SCROLL_ANIMATION_FLAGS)
1497     {
1498       DALI_LOG_SCROLL_STATE("[0x%X] Scrolling flags set, emitting signal", this);
1499
1500       mScrollInterrupted = true;
1501       // reset domain offset as scrolling from original plane.
1502       mDomainOffset = Vector2::ZERO;
1503       Self().SetProperty(Toolkit::ScrollView::Property::SCROLL_DOMAIN_OFFSET, Vector2::ZERO);
1504
1505       UpdateLocalScrollProperties();
1506       Vector2 currentScrollPosition = GetCurrentScrollPosition();
1507       DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 4 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
1508       mScrollCompletedSignal.Emit(currentScrollPosition);
1509     }
1510   }
1511
1512   return false;
1513 }
1514
1515 bool ScrollView::OnTouch(Actor actor, const TouchEvent& touch)
1516 {
1517   if(!mSensitive)
1518   {
1519     DALI_LOG_SCROLL_STATE("[0x%X], Not Sensitive, ignoring", this);
1520
1521     // Ignore this touch event, if scrollview is insensitive.
1522     return false;
1523   }
1524
1525   // Ignore events with multiple-touch points
1526   if(touch.GetPointCount() != 1)
1527   {
1528     DALI_LOG_SCROLL_STATE("[0x%X], multiple touch, ignoring", this);
1529
1530     return false;
1531   }
1532
1533   const PointState::Type pointState = touch.GetState(0);
1534   if(pointState == PointState::DOWN)
1535   {
1536     DALI_LOG_SCROLL_STATE("[0x%X] Down", this);
1537
1538     if(mGestureStackDepth == 0)
1539     {
1540       mTouchDownTime = touch.GetTime();
1541
1542       // This allows time for a pan-gesture to start, to avoid breaking snap-animation behavior with fast flicks.
1543       // If touch-down does not become a pan (after timeout interval), then snap-animation can be interrupted.
1544       mTouchDownTimeoutReached = false;
1545       mScrollInterrupted       = false;
1546       StartTouchDownTimer();
1547     }
1548   }
1549   else if((pointState == PointState::UP) ||
1550           ((pointState == PointState::INTERRUPTED) && (touch.GetHitActor(0) == Self())))
1551   {
1552     DALI_LOG_SCROLL_STATE("[0x%X] %s", this, ((pointState == PointState::UP) ? "Up" : "Interrupted"));
1553
1554     StopTouchDownTimer();
1555
1556     // if the user touches and releases without enough movement to go
1557     // into a gesture state, then we should snap to nearest point.
1558     // otherwise our scroll could be stopped (interrupted) half way through an animation.
1559     if(mGestureStackDepth == 0 && mTouchDownTimeoutReached)
1560     {
1561       if((pointState == PointState::INTERRUPTED) ||
1562          ((touch.GetTime() - mTouchDownTime) >= MINIMUM_TIME_BETWEEN_DOWN_AND_UP_FOR_RESET))
1563       {
1564         // Reset the velocity only if down was received a while ago
1565         mLastVelocity = Vector2(0.0f, 0.0f);
1566       }
1567
1568       UpdateLocalScrollProperties();
1569       // Only finish the transform if scrolling was interrupted on down or if we are scrolling
1570       if(mScrollInterrupted || mScrolling)
1571       {
1572         DALI_LOG_SCROLL_STATE("[0x%X] Calling FinishTransform", this);
1573
1574         FinishTransform();
1575       }
1576     }
1577     mTouchDownTimeoutReached = false;
1578     mScrollInterrupted       = false;
1579   }
1580
1581   return false;
1582 }
1583
1584 bool ScrollView::OnWheelEvent(Actor actor, const WheelEvent& event)
1585 {
1586   if(!mSensitive)
1587   {
1588     // Ignore this wheel event, if scrollview is insensitive.
1589     return false;
1590   }
1591
1592   Vector2 targetScrollPosition = GetPropertyPosition();
1593
1594   if(mRulerX->IsEnabled() && !mRulerY->IsEnabled())
1595   {
1596     // If only the ruler in the X axis is enabled, scroll in the X axis.
1597     if(mRulerX->GetType() == Ruler::FREE)
1598     {
1599       // Free panning mode
1600       targetScrollPosition.x += event.GetDelta() * mWheelScrollDistanceStep.x;
1601       ClampPosition(targetScrollPosition);
1602       ScrollTo(-targetScrollPosition);
1603     }
1604     else if(!mScrolling)
1605     {
1606       // Snap mode, only respond to the event when the previous snap animation is finished.
1607       ScrollTo(GetCurrentPage() - event.GetDelta());
1608     }
1609   }
1610   else
1611   {
1612     // If the ruler in the Y axis is enabled, scroll in the Y axis.
1613     if(mRulerY->GetType() == Ruler::FREE)
1614     {
1615       // Free panning mode
1616       targetScrollPosition.y += event.GetDelta() * mWheelScrollDistanceStep.y;
1617       ClampPosition(targetScrollPosition);
1618       ScrollTo(-targetScrollPosition);
1619     }
1620     else if(!mScrolling)
1621     {
1622       // Snap mode, only respond to the event when the previous snap animation is finished.
1623       ScrollTo(GetCurrentPage() - event.GetDelta() * mRulerX->GetTotalPages());
1624     }
1625   }
1626
1627   return true;
1628 }
1629
1630 void ScrollView::ResetScrolling()
1631 {
1632   Actor self = Self();
1633   self.GetCurrentProperty(Toolkit::ScrollView::Property::SCROLL_POSITION).Get(mScrollPostPosition);
1634   mScrollPrePosition = mScrollPostPosition;
1635   DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPostPosition.x, mScrollPostPosition.y);
1636   self.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPostPosition);
1637 }
1638
1639 void ScrollView::UpdateLocalScrollProperties()
1640 {
1641   Actor self = Self();
1642   self.GetCurrentProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION).Get(mScrollPrePosition);
1643   self.GetCurrentProperty(Toolkit::ScrollView::Property::SCROLL_POSITION).Get(mScrollPostPosition);
1644 }
1645
1646 // private functions
1647
1648 void ScrollView::PreAnimatedScrollSetup()
1649 {
1650   // SCROLL_PRE_POSITION is our unclamped property with wrapping
1651   // SCROLL_POSITION is our final scroll position after clamping
1652
1653   Actor self = Self();
1654
1655   Vector2 deltaPosition(mScrollPostPosition);
1656   WrapPosition(mScrollPostPosition);
1657   mDomainOffset += deltaPosition - mScrollPostPosition;
1658   Self().SetProperty(Toolkit::ScrollView::Property::SCROLL_DOMAIN_OFFSET, mDomainOffset);
1659
1660   if(mScrollStateFlags & SCROLL_X_STATE_MASK)
1661   {
1662     // already performing animation on internal x position
1663     StopAnimation(mInternalXAnimation);
1664   }
1665
1666   if(mScrollStateFlags & SCROLL_Y_STATE_MASK)
1667   {
1668     // already performing animation on internal y position
1669     StopAnimation(mInternalYAnimation);
1670   }
1671
1672   mScrollStateFlags = 0;
1673
1674   // Update Actor position with this wrapped value.
1675 }
1676
1677 void ScrollView::FinaliseAnimatedScroll()
1678 {
1679   // TODO - common animation finishing code in here
1680 }
1681
1682 void ScrollView::AnimateInternalXTo(float position, float duration, AlphaFunction alpha)
1683 {
1684   StopAnimation(mInternalXAnimation);
1685
1686   if(duration > Math::MACHINE_EPSILON_10)
1687   {
1688     Actor self = Self();
1689     DALI_LOG_SCROLL_STATE("[0x%X], Animating from[%.2f] to[%.2f]", this, self.GetCurrentProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION).Get<Vector2>().x, position);
1690     mInternalXAnimation = Animation::New(duration);
1691     DALI_LOG_SCROLL_STATE("[0x%X], mInternalXAnimation[0x%X]", this, mInternalXAnimation.GetObjectPtr());
1692     mInternalXAnimation.FinishedSignal().Connect(this, &ScrollView::OnScrollAnimationFinished);
1693     mInternalXAnimation.AnimateTo(Property(self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, 0), position, alpha, TimePeriod(duration));
1694     mInternalXAnimation.Play();
1695
1696     // erase current state flags
1697     mScrollStateFlags &= ~SCROLL_X_STATE_MASK;
1698     // add internal animation state flag
1699     mScrollStateFlags |= AnimatingInternalX;
1700   }
1701 }
1702
1703 void ScrollView::AnimateInternalYTo(float position, float duration, AlphaFunction alpha)
1704 {
1705   StopAnimation(mInternalYAnimation);
1706
1707   if(duration > Math::MACHINE_EPSILON_10)
1708   {
1709     Actor self = Self();
1710     DALI_LOG_SCROLL_STATE("[0x%X], Animating from[%.2f] to[%.2f]", this, self.GetCurrentProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION).Get<Vector2>().y, position);
1711     mInternalYAnimation = Animation::New(duration);
1712     DALI_LOG_SCROLL_STATE("[0x%X], mInternalYAnimation[0x%X]", this, mInternalYAnimation.GetObjectPtr());
1713     mInternalYAnimation.FinishedSignal().Connect(this, &ScrollView::OnScrollAnimationFinished);
1714     mInternalYAnimation.AnimateTo(Property(self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, 1), position, alpha, TimePeriod(duration));
1715     mInternalYAnimation.Play();
1716
1717     // erase current state flags
1718     mScrollStateFlags &= ~SCROLL_Y_STATE_MASK;
1719     // add internal animation state flag
1720     mScrollStateFlags |= AnimatingInternalY;
1721   }
1722 }
1723
1724 void ScrollView::OnScrollAnimationFinished(Animation& source)
1725 {
1726   // Guard against destruction during signal emission
1727   // Note that ScrollCompletedSignal is emitted from HandleSnapAnimationFinished()
1728   Toolkit::ScrollView handle(GetOwner());
1729
1730   bool scrollingFinished = false;
1731
1732   // update our local scroll positions
1733   UpdateLocalScrollProperties();
1734
1735   if(source == mInternalXAnimation)
1736   {
1737     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);
1738
1739     if(!(mScrollStateFlags & AnimatingInternalY))
1740     {
1741       scrollingFinished = true;
1742     }
1743     mInternalXAnimation.Reset();
1744     // wrap pre scroll x position and set it
1745     if(mWrapMode)
1746     {
1747       const RulerDomain rulerDomain = mRulerX->GetDomain();
1748       mScrollPrePosition.x          = -WrapInDomain(-mScrollPrePosition.x, rulerDomain.min, rulerDomain.max);
1749       DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y);
1750       handle.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPrePosition);
1751     }
1752     SnapInternalXTo(mScrollPostPosition.x);
1753   }
1754
1755   if(source == mInternalYAnimation)
1756   {
1757     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);
1758
1759     if(!(mScrollStateFlags & AnimatingInternalX))
1760     {
1761       scrollingFinished = true;
1762     }
1763     mInternalYAnimation.Reset();
1764     if(mWrapMode)
1765     {
1766       // wrap pre scroll y position and set it
1767       const RulerDomain rulerDomain = mRulerY->GetDomain();
1768       mScrollPrePosition.y          = -WrapInDomain(-mScrollPrePosition.y, rulerDomain.min, rulerDomain.max);
1769       DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y);
1770       handle.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPrePosition);
1771     }
1772     SnapInternalYTo(mScrollPostPosition.y);
1773   }
1774
1775   DALI_LOG_SCROLL_STATE("[0x%X] scrollingFinished[%d] Animation[0x%X]", this, scrollingFinished, source.GetObjectPtr());
1776
1777   if(scrollingFinished)
1778   {
1779     HandleSnapAnimationFinished();
1780   }
1781 }
1782
1783 void ScrollView::OnSnapInternalPositionFinished(Animation& source)
1784 {
1785   Actor self = Self();
1786   UpdateLocalScrollProperties();
1787   if(source == mInternalXAnimation)
1788   {
1789     DALI_LOG_SCROLL_STATE("[0x%X] Finished X PostPosition Animation", this);
1790
1791     // clear internal x animation flags
1792     mScrollStateFlags &= ~SCROLL_X_STATE_MASK;
1793     mInternalXAnimation.Reset();
1794     WrapPosition(mScrollPrePosition);
1795   }
1796   if(source == mInternalYAnimation)
1797   {
1798     DALI_LOG_SCROLL_STATE("[0x%X] Finished Y PostPosition Animation", this);
1799
1800     mScrollStateFlags &= ~SCROLL_Y_STATE_MASK;
1801     mInternalYAnimation.Reset();
1802     WrapPosition(mScrollPrePosition);
1803   }
1804 }
1805
1806 void ScrollView::SnapInternalXTo(float position)
1807 {
1808   Actor self = Self();
1809
1810   StopAnimation(mInternalXAnimation);
1811
1812   // erase current state flags
1813   mScrollStateFlags &= ~SCROLL_X_STATE_MASK;
1814
1815   // if internal x not equal to inputed parameter, animate it
1816   float duration = std::min(fabsf((position - mScrollPrePosition.x) / mMaxOvershoot.x) * mSnapOvershootDuration, mSnapOvershootDuration);
1817   DALI_LOG_SCROLL_STATE("[0x%X] duration[%.2f]", this, duration);
1818   if(duration > Math::MACHINE_EPSILON_1)
1819   {
1820     DALI_LOG_SCROLL_STATE("[0x%X] Starting X Snap Animation to[%.2f]", this, position);
1821
1822     mInternalXAnimation = Animation::New(duration);
1823     mInternalXAnimation.FinishedSignal().Connect(this, &ScrollView::OnSnapInternalPositionFinished);
1824     mInternalXAnimation.AnimateTo(Property(self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, 0), position);
1825     mInternalXAnimation.Play();
1826
1827     // add internal animation state flag
1828     mScrollStateFlags |= SnappingInternalX;
1829   }
1830 }
1831
1832 void ScrollView::SnapInternalYTo(float position)
1833 {
1834   Actor self = Self();
1835
1836   StopAnimation(mInternalYAnimation);
1837
1838   // erase current state flags
1839   mScrollStateFlags &= ~SCROLL_Y_STATE_MASK;
1840
1841   // if internal y not equal to inputed parameter, animate it
1842   float duration = std::min(fabsf((position - mScrollPrePosition.y) / mMaxOvershoot.y) * mSnapOvershootDuration, mSnapOvershootDuration);
1843   DALI_LOG_SCROLL_STATE("[0x%X] duration[%.2f]", this, duration);
1844   if(duration > Math::MACHINE_EPSILON_1)
1845   {
1846     DALI_LOG_SCROLL_STATE("[0x%X] Starting Y Snap Animation to[%.2f]", this, position);
1847
1848     mInternalYAnimation = Animation::New(duration);
1849     mInternalYAnimation.FinishedSignal().Connect(this, &ScrollView::OnSnapInternalPositionFinished);
1850     mInternalYAnimation.AnimateTo(Property(self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, 1), position);
1851     mInternalYAnimation.Play();
1852
1853     // add internal animation state flag
1854     mScrollStateFlags |= SnappingInternalY;
1855   }
1856 }
1857
1858 void ScrollView::GestureStarted()
1859 {
1860   // we handle the first gesture.
1861   // if we're currently doing a gesture and receive another
1862   // we continue and combine the effects of the gesture instead of reseting.
1863   if(mGestureStackDepth++ == 0)
1864   {
1865     Actor self = Self();
1866     StopTouchDownTimer();
1867     StopAnimation();
1868     mPanDelta     = Vector2::ZERO;
1869     mLastVelocity = Vector2::ZERO;
1870     if(!mScrolling)
1871     {
1872       mLockAxis = LockPossible;
1873     }
1874
1875     if(mScrollStateFlags & SCROLL_X_STATE_MASK)
1876     {
1877       StopAnimation(mInternalXAnimation);
1878     }
1879     if(mScrollStateFlags & SCROLL_Y_STATE_MASK)
1880     {
1881       StopAnimation(mInternalYAnimation);
1882     }
1883     mScrollStateFlags = 0;
1884
1885     if(mScrolling) // are we interrupting a current scroll?
1886     {
1887       // set mScrolling to false, in case user has code that interrogates mScrolling Getter() in complete.
1888       mScrolling = false;
1889       // send negative scroll position since scroll internal scroll position works as an offset for actors,
1890       // give applications the position within the domain from the scroll view's anchor position
1891       DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 5 [%.2f, %.2f]", this, -mScrollPostPosition.x, -mScrollPostPosition.y);
1892       mScrollCompletedSignal.Emit(-mScrollPostPosition);
1893     }
1894   }
1895 }
1896
1897 void ScrollView::GestureContinuing(const Vector2& panDelta)
1898 {
1899   mPanDelta.x += panDelta.x;
1900   mPanDelta.y += panDelta.y;
1901
1902   // Save the velocity, there is a bug in PanGesture
1903   // Whereby the GestureState::FINISHED's velocity is either:
1904   // NaN (due to time delta of zero between the last two events)
1905   // or 0 (due to position being the same between the last two events)
1906
1907   // Axis Auto Lock - locks the panning to the horizontal or vertical axis if the pan
1908   // appears mostly horizontal or mostly vertical respectively.
1909   if(mAxisAutoLock)
1910   {
1911     mLockAxis = GetLockAxis(mPanDelta, mLockAxis, mAxisAutoLockGradient);
1912   } // end if mAxisAutoLock
1913 }
1914
1915 // TODO: Upgrade to use a more powerful gesture detector (one that supports multiple touches on pan - so works as pan and flick gesture)
1916 // BUG: GestureState::FINISHED doesn't always return velocity on release (due to
1917 // timeDelta between last two events being 0 sometimes, or posiiton being the same)
1918 void ScrollView::OnPan(const PanGesture& gesture)
1919 {
1920   // Guard against destruction during signal emission
1921   // Note that Emit() methods are called indirectly e.g. from within ScrollView::OnGestureEx()
1922   Actor self(Self());
1923
1924   if(!mSensitive)
1925   {
1926     DALI_LOG_SCROLL_STATE("[0x%X] Pan Ignored, Insensitive", this);
1927
1928     // If another callback on the same original signal disables sensitivity,
1929     // this callback will still be called, so we must suppress it.
1930     return;
1931   }
1932
1933   // translate Gesture input to get useful data...
1934   switch(gesture.GetState())
1935   {
1936     case GestureState::STARTED:
1937     {
1938       DALI_LOG_SCROLL_STATE("[0x%X] Pan Started", this);
1939       const Vector2& position = gesture.GetPosition();
1940       mPanStartPosition       = position - gesture.GetDisplacement();
1941       UpdateLocalScrollProperties();
1942       GestureStarted();
1943       mPanning = true;
1944       self.SetProperty(Toolkit::ScrollView::Property::PANNING, true);
1945       self.SetProperty(Toolkit::ScrollView::Property::START_PAGE_POSITION, Vector3(position.x, position.y, 0.0f));
1946
1947       mConstraints.UpdateMainInternalConstraint(*this);
1948       Toolkit::ScrollBar scrollBar = mScrollBar.GetHandle();
1949       if(scrollBar && mTransientScrollBar)
1950       {
1951         Vector3                     size         = Self().GetCurrentProperty<Vector3>(Actor::Property::SIZE);
1952         const Toolkit::RulerDomain& rulerDomainX = mRulerX->GetDomain();
1953         const Toolkit::RulerDomain& rulerDomainY = mRulerY->GetDomain();
1954
1955         if((rulerDomainX.max > size.width) || (rulerDomainY.max > size.height))
1956         {
1957           scrollBar.ShowIndicator();
1958         }
1959       }
1960       break;
1961     }
1962
1963     case GestureState::CONTINUING:
1964     {
1965       if(mPanning)
1966       {
1967         DALI_LOG_SCROLL_STATE("[0x%X] Pan Continuing", this);
1968         GestureContinuing(gesture.GetScreenDisplacement());
1969       }
1970       else
1971       {
1972         // If we do not think we are panning, then we should not do anything here
1973         return;
1974       }
1975       break;
1976     }
1977
1978     case GestureState::FINISHED:
1979     case GestureState::CANCELLED:
1980     {
1981       if(mPanning)
1982       {
1983         DALI_LOG_SCROLL_STATE("[0x%X] Pan %s", this, ((gesture.GetState() == GestureState::FINISHED) ? "Finished" : "Cancelled"));
1984
1985         UpdateLocalScrollProperties();
1986         mLastVelocity = gesture.GetVelocity();
1987         mPanning      = false;
1988         self.SetProperty(Toolkit::ScrollView::Property::PANNING, false);
1989
1990         if(mConstraints.mScrollMainInternalPrePositionConstraint)
1991         {
1992           mConstraints.mScrollMainInternalPrePositionConstraint.Remove();
1993         }
1994
1995         Toolkit::ScrollBar scrollBar = mScrollBar.GetHandle();
1996         if(scrollBar && mTransientScrollBar)
1997         {
1998           scrollBar.HideIndicator();
1999         }
2000       }
2001       else
2002       {
2003         // If we do not think we are panning, then we should not do anything here
2004         return;
2005       }
2006       break;
2007     }
2008
2009     case GestureState::POSSIBLE:
2010     case GestureState::CLEAR:
2011     {
2012       // Nothing to do, not needed.
2013       break;
2014     }
2015
2016   } // end switch(gesture.state)
2017
2018   OnGestureEx(gesture.GetState());
2019 }
2020
2021 void ScrollView::OnGestureEx(GestureState state)
2022 {
2023   // call necessary signals for application developer
2024
2025   if(state == GestureState::STARTED)
2026   {
2027     Vector2 currentScrollPosition = GetCurrentScrollPosition();
2028     Self().SetProperty(Toolkit::ScrollView::Property::SCROLLING, true);
2029     mScrolling = true;
2030     DALI_LOG_SCROLL_STATE("[0x%X] mScrollStartedSignal 2 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
2031     mScrollStartedSignal.Emit(currentScrollPosition);
2032   }
2033   else if((state == GestureState::FINISHED) ||
2034           (state == GestureState::CANCELLED)) // Finished/default
2035   {
2036     // when all the gestures have finished, we finish the transform.
2037     // so if a user decides to pan (1 gesture), and then pan+zoom (2 gestures)
2038     // then stop panning (back to 1 gesture), and then stop zooming (0 gestures).
2039     // this is the point we end, and perform necessary snapping.
2040     mGestureStackDepth--;
2041     if(mGestureStackDepth == 0)
2042     {
2043       // no flick if we have not exceeded min flick distance
2044       if((fabsf(mPanDelta.x) < mMinFlickDistance.x) && (fabsf(mPanDelta.y) < mMinFlickDistance.y))
2045       {
2046         // reset flick velocity
2047         mLastVelocity = Vector2::ZERO;
2048       }
2049       FinishTransform();
2050     }
2051     else
2052     {
2053       DALI_LOG_SCROLL_STATE("[0x%X] mGestureStackDepth[%d]", this, mGestureStackDepth);
2054     }
2055   }
2056 }
2057
2058 void ScrollView::FinishTransform()
2059 {
2060   // at this stage internal x and x scroll position should have followed prescroll position exactly
2061   Actor self = Self();
2062
2063   PreAnimatedScrollSetup();
2064
2065   // convert pixels/millisecond to pixels per second
2066   bool animating = SnapWithVelocity(mLastVelocity * 1000.0f);
2067
2068   if(!animating)
2069   {
2070     // if not animating, then this pan has completed right now.
2071     SetScrollUpdateNotification(false);
2072     mScrolling = false;
2073     Self().SetProperty(Toolkit::ScrollView::Property::SCROLLING, false);
2074
2075     if(fabs(mScrollPrePosition.x - mScrollTargetPosition.x) > Math::MACHINE_EPSILON_10)
2076     {
2077       SnapInternalXTo(mScrollTargetPosition.x);
2078     }
2079     if(fabs(mScrollPrePosition.y - mScrollTargetPosition.y) > Math::MACHINE_EPSILON_10)
2080     {
2081       SnapInternalYTo(mScrollTargetPosition.y);
2082     }
2083     Vector2 currentScrollPosition = GetCurrentScrollPosition();
2084     DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 6 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y);
2085     mScrollCompletedSignal.Emit(currentScrollPosition);
2086   }
2087 }
2088
2089 bool ScrollView::OnAccessibilityPan(PanGesture gesture)
2090 {
2091   // Keep track of whether this is an AccessibilityPan
2092   mInAccessibilityPan = true;
2093   OnPan(gesture);
2094   mInAccessibilityPan = false;
2095
2096   return true;
2097 }
2098
2099 void ScrollView::ClampPosition(Vector2& position) const
2100 {
2101   ClampState2D clamped;
2102   ClampPosition(position, clamped);
2103 }
2104
2105 void ScrollView::ClampPosition(Vector2& position, ClampState2D& clamped) const
2106 {
2107   Vector3 size = Self().GetCurrentProperty<Vector3>(Actor::Property::SIZE);
2108
2109   ::ClampPosition(size, mRulerX, mRulerY, position, clamped);
2110 }
2111
2112 void ScrollView::WrapPosition(Vector2& position) const
2113 {
2114   if(mWrapMode)
2115   {
2116     const RulerDomain rulerDomainX = mRulerX->GetDomain();
2117     const RulerDomain rulerDomainY = mRulerY->GetDomain();
2118
2119     if(mRulerX->IsEnabled())
2120     {
2121       position.x = -WrapInDomain(-position.x, rulerDomainX.min, rulerDomainX.max);
2122     }
2123
2124     if(mRulerY->IsEnabled())
2125     {
2126       position.y = -WrapInDomain(-position.y, rulerDomainY.min, rulerDomainY.max);
2127     }
2128   }
2129 }
2130
2131 void ScrollView::SetProperty(BaseObject* object, Property::Index index, const Property::Value& value)
2132 {
2133   ScrollViewPropertyHandler::Set(object, index, value);
2134 }
2135
2136 Property::Value ScrollView::GetProperty(BaseObject* object, Property::Index index)
2137 {
2138   return ScrollViewPropertyHandler::Get(object, index);
2139 }
2140
2141 ScrollView::LockAxis GetLockAxis(const Vector2& panDelta, ScrollView::LockAxis currentLockAxis, float lockGradient)
2142 {
2143   if(panDelta.LengthSquared() > AUTOLOCK_AXIS_MINIMUM_DISTANCE2 &&
2144      currentLockAxis == ScrollView::LockPossible)
2145   {
2146     float dx = fabsf(panDelta.x);
2147     float dy = fabsf(panDelta.y);
2148     if(dx * lockGradient >= dy)
2149     {
2150       // 0.36:1 gradient to the horizontal (deviate < 20 degrees)
2151       currentLockAxis = ScrollView::LockVertical;
2152     }
2153     else if(dy * lockGradient > dx)
2154     {
2155       // 0.36:1 gradient to the vertical (deviate < 20 degrees)
2156       currentLockAxis = ScrollView::LockHorizontal;
2157     }
2158     else
2159     {
2160       currentLockAxis = ScrollView::LockNone;
2161     }
2162   }
2163   return currentLockAxis;
2164 }
2165
2166 } // namespace Internal
2167
2168 } // namespace Toolkit
2169
2170 } // namespace Dali