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