fix: Multiple issues with undo/redo stack handling

This commit is contained in:
WerWolv
2026-01-17 21:03:36 +01:00
parent 220b5f9772
commit 4a311ed69f
7 changed files with 129 additions and 100 deletions

View File

@@ -10,6 +10,8 @@
#include <hex/ui/imgui_imhex_extensions.h>
struct ImRect;
EXPORT_MODULE namespace hex {
class TutorialManager {
@@ -172,6 +174,8 @@ EXPORT_MODULE namespace hex {
static void setRenderer(std::function<DrawFunction(const std::string &)> renderer);
static void postElementRendered(ImGuiID id, const ImRect &boundingBox);
private:
TutorialManager() = delete;

View File

@@ -7,6 +7,7 @@
#include <map>
#include <memory>
#include <mutex>
#include <vector>
namespace hex::prv {
@@ -40,6 +41,8 @@ namespace hex::prv::undo {
bool add(std::unique_ptr<Operation> &&operation);
static std::recursive_mutex& getMutex();
const std::vector<std::unique_ptr<Operation>> &getAppliedOperations() const {
return m_undoStack;
}

View File

@@ -96,39 +96,6 @@ namespace hex {
}
void TutorialManager::init() {
EventImGuiElementRendered::subscribe([](ImGuiID id, const std::array<float, 4> bb){
const auto boundingBox = ImRect(bb[0], bb[1], bb[2], bb[3]);
if (!ImGui::IsRectVisible(boundingBox.Min, boundingBox.Max))
return;
{
const auto element = hex::s_highlights->find(id);
if (element != hex::s_highlights->end()) {
hex::s_highlightDisplays->emplace_back(boundingBox, element->second);
const auto window = ImGui::GetCurrentWindow();
if (window != nullptr && window->DockNode != nullptr && window->DockNode->TabBar != nullptr)
window->DockNode->TabBar->NextSelectedTabId = window->TabId;
}
}
{
const auto element = s_interactiveHelpItems->find(id);
if (element != s_interactiveHelpItems->end()) {
(*s_interactiveHelpDisplays)[id] = boundingBox;
}
}
if (id != 0 && boundingBox.Contains(ImGui::GetMousePos())) {
if ((s_hoveredRect.GetArea() == 0 || boundingBox.GetArea() < s_hoveredRect.GetArea()) && s_interactiveHelpItems->contains(id)) {
s_hoveredRect = boundingBox;
s_hoveredId = id;
}
}
});
if (*s_renderer == nullptr) {
*s_renderer = [](const std::string &message) {
return [message] {
@@ -141,6 +108,37 @@ namespace hex {
}
}
void TutorialManager::postElementRendered(ImGuiID id, const ImRect &boundingBox) {
if (!ImGui::IsRectVisible(boundingBox.Min, boundingBox.Max))
return;
{
const auto element = hex::s_highlights->find(id);
if (element != hex::s_highlights->end()) {
hex::s_highlightDisplays->emplace_back(boundingBox, element->second);
const auto window = ImGui::GetCurrentWindow();
if (window != nullptr && window->DockNode != nullptr && window->DockNode->TabBar != nullptr)
window->DockNode->TabBar->NextSelectedTabId = window->TabId;
}
}
{
const auto element = s_interactiveHelpItems->find(id);
if (element != s_interactiveHelpItems->end()) {
(*s_interactiveHelpDisplays)[id] = boundingBox;
}
}
if (id != 0 && boundingBox.Contains(ImGui::GetMousePos())) {
if ((s_hoveredRect.GetArea() == 0 || boundingBox.GetArea() < s_hoveredRect.GetArea()) && s_interactiveHelpItems->contains(id)) {
s_hoveredRect = boundingBox;
s_hoveredId = id;
}
}
}
const std::map<std::string, TutorialManager::Tutorial>& TutorialManager::getTutorials() {
return s_tutorials;
}

View File

@@ -3,13 +3,12 @@
#include <imgui.h>
#include <imgui_internal.h>
#include <array>
#include <hex/api/tutorial_manager.hpp>
#if !defined(IMGUI_TEST_ENGINE)
void ImGuiTestEngineHook_ItemAdd(ImGuiContext*, ImGuiID id, const ImRect& bb, const ImGuiLastItemData*) {
std::array<float, 4> boundingBox = { bb.Min.x, bb.Min.y, bb.Max.x, bb.Max.y };
hex::EventImGuiElementRendered::post(id, boundingBox);
hex::TutorialManager::postElementRendered(id, bb);
}
void ImGuiTestEngineHook_ItemInfo(ImGuiContext*, ImGuiID, const char*, ImGuiItemStatusFlags) {}

View File

@@ -12,8 +12,7 @@ namespace hex::prv::undo {
namespace {
std::atomic<bool> s_locked;
std::mutex s_mutex;
std::recursive_mutex s_mutex;
}
@@ -21,12 +20,13 @@ namespace hex::prv::undo {
}
std::recursive_mutex& Stack::getMutex() {
return s_mutex;
}
void Stack::undo(u32 count) {
std::scoped_lock lock(s_mutex);
s_locked = true;
ON_SCOPE_EXIT { s_locked = false; };
std::lock_guard lock(s_mutex);
// If there are no operations, we can't undo anything.
if (m_undoStack.empty())
@@ -42,14 +42,12 @@ namespace hex::prv::undo {
m_redoStack.emplace_back(std::move(m_undoStack.back()));
m_redoStack.back()->undo(m_provider);
m_undoStack.pop_back();
EventDataChanged::post(m_provider);
}
}
void Stack::redo(u32 count) {
std::scoped_lock lock(s_mutex);
s_locked = true;
ON_SCOPE_EXIT { s_locked = false; };
std::lock_guard lock(s_mutex);
// If there are no operations, we can't redo anything.
if (m_redoStack.empty())
@@ -65,10 +63,13 @@ namespace hex::prv::undo {
m_undoStack.emplace_back(std::move(m_redoStack.back()));
m_undoStack.back()->redo(m_provider);
m_redoStack.pop_back();
EventDataChanged::post(m_provider);
}
}
void Stack::groupOperations(u32 count, const UnlocalizedString &unlocalizedName) {
std::lock_guard lock(s_mutex);
if (count <= 1)
return;
@@ -94,14 +95,19 @@ namespace hex::prv::undo {
}
void Stack::apply(const Stack &otherStack) {
std::lock_guard lock(s_mutex);
for (const auto &operation : otherStack.m_undoStack) {
this->add(operation->clone());
}
}
void Stack::reapply() {
std::lock_guard lock(s_mutex);
for (const auto &operation : m_undoStack) {
operation->redo(m_provider);
EventDataChanged::post(m_provider);
}
}
@@ -109,14 +115,7 @@ namespace hex::prv::undo {
bool Stack::add(std::unique_ptr<Operation> &&operation) {
// If we're already inside of an undo/redo operation, ignore new operations being added
if (s_locked)
return false;
s_locked = true;
ON_SCOPE_EXIT { s_locked = false; };
std::scoped_lock lock(s_mutex);
std::lock_guard lock(s_mutex);
// Clear the redo stack
m_redoStack.clear();
@@ -133,10 +132,14 @@ namespace hex::prv::undo {
}
bool Stack::canUndo() const {
std::lock_guard lock(s_mutex);
return !m_undoStack.empty();
}
bool Stack::canRedo() const {
std::lock_guard lock(s_mutex);
return !m_redoStack.empty();
}