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