diff --git a/lib/abstractimageoperation.cpp b/lib/abstractimageoperation.cpp --- a/lib/abstractimageoperation.cpp +++ b/lib/abstractimageoperation.cpp @@ -52,14 +52,20 @@ mOp->undo(); } + void redo() override + { + mOp->redo(); + } + private: AbstractImageOperation* mOp; }; struct AbstractImageOperationPrivate { QString mText; QUrl mUrl; + ImageOperationCommand* mCommand; }; AbstractImageOperation::AbstractImageOperation() @@ -75,7 +81,11 @@ void AbstractImageOperation::applyToDocument(Document::Ptr doc) { d->mUrl = doc->url(); - redo(); + + d->mCommand = new ImageOperationCommand(this); + d->mCommand->setText(d->mText); + // QUndoStack::push() executes command by calling its redo() function + doc->undoStack()->push(d->mCommand); } Document::Ptr AbstractImageOperation::document() const @@ -88,12 +98,15 @@ void AbstractImageOperation::finish(bool ok) { if (ok) { - ImageOperationCommand* command = new ImageOperationCommand(this); - command->setText(d->mText); - document()->undoStack()->push(command); - document()->imageOperationCompleted(); + // Give QUndoStack time to update in case the redo/undo is executed immediately + // (e.g. undo crop just sets the previous image) + QTimer::singleShot(0, document().data(), &Document::imageOperationCompleted); } else { - deleteLater(); +#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) + // Remove command from undo stack without executing undo() + d->mCommand->setObsolete(true); +#endif + document()->undoStack()->undo(); } } diff --git a/tests/auto/documenttest.h b/tests/auto/documenttest.h --- a/tests/auto/documenttest.h +++ b/tests/auto/documenttest.h @@ -129,6 +129,7 @@ void testJobQueue(); void testCheckDocumentEditor(); void testUndoStackPush(); + void testUndoRedo(); void initTestCase(); void init(); diff --git a/tests/auto/documenttest.cpp b/tests/auto/documenttest.cpp --- a/tests/auto/documenttest.cpp +++ b/tests/auto/documenttest.cpp @@ -534,6 +534,7 @@ QVERIFY(doc->editor()); TestOperation* op = new TestOperation; op->applyToDocument(doc); + QTest::qWait(100); QVERIFY(doc->isModified()); QCOMPARE(modifiedDocumentListChangedSpy.count(), 1); modifiedDocumentListChangedSpy.clear(); @@ -841,3 +842,66 @@ QTest::qWait(100); QVERIFY(doc->undoStack()->isClean()); } + +void DocumentTest::testUndoRedo() +{ + class SuccessOperation : public AbstractImageOperation + { + public: + int mRedoCount = 0; + int mUndoCount = 0; + + protected: + virtual void redo() + { + mRedoCount++; + finish(true); + } + + virtual void undo() + { + mUndoCount++; + finish(true); + } + }; + + Document::Ptr doc = DocumentFactory::instance()->load(urlForTestFile("orient6.jpg")); + QSignalSpy modifiedSpy(doc.data(), &Document::modified); + QSignalSpy savedSpy(doc.data(), &Document::saved); + + SuccessOperation* op = new SuccessOperation; + QCOMPARE(op->mRedoCount, 0); + QCOMPARE(op->mUndoCount, 0); + + // Apply (redo) operation + op->applyToDocument(doc); + QVERIFY(modifiedSpy.wait()); + QCOMPARE(op->mRedoCount, 1); + QCOMPARE(op->mUndoCount, 0); + QCOMPARE(doc->undoStack()->count(), 1); + QVERIFY(!doc->undoStack()->isClean()); + + // Undo operation + doc->undoStack()->undo(); + QVERIFY(savedSpy.wait()); + QCOMPARE(op->mRedoCount, 1); + QCOMPARE(op->mUndoCount, 1); + QCOMPARE(doc->undoStack()->count(), 1); + QVERIFY(doc->undoStack()->isClean()); + + // Redo operation + doc->undoStack()->redo(); + QVERIFY(modifiedSpy.wait()); + QCOMPARE(op->mRedoCount, 2); + QCOMPARE(op->mUndoCount, 1); + QCOMPARE(doc->undoStack()->count(), 1); + QVERIFY(!doc->undoStack()->isClean()); + + // Undo operation again + doc->undoStack()->undo(); + QVERIFY(savedSpy.wait()); + QCOMPARE(op->mRedoCount, 2); + QCOMPARE(op->mUndoCount, 2); + QCOMPARE(doc->undoStack()->count(), 1); + QVERIFY(doc->undoStack()->isClean()); +}