c93d264a262720d6af2fed93fb4b6353412ed02f
[platform/core/uifw/dali-toolkit.git] / base / dali-toolkit / internal / controls / scrollable / scroll-view / scroll-view-twist-effect-impl.cpp
1
2 /*
3  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18
19 #include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-effect-impl.h>
20 #include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-twist-effect-impl.h>
21
22 using namespace Dali;
23
24 namespace // unnamed namespace
25 {
26
27 const char * const EFFECT_TIME( "ScrollViewTwistEffect::EFFECT_TIME" );
28 const char * const EFFECT_REFERENCE( "ScrollViewTwistEffect::EFFECT_REFERENCE" );
29 const char * const EFFECT_DEPTH( "ScrollViewTwistEffect::EFFECT_DEPTH");
30 const char * const EFFECT_ACTIVATE( "ScrollViewTwistEffect::EFFECT_ACTIVATE");
31
32 const float TWISTEFFECT_ANIMATION_MAX_TIME = 60.0f;         ///< Animation time (every time finishes, checks if it needs to go again)
33 const float TWISTEFFECT_DEFAULT_DROPOFF = 0.7f;             ///< Default drop off amount
34 const float TWISTEFFECT_DEFAULT_DROPOFF_DISTANCE_X = 720.0f;  ///< Default drop off distance
35 const float TWISTEFFECT_DEFAULT_DROPOFF_DISTANCE_Y = 1280.0f;  ///< Default drop off distance
36
37 // Hop Easing equation.
38 // Starts with a -ve cosine ranging from 0 to pi.
39 // Then plateaus.
40 // Then finishes with a -ve cosine ranging from pi to 0
41 // 0......(RISE).....PI (SUSTAIN) PI.....(FALL)......0
42 //                xxxxxxxxxxxxxxxxxxxxx
43 //              x                       x
44 //            x                           x
45 //           x                             x
46 //           x                             x
47 //          x                               x
48 //        x                                   x
49 // xxxxxx                                       xxxxxx
50
51 const float HOP_RISE(0.25f);
52 const float HOP_FALL(0.5f);
53 const float DELAY(0.5f);
54
55 float HopEasing(float progress)
56 {
57   // progress from 0.0 - HOP_RISE (go from 0.0 to 1.0)
58   if(progress < HOP_RISE)
59   {
60     return 0.5f - cosf(progress/HOP_RISE * Math::PI) * 0.5f;
61   }
62
63   progress += HOP_FALL - 1.0f;
64
65   // progress from 0.0 - HOP_FALL (go from 1.0 to 0.0)
66   if(progress > 0.0f )
67   {
68     return 0.5f + cosf(progress/HOP_FALL * Math::PI) * 0.5f;
69   }
70
71   // progress at plateau.
72   return 1.0f;
73 }
74
75 /**
76  * Gets a property index. If the property doesn't already exist, then
77  * it will create the property.
78  * @param[in] handle The handle that owns or will own the property
79  * @param[in] name The name for this property
80  * @param[in] propertyValue The initial value for this property
81  * @return The property index for this property is returned.
82  */
83 Property::Index SafeRegisterProperty( Handle& handle, const std::string& name, Property::Value propertyValue )
84 {
85   Property::Index index = handle.GetPropertyIndex( name );
86
87   if(index == Property::INVALID_INDEX)
88   {
89     index = handle.RegisterProperty( name, propertyValue );
90   }
91
92   return index;
93 }
94
95 /**
96  * Re-scales input value x from x0 - x1, to linearly map
97  * over the values y0 - y1. Values outside of this range
98  * will also conform to the trend (gradient) set.
99  * @param[in] x input X value
100  * @param[in] x0 input minimum bound
101  * @param[in] x1 input maximum bound
102  * @param[in] y0 output minimum bound
103  * @param[in] y1 output maximum bound
104  * @return The result of the mapping is returned.
105  */
106 float Rescale(float x, float x0, float x1, float y0, float y1)
107 {
108   return y0 + (y1 - y0) * (x - x0) / (x1-x0);
109 }
110
111 /**
112  * Returns the value of x chasing target.
113  * returns a value of x which is closer to target.
114  * but limited by maxDelta. #
115  * For example:
116  * x = 10.0f
117  * target = 50.0f
118  * maxDelta = 20.0f
119  * result is 30.0f (x is 20.0f units closer to target)
120  * However, if x is already within maxDelta units
121  * of target, x will equal target.
122  * For example:
123  * x = 55.0f
124  * target = 50.0f
125  * maxDelta = 20.0f
126  * result is 50.0f (x was already within 20.0f units of target)
127  */
128 float Chase( float x, float target, float maxDelta )
129 {
130   float delta = target - x;
131
132   if(delta > 0.0f)
133   {
134     x = std::min( x + maxDelta, target );
135   }
136   else
137   {
138     x = std::max( x - maxDelta, target );
139   }
140
141   return x;
142 }
143
144 // constraints ////////////////////////////////////////////////////////////////
145
146 /**
147  * ScrollTwistRotationConstraint
148  *
149  * Rotate constraint adjusts the angle of the Actors
150  * based on actor's world-position relative to the middle of the screen.
151  * When at middle of screen Angles on X and Y Axes is 0.
152  * When one screen away from the middle Angle is 90 degrees (pi/2)
153  */
154 struct ScrollDropoffTwistRotationConstraint
155 {
156   /**
157    * Constraint constructor
158    * @param[in] angleSwing The amount the Actor should revolve in radians
159    * for a given page worth of distance.
160    */
161   ScrollDropoffTwistRotationConstraint(const Vector2& angleSwing, const Vector2& dropOff, const Vector2& distance, AlphaFunction function)
162   : mAngleSwing(angleSwing),
163     mDropOff(dropOff),
164     mDropOffDistance(distance),
165     mDropOffFunction(function)
166   {
167   }
168
169   /**
170    * @param[in] current The current orientation of this Actor
171    * @param[in] actorPositionProperty The actor's world-position property
172    * @param[in] scrollOvershootXProperty The scroll-view's overshoot property (SCROLL_OVERSHOOT_X_PROPERTY_NAME)
173    * @param[in] scrollOvershootYProperty The scroll-view's overshoot property (SCROLL_OVERSHOOT_Y_PROPERTY_NAME)
174    * @param[in] pageSizeProperty The size of the page. (scrollView SIZE)
175    * @return The new orientation of this Actor.
176    */
177   Quaternion operator()(const Quaternion& current,
178                         const PropertyInput& actorPositionProperty,
179                         const PropertyInput& scrollablePositionProperty,
180                         const PropertyInput& scrollOvershootXProperty,
181                         const PropertyInput& scrollOvershootYProperty,
182                         const PropertyInput& pageSizeProperty)
183   {
184     const Vector3& position = actorPositionProperty.GetVector3();
185     const Vector3& parentPosition = scrollablePositionProperty.GetVector3();
186     const Vector3& pageSize = pageSizeProperty.GetVector3();
187     const Vector2 overshoot(scrollOvershootXProperty.GetFloat(), scrollOvershootYProperty.GetFloat());
188
189     if(fabsf(overshoot.x) < Math::MACHINE_EPSILON_0 && fabsf(overshoot.y) < Math::MACHINE_EPSILON_0)
190     {
191       return current;
192     }
193
194     // get distance from centre of scrollable container
195     Vector2 distance = position.GetVectorXY() - parentPosition.GetVectorXY();
196
197     if( overshoot.x > 0.0f )
198     {
199       distance.x += pageSize.x * 0.5f;
200     }
201     else
202     {
203       distance.x -= pageSize.x * 0.5f;
204     }
205     distance.x = Clamp(fabsf(distance.x), 0.0f, mDropOffDistance.x);
206
207     if( overshoot.y > 0.0f )
208     {
209       distance.y += pageSize.y * 0.5f;
210     }
211     else
212     {
213       distance.y -= pageSize.y * 0.5f;
214     }
215     distance.y = Clamp(fabsf(distance.y), 0.0f, mDropOffDistance.y);
216
217     Vector2 angleMod = distance / mDropOffDistance;
218     if(mDropOffFunction)
219     {
220       angleMod.x = mDropOffFunction(angleMod.x);
221       angleMod.y = mDropOffFunction(angleMod.y);
222     }
223     angleMod = Vector2::ONE - (angleMod * mDropOff);
224
225     Vector2 angle = angleMod * mAngleSwing * overshoot;
226
227     Quaternion rotation = Quaternion(angle.x, Vector3::YAXIS) *
228                           Quaternion(-angle.y, Vector3::XAXIS) *
229                           current;
230
231     return rotation;
232   }
233
234   const Vector2 mAngleSwing;                                    ///< Maximum amount in X and Y axes to rotate.
235   const Vector2 mDropOff;
236   const Vector2 mDropOffDistance;
237   AlphaFunction mDropOffFunction;
238 };
239
240 /**
241  * ScrollTwistRotationConstraint
242  *
243  * Rotate constraint adjusts the angle of the Actors
244  * based on actor's world-position relative to the middle of the screen.
245  * When at middle of screen Angles on X and Y Axes is 0.
246  * When one screen away from the middle Angle is 90 degrees (pi/2)
247  */
248 struct ScrollTwistRotationConstraint
249 {
250   /**
251    * Constraint constructor
252    * @param[in] angleSwing The amount the Actor should revolve in radians
253    * for a given page worth of distance.
254    */
255   ScrollTwistRotationConstraint(const Vector2& angleSwing)
256   : mAngleSwing(angleSwing)
257   {
258   }
259
260   /**
261    * @param[in] current The current orientation of this Actor
262    * @param[in] actorPositionProperty The actor's world-position property
263    * @param[in] scrollOvershootXProperty The scroll-view's overshoot property (SCROLL_OVERSHOOT_X_PROPERTY_NAME)
264    * @param[in] scrollOvershootYProperty The scroll-view's overshoot property (SCROLL_OVERSHOOT_Y_PROPERTY_NAME)
265    * @param[in] pageSizeProperty The size of the page. (scrollView SIZE)
266    * @return The new orientation of this Actor.
267    */
268   Quaternion operator()(const Quaternion& current,
269                         const PropertyInput& scrollOvershootXProperty,
270                         const PropertyInput& scrollOvershootYProperty)
271   {
272     const Vector2 overshoot(scrollOvershootXProperty.GetFloat(), scrollOvershootYProperty.GetFloat());
273
274     if( fabsf(overshoot.x) < Math::MACHINE_EPSILON_0 && fabsf(overshoot.y) < Math::MACHINE_EPSILON_0 )
275     {
276       return current;
277     }
278
279     Quaternion rotation = Quaternion(overshoot.x * mAngleSwing.x, Vector3::YAXIS) *
280                           Quaternion(-overshoot.y * mAngleSwing.y, Vector3::XAXIS) *
281                           current;
282
283     return rotation;
284   }
285
286   const Vector2 mAngleSwing;
287 };
288
289 /**
290  * ScrollTwistPositionConstraint
291  *
292  * Position constraint adjusts the position of the Actors
293  * based on their parent page's position relative to the middle of the screen.
294  * When at middle of the screen the position is not altered.
295  * When one screen away from middle the position is rotated about it's origin + mAnchor
296  */
297 struct ScrollTwistPositionConstraint
298 {
299   /**
300    * Constraint constructor
301    */
302   ScrollTwistPositionConstraint(float delayMin, float delayMax)
303   : mDelayMin(delayMin),
304     mDelayMax(delayMax),
305     mCurrentDelayFactor(0.0f)
306   {
307   }
308
309   /**
310    * @param[in] current The current position
311    * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME)
312    * @param[in] effectReferenceProperty The point in the scroll-view where the user touched the screen
313    * @param[in] effectTimeProperty The current timer. Starting from 0.0 when scroll animation/dragging
314    * commences. Ensures that constraint is applied and processed every frame (to achieve the delay effect)
315    * @param[in] sizeProperty The size of the ScrollView.
316    * @return The new position of this Actor.
317    */
318   Vector3 operator()(const Vector3& current,
319                      const PropertyInput& pagePositionProperty,
320                      const PropertyInput& scrollPositionProperty,
321                      const PropertyInput& effectReferenceProperty,
322                      const PropertyInput& effectTimeProperty,
323                      const PropertyInput& sizeProperty,
324                      const PropertyInput& activateProperty)
325   {
326     const Vector3& scrollPosition = scrollPositionProperty.GetVector3();
327     const float& activate = activateProperty.GetFloat();
328
329     if(activate < Math::MACHINE_EPSILON_0)
330     {
331       mScrollPosition = scrollPosition;
332       return current + mScrollPosition;
333     }
334     const Vector3& pagePosition = pagePositionProperty.GetVector3();
335     const Vector3& referencePoint = effectReferenceProperty.GetVector3();
336     // Determine the relative position of the actor from the scrolling reference point.
337     // (the further away from the refernce, the longer the delay should be)
338     Vector3 relativePosition = pagePosition + current - referencePoint;
339     float f = relativePosition.x;
340
341     // f represents this absolute distance. Get as a relative distance and inverse exponential
342     // (as delay equation is has an exponential effect i.e. the closer delayFactor to 1.0f,
343     // the longer the delay would appear exponentially)
344     f = fabsf(f / sizeProperty.GetVector3().width);
345     f = std::min(f, 1.0f);
346     f = 1.0f - (1.0f - f) * (1.0f - f);
347     // at center delay factor is mDelayMin, at maximum (1.0) it is mDelayMax
348     f = Rescale(f, 0.0f, 1.0f, mDelayMin, mDelayMax);
349
350     // Will take 0.25s for current delay factor to equal target delay factor
351     // This prevents users quickly dragging from different points and noticing a jerk.
352     mCurrentDelayFactor = Chase( mCurrentDelayFactor, f, 4.0f/60.0f );
353     float delay = activate * mCurrentDelayFactor;
354     mScrollPosition = mScrollPosition * delay + scrollPosition * (1.0f-delay);
355
356     return current + mScrollPosition;
357   }
358
359 private:
360
361   Vector3 mScrollPosition;              ///< The current scroll position
362   float mDelayMin;
363   float mDelayMax;
364   float mCurrentDelayFactor;
365
366 };
367
368 /**
369  * ScrollTwistScaleConstraint
370  *
371  * Scale constraint adjusts the scale of the Actors
372  * based on a supplied depth property value.
373  */
374 struct ScrollTwistScaleConstraint
375 {
376   /**
377    * Constraint constructor
378    */
379   ScrollTwistScaleConstraint(float scaleAmount)
380   : mScaleAmount(scaleAmount)
381   {
382   }
383
384   /**
385    * @param[in] current The current position
386    * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME)
387    * @return The new position of this Actor.
388    */
389   Vector3 operator()(const Vector3& current,
390                      const PropertyInput& depthProperty)
391   {
392     float depth = depthProperty.GetFloat();
393
394     return current * (1.0f - depth * mScaleAmount); // contract by mScaleAmount of original size.
395   }
396
397 private:
398
399   float mScaleAmount;
400 };
401
402 } // unnamed namespace
403
404 namespace Dali
405 {
406
407 namespace Toolkit
408 {
409
410 namespace Internal
411 {
412
413 ScrollViewTwistEffect::ScrollViewTwistEffect()
414 : mFlags(DefaultFlags),
415   mPropertyTime(Property::INVALID_INDEX),
416   mEnableEffect(true),
417   mAdditionalEffects(false),
418   mPropertyReference(Property::INVALID_INDEX),
419   mPropertyActivate(Property::INVALID_INDEX),
420   mMinimumDistanceForShrink(Toolkit::ScrollViewTwistEffect::DEFAULT_MINIMUM_DISTANCE_FOR_SHRINK),
421   mMaxSwingAngle(Math::PI_2, Math::PI_2),
422   mDropOff(TWISTEFFECT_DEFAULT_DROPOFF, TWISTEFFECT_DEFAULT_DROPOFF),
423   mDropOffDistance(TWISTEFFECT_DEFAULT_DROPOFF_DISTANCE_X, TWISTEFFECT_DEFAULT_DROPOFF_DISTANCE_Y),
424   mDropOffFunction(NULL)
425 {
426 }
427
428 ScrollViewTwistEffect::~ScrollViewTwistEffect()
429 {
430 }
431
432
433 float ScrollViewTwistEffect::GetMinimumDistanceForShrink() const
434 {
435   return mMinimumDistanceForShrink;
436 }
437
438 void ScrollViewTwistEffect::SetMinimumDistanceForShrink(float distance)
439 {
440   mMinimumDistanceForShrink = distance;
441 }
442
443 void ScrollViewTwistEffect::EnableEffect(bool enableFlag)
444 {
445   mEnableEffect = enableFlag;
446 }
447
448 void ScrollViewTwistEffect::ApplyToActor(Actor child,
449                                          bool additionalEffects,
450                                          const Vector2& angleSwing,
451                                          float scaleAmount,
452                                          float delayMin,
453                                          float delayMax)
454 {
455   mMaxSwingAngle = angleSwing;
456   mAdditionalEffects = additionalEffects;
457   mScaleFactor = scaleAmount;
458   mDelayMin = delayMin;
459   mDelayMax = delayMax;
460   if(mFlags & FlagDefaultDropOff)
461   {
462     Vector3 size = GetScrollView().GetCurrentSize();
463     // size may still be 0 if the effect is applied before scroll view hits the stage
464     if(size.x > Math::MACHINE_EPSILON_1)
465     {
466       mDropOffDistance.x = size.x;
467     }
468     if(size.y > Math::MACHINE_EPSILON_1)
469     {
470       mDropOffDistance.y = size.y;
471     }
472   }
473   if(scaleAmount > Math::MACHINE_EPSILON_0)
474   {
475     mFlags |= FlagScale;
476   }
477   else
478   {
479     mFlags = mFlags & ~FlagScale;
480   }
481   if(mMaxSwingAngle.LengthSquared() > Math::MACHINE_EPSILON_0)
482   {
483     mFlags |= FlagTwist;
484   }
485   else
486   {
487     mFlags = mFlags & ~FlagTwist;
488   }
489   Apply(child);
490 }
491
492 void ScrollViewTwistEffect::Apply(Actor child)
493 {
494   // Apply constraints to these actors //
495   Constraint constraint;
496
497   Toolkit::ScrollView scrollView = GetScrollView();
498
499   if( mFlags & FlagScale )
500   {
501     constraint = Constraint::New<Vector3>( Actor::SCALE,
502                                            Source(scrollView, scrollView.GetPropertyIndex( EFFECT_DEPTH ) ),
503                                            ScrollTwistScaleConstraint(mScaleFactor) );
504     constraint.SetRemoveAction( Constraint::Discard );
505     child.ApplyConstraint( constraint );
506   }
507
508   constraint = Constraint::New<Vector3>( Actor::POSITION,
509                                           ParentSource(Actor::POSITION),
510                                           Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_PROPERTY_NAME ) ),
511                                           Source(scrollView, scrollView.GetPropertyIndex( EFFECT_REFERENCE ) ),
512                                           Source(scrollView, scrollView.GetPropertyIndex( EFFECT_TIME ) ),
513                                           Source(scrollView, Actor::SIZE),
514                                           Source(scrollView, scrollView.GetPropertyIndex( EFFECT_ACTIVATE) ),
515                                          ScrollTwistPositionConstraint(mDelayMin, mDelayMax) );
516   constraint.SetRemoveAction( Constraint::Discard );
517   child.ApplyConstraint( constraint );
518
519   // use actor position to affect rotation
520   if(mFlags & FlagTwist)
521   {
522     if(mFlags & FlagDropOff)
523     {
524       constraint = Constraint::New<Quaternion>( Actor::ROTATION,
525                                                 LocalSource(Actor::WORLD_POSITION),
526                                                 Source(scrollView, Actor::WORLD_POSITION ),
527                                                 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_OVERSHOOT_X_PROPERTY_NAME ) ),
528                                                 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_OVERSHOOT_Y_PROPERTY_NAME ) ),
529                                                 Source(scrollView, Actor::SIZE ),
530                                                 ScrollDropoffTwistRotationConstraint(mMaxSwingAngle, mDropOff, mDropOffDistance, mDropOffFunction) );
531     }
532     else
533     {
534       constraint = Constraint::New<Quaternion>( Actor::ROTATION,
535                                                 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_OVERSHOOT_X_PROPERTY_NAME ) ),
536                                                 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_OVERSHOOT_Y_PROPERTY_NAME ) ),
537                                                 ScrollTwistRotationConstraint(mMaxSwingAngle) );
538     }
539     constraint.SetRemoveAction( Constraint::Discard );
540     child.ApplyConstraint( constraint );
541   }
542 }
543
544 void ScrollViewTwistEffect::SetMaxSwingAngle(const Vector2& maxSwingAngle)
545 {
546   mMaxSwingAngle = maxSwingAngle;
547 }
548
549 Vector2 ScrollViewTwistEffect::GetMaxSwingAngle() const
550 {
551   return mMaxSwingAngle;
552 }
553
554 void ScrollViewTwistEffect::SetSwingDropOff(const Vector2& dropOff, const Vector2& distance, AlphaFunction function)
555 {
556   if( distance.LengthSquared() > Math::MACHINE_EPSILON_1 && dropOff.LengthSquared() > Math::MACHINE_EPSILON_1 )
557   {
558     mFlags |= FlagDropOff;
559     mDropOff = dropOff;
560     mDropOffDistance = distance;
561     mDropOffFunction = function;
562   }
563   else
564   {
565     mFlags = mFlags & ~FlagDropOff;
566   }
567   // can no longer use default dop off
568   mFlags = mFlags & ~FlagDefaultDropOff;
569 }
570
571 void ScrollViewTwistEffect::GetSwingDropOff( Vector2& dropOff, Vector2& distance, AlphaFunction& function ) const
572 {
573   dropOff = mDropOff;
574   distance = mDropOffDistance;
575   function = mDropOffFunction;
576 }
577
578 void ScrollViewTwistEffect::OnAttach(Toolkit::ScrollView& scrollView)
579 {
580   // Create effect-time property if not already created.
581   if(mPropertyTime == Property::INVALID_INDEX)
582   {
583     mPropertyTime = SafeRegisterProperty( scrollView, EFFECT_TIME, 0.0f );
584     mPropertyReference = SafeRegisterProperty( scrollView, EFFECT_REFERENCE, Vector3::ZERO );
585     mPropertyDepth = SafeRegisterProperty( scrollView, EFFECT_DEPTH, 0.0f);
586     mPropertyActivate = SafeRegisterProperty(scrollView, EFFECT_ACTIVATE, 0.0f);
587   }
588   // currently cant change overshoot snap back duration, use the constant one from ScrollView
589   mActivationTime = Toolkit::ScrollView::DEFAULT_SNAP_OVERSHOOT_DURATION;
590
591   // Connect to the scroll view signals
592   scrollView.ScrollStartedSignal().Connect(this, &ScrollViewTwistEffect::OnScrollStart);
593   scrollView.SnapStartedSignal().Connect(this, &ScrollViewTwistEffect::OnScrollSnap);
594   scrollView.ScrollUpdatedSignal().Connect(this, &ScrollViewTwistEffect::OnScrollUpdate);
595   scrollView.ScrollCompletedSignal().Connect(this, &ScrollViewTwistEffect::OnScrollComplete);
596
597   AttachActor(scrollView);
598 }
599
600 void ScrollViewTwistEffect::OnDetach(Toolkit::ScrollView& scrollView)
601 {
602   scrollView.ScrollStartedSignal().Disconnect(this, &ScrollViewTwistEffect::OnScrollStart);
603   scrollView.SnapStartedSignal().Disconnect(this, &ScrollViewTwistEffect::OnScrollSnap);
604   scrollView.ScrollUpdatedSignal().Disconnect(this, &ScrollViewTwistEffect::OnScrollUpdate);
605   scrollView.ScrollCompletedSignal().Disconnect(this, &ScrollViewTwistEffect::OnScrollComplete);
606
607   if(mAnimation)
608   {
609     mAnimation.FinishedSignal().Disconnect(this, &ScrollViewTwistEffect::OnAnimationFinished);
610     mAnimation.Clear();
611     mAnimation.Reset();
612   }
613 }
614
615 void ScrollViewTwistEffect::AttachActor(Actor actor)
616 {
617
618 }
619
620 void ScrollViewTwistEffect::DetachActor(Actor actor)
621 {
622   // TODO: remove the specific constraint defined in AttachActor (and possibly
623   // unregister property) - neither functionality exists in Dali.
624 }
625
626 void ScrollViewTwistEffect::ContinueAnimation(float endTime)
627 {
628   // continue animating
629   if(mAnimation)
630   {
631     mAnimation.FinishedSignal().Disconnect(this, &ScrollViewTwistEffect::OnAnimationFinished);
632     mAnimation.Clear();
633   }
634
635   Actor scrollView = GetScrollView();
636
637   mAnimation = Animation::New(TWISTEFFECT_ANIMATION_MAX_TIME);
638   mAnimation.AnimateTo( Property(scrollView, mPropertyTime), endTime, AlphaFunctions::Linear );
639   mAnimation.FinishedSignal().Connect(this, &ScrollViewTwistEffect::OnAnimationFinished);
640   mAnimation.Play();
641 }
642
643 void ScrollViewTwistEffect::OnScrollStart( const Vector3& position )
644 {
645   if(mActivateAnimation)
646   {
647     // if the animation after Scroll complete not terminate before another scroll action, stop the animation before start again
648     mActivateAnimation.Stop();
649     mActivateAnimation.Clear();
650     mActivateAnimation = NULL;
651   }
652
653   GetScrollView().SetProperty(mPropertyTime, 0.0f);
654   if(mEnableEffect)
655   {
656     GetScrollView().SetProperty(mPropertyActivate, 1.0f);
657   }
658   else
659   {
660     GetScrollView().SetProperty(mPropertyActivate, 0.0f);
661   }
662   GetScrollView().SetProperty(mPropertyReference, position);
663
664   ContinueAnimation(TWISTEFFECT_ANIMATION_MAX_TIME);
665 }
666
667 void ScrollViewTwistEffect::OnScrollUpdate( const Vector3& position )
668 {
669   // nothing to do
670 }
671
672 void ScrollViewTwistEffect::OnScrollComplete( const Vector3& position )
673 {
674   if(!mEnableEffect)
675   {
676     OnActivateAnimationFinished(mAnimation);
677     return;
678   }
679   Actor scrollView = GetScrollView();
680   scrollView.SetProperty(mPropertyActivate, 1.0f);
681   mActivateAnimation = Animation::New(mActivationTime);
682   mActivateAnimation.AnimateTo( Property(scrollView, mPropertyActivate), 0.0f, AlphaFunctions::Linear);
683   mActivateAnimation.FinishedSignal().Connect(this, &ScrollViewTwistEffect::OnActivateAnimationFinished);
684   mActivateAnimation.Play();
685 }
686
687 void ScrollViewTwistEffect::OnScrollSnap( const Toolkit::ScrollView::SnapEvent& event )
688 {
689   // If a Flicking snap is occuring and the distance is more than mMinimumDistanceForShrink
690   // then animate depth effect i.e. shrink actors and then bring back in to regular size.
691   // NOTE: ScrollView Snap returns a value opposite of GetCurrentScrollPosition
692   // i.e. if you've "scrolled 100 pixels right" (so content on screen has shifted 100 pixels left)
693   // then GetCurrentScrollPosition returns a positive value (100.0f, 0.0f) (position of where you're
694   // look, not where content has been moved to).
695   // event.position returns a negative value (-100.0f, 0.0f)
696   // Would be a good idea to change SnapEvent in the API so it reflects GetCurrentScrollPosition.
697   // TODO: Change scroll-view API, check if anything uses SnapEvent and change them correspondingly
698   Vector3 targetScrollPosition(-event.position);
699
700   Vector3 delta = targetScrollPosition - GetScrollView().GetCurrentScrollPosition();
701
702   if(event.type==Flick && delta.Length() > mMinimumDistanceForShrink)
703   {
704     Actor scrollView = GetScrollView();
705
706     Animation animation = Animation::New(event.duration);
707     animation.AnimateTo( Property(scrollView, mPropertyDepth), 1.0f, HopEasing);
708     animation.Play();
709   }
710 }
711
712 void ScrollViewTwistEffect::OnAnimationFinished( Animation& animation )
713 {
714   // still unstable, so continue animating.
715   // TODO: Requires an instability check to ensure time animation finishes when delay is
716   // less noticeable. i.e. all present scrollPositions are approx the same as mScrollPosition in constraints.
717   // best solution for this is to switch to a single history vector of scroll position, and compare if
718   // position has not deviated >= 0.5 pixel for the past 1 second.
719   float endTime = GetScrollView().GetProperty<float>(mPropertyTime) + TWISTEFFECT_ANIMATION_MAX_TIME;
720   ContinueAnimation(endTime);
721 }
722
723 void ScrollViewTwistEffect::OnActivateAnimationFinished( Animation& animation )
724 {
725   if(mAnimation)
726   {
727     mAnimation.FinishedSignal().Disconnect(this, &ScrollViewTwistEffect::OnAnimationFinished);
728     mAnimation.Clear();
729     mAnimation.Reset();
730   }
731 }
732
733
734 } // namespace Internal
735
736 } // namespace Toolkit
737
738 } // namespace Dali