feat: Added basic introduction tutorial
This commit is contained in:
parent
346f1362c6
commit
f000b6bc0a
@ -32,7 +32,10 @@
|
||||
|
||||
/* Forward declarations */
|
||||
struct GLFWwindow;
|
||||
namespace hex { class Achievement; }
|
||||
namespace hex {
|
||||
class Achievement;
|
||||
class View;
|
||||
}
|
||||
|
||||
|
||||
namespace hex {
|
||||
@ -248,6 +251,7 @@ namespace hex {
|
||||
EVENT_DEF(EventImHexClosing);
|
||||
EVENT_DEF(EventAchievementUnlocked, const Achievement&);
|
||||
EVENT_DEF(EventSearchBoxClicked);
|
||||
EVENT_DEF(EventViewOpened, View*);
|
||||
|
||||
EVENT_DEF(EventProviderDataModified, prv::Provider *, u64, u64, const u8*);
|
||||
EVENT_DEF(EventProviderDataInserted, prv::Provider *, u64, u64);
|
||||
@ -269,6 +273,7 @@ namespace hex {
|
||||
EVENT_DEF(RequestAddBookmark, Region, std::string, std::string, color_t, u64*);
|
||||
EVENT_DEF(RequestRemoveBookmark, u64);
|
||||
EVENT_DEF(RequestSetPatternLanguageCode, std::string);
|
||||
EVENT_DEF(RequestRunPatternCode);
|
||||
EVENT_DEF(RequestLoadPatternLanguageFile, std::fs::path);
|
||||
EVENT_DEF(RequestSavePatternLanguageFile, std::fs::path);
|
||||
EVENT_DEF(RequestUpdateWindowTitle);
|
||||
|
@ -61,6 +61,9 @@ namespace hex {
|
||||
*/
|
||||
Step& allowSkip();
|
||||
|
||||
Step& onAppear(std::function<void()> callback);
|
||||
Step& onComplete(std::function<void()> callback);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Checks if this step is the current step
|
||||
@ -76,7 +79,7 @@ namespace hex {
|
||||
private:
|
||||
struct Highlight {
|
||||
std::string unlocalizedText;
|
||||
ImGuiID highlightId;
|
||||
std::vector<std::variant<Lang, std::string, int>> highlightIds;
|
||||
};
|
||||
|
||||
struct Message {
|
||||
@ -97,6 +100,7 @@ namespace hex {
|
||||
Tutorial *m_parent;
|
||||
std::vector<Highlight> m_highlights;
|
||||
std::optional<Message> m_message;
|
||||
std::function<void()> m_onAppear, m_onComplete;
|
||||
};
|
||||
|
||||
Step& addStep();
|
||||
|
@ -92,6 +92,8 @@ namespace hex {
|
||||
[[nodiscard]] bool didWindowJustOpen();
|
||||
void setWindowJustOpened(bool state);
|
||||
|
||||
void trackViewOpenState();
|
||||
|
||||
static void discardNavigationRequests();
|
||||
|
||||
[[nodiscard]] static std::string toWindowName(const std::string &unlocalizedName);
|
||||
@ -104,7 +106,7 @@ namespace hex {
|
||||
|
||||
private:
|
||||
std::string m_unlocalizedViewName;
|
||||
bool m_windowOpen = false;
|
||||
bool m_windowOpen = false, m_prevWindowOpen = false;
|
||||
std::map<Shortcut, ShortcutManager::ShortcutEntry> m_shortcuts;
|
||||
bool m_windowJustOpened = false;
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <hex/api/tutorial_manager.hpp>
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
#include <hex/api/localization_manager.hpp>
|
||||
#include <hex/api/task_manager.hpp>
|
||||
|
||||
#include <imgui_internal.h>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
@ -91,15 +92,23 @@ namespace hex {
|
||||
|
||||
{
|
||||
if (!unlocalizedText.empty()) {
|
||||
const auto margin = ImGui::GetStyle().WindowPadding;
|
||||
const auto mainWindowPos = ImHexApi::System::getMainWindowPosition();
|
||||
const auto mainWindowSize = ImHexApi::System::getMainWindowSize();
|
||||
|
||||
const ImVec2 windowPos = { rect.Min.x + 20_scaled, rect.Max.y + 10_scaled };
|
||||
const auto margin = ImGui::GetStyle().WindowPadding;
|
||||
|
||||
ImVec2 windowPos = { rect.Min.x + 20_scaled, rect.Max.y + 10_scaled };
|
||||
ImVec2 windowSize = { std::max<float>(rect.Max.x - rect.Min.x - 40_scaled, 300_scaled), 0 };
|
||||
|
||||
const char* text = Lang(unlocalizedText);
|
||||
const auto textSize = ImGui::CalcTextSize(text, nullptr, false, windowSize.x - margin.x * 2);
|
||||
windowSize.y = textSize.y + margin.y * 2;
|
||||
|
||||
if (windowPos.y + windowSize.y > mainWindowPos.y + mainWindowSize.y)
|
||||
windowPos.y = rect.Min.y - windowSize.y - 10_scaled;
|
||||
if (windowPos.y < mainWindowPos.y)
|
||||
windowPos.y = mainWindowPos.y + 10_scaled;
|
||||
|
||||
drawList->AddRectFilled(windowPos, windowPos + windowSize, ImGui::GetColorU32(ImGuiCol_WindowBg) | 0xFF000000);
|
||||
drawList->AddRect(windowPos, windowPos + windowSize, ImGui::GetColorU32(ImGuiCol_Border));
|
||||
drawList->AddText(nullptr, 0.0F, windowPos + margin, ImGui::GetColorU32(ImGuiCol_Text), text, nullptr, windowSize.x - margin.x * 2);
|
||||
@ -170,7 +179,7 @@ namespace hex {
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginDisabled(s_currentTutorial->second.m_currentStep == s_currentTutorial->second.m_steps.end() || (!message->allowSkip && s_currentTutorial->second.m_currentStep == s_currentTutorial->second.m_latestStep));
|
||||
ImGui::BeginDisabled(!message->allowSkip && s_currentTutorial->second.m_currentStep == s_currentTutorial->second.m_latestStep);
|
||||
if (ImGui::ArrowButton("Forwards", ImGuiDir_Right)) {
|
||||
s_currentTutorial->second.m_currentStep->advance(1);
|
||||
}
|
||||
@ -221,19 +230,51 @@ namespace hex {
|
||||
}
|
||||
|
||||
void TutorialManager::Tutorial::Step::addHighlights() const {
|
||||
for (const auto &[text, id] : this->m_highlights) {
|
||||
s_highlights.emplace(id, text.c_str());
|
||||
if (this->m_onAppear)
|
||||
this->m_onAppear();
|
||||
|
||||
for (const auto &[text, ids] : this->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.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void TutorialManager::Tutorial::Step::removeHighlights() const {
|
||||
for (const auto &[text, id] : this->m_highlights) {
|
||||
s_highlights.erase(id);
|
||||
for (const auto &[text, ids] : this->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());
|
||||
}
|
||||
}
|
||||
|
||||
void TutorialManager::Tutorial::Step::advance(i32 steps) const {
|
||||
m_parent->m_currentStep->removeHighlights();
|
||||
|
||||
if (m_parent->m_currentStep == m_parent->m_latestStep && steps > 0)
|
||||
std::advance(m_parent->m_latestStep, steps);
|
||||
std::advance(m_parent->m_currentStep, steps);
|
||||
|
||||
if (m_parent->m_currentStep != m_parent->m_steps.end())
|
||||
@ -244,22 +285,9 @@ namespace hex {
|
||||
|
||||
|
||||
TutorialManager::Tutorial::Step& TutorialManager::Tutorial::Step::addHighlight(const std::string& unlocalizedText, std::initializer_list<std::variant<Lang, std::string, int>>&& 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);
|
||||
}
|
||||
|
||||
this->m_highlights.emplace_back(
|
||||
unlocalizedText,
|
||||
idStack.get()
|
||||
ids
|
||||
);
|
||||
|
||||
return *this;
|
||||
@ -297,6 +325,19 @@ namespace hex {
|
||||
return *this;
|
||||
}
|
||||
|
||||
TutorialManager::Tutorial::Step& TutorialManager::Tutorial::Step::onAppear(std::function<void()> callback) {
|
||||
this->m_onAppear = std::move(callback);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
TutorialManager::Tutorial::Step& TutorialManager::Tutorial::Step::onComplete(std::function<void()> callback) {
|
||||
this->m_onComplete = std::move(callback);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool TutorialManager::Tutorial::Step::isCurrent() const {
|
||||
@ -311,7 +352,12 @@ namespace hex {
|
||||
void TutorialManager::Tutorial::Step::complete() const {
|
||||
if (this->isCurrent()) {
|
||||
this->advance();
|
||||
this->m_parent->m_latestStep = this->m_parent->m_currentStep;
|
||||
|
||||
if (this->m_onComplete) {
|
||||
TaskManager::doLater([this] {
|
||||
this->m_onComplete();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,6 +59,13 @@ namespace hex {
|
||||
this->m_windowJustOpened = state;
|
||||
}
|
||||
|
||||
void View::trackViewOpenState() {
|
||||
if (this->m_windowOpen && !this->m_prevWindowOpen)
|
||||
this->setWindowJustOpened(true);
|
||||
this->m_prevWindowOpen = this->m_windowOpen;
|
||||
}
|
||||
|
||||
|
||||
void View::discardNavigationRequests() {
|
||||
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows))
|
||||
ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NavEnableKeyboard;
|
||||
|
@ -789,6 +789,7 @@ namespace hex {
|
||||
|
||||
// Draw view
|
||||
view->draw();
|
||||
view->trackViewOpenState();
|
||||
|
||||
if (view->getWindowOpenState()) {
|
||||
auto window = ImGui::FindWindowByName(view->getName().c_str());
|
||||
@ -806,6 +807,7 @@ namespace hex {
|
||||
// Dock the window if it's not already docked
|
||||
if (view->didWindowJustOpen() && !ImGui::IsWindowDocked()) {
|
||||
ImGui::DockBuilderDockWindow(windowName.c_str(), ImHexApi::System::getMainDockSpaceId());
|
||||
EventViewOpened::post(view.get());
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
|
@ -82,6 +82,9 @@ add_imhex_plugin(
|
||||
source/content/tools/tcp_client_server.cpp
|
||||
source/content/tools/wiki_explainer.cpp
|
||||
|
||||
source/content/tutorials/tutorials.cpp
|
||||
source/content/tutorials/introduction.cpp
|
||||
|
||||
source/content/pl_visualizers/line_plot.cpp
|
||||
source/content/pl_visualizers/scatter_plot.cpp
|
||||
source/content/pl_visualizers/image.cpp
|
||||
|
@ -17,7 +17,7 @@ namespace hex::plugin::builtin {
|
||||
[[nodiscard]] bool hasViewMenuItemEntry() const override { return false; }
|
||||
|
||||
ImVec2 getMinSize() const override {
|
||||
return scaled({ 400, 300 });
|
||||
return scaled({ 600, 400 });
|
||||
}
|
||||
|
||||
ImVec2 getMaxSize() const override {
|
||||
|
@ -740,6 +740,18 @@
|
||||
"hex.builtin.tools.wiki_explain.invalid_response": "Invalid response from Wikipedia!",
|
||||
"hex.builtin.tools.wiki_explain.results": "Results",
|
||||
"hex.builtin.tools.wiki_explain.search": "Search",
|
||||
"hex.builtin.tutorial.introduction": "Introduction to ImHex",
|
||||
"hex.builtin.tutorial.introduction.description": "This tutorial will guide you through the basic usage of ImHex to get you started.",
|
||||
"hex.builtin.tutorial.introduction.step1.title": "Welcome to ImHex!",
|
||||
"hex.builtin.tutorial.introduction.step1.description": "ImHex is a Reverse Engineering Suite and Hex Editor with its main focus on visualizing binary data for easy comprehension.\n\nYou can continue to the next step by clicking the right arrow button below.",
|
||||
"hex.builtin.tutorial.introduction.step2.title": "Opening Data",
|
||||
"hex.builtin.tutorial.introduction.step2.description": "ImHex supports loading data from a variety of sources. This includes Files, Raw disks, another Process's memory and more.\n\nAll these options can be found on the Welcome screen or under the File menu.",
|
||||
"hex.builtin.tutorial.introduction.step2.highlight": "Let's create a new empty file by clicking on the 'New File' button.",
|
||||
"hex.builtin.tutorial.introduction.step3.highlight": "This is the Hex Editor. It displays the individual bytes of the loaded data and also allows you to edit them by double clicking one.\n\nYou can navigate the data by using the arrow keys or the mouse wheel.",
|
||||
"hex.builtin.tutorial.introduction.step4.highlight": "This is the Data Inspector. It displays the data of the currently selected bytes in a more readable format.\n\nYou can also edit the data here by double clicking on a row.",
|
||||
"hex.builtin.tutorial.introduction.step5.highlight.pattern_editor": "This is the Pattern Editor. It allows you to write code using the Pattern Language which can highlight and decode binary data structures inside of your loaded data.\n\nYou can learn more about the Pattern Language in the documentation.",
|
||||
"hex.builtin.tutorial.introduction.step5.highlight.pattern_data": "This view contains a tree view representing the data structures you defined using the Pattern Language.",
|
||||
"hex.builtin.tutorial.introduction.step6.highlight": "You can find more tutorials and documentation in the Help menu.",
|
||||
"hex.builtin.undo_operation.insert": "Inserted {0}",
|
||||
"hex.builtin.undo_operation.remove": "Removed {0}",
|
||||
"hex.builtin.undo_operation.write": "Wrote {0}",
|
||||
@ -1180,6 +1192,7 @@
|
||||
"hex.builtin.popup.safety_backup.report_error": "Send crash log to developers",
|
||||
"hex.builtin.popup.safety_backup.restore": "Yes, Restore",
|
||||
"hex.builtin.popup.safety_backup.title": "Restore lost data",
|
||||
"hex.builtin.popup.play_tutorial.desc": "As this is your first time launching ImHex, would you like to play through the interactive Tutorial?",
|
||||
"hex.builtin.welcome.start.create_file": "Create New File",
|
||||
"hex.builtin.welcome.start.open_file": "Open File",
|
||||
"hex.builtin.welcome.start.open_other": "Other Providers",
|
||||
|
@ -522,8 +522,7 @@ namespace hex::plugin::builtin {
|
||||
if (view->hasViewMenuItemEntry()) {
|
||||
auto &state = view->getWindowOpenState();
|
||||
|
||||
if (ImGui::MenuItem(Lang(view->getUnlocalizedName()), "", &state, ImHexApi::Provider::isValid() && !LayoutManager::isLayoutLocked()))
|
||||
view->setWindowJustOpened(state);
|
||||
ImGui::MenuItem(Lang(view->getUnlocalizedName()), "", &state, ImHexApi::Provider::isValid() && !LayoutManager::isLayoutLocked());
|
||||
}
|
||||
}
|
||||
|
||||
|
106
plugins/builtin/source/content/tutorials/introduction.cpp
Normal file
106
plugins/builtin/source/content/tutorials/introduction.cpp
Normal file
@ -0,0 +1,106 @@
|
||||
#include <content/providers/memory_file_provider.hpp>
|
||||
#include <hex/api/event_manager.hpp>
|
||||
#include <hex/api/shortcut_manager.hpp>
|
||||
#include <hex/api/tutorial_manager.hpp>
|
||||
#include <hex/ui/view.hpp>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
void registerIntroductionTutorial() {
|
||||
using enum TutorialManager::Position;
|
||||
auto &tutorial = TutorialManager::createTutorial("hex.builtin.tutorial.introduction", "hex.builtin.tutorial.introduction.description");
|
||||
|
||||
{
|
||||
tutorial.addStep()
|
||||
.setMessage(
|
||||
"hex.builtin.tutorial.introduction.step1.title",
|
||||
"hex.builtin.tutorial.introduction.step1.description",
|
||||
Bottom | Right
|
||||
)
|
||||
.allowSkip();
|
||||
}
|
||||
|
||||
{
|
||||
auto &step = tutorial.addStep();
|
||||
|
||||
step.setMessage(
|
||||
"hex.builtin.tutorial.introduction.step2.title",
|
||||
"hex.builtin.tutorial.introduction.step2.description",
|
||||
Bottom | Right
|
||||
)
|
||||
.addHighlight("hex.builtin.tutorial.introduction.step2.highlight",
|
||||
{
|
||||
"Welcome Screen/Start##SubWindow_685A2CE9",
|
||||
Lang("hex.builtin.welcome.start.create_file")
|
||||
})
|
||||
.onAppear([&step] {
|
||||
EventProviderOpened::subscribe(&step, [&step](prv::Provider *provider) {
|
||||
if (dynamic_cast<MemoryFileProvider*>(provider))
|
||||
step.complete();
|
||||
});
|
||||
})
|
||||
.onComplete([&step] {
|
||||
EventProviderOpened::unsubscribe(&step);
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
tutorial.addStep()
|
||||
.addHighlight("hex.builtin.tutorial.introduction.step3.highlight", {
|
||||
View::toWindowName("hex.builtin.view.hex_editor.name")
|
||||
})
|
||||
.allowSkip();
|
||||
}
|
||||
|
||||
{
|
||||
tutorial.addStep()
|
||||
.addHighlight("hex.builtin.tutorial.introduction.step4.highlight", {
|
||||
View::toWindowName("hex.builtin.view.data_inspector.name")
|
||||
})
|
||||
.onAppear([]{
|
||||
ImHexApi::HexEditor::setSelection(Region { 0, 1 });
|
||||
})
|
||||
.allowSkip();
|
||||
}
|
||||
|
||||
{
|
||||
tutorial.addStep()
|
||||
.addHighlight("hex.builtin.tutorial.introduction.step5.highlight.pattern_editor", {
|
||||
View::toWindowName("hex.builtin.view.pattern_editor.name")
|
||||
})
|
||||
.addHighlight("hex.builtin.tutorial.introduction.step5.highlight.pattern_data", {
|
||||
View::toWindowName("hex.builtin.view.pattern_data.name")
|
||||
})
|
||||
.onAppear([] {
|
||||
RequestSetPatternLanguageCode::post("\n\n\n\n\n\nstruct Test {\n u8 value;\n};\n\nTest test @ 0x00;");
|
||||
RequestRunPatternCode::post();
|
||||
})
|
||||
.allowSkip();
|
||||
}
|
||||
|
||||
{
|
||||
auto &step = tutorial.addStep();
|
||||
|
||||
step.addHighlight("hex.builtin.tutorial.introduction.step6.highlight", {
|
||||
"##MainMenuBar",
|
||||
"##menubar",
|
||||
Lang("hex.builtin.menu.help")
|
||||
})
|
||||
.addHighlight({
|
||||
"##Menu_00",
|
||||
Lang("hex.builtin.view.tutorials.name")
|
||||
})
|
||||
.onAppear([&step] {
|
||||
EventViewOpened::subscribe([&step](View *view){
|
||||
if (view->getUnlocalizedName() == "hex.builtin.view.tutorials.name")
|
||||
step.complete();
|
||||
});
|
||||
})
|
||||
.onComplete([&step]{
|
||||
EventViewOpened::unsubscribe(&step);
|
||||
})
|
||||
.allowSkip();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
9
plugins/builtin/source/content/tutorials/tutorials.cpp
Normal file
9
plugins/builtin/source/content/tutorials/tutorials.cpp
Normal file
@ -0,0 +1,9 @@
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
void registerIntroductionTutorial();
|
||||
|
||||
void registerTutorials() {
|
||||
registerIntroductionTutorial();
|
||||
}
|
||||
|
||||
}
|
@ -145,6 +145,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
ViewPatternEditor::~ViewPatternEditor() {
|
||||
RequestSetPatternLanguageCode::unsubscribe(this);
|
||||
RequestRunPatternCode::unsubscribe(this);
|
||||
EventFileLoaded::unsubscribe(this);
|
||||
EventProviderChanged::unsubscribe(this);
|
||||
EventProviderClosed::unsubscribe(this);
|
||||
@ -1103,6 +1104,10 @@ namespace hex::plugin::builtin {
|
||||
this->loadPatternFile(path, ImHexApi::Provider::get());
|
||||
});
|
||||
|
||||
RequestRunPatternCode::subscribe(this, [this] {
|
||||
this->m_triggerAutoEvaluate = true;
|
||||
});
|
||||
|
||||
RequestSavePatternLanguageFile::subscribe(this, [this](const std::fs::path &path) {
|
||||
wolv::io::File file(path, wolv::io::File::Mode::Create);
|
||||
file.writeString(wolv::util::trim(this->m_textEditor.GetText()));
|
||||
|
@ -27,6 +27,9 @@ namespace hex::plugin::builtin {
|
||||
|
||||
if (ImGui::BeginTable("Tutorials", 1, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImGui::GetContentRegionAvail())) {
|
||||
for (const auto &tutorial : tutorials | std::views::values) {
|
||||
if (this->m_selectedTutorial == nullptr)
|
||||
this->m_selectedTutorial = &tutorial;
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
|
@ -30,6 +30,8 @@
|
||||
|
||||
#include <string>
|
||||
#include <random>
|
||||
#include <content/popups/popup_question.hpp>
|
||||
#include <hex/api/tutorial_manager.hpp>
|
||||
#include <hex/api/workspace_manager.hpp>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
@ -539,6 +541,16 @@ namespace hex::plugin::builtin {
|
||||
PopupTelemetryRequest::open();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.general.prev_launch_version", "") == "") {
|
||||
PopupQuestion::open("hex.builtin.popup.play_tutorial.desc"_lang,
|
||||
[]{
|
||||
TutorialManager::startTutorial("hex.builtin.tutorial.introduction");
|
||||
},
|
||||
[]{ });
|
||||
}
|
||||
|
||||
ContentRegistry::Settings::write("hex.builtin.setting.general", "hex.builtin.setting.general.prev_launch_version", ImHexApi::System::getImHexVersion());
|
||||
});
|
||||
|
||||
// Clear project context if we go back to the welcome screen
|
||||
|
@ -39,6 +39,7 @@ namespace hex::plugin::builtin {
|
||||
void registerProjectHandlers();
|
||||
void registerAchievements();
|
||||
void registerReportGenerators();
|
||||
void registerTutorials();
|
||||
void loadWorkspaces();
|
||||
|
||||
void addFooterItems();
|
||||
@ -109,6 +110,7 @@ IMHEX_PLUGIN_SETUP("Built-in", "WerWolv", "Default ImHex functionality") {
|
||||
registerCommandForwarders();
|
||||
registerAchievements();
|
||||
registerReportGenerators();
|
||||
registerTutorials();
|
||||
loadWorkspaces();
|
||||
|
||||
addFooterItems();
|
||||
|
Loading…
Reference in New Issue
Block a user