feat: Added simple interactive help system

This commit is contained in:
WerWolv
2024-06-24 22:53:25 +02:00
parent 3d301c4202
commit e236872af3
8 changed files with 107 additions and 41 deletions

View File

@@ -37,6 +37,7 @@ set(LIBIMHEX_SOURCES
source/helpers/tar.cpp
source/helpers/debugging.cpp
source/helpers/default_paths.cpp
source/helpers/imgui_hooks.cpp
source/test/tests.cpp

View File

@@ -275,6 +275,7 @@ namespace hex {
EVENT_DEF_NO_LOG(EventFrameBegin);
EVENT_DEF_NO_LOG(EventFrameEnd);
EVENT_DEF_NO_LOG(EventSetTaskBarIconState, u32, u32, u32);
EVENT_DEF_NO_LOG(EventImGuiElementRendered, ImGuiID, const std::array<float, 4>&)
EVENT_DEF(RequestAddInitTask, std::string, bool, std::function<bool()>);
EVENT_DEF(RequestAddExitTask, std::string, std::function<bool()>);

View File

@@ -119,6 +119,8 @@ namespace hex {
decltype(m_steps)::iterator m_currentStep, m_latestStep;
};
static void init();
/**
* @brief Gets a list of all tutorials
* @return List of all tutorials
@@ -145,6 +147,10 @@ namespace hex {
*/
static void startTutorial(const UnlocalizedString &unlocalizedName);
static void startHelpHover();
static void addInteractiveHelpText(std::initializer_list<std::variant<Lang, std::string, int>> &&ids, UnlocalizedString unlocalizedString);
static void addInteractiveHelpLink(std::initializer_list<std::variant<Lang, std::string, int>> &&ids, std::string link);
/**
* @brief Draws the tutorial

View File

@@ -21,6 +21,11 @@ namespace hex {
AutoReset<std::map<ImGuiID, std::string>> s_highlights;
AutoReset<std::vector<std::pair<ImRect, std::string>>> s_highlightDisplays;
AutoReset<std::map<ImGuiID, std::function<void()>>> s_interactiveHelpItems;
ImRect s_hoveredRect;
ImGuiID s_hoveredId;
bool s_helpHoverActive = false;
class IDStack {
public:
@@ -56,8 +61,42 @@ namespace hex {
ImVector<ImGuiID> idStack;
};
ImGuiID calculateId(const auto &ids) {
IDStack idStack;
for (const auto &id : ids) {
std::visit(wolv::util::overloaded {
[&idStack](const Lang &id) {
idStack.add(id.get());
},
[&idStack](const auto &id) {
idStack.add(id);
}
}, id);
}
return idStack.get();
}
}
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]);
const auto element = hex::s_highlights->find(id);
if (element != hex::s_highlights->end()) {
hex::s_highlightDisplays->emplace_back(boundingBox, element->second);
}
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;
@@ -72,6 +111,28 @@ namespace hex {
return s_tutorials->try_emplace(unlocalizedName, Tutorial(unlocalizedName, unlocalizedDescription)).first->second;
}
void TutorialManager::startHelpHover() {
TaskManager::doLater([]{
s_helpHoverActive = true;
});
}
void TutorialManager::addInteractiveHelpText(std::initializer_list<std::variant<Lang, std::string, int>> &&ids, UnlocalizedString text) {
auto id = calculateId(ids);
s_interactiveHelpItems->emplace(id, [text = std::move(text)]{
log::info("{}", Lang(text).get());
});
}
void TutorialManager::addInteractiveHelpLink(std::initializer_list<std::variant<Lang, std::string, int>> &&ids, std::string link) {
auto id = calculateId(ids);
s_interactiveHelpItems->emplace(id, [link = std::move(link)]{
hex::openWebpage(link);
});
}
void TutorialManager::startTutorial(const UnlocalizedString &unlocalizedName) {
s_currentTutorial = s_tutorials->find(unlocalizedName);
if (s_currentTutorial == s_tutorials->end())
@@ -81,6 +142,22 @@ namespace hex {
}
void TutorialManager::drawHighlights() {
if (s_helpHoverActive && s_hoveredId != 0) {
ImGui::GetForegroundDrawList()->AddRectFilled(s_hoveredRect.Min, s_hoveredRect.Max, 0x30FFFFFF);
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
s_helpHoverActive = false;
auto it = s_interactiveHelpItems->find(s_hoveredId);
if (it != s_interactiveHelpItems->end()) {
it->second();
}
}
s_hoveredId = 0;
s_hoveredRect = {};
}
for (const auto &[rect, unlocalizedText] : *s_highlightDisplays) {
const auto drawList = ImGui::GetForegroundDrawList();
@@ -241,39 +318,13 @@ namespace hex {
m_onAppear();
for (const auto &[text, ids] : m_highlights) {
IDStack idStack;
for (const auto &id : ids) {
std::visit(wolv::util::overloaded {
[&idStack](const Lang &id) {
idStack.add(id.get());
},
[&idStack](const auto &id) {
idStack.add(id);
}
}, id);
}
s_highlights->emplace(idStack.get(), text);
s_highlights->emplace(calculateId(ids), text);
}
}
void TutorialManager::Tutorial::Step::removeHighlights() const {
for (const auto &[text, ids] : m_highlights) {
IDStack idStack;
for (const auto &id : ids) {
std::visit(wolv::util::overloaded {
[&idStack](const Lang &id) {
idStack.add(id.get());
},
[&idStack](const auto &id) {
idStack.add(id);
}
}, id);
}
s_highlights->erase(idStack.get());
s_highlights->erase(calculateId(ids));
}
}
@@ -368,15 +419,4 @@ namespace hex {
}
}
}
void ImGuiTestEngineHook_ItemAdd(ImGuiContext*, ImGuiID id, const ImRect& bb, const ImGuiLastItemData*) {
const auto element = hex::s_highlights->find(id);
if (element != hex::s_highlights->end()) {
hex::s_highlightDisplays->emplace_back(bb, element->second);
}
}
void ImGuiTestEngineHook_ItemInfo(ImGuiContext*, ImGuiID, const char*, ImGuiItemStatusFlags) {}
void ImGuiTestEngineHook_Log(ImGuiContext*, const char*, ...) {}
const char* ImGuiTestEngine_FindItemDebugLabel(ImGuiContext*, ImGuiID) { return nullptr; }
}

View File

@@ -0,0 +1,15 @@
#include <hex/api/event_manager.hpp>
#include <imgui.h>
#include <imgui_internal.h>
#include <array>
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);
}
void ImGuiTestEngineHook_ItemInfo(ImGuiContext*, ImGuiID, const char*, ImGuiItemStatusFlags) {}
void ImGuiTestEngineHook_Log(ImGuiContext*, const char*, ...) {}
const char* ImGuiTestEngine_FindItemDebugLabel(ImGuiContext*, ImGuiID) { return nullptr; }

View File

@@ -77,6 +77,8 @@ namespace hex {
EventWindowInitialized::post();
EventImHexStartupFinished::post();
TutorialManager::init();
}
Window::~Window() {

View File

@@ -8,6 +8,7 @@
#include <hex/api/project_file_manager.hpp>
#include <hex/api/layout_manager.hpp>
#include <hex/api/achievement_manager.hpp>
#include <hex/api/tutorial_manager.hpp>
#include <hex/helpers/crypto.hpp>
#include <hex/helpers/patches.hpp>

View File

@@ -1527,7 +1527,7 @@ namespace hex::plugin::builtin {
ImGui::TableNextColumn();
ImGuiExt::TextFormatted("{} ", "hex.ui.common.comment"_lang);
ImGui::TableNextColumn();
ImGui::TextWrapped(" \"%s\"", comment.c_str());
ImGuiExt::TextFormattedWrapped(" \"{}\"", comment);
}
ImGui::EndTable();