feat: Finish up work on new pl section system

This commit is contained in:
WerWolv 2022-11-08 21:43:22 +01:00
parent 23ce2ec271
commit 4c5d2f6ebb
22 changed files with 250 additions and 215 deletions

@ -1 +1 @@
Subproject commit 2fc96aedca0f665957eec878d75403eff7de5a91
Subproject commit a74070f38e7deae8c82e827f3a84a0e2619d5981

View File

@ -106,7 +106,7 @@ namespace hex {
EVENT_DEF(EventDataChanged);
EVENT_DEF(EventHighlightingChanged);
EVENT_DEF(EventWindowClosing, GLFWwindow *);
EVENT_DEF(EventRegionSelected, Region);
EVENT_DEF(EventRegionSelected, ImHexApi::HexEditor::ProviderRegion);
EVENT_DEF(EventSettingsChanged);
EVENT_DEF(EventAbnormalTermination, int);
EVENT_DEF(EventOSThemeChanged);
@ -138,6 +138,4 @@ namespace hex {
EVENT_DEF(RequestShowYesNoQuestionPopup, std::string, std::function<void()>, std::function<void()>);
EVENT_DEF(RequestShowFileChooserPopup, std::vector<std::fs::path>, std::vector<nfdfilteritem_t>, std::function<void(std::fs::path)>);
EVENT_DEF(QuerySelection, std::optional<Region> &);
}

View File

@ -64,6 +64,14 @@ namespace hex {
color_t m_color = 0x00;
};
struct ProviderRegion : public Region {
prv::Provider *provider;
[[nodiscard]] prv::Provider *getProvider() const { return this->provider; }
[[nodiscard]] Region getRegion() const { return { this->address, this->size }; }
};
namespace impl {
using HighlightingFunction = std::function<std::optional<color_t>(u64, const u8*, size_t, bool)>;
@ -75,6 +83,7 @@ namespace hex {
std::map<u32, Tooltip> &getTooltips();
std::map<u32, TooltipFunction> &getTooltipFunctions();
void setCurrentSelection(ProviderRegion region);
}
u32 addBackgroundHighlight(const Region &region, color_t color);
@ -96,9 +105,10 @@ namespace hex {
void removeForegroundHighlightingProvider(u32 id);
bool isSelectionValid();
std::optional<Region> getSelection();
void setSelection(const Region &region);
void setSelection(u64 address, size_t size);
std::optional<ProviderRegion> getSelection();
void setSelection(const Region &region, prv::Provider *provider = nullptr);
void setSelection(const ProviderRegion &region);
void setSelection(u64 address, size_t size, prv::Provider *provider = nullptr);
}

View File

@ -75,6 +75,11 @@ namespace hex {
return s_tooltipFunctions;
}
static std::optional<ProviderRegion> s_currentSelection;
void setCurrentSelection(std::optional<ProviderRegion> region) {
s_currentSelection = region;
}
}
u32 addBackgroundHighlight(const Region &region, color_t color) {
@ -181,19 +186,20 @@ namespace hex {
return getSelection().has_value();
}
std::optional<Region> getSelection() {
std::optional<Region> selection;
EventManager::post<QuerySelection>(selection);
return selection;
std::optional<ProviderRegion> getSelection() {
return impl::s_currentSelection;
}
void setSelection(const Region &region) {
void setSelection(const Region &region, prv::Provider *provider) {
setSelection(ProviderRegion { region, provider == nullptr ? Provider::get() : provider });
}
void setSelection(const ProviderRegion &region) {
EventManager::post<RequestSelectionChange>(region);
}
void setSelection(u64 address, size_t size) {
setSelection({ address, size });
void setSelection(u64 address, size_t size, prv::Provider *provider) {
setSelection({ { address, size }, provider == nullptr ? Provider::get() : provider });
}
}

View File

@ -54,8 +54,9 @@ add_library(${PROJECT_NAME} SHARED
source/content/views/view_find.cpp
source/content/helpers/math_evaluator.cpp
source/content/helpers/pattern_drawer.cpp
source/content/helpers/hex_editor.cpp
source/ui/hex_editor.cpp
source/ui/pattern_drawer.cpp
source/lang/de_DE.cpp
source/lang/en_US.cpp

View File

@ -12,9 +12,9 @@ namespace hex::plugin::builtin {
[[nodiscard]] bool isAvailable() const override { return true; }
[[nodiscard]] bool isReadable() const override { return true; }
[[nodiscard]] bool isWritable() const override { return true; }
[[nodiscard]] bool isResizable() const override { return true; }
[[nodiscard]] bool isSavable() const override { return true; }
[[nodiscard]] bool isWritable() const override { return !this->m_readOnly; }
[[nodiscard]] bool isResizable() const override { return !this->m_readOnly; }
[[nodiscard]] bool isSavable() const override { return !this->m_readOnly; }
[[nodiscard]] bool open() override;
void close() override { }
@ -42,8 +42,11 @@ namespace hex::plugin::builtin {
void loadSettings(const nlohmann::json &settings) override { hex::unused(settings); }
[[nodiscard]] nlohmann::json storeSettings(nlohmann::json settings) const override { return settings; }
void setReadOnly(bool readOnly) { this->m_readOnly = readOnly; }
private:
std::vector<u8> m_data;
bool m_readOnly = false;
};
}

View File

@ -33,6 +33,7 @@ namespace hex::plugin::builtin {
u64 m_startAddress = 0;
size_t m_validBytes = 0;
prv::Provider *m_selectedProvider = nullptr;
std::atomic<bool> m_dataValid = false;
std::vector<InspectorCacheEntry> m_cachedData, m_workData;
TaskHolder m_updateTask;

View File

@ -7,7 +7,7 @@
#include <hex/helpers/encoding_file.hpp>
#include <content/helpers/provider_extra_data.hpp>
#include <content/helpers/hex_editor.hpp>
#include <ui/hex_editor.hpp>
namespace hex::plugin::builtin {
@ -70,7 +70,7 @@ namespace hex::plugin::builtin {
void registerEvents();
void registerMenuItems();
HexEditor m_hexEditor;
ui::HexEditor m_hexEditor;
bool m_shouldOpenPopup = false;
std::unique_ptr<Popup> m_currPopup;

View File

@ -3,7 +3,7 @@
#include <hex.hpp>
#include <hex/ui/view.hpp>
#include <content/helpers/pattern_drawer.hpp>
#include <ui/pattern_drawer.hpp>
#include <hex/providers/provider.hpp>
#include <vector>
@ -20,7 +20,7 @@ namespace hex::plugin::builtin {
private:
std::map<hex::prv::Provider *, std::vector<pl::ptrn::Pattern*>> m_sortedPatterns;
hex::PatternDrawer m_patternDrawer;
ui::PatternDrawer m_patternDrawer;
};
}

View File

@ -6,7 +6,8 @@
#include <hex/providers/provider.hpp>
#include <content/helpers/provider_extra_data.hpp>
#include <content/helpers/hex_editor.hpp>
#include <ui/hex_editor.hpp>
#include <ui/pattern_drawer.hpp>
#include <content/providers/memory_file_provider.hpp>
#include <cstring>
@ -14,6 +15,7 @@
#include <string_view>
#include <thread>
#include <vector>
#include <functional>
#include <TextEditor.h>
@ -63,9 +65,7 @@ namespace hex::plugin::builtin {
bool m_syncPatternSourceCode = false;
bool m_autoLoadPatterns = true;
std::unique_ptr<MemoryFileProvider> m_sectionProvider = nullptr;
HexEditor m_hexEditor;
std::map<prv::Provider*, std::move_only_function<void()>> m_sectionWindowDrawer;
private:
void drawConsole(ImVec2 size, const std::vector<std::pair<pl::core::LogConsole::Level, std::string>> &console);
void drawEnvVars(ImVec2 size, std::list<PlData::EnvVar> &envVars);

View File

@ -17,21 +17,22 @@
#include <hex/providers/provider.hpp>
#include <hex/providers/buffered_reader.hpp>
namespace hex {
namespace hex::plugin::builtin::ui {
class HexEditor {
public:
HexEditor();
explicit HexEditor(prv::Provider *provider = nullptr);
~HexEditor();
void draw(prv::Provider *provider, float height = ImGui::GetContentRegionAvail().y);
void draw(float height = ImGui::GetContentRegionAvail().y);
void setProvider(prv::Provider *provider) { this->m_provider = provider; }
private:
enum class CellType { None, Hex, ASCII };
void drawCell(prv::Provider *provider, u64 address, u8 *data, size_t size, bool hovered, CellType cellType);
void drawCell(u64 address, u8 *data, size_t size, bool hovered, CellType cellType);
void drawSelectionFrame(u32 x, u32 y, u64 byteAddress, u16 bytesPerCell, const ImVec2 &cellPos, const ImVec2 &cellSize) const;
void drawEditor(prv::Provider *provider, const ImVec2 &size);
void drawFooter(prv::Provider *provider, const ImVec2 &size);
void drawEditor(const ImVec2 &size);
void drawFooter(const ImVec2 &size);
void drawTooltip(u64 address, const u8 *data, size_t size);
void handleSelection(u64 address, u32 bytesPerCell, const u8 *data, bool cellHovered);
@ -47,9 +48,7 @@ namespace hex {
if (!ImHexApi::Provider::isValid())
return;
auto provider = ImHexApi::Provider::get();
const size_t maxAddress = provider->getActualSize() + provider->getBaseAddress() - 1;
const size_t maxAddress = this->m_provider->getActualSize() + this->m_provider->getBaseAddress() - 1;
this->m_selectionChanged = this->m_selectionStart != start || this->m_selectionEnd != end;
@ -57,7 +56,8 @@ namespace hex {
this->m_selectionEnd = std::clamp<u128>(end, 0, maxAddress);
if (this->m_selectionChanged) {
EventManager::post<EventRegionSelected>(this->getSelection());
auto selection = this->getSelection();
EventManager::post<EventRegionSelected>(ImHexApi::HexEditor::ProviderRegion{ { selection.address, selection.size }, this->m_provider });
this->m_shouldModifyValue = true;
}
}
@ -161,6 +161,8 @@ namespace hex {
}
private:
prv::Provider *m_provider;
std::optional<u64> m_selectionStart;
std::optional<u64> m_selectionEnd;
float m_scrollPosition = 0;

View File

@ -4,14 +4,18 @@
#include <pl/pattern_visitor.hpp>
#include <hex/providers/provider.hpp>
namespace hex {
namespace hex::plugin::builtin::ui {
class PatternDrawer : public pl::PatternVisitor {
public:
PatternDrawer() = default;
void draw(const std::vector<std::shared_ptr<pl::ptrn::Pattern>> &patterns, float height = 0.0F);
private:
void draw(pl::ptrn::Pattern& pattern);
public:
void visit(pl::ptrn::PatternArrayDynamic& pattern) override;
void visit(pl::ptrn::PatternArrayStatic& pattern) override;
void visit(pl::ptrn::PatternBitfieldField& pattern) override;
@ -39,5 +43,6 @@ namespace hex {
private:
std::map<const pl::ptrn::Pattern*, u64> m_displayEnd;
std::vector<pl::ptrn::Pattern*> m_sortedPatterns;
};
}

View File

@ -3,6 +3,9 @@
#include <imgui.h>
#include <hex/ui/imgui_imhex_extensions.h>
namespace pl::ptrn { class Pattern; }
namespace hex::prv { class Provider; }
namespace hex::plugin::builtin::ui {

View File

@ -466,7 +466,7 @@ namespace hex::plugin::builtin {
class NodeDataSelection : public dp::Node {
public:
NodeDataSelection() : Node("hex.builtin.nodes.data_access.selection.header", { dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Integer, "hex.builtin.nodes.data_access.selection.address"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Integer, "hex.builtin.nodes.data_access.selection.size") }) {
EventManager::subscribe<EventRegionSelected>(this, [this](const Region &region) {
EventManager::subscribe<EventRegionSelected>(this, [this](const auto &region) {
this->m_address = region.address;
this->m_size = region.size;
});

View File

@ -18,22 +18,26 @@ namespace hex::plugin::builtin {
using NumberDisplayStyle = ContentRegistry::DataInspector::NumberDisplayStyle;
ViewDataInspector::ViewDataInspector() : View("hex.builtin.view.data_inspector.name") {
EventManager::subscribe<EventRegionSelected>(this, [this](Region region) {
EventManager::subscribe<EventRegionSelected>(this, [this](const auto &region) {
if (!ImHexApi::Provider::isValid() || region == Region::Invalid()) {
this->m_validBytes = 0;
} else {
auto provider = ImHexApi::Provider::get();
this->m_validBytes = u64(provider->getActualSize() - region.address);
this->m_validBytes = u64(region.getProvider()->getActualSize() - region.address);
this->m_startAddress = region.address;
this->m_selectedProvider = region.getProvider();
}
this->m_shouldInvalidate = true;
});
EventManager::subscribe<EventProviderClosed>(this, [this](const auto*) {
this->m_selectedProvider = nullptr;
});
}
ViewDataInspector::~ViewDataInspector() {
EventManager::unsubscribe<EventRegionSelected>(this);
EventManager::unsubscribe<EventProviderClosed>(this);
}
void ViewDataInspector::drawContent() {
@ -47,17 +51,18 @@ namespace hex::plugin::builtin {
this->m_updateTask = TaskManager::createBackgroundTask("Update Inspector",
[this, validBytes = this->m_validBytes, startAddress = this->m_startAddress, endian = this->m_endian, invert = this->m_invert, numberDisplayStyle = this->m_numberDisplayStyle](auto &) {
auto provider = ImHexApi::Provider::get();
this->m_workData.clear();
if (this->m_selectedProvider == nullptr)
return;
// Decode bytes using registered inspectors
for (auto &entry : ContentRegistry::DataInspector::getEntries()) {
if (validBytes < entry.requiredSize)
continue;
std::vector<u8> buffer(validBytes > entry.maxSize ? entry.maxSize : validBytes);
provider->read(startAddress, buffer.data(), buffer.size());
this->m_selectedProvider->read(startAddress, buffer.data(), buffer.size());
if (invert) {
for (auto &byte : buffer)
@ -81,14 +86,14 @@ namespace hex::plugin::builtin {
pl::PatternLanguage runtime;
ContentRegistry::PatternLanguage::configureRuntime(runtime, nullptr);
runtime.setDataSource([invert, provider](u64 offset, u8 *buffer, size_t size) {
provider->read(offset, buffer, size);
runtime.setDataSource([this, invert](u64 offset, u8 *buffer, size_t size) {
this->m_selectedProvider->read(offset, buffer, size);
if (invert) {
for (size_t i = 0; i < size; i++)
buffer[i] ^= 0xFF;
}
}, provider->getBaseAddress(), provider->getActualSize());
}, this->m_selectedProvider->getBaseAddress(), this->m_selectedProvider->getActualSize());
runtime.setDangerousFunctionCallHandler([]{ return false; });
runtime.setDefaultEndian(endian);
@ -139,9 +144,7 @@ namespace hex::plugin::builtin {
}
if (ImGui::Begin(View::toWindowName("hex.builtin.view.data_inspector.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
auto provider = ImHexApi::Provider::get();
if (ImHexApi::Provider::isValid() && provider->isReadable() && this->m_validBytes > 0) {
if (this->m_selectedProvider != nullptr && this->m_selectedProvider->isReadable() && this->m_validBytes > 0) {
if (ImGui::BeginTable("##datainspector", 2, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg, ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * (this->m_cachedData.size() + 1)))) {
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("hex.builtin.view.data_inspector.table.name"_lang);
@ -177,7 +180,7 @@ namespace hex::plugin::builtin {
if (ImGui::InputText("##InspectorLineEditing", this->m_editingValue, ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll)) {
auto bytes = (*editingFunction)(this->m_editingValue, this->m_endian);
provider->write(this->m_startAddress, bytes.data(), bytes.size());
this->m_selectedProvider->write(this->m_startAddress, bytes.data(), bytes.size());
this->m_editingValue.clear();
editing = false;
this->m_shouldInvalidate = true;

View File

@ -443,7 +443,7 @@ namespace hex::plugin::builtin {
auto provider = ImHexApi::Provider::get();
return Region { provider->getBaseAddress(), provider->getActualSize() };
} else {
return ImHexApi::HexEditor::getSelection().value();
return ImHexApi::HexEditor::getSelection()->getRegion();
}
}();

View File

@ -7,7 +7,7 @@
namespace hex::plugin::builtin {
ViewHashes::ViewHashes() : View("hex.builtin.view.hashes.name") {
EventManager::subscribe<EventRegionSelected>(this, [this](const Region &) {
EventManager::subscribe<EventRegionSelected>(this, [this](const auto &) {
for (auto &function : this->m_hashFunctions)
function.reset();
});

View File

@ -584,7 +584,10 @@ namespace hex::plugin::builtin {
void ViewHexEditor::drawContent() {
if (ImGui::Begin(View::toWindowName(this->getUnlocalizedName()).c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
this->m_hexEditor.draw(ImHexApi::Provider::get());
this->m_hexEditor.setProvider(ImHexApi::Provider::get());
this->m_hexEditor.draw();
this->drawPopup();
}
ImGui::End();
@ -686,11 +689,13 @@ namespace hex::plugin::builtin {
// Remove selection
ShortcutManager::addShortcut(this, Keys::Escape, [this] {
auto &data = ProviderExtraData::getCurrent().editor;
auto provider = ImHexApi::Provider::get();
auto &data = ProviderExtraData::get(provider).editor;
data.selectionStart.reset();
data.selectionEnd.reset();
EventManager::post<EventRegionSelected>(this->getSelection());
EventManager::post<EventRegionSelected>(ImHexApi::HexEditor::ProviderRegion{ this->getSelection(), provider });
});
// Move cursor around
@ -873,11 +878,6 @@ namespace hex::plugin::builtin {
}
});
EventManager::subscribe<QuerySelection>(this, [this](auto &region) {
if (isSelectionValid())
region = getSelection();
});
EventManager::subscribe<EventProviderChanged>(this, [this](auto *oldProvider, auto *newProvider) {
if (oldProvider != nullptr) {
auto &oldData = ProviderExtraData::get(oldProvider).editor;
@ -897,8 +897,9 @@ namespace hex::plugin::builtin {
}
this->m_hexEditor.forceUpdateScrollPosition();
if (isSelectionValid())
EventManager::post<EventRegionSelected>(getSelection());
if (isSelectionValid()) {
EventManager::post<EventRegionSelected>(ImHexApi::HexEditor::ProviderRegion{ this->getSelection(), newProvider });
}
});
}

View File

@ -25,89 +25,6 @@ namespace hex::plugin::builtin {
EventManager::unsubscribe<EventHighlightingChanged>(this);
}
static bool sortPatterns(prv::Provider *provider, const ImGuiTableSortSpecs* sortSpecs, const pl::ptrn::Pattern * left, const pl::ptrn::Pattern * right) {
if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("name")) {
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
return left->getDisplayName() > right->getDisplayName();
else
return left->getDisplayName() < right->getDisplayName();
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("offset")) {
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
return left->getOffset() > right->getOffset();
else
return left->getOffset() < right->getOffset();
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("size")) {
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
return left->getSize() > right->getSize();
else
return left->getSize() < right->getSize();
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("value")) {
size_t biggerSize = std::max(left->getSize(), right->getSize());
std::vector<u8> leftBuffer(biggerSize, 0x00), rightBuffer(biggerSize, 0x00);
provider->read(left->getOffset(), leftBuffer.data(), left->getSize());
provider->read(right->getOffset(), rightBuffer.data(), right->getSize());
if (left->getEndian() != std::endian::native)
std::reverse(leftBuffer.begin(), leftBuffer.end());
if (right->getEndian() != std::endian::native)
std::reverse(rightBuffer.begin(), rightBuffer.end());
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
return leftBuffer > rightBuffer;
else
return leftBuffer < rightBuffer;
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("type")) {
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
return left->getTypeName() > right->getTypeName();
else
return left->getTypeName() < right->getTypeName();
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("color")) {
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
return left->getColor() > right->getColor();
else
return left->getColor() < right->getColor();
}
return false;
}
static bool beginPatternTable(prv::Provider *&provider, const std::vector<std::shared_ptr<pl::ptrn::Pattern>> &patterns, std::vector<pl::ptrn::Pattern*> &sortedPatterns) {
if (ImGui::BeginTable("##Patterntable", 6, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) {
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("hex.builtin.view.pattern_data.var_name"_lang, ImGuiTableColumnFlags_PreferSortAscending, 0, ImGui::GetID("name"));
ImGui::TableSetupColumn("hex.builtin.view.pattern_data.color"_lang, ImGuiTableColumnFlags_PreferSortAscending, 0, ImGui::GetID("color"));
ImGui::TableSetupColumn("hex.builtin.view.pattern_data.offset"_lang, ImGuiTableColumnFlags_PreferSortAscending | ImGuiTableColumnFlags_DefaultSort, 0, ImGui::GetID("offset"));
ImGui::TableSetupColumn("hex.builtin.view.pattern_data.size"_lang, ImGuiTableColumnFlags_PreferSortAscending, 0, ImGui::GetID("size"));
ImGui::TableSetupColumn("hex.builtin.view.pattern_data.type"_lang, ImGuiTableColumnFlags_PreferSortAscending, 0, ImGui::GetID("type"));
ImGui::TableSetupColumn("hex.builtin.view.pattern_data.value"_lang, ImGuiTableColumnFlags_PreferSortAscending, 0, ImGui::GetID("value"));
auto sortSpecs = ImGui::TableGetSortSpecs();
if (!patterns.empty() && (sortSpecs->SpecsDirty || sortedPatterns.empty())) {
sortedPatterns.clear();
std::transform(patterns.begin(), patterns.end(), std::back_inserter(sortedPatterns), [](const std::shared_ptr<pl::ptrn::Pattern> &pattern) {
return pattern.get();
});
std::sort(sortedPatterns.begin(), sortedPatterns.end(), [&sortSpecs, &provider](pl::ptrn::Pattern *left, pl::ptrn::Pattern *right) -> bool {
return sortPatterns(provider, sortSpecs, left, right);
});
for (auto &pattern : sortedPatterns)
pattern->sort([&sortSpecs, &provider](const pl::ptrn::Pattern *left, const pl::ptrn::Pattern *right){
return sortPatterns(provider, sortSpecs, left, right);
});
sortSpecs->SpecsDirty = false;
}
return true;
}
return false;
}
void ViewPatternData::drawContent() {
if (ImGui::Begin(View::toWindowName("hex.builtin.view.pattern_data.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
if (ImHexApi::Provider::isValid()) {
@ -123,18 +40,7 @@ namespace hex::plugin::builtin {
}
}();
auto &sortedPatterns = this->m_sortedPatterns[ImHexApi::Provider::get()];
if (beginPatternTable(provider, patterns, sortedPatterns)) {
ImGui::TableHeadersRow();
if (!sortedPatterns.empty()) {
for (auto &pattern : sortedPatterns) {
this->m_patternDrawer.draw(*pattern);
}
}
ImGui::EndTable();
}
this->m_patternDrawer.draw(patterns);
}
}
ImGui::End();

View File

@ -114,7 +114,7 @@ namespace hex::plugin::builtin {
this->m_textEditor.Render("hex.builtin.view.pattern_editor.name"_lang, textEditorSize, true);
auto settingsSize = ImGui::GetContentRegionAvail();
settingsSize.y -= ImGui::GetTextLineHeightWithSpacing() * 2.5;
settingsSize.y -= ImGui::GetTextLineHeightWithSpacing() * 2.5F;
if (ImGui::BeginTabBar("##settings")) {
if (ImGui::BeginTabItem("hex.builtin.view.pattern_editor.console"_lang)) {
@ -438,8 +438,8 @@ namespace hex::plugin::builtin {
void ViewPatternEditor::drawSectionSelector(ImVec2 size, std::map<u64, pl::api::Section> &sections) {
if (ImGui::BeginTable("##sections_table", 3, ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY, size)) {
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch, 0.5F);
ImGui::TableSetupColumn("Size", ImGuiTableColumnFlags_WidthStretch, 0.5F);
ImGui::TableSetupColumn("hex.builtin.common.name"_lang, ImGuiTableColumnFlags_WidthStretch, 0.5F);
ImGui::TableSetupColumn("hex.builtin.common.size"_lang, ImGuiTableColumnFlags_WidthStretch, 0.5F);
ImGui::TableSetupColumn("##button", ImGuiTableColumnFlags_WidthFixed, 20_scaled);
ImGui::TableHeadersRow();
@ -455,11 +455,13 @@ namespace hex::plugin::builtin {
ImGui::TextFormatted("{} | 0x{:02X}", hex::toByteString(section.data.size()), section.data.size());
ImGui::TableNextColumn();
if (ImGui::IconButton(ICON_VS_OPEN_PREVIEW, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
this->m_sectionProvider = std::make_unique<MemoryFileProvider>();
this->m_sectionProvider->resize(section.data.size());
this->m_sectionProvider->writeRaw(0, section.data.data(), section.data.size());
auto dataProvider = std::make_unique<MemoryFileProvider>();
dataProvider->resize(section.data.size());
dataProvider->writeRaw(0x00, section.data.data(), section.data.size());
dataProvider->setReadOnly(true);
this->m_hexEditor.setBackgroundHighlightCallback([this, id](u64 address, const u8 *, size_t) -> std::optional<color_t> {
auto hexEditor = ui::HexEditor();
hexEditor.setBackgroundHighlightCallback([this, id](u64 address, const u8 *, size_t) -> std::optional<color_t> {
if (this->m_runningEvaluators != 0)
return std::nullopt;
if (!ImHexApi::Provider::isValid())
@ -478,6 +480,15 @@ namespace hex::plugin::builtin {
return color;
});
auto patternProvider = ImHexApi::Provider::get();
this->m_sectionWindowDrawer[patternProvider] = [id, patternProvider, dataProvider = std::move(dataProvider), hexEditor, patternDrawer = ui::PatternDrawer()] mutable {
hexEditor.setProvider(dataProvider.get());
hexEditor.draw(480_scaled);
patternDrawer.draw(ProviderExtraData::get(patternProvider).patternLanguage.runtime->getAllPatterns(id), 150_scaled);
};
}
ImGui::PopID();
@ -527,17 +538,17 @@ namespace hex::plugin::builtin {
ImGui::EndPopup();
}
auto open = this->m_sectionProvider != nullptr && this->m_sectionProvider->isReadable();
auto open = this->m_sectionWindowDrawer.contains(provider);
if (open) {
ImGui::SetNextWindowSize(scaled(ImVec2(500, 650)), ImGuiCond_Appearing);
if (ImGui::Begin("hex.builtin.view.pattern_editor.section_popup"_lang, &open)) {
this->m_hexEditor.draw(this->m_sectionProvider.get());
if (ImGui::Begin("hex.builtin.view.pattern_editor.section_popup"_lang, &open, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
this->m_sectionWindowDrawer[provider]();
}
ImGui::End();
}
if (!open)
this->m_sectionProvider = nullptr;
this->m_sectionWindowDrawer.erase(provider);
}

View File

@ -1,4 +1,4 @@
#include <content/helpers/hex_editor.hpp>
#include <ui/hex_editor.hpp>
#include <hex/api/imhex_api.hpp>
#include <hex/api/content_registry.hpp>
@ -7,7 +7,7 @@
#include <hex/helpers/encoding_file.hpp>
#include <hex/helpers/utils.hpp>
namespace hex {
namespace hex::plugin::builtin::ui {
/* Data Visualizer */
@ -70,7 +70,7 @@ namespace hex {
/* Hex Editor */
HexEditor::HexEditor() {
HexEditor::HexEditor(prv::Provider *provider) : m_provider(provider) {
this->m_currDataVisualizer = ContentRegistry::HexEditor::impl::getVisualizers()["hex.builtin.visualizer.hexadecimal.8bit"];
this->m_grayZeroHighlighter = ImHexApi::HexEditor::addForegroundHighlightingProvider([this](u64 address, const u8 *data, size_t size, bool hasColor) -> std::optional<color_t> {
@ -237,7 +237,7 @@ namespace hex {
ImGui::PopStyleVar();
}
void HexEditor::drawCell(prv::Provider *provider, u64 address, u8 *data, size_t size, bool hovered, CellType cellType) {
void HexEditor::drawCell(u64 address, u8 *data, size_t size, bool hovered, CellType cellType) {
static DataVisualizerAscii asciiVisualizer;
if (this->m_shouldUpdateEditingValue) {
@ -253,7 +253,7 @@ namespace hex {
else
asciiVisualizer.draw(address, data, size, this->m_upperCaseHex);
if (hovered && provider->isWritable()) {
if (hovered && this->m_provider->isWritable()) {
// Enter editing mode when double-clicking a cell
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
this->m_editingAddress = address;
@ -278,13 +278,13 @@ namespace hex {
if (shouldExitEditingMode || this->m_shouldModifyValue) {
provider->write(*this->m_editingAddress, this->m_editingBytes.data(), this->m_editingBytes.size());
this->m_provider->write(*this->m_editingAddress, this->m_editingBytes.data(), this->m_editingBytes.size());
if (!this->m_selectionChanged && !ImGui::IsMouseDown(ImGuiMouseButton_Left) && !ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
auto nextEditingAddress = *this->m_editingAddress + this->m_currDataVisualizer->getBytesPerCell();
this->setSelection(nextEditingAddress, nextEditingAddress);
if (nextEditingAddress >= provider->getSize())
if (nextEditingAddress >= this->m_provider->getSize())
this->m_editingAddress = std::nullopt;
else
this->m_editingAddress = nextEditingAddress;
@ -334,7 +334,7 @@ namespace hex {
drawList->AddLine(cellPos + ImVec2(0, cellSize.y), cellPos + cellSize + ImVec2(1, 0), ImColor(SelectionFrameColor), 1.0F);
}
void HexEditor::drawEditor(prv::Provider *provider, const ImVec2 &size) {
void HexEditor::drawEditor(const ImVec2 &size) {
const float SeparatorColumWidth = 6_scaled;
const auto CharacterSize = ImGui::CalcTextSize("0");
@ -377,13 +377,13 @@ namespace hex {
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (provider != nullptr && provider->isReadable()) {
if (this->m_provider != nullptr && this->m_provider->isReadable()) {
std::pair<Region, bool> validRegion = { Region::Invalid(), false };
const auto isCurrRegionValid = [&validRegion, &provider](u64 address){
const auto isCurrRegionValid = [this, &validRegion](u64 address){
auto &[currRegion, currRegionValid] = validRegion;
if (!Region{ address, 1 }.isWithin(currRegion)) {
validRegion = provider->getRegionValidity(address);
validRegion = this->m_provider->getRegionValidity(address);
}
return currRegionValid;
@ -391,7 +391,7 @@ namespace hex {
ImGuiListClipper clipper;
clipper.Begin(std::ceil(provider->getSize() / (long double)(this->m_bytesPerRow)), CharacterSize.y);
clipper.Begin(std::ceil(this->m_provider->getSize() / (long double)(this->m_bytesPerRow)), CharacterSize.y);
while (clipper.Step()) {
this->m_visibleRowCount = clipper.DisplayEnd - clipper.DisplayStart;
@ -401,18 +401,18 @@ namespace hex {
// Draw address column
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextFormatted(this->m_upperCaseHex ? "{:08X}: " : "{:08x}: ", y * this->m_bytesPerRow + provider->getBaseAddress() + provider->getCurrentPageAddress());
ImGui::TextFormatted(this->m_upperCaseHex ? "{:08X}: " : "{:08x}: ", y * this->m_bytesPerRow + this->m_provider->getBaseAddress() + this->m_provider->getCurrentPageAddress());
ImGui::TableNextColumn();
const u8 validBytes = std::min<u64>(this->m_bytesPerRow, provider->getSize() - y * this->m_bytesPerRow);
const u8 validBytes = std::min<u64>(this->m_bytesPerRow, this->m_provider->getSize() - y * this->m_bytesPerRow);
std::vector<u8> bytes(this->m_bytesPerRow, 0x00);
provider->read(y * this->m_bytesPerRow + provider->getBaseAddress() + provider->getCurrentPageAddress(), bytes.data(), validBytes);
this->m_provider->read(y * this->m_bytesPerRow + this->m_provider->getBaseAddress() + this->m_provider->getCurrentPageAddress(), bytes.data(), validBytes);
std::vector<std::tuple<std::optional<color_t>, std::optional<color_t>>> cellColors;
{
for (u64 x = 0; x < std::ceil(float(validBytes) / bytesPerCell); x++) {
const u64 byteAddress = y * this->m_bytesPerRow + x * bytesPerCell + provider->getBaseAddress() + provider->getCurrentPageAddress();
const u64 byteAddress = y * this->m_bytesPerRow + x * bytesPerCell + this->m_provider->getBaseAddress() + this->m_provider->getCurrentPageAddress();
const auto cellBytes = std::min<u64>(validBytes, bytesPerCell);
@ -438,7 +438,7 @@ namespace hex {
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(3, 0));
for (u64 x = 0; x < columnCount; x++) {
const u64 byteAddress = y * this->m_bytesPerRow + x * bytesPerCell + provider->getBaseAddress() + provider->getCurrentPageAddress();
const u64 byteAddress = y * this->m_bytesPerRow + x * bytesPerCell + this->m_provider->getBaseAddress() + this->m_provider->getCurrentPageAddress();
ImGui::TableNextColumn();
if (isColumnSeparatorColumn(x, columnCount))
@ -486,7 +486,7 @@ namespace hex {
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
ImGui::PushItemWidth((CharacterSize * maxCharsPerCell).x);
if (isCurrRegionValid(byteAddress))
this->drawCell(provider, byteAddress, &bytes[x * bytesPerCell], bytesPerCell, cellHovered, CellType::Hex);
this->drawCell(byteAddress, &bytes[x * bytesPerCell], bytesPerCell, cellHovered, CellType::Hex);
else
ImGui::TextFormatted("{}", std::string(maxCharsPerCell, '?'));
ImGui::PopItemWidth();
@ -513,7 +513,7 @@ namespace hex {
for (u64 x = 0; x < this->m_bytesPerRow; x++) {
ImGui::TableNextColumn();
const u64 byteAddress = y * this->m_bytesPerRow + x + provider->getBaseAddress() + provider->getCurrentPageAddress();
const u64 byteAddress = y * this->m_bytesPerRow + x + this->m_provider->getBaseAddress() + this->m_provider->getCurrentPageAddress();
const auto cellStartPos = getCellPosition();
const auto cellSize = CharacterSize + ImVec2(this->m_characterCellPadding, 0);
@ -543,7 +543,7 @@ namespace hex {
if (!isCurrRegionValid(byteAddress))
ImGui::TextFormatted("?");
else
this->drawCell(provider, byteAddress, &bytes[x], 1, cellHovered, CellType::ASCII);
this->drawCell(byteAddress, &bytes[x], 1, cellHovered, CellType::ASCII);
ImGui::PopItemWidth();
ImGui::PopStyleVar();
}
@ -562,9 +562,9 @@ namespace hex {
std::vector<std::pair<u64, CustomEncodingData>> encodingData;
u32 offset = 0;
do {
const u64 address = y * this->m_bytesPerRow + offset + provider->getBaseAddress() + provider->getCurrentPageAddress();
const u64 address = y * this->m_bytesPerRow + offset + this->m_provider->getBaseAddress() + this->m_provider->getCurrentPageAddress();
auto result = queryCustomEncodingData(provider, *this->m_currCustomEncoding, address);
auto result = queryCustomEncodingData(this->m_provider, *this->m_currCustomEncoding, address);
offset += std::max<size_t>(1, result.advance);
encodingData.emplace_back(address, result);
@ -621,13 +621,13 @@ namespace hex {
auto fractionPerLine = 1.0 / (this->m_visibleRowCount + 1);
if (y == (u64(clipper.DisplayStart) + 3)) {
if (i128(this->m_selectionEnd.value() - provider->getBaseAddress() - provider->getCurrentPageAddress()) <= (i64(clipper.DisplayStart + 3) * this->m_bytesPerRow)) {
if (i128(this->m_selectionEnd.value() - this->m_provider->getBaseAddress() - this->m_provider->getCurrentPageAddress()) <= (i64(clipper.DisplayStart + 3) * this->m_bytesPerRow)) {
this->m_shouldScrollToSelection = false;
ImGui::SetScrollHereY(fractionPerLine * 5);
}
} else if (y == (u64(clipper.DisplayEnd) - 1)) {
if (i128(this->m_selectionEnd.value() - provider->getBaseAddress() - provider->getCurrentPageAddress()) >= (i64(clipper.DisplayEnd - 2) * this->m_bytesPerRow)) {
if (i128(this->m_selectionEnd.value() - this->m_provider->getBaseAddress() - this->m_provider->getCurrentPageAddress()) >= (i64(clipper.DisplayEnd - 2) * this->m_bytesPerRow)) {
this->m_shouldScrollToSelection = false;
ImGui::SetScrollHereY(fractionPerLine * (this->m_visibleRowCount));
}
@ -638,7 +638,7 @@ namespace hex {
if (this->m_shouldJumpWhenOffScreen) {
this->m_shouldJumpWhenOffScreen = false;
const auto pageAddress = provider->getCurrentPageAddress() + provider->getBaseAddress();
const auto pageAddress = this->m_provider->getCurrentPageAddress() + this->m_provider->getBaseAddress();
auto newSelection = getSelection();
newSelection.address -= pageAddress;
@ -657,9 +657,9 @@ namespace hex {
this->m_shouldJumpToSelection = false;
auto newSelection = getSelection();
provider->setCurrentPage(provider->getPageOfAddress(newSelection.address).value_or(0));
this->m_provider->setCurrentPage(this->m_provider->getPageOfAddress(newSelection.address).value_or(0));
const auto pageAddress = provider->getCurrentPageAddress() + provider->getBaseAddress();
const auto pageAddress = this->m_provider->getCurrentPageAddress() + this->m_provider->getBaseAddress();
auto scrollPos = (static_cast<long double>(newSelection.getStartAddress() - pageAddress) / this->m_bytesPerRow) * CharacterSize.y;
bool scrollUpwards = scrollPos < ImGui::GetScrollY();
auto scrollFraction = scrollUpwards ? 0.0F : (1.0F - ((1.0F / this->m_visibleRowCount) * 2));
@ -693,12 +693,12 @@ namespace hex {
this->m_enteredEditingMode = false;
}
void HexEditor::drawFooter(prv::Provider *provider, const ImVec2 &size) {
if (provider != nullptr && provider->isReadable()) {
const auto pageCount = provider->getPageCount();
void HexEditor::drawFooter(const ImVec2 &size) {
if (this->m_provider != nullptr && this->m_provider->isReadable()) {
const auto pageCount = this->m_provider->getPageCount();
constexpr static u32 MinPage = 1;
const auto windowEndPos = ImGui::GetWindowPos() + ImGui::GetWindowSize() - ImGui::GetStyle().WindowPadding;
const auto windowEndPos = ImGui::GetWindowPos() + size - ImGui::GetStyle().WindowPadding;
ImGui::GetWindowDrawList()->AddLine(windowEndPos - ImVec2(0, size.y - 1_scaled), windowEndPos - size + ImVec2(0, 1_scaled), ImGui::GetColorU32(ImGuiCol_Separator), 2.0_scaled);
if (ImGui::BeginChild("##footer", size, false, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
@ -708,7 +708,7 @@ namespace hex {
// Page slider
ImGui::TableNextColumn();
{
u32 page = provider->getCurrentPage() + 1;
u32 page = this->m_provider->getCurrentPage() + 1;
ImGui::TextFormatted("{}: ", "hex.builtin.view.hex_editor.page"_lang);
ImGui::SameLine();
@ -716,7 +716,7 @@ namespace hex {
ImGui::BeginDisabled(pageCount <= 1);
{
if (ImGui::SliderScalar("##page_selection", ImGuiDataType_U32, &page, &MinPage, &pageCount, hex::format("%d / {}", pageCount).c_str()))
provider->setCurrentPage(page - 1);
this->m_provider->setCurrentPage(page - 1);
}
ImGui::EndDisabled();
}
@ -724,7 +724,7 @@ namespace hex {
// Page Address
ImGui::TableNextColumn();
{
ImGui::TextFormatted("{0}: 0x{1:08X} - 0x{2:08X} ({1} - {2})", "hex.builtin.view.hex_editor.region"_lang, provider->getCurrentPageAddress(), provider->getSize());
ImGui::TextFormatted("{0}: 0x{1:08X} - 0x{2:08X} ({1} - {2})", "hex.builtin.view.hex_editor.region"_lang, this->m_provider->getCurrentPageAddress(), this->m_provider->getSize());
}
ImGui::TableNextRow();
@ -752,9 +752,9 @@ namespace hex {
ImGui::TableNextColumn();
{
ImGui::TextFormatted("{0}: 0x{1:08X} (0x{2:X} | {3})", "hex.builtin.view.hex_editor.data_size"_lang,
provider->getActualSize(),
provider->getActualSize(),
hex::toByteString(provider->getActualSize())
this->m_provider->getActualSize(),
this->m_provider->getActualSize(),
hex::toByteString(this->m_provider->getActualSize())
);
}
@ -788,14 +788,14 @@ namespace hex {
}
}
void HexEditor::draw(prv::Provider *provider, float height) {
void HexEditor::draw(float height) {
const auto width = ImGui::GetContentRegionAvail().x;
const auto FooterSize = ImVec2(width, ImGui::GetTextLineHeightWithSpacing() * 2.3F);
const auto TableSize = ImVec2(width, height - FooterSize.y);
this->drawEditor(provider, TableSize);
this->drawFooter(provider, FooterSize);
this->drawEditor(TableSize);
this->drawFooter(FooterSize);
this->m_selectionChanged = false;
}

View File

@ -1,4 +1,4 @@
#include <content/helpers/pattern_drawer.hpp>
#include <ui/pattern_drawer.hpp>
#include <pl/patterns/pattern_array_dynamic.hpp>
#include <pl/patterns/pattern_array_static.hpp>
@ -21,11 +21,12 @@
#include <hex/api/imhex_api.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/api/localization.hpp>
#include <imgui.h>
#include <hex/ui/imgui_imhex_extensions.h>
namespace hex {
namespace hex::plugin::builtin::ui {
namespace {
@ -414,7 +415,7 @@ namespace hex {
auto &displayEnd = this->getDisplayEnd(pattern);
if (chunkCount > displayEnd) {
ImGui::Selectable("... (Double-click to see more items)", false, ImGuiSelectableFlags_SpanAllColumns);
ImGui::Selectable(hex::format("... ({})", "hex.builtin.pattern_drawer.double_click"_lang).c_str(), false, ImGuiSelectableFlags_SpanAllColumns);
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
displayEnd += DisplayEndStep;
break;
@ -470,4 +471,88 @@ namespace hex {
auto [value, success] = this->m_displayEnd.emplace(&pattern, DisplayEndDefault);
return value->second;
}
static bool sortPatterns(const ImGuiTableSortSpecs* sortSpecs, const pl::ptrn::Pattern * left, const pl::ptrn::Pattern * right) {
if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("name")) {
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
return left->getDisplayName() > right->getDisplayName();
else
return left->getDisplayName() < right->getDisplayName();
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("offset")) {
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
return left->getOffset() > right->getOffset();
else
return left->getOffset() < right->getOffset();
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("size")) {
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
return left->getSize() > right->getSize();
else
return left->getSize() < right->getSize();
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("value")) {
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
return left->getValue() > right->getValue();
else
return left->getValue() < right->getValue();
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("type")) {
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
return left->getTypeName() > right->getTypeName();
else
return left->getTypeName() < right->getTypeName();
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("color")) {
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
return left->getColor() > right->getColor();
else
return left->getColor() < right->getColor();
}
return false;
}
static bool beginPatternTable(const std::vector<std::shared_ptr<pl::ptrn::Pattern>> &patterns, std::vector<pl::ptrn::Pattern*> &sortedPatterns, float height) {
if (ImGui::BeginTable("##Patterntable", 6, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY, ImVec2(0, height))) {
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("hex.builtin.pattern_drawer.var_name"_lang, ImGuiTableColumnFlags_PreferSortAscending, 0, ImGui::GetID("name"));
ImGui::TableSetupColumn("hex.builtin.pattern_drawer.color"_lang, ImGuiTableColumnFlags_PreferSortAscending, 0, ImGui::GetID("color"));
ImGui::TableSetupColumn("hex.builtin.pattern_drawer.offset"_lang, ImGuiTableColumnFlags_PreferSortAscending | ImGuiTableColumnFlags_DefaultSort, 0, ImGui::GetID("offset"));
ImGui::TableSetupColumn("hex.builtin.pattern_drawer.size"_lang, ImGuiTableColumnFlags_PreferSortAscending, 0, ImGui::GetID("size"));
ImGui::TableSetupColumn("hex.builtin.pattern_drawer.type"_lang, ImGuiTableColumnFlags_PreferSortAscending, 0, ImGui::GetID("type"));
ImGui::TableSetupColumn("hex.builtin.pattern_drawer.value"_lang, ImGuiTableColumnFlags_PreferSortAscending, 0, ImGui::GetID("value"));
auto sortSpecs = ImGui::TableGetSortSpecs();
if (!patterns.empty() && (sortSpecs->SpecsDirty || sortedPatterns.empty())) {
sortedPatterns.clear();
std::transform(patterns.begin(), patterns.end(), std::back_inserter(sortedPatterns), [](const std::shared_ptr<pl::ptrn::Pattern> &pattern) {
return pattern.get();
});
std::sort(sortedPatterns.begin(), sortedPatterns.end(), [&sortSpecs](pl::ptrn::Pattern *left, pl::ptrn::Pattern *right) -> bool {
return sortPatterns(sortSpecs, left, right);
});
for (auto &pattern : sortedPatterns)
pattern->sort([&sortSpecs](const pl::ptrn::Pattern *left, const pl::ptrn::Pattern *right){
return sortPatterns(sortSpecs, left, right);
});
sortSpecs->SpecsDirty = false;
}
return true;
}
return false;
}
void PatternDrawer::draw(const std::vector<std::shared_ptr<pl::ptrn::Pattern>> &patterns, float height) {
if (beginPatternTable(patterns, this->m_sortedPatterns, height)) {
ImGui::TableHeadersRow();
for (auto &pattern : this->m_sortedPatterns) {
this->draw(*pattern);
}
ImGui::EndTable();
}
}
}