feat: Integrate the interactive help option and add a help text for every view

This commit is contained in:
WerWolv 2025-11-30 16:42:01 +01:00
parent 989f7f7678
commit 9ae233a41c
46 changed files with 204 additions and 9 deletions

View File

@ -14,6 +14,7 @@
#include <map>
#include <string>
#include <hex/api/tutorial_manager.hpp>
namespace hex {
@ -147,11 +148,17 @@ namespace hex {
public:
explicit Window(UnlocalizedString unlocalizedName, const char *icon) : View(std::move(unlocalizedName), icon) {}
/**
* @brief Draws help text for the view
*/
virtual void drawHelpText() = 0;
void draw() final {
if (this->shouldDraw()) {
ImGui::SetNextWindowSizeConstraints(this->getMinSize(), this->getMaxSize());
const auto title = fmt::format("{} {}", this->getIcon(), View::toWindowName(this->getUnlocalizedName()));
if (ImGui::Begin(title.c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse | this->getWindowFlags())) {
TutorialManager::setLastItemInteractiveHelpPopup([this]{ this->drawHelpText(); });
this->drawContent();
}
ImGui::End();

View File

@ -97,6 +97,9 @@ namespace hex {
EventImGuiElementRendered::subscribe([](ImGuiID id, const std::array<float, 4> bb){
const auto boundingBox = ImRect(bb[0], bb[1], bb[2], bb[3]);
if (!ImGui::IsItemVisible())
return;
{
const auto element = hex::s_highlights->find(id);
if (element != hex::s_highlights->end()) {

View File

@ -12,6 +12,7 @@ namespace hex::plugin::builtin {
void drawContent() override;
void drawAlwaysVisibleContent() override;
void drawHelpText() override;
[[nodiscard]] bool shouldDraw() const override { return true; }
[[nodiscard]] bool hasViewMenuItemEntry() const override { return false; }

View File

@ -15,6 +15,7 @@ namespace hex::plugin::builtin {
~ViewBookmarks() override;
void drawContent() override;
void drawHelpText() override;
private:
struct Bookmark {

View File

@ -23,6 +23,8 @@ namespace hex::plugin::builtin {
return ContentRegistry::Views::getViewByName("hex.builtin.view.hex_editor.name");
}
void drawHelpText() override;
private:
struct InspectorCacheEntry {
UnlocalizedString unlocalizedName;

View File

@ -42,6 +42,7 @@ namespace hex::plugin::builtin {
~ViewDataProcessor() override;
void drawContent() override;
void drawHelpText() override;
static nlohmann::json saveNode(const dp::Node *node);
static nlohmann::json saveNodes(const Workspace &workspace);

View File

@ -27,6 +27,8 @@ namespace hex::plugin::builtin {
return ContentRegistry::Views::getViewByName("hex.builtin.view.hex_editor.name");
}
void drawHelpText() override;
private:
using Occurrence = hex::ContentRegistry::DataFormatter::impl::FindOccurrence;

View File

@ -79,6 +79,8 @@ namespace hex::plugin::builtin {
m_currPopup.reset();
}
void drawHelpText() override;
private:
void drawPopup();

View File

@ -14,6 +14,7 @@ namespace hex::plugin::builtin {
~ViewHighlightRules() override = default;
void drawContent() override;
void drawHelpText() override;
[[nodiscard]] bool hasViewMenuItemEntry() const override { return false; }

View File

@ -14,6 +14,8 @@ namespace hex::plugin::builtin {
void drawContent() override;
void drawHelpText() override;
private:
void analyze();

View File

@ -10,6 +10,7 @@ namespace hex::plugin::builtin {
~ViewLogs() override = default;
void drawContent() override;
void drawHelpText() override;
[[nodiscard]] bool shouldDraw() const override { return true; }
[[nodiscard]] bool hasViewMenuItemEntry() const override { return false; }

View File

@ -13,6 +13,7 @@ namespace hex::plugin::builtin {
void drawContent() override;
void drawAlwaysVisibleContent() override;
void drawHelpText() override;
private:
u64 m_selectedPatch = 0x00;

View File

@ -24,6 +24,8 @@ namespace hex::plugin::builtin {
return ContentRegistry::Views::getViewByName("hex.builtin.view.pattern_editor.name");
}
void drawHelpText() override;
private:
bool m_rowColoring = false;
u32 m_maxFilterItems = 128;

View File

@ -78,6 +78,8 @@ namespace hex::plugin::builtin {
Deny
};
void drawHelpText() override;
private:
class PopupAcceptPattern;

View File

@ -52,6 +52,7 @@ namespace hex::plugin::builtin {
~ViewStore() override = default;
void drawContent() override;
void drawHelpText() override;
[[nodiscard]] bool shouldDraw() const override { return true; }
[[nodiscard]] bool hasViewMenuItemEntry() const override { return false; }

View File

@ -12,6 +12,7 @@ namespace hex::plugin::builtin {
~ViewThemeManager() override = default;
void drawContent() override;
void drawHelpText() override;
[[nodiscard]] bool shouldDraw() const override { return true; }
[[nodiscard]] bool hasViewMenuItemEntry() const override { return false; }

View File

@ -14,6 +14,7 @@ namespace hex::plugin::builtin {
void drawContent() override;
void drawAlwaysVisibleContent() override;
void drawHelpText() override;
private:
std::vector<ContentRegistry::Tools::impl::Entry>::const_iterator m_dragStartIterator;

View File

@ -12,6 +12,7 @@ namespace hex::plugin::builtin {
~ViewTutorials() override = default;
void drawContent() override;
void drawHelpText() override;
[[nodiscard]] bool shouldDraw() const override { return true; }
[[nodiscard]] bool hasViewMenuItemEntry() const override { return false; }

View File

@ -27,6 +27,7 @@
#include <toasts/toast_notification.hpp>
#include <csignal>
#include <fonts/tabler_icons.hpp>
namespace hex::plugin::builtin {
@ -45,6 +46,9 @@ namespace hex::plugin::builtin {
hex::openWebpage("https://github.com/WerWolv/ImHex/discussions/categories/feedback");
});
ContentRegistry::UserInterface::addTitleBarButton(ICON_TA_HELP, ImGuiCustomCol_ToolbarGray, "hex.builtin.title_bar_button.interactive_help", []{
TutorialManager::startHelpHover();
});
}
static void drawGlobalPopups() {

View File

@ -468,4 +468,7 @@ namespace hex::plugin::builtin {
}
}
void ViewAchievements::drawHelpText() {
ImGuiExt::TextFormattedWrapped("This is the Achievements view. Before you ask yourself what achievements are doing in a hex editor: The Achievements are meant to be a fun way to learn about the various features of ImHex without having to read through the entire documentation. Read through the achievements, try to unlock them all, and have fun!");
}
}

View File

@ -639,4 +639,18 @@ namespace hex::plugin::builtin {
});
}
void ViewBookmarks::drawHelpText() {
ImGuiExt::TextFormattedWrapped("All your created Bookmarks will be listed in here.");
ImGui::NewLine();
ImGuiExt::TextFormattedWrapped("Bookmarks provide an easy way to mark important regions in your binary and quickly navigate to them later. You can also name them, add further information through comments or change their color.");
ImGui::NewLine();
ImGuiExt::TextFormattedWrapped(
"To create a Bookmark, select a byte region in the Hex Editor view and use the {} option in the {} menu or use the shortcut '{}'.",
"hex.builtin.menu.edit.bookmark.create"_lang, "hex.builtin.menu.edit"_lang,
ShortcutManager::getShortcutByName(
{ "hex.builtin.menu.edit", "hex.builtin.menu.edit.bookmark.create" },
ContentRegistry::Views::getViewByName("hex.builtin.view.hex_editor.name")
).toString()
);
}
}

View File

@ -608,5 +608,13 @@ namespace hex::plugin::builtin {
return displayFunction;
}
void ViewDataInspector::drawHelpText() {
ImGuiExt::TextFormattedWrapped("This view decodes bytes, starting from the currently selected address in the Hex Editor View, as various different data types.");
ImGui::NewLine();
ImGuiExt::TextFormattedWrapped("The decoding here may or may not make sense depending on the actual data at the selected address but it can give a rough idea of what kind of data is present. If certain types make no sense, they can be hidden by entering the editing mode (pencil icon) and clicking the eye icon next to the corresponding row.");
ImGui::NewLine();
ImGuiExt::TextFormattedWrapped("By clicking on a row, the corresponding bytes will be selected in the Hex Editor View and you can use the navigation buttons at the top to move to the next or previous value, assuming you're dealing with a list of such values.");
ImGuiExt::TextFormattedWrapped("Double-clicking a row (if editable) will allow you to change the value and write it back to the underlying data. Some types may also have additional options available in the context menu (right-click on a row).");
}
}

View File

@ -1293,4 +1293,13 @@ namespace hex::plugin::builtin {
}
}
void ViewDataProcessor::drawHelpText() {
ImGuiExt::TextFormattedWrapped("This view lets you create and evaluate data processing pipelines using a node-based interface.\n\n");
ImGui::NewLine();
ImGuiExt::TextFormattedWrapped("Right click anywhere in the workspace to add nodes. Connect the nodes by dragging links between their input and output attributes. "
"Nodes that have no output attributes are considered end nodes and will produce the final output of the pipeline.\n\n"
"Click the green play button at the bottom to evaluate the current pipeline. If any errors occur during evaluation, "
"the affected node will be highlighted in red and an error message will be shown when hovering over it.");
}
}

View File

@ -1149,4 +1149,16 @@ namespace hex::plugin::builtin {
}
}
void ViewFind::drawHelpText() {
ImGuiExt::TextFormattedWrapped("This view lets you search for all occurrences of a specific pattern in the opened data source.");
ImGui::NewLine();
ImGuiExt::TextFormattedWrapped(
"- Strings: Search for all strings matching the specified criteria.\n"
"- Sequences: Search for a specific string character sequence in various encodings.\n"
"- Regex: Search for all strings matching the specified regular expression.\n"
"- Binary Pattern: Search for a specific byte pattern with wildcards.\n"
"- Numeric Value: Search for numeric values within a specified range in various formats."
);
}
}

View File

@ -1738,4 +1738,16 @@ namespace hex::plugin::builtin {
this);
}
void ViewHexEditor::drawHelpText() {
ImGuiExt::TextFormattedWrapped("This is the main view you'll be working with. It shows the raw data of the currently opened data source together with any highlighting that may be generated by other parts of ImHex.");
ImGui::NewLine();
ImGuiExt::TextFormattedWrapped("You can edit cells by simply double clicking them. By default, this will overwrite the existing value of that cell. To insert new bytes instead, toggle the {} option in the {} menu.",
"hex.builtin.view.hex_editor.menu.edit.insert_mode"_lang, "hex.builtin.menu.edit"_lang
);
ImGui::NewLine();
ImGuiExt::TextFormattedWrapped("Many more options can be found by right clicking anywhere in this view, or by using the toggles and options in the footer bar. This includes options to change the number of bytes shown per row, the encoding used to display text, the minimap and the cell's data format.",
"hex.builtin.view.hex_editor.menu.edit.insert_mode"_lang, "hex.builtin.menu.edit"_lang
);
}
}

View File

@ -327,4 +327,13 @@ namespace hex::plugin::builtin {
}
}
void ViewHighlightRules::drawHelpText() {
ImGuiExt::TextFormattedWrapped("This view allows you to create custom highlighting rules based on mathematical expressions. Each rule can contain multiple expressions, each defining a color and a mathematical condition. When the condition evaluates to true for a given byte or range of bytes, those bytes's text will be highlighted with the specified color in the hex editor.\n\n"
"You can use the following variables in your expressions:\n"
"- 'value': The byte value at the current offset.\n"
"- 'offset': The current byte offset within the data source.\n\n"
"Examples of expressions:\n"
"- 'value == 0x90' : Highlights all x86 NOP instructions (0x90).\n"
"- 'value >= 0x41 && value <= 0x5A' : Highlights all uppercase ASCII letters.");
}
}

View File

@ -240,4 +240,13 @@ namespace hex::plugin::builtin {
ImGui::EndChild();
}
void ViewInformation::drawHelpText() {
ImGuiExt::TextFormattedWrapped("This view provides various analyses and information about the currently opened data source. "
"Use the settings panel to select which sections to analyze and the region to analyze. "
"Click the 'Analyze' button to start the analysis.");
ImGui::NewLine();
ImGuiExt::TextFormattedWrapped("The results of the analysis will be displayed in separate sections below the settings panel. "
"Each section can be enabled or disabled individually, and some sections may have additional settings available via the gear icon.");
}
}

View File

@ -71,4 +71,7 @@ namespace hex::plugin::builtin {
}
}
void ViewLogs::drawHelpText() {
ImGuiExt::TextUnformattedCentered("This view displays all log messages generated by ImHex. In general it contains the same information as if you ran ImHex from the command line.");
}
}

View File

@ -219,4 +219,11 @@ namespace hex::plugin::builtin {
}
}
void ViewPatches::drawHelpText() {
ImGuiExt::TextFormattedWrapped("This view shows a list of all patches (modifications, insertions, deletions) that were made to the current data source so far.");
ImGui::NewLine();
ImGuiExt::TextFormattedWrapped("The small arrow next to a patch indicates the current position in the undo/redo stack. When undoing operations, the arrow will move downwards and modifying any data will create new patches from the current position, discarding any patches above it.");
ImGuiExt::TextFormattedWrapped("Hovering over a patch will also show a tooltip with more detailed information about the patch and clicking on a patch will select the modified region in the hex editor.");
}
}

View File

@ -379,4 +379,10 @@ namespace hex::plugin::builtin {
}
}
void ViewPatternData::drawHelpText() {
ImGuiExt::TextFormattedWrapped("This view displays the pattern tree generated by the previously executed pattern script. It only becomes active once a pattern has been successfully executed.");
ImGui::NewLine();
ImGuiExt::TextFormattedWrapped("Clicking on a pattern in the tree will select the corresponding bytes in the Hex Editor view and jump to its definition in the Pattern Editor view. Additionally you can edit the values of patterns by double clicking them which will automatically update the bytes in your loaded data source.");
}
}

View File

@ -404,7 +404,7 @@ namespace hex::plugin::builtin {
}
void ViewPatternEditor::drawPatternSettings() {
const auto size = scaled(500, 150);
const auto size = scaled(0, 150);
if (ImGuiExt::BeginSubWindow("hex.builtin.view.pattern_editor.settings"_lang, nullptr, size)) {
this->drawVariableSettings(*m_patternVariables);
@ -2624,6 +2624,13 @@ namespace hex::plugin::builtin {
savePatternAsNewFile(trackFile);
}
void ViewPatternEditor::drawHelpText() {
ImGuiExt::TextFormattedWrapped("This is the Pattern Editor view, where you can write and edit pattern matching code to analyze the loaded data. For more information on how to write pattern code, please refer to the official documentation and the check out the existing patterns included with ImHex.");
ImGui::NewLine();
ImGuiExt::TextFormattedWrapped("This view works in close conjunction with the Hex Editor view and the Pattern Data view. When you finished writing your code, click on the Play button at the bottom of the view or press {} to evaluate the pattern.",
ShortcutManager::getShortcutByName({ "hex.builtin.menu.edit","hex.builtin.view.pattern_editor.menu.edit.run_pattern" }).toString()
);
ImGuiExt::TextFormattedWrapped("This will execute your code, output any log messages to the console window below and create a pattern tree that gets displayed in the Pattern Data view and highlights matching regions in the Hex Editor view.");
}
}

View File

@ -401,4 +401,8 @@ namespace hex::plugin::builtin {
m_download = {};
}
void ViewStore::drawHelpText() {
ImGuiExt::TextFormattedWrapped("This view lets you download and update additional content for ImHex, such as pattern files, magic files, themes and more. All content is provided by the ImHex community and can be freely used within ImHex.");
}
}

View File

@ -127,4 +127,8 @@ namespace hex::plugin::builtin {
}
}
void ViewThemeManager::drawHelpText() {
ImGuiExt::TextFormattedWrapped("This view allows you to customize the colors and styles of the ImHex interface. You can modify individual colors and styles and save them as themes that you can load later or share with others.");
}
}

View File

@ -105,4 +105,11 @@ namespace hex::plugin::builtin {
}
}
void ViewTools::drawHelpText() {
ImGuiExt::TextFormattedWrapped("This view contains various standalone tools that didn't fit anywhere else but are useful nonetheless.");
ImGui::NewLine();
ImGuiExt::TextFormattedWrapped("Click on the arrow icon in the title bar of each tool to open or collapse it. When collapsed, you can drag the tool out of this window to create a separate floating window which can also be docked anywhere you like.");
}
}

View File

@ -73,4 +73,7 @@ namespace hex::plugin::builtin {
}
}
void ViewTutorials::drawHelpText() {
ImGuiExt::TextFormattedWrapped("This view contains all available tutorials to help you get started with ImHex. Select a tutorial from the list and click the 'Start Tutorial' button to begin.");
}
}

View File

@ -20,6 +20,7 @@ namespace hex::plugin::diffing {
void drawContent() override;
void drawAlwaysVisibleContent() override;
void drawHelpText() override;
ImGuiWindowFlags getWindowFlags() const override { return ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse; }
public:

View File

@ -543,4 +543,15 @@ namespace hex::plugin::diffing {
this
);
}
void ViewDiff::drawHelpText() {
ImGuiExt::TextFormattedWrapped("This view allows you to do binary comparisons between two data sources. Select the data sources you want to compare from the dropdown menus at the top. Once both data sources are selected, the differences will be calculated automatically.");
ImGui::NewLine();
ImGuiExt::TextFormattedWrapped("Differences are highlighted in the hex editors. Green indicates added bytes, red indicates removed bytes, and yellow indicates modified bytes. All differences are also listed in the table below the hex editors, where you can click on a difference to jump to it in both hex editors.");
ImGui::NewLine();
ImGuiExt::TextFormattedWrapped(
"By default, a simple byte-by-byte comparison algorithm is used. This is quick but will only identify byte modifications but doesn't match insertions or deletions.\n"
"For a more sophisticated comparison, you can select a different diffing algorithm from the settings menu (gear icon)."
);
}
}

View File

@ -19,6 +19,7 @@ namespace hex::plugin::disasm {
~ViewDisassembler() override;
void drawContent() override;
void drawHelpText() override;
private:
TaskHolder m_disassemblerTask;

View File

@ -308,4 +308,9 @@ namespace hex::plugin::disasm {
}
}
void ViewDisassembler::drawHelpText() {
ImGuiExt::TextFormattedWrapped("This view lets you disassemble byte regions into assembly instructions of various different architectures.");
ImGui::NewLine();
ImGuiExt::TextFormattedWrapped("Select the desired Architecture from the tabs in the settings panel and configure its options as needed. Clicking the \"Disassemble\" button will disassemble the selected region (or the entire data if no region is selected) and display the resulting instructions in a table below.");
}
}

View File

@ -12,6 +12,7 @@ namespace hex::plugin::hashes {
~ViewHashes() override;
void drawContent() override;
void drawHelpText() override;
private:
bool importHashes(prv::Provider *provider, const nlohmann::json &json);

View File

@ -342,4 +342,10 @@ namespace hex::plugin::hashes {
return true;
}
void ViewHashes::drawHelpText() {
ImGuiExt::TextFormattedWrapped("This view allows you to compute various hashes (MD5, SHA1, etc.) on selected data regions.");
ImGui::NewLine();
ImGuiExt::TextFormattedWrapped("Add a new hash function by double clicking on the last row in the Hashes table and configure it to your needs. You can add multiple hash functions and see their results in real-time as you select different regions of data in the hex editor. "
"Hold SHIFT while selecting data to see hash results in the tooltip.");
}
}

View File

@ -172,6 +172,7 @@ public:
void drawContent() override {
m_drawFunction();
}
void drawHelpText() override {}
private:
DrawFunction m_drawFunction;

View File

@ -17,6 +17,7 @@ namespace hex::plugin::windows {
~ViewTTYConsole() override = default;
void drawContent() override;
void drawHelpText() override;
private:
void drawSettings();

View File

@ -459,4 +459,9 @@ namespace hex::plugin::windows {
m_transmitting = false;
}
void ViewTTYConsole::drawHelpText() {
ImGuiExt::TextFormattedWrapped("This view can send and receive data over a Serial (TTY) port.");
ImGui::NewLine();
ImGuiExt::TextFormattedWrapped("Connect your device to a Serial Port (or a USB port with a Serial adapter) and configure the connection settings on the left side. Once connected, you can send and receive data using the console below.");
}
}

View File

@ -17,6 +17,7 @@ namespace hex::plugin::yara {
~ViewYara() override;
void drawContent() override;
void drawHelpText() override;
private:
PerProvider<std::vector<std::pair<std::fs::path, std::fs::path>>> m_rulePaths;

View File

@ -337,4 +337,13 @@ namespace hex::plugin::yara {
});
}
void ViewYara::drawHelpText() {
ImGuiExt::TextFormattedWrapped("This view allows you to apply YARA rules to the currently opened file and highlights matched regions.");
ImGui::NewLine();
ImGuiExt::TextFormattedWrapped(
"You can add YARA rules by clicking the + button in the top right corner of the view. "
"This will open a file chooser where you can select one or more YARA files to add."
"For further information on how to write YARA rules, please refer to its official documentation."
);
}
}