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