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