impr: Move pattern sections from pattern editor view to pattern data view

This commit is contained in:
WerWolv
2025-07-12 22:05:13 +02:00
parent 01d0f03fdd
commit 26de4c11e1
17 changed files with 95 additions and 203 deletions

View File

@@ -17,56 +17,69 @@ namespace hex::plugin::builtin {
ContentRegistry::Settings::onChange("hex.builtin.setting.interface", "hex.builtin.setting.interface.pattern_tree_style", [this](const ContentRegistry::Settings::SettingsValue &value) {
m_treeStyle = ui::PatternDrawer::TreeStyle(value.get<int>(0));
for (auto &drawer : m_patternDrawer.all())
drawer->setTreeStyle(m_treeStyle);
for (auto &drawers : m_patternDrawer.all())
for (auto &[id, drawer] : drawers)
drawer->setTreeStyle(m_treeStyle);
});
ContentRegistry::Settings::onChange("hex.builtin.setting.interface", "hex.builtin.setting.interface.pattern_data_row_bg", [this](const ContentRegistry::Settings::SettingsValue &value) {
m_rowColoring = bool(value.get<int>(false));
for (auto &drawer : m_patternDrawer.all())
drawer->enableRowColoring(m_rowColoring);
for (auto &drawers : m_patternDrawer.all())
for (auto &[id, drawer] : drawers)
drawer->enableRowColoring(m_rowColoring);
});
ContentRegistry::Settings::onChange("hex.builtin.setting.general", "hex.builtin.setting.general.pattern_data_max_filter_items", [this](const ContentRegistry::Settings::SettingsValue &value) {
m_maxFilterItems = value.get<u32>(128);
for (auto &drawer : m_patternDrawer.all())
drawer->setMaxFilterDisplayItems(m_maxFilterItems);
for (auto &drawers : m_patternDrawer.all())
for (auto &[id, drawer] : drawers)
drawer->setMaxFilterDisplayItems(m_maxFilterItems);
});
EventPatternEvaluating::subscribe(this, [this]{
(*m_patternDrawer)->reset();
for (auto &drawers : m_patternDrawer.all())
for (auto &[id, drawer] : drawers)
drawer->reset();
});
EventPatternExecuted::subscribe(this, [this](const auto&){
(*m_patternDrawer)->reset();
for (auto &drawers : m_patternDrawer.all())
for (auto &[id, drawer] : drawers)
drawer->reset();
const auto &sections = ContentRegistry::PatternLanguage::getRuntime().getSections();
auto ids = sections | std::views::keys | std::ranges::to<std::vector<u64>>();
ids.push_back(0);
for (const auto &id : ids) {
auto drawer = std::make_unique<ui::PatternDrawer>();
drawer->setSelectionCallback([](const pl::ptrn::Pattern *pattern) {
ImHexApi::HexEditor::setSelection(Region(pattern->getOffset(), pattern->getSize()));
RequestPatternEditorSelectionChange::post(pattern->getLine(), 0);
});
drawer->setHoverCallback([this](const pl::ptrn::Pattern *pattern) {
if (pattern == nullptr)
m_hoveredPatternRegion = Region::Invalid();
else
m_hoveredPatternRegion = Region(pattern->getOffset(), pattern->getSize());
});
drawer->setTreeStyle(m_treeStyle);
drawer->enableRowColoring(m_rowColoring);
(*m_patternDrawer)[id] = std::move(drawer);
}
});
RequestJumpToPattern::subscribe(this, [this](const pl::ptrn::Pattern *pattern) {
(*m_patternDrawer)->jumpToPattern(pattern);
(*m_patternDrawer)[0]->jumpToPattern(pattern);
});
ImHexApi::HexEditor::addHoverHighlightProvider([this](const prv::Provider *, u64, size_t) -> std::set<Region> {
return { m_hoveredPatternRegion };
});
m_patternDrawer.setOnCreateCallback([this](const prv::Provider *, auto &drawer) {
drawer = std::make_unique<ui::PatternDrawer>();
drawer->setSelectionCallback([](const pl::ptrn::Pattern *pattern) {
ImHexApi::HexEditor::setSelection(Region(pattern->getOffset(), pattern->getSize()));
RequestPatternEditorSelectionChange::post(pattern->getLine(), 0);
});
drawer->setHoverCallback([this](const pl::ptrn::Pattern *pattern) {
if (pattern == nullptr)
m_hoveredPatternRegion = Region::Invalid();
else
m_hoveredPatternRegion = Region(pattern->getOffset(), pattern->getSize());
});
drawer->setTreeStyle(m_treeStyle);
drawer->enableRowColoring(m_rowColoring);
});
}
ViewPatternData::~ViewPatternData() {
@@ -78,21 +91,64 @@ namespace hex::plugin::builtin {
// Draw the pattern tree if the provider is valid
if (ImHexApi::Provider::isValid()) {
// Make sure the runtime has finished evaluating and produced valid patterns
bool patternsValid = false;
auto &runtime = ContentRegistry::PatternLanguage::getRuntime();
if (TRY_LOCK(ContentRegistry::PatternLanguage::getRuntimeLock())) {
patternsValid = runtime.arePatternsValid();
}
const auto height = std::max(ImGui::GetContentRegionAvail().y - ImGui::GetTextLineHeightWithSpacing() - (ImGui::GetStyle().FramePadding.y * 2), ImGui::GetTextLineHeightWithSpacing() * 5);
if (ImGui::BeginTabBar("##SectionSelector")) {
if (*m_patternDrawer != nullptr) {
(*m_patternDrawer)->enablePatternEditing(ImHexApi::Provider::get()->isWritable());
if (!runtime.arePatternsValid()) {
(*m_patternDrawer)->draw({ }, nullptr, height);
const auto height = std::max(ImGui::GetContentRegionAvail().y - ImGui::GetTextLineHeightWithSpacing() - (ImGui::GetStyle().FramePadding.y * 2), ImGui::GetTextLineHeightWithSpacing() * 5);
if (!patternsValid) {
ImGui::BeginDisabled();
if (ImGui::BeginTabItem("hex.builtin.view.pattern_data.section.main"_lang)) {
static ui::PatternDrawer emptyDrawer;
emptyDrawer.draw({ }, nullptr, height);
ImGui::EndTabItem();
}
ImGui::EndDisabled();
} else {
// If the runtime has finished evaluating, draw the patterns
if (TRY_LOCK(ContentRegistry::PatternLanguage::getRuntimeLock())) {
(*m_patternDrawer)->draw(runtime.getPatterns(), &runtime, height);
static i32 selectedSection = -1;
for (auto &[id, drawer] : *m_patternDrawer) {
drawer->enablePatternEditing(ImHexApi::Provider::get()->isWritable());
// If the runtime has finished evaluating, draw the patterns
if (TRY_LOCK(ContentRegistry::PatternLanguage::getRuntimeLock())) {
const auto &sections = runtime.getSections();
if (id != 0 && !sections.contains(id))
continue;
if (ImGui::BeginTabItem(id == 0 ? "hex.builtin.view.pattern_data.section.main"_lang : sections.at(id).name.c_str())) {
drawer->draw(runtime.getPatterns(id), &runtime, height);
ImGui::EndTabItem();
}
if (id != 0) {
if (ImGui::IsMouseDown(ImGuiMouseButton_Right) && ImGui::IsItemHovered() && !ImGui::IsMouseDragging(ImGuiMouseButton_Right)) {
ImGui::OpenPopup("##PatternDataContextMenu");
selectedSection = id;
}
}
}
}
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);
}
}
ImGui::EndPopup();
}
}
ImGui::EndTabBar();
}
}
}

View File

@@ -475,10 +475,6 @@ namespace hex::plugin::builtin {
this->drawVariableSettings(settingsSize, *m_patternVariables);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("hex.builtin.view.pattern_editor.sections"_lang)) {
this->drawSectionSelector(settingsSize, *m_sections);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("hex.builtin.view.pattern_editor.virtual_files"_lang)) {
this->drawVirtualFiles(settingsSize, *m_virtualFiles);
ImGui::EndTabItem();
@@ -1291,108 +1287,6 @@ namespace hex::plugin::builtin {
ImGui::EndChild();
}
void ViewPatternEditor::drawSectionSelector(ImVec2 size, const std::map<u64, pl::api::Section> &sections) {
auto &runtime = ContentRegistry::PatternLanguage::getRuntime();
if (ImGui::BeginTable("##sections_table", 3, ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY, size)) {
if (sections.empty()) {
ImGuiExt::TextOverlay("hex.builtin.view.pattern_editor.no_sections"_lang, ImGui::GetWindowPos() + ImGui::GetWindowSize() / 2, ImGui::GetWindowWidth() * 0.7);
}
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("hex.ui.common.name"_lang, ImGuiTableColumnFlags_WidthStretch, 0.5F);
ImGui::TableSetupColumn("hex.ui.common.size"_lang, ImGuiTableColumnFlags_WidthStretch, 0.5F);
ImGui::TableSetupColumn("##button", ImGuiTableColumnFlags_WidthFixed, 50_scaled);
ImGui::TableHeadersRow();
if (TRY_LOCK(ContentRegistry::PatternLanguage::getRuntimeLock())) {
for (auto &[id, section] : sections) {
if (section.name.empty())
continue;
ImGui::PushID(id);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextUnformatted(section.name.c_str());
ImGui::TableNextColumn();
ImGuiExt::TextFormatted("{} | 0x{:02X}", hex::toByteString(section.data.size()), section.data.size());
ImGui::TableNextColumn();
if (ImGuiExt::DimmedIconButton(ICON_VS_OPEN_PREVIEW, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
auto dataProvider = std::make_shared<prv::MemoryProvider>(section.data);
auto hexEditor = ui::HexEditor(m_sectionHexEditor);
hexEditor.setBackgroundHighlightCallback([this, id, &runtime](u64 address, const u8 *, size_t) -> std::optional<color_t> {
if (m_runningEvaluators != 0)
return std::nullopt;
if (!ImHexApi::Provider::isValid())
return std::nullopt;
std::optional<ImColor> color;
for (const auto &pattern : runtime.getPatternsAtAddress(address, id)) {
auto visibility = pattern->getVisibility();
if (visibility == pl::ptrn::Visibility::Hidden || visibility == pl::ptrn::Visibility::HighlightHidden)
continue;
if (color.has_value())
color = ImAlphaBlendColors(*color, pattern->getColor());
else
color = pattern->getColor();
}
return color;
});
auto patternProvider = ImHexApi::Provider::get();
m_sectionWindowDrawer[patternProvider] = [this, id, patternProvider, dataProvider, hexEditor, patternDrawer = std::make_shared<ui::PatternDrawer>(), &runtime]() mutable {
hexEditor.setProvider(dataProvider.get());
hexEditor.draw(ImGui::GetContentRegionAvail().y * 0.7);
patternDrawer->setSelectionCallback([&](const pl::ptrn::Pattern *pattern) {
hexEditor.setSelection(Region { pattern->getOffset(), pattern->getSize() });
});
const auto &patterns = [&, this]() -> const auto& {
if (patternProvider->isReadable() && *m_executionDone) {
return runtime.getPatterns(id);
} else {
static const std::vector<std::shared_ptr<pl::ptrn::Pattern>> empty;
return empty;
}
}();
if (*m_executionDone)
patternDrawer->draw(patterns, &runtime, ImGui::GetContentRegionAvail().y);
};
}
ImGui::SetItemTooltip("%s", "hex.builtin.view.pattern_editor.sections.view"_lang.get());
ImGui::SameLine();
if (ImGuiExt::DimmedIconButton(ICON_VS_SAVE_AS, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
fs::openFileBrowser(fs::DialogMode::Save, {}, [id, &runtime](const auto &path) {
wolv::io::File file(path, wolv::io::File::Mode::Create);
if (!file.isValid()) {
ui::ToastError::open("hex.builtin.popup.error.create"_lang);
return;
}
file.writeVector(runtime.getSection(id));
});
}
ImGui::SetItemTooltip("%s", "hex.builtin.view.pattern_editor.sections.export"_lang.get());
ImGui::PopID();
}
}
ImGui::EndTable();
}
}
void ViewPatternEditor::drawVirtualFiles(ImVec2 size, const std::vector<VirtualFile> &virtualFiles) const {
std::vector<const VirtualFile*> virtualFilePointers;
@@ -1488,20 +1382,6 @@ namespace hex::plugin::builtin {
if (provider == nullptr)
return;
auto open = m_sectionWindowDrawer.contains(provider);
if (open) {
ImGui::SetNextWindowSize(scaled(ImVec2(600, 700)), ImGuiCond_Appearing);
if (ImGui::Begin("hex.builtin.view.pattern_editor.section_popup"_lang, &open, ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
m_sectionWindowDrawer[provider]();
}
ImGui::End();
}
if (!open && m_sectionWindowDrawer.contains(provider)) {
ImHexApi::HexEditor::setSelection(Region::Invalid());
m_sectionWindowDrawer.erase(provider);
}
if (!m_lastEvaluationProcessed) {
if (!m_lastEvaluationResult) {
const auto processMessage = [](const auto &message) {
@@ -1929,7 +1809,6 @@ namespace hex::plugin::builtin {
m_consoleLongestLineLength.get(provider) = 0;
m_consoleNeedsUpdate = true;
m_sectionWindowDrawer.clear();
m_consoleEditor.get(provider).SetText("");
m_virtualFiles->clear();
@@ -2009,7 +1888,6 @@ namespace hex::plugin::builtin {
ON_SCOPE_EXIT {
runtime.getInternals().evaluator->setDebugMode(false);
*m_lastEvaluationOutVars = runtime.getOutVariables();
*m_sections = runtime.getSections();
m_runningEvaluators -= 1;