Added actor creation from json snippet
[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::SetSwingDropOff(const Vector2& dropOff, const Vector2& distance, AlphaFunction function)
545 {
546   if( mDropOffDistance.LengthSquared() > Math::MACHINE_EPSILON_1 && mDropOff.LengthSquared() > Math::MACHINE_EPSILON_1 )
547   {
548     mFlags |= FlagDropOff;
549     mDropOff = dropOff;
550     mDropOffDistance = distance;
551     mDropOffFunction = function;
552   }
553   else
554   {
555     mFlags = mFlags & ~FlagDropOff;
556   }
557   // can no longer use default dop off
558   mFlags = mFlags & ~FlagDefaultDropOff;
559 }
560
561 void ScrollViewTwistEffect::OnAttach(Toolkit::ScrollView& scrollView)
562 {
563   // Create effect-time property if not already created.
564   if(mPropertyTime == Property::INVALID_INDEX)
565   {
566     mPropertyTime = SafeRegisterProperty( scrollView, EFFECT_TIME, 0.0f );
567     mPropertyReference = SafeRegisterProperty( scrollView, EFFECT_REFERENCE, Vector3::ZERO );
568     mPropertyDepth = SafeRegisterProperty( scrollView, EFFECT_DEPTH, 0.0f);
569     mPropertyActivate = SafeRegisterProperty(scrollView, EFFECT_ACTIVATE, 0.0f);
570   }
571   // currently cant change overshoot snap back duration, use the constant one from ScrollView
572   mActivationTime = Toolkit::ScrollView::DEFAULT_SNAP_OVERSHOOT_DURATION;
573
574   // Connect to the scroll view signals
575   scrollView.ScrollStartedSignal().Connect(this, &ScrollViewTwistEffect::OnScrollStart);
576   scrollView.SnapStartedSignal().Connect(this, &ScrollViewTwistEffect::OnScrollSnap);
577   scrollView.ScrollUpdatedSignal().Connect(this, &ScrollViewTwistEffect::OnScrollUpdate);
578   scrollView.ScrollCompletedSignal().Connect(this, &ScrollViewTwistEffect::OnScrollComplete);
579
580   AttachActor(scrollView);
581 }
582
583 void ScrollViewTwistEffect::OnDetach(Toolkit::ScrollView& scrollView)
584 {
585   scrollView.ScrollStartedSignal().Disconnect(this, &ScrollViewTwistEffect::OnScrollStart);
586   scrollView.SnapStartedSignal().Disconnect(this, &ScrollViewTwistEffect::OnScrollSnap);
587   scrollView.ScrollUpdatedSignal().Disconnect(this, &ScrollViewTwistEffect::OnScrollUpdate);
588   scrollView.ScrollCompletedSignal().Disconnect(this, &ScrollViewTwistEffect::OnScrollComplete);
589
590   if(mAnimation)
591   {
592     mAnimation.FinishedSignal().Disconnect(this, &ScrollViewTwistEffect::OnAnimationFinished);
593     mAnimation.Clear();
594     mAnimation.Reset();
595   }
596 }
597
598 void ScrollViewTwistEffect::AttachActor(Actor actor)
599 {
600
601 }
602
603 void ScrollViewTwistEffect::DetachActor(Actor actor)
604 {
605   // TODO: remove the specific constraint defined in AttachActor (and possibly
606   // unregister property) - neither functionality exists in Dali.
607 }
608
609 void ScrollViewTwistEffect::ContinueAnimation(float endTime)
610 {
611   // continue animating
612   if(mAnimation)
613   {
614     mAnimation.FinishedSignal().Disconnect(this, &ScrollViewTwistEffect::OnAnimationFinished);
615     mAnimation.Clear();
616   }
617
618   Actor scrollView = GetScrollView();
619
620   mAnimation = Animation::New(TWISTEFFECT_ANIMATION_MAX_TIME);
621   mAnimation.AnimateTo( Property(scrollView, mPropertyTime), endTime, AlphaFunctions::Linear );
622   mAnimation.FinishedSignal().Connect(this, &ScrollViewTwistEffect::OnAnimationFinished);
623   mAnimation.Play();
624 }
625
626 void ScrollViewTwistEffect::OnScrollStart( const Vector3& position )
627 {
628   if(mActivateAnimation)
629   {
630     // if the animation after Scroll complete not terminate before another scroll action, stop the animation before start again
631     mActivateAnimation.Stop();
632     mActivateAnimation.Clear();
633     mActivateAnimation = NULL;
634   }
635
636   GetScrollView().SetProperty(mPropertyTime, 0.0f);
637   if(mEnableEffect)
638   {
639     GetScrollView().SetProperty(mPropertyActivate, 1.0f);
640   }
641   else
642   {
643     GetScrollView().SetProperty(mPropertyActivate, 0.0f);
644   }
645   GetScrollView().SetProperty(mPropertyReference, position);
646
647   ContinueAnimation(TWISTEFFECT_ANIMATION_MAX_TIME);
648 }
649
650 void ScrollViewTwistEffect::OnScrollUpdate( const Vector3& position )
651 {
652   // nothing to do
653 }
654
655 void ScrollViewTwistEffect::OnScrollComplete( const Vector3& position )
656 {
657   if(!mEnableEffect)
658   {
659     OnActivateAnimationFinished(mAnimation);
660     return;
661   }
662   Actor scrollView = GetScrollView();
663   scrollView.SetProperty(mPropertyActivate, 1.0f);
664   mActivateAnimation = Animation::New(mActivationTime);
665   mActivateAnimation.AnimateTo( Property(scrollView, mPropertyActivate), 0.0f, AlphaFunctions::Linear);
666   mActivateAnimation.FinishedSignal().Connect(this, &ScrollViewTwistEffect::OnActivateAnimationFinished);
667   mActivateAnimation.Play();
668 }
669
670 void ScrollViewTwistEffect::OnScrollSnap( const Toolkit::ScrollView::SnapEvent& event )
671 {
672   // If a Flicking snap is occuring and the distance is more than mMinimumDistanceForShrink
673   // then animate depth effect i.e. shrink actors and then bring back in to regular size.
674   // NOTE: ScrollView Snap returns a value opposite of GetCurrentScrollPosition
675   // i.e. if you've "scrolled 100 pixels right" (so content on screen has shifted 100 pixels left)
676   // then GetCurrentScrollPosition returns a positive value (100.0f, 0.0f) (position of where you're
677   // look, not where content has been moved to).
678   // event.position returns a negative value (-100.0f, 0.0f)
679   // Would be a good idea to change SnapEvent in the API so it reflects GetCurrentScrollPosition.
680   // TODO: Change scroll-view API, check if anything uses SnapEvent and change them correspondingly
681   Vector3 targetScrollPosition(-event.position);
682
683   Vector3 delta = targetScrollPosition - GetScrollView().GetCurrentScrollPosition();
684
685   if(event.type==Flick && delta.Length() > mMinimumDistanceForShrink)
686   {
687     Actor scrollView = GetScrollView();
688
689     Animation animation = Animation::New(event.duration);
690     animation.AnimateTo( Property(scrollView, mPropertyDepth), 1.0f, HopEasing);
691     animation.Play();
692   }
693 }
694
695 void ScrollViewTwistEffect::OnAnimationFinished( Animation& animation )
696 {
697   // still unstable, so continue animating.
698   // TODO: Requires an instability check to ensure time animation finishes when delay is
699   // less noticeable. i.e. all present scrollPositions are approx the same as mScrollPosition in constraints.
700   // best solution for this is to switch to a single history vector of scroll position, and compare if
701   // position has not deviated >= 0.5 pixel for the past 1 second.
702   float endTime = GetScrollView().GetProperty<float>(mPropertyTime) + TWISTEFFECT_ANIMATION_MAX_TIME;
703   ContinueAnimation(endTime);
704 }
705
706 void ScrollViewTwistEffect::OnActivateAnimationFinished( Animation& animation )
707 {
708   if(mAnimation)
709   {
710     mAnimation.FinishedSignal().Disconnect(this, &ScrollViewTwistEffect::OnAnimationFinished);
711     mAnimation.Clear();
712     mAnimation.Reset();
713   }
714 }
715
716
717 } // namespace Internal
718
719 } // namespace Toolkit
720
721 } // namespace Dali