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 float DEFAULT_MAX_OVERSHOOT_HEIGHT = 36.0f; // 36 pixels
34 const Vector2 OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE( 720.0f, 42.0f );
35 const float OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD = 180.0f;
37 // local helper function to resize the height of the bounce actor
38 float GetBounceActorHeight( float width )
40 return (width > OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD) ? OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.height : OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.height * 0.5f;
43 const float DEFAULT_OVERSHOOT_ANIMATION_DURATION = 0.35f; // time in seconds
44 const float MAX_OVERSHOOT_NOTIFY_AMOUNT = 0.9f; // maximum amount to set notification for increased overshoot, beyond this we just wait for it to reduce again
45 const float MIN_OVERSHOOT_NOTIFY_AMOUNT = Math::MACHINE_EPSILON_1; // minimum amount to set notification for reduced overshoot, beyond this we just wait for it to increase again
46 const float OVERSHOOT_NOTIFY_STEP = 0.1f; // amount to set notifications beyond current overshoot value
59 ScrollOvershootIndicator::ScrollOvershootIndicator() :
65 ScrollOvershootIndicator::~ScrollOvershootIndicator()
70 ScrollOvershootIndicator* ScrollOvershootIndicator::New()
72 ScrollOvershootIndicator* scrollOvershootPtr = new ScrollOvershootIndicator();
73 return scrollOvershootPtr;
76 void ScrollOvershootIndicator::AttachToScrollable(Scrollable& scrollable)
80 mEffectX = ScrollOvershootEffectRipple::New(false, scrollable);
85 mEffectY = ScrollOvershootEffectRipple::New(true, scrollable);
90 void ScrollOvershootIndicator::DetachFromScrollable(Scrollable& scrollable)
94 mEffectX->Remove(scrollable);
98 mEffectY->Remove(scrollable);
102 void ScrollOvershootIndicator::Reset()
108 void ScrollOvershootIndicator::SetOvershootEffectColor( const Vector4& color )
112 mEffectX->SetOvershootEffectColor(color);
116 mEffectY->SetOvershootEffectColor(color);
120 void ScrollOvershootIndicator::ClearOvershoot()
124 mEffectX->SetOvershoot(0.0f);
128 mEffectY->SetOvershoot(0.0f);
132 ScrollOvershootEffect::ScrollOvershootEffect( bool vertical ) :
138 bool ScrollOvershootEffect::IsVertical() const
143 ScrollOvershootEffectRipple::ScrollOvershootEffectRipple( bool vertical, Scrollable& scrollable ) :
144 ScrollOvershootEffect( vertical ),
145 mAttachedScrollView(scrollable),
146 mCanScrollPropertyIndex(Property::INVALID_INDEX),
147 mOvershootProperty(Property::INVALID_INDEX),
148 mEffectOvershootProperty(Property::INVALID_INDEX),
149 mMaxOvershootImageSize(DEFAULT_MAX_OVERSHOOT_HEIGHT),
150 mOvershootAnimationDuration(DEFAULT_OVERSHOOT_ANIMATION_DURATION),
152 mAnimationStateFlags(0)
154 mOvershootOverlay = CreateBouncingEffectActor(mEffectOvershootProperty);
155 mOvershootOverlay.SetColor(mAttachedScrollView.GetOvershootEffectColor());
156 mOvershootOverlay.SetParentOrigin(ParentOrigin::TOP_LEFT);
157 mOvershootOverlay.SetAnchorPoint(AnchorPoint::TOP_LEFT);
158 mOvershootOverlay.SetDrawMode(DrawMode::OVERLAY);
159 mOvershootOverlay.SetVisible(false);
163 void ScrollOvershootEffectRipple::Apply()
165 Actor self = mAttachedScrollView.Self();
166 mOvershootProperty = self.GetPropertyIndex(IsVertical() ? Toolkit::ScrollView::SCROLL_OVERSHOOT_Y_PROPERTY_NAME : Toolkit::ScrollView::SCROLL_OVERSHOOT_X_PROPERTY_NAME);
167 mCanScrollPropertyIndex = self.GetPropertyIndex(IsVertical() ? Scrollable::SCROLLABLE_CAN_SCROLL_VERTICAL : Scrollable::SCROLLABLE_CAN_SCROLL_HORIZONTAL);
169 // make sure height is set, since we only create a constraint for image width
170 mOvershootOverlay.SetSize(OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.width, OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.height);
172 mAttachedScrollView.AddOverlay(mOvershootOverlay);
174 UpdatePropertyNotifications();
177 void ScrollOvershootEffectRipple::Remove( Scrollable& scrollable )
179 if(mOvershootOverlay)
181 if(mOvershootIncreaseNotification)
183 scrollable.Self().RemovePropertyNotification(mOvershootIncreaseNotification);
184 mOvershootIncreaseNotification.Reset();
186 if(mOvershootDecreaseNotification)
188 scrollable.Self().RemovePropertyNotification(mOvershootDecreaseNotification);
189 mOvershootDecreaseNotification.Reset();
191 scrollable.RemoveOverlay(mOvershootOverlay);
195 void ScrollOvershootEffectRipple::Reset()
197 mOvershootOverlay.SetVisible(false);
198 mOvershootOverlay.SetProperty( mEffectOvershootProperty, 0.f);
201 void ScrollOvershootEffectRipple::UpdatePropertyNotifications()
203 float absOvershoot = fabsf(mOvershoot);
205 Actor self = mAttachedScrollView.Self();
206 // update overshoot increase notify
207 if( mOvershootIncreaseNotification )
209 self.RemovePropertyNotification( mOvershootIncreaseNotification );
210 mOvershootIncreaseNotification.Reset();
212 if( absOvershoot < MAX_OVERSHOOT_NOTIFY_AMOUNT )
214 float increaseStep = absOvershoot + OVERSHOOT_NOTIFY_STEP;
215 if( increaseStep > MAX_OVERSHOOT_NOTIFY_AMOUNT )
217 increaseStep = MAX_OVERSHOOT_NOTIFY_AMOUNT;
219 mOvershootIncreaseNotification = self.AddPropertyNotification( mOvershootProperty, OutsideCondition(-increaseStep, increaseStep) );
220 mOvershootIncreaseNotification.SetNotifyMode(PropertyNotification::NotifyOnTrue);
221 mOvershootIncreaseNotification.NotifySignal().Connect(this, &ScrollOvershootEffectRipple::OnOvershootNotification);
224 // update overshoot decrease notify
225 if( mOvershootDecreaseNotification )
227 self.RemovePropertyNotification( mOvershootDecreaseNotification );
228 mOvershootDecreaseNotification.Reset();
230 if( absOvershoot > MIN_OVERSHOOT_NOTIFY_AMOUNT )
232 float reduceStep = absOvershoot - OVERSHOOT_NOTIFY_STEP;
233 if( reduceStep < MIN_OVERSHOOT_NOTIFY_AMOUNT )
235 reduceStep = MIN_OVERSHOOT_NOTIFY_AMOUNT;
237 mOvershootDecreaseNotification = self.AddPropertyNotification( mOvershootProperty, InsideCondition(-reduceStep, reduceStep) );
238 mOvershootDecreaseNotification.SetNotifyMode(PropertyNotification::NotifyOnTrue);
239 mOvershootDecreaseNotification.NotifySignal().Connect(this, &ScrollOvershootEffectRipple::OnOvershootNotification);
243 void ScrollOvershootEffectRipple::SetOvershootEffectColor( const Vector4& color )
245 if(mOvershootOverlay)
247 mOvershootOverlay.SetColor(color);
251 void ScrollOvershootEffectRipple::UpdateVisibility( bool visible )
253 mOvershootOverlay.SetVisible(visible);
254 // make sure overshoot image is correctly placed
257 Actor self = mAttachedScrollView.Self();
258 if(mOvershoot > 0.0f)
260 // positive overshoot
261 const Vector3 size = mOvershootOverlay.GetCurrentSize();
262 Vector3 relativeOffset;
263 const Vector3 parentSize = self.GetCurrentSize();
266 mOvershootOverlay.SetRotation(Quaternion(0.0f, Vector3::ZAXIS));
267 mOvershootOverlay.SetSize(parentSize.width, GetBounceActorHeight(parentSize.width), size.depth);
271 mOvershootOverlay.SetRotation(Quaternion(1.5f * Math::PI, Vector3::ZAXIS));
272 mOvershootOverlay.SetSize(parentSize.height, GetBounceActorHeight(parentSize.height), size.depth);
273 relativeOffset = Vector3(0.0f, 1.0f, 0.0f);
275 mOvershootOverlay.SetPosition(relativeOffset * parentSize);
279 // negative overshoot
280 const Vector3 size = mOvershootOverlay.GetCurrentSize();
281 Vector3 relativeOffset;
282 const Vector3 parentSize = self.GetCurrentSize();
285 mOvershootOverlay.SetRotation(Quaternion(Math::PI, Vector3::ZAXIS));
286 mOvershootOverlay.SetSize(parentSize.width, GetBounceActorHeight(parentSize.width), size.depth);
287 relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
291 mOvershootOverlay.SetRotation(Quaternion(0.5f * Math::PI, Vector3::ZAXIS));
292 mOvershootOverlay.SetSize(parentSize.height, GetBounceActorHeight(parentSize.height), size.depth);
293 relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
295 mOvershootOverlay.SetPosition(relativeOffset * parentSize);
300 void ScrollOvershootEffectRipple::OnOvershootNotification(PropertyNotification& source)
302 Actor self = mAttachedScrollView.Self();
303 mOvershoot = self.GetProperty<float>(mOvershootProperty);
304 if( source == mOvershootIncreaseNotification )
306 if( mOvershoot > Math::MACHINE_EPSILON_0 )
310 else if ( mOvershoot < -Math::MACHINE_EPSILON_0 )
315 else if( source == mOvershootDecreaseNotification )
318 // overshoot reducing
320 UpdatePropertyNotifications();
323 void ScrollOvershootEffectRipple::SetOvershoot(float amount, bool animate)
325 float absAmount = fabsf(amount);
326 bool animatingOn = absAmount > Math::MACHINE_EPSILON_0;
327 if( (animatingOn && (mAnimationStateFlags & AnimatingIn)) )
329 // trying to do what we are already doing
330 if( mAnimationStateFlags & AnimateBack )
332 mAnimationStateFlags &= ~AnimateBack;
336 if( (!animatingOn && (mAnimationStateFlags & AnimatingOut)) )
338 // trying to do what we are already doing
341 if( !animatingOn && (mAnimationStateFlags & AnimatingIn) )
343 // dont interrupt while animating on
344 mAnimationStateFlags |= AnimateBack;
347 // When we need to animate overshoot to 0
348 if( mOvershootAnimationDuration > Math::MACHINE_EPSILON_1 )
350 // setup the new overshoot to 0 animation
351 float currentOvershoot = fabsf( mOvershootOverlay.GetProperty( mEffectOvershootProperty ).Get<float>() );
352 float duration = mOvershootAnimationDuration * (animatingOn ? (1.0f - currentOvershoot) : currentOvershoot);
354 if( duration > Math::MACHINE_EPSILON_0 )
356 if(mScrollOvershootAnimation)
358 mScrollOvershootAnimation.FinishedSignal().Disconnect( this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished );
359 mScrollOvershootAnimation.Stop();
360 mScrollOvershootAnimation.Clear();
361 mScrollOvershootAnimation = NULL;
363 mScrollOvershootAnimation = Animation::New(duration);
364 mScrollOvershootAnimation.FinishedSignal().Connect( this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished );
365 mScrollOvershootAnimation.AnimateTo( Property(mOvershootOverlay, mEffectOvershootProperty), amount, TimePeriod(duration) );
366 mScrollOvershootAnimation.Play();
367 mAnimationStateFlags = animatingOn ? AnimatingIn : AnimatingOut;
372 mOvershootOverlay.SetProperty( mEffectOvershootProperty, amount);
374 if( absAmount > Math::MACHINE_EPSILON_1 )
376 UpdateVisibility(true);
380 void ScrollOvershootEffectRipple::OnOvershootAnimFinished(Animation& animation)
382 bool animateOff = false;
383 if( mAnimationStateFlags & AnimatingOut )
385 // should now be offscreen
386 mOvershootOverlay.SetVisible(false);
388 if( (mAnimationStateFlags & AnimateBack) )
392 mScrollOvershootAnimation.FinishedSignal().Disconnect( this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished );
393 mScrollOvershootAnimation.Stop();
394 mScrollOvershootAnimation.Clear();
395 mScrollOvershootAnimation = NULL;
396 mAnimationStateFlags = 0;
399 SetOvershoot(0.0f, true);
403 ScrollOvershootEffectRipplePtr ScrollOvershootEffectRipple::New( bool vertical, Scrollable& scrollable )
405 return new ScrollOvershootEffectRipple(vertical, scrollable);
408 } // namespace Internal
410 } // namespace Toolkit