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.
18 #include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-overshoot-indicator-impl.h>
21 #include <boost/bind.hpp>
23 #include <dali-toolkit/internal/controls/scrollable/scrollable-impl.h>
24 #include <dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view.h>
25 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
38 const float DEFAULT_MAX_OVERSHOOT_HEIGHT = 36.0f; // 36 pixels
39 const Rect<int> OVERSHOOT_RIPPLE_IMAGE_1_PIXEL_AREA( 0, 0, 720, 58 );
40 const float DEFAULT_OVERSHOOT_ANIMATION_DURATION = 0.35f; // time in seconds
41 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
42 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
43 const float OVERSHOOT_NOTIFY_STEP = 0.1f; // amount to set notifications beyond current overshoot value
45 ScrollOvershootIndicator::ScrollOvershootIndicator() :
51 ScrollOvershootIndicator::~ScrollOvershootIndicator()
56 ScrollOvershootIndicator* ScrollOvershootIndicator::New()
58 ScrollOvershootIndicator* scrollOvershootPtr = new ScrollOvershootIndicator();
59 return scrollOvershootPtr;
62 void ScrollOvershootIndicator::AttachToScrollable(Scrollable& scrollable)
66 mEffectX = ScrollOvershootEffectRipple::New(false, scrollable);
71 mEffectY = ScrollOvershootEffectRipple::New(true, scrollable);
76 void ScrollOvershootIndicator::DetachFromScrollable(Scrollable& scrollable)
80 mEffectX->Remove(scrollable);
84 mEffectY->Remove(scrollable);
88 void ScrollOvershootIndicator::Reset()
94 ScrollOvershootEffect::ScrollOvershootEffect( bool vertical ) :
100 bool ScrollOvershootEffect::IsVertical() const
105 ScrollOvershootEffectRipple::ScrollOvershootEffectRipple( bool vertical, Scrollable& scrollable ) :
106 ScrollOvershootEffect( vertical ),
107 mAttachedScrollView(scrollable),
108 mCanScrollPropertyIndex(Property::INVALID_INDEX),
109 mOvershootProperty(Property::INVALID_INDEX),
110 mEffectOvershootProperty(Property::INVALID_INDEX),
111 mMaxOvershootImageSize(DEFAULT_MAX_OVERSHOOT_HEIGHT),
112 mOvershootAnimationDuration(DEFAULT_OVERSHOOT_ANIMATION_DURATION),
114 mAnimationStateFlags(0)
116 mRippleEffect = BouncingEffect::New(Scrollable::DEFAULT_OVERSHOOT_COLOUR);
117 mOvershootImage = CreateSolidColorActor(Vector4::ONE);
118 mOvershootImage.SetParentOrigin(ParentOrigin::TOP_LEFT);
119 mOvershootImage.SetAnchorPoint(AnchorPoint::TOP_LEFT);
120 mOvershootImage.SetDrawMode(DrawMode::OVERLAY);
121 mOvershootImage.SetShaderEffect(mRippleEffect);
122 mOvershootImage.SetVisible(false);
125 void ScrollOvershootEffectRipple::Apply()
127 Actor self = mAttachedScrollView.Self();
128 mOvershootProperty = self.GetPropertyIndex(IsVertical() ? Toolkit::ScrollView::SCROLL_OVERSHOOT_Y_PROPERTY_NAME : Toolkit::ScrollView::SCROLL_OVERSHOOT_X_PROPERTY_NAME);
129 mCanScrollPropertyIndex = self.GetPropertyIndex(IsVertical() ? Scrollable::SCROLLABLE_CAN_SCROLL_VERTICAL : Scrollable::SCROLLABLE_CAN_SCROLL_HORIZONTAL);
130 mEffectOvershootProperty = mRippleEffect.GetPropertyIndex(mRippleEffect.GetProgressRatePropertyName());
132 // make sure height is set, since we only create a constraint for image width
133 mOvershootImage.SetSize(OVERSHOOT_RIPPLE_IMAGE_1_PIXEL_AREA.width, OVERSHOOT_RIPPLE_IMAGE_1_PIXEL_AREA.height);
135 mAttachedScrollView.AddOverlay(mOvershootImage);
137 UpdatePropertyNotifications();
140 void ScrollOvershootEffectRipple::Remove( Scrollable& scrollable )
144 if(mOvershootIncreaseNotification)
146 scrollable.Self().RemovePropertyNotification(mOvershootIncreaseNotification);
147 mOvershootIncreaseNotification.Reset();
149 if(mOvershootDecreaseNotification)
151 scrollable.Self().RemovePropertyNotification(mOvershootDecreaseNotification);
152 mOvershootDecreaseNotification.Reset();
154 scrollable.RemoveOverlay(mOvershootImage);
158 void ScrollOvershootEffectRipple::Reset()
160 mOvershootImage.SetVisible(false);
161 mRippleEffect.SetUniform(mRippleEffect.GetProgressRatePropertyName(), 0.0f);
164 void ScrollOvershootEffectRipple::UpdatePropertyNotifications()
166 float absOvershoot = fabsf(mOvershoot);
168 Actor self = mAttachedScrollView.Self();
169 // update overshoot increase notify
170 if( mOvershootIncreaseNotification )
172 self.RemovePropertyNotification( mOvershootIncreaseNotification );
173 mOvershootIncreaseNotification.Reset();
175 if( absOvershoot < MAX_OVERSHOOT_NOTIFY_AMOUNT )
177 float increaseStep = absOvershoot + OVERSHOOT_NOTIFY_STEP;
178 if( increaseStep > MAX_OVERSHOOT_NOTIFY_AMOUNT )
180 increaseStep = MAX_OVERSHOOT_NOTIFY_AMOUNT;
182 mOvershootIncreaseNotification = self.AddPropertyNotification( mOvershootProperty, OutsideCondition(-increaseStep, increaseStep) );
183 mOvershootIncreaseNotification.SetNotifyMode(PropertyNotification::NotifyOnTrue);
184 mOvershootIncreaseNotification.NotifySignal().Connect(this, &ScrollOvershootEffectRipple::OnOvershootNotification);
187 // update overshoot decrease notify
188 if( mOvershootDecreaseNotification )
190 self.RemovePropertyNotification( mOvershootDecreaseNotification );
191 mOvershootDecreaseNotification.Reset();
193 if( absOvershoot > MIN_OVERSHOOT_NOTIFY_AMOUNT )
195 float reduceStep = absOvershoot - OVERSHOOT_NOTIFY_STEP;
196 if( reduceStep < MIN_OVERSHOOT_NOTIFY_AMOUNT )
198 reduceStep = MIN_OVERSHOOT_NOTIFY_AMOUNT;
200 mOvershootDecreaseNotification = self.AddPropertyNotification( mOvershootProperty, InsideCondition(-reduceStep, reduceStep) );
201 mOvershootDecreaseNotification.SetNotifyMode(PropertyNotification::NotifyOnTrue);
202 mOvershootDecreaseNotification.NotifySignal().Connect(this, &ScrollOvershootEffectRipple::OnOvershootNotification);
206 void ScrollOvershootEffectRipple::UpdateVisibility( bool visible )
208 mOvershootImage.SetVisible(visible);
209 // make sure overshoot image is correctly placed
212 Actor self = mAttachedScrollView.Self();
213 if(mOvershoot > 0.0f)
215 // positive overshoot
216 const Vector3 imageSize = mOvershootImage.GetCurrentSize();
217 Vector3 relativeOffset;
218 const Vector3 parentSize = self.GetCurrentSize();
221 mOvershootImage.SetRotation(Quaternion(0.0f, Vector3::ZAXIS));
222 mOvershootImage.SetSize(parentSize.width, imageSize.height, imageSize.depth);
226 mOvershootImage.SetRotation(Quaternion(1.5f * Math::PI, Vector3::ZAXIS));
227 mOvershootImage.SetSize(parentSize.height, imageSize.height, imageSize.depth);
228 relativeOffset = Vector3(0.0f, 1.0f, 0.0f);
230 mOvershootImage.SetPosition(relativeOffset * parentSize);
234 // negative overshoot
235 const Vector3 imageSize = mOvershootImage.GetCurrentSize();
236 Vector3 relativeOffset;
237 const Vector3 parentSize = self.GetCurrentSize();
240 mOvershootImage.SetRotation(Quaternion(Math::PI, Vector3::ZAXIS));
241 mOvershootImage.SetSize(parentSize.width, imageSize.height, imageSize.depth);
242 relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
246 mOvershootImage.SetRotation(Quaternion(0.5f * Math::PI, Vector3::ZAXIS));
247 mOvershootImage.SetSize(parentSize.height, imageSize.height, imageSize.depth);
248 relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
250 mOvershootImage.SetPosition(relativeOffset * parentSize);
255 void ScrollOvershootEffectRipple::OnOvershootNotification(PropertyNotification& source)
257 Actor self = mAttachedScrollView.Self();
258 mOvershoot = self.GetProperty<float>(mOvershootProperty);
259 if( source == mOvershootIncreaseNotification )
261 if( mOvershoot > Math::MACHINE_EPSILON_0 )
265 else if ( mOvershoot < -Math::MACHINE_EPSILON_0 )
270 else if( source == mOvershootDecreaseNotification )
273 // overshoot reducing
275 UpdatePropertyNotifications();
278 void ScrollOvershootEffectRipple::SetOvershoot(float amount, bool animate)
280 float absAmount = fabsf(amount);
281 bool animatingOn = absAmount > Math::MACHINE_EPSILON_0;
282 if( (animatingOn && (mAnimationStateFlags & AnimatingIn)) )
284 // trying to do what we are already doing
285 if( mAnimationStateFlags & AnimateBack )
287 mAnimationStateFlags &= ~AnimateBack;
291 if( (!animatingOn && (mAnimationStateFlags & AnimatingOut)) )
293 // trying to do what we are already doing
296 if( !animatingOn && (mAnimationStateFlags & AnimatingIn) )
298 // dont interrupt while animating on
299 mAnimationStateFlags |= AnimateBack;
302 // When we need to animate overshoot to 0
303 if( mOvershootAnimationDuration > Math::MACHINE_EPSILON_1 )
305 // setup the new overshoot to 0 animation
306 float currentOvershoot = fabsf( mRippleEffect.GetProperty<float>( mEffectOvershootProperty ) );
307 float duration = mOvershootAnimationDuration * (animatingOn ? (1.0f - currentOvershoot) : currentOvershoot);
309 if( duration > Math::MACHINE_EPSILON_0 )
311 if(mScrollOvershootAnimation)
313 mScrollOvershootAnimation.FinishedSignal().Disconnect( this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished );
314 mScrollOvershootAnimation.Stop();
315 mScrollOvershootAnimation.Clear();
316 mScrollOvershootAnimation = NULL;
318 mScrollOvershootAnimation = Animation::New(duration);
319 mScrollOvershootAnimation.FinishedSignal().Connect( this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished );
320 mScrollOvershootAnimation.AnimateTo( Property(mRippleEffect, mEffectOvershootProperty), amount, TimePeriod(0.0f, duration) );
321 mScrollOvershootAnimation.Play();
322 mAnimationStateFlags = animatingOn ? AnimatingIn : AnimatingOut;
327 mRippleEffect.SetProgressRate(amount);
329 if( absAmount > Math::MACHINE_EPSILON_1 )
331 UpdateVisibility(true);
335 void ScrollOvershootEffectRipple::OnOvershootAnimFinished(Animation& animation)
337 bool animateOff = false;
338 if( mAnimationStateFlags & AnimatingOut )
340 // should now be offscreen
341 mOvershootImage.SetVisible(false);
343 if( (mAnimationStateFlags & AnimateBack) )
347 mScrollOvershootAnimation.FinishedSignal().Disconnect( this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished );
348 mScrollOvershootAnimation.Stop();
349 mScrollOvershootAnimation.Clear();
350 mScrollOvershootAnimation = NULL;
351 mAnimationStateFlags = 0;
354 SetOvershoot(0.0f, true);
358 ScrollOvershootEffectRipplePtr ScrollOvershootEffectRipple::New( bool vertical, Scrollable& scrollable )
360 return new ScrollOvershootEffectRipple(vertical, scrollable);
363 } // namespace Internal
365 } // namespace Toolkit