feat: Initial work rework current project system

This commit is contained in:
WerWolv 2025-02-10 21:44:14 +01:00
parent b3d208e6e6
commit efe0928fc6
14 changed files with 464 additions and 49 deletions

View File

@ -22,6 +22,9 @@ set(LIBIMHEX_SOURCES
source/data_processor/link.cpp
source/data_processor/node.cpp
source/project/project.cpp
source/project/project_manager.cpp
source/helpers/utils.cpp
source/helpers/utils_linux.cpp
source/helpers/fs.cpp

View File

@ -0,0 +1,57 @@
#pragma once
#include <hex.hpp>
#include <hex/api/localization_manager.hpp>
#include <memory>
#include <list>
#include <string>
namespace hex::proj {
class Content {
public:
explicit Content(UnlocalizedString type, std::string name)
: m_type(std::move(type)), m_name(std::move(name)) { }
void setData(std::string data) { m_data = std::move(data); }
const std::string& getData() const { return m_data; }
const std::string& getName() const { return m_name; }
void setName(std::string name) { m_name = std::move(name); }
const UnlocalizedString& getType() const { return m_type; }
bool isOpen() const { return m_open; }
void setOpen(bool open) { m_open = open; }
bool isEmpty() const { return m_data.empty(); }
private:
UnlocalizedString m_type;
std::string m_name;
std::string m_data;
bool m_open = false;
};
class Project {
public:
explicit Project(std::string name) : m_name(std::move(name)) {}
Project(const Project &) = delete;
Project(Project &&) = delete;
~Project();
Project &operator=(const Project &) = delete;
Project &operator=(Project &&) = delete;
const std::string &getName() const { return m_name; }
void addContent(UnlocalizedString type);
const std::list<std::unique_ptr<Content>>& getContents() const { return m_contents; }
private:
std::string m_name;
std::list<std::unique_ptr<Content>> m_contents;
};
}

View File

@ -0,0 +1,43 @@
#pragma once
#include <hex.hpp>
#include <hex/project/project.hpp>
#include <list>
#include <string>
#include <filesystem>
#include <functional>
namespace hex::proj {
class ProjectManager {
ProjectManager() = default;
public:
static void createProject(std::string name);
static void loadProject(const std::filesystem::path &path);
static void removeProject(const Project &project);
using LoadFunction = std::function<void(const Content&)>;
using StoreFunction = std::function<void(Content&)>;
struct ContentHandler {
UnlocalizedString type;
bool allowMultiple = false;
LoadFunction load;
StoreFunction store;
};
static void registerContentHandler(ContentHandler handler);
static const std::list<ContentHandler>& getContentHandlers();
static const ContentHandler* getContentHandler(const UnlocalizedString &typeName);
static const std::list<std::unique_ptr<Project>> &getProjects();
static void storeContent(Content &content);
static void loadContent(Content &content);
static Content* getLoadedContent(const UnlocalizedString &type);
};
}

View File

@ -0,0 +1,28 @@
#include <hex/project/project.hpp>
#include <hex/project/project_manager.hpp>
namespace hex::proj {
Project::~Project() {
for (auto &content : m_contents) {
ProjectManager::storeContent(*content);
}
}
void Project::addContent(UnlocalizedString type) {
const auto &content = m_contents.emplace_back(
std::make_unique<Content>(
type,
fmt::format("Unnamed {}", Lang(type))
)
);
const auto loadedContent = ProjectManager::getLoadedContent(type);
ProjectManager::storeContent(loadedContent == nullptr ? *content : *loadedContent);
ProjectManager::loadContent(*content);
}
}

View File

@ -0,0 +1,89 @@
#include <hex/helpers/auto_reset.hpp>
#include <hex/project/project_manager.hpp>
namespace hex::proj {
static AutoReset<std::list<std::unique_ptr<Project>>> s_projects;
static AutoReset<std::list<ProjectManager::ContentHandler>> s_contentHandlers;
void ProjectManager::createProject(std::string name) {
s_projects->emplace_back(std::make_unique<Project>(std::move(name)));
}
void ProjectManager::loadProject(const std::filesystem::path &path) {
std::ignore = path;
}
void ProjectManager::removeProject(const Project &projectToClose) {
std::erase_if(*s_projects, [&](const std::unique_ptr<Project> &project) {
return project.get() == &projectToClose;
});
}
const std::list<std::unique_ptr<Project>> &ProjectManager::getProjects() {
return *s_projects;
}
const std::list<ProjectManager::ContentHandler> &ProjectManager::getContentHandlers() {
return *s_contentHandlers;
}
void ProjectManager::registerContentHandler(ContentHandler handler) {
s_contentHandlers->emplace_back(std::move(handler));
}
const ProjectManager::ContentHandler* ProjectManager::getContentHandler(const UnlocalizedString &typeName) {
for (const auto &handler : getContentHandlers()) {
if (handler.type == typeName) {
return &handler;
}
}
return nullptr;
}
void ProjectManager::storeContent(Content &content) {
const auto *handler = getContentHandler(content.getType());
if (handler != nullptr) {
handler->store(content);
}
}
void ProjectManager::loadContent(Content &content) {
const auto *handler = getContentHandler(content.getType());
if (handler != nullptr) {
if (!handler->allowMultiple) {
for (const auto &project : getProjects()) {
for (const auto &projectContent : project->getContents()) {
if (projectContent->isOpen() && projectContent->getType() == content.getType()) {
storeContent(*projectContent);
projectContent->setOpen(false);
}
}
}
}
if (handler->type == content.getType()) {
handler->load(content);
content.setOpen(true);
}
}
}
Content* ProjectManager::getLoadedContent(const UnlocalizedString& type) {
for (const auto &project : getProjects()) {
for (const auto &projectContent : project->getContents()) {
if (projectContent->isOpen() && projectContent->getType() == type) {
return projectContent.get();
}
}
}
return nullptr;
}
}

View File

@ -51,6 +51,8 @@ add_imhex_plugin(
source/content/helpers/demangle.cpp
source/content/sidebar/project_explorer.cpp
source/content/data_processor_nodes/basic_nodes.cpp
source/content/data_processor_nodes/control_nodes.cpp
source/content/data_processor_nodes/decode_nodes.cpp

View File

@ -32,8 +32,8 @@ namespace hex::plugin::builtin {
private:
std::string m_currFilter;
PerProvider<std::list<Bookmark>> m_bookmarks;
PerProvider<u64> m_currBookmarkId;
std::list<Bookmark> m_bookmarks;
u64 m_currBookmarkId;
};
}

View File

@ -56,7 +56,7 @@ namespace hex::plugin::builtin {
void reloadCustomNodes();
void updateNodePositions();
std::vector<Workspace*> &getWorkspaceStack() { return *m_workspaceStack; }
std::vector<Workspace*> &getWorkspaceStack() { return m_workspaceStack; }
private:
void drawContextMenus(ViewDataProcessor::Workspace &workspace);
@ -76,8 +76,8 @@ namespace hex::plugin::builtin {
std::vector<CustomNode> m_customNodes;
PerProvider<Workspace> m_mainWorkspace;
PerProvider<std::vector<Workspace*>> m_workspaceStack;
Workspace m_mainWorkspace;
std::vector<Workspace*> m_workspaceStack;
TaskHolder m_evaluationTask;
};

View File

@ -399,6 +399,10 @@
"hex.builtin.popup.save_layout.title": "Save Layout",
"hex.builtin.popup.save_layout.desc": "Enter a name under which to save the current layout.",
"hex.builtin.popup.waiting_for_tasks.desc": "There are still tasks running in the background.\nImHex will close after they are finished.",
"hex.builtin.project.content_type.pattern": "Pattern Source Code",
"hex.builtin.project.content_type.bookmarks": "Bookmarks",
"hex.builtin.project.content_type.data_processor": "Data Processor Nodes",
"hex.builtin.project.free_items": "Free Items",
"hex.builtin.provider.rename": "Rename",
"hex.builtin.provider.rename.desc": "Enter a name for this provider.",
"hex.builtin.provider.tooltip.show_more": "Hold SHIFT for more information",

View File

@ -0,0 +1,145 @@
#include <imgui.h>
#include <imgui_internal.h>
#include <hex/ui/imgui_imhex_extensions.h>
#include <fonts/vscode_icons.hpp>
#include <hex/api/content_registry.hpp>
#include <hex/api/task_manager.hpp>
#include <hex/project/project.hpp>
#include <hex/project/project_manager.hpp>
#include <hex/helpers/utils.hpp>
namespace hex::plugin::builtin {
namespace {
void drawContent(proj::Content &content) {
static proj::Content *rightClickedContent = nullptr;
static proj::Content *renamingContent = nullptr;
static std::string renameText;
ImGui::TableNextColumn();
if (renamingContent != &content) {
ImGui::Selectable(content.getName().c_str(), content.isOpen(), ImGuiSelectableFlags_SpanAllColumns);
} else {
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2());
if (ImGui::InputText("##ContentName", renameText)) {
renamingContent->setName(renameText);
}
ImGui::SetKeyboardFocusHere(-1);
ImGui::PopStyleVar();
if (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter)) {
renamingContent->setName(renameText);
renamingContent = nullptr;
}
if (ImGui::IsKeyPressed(ImGuiKey_Escape)) {
renamingContent = nullptr;
}
}
if (ImGui::IsItemHovered()) {
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
proj::ProjectManager::loadContent(content);
}
if (ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
rightClickedContent = &content;
ImGui::OpenPopup("ContentContextMenu");
}
} else {
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left) && renamingContent == &content)
renamingContent = nullptr;
}
ImGui::TableNextColumn();
ImGui::TextUnformatted(Lang(content.getType()));
if (rightClickedContent == &content) {
if (ImGui::BeginPopup("ContentContextMenu")) {
if (ImGui::MenuItemEx("Open", ICON_VS_OPEN_PREVIEW)) {
proj::ProjectManager::loadContent(content);
}
if (ImGui::MenuItemEx("Rename", ICON_VS_DIFF_RENAMED)) {
renamingContent = &content;
renameText = content.getName();
}
ImGui::EndPopup();
}
}
}
void drawProject(proj::Project &project) {
static proj::Project *rightClickedProject = nullptr;
bool open = ImGui::TreeNodeEx(project.getName().c_str(), ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_SpanAllColumns);
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
rightClickedProject = &project;
ImGui::OpenPopup("ProjectContextMenu");
}
if (rightClickedProject == &project) {
if (ImGui::BeginPopup("ProjectContextMenu")) {
if (ImGui::BeginMenuEx("Add", ICON_VS_FILE_ADD)) {
for (const auto &handler : proj::ProjectManager::getContentHandlers()) {
if (ImGui::MenuItem(Lang(handler.type))) {
rightClickedProject->addContent(handler.type);
}
}
ImGui::EndMenu();
}
if (ImGui::MenuItemEx("Close", ICON_VS_CLOSE)) {
TaskManager::doLater([project = rightClickedProject] {
proj::ProjectManager::removeProject(*project);
});
}
ImGui::EndPopup();
}
}
if (open) {
for (const auto &content : project.getContents()) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::PushID(content.get());
drawContent(*content);
ImGui::PopID();
}
ImGui::TreePop();
}
}
}
void registerProjectExplorer() {
ContentRegistry::Interface::addSidebarItem(ICON_VS_PROJECT, [] {
if (ImGui::BeginTable("Projects", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) {
ImGui::TableSetupColumn("##Icon", ImGuiTableColumnFlags_WidthFixed, 20_scaled);
ImGui::TableSetupColumn("##Name", ImGuiTableColumnFlags_WidthStretch, 20_scaled);
ImGui::TableSetupColumn("##Type", ImGuiTableColumnFlags_WidthFixed, 100_scaled);
for (auto &project : proj::ProjectManager::getProjects()) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TableNextColumn();
ImGui::PushID(project.get());
drawProject(*project);
ImGui::PopID();
ImGui::NewLine();
}
ImGui::EndTable();
}
});
proj::ProjectManager::createProject("Project 1");
proj::ProjectManager::createProject("Project 2");
proj::ProjectManager::createProject("Free Items");
}
}

View File

@ -12,6 +12,7 @@
#include <content/providers/view_provider.hpp>
#include <fonts/vscode_icons.hpp>
#include <hex/project/project_manager.hpp>
#include <nlohmann/json.hpp>
@ -45,16 +46,16 @@ namespace hex::plugin::builtin {
bookmarkId
};
m_bookmarks->emplace_back(std::move(bookmark), true);
m_bookmarks.emplace_back(std::move(bookmark), true);
ImHexApi::Provider::markDirty();
EventBookmarkCreated::post(m_bookmarks->back().entry);
EventBookmarkCreated::post(m_bookmarks.back().entry);
EventHighlightingChanged::post();
});
RequestRemoveBookmark::subscribe([this](u64 id) {
std::erase_if(m_bookmarks.get(), [id](const auto &bookmark) {
std::erase_if(m_bookmarks, [id](const auto &bookmark) {
return bookmark.entry.id == id;
});
});
@ -65,7 +66,7 @@ namespace hex::plugin::builtin {
// Check all bookmarks for potential overlaps with the current address
std::optional<ImColor> color;
for (const auto &bookmark : *m_bookmarks) {
for (const auto &bookmark : m_bookmarks) {
if (!bookmark.highlightVisible)
continue;
@ -82,7 +83,7 @@ namespace hex::plugin::builtin {
std::ignore = data;
// Loop over all bookmarks
for (const auto &[bookmark, highlightVisible] : *m_bookmarks) {
for (const auto &[bookmark, highlightVisible] : m_bookmarks) {
if (!highlightVisible)
continue;
@ -158,7 +159,7 @@ namespace hex::plugin::builtin {
return true;
auto data = nlohmann::json::parse(fileContent.begin(), fileContent.end());
m_bookmarks.get(provider).clear();
m_bookmarks.clear();
return this->importBookmarks(provider, data);
},
.store = [this](prv::Provider *provider, const std::fs::path &basePath, const Tar &tar) -> bool {
@ -170,11 +171,26 @@ namespace hex::plugin::builtin {
return result;
}
});
proj::ProjectManager::registerContentHandler({
.type = "hex.builtin.project.content_type.bookmarks",
.load = [this](const proj::Content &content) {
m_bookmarks.clear();
const auto data = content.isEmpty() ? nlohmann::json() : nlohmann::json::parse(content.getData());
this->importBookmarks(ImHexApi::Provider::get(), data);
EventHighlightingChanged::post();
},
.store = [this](proj::Content &content) {
nlohmann::json data;
this->exportBookmarks(ImHexApi::Provider::get(), data);
content.setData(data.dump(4));
}
});
ContentRegistry::Reports::addReportProvider([this](prv::Provider *provider) -> std::string {
std::string result;
const auto &bookmarks = m_bookmarks.get(provider);
const auto &bookmarks = m_bookmarks;
if (bookmarks.empty())
return "";
@ -252,7 +268,7 @@ namespace hex::plugin::builtin {
void ViewBookmarks::drawDropTarget(std::list<Bookmark>::iterator it, float height) {
height = std::max(height, 1.0F);
if (it != m_bookmarks->begin()) {
if (it != m_bookmarks.begin()) {
ImGui::SetCursorPosY(ImGui::GetCursorPosY() - height);
} else {
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + height);
@ -261,7 +277,7 @@ namespace hex::plugin::builtin {
ImGui::InvisibleButton("##DropTarget", ImVec2(ImGui::GetContentRegionAvail().x, height * 2.0F));
const auto dropTarget = ImRect(ImGui::GetItemRectMin(), ImVec2(ImGui::GetItemRectMax().x, ImGui::GetItemRectMin().y + 2_scaled));
if (it == m_bookmarks->begin()) {
if (it == m_bookmarks.begin()) {
ImGui::SetCursorPosY(ImGui::GetCursorPosY() - height);
}
@ -274,13 +290,13 @@ namespace hex::plugin::builtin {
u64 droppedBookmarkId = *static_cast<const u64*>(payload->Data);
// Find the correct bookmark with that id
auto droppedIter = std::ranges::find_if(m_bookmarks->begin(), m_bookmarks->end(), [droppedBookmarkId](const auto &bookmarkItem) {
auto droppedIter = std::ranges::find_if(m_bookmarks.begin(), m_bookmarks.end(), [droppedBookmarkId](const auto &bookmarkItem) {
return bookmarkItem.entry.id == droppedBookmarkId;
});
// Swap the two bookmarks
if (droppedIter != m_bookmarks->end()) {
m_bookmarks->splice(it, m_bookmarks, droppedIter);
if (droppedIter != m_bookmarks.end()) {
m_bookmarks.splice(it, m_bookmarks, droppedIter);
EventHighlightingChanged::post();
}
@ -298,18 +314,18 @@ namespace hex::plugin::builtin {
ImGui::PopItemWidth();
if (ImGui::BeginChild("##bookmarks")) {
if (m_bookmarks->empty()) {
if (m_bookmarks.empty()) {
ImGuiExt::TextOverlay("hex.builtin.view.bookmarks.no_bookmarks"_lang, ImGui::GetWindowPos() + ImGui::GetWindowSize() / 2, ImGui::GetWindowWidth() * 0.7);
}
auto bookmarkToRemove = m_bookmarks->end();
auto bookmarkToRemove = m_bookmarks.end();
const auto defaultItemSpacing = ImGui::GetStyle().ItemSpacing.y;
ImGui::Dummy({ ImGui::GetContentRegionAvail().x, 0 });
drawDropTarget(m_bookmarks->begin(), defaultItemSpacing);
drawDropTarget(m_bookmarks.begin(), defaultItemSpacing);
// Draw all bookmarks
for (auto it = m_bookmarks->begin(); it != m_bookmarks->end(); ++it) {
for (auto it = m_bookmarks.begin(); it != m_bookmarks.end(); ++it) {
auto &[bookmark, highlightVisible] = *it;
auto &[region, name, comment, color, locked, bookmarkId] = bookmark;
@ -529,15 +545,15 @@ namespace hex::plugin::builtin {
}
// Remove the bookmark that was marked for removal
if (bookmarkToRemove != m_bookmarks->end()) {
m_bookmarks->erase(bookmarkToRemove);
if (bookmarkToRemove != m_bookmarks.end()) {
m_bookmarks.erase(bookmarkToRemove);
EventHighlightingChanged::post();
}
}
ImGui::EndChild();
}
bool ViewBookmarks::importBookmarks(prv::Provider *provider, const nlohmann::json &json) {
bool ViewBookmarks::importBookmarks(prv::Provider *, const nlohmann::json &json) {
if (!json.contains("bookmarks"))
return false;
@ -549,30 +565,32 @@ namespace hex::plugin::builtin {
if (!region.contains("address") || !region.contains("size"))
continue;
m_bookmarks.get(provider).push_back({
m_bookmarks.push_back({
{
.region = { region["address"], region["size"] },
.name = bookmark["name"],
.comment = bookmark["comment"],
.color = bookmark["color"],
.locked = bookmark["locked"],
.id = bookmark.contains("id") ? bookmark["id"].get<u64>() : m_currBookmarkId.get(provider),
.id = bookmark.contains("id") ? bookmark["id"].get<u64>() : m_currBookmarkId,
},
bookmark.contains("highlightVisible") ? bookmark["highlightVisible"].get<bool>() : true,
});
if (bookmark.contains("id"))
m_currBookmarkId.get(provider) = std::max<u64>(m_currBookmarkId.get(provider), bookmark["id"].get<i64>() + 1);
m_currBookmarkId = std::max<u64>(m_currBookmarkId, bookmark["id"].get<i64>() + 1);
else
m_currBookmarkId.get(provider) += 1;
m_currBookmarkId += 1;
}
EventHighlightingChanged::post();
return true;
}
bool ViewBookmarks::exportBookmarks(prv::Provider *provider, nlohmann::json &json) {
bool ViewBookmarks::exportBookmarks(prv::Provider *, nlohmann::json &json) {
json["bookmarks"] = nlohmann::json::array();
size_t index = 0;
for (const auto &[bookmark, highlightVisible] : m_bookmarks.get(provider)) {
for (const auto &[bookmark, highlightVisible] : m_bookmarks) {
json["bookmarks"][index] = {
{ "name", bookmark.name },
{ "comment", bookmark.comment },
@ -627,7 +645,7 @@ namespace hex::plugin::builtin {
wolv::io::File(path, wolv::io::File::Mode::Create).writeString(json.dump(4));
});
}, [this]{
return ImHexApi::Provider::isValid() && !m_bookmarks->empty();
return ImHexApi::Provider::isValid() && !m_bookmarks.empty();
});
}

View File

@ -14,6 +14,7 @@
#include <imnodes.h>
#include <imnodes_internal.h>
#include <hex/project/project_manager.hpp>
#include <nlohmann/json.hpp>
#include <wolv/io/file.hpp>
@ -361,28 +362,39 @@ namespace hex::plugin::builtin {
ProjectFile::registerPerProviderHandler({
.basePath = "data_processor.json",
.required = false,
.load = [this](prv::Provider *provider, const std::fs::path &basePath, const Tar &tar) {
.load = [this](prv::Provider *, const std::fs::path &basePath, const Tar &tar) {
std::string save = tar.readString(basePath);
ViewDataProcessor::loadNodes(m_mainWorkspace.get(provider), nlohmann::json::parse(save));
this->loadNodes(m_mainWorkspace, nlohmann::json::parse(save));
m_updateNodePositions = true;
return true;
},
.store = [this](prv::Provider *provider, const std::fs::path &basePath, const Tar &tar) {
tar.writeString(basePath, ViewDataProcessor::saveNodes(m_mainWorkspace.get(provider)).dump(4));
.store = [this](prv::Provider *, const std::fs::path &basePath, const Tar &tar) {
tar.writeString(basePath, this->saveNodes(m_mainWorkspace).dump(4));
return true;
}
});
proj::ProjectManager::registerContentHandler({
.type = "hex.builtin.project.content_type.data_processor",
.load = [this](const proj::Content &content) {
const nlohmann::json data = content.isEmpty() ? nlohmann::json() : nlohmann::json::parse(content.getData());
this->loadNodes(m_mainWorkspace, data);
m_updateNodePositions = true;
},
.store = [this](proj::Content &content) {
content.setData(this->saveNodes(m_mainWorkspace).dump(4));
}
});
EventProviderOpened::subscribe(this, [this](auto *provider) {
m_mainWorkspace.get(provider) = { };
m_workspaceStack.get(provider).push_back(&m_mainWorkspace.get(provider));
EventProviderOpened::subscribe(this, [this](auto *) {
m_mainWorkspace = { };
m_workspaceStack.push_back(&m_mainWorkspace);
});
EventProviderChanged::subscribe(this, [this](const auto *, const auto *) {
for (auto *workspace : *m_workspaceStack) {
for (auto *workspace : m_workspaceStack) {
for (auto &node : workspace->nodes) {
node->setCurrentOverlay(nullptr);
}
@ -399,7 +411,7 @@ namespace hex::plugin::builtin {
[&](const std::fs::path &path) {
wolv::io::File file(path, wolv::io::File::Mode::Read);
if (file.isValid()) {
ViewDataProcessor::loadNodes(*m_mainWorkspace, nlohmann::json::parse(file.readString()));
ViewDataProcessor::loadNodes(m_mainWorkspace, nlohmann::json::parse(file.readString()));
m_updateNodePositions = true;
}
});
@ -411,17 +423,17 @@ namespace hex::plugin::builtin {
[&, this](const std::fs::path &path) {
wolv::io::File file(path, wolv::io::File::Mode::Create);
if (file.isValid())
file.writeString(ViewDataProcessor::saveNodes(*m_mainWorkspace).dump(4));
file.writeString(ViewDataProcessor::saveNodes(m_mainWorkspace).dump(4));
});
}, [this]{
return !m_workspaceStack->empty() && !m_workspaceStack->back()->nodes.empty() && ImHexApi::Provider::isValid();
return !m_workspaceStack.empty() && !m_workspaceStack.back()->nodes.empty() && ImHexApi::Provider::isValid();
});
ContentRegistry::FileHandler::add({ ".hexnode" }, [this](const auto &path) {
wolv::io::File file(path, wolv::io::File::Mode::Read);
if (!file.isValid()) return false;
ViewDataProcessor::loadNodes(*m_mainWorkspace, file.readString());
ViewDataProcessor::loadNodes(m_mainWorkspace, file.readString());
m_updateNodePositions = true;
return true;
@ -884,7 +896,7 @@ namespace hex::plugin::builtin {
}
void ViewDataProcessor::drawContent() {
auto &workspace = *m_workspaceStack->back();
auto &workspace = *m_workspaceStack.back();
ImGui::BeginDisabled(m_evaluationTask.isRunning());
@ -962,7 +974,7 @@ namespace hex::plugin::builtin {
ImGuiExt::TextFormattedCentered("{}", "hex.builtin.view.data_processor.help_text"_lang);
// Draw a close button if there is more than one workspace on the stack
if (m_workspaceStack->size() > 1) {
if (m_workspaceStack.size() > 1) {
ImGui::SetCursorPos(ImVec2(ImGui::GetContentRegionAvail().x - ImGui::GetTextLineHeightWithSpacing() * 1.5F, ImGui::GetTextLineHeightWithSpacing() * 0.2F));
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4.0F, 4.0F));
if (ImGuiExt::DimmedIconButton(ICON_VS_CLOSE, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed))) {
@ -1079,7 +1091,7 @@ namespace hex::plugin::builtin {
// Remove the top-most workspace from the stack if requested
if (popWorkspace) {
m_workspaceStack->pop_back();
m_workspaceStack.pop_back();
m_updateNodePositions = true;
}
}

View File

@ -36,6 +36,8 @@
#include <content/global_actions.hpp>
#include <fonts/fonts.hpp>
#include <hex/project/project.hpp>
#include <hex/project/project_manager.hpp>
#include <ui/menu_items.hpp>
namespace hex::plugin::builtin {
@ -333,9 +335,9 @@ namespace hex::plugin::builtin {
const auto availableSize = g.CurrentWindow->Size;
const auto windowPosition = ImGui::GetCursorScreenPos();
auto textEditorSize = availableSize;
textEditorSize.y *= 3.5 / 5.0;
textEditorSize.y *= 3.5F / 5.0F;
textEditorSize.y -= ImGui::GetTextLineHeightWithSpacing();
textEditorSize.y = std::clamp(textEditorSize.y + height,200.0F, availableSize.y-200.0F);
textEditorSize.y = std::clamp(textEditorSize.y + height, 200.0F, availableSize.y - 200.0F);
if (g.NavWindow != nullptr) {
std::string name = g.NavWindow->Name;
@ -1425,7 +1427,7 @@ namespace hex::plugin::builtin {
const auto &currScope = evaluator->getScope(-m_debuggerScopeIndex);
if (ImGui::BeginCombo("##scope", displayValue(currScope.parent, m_debuggerScopeIndex).c_str())) {
for (size_t i = 0; i < evaluator->getScopeCount(); i++) {
auto &scope = evaluator->getScope(-i);
auto &scope = evaluator->getScope(-i32(i));
if (ImGui::Selectable(displayValue(scope.parent, i).c_str(), i == size_t(m_debuggerScopeIndex))) {
m_debuggerScopeIndex = i;
@ -2299,6 +2301,16 @@ namespace hex::plugin::builtin {
}
});
proj::ProjectManager::registerContentHandler({
.type = "hex.builtin.project.content_type.pattern",
.load = [this](const proj::Content &content) {
m_textEditor.SetText(content.getData());
},
.store = [this](proj::Content &content) {
content.setData(wolv::util::trim(m_textEditor.GetText()));
}
});
ShortcutManager::addShortcut(this, CTRLCMD + Keys::G + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.goto_line", [this] {
m_openGotoLinePopUp = true;
});

View File

@ -45,6 +45,7 @@ namespace hex::plugin::builtin {
void registerReportGenerators();
void registerTutorials();
void registerDataInformationSections();
void registerProjectExplorer();
void loadWorkspaces();
void addWindowDecoration();
@ -140,6 +141,7 @@ IMHEX_PLUGIN_SETUP("Built-in", "WerWolv", "Default ImHex functionality") {
registerReportGenerators();
registerTutorials();
registerDataInformationSections();
registerProjectExplorer();
loadWorkspaces();
addWindowDecoration();
createWelcomeScreen();