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.
19 #include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-effect-impl.h>
20 #include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-slide-effect-impl.h>
24 typedef Dali::Toolkit::Internal::ScrollSlideInfo ScrollSlideInfo;
25 typedef Dali::Toolkit::Internal::ScrollSlideInfoPtr ScrollSlideInfoPtr;
37 * ScrollSlideInfo structure contains
38 * common info that is shared amongst the constraints applied to Actors.
39 * The constraints + effect all share ownership of this info struct.
40 * The info is written to by the ScrollSlideInfoUpdate constraint. While
41 * the other constraints read from by the other constraints. Due to the order
42 * in which the constraints are applied, all constraints will get the current
43 * property values for these properties.
44 * The advantage of doing this is that:
45 * A) Constraints are not restricted by the 6 property limit. to function.
46 * B) Properties which rarely change or only change when another property changes
47 * (e.g. time), such as scroll position, scroll domain, size, wrap mode don't need
48 * to be checked for each constraint to apply.
50 class ScrollSlideInfo : public RefObject
65 virtual ~ScrollSlideInfo()
71 Vector3 mScrollPosition;
72 Vector3 mEffectReference;
74 Vector3 mScrollPositionMin;
75 Vector3 mScrollPositionMax;
81 } // namespace Internal
83 } // namespace Toolkit
88 namespace // unnamed namespace
91 const float SLIDEEFFECT_ANIMATION_MAX_TIME = 60.0f; ///< Animation time (every time finishes, checks if it needs to go again)
92 const float COMPLETION_START_DURATION = 0.25f; ///< Maximum time for completion of effect after scroll-view initially completes (due to delay effect)
93 const float COMPLETION_END_DURATION = 5.0f; ///< Maximum time for completion of effect after scroll-view initially completes (due to delay effect)
94 const float ANIMATION_BLEND_COEFFICIENT = 0.05f; ///< Animation blending coefficient (blends between target value e.g. 5% and current value 9%)
95 const float INV_ANIMATION_BLEND_COEFFICIENT = (1.0f - ANIMATION_BLEND_COEFFICIENT);
96 const float DEFAULT_MAX_DELAY_DURATION = 0.25f; ///< Default maximum delay duration of the effect after scroll completes is 0.25f
97 const float EFFECT_SNAP_GROW_DURATION = 0.33f; ///< Take 1/3rd of a second for the snap effect property to grow
98 const float EFFECT_SNAP_DECAY_DURATION = 0.667f; ///< Take 2/3rds of a second for the snap effect property to decay
101 * Gets a property index. If the property doesn't already exist, then
102 * it will create the property.
103 * @param[in] handle The handle that owns or will own the property
104 * @param[in] name The name for this property
105 * @param[in] propertyValue The initial value for this property
106 * @return The property index for this property is returned.
108 Property::Index SafeRegisterProperty( Handle& handle, const std::string& name, Property::Value propertyValue )
110 Property::Index index = handle.GetPropertyIndex( name );
112 if(index == Property::INVALID_INDEX)
114 index = handle.RegisterProperty( name, propertyValue );
121 * Re-scales input value x from x0 - x1, to linearly map
122 * over the values y0 - y1. Values outside of this range
123 * will also conform to the trend (gradient) set.
124 * @param[in] y0 output minimum bound
125 * @param[in] y1 output maximum bound
126 * @param[in] x input X value
127 * @param[in] x0 (optional) input minimum bound (default 0.0)
128 * @param[in] x1 (optional) input maximum bound (default 1.0)
129 * @return The result of the mapping is returned.
131 float Mix(float y0, float y1, float x, float x0 = 0.0f, float x1 = 1.0f)
133 return y0 + (y1 - y0) * (x - x0) / (x1-x0);
137 * Returns the value of x chasing target.
138 * returns a value of x which is closer to target.
139 * but limited by maxDelta. #
144 * result is 30.0f (x is 20.0f units closer to target)
145 * However, if x is already within maxDelta units
146 * of target, x will equal target.
151 * result is 50.0f (x was already within 20.0f units of target)
153 float Chase( float x, float target, float maxDelta )
155 float delta = target - x;
159 x = std::min( x + maxDelta, target );
163 x = std::max( x - maxDelta, target );
169 // constraints ////////////////////////////////////////////////////////////////
172 * ScrollSlideInfoUpdate
174 * Info constraint updates an info struct with property info,
175 * so that constraints can use this instead of having it passed through
178 struct ScrollSlideInfoUpdate
181 * Constraint constructor
183 ScrollSlideInfoUpdate(ScrollSlideInfoPtr scrollInfo)
184 : mScrollSlideInfo(scrollInfo)
189 * @param[in] current The current value
190 * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME)
191 * @param[in] effectReferenceProperty The point in the scroll-view where the user touched the screen
192 * @param[in] effectTimeProperty The current timer. Starting from 0.0 when scroll animation/dragging
193 * commences. Ensures that constraint is applied and processed every frame (to achieve the delay effect)
194 * @param[in] sizeProperty The size of the ScrollView.
195 * @return The new position of this Actor.
197 float operator()(const float& current,
198 const PropertyInput& scrollPositionProperty,
199 const PropertyInput& effectReferenceProperty,
200 const PropertyInput& scrollSizeProperty,
201 const PropertyInput& scrollPositionMinProperty,
202 const PropertyInput& scrollPositionMaxProperty,
203 const PropertyInput& scrollWrapProperty)
205 mScrollSlideInfo->mScrollPosition = scrollPositionProperty.GetVector3();
206 mScrollSlideInfo->mEffectReference = effectReferenceProperty.GetVector3();
207 mScrollSlideInfo->mScrollSize = scrollSizeProperty.GetVector3();
208 mScrollSlideInfo->mScrollPositionMin = scrollPositionMinProperty.GetVector3();
209 mScrollSlideInfo->mScrollPositionMax = scrollPositionMaxProperty.GetVector3();
210 mScrollSlideInfo->mScrollWrap = scrollWrapProperty.GetBoolean();
217 ScrollSlideInfoPtr mScrollSlideInfo;
223 * ScrollSlidePositionConstraint
225 * Position constraint adjusts the position of the Actors
226 * based on their parent page's position relative to the middle of the screen.
227 * When at middle of the screen the position is not altered.
228 * When one screen away from middle the position is rotated about it's origin + mAnchor
230 struct ScrollSlidePositionConstraint
233 * Constraint constructor
235 ScrollSlidePositionConstraint(ScrollSlideInfoPtr scrollInfo, float delayMin, float delayMax)
236 : mScrollSlideInfo(scrollInfo),
237 mScrollPosition(scrollInfo->mScrollPosition),
245 * @param[in] current The current position
246 * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME)
247 * @param[in] effectReferenceProperty The point in the scroll-view where the user touched the screen
248 * @param[in] effectTimeProperty The current timer. Starting from 0.0 when scroll animation/dragging
249 * commences. Ensures that constraint is applied and processed every frame (to achieve the delay effect)
250 * @param[in] sizeProperty The size of the ScrollView.
251 * @return The new position of this Actor.
253 Vector3 operator()(const Vector3& current,
254 const PropertyInput& pagePositionProperty,
255 const PropertyInput& effectTimeProperty,
256 const PropertyInput& deltaPositionProperty,
257 const PropertyInput& snapProperty)
259 const float complete = snapProperty.GetFloat();
260 bool activate = (complete > Math::MACHINE_EPSILON_1);
261 const Vector3& pagePosition = pagePositionProperty.GetVector3();
262 const Vector3& scrollPosition = mScrollSlideInfo->mScrollPosition;
264 // Get position of page.
265 Vector2 relativePosition(pagePosition + scrollPosition);
267 // short circuit: for orthognal view and when the blending has been deactivated.
269 (fabsf(relativePosition.x) < Math::MACHINE_EPSILON_1) &&
270 (fabsf(relativePosition.y) < Math::MACHINE_EPSILON_1) )
272 Vector3 actorPosition(current + scrollPosition);
273 return actorPosition;
276 const Vector3& referencePoint = mScrollSlideInfo->mEffectReference;
277 const Vector3& scrollSize = mScrollSlideInfo->mScrollSize;
278 const Vector3& deltaPosition = deltaPositionProperty.GetVector3();
280 // 1. Determine the relative position of the actor from the scrolling reference point.
281 // (the further away from the reference, the longer the delay should be)
282 const Vector3& min = mScrollSlideInfo->mScrollPositionMin;
283 const Vector3& max = mScrollSlideInfo->mScrollPositionMax;
285 relativePosition.y = (pagePosition.y + current.y - referencePoint.y) / scrollSize.height;
287 // Smoothen the relativePosition value by averaging with mRelativePosition (avoids sudden jerk when
288 // user touche different points)
289 float shortestDirection = ShortestDistanceInDomain(mRelativePosition.y, relativePosition.y, min.y, max.y);
290 mRelativePosition.y += activate ? shortestDirection * ANIMATION_BLEND_COEFFICIENT : shortestDirection;
292 // f represents this absolute distance. Get as a relative distance and inverse exponential
293 // (as delay equation has an exponential effect i.e. the closer delayFactor to 1.0f,
294 // the longer the delay would appear exponentially)
295 float f = fabsf(mRelativePosition.y) * complete;
296 f = std::min(f, 1.0f);
297 f = 1.0f - powf(ANIMATION_BLEND_COEFFICIENT, f);
299 // at center delay factor is mDelayMin, at maximum (1.0) it is mDelayMax
300 f = Mix(mDelayMin, mDelayMax, f);
302 // 2. Now that f (delay factor) has been determined for this Actor,
303 // move mScrollPosition towards the actual scroll position, at rate determined by f.
304 float shortest = ShortestDistanceInDomain(mScrollPosition.x, WrapInDomain(scrollPosition.x, -min.x, -max.x), min.x, max.x);
305 mScrollPosition.x += activate ? shortest * (1.0f-f) : shortest;
306 mScrollPosition.x = WrapInDomain(mScrollPosition.x, -min.x, -max.x);
307 mScrollPosition.y = scrollPosition.y;
309 Vector3 actorPosition(current + pagePosition + mScrollPosition);
311 // Get position of actor.
312 bool wrap = mScrollSlideInfo->mScrollWrap;
316 if(fabsf(min.x - max.x) > Math::MACHINE_EPSILON_1)
318 // WRAP X (based on the position of the right side)
319 actorPosition.x = WrapInDomain(actorPosition.x + scrollSize.x, min.x, max.x) - scrollSize.width;
323 const float targetRelativePositionX = (referencePoint.x + deltaPosition.x);
325 float blend(Mix(1.0f, ANIMATION_BLEND_COEFFICIENT, 1.0f - (1.0f - complete) * (1.0f - complete) ));
326 float invBlend(1.0f - blend);
328 mRelativePosition.x = activate ? mRelativePosition.x * invBlend + targetRelativePositionX * blend : targetRelativePositionX;
329 mRelativePosition.x = Chase( mRelativePosition.x, targetRelativePositionX, 1.0f );
331 relativePosition.x = (actorPosition.x - mRelativePosition.x) / scrollSize.width;
333 float difference = fabsf(ShortestDistanceInDomain(mScrollPosition.x, scrollPosition.x, -max.x, -min.x));
334 mAverageSpeed = activate ? mAverageSpeed * invBlend + difference * blend : 0.0f;
336 actorPosition.x += relativePosition.x * mAverageSpeed;
338 return actorPosition - pagePosition;
343 ScrollSlideInfoPtr mScrollSlideInfo;
344 Vector3 mScrollPosition; ///< The current scroll position
345 float mDelayMin; ///< Minimum delay rate (at closest position to touch)
346 float mDelayMax; ///< Maximum delay rate (at furthest position from touch - 1 page away)
347 Vector2 mRelativePosition;
348 float mAverageSpeed; ///< The Average speed of the Actor (proportional to mScrollPosition - scrollPosition)
353 * ScrollSlideScaleConstraint
356 struct ScrollSlideScaleConstraint
359 * Constraint constructor
361 ScrollSlideScaleConstraint()
366 * @param[in] current The current position
367 * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME)
368 * @param[in] effectReferenceProperty The point in the scroll-view where the user touched the screen
369 * @param[in] effectTimeProperty The current timer. Starting from 0.0 when scroll animation/dragging
370 * commences. Ensures that constraint is applied and processed every frame (to achieve the delay effect)
371 * @param[in] sizeProperty The size of the ScrollView.
372 * @return The new position of this Actor.
374 Vector3 operator()(const Vector3& current,
375 const PropertyInput& snapProperty)
377 float scale = 1.0f + snapProperty.GetFloat() * 0.008f;
378 return Vector3( current.x * scale, current.y * scale, current.z);
384 * Applies the slide constraints to the child actor for overshoot effect.
386 * @param[in] scrollView The ScrollView containing the pages.
387 * @param[in] child The child to be affected with the slide effect.
388 * @param[in] angleSwing The maximum amount the child actor should
389 * rotate in radians for each axis (X and Y) as the page is scrolled.
390 * move for each axis (X and Y) as the page is scrolled.
392 void ApplyScrollSlideConstraints(ScrollSlideInfoPtr scrollSlideInfo,
393 Toolkit::ScrollView scrollView,
398 // Apply constraints to these actors //
399 Constraint constraint = Constraint::New<Vector3>( Actor::POSITION,
400 ParentSource(Actor::POSITION),
401 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollViewSlideEffect::EFFECT_TIME ) ),
402 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_DELTA_PROPERTY_NAME ) ),
403 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollViewSlideEffect::EFFECT_ACTIVE ) ),
404 ScrollSlidePositionConstraint(scrollSlideInfo, delayMin, delayMax) );
405 constraint.SetRemoveAction( Constraint::Discard );
406 child.ApplyConstraint( constraint );
408 constraint = Constraint::New<Vector3>( Actor::SCALE,
409 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollViewSlideEffect::EFFECT_ACTIVE ) ),
410 ScrollSlideScaleConstraint() );
411 constraint.SetRemoveAction( Constraint::Discard );
412 child.ApplyConstraint( constraint );
415 } // unnamed namespace
426 ScrollViewSlideEffect::ScrollViewSlideEffect()
427 : mScrollSlideInfo(new ScrollSlideInfo()),
428 mPropertyTime(Property::INVALID_INDEX),
429 mPropertyReference(Property::INVALID_INDEX),
430 mPropertyActive(Property::INVALID_INDEX),
431 mDelayReferenceOffset(Vector3::ZERO),
432 mMaxDelayDuration(DEFAULT_MAX_DELAY_DURATION)
436 ScrollViewSlideEffect::~ScrollViewSlideEffect()
440 bool ScrollViewSlideEffect::GetSlideDirection() const
442 return mScrollSlideInfo->mVertical;
445 void ScrollViewSlideEffect::SetSlideDirection(bool vertical)
447 mScrollSlideInfo->mVertical = vertical;
450 const Vector3& ScrollViewSlideEffect::GetDelayReferenceOffset() const
452 return mDelayReferenceOffset;
455 void ScrollViewSlideEffect::SetDelayReferenceOffset(const Vector3& offset)
457 mDelayReferenceOffset = offset;
460 float ScrollViewSlideEffect::GetMaxDelayDuration() const
462 return mMaxDelayDuration;
465 void ScrollViewSlideEffect::SetMaxDelayDuration(float duration)
467 mMaxDelayDuration = duration;
470 void ScrollViewSlideEffect::ApplyToActor(Actor child,
474 ApplyScrollSlideConstraints( mScrollSlideInfo,
481 void ScrollViewSlideEffect::OnAttach(Toolkit::ScrollView& scrollView)
483 mScrollSlideInfo->mScrollPosition = scrollView.GetProperty<Vector3>( scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_PROPERTY_NAME ) );
484 mScrollSlideInfo->mScrollSize = scrollView.GetProperty<Vector3>( Actor::SIZE );
485 mScrollSlideInfo->mScrollPositionMin = scrollView.GetProperty<Vector3>( scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MIN_PROPERTY_NAME ) );
486 mScrollSlideInfo->mScrollPositionMax = scrollView.GetProperty<Vector3>( scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MAX_PROPERTY_NAME ) );
487 mScrollSlideInfo->mScrollWrap = scrollView.GetProperty<bool>( scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_WRAP_PROPERTY_NAME ) );
488 mScrollSlideInfo->mVertical = false;
490 // Create effect-time property if not already created.
491 if(mPropertyTime == Property::INVALID_INDEX)
493 mPropertyTime = SafeRegisterProperty( scrollView, Toolkit::ScrollViewSlideEffect::EFFECT_TIME, 0.0f );
494 mPropertyReference = SafeRegisterProperty( scrollView, Toolkit::ScrollViewSlideEffect::EFFECT_REFERENCE, Vector3::ZERO );
495 mPropertyActive = SafeRegisterProperty( scrollView, Toolkit::ScrollViewSlideEffect::EFFECT_ACTIVE, 0.0f );
498 // Create constraint to update ScrollSlideInfo
499 // Doesn't matter what this is applied to and on what property.
500 // Just needs to update mScrollSlideInfo values as properties change.
501 // The minor constraints (applied to the Actors) can use this mScrollSlideInfo.
502 Constraint constraint = Constraint::New<float>( mPropertyTime,
503 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_PROPERTY_NAME ) ),
504 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollViewSlideEffect::EFFECT_REFERENCE ) ),
505 Source(scrollView, Actor::SIZE),
506 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MIN_PROPERTY_NAME ) ),
507 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MAX_PROPERTY_NAME ) ),
508 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_WRAP_PROPERTY_NAME ) ),
509 ScrollSlideInfoUpdate(mScrollSlideInfo) );
510 constraint.SetRemoveAction( Constraint::Discard );
511 mInfoUpdateConstraint = scrollView.ApplyConstraint( constraint );
513 // Connect to the scroll view signals
514 scrollView.ScrollStartedSignal().Connect(this, &ScrollViewSlideEffect::OnScrollStart);
515 scrollView.SnapStartedSignal().Connect(this, &ScrollViewSlideEffect::OnScrollSnapStarted);
516 scrollView.TouchedSignal().Connect(this, &ScrollViewSlideEffect::OnScrollTouched);
518 AttachActor(scrollView);
521 bool ScrollViewSlideEffect::OnScrollTouched(Actor actor, const TouchEvent& event)
523 // Ignore events with multiple-touch points
524 if (event.GetPointCount() != 1)
529 if (event.GetPoint(0).state == TouchPoint::Down)
531 const TouchPoint& point = event.GetPoint(0);
532 Vector3 touchPosition(point.local - Stage::GetCurrent().GetSize() * 0.5f);
534 Vector3 scrollPosition = GetScrollView().GetCurrentScrollPosition();
535 GetScrollView().SetProperty(mPropertyReference, scrollPosition + touchPosition + mDelayReferenceOffset);
541 void ScrollViewSlideEffect::OnDetach(Toolkit::ScrollView& scrollView)
543 scrollView.ScrollStartedSignal().Disconnect(this, &ScrollViewSlideEffect::OnScrollStart);
544 scrollView.SnapStartedSignal().Disconnect(this, &ScrollViewSlideEffect::OnScrollSnapStarted);
545 scrollView.TouchedSignal().Disconnect(this, &ScrollViewSlideEffect::OnScrollTouched);
546 scrollView.RemoveConstraint( mInfoUpdateConstraint );
550 mAnimation.FinishedSignal().Disconnect(this, &ScrollViewSlideEffect::OnAnimationFinished);
557 mAnimationSnap.FinishedSignal().Disconnect(this, &ScrollViewSlideEffect::OnAnimationSnapFinished);
558 mAnimationSnap.Clear();
559 mAnimationSnap.Reset();
563 void ScrollViewSlideEffect::AttachActor(Actor actor)
568 void ScrollViewSlideEffect::DetachActor(Actor actor)
570 // TODO: remove the specific constraint defined in AttachActor (and possibly
571 // unregister property) - neither functionality exists in Dali.
574 void ScrollViewSlideEffect::ContinueAnimation(float endTime)
576 // continue animating
579 mAnimation.FinishedSignal().Disconnect(this, &ScrollViewSlideEffect::OnAnimationFinished);
583 Actor scrollView = GetScrollView();
585 mAnimation = Animation::New(SLIDEEFFECT_ANIMATION_MAX_TIME);
586 mAnimation.AnimateTo( Property(scrollView, mPropertyTime), endTime, AlphaFunctions::Linear );
587 mAnimation.FinishedSignal().Connect(this, &ScrollViewSlideEffect::OnAnimationFinished);
591 void ScrollViewSlideEffect::OnScrollStart( const Vector3& position )
593 Actor scrollView = GetScrollView();
594 GetScrollView().SetProperty(mPropertyTime, 0.0f);
596 ContinueAnimation(SLIDEEFFECT_ANIMATION_MAX_TIME);
600 mAnimationSnap.FinishedSignal().Disconnect(this, &ScrollViewSlideEffect::OnAnimationSnapFinished);
601 mAnimationSnap.Clear();
604 mAnimationSnap = Animation::New( EFFECT_SNAP_GROW_DURATION );
605 mAnimationSnap.AnimateTo( Property(scrollView, mPropertyActive), 1.0f, AlphaFunctions::Linear );
606 mAnimationSnap.FinishedSignal().Connect(this, &ScrollViewSlideEffect::OnAnimationSnapFinished);
607 mAnimationSnap.Play();
610 void ScrollViewSlideEffect::OnScrollSnapStarted(const Toolkit::ScrollView::SnapEvent& event)
614 mAnimationSnap.Clear();
617 Actor scrollView = GetScrollView();
618 mAnimationSnap = Animation::New(EFFECT_SNAP_DECAY_DURATION );
619 mAnimationSnap.AnimateTo( Property(scrollView, mPropertyActive), 0.0f, AlphaFunctions::Linear );
620 mAnimationSnap.FinishedSignal().Connect(this, &ScrollViewSlideEffect::OnAnimationSnapFinished);
621 mAnimationSnap.Play();
624 void ScrollViewSlideEffect::OnAnimationSnapFinished( Animation& animation )
626 mAnimationSnap.FinishedSignal().Disconnect(this, &ScrollViewSlideEffect::OnAnimationSnapFinished);
627 mAnimationSnap.Clear();
629 // stop time animation
632 mAnimation.FinishedSignal().Disconnect(this, &ScrollViewSlideEffect::OnAnimationFinished);
637 void ScrollViewSlideEffect::OnAnimationFinished( Animation& animation )
639 // still unstable, so continue animating.
640 // TODO: Requires an instability check to ensure time animation finishes when delay is
641 // less noticeable. i.e. all present scrollPositions are approx the same as mScrollPosition in constraints.
642 // best solution for this is to switch to a single history vector of scroll position, and compare if
643 // position has not deviated >= 0.5 pixel for the past 1 second.
644 float endTime = GetScrollView().GetProperty<float>(mPropertyTime) + SLIDEEFFECT_ANIMATION_MAX_TIME;
645 ContinueAnimation(endTime);
648 } // namespace Internal
650 } // namespace Toolkit