mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-30 05:05:19 -05:00
refactor: Rework features that use external libraries into optional plugins (#1470)
This commit is contained in:
114
plugins/ui/include/popups/popup_file_chooser.hpp
Normal file
114
plugins/ui/include/popups/popup_file_chooser.hpp
Normal file
@@ -0,0 +1,114 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/ui/popup.hpp>
|
||||
|
||||
#include <hex/api/localization_manager.hpp>
|
||||
|
||||
#include <wolv/utils/string.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace hex::ui {
|
||||
|
||||
class PopupFileChooser : public Popup<PopupFileChooser> {
|
||||
public:
|
||||
PopupFileChooser(const std::vector<std::fs::path> &basePaths, const std::vector<std::fs::path> &files, const std::vector<hex::fs::ItemFilter> &validExtensions, bool multiple, const std::function<void(std::fs::path)> &callback)
|
||||
: hex::Popup<PopupFileChooser>("hex.ui.common.choose_file"),
|
||||
m_indices({ }),
|
||||
m_openCallback(callback),
|
||||
m_validExtensions(validExtensions), m_multiple(multiple) {
|
||||
|
||||
for (const auto &path : files) {
|
||||
std::fs::path adjustedPath;
|
||||
for (const auto &basePath : basePaths) {
|
||||
if (isSubpath(basePath, path)) {
|
||||
adjustedPath = std::fs::relative(path, basePath);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (adjustedPath.empty())
|
||||
adjustedPath = path.filename();
|
||||
|
||||
m_files.push_back({ path, adjustedPath });
|
||||
}
|
||||
|
||||
std::sort(m_files.begin(), m_files.end(), [](const auto &a, const auto &b) {
|
||||
return a.first < b.first;
|
||||
});
|
||||
}
|
||||
|
||||
void drawContent() override {
|
||||
bool doubleClicked = false;
|
||||
|
||||
if (ImGui::BeginListBox("##files", scaled(ImVec2(500, 400)))) {
|
||||
u32 index = 0;
|
||||
for (auto &[path, pathName] : m_files) {
|
||||
ImGui::PushID(index);
|
||||
|
||||
bool selected = m_indices.contains(index);
|
||||
if (ImGui::Selectable(wolv::util::toUTF8String(pathName).c_str(), selected, ImGuiSelectableFlags_DontClosePopups)) {
|
||||
if (!m_multiple) {
|
||||
m_indices.clear();
|
||||
m_indices.insert(index);
|
||||
} else {
|
||||
if (selected) {
|
||||
m_indices.erase(index);
|
||||
} else {
|
||||
m_indices.insert(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0))
|
||||
doubleClicked = true;
|
||||
|
||||
ImGuiExt::InfoTooltip(wolv::util::toUTF8String(path).c_str());
|
||||
|
||||
ImGui::PopID();
|
||||
index++;
|
||||
}
|
||||
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
|
||||
if (ImGui::Button("hex.ui.common.open"_lang) || doubleClicked) {
|
||||
for (const auto &index : m_indices)
|
||||
m_openCallback(m_files[index].first);
|
||||
Popup::close();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button("hex.ui.common.browse"_lang)) {
|
||||
fs::openFileBrowser(fs::DialogMode::Open, m_validExtensions, [this](const auto &path) {
|
||||
m_openCallback(path);
|
||||
Popup::close();
|
||||
}, {}, m_multiple);
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape)))
|
||||
this->close();
|
||||
}
|
||||
|
||||
[[nodiscard]] ImGuiWindowFlags getFlags() const override {
|
||||
return ImGuiWindowFlags_AlwaysAutoResize;
|
||||
}
|
||||
|
||||
private:
|
||||
static bool isSubpath(const std::fs::path &basePath, const std::fs::path &path) {
|
||||
auto relativePath = std::fs::relative(path, basePath);
|
||||
|
||||
return !relativePath.empty() && relativePath.native()[0] != '.';
|
||||
}
|
||||
|
||||
private:
|
||||
std::set<u32> m_indices;
|
||||
std::vector<std::pair<std::fs::path, std::fs::path>> m_files;
|
||||
std::function<void(std::fs::path)> m_openCallback;
|
||||
std::vector<hex::fs::ItemFilter> m_validExtensions;
|
||||
bool m_multiple = false;
|
||||
};
|
||||
|
||||
}
|
||||
88
plugins/ui/include/popups/popup_notification.hpp
Normal file
88
plugins/ui/include/popups/popup_notification.hpp
Normal file
@@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/ui/popup.hpp>
|
||||
|
||||
#include <hex/api/localization_manager.hpp>
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace hex::ui {
|
||||
|
||||
namespace impl {
|
||||
|
||||
template<typename T>
|
||||
class PopupNotification : public Popup<T> {
|
||||
public:
|
||||
PopupNotification(UnlocalizedString unlocalizedName, std::string message, std::function<void()> function)
|
||||
: hex::Popup<T>(std::move(unlocalizedName), false),
|
||||
m_message(std::move(message)), m_function(std::move(function)) { }
|
||||
|
||||
void drawContent() override {
|
||||
ImGuiExt::TextFormattedWrapped("{}", m_message.c_str());
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
if (ImGui::Button("hex.ui.common.okay"_lang) || ImGui::IsKeyDown(ImGuiKey_Escape))
|
||||
m_function();
|
||||
|
||||
ImGui::SetWindowPos((ImHexApi::System::getMainWindowSize() - ImGui::GetWindowSize()) / 2, ImGuiCond_Appearing);
|
||||
|
||||
if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape)))
|
||||
this->close();
|
||||
}
|
||||
|
||||
[[nodiscard]] ImGuiWindowFlags getFlags() const override {
|
||||
return ImGuiWindowFlags_AlwaysAutoResize;
|
||||
}
|
||||
|
||||
[[nodiscard]] ImVec2 getMinSize() const override {
|
||||
return scaled({ 400, 100 });
|
||||
}
|
||||
|
||||
[[nodiscard]] ImVec2 getMaxSize() const override {
|
||||
return scaled({ 600, 300 });
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_message;
|
||||
std::function<void()> m_function;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
class PopupInfo : public impl::PopupNotification<PopupInfo> {
|
||||
public:
|
||||
explicit PopupInfo(std::string message)
|
||||
: PopupNotification("hex.ui.common.info", std::move(message), [this] {
|
||||
Popup::close();
|
||||
}) { }
|
||||
};
|
||||
|
||||
class PopupWarning : public impl::PopupNotification<PopupWarning> {
|
||||
public:
|
||||
explicit PopupWarning(std::string message)
|
||||
: PopupNotification("hex.ui.common.warning", std::move(message), [this] {
|
||||
Popup::close();
|
||||
}) { }
|
||||
};
|
||||
|
||||
class PopupError : public impl::PopupNotification<PopupError> {
|
||||
public:
|
||||
explicit PopupError(std::string message)
|
||||
: PopupNotification("hex.ui.common.error", std::move(message), [this] {
|
||||
Popup::close();
|
||||
}) { }
|
||||
};
|
||||
|
||||
class PopupFatal : public impl::PopupNotification<PopupFatal> {
|
||||
public:
|
||||
explicit PopupFatal(std::string message)
|
||||
: PopupNotification("hex.ui.common.fatal", std::move(message), [this] {
|
||||
ImHexApi::System::closeImHex();
|
||||
Popup::close();
|
||||
}) { }
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
57
plugins/ui/include/popups/popup_question.hpp
Normal file
57
plugins/ui/include/popups/popup_question.hpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/ui/popup.hpp>
|
||||
|
||||
#include <hex/api/localization_manager.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace hex::ui {
|
||||
|
||||
class PopupQuestion : public Popup<PopupQuestion> {
|
||||
public:
|
||||
PopupQuestion(std::string message, std::function<void()> yesFunction, std::function<void()> noFunction)
|
||||
: hex::Popup<PopupQuestion>("hex.ui.common.question", false),
|
||||
m_message(std::move(message)),
|
||||
m_yesFunction(std::move(yesFunction)), m_noFunction(std::move(noFunction)) { }
|
||||
|
||||
void drawContent() override {
|
||||
ImGuiExt::TextFormattedWrapped("{}", m_message.c_str());
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
|
||||
auto width = ImGui::GetWindowWidth();
|
||||
ImGui::SetCursorPosX(width / 9);
|
||||
if (ImGui::Button("hex.ui.common.yes"_lang, ImVec2(width / 3, 0))) {
|
||||
m_yesFunction();
|
||||
this->close();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(width / 9 * 5);
|
||||
if (ImGui::Button("hex.ui.common.no"_lang, ImVec2(width / 3, 0))) {
|
||||
m_noFunction();
|
||||
this->close();
|
||||
}
|
||||
|
||||
ImGui::SetWindowPos((ImHexApi::System::getMainWindowSize() - ImGui::GetWindowSize()) / 2, ImGuiCond_Appearing);
|
||||
}
|
||||
|
||||
[[nodiscard]] ImGuiWindowFlags getFlags() const override {
|
||||
return ImGuiWindowFlags_AlwaysAutoResize;
|
||||
}
|
||||
|
||||
[[nodiscard]] ImVec2 getMinSize() const override {
|
||||
return scaled({ 400, 100 });
|
||||
}
|
||||
|
||||
[[nodiscard]] ImVec2 getMaxSize() const override {
|
||||
return scaled({ 600, 300 });
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_message;
|
||||
std::function<void()> m_yesFunction, m_noFunction;
|
||||
};
|
||||
|
||||
}
|
||||
73
plugins/ui/include/popups/popup_text_input.hpp
Normal file
73
plugins/ui/include/popups/popup_text_input.hpp
Normal file
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/ui/popup.hpp>
|
||||
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
#include <hex/api/localization_manager.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include <fonts/codicons_font.h>
|
||||
|
||||
namespace hex::ui {
|
||||
|
||||
class PopupTextInput : public Popup<PopupTextInput> {
|
||||
public:
|
||||
PopupTextInput(UnlocalizedString unlocalizedName, UnlocalizedString message, std::function<void(std::string)> function)
|
||||
: hex::Popup<PopupTextInput>(std::move(unlocalizedName), false),
|
||||
m_message(std::move(message)), m_function(std::move(function)) { }
|
||||
|
||||
void drawContent() override {
|
||||
ImGuiExt::TextFormattedWrapped("{}", Lang(m_message));
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGui::PushItemWidth(-1);
|
||||
|
||||
if (m_justOpened) {
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
m_justOpened = false;
|
||||
}
|
||||
|
||||
ImGuiExt::InputTextIcon("##input", ICON_VS_SYMBOL_KEY, m_input);
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
|
||||
auto width = ImGui::GetWindowWidth();
|
||||
ImGui::SetCursorPosX(width / 9);
|
||||
if (ImGui::Button("hex.ui.common.okay"_lang, ImVec2(width / 3, 0)) || ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Enter))) {
|
||||
m_function(m_input);
|
||||
this->close();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(width / 9 * 5);
|
||||
if (ImGui::Button("hex.ui.common.cancel"_lang, ImVec2(width / 3, 0)) || ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape))) {
|
||||
this->close();
|
||||
}
|
||||
|
||||
ImGui::SetWindowPos((ImHexApi::System::getMainWindowSize() - ImGui::GetWindowSize()) / 2, ImGuiCond_Appearing);
|
||||
}
|
||||
|
||||
[[nodiscard]] ImGuiWindowFlags getFlags() const override {
|
||||
return ImGuiWindowFlags_AlwaysAutoResize;
|
||||
}
|
||||
|
||||
[[nodiscard]] ImVec2 getMinSize() const override {
|
||||
return scaled({ 400, 100 });
|
||||
}
|
||||
|
||||
[[nodiscard]] ImVec2 getMaxSize() const override {
|
||||
return scaled({ 600, 300 });
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_input;
|
||||
|
||||
UnlocalizedString m_message;
|
||||
std::function<void(std::string)> m_function;
|
||||
bool m_justOpened = true;
|
||||
};
|
||||
|
||||
}
|
||||
55
plugins/ui/include/toasts/toast_notification.hpp
Normal file
55
plugins/ui/include/toasts/toast_notification.hpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
#include <hex/ui/imgui_imhex_extensions.h>
|
||||
#include <hex/ui/toast.hpp>
|
||||
|
||||
#include <fonts/codicons_font.h>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
|
||||
namespace hex::ui {
|
||||
|
||||
namespace impl {
|
||||
|
||||
template<typename T>
|
||||
struct ToastNotification : Toast<T> {
|
||||
ToastNotification(ImColor color, const char *icon, UnlocalizedString title, UnlocalizedString message)
|
||||
: Toast<T>(color), m_icon(icon), m_title(std::move(title)), m_message(std::move(message)) {}
|
||||
|
||||
void drawContent() final {
|
||||
ImGuiExt::TextFormattedColored(this->getColor(), "{}", m_icon);
|
||||
ImGui::SameLine();
|
||||
ImGui::PushFont(ImHexApi::Fonts::Bold());
|
||||
{
|
||||
ImGuiExt::TextFormatted("{}", hex::limitStringLength(Lang(m_title).get(), 30));
|
||||
}
|
||||
ImGui::PopFont();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGuiExt::TextFormattedWrapped("{}", hex::limitStringLength(Lang(m_message).get(), 60));
|
||||
}
|
||||
|
||||
private:
|
||||
const char *m_icon;
|
||||
UnlocalizedString m_title, m_message;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
struct ToastInfo : impl::ToastNotification<ToastInfo> {
|
||||
ToastInfo(UnlocalizedString title, UnlocalizedString message)
|
||||
: ToastNotification(ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_LoggerInfo), ICON_VS_INFO, std::move(title), std::move(message)) {}
|
||||
};
|
||||
|
||||
struct ToastWarn : impl::ToastNotification<ToastWarn> {
|
||||
ToastWarn(UnlocalizedString title, UnlocalizedString message)
|
||||
: ToastNotification(ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_LoggerWarning), ICON_VS_WARNING, std::move(title), std::move(message)) {}
|
||||
};
|
||||
|
||||
struct ToastError : impl::ToastNotification<ToastError> {
|
||||
ToastError(UnlocalizedString title, UnlocalizedString message)
|
||||
: ToastNotification(ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_LoggerError), ICON_VS_ERROR, std::move(title), std::move(message)) {}
|
||||
};
|
||||
|
||||
}
|
||||
278
plugins/ui/include/ui/hex_editor.hpp
Normal file
278
plugins/ui/include/ui/hex_editor.hpp
Normal file
@@ -0,0 +1,278 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
#include <hex/api/content_registry.hpp>
|
||||
#include <hex/providers/provider.hpp>
|
||||
#include <hex/helpers/encoding_file.hpp>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <hex/ui/view.hpp>
|
||||
|
||||
namespace hex::ui {
|
||||
|
||||
class HexEditor {
|
||||
public:
|
||||
explicit HexEditor(prv::Provider *provider = nullptr);
|
||||
~HexEditor();
|
||||
void draw(float height = ImGui::GetContentRegionAvail().y);
|
||||
|
||||
void setProvider(prv::Provider *provider) {
|
||||
m_provider = provider;
|
||||
m_currValidRegion = { Region::Invalid(), false };
|
||||
}
|
||||
void setUnknownDataCharacter(char character) { m_unknownDataCharacter = character; }
|
||||
private:
|
||||
enum class CellType { None, Hex, ASCII };
|
||||
|
||||
void drawCell(u64 address, const u8 *data, size_t size, bool hovered, CellType cellType);
|
||||
void drawSelectionFrame(u32 x, u32 y, Region selection, u64 byteAddress, u16 bytesPerCell, const ImVec2 &cellPos, const ImVec2 &cellSize, const ImColor &backgroundColor) const;
|
||||
void drawEditor(const ImVec2 &size);
|
||||
void drawFooter(const ImVec2 &size);
|
||||
void drawTooltip(u64 address, const u8 *data, size_t size) const;
|
||||
|
||||
void handleSelection(u64 address, u32 bytesPerCell, const u8 *data, bool cellHovered);
|
||||
std::optional<color_t> applySelectionColor(u64 byteAddress, std::optional<color_t> color);
|
||||
|
||||
public:
|
||||
void setSelectionUnchecked(std::optional<u64> start, std::optional<u64> end) {
|
||||
m_selectionStart = start;
|
||||
m_selectionEnd = end;
|
||||
m_cursorPosition = end;
|
||||
}
|
||||
void setSelection(const Region ®ion) { this->setSelection(region.getStartAddress(), region.getEndAddress()); }
|
||||
void setSelection(u128 start, u128 end) {
|
||||
if (!ImHexApi::Provider::isValid())
|
||||
return;
|
||||
|
||||
if (start > m_provider->getBaseAddress() + m_provider->getActualSize())
|
||||
return;
|
||||
|
||||
if (start < m_provider->getBaseAddress())
|
||||
return;
|
||||
|
||||
if (m_provider->getActualSize() == 0)
|
||||
return;
|
||||
|
||||
const size_t maxAddress = m_provider->getActualSize() + m_provider->getBaseAddress() - 1;
|
||||
|
||||
constexpr static auto alignDown = [](u128 value, u128 alignment) {
|
||||
return value & ~(alignment - 1);
|
||||
};
|
||||
|
||||
m_selectionChanged = m_selectionStart != start || m_selectionEnd != end;
|
||||
|
||||
if (!m_selectionStart.has_value()) m_selectionStart = start;
|
||||
if (!m_selectionEnd.has_value()) m_selectionEnd = end;
|
||||
|
||||
if (auto bytesPerCell = m_currDataVisualizer->getBytesPerCell(); bytesPerCell > 1) {
|
||||
if (end > start) {
|
||||
start = alignDown(start, bytesPerCell);
|
||||
end = alignDown(end, bytesPerCell) + (bytesPerCell - 1);
|
||||
} else {
|
||||
start = alignDown(start, bytesPerCell) + (bytesPerCell - 1);
|
||||
end = alignDown(end, bytesPerCell);
|
||||
}
|
||||
}
|
||||
|
||||
m_selectionStart = std::clamp<u128>(start, 0, maxAddress);
|
||||
m_selectionEnd = std::clamp<u128>(end, 0, maxAddress);
|
||||
m_cursorPosition = m_selectionEnd;
|
||||
|
||||
if (m_selectionChanged) {
|
||||
auto selection = this->getSelection();
|
||||
EventRegionSelected::post(ImHexApi::HexEditor::ProviderRegion{ { selection.address, selection.size }, m_provider });
|
||||
m_shouldModifyValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] Region getSelection() const {
|
||||
if (!isSelectionValid())
|
||||
return Region::Invalid();
|
||||
|
||||
const auto start = std::min(m_selectionStart.value(), m_selectionEnd.value());
|
||||
const auto end = std::max(m_selectionStart.value(), m_selectionEnd.value());
|
||||
const size_t size = end - start + 1;
|
||||
|
||||
return { start, size };
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<u64> getCursorPosition() const {
|
||||
return m_cursorPosition;
|
||||
}
|
||||
|
||||
void setCursorPosition(u64 cursorPosition) {
|
||||
m_cursorPosition = cursorPosition;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isSelectionValid() const {
|
||||
return m_selectionStart.has_value() && m_selectionEnd.has_value();
|
||||
}
|
||||
|
||||
void jumpToSelection(bool center = true) {
|
||||
m_shouldJumpToSelection = true;
|
||||
|
||||
if (center)
|
||||
m_centerOnJump = true;
|
||||
}
|
||||
|
||||
void scrollToSelection() {
|
||||
m_shouldScrollToSelection = true;
|
||||
}
|
||||
|
||||
void jumpIfOffScreen() {
|
||||
m_shouldJumpWhenOffScreen = true;
|
||||
}
|
||||
|
||||
[[nodiscard]] u16 getBytesPerRow() const {
|
||||
return m_bytesPerRow;
|
||||
}
|
||||
|
||||
[[nodiscard]] u16 getBytesPerCell() const {
|
||||
return m_currDataVisualizer->getBytesPerCell();
|
||||
}
|
||||
|
||||
void setBytesPerRow(u16 bytesPerRow) {
|
||||
m_bytesPerRow = bytesPerRow;
|
||||
}
|
||||
|
||||
[[nodiscard]] u16 getVisibleRowCount() const {
|
||||
return m_visibleRowCount;
|
||||
}
|
||||
|
||||
void setSelectionColor(color_t color) {
|
||||
m_selectionColor = color;
|
||||
}
|
||||
|
||||
void enableUpperCaseHex(bool upperCaseHex) {
|
||||
m_upperCaseHex = upperCaseHex;
|
||||
}
|
||||
|
||||
void enableGrayOutZeros(bool grayOutZeros) {
|
||||
m_grayOutZero = grayOutZeros;
|
||||
}
|
||||
|
||||
void enableShowAscii(bool showAscii) {
|
||||
m_showAscii = showAscii;
|
||||
}
|
||||
|
||||
void enableShowHumanReadableUnits(bool showHumanReadableUnits) {
|
||||
m_showHumanReadableUnits = showHumanReadableUnits;
|
||||
}
|
||||
|
||||
void enableSyncScrolling(bool syncScrolling) {
|
||||
m_syncScrolling = syncScrolling;
|
||||
}
|
||||
|
||||
void setByteCellPadding(u32 byteCellPadding) {
|
||||
m_byteCellPadding = byteCellPadding;
|
||||
}
|
||||
|
||||
void setCharacterCellPadding(u32 characterCellPadding) {
|
||||
m_characterCellPadding = characterCellPadding;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::optional<EncodingFile>& getCustomEncoding() const {
|
||||
return m_currCustomEncoding;
|
||||
}
|
||||
|
||||
void setCustomEncoding(const EncodingFile &encoding) {
|
||||
m_currCustomEncoding = encoding;
|
||||
m_encodingLineStartAddresses.clear();
|
||||
}
|
||||
|
||||
void setCustomEncoding(EncodingFile &&encoding) {
|
||||
m_currCustomEncoding = std::move(encoding);
|
||||
m_encodingLineStartAddresses.clear();
|
||||
}
|
||||
|
||||
void forceUpdateScrollPosition() {
|
||||
m_shouldUpdateScrollPosition = true;
|
||||
}
|
||||
|
||||
void setForegroundHighlightCallback(const std::function<std::optional<color_t>(u64, const u8 *, size_t)> &callback) {
|
||||
m_foregroundColorCallback = callback;
|
||||
}
|
||||
|
||||
void setBackgroundHighlightCallback(const std::function<std::optional<color_t>(u64, const u8 *, size_t)> &callback) {
|
||||
m_backgroundColorCallback = callback;
|
||||
}
|
||||
|
||||
void setTooltipCallback(const std::function<void(u64, const u8 *, size_t)> &callback) {
|
||||
m_tooltipCallback = callback;
|
||||
}
|
||||
|
||||
[[nodiscard]] float getScrollPosition() const {
|
||||
return m_scrollPosition;
|
||||
}
|
||||
|
||||
void setScrollPosition(float scrollPosition) {
|
||||
m_scrollPosition = scrollPosition;
|
||||
}
|
||||
|
||||
void setEditingAddress(u64 address) {
|
||||
m_editingAddress = address;
|
||||
m_shouldModifyValue = false;
|
||||
m_enteredEditingMode = true;
|
||||
|
||||
m_editingBytes.resize(m_currDataVisualizer->getBytesPerCell());
|
||||
m_provider->read(address + m_provider->getBaseAddress(), m_editingBytes.data(), m_editingBytes.size());
|
||||
m_editingCellType = CellType::Hex;
|
||||
}
|
||||
|
||||
void clearEditingAddress() {
|
||||
m_editingAddress = std::nullopt;
|
||||
}
|
||||
|
||||
private:
|
||||
prv::Provider *m_provider;
|
||||
|
||||
std::optional<u64> m_selectionStart;
|
||||
std::optional<u64> m_selectionEnd;
|
||||
std::optional<u64> m_cursorPosition;
|
||||
ImS64 m_scrollPosition = 0;
|
||||
|
||||
u16 m_bytesPerRow = 16;
|
||||
std::endian m_dataVisualizerEndianness = std::endian::little;
|
||||
std::shared_ptr<ContentRegistry::HexEditor::DataVisualizer> m_currDataVisualizer;
|
||||
char m_unknownDataCharacter = '?';
|
||||
|
||||
bool m_shouldJumpToSelection = false;
|
||||
bool m_centerOnJump = false;
|
||||
bool m_shouldScrollToSelection = false;
|
||||
bool m_shouldJumpWhenOffScreen = false;
|
||||
bool m_shouldUpdateScrollPosition = false;
|
||||
|
||||
bool m_selectionChanged = false;
|
||||
|
||||
u16 m_visibleRowCount = 0;
|
||||
|
||||
CellType m_editingCellType = CellType::None;
|
||||
std::optional<u64> m_editingAddress;
|
||||
bool m_shouldModifyValue = false;
|
||||
bool m_enteredEditingMode = false;
|
||||
bool m_shouldUpdateEditingValue = false;
|
||||
std::vector<u8> m_editingBytes;
|
||||
|
||||
color_t m_selectionColor = 0x00;
|
||||
bool m_upperCaseHex = true;
|
||||
bool m_grayOutZero = true;
|
||||
bool m_showAscii = true;
|
||||
bool m_showCustomEncoding = true;
|
||||
bool m_showHumanReadableUnits = true;
|
||||
bool m_syncScrolling = false;
|
||||
u32 m_byteCellPadding = 0, m_characterCellPadding = 0;
|
||||
bool m_footerCollapsed = true;
|
||||
|
||||
std::optional<EncodingFile> m_currCustomEncoding;
|
||||
std::vector<u64> m_encodingLineStartAddresses;
|
||||
|
||||
std::pair<Region, bool> m_currValidRegion = { Region::Invalid(), false };
|
||||
|
||||
static std::optional<color_t> defaultColorCallback(u64, const u8 *, size_t) { return std::nullopt; }
|
||||
static void defaultTooltipCallback(u64, const u8 *, size_t) { }
|
||||
std::function<std::optional<color_t>(u64, const u8 *, size_t)> m_foregroundColorCallback = defaultColorCallback, m_backgroundColorCallback = defaultColorCallback;
|
||||
std::function<void(u64, const u8 *, size_t)> m_tooltipCallback = defaultTooltipCallback;
|
||||
};
|
||||
|
||||
}
|
||||
122
plugins/ui/include/ui/pattern_drawer.hpp
Normal file
122
plugins/ui/include/ui/pattern_drawer.hpp
Normal file
@@ -0,0 +1,122 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/api/task_manager.hpp>
|
||||
#include <hex/api/content_registry.hpp>
|
||||
|
||||
#include <pl/patterns/pattern.hpp>
|
||||
#include <pl/pattern_visitor.hpp>
|
||||
|
||||
#include <pl/formatters.hpp>
|
||||
|
||||
#include <set>
|
||||
|
||||
struct ImGuiTableSortSpecs;
|
||||
|
||||
namespace hex::ui {
|
||||
|
||||
class PatternDrawer : public pl::PatternVisitor {
|
||||
public:
|
||||
PatternDrawer() {
|
||||
m_formatters = pl::gen::fmt::createFormatters();
|
||||
}
|
||||
|
||||
virtual ~PatternDrawer() = default;
|
||||
|
||||
void draw(const std::vector<std::shared_ptr<pl::ptrn::Pattern>> &patterns, pl::PatternLanguage *runtime = nullptr, float height = 0.0F);
|
||||
|
||||
enum class TreeStyle {
|
||||
Default = 0,
|
||||
AutoExpanded = 1,
|
||||
Flattened = 2
|
||||
};
|
||||
|
||||
void setTreeStyle(TreeStyle style) { m_treeStyle = style; }
|
||||
void setSelectionCallback(std::function<void(Region)> callback) { m_selectionCallback = std::move(callback); }
|
||||
void enableRowColoring(bool enabled) { m_rowColoring = enabled; }
|
||||
void reset();
|
||||
|
||||
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;
|
||||
void visit(pl::ptrn::PatternBitfieldArray& pattern) override;
|
||||
void visit(pl::ptrn::PatternBitfield& 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;
|
||||
|
||||
private:
|
||||
constexpr static auto ChunkSize = 512;
|
||||
constexpr static auto DisplayEndStep = 64;
|
||||
|
||||
void drawArray(pl::ptrn::Pattern& pattern, pl::ptrn::IIterable &iterable, bool isInlined);
|
||||
u64& getDisplayEnd(const pl::ptrn::Pattern& pattern);
|
||||
void makeSelectable(const pl::ptrn::Pattern &pattern);
|
||||
|
||||
void drawValueColumn(pl::ptrn::Pattern& pattern);
|
||||
void drawVisualizer(const std::map<std::string, ContentRegistry::PatternLanguage::impl::Visualizer> &visualizers, const std::vector<pl::core::Token::Literal> &arguments, pl::ptrn::Pattern &pattern, pl::ptrn::IIterable &iterable, bool reset);
|
||||
void drawFavoriteColumn(const pl::ptrn::Pattern& pattern);
|
||||
void drawColorColumn(const pl::ptrn::Pattern& pattern);
|
||||
|
||||
bool beginPatternTable(const std::vector<std::shared_ptr<pl::ptrn::Pattern>> &patterns, std::vector<pl::ptrn::Pattern*> &sortedPatterns, float height) const;
|
||||
bool createTreeNode(const pl::ptrn::Pattern& pattern, bool leaf = false);
|
||||
void createDefaultEntry(const pl::ptrn::Pattern &pattern);
|
||||
void closeTreeNode(bool inlined) const;
|
||||
|
||||
bool sortPatterns(const ImGuiTableSortSpecs* sortSpecs, const pl::ptrn::Pattern * left, const pl::ptrn::Pattern * right) const;
|
||||
bool isEditingPattern(const pl::ptrn::Pattern& pattern) const;
|
||||
void resetEditing();
|
||||
bool matchesFilter(const std::vector<std::string> &filterPath, const std::vector<std::string> &patternPath, bool fullMatch) const;
|
||||
void traversePatternTree(pl::ptrn::Pattern &pattern, std::vector<std::string> &patternPath, const std::function<void(pl::ptrn::Pattern&)> &callback);
|
||||
std::string getDisplayName(const pl::ptrn::Pattern& pattern) const;
|
||||
|
||||
struct Filter {
|
||||
std::vector<std::string> path;
|
||||
std::optional<pl::core::Token::Literal> value;
|
||||
};
|
||||
|
||||
std::optional<Filter> parseRValueFilter(const std::string &filter) const;
|
||||
|
||||
private:
|
||||
std::map<const pl::ptrn::Pattern*, u64> m_displayEnd;
|
||||
std::vector<pl::ptrn::Pattern*> m_sortedPatterns;
|
||||
|
||||
const pl::ptrn::Pattern *m_editingPattern = nullptr;
|
||||
u64 m_editingPatternOffset = 0;
|
||||
|
||||
TreeStyle m_treeStyle = TreeStyle::Default;
|
||||
bool m_rowColoring = false;
|
||||
pl::ptrn::Pattern *m_currVisualizedPattern = nullptr;
|
||||
|
||||
std::set<pl::ptrn::Pattern*> m_visualizedPatterns;
|
||||
std::string m_lastVisualizerError;
|
||||
|
||||
std::string m_filterText;
|
||||
Filter m_filter;
|
||||
std::vector<std::string> m_currPatternPath;
|
||||
std::map<std::vector<std::string>, std::unique_ptr<pl::ptrn::Pattern>> m_favorites;
|
||||
std::map<std::string, std::vector<std::unique_ptr<pl::ptrn::Pattern>>> m_groups;
|
||||
bool m_showFavoriteStars = false;
|
||||
bool m_favoritesUpdated = false;
|
||||
bool m_showSpecName = false;
|
||||
|
||||
TaskHolder m_favoritesUpdateTask;
|
||||
|
||||
std::function<void(Region)> m_selectionCallback = [](Region) { };
|
||||
|
||||
pl::gen::fmt::FormatterArray m_formatters;
|
||||
};
|
||||
}
|
||||
61
plugins/ui/include/ui/widgets.hpp
Normal file
61
plugins/ui/include/ui/widgets.hpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <imgui.h>
|
||||
#include <hex/ui/imgui_imhex_extensions.h>
|
||||
|
||||
namespace pl::ptrn { class Pattern; }
|
||||
namespace hex::prv { class Provider; }
|
||||
|
||||
namespace hex::ui {
|
||||
|
||||
|
||||
enum class RegionType : int {
|
||||
EntireData,
|
||||
Selection,
|
||||
Region
|
||||
};
|
||||
|
||||
inline void regionSelectionPicker(Region *region, prv::Provider *provider, RegionType *type, bool showHeader = true, bool firstEntry = false) {
|
||||
if (showHeader)
|
||||
ImGuiExt::Header("hex.ui.common.range"_lang, firstEntry);
|
||||
|
||||
if (ImGui::RadioButton("hex.ui.common.range.entire_data"_lang, *type == RegionType::EntireData))
|
||||
*type = RegionType::EntireData;
|
||||
if (ImGui::RadioButton("hex.ui.common.range.selection"_lang, *type == RegionType::Selection))
|
||||
*type = RegionType::Selection;
|
||||
if (ImGui::RadioButton("hex.ui.common.region"_lang, *type == RegionType::Region))
|
||||
*type = RegionType::Region;
|
||||
|
||||
switch (*type) {
|
||||
case RegionType::EntireData:
|
||||
*region = { provider->getBaseAddress(), provider->getActualSize() };
|
||||
break;
|
||||
case RegionType::Selection:
|
||||
*region = ImHexApi::HexEditor::getSelection().value_or(
|
||||
ImHexApi::HexEditor::ProviderRegion {
|
||||
{ 0, 1 },
|
||||
provider }
|
||||
).getRegion();
|
||||
break;
|
||||
case RegionType::Region:
|
||||
ImGui::SameLine();
|
||||
|
||||
const auto width = ImGui::GetContentRegionAvail().x / 2 - ImGui::CalcTextSize(" - ").x / 2;
|
||||
u64 start = region->getStartAddress(), end = region->getEndAddress();
|
||||
|
||||
ImGui::PushItemWidth(width);
|
||||
ImGuiExt::InputHexadecimal("##start", &start);
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::TextUnformatted(" - ");
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::PushItemWidth(width);
|
||||
ImGuiExt::InputHexadecimal("##end", &end);
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
*region = { start, (end - start) + 1 };
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user