feat: Added simplified pattern value editor

This commit is contained in:
WerWolv 2025-07-24 23:29:13 +02:00
parent ca82fafac5
commit e0180b718f
8 changed files with 331 additions and 121 deletions

@ -1 +1 @@
Subproject commit a3f3c1f40ed4860f1c163269647c3dfd37078eb2
Subproject commit 44cbfa2454105b714e3d71471e06f33ef5154373

View File

@ -24,6 +24,7 @@ namespace hex::plugin::builtin {
PerProvider<std::map<u64, std::unique_ptr<ui::PatternDrawer>>> m_patternDrawer;
Region m_hoveredPatternRegion = Region::Invalid();
ui::PatternValueEditor m_patternValueEditor;
};
}

View File

@ -141,16 +141,67 @@ namespace hex::plugin::builtin {
if (ImGui::BeginPopup("##PatternDataContextMenu")) {
if (ImGui::MenuItemEx("hex.builtin.view.pattern_data.section.view_raw"_lang, ICON_VS_OPEN_PREVIEW)) {
const auto &sections = runtime.getSections();
if (auto it = sections.find(selectedSection); it != sections.end()) {
const auto &[id, section] = *it;
ImHexApi::Provider::add<prv::MemoryProvider>(section.data, section.name);
if (TRY_LOCK(ContentRegistry::PatternLanguage::getRuntimeLock())) {
const auto &sections = runtime.getSections();
if (auto it = sections.find(selectedSection); it != sections.end()) {
const auto &[id, section] = *it;
ImHexApi::Provider::add<prv::MemoryProvider>(section.data, section.name);
}
}
}
ImGui::EndPopup();
}
}
constexpr static auto SimplifiedEditorAttribute = "hex::editor_export";
if (TRY_LOCK(ContentRegistry::PatternLanguage::getRuntimeLock()) && patternsValid) {
const auto &patterns = runtime.getPatternsWithAttribute(SimplifiedEditorAttribute);
if (!patterns.empty()) {
const auto tabName = "hex.builtin.view.pattern_data.simplified_editor"_lang;
ImGui::TabItemSpacing("##spacing", 0, ImGui::GetContentRegionAvail().x - ImGui::TabItemCalcSize(tabName, false).x);
if (ImGui::BeginTabItem(tabName, nullptr, ImGuiTabItemFlags_Trailing)) {
for (const auto &pattern : patterns) {
try {
const auto attribute = pattern->getAttributeArguments(SimplifiedEditorAttribute);
const auto name = attribute.size() >= 1 ? attribute[0].toString() : pattern->getDisplayName();
const auto description = attribute.size() >= 2 ? attribute[1].toString() : pattern->getComment();
const auto widgetPos = 200_scaled;
ImGui::TextUnformatted(name.c_str());
ImGui::SameLine(0, 20_scaled);
if (ImGui::GetCursorPosX() < widgetPos)
ImGui::SetCursorPosX(widgetPos);
ImGui::PushStyleVarY(ImGuiStyleVar_FramePadding, 0);
ImGui::PushItemWidth(-50_scaled);
pattern->accept(m_patternValueEditor);
ImGui::PopItemWidth();
ImGui::PopStyleVar();
if (!description.empty()) {
ImGui::PushFont(nullptr, ImGui::GetFontSize() * 0.8F);
ImGui::BeginDisabled();
ImGui::Indent();
ImGui::TextWrapped("%s", description.c_str());
ImGui::Unindent();
ImGui::EndDisabled();
ImGui::PopFont();
}
ImGui::Separator();
} catch (const std::exception &e) {
ImGui::TextUnformatted(pattern->getDisplayName().c_str());
ImGui::TextUnformatted(e.what());
}
}
ImGui::EndTabItem();
}
}
}
ImGui::EndTabBar();
}

View File

@ -12,6 +12,7 @@ add_imhex_plugin(
source/ui/pattern_drawer.cpp
source/ui/visualizer_drawer.cpp
source/ui/menu_items.cpp
source/ui/pattern_value_editor.cpp
INCLUDES
include
LIBRARIES

View File

@ -8,9 +8,11 @@
#include <pl/pattern_visitor.hpp>
#include <pl/formatters.hpp>
#include <pl/patterns/pattern_error.hpp>
#include <set>
#include <pl/patterns/pattern_error.hpp>
#include <ui/pattern_value_editor.hpp>
struct ImGuiTableSortSpecs;
@ -20,9 +22,12 @@ namespace hex::ui {
public:
PatternDrawer() {
m_formatters = pl::gen::fmt::createFormatters();
m_valueEditor = PatternValueEditor([this]() {
this->resetEditing();
});
}
virtual ~PatternDrawer() = default;
~PatternDrawer() override = default;
void draw(const std::vector<std::shared_ptr<pl::ptrn::Pattern>> &patterns, const pl::PatternLanguage *runtime = nullptr, float height = 0.0F);
@ -111,6 +116,7 @@ namespace hex::ui {
const pl::ptrn::Pattern *m_editingPattern = nullptr;
u64 m_editingPatternOffset = 0;
hex::ui::VisualizerDrawer m_visualizerDrawer;
hex::ui::PatternValueEditor m_valueEditor;
TreeStyle m_treeStyle = TreeStyle::Default;
bool m_rowColoring = false;

View File

@ -0,0 +1,39 @@
#pragma once
#include <hex.hpp>
#include <pl/pattern_visitor.hpp>
#include <functional>
namespace hex::ui {
class PatternValueEditor : public pl::PatternVisitor {
public:
PatternValueEditor() = default;
explicit PatternValueEditor(const std::function<void()>& onEditCallback) : m_onEditCallback(onEditCallback) {}
void visit(pl::ptrn::PatternArrayDynamic& pattern) override;
void visit(pl::ptrn::PatternArrayStatic& pattern) override;
void visit(pl::ptrn::PatternBitfield& pattern) override;
void visit(pl::ptrn::PatternBitfieldField& pattern) override;
void visit(pl::ptrn::PatternBitfieldArray& pattern) override;
void visit(pl::ptrn::PatternBoolean& pattern) override;
void visit(pl::ptrn::PatternCharacter& pattern) override;
void visit(pl::ptrn::PatternEnum& pattern) override;
void visit(pl::ptrn::PatternFloat& pattern) override;
void visit(pl::ptrn::PatternPadding& pattern) override;
void visit(pl::ptrn::PatternPointer& pattern) override;
void visit(pl::ptrn::PatternSigned& pattern) override;
void visit(pl::ptrn::PatternString& pattern) override;
void visit(pl::ptrn::PatternStruct& pattern) override;
void visit(pl::ptrn::PatternUnion& pattern) override;
void visit(pl::ptrn::PatternUnsigned& pattern) override;
void visit(pl::ptrn::PatternWideCharacter& pattern) override;
void visit(pl::ptrn::PatternWideString& pattern) override;
void visit(pl::ptrn::PatternError& pattern) override;
void visit(pl::ptrn::Pattern& pattern) override;
private:
std::function<void()> m_onEditCallback = [](){};
};
}

View File

@ -572,50 +572,7 @@ namespace hex::ui {
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
auto value = pattern.getValue();
auto valueString = pattern.toString();
if (const auto *enumPattern = dynamic_cast<pl::ptrn::PatternBitfieldFieldEnum*>(&pattern); enumPattern != nullptr) {
if (ImGui::BeginCombo("##Enum", pattern.getFormattedValue().c_str())) {
auto currValue = pattern.getValue().toUnsigned();
for (auto &[name, enumValue] : enumPattern->getEnumValues()) {
auto min = enumValue.min.toUnsigned();
auto max = enumValue.max.toUnsigned();
bool isSelected = min <= currValue && max >= currValue;
if (ImGui::Selectable(fmt::format("{}::{}", pattern.getTypeName(), name, min, pattern.getSize() * 2).c_str(), isSelected)) {
pattern.setValue(enumValue.min);
this->resetEditing();
}
if (isSelected)
ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
} else if (dynamic_cast<pl::ptrn::PatternBitfieldFieldBoolean*>(&pattern) != nullptr) {
bool boolValue = value.toBoolean();
if (ImGui::Checkbox("##boolean", &boolValue)) {
pattern.setValue(boolValue);
}
} else if (std::holds_alternative<i128>(value)) {
if (ImGui::InputText("##Value", valueString, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue)) {
wolv::math_eval::MathEvaluator<i128> mathEvaluator;
if (auto result = mathEvaluator.evaluate(valueString); result.has_value())
pattern.setValue(result.value());
this->resetEditing();
}
} else if (std::holds_alternative<u128>(value)) {
if (ImGui::InputText("##Value", valueString, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue)) {
wolv::math_eval::MathEvaluator<u128> mathEvaluator;
if (auto result = mathEvaluator.evaluate(valueString); result.has_value())
pattern.setValue(result.value());
this->resetEditing();
}
}
m_valueEditor.visit(pattern);
ImGui::PopItemWidth();
ImGui::PopStyleVar();
@ -672,10 +629,7 @@ namespace hex::ui {
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
bool value = pattern.getValue().toBoolean();
if (ImGui::Checkbox("##boolean", &value)) {
pattern.setValue(value);
}
m_valueEditor.visit(pattern);
ImGui::PopItemWidth();
ImGui::PopStyleVar();
@ -695,16 +649,8 @@ namespace hex::ui {
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
ImGui::SetKeyboardFocusHere();
auto value = hex::encodeByteString(pattern.getBytes());
if (ImGui::InputText("##Character", value.data(), value.size() + 1, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue)) {
if (!value.empty()) {
auto result = hex::decodeByteString(value);
if (!result.empty())
pattern.setValue(char(result[0]));
m_valueEditor.visit(pattern);
this->resetEditing();
}
}
ImGui::PopItemWidth();
ImGui::PopStyleVar();
}
@ -726,22 +672,7 @@ namespace hex::ui {
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::BeginCombo("##Enum", pattern.getFormattedValue().c_str())) {
auto currValue = pattern.getValue().toUnsigned();
for (auto &[name, enumValue] : pattern.getEnumValues()) {
auto min = enumValue.min.toUnsigned();
auto max = enumValue.max.toUnsigned();
bool isSelected = min <= currValue && max >= currValue;
if (ImGui::Selectable(fmt::format("{}::{}", pattern.getTypeName(), name, min, pattern.getSize() * 2).c_str(), isSelected)) {
pattern.setValue(enumValue.min);
this->resetEditing();
}
if (isSelected)
ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
m_valueEditor.visit(pattern);
ImGui::PopItemWidth();
ImGui::PopStyleVar();
@ -761,15 +692,7 @@ namespace hex::ui {
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
ImGui::SetKeyboardFocusHere();
auto value = pattern.toString();
if (ImGui::InputText("##Value", value, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue)) {
wolv::math_eval::MathEvaluator<long double> mathEvaluator;
if (auto result = mathEvaluator.evaluate(value); result.has_value())
pattern.setValue(double(result.value()));
this->resetEditing();
}
m_valueEditor.visit(pattern);
ImGui::PopItemWidth();
ImGui::PopStyleVar();
@ -815,15 +738,7 @@ namespace hex::ui {
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
ImGui::SetKeyboardFocusHere();
auto value = pattern.getFormattedValue();
if (ImGui::InputText("##Value", value, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue)) {
wolv::math_eval::MathEvaluator<i128> mathEvaluator;
if (auto result = mathEvaluator.evaluate(value); result.has_value())
pattern.setValue(result.value());
this->resetEditing();
}
m_valueEditor.visit(pattern);
ImGui::PopItemWidth();
ImGui::PopStyleVar();
@ -844,11 +759,7 @@ namespace hex::ui {
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
ImGui::SetKeyboardFocusHere();
auto value = pattern.toString();
if (ImGui::InputText("##Value", value.data(), value.size() + 1, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue)) {
pattern.setValue(value);
this->resetEditing();
}
m_valueEditor.visit(pattern);
ImGui::PopItemWidth();
ImGui::PopStyleVar();
@ -877,11 +788,8 @@ namespace hex::ui {
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
ImGui::SetKeyboardFocusHere();
auto value = pattern.toString();
if (ImGui::InputText("##Value", value, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue)) {
pattern.setValue(value);
this->resetEditing();
}
m_valueEditor.visit(pattern);
ImGui::PopItemWidth();
ImGui::PopStyleVar();
} else {
@ -925,11 +833,8 @@ namespace hex::ui {
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
ImGui::SetKeyboardFocusHere();
auto value = pattern.toString();
if (ImGui::InputText("##Value", value, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue)) {
pattern.setValue(value);
this->resetEditing();
}
m_valueEditor.visit(pattern);
ImGui::PopItemWidth();
ImGui::PopStyleVar();
} else {
@ -966,15 +871,8 @@ namespace hex::ui {
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
ImGui::SetKeyboardFocusHere();
auto value = pattern.toString();
if (ImGui::InputText("##Value", value, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue)) {
wolv::math_eval::MathEvaluator<u128> mathEvaluator;
m_valueEditor.visit(pattern);
if (auto result = mathEvaluator.evaluate(value); result.has_value())
pattern.setValue(result.value());
this->resetEditing();
}
ImGui::PopItemWidth();
ImGui::PopStyleVar();
}

View File

@ -0,0 +1,214 @@
#include <ui/pattern_value_editor.hpp>
#include <imgui.h>
#include <hex/helpers/utils.hpp>
#include <hex/ui/imgui_imhex_extensions.h>
#include <wolv/math_eval/math_evaluator.hpp>
#include <pl/patterns/pattern_array_dynamic.hpp>
#include <pl/patterns/pattern_array_static.hpp>
#include <pl/patterns/pattern_bitfield.hpp>
#include <pl/patterns/pattern_boolean.hpp>
#include <pl/patterns/pattern_character.hpp>
#include <pl/patterns/pattern_enum.hpp>
#include <pl/patterns/pattern_float.hpp>
#include <pl/patterns/pattern_pointer.hpp>
#include <pl/patterns/pattern_signed.hpp>
#include <pl/patterns/pattern_string.hpp>
#include <pl/patterns/pattern_struct.hpp>
#include <pl/patterns/pattern_union.hpp>
#include <pl/patterns/pattern_unsigned.hpp>
#include <pl/patterns/pattern_wide_character.hpp>
#include <pl/patterns/pattern_wide_string.hpp>
namespace hex::ui {
void PatternValueEditor::visit(pl::ptrn::PatternArrayDynamic& pattern) {
std::ignore = pattern;
}
void PatternValueEditor::visit(pl::ptrn::PatternArrayStatic& pattern) {
std::ignore = pattern;
}
void PatternValueEditor::visit(pl::ptrn::PatternBitfield& pattern) {
std::ignore = pattern;
}
void PatternValueEditor::visit(pl::ptrn::PatternBitfieldField& pattern) {
auto value = pattern.getValue();
auto valueString = pattern.toString();
if (const auto *enumPattern = dynamic_cast<pl::ptrn::PatternBitfieldFieldEnum*>(&pattern); enumPattern != nullptr) {
if (ImGui::BeginCombo("##Enum", pattern.getFormattedValue().c_str())) {
auto currValue = pattern.getValue().toUnsigned();
for (auto &[name, enumValue] : enumPattern->getEnumValues()) {
auto min = enumValue.min.toUnsigned();
auto max = enumValue.max.toUnsigned();
bool isSelected = min <= currValue && max >= currValue;
if (ImGui::Selectable(fmt::format("{}::{}", pattern.getTypeName(), name, min, pattern.getSize() * 2).c_str(), isSelected)) {
pattern.setValue(enumValue.min);
m_onEditCallback();
}
if (isSelected)
ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
} else if (dynamic_cast<pl::ptrn::PatternBitfieldFieldBoolean*>(&pattern) != nullptr) {
bool boolValue = value.toBoolean();
if (ImGui::Checkbox("##boolean", &boolValue)) {
pattern.setValue(boolValue);
}
} else if (std::holds_alternative<i128>(value)) {
if (ImGui::InputText("##Value", valueString, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue)) {
wolv::math_eval::MathEvaluator<i128> mathEvaluator;
if (auto result = mathEvaluator.evaluate(valueString); result.has_value())
pattern.setValue(result.value());
m_onEditCallback();
}
} else if (std::holds_alternative<u128>(value)) {
if (ImGui::InputText("##Value", valueString, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue)) {
wolv::math_eval::MathEvaluator<u128> mathEvaluator;
if (auto result = mathEvaluator.evaluate(valueString); result.has_value())
pattern.setValue(result.value());
m_onEditCallback();
}
}
}
void PatternValueEditor::visit(pl::ptrn::PatternBitfieldArray& pattern) {
std::ignore = pattern;
}
void PatternValueEditor::visit(pl::ptrn::PatternBoolean& pattern) {
bool value = pattern.getValue().toBoolean();
if (ImGui::Checkbox("##boolean", &value)) {
pattern.setValue(value);
m_onEditCallback();
}
}
void PatternValueEditor::visit(pl::ptrn::PatternCharacter& pattern) {
auto value = hex::encodeByteString(pattern.getBytes());
if (ImGui::InputText("##Character", value.data(), value.size() + 1, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue)) {
if (!value.empty()) {
auto result = hex::decodeByteString(value);
if (!result.empty())
pattern.setValue(char(result[0]));
m_onEditCallback();
}
}
}
void PatternValueEditor::visit(pl::ptrn::PatternEnum& pattern) {
if (ImGui::BeginCombo("##Enum", pattern.getFormattedValue().c_str())) {
auto currValue = pattern.getValue().toUnsigned();
for (auto &[name, enumValue] : pattern.getEnumValues()) {
auto min = enumValue.min.toUnsigned();
auto max = enumValue.max.toUnsigned();
bool isSelected = min <= currValue && max >= currValue;
if (ImGui::Selectable(fmt::format("{}::{}", pattern.getTypeName(), name, min, pattern.getSize() * 2).c_str(), isSelected)) {
pattern.setValue(enumValue.min);
m_onEditCallback();
}
if (isSelected)
ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
}
void PatternValueEditor::visit(pl::ptrn::PatternFloat& pattern) {
auto value = pattern.toString();
if (ImGui::InputText("##Value", value, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue)) {
wolv::math_eval::MathEvaluator<long double> mathEvaluator;
if (auto result = mathEvaluator.evaluate(value); result.has_value())
pattern.setValue(double(result.value()));
m_onEditCallback();
}
}
void PatternValueEditor::visit(pl::ptrn::PatternPadding& pattern) {
std::ignore = pattern;
}
void PatternValueEditor::visit(pl::ptrn::PatternPointer& pattern) {
std::ignore = pattern;
}
void PatternValueEditor::visit(pl::ptrn::PatternSigned& pattern) {
auto value = pattern.getFormattedValue();
if (ImGui::InputText("##Value", value, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue)) {
wolv::math_eval::MathEvaluator<i128> mathEvaluator;
if (auto result = mathEvaluator.evaluate(value); result.has_value())
pattern.setValue(result.value());
m_onEditCallback();
}
}
void PatternValueEditor::visit(pl::ptrn::PatternString& pattern) {
auto value = pattern.toString();
if (ImGui::InputText("##Value", value.data(), value.size() + 1, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue)) {
pattern.setValue(value);
m_onEditCallback();
}
}
void PatternValueEditor::visit(pl::ptrn::PatternStruct& pattern) {
auto value = pattern.toString();
if (ImGui::InputText("##Value", value, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue)) {
pattern.setValue(value);
m_onEditCallback();
}
}
void PatternValueEditor::visit(pl::ptrn::PatternUnion& pattern) {
auto value = pattern.toString();
if (ImGui::InputText("##Value", value, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue)) {
pattern.setValue(value);
m_onEditCallback();
}
}
void PatternValueEditor::visit(pl::ptrn::PatternUnsigned& pattern) {
auto value = pattern.toString();
if (ImGui::InputText("##Value", value, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue)) {
wolv::math_eval::MathEvaluator<u128> mathEvaluator;
if (auto result = mathEvaluator.evaluate(value); result.has_value())
pattern.setValue(result.value());
m_onEditCallback();
}
}
void PatternValueEditor::visit(pl::ptrn::PatternWideCharacter& pattern) {
std::ignore = pattern;
}
void PatternValueEditor::visit(pl::ptrn::PatternWideString& pattern) {
std::ignore = pattern;
}
void PatternValueEditor::visit(pl::ptrn::PatternError& pattern) {
std::ignore = pattern;
}
void PatternValueEditor::visit(pl::ptrn::Pattern& pattern) {
std::ignore = pattern;
}
}