License conversion from Flora to Apache 2.0
[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                         const PropertyInput& activateProperty)
184   {
185     const Vector3& position = actorPositionProperty.GetVector3();
186     const Vector3& parentPosition = scrollablePositionProperty.GetVector3();
187     const Vector3& pageSize = pageSizeProperty.GetVector3();
188     const Vector2 overshoot(scrollOvershootXProperty.GetFloat(), scrollOvershootYProperty.GetFloat());
189
190     if(fabsf(overshoot.x) < Math::MACHINE_EPSILON_0 && fabsf(overshoot.y) < Math::MACHINE_EPSILON_0)
191     {
192       return current;
193     }
194
195     const float& activate = activateProperty.GetFloat();
196
197     if(activate < Math::MACHINE_EPSILON_0)
198     {
199       return current;
200     }
201
202     // get distance from centre of scrollable container
203     Vector2 distance = position.GetVectorXY() - parentPosition.GetVectorXY();
204
205     if( overshoot.x > 0.0f )
206     {
207       distance.x += pageSize.x * 0.5f;
208     }
209     else
210     {
211       distance.x -= pageSize.x * 0.5f;
212     }
213     distance.x = Clamp(fabsf(distance.x), 0.0f, mDropOffDistance.x);
214
215     if( overshoot.y > 0.0f )
216     {
217       distance.y += pageSize.y * 0.5f;
218     }
219     else
220     {
221       distance.y -= pageSize.y * 0.5f;
222     }
223     distance.y = Clamp(fabsf(distance.y), 0.0f, mDropOffDistance.y);
224
225     Vector2 angleMod = distance / mDropOffDistance;
226     if(mDropOffFunction)
227     {
228       angleMod.x = mDropOffFunction(angleMod.x);
229       angleMod.y = mDropOffFunction(angleMod.y);
230     }
231     angleMod = Vector2::ONE - (angleMod * mDropOff);
232
233     Vector2 angle = angleMod * mAngleSwing * overshoot;
234
235     Quaternion rotation = Quaternion(angle.x, Vector3::YAXIS) *
236                           Quaternion(-angle.y, Vector3::XAXIS) *
237                           current;
238
239     return rotation;
240   }
241
242   const Vector2 mAngleSwing;                                    ///< Maximum amount in X and Y axes to rotate.
243   const Vector2 mDropOff;
244   const Vector2 mDropOffDistance;
245   AlphaFunction mDropOffFunction;
246 };
247
248 /**
249  * ScrollTwistRotationConstraint
250  *
251  * Rotate constraint adjusts the angle of the Actors
252  * based on actor's world-position relative to the middle of the screen.
253  * When at middle of screen Angles on X and Y Axes is 0.
254  * When one screen away from the middle Angle is 90 degrees (pi/2)
255  */
256 struct ScrollTwistRotationConstraint
257 {
258   /**
259    * Constraint constructor
260    * @param[in] angleSwing The amount the Actor should revolve in radians
261    * for a given page worth of distance.
262    */
263   ScrollTwistRotationConstraint(const Vector2& angleSwing)
264   : mAngleSwing(angleSwing)
265   {
266   }
267
268   /**
269    * @param[in] current The current orientation of this Actor
270    * @param[in] actorPositionProperty The actor's world-position property
271    * @param[in] scrollOvershootXProperty The scroll-view's overshoot property (SCROLL_OVERSHOOT_X_PROPERTY_NAME)
272    * @param[in] scrollOvershootYProperty The scroll-view's overshoot property (SCROLL_OVERSHOOT_Y_PROPERTY_NAME)
273    * @param[in] pageSizeProperty The size of the page. (scrollView SIZE)
274    * @return The new orientation of this Actor.
275    */
276   Quaternion operator()(const Quaternion& current,
277                         const PropertyInput& scrollOvershootXProperty,
278                         const PropertyInput& scrollOvershootYProperty,
279                         const PropertyInput& activateProperty)
280   {
281     const Vector2 overshoot(scrollOvershootXProperty.GetFloat(), scrollOvershootYProperty.GetFloat());
282
283     if(fabsf(overshoot.x) < Math::MACHINE_EPSILON_0 && fabsf(overshoot.y) < Math::MACHINE_EPSILON_0)
284     {
285       return current;
286     }
287
288     const float& activate = activateProperty.GetFloat();
289
290     if(activate < Math::MACHINE_EPSILON_0)
291     {
292       return current;
293     }
294
295     Quaternion rotation = Quaternion(overshoot.x * mAngleSwing.x, Vector3::YAXIS) *
296                           Quaternion(-overshoot.y * mAngleSwing.y, Vector3::XAXIS) *
297                           current;
298
299     return rotation;
300   }
301
302   const Vector2 mAngleSwing;
303 };
304
305 /**
306  * ScrollTwistPositionConstraint
307  *
308  * Position constraint adjusts the position of the Actors
309  * based on their parent page's position relative to the middle of the screen.
310  * When at middle of the screen the position is not altered.
311  * When one screen away from middle the position is rotated about it's origin + mAnchor
312  */
313 struct ScrollTwistPositionConstraint
314 {
315   /**
316    * Constraint constructor
317    */
318   ScrollTwistPositionConstraint(float delayMin, float delayMax)
319   : mDelayMin(delayMin),
320     mDelayMax(delayMax),
321     mCurrentDelayFactor(0.0f)
322   {
323   }
324
325   /**
326    * @param[in] current The current position
327    * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME)
328    * @param[in] effectReferenceProperty The point in the scroll-view where the user touched the screen
329    * @param[in] effectTimeProperty The current timer. Starting from 0.0 when scroll animation/dragging
330    * commences. Ensures that constraint is applied and processed every frame (to achieve the delay effect)
331    * @param[in] sizeProperty The size of the ScrollView.
332    * @return The new position of this Actor.
333    */
334   Vector3 operator()(const Vector3& current,
335                      const PropertyInput& pagePositionProperty,
336                      const PropertyInput& scrollPositionProperty,
337                      const PropertyInput& effectReferenceProperty,
338                      const PropertyInput& effectTimeProperty,
339                      const PropertyInput& sizeProperty,
340                      const PropertyInput& activateProperty)
341   {
342     const Vector3& scrollPosition = scrollPositionProperty.GetVector3();
343     const float& activate = activateProperty.GetFloat();
344
345     if(activate < Math::MACHINE_EPSILON_0)
346     {
347       mScrollPosition = scrollPosition;
348       return current + mScrollPosition;
349     }
350     const Vector3& pagePosition = pagePositionProperty.GetVector3();
351     const Vector3& referencePoint = effectReferenceProperty.GetVector3();
352     // Determine the relative position of the actor from the scrolling reference point.
353     // (the further away from the refernce, the longer the delay should be)
354     Vector3 relativePosition = pagePosition + current - referencePoint;
355     float f = relativePosition.x;
356
357     // f represents this absolute distance. Get as a relative distance and inverse exponential
358     // (as delay equation is has an exponential effect i.e. the closer delayFactor to 1.0f,
359     // the longer the delay would appear exponentially)
360     f = fabsf(f / sizeProperty.GetVector3().width);
361     f = std::min(f, 1.0f);
362     f = 1.0f - (1.0f - f) * (1.0f - f);
363     // at center delay factor is mDelayMin, at maximum (1.0) it is mDelayMax
364     f = Rescale(f, 0.0f, 1.0f, mDelayMin, mDelayMax);
365
366     // Will take 0.25s for current delay factor to equal target delay factor
367     // This prevents users quickly dragging from different points and noticing a jerk.
368     mCurrentDelayFactor = Chase( mCurrentDelayFactor, f, 4.0f/60.0f );
369     float delay = activate * mCurrentDelayFactor;
370     mScrollPosition = mScrollPosition * delay + scrollPosition * (1.0f-delay);
371
372     return current + mScrollPosition;
373   }
374
375 private:
376
377   Vector3 mScrollPosition;              ///< The current scroll position
378   float mDelayMin;
379   float mDelayMax;
380   float mCurrentDelayFactor;
381
382 };
383
384 /**
385  * ScrollTwistScaleConstraint
386  *
387  * Scale constraint adjusts the scale of the Actors
388  * based on a supplied depth property value.
389  */
390 struct ScrollTwistScaleConstraint
391 {
392   /**
393    * Constraint constructor
394    */
395   ScrollTwistScaleConstraint(float scaleAmount)
396   : mScaleAmount(scaleAmount)
397   {
398   }
399
400   /**
401    * @param[in] current The current position
402    * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME)
403    * @return The new position of this Actor.
404    */
405   Vector3 operator()(const Vector3& current,
406                      const PropertyInput& depthProperty)
407   {
408     float depth = depthProperty.GetFloat();
409
410     return current * (1.0f - depth * mScaleAmount); // contract by mScaleAmount of original size.
411   }
412
413 private:
414
415   float mScaleAmount;
416 };
417
418 } // unnamed namespace
419
420 namespace Dali
421 {
422
423 namespace Toolkit
424 {
425
426 namespace Internal
427 {
428
429 ScrollViewTwistEffect::ScrollViewTwistEffect()
430 : mFlags(DefaultFlags),
431   mPropertyTime(Property::INVALID_INDEX),
432   mEnableEffect(true),
433   mAdditionalEffects(false),
434   mPropertyReference(Property::INVALID_INDEX),
435   mPropertyActivate(Property::INVALID_INDEX),
436   mMinimumDistanceForShrink(Toolkit::ScrollViewTwistEffect::DEFAULT_MINIMUM_DISTANCE_FOR_SHRINK),
437   mMaxSwingAngle(Math::PI_2, Math::PI_2),
438   mDropOff(TWISTEFFECT_DEFAULT_DROPOFF, TWISTEFFECT_DEFAULT_DROPOFF),
439   mDropOffDistance(TWISTEFFECT_DEFAULT_DROPOFF_DISTANCE_X, TWISTEFFECT_DEFAULT_DROPOFF_DISTANCE_Y),
440   mDropOffFunction(NULL)
441 {
442 }
443
444 ScrollViewTwistEffect::~ScrollViewTwistEffect()
445 {
446 }
447
448
449 float ScrollViewTwistEffect::GetMinimumDistanceForShrink() const
450 {
451   return mMinimumDistanceForShrink;
452 }
453
454 void ScrollViewTwistEffect::SetMinimumDistanceForShrink(float distance)
455 {
456   mMinimumDistanceForShrink = distance;
457 }
458
459 void ScrollViewTwistEffect::EnableEffect(bool enableFlag)
460 {
461   mEnableEffect = enableFlag;
462 }
463
464 void ScrollViewTwistEffect::ApplyToActor(Actor child,
465                                          bool additionalEffects,
466                                          const Vector2& angleSwing,
467                                          float scaleAmount,
468                                          float delayMin,
469                                          float delayMax)
470 {
471   mMaxSwingAngle = angleSwing;
472   mAdditionalEffects = additionalEffects;
473   mScaleFactor = scaleAmount;
474   mDelayMin = delayMin;
475   mDelayMax = delayMax;
476   if(mFlags & FlagDefaultDropOff)
477   {
478     Vector3 size = GetScrollView().GetCurrentSize();
479     // size may still be 0 if the effect is applied before scroll view hits the stage
480     if(size.x > Math::MACHINE_EPSILON_1)
481     {
482       mDropOffDistance.x = size.x;
483     }
484     if(size.y > Math::MACHINE_EPSILON_1)
485     {
486       mDropOffDistance.y = size.y;
487     }
488   }
489   if(scaleAmount > Math::MACHINE_EPSILON_0)
490   {
491     mFlags |= FlagScale;
492   }
493   else
494   {
495     mFlags = mFlags & ~FlagScale;
496   }
497   if(mMaxSwingAngle.LengthSquared() > Math::MACHINE_EPSILON_0)
498   {
499     mFlags |= FlagTwist;
500   }
501   else
502   {
503     mFlags = mFlags & ~FlagTwist;
504   }
505   Apply(child);
506 }
507
508 void ScrollViewTwistEffect::Apply(Actor child)
509 {
510   // Apply constraints to these actors //
511   Constraint constraint;
512
513   Toolkit::ScrollView scrollView = GetScrollView();
514
515   if( mFlags & FlagScale )
516   {
517     constraint = Constraint::New<Vector3>( Actor::SCALE,
518                                            Source(scrollView, scrollView.GetPropertyIndex( EFFECT_DEPTH ) ),
519                                            ScrollTwistScaleConstraint(mScaleFactor) );
520     constraint.SetRemoveAction( Constraint::Discard );
521     child.ApplyConstraint( constraint );
522   }
523
524   constraint = Constraint::New<Vector3>( Actor::POSITION,
525                                           ParentSource(Actor::POSITION),
526                                           Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_PROPERTY_NAME ) ),
527                                           Source(scrollView, scrollView.GetPropertyIndex( EFFECT_REFERENCE ) ),
528                                           Source(scrollView, scrollView.GetPropertyIndex( EFFECT_TIME ) ),
529                                           Source(scrollView, Actor::SIZE),
530                                           Source(scrollView, scrollView.GetPropertyIndex( EFFECT_ACTIVATE) ),
531                                          ScrollTwistPositionConstraint(mDelayMin, mDelayMax) );
532   constraint.SetRemoveAction( Constraint::Discard );
533   child.ApplyConstraint( constraint );
534
535   // use actor position to affect rotation
536   if(mFlags & FlagTwist)
537   {
538     if(mFlags & FlagDropOff)
539     {
540       constraint = Constraint::New<Quaternion>( Actor::ROTATION,
541                                                 LocalSource(Actor::WORLD_POSITION),
542                                                 Source(scrollView, Actor::WORLD_POSITION ),
543                                                 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_OVERSHOOT_X_PROPERTY_NAME ) ),
544                                                 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_OVERSHOOT_Y_PROPERTY_NAME ) ),
545                                                 Source(scrollView, Actor::SIZE ),
546                                                 Source(scrollView, scrollView.GetPropertyIndex( EFFECT_ACTIVATE) ),
547                                                 ScrollDropoffTwistRotationConstraint(mMaxSwingAngle, mDropOff, mDropOffDistance, mDropOffFunction) );
548     }
549     else
550     {
551       constraint = Constraint::New<Quaternion>( Actor::ROTATION,
552                                                 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_OVERSHOOT_X_PROPERTY_NAME ) ),
553                                                 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_OVERSHOOT_Y_PROPERTY_NAME ) ),
554                                                 Source(scrollView, scrollView.GetPropertyIndex( EFFECT_ACTIVATE) ),
555                                                 ScrollTwistRotationConstraint(mMaxSwingAngle) );
556     }
557     constraint.SetRemoveAction( Constraint::Discard );
558     child.ApplyConstraint( constraint );
559   }
560 }
561
562 void ScrollViewTwistEffect::SetSwingDropOff(const Vector2& dropOff, const Vector2& distance, AlphaFunction function)
563 {
564   if( mDropOffDistance.LengthSquared() > Math::MACHINE_EPSILON_1 && mDropOff.LengthSquared() > Math::MACHINE_EPSILON_1 )
565   {
566     mFlags |= FlagDropOff;
567     mDropOff = dropOff;
568     mDropOffDistance = distance;
569     mDropOffFunction = function;
570   }
571   else
572   {
573     mFlags = mFlags & ~FlagDropOff;
574   }
575   // can no longer use default dop off
576   mFlags = mFlags & ~FlagDefaultDropOff;
577 }
578
579 void ScrollViewTwistEffect::OnAttach(Toolkit::ScrollView& scrollView)
580 {
581   // Create effect-time property if not already created.
582   if(mPropertyTime == Property::INVALID_INDEX)
583   {
584     mPropertyTime = SafeRegisterProperty( scrollView, EFFECT_TIME, 0.0f );
585     mPropertyReference = SafeRegisterProperty( scrollView, EFFECT_REFERENCE, Vector3::ZERO );
586     mPropertyDepth = SafeRegisterProperty( scrollView, EFFECT_DEPTH, 0.0f);
587     mPropertyActivate = SafeRegisterProperty(scrollView, EFFECT_ACTIVATE, 1.0f);
588   }
589
590   // Connect to the scroll view signals
591   scrollView.ScrollStartedSignal().Connect(this, &ScrollViewTwistEffect::OnScrollStart);
592   scrollView.SnapStartedSignal().Connect(this, &ScrollViewTwistEffect::OnScrollSnap);
593   scrollView.ScrollUpdatedSignal().Connect(this, &ScrollViewTwistEffect::OnScrollUpdate);
594   scrollView.ScrollCompletedSignal().Connect(this, &ScrollViewTwistEffect::OnScrollComplete);
595
596   AttachActor(scrollView);
597 }
598
599 void ScrollViewTwistEffect::OnDetach(Toolkit::ScrollView& scrollView)
600 {
601   scrollView.ScrollStartedSignal().Disconnect(this, &ScrollViewTwistEffect::OnScrollStart);
602   scrollView.SnapStartedSignal().Disconnect(this, &ScrollViewTwistEffect::OnScrollSnap);
603   scrollView.ScrollUpdatedSignal().Disconnect(this, &ScrollViewTwistEffect::OnScrollUpdate);
604   scrollView.ScrollCompletedSignal().Disconnect(this, &ScrollViewTwistEffect::OnScrollComplete);
605
606   if(mAnimation)
607   {
608     mAnimation.FinishedSignal().Disconnect(this, &ScrollViewTwistEffect::OnAnimationFinished);
609     mAnimation.Clear();
610     mAnimation.Reset();
611   }
612 }
613
614 void ScrollViewTwistEffect::AttachActor(Actor actor)
615 {
616
617 }
618
619 void ScrollViewTwistEffect::DetachActor(Actor actor)
620 {
621   // TODO: remove the specific constraint defined in AttachActor (and possibly
622   // unregister property) - neither functionality exists in Dali.
623 }
624
625 void ScrollViewTwistEffect::ContinueAnimation(float endTime)
626 {
627   // continue animating
628   if(mAnimation)
629   {
630     mAnimation.FinishedSignal().Disconnect(this, &ScrollViewTwistEffect::OnAnimationFinished);
631     mAnimation.Clear();
632   }
633
634   Actor scrollView = GetScrollView();
635
636   mAnimation = Animation::New(TWISTEFFECT_ANIMATION_MAX_TIME);
637   mAnimation.AnimateTo( Property(scrollView, mPropertyTime), endTime, AlphaFunctions::Linear );
638   mAnimation.FinishedSignal().Connect(this, &ScrollViewTwistEffect::OnAnimationFinished);
639   mAnimation.Play();
640 }
641
642 void ScrollViewTwistEffect::OnScrollStart( const Vector3& position )
643 {
644   if(mActivateAnimation)
645   {
646     // if the animation after Scroll complete not terminate before another scroll action, stop the animation before start again
647     mActivateAnimation.Stop();
648     mActivateAnimation.Clear();
649     mActivateAnimation = NULL;
650   }
651
652   GetScrollView().SetProperty(mPropertyTime, 0.0f);
653   if(mEnableEffect)
654   {
655     GetScrollView().SetProperty(mPropertyActivate, 1.0f);
656   }
657   else
658   {
659     GetScrollView().SetProperty(mPropertyActivate, 0.0f);
660   }
661   GetScrollView().SetProperty(mPropertyReference, position);
662
663   ContinueAnimation(TWISTEFFECT_ANIMATION_MAX_TIME);
664 }
665
666 void ScrollViewTwistEffect::OnScrollUpdate( const Vector3& position )
667 {
668   // nothing to do
669 }
670
671 void ScrollViewTwistEffect::OnScrollComplete( const Vector3& position )
672 {
673   if(!mEnableEffect)
674   {
675     OnActivateAnimationFinished(mAnimation);
676     return;
677   }
678   Actor scrollView = GetScrollView();
679   scrollView.SetProperty(mPropertyActivate, 1.0f);
680   mActivateAnimation = Animation::New(DELAY);
681   mActivateAnimation.AnimateTo( Property(scrollView, mPropertyActivate), 0.0f, AlphaFunctions::Linear);
682   mActivateAnimation.FinishedSignal().Connect(this, &ScrollViewTwistEffect::OnActivateAnimationFinished);
683   mActivateAnimation.Play();
684 }
685
686 void ScrollViewTwistEffect::OnScrollSnap( const Toolkit::ScrollView::SnapEvent& event )
687 {
688   // If a Flicking snap is occuring and the distance is more than mMinimumDistanceForShrink
689   // then animate depth effect i.e. shrink actors and then bring back in to regular size.
690   // NOTE: ScrollView Snap returns a value opposite of GetCurrentScrollPosition
691   // i.e. if you've "scrolled 100 pixels right" (so content on screen has shifted 100 pixels left)
692   // then GetCurrentScrollPosition returns a positive value (100.0f, 0.0f) (position of where you're
693   // look, not where content has been moved to).
694   // event.position returns a negative value (-100.0f, 0.0f)
695   // Would be a good idea to change SnapEvent in the API so it reflects GetCurrentScrollPosition.
696   // TODO: Change scroll-view API, check if anything uses SnapEvent and change them correspondingly
697   Vector3 targetScrollPosition(-event.position);
698
699   Vector3 delta = targetScrollPosition - GetScrollView().GetCurrentScrollPosition();
700
701   if(event.type==Flick && delta.Length() > mMinimumDistanceForShrink)
702   {
703     Actor scrollView = GetScrollView();
704
705     Animation animation = Animation::New(event.duration);
706     animation.AnimateTo( Property(scrollView, mPropertyDepth), 1.0f, HopEasing);
707     animation.Play();
708   }
709 }
710
711 void ScrollViewTwistEffect::OnAnimationFinished( Animation& animation )
712 {
713   // still unstable, so continue animating.
714   // TODO: Requires an instability check to ensure time animation finishes when delay is
715   // less noticeable. i.e. all present scrollPositions are approx the same as mScrollPosition in constraints.
716   // best solution for this is to switch to a single history vector of scroll position, and compare if
717   // position has not deviated >= 0.5 pixel for the past 1 second.
718   float endTime = GetScrollView().GetProperty<float>(mPropertyTime) + TWISTEFFECT_ANIMATION_MAX_TIME;
719   ContinueAnimation(endTime);
720 }
721
722 void ScrollViewTwistEffect::OnActivateAnimationFinished( Animation& animation )
723 {
724   if(mAnimation)
725   {
726     mAnimation.FinishedSignal().Disconnect(this, &ScrollViewTwistEffect::OnAnimationFinished);
727     mAnimation.Clear();
728     mAnimation.Reset();
729   }
730 }
731
732
733 } // namespace Internal
734
735 } // namespace Toolkit
736
737 } // namespace Dali