Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Undostack: Introduce UndoMacro #143

Merged
merged 3 commits into from
Feb 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ set(PROJECT_SOURCES
source/undostack/undostack.h
source/undostack/command.cpp
source/undostack/command.h
source/undostack/undomacro.cpp
source/undostack/undomacro.h
)

if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
Expand Down
41 changes: 40 additions & 1 deletion source/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ MainWindow::MainWindow()
this->redoAction = new QAction("Redo", this);
this->redoAction->setShortcuts(QKeySequence::Redo);
this->ui->menuEdit->addAction(this->redoAction);

// Initialize widgets used for Undo/Redo
this->m_progressDialog = std::make_unique<QProgressDialog>(this);
this->m_progressDialog->close();
this->m_progressDialog->setAutoReset(true);
this->m_progressDialog->reset();
this->m_progressDialog->setWindowModality(Qt::WindowModal);
this->ui->menuEdit->addSeparator();

this->ui->menuEdit->addAction(this->undoAction);
Expand Down Expand Up @@ -748,7 +755,7 @@ void MainWindow::openPalFiles(QStringList filePaths, PaletteWidget *widget)
this->palWidget->selectPath(firstFound);
}
} else if (widget == this->trnUniqueWidget) {
for (QString path : filePaths) {
for (const QString &path : filePaths) {
if (this->loadTrnUnique(path) && firstFound.isEmpty()) {
firstFound = path;
}
Expand Down Expand Up @@ -1681,6 +1688,38 @@ void MainWindow::on_actionClose_Translation_triggered()
this->trnWidget->selectPath(D1Trn::IDENTITY_PATH);
}

void MainWindow::setupUndoMacroWidget(std::unique_ptr<UserData> &userData, enum OperationType opType)
{
this->m_currMacroOpType = opType;
int prevMaximum = this->m_progressDialog->maximum();

this->m_progressDialog->reset();
this->m_progressDialog->show();
this->m_progressDialog->setLabelText(userData->labelText());
this->m_progressDialog->setCancelButtonText(userData->cancelButtonText());
this->m_progressDialog->setMinimum(userData->min());
this->m_progressDialog->setMaximum(userData->max());

if (opType == OperationType::Undo && prevMaximum == userData->max())
this->m_currProgDialogPos = this->m_progressDialog->maximum() - m_currProgDialogPos;
else if (this->m_currProgDialogPos == prevMaximum || prevMaximum != userData->max())
this->m_currProgDialogPos = 0;
}

void MainWindow::updateUndoMacroWidget(bool &result)
{
this->m_currProgDialogPos++;

this->m_progressDialog->setValue(m_currProgDialogPos);
if (this->m_progressDialog->wasCanceled()) {
this->m_progressDialog->reset();
result = true;
return;
}

QCoreApplication::processEvents();
}

#if defined(Q_OS_WIN)
#define OS_TYPE "Windows"
#elif defined(Q_OS_QNX)
Expand Down
9 changes: 9 additions & 0 deletions source/mainwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ public slots:
void actionReplaceTile_triggered();
void actionDelTile_triggered();

// slots used for UndoMacro signals
void setupUndoMacroWidget(std::unique_ptr<UserData> &userData, enum OperationType opType);
void updateUndoMacroWidget(bool &result);

void buildRecentFilesMenu();
void addRecentFile(QString filePath);
void on_actionClear_History_triggered();
Expand Down Expand Up @@ -197,6 +201,11 @@ private slots:
QMap<QString, D1Trn *> trns; // key: path, value: pointer to translation
QMap<QString, D1Trn *> trnsUnique; // key: path, value: pointer to translation

std::unique_ptr<QProgressDialog> m_progressDialog;

// Palette hits are instantiated in main window to make them available to the three PaletteWidgets
QPointer<D1PalHits> palHits;

int m_currProgDialogPos { 0 };
enum OperationType m_currMacroOpType;
};
10 changes: 10 additions & 0 deletions source/undostack/command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,13 @@ bool Command::isObsolete() const
{
return m_isObsolete;
}

void Command::setMacroID(unsigned int macroID)
{
m_macroID = macroID;
}

unsigned int Command::macroID() const
{
return m_macroID;
}
3 changes: 3 additions & 0 deletions source/undostack/command.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ class Command {

void setObsolete(bool isObsolete);
bool isObsolete() const;
void setMacroID(unsigned int macroID);
unsigned int macroID() const;

virtual ~Command() = default;

private:
unsigned int m_macroID { 0 };
bool m_isObsolete = false;
};
31 changes: 5 additions & 26 deletions source/undostack/framecmds.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,39 +40,18 @@ void ReplaceFrameCommand::redo()
emit this->replaced(frameIndexToReplace, imgToReplace);
}

AddFrameCommand::AddFrameCommand(IMAGE_FILE_MODE mode, int index, const QString imagefilePath)
: startingIndex(index)
, mode(mode)
AddFrameCommand::AddFrameCommand(int index, QImage &img)
: m_index(index)
, m_image(std::move(img))
{
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()) {
break;
}

images.emplace_back(image);
numImages++;
}

if (mode != IMAGE_FILE_MODE::AUTO && numImages == 0) {
throw std::exception();
}

endingIndex = startingIndex + numImages;
}

void AddFrameCommand::undo()
{
emit this->undoAdded(startingIndex, endingIndex);
emit this->undoAdded(m_index);
}

void AddFrameCommand::redo()
{
emit this->added(startingIndex, images, mode);
emit this->added(m_index, m_image);
}
12 changes: 5 additions & 7 deletions source/undostack/framecmds.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,17 @@ class AddFrameCommand : public QObject, public Command {
Q_OBJECT

public:
explicit AddFrameCommand(IMAGE_FILE_MODE mode, int index, const QString imagefilePath);
explicit AddFrameCommand(int index, QImage &image);
~AddFrameCommand() = default;

void undo() override;
void redo() override;

signals:
void undoAdded(int startingIndex, int endingIndex);
void added(int startingIndex, const std::vector<QImage> &images, IMAGE_FILE_MODE mode);
void undoAdded(int index);
void added(int index, const QImage &image);

private:
std::vector<QImage> images;
int startingIndex = 0;
int endingIndex = 0;
IMAGE_FILE_MODE mode;
QImage m_image;
int m_index = 0;
};
60 changes: 60 additions & 0 deletions source/undostack/undomacro.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#include "undomacro.h"

#include <utility>

UserData::UserData(QString labelText, QString cancelButtonText, std::pair<int, int> &&minMax)
: m_labelText(std::move(labelText))
, m_cancelButtonText(std::move(cancelButtonText))
, m_minMax(minMax)
{
}

UserData::UserData(QString labelText, QString cancelButtonText)
: m_labelText(std::move(labelText))
, m_cancelButtonText(std::move(cancelButtonText))
, m_minMax({ 0, 0 })
{
}

UndoMacroFactory::UndoMacroFactory(UserData &&userData)
: m_userData(std::make_unique<UserData>(userData.labelText(), userData.cancelButtonText(), std::make_pair(userData.min(), userData.max())))
{
}

void UndoMacroFactory::setUserData(const UserData &&userData)
{
m_userData = std::make_unique<UserData>(userData.labelText(), userData.cancelButtonText(), std::make_pair(userData.min(), userData.max()));
}

void UndoMacroFactory::add(std::unique_ptr<Command> cmd)
{
m_commands.push_back(std::move(cmd));
}

UndoMacro::UndoMacro(std::unique_ptr<UserData> userData, std::pair<int, int> rangeIdxs)
: m_userData(std::move(userData))
, m_rangeIdxs(rangeIdxs)
{
}

UndoMacro::UndoMacro(UndoMacro &&undoMacro) noexcept
: m_userData(std::move(undoMacro.m_userData))
, m_rangeIdxs(undoMacro.m_rangeIdxs)
{
}

UndoMacro &UndoMacro::operator=(UndoMacro &&undoMacro) noexcept
{
m_userData = std::move(undoMacro.m_userData);
m_rangeIdxs = undoMacro.m_rangeIdxs;
return *this;
}

void UndoMacro::setLastIndex(int index)
{
m_rangeIdxs.second = index;
/* TODO: later on, if/when we will implement more types of macros, we could use some update method of UserData
* instead of using setters directly
*/
m_userData->setMax((m_rangeIdxs.second - m_rangeIdxs.first) + 1);
}
93 changes: 93 additions & 0 deletions source/undostack/undomacro.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#pragma once

#include "command.h"
#include <QString>
#include <memory>
#include <string_view>
#include <vector>

class UserData {
private:
QString m_labelText;
QString m_cancelButtonText;
std::pair<int, int> m_minMax;

public:
UserData(QString labelText, QString cancelButtonText, std::pair<int, int> &&minMax);
UserData(QString labelText, QString cancelButtonText);
~UserData() = default;

[[nodiscard]] int min() const
{
return m_minMax.first;
}
[[nodiscard]] int max() const
{
return m_minMax.second;
}
[[nodiscard]] const QString &labelText() const
{
return m_labelText;
}
[[nodiscard]] const QString &cancelButtonText() const
{
return m_cancelButtonText;
}
void setMin(int min)
{
m_minMax.first = min;
}
void setMax(int max)
{
m_minMax.second = max;
}
};

class UndoMacroFactory {
private:
std::unique_ptr<UserData> m_userData;
std::vector<std::unique_ptr<Command>> m_commands;

public:
UndoMacroFactory(UserData &&userData);
UndoMacroFactory() = default;
~UndoMacroFactory() = default;

void setUserData(const UserData &&userData);

void add(std::unique_ptr<Command> cmd);
[[nodiscard]] std::vector<std::unique_ptr<Command>> &cmds()
{
return m_commands;
};
[[nodiscard]] std::unique_ptr<UserData> &userdata()
{
return m_userData;
};
};

class UndoMacro {
private:
std::unique_ptr<UserData> m_userData;
std::pair<int, int> m_rangeIdxs;

public:
UndoMacro(std::unique_ptr<UserData> userData, std::pair<int, int> rangeIdxs);
UndoMacro(UndoMacro &&undoMacro) noexcept;
UndoMacro &operator=(UndoMacro &&undoMacro) noexcept;
~UndoMacro() = default;

[[nodiscard]] std::unique_ptr<UserData> &userdata()
{
return m_userData;
};
[[nodiscard]] int beginIndex() const
{
return m_rangeIdxs.first;
}
[[nodiscard]] int lastIndex() const
{
return m_rangeIdxs.second;
}
void setLastIndex(int index);
};
Loading
Loading