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