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 <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>
30 const Vector2 OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE( 720.0f, 42.0f );
31 const float OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD = 180.0f;
33 // local helper function to resize the height of the bounce actor
34 float GetBounceActorHeight( float width )
36 return (width > OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD) ? OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.height : OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.height * 0.5f;
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
54 ScrollOvershootIndicator::ScrollOvershootIndicator() :
60 ScrollOvershootIndicator::~ScrollOvershootIndicator()
65 ScrollOvershootIndicator* ScrollOvershootIndicator::New()
67 ScrollOvershootIndicator* scrollOvershootPtr = new ScrollOvershootIndicator();
68 return scrollOvershootPtr;
71 void ScrollOvershootIndicator::AttachToScrollable(Scrollable& scrollable)
75 mEffectX = ScrollOvershootEffectRipple::New(false, scrollable);
80 mEffectY = ScrollOvershootEffectRipple::New(true, scrollable);
85 void ScrollOvershootIndicator::DetachFromScrollable(Scrollable& scrollable)
89 mEffectX->Remove(scrollable);
93 mEffectY->Remove(scrollable);
97 void ScrollOvershootIndicator::Reset()
103 void ScrollOvershootIndicator::SetOvershootEffectColor( const Vector4& color )
107 mEffectX->SetOvershootEffectColor(color);
111 mEffectY->SetOvershootEffectColor(color);
115 void ScrollOvershootIndicator::ClearOvershoot()
119 mEffectX->SetOvershoot(0.0f);
123 mEffectY->SetOvershoot(0.0f);
127 ScrollOvershootEffect::ScrollOvershootEffect( bool vertical ) :
133 bool ScrollOvershootEffect::IsVertical() const
138 ScrollOvershootEffectRipple::ScrollOvershootEffectRipple( bool vertical, Scrollable& scrollable ) :
139 ScrollOvershootEffect( vertical ),
140 mAttachedScrollView(scrollable),
141 mOvershootProperty(Property::INVALID_INDEX),
142 mEffectOvershootProperty(Property::INVALID_INDEX),
144 mAnimationStateFlags(0)
146 mOvershootOverlay = CreateBouncingEffectActor(mEffectOvershootProperty);
147 mOvershootOverlay.SetColor(mAttachedScrollView.GetOvershootEffectColor());
148 mOvershootOverlay.SetParentOrigin(ParentOrigin::TOP_LEFT);
149 mOvershootOverlay.SetAnchorPoint(AnchorPoint::TOP_LEFT);
150 mOvershootOverlay.SetDrawMode(DrawMode::OVERLAY);
151 mOvershootOverlay.SetVisible(false);
155 void ScrollOvershootEffectRipple::Apply()
157 Actor self = mAttachedScrollView.Self();
158 mOvershootProperty = IsVertical() ? Toolkit::ScrollView::Property::OVERSHOOT_Y : Toolkit::ScrollView::Property::OVERSHOOT_X;
160 // make sure height is set, since we only create a constraint for image width
161 mOvershootOverlay.SetSize(OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.width, OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.height);
163 mAttachedScrollView.AddOverlay(mOvershootOverlay);
165 UpdatePropertyNotifications();
168 void ScrollOvershootEffectRipple::Remove( Scrollable& scrollable )
170 if(mOvershootOverlay)
172 if(mOvershootIncreaseNotification)
174 scrollable.Self().RemovePropertyNotification(mOvershootIncreaseNotification);
175 mOvershootIncreaseNotification.Reset();
177 if(mOvershootDecreaseNotification)
179 scrollable.Self().RemovePropertyNotification(mOvershootDecreaseNotification);
180 mOvershootDecreaseNotification.Reset();
182 scrollable.RemoveOverlay(mOvershootOverlay);
186 void ScrollOvershootEffectRipple::Reset()
188 mOvershootOverlay.SetVisible(false);
189 mOvershootOverlay.SetProperty( mEffectOvershootProperty, 0.f);
192 void ScrollOvershootEffectRipple::UpdatePropertyNotifications()
194 float absOvershoot = fabsf(mOvershoot);
196 Actor self = mAttachedScrollView.Self();
197 // update overshoot increase notify
198 if( mOvershootIncreaseNotification )
200 self.RemovePropertyNotification( mOvershootIncreaseNotification );
201 mOvershootIncreaseNotification.Reset();
203 if( absOvershoot < MAX_OVERSHOOT_NOTIFY_AMOUNT )
205 float increaseStep = absOvershoot + OVERSHOOT_NOTIFY_STEP;
206 if( increaseStep > MAX_OVERSHOOT_NOTIFY_AMOUNT )
208 increaseStep = MAX_OVERSHOOT_NOTIFY_AMOUNT;
210 mOvershootIncreaseNotification = self.AddPropertyNotification( mOvershootProperty, OutsideCondition(-increaseStep, increaseStep) );
211 mOvershootIncreaseNotification.SetNotifyMode(PropertyNotification::NotifyOnTrue);
212 mOvershootIncreaseNotification.NotifySignal().Connect(this, &ScrollOvershootEffectRipple::OnOvershootNotification);
215 // update overshoot decrease notify
216 if( mOvershootDecreaseNotification )
218 self.RemovePropertyNotification( mOvershootDecreaseNotification );
219 mOvershootDecreaseNotification.Reset();
221 if( absOvershoot > MIN_OVERSHOOT_NOTIFY_AMOUNT )
223 float reduceStep = absOvershoot - OVERSHOOT_NOTIFY_STEP;
224 if( reduceStep < MIN_OVERSHOOT_NOTIFY_AMOUNT )
226 reduceStep = MIN_OVERSHOOT_NOTIFY_AMOUNT;
228 mOvershootDecreaseNotification = self.AddPropertyNotification( mOvershootProperty, InsideCondition(-reduceStep, reduceStep) );
229 mOvershootDecreaseNotification.SetNotifyMode(PropertyNotification::NotifyOnTrue);
230 mOvershootDecreaseNotification.NotifySignal().Connect(this, &ScrollOvershootEffectRipple::OnOvershootNotification);
234 void ScrollOvershootEffectRipple::SetOvershootEffectColor( const Vector4& color )
236 if(mOvershootOverlay)
238 mOvershootOverlay.SetColor(color);
242 void ScrollOvershootEffectRipple::UpdateVisibility( bool visible )
244 mOvershootOverlay.SetVisible(visible);
245 // make sure overshoot image is correctly placed
248 Actor self = mAttachedScrollView.Self();
249 if(mOvershoot > 0.0f)
251 // positive overshoot
252 const Vector3 size = mOvershootOverlay.GetCurrentSize();
253 Vector3 relativeOffset;
254 const Vector3 parentSize = self.GetCurrentSize();
257 mOvershootOverlay.SetOrientation( Quaternion( Radian( 0.0f ), Vector3::ZAXIS ) );
258 mOvershootOverlay.SetSize(parentSize.width, GetBounceActorHeight(parentSize.width), size.depth);
262 mOvershootOverlay.SetOrientation( Quaternion( Radian( 1.5f * Math::PI ), Vector3::ZAXIS ) );
263 mOvershootOverlay.SetSize(parentSize.height, GetBounceActorHeight(parentSize.height), size.depth);
264 relativeOffset = Vector3(0.0f, 1.0f, 0.0f);
266 mOvershootOverlay.SetPosition(relativeOffset * parentSize);
270 // negative overshoot
271 const Vector3 size = mOvershootOverlay.GetCurrentSize();
272 Vector3 relativeOffset;
273 const Vector3 parentSize = self.GetCurrentSize();
276 mOvershootOverlay.SetOrientation( Quaternion( Radian( Math::PI ), Vector3::ZAXIS ) );
277 mOvershootOverlay.SetSize(parentSize.width, GetBounceActorHeight(parentSize.width), size.depth);
278 relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
282 mOvershootOverlay.SetOrientation( Quaternion( Radian( 0.5f * Math::PI ), Vector3::ZAXIS ) );
283 mOvershootOverlay.SetSize(parentSize.height, GetBounceActorHeight(parentSize.height), size.depth);
284 relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
286 mOvershootOverlay.SetPosition(relativeOffset * parentSize);
291 void ScrollOvershootEffectRipple::OnOvershootNotification(PropertyNotification& source)
293 Actor self = mAttachedScrollView.Self();
294 mOvershoot = self.GetProperty<float>(mOvershootProperty);
295 SetOvershoot(mOvershoot, false);
296 UpdatePropertyNotifications();
299 void ScrollOvershootEffectRipple::SetOvershoot(float amount, bool animate)
301 float absAmount = fabsf(amount);
302 bool animatingOn = absAmount > Math::MACHINE_EPSILON_0;
303 if( (animatingOn && (mAnimationStateFlags & AnimatingIn)) )
305 // trying to do what we are already doing
306 if( mAnimationStateFlags & AnimateBack )
308 mAnimationStateFlags &= ~AnimateBack;
312 if( (!animatingOn && (mAnimationStateFlags & AnimatingOut)) )
314 // trying to do what we are already doing
317 if( !animatingOn && (mAnimationStateFlags & AnimatingIn) )
319 // dont interrupt while animating on
320 mAnimationStateFlags |= AnimateBack;
324 if( absAmount > Math::MACHINE_EPSILON_1 )
326 UpdateVisibility(true);
329 float overshootAnimationSpeed = mAttachedScrollView.Self().GetProperty<float>(Toolkit::Scrollable::Property::OVERSHOOT_ANIMATION_SPEED);
331 if( animate && overshootAnimationSpeed > Math::MACHINE_EPSILON_0 )
333 float currentOvershoot = fabsf( mOvershootOverlay.GetProperty( mEffectOvershootProperty ).Get<float>() );
334 float duration = mOvershootOverlay.GetCurrentSize().height * (animatingOn ? (1.0f - currentOvershoot) : currentOvershoot) / overshootAnimationSpeed;
336 if( duration > Math::MACHINE_EPSILON_0 )
338 if(mScrollOvershootAnimation)
340 mScrollOvershootAnimation.FinishedSignal().Disconnect( this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished );
341 mScrollOvershootAnimation.Stop();
342 mScrollOvershootAnimation.Reset();
344 mScrollOvershootAnimation = Animation::New(duration);
345 mScrollOvershootAnimation.FinishedSignal().Connect( this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished );
346 mScrollOvershootAnimation.AnimateTo( Property(mOvershootOverlay, mEffectOvershootProperty), amount, TimePeriod(duration) );
347 mScrollOvershootAnimation.Play();
348 mAnimationStateFlags = animatingOn ? AnimatingIn : AnimatingOut;
353 mOvershootOverlay.SetProperty( mEffectOvershootProperty, amount);
357 void ScrollOvershootEffectRipple::OnOvershootAnimFinished(Animation& animation)
359 bool animateOff = false;
360 if( mAnimationStateFlags & AnimatingOut )
362 // should now be offscreen
363 mOvershootOverlay.SetVisible(false);
365 if( (mAnimationStateFlags & AnimateBack) )
369 mScrollOvershootAnimation.FinishedSignal().Disconnect( this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished );
370 mScrollOvershootAnimation.Stop();
371 mScrollOvershootAnimation.Reset();
372 mAnimationStateFlags = 0;
375 SetOvershoot(0.0f, true);
379 ScrollOvershootEffectRipplePtr ScrollOvershootEffectRipple::New( bool vertical, Scrollable& scrollable )
381 return new ScrollOvershootEffectRipple(vertical, scrollable);
384 } // namespace Internal
386 } // namespace Toolkit