add Action and ActionsHistory

This commit is contained in:
MihailRis 2025-03-12 01:51:46 +03:00
parent fa990e3939
commit 223a846784

160
src/devtools/actions.hpp Normal file
View File

@ -0,0 +1,160 @@
#pragma once
#include <vector>
#include <memory>
class Action {
public:
virtual ~Action() = default;
virtual void apply() = 0;
virtual void revert() = 0;
};
class InversedAction : public Action {
public:
InversedAction(std::unique_ptr<Action> action) : action(std::move(action)) {}
void apply() override {
action->revert();
}
void revert() override {
action->apply();
}
private:
std::unique_ptr<Action> action;
};
class CombinedAction : public Action {
public:
CombinedAction(std::vector<std::unique_ptr<Action>> actions)
: actions(std::move(actions)) {
}
void apply() override {
for (auto& action : actions) {
action->apply();
}
}
void revert() override {
for (int i = actions.size() - 1; i >= 0; i--) {
actions[i]->revert();
}
}
private:
std::vector<std::unique_ptr<Action>> actions;
};
class ActionsHistory {
public:
ActionsHistory() {};
/// @brief Remove all actions available to redo
void clearRedo() {
if (actionPtr < actions.size()) {
actions.erase(actions.begin() + actionPtr, actions.end());
}
}
/// @brief Store action without applying
void store(std::unique_ptr<Action> action, bool reverse=false) {
if (lock) {
return;
}
if (reverse) {
action = std::make_unique<InversedAction>(std::move(action));
}
clearRedo();
actions.emplace_back(std::move(action));
actionPtr++;
}
/// @brief Apply action and store it
void apply(std::unique_ptr<Action> action) {
if (lock) {
return;
}
clearRedo();
lock = true;
action->apply();
lock = false;
actions.emplace_back(std::move(action));
actionPtr++;
}
/// @brief Revert the last action
/// @return true if any action reverted
bool undo() {
if (lock || actionPtr == 0) {
return false;
}
auto& action = actions[--actionPtr];
lock = true;
action->revert();
lock = false;
return true;
}
/// @brief Revert the last action
/// @return true if any action reapplied
bool redo() {
if (lock || actionPtr == actions.size()) {
return false;
}
auto& action = actions[actionPtr++];
lock = true;
action->apply();
lock = false;
return true;
}
/// @brief Clear history without reverting actions
void clear() {
actionPtr = 0;
actions.clear();
}
/// @brief Squash last n actions into one CombinedAction
/// @param n number of actions to squash
void squash(ptrdiff_t n) {
if (n < 2) {
return;
}
n = std::min(n, static_cast<ptrdiff_t>(actionPtr));
std::vector<std::unique_ptr<Action>> squashing;
for (size_t i = actionPtr - n; i < actionPtr; i++) {
squashing.emplace_back(std::move(actions[i]));
}
actions.erase(actions.begin() + actionPtr - n, actions.end());
actionPtr -= n;
store(std::make_unique<CombinedAction>(std::move(squashing)));
}
size_t size() const {
return actionPtr;
}
/// @brief On destruction squashing actions stored since initialization
struct Combination {
ActionsHistory& history;
size_t historySize;
Combination(ActionsHistory& history)
: history(history), historySize(history.size()) {
}
~Combination() {
history.squash(history.size() - historySize);
}
};
Combination beginCombination() {
return Combination(*this);
}
private:
std::vector<std::unique_ptr<Action>> actions;
size_t actionPtr = 0;
bool lock = false;
};