3 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
20 #include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-slide-effect-impl.h>
23 #include <dali/public-api/events/touch-event.h>
24 #include <dali/public-api/common/stage.h>
30 typedef Dali::Toolkit::Internal::ScrollSlideInfo ScrollSlideInfo;
31 typedef Dali::Toolkit::Internal::ScrollSlideInfoPtr ScrollSlideInfoPtr;
43 * ScrollSlideInfo structure contains
44 * common info that is shared amongst the constraints applied to Actors.
45 * The constraints + effect all share ownership of this info struct.
46 * The info is written to by the ScrollSlideInfoUpdate constraint. While
47 * the other constraints read from by the other constraints. Due to the order
48 * in which the constraints are applied, all constraints will get the current
49 * property values for these properties.
50 * The advantage of doing this is that:
51 * A) Constraints are not restricted by the 6 property limit. to function.
52 * B) Properties which rarely change or only change when another property changes
53 * (e.g. time), such as scroll position, scroll domain, size, wrap mode don't need
54 * to be checked for each constraint to apply.
56 class ScrollSlideInfo : public RefObject
73 virtual ~ScrollSlideInfo()
79 Vector3 mScrollPosition;
80 Vector3 mEffectReference;
82 Vector3 mScrollPositionMin;
83 Vector3 mScrollPositionMax;
89 } // namespace Internal
91 } // namespace Toolkit
96 namespace // unnamed namespace
99 const float SLIDEEFFECT_ANIMATION_MAX_TIME = 60.0f; ///< Animation time (every time finishes, checks if it needs to go again)
100 const float COMPLETION_START_DURATION = 0.25f; ///< Maximum time for completion of effect after scroll-view initially completes (due to delay effect)
101 const float COMPLETION_END_DURATION = 5.0f; ///< Maximum time for completion of effect after scroll-view initially completes (due to delay effect)
102 const float ANIMATION_BLEND_COEFFICIENT = 0.05f; ///< Animation blending coefficient (blends between target value e.g. 5% and current value 9%)
103 const float INV_ANIMATION_BLEND_COEFFICIENT = (1.0f - ANIMATION_BLEND_COEFFICIENT);
104 const float DEFAULT_MAX_DELAY_DURATION = 0.25f; ///< Default maximum delay duration of the effect after scroll completes is 0.25f
105 const float EFFECT_SNAP_GROW_DURATION = 0.33f; ///< Take 1/3rd of a second for the snap effect property to grow
106 const float EFFECT_SNAP_DECAY_DURATION = 0.667f; ///< Take 2/3rds of a second for the snap effect property to decay
109 * Gets a property index. If the property doesn't already exist, then
110 * it will create the property.
111 * @param[in] handle The handle that owns or will own the property
112 * @param[in] name The name for this property
113 * @param[in] propertyValue The initial value for this property
114 * @return The property index for this property is returned.
116 Property::Index SafeRegisterProperty( Handle& handle, const std::string& name, Property::Value propertyValue )
118 Property::Index index = handle.GetPropertyIndex( name );
120 if(index == Property::INVALID_INDEX)
122 index = handle.RegisterProperty( name, propertyValue );
129 * Re-scales input value x from x0 - x1, to linearly map
130 * over the values y0 - y1. Values outside of this range
131 * will also conform to the trend (gradient) set.
132 * @param[in] y0 output minimum bound
133 * @param[in] y1 output maximum bound
134 * @param[in] x input X value
135 * @param[in] x0 (optional) input minimum bound (default 0.0)
136 * @param[in] x1 (optional) input maximum bound (default 1.0)
137 * @return The result of the mapping is returned.
139 float Mix(float y0, float y1, float x, float x0 = 0.0f, float x1 = 1.0f)
141 return y0 + (y1 - y0) * (x - x0) / (x1-x0);
145 * Returns the value of x chasing target.
146 * returns a value of x which is closer to target.
147 * but limited by maxDelta. #
152 * result is 30.0f (x is 20.0f units closer to target)
153 * However, if x is already within maxDelta units
154 * of target, x will equal target.
159 * result is 50.0f (x was already within 20.0f units of target)
161 float Chase( float x, float target, float maxDelta )
163 float delta = target - x;
167 x = std::min( x + maxDelta, target );
171 x = std::max( x - maxDelta, target );
177 // constraints ////////////////////////////////////////////////////////////////
180 * ScrollSlideInfoUpdate
182 * Info constraint updates an info struct with property info,
183 * so that constraints can use this instead of having it passed through
186 struct ScrollSlideInfoUpdate
189 * Constraint constructor
191 ScrollSlideInfoUpdate(ScrollSlideInfoPtr scrollInfo)
192 : mScrollSlideInfo(scrollInfo)
197 * @param[in] current The current value
198 * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME)
199 * @param[in] effectReferenceProperty The point in the scroll-view where the user touched the screen
200 * @param[in] effectTimeProperty The current timer. Starting from 0.0 when scroll animation/dragging
201 * commences. Ensures that constraint is applied and processed every frame (to achieve the delay effect)
202 * @param[in] sizeProperty The size of the ScrollView.
203 * @return The new position of this Actor.
205 float operator()(const float& current,
206 const PropertyInput& scrollPositionProperty,
207 const PropertyInput& effectReferenceProperty,
208 const PropertyInput& scrollSizeProperty,
209 const PropertyInput& scrollPositionMinProperty,
210 const PropertyInput& scrollPositionMaxProperty,
211 const PropertyInput& scrollWrapProperty)
213 mScrollSlideInfo->mScrollPosition = scrollPositionProperty.GetVector3();
214 mScrollSlideInfo->mEffectReference = effectReferenceProperty.GetVector3();
215 mScrollSlideInfo->mScrollSize = scrollSizeProperty.GetVector3();
216 mScrollSlideInfo->mScrollPositionMin = scrollPositionMinProperty.GetVector3();
217 mScrollSlideInfo->mScrollPositionMax = scrollPositionMaxProperty.GetVector3();
218 mScrollSlideInfo->mScrollWrap = scrollWrapProperty.GetBoolean();
225 ScrollSlideInfoPtr mScrollSlideInfo;
231 * ScrollSlidePositionConstraint
233 * Position constraint adjusts the position of the Actors
234 * based on their parent page's position relative to the middle of the screen.
235 * When at middle of the screen the position is not altered.
236 * When one screen away from middle the position is rotated about it's origin + mAnchor
238 struct ScrollSlidePositionConstraint
241 * Constraint constructor
243 ScrollSlidePositionConstraint(ScrollSlideInfoPtr scrollInfo, float delayMin, float delayMax)
244 : mScrollSlideInfo(scrollInfo),
245 mScrollPosition(scrollInfo->mScrollPosition),
253 * @param[in] current The current position
254 * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME)
255 * @param[in] effectReferenceProperty The point in the scroll-view where the user touched the screen
256 * @param[in] effectTimeProperty The current timer. Starting from 0.0 when scroll animation/dragging
257 * commences. Ensures that constraint is applied and processed every frame (to achieve the delay effect)
258 * @param[in] sizeProperty The size of the ScrollView.
259 * @return The new position of this Actor.
261 Vector3 operator()(const Vector3& current,
262 const PropertyInput& pagePositionProperty,
263 const PropertyInput& effectTimeProperty,
264 const PropertyInput& deltaPositionProperty,
265 const PropertyInput& snapProperty)
267 const float complete = snapProperty.GetFloat();
268 bool activate = (complete > Math::MACHINE_EPSILON_1);
269 const Vector3& pagePosition = pagePositionProperty.GetVector3();
270 const Vector3& scrollPosition = mScrollSlideInfo->mScrollPosition;
272 // Get position of page.
273 Vector2 relativePosition(pagePosition + scrollPosition);
275 // short circuit: for orthognal view and when the blending has been deactivated.
277 (fabsf(relativePosition.x) < Math::MACHINE_EPSILON_1) &&
278 (fabsf(relativePosition.y) < Math::MACHINE_EPSILON_1) )
280 Vector3 actorPosition(current + scrollPosition);
281 return actorPosition;
284 const Vector3& referencePoint = mScrollSlideInfo->mEffectReference;
285 const Vector3& scrollSize = mScrollSlideInfo->mScrollSize;
286 const Vector3& deltaPosition = deltaPositionProperty.GetVector3();
288 // 1. Determine the relative position of the actor from the scrolling reference point.
289 // (the further away from the reference, the longer the delay should be)
290 const Vector3& min = mScrollSlideInfo->mScrollPositionMin;
291 const Vector3& max = mScrollSlideInfo->mScrollPositionMax;
293 relativePosition.y = (pagePosition.y + current.y - referencePoint.y) / scrollSize.height;
295 // Smoothen the relativePosition value by averaging with mRelativePosition (avoids sudden jerk when
296 // user touche different points)
297 float shortestDirection = ShortestDistanceInDomain(mRelativePosition.y, relativePosition.y, min.y, max.y);
298 mRelativePosition.y += activate ? shortestDirection * ANIMATION_BLEND_COEFFICIENT : shortestDirection;
300 // f represents this absolute distance. Get as a relative distance and inverse exponential
301 // (as delay equation has an exponential effect i.e. the closer delayFactor to 1.0f,
302 // the longer the delay would appear exponentially)
303 float f = fabsf(mRelativePosition.y) * complete;
304 f = std::min(f, 1.0f);
305 f = 1.0f - powf(ANIMATION_BLEND_COEFFICIENT, f);
307 // at center delay factor is mDelayMin, at maximum (1.0) it is mDelayMax
308 f = Mix(mDelayMin, mDelayMax, f);
310 // 2. Now that f (delay factor) has been determined for this Actor,
311 // move mScrollPosition towards the actual scroll position, at rate determined by f.
312 float shortest = ShortestDistanceInDomain(mScrollPosition.x, WrapInDomain(scrollPosition.x, -min.x, -max.x), min.x, max.x);
313 mScrollPosition.x += activate ? shortest * (1.0f-f) : shortest;
314 mScrollPosition.x = WrapInDomain(mScrollPosition.x, -min.x, -max.x);
315 mScrollPosition.y = scrollPosition.y;
317 Vector3 actorPosition(current + pagePosition + mScrollPosition);
319 // Get position of actor.
320 bool wrap = mScrollSlideInfo->mScrollWrap;
324 if(fabsf(min.x - max.x) > Math::MACHINE_EPSILON_1)
326 // WRAP X (based on the position of the right side)
327 actorPosition.x = WrapInDomain(actorPosition.x + scrollSize.x, min.x, max.x) - scrollSize.width;
331 const float targetRelativePositionX = (referencePoint.x + deltaPosition.x);
333 float blend(Mix(1.0f, ANIMATION_BLEND_COEFFICIENT, 1.0f - (1.0f - complete) * (1.0f - complete) ));
334 float invBlend(1.0f - blend);
336 mRelativePosition.x = activate ? mRelativePosition.x * invBlend + targetRelativePositionX * blend : targetRelativePositionX;
337 mRelativePosition.x = Chase( mRelativePosition.x, targetRelativePositionX, 1.0f );
339 relativePosition.x = (actorPosition.x - mRelativePosition.x) / scrollSize.width;
341 float difference = fabsf(ShortestDistanceInDomain(mScrollPosition.x, scrollPosition.x, -max.x, -min.x));
342 mAverageSpeed = activate ? mAverageSpeed * invBlend + difference * blend : 0.0f;
344 actorPosition.x += relativePosition.x * mAverageSpeed;
346 return actorPosition - pagePosition;
351 ScrollSlideInfoPtr mScrollSlideInfo;
352 Vector3 mScrollPosition; ///< The current scroll position
353 float mDelayMin; ///< Minimum delay rate (at closest position to touch)
354 float mDelayMax; ///< Maximum delay rate (at furthest position from touch - 1 page away)
355 Vector2 mRelativePosition;
356 float mAverageSpeed; ///< The Average speed of the Actor (proportional to mScrollPosition - scrollPosition)
361 * ScrollSlideScaleConstraint
364 struct ScrollSlideScaleConstraint
367 * Constraint constructor
369 ScrollSlideScaleConstraint()
374 * @param[in] current The current position
375 * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME)
376 * @param[in] effectReferenceProperty The point in the scroll-view where the user touched the screen
377 * @param[in] effectTimeProperty The current timer. Starting from 0.0 when scroll animation/dragging
378 * commences. Ensures that constraint is applied and processed every frame (to achieve the delay effect)
379 * @param[in] sizeProperty The size of the ScrollView.
380 * @return The new position of this Actor.
382 Vector3 operator()(const Vector3& current,
383 const PropertyInput& snapProperty)
385 float scale = 1.0f + snapProperty.GetFloat() * 0.008f;
386 return Vector3( current.x * scale, current.y * scale, current.z);
392 * Applies the slide constraints to the child actor for overshoot effect.
394 * @param[in] scrollView The ScrollView containing the pages.
395 * @param[in] child The child to be affected with the slide effect.
396 * @param[in] angleSwing The maximum amount the child actor should
397 * rotate in radians for each axis (X and Y) as the page is scrolled.
398 * move for each axis (X and Y) as the page is scrolled.
400 void ApplyScrollSlideConstraints(ScrollSlideInfoPtr scrollSlideInfo,
401 Toolkit::ScrollView scrollView,
406 // Apply constraints to these actors //
407 Constraint constraint = Constraint::New<Vector3>( Actor::POSITION,
408 ParentSource(Actor::POSITION),
409 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollViewSlideEffect::EFFECT_TIME ) ),
410 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_DELTA_PROPERTY_NAME ) ),
411 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollViewSlideEffect::EFFECT_ACTIVE ) ),
412 ScrollSlidePositionConstraint(scrollSlideInfo, delayMin, delayMax) );
413 constraint.SetRemoveAction( Constraint::Discard );
414 child.ApplyConstraint( constraint );
416 constraint = Constraint::New<Vector3>( Actor::SCALE,
417 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollViewSlideEffect::EFFECT_ACTIVE ) ),
418 ScrollSlideScaleConstraint() );
419 constraint.SetRemoveAction( Constraint::Discard );
420 child.ApplyConstraint( constraint );
423 } // unnamed namespace
434 ScrollViewSlideEffect::ScrollViewSlideEffect()
435 : mScrollSlideInfo(new ScrollSlideInfo()),
436 mPropertyTime(Property::INVALID_INDEX),
437 mPropertyReference(Property::INVALID_INDEX),
438 mPropertyActive(Property::INVALID_INDEX),
439 mDelayReferenceOffset(Vector3::ZERO),
440 mMaxDelayDuration(DEFAULT_MAX_DELAY_DURATION)
444 ScrollViewSlideEffect::~ScrollViewSlideEffect()
448 bool ScrollViewSlideEffect::GetSlideDirection() const
450 return mScrollSlideInfo->mVertical;
453 void ScrollViewSlideEffect::SetSlideDirection(bool vertical)
455 mScrollSlideInfo->mVertical = vertical;
458 const Vector3& ScrollViewSlideEffect::GetDelayReferenceOffset() const
460 return mDelayReferenceOffset;
463 void ScrollViewSlideEffect::SetDelayReferenceOffset(const Vector3& offset)
465 mDelayReferenceOffset = offset;
468 float ScrollViewSlideEffect::GetMaxDelayDuration() const
470 return mMaxDelayDuration;
473 void ScrollViewSlideEffect::SetMaxDelayDuration(float duration)
475 mMaxDelayDuration = duration;
478 void ScrollViewSlideEffect::ApplyToActor(Actor child,
482 ApplyScrollSlideConstraints( mScrollSlideInfo,
489 void ScrollViewSlideEffect::OnAttach(Toolkit::ScrollView& scrollView)
491 mScrollSlideInfo->mScrollPosition = scrollView.GetProperty<Vector3>( scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_PROPERTY_NAME ) );
492 mScrollSlideInfo->mScrollSize = scrollView.GetProperty<Vector3>( Actor::SIZE );
493 mScrollSlideInfo->mScrollPositionMin = scrollView.GetProperty<Vector3>( scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MIN_PROPERTY_NAME ) );
494 mScrollSlideInfo->mScrollPositionMax = scrollView.GetProperty<Vector3>( scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MAX_PROPERTY_NAME ) );
495 mScrollSlideInfo->mScrollWrap = scrollView.GetProperty<bool>( scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_WRAP_PROPERTY_NAME ) );
496 mScrollSlideInfo->mVertical = false;
498 // Create effect-time property if not already created.
499 if(mPropertyTime == Property::INVALID_INDEX)
501 mPropertyTime = SafeRegisterProperty( scrollView, Toolkit::ScrollViewSlideEffect::EFFECT_TIME, 0.0f );
502 mPropertyReference = SafeRegisterProperty( scrollView, Toolkit::ScrollViewSlideEffect::EFFECT_REFERENCE, Vector3::ZERO );
503 mPropertyActive = SafeRegisterProperty( scrollView, Toolkit::ScrollViewSlideEffect::EFFECT_ACTIVE, 0.0f );
506 // Create constraint to update ScrollSlideInfo
507 // Doesn't matter what this is applied to and on what property.
508 // Just needs to update mScrollSlideInfo values as properties change.
509 // The minor constraints (applied to the Actors) can use this mScrollSlideInfo.
510 Constraint constraint = Constraint::New<float>( mPropertyTime,
511 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_PROPERTY_NAME ) ),
512 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollViewSlideEffect::EFFECT_REFERENCE ) ),
513 Source(scrollView, Actor::SIZE),
514 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MIN_PROPERTY_NAME ) ),
515 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MAX_PROPERTY_NAME ) ),
516 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_WRAP_PROPERTY_NAME ) ),
517 ScrollSlideInfoUpdate(mScrollSlideInfo) );
518 constraint.SetRemoveAction( Constraint::Discard );
519 mInfoUpdateConstraint = scrollView.ApplyConstraint( constraint );
521 // Connect to the scroll view signals
522 scrollView.ScrollStartedSignal().Connect(this, &ScrollViewSlideEffect::OnScrollStart);
523 scrollView.SnapStartedSignal().Connect(this, &ScrollViewSlideEffect::OnScrollSnapStarted);
524 scrollView.TouchedSignal().Connect(this, &ScrollViewSlideEffect::OnScrollTouched);
526 AttachActor(scrollView);
529 bool ScrollViewSlideEffect::OnScrollTouched(Actor actor, const TouchEvent& event)
531 // Ignore events with multiple-touch points
532 if (event.GetPointCount() != 1)
537 if (event.GetPoint(0).state == TouchPoint::Down)
539 const TouchPoint& point = event.GetPoint(0);
540 Vector3 touchPosition(point.local - Stage::GetCurrent().GetSize() * 0.5f);
542 Vector3 scrollPosition = GetScrollView().GetCurrentScrollPosition();
543 GetScrollView().SetProperty(mPropertyReference, scrollPosition + touchPosition + mDelayReferenceOffset);
549 void ScrollViewSlideEffect::OnDetach(Toolkit::ScrollView& scrollView)
551 scrollView.ScrollStartedSignal().Disconnect(this, &ScrollViewSlideEffect::OnScrollStart);
552 scrollView.SnapStartedSignal().Disconnect(this, &ScrollViewSlideEffect::OnScrollSnapStarted);
553 scrollView.TouchedSignal().Disconnect(this, &ScrollViewSlideEffect::OnScrollTouched);
554 scrollView.RemoveConstraint( mInfoUpdateConstraint );
558 mAnimation.FinishedSignal().Disconnect(this, &ScrollViewSlideEffect::OnAnimationFinished);
565 mAnimationSnap.FinishedSignal().Disconnect(this, &ScrollViewSlideEffect::OnAnimationSnapFinished);
566 mAnimationSnap.Clear();
567 mAnimationSnap.Reset();
571 void ScrollViewSlideEffect::AttachActor(Actor actor)
576 void ScrollViewSlideEffect::DetachActor(Actor actor)
578 // TODO: remove the specific constraint defined in AttachActor (and possibly
579 // unregister property) - neither functionality exists in Dali.
582 void ScrollViewSlideEffect::ContinueAnimation(float endTime)
584 // continue animating
587 mAnimation.FinishedSignal().Disconnect(this, &ScrollViewSlideEffect::OnAnimationFinished);
591 Actor scrollView = GetScrollView();
593 mAnimation = Animation::New(SLIDEEFFECT_ANIMATION_MAX_TIME);
594 mAnimation.AnimateTo( Property(scrollView, mPropertyTime), endTime, AlphaFunctions::Linear );
595 mAnimation.FinishedSignal().Connect(this, &ScrollViewSlideEffect::OnAnimationFinished);
599 void ScrollViewSlideEffect::OnScrollStart( const Vector3& position )
601 Actor scrollView = GetScrollView();
602 GetScrollView().SetProperty(mPropertyTime, 0.0f);
604 ContinueAnimation(SLIDEEFFECT_ANIMATION_MAX_TIME);
608 mAnimationSnap.FinishedSignal().Disconnect(this, &ScrollViewSlideEffect::OnAnimationSnapFinished);
609 mAnimationSnap.Clear();
612 mAnimationSnap = Animation::New( EFFECT_SNAP_GROW_DURATION );
613 mAnimationSnap.AnimateTo( Property(scrollView, mPropertyActive), 1.0f, AlphaFunctions::Linear );
614 mAnimationSnap.FinishedSignal().Connect(this, &ScrollViewSlideEffect::OnAnimationSnapFinished);
615 mAnimationSnap.Play();
618 void ScrollViewSlideEffect::OnScrollSnapStarted(const Toolkit::ScrollView::SnapEvent& event)
622 mAnimationSnap.Clear();
625 Actor scrollView = GetScrollView();
626 mAnimationSnap = Animation::New(EFFECT_SNAP_DECAY_DURATION );
627 mAnimationSnap.AnimateTo( Property(scrollView, mPropertyActive), 0.0f, AlphaFunctions::Linear );
628 mAnimationSnap.FinishedSignal().Connect(this, &ScrollViewSlideEffect::OnAnimationSnapFinished);
629 mAnimationSnap.Play();
632 void ScrollViewSlideEffect::OnAnimationSnapFinished( Animation& animation )
634 mAnimationSnap.FinishedSignal().Disconnect(this, &ScrollViewSlideEffect::OnAnimationSnapFinished);
635 mAnimationSnap.Clear();
637 // stop time animation
640 mAnimation.FinishedSignal().Disconnect(this, &ScrollViewSlideEffect::OnAnimationFinished);
645 void ScrollViewSlideEffect::OnAnimationFinished( Animation& animation )
647 // still unstable, so continue animating.
648 // TODO: Requires an instability check to ensure time animation finishes when delay is
649 // less noticeable. i.e. all present scrollPositions are approx the same as mScrollPosition in constraints.
650 // best solution for this is to switch to a single history vector of scroll position, and compare if
651 // position has not deviated >= 0.5 pixel for the past 1 second.
652 float endTime = GetScrollView().GetProperty<float>(mPropertyTime) + SLIDEEFFECT_ANIMATION_MAX_TIME;
653 ContinueAnimation(endTime);
656 } // namespace Internal
658 } // namespace Toolkit