diff --git a/autotests/libkwineffects/timelinetest.cpp b/autotests/libkwineffects/timelinetest.cpp --- a/autotests/libkwineffects/timelinetest.cpp +++ b/autotests/libkwineffects/timelinetest.cpp @@ -43,6 +43,14 @@ void testSetDurationRetargeting(); void testSetDurationRetargetingSmallDuration(); void testRunning(); + void testStrictRedirectSourceMode_data(); + void testStrictRedirectSourceMode(); + void testRelaxedRedirectSourceMode_data(); + void testRelaxedRedirectSourceMode(); + void testStrictRedirectTargetMode_data(); + void testStrictRedirectTargetMode(); + void testRelaxedRedirectTargetMode_data(); + void testRelaxedRedirectTargetMode(); }; void TimeLineTest::testUpdateForward() @@ -251,6 +259,156 @@ QVERIFY(timeLine.done()); } +void TimeLineTest::testStrictRedirectSourceMode_data() +{ + QTest::addColumn("initialDirection"); + QTest::addColumn("initialValue"); + QTest::addColumn("finalDirection"); + QTest::addColumn("finalValue"); + + QTest::newRow("forward -> backward") << KWin::TimeLine::Forward << 0.0 << KWin::TimeLine::Backward << 0.0; + QTest::newRow("backward -> forward") << KWin::TimeLine::Backward << 1.0 << KWin::TimeLine::Forward << 1.0; +} + +void TimeLineTest::testStrictRedirectSourceMode() +{ + QFETCH(KWin::TimeLine::Direction, initialDirection); + KWin::TimeLine timeLine(1000ms, initialDirection); + timeLine.setEasingCurve(QEasingCurve::Linear); + timeLine.setSourceRedirectMode(KWin::TimeLine::RedirectMode::Strict); + + QTEST(timeLine.direction(), "initialDirection"); + QTEST(timeLine.value(), "initialValue"); + QCOMPARE(timeLine.sourceRedirectMode(), KWin::TimeLine::RedirectMode::Strict); + QVERIFY(!timeLine.running()); + QVERIFY(!timeLine.done()); + + QFETCH(KWin::TimeLine::Direction, finalDirection); + timeLine.setDirection(finalDirection); + + QTEST(timeLine.direction(), "finalDirection"); + QTEST(timeLine.value(), "finalValue"); + QCOMPARE(timeLine.sourceRedirectMode(), KWin::TimeLine::RedirectMode::Strict); + QVERIFY(!timeLine.running()); + QVERIFY(timeLine.done()); +} + +void TimeLineTest::testRelaxedRedirectSourceMode_data() +{ + QTest::addColumn("initialDirection"); + QTest::addColumn("initialValue"); + QTest::addColumn("finalDirection"); + QTest::addColumn("finalValue"); + + QTest::newRow("forward -> backward") << KWin::TimeLine::Forward << 0.0 << KWin::TimeLine::Backward << 1.0; + QTest::newRow("backward -> forward") << KWin::TimeLine::Backward << 1.0 << KWin::TimeLine::Forward << 0.0; +} + +void TimeLineTest::testRelaxedRedirectSourceMode() +{ + QFETCH(KWin::TimeLine::Direction, initialDirection); + KWin::TimeLine timeLine(1000ms, initialDirection); + timeLine.setEasingCurve(QEasingCurve::Linear); + timeLine.setSourceRedirectMode(KWin::TimeLine::RedirectMode::Relaxed); + + QTEST(timeLine.direction(), "initialDirection"); + QTEST(timeLine.value(), "initialValue"); + QCOMPARE(timeLine.sourceRedirectMode(), KWin::TimeLine::RedirectMode::Relaxed); + QVERIFY(!timeLine.running()); + QVERIFY(!timeLine.done()); + + QFETCH(KWin::TimeLine::Direction, finalDirection); + timeLine.setDirection(finalDirection); + + QTEST(timeLine.direction(), "finalDirection"); + QTEST(timeLine.value(), "finalValue"); + QCOMPARE(timeLine.sourceRedirectMode(), KWin::TimeLine::RedirectMode::Relaxed); + QVERIFY(!timeLine.running()); + QVERIFY(!timeLine.done()); +} + +void TimeLineTest::testStrictRedirectTargetMode_data() +{ + QTest::addColumn("initialDirection"); + QTest::addColumn("initialValue"); + QTest::addColumn("finalDirection"); + QTest::addColumn("finalValue"); + + QTest::newRow("forward -> backward") << KWin::TimeLine::Forward << 0.0 << KWin::TimeLine::Backward << 1.0; + QTest::newRow("backward -> forward") << KWin::TimeLine::Backward << 1.0 << KWin::TimeLine::Forward << 0.0; +} + +void TimeLineTest::testStrictRedirectTargetMode() +{ + QFETCH(KWin::TimeLine::Direction, initialDirection); + KWin::TimeLine timeLine(1000ms, initialDirection); + timeLine.setEasingCurve(QEasingCurve::Linear); + timeLine.setTargetRedirectMode(KWin::TimeLine::RedirectMode::Strict); + + QTEST(timeLine.direction(), "initialDirection"); + QTEST(timeLine.value(), "initialValue"); + QCOMPARE(timeLine.targetRedirectMode(), KWin::TimeLine::RedirectMode::Strict); + QVERIFY(!timeLine.running()); + QVERIFY(!timeLine.done()); + + timeLine.update(1000ms); + QTEST(timeLine.value(), "finalValue"); + QVERIFY(!timeLine.running()); + QVERIFY(timeLine.done()); + + QFETCH(KWin::TimeLine::Direction, finalDirection); + timeLine.setDirection(finalDirection); + + QTEST(timeLine.direction(), "finalDirection"); + QTEST(timeLine.value(), "finalValue"); + QVERIFY(!timeLine.running()); + QVERIFY(timeLine.done()); +} + +void TimeLineTest::testRelaxedRedirectTargetMode_data() +{ + QTest::addColumn("initialDirection"); + QTest::addColumn("initialValue"); + QTest::addColumn("finalDirection"); + QTest::addColumn("finalValue"); + + QTest::newRow("forward -> backward") << KWin::TimeLine::Forward << 0.0 << KWin::TimeLine::Backward << 1.0; + QTest::newRow("backward -> forward") << KWin::TimeLine::Backward << 1.0 << KWin::TimeLine::Forward << 0.0; +} + +void TimeLineTest::testRelaxedRedirectTargetMode() +{ + QFETCH(KWin::TimeLine::Direction, initialDirection); + KWin::TimeLine timeLine(1000ms, initialDirection); + timeLine.setEasingCurve(QEasingCurve::Linear); + timeLine.setTargetRedirectMode(KWin::TimeLine::RedirectMode::Relaxed); + + QTEST(timeLine.direction(), "initialDirection"); + QTEST(timeLine.value(), "initialValue"); + QCOMPARE(timeLine.targetRedirectMode(), KWin::TimeLine::RedirectMode::Relaxed); + QVERIFY(!timeLine.running()); + QVERIFY(!timeLine.done()); + + timeLine.update(1000ms); + QTEST(timeLine.value(), "finalValue"); + QVERIFY(!timeLine.running()); + QVERIFY(timeLine.done()); + + QFETCH(KWin::TimeLine::Direction, finalDirection); + timeLine.setDirection(finalDirection); + + QTEST(timeLine.direction(), "finalDirection"); + QTEST(timeLine.value(), "finalValue"); + QVERIFY(!timeLine.running()); + QVERIFY(!timeLine.done()); + + timeLine.update(1000ms); + QTEST(timeLine.direction(), "finalDirection"); + QTEST(timeLine.value(), "initialValue"); + QVERIFY(!timeLine.running()); + QVERIFY(timeLine.done()); +} + QTEST_MAIN(TimeLineTest) #include "timelinetest.moc" diff --git a/libkwineffects/kwineffects.h b/libkwineffects/kwineffects.h --- a/libkwineffects/kwineffects.h +++ b/libkwineffects/kwineffects.h @@ -3584,6 +3584,65 @@ **/ void reset(); + enum class RedirectMode { + Strict, + Relaxed + }; + + /** + * Returns the redirect mode for the source position. + * + * The redirect mode controls behavior of the timeline when its direction is + * changed at the source position, e.g. what should we do when the timeline + * initially goes forward and we change its direction to go backward. + * + * In the strict mode, the timeline will stop. + * + * In the relaxed mode, the timeline will go in the new direction. For example, + * if the timeline goes forward(from 0 to 1), then with the new direction it + * will go backward(from 1 to 0). + * + * The default is RedirectMode::Relaxed. + * + * @see targetRedirectMode + * @since 5.15 + **/ + RedirectMode sourceRedirectMode() const; + + /** + * Sets the redirect mode for the source position. + * + * @param mode The new mode. + * @since 5.15 + **/ + void setSourceRedirectMode(RedirectMode mode); + + /** + * Returns the redirect mode for the target position. + * + * The redirect mode controls behavior of the timeline when its direction is + * changed at the target position. + * + * In the strict mode, subsequent update calls won't have any effect on the + * current value of the timeline. + * + * In the relaxed mode, the timeline will go in the new direction. + * + * The default is RedirectMode::Strict. + * + * @see sourceRedirectMode + * @since 5.15 + **/ + RedirectMode targetRedirectMode() const; + + /** + * Sets the redirect mode for the target position. + * + * @param mode The new mode. + * @since 5.15 + **/ + void setTargetRedirectMode(RedirectMode mode); + TimeLine &operator=(const TimeLine &other); private: @@ -3824,6 +3883,7 @@ Q_DECLARE_METATYPE(KWin::EffectWindow*) Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(KWin::TimeLine) +Q_DECLARE_METATYPE(KWin::TimeLine::Direction) /** @} */ diff --git a/libkwineffects/kwineffects.cpp b/libkwineffects/kwineffects.cpp --- a/libkwineffects/kwineffects.cpp +++ b/libkwineffects/kwineffects.cpp @@ -1953,6 +1953,8 @@ std::chrono::milliseconds elapsed = std::chrono::milliseconds::zero(); bool done = false; + RedirectMode sourceRedirectMode = RedirectMode::Relaxed; + RedirectMode targetRedirectMode = RedirectMode::Strict; }; TimeLine::TimeLine(std::chrono::milliseconds duration, Direction direction) @@ -2038,10 +2040,21 @@ if (d->direction == direction) { return; } - if (d->elapsed > std::chrono::milliseconds::zero()) { + + d->direction = direction; + + if (d->elapsed > std::chrono::milliseconds::zero() + || d->sourceRedirectMode == RedirectMode::Strict) { d->elapsed = d->duration - d->elapsed; } - d->direction = direction; + + if (d->done && d->targetRedirectMode == RedirectMode::Relaxed) { + d->done = false; + } + + if (d->elapsed >= d->duration) { + d->done = true; + } } void TimeLine::toggleDirection() @@ -2081,6 +2094,26 @@ d->done = false; } +TimeLine::RedirectMode TimeLine::sourceRedirectMode() const +{ + return d->sourceRedirectMode; +} + +void TimeLine::setSourceRedirectMode(RedirectMode mode) +{ + d->sourceRedirectMode = mode; +} + +TimeLine::RedirectMode TimeLine::targetRedirectMode() const +{ + return d->targetRedirectMode; +} + +void TimeLine::setTargetRedirectMode(RedirectMode mode) +{ + d->targetRedirectMode = mode; +} + TimeLine &TimeLine::operator=(const TimeLine &other) { d = other.d;