Changeset View
Changeset View
Standalone View
Standalone View
libkwineffects/kwinanimationeffect.cpp
Show First 20 Lines • Show All 248 Lines • ▼ Show 20 Line(s) | 220 | { | |||
---|---|---|---|---|---|
249 | PreviousWindowPixmapLockPtr previousPixmap; | 249 | PreviousWindowPixmapLockPtr previousPixmap; | ||
250 | if (a == CrossFadePrevious) { | 250 | if (a == CrossFadePrevious) { | ||
251 | previousPixmap = PreviousWindowPixmapLockPtr::create(w); | 251 | previousPixmap = PreviousWindowPixmapLockPtr::create(w); | ||
252 | } | 252 | } | ||
253 | 253 | | |||
254 | it->first.append(AniData( | 254 | it->first.append(AniData( | ||
255 | a, // Attribute | 255 | a, // Attribute | ||
256 | meta, // Metadata | 256 | meta, // Metadata | ||
257 | ms, // Duration | | |||
258 | to, // Target | 257 | to, // Target | ||
259 | curve, // Easing curve | | |||
260 | delay, // Delay | 258 | delay, // Delay | ||
261 | from, // Source | 259 | from, // Source | ||
262 | waitAtSource, // Whether the animation should be kept at source | 260 | waitAtSource, // Whether the animation should be kept at source | ||
263 | keepAtTarget, // Whether the animation is persistent | 261 | keepAtTarget, // Whether the animation is persistent | ||
264 | fullscreen, // Full screen effect lock | 262 | fullscreen, // Full screen effect lock | ||
265 | keepAlive, // Keep alive flag | 263 | keepAlive, // Keep alive flag | ||
266 | previousPixmap // Previous window pixmap lock | 264 | previousPixmap // Previous window pixmap lock | ||
267 | )); | 265 | )); | ||
268 | 266 | | |||
269 | quint64 ret_id = ++d->m_animCounter; | 267 | const quint64 ret_id = ++d->m_animCounter; | ||
270 | it->first.last().id = ret_id; | 268 | AniData &animation = it->first.last(); | ||
269 | animation.id = ret_id; | ||||
270 | | ||||
271 | animation.timeLine.setDirection(TimeLine::Forward); | ||||
272 | animation.timeLine.setDuration(std::chrono::milliseconds(ms)); | ||||
273 | animation.timeLine.setEasingCurve(curve); | ||||
274 | animation.timeLine.setSourceRedirectMode(TimeLine::RedirectMode::Strict); | ||||
275 | animation.timeLine.setTargetRedirectMode(TimeLine::RedirectMode::Relaxed); | ||||
276 | | ||||
271 | it->second = QRect(); | 277 | it->second = QRect(); | ||
272 | 278 | | |||
273 | d->m_animationsTouched = true; | 279 | d->m_animationsTouched = true; | ||
274 | 280 | | |||
275 | if (delay > 0) { | 281 | if (delay > 0) { | ||
276 | QTimer::singleShot(delay, this, SLOT(triggerRepaint())); | 282 | QTimer::singleShot(delay, this, SLOT(triggerRepaint())); | ||
277 | const QSize &s = effects->virtualScreenSize(); | 283 | const QSize &s = effects->virtualScreenSize(); | ||
278 | if (waitAtSource) | 284 | if (waitAtSource) | ||
Show All 13 Lines | 294 | { | |||
292 | for (AniMap::iterator entry = d->m_animations.begin(), | 298 | for (AniMap::iterator entry = d->m_animations.begin(), | ||
293 | mapEnd = d->m_animations.end(); entry != mapEnd; ++entry) { | 299 | mapEnd = d->m_animations.end(); entry != mapEnd; ++entry) { | ||
294 | for (QList<AniData>::iterator anim = entry->first.begin(), | 300 | for (QList<AniData>::iterator anim = entry->first.begin(), | ||
295 | animEnd = entry->first.end(); anim != animEnd; ++anim) { | 301 | animEnd = entry->first.end(); anim != animEnd; ++anim) { | ||
296 | if (anim->id == animationId) { | 302 | if (anim->id == animationId) { | ||
297 | anim->from.set(interpolated(*anim, 0), interpolated(*anim, 1)); | 303 | anim->from.set(interpolated(*anim, 0), interpolated(*anim, 1)); | ||
298 | validate(anim->attribute, anim->meta, nullptr, &newTarget, entry.key()); | 304 | validate(anim->attribute, anim->meta, nullptr, &newTarget, entry.key()); | ||
299 | anim->to.set(newTarget[0], newTarget[1]); | 305 | anim->to.set(newTarget[0], newTarget[1]); | ||
300 | anim->duration = anim->time + newRemainingTime; | 306 | | ||
307 | anim->timeLine.setDirection(TimeLine::Forward); | ||||
308 | anim->timeLine.setDuration(std::chrono::milliseconds(newRemainingTime)); | ||||
davidedmundson: This is different. Though IMHO arguably more correct - please can you check.
Lets say I'm… | |||||
That's why I posted video of the Morphing Popups. :-) IMHO, the behavior of the old code is incorrect. If I retarget animation from the current position to a new position, then there should not be any jumps. Animation should go from the current position to the new target position.
This is the correct behavior, imho. The opacity should go from 0.5 to 1.0, otherwise animation won't be smooth. zzag: That's why I posted video of the Morphing Popups. :-)
IMHO, the behavior of the old code is… | |||||
... also please notice that the current value of the timeline will go from 0.0 to 1.0. zzag: ... also please notice that the current value of the timeline will go from 0.0 to 1.0. | |||||
309 | anim->timeLine.reset(); | ||||
310 | | ||||
301 | return true; | 311 | return true; | ||
302 | } | 312 | } | ||
303 | } | 313 | } | ||
304 | } | 314 | } | ||
305 | return false; // no animation found | 315 | return false; // no animation found | ||
306 | } | 316 | } | ||
307 | 317 | | |||
308 | bool AnimationEffect::cancel(quint64 animationId) | 318 | bool AnimationEffect::cancel(quint64 animationId) | ||
Show All 37 Lines | 352 | while (entry != mapEnd) { | |||
346 | while (anim != animEnd) { | 356 | while (anim != animEnd) { | ||
347 | if (anim->startTime > clock()) { | 357 | if (anim->startTime > clock()) { | ||
348 | if (!anim->waitAtSource) { | 358 | if (!anim->waitAtSource) { | ||
349 | ++anim; | 359 | ++anim; | ||
350 | ++animCounter; | 360 | ++animCounter; | ||
351 | continue; | 361 | continue; | ||
352 | } | 362 | } | ||
353 | } else { | 363 | } else { | ||
354 | anim->addTime(time); | 364 | anim->timeLine.update(std::chrono::milliseconds(time)); | ||
355 | } | 365 | } | ||
356 | 366 | | |||
357 | if (anim->time < anim->duration || anim->keepAtTarget) { | 367 | if (!anim->timeLine.done() || anim->keepAtTarget) { | ||
358 | // if (anim->attribute != Brightness && anim->attribute != Saturation && anim->attribute != Opacity) | 368 | // if (anim->attribute != Brightness && anim->attribute != Saturation && anim->attribute != Opacity) | ||
359 | // transformed = true; | 369 | // transformed = true; | ||
360 | d->m_animated = true; | 370 | d->m_animated = true; | ||
361 | ++anim; | 371 | ++anim; | ||
362 | ++animCounter; | 372 | ++animCounter; | ||
363 | } else { | 373 | } else { | ||
364 | EffectWindow *oldW = entry.key(); | 374 | EffectWindow *oldW = entry.key(); | ||
365 | d->m_justEndedAnimation = anim->id; | 375 | d->m_justEndedAnimation = anim->id; | ||
▲ Show 20 Lines • Show All 297 Lines • ▼ Show 20 Line(s) | 668 | if ( d->m_animated ) { | |||
663 | } else { | 673 | } else { | ||
664 | AniMap::const_iterator it = d->m_animations.constBegin(), end = d->m_animations.constEnd(); | 674 | AniMap::const_iterator it = d->m_animations.constBegin(), end = d->m_animations.constEnd(); | ||
665 | for (; it != end; ++it) { | 675 | for (; it != end; ++it) { | ||
666 | bool addRepaint = false; | 676 | bool addRepaint = false; | ||
667 | QList<AniData>::const_iterator anim = it->first.constBegin(); | 677 | QList<AniData>::const_iterator anim = it->first.constBegin(); | ||
668 | for (; anim != it->first.constEnd(); ++anim) { | 678 | for (; anim != it->first.constEnd(); ++anim) { | ||
669 | if (anim->startTime > clock()) | 679 | if (anim->startTime > clock()) | ||
670 | continue; | 680 | continue; | ||
671 | if (anim->time < anim->duration) { | 681 | if (!anim->timeLine.done()) { | ||
672 | addRepaint = true; | 682 | addRepaint = true; | ||
673 | break; | 683 | break; | ||
674 | } | 684 | } | ||
675 | } | 685 | } | ||
676 | if (addRepaint) { | 686 | if (addRepaint) { | ||
677 | it.key()->addLayerRepaint(it->second); | 687 | it.key()->addLayerRepaint(it->second); | ||
678 | } | 688 | } | ||
679 | } | 689 | } | ||
680 | } | 690 | } | ||
681 | } | 691 | } | ||
682 | effects->postPaintScreen(); | 692 | effects->postPaintScreen(); | ||
683 | } | 693 | } | ||
684 | 694 | | |||
685 | float AnimationEffect::interpolated( const AniData &a, int i ) const | 695 | float AnimationEffect::interpolated( const AniData &a, int i ) const | ||
686 | { | 696 | { | ||
687 | if (a.startTime > clock()) | 697 | if (a.startTime > clock()) | ||
688 | return a.from[i]; | 698 | return a.from[i]; | ||
689 | if (a.time < a.duration) | 699 | if (!a.timeLine.done()) | ||
690 | return a.from[i] + a.curve.valueForProgress( ((float)a.time)/a.duration )*(a.to[i] - a.from[i]); | 700 | return a.from[i] + a.timeLine.value() * (a.to[i] - a.from[i]); | ||
691 | return a.to[i]; // we're done and "waiting" at the target value | 701 | return a.to[i]; // we're done and "waiting" at the target value | ||
692 | } | 702 | } | ||
693 | 703 | | |||
694 | float AnimationEffect::progress( const AniData &a ) const | 704 | float AnimationEffect::progress( const AniData &a ) const | ||
695 | { | 705 | { | ||
696 | if (a.startTime > clock()) | 706 | return a.startTime < clock() ? a.timeLine.value() : 0.0; | ||
697 | return 0.0; | | |||
698 | if (a.time < a.duration) | | |||
699 | return a.curve.valueForProgress( ((float)a.time)/a.duration ); | | |||
700 | return 1.0; // we're done and "waiting" at the target value | | |||
701 | } | 707 | } | ||
702 | 708 | | |||
703 | 709 | | |||
704 | // TODO - get this out of the header - the functionpointer usage of QEasingCurve somehow sucks ;-) | 710 | // TODO - get this out of the header - the functionpointer usage of QEasingCurve somehow sucks ;-) | ||
705 | // qreal AnimationEffect::qecGaussian(qreal progress) // exp(-5*(2*x-1)^2) | 711 | // qreal AnimationEffect::qecGaussian(qreal progress) // exp(-5*(2*x-1)^2) | ||
706 | // { | 712 | // { | ||
707 | // progress = 2*progress - 1; | 713 | // progress = 2*progress - 1; | ||
708 | // progress *= -5*progress; | 714 | // progress *= -5*progress; | ||
▲ Show 20 Lines • Show All 65 Lines • ▼ Show 20 Line(s) | 778 | } else { | |||
774 | for (; it != end; ++it) { | 780 | for (; it != end; ++it) { | ||
775 | it.key()->addLayerRepaint(it->second); | 781 | it.key()->addLayerRepaint(it->second); | ||
776 | } | 782 | } | ||
777 | } | 783 | } | ||
778 | } | 784 | } | ||
779 | 785 | | |||
780 | static float fixOvershoot(float f, const AniData &d, short int dir, float s = 1.1) | 786 | static float fixOvershoot(float f, const AniData &d, short int dir, float s = 1.1) | ||
781 | { | 787 | { | ||
782 | switch(d.curve.type()) { | 788 | switch(d.timeLine.easingCurve().type()) { | ||
783 | case QEasingCurve::InOutElastic: | 789 | case QEasingCurve::InOutElastic: | ||
784 | case QEasingCurve::InOutBack: | 790 | case QEasingCurve::InOutBack: | ||
785 | return f * s; | 791 | return f * s; | ||
786 | case QEasingCurve::InElastic: | 792 | case QEasingCurve::InElastic: | ||
787 | case QEasingCurve::OutInElastic: | 793 | case QEasingCurve::OutInElastic: | ||
788 | case QEasingCurve::OutBack: | 794 | case QEasingCurve::OutBack: | ||
789 | return (dir&2) ? f * s : f; | 795 | return (dir&2) ? f * s : f; | ||
790 | case QEasingCurve::OutElastic: | 796 | case QEasingCurve::OutElastic: | ||
▲ Show 20 Lines • Show All 192 Lines • Show Last 20 Lines |
This is different. Though IMHO arguably more correct - please can you check.
Lets say I'm animating opacity 0 to 1 for 10 seconds.
After 5 seconds through I decide I want to make it 15 seconds, so I call retarget(id, 1, 10)
With the old code I'm 1/3 of the way through a 15s animation that goes from 0.5 -> 1 which means my current value is 0.666
With the new code I'm at the start of a 10s animation from 0.5 to 1. Which means my current value is 0.5