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