From ea601a7d0370e3dfe0978007fdbc6a27478a52fb Mon Sep 17 00:00:00 2001 From: WerWolv Date: Sun, 25 Feb 2024 14:30:56 +0100 Subject: [PATCH] feat: Added option to highlight pattern parents in the hex editor when hovering --- lib/libimhex/include/hex/api/imhex_api.hpp | 22 +++++++++ .../include/hex/providers/provider_data.hpp | 14 +++--- lib/libimhex/source/api/imhex_api.cpp | 35 +++++++++++++- .../content/views/view_information.hpp | 2 +- .../content/views/view_pattern_editor.hpp | 5 +- plugins/builtin/romfs/lang/en_US.json | 1 + .../source/content/settings_entries.cpp | 1 + .../source/content/views/view_hex_editor.cpp | 18 +++++++- .../source/content/views/view_information.cpp | 2 +- .../content/views/view_pattern_data.cpp | 2 +- .../content/views/view_pattern_editor.cpp | 46 ++++++++++++++----- plugins/ui/source/ui/hex_editor.cpp | 18 ++++++++ 12 files changed, 141 insertions(+), 25 deletions(-) diff --git a/lib/libimhex/include/hex/api/imhex_api.hpp b/lib/libimhex/include/hex/api/imhex_api.hpp index 42718070f..0675b8960 100644 --- a/lib/libimhex/include/hex/api/imhex_api.hpp +++ b/lib/libimhex/include/hex/api/imhex_api.hpp @@ -73,15 +73,18 @@ namespace hex { namespace impl { using HighlightingFunction = std::function(u64, const u8*, size_t, bool)>; + using HoveringFunction = std::function; const std::map& getBackgroundHighlights(); const std::map& getBackgroundHighlightingFunctions(); const std::map& getForegroundHighlights(); const std::map& getForegroundHighlightingFunctions(); + const std::map& getHoveringFunctions(); const std::map& getTooltips(); const std::map& getTooltipFunctions(); void setCurrentSelection(const std::optional ®ion); + void setHoveredRegion(const prv::Provider *provider, const Region ®ion); } /** @@ -170,6 +173,19 @@ namespace hex { */ void removeForegroundHighlightingProvider(u32 id); + /** + * @brief Adds a hovering provider to the Hex Editor using a callback function + * @param function Function that draws the highlighting based on the hovered region + * @return Unique ID used to remove the highlighting again later + */ + u32 addHoverHighlightProvider(const impl::HoveringFunction &function); + + /** + * @brief Removes a hovering color highlighting from the Hex Editor + * @param id The ID of the highlighting to remove + */ + void removeHoverHighlightProvider(u32 id); + /** * @brief Checks if there's a valid selection in the Hex Editor right now */ @@ -215,6 +231,12 @@ namespace hex { */ void addVirtualFile(const std::fs::path &path, std::vector data, Region region = Region::Invalid()); + /** + * @brief Gets the currently hovered cell region in the Hex Editor + * @return + */ + const std::optional& getHoveredRegion(const prv::Provider *provider); + } /* Functions to interact with Bookmarks */ diff --git a/lib/libimhex/include/hex/providers/provider_data.hpp b/lib/libimhex/include/hex/providers/provider_data.hpp index 91154b562..fe1d21661 100644 --- a/lib/libimhex/include/hex/providers/provider_data.hpp +++ b/lib/libimhex/include/hex/providers/provider_data.hpp @@ -32,19 +32,19 @@ namespace hex { return &this->get(); } - T& get(prv::Provider *provider = ImHexApi::Provider::get()) { + T& get(const prv::Provider *provider = ImHexApi::Provider::get()) { return m_data[provider]; } - const T& get(prv::Provider *provider = ImHexApi::Provider::get()) const { + const T& get(const prv::Provider *provider = ImHexApi::Provider::get()) const { return m_data.at(provider); } - void set(const T &data, prv::Provider *provider = ImHexApi::Provider::get()) { + void set(const T &data, const prv::Provider *provider = ImHexApi::Provider::get()) { m_data[provider] = data; } - void set(T &&data, prv::Provider *provider = ImHexApi::Provider::get()) { + void set(T &&data, const prv::Provider *provider = ImHexApi::Provider::get()) { m_data[provider] = std::move(data); } @@ -74,7 +74,7 @@ namespace hex { return m_data | std::views::values; } - void setOnCreateCallback(std::function callback) { + void setOnCreateCallback(std::function callback) { m_onCreateCallback = std::move(callback); } @@ -120,8 +120,8 @@ namespace hex { } private: - std::map m_data; - std::function m_onCreateCallback; + std::map m_data; + std::function m_onCreateCallback; }; } \ No newline at end of file diff --git a/lib/libimhex/source/api/imhex_api.cpp b/lib/libimhex/source/api/imhex_api.cpp index 8ddd42ee0..41e99a08c 100644 --- a/lib/libimhex/source/api/imhex_api.cpp +++ b/lib/libimhex/source/api/imhex_api.cpp @@ -2,10 +2,11 @@ #include #include -#include #include #include #include +#include +#include #include @@ -67,11 +68,24 @@ namespace hex { return *s_tooltipFunctions; } + static AutoReset> s_hoveringFunctions; + const std::map& getHoveringFunctions() { + return *s_hoveringFunctions; + } + static AutoReset> s_currentSelection; void setCurrentSelection(const std::optional ®ion) { *s_currentSelection = region; } + static PerProvider> s_hoveredRegion; + void setHoveredRegion(const prv::Provider *provider, const Region ®ion) { + if (region == Region::Invalid()) + s_hoveredRegion.get(provider).reset(); + else + s_hoveredRegion.get(provider) = region; + } + } u32 addBackgroundHighlight(const Region ®ion, color_t color) { @@ -150,6 +164,20 @@ namespace hex { TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); }); } + u32 addHoverHighlightProvider(const impl::HoveringFunction &function) { + static u32 id = 0; + + id++; + + impl::s_hoveringFunctions->insert({ id, function }); + + return id; + } + + void removeHoverHighlightProvider(u32 id) { + impl::s_hoveringFunctions->erase(id); + } + static u32 tooltipId = 0; u32 addTooltip(Region region, std::string value, color_t color) { tooltipId++; @@ -202,6 +230,11 @@ namespace hex { void addVirtualFile(const std::fs::path &path, std::vector data, Region region) { RequestAddVirtualFile::post(path, std::move(data), region); } + + const std::optional& getHoveredRegion(const prv::Provider *provider) { + return impl::s_hoveredRegion.get(provider); + } + } diff --git a/plugins/builtin/include/content/views/view_information.hpp b/plugins/builtin/include/content/views/view_information.hpp index d5c4d411e..a48425878 100644 --- a/plugins/builtin/include/content/views/view_information.hpp +++ b/plugins/builtin/include/content/views/view_information.hpp @@ -21,7 +21,7 @@ namespace hex::plugin::builtin { bool valid = false; TaskHolder task; - prv::Provider *analyzedProvider = nullptr; + const prv::Provider *analyzedProvider = nullptr; Region analysisRegion = { 0, 0 }; ui::RegionType selectionType = ui::RegionType::EntireData; diff --git a/plugins/builtin/include/content/views/view_pattern_editor.hpp b/plugins/builtin/include/content/views/view_pattern_editor.hpp index da53202a4..367736281 100644 --- a/plugins/builtin/include/content/views/view_pattern_editor.hpp +++ b/plugins/builtin/include/content/views/view_pattern_editor.hpp @@ -221,7 +221,10 @@ namespace hex::plugin::builtin { std::array m_accessHistory = {}; u32 m_accessHistoryIndex = 0; - bool replace = false; + bool m_parentHighlightingEnabled = true; + bool m_replaceMode = false; + + static inline std::array m_findHistory; static inline u32 m_findHistorySize = 0; static inline u32 m_findHistoryIndex = 0; diff --git a/plugins/builtin/romfs/lang/en_US.json b/plugins/builtin/romfs/lang/en_US.json index 9c942fd50..3ed1a05ab 100644 --- a/plugins/builtin/romfs/lang/en_US.json +++ b/plugins/builtin/romfs/lang/en_US.json @@ -464,6 +464,7 @@ "hex.builtin.setting.hex_editor.bytes_per_row": "Bytes per row", "hex.builtin.setting.hex_editor.char_padding": "Extra character cell padding", "hex.builtin.setting.hex_editor.highlight_color": "Selection highlight color", + "hex.builtin.setting.hex_editor.pattern_parent_highlighting": "Highlight pattern parents on hover", "hex.builtin.setting.hex_editor.sync_scrolling": "Synchronize editor scroll position", "hex.builtin.setting.imhex": "ImHex", "hex.builtin.setting.imhex.recent_files": "Recent Files", diff --git a/plugins/builtin/source/content/settings_entries.cpp b/plugins/builtin/source/content/settings_entries.cpp index 57351140f..fee3873d9 100644 --- a/plugins/builtin/source/content/settings_entries.cpp +++ b/plugins/builtin/source/content/settings_entries.cpp @@ -767,6 +767,7 @@ namespace hex::plugin::builtin { ContentRegistry::Settings::add("hex.builtin.setting.hex_editor", "", "hex.builtin.setting.hex_editor.byte_padding", 0, 0, 50); ContentRegistry::Settings::add("hex.builtin.setting.hex_editor", "", "hex.builtin.setting.hex_editor.char_padding", 0, 0, 50); + ContentRegistry::Settings::add("hex.builtin.setting.hex_editor", "", "hex.builtin.setting.hex_editor.pattern_parent_highlighting", true); } diff --git a/plugins/builtin/source/content/views/view_hex_editor.cpp b/plugins/builtin/source/content/views/view_hex_editor.cpp index 6de662ef8..24d77bf55 100644 --- a/plugins/builtin/source/content/views/view_hex_editor.cpp +++ b/plugins/builtin/source/content/views/view_hex_editor.cpp @@ -19,6 +19,8 @@ #include #include #include +#include +#include using namespace std::literals::string_literals; @@ -404,8 +406,20 @@ namespace hex::plugin::builtin { }); m_hexEditor.setBackgroundHighlightCallback([this](u64 address, const u8 *data, size_t size) -> std::optional { - if (auto highlight = m_backgroundHighlights->find(address); highlight != m_backgroundHighlights->end()) - return highlight->second; + bool hovered = false; + for (const auto &[id, hoverFunction] : ImHexApi::HexEditor::impl::getHoveringFunctions()) { + if (hoverFunction(m_hexEditor.getProvider(), address, data, size)) { + hovered = true; + break; + } + } + + if (auto highlight = m_backgroundHighlights->find(address); highlight != m_backgroundHighlights->end()) { + if (hovered) + return ImAlphaBlendColors(highlight->second, 0xA0FFFFFF); + else + return highlight->second; + } std::optional result; for (const auto &[id, callback] : ImHexApi::HexEditor::impl::getBackgroundHighlightingFunctions()) { diff --git a/plugins/builtin/source/content/views/view_information.cpp b/plugins/builtin/source/content/views/view_information.cpp index d04cb2e47..52711f040 100644 --- a/plugins/builtin/source/content/views/view_information.cpp +++ b/plugins/builtin/source/content/views/view_information.cpp @@ -15,7 +15,7 @@ namespace hex::plugin::builtin { using namespace hex::literals; ViewInformation::ViewInformation() : View::Window("hex.builtin.view.information.name", ICON_VS_GRAPH_LINE) { - m_analysisData.setOnCreateCallback([](prv::Provider *provider, AnalysisData &data) { + m_analysisData.setOnCreateCallback([](const prv::Provider *provider, AnalysisData &data) { data.analyzedProvider = provider; for (const auto &informationSectionConstructor : ContentRegistry::DataInformation::impl::getInformationSectionConstructors()) { diff --git a/plugins/builtin/source/content/views/view_pattern_data.cpp b/plugins/builtin/source/content/views/view_pattern_data.cpp index c6d502606..72f1142db 100644 --- a/plugins/builtin/source/content/views/view_pattern_data.cpp +++ b/plugins/builtin/source/content/views/view_pattern_data.cpp @@ -37,7 +37,7 @@ namespace hex::plugin::builtin { (*m_patternDrawer)->jumpToPattern(pattern); }); - m_patternDrawer.setOnCreateCallback([this](prv::Provider *provider, auto &drawer) { + m_patternDrawer.setOnCreateCallback([this](const prv::Provider *provider, auto &drawer) { drawer = std::make_unique(); drawer->setSelectionCallback([](const pl::ptrn::Pattern *pattern) { diff --git a/plugins/builtin/source/content/views/view_pattern_editor.cpp b/plugins/builtin/source/content/views/view_pattern_editor.cpp index d522f8447..ea9719f04 100644 --- a/plugins/builtin/source/content/views/view_pattern_editor.cpp +++ b/plugins/builtin/source/content/views/view_pattern_editor.cpp @@ -278,24 +278,24 @@ namespace hex::plugin::builtin { bool openFindPopup = false; ImGui::PushID(&this->m_textEditor); if (clickedMenuFind) { - replace = false; + m_replaceMode = false; openFindPopup = true; } if (clickedMenuReplace) { - replace = true; + m_replaceMode = true; openFindPopup = true; } // shortcuts to open the find/replace popup if (ImGui::IsItemHovered()) { if (ImGui::IsKeyPressed(ImGuiKey_F, false) && ImGui::GetIO().KeyCtrl) { - replace = false; + m_replaceMode = false; openFindPopup = true; } if (ImGui::IsKeyPressed(ImGuiKey_H, false) && ImGui::GetIO().KeyCtrl) { - replace = true; + m_replaceMode = true; openFindPopup = true; } } @@ -350,7 +350,7 @@ namespace hex::plugin::builtin { windowPosForPopup.x += windowSize.x - popupSize.x; findReplaceHandler->SetFindWindowPos(windowPosForPopup); - if (replace) { + if (m_replaceMode) { // Remove one window padding popupSize.y -= style.WindowPadding.y; // Add the replace window height @@ -604,10 +604,10 @@ namespace hex::plugin::builtin { ImGui::TableNextRow(); ImGui::TableNextColumn(); - bool oldReplace = replace; - ImGuiExt::DimmedIconToggle(ICON_VS_TRIANGLE_DOWN, ICON_VS_TRIANGLE_RIGHT, &replace); - if (oldReplace != replace) { - if (replace) + bool oldReplace = m_replaceMode; + ImGuiExt::DimmedIconToggle(ICON_VS_TRIANGLE_DOWN, ICON_VS_TRIANGLE_RIGHT, &m_replaceMode); + if (oldReplace != m_replaceMode) { + if (m_replaceMode) requestFocusReplace = true; else requestFocusFind = true; @@ -644,7 +644,7 @@ namespace hex::plugin::builtin { findReplaceHandler->SetFindWord(&m_textEditor,findWord); } - if ((!replace && requestFocus) || requestFocusFind) { + if ((!m_replaceMode && requestFocus) || requestFocusFind) { ImGui::SetKeyboardFocusHere(-1); requestFocus = false; requestFocusFind = false; @@ -755,7 +755,7 @@ namespace hex::plugin::builtin { if (ImGuiExt::IconButton(ICON_VS_ARROW_UP, ImVec4(1, 1, 1, 1))) upArrowFind = true; - if (replace) { + if (m_replaceMode) { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::TableNextColumn(); @@ -1837,6 +1837,10 @@ namespace hex::plugin::builtin { } }); + ContentRegistry::Settings::onChange("hex.builtin.setting.interface", "hex.builtin.setting.interface.parent_highlighting", [this](const ContentRegistry::Settings::SettingsValue &value) { + m_parentHighlightingEnabled = bool(value.get(false)); + }); + ImHexApi::HexEditor::addBackgroundHighlightingProvider([this](u64 address, const u8 *data, size_t size, bool) -> std::optional { hex::unused(data, size); @@ -1859,6 +1863,26 @@ namespace hex::plugin::builtin { return color; }); + ImHexApi::HexEditor::addHoverHighlightProvider([this](const prv::Provider *provider, u64 address, const u8 *, size_t) { + if (!m_parentHighlightingEnabled) return false; + + const auto &runtime = ContentRegistry::PatternLanguage::getRuntime(); + + if (auto hoveredRegion = ImHexApi::HexEditor::getHoveredRegion(provider)) { + for (const auto &pattern : runtime.getPatternsAtAddress(hoveredRegion->getStartAddress())) { + const pl::ptrn::Pattern * checkPattern = pattern; + if (auto parent = checkPattern->getParent(); parent != nullptr) + checkPattern = parent; + + if (checkPattern->getOffset() <= address && checkPattern->getOffset() + checkPattern->getSize() > address) { + return true; + } + } + } + + return false; + }); + ImHexApi::HexEditor::addTooltipProvider([this](u64 address, const u8 *data, size_t size) { hex::unused(data, size); diff --git a/plugins/ui/source/ui/hex_editor.cpp b/plugins/ui/source/ui/hex_editor.cpp index ff0954cb0..25c8d5ddc 100644 --- a/plugins/ui/source/ui/hex_editor.cpp +++ b/plugins/ui/source/ui/hex_editor.cpp @@ -9,6 +9,7 @@ #include #include +#include namespace hex::ui { @@ -421,6 +422,7 @@ namespace hex::ui { ImGuiExt::TextFormattedCentered("{}", "hex.ui.hex_editor.no_bytes"_lang); } + Region hoveredCell = Region::Invalid(); if (ImGui::BeginChild("Hex View", size, ImGuiChildFlags_None, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) { this->drawScrollbar(CharacterSize); @@ -596,6 +598,11 @@ namespace hex::ui { this->drawCell(byteAddress, &bytes[x * bytesPerCell], bytesPerCell, cellHovered, CellType::Hex); else ImGuiExt::TextFormatted("{}", std::string(maxCharsPerCell, '?')); + + if (cellHovered) { + hoveredCell = { byteAddress, bytesPerCell }; + } + ImGui::PopItemWidth(); ImGui::PopStyleVar(); } @@ -650,6 +657,11 @@ namespace hex::ui { ImGuiExt::TextFormattedDisabled("{}", m_unknownDataCharacter); else this->drawCell(byteAddress, &bytes[x], 1, cellHovered, CellType::ASCII); + + if (cellHovered) { + hoveredCell = { byteAddress, bytesPerCell }; + } + ImGui::PopItemWidth(); ImGui::PopStyleVar(); } @@ -733,6 +745,10 @@ namespace hex::ui { ImGui::Dummy({ 0, 0 }); this->handleSelection(address, data.advance, &bytes[address % m_bytesPerRow], cellHovered); + + if (cellHovered) { + hoveredCell = { address, data.advance }; + } } } @@ -798,6 +814,8 @@ namespace hex::ui { } ImGui::EndChild(); + ImHexApi::HexEditor::impl::setHoveredRegion(m_provider, hoveredCell); + m_shouldScrollToSelection = false; }