From 2f4d2ea1ca9ed2b224973fff2718d8a8e87ee8d8 Mon Sep 17 00:00:00 2001 From: tetektoza Date: Sat, 11 Nov 2023 01:49:08 +0100 Subject: [PATCH] FrameCmds: Make replace frame option to be undoable/redoable This commit makes "replace frame" option to be undoable, it allows to replace frame and then undo or redo the same action. --- CMakeLists.txt | 1 - source/celview.cpp | 23 ++++++++++++++++++----- source/celview.h | 3 ++- source/framecmds.cpp | 17 +++++++++++++++++ source/framecmds.h | 20 ++++++++++++++++++++ source/levelcelview.cpp | 14 ++++++++++++-- source/levelcelview.h | 4 +++- source/mainwindow.cpp | 4 ++-- 8 files changed, 74 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f26e811..da266d4a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,6 @@ find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets) include_directories(source/) set(PROJECT_SOURCES source/celview.cpp - source/celview.h source/celview.ui source/framecmds.cpp source/config.cpp diff --git a/source/celview.cpp b/source/celview.cpp index cfb6a90e..4463e1d0 100644 --- a/source/celview.cpp +++ b/source/celview.cpp @@ -209,6 +209,9 @@ void CelView::insertFrame(IMAGE_FILE_MODE mode, int index, const QString &imagef QImageReader reader = QImageReader(imagefilePath); int numImages = 0; + // FIXME: this loop should have some sort of a progress bar, we support + // status bar, but if user loads a .gif which could contain up to hundreds + // of frames, loading might take quite a bit while (true) { QImage image = reader.read(); if (image.isNull()) { @@ -224,7 +227,16 @@ void CelView::insertFrame(IMAGE_FILE_MODE mode, int index, const QString &imagef } } -void CelView::replaceCurrentFrame(const QString &imagefilePath) +void CelView::replaceCurrentFrame(int frameIdx, const QImage &image) +{ + this->gfx->replaceFrame(frameIdx, image); + + // update the view + this->initialize(this->gfx); + this->displayFrame(); +} + +void CelView::sendReplaceCurrentFrameCmd(const QString &imagefilePath) { QImage image = QImage(imagefilePath); @@ -232,11 +244,12 @@ void CelView::replaceCurrentFrame(const QString &imagefilePath) return; } - this->gfx->replaceFrame(this->currentFrameIndex, image); + // send a command to undostack, making replacing frame undo/redoable + ReplaceFrameCommand *command = new ReplaceFrameCommand(this->currentFrameIndex, image, this->gfx->getFrameImage(this->currentFrameIndex)); + QObject::connect(command, &ReplaceFrameCommand::replaced, this, &CelView::replaceCurrentFrame); + QObject::connect(command, &ReplaceFrameCommand::undoReplaced, this, &CelView::replaceCurrentFrame); - // update the view - this->initialize(this->gfx); - this->displayFrame(); + undoStack->push(command); } void CelView::sendRemoveFrameCmd() diff --git a/source/celview.h b/source/celview.h index edf87ee3..6baf2633 100644 --- a/source/celview.h +++ b/source/celview.h @@ -58,7 +58,8 @@ class CelView : public QWidget { int getCurrentFrameIndex(); void framePixelClicked(unsigned x, unsigned y); void insertImageFiles(IMAGE_FILE_MODE mode, const QStringList &imagefilePaths, bool append); - void replaceCurrentFrame(const QString &imagefilePath); + void sendReplaceCurrentFrameCmd(const QString &imagefilePath); + void replaceCurrentFrame(int frameIdx, const QImage &image); void removeCurrentFrame(int frameIdx); void regroupFrames(int numGroups); void updateGroupIndex(); diff --git a/source/framecmds.cpp b/source/framecmds.cpp index 4a306952..b7e39d67 100644 --- a/source/framecmds.cpp +++ b/source/framecmds.cpp @@ -15,3 +15,20 @@ void RemoveFrameCommand::redo() // emit this signal which will call LevelCelView/CelView::removeCurrentFrame emit this->removed(frameIndexToRevert); } + +ReplaceFrameCommand::ReplaceFrameCommand(int currentFrameIndex, const QImage imgToReplace, const QImage imgToRestore, QUndoCommand *parent) : + frameIndexToReplace(currentFrameIndex), imgToReplace(imgToReplace), imgToRestore(imgToRestore) +{ +} + +void ReplaceFrameCommand::undo() +{ + emit this->undoReplaced(frameIndexToReplace, imgToRestore); +} + +void ReplaceFrameCommand::redo() +{ + // emit this signal which will call LevelCelView/CelView::replaceCurrentFrame + emit this->replaced(frameIndexToReplace, imgToReplace); +} + diff --git a/source/framecmds.h b/source/framecmds.h index b679d99d..9b2ea26f 100644 --- a/source/framecmds.h +++ b/source/framecmds.h @@ -25,3 +25,23 @@ class RemoveFrameCommand : public QObject, public QUndoCommand { QImage imgToRevert; int frameIndexToRevert = 0; }; + +class ReplaceFrameCommand : public QObject, public QUndoCommand { + Q_OBJECT + +public: + explicit ReplaceFrameCommand(int currentFrameIndex, const QImage imgToReplace, const QImage imgToRestore, QUndoCommand *parent = nullptr); + ~ReplaceFrameCommand() = default; + + void undo() override; + void redo() override; + +signals: + void undoReplaced(int idxToRemove, const QImage imgToRestore); + void replaced(int idxToReplace, const QImage imgToReplace); + +private: + QImage imgToReplace; + QImage imgToRestore; + int frameIndexToReplace = 0; +}; diff --git a/source/levelcelview.cpp b/source/levelcelview.cpp index 3cd85271..83b29728 100644 --- a/source/levelcelview.cpp +++ b/source/levelcelview.cpp @@ -687,7 +687,7 @@ void LevelCelView::insertTiles(IMAGE_FILE_MODE mode, const QStringList &imagefil this->displayFrame(); } -void LevelCelView::replaceCurrentFrame(const QString &imagefilePath) +void LevelCelView::sendReplaceCurrentFrameCmd(const QString &imagefilePath) { QImage image = QImage(imagefilePath); @@ -701,7 +701,17 @@ void LevelCelView::replaceCurrentFrame(const QString &imagefilePath) return; } - D1GfxFrame *frame = this->gfx->replaceFrame(this->currentFrameIndex, image); + // send a command to undostack, making replacing frame undo/redoable + ReplaceFrameCommand *command = new ReplaceFrameCommand(this->currentFrameIndex, image, this->gfx->getFrameImage(this->currentFrameIndex)); + QObject::connect(command, &ReplaceFrameCommand::replaced, this, &LevelCelView::replaceCurrentFrame); + QObject::connect(command, &ReplaceFrameCommand::undoReplaced, this, &LevelCelView::replaceCurrentFrame); + + undoStack->push(command); +} + +void LevelCelView::replaceCurrentFrame(int frameIdx, const QImage &image) +{ + D1GfxFrame *frame = this->gfx->replaceFrame(frameIdx, image); if (frame != nullptr) { LevelTabFrameWidget::selectFrameType(frame); diff --git a/source/levelcelview.h b/source/levelcelview.h index addbb484..5ed9774d 100644 --- a/source/levelcelview.h +++ b/source/levelcelview.h @@ -55,7 +55,9 @@ class LevelCelView : public QWidget { void insertImageFiles(IMAGE_FILE_MODE mode, const QStringList &imagefilePaths, bool append); - void replaceCurrentFrame(const QString &imagefilePath); + void sendReplaceCurrentFrameCmd(const QString &imagefilePath); + void replaceCurrentFrame(int frameIdx, const QImage &image); + void sendRemoveFrameCmd(); void removeCurrentFrame(int idx); diff --git a/source/mainwindow.cpp b/source/mainwindow.cpp index 7cfdc4eb..023693e3 100644 --- a/source/mainwindow.cpp +++ b/source/mainwindow.cpp @@ -1085,10 +1085,10 @@ void MainWindow::on_actionReplace_Frame_triggered() this->ui->statusBar->repaint(); if (this->celView != nullptr) { - this->celView->replaceCurrentFrame(imgFilePath); + this->celView->sendReplaceCurrentFrameCmd(imgFilePath); } if (this->levelCelView != nullptr) { - this->levelCelView->replaceCurrentFrame(imgFilePath); + this->levelCelView->sendReplaceCurrentFrameCmd(imgFilePath); } this->updateWindow();