diff --git a/autotests/kmessagewidgetautotest.h b/autotests/kmessagewidgetautotest.h --- a/autotests/kmessagewidgetautotest.h +++ b/autotests/kmessagewidgetautotest.h @@ -27,16 +27,18 @@ Q_OBJECT private Q_SLOTS: - void initTestCase(); void testAnimationSignals(); - -public Q_SLOTS: - void hideAnimationFinished(); - void showAnimationFinished(); - -private: - int m_hideSignals; - int m_showSignals; + void testShowOnVisible(); + void testHideOnInvisible(); + void testOverlappingShowAndHide_data(); + void testOverlappingShowAndHide(); + void testOverlappingHideAndShow_data(); + void testOverlappingHideAndShow(); + void testOverlappingDoubleShow_data(); + void testOverlappingDoubleShow(); + void testOverlappingDoubleHide_data(); + void testOverlappingDoubleHide(); + void testHideWithNotYetShownParent(); }; #endif diff --git a/autotests/kmessagewidgetautotest.cpp b/autotests/kmessagewidgetautotest.cpp --- a/autotests/kmessagewidgetautotest.cpp +++ b/autotests/kmessagewidgetautotest.cpp @@ -1,5 +1,6 @@ /* Copyright 2014 Dominik Haumann + Copyright 2017 Friedrich W. H. Kossebau This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -24,56 +25,230 @@ QTEST_MAIN(KMessageWidgetTest) -void KMessageWidgetTest::initTestCase() -{ - m_hideSignals = 0; - m_showSignals = 0; -} +// KMessageWidget is currently hardcoded to a 500 ms timeline and default QTimeLine 40 ms update intervall +// let's have 7 updates for now, should be save +const int overlappingWaitingTime = 280; void KMessageWidgetTest::testAnimationSignals() { KMessageWidget w(QStringLiteral("Hello world")); - connect(&w, &KMessageWidget::showAnimationFinished, this, &KMessageWidgetTest::showAnimationFinished); - connect(&w, &KMessageWidget::hideAnimationFinished, this, &KMessageWidgetTest::hideAnimationFinished); + QSignalSpy showSignalsSpy(&w, &KMessageWidget::showAnimationFinished); + QSignalSpy hideSignalsSpy(&w, &KMessageWidget::hideAnimationFinished); - QCOMPARE(m_showSignals, 0); + QCOMPARE(showSignalsSpy.count(), 0); // // test: showing the message widget should emit showAnimationFinished() // exactly once after the show animation is finished // w.animatedShow(); while (w.isShowAnimationRunning()) { - QCOMPARE(m_showSignals, 0); + QCOMPARE(showSignalsSpy.count(), 0); QTest::qWait(50); } QVERIFY(! w.isShowAnimationRunning()); - QCOMPARE(m_showSignals, 1); + QCOMPARE(showSignalsSpy.count(), 1); + QVERIFY(w.isVisible()); // // test: hiding the message widget should emit hideAnimationFinished() // exactly once after the show animation is finished // w.animatedHide(); while (w.isHideAnimationRunning()) { - QCOMPARE(m_hideSignals, 0); + QCOMPARE(hideSignalsSpy.count(), 0); + QTest::qWait(50); + } + + QCOMPARE(hideSignalsSpy.count(), 1); + QVERIFY(! w.isVisible()); +} + +void KMessageWidgetTest::testShowOnVisible() +{ + KMessageWidget w(QStringLiteral("Hello world")); + QSignalSpy showSignalsSpy(&w, &KMessageWidget::showAnimationFinished); + w.show(); + + // test: call show on visible + w.animatedShow(); + + while (w.isShowAnimationRunning()) { + QCOMPARE(showSignalsSpy.count(), 0); + QTest::qWait(50); + } + + QCOMPARE(showSignalsSpy.count(), 1); + QVERIFY(w.isVisible()); +} + +void KMessageWidgetTest::testHideOnInvisible() +{ + KMessageWidget w(QStringLiteral("Hello world")); + QSignalSpy hideSignalsSpy(&w, &KMessageWidget::hideAnimationFinished); + + // test: call hide on visible + w.animatedHide(); + + while (w.isHideAnimationRunning()) { + QCOMPARE(hideSignalsSpy.count(), 0); + QTest::qWait(50); + } + + QCOMPARE(hideSignalsSpy.count(), 1); + QVERIFY(! w.isVisible()); +} + +void KMessageWidgetTest::testOverlappingShowAndHide_data() +{ + QTest::addColumn("delay"); + QTest::newRow("instant") << false; + QTest::newRow("delay") << true; +} + +void KMessageWidgetTest::testOverlappingShowAndHide() +{ + QFETCH(bool, delay); + + KMessageWidget w(QStringLiteral("Hello world")); + + QSignalSpy showSignalsSpy(&w, &KMessageWidget::showAnimationFinished); + QSignalSpy hideSignalsSpy(&w, &KMessageWidget::hideAnimationFinished); + + // test: call hide while show animation still running + w.animatedShow(); + if (delay) { + QTest::qWait(overlappingWaitingTime); + } + w.animatedHide(); + + QVERIFY(! w.isShowAnimationRunning()); + QCOMPARE(showSignalsSpy.count(), 1); + + while (w.isHideAnimationRunning()) { + QCOMPARE(hideSignalsSpy.count(), 0); QTest::qWait(50); } + QCOMPARE(hideSignalsSpy.count(), 1); + QVERIFY(! w.isVisible()); +} + +void KMessageWidgetTest::testOverlappingHideAndShow_data() +{ + QTest::addColumn("delay"); + QTest::newRow("instant") << false; + QTest::newRow("delay") << true; +} + +void KMessageWidgetTest::testOverlappingHideAndShow() +{ + QFETCH(bool, delay); + + KMessageWidget w(QStringLiteral("Hello world")); + QSignalSpy showSignalsSpy(&w, &KMessageWidget::showAnimationFinished); + QSignalSpy hideSignalsSpy(&w, &KMessageWidget::hideAnimationFinished); + w.show(); + + // test: call show while hide animation still running + w.animatedHide(); + if (delay) { + QTest::qWait(overlappingWaitingTime); + } + w.animatedShow(); + QVERIFY(! w.isHideAnimationRunning()); - QCOMPARE(m_hideSignals, 1); + QCOMPARE(hideSignalsSpy.count(), 1); + + while (w.isShowAnimationRunning()) { + QCOMPARE(showSignalsSpy.count(), 0); + QTest::qWait(50); + } + + QCOMPARE(showSignalsSpy.count(), 1); + QVERIFY(w.isVisible()); +} + +void KMessageWidgetTest::testOverlappingDoubleShow_data() +{ + QTest::addColumn("delay"); + QTest::newRow("instant") << false; + QTest::newRow("delay") << true; +} + +void KMessageWidgetTest::testOverlappingDoubleShow() +{ + QFETCH(bool, delay); + + KMessageWidget w(QStringLiteral("Hello world")); + QSignalSpy showSignalsSpy(&w, &KMessageWidget::showAnimationFinished); + + // test: call show while show animation still running + w.animatedShow(); + if (delay) { + QTest::qWait(overlappingWaitingTime); + } + w.animatedShow(); + + while (w.isShowAnimationRunning()) { + QCOMPARE(showSignalsSpy.count(), 0); + QTest::qWait(50); + } + + QCOMPARE(showSignalsSpy.count(), 1); + QVERIFY(w.isVisible()); } -void KMessageWidgetTest::hideAnimationFinished() +void KMessageWidgetTest::testOverlappingDoubleHide_data() { - ++m_hideSignals; + QTest::addColumn("delay"); + QTest::newRow("instant") << false; + QTest::newRow("delay") << true; } -void KMessageWidgetTest::showAnimationFinished() +void KMessageWidgetTest::testOverlappingDoubleHide() { - ++m_showSignals; + QFETCH(bool, delay); + + KMessageWidget w(QStringLiteral("Hello world")); + QSignalSpy hideSignalsSpy(&w, &KMessageWidget::hideAnimationFinished); + w.show(); + + // test: call hide while hide animation still running + w.animatedHide(); + if (delay) { + QTest::qWait(overlappingWaitingTime); + } + w.animatedHide(); + + while (w.isHideAnimationRunning()) { + QCOMPARE(hideSignalsSpy.count(), 0); + QTest::qWait(50); + } + + QCOMPARE(hideSignalsSpy.count(), 1); + QVERIFY(! w.isVisible()); +} + +void KMessageWidgetTest::testHideWithNotYetShownParent() +{ + QWidget parent; + KMessageWidget w(QStringLiteral("Hello world"), &parent); + QSignalSpy hideSignalsSpy(&w, &KMessageWidget::hideAnimationFinished); + + // test: call hide while not yet visible + w.animatedHide(); + + while (w.isHideAnimationRunning()) { + QCOMPARE(hideSignalsSpy.count(), 0); + QTest::qWait(50); + } + parent.show(); + + QCOMPARE(hideSignalsSpy.count(), 1); + QVERIFY(! w.isVisible()); } diff --git a/src/kmessagewidget.cpp b/src/kmessagewidget.cpp --- a/src/kmessagewidget.cpp +++ b/src/kmessagewidget.cpp @@ -414,13 +414,20 @@ void KMessageWidget::animatedShow() { + // Test before styleHint, as there might have been a style change while animation was running + if (isHideAnimationRunning()) { + d->timeLine->stop(); + emit hideAnimationFinished(); + } + if (!style()->styleHint(QStyle::SH_Widget_Animate, nullptr, this)) { show(); emit showAnimationFinished(); return; } - if (isVisible()) { + if (isVisible() && (d->timeLine->state() == QTimeLine::NotRunning)) { + emit showAnimationFinished(); return; } @@ -439,13 +446,24 @@ void KMessageWidget::animatedHide() { + // test this before isVisible, as animatedShow might have been called directly before, + // so the first timeline event is not yet done and the widget is still hidden + // And before styleHint, as there might have been a style change while animation was running + if (isShowAnimationRunning()) { + d->timeLine->stop(); + emit showAnimationFinished(); + } + if (!style()->styleHint(QStyle::SH_Widget_Animate, nullptr, this)) { hide(); emit hideAnimationFinished(); return; } if (!isVisible()) { + // explicitely hide it, so it stays hidden in case it is only not visible due to the parents + hide(); + emit hideAnimationFinished(); return; }