diff --git a/lib/libimhex/include/hex/api/content_registry.hpp b/lib/libimhex/include/hex/api/content_registry.hpp index 5c860068c..c5b06c965 100644 --- a/lib/libimhex/include/hex/api/content_registry.hpp +++ b/lib/libimhex/include/hex/api/content_registry.hpp @@ -258,6 +258,7 @@ namespace hex { }; std::map &getVisualizers(); + std::map &getInlineVisualizers(); std::map &getPragmas(); std::vector &getFunctions(); @@ -317,6 +318,15 @@ namespace hex { */ void addVisualizer(const std::string &name, const impl::VisualizerFunctionCallback &func, u32 parameterCount); + /** + * @brief Adds a new inline visualizer to the pattern language + * @note Inline visualizers are extensions to the [[hex::inline_visualize]] attribute, used to visualize data + * @param name The name of the visualizer + * @param func The function callback + * @param parameterCount The amount of parameters the function takes + */ + void addInlineVisualizer(const std::string &name, const impl::VisualizerFunctionCallback &func, u32 parameterCount); + } /* View Registry. Allows adding of new windows */ diff --git a/lib/libimhex/source/api/content_registry.cpp b/lib/libimhex/source/api/content_registry.cpp index ffb2f7b55..08828c503 100644 --- a/lib/libimhex/source/api/content_registry.cpp +++ b/lib/libimhex/source/api/content_registry.cpp @@ -336,6 +336,11 @@ namespace hex { impl::getVisualizers()[name] = impl::Visualizer { parameterCount, function }; } + void addInlineVisualizer(const std::string &name, const impl::VisualizerFunctionCallback &function, u32 parameterCount) { + log::debug("Registered new inline pattern visualizer function: {}", name); + impl::getInlineVisualizers()[name] = impl::Visualizer { parameterCount, function }; + } + namespace impl { @@ -345,6 +350,12 @@ namespace hex { return visualizers; } + std::map &getInlineVisualizers() { + static std::map visualizers; + + return visualizers; + } + std::map &getPragmas() { static std::map pragmas; diff --git a/main/source/init/tasks.cpp b/main/source/init/tasks.cpp index 873bd8b0d..ca8641e0a 100644 --- a/main/source/init/tasks.cpp +++ b/main/source/init/tasks.cpp @@ -367,6 +367,7 @@ namespace hex::init { ContentRegistry::PatternLanguage::impl::getFunctions().clear(); ContentRegistry::PatternLanguage::impl::getPragmas().clear(); ContentRegistry::PatternLanguage::impl::getVisualizers().clear(); + ContentRegistry::PatternLanguage::impl::getInlineVisualizers().clear(); ContentRegistry::Views::impl::getEntries().clear(); impl::PopupBase::getOpenPopups().clear(); diff --git a/plugins/builtin/CMakeLists.txt b/plugins/builtin/CMakeLists.txt index be29acaf7..0a890f225 100644 --- a/plugins/builtin/CMakeLists.txt +++ b/plugins/builtin/CMakeLists.txt @@ -13,6 +13,7 @@ add_library(${PROJECT_NAME} SHARED source/content/pl_builtin_functions.cpp source/content/pl_pragmas.cpp source/content/pl_visualizers.cpp + source/content/pl_inline_visualizers.cpp source/content/settings_entries.cpp source/content/tools_entries.cpp source/content/data_processor_nodes.cpp diff --git a/plugins/builtin/include/ui/pattern_drawer.hpp b/plugins/builtin/include/ui/pattern_drawer.hpp index b2351cd6b..34fd2313a 100644 --- a/plugins/builtin/include/ui/pattern_drawer.hpp +++ b/plugins/builtin/include/ui/pattern_drawer.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -61,7 +62,7 @@ namespace hex::plugin::builtin::ui { void makeSelectable(const pl::ptrn::Pattern &pattern); void drawValueColumn(pl::ptrn::Pattern& pattern); - void drawVisualizer(const std::vector &arguments, pl::ptrn::Pattern &pattern, pl::ptrn::IIterable &iterable, bool reset); + void drawVisualizer(const std::map &visualizers, const std::vector &arguments, pl::ptrn::Pattern &pattern, pl::ptrn::IIterable &iterable, bool reset); void drawFavoriteColumn(const pl::ptrn::Pattern& pattern); bool createTreeNode(const pl::ptrn::Pattern& pattern, bool leaf = false); diff --git a/plugins/builtin/source/content/pl_inline_visualizers.cpp b/plugins/builtin/source/content/pl_inline_visualizers.cpp new file mode 100644 index 000000000..96fb311cc --- /dev/null +++ b/plugins/builtin/source/content/pl_inline_visualizers.cpp @@ -0,0 +1,68 @@ +#include + +#include + +#include +#include + +#include + +#include + + +#include + +namespace hex::plugin::builtin { + + namespace { + + void drawColorInlineVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span arguments) { + auto r = arguments[0].toFloatingPoint(); + auto g = arguments[1].toFloatingPoint(); + auto b = arguments[2].toFloatingPoint(); + auto a = arguments[3].toFloatingPoint(); + + ImGui::ColorButton("color", ImVec4(r / 255.0F, g / 255.0F, b / 255.0F, a / 255.0F), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight())); + } + + void drawGaugeInlineVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span arguments) { + auto value = arguments[0].toFloatingPoint(); + + const auto color = ImGui::GetStyleColorVec4(ImGuiCol_Text); + + ImGui::PushStyleColor(ImGuiCol_PlotHistogram, ImVec4(color.x, color.y, color.z, 0.2F)); + ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.0F, 0.0F, 0.0F, 0.0F)); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(color.x, color.y, color.z, 0.5F)); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0F); + + ImGui::ProgressBar(value / 100.0F, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()), ""); + + ImGui::PopStyleVar(1); + ImGui::PopStyleColor(3); + } + + void drawButtonInlineVisualizer(pl::ptrn::Pattern &pattern, pl::ptrn::IIterable &, bool, std::span arguments) { + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); + ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0, 0.5F)); + + if (ImGui::Button(hex::format(" {} {}", ICON_VS_PLAY, pattern.getFormattedValue()).c_str(), ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()))) { + auto *evaluator = pattern.getEvaluator(); + const auto functionName = arguments[0].toString(false); + const auto &function = evaluator->findFunction(functionName); + + if (function.has_value()) + function->func(evaluator, { pattern.clone() }); + } + + ImGui::PopStyleVar(2); + } + + } + + void registerPatternLanguageInlineVisualizers() { + ContentRegistry::PatternLanguage::addInlineVisualizer("color", drawColorInlineVisualizer, 4); + ContentRegistry::PatternLanguage::addInlineVisualizer("gauge", drawGaugeInlineVisualizer, 1); + ContentRegistry::PatternLanguage::addInlineVisualizer("button", drawButtonInlineVisualizer, 1); + } + +} diff --git a/plugins/builtin/source/plugin_builtin.cpp b/plugins/builtin/source/plugin_builtin.cpp index 51c28682a..f36207d96 100644 --- a/plugins/builtin/source/plugin_builtin.cpp +++ b/plugins/builtin/source/plugin_builtin.cpp @@ -15,6 +15,7 @@ namespace hex::plugin::builtin { void registerPatternLanguageFunctions(); void registerPatternLanguagePragmas(); void registerPatternLanguageVisualizers(); + void registerPatternLanguageInlineVisualizers(); void registerCommandPaletteCommands(); void registerSettings(); void loadSettings(); @@ -57,6 +58,7 @@ IMHEX_PLUGIN_SETUP("Built-in", "WerWolv", "Default ImHex functionality") { registerPatternLanguageFunctions(); registerPatternLanguagePragmas(); registerPatternLanguageVisualizers(); + registerPatternLanguageInlineVisualizers(); registerCommandPaletteCommands(); registerSettings(); loadSettings(); diff --git a/plugins/builtin/source/ui/pattern_drawer.cpp b/plugins/builtin/source/ui/pattern_drawer.cpp index 479d9065c..47f3400c6 100644 --- a/plugins/builtin/source/ui/pattern_drawer.cpp +++ b/plugins/builtin/source/ui/pattern_drawer.cpp @@ -219,11 +219,9 @@ namespace hex::plugin::builtin::ui { ImGui::TableNextColumn(); } - void PatternDrawer::drawVisualizer(const std::vector &arguments, pl::ptrn::Pattern &pattern, pl::ptrn::IIterable &iterable, bool reset) { + void PatternDrawer::drawVisualizer(const std::map &visualizers, const std::vector &arguments, pl::ptrn::Pattern &pattern, pl::ptrn::IIterable &iterable, bool reset) { auto visualizerName = arguments.front().toString(true); - const auto &visualizers = ContentRegistry::PatternLanguage::impl::getVisualizers(); - if (auto entry = visualizers.find(visualizerName); entry != visualizers.end()) { const auto &[name, visualizer] = *entry; if (visualizer.parameterCount != arguments.size() - 1) { @@ -248,12 +246,12 @@ namespace hex::plugin::builtin::ui { const auto value = pattern.getFormattedValue(); const auto width = ImGui::GetColumnWidth(); - if (const auto &arguments = pattern.getAttributeArguments("hex::visualize"); !arguments.empty()) { + if (const auto &visualizeArgs = pattern.getAttributeArguments("hex::visualize"); !visualizeArgs.empty()) { ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0, 0.5F)); bool shouldReset = false; - if (ImGui::Button(hex::format("{} {}", ICON_VS_EYE_WATCH, value).c_str(), ImVec2(width, ImGui::GetTextLineHeight()))) { + if (ImGui::Button(hex::format(" {} {}", ICON_VS_EYE_WATCH, value).c_str(), ImVec2(width, ImGui::GetTextLineHeight()))) { auto previousPattern = this->m_currVisualizedPattern; this->m_currVisualizedPattern = &pattern; @@ -270,12 +268,14 @@ namespace hex::plugin::builtin::ui { if (ImGui::BeginPopup("Visualizer")) { if (this->m_currVisualizedPattern == &pattern) { - drawVisualizer(arguments, pattern, dynamic_cast(pattern), !this->m_visualizedPatterns.contains(&pattern) || shouldReset); + drawVisualizer(ContentRegistry::PatternLanguage::impl::getVisualizers(), visualizeArgs, pattern, dynamic_cast(pattern), !this->m_visualizedPatterns.contains(&pattern) || shouldReset); this->m_visualizedPatterns.insert(&pattern); } ImGui::EndPopup(); } + } else if (const auto &inlineVisualizeArgs = pattern.getAttributeArguments("hex::inline_visualize"); !inlineVisualizeArgs.empty()) { + drawVisualizer(ContentRegistry::PatternLanguage::impl::getInlineVisualizers(), inlineVisualizeArgs, pattern, dynamic_cast(pattern), true); } else { ImGui::TextFormatted("{}", value); } @@ -660,20 +660,16 @@ namespace hex::plugin::builtin::ui { drawSizeColumn(pattern); drawTypenameColumn(pattern, "struct"); - if (this->isEditingPattern(pattern)) { - if (pattern.getWriteFormatterFunction().empty()) - ImGui::TextFormatted("{}", pattern.getFormattedValue()); - else { - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); - ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); - auto value = pattern.toString(); - if (ImGui::InputText("##Value", value, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue)) { - pattern.setValue(value); - this->resetEditing(); - } - ImGui::PopItemWidth(); - ImGui::PopStyleVar(); + if (this->isEditingPattern(pattern) && !pattern.getWriteFormatterFunction().empty()) { + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); + ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); + auto value = pattern.toString(); + if (ImGui::InputText("##Value", value, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue)) { + pattern.setValue(value); + this->resetEditing(); } + ImGui::PopItemWidth(); + ImGui::PopStyleVar(); } else { drawValueColumn(pattern); } @@ -712,20 +708,16 @@ namespace hex::plugin::builtin::ui { drawSizeColumn(pattern); drawTypenameColumn(pattern, "union"); - if (this->isEditingPattern(pattern)) { - if (pattern.getWriteFormatterFunction().empty()) - ImGui::TextFormatted("{}", pattern.getFormattedValue()); - else { - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); - ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); - auto value = pattern.toString(); - if (ImGui::InputText("##Value", value, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue)) { - pattern.setValue(value); - this->resetEditing(); - } - ImGui::PopItemWidth(); - ImGui::PopStyleVar(); + if (this->isEditingPattern(pattern) && !pattern.getWriteFormatterFunction().empty()) { + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); + ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); + auto value = pattern.toString(); + if (ImGui::InputText("##Value", value, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue)) { + pattern.setValue(value); + this->resetEditing(); } + ImGui::PopItemWidth(); + ImGui::PopStyleVar(); } else { drawValueColumn(pattern); }