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
71 virtual ~ScrollSlideInfo()
77 Vector3 mScrollPosition;
78 Vector3 mEffectReference;
80 Vector3 mScrollPositionMin;
81 Vector3 mScrollPositionMax;
87 } // namespace Internal
89 } // namespace Toolkit
94 namespace // unnamed namespace
97 const float SLIDEEFFECT_ANIMATION_MAX_TIME = 60.0f; ///< Animation time (every time finishes, checks if it needs to go again)
98 const float COMPLETION_START_DURATION = 0.25f; ///< Maximum time for completion of effect after scroll-view initially completes (due to delay effect)
99 const float COMPLETION_END_DURATION = 5.0f; ///< Maximum time for completion of effect after scroll-view initially completes (due to delay effect)
100 const float ANIMATION_BLEND_COEFFICIENT = 0.05f; ///< Animation blending coefficient (blends between target value e.g. 5% and current value 9%)
101 const float INV_ANIMATION_BLEND_COEFFICIENT = (1.0f - ANIMATION_BLEND_COEFFICIENT);
102 const float DEFAULT_MAX_DELAY_DURATION = 0.25f; ///< Default maximum delay duration of the effect after scroll completes is 0.25f
103 const float EFFECT_SNAP_GROW_DURATION = 0.33f; ///< Take 1/3rd of a second for the snap effect property to grow
104 const float EFFECT_SNAP_DECAY_DURATION = 0.667f; ///< Take 2/3rds of a second for the snap effect property to decay
107 * Gets a property index. If the property doesn't already exist, then
108 * it will create the property.
109 * @param[in] handle The handle that owns or will own the property
110 * @param[in] name The name for this property
111 * @param[in] propertyValue The initial value for this property
112 * @return The property index for this property is returned.
114 Property::Index SafeRegisterProperty( Handle& handle, const std::string& name, Property::Value propertyValue )
116 Property::Index index = handle.GetPropertyIndex( name );
118 if(index == Property::INVALID_INDEX)
120 index = handle.RegisterProperty( name, propertyValue );
127 * Re-scales input value x from x0 - x1, to linearly map
128 * over the values y0 - y1. Values outside of this range
129 * will also conform to the trend (gradient) set.
130 * @param[in] y0 output minimum bound
131 * @param[in] y1 output maximum bound
132 * @param[in] x input X value
133 * @param[in] x0 (optional) input minimum bound (default 0.0)
134 * @param[in] x1 (optional) input maximum bound (default 1.0)
135 * @return The result of the mapping is returned.
137 float Mix(float y0, float y1, float x, float x0 = 0.0f, float x1 = 1.0f)
139 return y0 + (y1 - y0) * (x - x0) / (x1-x0);
143 * Returns the value of x chasing target.
144 * returns a value of x which is closer to target.
145 * but limited by maxDelta. #
150 * result is 30.0f (x is 20.0f units closer to target)
151 * However, if x is already within maxDelta units
152 * of target, x will equal target.
157 * result is 50.0f (x was already within 20.0f units of target)
159 float Chase( float x, float target, float maxDelta )
161 float delta = target - x;
165 x = std::min( x + maxDelta, target );
169 x = std::max( x - maxDelta, target );
175 // constraints ////////////////////////////////////////////////////////////////
178 * ScrollSlideInfoUpdate
180 * Info constraint updates an info struct with property info,
181 * so that constraints can use this instead of having it passed through
184 struct ScrollSlideInfoUpdate
187 * Constraint constructor
189 ScrollSlideInfoUpdate(ScrollSlideInfoPtr scrollInfo)
190 : mScrollSlideInfo(scrollInfo)
195 * @param[in] current The current value
196 * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME)
197 * @param[in] effectReferenceProperty The point in the scroll-view where the user touched the screen
198 * @param[in] effectTimeProperty The current timer. Starting from 0.0 when scroll animation/dragging
199 * commences. Ensures that constraint is applied and processed every frame (to achieve the delay effect)
200 * @param[in] sizeProperty The size of the ScrollView.
201 * @return The new position of this Actor.
203 float operator()(const float& current,
204 const PropertyInput& scrollPositionProperty,
205 const PropertyInput& effectReferenceProperty,
206 const PropertyInput& scrollSizeProperty,
207 const PropertyInput& scrollPositionMinProperty,
208 const PropertyInput& scrollPositionMaxProperty,
209 const PropertyInput& scrollWrapProperty)
211 mScrollSlideInfo->mScrollPosition = scrollPositionProperty.GetVector3();
212 mScrollSlideInfo->mEffectReference = effectReferenceProperty.GetVector3();
213 mScrollSlideInfo->mScrollSize = scrollSizeProperty.GetVector3();
214 mScrollSlideInfo->mScrollPositionMin = scrollPositionMinProperty.GetVector3();
215 mScrollSlideInfo->mScrollPositionMax = scrollPositionMaxProperty.GetVector3();
216 mScrollSlideInfo->mScrollWrap = scrollWrapProperty.GetBoolean();
223 ScrollSlideInfoPtr mScrollSlideInfo;
229 * ScrollSlidePositionConstraint
231 * Position constraint adjusts the position of the Actors
232 * based on their parent page's position relative to the middle of the screen.
233 * When at middle of the screen the position is not altered.
234 * When one screen away from middle the position is rotated about it's origin + mAnchor
236 struct ScrollSlidePositionConstraint
239 * Constraint constructor
241 ScrollSlidePositionConstraint(ScrollSlideInfoPtr scrollInfo, float delayMin, float delayMax)
242 : mScrollSlideInfo(scrollInfo),
243 mScrollPosition(scrollInfo->mScrollPosition),
251 * @param[in] current The current position
252 * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME)
253 * @param[in] effectReferenceProperty The point in the scroll-view where the user touched the screen
254 * @param[in] effectTimeProperty The current timer. Starting from 0.0 when scroll animation/dragging
255 * commences. Ensures that constraint is applied and processed every frame (to achieve the delay effect)
256 * @param[in] sizeProperty The size of the ScrollView.
257 * @return The new position of this Actor.
259 Vector3 operator()(const Vector3& current,
260 const PropertyInput& pagePositionProperty,
261 const PropertyInput& effectTimeProperty,
262 const PropertyInput& deltaPositionProperty,
263 const PropertyInput& snapProperty)
265 const float complete = snapProperty.GetFloat();
266 bool activate = (complete > Math::MACHINE_EPSILON_1);
267 const Vector3& pagePosition = pagePositionProperty.GetVector3();
268 const Vector3& scrollPosition = mScrollSlideInfo->mScrollPosition;
270 // Get position of page.
271 Vector2 relativePosition(pagePosition + scrollPosition);
273 // short circuit: for orthognal view and when the blending has been deactivated.
275 (fabsf(relativePosition.x) < Math::MACHINE_EPSILON_1) &&
276 (fabsf(relativePosition.y) < Math::MACHINE_EPSILON_1) )
278 Vector3 actorPosition(current + scrollPosition);
279 return actorPosition;
282 const Vector3& referencePoint = mScrollSlideInfo->mEffectReference;
283 const Vector3& scrollSize = mScrollSlideInfo->mScrollSize;
284 const Vector3& deltaPosition = deltaPositionProperty.GetVector3();
286 // 1. Determine the relative position of the actor from the scrolling reference point.
287 // (the further away from the reference, the longer the delay should be)
288 const Vector3& min = mScrollSlideInfo->mScrollPositionMin;
289 const Vector3& max = mScrollSlideInfo->mScrollPositionMax;
291 relativePosition.y = (pagePosition.y + current.y - referencePoint.y) / scrollSize.height;
293 // Smoothen the relativePosition value by averaging with mRelativePosition (avoids sudden jerk when
294 // user touche different points)
295 float shortestDirection = ShortestDistanceInDomain(mRelativePosition.y, relativePosition.y, min.y, max.y);
296 mRelativePosition.y += activate ? shortestDirection * ANIMATION_BLEND_COEFFICIENT : shortestDirection;
298 // f represents this absolute distance. Get as a relative distance and inverse exponential
299 // (as delay equation has an exponential effect i.e. the closer delayFactor to 1.0f,
300 // the longer the delay would appear exponentially)
301 float f = fabsf(mRelativePosition.y) * complete;
302 f = std::min(f, 1.0f);
303 f = 1.0f - powf(ANIMATION_BLEND_COEFFICIENT, f);
305 // at center delay factor is mDelayMin, at maximum (1.0) it is mDelayMax
306 f = Mix(mDelayMin, mDelayMax, f);
308 // 2. Now that f (delay factor) has been determined for this Actor,
309 // move mScrollPosition towards the actual scroll position, at rate determined by f.
310 float shortest = ShortestDistanceInDomain(mScrollPosition.x, WrapInDomain(scrollPosition.x, -min.x, -max.x), min.x, max.x);
311 mScrollPosition.x += activate ? shortest * (1.0f-f) : shortest;
312 mScrollPosition.x = WrapInDomain(mScrollPosition.x, -min.x, -max.x);
313 mScrollPosition.y = scrollPosition.y;
315 Vector3 actorPosition(current + pagePosition + mScrollPosition);
317 // Get position of actor.
318 bool wrap = mScrollSlideInfo->mScrollWrap;
322 if(fabsf(min.x - max.x) > Math::MACHINE_EPSILON_1)
324 // WRAP X (based on the position of the right side)
325 actorPosition.x = WrapInDomain(actorPosition.x + scrollSize.x, min.x, max.x) - scrollSize.width;
329 const float targetRelativePositionX = (referencePoint.x + deltaPosition.x);
331 float blend(Mix(1.0f, ANIMATION_BLEND_COEFFICIENT, 1.0f - (1.0f - complete) * (1.0f - complete) ));
332 float invBlend(1.0f - blend);
334 mRelativePosition.x = activate ? mRelativePosition.x * invBlend + targetRelativePositionX * blend : targetRelativePositionX;
335 mRelativePosition.x = Chase( mRelativePosition.x, targetRelativePositionX, 1.0f );
337 relativePosition.x = (actorPosition.x - mRelativePosition.x) / scrollSize.width;
339 float difference = fabsf(ShortestDistanceInDomain(mScrollPosition.x, scrollPosition.x, -max.x, -min.x));
340 mAverageSpeed = activate ? mAverageSpeed * invBlend + difference * blend : 0.0f;
342 actorPosition.x += relativePosition.x * mAverageSpeed;
344 return actorPosition - pagePosition;
349 ScrollSlideInfoPtr mScrollSlideInfo;
350 Vector3 mScrollPosition; ///< The current scroll position
351 float mDelayMin; ///< Minimum delay rate (at closest position to touch)
352 float mDelayMax; ///< Maximum delay rate (at furthest position from touch - 1 page away)
353 Vector2 mRelativePosition;
354 float mAverageSpeed; ///< The Average speed of the Actor (proportional to mScrollPosition - scrollPosition)
359 * ScrollSlideScaleConstraint
362 struct ScrollSlideScaleConstraint
365 * Constraint constructor
367 ScrollSlideScaleConstraint()
372 * @param[in] current The current position
373 * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME)
374 * @param[in] effectReferenceProperty The point in the scroll-view where the user touched the screen
375 * @param[in] effectTimeProperty The current timer. Starting from 0.0 when scroll animation/dragging
376 * commences. Ensures that constraint is applied and processed every frame (to achieve the delay effect)
377 * @param[in] sizeProperty The size of the ScrollView.
378 * @return The new position of this Actor.
380 Vector3 operator()(const Vector3& current,
381 const PropertyInput& snapProperty)
383 float scale = 1.0f + snapProperty.GetFloat() * 0.008f;
384 return Vector3( current.x * scale, current.y * scale, current.z);
390 * Applies the slide constraints to the child actor for overshoot effect.
392 * @param[in] scrollView The ScrollView containing the pages.
393 * @param[in] child The child to be affected with the slide effect.
394 * @param[in] angleSwing The maximum amount the child actor should
395 * rotate in radians for each axis (X and Y) as the page is scrolled.
396 * move for each axis (X and Y) as the page is scrolled.
398 void ApplyScrollSlideConstraints(ScrollSlideInfoPtr scrollSlideInfo,
399 Toolkit::ScrollView scrollView,
404 // Apply constraints to these actors //
405 Constraint constraint = Constraint::New<Vector3>( Actor::POSITION,
406 ParentSource(Actor::POSITION),
407 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollViewSlideEffect::EFFECT_TIME ) ),
408 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_DELTA_PROPERTY_NAME ) ),
409 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollViewSlideEffect::EFFECT_ACTIVE ) ),
410 ScrollSlidePositionConstraint(scrollSlideInfo, delayMin, delayMax) );
411 constraint.SetRemoveAction( Constraint::Discard );
412 child.ApplyConstraint( constraint );
414 constraint = Constraint::New<Vector3>( Actor::SCALE,
415 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollViewSlideEffect::EFFECT_ACTIVE ) ),
416 ScrollSlideScaleConstraint() );
417 constraint.SetRemoveAction( Constraint::Discard );
418 child.ApplyConstraint( constraint );
421 } // unnamed namespace
432 ScrollViewSlideEffect::ScrollViewSlideEffect()
433 : mScrollSlideInfo(new ScrollSlideInfo()),
434 mPropertyTime(Property::INVALID_INDEX),
435 mPropertyReference(Property::INVALID_INDEX),
436 mPropertyActive(Property::INVALID_INDEX),
437 mDelayReferenceOffset(Vector3::ZERO),
438 mMaxDelayDuration(DEFAULT_MAX_DELAY_DURATION)
442 ScrollViewSlideEffect::~ScrollViewSlideEffect()
446 bool ScrollViewSlideEffect::GetSlideDirection() const
448 return mScrollSlideInfo->mVertical;
451 void ScrollViewSlideEffect::SetSlideDirection(bool vertical)
453 mScrollSlideInfo->mVertical = vertical;
456 const Vector3& ScrollViewSlideEffect::GetDelayReferenceOffset() const
458 return mDelayReferenceOffset;
461 void ScrollViewSlideEffect::SetDelayReferenceOffset(const Vector3& offset)
463 mDelayReferenceOffset = offset;
466 float ScrollViewSlideEffect::GetMaxDelayDuration() const
468 return mMaxDelayDuration;
471 void ScrollViewSlideEffect::SetMaxDelayDuration(float duration)
473 mMaxDelayDuration = duration;
476 void ScrollViewSlideEffect::ApplyToActor(Actor child,
480 ApplyScrollSlideConstraints( mScrollSlideInfo,
487 void ScrollViewSlideEffect::OnAttach(Toolkit::ScrollView& scrollView)
489 mScrollSlideInfo->mScrollPosition = scrollView.GetProperty<Vector3>( scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_PROPERTY_NAME ) );
490 mScrollSlideInfo->mScrollSize = scrollView.GetProperty<Vector3>( Actor::SIZE );
491 mScrollSlideInfo->mScrollPositionMin = scrollView.GetProperty<Vector3>( scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MIN_PROPERTY_NAME ) );
492 mScrollSlideInfo->mScrollPositionMax = scrollView.GetProperty<Vector3>( scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MAX_PROPERTY_NAME ) );
493 mScrollSlideInfo->mScrollWrap = scrollView.GetProperty<bool>( scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_WRAP_PROPERTY_NAME ) );
494 mScrollSlideInfo->mVertical = false;
496 // Create effect-time property if not already created.
497 if(mPropertyTime == Property::INVALID_INDEX)
499 mPropertyTime = SafeRegisterProperty( scrollView, Toolkit::ScrollViewSlideEffect::EFFECT_TIME, 0.0f );
500 mPropertyReference = SafeRegisterProperty( scrollView, Toolkit::ScrollViewSlideEffect::EFFECT_REFERENCE, Vector3::ZERO );
501 mPropertyActive = SafeRegisterProperty( scrollView, Toolkit::ScrollViewSlideEffect::EFFECT_ACTIVE, 0.0f );
504 // Create constraint to update ScrollSlideInfo
505 // Doesn't matter what this is applied to and on what property.
506 // Just needs to update mScrollSlideInfo values as properties change.
507 // The minor constraints (applied to the Actors) can use this mScrollSlideInfo.
508 Constraint constraint = Constraint::New<float>( mPropertyTime,
509 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_PROPERTY_NAME ) ),
510 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollViewSlideEffect::EFFECT_REFERENCE ) ),
511 Source(scrollView, Actor::SIZE),
512 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MIN_PROPERTY_NAME ) ),
513 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MAX_PROPERTY_NAME ) ),
514 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_WRAP_PROPERTY_NAME ) ),
515 ScrollSlideInfoUpdate(mScrollSlideInfo) );
516 constraint.SetRemoveAction( Constraint::Discard );
517 mInfoUpdateConstraint = scrollView.ApplyConstraint( constraint );
519 // Connect to the scroll view signals
520 scrollView.ScrollStartedSignal().Connect(this, &ScrollViewSlideEffect::OnScrollStart);
521 scrollView.SnapStartedSignal().Connect(this, &ScrollViewSlideEffect::OnScrollSnapStarted);
522 scrollView.TouchedSignal().Connect(this, &ScrollViewSlideEffect::OnScrollTouched);
524 AttachActor(scrollView);
527 bool ScrollViewSlideEffect::OnScrollTouched(Actor actor, const TouchEvent& event)
529 // Ignore events with multiple-touch points
530 if (event.GetPointCount() != 1)
535 if (event.GetPoint(0).state == TouchPoint::Down)
537 const TouchPoint& point = event.GetPoint(0);
538 Vector3 touchPosition(point.local - Stage::GetCurrent().GetSize() * 0.5f);
540 Vector3 scrollPosition = GetScrollView().GetCurrentScrollPosition();
541 GetScrollView().SetProperty(mPropertyReference, scrollPosition + touchPosition + mDelayReferenceOffset);
547 void ScrollViewSlideEffect::OnDetach(Toolkit::ScrollView& scrollView)
549 scrollView.ScrollStartedSignal().Disconnect(this, &ScrollViewSlideEffect::OnScrollStart);
550 scrollView.SnapStartedSignal().Disconnect(this, &ScrollViewSlideEffect::OnScrollSnapStarted);
551 scrollView.TouchedSignal().Disconnect(this, &ScrollViewSlideEffect::OnScrollTouched);
552 scrollView.RemoveConstraint( mInfoUpdateConstraint );
556 mAnimation.FinishedSignal().Disconnect(this, &ScrollViewSlideEffect::OnAnimationFinished);
563 mAnimationSnap.FinishedSignal().Disconnect(this, &ScrollViewSlideEffect::OnAnimationSnapFinished);
564 mAnimationSnap.Clear();
565 mAnimationSnap.Reset();
569 void ScrollViewSlideEffect::AttachActor(Actor actor)
574 void ScrollViewSlideEffect::DetachActor(Actor actor)
576 // TODO: remove the specific constraint defined in AttachActor (and possibly
577 // unregister property) - neither functionality exists in Dali.
580 void ScrollViewSlideEffect::ContinueAnimation(float endTime)
582 // continue animating
585 mAnimation.FinishedSignal().Disconnect(this, &ScrollViewSlideEffect::OnAnimationFinished);
589 Actor scrollView = GetScrollView();
591 mAnimation = Animation::New(SLIDEEFFECT_ANIMATION_MAX_TIME);
592 mAnimation.AnimateTo( Property(scrollView, mPropertyTime), endTime, AlphaFunctions::Linear );
593 mAnimation.FinishedSignal().Connect(this, &ScrollViewSlideEffect::OnAnimationFinished);
597 void ScrollViewSlideEffect::OnScrollStart( const Vector3& position )
599 Actor scrollView = GetScrollView();
600 GetScrollView().SetProperty(mPropertyTime, 0.0f);
602 ContinueAnimation(SLIDEEFFECT_ANIMATION_MAX_TIME);
606 mAnimationSnap.FinishedSignal().Disconnect(this, &ScrollViewSlideEffect::OnAnimationSnapFinished);
607 mAnimationSnap.Clear();
610 mAnimationSnap = Animation::New( EFFECT_SNAP_GROW_DURATION );
611 mAnimationSnap.AnimateTo( Property(scrollView, mPropertyActive), 1.0f, AlphaFunctions::Linear );
612 mAnimationSnap.FinishedSignal().Connect(this, &ScrollViewSlideEffect::OnAnimationSnapFinished);
613 mAnimationSnap.Play();
616 void ScrollViewSlideEffect::OnScrollSnapStarted(const Toolkit::ScrollView::SnapEvent& event)
620 mAnimationSnap.Clear();
623 Actor scrollView = GetScrollView();
624 mAnimationSnap = Animation::New(EFFECT_SNAP_DECAY_DURATION );
625 mAnimationSnap.AnimateTo( Property(scrollView, mPropertyActive), 0.0f, AlphaFunctions::Linear );
626 mAnimationSnap.FinishedSignal().Connect(this, &ScrollViewSlideEffect::OnAnimationSnapFinished);
627 mAnimationSnap.Play();
630 void ScrollViewSlideEffect::OnAnimationSnapFinished( Animation& animation )
632 mAnimationSnap.FinishedSignal().Disconnect(this, &ScrollViewSlideEffect::OnAnimationSnapFinished);
633 mAnimationSnap.Clear();
635 // stop time animation
638 mAnimation.FinishedSignal().Disconnect(this, &ScrollViewSlideEffect::OnAnimationFinished);
643 void ScrollViewSlideEffect::OnAnimationFinished( Animation& animation )
645 // still unstable, so continue animating.
646 // TODO: Requires an instability check to ensure time animation finishes when delay is
647 // less noticeable. i.e. all present scrollPositions are approx the same as mScrollPosition in constraints.
648 // best solution for this is to switch to a single history vector of scroll position, and compare if
649 // position has not deviated >= 0.5 pixel for the past 1 second.
650 float endTime = GetScrollView().GetProperty<float>(mPropertyTime) + SLIDEEFFECT_ANIMATION_MAX_TIME;
651 ContinueAnimation(endTime);
654 } // namespace Internal
656 } // namespace Toolkit