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