2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 // Licensed under the Flora License, Version 1.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://floralicense.org/license/
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.
17 #include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-overshoot-indicator-impl.h>
20 #include <boost/bind.hpp>
22 #include <dali-toolkit/internal/controls/scrollable/scrollable-impl.h>
23 #include <dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view.h>
24 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
37 const float DEFAULT_MAX_OVERSHOOT_HEIGHT = 36.0f; // 36 pixels
38 const Rect<int> OVERSHOOT_RIPPLE_IMAGE_1_PIXEL_AREA( 0, 0, 720, 58 );
39 const float DEFAULT_OVERSHOOT_ANIMATION_DURATION = 0.35f; // time in seconds
41 ScrollOvershootIndicator::ScrollOvershootIndicator(Scrollable& scrollable) :
42 mScrollable(scrollable),
48 ScrollOvershootIndicator::~ScrollOvershootIndicator()
53 ScrollOvershootIndicator* ScrollOvershootIndicator::New(Scrollable& scrollable)
55 ScrollOvershootIndicator* scrollOvershootPtr = new ScrollOvershootIndicator(scrollable);
56 return scrollOvershootPtr;
59 void ScrollOvershootIndicator::Enable(bool enable)
63 Actor scrollableActor = mScrollable.Self();
66 mEffectX = ScrollOvershootEffectRipple::New(false);
68 mEffectX->Apply(mScrollable);
71 mEffectY = ScrollOvershootEffectRipple::New(true);
73 mEffectY->Apply(mScrollable);
74 mEffectY->SetPropertyNotifications(scrollableActor);
80 mEffectX->Remove(mScrollable);
84 mEffectY->Remove(mScrollable);
89 void ScrollOvershootIndicator::Reset()
95 ScrollOvershootEffect::ScrollOvershootEffect(bool vertical) :
101 ScrollOvershootEffectRipple::ScrollOvershootEffectRipple(bool vertical) :
102 ScrollOvershootEffect(vertical),
103 mMaxOvershootImageSize(DEFAULT_MAX_OVERSHOOT_HEIGHT)
105 mRippleEffect = BouncingEffect::New(Scrollable::DEFAULT_OVERSHOOT_COLOUR);
106 mOvershootImage = CreateSolidColorActor(Vector4::ONE);
107 mOvershootImage.SetParentOrigin(ParentOrigin::TOP_LEFT);
108 mOvershootImage.SetAnchorPoint(AnchorPoint::TOP_LEFT);
109 mOvershootImage.SetDrawMode(DrawMode::OVERLAY);
110 mOvershootImage.SetShaderEffect(mRippleEffect);
111 mOvershootImage.SetVisible(false);
112 mAnimatingOvershootOn = false;
113 mAnimateOvershootOff = false;
116 void ScrollOvershootEffectRipple::Apply(Scrollable& scrollable)
118 Actor scrollableActor = scrollable.Self();
120 // make sure height is set, since we only create a constraint for image width
121 mOvershootImage.SetSize(OVERSHOOT_RIPPLE_IMAGE_1_PIXEL_AREA.width, OVERSHOOT_RIPPLE_IMAGE_1_PIXEL_AREA.height);
123 UpdateConstraints(scrollableActor);
124 scrollable.AddOverlay(mOvershootImage);
126 SetPropertyNotifications(scrollableActor);
129 void ScrollOvershootEffectRipple::Remove(Scrollable& scrollable)
135 mOvershootImage.RemoveConstraint(mSizeConstraint);
136 mSizeConstraint = NULL;
138 if(mPositionConstraint)
140 mOvershootImage.RemoveConstraint(mPositionConstraint);
141 mPositionConstraint = NULL;
143 scrollable.RemoveOverlay(mOvershootImage);
147 void ScrollOvershootEffectRipple::Reset()
149 mAnimatingOvershootOn = false;
150 mAnimateOvershootOff = false;
151 mOvershootImage.SetVisible(false);
152 mRippleEffect.SetProgressRate(0.0f);
153 if(mScrollOvershootAnimation)
155 mScrollOvershootAnimation.Clear();
156 mScrollOvershootAnimation.Reset();
160 void ScrollOvershootEffectRipple::UpdateConstraints(Actor& scrollable)
162 int overshootPropertyIndex = mRippleEffect.GetPropertyIndex(mRippleEffect.GetProgressRatePropertyName());
163 Constraint constraint;
166 constraint = Constraint::New<float>( Actor::SIZE_WIDTH,
167 Source( scrollable, IsVertical() ? Actor::SIZE_WIDTH : Actor::SIZE_HEIGHT),
168 EqualToConstraint() );
169 mSizeConstraint = mOvershootImage.ApplyConstraint(constraint);
172 if(!mPositionConstraint)
174 constraint = Constraint::New<Vector3>( Actor::POSITION,
175 Source( scrollable, Actor::SIZE ),
176 Source( mRippleEffect, overshootPropertyIndex ),
177 boost::bind( &ScrollOvershootEffectRipple::PositionConstraint, this, _1, _2, _3) );
178 mPositionConstraint = mOvershootImage.ApplyConstraint(constraint);
182 void ScrollOvershootEffectRipple::SetPropertyNotifications(Actor& scrollable)
184 int overshootXPropertyIndex = scrollable.GetPropertyIndex(Toolkit::ScrollView::SCROLL_OVERSHOOT_X_PROPERTY_NAME);
185 int overshootYPropertyIndex = scrollable.GetPropertyIndex(Toolkit::ScrollView::SCROLL_OVERSHOOT_Y_PROPERTY_NAME);
186 mCanScrollPropertyIndex = scrollable.GetPropertyIndex(IsVertical() ? Scrollable::SCROLLABLE_CAN_SCROLL_VERTICAL : Scrollable::SCROLLABLE_CAN_SCROLL_HORIZONTAL);
188 if(!mOvershootNegativeNotification)
190 mOvershootNegativeNotification = scrollable.AddPropertyNotification(IsVertical() ? overshootYPropertyIndex : overshootXPropertyIndex, LessThanCondition(-Math::MACHINE_EPSILON_1));
191 mOvershootNegativeNotification.SetNotifyMode(PropertyNotification::NotifyOnChanged);
192 mOvershootNegativeNotification.NotifySignal().Connect(this, &ScrollOvershootEffectRipple::OnNegativeOvershootNotification);
195 if(!mOvershootPositiveNotification)
197 mOvershootPositiveNotification = scrollable.AddPropertyNotification(IsVertical() ? overshootYPropertyIndex : overshootXPropertyIndex, GreaterThanCondition(Math::MACHINE_EPSILON_1));
198 mOvershootPositiveNotification.SetNotifyMode(PropertyNotification::NotifyOnChanged);
199 mOvershootPositiveNotification.NotifySignal().Connect(this, &ScrollOvershootEffectRipple::OnPositiveOvershootNotification);
203 Vector3 ScrollOvershootEffectRipple::PositionConstraint(const Vector3& current,
204 const PropertyInput& parentSizeProperty, const PropertyInput& overshootProperty)
206 float overshoot = overshootProperty.GetFloat();
207 const Vector3 parentSize = parentSizeProperty.GetVector3();
209 Vector3 relativeOffset = Vector3::ZERO;
213 if(overshoot > Math::MACHINE_EPSILON_0)
215 relativeOffset = Vector3(0.0f, 0.0f, 0.0f);
217 else if (overshoot < -Math::MACHINE_EPSILON_0)
219 relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
224 if(overshoot > Math::MACHINE_EPSILON_0)
226 relativeOffset = Vector3(0.0f, 1.0f, 0.0f);
228 else if (overshoot < -Math::MACHINE_EPSILON_0)
230 relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
234 return relativeOffset * parentSize;
237 void ScrollOvershootEffectRipple::OnPositiveOvershootNotification(PropertyNotification& source)
239 Actor delegate = Actor::DownCast(source.GetTarget());
240 float overshoot = delegate.GetProperty<float>(source.GetTargetProperty());
241 bool canScroll = delegate.GetProperty<bool>(mCanScrollPropertyIndex);
244 mOvershootImage.SetVisible(false);
247 mOvershootImage.SetVisible(true);
249 if (fabsf(overshoot) < Math::MACHINE_EPSILON_1)
251 AnimateScrollOvershoot(0.0f);
256 const Vector3 imageSize = mOvershootImage.GetCurrentSize();
257 Vector3 relativeOffset = Vector3::ZERO;
258 const Vector3 parentSize = delegate.GetCurrentSize();
259 AnimateScrollOvershoot(1.0f);
262 mOvershootImage.SetRotation(Quaternion(0.0f, Vector3::ZAXIS));
263 mOvershootImage.SetSize(parentSize.width, imageSize.height, imageSize.depth);
264 relativeOffset = Vector3(0.0f, 0.0f, 0.0f);
268 mOvershootImage.SetRotation(Quaternion(1.5f * Math::PI, Vector3::ZAXIS));
269 mOvershootImage.SetSize(parentSize.height, imageSize.height, imageSize.depth);
270 relativeOffset = Vector3(0.0f, 1.0f, 0.0f);
272 mOvershootImage.SetPosition(relativeOffset * parentSize);
276 void ScrollOvershootEffectRipple::OnNegativeOvershootNotification(PropertyNotification& source)
278 Actor delegate = Actor::DownCast(source.GetTarget());
279 float overshoot = delegate.GetProperty<float>(source.GetTargetProperty());
280 bool canScroll = delegate.GetProperty<bool>(mCanScrollPropertyIndex);
283 mOvershootImage.SetVisible(false);
286 mOvershootImage.SetVisible(true);
288 if (fabsf(overshoot) < Math::MACHINE_EPSILON_1)
290 AnimateScrollOvershoot(0.0f);
296 const Vector3 imageSize = mOvershootImage.GetCurrentSize();
297 Vector3 relativeOffset = Vector3::ZERO;
298 const Vector3 parentSize = delegate.GetCurrentSize();
299 AnimateScrollOvershoot(-1.0f);
302 mOvershootImage.SetRotation(Quaternion(Math::PI, Vector3::ZAXIS));
303 mOvershootImage.SetSize(parentSize.width, imageSize.height, imageSize.depth);
304 relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
308 mOvershootImage.SetRotation(Quaternion(0.5f * Math::PI, Vector3::ZAXIS));
309 mOvershootImage.SetSize(parentSize.height, imageSize.height, imageSize.depth);
310 relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
312 mOvershootImage.SetPosition(relativeOffset * parentSize);
316 void ScrollOvershootEffectRipple::AnimateScrollOvershoot(float overshootAmount)
318 bool animatingOn = fabsf(overshootAmount) > Math::MACHINE_EPSILON_1;
320 // make sure we animate back if needed
321 mAnimateOvershootOff = (!animatingOn && mAnimatingOvershootOn);
323 int overShootProperty = mRippleEffect.GetPropertyIndex(mRippleEffect.GetProgressRatePropertyName());
324 float currentOvershoot = mRippleEffect.GetProperty<float>(overShootProperty);
325 if(((currentOvershoot < 0.0f && overshootAmount > 0.0f)
326 || (currentOvershoot > 0.0f && overshootAmount < 0.0f)))
328 // cancel current animation
329 mAnimatingOvershootOn = false;
330 // reset currentOvershoot to 0.0f
331 mRippleEffect.SetProperty(overShootProperty, 0.0f);
332 currentOvershoot = 0.0f;
334 if( mAnimatingOvershootOn )
336 // animating on in same direction, do not allow animate off
339 float duration = DEFAULT_OVERSHOOT_ANIMATION_DURATION * (animatingOn ? (1.0f - fabsf(currentOvershoot)) : fabsf(currentOvershoot));
341 if(mScrollOvershootAnimation)
343 mScrollOvershootAnimation.Clear();
344 mScrollOvershootAnimation.Reset();
346 mScrollOvershootAnimation = Animation::New(duration);
347 mScrollOvershootAnimation.FinishedSignal().Connect(this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished);
348 mScrollOvershootAnimation.AnimateTo( Property(mRippleEffect, overShootProperty), overshootAmount, TimePeriod(0.0f, duration) );
349 mScrollOvershootAnimation.Play();
351 mOvershootImage.SetVisible(true);
353 mAnimatingOvershootOn = animatingOn;
356 void ScrollOvershootEffectRipple::OnOvershootAnimFinished(Animation& animation)
358 if(!mAnimatingOvershootOn && !mAnimateOvershootOff)
360 // just finished animating overshoot to 0
361 mOvershootImage.SetVisible(false);
363 mAnimatingOvershootOn = false;
364 mScrollOvershootAnimation.FinishedSignal().Disconnect(this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished);
365 mScrollOvershootAnimation.Clear();
366 mScrollOvershootAnimation.Reset();
367 if(mAnimateOvershootOff)
369 AnimateScrollOvershoot(0.0f);
373 ScrollOvershootEffectRipplePtr ScrollOvershootEffectRipple::New(bool vertical)
375 return new ScrollOvershootEffectRipple(vertical);
378 } // namespace Internal
380 } // namespace Toolkit