2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-overshoot-indicator-impl.h>
22 #include <boost/bind.hpp>
25 #include <dali-toolkit/internal/controls/scrollable/scrollable-impl.h>
26 #include <dali-toolkit/internal/controls/scrollable/bouncing-effect-actor.h>
27 #include <dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view.h>
33 const Vector2 OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE( 720.0f, 42.0f );
34 const float OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD = 180.0f;
36 // local helper function to resize the height of the bounce actor
37 float GetBounceActorHeight( float width )
39 return (width > OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD) ? OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.height : OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.height * 0.5f;
42 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
43 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
44 const float OVERSHOOT_NOTIFY_STEP = 0.01f; // amount to set notifications beyond current overshoot value
57 ScrollOvershootIndicator::ScrollOvershootIndicator() :
63 ScrollOvershootIndicator::~ScrollOvershootIndicator()
68 ScrollOvershootIndicator* ScrollOvershootIndicator::New()
70 ScrollOvershootIndicator* scrollOvershootPtr = new ScrollOvershootIndicator();
71 return scrollOvershootPtr;
74 void ScrollOvershootIndicator::AttachToScrollable(Scrollable& scrollable)
78 mEffectX = ScrollOvershootEffectRipple::New(false, scrollable);
83 mEffectY = ScrollOvershootEffectRipple::New(true, scrollable);
88 void ScrollOvershootIndicator::DetachFromScrollable(Scrollable& scrollable)
92 mEffectX->Remove(scrollable);
96 mEffectY->Remove(scrollable);
100 void ScrollOvershootIndicator::Reset()
106 void ScrollOvershootIndicator::SetOvershootEffectColor( const Vector4& color )
110 mEffectX->SetOvershootEffectColor(color);
114 mEffectY->SetOvershootEffectColor(color);
118 void ScrollOvershootIndicator::ClearOvershoot()
122 mEffectX->SetOvershoot(0.0f);
126 mEffectY->SetOvershoot(0.0f);
130 ScrollOvershootEffect::ScrollOvershootEffect( bool vertical ) :
136 bool ScrollOvershootEffect::IsVertical() const
141 ScrollOvershootEffectRipple::ScrollOvershootEffectRipple( bool vertical, Scrollable& scrollable ) :
142 ScrollOvershootEffect( vertical ),
143 mAttachedScrollView(scrollable),
144 mOvershootProperty(Property::INVALID_INDEX),
145 mEffectOvershootProperty(Property::INVALID_INDEX),
147 mAnimationStateFlags(0)
149 mOvershootOverlay = CreateBouncingEffectActor(mEffectOvershootProperty);
150 mOvershootOverlay.SetColor(mAttachedScrollView.GetOvershootEffectColor());
151 mOvershootOverlay.SetParentOrigin(ParentOrigin::TOP_LEFT);
152 mOvershootOverlay.SetAnchorPoint(AnchorPoint::TOP_LEFT);
153 mOvershootOverlay.SetDrawMode(DrawMode::OVERLAY);
154 mOvershootOverlay.SetVisible(false);
158 void ScrollOvershootEffectRipple::Apply()
160 Actor self = mAttachedScrollView.Self();
161 mOvershootProperty = self.GetPropertyIndex(IsVertical() ? Toolkit::ScrollView::SCROLL_OVERSHOOT_Y_PROPERTY_NAME : Toolkit::ScrollView::SCROLL_OVERSHOOT_X_PROPERTY_NAME);
163 // make sure height is set, since we only create a constraint for image width
164 mOvershootOverlay.SetSize(OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.width, OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.height);
166 mAttachedScrollView.AddOverlay(mOvershootOverlay);
168 UpdatePropertyNotifications();
171 void ScrollOvershootEffectRipple::Remove( Scrollable& scrollable )
173 if(mOvershootOverlay)
175 if(mOvershootIncreaseNotification)
177 scrollable.Self().RemovePropertyNotification(mOvershootIncreaseNotification);
178 mOvershootIncreaseNotification.Reset();
180 if(mOvershootDecreaseNotification)
182 scrollable.Self().RemovePropertyNotification(mOvershootDecreaseNotification);
183 mOvershootDecreaseNotification.Reset();
185 scrollable.RemoveOverlay(mOvershootOverlay);
189 void ScrollOvershootEffectRipple::Reset()
191 mOvershootOverlay.SetVisible(false);
192 mOvershootOverlay.SetProperty( mEffectOvershootProperty, 0.f);
195 void ScrollOvershootEffectRipple::UpdatePropertyNotifications()
197 float absOvershoot = fabsf(mOvershoot);
199 Actor self = mAttachedScrollView.Self();
200 // update overshoot increase notify
201 if( mOvershootIncreaseNotification )
203 self.RemovePropertyNotification( mOvershootIncreaseNotification );
204 mOvershootIncreaseNotification.Reset();
206 if( absOvershoot < MAX_OVERSHOOT_NOTIFY_AMOUNT )
208 float increaseStep = absOvershoot + OVERSHOOT_NOTIFY_STEP;
209 if( increaseStep > MAX_OVERSHOOT_NOTIFY_AMOUNT )
211 increaseStep = MAX_OVERSHOOT_NOTIFY_AMOUNT;
213 mOvershootIncreaseNotification = self.AddPropertyNotification( mOvershootProperty, OutsideCondition(-increaseStep, increaseStep) );
214 mOvershootIncreaseNotification.SetNotifyMode(PropertyNotification::NotifyOnTrue);
215 mOvershootIncreaseNotification.NotifySignal().Connect(this, &ScrollOvershootEffectRipple::OnOvershootNotification);
218 // update overshoot decrease notify
219 if( mOvershootDecreaseNotification )
221 self.RemovePropertyNotification( mOvershootDecreaseNotification );
222 mOvershootDecreaseNotification.Reset();
224 if( absOvershoot > MIN_OVERSHOOT_NOTIFY_AMOUNT )
226 float reduceStep = absOvershoot - OVERSHOOT_NOTIFY_STEP;
227 if( reduceStep < MIN_OVERSHOOT_NOTIFY_AMOUNT )
229 reduceStep = MIN_OVERSHOOT_NOTIFY_AMOUNT;
231 mOvershootDecreaseNotification = self.AddPropertyNotification( mOvershootProperty, InsideCondition(-reduceStep, reduceStep) );
232 mOvershootDecreaseNotification.SetNotifyMode(PropertyNotification::NotifyOnTrue);
233 mOvershootDecreaseNotification.NotifySignal().Connect(this, &ScrollOvershootEffectRipple::OnOvershootNotification);
237 void ScrollOvershootEffectRipple::SetOvershootEffectColor( const Vector4& color )
239 if(mOvershootOverlay)
241 mOvershootOverlay.SetColor(color);
245 void ScrollOvershootEffectRipple::UpdateVisibility( bool visible )
247 mOvershootOverlay.SetVisible(visible);
248 // make sure overshoot image is correctly placed
251 Actor self = mAttachedScrollView.Self();
252 if(mOvershoot > 0.0f)
254 // positive overshoot
255 const Vector3 size = mOvershootOverlay.GetCurrentSize();
256 Vector3 relativeOffset;
257 const Vector3 parentSize = self.GetCurrentSize();
260 mOvershootOverlay.SetOrientation(Quaternion(0.0f, Vector3::ZAXIS));
261 mOvershootOverlay.SetSize(parentSize.width, GetBounceActorHeight(parentSize.width), size.depth);
265 mOvershootOverlay.SetOrientation(Quaternion(1.5f * Math::PI, Vector3::ZAXIS));
266 mOvershootOverlay.SetSize(parentSize.height, GetBounceActorHeight(parentSize.height), size.depth);
267 relativeOffset = Vector3(0.0f, 1.0f, 0.0f);
269 mOvershootOverlay.SetPosition(relativeOffset * parentSize);
273 // negative overshoot
274 const Vector3 size = mOvershootOverlay.GetCurrentSize();
275 Vector3 relativeOffset;
276 const Vector3 parentSize = self.GetCurrentSize();
279 mOvershootOverlay.SetOrientation(Quaternion(Math::PI, Vector3::ZAXIS));
280 mOvershootOverlay.SetSize(parentSize.width, GetBounceActorHeight(parentSize.width), size.depth);
281 relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
285 mOvershootOverlay.SetOrientation(Quaternion(0.5f * Math::PI, Vector3::ZAXIS));
286 mOvershootOverlay.SetSize(parentSize.height, GetBounceActorHeight(parentSize.height), size.depth);
287 relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
289 mOvershootOverlay.SetPosition(relativeOffset * parentSize);
294 void ScrollOvershootEffectRipple::OnOvershootNotification(PropertyNotification& source)
296 Actor self = mAttachedScrollView.Self();
297 mOvershoot = self.GetProperty<float>(mOvershootProperty);
298 SetOvershoot(mOvershoot, false);
299 UpdatePropertyNotifications();
302 void ScrollOvershootEffectRipple::SetOvershoot(float amount, bool animate)
304 float absAmount = fabsf(amount);
305 bool animatingOn = absAmount > Math::MACHINE_EPSILON_0;
306 if( (animatingOn && (mAnimationStateFlags & AnimatingIn)) )
308 // trying to do what we are already doing
309 if( mAnimationStateFlags & AnimateBack )
311 mAnimationStateFlags &= ~AnimateBack;
315 if( (!animatingOn && (mAnimationStateFlags & AnimatingOut)) )
317 // trying to do what we are already doing
320 if( !animatingOn && (mAnimationStateFlags & AnimatingIn) )
322 // dont interrupt while animating on
323 mAnimationStateFlags |= AnimateBack;
327 if( absAmount > Math::MACHINE_EPSILON_1 )
329 UpdateVisibility(true);
332 float overshootAnimationSpeed = mAttachedScrollView.Self().GetProperty<float>(Toolkit::Scrollable::Property::OVERSHOOT_ANIMATION_SPEED);
334 if( animate && overshootAnimationSpeed > Math::MACHINE_EPSILON_0 )
336 float currentOvershoot = fabsf( mOvershootOverlay.GetProperty( mEffectOvershootProperty ).Get<float>() );
337 float duration = mOvershootOverlay.GetCurrentSize().height * (animatingOn ? (1.0f - currentOvershoot) : currentOvershoot) / overshootAnimationSpeed;
339 if( duration > Math::MACHINE_EPSILON_0 )
341 if(mScrollOvershootAnimation)
343 mScrollOvershootAnimation.FinishedSignal().Disconnect( this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished );
344 mScrollOvershootAnimation.Stop();
345 mScrollOvershootAnimation.Reset();
347 mScrollOvershootAnimation = Animation::New(duration);
348 mScrollOvershootAnimation.FinishedSignal().Connect( this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished );
349 mScrollOvershootAnimation.AnimateTo( Property(mOvershootOverlay, mEffectOvershootProperty), amount, TimePeriod(duration) );
350 mScrollOvershootAnimation.Play();
351 mAnimationStateFlags = animatingOn ? AnimatingIn : AnimatingOut;
356 mOvershootOverlay.SetProperty( mEffectOvershootProperty, amount);
360 void ScrollOvershootEffectRipple::OnOvershootAnimFinished(Animation& animation)
362 bool animateOff = false;
363 if( mAnimationStateFlags & AnimatingOut )
365 // should now be offscreen
366 mOvershootOverlay.SetVisible(false);
368 if( (mAnimationStateFlags & AnimateBack) )
372 mScrollOvershootAnimation.FinishedSignal().Disconnect( this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished );
373 mScrollOvershootAnimation.Stop();
374 mScrollOvershootAnimation.Reset();
375 mAnimationStateFlags = 0;
378 SetOvershoot(0.0f, true);
382 ScrollOvershootEffectRipplePtr ScrollOvershootEffectRipple::New( bool vertical, Scrollable& scrollable )
384 return new ScrollOvershootEffectRipple(vertical, scrollable);
387 } // namespace Internal
389 } // namespace Toolkit