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
42 ScrollOvershootIndicator::ScrollOvershootIndicator(Scrollable& scrollable) :
43 mScrollable(scrollable),
49 ScrollOvershootIndicator::~ScrollOvershootIndicator()
54 ScrollOvershootIndicator* ScrollOvershootIndicator::New(Scrollable& scrollable)
56 ScrollOvershootIndicator* scrollOvershootPtr = new ScrollOvershootIndicator(scrollable);
57 return scrollOvershootPtr;
60 void ScrollOvershootIndicator::Enable(bool enable)
64 Actor scrollableActor = mScrollable.Self();
67 mEffectX = ScrollOvershootEffectRipple::New(false);
69 mEffectX->Apply(mScrollable);
72 mEffectY = ScrollOvershootEffectRipple::New(true);
74 mEffectY->Apply(mScrollable);
75 mEffectY->SetPropertyNotifications(scrollableActor);
81 mEffectX->Remove(mScrollable);
85 mEffectY->Remove(mScrollable);
90 void ScrollOvershootIndicator::Reset()
96 ScrollOvershootEffect::ScrollOvershootEffect(bool vertical) :
102 ScrollOvershootEffectRipple::ScrollOvershootEffectRipple(bool vertical) :
103 ScrollOvershootEffect(vertical),
104 mMaxOvershootImageSize(DEFAULT_MAX_OVERSHOOT_HEIGHT)
106 mRippleEffect = BouncingEffect::New(Scrollable::DEFAULT_OVERSHOOT_COLOUR);
107 mOvershootImage = CreateSolidColorActor(Vector4::ONE);
108 mOvershootImage.SetParentOrigin(ParentOrigin::TOP_LEFT);
109 mOvershootImage.SetAnchorPoint(AnchorPoint::TOP_LEFT);
110 mOvershootImage.SetDrawMode(DrawMode::OVERLAY);
111 mOvershootImage.SetShaderEffect(mRippleEffect);
112 mOvershootImage.SetVisible(false);
113 mAnimatingOvershootOn = false;
114 mAnimateOvershootOff = false;
117 void ScrollOvershootEffectRipple::Apply(Scrollable& scrollable)
119 Actor scrollableActor = scrollable.Self();
121 // make sure height is set, since we only create a constraint for image width
122 mOvershootImage.SetSize(OVERSHOOT_RIPPLE_IMAGE_1_PIXEL_AREA.width, OVERSHOOT_RIPPLE_IMAGE_1_PIXEL_AREA.height);
124 UpdateConstraints(scrollableActor);
125 scrollable.AddOverlay(mOvershootImage);
127 SetPropertyNotifications(scrollableActor);
130 void ScrollOvershootEffectRipple::Remove(Scrollable& scrollable)
136 mOvershootImage.RemoveConstraint(mSizeConstraint);
137 mSizeConstraint = NULL;
139 if(mPositionConstraint)
141 mOvershootImage.RemoveConstraint(mPositionConstraint);
142 mPositionConstraint = NULL;
144 scrollable.RemoveOverlay(mOvershootImage);
148 void ScrollOvershootEffectRipple::Reset()
150 mAnimatingOvershootOn = false;
151 mAnimateOvershootOff = false;
152 mOvershootImage.SetVisible(false);
153 mRippleEffect.SetProgressRate(0.0f);
154 if(mScrollOvershootAnimation)
156 mScrollOvershootAnimation.Clear();
157 mScrollOvershootAnimation.Reset();
161 void ScrollOvershootEffectRipple::UpdateConstraints(Actor& scrollable)
163 int overshootPropertyIndex = mRippleEffect.GetPropertyIndex(mRippleEffect.GetProgressRatePropertyName());
164 Constraint constraint;
167 constraint = Constraint::New<float>( Actor::SIZE_WIDTH,
168 Source( scrollable, IsVertical() ? Actor::SIZE_WIDTH : Actor::SIZE_HEIGHT),
169 EqualToConstraint() );
170 mSizeConstraint = mOvershootImage.ApplyConstraint(constraint);
173 if(!mPositionConstraint)
175 constraint = Constraint::New<Vector3>( Actor::POSITION,
176 Source( scrollable, Actor::SIZE ),
177 Source( mRippleEffect, overshootPropertyIndex ),
178 boost::bind( &ScrollOvershootEffectRipple::PositionConstraint, this, _1, _2, _3) );
179 mPositionConstraint = mOvershootImage.ApplyConstraint(constraint);
183 void ScrollOvershootEffectRipple::SetPropertyNotifications(Actor& scrollable)
185 int overshootXPropertyIndex = scrollable.GetPropertyIndex(Toolkit::ScrollView::SCROLL_OVERSHOOT_X_PROPERTY_NAME);
186 int overshootYPropertyIndex = scrollable.GetPropertyIndex(Toolkit::ScrollView::SCROLL_OVERSHOOT_Y_PROPERTY_NAME);
187 mCanScrollPropertyIndex = scrollable.GetPropertyIndex(IsVertical() ? Scrollable::SCROLLABLE_CAN_SCROLL_VERTICAL : Scrollable::SCROLLABLE_CAN_SCROLL_HORIZONTAL);
189 if(!mOvershootNegativeNotification)
191 mOvershootNegativeNotification = scrollable.AddPropertyNotification(IsVertical() ? overshootYPropertyIndex : overshootXPropertyIndex, LessThanCondition(-Math::MACHINE_EPSILON_1));
192 mOvershootNegativeNotification.SetNotifyMode(PropertyNotification::NotifyOnChanged);
193 mOvershootNegativeNotification.NotifySignal().Connect(this, &ScrollOvershootEffectRipple::OnNegativeOvershootNotification);
196 if(!mOvershootPositiveNotification)
198 mOvershootPositiveNotification = scrollable.AddPropertyNotification(IsVertical() ? overshootYPropertyIndex : overshootXPropertyIndex, GreaterThanCondition(Math::MACHINE_EPSILON_1));
199 mOvershootPositiveNotification.SetNotifyMode(PropertyNotification::NotifyOnChanged);
200 mOvershootPositiveNotification.NotifySignal().Connect(this, &ScrollOvershootEffectRipple::OnPositiveOvershootNotification);
204 Vector3 ScrollOvershootEffectRipple::PositionConstraint(const Vector3& current,
205 const PropertyInput& parentSizeProperty, const PropertyInput& overshootProperty)
207 float overshoot = overshootProperty.GetFloat();
208 const Vector3 parentSize = parentSizeProperty.GetVector3();
210 Vector3 relativeOffset = Vector3::ZERO;
214 if(overshoot > Math::MACHINE_EPSILON_0)
216 relativeOffset = Vector3(0.0f, 0.0f, 0.0f);
218 else if (overshoot < -Math::MACHINE_EPSILON_0)
220 relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
225 if(overshoot > Math::MACHINE_EPSILON_0)
227 relativeOffset = Vector3(0.0f, 1.0f, 0.0f);
229 else if (overshoot < -Math::MACHINE_EPSILON_0)
231 relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
235 return relativeOffset * parentSize;
238 void ScrollOvershootEffectRipple::OnPositiveOvershootNotification(PropertyNotification& source)
240 Actor delegate = Actor::DownCast(source.GetTarget());
241 float overshoot = delegate.GetProperty<float>(source.GetTargetProperty());
242 bool canScroll = delegate.GetProperty<bool>(mCanScrollPropertyIndex);
245 mOvershootImage.SetVisible(false);
248 mOvershootImage.SetVisible(true);
250 if (fabsf(overshoot) < Math::MACHINE_EPSILON_1)
252 AnimateScrollOvershoot(0.0f);
257 const Vector3 imageSize = mOvershootImage.GetCurrentSize();
258 Vector3 relativeOffset = Vector3::ZERO;
259 const Vector3 parentSize = delegate.GetCurrentSize();
260 AnimateScrollOvershoot(1.0f);
263 mOvershootImage.SetRotation(Quaternion(0.0f, Vector3::ZAXIS));
264 mOvershootImage.SetSize(parentSize.width, imageSize.height, imageSize.depth);
265 relativeOffset = Vector3(0.0f, 0.0f, 0.0f);
269 mOvershootImage.SetRotation(Quaternion(1.5f * Math::PI, Vector3::ZAXIS));
270 mOvershootImage.SetSize(parentSize.height, imageSize.height, imageSize.depth);
271 relativeOffset = Vector3(0.0f, 1.0f, 0.0f);
273 mOvershootImage.SetPosition(relativeOffset * parentSize);
277 void ScrollOvershootEffectRipple::OnNegativeOvershootNotification(PropertyNotification& source)
279 Actor delegate = Actor::DownCast(source.GetTarget());
280 float overshoot = delegate.GetProperty<float>(source.GetTargetProperty());
281 bool canScroll = delegate.GetProperty<bool>(mCanScrollPropertyIndex);
284 mOvershootImage.SetVisible(false);
287 mOvershootImage.SetVisible(true);
289 if (fabsf(overshoot) < Math::MACHINE_EPSILON_1)
291 AnimateScrollOvershoot(0.0f);
297 const Vector3 imageSize = mOvershootImage.GetCurrentSize();
298 Vector3 relativeOffset = Vector3::ZERO;
299 const Vector3 parentSize = delegate.GetCurrentSize();
300 AnimateScrollOvershoot(-1.0f);
303 mOvershootImage.SetRotation(Quaternion(Math::PI, Vector3::ZAXIS));
304 mOvershootImage.SetSize(parentSize.width, imageSize.height, imageSize.depth);
305 relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
309 mOvershootImage.SetRotation(Quaternion(0.5f * Math::PI, Vector3::ZAXIS));
310 mOvershootImage.SetSize(parentSize.height, imageSize.height, imageSize.depth);
311 relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
313 mOvershootImage.SetPosition(relativeOffset * parentSize);
317 void ScrollOvershootEffectRipple::AnimateScrollOvershoot(float overshootAmount)
319 bool animatingOn = fabsf(overshootAmount) > Math::MACHINE_EPSILON_1;
321 // make sure we animate back if needed
322 mAnimateOvershootOff = (!animatingOn && mAnimatingOvershootOn);
324 int overShootProperty = mRippleEffect.GetPropertyIndex(mRippleEffect.GetProgressRatePropertyName());
325 float currentOvershoot = mRippleEffect.GetProperty<float>(overShootProperty);
326 if(((currentOvershoot < 0.0f && overshootAmount > 0.0f)
327 || (currentOvershoot > 0.0f && overshootAmount < 0.0f)))
329 // cancel current animation
330 mAnimatingOvershootOn = false;
331 // reset currentOvershoot to 0.0f
332 mRippleEffect.SetProperty(overShootProperty, 0.0f);
333 currentOvershoot = 0.0f;
335 if( mAnimatingOvershootOn )
337 // animating on in same direction, do not allow animate off
340 float duration = DEFAULT_OVERSHOOT_ANIMATION_DURATION * (animatingOn ? (1.0f - fabsf(currentOvershoot)) : fabsf(currentOvershoot));
342 if(mScrollOvershootAnimation)
344 mScrollOvershootAnimation.Clear();
345 mScrollOvershootAnimation.Reset();
347 mScrollOvershootAnimation = Animation::New(duration);
348 mScrollOvershootAnimation.FinishedSignal().Connect(this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished);
349 mScrollOvershootAnimation.AnimateTo( Property(mRippleEffect, overShootProperty), overshootAmount, TimePeriod(0.0f, duration) );
350 mScrollOvershootAnimation.Play();
352 mOvershootImage.SetVisible(true);
354 mAnimatingOvershootOn = animatingOn;
357 void ScrollOvershootEffectRipple::OnOvershootAnimFinished(Animation& animation)
359 if(!mAnimatingOvershootOn && !mAnimateOvershootOff)
361 // just finished animating overshoot to 0
362 mOvershootImage.SetVisible(false);
364 mAnimatingOvershootOn = false;
365 mScrollOvershootAnimation.FinishedSignal().Disconnect(this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished);
366 mScrollOvershootAnimation.Clear();
367 mScrollOvershootAnimation.Reset();
368 if(mAnimateOvershootOff)
370 AnimateScrollOvershoot(0.0f);
374 ScrollOvershootEffectRipplePtr ScrollOvershootEffectRipple::New(bool vertical)
376 return new ScrollOvershootEffectRipple(vertical);
379 } // namespace Internal
381 } // namespace Toolkit