feat: Implement better and more complete undo/redo stack (#1433)

This PR aims to implement a more complete undo/redo stack that, unlike
the old one, also supports undoing insertions, deletions and resize
operations
This commit is contained in:
Nik
2023-11-25 12:43:48 +01:00
committed by GitHub
parent e5f36ca08d
commit 7e660450ed
36 changed files with 904 additions and 325 deletions

View File

@@ -57,7 +57,7 @@ namespace hex::plugin::builtin {
}
[[nodiscard]] ImVec2 getMaxSize() const override {
return scaled({ 600, 300 });
return scaled({ 600, 600 });
}
private:

View File

@@ -20,12 +20,9 @@ namespace hex::plugin::builtin {
[[nodiscard]] bool isResizable() const override;
[[nodiscard]] bool isSavable() const override;
void read(u64 offset, void *buffer, size_t size, bool overlays) override;
void write(u64 offset, const void *buffer, size_t size) override;
void resize(size_t newSize) override;
void insert(u64 offset, size_t size) override;
void remove(u64 offset, size_t size) override;
void resizeRaw(size_t newSize) override;
void insertRaw(u64 offset, size_t size) override;
void removeRaw(u64 offset, size_t size) override;
void readRaw(u64 offset, void *buffer, size_t size) override;
void writeRaw(u64 offset, const void *buffer, size_t size) override;

View File

@@ -24,9 +24,9 @@ namespace hex::plugin::builtin {
void writeRaw(u64 offset, const void *buffer, size_t size) override;
[[nodiscard]] size_t getActualSize() const override { return this->m_data.size(); }
void resize(size_t newSize) override;
void insert(u64 offset, size_t size) override;
void remove(u64 offset, size_t size) override;
void resizeRaw(size_t newSize) override;
void insertRaw(u64 offset, size_t size) override;
void removeRaw(u64 offset, size_t size) override;
void save() override;

View File

@@ -0,0 +1,46 @@
#pragma once
#include <hex/providers/undo_redo/operations/operation.hpp>
#include <hex/helpers/fmt.hpp>
namespace hex::plugin::builtin::undo {
class OperationBookmark : public prv::undo::Operation {
public:
explicit OperationBookmark(ImHexApi::Bookmarks::Entry entry) :
m_entry(std::move(entry)) { }
void undo(prv::Provider *provider) override {
hex::unused(provider);
ImHexApi::Bookmarks::remove(this->m_entry.id);
}
void redo(prv::Provider *provider) override {
hex::unused(provider);
auto &[region, name, comment, color, locked, id] = this->m_entry;
id = ImHexApi::Bookmarks::add(region, name, comment, color);
}
[[nodiscard]] std::string format() const override {
return hex::format("Bookmark {} created", this->m_entry.name);
}
std::unique_ptr<Operation> clone() const override {
return std::make_unique<OperationBookmark>(*this);
}
[[nodiscard]] Region getRegion() const override {
return this->m_entry.region;
}
bool shouldHighlight() const override { return false; }
private:
ImHexApi::Bookmarks::Entry m_entry;
};
}

View File

@@ -0,0 +1,40 @@
#pragma once
#include <hex/providers/undo_redo/operations/operation.hpp>
#include <hex/helpers/fmt.hpp>
#include <hex/helpers/utils.hpp>
namespace hex::plugin::builtin::undo {
class OperationInsert : public prv::undo::Operation {
public:
OperationInsert(u64 offset, u64 size) :
m_offset(offset), m_size(size) { }
void undo(prv::Provider *provider) override {
provider->removeRaw(this->m_offset, this->m_size);
}
void redo(prv::Provider *provider) override {
provider->insertRaw(this->m_offset, this->m_size);
}
[[nodiscard]] std::string format() const override {
return hex::format("hex.builtin.undo_operation.insert"_lang, hex::toByteString(this->m_size), this->m_offset);
}
std::unique_ptr<Operation> clone() const override {
return std::make_unique<OperationInsert>(*this);
}
[[nodiscard]] Region getRegion() const override {
return { this->m_offset, this->m_size };
}
private:
u64 m_offset;
u64 m_size;
};
}

View File

@@ -0,0 +1,48 @@
#pragma once
#include <hex/providers/undo_redo/operations/operation.hpp>
#include <hex/helpers/fmt.hpp>
#include <hex/helpers/utils.hpp>
namespace hex::plugin::builtin::undo {
class OperationRemove : public prv::undo::Operation {
public:
OperationRemove(u64 offset, u64 size) :
m_offset(offset), m_size(size) { }
void undo(prv::Provider *provider) override {
provider->insertRaw(this->m_offset, this->m_size);
provider->writeRaw(this->m_offset, this->m_removedData.data(), this->m_removedData.size());
}
void redo(prv::Provider *provider) override {
this->m_removedData.resize(this->m_size);
provider->readRaw(this->m_offset, this->m_removedData.data(), this->m_removedData.size());
provider->removeRaw(this->m_offset, this->m_size);
}
[[nodiscard]] std::string format() const override {
return hex::format("hex.builtin.undo_operation.remove"_lang, hex::toByteString(this->m_size), this->m_offset);
}
std::unique_ptr<Operation> clone() const override {
return std::make_unique<OperationRemove>(*this);
}
[[nodiscard]] Region getRegion() const override {
return { this->m_offset, this->m_size };
}
bool shouldHighlight() const override { return false; }
private:
u64 m_offset;
u64 m_size;
std::vector<u8> m_removedData;
};
}

View File

@@ -0,0 +1,49 @@
#pragma once
#include <hex/helpers/crypto.hpp>
#include <hex/providers/undo_redo/operations/operation.hpp>
#include <hex/helpers/fmt.hpp>
#include <hex/helpers/utils.hpp>
namespace hex::plugin::builtin::undo {
class OperationWrite : public prv::undo::Operation {
public:
OperationWrite(u64 offset, u64 size, const u8 *oldData, const u8 *newData) :
m_offset(offset),
m_oldData(oldData, oldData + size),
m_newData(newData, newData + size) { }
void undo(prv::Provider *provider) override {
provider->writeRaw(this->m_offset, this->m_oldData.data(), this->m_oldData.size());
}
void redo(prv::Provider *provider) override {
provider->writeRaw(this->m_offset, this->m_newData.data(), this->m_newData.size());
}
[[nodiscard]] std::string format() const override {
return hex::format("hex.builtin.undo_operation.write"_lang, hex::toByteString(this->m_newData.size()), this->m_offset);
}
std::vector<std::string> formatContent() const override {
return {
hex::format("{} {} {}", hex::crypt::encode16(this->m_oldData), ICON_VS_ARROW_RIGHT, hex::crypt::encode16(this->m_newData)),
};
}
std::unique_ptr<Operation> clone() const override {
return std::make_unique<OperationWrite>(*this);
}
[[nodiscard]] Region getRegion() const override {
return { this->m_offset, this->m_oldData.size() };
}
private:
u64 m_offset;
std::vector<u8> m_oldData, m_newData;
};
}

View File

@@ -53,10 +53,10 @@ namespace hex::plugin::builtin {
[[nodiscard]] bool open() override { return true; }
void close() override { }
void resize(size_t newSize) override {
void resizeRaw(size_t newSize) override {
this->m_size = newSize;
}
void insert(u64 offset, size_t size) override {
void insertRaw(u64 offset, size_t size) override {
if (this->m_provider == nullptr)
return;
@@ -64,7 +64,7 @@ namespace hex::plugin::builtin {
this->m_provider->insert(offset + this->m_startAddress, size);
}
void remove(u64 offset, size_t size) override {
void removeRaw(u64 offset, size_t size) override {
if (this->m_provider == nullptr)
return;

View File

@@ -23,6 +23,7 @@ namespace hex::plugin::builtin {
std::list<ImHexApi::Bookmarks::Entry>::iterator m_dragStartIterator;
PerProvider<std::list<ImHexApi::Bookmarks::Entry>> m_bookmarks;
PerProvider<u64> m_currBookmarkId;
};
}

View File

@@ -16,7 +16,7 @@ namespace hex::plugin::builtin {
private:
u64 m_selectedPatch = 0x00;
PerProvider<u32> m_numPatches;
PerProvider<u32> m_numOperations;
};
}