mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-27 23:37:05 -05:00
fix: Multiple issues with undo/redo stack handling
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user