Enable setting of ScrollView properties within TextSelectionToolbar via json
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / scrollable / scroll-view / scroll-overshoot-indicator-impl.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-overshoot-indicator-impl.h>
20
21 // INTERNAL INCLUDES
22 #include <dali-toolkit/internal/controls/scrollable/scrollable-impl.h>
23 #include <dali-toolkit/internal/controls/scrollable/bouncing-effect-actor.h>
24 #include <dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view.h>
25
26 using namespace Dali;
27
28 namespace
29 {
30
31 const float OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD = 180.0f;
32
33 // local helper function to resize the height of the bounce actor
34 float GetBounceActorHeight( float width, float defaultHeight )
35 {
36   return (width > OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD) ? defaultHeight : defaultHeight * 0.5f;
37 }
38
39 const float MAX_OVERSHOOT_NOTIFY_AMOUNT = 0.99f;                     // maximum amount to set notification for increased overshoot, beyond this we just wait for it to reduce again
40 const float MIN_OVERSHOOT_NOTIFY_AMOUNT = Math::MACHINE_EPSILON_0;  // minimum amount to set notification for reduced overshoot, beyond this we just wait for it to increase again
41 const float OVERSHOOT_NOTIFY_STEP = 0.01f;                          // amount to set notifications beyond current overshoot value
42
43 }
44
45 namespace Dali
46 {
47
48 namespace Toolkit
49 {
50
51 namespace Internal
52 {
53
54 ScrollOvershootIndicator::ScrollOvershootIndicator() :
55   mEffectX(NULL),
56   mEffectY(NULL)
57 {
58 }
59
60 ScrollOvershootIndicator::~ScrollOvershootIndicator()
61 {
62
63 }
64
65 ScrollOvershootIndicator* ScrollOvershootIndicator::New()
66 {
67   ScrollOvershootIndicator* scrollOvershootPtr = new ScrollOvershootIndicator();
68   return scrollOvershootPtr;
69 }
70
71 void ScrollOvershootIndicator::AttachToScrollable(Scrollable& scrollable)
72 {
73   if(!mEffectX)
74   {
75     mEffectX = ScrollOvershootEffectRipple::New(false, scrollable);
76   }
77   mEffectX->Apply();
78   if(!mEffectY)
79   {
80     mEffectY = ScrollOvershootEffectRipple::New(true, scrollable);
81   }
82   mEffectY->Apply();
83 }
84
85 void ScrollOvershootIndicator::DetachFromScrollable(Scrollable& scrollable)
86 {
87   if(mEffectX)
88   {
89     mEffectX->Remove(scrollable);
90   }
91   if(mEffectY)
92   {
93     mEffectY->Remove(scrollable);
94   }
95 }
96
97 void ScrollOvershootIndicator::Reset()
98 {
99   mEffectX->Reset();
100   mEffectY->Reset();
101 }
102
103 void ScrollOvershootIndicator::SetOvershootEffectColor( const Vector4& color )
104 {
105   if(mEffectX)
106   {
107     mEffectX->SetOvershootEffectColor(color);
108   }
109   if(mEffectY)
110   {
111     mEffectY->SetOvershootEffectColor(color);
112   }
113 }
114
115 ScrollOvershootEffect::ScrollOvershootEffect( bool vertical ) :
116     mVertical(vertical)
117 {
118
119 }
120
121 bool ScrollOvershootEffect::IsVertical() const
122 {
123   return mVertical;
124 }
125
126 ScrollOvershootEffectRipple::ScrollOvershootEffectRipple( bool vertical, Scrollable& scrollable ) :
127     ScrollOvershootEffect( vertical ),
128     mAttachedScrollView(scrollable),
129     mOvershootProperty(Property::INVALID_INDEX),
130     mEffectOvershootProperty(Property::INVALID_INDEX),
131     mOvershoot(0.0f),
132     mOvershootSize( scrollable.GetOvershootSize() ),
133     mAnimationStateFlags(0)
134 {
135   mOvershootOverlay = CreateBouncingEffectActor(mEffectOvershootProperty);
136   mOvershootOverlay.SetColor(mAttachedScrollView.GetOvershootEffectColor());
137   mOvershootOverlay.SetParentOrigin(ParentOrigin::TOP_LEFT);
138   mOvershootOverlay.SetAnchorPoint(AnchorPoint::TOP_LEFT);
139   mOvershootOverlay.SetVisible(false);
140
141 }
142
143 void ScrollOvershootEffectRipple::Apply()
144 {
145   Actor self = mAttachedScrollView.Self();
146   mOvershootProperty = IsVertical() ? Toolkit::ScrollView::Property::OVERSHOOT_Y : Toolkit::ScrollView::Property::OVERSHOOT_X;
147
148   // make sure height is set, since we only create a constraint for image width
149   mOvershootSize = mAttachedScrollView.GetOvershootSize();
150   mOvershootOverlay.SetSize( mOvershootSize );
151
152   mAttachedScrollView.AddOverlay(mOvershootOverlay);
153
154   UpdatePropertyNotifications();
155 }
156
157 void ScrollOvershootEffectRipple::Remove( Scrollable& scrollable )
158 {
159   if(mOvershootOverlay)
160   {
161     if(mOvershootIncreaseNotification)
162     {
163       scrollable.Self().RemovePropertyNotification(mOvershootIncreaseNotification);
164       mOvershootIncreaseNotification.Reset();
165     }
166     if(mOvershootDecreaseNotification)
167     {
168       scrollable.Self().RemovePropertyNotification(mOvershootDecreaseNotification);
169       mOvershootDecreaseNotification.Reset();
170     }
171     scrollable.RemoveOverlay(mOvershootOverlay);
172   }
173 }
174
175 void ScrollOvershootEffectRipple::Reset()
176 {
177   mOvershootOverlay.SetVisible(false);
178   mOvershootOverlay.SetProperty( mEffectOvershootProperty, 0.f);
179 }
180
181 void ScrollOvershootEffectRipple::UpdatePropertyNotifications()
182 {
183   float absOvershoot = fabsf(mOvershoot);
184
185   Actor self = mAttachedScrollView.Self();
186   // update overshoot increase notify
187   if( mOvershootIncreaseNotification )
188   {
189     self.RemovePropertyNotification( mOvershootIncreaseNotification );
190     mOvershootIncreaseNotification.Reset();
191   }
192   if( absOvershoot < MAX_OVERSHOOT_NOTIFY_AMOUNT )
193   {
194     float increaseStep = absOvershoot + OVERSHOOT_NOTIFY_STEP;
195     if( increaseStep > MAX_OVERSHOOT_NOTIFY_AMOUNT )
196     {
197       increaseStep = MAX_OVERSHOOT_NOTIFY_AMOUNT;
198     }
199     mOvershootIncreaseNotification = self.AddPropertyNotification( mOvershootProperty, OutsideCondition(-increaseStep, increaseStep) );
200     mOvershootIncreaseNotification.SetNotifyMode(PropertyNotification::NotifyOnTrue);
201     mOvershootIncreaseNotification.NotifySignal().Connect(this, &ScrollOvershootEffectRipple::OnOvershootNotification);
202   }
203
204   // update overshoot decrease notify
205   if( mOvershootDecreaseNotification )
206   {
207     self.RemovePropertyNotification( mOvershootDecreaseNotification );
208     mOvershootDecreaseNotification.Reset();
209   }
210   if( absOvershoot > MIN_OVERSHOOT_NOTIFY_AMOUNT )
211   {
212     float reduceStep = absOvershoot - OVERSHOOT_NOTIFY_STEP;
213     if( reduceStep < MIN_OVERSHOOT_NOTIFY_AMOUNT )
214     {
215       reduceStep = MIN_OVERSHOOT_NOTIFY_AMOUNT;
216     }
217     mOvershootDecreaseNotification = self.AddPropertyNotification( mOvershootProperty, InsideCondition(-reduceStep, reduceStep) );
218     mOvershootDecreaseNotification.SetNotifyMode(PropertyNotification::NotifyOnTrue);
219     mOvershootDecreaseNotification.NotifySignal().Connect(this, &ScrollOvershootEffectRipple::OnOvershootNotification);
220   }
221 }
222
223 void ScrollOvershootEffectRipple::SetOvershootEffectColor( const Vector4& color )
224 {
225   if(mOvershootOverlay)
226   {
227     mOvershootOverlay.SetColor(color);
228   }
229 }
230
231 void ScrollOvershootEffectRipple::UpdateVisibility( bool visible )
232 {
233   mOvershootOverlay.SetVisible(visible);
234   // make sure overshoot image is correctly placed
235   if( visible )
236   {
237     Actor self = mAttachedScrollView.Self();
238     if(mOvershoot > 0.0f)
239     {
240       // positive overshoot
241       const Vector3 size = mOvershootOverlay.GetCurrentSize();
242       Vector3 relativeOffset;
243       const Vector3 parentSize = self.GetCurrentSize();
244       if(IsVertical())
245       {
246         mOvershootOverlay.SetOrientation( Quaternion( Radian( 0.0f ), Vector3::ZAXIS ) );
247         mOvershootOverlay.SetSize(parentSize.width, GetBounceActorHeight(parentSize.width, mOvershootSize.height), size.depth);
248       }
249       else
250       {
251         mOvershootOverlay.SetOrientation( Quaternion( Radian( 1.5f * Math::PI ), Vector3::ZAXIS ) );
252         mOvershootOverlay.SetSize(parentSize.height, GetBounceActorHeight(parentSize.height, mOvershootSize.height), size.depth);
253         relativeOffset = Vector3(0.0f, 1.0f, 0.0f);
254       }
255       mOvershootOverlay.SetPosition(relativeOffset * parentSize);
256     }
257     else
258     {
259       // negative overshoot
260       const Vector3 size = mOvershootOverlay.GetCurrentSize();
261       Vector3 relativeOffset;
262       const Vector3 parentSize = self.GetCurrentSize();
263       if(IsVertical())
264       {
265         mOvershootOverlay.SetOrientation( Quaternion( Radian( Math::PI ), Vector3::ZAXIS ) );
266         mOvershootOverlay.SetSize(parentSize.width, GetBounceActorHeight(parentSize.width, mOvershootSize.height), size.depth);
267         relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
268       }
269       else
270       {
271         mOvershootOverlay.SetOrientation( Quaternion( Radian( 0.5f * Math::PI ), Vector3::ZAXIS ) );
272         mOvershootOverlay.SetSize(parentSize.height, GetBounceActorHeight(parentSize.height, mOvershootSize.height), size.depth);
273         relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
274       }
275       mOvershootOverlay.SetPosition(relativeOffset * parentSize);
276     }
277   }
278 }
279
280 void ScrollOvershootEffectRipple::OnOvershootNotification(PropertyNotification& source)
281 {
282   Actor self = mAttachedScrollView.Self();
283   mOvershoot = self.GetProperty<float>(mOvershootProperty);
284   SetOvershoot(mOvershoot, false);
285   UpdatePropertyNotifications();
286 }
287
288 void ScrollOvershootEffectRipple::SetOvershoot(float amount, bool animate)
289 {
290   float absAmount = fabsf(amount);
291   bool animatingOn = absAmount > Math::MACHINE_EPSILON_0;
292   if( (animatingOn && (mAnimationStateFlags & AnimatingIn)) )
293   {
294     // trying to do what we are already doing
295     if( mAnimationStateFlags & AnimateBack )
296     {
297       mAnimationStateFlags &= ~AnimateBack;
298     }
299     return;
300   }
301   if( (!animatingOn && (mAnimationStateFlags & AnimatingOut)) )
302   {
303     // trying to do what we are already doing
304     return;
305   }
306   if( !animatingOn && (mAnimationStateFlags & AnimatingIn) )
307   {
308     // dont interrupt while animating on
309     mAnimationStateFlags |= AnimateBack;
310     return;
311   }
312
313   if( absAmount > Math::MACHINE_EPSILON_1 )
314   {
315     UpdateVisibility(true);
316   }
317
318   float overshootAnimationSpeed = mAttachedScrollView.Self().GetProperty<float>(Toolkit::Scrollable::Property::OVERSHOOT_ANIMATION_SPEED);
319
320   if( animate && overshootAnimationSpeed > Math::MACHINE_EPSILON_0 )
321   {
322     float currentOvershoot = fabsf( mOvershootOverlay.GetProperty( mEffectOvershootProperty ).Get<float>() );
323     float duration = mOvershootOverlay.GetCurrentSize().height * (animatingOn ? (1.0f - currentOvershoot) : currentOvershoot) / overshootAnimationSpeed;
324
325     if( duration > Math::MACHINE_EPSILON_0 )
326     {
327       if(mScrollOvershootAnimation)
328       {
329         mScrollOvershootAnimation.FinishedSignal().Disconnect( this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished );
330         mScrollOvershootAnimation.Stop();
331         mScrollOvershootAnimation.Reset();
332       }
333       mScrollOvershootAnimation = Animation::New(duration);
334       mScrollOvershootAnimation.FinishedSignal().Connect( this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished );
335       mScrollOvershootAnimation.AnimateTo( Property(mOvershootOverlay, mEffectOvershootProperty), amount, TimePeriod(duration) );
336       mScrollOvershootAnimation.Play();
337       mAnimationStateFlags = animatingOn ? AnimatingIn : AnimatingOut;
338     }
339   }
340   else
341   {
342     mOvershootOverlay.SetProperty( mEffectOvershootProperty, amount);
343   }
344 }
345
346 void ScrollOvershootEffectRipple::OnOvershootAnimFinished(Animation& animation)
347 {
348   bool animateOff = false;
349   if( mAnimationStateFlags & AnimatingOut )
350   {
351     // should now be offscreen
352     mOvershootOverlay.SetVisible(false);
353   }
354   if( (mAnimationStateFlags & AnimateBack) )
355   {
356     animateOff = true;
357   }
358   mScrollOvershootAnimation.FinishedSignal().Disconnect( this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished );
359   mScrollOvershootAnimation.Stop();
360   mScrollOvershootAnimation.Reset();
361   mAnimationStateFlags = 0;
362   if( animateOff )
363   {
364     SetOvershoot(0.0f, true);
365   }
366 }
367
368 ScrollOvershootEffectRipplePtr ScrollOvershootEffectRipple::New( bool vertical, Scrollable& scrollable )
369 {
370   return new ScrollOvershootEffectRipple(vertical, scrollable);
371 }
372
373 } // namespace Internal
374
375 } // namespace Toolkit
376
377 } // namespace Dali