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