1
0
mirror of synced 2024-11-24 15:50:16 +01:00

feat: Added option to highlight pattern parents in the hex editor when hovering

This commit is contained in:
WerWolv 2024-02-25 14:30:56 +01:00
parent dac45659c0
commit ea601a7d03
12 changed files with 141 additions and 25 deletions

View File

@ -73,15 +73,18 @@ namespace hex {
namespace impl {
using HighlightingFunction = std::function<std::optional<color_t>(u64, const u8*, size_t, bool)>;
using HoveringFunction = std::function<bool(const prv::Provider *, u64, const u8*, size_t)>;
const std::map<u32, Highlighting>& getBackgroundHighlights();
const std::map<u32, HighlightingFunction>& getBackgroundHighlightingFunctions();
const std::map<u32, Highlighting>& getForegroundHighlights();
const std::map<u32, HighlightingFunction>& getForegroundHighlightingFunctions();
const std::map<u32, HoveringFunction>& getHoveringFunctions();
const std::map<u32, Tooltip>& getTooltips();
const std::map<u32, TooltipFunction>& getTooltipFunctions();
void setCurrentSelection(const std::optional<ProviderRegion> &region);
void setHoveredRegion(const prv::Provider *provider, const Region &region);
}
/**
@ -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<u8> data, Region region = Region::Invalid());
/**
* @brief Gets the currently hovered cell region in the Hex Editor
* @return
*/
const std::optional<Region>& getHoveredRegion(const prv::Provider *provider);
}
/* Functions to interact with Bookmarks */

View File

@ -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<void(prv::Provider *, T&)> callback) {
void setOnCreateCallback(std::function<void(const prv::Provider *, T&)> callback) {
m_onCreateCallback = std::move(callback);
}
@ -120,8 +120,8 @@ namespace hex {
}
private:
std::map<prv::Provider *, T> m_data;
std::function<void(prv::Provider *, T&)> m_onCreateCallback;
std::map<const prv::Provider *, T> m_data;
std::function<void(const prv::Provider *, T&)> m_onCreateCallback;
};
}

View File

@ -2,10 +2,11 @@
#include <hex/api/event_manager.hpp>
#include <hex/api/task_manager.hpp>
#include <hex/providers/provider.hpp>
#include <hex/helpers/fmt.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/helpers/auto_reset.hpp>
#include <hex/providers/provider_data.hpp>
#include <hex/providers/provider.hpp>
#include <wolv/utils/string.hpp>
@ -67,11 +68,24 @@ namespace hex {
return *s_tooltipFunctions;
}
static AutoReset<std::map<u32, HoveringFunction>> s_hoveringFunctions;
const std::map<u32, HoveringFunction>& getHoveringFunctions() {
return *s_hoveringFunctions;
}
static AutoReset<std::optional<ProviderRegion>> s_currentSelection;
void setCurrentSelection(const std::optional<ProviderRegion> &region) {
*s_currentSelection = region;
}
static PerProvider<std::optional<Region>> s_hoveredRegion;
void setHoveredRegion(const prv::Provider *provider, const Region &region) {
if (region == Region::Invalid())
s_hoveredRegion.get(provider).reset();
else
s_hoveredRegion.get(provider) = region;
}
}
u32 addBackgroundHighlight(const Region &region, 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<u8> data, Region region) {
RequestAddVirtualFile::post(path, std::move(data), region);
}
const std::optional<Region>& getHoveredRegion(const prv::Provider *provider) {
return impl::s_hoveredRegion.get(provider);
}
}

View File

@ -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;

View File

@ -221,7 +221,10 @@ namespace hex::plugin::builtin {
std::array<AccessData, 512> m_accessHistory = {};
u32 m_accessHistoryIndex = 0;
bool replace = false;
bool m_parentHighlightingEnabled = true;
bool m_replaceMode = false;
static inline std::array<std::string,256> m_findHistory;
static inline u32 m_findHistorySize = 0;
static inline u32 m_findHistoryIndex = 0;

View File

@ -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",

View File

@ -767,6 +767,7 @@ namespace hex::plugin::builtin {
ContentRegistry::Settings::add<Widgets::SliderInteger>("hex.builtin.setting.hex_editor", "", "hex.builtin.setting.hex_editor.byte_padding", 0, 0, 50);
ContentRegistry::Settings::add<Widgets::SliderInteger>("hex.builtin.setting.hex_editor", "", "hex.builtin.setting.hex_editor.char_padding", 0, 0, 50);
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.hex_editor", "", "hex.builtin.setting.hex_editor.pattern_parent_highlighting", true);
}

View File

@ -19,6 +19,8 @@
#include <popups/popup_file_chooser.hpp>
#include <content/popups/popup_blocking_task.hpp>
#include <content/popups/hex_editor/popup_hex_editor_find.hpp>
#include <wolv/utils/lock.hpp>
#include <pl/patterns/pattern.hpp>
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<color_t> {
if (auto highlight = m_backgroundHighlights->find(address); highlight != m_backgroundHighlights->end())
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<color_t> result;
for (const auto &[id, callback] : ImHexApi::HexEditor::impl::getBackgroundHighlightingFunctions()) {

View File

@ -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()) {

View File

@ -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<ui::PatternDrawer>();
drawer->setSelectionCallback([](const pl::ptrn::Pattern *pattern) {

View File

@ -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<int>(false));
});
ImHexApi::HexEditor::addBackgroundHighlightingProvider([this](u64 address, const u8 *data, size_t size, bool) -> std::optional<color_t> {
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);

View File

@ -9,6 +9,7 @@
#include <wolv/utils/guards.hpp>
#include <fonts/codicons_font.h>
#include <hex/providers/buffered_reader.hpp>
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;
}