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>
36 const char* OVERSHOOT_OVERLAY_IMAGE_PATH = DALI_IMAGE_DIR "scroll_overshoot.png";
37 const char* OVERSHOOT_OVERLAY_RIPPLE_IMAGE_PATH = DALI_IMAGE_DIR "overshoot_ripple.png";
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 ScrollOvershootEffectGradient::ScrollOvershootEffectGradient(bool vertical) :
103 ScrollOvershootEffect(vertical),
104 mMaxOvershootImageSize(DEFAULT_MAX_OVERSHOOT_HEIGHT)
106 Image overshootImage = Image::New( OVERSHOOT_OVERLAY_IMAGE_PATH );
107 mOvershootImage = ImageActor::New( overshootImage );
108 mOvershootImage.SetParentOrigin(ParentOrigin::TOP_LEFT);
109 mOvershootImage.SetAnchorPoint(AnchorPoint::TOP_LEFT);
110 mOvershootImage.SetDrawMode(DrawMode::OVERLAY);
113 void ScrollOvershootEffectGradient::Apply(Scrollable& scrollable)
115 Actor scrollableActor = scrollable.Self();
116 int overshootXPropertyIndex = scrollableActor.GetPropertyIndex(Toolkit::ScrollView::SCROLL_OVERSHOOT_X_PROPERTY_NAME);
117 int overshootYPropertyIndex = scrollableActor.GetPropertyIndex(Toolkit::ScrollView::SCROLL_OVERSHOOT_Y_PROPERTY_NAME);
119 Constraint constraint = Constraint::New<Vector3>( Actor::SIZE,
120 Source( scrollableActor, overshootXPropertyIndex ),
121 Source( scrollableActor, overshootYPropertyIndex ),
122 Source( scrollableActor, Actor::SIZE ),
123 boost::bind( &ScrollOvershootEffectGradient::SizeConstraint, this, _1, _2, _3, _4) );
124 mSizeConstraint = mOvershootImage.ApplyConstraint(constraint);
126 constraint = Constraint::New<Quaternion>( Actor::ROTATION,
127 Source( scrollableActor, overshootXPropertyIndex ),
128 Source( scrollableActor, overshootYPropertyIndex ),
129 boost::bind( &ScrollOvershootEffectGradient::RotationConstraint, this, _1, _2, _3) );
130 mRotationConstraint = mOvershootImage.ApplyConstraint(constraint);
132 constraint = Constraint::New<Vector3>( Actor::POSITION,
133 Source( scrollableActor, Actor::SIZE ),
134 Source( scrollableActor, overshootXPropertyIndex ),
135 Source( scrollableActor, overshootYPropertyIndex ),
136 boost::bind( &ScrollOvershootEffectGradient::PositionConstraint, this, _1, _2, _3, _4) );
137 mPositionConstraint = mOvershootImage.ApplyConstraint(constraint);
139 constraint = Constraint::New<bool>( Actor::VISIBLE,
140 Source( scrollableActor, IsVertical() ? scrollableActor.GetPropertyIndex(Scrollable::SCROLLABLE_CAN_SCROLL_VERTICAL) : scrollableActor.GetPropertyIndex(Scrollable::SCROLLABLE_CAN_SCROLL_HORIZONTAL)),
141 boost::bind( &ScrollOvershootEffectGradient::VisibilityConstraint, this, _1, _2) );
142 mVisibilityConstraint = mOvershootImage.ApplyConstraint(constraint);
143 scrollable.AddOverlay(mOvershootImage);
144 SetPropertyNotifications(scrollableActor);
147 void ScrollOvershootEffectGradient::Remove(Scrollable& scrollable)
153 mOvershootImage.RemoveConstraint(mSizeConstraint);
154 mSizeConstraint = NULL;
156 if(mRotationConstraint)
158 mOvershootImage.RemoveConstraint(mRotationConstraint);
159 mRotationConstraint = NULL;
161 if(mPositionConstraint)
163 mOvershootImage.RemoveConstraint(mPositionConstraint);
164 mPositionConstraint = NULL;
166 if(mVisibilityConstraint)
168 mOvershootImage.RemoveConstraint(mVisibilityConstraint);
169 mVisibilityConstraint = NULL;
171 scrollable.RemoveOverlay(mOvershootImage);
175 Vector3 ScrollOvershootEffectGradient::SizeConstraint(const Vector3& current,
176 const PropertyInput& overshootPropertyX, const PropertyInput& overshootPropertyY,
177 const PropertyInput& parentSizeProperty)
179 float overshootx = overshootPropertyX.GetFloat();
180 float overshooty = overshootPropertyY.GetFloat();
181 const Vector3 parentSize = parentSizeProperty.GetVector3();
183 float overlayWidth = IsVertical() ? parentSize.x : parentSize.y;
184 float overlayHeight = mMaxOvershootImageSize * fabsf(IsVertical() ? overshooty : overshootx);
186 return Vector3(overlayWidth, overlayHeight, current.z);
189 Quaternion ScrollOvershootEffectGradient::RotationConstraint(const Quaternion& current,
190 const PropertyInput& overshootPropertyX, const PropertyInput& overshootPropertyY)
192 float overshootx = overshootPropertyX.GetFloat();
193 float overshooty = overshootPropertyY.GetFloat();
199 if(overshooty < -Math::MACHINE_EPSILON_0)
201 rotation = Quaternion(Math::PI, Vector3::ZAXIS);
203 else if(overshooty > Math::MACHINE_EPSILON_0)
205 rotation = Quaternion(0.0f, Vector3::ZAXIS);
210 if(overshootx < -Math::MACHINE_EPSILON_0)
212 rotation = Quaternion(0.5f * Math::PI, Vector3::ZAXIS);
214 else if(overshootx > Math::MACHINE_EPSILON_0)
216 rotation = Quaternion(1.5f * Math::PI, Vector3::ZAXIS);
223 Vector3 ScrollOvershootEffectGradient::PositionConstraint(const Vector3& current,
224 const PropertyInput& parentSizeProperty,
225 const PropertyInput& overshootPropertyX, const PropertyInput& overshootPropertyY)
227 float overshootx = overshootPropertyX.GetFloat();
228 float overshooty = overshootPropertyY.GetFloat();
229 const Vector3 parentSize = parentSizeProperty.GetVector3();
231 Vector3 relativeOffset = Vector3::ZERO;
235 if(overshooty > Math::MACHINE_EPSILON_0)
237 relativeOffset = Vector3(0.0f, 0.0f, 0.0f);
239 else if (overshooty < -Math::MACHINE_EPSILON_0)
241 relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
246 if(overshootx > Math::MACHINE_EPSILON_0)
248 relativeOffset = Vector3(0.0f, 1.0f, 0.0f);
250 else if (overshootx < -Math::MACHINE_EPSILON_0)
252 relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
256 return relativeOffset * parentSize;
259 bool ScrollOvershootEffectGradient::VisibilityConstraint(const bool& current,
260 const PropertyInput& canScrollProperty)
262 return canScrollProperty.GetBoolean();
265 ScrollOvershootEffectGradientPtr ScrollOvershootEffectGradient::New(bool vertical)
267 return new ScrollOvershootEffectGradient(vertical);
273 const std::string OVERSHOOT_PROPERTY_NAME( "uOvershoot" );
274 const std::string OVERSHOOT_IMAGE_COUNT_PROPERTY_NAME( "uOvershootImageCount" );
278 OvershootRippleEffect::OvershootRippleEffect()
282 //Call the Parent copy constructor to add reference to the implementation for this object
283 OvershootRippleEffect::OvershootRippleEffect(ShaderEffect handle)
284 :ShaderEffect(handle)
288 OvershootRippleEffect::~OvershootRippleEffect()
292 OvershootRippleEffect OvershootRippleEffect::New()
294 std::string vertextShader(
295 "precision mediump float; \n"
296 "uniform float uOvershoot; \n"
297 "uniform float uOvershootImageCount; \n"
300 " gl_Position = uProjection * uModelView * vec4(aPosition, 1.0); \n"
301 " vTexCoord = aTexCoord; \n"
302 " vTexCoord.y += (1.0 / uOvershootImageCount) * min(floor((abs(uOvershoot) * (uOvershootImageCount - 1.0)) + 0.5), (uOvershootImageCount - 1.0)); \n"
305 std::string fragmentShader(
308 " gl_FragColor = texture2D(sTexture, vTexCoord); \n"
311 // Create the implementation, temporarily owned on stack,
312 Dali::ShaderEffect shaderEffectCustom = Dali::ShaderEffect::New(
316 /* Pass ownership to OvershootRippleEffect through overloaded constructor, So that it now has access to the
317 Dali::ShaderEffect implementation */
318 OvershootRippleEffect handle( shaderEffectCustom );
319 handle.SetUniform(OVERSHOOT_PROPERTY_NAME, 0.0f);
320 handle.SetUniform(OVERSHOOT_IMAGE_COUNT_PROPERTY_NAME, 10.0f);
324 void OvershootRippleEffect::SetOvershoot( float overshoot )
326 SetUniform( OVERSHOOT_PROPERTY_NAME, overshoot );
329 void OvershootRippleEffect::SetOvershootImageCount( float imageCount )
331 SetUniform( OVERSHOOT_IMAGE_COUNT_PROPERTY_NAME, imageCount );
334 const std::string& OvershootRippleEffect::GetOvershootPropertyName() const
336 return OVERSHOOT_PROPERTY_NAME;
339 const std::string& OvershootRippleEffect::GetOvershootImageCountPropertyName() const
341 return OVERSHOOT_IMAGE_COUNT_PROPERTY_NAME;
344 ScrollOvershootEffectRipple::ScrollOvershootEffectRipple(bool vertical) :
345 ScrollOvershootEffect(vertical),
346 mMaxOvershootImageSize(DEFAULT_MAX_OVERSHOOT_HEIGHT)
348 mRippleEffect = OvershootRippleEffect::New();
349 Image overshootImage = Image::New( OVERSHOOT_OVERLAY_RIPPLE_IMAGE_PATH );
350 mOvershootImage = ImageActor::New( overshootImage );
351 mOvershootImage.SetParentOrigin(ParentOrigin::TOP_LEFT);
352 mOvershootImage.SetAnchorPoint(AnchorPoint::TOP_LEFT);
353 mOvershootImage.SetDrawMode(DrawMode::OVERLAY);
354 mOvershootImage.SetShaderEffect(mRippleEffect);
355 mOvershootImage.SetPixelArea(OVERSHOOT_RIPPLE_IMAGE_1_PIXEL_AREA);
356 mOvershootImage.SetVisible(false);
357 mAnimatingOvershootOn = false;
358 mAnimateOvershootOff = false;
361 void ScrollOvershootEffectRipple::Apply(Scrollable& scrollable)
363 Actor scrollableActor = scrollable.Self();
365 // make sure height is set, since we only create a constraint for image width
366 mOvershootImage.SetSize(OVERSHOOT_RIPPLE_IMAGE_1_PIXEL_AREA.width, OVERSHOOT_RIPPLE_IMAGE_1_PIXEL_AREA.height);
368 UpdateConstraints(scrollableActor);
369 scrollable.AddOverlay(mOvershootImage);
371 SetPropertyNotifications(scrollableActor);
374 void ScrollOvershootEffectRipple::Remove(Scrollable& scrollable)
380 mOvershootImage.RemoveConstraint(mSizeConstraint);
381 mSizeConstraint = NULL;
383 if(mPositionConstraint)
385 mOvershootImage.RemoveConstraint(mPositionConstraint);
386 mPositionConstraint = NULL;
388 scrollable.RemoveOverlay(mOvershootImage);
392 void ScrollOvershootEffectRipple::Reset()
394 mAnimatingOvershootOn = false;
395 mAnimateOvershootOff = false;
396 mOvershootImage.SetVisible(false);
397 mRippleEffect.SetUniform(mRippleEffect.GetOvershootPropertyName(), 0.0f);
398 if(mScrollOvershootAnimation)
400 mScrollOvershootAnimation.Clear();
401 mScrollOvershootAnimation.Reset();
405 void ScrollOvershootEffectRipple::UpdateConstraints(Actor& scrollable)
407 int overshootPropertyIndex = mRippleEffect.GetPropertyIndex(mRippleEffect.GetOvershootPropertyName());
408 Constraint constraint;
411 constraint = Constraint::New<float>( Actor::SIZE_WIDTH,
412 Source( scrollable, IsVertical() ? Actor::SIZE_WIDTH : Actor::SIZE_HEIGHT),
413 EqualToConstraint() );
414 mSizeConstraint = mOvershootImage.ApplyConstraint(constraint);
417 if(!mPositionConstraint)
419 constraint = Constraint::New<Vector3>( Actor::POSITION,
420 Source( scrollable, Actor::SIZE ),
421 Source( mRippleEffect, overshootPropertyIndex ),
422 boost::bind( &ScrollOvershootEffectRipple::PositionConstraint, this, _1, _2, _3) );
423 mPositionConstraint = mOvershootImage.ApplyConstraint(constraint);
427 void ScrollOvershootEffectRipple::SetPropertyNotifications(Actor& scrollable)
429 int overshootXPropertyIndex = scrollable.GetPropertyIndex(Toolkit::ScrollView::SCROLL_OVERSHOOT_X_PROPERTY_NAME);
430 int overshootYPropertyIndex = scrollable.GetPropertyIndex(Toolkit::ScrollView::SCROLL_OVERSHOOT_Y_PROPERTY_NAME);
431 mCanScrollPropertyIndex = scrollable.GetPropertyIndex(IsVertical() ? Scrollable::SCROLLABLE_CAN_SCROLL_VERTICAL : Scrollable::SCROLLABLE_CAN_SCROLL_HORIZONTAL);
432 if(scrollable.OnStage())
434 if(!mOvershootNegativeNotification)
436 mOvershootNegativeNotification = scrollable.AddPropertyNotification(IsVertical() ? overshootYPropertyIndex : overshootXPropertyIndex, LessThanCondition(-Math::MACHINE_EPSILON_1));
437 mOvershootNegativeNotification.SetNotifyMode(PropertyNotification::NotifyOnChanged);
438 mOvershootNegativeNotification.NotifySignal().Connect(this, &ScrollOvershootEffectRipple::OnNegativeOvershootNotification);
441 if(!mOvershootPositiveNotification)
443 mOvershootPositiveNotification = scrollable.AddPropertyNotification(IsVertical() ? overshootYPropertyIndex : overshootXPropertyIndex, GreaterThanCondition(Math::MACHINE_EPSILON_1));
444 mOvershootPositiveNotification.SetNotifyMode(PropertyNotification::NotifyOnChanged);
445 mOvershootPositiveNotification.NotifySignal().Connect(this, &ScrollOvershootEffectRipple::OnPositiveOvershootNotification);
450 Vector3 ScrollOvershootEffectRipple::PositionConstraint(const Vector3& current,
451 const PropertyInput& parentSizeProperty, const PropertyInput& overshootProperty)
453 float overshoot = overshootProperty.GetFloat();
454 const Vector3 parentSize = parentSizeProperty.GetVector3();
456 Vector3 relativeOffset = Vector3::ZERO;
460 if(overshoot > Math::MACHINE_EPSILON_0)
462 relativeOffset = Vector3(0.0f, 0.0f, 0.0f);
464 else if (overshoot < -Math::MACHINE_EPSILON_0)
466 relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
471 if(overshoot > Math::MACHINE_EPSILON_0)
473 relativeOffset = Vector3(0.0f, 1.0f, 0.0f);
475 else if (overshoot < -Math::MACHINE_EPSILON_0)
477 relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
481 return relativeOffset * parentSize;
484 void ScrollOvershootEffectRipple::OnPositiveOvershootNotification(PropertyNotification& source)
486 Actor delegate = Actor::DownCast(source.GetTarget());
487 float overshoot = delegate.GetProperty<float>(source.GetTargetProperty());
488 bool canScroll = delegate.GetProperty<bool>(mCanScrollPropertyIndex);
491 mOvershootImage.SetVisible(false);
494 mOvershootImage.SetVisible(true);
496 if (fabsf(overshoot) < Math::MACHINE_EPSILON_1)
498 AnimateScrollOvershoot(0.0f);
503 const Vector3 imageSize = mOvershootImage.GetCurrentSize();
504 Vector3 relativeOffset = Vector3::ZERO;
505 const Vector3 parentSize = delegate.GetCurrentSize();
506 AnimateScrollOvershoot(1.0f);
509 mOvershootImage.SetRotation(Quaternion(0.0f, Vector3::ZAXIS));
510 mOvershootImage.SetSize(parentSize.width, imageSize.height, imageSize.depth);
511 relativeOffset = Vector3(0.0f, 0.0f, 0.0f);
515 mOvershootImage.SetRotation(Quaternion(1.5f * Math::PI, Vector3::ZAXIS));
516 mOvershootImage.SetSize(parentSize.height, imageSize.height, imageSize.depth);
517 relativeOffset = Vector3(0.0f, 1.0f, 0.0f);
519 mOvershootImage.SetPosition(relativeOffset * parentSize);
523 void ScrollOvershootEffectRipple::OnNegativeOvershootNotification(PropertyNotification& source)
525 Actor delegate = Actor::DownCast(source.GetTarget());
526 float overshoot = delegate.GetProperty<float>(source.GetTargetProperty());
527 bool canScroll = delegate.GetProperty<bool>(mCanScrollPropertyIndex);
530 mOvershootImage.SetVisible(false);
533 mOvershootImage.SetVisible(true);
535 if (fabsf(overshoot) < Math::MACHINE_EPSILON_1)
537 AnimateScrollOvershoot(0.0f);
543 const Vector3 imageSize = mOvershootImage.GetCurrentSize();
544 Vector3 relativeOffset = Vector3::ZERO;
545 const Vector3 parentSize = delegate.GetCurrentSize();
546 AnimateScrollOvershoot(-1.0f);
549 mOvershootImage.SetRotation(Quaternion(Math::PI, Vector3::ZAXIS));
550 mOvershootImage.SetSize(parentSize.width, imageSize.height, imageSize.depth);
551 relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
555 mOvershootImage.SetRotation(Quaternion(0.5f * Math::PI, Vector3::ZAXIS));
556 mOvershootImage.SetSize(parentSize.height, imageSize.height, imageSize.depth);
557 relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
559 mOvershootImage.SetPosition(relativeOffset * parentSize);
563 void ScrollOvershootEffectRipple::AnimateScrollOvershoot(float overshootAmount)
565 bool animatingOn = fabsf(overshootAmount) > Math::MACHINE_EPSILON_1;
567 // make sure we animate back if needed
568 mAnimateOvershootOff = (!animatingOn && mAnimatingOvershootOn);
570 int overShootProperty = mRippleEffect.GetPropertyIndex(mRippleEffect.GetOvershootPropertyName());
571 float currentOvershoot = mRippleEffect.GetProperty<float>(overShootProperty);
572 if(((currentOvershoot < 0.0f && overshootAmount > 0.0f)
573 || (currentOvershoot > 0.0f && overshootAmount < 0.0f)))
575 // cancel current animation
576 mAnimatingOvershootOn = false;
577 // reset currentOvershoot to 0.0f
578 mRippleEffect.SetProperty(overShootProperty, 0.0f);
579 currentOvershoot = 0.0f;
581 if( mAnimatingOvershootOn )
583 // animating on in same direction, do not allow animate off
586 float duration = DEFAULT_OVERSHOOT_ANIMATION_DURATION * (animatingOn ? (1.0f - fabsf(currentOvershoot)) : fabsf(currentOvershoot));
588 if(mScrollOvershootAnimation)
590 mScrollOvershootAnimation.Clear();
591 mScrollOvershootAnimation.Reset();
593 mScrollOvershootAnimation = Animation::New(duration);
594 mScrollOvershootAnimation.FinishedSignal().Connect(this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished);
595 mScrollOvershootAnimation.AnimateTo( Property(mRippleEffect, overShootProperty), overshootAmount, TimePeriod(0.0f, duration) );
596 mScrollOvershootAnimation.Play();
598 mOvershootImage.SetVisible(true);
600 mAnimatingOvershootOn = animatingOn;
603 void ScrollOvershootEffectRipple::OnOvershootAnimFinished(Animation& animation)
605 if(!mAnimatingOvershootOn && !mAnimateOvershootOff)
607 // just finished animating overshoot to 0
608 mOvershootImage.SetVisible(false);
610 mAnimatingOvershootOn = false;
611 mScrollOvershootAnimation.FinishedSignal().Disconnect(this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished);
612 mScrollOvershootAnimation.Clear();
613 mScrollOvershootAnimation.Reset();
614 if(mAnimateOvershootOff)
616 AnimateScrollOvershoot(0.0f);
620 ScrollOvershootEffectRipplePtr ScrollOvershootEffectRipple::New(bool vertical)
622 return new ScrollOvershootEffectRipple(vertical);
625 } // namespace Internal
627 } // namespace Toolkit