From c89a870fe9f16d7cdc248087ca77f592c620c7ab Mon Sep 17 00:00:00 2001 From: WerWolv Date: Tue, 21 Nov 2023 13:47:50 +0100 Subject: [PATCH] refactor: Streamline entire view system --- lib/libimhex/include/hex/api/imhex_api.hpp | 9 + .../include/hex/ui/imgui_imhex_extensions.h | 2 + lib/libimhex/include/hex/ui/view.hpp | 138 +++- lib/libimhex/source/api/imhex_api.cpp | 11 + lib/libimhex/source/helpers/utils.cpp | 4 + .../source/ui/imgui_imhex_extensions.cpp | 11 + lib/libimhex/source/ui/view.cpp | 39 +- main/gui/source/init/tasks.cpp | 3 +- main/gui/source/window/window.cpp | 9 +- .../include/content/views/view_about.hpp | 6 +- .../content/views/view_achievements.hpp | 8 +- .../include/content/views/view_bookmarks.hpp | 2 +- .../content/views/view_command_palette.hpp | 7 +- .../include/content/views/view_constants.hpp | 10 +- .../content/views/view_data_inspector.hpp | 2 +- .../content/views/view_data_processor.hpp | 2 +- .../include/content/views/view_diff.hpp | 3 +- .../content/views/view_disassembler.hpp | 2 +- .../include/content/views/view_find.hpp | 2 +- .../include/content/views/view_hashes.hpp | 2 +- .../include/content/views/view_hex_editor.hpp | 5 +- .../content/views/view_information.hpp | 2 +- .../include/content/views/view_logs.hpp | 5 +- .../include/content/views/view_patches.hpp | 4 +- .../content/views/view_pattern_data.hpp | 2 +- .../content/views/view_pattern_editor.hpp | 9 +- .../content/views/view_provider_settings.hpp | 5 +- .../include/content/views/view_settings.hpp | 7 +- .../include/content/views/view_store.hpp | 4 +- .../content/views/view_theme_manager.hpp | 5 +- .../include/content/views/view_tools.hpp | 4 +- .../include/content/views/view_yara.hpp | 2 +- .../source/content/views/view_about.cpp | 41 +- .../content/views/view_achievements.cpp | 202 +++--- .../source/content/views/view_bookmarks.cpp | 363 +++++----- .../content/views/view_command_palette.cpp | 4 +- .../source/content/views/view_constants.cpp | 162 ++--- .../content/views/view_data_inspector.cpp | 393 +++++----- .../content/views/view_data_processor.cpp | 303 ++++---- .../source/content/views/view_diff.cpp | 269 ++++--- .../content/views/view_disassembler.cpp | 679 +++++++++--------- .../source/content/views/view_find.cpp | 590 ++++++++------- .../source/content/views/view_hashes.cpp | 201 +++--- .../source/content/views/view_hex_editor.cpp | 23 +- .../source/content/views/view_information.cpp | 381 +++++----- .../source/content/views/view_logs.cpp | 63 +- .../source/content/views/view_patches.cpp | 101 ++- .../content/views/view_pattern_data.cpp | 25 +- .../content/views/view_pattern_editor.cpp | 323 +++++---- .../content/views/view_provider_settings.cpp | 66 +- .../source/content/views/view_settings.cpp | 11 +- .../source/content/views/view_store.cpp | 16 +- .../content/views/view_theme_manager.cpp | 197 +++-- .../source/content/views/view_tools.cpp | 50 +- .../source/content/views/view_yara.cpp | 250 ++++--- .../include/views/view_tty_console.hpp | 2 +- .../windows/source/views/view_tty_console.cpp | 246 ++++--- 57 files changed, 2643 insertions(+), 2644 deletions(-) diff --git a/lib/libimhex/include/hex/api/imhex_api.hpp b/lib/libimhex/include/hex/api/imhex_api.hpp index 6caa95c7b..dc8ad8382 100644 --- a/lib/libimhex/include/hex/api/imhex_api.hpp +++ b/lib/libimhex/include/hex/api/imhex_api.hpp @@ -11,6 +11,7 @@ using ImGuiID = unsigned int; struct ImVec2; +struct ImFontAtlas; namespace hex { @@ -340,12 +341,14 @@ namespace hex { void setCustomFontPath(const std::fs::path &path); void setFontSize(float size); + void setFontAtlas(ImFontAtlas *fontAtlas); void setGPUVendor(const std::string &vendor); void setPortableVersion(bool enabled); void addInitArgument(const std::string &key, const std::string &value = { }); + } struct ProgramArguments { @@ -457,6 +460,12 @@ namespace hex { */ float getFontSize(); + /** + * @brief Gets the current font atlas + * @return Current font atlas + */ + ImFontAtlas* getFontAtlas(); + /** * @brief Sets if ImHex should follow the system theme diff --git a/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h b/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h index a04a6dcde..c25abcf71 100644 --- a/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h +++ b/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h @@ -274,6 +274,8 @@ namespace ImGuiExt { void BeginSubWindow(const char *label, ImVec2 size = ImVec2(0, 0), ImGuiChildFlags flags = ImGuiChildFlags_None); void EndSubWindow(); + void ConfirmButtons(const char *textLeft, const char *textRight, const std::function &leftButtonCallback, const std::function &rightButtonCallback); + template constexpr ImGuiDataType getImGuiDataType() { if constexpr (std::same_as) return ImGuiDataType_U8; diff --git a/lib/libimhex/include/hex/ui/view.hpp b/lib/libimhex/include/hex/ui/view.hpp index abcb72c74..898576e8e 100644 --- a/lib/libimhex/include/hex/ui/view.hpp +++ b/lib/libimhex/include/hex/ui/view.hpp @@ -18,25 +18,72 @@ #include -#include +#include #include namespace hex { class View { - public: explicit View(std::string unlocalizedName); + public: virtual ~View() = default; - virtual void drawContent() = 0; - virtual void drawAlwaysVisible() { } - [[nodiscard]] virtual bool isAvailable() const; - [[nodiscard]] virtual bool shouldProcess() const { return this->isAvailable() && this->getWindowOpenState(); } + /** + * @brief Draws the view + * @note Do not override this method. Override drawContent() instead + */ + virtual void draw() = 0; + /** + * @brief Draws the content of the view + */ + virtual void drawContent() = 0; + + /** + * @brief Draws content that should always be visible, even if the view is not open + */ + virtual void drawAlwaysVisibleContent() { } + + /** + * @brief Whether or not the view window should be drawn + * @return True if the view window should be drawn, false otherwise + */ + [[nodiscard]] virtual bool shouldDraw() const; + + /** + * @brief Whether or not the entire view should be processed + * If this returns false, the view will not be drawn and no shortcuts will be handled. This includes things + * drawn in the drawAlwaysVisibleContent() function. + * @return True if the view should be processed, false otherwise + */ + [[nodiscard]] virtual bool shouldProcess() const { return true; } + + /** + * @brief Whether or not the view should have an entry in the view menu + * @return True if the view should have an entry in the view menu, false otherwise + */ [[nodiscard]] virtual bool hasViewMenuItemEntry() const; + + /** + * @brief Gets the minimum size of the view window + * @return The minimum size of the view window + */ [[nodiscard]] virtual ImVec2 getMinSize() const; + + /** + * @brief Gets the maximum size of the view window + * @return The maximum size of the view window + */ [[nodiscard]] virtual ImVec2 getMaxSize() const; + /** + * @brief Gets additional window flags for the view window + * @return Additional window flags for the view window + */ + [[nodiscard]] virtual ImGuiWindowFlags getWindowFlags() const; + + + [[nodiscard]] bool &getWindowOpenState(); [[nodiscard]] const bool &getWindowOpenState() const; @@ -46,18 +93,15 @@ namespace hex { [[nodiscard]] bool didWindowJustOpen(); void setWindowJustOpened(bool state); - static void confirmButtons(const std::string &textLeft, const std::string &textRight, const std::function &leftButtonFn, const std::function &rightButtonFn); static void discardNavigationRequests(); - static std::string toWindowName(const std::string &unlocalizedName) { - return LangEntry(unlocalizedName) + "###" + unlocalizedName; - } + [[nodiscard]] static std::string toWindowName(const std::string &unlocalizedName); - static ImFontAtlas *getFontAtlas(); - static void setFontAtlas(ImFontAtlas *atlas); - - static ImFontConfig getFontConfig(); - static void setFontConfig(ImFontConfig config); + public: + class Window; + class Special; + class Floating; + class Modal; private: std::string m_unlocalizedViewName; @@ -68,4 +112,68 @@ namespace hex { friend class ShortcutManager; }; + + /** + * @brief A view that draws a regular window. This should be the default for most views + */ + class View::Window : public View { + public: + explicit Window(std::string unlocalizedName) : View(std::move(unlocalizedName)) {} + + void draw() final { + if (this->shouldDraw()) { + ImGui::SetNextWindowSizeConstraints(this->getMinSize(), this->getMaxSize()); + + if (ImGui::Begin(View::toWindowName(this->getUnlocalizedName()).c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse | this->getWindowFlags())) { + this->drawContent(); + } + ImGui::End(); + } + } + }; + + /** + * @brief A view that doesn't handle any window creation and just draws its content. + * This should be used if you intend to draw your own special window + */ + class View::Special : public View { + public: + explicit Special(std::string unlocalizedName) : View(std::move(unlocalizedName)) {} + + void draw() final { + if (this->shouldDraw()) { + this->drawContent(); + } + } + }; + + /** + * @brief A view that draws a floating window. These are the same as regular windows but cannot be docked + */ + class View::Floating : public View::Window { + public: + explicit Floating(std::string unlocalizedName) : Window(std::move(unlocalizedName)) {} + + [[nodiscard]] ImGuiWindowFlags getWindowFlags() const final { return ImGuiWindowFlags_NoDocking; } + }; + + /** + * @brief A view that draws a modal window. The window will always be drawn on top and will block input to other windows + */ + class View::Modal : public View { + public: + explicit Modal(std::string unlocalizedName) : View(std::move(unlocalizedName)) {} + + void draw() final { + if (this->shouldDraw()) { + ImGui::SetNextWindowSizeConstraints(this->getMinSize(), this->getMaxSize()); + + if (ImGui::BeginPopupModal(View::toWindowName(this->getUnlocalizedName()).c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse | this->getWindowFlags())) { + this->drawContent(); + } + ImGui::EndPopup(); + } + } + }; + } \ No newline at end of file diff --git a/lib/libimhex/source/api/imhex_api.cpp b/lib/libimhex/source/api/imhex_api.cpp index e893898fe..fe1fd756c 100644 --- a/lib/libimhex/source/api/imhex_api.cpp +++ b/lib/libimhex/source/api/imhex_api.cpp @@ -421,6 +421,12 @@ namespace hex { s_fontSize = size; } + static ImFontAtlas *s_fontAtlas; + void setFontAtlas(ImFontAtlas* fontAtlas) { + s_fontAtlas = fontAtlas; + } + + static std::string s_gpuVendor; void setGPUVendor(const std::string &vendor) { s_gpuVendor = vendor; @@ -508,6 +514,11 @@ namespace hex { return impl::s_fontSize; } + ImFontAtlas* getFontAtlas() { + return impl::s_fontAtlas; + } + + static bool s_systemThemeDetection; diff --git a/lib/libimhex/source/helpers/utils.cpp b/lib/libimhex/source/helpers/utils.cpp index 4b17641fc..9ee4f9e71 100644 --- a/lib/libimhex/source/helpers/utils.cpp +++ b/lib/libimhex/source/helpers/utils.cpp @@ -37,6 +37,10 @@ namespace hex { return vector * ImHexApi::System::getGlobalScale(); } + ImVec2 scaled(float x, float y) { + return ImVec2(x, y) * ImHexApi::System::getGlobalScale(); + } + std::string to_string(u128 value) { char data[45] = { 0 }; diff --git a/lib/libimhex/source/ui/imgui_imhex_extensions.cpp b/lib/libimhex/source/ui/imgui_imhex_extensions.cpp index 3e2f5540a..2b7ab05be 100644 --- a/lib/libimhex/source/ui/imgui_imhex_extensions.cpp +++ b/lib/libimhex/source/ui/imgui_imhex_extensions.cpp @@ -917,6 +917,17 @@ namespace ImGuiExt { ImGui::EndChild(); } + void ConfirmButtons(const char *textLeft, const char *textRight, const std::function &leftButtonCallback, const std::function &rightButtonCallback) { + auto width = ImGui::GetWindowWidth(); + ImGui::SetCursorPosX(width / 9); + if (ImGui::Button(textLeft, ImVec2(width / 3, 0))) + leftButtonCallback(); + ImGui::SameLine(); + ImGui::SetCursorPosX(width / 9 * 5); + if (ImGui::Button(textRight, ImVec2(width / 3, 0))) + rightButtonCallback(); + } + } namespace ImGui { diff --git a/lib/libimhex/source/ui/view.cpp b/lib/libimhex/source/ui/view.cpp index c42bae0a1..4a6386342 100644 --- a/lib/libimhex/source/ui/view.cpp +++ b/lib/libimhex/source/ui/view.cpp @@ -7,16 +7,9 @@ namespace hex { - namespace { - - ImFontAtlas *s_fontAtlas; - ImFontConfig s_fontConfig; - - } - View::View(std::string unlocalizedName) : m_unlocalizedViewName(std::move(unlocalizedName)) { } - bool View::isAvailable() const { + bool View::shouldDraw() const { return ImHexApi::Provider::isValid() && ImHexApi::Provider::get()->isAvailable(); } @@ -25,13 +18,18 @@ namespace hex { } ImVec2 View::getMinSize() const { - return scaled(ImVec2(300, 400)); + return scaled({ 300, 400 }); } ImVec2 View::getMaxSize() const { return { FLT_MAX, FLT_MAX }; } + ImGuiWindowFlags View::getWindowFlags() const { + return ImGuiWindowFlags_None; + } + + bool &View::getWindowOpenState() { return this->m_windowOpen; @@ -50,11 +48,7 @@ namespace hex { } bool View::didWindowJustOpen() { - bool result = this->m_windowJustOpened; - - this->m_windowJustOpened = false; - - return result; + return std::exchange(this->m_windowJustOpened, false); } void View::setWindowJustOpened(bool state) { @@ -66,21 +60,8 @@ namespace hex { ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NavEnableKeyboard; } - void View::confirmButtons(const std::string &textLeft, const std::string &textRight, const std::function &leftButtonFn, const std::function &rightButtonFn) { - auto width = ImGui::GetWindowWidth(); - ImGui::SetCursorPosX(width / 9); - if (ImGui::Button(textLeft.c_str(), ImVec2(width / 3, 0))) - leftButtonFn(); - ImGui::SameLine(); - ImGui::SetCursorPosX(width / 9 * 5); - if (ImGui::Button(textRight.c_str(), ImVec2(width / 3, 0))) - rightButtonFn(); + std::string View::toWindowName(const std::string &unlocalizedName) { + return LangEntry(unlocalizedName) + "###" + unlocalizedName; } - ImFontAtlas *View::getFontAtlas() { return s_fontAtlas; } - void View::setFontAtlas(ImFontAtlas *atlas) { s_fontAtlas = atlas; } - - ImFontConfig View::getFontConfig() { return s_fontConfig; } - void View::setFontConfig(ImFontConfig config) { s_fontConfig = std::move(config); } - } \ No newline at end of file diff --git a/main/gui/source/init/tasks.cpp b/main/gui/source/init/tasks.cpp index fd7659387..c06c39043 100644 --- a/main/gui/source/init/tasks.cpp +++ b/main/gui/source/init/tasks.cpp @@ -325,8 +325,7 @@ namespace hex::init { } // Configure ImGui to use the font atlas - View::setFontAtlas(fonts); - View::setFontConfig(cfg); + ImHexApi::System::impl::setFontAtlas(fonts); return true; } diff --git a/main/gui/source/window/window.cpp b/main/gui/source/window/window.cpp index a98f3d330..bb5b842e7 100644 --- a/main/gui/source/window/window.cpp +++ b/main/gui/source/window/window.cpp @@ -810,17 +810,14 @@ namespace hex { ImGui::GetCurrentContext()->NextWindowData.ClearFlags(); // Draw always visible views - view->drawAlwaysVisible(); + view->drawAlwaysVisibleContent(); // Skip views that shouldn't be processed currently if (!view->shouldProcess()) continue; // Draw view - if (view->isAvailable()) { - ImGui::SetNextWindowSizeConstraints(view->getMinSize(), view->getMaxSize()); - view->drawContent(); - } + view->draw(); if (view->getWindowOpenState()) { auto window = ImGui::FindWindowByName(view->getName().c_str()); @@ -1137,7 +1134,7 @@ namespace hex { void Window::initImGui() { IMGUI_CHECKVERSION(); - auto fonts = View::getFontAtlas(); + auto fonts = ImHexApi::System::getFontAtlas(); // Initialize ImGui and all other ImGui extensions GImGui = ImGui::CreateContext(fonts); diff --git a/plugins/builtin/include/content/views/view_about.hpp b/plugins/builtin/include/content/views/view_about.hpp index 99278c11f..942f0edfd 100644 --- a/plugins/builtin/include/content/views/view_about.hpp +++ b/plugins/builtin/include/content/views/view_about.hpp @@ -8,13 +8,13 @@ namespace hex::plugin::builtin { - class ViewAbout : public View { + class ViewAbout : public View::Modal { public: ViewAbout(); void drawContent() override; - [[nodiscard]] bool isAvailable() const override { return true; } + [[nodiscard]] bool shouldDraw() const override { return true; } [[nodiscard]] bool hasViewMenuItemEntry() const override { return false; } [[nodiscard]] ImVec2 getMinSize() const override { @@ -22,8 +22,6 @@ namespace hex::plugin::builtin { } private: - bool m_aboutWindowOpen = false; - void drawAboutPopup(); void drawAboutMainPage(); diff --git a/plugins/builtin/include/content/views/view_achievements.hpp b/plugins/builtin/include/content/views/view_achievements.hpp index af881c7bb..0c7300bc4 100644 --- a/plugins/builtin/include/content/views/view_achievements.hpp +++ b/plugins/builtin/include/content/views/view_achievements.hpp @@ -5,15 +5,15 @@ namespace hex::plugin::builtin { - class ViewAchievements : public View { + class ViewAchievements : public View::Floating { public: ViewAchievements(); ~ViewAchievements() override; void drawContent() override; - void drawAlwaysVisible() override; + void drawAlwaysVisibleContent() override; - [[nodiscard]] bool isAvailable() const override { return true; } + [[nodiscard]] bool shouldDraw() const override { return true; } [[nodiscard]] bool hasViewMenuItemEntry() const override { return false; } [[nodiscard]] ImVec2 getMinSize() const override { @@ -28,8 +28,6 @@ namespace hex::plugin::builtin { ImVec2 drawAchievementTree(ImDrawList *drawList, const AchievementManager::AchievementNode * prevNode, const std::vector &nodes, ImVec2 position); private: - bool m_viewOpen = false; - std::list m_achievementUnlockQueue; const Achievement *m_currAchievement = nullptr; const Achievement *m_achievementToGoto = nullptr; diff --git a/plugins/builtin/include/content/views/view_bookmarks.hpp b/plugins/builtin/include/content/views/view_bookmarks.hpp index 66dc43d4c..5b0443739 100644 --- a/plugins/builtin/include/content/views/view_bookmarks.hpp +++ b/plugins/builtin/include/content/views/view_bookmarks.hpp @@ -6,7 +6,7 @@ namespace hex::plugin::builtin { - class ViewBookmarks : public View { + class ViewBookmarks : public View::Window { public: ViewBookmarks(); ~ViewBookmarks() override; diff --git a/plugins/builtin/include/content/views/view_command_palette.hpp b/plugins/builtin/include/content/views/view_command_palette.hpp index 5e6a3502d..b77eec92e 100644 --- a/plugins/builtin/include/content/views/view_command_palette.hpp +++ b/plugins/builtin/include/content/views/view_command_palette.hpp @@ -8,14 +8,15 @@ namespace hex::plugin::builtin { - class ViewCommandPalette : public View { + class ViewCommandPalette : public View::Special { public: ViewCommandPalette(); ~ViewCommandPalette() override = default; - void drawContent() override; + void drawContent() override {} + void drawAlwaysVisibleContent() override; - [[nodiscard]] bool isAvailable() const override { return true; } + [[nodiscard]] bool shouldDraw() const override { return false; } [[nodiscard]] bool shouldProcess() const override { return true; } [[nodiscard]] bool hasViewMenuItemEntry() const override { return false; } diff --git a/plugins/builtin/include/content/views/view_constants.hpp b/plugins/builtin/include/content/views/view_constants.hpp index d4eb0d436..f9334e3e3 100644 --- a/plugins/builtin/include/content/views/view_constants.hpp +++ b/plugins/builtin/include/content/views/view_constants.hpp @@ -20,21 +20,13 @@ namespace hex::plugin::builtin { std::string value; }; - class ViewConstants : public View { + class ViewConstants : public View::Window { public: explicit ViewConstants(); ~ViewConstants() override = default; void drawContent() override; - ImVec2 getMinSize() const override { - return scaled(ImVec2(300, 400)); - } - - ImVec2 getMaxSize() const override { - return { FLT_MAX, 800_scaled }; - } - private: void reloadConstants(); diff --git a/plugins/builtin/include/content/views/view_data_inspector.hpp b/plugins/builtin/include/content/views/view_data_inspector.hpp index 5aa8011a1..367f558be 100644 --- a/plugins/builtin/include/content/views/view_data_inspector.hpp +++ b/plugins/builtin/include/content/views/view_data_inspector.hpp @@ -11,7 +11,7 @@ namespace hex::plugin::builtin { - class ViewDataInspector : public View { + class ViewDataInspector : public View::Window { public: explicit ViewDataInspector(); ~ViewDataInspector() override; diff --git a/plugins/builtin/include/content/views/view_data_processor.hpp b/plugins/builtin/include/content/views/view_data_processor.hpp index 0a80ad1d8..b22555215 100644 --- a/plugins/builtin/include/content/views/view_data_processor.hpp +++ b/plugins/builtin/include/content/views/view_data_processor.hpp @@ -11,7 +11,7 @@ namespace hex::plugin::builtin { - class ViewDataProcessor : public View { + class ViewDataProcessor : public View::Window { public: struct Workspace { Workspace() = default; diff --git a/plugins/builtin/include/content/views/view_diff.hpp b/plugins/builtin/include/content/views/view_diff.hpp index d4b3bd164..e8ff4ed99 100644 --- a/plugins/builtin/include/content/views/view_diff.hpp +++ b/plugins/builtin/include/content/views/view_diff.hpp @@ -12,12 +12,13 @@ namespace hex::plugin::builtin { - class ViewDiff : public View { + class ViewDiff : public View::Window { public: ViewDiff(); ~ViewDiff() override; void drawContent() override; + ImGuiWindowFlags getWindowFlags() const override { return ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse; } public: struct Column { diff --git a/plugins/builtin/include/content/views/view_disassembler.hpp b/plugins/builtin/include/content/views/view_disassembler.hpp index cc5e38a35..4a4d93476 100644 --- a/plugins/builtin/include/content/views/view_disassembler.hpp +++ b/plugins/builtin/include/content/views/view_disassembler.hpp @@ -21,7 +21,7 @@ namespace hex::plugin::builtin { std::string operators; }; - class ViewDisassembler : public View { + class ViewDisassembler : public View::Window { public: explicit ViewDisassembler(); ~ViewDisassembler() override; diff --git a/plugins/builtin/include/content/views/view_find.hpp b/plugins/builtin/include/content/views/view_find.hpp index 3f72511a4..26299639f 100644 --- a/plugins/builtin/include/content/views/view_find.hpp +++ b/plugins/builtin/include/content/views/view_find.hpp @@ -13,7 +13,7 @@ namespace hex::plugin::builtin { - class ViewFind : public View { + class ViewFind : public View::Window { public: ViewFind(); ~ViewFind() override = default; diff --git a/plugins/builtin/include/content/views/view_hashes.hpp b/plugins/builtin/include/content/views/view_hashes.hpp index 38b5330ef..bd64094eb 100644 --- a/plugins/builtin/include/content/views/view_hashes.hpp +++ b/plugins/builtin/include/content/views/view_hashes.hpp @@ -6,7 +6,7 @@ namespace hex::plugin::builtin { - class ViewHashes : public View { + class ViewHashes : public View::Window { public: explicit ViewHashes(); ~ViewHashes() override; diff --git a/plugins/builtin/include/content/views/view_hex_editor.hpp b/plugins/builtin/include/content/views/view_hex_editor.hpp index ad22f93e5..56460cd54 100644 --- a/plugins/builtin/include/content/views/view_hex_editor.hpp +++ b/plugins/builtin/include/content/views/view_hex_editor.hpp @@ -9,12 +9,15 @@ namespace hex::plugin::builtin { - class ViewHexEditor : public View { + class ViewHexEditor : public View::Window { public: ViewHexEditor(); ~ViewHexEditor() override; void drawContent() override; + [[nodiscard]] ImGuiWindowFlags getWindowFlags() const override { + return ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse; + } class Popup { public: diff --git a/plugins/builtin/include/content/views/view_information.hpp b/plugins/builtin/include/content/views/view_information.hpp index d83114bf1..e2c897f39 100644 --- a/plugins/builtin/include/content/views/view_information.hpp +++ b/plugins/builtin/include/content/views/view_information.hpp @@ -10,7 +10,7 @@ namespace hex::plugin::builtin { - class ViewInformation : public View { + class ViewInformation : public View::Window { public: explicit ViewInformation(); ~ViewInformation() override; diff --git a/plugins/builtin/include/content/views/view_logs.hpp b/plugins/builtin/include/content/views/view_logs.hpp index f62ea2a18..189536840 100644 --- a/plugins/builtin/include/content/views/view_logs.hpp +++ b/plugins/builtin/include/content/views/view_logs.hpp @@ -4,19 +4,18 @@ namespace hex::plugin::builtin { - class ViewLogs : public View { + class ViewLogs : public View::Floating { public: ViewLogs(); ~ViewLogs() override = default; void drawContent() override; - [[nodiscard]] bool isAvailable() const override { return true; } + [[nodiscard]] bool shouldDraw() const override { return true; } [[nodiscard]] bool hasViewMenuItemEntry() const override { return false; } private: int m_logLevel = 1; - bool m_viewOpen = false; }; } \ No newline at end of file diff --git a/plugins/builtin/include/content/views/view_patches.hpp b/plugins/builtin/include/content/views/view_patches.hpp index bfa345fde..b1823ea2b 100644 --- a/plugins/builtin/include/content/views/view_patches.hpp +++ b/plugins/builtin/include/content/views/view_patches.hpp @@ -6,13 +6,13 @@ namespace hex::plugin::builtin { - class ViewPatches : public View { + class ViewPatches : public View::Window { public: explicit ViewPatches(); ~ViewPatches() override = default; void drawContent() override; - void drawAlwaysVisible() override; + void drawAlwaysVisibleContent() override; private: u64 m_selectedPatch = 0x00; diff --git a/plugins/builtin/include/content/views/view_pattern_data.hpp b/plugins/builtin/include/content/views/view_pattern_data.hpp index 8eaea906b..3f37b87f4 100644 --- a/plugins/builtin/include/content/views/view_pattern_data.hpp +++ b/plugins/builtin/include/content/views/view_pattern_data.hpp @@ -6,7 +6,7 @@ namespace hex::plugin::builtin { - class ViewPatternData : public View { + class ViewPatternData : public View::Window { public: ViewPatternData(); ~ViewPatternData() override; diff --git a/plugins/builtin/include/content/views/view_pattern_editor.hpp b/plugins/builtin/include/content/views/view_pattern_editor.hpp index 03d74a67f..2f34656d8 100644 --- a/plugins/builtin/include/content/views/view_pattern_editor.hpp +++ b/plugins/builtin/include/content/views/view_pattern_editor.hpp @@ -24,13 +24,16 @@ namespace pl::ptrn { class Pattern; } namespace hex::plugin::builtin { - class ViewPatternEditor : public View { + class ViewPatternEditor : public View::Window { public: ViewPatternEditor(); ~ViewPatternEditor() override; - void drawAlwaysVisible() override; + void drawAlwaysVisibleContent() override; void drawContent() override; + [[nodiscard]] ImGuiWindowFlags getWindowFlags() const override { + return ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse; + } private: enum class DangerousFunctionPerms : u8 { @@ -79,7 +82,7 @@ namespace hex::plugin::builtin { ImGui::NewLine(); ImGui::TextUnformatted("hex.builtin.view.pattern_editor.accept_pattern.question"_lang); - confirmButtons("hex.builtin.common.yes"_lang, "hex.builtin.common.no"_lang, + ImGuiExt::ConfirmButtons("hex.builtin.common.yes"_lang, "hex.builtin.common.no"_lang, [this, provider] { this->m_view->loadPatternFile(this->m_view->m_possiblePatternFiles.get(provider)[this->m_selectedPatternFile], provider); this->close(); diff --git a/plugins/builtin/include/content/views/view_provider_settings.hpp b/plugins/builtin/include/content/views/view_provider_settings.hpp index e8e2d684f..96a03688c 100644 --- a/plugins/builtin/include/content/views/view_provider_settings.hpp +++ b/plugins/builtin/include/content/views/view_provider_settings.hpp @@ -4,17 +4,16 @@ namespace hex::plugin::builtin { - class ViewProviderSettings : public hex::View { + class ViewProviderSettings : public View::Modal { public: ViewProviderSettings(); ~ViewProviderSettings() override; void drawContent() override; - void drawAlwaysVisible() override; [[nodiscard]] bool hasViewMenuItemEntry() const override; - [[nodiscard]] bool isAvailable() const override; + [[nodiscard]] bool shouldDraw() const override { return true; } }; } \ No newline at end of file diff --git a/plugins/builtin/include/content/views/view_settings.hpp b/plugins/builtin/include/content/views/view_settings.hpp index 6b5b8f8ab..ac89dc69f 100644 --- a/plugins/builtin/include/content/views/view_settings.hpp +++ b/plugins/builtin/include/content/views/view_settings.hpp @@ -4,14 +4,15 @@ namespace hex::plugin::builtin { - class ViewSettings : public View { + class ViewSettings : public View::Floating { public: explicit ViewSettings(); ~ViewSettings() override; - void drawContent() override; + void drawContent() override {} + void drawAlwaysVisibleContent() override; - [[nodiscard]] bool isAvailable() const override { return true; } + [[nodiscard]] bool shouldDraw() const override { return false; } [[nodiscard]] bool hasViewMenuItemEntry() const override { return false; } [[nodiscard]] ImVec2 getMinSize() const override { return scaled({ 700, 400 }); } diff --git a/plugins/builtin/include/content/views/view_store.hpp b/plugins/builtin/include/content/views/view_store.hpp index f77522e4d..3c1fd91e0 100644 --- a/plugins/builtin/include/content/views/view_store.hpp +++ b/plugins/builtin/include/content/views/view_store.hpp @@ -44,14 +44,14 @@ namespace hex::plugin::builtin { std::function downloadCallback; }; - class ViewStore : public View { + class ViewStore : public View::Floating { public: ViewStore(); ~ViewStore() override = default; void drawContent() override; - [[nodiscard]] bool isAvailable() const override { return true; } + [[nodiscard]] bool shouldDraw() const override { return false; } [[nodiscard]] bool hasViewMenuItemEntry() const override { return false; } [[nodiscard]] ImVec2 getMinSize() const override { return scaled({ 600, 400 }); } diff --git a/plugins/builtin/include/content/views/view_theme_manager.hpp b/plugins/builtin/include/content/views/view_theme_manager.hpp index f049e05b2..14c9c12ea 100644 --- a/plugins/builtin/include/content/views/view_theme_manager.hpp +++ b/plugins/builtin/include/content/views/view_theme_manager.hpp @@ -6,19 +6,18 @@ namespace hex::plugin::builtin { - class ViewThemeManager : public View { + class ViewThemeManager : public View::Floating { public: ViewThemeManager(); ~ViewThemeManager() override = default; void drawContent() override; - [[nodiscard]] bool isAvailable() const override { return true; } + [[nodiscard]] bool shouldDraw() const override { return true; } [[nodiscard]] bool hasViewMenuItemEntry() const override { return false; } private: std::string m_themeName; - bool m_viewOpen = false; std::optional m_startingColor; std::optional m_hoveredColorId; diff --git a/plugins/builtin/include/content/views/view_tools.hpp b/plugins/builtin/include/content/views/view_tools.hpp index 436a5d21e..b4529b066 100644 --- a/plugins/builtin/include/content/views/view_tools.hpp +++ b/plugins/builtin/include/content/views/view_tools.hpp @@ -7,13 +7,13 @@ namespace hex::plugin::builtin { - class ViewTools : public View { + class ViewTools : public View::Window { public: ViewTools(); ~ViewTools() override = default; void drawContent() override; - void drawAlwaysVisible() override; + void drawAlwaysVisibleContent() override; private: std::vector::iterator m_dragStartIterator; diff --git a/plugins/builtin/include/content/views/view_yara.hpp b/plugins/builtin/include/content/views/view_yara.hpp index 763b193a6..e91ab9e13 100644 --- a/plugins/builtin/include/content/views/view_yara.hpp +++ b/plugins/builtin/include/content/views/view_yara.hpp @@ -8,7 +8,7 @@ namespace hex::plugin::builtin { - class ViewYara : public View { + class ViewYara : public View::Window { public: ViewYara(); ~ViewYara() override; diff --git a/plugins/builtin/source/content/views/view_about.cpp b/plugins/builtin/source/content/views/view_about.cpp index fd52ff4b7..57e4f2425 100644 --- a/plugins/builtin/source/content/views/view_about.cpp +++ b/plugins/builtin/source/content/views/view_about.cpp @@ -16,12 +16,11 @@ namespace hex::plugin::builtin { - ViewAbout::ViewAbout() : View("hex.builtin.view.help.about.name") { + ViewAbout::ViewAbout() : View::Modal("hex.builtin.view.help.about.name") { // Add "About" menu item to the help menu ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.help", "hex.builtin.view.help.about.name" }, 1000, Shortcut::None, [this] { - TaskManager::doLater([] { ImGui::OpenPopup(View::toWindowName("hex.builtin.view.help.about.name").c_str()); }); - this->m_aboutWindowOpen = true; + TaskManager::doLater([this] { ImGui::OpenPopup(View::toWindowName(this->getUnlocalizedName()).c_str()); }); this->getWindowOpenState() = true; }); @@ -481,38 +480,30 @@ namespace hex::plugin::builtin { Tab { "hex.builtin.view.help.about.license", &ViewAbout::drawLicensePage }, }; - if (ImGui::BeginPopupModal(View::toWindowName("hex.builtin.view.help.about.name").c_str(), &this->m_aboutWindowOpen)) { + // Allow the window to be closed by pressing ESC + if (ImGui::IsKeyDown(ImGui::GetKeyIndex(ImGuiKey_Escape))) + ImGui::CloseCurrentPopup(); - // Allow the window to be closed by pressing ESC - if (ImGui::IsKeyDown(ImGui::GetKeyIndex(ImGuiKey_Escape))) - ImGui::CloseCurrentPopup(); + if (ImGui::BeginTabBar("about_tab_bar")) { + // Draw all tabs + for (const auto &[unlocalizedName, function] : Tabs) { + if (ImGui::BeginTabItem(LangEntry(unlocalizedName))) { + ImGui::NewLine(); - if (ImGui::BeginTabBar("about_tab_bar")) { - // Draw all tabs - for (const auto &[unlocalizedName, function] : Tabs) { - if (ImGui::BeginTabItem(LangEntry(unlocalizedName))) { - ImGui::NewLine(); - - if (ImGui::BeginChild(1)) { - (this->*function)(); - } - ImGui::EndChild(); - - ImGui::EndTabItem(); + if (ImGui::BeginChild(1)) { + (this->*function)(); } - } + ImGui::EndChild(); - ImGui::EndTabBar(); + ImGui::EndTabItem(); + } } - ImGui::EndPopup(); + ImGui::EndTabBar(); } } void ViewAbout::drawContent() { - if (!this->m_aboutWindowOpen) - this->getWindowOpenState() = false; - this->drawAboutPopup(); } diff --git a/plugins/builtin/source/content/views/view_achievements.cpp b/plugins/builtin/source/content/views/view_achievements.cpp index 093305d9f..b1697a607 100644 --- a/plugins/builtin/source/content/views/view_achievements.cpp +++ b/plugins/builtin/source/content/views/view_achievements.cpp @@ -9,10 +9,9 @@ namespace hex::plugin::builtin { - ViewAchievements::ViewAchievements() : View("hex.builtin.view.achievements.name") { + ViewAchievements::ViewAchievements() : View::Floating("hex.builtin.view.achievements.name") { // Add achievements menu item to Extas menu ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.extras", "hex.builtin.view.achievements.name" }, 2600, Shortcut::None, [&, this] { - this->m_viewOpen = true; this->getWindowOpenState() = true; }); @@ -24,7 +23,6 @@ namespace hex::plugin::builtin { EventManager::subscribe(this, [this](const std::string &name) { if (name == "Achievements") { TaskManager::doLater([this] { - this->m_viewOpen = true; this->getWindowOpenState() = true; }); } @@ -302,113 +300,108 @@ namespace hex::plugin::builtin { } void ViewAchievements::drawContent() { - if (ImGui::Begin(View::toWindowName("hex.builtin.view.achievements.name").c_str(), &this->m_viewOpen, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoDocking)) { - if (ImGui::BeginTabBar("##achievement_categories")) { - auto &startNodes = AchievementManager::getAchievementStartNodes(); + if (ImGui::BeginTabBar("##achievement_categories")) { + auto &startNodes = AchievementManager::getAchievementStartNodes(); - // Get all achievement category names - std::vector categories; - for (const auto &[categoryName, achievements] : startNodes) { - categories.push_back(categoryName); - } - - std::reverse(categories.begin(), categories.end()); - - // Draw each individual achievement category - for (const auto &categoryName : categories) { - const auto &achievements = startNodes[categoryName]; - - // Check if any achievements in the category are unlocked or unlockable - bool visible = false; - for (const auto &achievement : achievements) { - if (achievement->isUnlocked() || achievement->isUnlockable()) { - visible = true; - break; - } - } - - // If all achievements in this category are invisible, don't draw it - if (!visible) - continue; - - ImGuiTabItemFlags flags = ImGuiTabItemFlags_None; - - // Handle jumping to the category of an achievement - if (this->m_achievementToGoto != nullptr) { - if (this->m_achievementToGoto->getUnlocalizedCategory() == categoryName) { - flags |= ImGuiTabItemFlags_SetSelected; - } - } - - // Draw the achievement category - if (ImGui::BeginTabItem(LangEntry(categoryName), nullptr, flags)) { - auto drawList = ImGui::GetWindowDrawList(); - - const auto cursorPos = ImGui::GetCursorPos(); - const auto windowPos = ImGui::GetWindowPos() + ImVec2(0, cursorPos.y); - const auto windowSize = ImGui::GetWindowSize() - ImVec2(0, cursorPos.y); - const float borderSize = 20.0_scaled; - - const auto windowPadding = ImGui::GetStyle().WindowPadding; - const auto innerWindowPos = windowPos + ImVec2(borderSize, borderSize); - const auto innerWindowSize = windowSize - ImVec2(borderSize * 2, borderSize * 2) - ImVec2(0, ImGui::GetTextLineHeightWithSpacing()); - - // Prevent the achievement tree from being drawn outside of the window - drawList->PushClipRect(innerWindowPos, innerWindowPos + innerWindowSize, true); - - drawList->ChannelsSplit(4); - - drawList->ChannelsSetCurrent(0); - - // Draw achievement background - drawBackground(drawList, innerWindowPos, innerWindowPos + innerWindowSize, this->m_offset); - - // Draw the achievement tree - auto maxPos = drawAchievementTree(drawList, nullptr, achievements, innerWindowPos + scaled({ 100, 100 }) + this->m_offset); - - drawList->ChannelsSetCurrent(3); - - // Draw the achievement overlay - drawOverlay(drawList, innerWindowPos, innerWindowPos + innerWindowSize, categoryName); - - drawList->ChannelsMerge(); - - // Handle dragging the achievement tree around - if (ImGui::IsMouseHoveringRect(innerWindowPos, innerWindowPos + innerWindowSize)) { - auto dragDelta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Left); - this->m_offset += dragDelta; - ImGui::ResetMouseDragDelta(ImGuiMouseButton_Left); - } - - // Clamp the achievement tree to the window - this->m_offset = -ImClamp(-this->m_offset, { 0, 0 }, ImMax(maxPos - innerWindowPos - innerWindowSize, { 0, 0 })); - - drawList->PopClipRect(); - - // Draw settings below the window - ImGui::SetCursorScreenPos(innerWindowPos + ImVec2(0, innerWindowSize.y + windowPadding.y)); - ImGui::BeginGroup(); - { - if (ImGui::Checkbox("Show popup", &this->m_showPopup)) - ContentRegistry::Settings::write("hex.builtin.setting.interface", "hex.builtin.setting.interface.achievement_popup", this->m_showPopup); - } - ImGui::EndGroup(); - - ImGui::EndTabItem(); - } - } - - ImGui::EndTabBar(); + // Get all achievement category names + std::vector categories; + for (const auto &[categoryName, achievements] : startNodes) { + categories.push_back(categoryName); } - } - ImGui::End(); - this->getWindowOpenState() = this->m_viewOpen; + std::reverse(categories.begin(), categories.end()); + + // Draw each individual achievement category + for (const auto &categoryName : categories) { + const auto &achievements = startNodes[categoryName]; + + // Check if any achievements in the category are unlocked or unlockable + bool visible = false; + for (const auto &achievement : achievements) { + if (achievement->isUnlocked() || achievement->isUnlockable()) { + visible = true; + break; + } + } + + // If all achievements in this category are invisible, don't draw it + if (!visible) + continue; + + ImGuiTabItemFlags flags = ImGuiTabItemFlags_None; + + // Handle jumping to the category of an achievement + if (this->m_achievementToGoto != nullptr) { + if (this->m_achievementToGoto->getUnlocalizedCategory() == categoryName) { + flags |= ImGuiTabItemFlags_SetSelected; + } + } + + // Draw the achievement category + if (ImGui::BeginTabItem(LangEntry(categoryName), nullptr, flags)) { + auto drawList = ImGui::GetWindowDrawList(); + + const auto cursorPos = ImGui::GetCursorPos(); + const auto windowPos = ImGui::GetWindowPos() + ImVec2(0, cursorPos.y); + const auto windowSize = ImGui::GetWindowSize() - ImVec2(0, cursorPos.y); + const float borderSize = 20.0_scaled; + + const auto windowPadding = ImGui::GetStyle().WindowPadding; + const auto innerWindowPos = windowPos + ImVec2(borderSize, borderSize); + const auto innerWindowSize = windowSize - ImVec2(borderSize * 2, borderSize * 2) - ImVec2(0, ImGui::GetTextLineHeightWithSpacing()); + + // Prevent the achievement tree from being drawn outside of the window + drawList->PushClipRect(innerWindowPos, innerWindowPos + innerWindowSize, true); + + drawList->ChannelsSplit(4); + + drawList->ChannelsSetCurrent(0); + + // Draw achievement background + drawBackground(drawList, innerWindowPos, innerWindowPos + innerWindowSize, this->m_offset); + + // Draw the achievement tree + auto maxPos = drawAchievementTree(drawList, nullptr, achievements, innerWindowPos + scaled({ 100, 100 }) + this->m_offset); + + drawList->ChannelsSetCurrent(3); + + // Draw the achievement overlay + drawOverlay(drawList, innerWindowPos, innerWindowPos + innerWindowSize, categoryName); + + drawList->ChannelsMerge(); + + // Handle dragging the achievement tree around + if (ImGui::IsMouseHoveringRect(innerWindowPos, innerWindowPos + innerWindowSize)) { + auto dragDelta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Left); + this->m_offset += dragDelta; + ImGui::ResetMouseDragDelta(ImGuiMouseButton_Left); + } + + // Clamp the achievement tree to the window + this->m_offset = -ImClamp(-this->m_offset, { 0, 0 }, ImMax(maxPos - innerWindowPos - innerWindowSize, { 0, 0 })); + + drawList->PopClipRect(); + + // Draw settings below the window + ImGui::SetCursorScreenPos(innerWindowPos + ImVec2(0, innerWindowSize.y + windowPadding.y)); + ImGui::BeginGroup(); + { + if (ImGui::Checkbox("Show popup", &this->m_showPopup)) + ContentRegistry::Settings::write("hex.builtin.setting.interface", "hex.builtin.setting.interface.achievement_popup", this->m_showPopup); + } + ImGui::EndGroup(); + + ImGui::EndTabItem(); + } + } + + ImGui::EndTabBar(); + } this->m_achievementToGoto = nullptr; } - void ViewAchievements::drawAlwaysVisible() { + void ViewAchievements::drawAlwaysVisibleContent() { // Handle showing the achievement unlock popup if (this->m_achievementUnlockQueueTimer >= 0 && this->m_showPopup) { @@ -439,8 +432,7 @@ namespace hex::plugin::builtin { // Handle clicking on the popup if (ImGui::IsWindowHovered() && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) { // Open the achievement window and jump to the achievement - this->m_viewOpen = true; - this->getWindowOpenState() = this->m_viewOpen; + this->getWindowOpenState() = true; this->m_achievementToGoto = this->m_currAchievement; } } diff --git a/plugins/builtin/source/content/views/view_bookmarks.cpp b/plugins/builtin/source/content/views/view_bookmarks.cpp index b2b1ba9d2..3e6bd46e3 100644 --- a/plugins/builtin/source/content/views/view_bookmarks.cpp +++ b/plugins/builtin/source/content/views/view_bookmarks.cpp @@ -17,7 +17,7 @@ namespace hex::plugin::builtin { - ViewBookmarks::ViewBookmarks() : View("hex.builtin.view.bookmarks.name") { + ViewBookmarks::ViewBookmarks() : View::Window("hex.builtin.view.bookmarks.name") { // Handle bookmark add requests sent by the API EventManager::subscribe(this, [this](Region region, std::string name, std::string comment, color_t color) { @@ -201,193 +201,190 @@ namespace hex::plugin::builtin { } void ViewBookmarks::drawContent() { - if (ImGui::Begin(View::toWindowName("hex.builtin.view.bookmarks.name").c_str(), &this->getWindowOpenState())) { - auto provider = ImHexApi::Provider::get(); + auto provider = ImHexApi::Provider::get(); - // Draw filter input - ImGui::PushItemWidth(-1); - ImGuiExt::InputTextIcon("##filter", ICON_VS_FILTER, this->m_currFilter); - ImGui::PopItemWidth(); + // Draw filter input + ImGui::PushItemWidth(-1); + ImGuiExt::InputTextIcon("##filter", ICON_VS_FILTER, this->m_currFilter); + ImGui::PopItemWidth(); - ImGui::NewLine(); + ImGui::NewLine(); - if (ImGui::BeginChild("##bookmarks")) { - if (this->m_bookmarks->empty()) { - ImGuiExt::TextFormattedCentered("hex.builtin.view.bookmarks.no_bookmarks"_lang); - } - - int id = 1; - auto bookmarkToRemove = this->m_bookmarks->end(); - - // Draw all bookmarks - for (auto iter = this->m_bookmarks->begin(); iter != this->m_bookmarks->end(); iter++) { - auto &[region, name, comment, color, locked] = *iter; - - // Apply filter - if (!this->m_currFilter.empty()) { - if (!name.contains(this->m_currFilter) && !comment.contains(this->m_currFilter)) - continue; - } - - auto headerColor = ImColor(color); - auto hoverColor = ImColor(color); - hoverColor.Value.w *= 1.3F; - - // Draw bookmark header in the same color as the bookmark was set to - ImGui::PushID(id); - ImGui::PushStyleColor(ImGuiCol_Header, color); - ImGui::PushStyleColor(ImGuiCol_HeaderActive, color); - ImGui::PushStyleColor(ImGuiCol_HeaderHovered, u32(hoverColor)); - - ON_SCOPE_EXIT { - ImGui::PopID(); - ImGui::PopStyleColor(3); - id++; - }; - - bool open = true; - if (!ImGui::CollapsingHeader(hex::format("{}###bookmark", name).c_str(), locked ? nullptr : &open)) { - // Handle dragging bookmarks up and down when they're collapsed - - // Set the currently held bookmark as the one being dragged - if (ImGui::IsMouseClicked(0) && ImGui::IsItemActivated() && this->m_dragStartIterator == this->m_bookmarks->end()) - this->m_dragStartIterator = iter; - - // When the mouse moved away from the current bookmark, swap the dragged bookmark with the current one - if (ImGui::IsItemHovered() && this->m_dragStartIterator != this->m_bookmarks->end()) { - std::iter_swap(iter, this->m_dragStartIterator); - this->m_dragStartIterator = iter; - } - - // When the mouse is released, reset the dragged bookmark - if (!ImGui::IsMouseDown(0)) - this->m_dragStartIterator = this->m_bookmarks->end(); - } else { - const auto rowHeight = ImGui::GetTextLineHeightWithSpacing() + 2 * ImGui::GetStyle().FramePadding.y; - if (ImGui::BeginTable("##bookmark_table", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) { - ImGui::TableSetupColumn("##name"); - ImGui::TableSetupColumn("##spacing", ImGuiTableColumnFlags_WidthFixed, 20); - ImGui::TableSetupColumn("##value", ImGuiTableColumnFlags_WidthStretch); - - ImGui::TableNextRow(ImGuiTableRowFlags_None, rowHeight); - ImGui::TableNextColumn(); - - // Draw bookmark name - ImGui::TextUnformatted("hex.builtin.view.bookmarks.header.name"_lang); - ImGui::TableNextColumn(); - ImGui::TableNextColumn(); - - // Draw lock/unlock button - if (locked) { - if (ImGuiExt::IconButton(ICON_VS_LOCK, ImGui::GetStyleColorVec4(ImGuiCol_Text))) locked = false; - ImGuiExt::InfoTooltip("hex.builtin.view.bookmarks.tooltip.unlock"_lang); - } else { - if (ImGuiExt::IconButton(ICON_VS_UNLOCK, ImGui::GetStyleColorVec4(ImGuiCol_Text))) locked = true; - ImGuiExt::InfoTooltip("hex.builtin.view.bookmarks.tooltip.lock"_lang); - } - - ImGui::SameLine(); - - // Draw color button - if (ImGui::ColorButton("hex.builtin.view.bookmarks.header.color"_lang, headerColor.Value, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoAlpha)) { - if (!locked) - ImGui::OpenPopup("hex.builtin.view.bookmarks.header.color"_lang); - } - ImGuiExt::InfoTooltip("hex.builtin.view.bookmarks.header.color"_lang); - - // Draw color picker - if (ImGui::BeginPopup("hex.builtin.view.bookmarks.header.color"_lang)) { - drawColorPopup(headerColor); - color = headerColor; - ImGui::EndPopup(); - } - - ImGui::SameLine(); - - // Draw bookmark name if the bookmark is locked or an input text box if it's unlocked - if (locked) - ImGui::TextUnformatted(name.data()); - else { - ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); - ImGui::InputText("##nameInput", name); - ImGui::PopItemWidth(); - } - - ImGui::TableNextRow(ImGuiTableRowFlags_None, rowHeight); - ImGui::TableNextColumn(); - - ImGui::TextUnformatted("hex.builtin.common.address"_lang); - ImGui::TableNextColumn(); - ImGui::TableNextColumn(); - - // Draw jump to address button - if (ImGuiExt::IconButton(ICON_VS_DEBUG_STEP_BACK, ImGui::GetStyleColorVec4(ImGuiCol_Text))) - ImHexApi::HexEditor::setSelection(region); - ImGuiExt::InfoTooltip("hex.builtin.view.bookmarks.tooltip.jump_to"_lang); - - ImGui::SameLine(); - - // Draw open in new view button - if (ImGuiExt::IconButton(ICON_VS_GO_TO_FILE, ImGui::GetStyleColorVec4(ImGuiCol_Text))) { - TaskManager::doLater([region, provider]{ - auto newProvider = ImHexApi::Provider::createProvider("hex.builtin.provider.view", true); - if (auto *viewProvider = dynamic_cast(newProvider); viewProvider != nullptr) { - viewProvider->setProvider(region.getStartAddress(), region.getSize(), provider); - if (viewProvider->open()) { - EventManager::post(viewProvider); - AchievementManager::unlockAchievement("hex.builtin.achievement.hex_editor", "hex.builtin.achievement.hex_editor.open_new_view.name"); - } - } - }); - } - ImGuiExt::InfoTooltip("hex.builtin.view.bookmarks.tooltip.open_in_view"_lang); - - ImGui::SameLine(); - - // Draw the address of the bookmark - ImGuiExt::TextFormatted("hex.builtin.view.bookmarks.address"_lang, region.getStartAddress(), region.getEndAddress()); - - ImGui::TableNextRow(ImGuiTableRowFlags_None, rowHeight); - ImGui::TableNextColumn(); - - // Draw size of the bookmark - ImGui::TextUnformatted("hex.builtin.common.size"_lang); - ImGui::TableNextColumn(); - ImGui::TableNextColumn(); - ImGuiExt::TextFormatted(hex::toByteString(region.size)); - - ImGui::EndTable(); - } - - // Draw comment if the bookmark is locked or an input text box if it's unlocked - if (locked) { - if (!comment.empty()) { - ImGuiExt::Header("hex.builtin.view.bookmarks.header.comment"_lang); - ImGuiExt::TextFormattedWrapped("{}", comment.data()); - } - } - else { - ImGuiExt::Header("hex.builtin.view.bookmarks.header.comment"_lang); - ImGui::InputTextMultiline("##commentInput", comment, ImVec2(ImGui::GetContentRegionAvail().x, 150_scaled)); - } - - ImGui::NewLine(); - } - - // Mark a bookmark for removal when the user clicks the remove button - if (!open) - bookmarkToRemove = iter; - } - - // Remove the bookmark that was marked for removal - if (bookmarkToRemove != this->m_bookmarks->end()) { - this->m_bookmarks->erase(bookmarkToRemove); - EventManager::post(); - } + if (ImGui::BeginChild("##bookmarks")) { + if (this->m_bookmarks->empty()) { + ImGuiExt::TextFormattedCentered("hex.builtin.view.bookmarks.no_bookmarks"_lang); + } + + int id = 1; + auto bookmarkToRemove = this->m_bookmarks->end(); + + // Draw all bookmarks + for (auto iter = this->m_bookmarks->begin(); iter != this->m_bookmarks->end(); iter++) { + auto &[region, name, comment, color, locked] = *iter; + + // Apply filter + if (!this->m_currFilter.empty()) { + if (!name.contains(this->m_currFilter) && !comment.contains(this->m_currFilter)) + continue; + } + + auto headerColor = ImColor(color); + auto hoverColor = ImColor(color); + hoverColor.Value.w *= 1.3F; + + // Draw bookmark header in the same color as the bookmark was set to + ImGui::PushID(id); + ImGui::PushStyleColor(ImGuiCol_Header, color); + ImGui::PushStyleColor(ImGuiCol_HeaderActive, color); + ImGui::PushStyleColor(ImGuiCol_HeaderHovered, u32(hoverColor)); + + ON_SCOPE_EXIT { + ImGui::PopID(); + ImGui::PopStyleColor(3); + id++; + }; + + bool open = true; + if (!ImGui::CollapsingHeader(hex::format("{}###bookmark", name).c_str(), locked ? nullptr : &open)) { + // Handle dragging bookmarks up and down when they're collapsed + + // Set the currently held bookmark as the one being dragged + if (ImGui::IsMouseClicked(0) && ImGui::IsItemActivated() && this->m_dragStartIterator == this->m_bookmarks->end()) + this->m_dragStartIterator = iter; + + // When the mouse moved away from the current bookmark, swap the dragged bookmark with the current one + if (ImGui::IsItemHovered() && this->m_dragStartIterator != this->m_bookmarks->end()) { + std::iter_swap(iter, this->m_dragStartIterator); + this->m_dragStartIterator = iter; + } + + // When the mouse is released, reset the dragged bookmark + if (!ImGui::IsMouseDown(0)) + this->m_dragStartIterator = this->m_bookmarks->end(); + } else { + const auto rowHeight = ImGui::GetTextLineHeightWithSpacing() + 2 * ImGui::GetStyle().FramePadding.y; + if (ImGui::BeginTable("##bookmark_table", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) { + ImGui::TableSetupColumn("##name"); + ImGui::TableSetupColumn("##spacing", ImGuiTableColumnFlags_WidthFixed, 20); + ImGui::TableSetupColumn("##value", ImGuiTableColumnFlags_WidthStretch); + + ImGui::TableNextRow(ImGuiTableRowFlags_None, rowHeight); + ImGui::TableNextColumn(); + + // Draw bookmark name + ImGui::TextUnformatted("hex.builtin.view.bookmarks.header.name"_lang); + ImGui::TableNextColumn(); + ImGui::TableNextColumn(); + + // Draw lock/unlock button + if (locked) { + if (ImGuiExt::IconButton(ICON_VS_LOCK, ImGui::GetStyleColorVec4(ImGuiCol_Text))) locked = false; + ImGuiExt::InfoTooltip("hex.builtin.view.bookmarks.tooltip.unlock"_lang); + } else { + if (ImGuiExt::IconButton(ICON_VS_UNLOCK, ImGui::GetStyleColorVec4(ImGuiCol_Text))) locked = true; + ImGuiExt::InfoTooltip("hex.builtin.view.bookmarks.tooltip.lock"_lang); + } + + ImGui::SameLine(); + + // Draw color button + if (ImGui::ColorButton("hex.builtin.view.bookmarks.header.color"_lang, headerColor.Value, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoAlpha)) { + if (!locked) + ImGui::OpenPopup("hex.builtin.view.bookmarks.header.color"_lang); + } + ImGuiExt::InfoTooltip("hex.builtin.view.bookmarks.header.color"_lang); + + // Draw color picker + if (ImGui::BeginPopup("hex.builtin.view.bookmarks.header.color"_lang)) { + drawColorPopup(headerColor); + color = headerColor; + ImGui::EndPopup(); + } + + ImGui::SameLine(); + + // Draw bookmark name if the bookmark is locked or an input text box if it's unlocked + if (locked) + ImGui::TextUnformatted(name.data()); + else { + ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); + ImGui::InputText("##nameInput", name); + ImGui::PopItemWidth(); + } + + ImGui::TableNextRow(ImGuiTableRowFlags_None, rowHeight); + ImGui::TableNextColumn(); + + ImGui::TextUnformatted("hex.builtin.common.address"_lang); + ImGui::TableNextColumn(); + ImGui::TableNextColumn(); + + // Draw jump to address button + if (ImGuiExt::IconButton(ICON_VS_DEBUG_STEP_BACK, ImGui::GetStyleColorVec4(ImGuiCol_Text))) + ImHexApi::HexEditor::setSelection(region); + ImGuiExt::InfoTooltip("hex.builtin.view.bookmarks.tooltip.jump_to"_lang); + + ImGui::SameLine(); + + // Draw open in new view button + if (ImGuiExt::IconButton(ICON_VS_GO_TO_FILE, ImGui::GetStyleColorVec4(ImGuiCol_Text))) { + TaskManager::doLater([region, provider]{ + auto newProvider = ImHexApi::Provider::createProvider("hex.builtin.provider.view", true); + if (auto *viewProvider = dynamic_cast(newProvider); viewProvider != nullptr) { + viewProvider->setProvider(region.getStartAddress(), region.getSize(), provider); + if (viewProvider->open()) { + EventManager::post(viewProvider); + AchievementManager::unlockAchievement("hex.builtin.achievement.hex_editor", "hex.builtin.achievement.hex_editor.open_new_view.name"); + } + } + }); + } + ImGuiExt::InfoTooltip("hex.builtin.view.bookmarks.tooltip.open_in_view"_lang); + + ImGui::SameLine(); + + // Draw the address of the bookmark + ImGuiExt::TextFormatted("hex.builtin.view.bookmarks.address"_lang, region.getStartAddress(), region.getEndAddress()); + + ImGui::TableNextRow(ImGuiTableRowFlags_None, rowHeight); + ImGui::TableNextColumn(); + + // Draw size of the bookmark + ImGui::TextUnformatted("hex.builtin.common.size"_lang); + ImGui::TableNextColumn(); + ImGui::TableNextColumn(); + ImGuiExt::TextFormatted(hex::toByteString(region.size)); + + ImGui::EndTable(); + } + + // Draw comment if the bookmark is locked or an input text box if it's unlocked + if (locked) { + if (!comment.empty()) { + ImGuiExt::Header("hex.builtin.view.bookmarks.header.comment"_lang); + ImGuiExt::TextFormattedWrapped("{}", comment.data()); + } + } + else { + ImGuiExt::Header("hex.builtin.view.bookmarks.header.comment"_lang); + ImGui::InputTextMultiline("##commentInput", comment, ImVec2(ImGui::GetContentRegionAvail().x, 150_scaled)); + } + + ImGui::NewLine(); + } + + // Mark a bookmark for removal when the user clicks the remove button + if (!open) + bookmarkToRemove = iter; + } + + // Remove the bookmark that was marked for removal + if (bookmarkToRemove != this->m_bookmarks->end()) { + this->m_bookmarks->erase(bookmarkToRemove); + EventManager::post(); } - ImGui::EndChild(); } - ImGui::End(); + ImGui::EndChild(); } bool ViewBookmarks::importBookmarks(prv::Provider *provider, const nlohmann::json &json) { diff --git a/plugins/builtin/source/content/views/view_command_palette.cpp b/plugins/builtin/source/content/views/view_command_palette.cpp index a6d78ed33..9d48e45cf 100644 --- a/plugins/builtin/source/content/views/view_command_palette.cpp +++ b/plugins/builtin/source/content/views/view_command_palette.cpp @@ -5,7 +5,7 @@ namespace hex::plugin::builtin { - ViewCommandPalette::ViewCommandPalette() : View("hex.builtin.view.command_palette.name") { + ViewCommandPalette::ViewCommandPalette() : View::Special("hex.builtin.view.command_palette.name") { // Add global shortcut to open the command palette ShortcutManager::addGlobalShortcut(CTRLCMD + SHIFT + Keys::P, "hex.builtin.view.command_palette.name", [this] { EventManager::post("hex.builtin.view.command_palette.name"_lang); @@ -20,7 +20,7 @@ namespace hex::plugin::builtin { }); } - void ViewCommandPalette::drawContent() { + void ViewCommandPalette::drawAlwaysVisibleContent() { // If the command palette is hidden, don't draw it if (!this->m_commandPaletteOpen) return; diff --git a/plugins/builtin/source/content/views/view_constants.cpp b/plugins/builtin/source/content/views/view_constants.cpp index d741d0ef9..f25745370 100644 --- a/plugins/builtin/source/content/views/view_constants.cpp +++ b/plugins/builtin/source/content/views/view_constants.cpp @@ -12,7 +12,7 @@ namespace hex::plugin::builtin { - ViewConstants::ViewConstants() : View("hex.builtin.view.constants.name") { + ViewConstants::ViewConstants() : View::Window("hex.builtin.view.constants.name") { this->reloadConstants(); } @@ -63,92 +63,88 @@ namespace hex::plugin::builtin { } void ViewConstants::drawContent() { - if (ImGui::Begin(View::toWindowName("hex.builtin.view.constants.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) { + ImGui::PushItemWidth(-1); - ImGui::PushItemWidth(-1); + if (ImGuiExt::InputTextIcon("##search", ICON_VS_FILTER, this->m_filter)) { + this->m_filterIndices.clear(); - if (ImGuiExt::InputTextIcon("##search", ICON_VS_FILTER, this->m_filter)) { - this->m_filterIndices.clear(); - - // Filter the constants according to the entered value - for (u64 i = 0; i < this->m_constants.size(); i++) { - auto &constant = this->m_constants[i]; - if (hex::containsIgnoreCase(constant.name, this->m_filter) || - hex::containsIgnoreCase(constant.category, this->m_filter) || - hex::containsIgnoreCase(constant.description, this->m_filter) || - hex::containsIgnoreCase(constant.value, this->m_filter)) - this->m_filterIndices.push_back(i); - } - } - - ImGui::PopItemWidth(); - - if (ImGui::BeginTable("##strings", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) { - ImGui::TableSetupScrollFreeze(0, 1); - ImGui::TableSetupColumn("hex.builtin.view.constants.row.category"_lang, 0, -1, ImGui::GetID("category")); - ImGui::TableSetupColumn("hex.builtin.view.constants.row.name"_lang, 0, -1, ImGui::GetID("name")); - ImGui::TableSetupColumn("hex.builtin.view.constants.row.desc"_lang, 0, -1, ImGui::GetID("desc")); - ImGui::TableSetupColumn("hex.builtin.view.constants.row.value"_lang, 0, -1, ImGui::GetID("value")); - - auto sortSpecs = ImGui::TableGetSortSpecs(); - - // Handle table sorting - if (sortSpecs->SpecsDirty) { - std::sort(this->m_constants.begin(), this->m_constants.end(), [&sortSpecs](const Constant &left, const Constant &right) -> bool { - if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("category")) { - if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending) - return left.category > right.category; - else - return left.category < right.category; - } else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("name")) { - if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending) - return left.name > right.name; - else - return left.name < right.name; - } else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("desc")) { - if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending) - return left.description > right.description; - else - return left.description < right.description; - } else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("value")) { - if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending) - return left.value > right.value; - else - return left.value < right.value; - } - - return false; - }); - - sortSpecs->SpecsDirty = false; - } - - ImGui::TableHeadersRow(); - - ImGuiListClipper clipper; - clipper.Begin(this->m_filterIndices.size()); - - // Draw the constants table - while (clipper.Step()) { - for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { - auto &constant = this->m_constants[this->m_filterIndices[i]]; - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::TextUnformatted(constant.category.c_str()); - ImGui::TableNextColumn(); - ImGui::TextUnformatted(constant.name.c_str()); - ImGui::TableNextColumn(); - ImGui::TextUnformatted(constant.description.c_str()); - ImGui::TableNextColumn(); - ImGui::TextUnformatted(constant.value.c_str()); - } - } - clipper.End(); - - ImGui::EndTable(); + // Filter the constants according to the entered value + for (u64 i = 0; i < this->m_constants.size(); i++) { + auto &constant = this->m_constants[i]; + if (hex::containsIgnoreCase(constant.name, this->m_filter) || + hex::containsIgnoreCase(constant.category, this->m_filter) || + hex::containsIgnoreCase(constant.description, this->m_filter) || + hex::containsIgnoreCase(constant.value, this->m_filter)) + this->m_filterIndices.push_back(i); } } - ImGui::End(); + + ImGui::PopItemWidth(); + + if (ImGui::BeginTable("##strings", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) { + ImGui::TableSetupScrollFreeze(0, 1); + ImGui::TableSetupColumn("hex.builtin.view.constants.row.category"_lang, 0, -1, ImGui::GetID("category")); + ImGui::TableSetupColumn("hex.builtin.view.constants.row.name"_lang, 0, -1, ImGui::GetID("name")); + ImGui::TableSetupColumn("hex.builtin.view.constants.row.desc"_lang, 0, -1, ImGui::GetID("desc")); + ImGui::TableSetupColumn("hex.builtin.view.constants.row.value"_lang, 0, -1, ImGui::GetID("value")); + + auto sortSpecs = ImGui::TableGetSortSpecs(); + + // Handle table sorting + if (sortSpecs->SpecsDirty) { + std::sort(this->m_constants.begin(), this->m_constants.end(), [&sortSpecs](const Constant &left, const Constant &right) -> bool { + if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("category")) { + if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending) + return left.category > right.category; + else + return left.category < right.category; + } else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("name")) { + if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending) + return left.name > right.name; + else + return left.name < right.name; + } else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("desc")) { + if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending) + return left.description > right.description; + else + return left.description < right.description; + } else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("value")) { + if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending) + return left.value > right.value; + else + return left.value < right.value; + } + + return false; + }); + + sortSpecs->SpecsDirty = false; + } + + ImGui::TableHeadersRow(); + + ImGuiListClipper clipper; + clipper.Begin(this->m_filterIndices.size()); + + // Draw the constants table + while (clipper.Step()) { + for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { + auto &constant = this->m_constants[this->m_filterIndices[i]]; + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::TextUnformatted(constant.category.c_str()); + ImGui::TableNextColumn(); + ImGui::TextUnformatted(constant.name.c_str()); + ImGui::TableNextColumn(); + ImGui::TextUnformatted(constant.description.c_str()); + ImGui::TableNextColumn(); + ImGui::TextUnformatted(constant.value.c_str()); + } + } + clipper.End(); + + ImGui::EndTable(); + } } } \ No newline at end of file diff --git a/plugins/builtin/source/content/views/view_data_inspector.cpp b/plugins/builtin/source/content/views/view_data_inspector.cpp index 4d8bc9850..3693010b1 100644 --- a/plugins/builtin/source/content/views/view_data_inspector.cpp +++ b/plugins/builtin/source/content/views/view_data_inspector.cpp @@ -19,7 +19,7 @@ namespace hex::plugin::builtin { using NumberDisplayStyle = ContentRegistry::DataInspector::NumberDisplayStyle; - ViewDataInspector::ViewDataInspector() : View("hex.builtin.view.data_inspector.name") { + ViewDataInspector::ViewDataInspector() : View::Window("hex.builtin.view.data_inspector.name") { // Handle region selection EventManager::subscribe(this, [this](const auto ®ion) { @@ -208,205 +208,202 @@ namespace hex::plugin::builtin { this->updateInspectorRows(); } - if (ImGui::Begin(View::toWindowName("hex.builtin.view.data_inspector.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) { - if (this->m_selectedProvider != nullptr && this->m_selectedProvider->isReadable() && this->m_validBytes > 0) { - u32 validLineCount = this->m_cachedData.size(); - if (!this->m_tableEditingModeEnabled) { - validLineCount = std::count_if(this->m_cachedData.begin(), this->m_cachedData.end(), [this](const auto &entry) { - return !this->m_hiddenValues.contains(entry.filterValue); - }); - } - - if (ImGui::BeginTable("##datainspector", this->m_tableEditingModeEnabled ? 3 : 2, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg, ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * (validLineCount + 1)))) { - ImGui::TableSetupScrollFreeze(0, 1); - ImGui::TableSetupColumn("hex.builtin.view.data_inspector.table.name"_lang, ImGuiTableColumnFlags_WidthFixed); - ImGui::TableSetupColumn("hex.builtin.view.data_inspector.table.value"_lang, ImGuiTableColumnFlags_WidthStretch); - - if (this->m_tableEditingModeEnabled) - ImGui::TableSetupColumn("##favorite", ImGuiTableColumnFlags_WidthFixed, ImGui::GetTextLineHeight()); - - ImGui::TableHeadersRow(); - - int inspectorRowId = 1; - for (auto &[unlocalizedName, displayFunction, editingFunction, editing, filterValue] : this->m_cachedData) { - bool grayedOut = false; - if (this->m_hiddenValues.contains(filterValue)) { - if (!this->m_tableEditingModeEnabled) - continue; - else - grayedOut = true; - } - - ImGui::PushID(inspectorRowId); - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - - ImGui::BeginDisabled(grayedOut); - - // Render inspector row name - ImGui::TextUnformatted(LangEntry(unlocalizedName)); - ImGui::TableNextColumn(); - - if (!editing) { - // Handle regular display case - - // Render inspector row value - const auto ©Value = displayFunction(); - - ImGui::SameLine(); - - // Handle copying the value to the clipboard when clicking the row - if (ImGui::Selectable("##InspectorLine", false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap)) { - ImGui::SetClipboardText(copyValue.c_str()); - } - - // Enter editing mode when double-clicking the row - if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) && editingFunction.has_value()) { - editing = true; - this->m_editingValue = copyValue; - } - - } else { - // Handle editing mode - - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); - ImGui::SetNextItemWidth(-1); - ImGui::SetKeyboardFocusHere(); - - // Draw input text box - if (ImGui::InputText("##InspectorLineEditing", this->m_editingValue, ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll)) { - // Turn the entered value into bytes - auto bytes = editingFunction.value()(this->m_editingValue, this->m_endian); - - // Write those bytes to the selected provider at the current address - this->m_selectedProvider->write(this->m_startAddress, bytes.data(), bytes.size()); - - // Disable editing mode - this->m_editingValue.clear(); - editing = false; - - // Reload all inspector rows - this->m_shouldInvalidate = true; - } - ImGui::PopStyleVar(); - - // Disable editing mode when clicking outside the input text box - if (!ImGui::IsItemHovered() && ImGui::IsAnyMouseDown()) { - this->m_editingValue.clear(); - editing = false; - } - } - - ImGui::EndDisabled(); - - if (this->m_tableEditingModeEnabled) { - ImGui::TableNextColumn(); - - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImGui::GetStyleColorVec4(ImGuiCol_Text)); - - bool hidden = this->m_hiddenValues.contains(filterValue); - if (ImGuiExt::DimmedButton(hidden ? ICON_VS_EYE : ICON_VS_EYE_CLOSED)) { - if (hidden) - this->m_hiddenValues.erase(filterValue); - else - this->m_hiddenValues.insert(filterValue); - - { - std::vector filterValues(this->m_hiddenValues.begin(), this->m_hiddenValues.end()); - - ContentRegistry::Settings::write("hex.builtin.setting.data_inspector", "hex.builtin.setting.data_inspector.hidden_rows", filterValues); - } - } - - ImGui::PopStyleColor(); - ImGui::PopStyleVar(); - } - - ImGui::PopID(); - inspectorRowId++; - } - - ImGui::EndTable(); - } - - ImGuiExt::DimmedButtonToggle("hex.builtin.common.edit"_lang, &this->m_tableEditingModeEnabled, ImVec2(ImGui::GetContentRegionAvail().x, 0)); - - ImGui::NewLine(); - ImGui::Separator(); - ImGui::NewLine(); - - // Draw inspector settings - - // Draw endian setting - { - int selection = [this] { - switch (this->m_endian) { - default: - case std::endian::little: return 0; - case std::endian::big: return 1; - } - }(); - - std::array options = { "hex.builtin.common.little"_lang, "hex.builtin.common.big"_lang }; - - if (ImGui::SliderInt("hex.builtin.common.endian"_lang, &selection, 0, options.size() - 1, options[selection], ImGuiSliderFlags_NoInput)) { - this->m_shouldInvalidate = true; - - switch (selection) { - default: - case 0: this->m_endian = std::endian::little; break; - case 1: this->m_endian = std::endian::big; break; - } - } - } - - // Draw radix setting - { - int selection = [this] { - switch (this->m_numberDisplayStyle) { - default: - case NumberDisplayStyle::Decimal: return 0; - case NumberDisplayStyle::Hexadecimal: return 1; - case NumberDisplayStyle::Octal: return 2; - } - }(); - std::array options = { "hex.builtin.common.decimal"_lang, "hex.builtin.common.hexadecimal"_lang, "hex.builtin.common.octal"_lang }; - - if (ImGui::SliderInt("hex.builtin.common.number_format"_lang, &selection, 0, options.size() - 1, options[selection], ImGuiSliderFlags_NoInput)) { - this->m_shouldInvalidate = true; - - switch (selection) { - default: - case 0: this->m_numberDisplayStyle = NumberDisplayStyle::Decimal; break; - case 1: this->m_numberDisplayStyle = NumberDisplayStyle::Hexadecimal; break; - case 2: this->m_numberDisplayStyle = NumberDisplayStyle::Octal; break; - } - } - } - - // Draw invert setting - { - int selection = this->m_invert ? 1 : 0; - std::array options = { "hex.builtin.common.no"_lang, "hex.builtin.common.yes"_lang }; - - if (ImGui::SliderInt("hex.builtin.view.data_inspector.invert"_lang, &selection, 0, options.size() - 1, options[selection], ImGuiSliderFlags_NoInput)) { - this->m_shouldInvalidate = true; - - this->m_invert = selection == 1; - } - } - } else { - // Draw a message when no bytes are selected - std::string text = "hex.builtin.view.data_inspector.no_data"_lang; - auto textSize = ImGui::CalcTextSize(text.c_str()); - auto availableSpace = ImGui::GetContentRegionAvail(); - - ImGui::SetCursorPos((availableSpace - textSize) / 2.0F); - ImGui::TextUnformatted(text.c_str()); + if (this->m_selectedProvider != nullptr && this->m_selectedProvider->isReadable() && this->m_validBytes > 0) { + u32 validLineCount = this->m_cachedData.size(); + if (!this->m_tableEditingModeEnabled) { + validLineCount = std::count_if(this->m_cachedData.begin(), this->m_cachedData.end(), [this](const auto &entry) { + return !this->m_hiddenValues.contains(entry.filterValue); + }); } + + if (ImGui::BeginTable("##datainspector", this->m_tableEditingModeEnabled ? 3 : 2, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg, ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * (validLineCount + 1)))) { + ImGui::TableSetupScrollFreeze(0, 1); + ImGui::TableSetupColumn("hex.builtin.view.data_inspector.table.name"_lang, ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("hex.builtin.view.data_inspector.table.value"_lang, ImGuiTableColumnFlags_WidthStretch); + + if (this->m_tableEditingModeEnabled) + ImGui::TableSetupColumn("##favorite", ImGuiTableColumnFlags_WidthFixed, ImGui::GetTextLineHeight()); + + ImGui::TableHeadersRow(); + + int inspectorRowId = 1; + for (auto &[unlocalizedName, displayFunction, editingFunction, editing, filterValue] : this->m_cachedData) { + bool grayedOut = false; + if (this->m_hiddenValues.contains(filterValue)) { + if (!this->m_tableEditingModeEnabled) + continue; + else + grayedOut = true; + } + + ImGui::PushID(inspectorRowId); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + + ImGui::BeginDisabled(grayedOut); + + // Render inspector row name + ImGui::TextUnformatted(LangEntry(unlocalizedName)); + ImGui::TableNextColumn(); + + if (!editing) { + // Handle regular display case + + // Render inspector row value + const auto ©Value = displayFunction(); + + ImGui::SameLine(); + + // Handle copying the value to the clipboard when clicking the row + if (ImGui::Selectable("##InspectorLine", false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap)) { + ImGui::SetClipboardText(copyValue.c_str()); + } + + // Enter editing mode when double-clicking the row + if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) && editingFunction.has_value()) { + editing = true; + this->m_editingValue = copyValue; + } + + } else { + // Handle editing mode + + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); + ImGui::SetNextItemWidth(-1); + ImGui::SetKeyboardFocusHere(); + + // Draw input text box + if (ImGui::InputText("##InspectorLineEditing", this->m_editingValue, ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll)) { + // Turn the entered value into bytes + auto bytes = editingFunction.value()(this->m_editingValue, this->m_endian); + + // Write those bytes to the selected provider at the current address + this->m_selectedProvider->write(this->m_startAddress, bytes.data(), bytes.size()); + + // Disable editing mode + this->m_editingValue.clear(); + editing = false; + + // Reload all inspector rows + this->m_shouldInvalidate = true; + } + ImGui::PopStyleVar(); + + // Disable editing mode when clicking outside the input text box + if (!ImGui::IsItemHovered() && ImGui::IsAnyMouseDown()) { + this->m_editingValue.clear(); + editing = false; + } + } + + ImGui::EndDisabled(); + + if (this->m_tableEditingModeEnabled) { + ImGui::TableNextColumn(); + + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImGui::GetStyleColorVec4(ImGuiCol_Text)); + + bool hidden = this->m_hiddenValues.contains(filterValue); + if (ImGuiExt::DimmedButton(hidden ? ICON_VS_EYE : ICON_VS_EYE_CLOSED)) { + if (hidden) + this->m_hiddenValues.erase(filterValue); + else + this->m_hiddenValues.insert(filterValue); + + { + std::vector filterValues(this->m_hiddenValues.begin(), this->m_hiddenValues.end()); + + ContentRegistry::Settings::write("hex.builtin.setting.data_inspector", "hex.builtin.setting.data_inspector.hidden_rows", filterValues); + } + } + + ImGui::PopStyleColor(); + ImGui::PopStyleVar(); + } + + ImGui::PopID(); + inspectorRowId++; + } + + ImGui::EndTable(); + } + + ImGuiExt::DimmedButtonToggle("hex.builtin.common.edit"_lang, &this->m_tableEditingModeEnabled, ImVec2(ImGui::GetContentRegionAvail().x, 0)); + + ImGui::NewLine(); + ImGui::Separator(); + ImGui::NewLine(); + + // Draw inspector settings + + // Draw endian setting + { + int selection = [this] { + switch (this->m_endian) { + default: + case std::endian::little: return 0; + case std::endian::big: return 1; + } + }(); + + std::array options = { "hex.builtin.common.little"_lang, "hex.builtin.common.big"_lang }; + + if (ImGui::SliderInt("hex.builtin.common.endian"_lang, &selection, 0, options.size() - 1, options[selection], ImGuiSliderFlags_NoInput)) { + this->m_shouldInvalidate = true; + + switch (selection) { + default: + case 0: this->m_endian = std::endian::little; break; + case 1: this->m_endian = std::endian::big; break; + } + } + } + + // Draw radix setting + { + int selection = [this] { + switch (this->m_numberDisplayStyle) { + default: + case NumberDisplayStyle::Decimal: return 0; + case NumberDisplayStyle::Hexadecimal: return 1; + case NumberDisplayStyle::Octal: return 2; + } + }(); + std::array options = { "hex.builtin.common.decimal"_lang, "hex.builtin.common.hexadecimal"_lang, "hex.builtin.common.octal"_lang }; + + if (ImGui::SliderInt("hex.builtin.common.number_format"_lang, &selection, 0, options.size() - 1, options[selection], ImGuiSliderFlags_NoInput)) { + this->m_shouldInvalidate = true; + + switch (selection) { + default: + case 0: this->m_numberDisplayStyle = NumberDisplayStyle::Decimal; break; + case 1: this->m_numberDisplayStyle = NumberDisplayStyle::Hexadecimal; break; + case 2: this->m_numberDisplayStyle = NumberDisplayStyle::Octal; break; + } + } + } + + // Draw invert setting + { + int selection = this->m_invert ? 1 : 0; + std::array options = { "hex.builtin.common.no"_lang, "hex.builtin.common.yes"_lang }; + + if (ImGui::SliderInt("hex.builtin.view.data_inspector.invert"_lang, &selection, 0, options.size() - 1, options[selection], ImGuiSliderFlags_NoInput)) { + this->m_shouldInvalidate = true; + + this->m_invert = selection == 1; + } + } + } else { + // Draw a message when no bytes are selected + std::string text = "hex.builtin.view.data_inspector.no_data"_lang; + auto textSize = ImGui::CalcTextSize(text.c_str()); + auto availableSpace = ImGui::GetContentRegionAvail(); + + ImGui::SetCursorPos((availableSpace - textSize) / 2.0F); + ImGui::TextUnformatted(text.c_str()); } - ImGui::End(); } } \ No newline at end of file diff --git a/plugins/builtin/source/content/views/view_data_processor.cpp b/plugins/builtin/source/content/views/view_data_processor.cpp index 6741d130b..9b2c887c8 100644 --- a/plugins/builtin/source/content/views/view_data_processor.cpp +++ b/plugins/builtin/source/content/views/view_data_processor.cpp @@ -347,7 +347,7 @@ namespace hex::plugin::builtin { ViewDataProcessor::Workspace m_workspace; }; - ViewDataProcessor::ViewDataProcessor() : View("hex.builtin.view.data_processor.name") { + ViewDataProcessor::ViewDataProcessor() : View::Window("hex.builtin.view.data_processor.name") { ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.custom", "hex.builtin.nodes.custom.custom", this); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.custom", "hex.builtin.nodes.custom.input"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.custom", "hex.builtin.nodes.custom.output"); @@ -842,180 +842,177 @@ namespace hex::plugin::builtin { auto &workspace = *this->m_workspaceStack->back(); bool popWorkspace = false; - if (ImGui::Begin(View::toWindowName("hex.builtin.view.data_processor.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) { - // Set the ImNodes context to the current workspace context - ImNodes::SetCurrentContext(workspace.context.get()); + // Set the ImNodes context to the current workspace context + ImNodes::SetCurrentContext(workspace.context.get()); - this->drawContextMenus(workspace); + this->drawContextMenus(workspace); - // Draw error tooltip when hovering over a node that has an error - { - int nodeId; - if (ImNodes::IsNodeHovered(&nodeId) && workspace.currNodeError.has_value() && workspace.currNodeError->node->getId() == nodeId) { - ImGui::BeginTooltip(); - ImGui::TextUnformatted("hex.builtin.common.error"_lang); - ImGui::Separator(); - ImGui::TextUnformatted(workspace.currNodeError->message.c_str()); - ImGui::EndTooltip(); - } + // Draw error tooltip when hovering over a node that has an error + { + int nodeId; + if (ImNodes::IsNodeHovered(&nodeId) && workspace.currNodeError.has_value() && workspace.currNodeError->node->getId() == nodeId) { + ImGui::BeginTooltip(); + ImGui::TextUnformatted("hex.builtin.common.error"_lang); + ImGui::Separator(); + ImGui::TextUnformatted(workspace.currNodeError->message.c_str()); + ImGui::EndTooltip(); + } + } + + // Draw the main node editor workspace window + if (ImGui::BeginChild("##node_editor", ImGui::GetContentRegionAvail() - ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 1.3F))) { + ImNodes::BeginNodeEditor(); + + // Loop over all nodes that have been placed in the workspace + for (auto &node : workspace.nodes) { + ImNodes::SnapNodeToGrid(node->getId()); + + // If the node has an error, draw it with a red outline + const bool hasError = workspace.currNodeError.has_value() && workspace.currNodeError->node == node.get(); + if (hasError) + ImNodes::PushColorStyle(ImNodesCol_NodeOutline, 0xFF0000FF); + + // Draw the node + this->drawNode(*node); + + if (hasError) + ImNodes::PopColorStyle(); } - // Draw the main node editor workspace window - if (ImGui::BeginChild("##node_editor", ImGui::GetContentRegionAvail() - ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 1.3F))) { - ImNodes::BeginNodeEditor(); + this->m_updateNodePositions = false; - // Loop over all nodes that have been placed in the workspace - for (auto &node : workspace.nodes) { - ImNodes::SnapNodeToGrid(node->getId()); - - // If the node has an error, draw it with a red outline - const bool hasError = workspace.currNodeError.has_value() && workspace.currNodeError->node == node.get(); - if (hasError) - ImNodes::PushColorStyle(ImNodesCol_NodeOutline, 0xFF0000FF); - - // Draw the node - this->drawNode(*node); - - if (hasError) - ImNodes::PopColorStyle(); - } - - this->m_updateNodePositions = false; - - // Handle removing links that are connected to attributes that don't exist anymore - { - std::vector linksToRemove; - for (const auto &link : workspace.links) { - if (ImNodes::ObjectPoolFind(ImNodes::EditorContextGet().Pins, link.getFromId()) == -1 || - ImNodes::ObjectPoolFind(ImNodes::EditorContextGet().Pins, link.getToId()) == -1) { - - linksToRemove.push_back(link.getId()); - } - } - for (auto linkId : linksToRemove) - this->eraseLink(workspace, linkId); - } - - // Draw links + // Handle removing links that are connected to attributes that don't exist anymore + { + std::vector linksToRemove; for (const auto &link : workspace.links) { - ImNodes::Link(link.getId(), link.getFromId(), link.getToId()); - } + if (ImNodes::ObjectPoolFind(ImNodes::EditorContextGet().Pins, link.getFromId()) == -1 || + ImNodes::ObjectPoolFind(ImNodes::EditorContextGet().Pins, link.getToId()) == -1) { - // Draw the mini map in the bottom right - ImNodes::MiniMap(0.2F, ImNodesMiniMapLocation_BottomRight); - - // Draw the help text if no nodes have been placed yet - if (workspace.nodes.empty()) - ImGuiExt::TextFormattedCentered("{}", "hex.builtin.view.data_processor.help_text"_lang); - - // Draw a close button if there is more than one workspace on the stack - if (this->m_workspaceStack->size() > 1) { - ImGui::SetCursorPos(ImVec2(ImGui::GetContentRegionAvail().x - ImGui::GetTextLineHeightWithSpacing() * 1.5F, ImGui::GetTextLineHeightWithSpacing() * 0.2F)); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4.0F, 4.0F)); - if (ImGuiExt::DimmedIconButton(ICON_VS_CLOSE, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed))) { - popWorkspace = true; + linksToRemove.push_back(link.getId()); } - ImGui::PopStyleVar(); } - - ImNodes::EndNodeEditor(); - } - ImGui::EndChild(); - - // Draw the control bar at the bottom - { - if (ImGuiExt::IconButton(ICON_VS_DEBUG_START, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarGreen)) || this->m_continuousEvaluation) - this->processNodes(workspace); - - ImGui::SameLine(); - - ImGui::Checkbox("Continuous evaluation", &this->m_continuousEvaluation); - } - - - // Erase links that have been distroyed - { - int linkId; - if (ImNodes::IsLinkDestroyed(&linkId)) { + for (auto linkId : linksToRemove) this->eraseLink(workspace, linkId); - } } - // Handle creation of new links - { - int from, to; - if (ImNodes::IsLinkCreated(&from, &to)) { + // Draw links + for (const auto &link : workspace.links) { + ImNodes::Link(link.getId(), link.getFromId(), link.getToId()); + } - do { - dp::Attribute *fromAttr = nullptr, *toAttr = nullptr; + // Draw the mini map in the bottom right + ImNodes::MiniMap(0.2F, ImNodesMiniMapLocation_BottomRight); - // Find the attributes that are connected by the link - for (auto &node : workspace.nodes) { - for (auto &attribute : node->getAttributes()) { - if (attribute.getId() == from) - fromAttr = &attribute; - else if (attribute.getId() == to) - toAttr = &attribute; - } + // Draw the help text if no nodes have been placed yet + if (workspace.nodes.empty()) + ImGuiExt::TextFormattedCentered("{}", "hex.builtin.view.data_processor.help_text"_lang); + + // Draw a close button if there is more than one workspace on the stack + if (this->m_workspaceStack->size() > 1) { + ImGui::SetCursorPos(ImVec2(ImGui::GetContentRegionAvail().x - ImGui::GetTextLineHeightWithSpacing() * 1.5F, ImGui::GetTextLineHeightWithSpacing() * 0.2F)); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4.0F, 4.0F)); + if (ImGuiExt::DimmedIconButton(ICON_VS_CLOSE, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed))) { + popWorkspace = true; + } + ImGui::PopStyleVar(); + } + + ImNodes::EndNodeEditor(); + } + ImGui::EndChild(); + + // Draw the control bar at the bottom + { + if (ImGuiExt::IconButton(ICON_VS_DEBUG_START, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarGreen)) || this->m_continuousEvaluation) + this->processNodes(workspace); + + ImGui::SameLine(); + + ImGui::Checkbox("Continuous evaluation", &this->m_continuousEvaluation); + } + + + // Erase links that have been distroyed + { + int linkId; + if (ImNodes::IsLinkDestroyed(&linkId)) { + this->eraseLink(workspace, linkId); + } + } + + // Handle creation of new links + { + int from, to; + if (ImNodes::IsLinkCreated(&from, &to)) { + + do { + dp::Attribute *fromAttr = nullptr, *toAttr = nullptr; + + // Find the attributes that are connected by the link + for (auto &node : workspace.nodes) { + for (auto &attribute : node->getAttributes()) { + if (attribute.getId() == from) + fromAttr = &attribute; + else if (attribute.getId() == to) + toAttr = &attribute; } - - // If one of the attributes could not be found, the link is invalid and can't be created - if (fromAttr == nullptr || toAttr == nullptr) - break; - - // If the attributes have different types, don't create the link - if (fromAttr->getType() != toAttr->getType()) - break; - - // If the link tries to connect two input or two output attributes, don't create the link - if (fromAttr->getIOType() == toAttr->getIOType()) - break; - - // If the link tries to connect to a input attribute that already has a link connected to it, don't create the link - if (!toAttr->getConnectedAttributes().empty()) - break; - - // Add a new link to the current workspace - auto newLink = workspace.links.emplace_back(from, to); - - // Add the link to the attributes that are connected by it - fromAttr->addConnectedAttribute(newLink.getId(), toAttr); - toAttr->addConnectedAttribute(newLink.getId(), fromAttr); - - AchievementManager::unlockAchievement("hex.builtin.achievement.data_processor", "hex.builtin.achievement.data_processor.create_connection.name"); - } while (false); - } - } - - // Handle deletion of links using the Delete key - { - const int selectedLinkCount = ImNodes::NumSelectedLinks(); - if (selectedLinkCount > 0 && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete))) { - std::vector selectedLinks; - selectedLinks.resize(static_cast(selectedLinkCount)); - ImNodes::GetSelectedLinks(selectedLinks.data()); - ImNodes::ClearLinkSelection(); - - for (const int id : selectedLinks) { - eraseLink(workspace, id); } - } + + // If one of the attributes could not be found, the link is invalid and can't be created + if (fromAttr == nullptr || toAttr == nullptr) + break; + + // If the attributes have different types, don't create the link + if (fromAttr->getType() != toAttr->getType()) + break; + + // If the link tries to connect two input or two output attributes, don't create the link + if (fromAttr->getIOType() == toAttr->getIOType()) + break; + + // If the link tries to connect to a input attribute that already has a link connected to it, don't create the link + if (!toAttr->getConnectedAttributes().empty()) + break; + + // Add a new link to the current workspace + auto newLink = workspace.links.emplace_back(from, to); + + // Add the link to the attributes that are connected by it + fromAttr->addConnectedAttribute(newLink.getId(), toAttr); + toAttr->addConnectedAttribute(newLink.getId(), fromAttr); + + AchievementManager::unlockAchievement("hex.builtin.achievement.data_processor", "hex.builtin.achievement.data_processor.create_connection.name"); + } while (false); } + } - // Handle deletion of noes using the Delete key - { - const int selectedNodeCount = ImNodes::NumSelectedNodes(); - if (selectedNodeCount > 0 && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete))) { - std::vector selectedNodes; - selectedNodes.resize(static_cast(selectedNodeCount)); - ImNodes::GetSelectedNodes(selectedNodes.data()); - ImNodes::ClearNodeSelection(); + // Handle deletion of links using the Delete key + { + const int selectedLinkCount = ImNodes::NumSelectedLinks(); + if (selectedLinkCount > 0 && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete))) { + std::vector selectedLinks; + selectedLinks.resize(static_cast(selectedLinkCount)); + ImNodes::GetSelectedLinks(selectedLinks.data()); + ImNodes::ClearLinkSelection(); - this->eraseNodes(workspace, selectedNodes); + for (const int id : selectedLinks) { + eraseLink(workspace, id); } } } - ImGui::End(); + + // Handle deletion of noes using the Delete key + { + const int selectedNodeCount = ImNodes::NumSelectedNodes(); + if (selectedNodeCount > 0 && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete))) { + std::vector selectedNodes; + selectedNodes.resize(static_cast(selectedNodeCount)); + ImNodes::GetSelectedNodes(selectedNodes.data()); + ImNodes::ClearNodeSelection(); + + this->eraseNodes(workspace, selectedNodes); + } + } // Remove the top-most workspace from the stack if requested if (popWorkspace) { diff --git a/plugins/builtin/source/content/views/view_diff.cpp b/plugins/builtin/source/content/views/view_diff.cpp index fa8bc1dcc..25b149a0b 100644 --- a/plugins/builtin/source/content/views/view_diff.cpp +++ b/plugins/builtin/source/content/views/view_diff.cpp @@ -15,7 +15,7 @@ namespace hex::plugin::builtin { } - ViewDiff::ViewDiff() : View("hex.builtin.view.diff.name") { + ViewDiff::ViewDiff() : View::Window("hex.builtin.view.diff.name") { // Clear the selected diff providers when a provider is closed EventManager::subscribe(this, [this](prv::Provider *) { @@ -183,147 +183,142 @@ namespace hex::plugin::builtin { } void ViewDiff::drawContent() { - if (ImGui::Begin(View::toWindowName("hex.builtin.view.diff.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) { + auto &[a, b] = this->m_columns; - auto &[a, b] = this->m_columns; + a.hexEditor.enableSyncScrolling(false); + b.hexEditor.enableSyncScrolling(false); - a.hexEditor.enableSyncScrolling(false); - b.hexEditor.enableSyncScrolling(false); + if (a.scrollLock > 0) a.scrollLock--; + if (b.scrollLock > 0) b.scrollLock--; - if (a.scrollLock > 0) a.scrollLock--; - if (b.scrollLock > 0) b.scrollLock--; - - // Change the hex editor providers if the user selected a new provider - { - const auto &providers = ImHexApi::Provider::getProviders(); - if (a.provider >= 0 && size_t(a.provider) < providers.size()) - a.hexEditor.setProvider(providers[a.provider]); - else - a.hexEditor.setProvider(nullptr); - - if (b.provider >= 0 && size_t(b.provider) < providers.size()) - b.hexEditor.setProvider(providers[b.provider]); - else - b.hexEditor.setProvider(nullptr); - } - - // Analyze the providers if they are valid and the user selected a new provider - if (!this->m_analyzed && a.provider != -1 && b.provider != -1 && !this->m_diffTask.isRunning()) { - const auto &providers = ImHexApi::Provider::getProviders(); - auto providerA = providers[a.provider]; - auto providerB = providers[b.provider]; - - this->analyze(providerA, providerB); - } - - const auto height = ImGui::GetContentRegionAvail().y; - - // Draw the two hex editor columns side by side - if (ImGui::BeginTable("##binary_diff", 2, ImGuiTableFlags_None, ImVec2(0, height - 250_scaled))) { - ImGui::TableSetupColumn("hex.builtin.view.diff.provider_a"_lang); - ImGui::TableSetupColumn("hex.builtin.view.diff.provider_b"_lang); - ImGui::TableHeadersRow(); - - ImGui::BeginDisabled(this->m_diffTask.isRunning()); - { - // Draw first provider selector - ImGui::TableNextColumn(); - if (drawProviderSelector(a)) this->m_analyzed = false; - - // Draw second provider selector - ImGui::TableNextColumn(); - if (drawProviderSelector(b)) this->m_analyzed = false; - } - ImGui::EndDisabled(); - - ImGui::TableNextRow(); - - // Draw first hex editor column - ImGui::TableNextColumn(); - bool scrollB = drawDiffColumn(a, height - 250_scaled); - - // Draw second hex editor column - ImGui::TableNextColumn(); - bool scrollA = drawDiffColumn(b, height - 250_scaled); - - // Sync the scroll positions of the hex editors - { - if (scrollA && a.scrollLock == 0) { - a.hexEditor.setScrollPosition(b.hexEditor.getScrollPosition()); - a.hexEditor.forceUpdateScrollPosition(); - } - if (scrollB && b.scrollLock == 0) { - b.hexEditor.setScrollPosition(a.hexEditor.getScrollPosition()); - b.hexEditor.forceUpdateScrollPosition(); - } - } - - ImGui::EndTable(); - } - - // Draw the differences table - if (ImGui::BeginTable("##differences", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollY | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Resizable, ImVec2(0, 200_scaled))) { - ImGui::TableSetupScrollFreeze(0, 1); - ImGui::TableSetupColumn("hex.builtin.common.begin"_lang); - ImGui::TableSetupColumn("hex.builtin.common.end"_lang); - ImGui::TableSetupColumn("hex.builtin.common.type"_lang); - ImGui::TableHeadersRow(); - - // Draw the differences if the providers have been analyzed - if (this->m_analyzed) { - ImGuiListClipper clipper; - clipper.Begin(int(this->m_diffs.size())); - - while (clipper.Step()) - for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { - ImGui::TableNextRow(); - - // Prevent the list from trying to access non-existing diffs - if (size_t(i) >= this->m_diffs.size()) - break; - - ImGui::PushID(i); - - const auto &diff = this->m_diffs[i]; - - // Draw a clickable row for each difference that will select the difference in both hex editors - - // Draw start address - ImGui::TableNextColumn(); - if (ImGui::Selectable(hex::format("0x{:02X}", diff.region.getStartAddress()).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) { - a.hexEditor.setSelection(diff.region); - a.hexEditor.jumpToSelection(); - b.hexEditor.setSelection(diff.region); - b.hexEditor.jumpToSelection(); - } - - // Draw end address - ImGui::TableNextColumn(); - ImGui::TextUnformatted(hex::format("0x{:02X}", diff.region.getEndAddress()).c_str()); - - // Draw difference type - ImGui::TableNextColumn(); - switch (diff.type) { - case DifferenceType::Modified: - ImGuiExt::TextFormattedColored(ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_DiffChanged), "hex.builtin.view.diff.modified"_lang); - break; - case DifferenceType::Added: - ImGuiExt::TextFormattedColored(ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_DiffAdded), "hex.builtin.view.diff.added"_lang); - break; - case DifferenceType::Removed: - ImGuiExt::TextFormattedColored(ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_DiffRemoved), "hex.builtin.view.diff.removed"_lang); - break; - } - - ImGui::PopID(); - } - } - - ImGui::EndTable(); - } + // Change the hex editor providers if the user selected a new provider + { + const auto &providers = ImHexApi::Provider::getProviders(); + if (a.provider >= 0 && size_t(a.provider) < providers.size()) + a.hexEditor.setProvider(providers[a.provider]); + else + a.hexEditor.setProvider(nullptr); + if (b.provider >= 0 && size_t(b.provider) < providers.size()) + b.hexEditor.setProvider(providers[b.provider]); + else + b.hexEditor.setProvider(nullptr); + } + + // Analyze the providers if they are valid and the user selected a new provider + if (!this->m_analyzed && a.provider != -1 && b.provider != -1 && !this->m_diffTask.isRunning()) { + const auto &providers = ImHexApi::Provider::getProviders(); + auto providerA = providers[a.provider]; + auto providerB = providers[b.provider]; + + this->analyze(providerA, providerB); + } + + const auto height = ImGui::GetContentRegionAvail().y; + + // Draw the two hex editor columns side by side + if (ImGui::BeginTable("##binary_diff", 2, ImGuiTableFlags_None, ImVec2(0, height - 250_scaled))) { + ImGui::TableSetupColumn("hex.builtin.view.diff.provider_a"_lang); + ImGui::TableSetupColumn("hex.builtin.view.diff.provider_b"_lang); + ImGui::TableHeadersRow(); + + ImGui::BeginDisabled(this->m_diffTask.isRunning()); + { + // Draw first provider selector + ImGui::TableNextColumn(); + if (drawProviderSelector(a)) this->m_analyzed = false; + + // Draw second provider selector + ImGui::TableNextColumn(); + if (drawProviderSelector(b)) this->m_analyzed = false; + } + ImGui::EndDisabled(); + + ImGui::TableNextRow(); + + // Draw first hex editor column + ImGui::TableNextColumn(); + bool scrollB = drawDiffColumn(a, height - 250_scaled); + + // Draw second hex editor column + ImGui::TableNextColumn(); + bool scrollA = drawDiffColumn(b, height - 250_scaled); + + // Sync the scroll positions of the hex editors + { + if (scrollA && a.scrollLock == 0) { + a.hexEditor.setScrollPosition(b.hexEditor.getScrollPosition()); + a.hexEditor.forceUpdateScrollPosition(); + } + if (scrollB && b.scrollLock == 0) { + b.hexEditor.setScrollPosition(a.hexEditor.getScrollPosition()); + b.hexEditor.forceUpdateScrollPosition(); + } + } + + ImGui::EndTable(); + } + + // Draw the differences table + if (ImGui::BeginTable("##differences", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollY | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Resizable, ImVec2(0, 200_scaled))) { + ImGui::TableSetupScrollFreeze(0, 1); + ImGui::TableSetupColumn("hex.builtin.common.begin"_lang); + ImGui::TableSetupColumn("hex.builtin.common.end"_lang); + ImGui::TableSetupColumn("hex.builtin.common.type"_lang); + ImGui::TableHeadersRow(); + + // Draw the differences if the providers have been analyzed + if (this->m_analyzed) { + ImGuiListClipper clipper; + clipper.Begin(int(this->m_diffs.size())); + + while (clipper.Step()) + for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { + ImGui::TableNextRow(); + + // Prevent the list from trying to access non-existing diffs + if (size_t(i) >= this->m_diffs.size()) + break; + + ImGui::PushID(i); + + const auto &diff = this->m_diffs[i]; + + // Draw a clickable row for each difference that will select the difference in both hex editors + + // Draw start address + ImGui::TableNextColumn(); + if (ImGui::Selectable(hex::format("0x{:02X}", diff.region.getStartAddress()).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) { + a.hexEditor.setSelection(diff.region); + a.hexEditor.jumpToSelection(); + b.hexEditor.setSelection(diff.region); + b.hexEditor.jumpToSelection(); + } + + // Draw end address + ImGui::TableNextColumn(); + ImGui::TextUnformatted(hex::format("0x{:02X}", diff.region.getEndAddress()).c_str()); + + // Draw difference type + ImGui::TableNextColumn(); + switch (diff.type) { + case DifferenceType::Modified: + ImGuiExt::TextFormattedColored(ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_DiffChanged), "hex.builtin.view.diff.modified"_lang); + break; + case DifferenceType::Added: + ImGuiExt::TextFormattedColored(ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_DiffAdded), "hex.builtin.view.diff.added"_lang); + break; + case DifferenceType::Removed: + ImGuiExt::TextFormattedColored(ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_DiffRemoved), "hex.builtin.view.diff.removed"_lang); + break; + } + + ImGui::PopID(); + } + } + + ImGui::EndTable(); } - ImGui::End(); } } \ No newline at end of file diff --git a/plugins/builtin/source/content/views/view_disassembler.cpp b/plugins/builtin/source/content/views/view_disassembler.cpp index dd2de7128..1c771e488 100644 --- a/plugins/builtin/source/content/views/view_disassembler.cpp +++ b/plugins/builtin/source/content/views/view_disassembler.cpp @@ -9,7 +9,7 @@ using namespace std::literals::string_literals; namespace hex::plugin::builtin { - ViewDisassembler::ViewDisassembler() : View("hex.builtin.view.disassembler.name") { + ViewDisassembler::ViewDisassembler() : View::Window("hex.builtin.view.disassembler.name") { EventManager::subscribe(this, [this](const auto*) { this->m_disassembly.clear(); }); @@ -91,366 +91,361 @@ namespace hex::plugin::builtin { } void ViewDisassembler::drawContent() { + auto provider = ImHexApi::Provider::get(); + if (ImHexApi::Provider::isValid() && provider->isReadable()) { + ImGuiExt::Header("hex.builtin.view.disassembler.position"_lang, true); - if (ImGui::Begin(View::toWindowName("hex.builtin.view.disassembler.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) { + // Draw base address input + ImGuiExt::InputHexadecimal("hex.builtin.view.disassembler.base"_lang, &this->m_baseAddress, ImGuiInputTextFlags_CharsHexadecimal); - auto provider = ImHexApi::Provider::get(); - if (ImHexApi::Provider::isValid() && provider->isReadable()) { - ImGuiExt::Header("hex.builtin.view.disassembler.position"_lang, true); + // Draw region selection picker + ui::regionSelectionPicker(&this->m_codeRegion, provider, &this->m_range); - // Draw base address input - ImGuiExt::InputHexadecimal("hex.builtin.view.disassembler.base"_lang, &this->m_baseAddress, ImGuiInputTextFlags_CharsHexadecimal); + // Draw settings + { + ImGuiExt::Header("hex.builtin.common.settings"_lang); - // Draw region selection picker - ui::regionSelectionPicker(&this->m_codeRegion, provider, &this->m_range); + // Draw architecture selector + if (ImGui::Combo("hex.builtin.view.disassembler.arch"_lang, reinterpret_cast(&this->m_architecture), Disassembler::ArchitectureNames.data(), Disassembler::getArchitectureSupportedCount())) + this->m_mode = cs_mode(0); - // Draw settings - { - ImGuiExt::Header("hex.builtin.common.settings"_lang); + // Draw sub-settings for each architecture + if (ImGuiExt::BeginBox()) { - // Draw architecture selector - if (ImGui::Combo("hex.builtin.view.disassembler.arch"_lang, reinterpret_cast(&this->m_architecture), Disassembler::ArchitectureNames.data(), Disassembler::getArchitectureSupportedCount())) - this->m_mode = cs_mode(0); - - // Draw sub-settings for each architecture - if (ImGuiExt::BeginBox()) { - - // Draw endian radio buttons. This setting is available for all architectures - static int littleEndian = true; - ImGui::RadioButton("hex.builtin.common.little_endian"_lang, &littleEndian, true); - ImGui::SameLine(); - ImGui::RadioButton("hex.builtin.common.big_endian"_lang, &littleEndian, false); - - ImGui::NewLine(); - - // Draw architecture specific settings - switch (this->m_architecture) { - case Architecture::ARM: - { - static int mode = CS_MODE_ARM; - ImGui::RadioButton("hex.builtin.view.disassembler.arm.arm"_lang, &mode, CS_MODE_ARM); - ImGui::SameLine(); - ImGui::RadioButton("hex.builtin.view.disassembler.arm.thumb"_lang, &mode, CS_MODE_THUMB); - - static int extraMode = 0; - ImGui::RadioButton("hex.builtin.view.disassembler.arm.default"_lang, &extraMode, 0); - ImGui::SameLine(); - ImGui::RadioButton("hex.builtin.view.disassembler.arm.cortex_m"_lang, &extraMode, CS_MODE_MCLASS); - ImGui::SameLine(); - ImGui::RadioButton("hex.builtin.view.disassembler.arm.armv8"_lang, &extraMode, CS_MODE_V8); - - this->m_mode = cs_mode(mode | extraMode); - } - break; - case Architecture::MIPS: - { - static int mode = CS_MODE_MIPS32; - ImGui::RadioButton("hex.builtin.view.disassembler.mips.mips32"_lang, &mode, CS_MODE_MIPS32); - ImGui::SameLine(); - ImGui::RadioButton("hex.builtin.view.disassembler.mips.mips64"_lang, &mode, CS_MODE_MIPS64); - ImGui::SameLine(); - ImGui::RadioButton("hex.builtin.view.disassembler.mips.mips32R6"_lang, &mode, CS_MODE_MIPS32R6); - - ImGui::RadioButton("hex.builtin.view.disassembler.mips.mips2"_lang, &mode, CS_MODE_MIPS2); - ImGui::SameLine(); - ImGui::RadioButton("hex.builtin.view.disassembler.mips.mips3"_lang, &mode, CS_MODE_MIPS3); - - static bool microMode; - ImGui::Checkbox("hex.builtin.view.disassembler.mips.micro"_lang, µMode); - - this->m_mode = cs_mode(mode | (microMode ? CS_MODE_MICRO : cs_mode(0))); - } - break; - case Architecture::X86: - { - static int mode = CS_MODE_32; - ImGui::RadioButton("hex.builtin.view.disassembler.16bit"_lang, &mode, CS_MODE_16); - ImGui::SameLine(); - ImGui::RadioButton("hex.builtin.view.disassembler.32bit"_lang, &mode, CS_MODE_32); - ImGui::SameLine(); - ImGui::RadioButton("hex.builtin.view.disassembler.64bit"_lang, &mode, CS_MODE_64); - - this->m_mode = cs_mode(mode); - } - break; - case Architecture::PPC: - { - static int mode = CS_MODE_32; - ImGui::RadioButton("hex.builtin.view.disassembler.32bit"_lang, &mode, CS_MODE_32); - ImGui::SameLine(); - ImGui::RadioButton("hex.builtin.view.disassembler.64bit"_lang, &mode, CS_MODE_64); - - static bool qpx = false; - ImGui::Checkbox("hex.builtin.view.disassembler.ppc.qpx"_lang, &qpx); - - #if CS_API_MAJOR >= 5 - static bool spe = false; - ImGui::Checkbox("hex.builtin.view.disassembler.ppc.spe"_lang, &spe); - static bool booke = false; - ImGui::Checkbox("hex.builtin.view.disassembler.ppc.booke"_lang, &booke); - - this->m_mode = cs_mode(mode | (qpx ? CS_MODE_QPX : cs_mode(0)) | (spe ? CS_MODE_SPE : cs_mode(0)) | (booke ? CS_MODE_BOOKE : cs_mode(0))); - #else - this->m_mode = cs_mode(mode | (qpx ? CS_MODE_QPX : cs_mode(0))); - #endif - } - break; - case Architecture::SPARC: - { - static bool v9Mode = false; - ImGui::Checkbox("hex.builtin.view.disassembler.sparc.v9"_lang, &v9Mode); - - this->m_mode = cs_mode(v9Mode ? CS_MODE_V9 : cs_mode(0)); - } - break; - #if CS_API_MAJOR >= 5 - case Architecture::RISCV: - { - static int mode = CS_MODE_RISCV32; - ImGui::RadioButton("hex.builtin.view.disassembler.32bit"_lang, &mode, CS_MODE_RISCV32); - ImGui::SameLine(); - ImGui::RadioButton("hex.builtin.view.disassembler.64bit"_lang, &mode, CS_MODE_RISCV64); - - static bool compressed = false; - ImGui::Checkbox("hex.builtin.view.disassembler.riscv.compressed"_lang, &compressed); - - this->m_mode = cs_mode(mode | (compressed ? CS_MODE_RISCVC : cs_mode(0))); - } - break; - #endif - case Architecture::M68K: - { - static int selectedMode = 0; - - std::pair modes[] = { - {"hex.builtin.view.disassembler.m68k.000"_lang, CS_MODE_M68K_000}, - { "hex.builtin.view.disassembler.m68k.010"_lang, CS_MODE_M68K_010}, - { "hex.builtin.view.disassembler.m68k.020"_lang, CS_MODE_M68K_020}, - { "hex.builtin.view.disassembler.m68k.030"_lang, CS_MODE_M68K_030}, - { "hex.builtin.view.disassembler.m68k.040"_lang, CS_MODE_M68K_040}, - { "hex.builtin.view.disassembler.m68k.060"_lang, CS_MODE_M68K_060}, - }; - - if (ImGui::BeginCombo("hex.builtin.view.disassembler.settings.mode"_lang, modes[selectedMode].first)) { - for (u32 i = 0; i < IM_ARRAYSIZE(modes); i++) { - if (ImGui::Selectable(modes[i].first)) - selectedMode = i; - } - ImGui::EndCombo(); - } - - this->m_mode = cs_mode(modes[selectedMode].second); - } - break; - case Architecture::M680X: - { - static int selectedMode = 0; - - std::pair modes[] = { - {"hex.builtin.view.disassembler.m680x.6301"_lang, CS_MODE_M680X_6301 }, - { "hex.builtin.view.disassembler.m680x.6309"_lang, CS_MODE_M680X_6309 }, - { "hex.builtin.view.disassembler.m680x.6800"_lang, CS_MODE_M680X_6800 }, - { "hex.builtin.view.disassembler.m680x.6801"_lang, CS_MODE_M680X_6801 }, - { "hex.builtin.view.disassembler.m680x.6805"_lang, CS_MODE_M680X_6805 }, - { "hex.builtin.view.disassembler.m680x.6808"_lang, CS_MODE_M680X_6808 }, - { "hex.builtin.view.disassembler.m680x.6809"_lang, CS_MODE_M680X_6809 }, - { "hex.builtin.view.disassembler.m680x.6811"_lang, CS_MODE_M680X_6811 }, - { "hex.builtin.view.disassembler.m680x.cpu12"_lang, CS_MODE_M680X_CPU12}, - { "hex.builtin.view.disassembler.m680x.hcs08"_lang, CS_MODE_M680X_HCS08}, - }; - - if (ImGui::BeginCombo("hex.builtin.view.disassembler.settings.mode"_lang, modes[selectedMode].first)) { - for (u32 i = 0; i < IM_ARRAYSIZE(modes); i++) { - if (ImGui::Selectable(modes[i].first)) - selectedMode = i; - } - ImGui::EndCombo(); - } - - this->m_mode = cs_mode(modes[selectedMode].second); - } - break; - #if CS_API_MAJOR >= 5 - case Architecture::MOS65XX: - { - static int selectedMode = 0; - - std::pair modes[] = { - {"hex.builtin.view.disassembler.mos65xx.6502"_lang, CS_MODE_MOS65XX_6502 }, - { "hex.builtin.view.disassembler.mos65xx.65c02"_lang, CS_MODE_MOS65XX_65C02 }, - { "hex.builtin.view.disassembler.mos65xx.w65c02"_lang, CS_MODE_MOS65XX_W65C02 }, - { "hex.builtin.view.disassembler.mos65xx.65816"_lang, CS_MODE_MOS65XX_65816 }, - { "hex.builtin.view.disassembler.mos65xx.65816_long_m"_lang, CS_MODE_MOS65XX_65816_LONG_M }, - { "hex.builtin.view.disassembler.mos65xx.65816_long_x"_lang, CS_MODE_MOS65XX_65816_LONG_X }, - { "hex.builtin.view.disassembler.mos65xx.65816_long_mx"_lang, CS_MODE_MOS65XX_65816_LONG_MX}, - }; - - if (ImGui::BeginCombo("hex.builtin.view.disassembler.settings.mode"_lang, modes[selectedMode].first)) { - for (u32 i = 0; i < IM_ARRAYSIZE(modes); i++) { - if (ImGui::Selectable(modes[i].first)) - selectedMode = i; - } - ImGui::EndCombo(); - } - - this->m_mode = cs_mode(modes[selectedMode].second); - } - break; - #endif - #if CS_API_MAJOR >= 5 - case Architecture::BPF: - { - static int mode = CS_MODE_BPF_CLASSIC; - ImGui::RadioButton("hex.builtin.view.disassembler.bpf.classic"_lang, &mode, CS_MODE_BPF_CLASSIC); - ImGui::SameLine(); - ImGui::RadioButton("hex.builtin.view.disassembler.bpf.extended"_lang, &mode, CS_MODE_BPF_EXTENDED); - - this->m_mode = cs_mode(mode); - } - break; - case Architecture::SH: - { - static u32 selectionMode = 0; - static bool fpu = false; - static bool dsp = false; - - std::pair modes[] = { - { "hex.builtin.view.disassembler.sh.sh2"_lang, CS_MODE_SH2 }, - { "hex.builtin.view.disassembler.sh.sh2a"_lang, CS_MODE_SH2A }, - { "hex.builtin.view.disassembler.sh.sh3"_lang, CS_MODE_SH3 }, - { "hex.builtin.view.disassembler.sh.sh4"_lang, CS_MODE_SH4 }, - { "hex.builtin.view.disassembler.sh.sh4a"_lang, CS_MODE_SH4A }, - }; - - if (ImGui::BeginCombo("hex.builtin.view.disassembler.settings.mode"_lang, modes[selectionMode].first)) { - for (u32 i = 0; i < IM_ARRAYSIZE(modes); i++) { - if (ImGui::Selectable(modes[i].first)) - selectionMode = i; - } - ImGui::EndCombo(); - } - - ImGui::Checkbox("hex.builtin.view.disassembler.sh.fpu"_lang, &fpu); - ImGui::SameLine(); - ImGui::Checkbox("hex.builtin.view.disassembler.sh.dsp"_lang, &dsp); - - this->m_mode = cs_mode(modes[selectionMode].second | (fpu ? CS_MODE_SHFPU : cs_mode(0)) | (dsp ? CS_MODE_SHDSP : cs_mode(0))); - } - break; - case Architecture::TRICORE: - { - static u32 selectionMode = 0; - - std::pair modes[] = { - { "hex.builtin.view.disassembler.tricore.110"_lang, CS_MODE_TRICORE_110 }, - { "hex.builtin.view.disassembler.tricore.120"_lang, CS_MODE_TRICORE_120 }, - { "hex.builtin.view.disassembler.tricore.130"_lang, CS_MODE_TRICORE_130 }, - { "hex.builtin.view.disassembler.tricore.131"_lang, CS_MODE_TRICORE_131 }, - { "hex.builtin.view.disassembler.tricore.160"_lang, CS_MODE_TRICORE_160 }, - { "hex.builtin.view.disassembler.tricore.161"_lang, CS_MODE_TRICORE_161 }, - { "hex.builtin.view.disassembler.tricore.162"_lang, CS_MODE_TRICORE_162 }, - }; - - if (ImGui::BeginCombo("hex.builtin.view.disassembler.settings.mode"_lang, modes[selectionMode].first)) { - for (u32 i = 0; i < IM_ARRAYSIZE(modes); i++) { - if (ImGui::Selectable(modes[i].first)) - selectionMode = i; - } - ImGui::EndCombo(); - } - - this->m_mode = cs_mode(modes[selectionMode].second); - } - break; - case Architecture::WASM: - #endif - case Architecture::EVM: - case Architecture::TMS320C64X: - case Architecture::ARM64: - case Architecture::SYSZ: - case Architecture::XCORE: - this->m_mode = cs_mode(0); - break; - } - - ImGuiExt::EndBox(); - } - } - - // Draw disassemble button - ImGui::BeginDisabled(this->m_disassemblerTask.isRunning()); - { - if (ImGui::Button("hex.builtin.view.disassembler.disassemble"_lang)) - this->disassemble(); - } - ImGui::EndDisabled(); - - // Draw a spinner if the disassembler is running - if (this->m_disassemblerTask.isRunning()) { + // Draw endian radio buttons. This setting is available for all architectures + static int littleEndian = true; + ImGui::RadioButton("hex.builtin.common.little_endian"_lang, &littleEndian, true); ImGui::SameLine(); - ImGuiExt::TextSpinner("hex.builtin.view.disassembler.disassembling"_lang); - } + ImGui::RadioButton("hex.builtin.common.big_endian"_lang, &littleEndian, false); - ImGui::NewLine(); + ImGui::NewLine(); - ImGui::TextUnformatted("hex.builtin.view.disassembler.disassembly.title"_lang); - ImGui::Separator(); - - // Draw disassembly table - if (ImGui::BeginTable("##disassembly", 4, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable)) { - ImGui::TableSetupScrollFreeze(0, 1); - ImGui::TableSetupColumn("hex.builtin.view.disassembler.disassembly.address"_lang); - ImGui::TableSetupColumn("hex.builtin.view.disassembler.disassembly.offset"_lang); - ImGui::TableSetupColumn("hex.builtin.view.disassembler.disassembly.bytes"_lang); - ImGui::TableSetupColumn("hex.builtin.view.disassembler.disassembly.title"_lang); - - if (!this->m_disassemblerTask.isRunning()) { - ImGuiListClipper clipper; - clipper.Begin(this->m_disassembly.size()); - - ImGui::TableHeadersRow(); - while (clipper.Step()) { - for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { - const auto &instruction = this->m_disassembly[i]; - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - - // Draw a selectable label for the address - ImGui::PushID(i); - if (ImGui::Selectable("##DisassemblyLine", false, ImGuiSelectableFlags_SpanAllColumns)) { - ImHexApi::HexEditor::setSelection(instruction.offset, instruction.size); - } - ImGui::PopID(); - - // Draw instruction address + // Draw architecture specific settings + switch (this->m_architecture) { + case Architecture::ARM: + { + static int mode = CS_MODE_ARM; + ImGui::RadioButton("hex.builtin.view.disassembler.arm.arm"_lang, &mode, CS_MODE_ARM); ImGui::SameLine(); - ImGuiExt::TextFormatted("0x{0:X}", instruction.address); + ImGui::RadioButton("hex.builtin.view.disassembler.arm.thumb"_lang, &mode, CS_MODE_THUMB); - // Draw instruction offset - ImGui::TableNextColumn(); - ImGuiExt::TextFormatted("0x{0:X}", instruction.offset); - - // Draw instruction bytes - ImGui::TableNextColumn(); - ImGui::TextUnformatted(instruction.bytes.c_str()); - - // Draw instruction mnemonic and operands - ImGui::TableNextColumn(); - ImGuiExt::TextFormattedColored(ImColor(0xFFD69C56), "{}", instruction.mnemonic); + static int extraMode = 0; + ImGui::RadioButton("hex.builtin.view.disassembler.arm.default"_lang, &extraMode, 0); ImGui::SameLine(); - ImGui::TextUnformatted(instruction.operators.c_str()); + ImGui::RadioButton("hex.builtin.view.disassembler.arm.cortex_m"_lang, &extraMode, CS_MODE_MCLASS); + ImGui::SameLine(); + ImGui::RadioButton("hex.builtin.view.disassembler.arm.armv8"_lang, &extraMode, CS_MODE_V8); + + this->m_mode = cs_mode(mode | extraMode); } - } + break; + case Architecture::MIPS: + { + static int mode = CS_MODE_MIPS32; + ImGui::RadioButton("hex.builtin.view.disassembler.mips.mips32"_lang, &mode, CS_MODE_MIPS32); + ImGui::SameLine(); + ImGui::RadioButton("hex.builtin.view.disassembler.mips.mips64"_lang, &mode, CS_MODE_MIPS64); + ImGui::SameLine(); + ImGui::RadioButton("hex.builtin.view.disassembler.mips.mips32R6"_lang, &mode, CS_MODE_MIPS32R6); - clipper.End(); + ImGui::RadioButton("hex.builtin.view.disassembler.mips.mips2"_lang, &mode, CS_MODE_MIPS2); + ImGui::SameLine(); + ImGui::RadioButton("hex.builtin.view.disassembler.mips.mips3"_lang, &mode, CS_MODE_MIPS3); + + static bool microMode; + ImGui::Checkbox("hex.builtin.view.disassembler.mips.micro"_lang, µMode); + + this->m_mode = cs_mode(mode | (microMode ? CS_MODE_MICRO : cs_mode(0))); + } + break; + case Architecture::X86: + { + static int mode = CS_MODE_32; + ImGui::RadioButton("hex.builtin.view.disassembler.16bit"_lang, &mode, CS_MODE_16); + ImGui::SameLine(); + ImGui::RadioButton("hex.builtin.view.disassembler.32bit"_lang, &mode, CS_MODE_32); + ImGui::SameLine(); + ImGui::RadioButton("hex.builtin.view.disassembler.64bit"_lang, &mode, CS_MODE_64); + + this->m_mode = cs_mode(mode); + } + break; + case Architecture::PPC: + { + static int mode = CS_MODE_32; + ImGui::RadioButton("hex.builtin.view.disassembler.32bit"_lang, &mode, CS_MODE_32); + ImGui::SameLine(); + ImGui::RadioButton("hex.builtin.view.disassembler.64bit"_lang, &mode, CS_MODE_64); + + static bool qpx = false; + ImGui::Checkbox("hex.builtin.view.disassembler.ppc.qpx"_lang, &qpx); + + #if CS_API_MAJOR >= 5 + static bool spe = false; + ImGui::Checkbox("hex.builtin.view.disassembler.ppc.spe"_lang, &spe); + static bool booke = false; + ImGui::Checkbox("hex.builtin.view.disassembler.ppc.booke"_lang, &booke); + + this->m_mode = cs_mode(mode | (qpx ? CS_MODE_QPX : cs_mode(0)) | (spe ? CS_MODE_SPE : cs_mode(0)) | (booke ? CS_MODE_BOOKE : cs_mode(0))); + #else + this->m_mode = cs_mode(mode | (qpx ? CS_MODE_QPX : cs_mode(0))); + #endif + } + break; + case Architecture::SPARC: + { + static bool v9Mode = false; + ImGui::Checkbox("hex.builtin.view.disassembler.sparc.v9"_lang, &v9Mode); + + this->m_mode = cs_mode(v9Mode ? CS_MODE_V9 : cs_mode(0)); + } + break; + #if CS_API_MAJOR >= 5 + case Architecture::RISCV: + { + static int mode = CS_MODE_RISCV32; + ImGui::RadioButton("hex.builtin.view.disassembler.32bit"_lang, &mode, CS_MODE_RISCV32); + ImGui::SameLine(); + ImGui::RadioButton("hex.builtin.view.disassembler.64bit"_lang, &mode, CS_MODE_RISCV64); + + static bool compressed = false; + ImGui::Checkbox("hex.builtin.view.disassembler.riscv.compressed"_lang, &compressed); + + this->m_mode = cs_mode(mode | (compressed ? CS_MODE_RISCVC : cs_mode(0))); + } + break; + #endif + case Architecture::M68K: + { + static int selectedMode = 0; + + std::pair modes[] = { + {"hex.builtin.view.disassembler.m68k.000"_lang, CS_MODE_M68K_000}, + { "hex.builtin.view.disassembler.m68k.010"_lang, CS_MODE_M68K_010}, + { "hex.builtin.view.disassembler.m68k.020"_lang, CS_MODE_M68K_020}, + { "hex.builtin.view.disassembler.m68k.030"_lang, CS_MODE_M68K_030}, + { "hex.builtin.view.disassembler.m68k.040"_lang, CS_MODE_M68K_040}, + { "hex.builtin.view.disassembler.m68k.060"_lang, CS_MODE_M68K_060}, + }; + + if (ImGui::BeginCombo("hex.builtin.view.disassembler.settings.mode"_lang, modes[selectedMode].first)) { + for (u32 i = 0; i < IM_ARRAYSIZE(modes); i++) { + if (ImGui::Selectable(modes[i].first)) + selectedMode = i; + } + ImGui::EndCombo(); + } + + this->m_mode = cs_mode(modes[selectedMode].second); + } + break; + case Architecture::M680X: + { + static int selectedMode = 0; + + std::pair modes[] = { + {"hex.builtin.view.disassembler.m680x.6301"_lang, CS_MODE_M680X_6301 }, + { "hex.builtin.view.disassembler.m680x.6309"_lang, CS_MODE_M680X_6309 }, + { "hex.builtin.view.disassembler.m680x.6800"_lang, CS_MODE_M680X_6800 }, + { "hex.builtin.view.disassembler.m680x.6801"_lang, CS_MODE_M680X_6801 }, + { "hex.builtin.view.disassembler.m680x.6805"_lang, CS_MODE_M680X_6805 }, + { "hex.builtin.view.disassembler.m680x.6808"_lang, CS_MODE_M680X_6808 }, + { "hex.builtin.view.disassembler.m680x.6809"_lang, CS_MODE_M680X_6809 }, + { "hex.builtin.view.disassembler.m680x.6811"_lang, CS_MODE_M680X_6811 }, + { "hex.builtin.view.disassembler.m680x.cpu12"_lang, CS_MODE_M680X_CPU12}, + { "hex.builtin.view.disassembler.m680x.hcs08"_lang, CS_MODE_M680X_HCS08}, + }; + + if (ImGui::BeginCombo("hex.builtin.view.disassembler.settings.mode"_lang, modes[selectedMode].first)) { + for (u32 i = 0; i < IM_ARRAYSIZE(modes); i++) { + if (ImGui::Selectable(modes[i].first)) + selectedMode = i; + } + ImGui::EndCombo(); + } + + this->m_mode = cs_mode(modes[selectedMode].second); + } + break; + #if CS_API_MAJOR >= 5 + case Architecture::MOS65XX: + { + static int selectedMode = 0; + + std::pair modes[] = { + {"hex.builtin.view.disassembler.mos65xx.6502"_lang, CS_MODE_MOS65XX_6502 }, + { "hex.builtin.view.disassembler.mos65xx.65c02"_lang, CS_MODE_MOS65XX_65C02 }, + { "hex.builtin.view.disassembler.mos65xx.w65c02"_lang, CS_MODE_MOS65XX_W65C02 }, + { "hex.builtin.view.disassembler.mos65xx.65816"_lang, CS_MODE_MOS65XX_65816 }, + { "hex.builtin.view.disassembler.mos65xx.65816_long_m"_lang, CS_MODE_MOS65XX_65816_LONG_M }, + { "hex.builtin.view.disassembler.mos65xx.65816_long_x"_lang, CS_MODE_MOS65XX_65816_LONG_X }, + { "hex.builtin.view.disassembler.mos65xx.65816_long_mx"_lang, CS_MODE_MOS65XX_65816_LONG_MX}, + }; + + if (ImGui::BeginCombo("hex.builtin.view.disassembler.settings.mode"_lang, modes[selectedMode].first)) { + for (u32 i = 0; i < IM_ARRAYSIZE(modes); i++) { + if (ImGui::Selectable(modes[i].first)) + selectedMode = i; + } + ImGui::EndCombo(); + } + + this->m_mode = cs_mode(modes[selectedMode].second); + } + break; + #endif + #if CS_API_MAJOR >= 5 + case Architecture::BPF: + { + static int mode = CS_MODE_BPF_CLASSIC; + ImGui::RadioButton("hex.builtin.view.disassembler.bpf.classic"_lang, &mode, CS_MODE_BPF_CLASSIC); + ImGui::SameLine(); + ImGui::RadioButton("hex.builtin.view.disassembler.bpf.extended"_lang, &mode, CS_MODE_BPF_EXTENDED); + + this->m_mode = cs_mode(mode); + } + break; + case Architecture::SH: + { + static u32 selectionMode = 0; + static bool fpu = false; + static bool dsp = false; + + std::pair modes[] = { + { "hex.builtin.view.disassembler.sh.sh2"_lang, CS_MODE_SH2 }, + { "hex.builtin.view.disassembler.sh.sh2a"_lang, CS_MODE_SH2A }, + { "hex.builtin.view.disassembler.sh.sh3"_lang, CS_MODE_SH3 }, + { "hex.builtin.view.disassembler.sh.sh4"_lang, CS_MODE_SH4 }, + { "hex.builtin.view.disassembler.sh.sh4a"_lang, CS_MODE_SH4A }, + }; + + if (ImGui::BeginCombo("hex.builtin.view.disassembler.settings.mode"_lang, modes[selectionMode].first)) { + for (u32 i = 0; i < IM_ARRAYSIZE(modes); i++) { + if (ImGui::Selectable(modes[i].first)) + selectionMode = i; + } + ImGui::EndCombo(); + } + + ImGui::Checkbox("hex.builtin.view.disassembler.sh.fpu"_lang, &fpu); + ImGui::SameLine(); + ImGui::Checkbox("hex.builtin.view.disassembler.sh.dsp"_lang, &dsp); + + this->m_mode = cs_mode(modes[selectionMode].second | (fpu ? CS_MODE_SHFPU : cs_mode(0)) | (dsp ? CS_MODE_SHDSP : cs_mode(0))); + } + break; + case Architecture::TRICORE: + { + static u32 selectionMode = 0; + + std::pair modes[] = { + { "hex.builtin.view.disassembler.tricore.110"_lang, CS_MODE_TRICORE_110 }, + { "hex.builtin.view.disassembler.tricore.120"_lang, CS_MODE_TRICORE_120 }, + { "hex.builtin.view.disassembler.tricore.130"_lang, CS_MODE_TRICORE_130 }, + { "hex.builtin.view.disassembler.tricore.131"_lang, CS_MODE_TRICORE_131 }, + { "hex.builtin.view.disassembler.tricore.160"_lang, CS_MODE_TRICORE_160 }, + { "hex.builtin.view.disassembler.tricore.161"_lang, CS_MODE_TRICORE_161 }, + { "hex.builtin.view.disassembler.tricore.162"_lang, CS_MODE_TRICORE_162 }, + }; + + if (ImGui::BeginCombo("hex.builtin.view.disassembler.settings.mode"_lang, modes[selectionMode].first)) { + for (u32 i = 0; i < IM_ARRAYSIZE(modes); i++) { + if (ImGui::Selectable(modes[i].first)) + selectionMode = i; + } + ImGui::EndCombo(); + } + + this->m_mode = cs_mode(modes[selectionMode].second); + } + break; + case Architecture::WASM: + #endif + case Architecture::EVM: + case Architecture::TMS320C64X: + case Architecture::ARM64: + case Architecture::SYSZ: + case Architecture::XCORE: + this->m_mode = cs_mode(0); + break; } - ImGui::EndTable(); + ImGuiExt::EndBox(); } } + + // Draw disassemble button + ImGui::BeginDisabled(this->m_disassemblerTask.isRunning()); + { + if (ImGui::Button("hex.builtin.view.disassembler.disassemble"_lang)) + this->disassemble(); + } + ImGui::EndDisabled(); + + // Draw a spinner if the disassembler is running + if (this->m_disassemblerTask.isRunning()) { + ImGui::SameLine(); + ImGuiExt::TextSpinner("hex.builtin.view.disassembler.disassembling"_lang); + } + + ImGui::NewLine(); + + ImGui::TextUnformatted("hex.builtin.view.disassembler.disassembly.title"_lang); + ImGui::Separator(); + + // Draw disassembly table + if (ImGui::BeginTable("##disassembly", 4, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable)) { + ImGui::TableSetupScrollFreeze(0, 1); + ImGui::TableSetupColumn("hex.builtin.view.disassembler.disassembly.address"_lang); + ImGui::TableSetupColumn("hex.builtin.view.disassembler.disassembly.offset"_lang); + ImGui::TableSetupColumn("hex.builtin.view.disassembler.disassembly.bytes"_lang); + ImGui::TableSetupColumn("hex.builtin.view.disassembler.disassembly.title"_lang); + + if (!this->m_disassemblerTask.isRunning()) { + ImGuiListClipper clipper; + clipper.Begin(this->m_disassembly.size()); + + ImGui::TableHeadersRow(); + while (clipper.Step()) { + for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { + const auto &instruction = this->m_disassembly[i]; + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + + // Draw a selectable label for the address + ImGui::PushID(i); + if (ImGui::Selectable("##DisassemblyLine", false, ImGuiSelectableFlags_SpanAllColumns)) { + ImHexApi::HexEditor::setSelection(instruction.offset, instruction.size); + } + ImGui::PopID(); + + // Draw instruction address + ImGui::SameLine(); + ImGuiExt::TextFormatted("0x{0:X}", instruction.address); + + // Draw instruction offset + ImGui::TableNextColumn(); + ImGuiExt::TextFormatted("0x{0:X}", instruction.offset); + + // Draw instruction bytes + ImGui::TableNextColumn(); + ImGui::TextUnformatted(instruction.bytes.c_str()); + + // Draw instruction mnemonic and operands + ImGui::TableNextColumn(); + ImGuiExt::TextFormattedColored(ImColor(0xFFD69C56), "{}", instruction.mnemonic); + ImGui::SameLine(); + ImGui::TextUnformatted(instruction.operators.c_str()); + } + } + + clipper.End(); + } + + ImGui::EndTable(); + } } - ImGui::End(); } } diff --git a/plugins/builtin/source/content/views/view_find.cpp b/plugins/builtin/source/content/views/view_find.cpp index 7d3ba7546..37d43ba13 100644 --- a/plugins/builtin/source/content/views/view_find.cpp +++ b/plugins/builtin/source/content/views/view_find.cpp @@ -14,7 +14,7 @@ namespace hex::plugin::builtin { - ViewFind::ViewFind() : View("hex.builtin.view.find.name") { + ViewFind::ViewFind() : View::Window("hex.builtin.view.find.name") { const static auto HighlightColor = [] { return (ImGuiExt::GetCustomColorU32(ImGuiCustomCol_FindHighlight) & 0x00FFFFFF) | 0x70000000; }; ImHexApi::HexEditor::addBackgroundHighlightingProvider([this](u64 address, const u8* data, size_t size, bool) -> std::optional { @@ -599,347 +599,343 @@ namespace hex::plugin::builtin { } void ViewFind::drawContent() { - if (ImGui::Begin(View::toWindowName("hex.builtin.view.find.name").c_str(), &this->getWindowOpenState())) { - auto provider = ImHexApi::Provider::get(); + auto provider = ImHexApi::Provider::get(); - ImGui::BeginDisabled(this->m_searchTask.isRunning()); - { - ui::regionSelectionPicker(&this->m_searchSettings.region, provider, &this->m_searchSettings.range, true, true); + ImGui::BeginDisabled(this->m_searchTask.isRunning()); + { + ui::regionSelectionPicker(&this->m_searchSettings.region, provider, &this->m_searchSettings.range, true, true); - ImGui::NewLine(); + ImGui::NewLine(); - if (ImGui::BeginTabBar("SearchMethods")) { - const std::array StringTypes = { - "hex.builtin.common.encoding.ascii"_lang, - "hex.builtin.common.encoding.utf16le"_lang, - "hex.builtin.common.encoding.utf16be"_lang, - hex::format("{} + {}", "hex.builtin.common.encoding.ascii"_lang, "hex.builtin.common.encoding.utf16le"_lang), - hex::format("{} + {}", "hex.builtin.common.encoding.ascii"_lang, "hex.builtin.common.encoding.utf16be"_lang) - }; + if (ImGui::BeginTabBar("SearchMethods")) { + const std::array StringTypes = { + "hex.builtin.common.encoding.ascii"_lang, + "hex.builtin.common.encoding.utf16le"_lang, + "hex.builtin.common.encoding.utf16be"_lang, + hex::format("{} + {}", "hex.builtin.common.encoding.ascii"_lang, "hex.builtin.common.encoding.utf16le"_lang), + hex::format("{} + {}", "hex.builtin.common.encoding.ascii"_lang, "hex.builtin.common.encoding.utf16be"_lang) + }; - auto &mode = this->m_searchSettings.mode; - if (ImGui::BeginTabItem("hex.builtin.view.find.strings"_lang)) { - auto &settings = this->m_searchSettings.strings; - mode = SearchSettings::Mode::Strings; + auto &mode = this->m_searchSettings.mode; + if (ImGui::BeginTabItem("hex.builtin.view.find.strings"_lang)) { + auto &settings = this->m_searchSettings.strings; + mode = SearchSettings::Mode::Strings; - ImGui::InputInt("hex.builtin.view.find.strings.min_length"_lang, &settings.minLength, 1, 1); - if (settings.minLength < 1) - settings.minLength = 1; + ImGui::InputInt("hex.builtin.view.find.strings.min_length"_lang, &settings.minLength, 1, 1); + if (settings.minLength < 1) + settings.minLength = 1; - if (ImGui::BeginCombo("hex.builtin.common.type"_lang, StringTypes[std::to_underlying(settings.type)].c_str())) { - for (size_t i = 0; i < StringTypes.size(); i++) { - auto type = static_cast(i); + if (ImGui::BeginCombo("hex.builtin.common.type"_lang, StringTypes[std::to_underlying(settings.type)].c_str())) { + for (size_t i = 0; i < StringTypes.size(); i++) { + auto type = static_cast(i); - if (ImGui::Selectable(StringTypes[i].c_str(), type == settings.type)) - settings.type = type; - } - ImGui::EndCombo(); + if (ImGui::Selectable(StringTypes[i].c_str(), type == settings.type)) + settings.type = type; } - - if (ImGui::CollapsingHeader("hex.builtin.view.find.strings.match_settings"_lang)) { - ImGui::Checkbox("hex.builtin.view.find.strings.null_term"_lang, &settings.nullTermination); - - ImGuiExt::Header("hex.builtin.view.find.strings.chars"_lang); - ImGui::Checkbox(hex::format("{} [a-z]", "hex.builtin.view.find.strings.lower_case"_lang.get()).c_str(), &settings.lowerCaseLetters); - ImGui::Checkbox(hex::format("{} [A-Z]", "hex.builtin.view.find.strings.upper_case"_lang.get()).c_str(), &settings.upperCaseLetters); - ImGui::Checkbox(hex::format("{} [0-9]", "hex.builtin.view.find.strings.numbers"_lang.get()).c_str(), &settings.numbers); - ImGui::Checkbox(hex::format("{} [_]", "hex.builtin.view.find.strings.underscores"_lang.get()).c_str(), &settings.underscores); - ImGui::Checkbox(hex::format("{} [!\"#$%...]", "hex.builtin.view.find.strings.symbols"_lang.get()).c_str(), &settings.symbols); - ImGui::Checkbox(hex::format("{} [ \\f\\t\\v]", "hex.builtin.view.find.strings.spaces"_lang.get()).c_str(), &settings.spaces); - ImGui::Checkbox(hex::format("{} [\\r\\n]", "hex.builtin.view.find.strings.line_feeds"_lang.get()).c_str(), &settings.lineFeeds); - } - - this->m_settingsValid = true; - - ImGui::EndTabItem(); + ImGui::EndCombo(); } - if (ImGui::BeginTabItem("hex.builtin.view.find.sequences"_lang)) { - auto &settings = this->m_searchSettings.bytes; - - mode = SearchSettings::Mode::Sequence; - - ImGuiExt::InputTextIcon("hex.builtin.common.value"_lang, ICON_VS_SYMBOL_KEY, settings.sequence); - - this->m_settingsValid = !settings.sequence.empty() && !hex::decodeByteString(settings.sequence).empty(); - - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("hex.builtin.view.find.regex"_lang)) { - auto &settings = this->m_searchSettings.regex; - - mode = SearchSettings::Mode::Regex; - - ImGui::InputInt("hex.builtin.view.find.strings.min_length"_lang, &settings.minLength, 1, 1); - if (settings.minLength < 1) - settings.minLength = 1; - - if (ImGui::BeginCombo("hex.builtin.common.type"_lang, StringTypes[std::to_underlying(settings.type)].c_str())) { - for (size_t i = 0; i < StringTypes.size(); i++) { - auto type = static_cast(i); - - if (ImGui::Selectable(StringTypes[i].c_str(), type == settings.type)) - settings.type = type; - } - ImGui::EndCombo(); - } + if (ImGui::CollapsingHeader("hex.builtin.view.find.strings.match_settings"_lang)) { ImGui::Checkbox("hex.builtin.view.find.strings.null_term"_lang, &settings.nullTermination); - ImGui::NewLine(); + ImGuiExt::Header("hex.builtin.view.find.strings.chars"_lang); + ImGui::Checkbox(hex::format("{} [a-z]", "hex.builtin.view.find.strings.lower_case"_lang.get()).c_str(), &settings.lowerCaseLetters); + ImGui::Checkbox(hex::format("{} [A-Z]", "hex.builtin.view.find.strings.upper_case"_lang.get()).c_str(), &settings.upperCaseLetters); + ImGui::Checkbox(hex::format("{} [0-9]", "hex.builtin.view.find.strings.numbers"_lang.get()).c_str(), &settings.numbers); + ImGui::Checkbox(hex::format("{} [_]", "hex.builtin.view.find.strings.underscores"_lang.get()).c_str(), &settings.underscores); + ImGui::Checkbox(hex::format("{} [!\"#$%...]", "hex.builtin.view.find.strings.symbols"_lang.get()).c_str(), &settings.symbols); + ImGui::Checkbox(hex::format("{} [ \\f\\t\\v]", "hex.builtin.view.find.strings.spaces"_lang.get()).c_str(), &settings.spaces); + ImGui::Checkbox(hex::format("{} [\\r\\n]", "hex.builtin.view.find.strings.line_feeds"_lang.get()).c_str(), &settings.lineFeeds); + } - ImGuiExt::InputTextIcon("hex.builtin.view.find.regex.pattern"_lang, ICON_VS_REGEX, settings.pattern); + this->m_settingsValid = true; - try { - std::regex regex(settings.pattern); - this->m_settingsValid = true; - } catch (const std::regex_error &) { - this->m_settingsValid = false; + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("hex.builtin.view.find.sequences"_lang)) { + auto &settings = this->m_searchSettings.bytes; + + mode = SearchSettings::Mode::Sequence; + + ImGuiExt::InputTextIcon("hex.builtin.common.value"_lang, ICON_VS_SYMBOL_KEY, settings.sequence); + + this->m_settingsValid = !settings.sequence.empty() && !hex::decodeByteString(settings.sequence).empty(); + + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("hex.builtin.view.find.regex"_lang)) { + auto &settings = this->m_searchSettings.regex; + + mode = SearchSettings::Mode::Regex; + + ImGui::InputInt("hex.builtin.view.find.strings.min_length"_lang, &settings.minLength, 1, 1); + if (settings.minLength < 1) + settings.minLength = 1; + + if (ImGui::BeginCombo("hex.builtin.common.type"_lang, StringTypes[std::to_underlying(settings.type)].c_str())) { + for (size_t i = 0; i < StringTypes.size(); i++) { + auto type = static_cast(i); + + if (ImGui::Selectable(StringTypes[i].c_str(), type == settings.type)) + settings.type = type; + } + ImGui::EndCombo(); + } + + ImGui::Checkbox("hex.builtin.view.find.strings.null_term"_lang, &settings.nullTermination); + + ImGui::NewLine(); + + ImGuiExt::InputTextIcon("hex.builtin.view.find.regex.pattern"_lang, ICON_VS_REGEX, settings.pattern); + + try { + std::regex regex(settings.pattern); + this->m_settingsValid = true; + } catch (const std::regex_error &) { + this->m_settingsValid = false; + } + + if (settings.pattern.empty()) + this->m_settingsValid = false; + + ImGui::Checkbox("hex.builtin.view.find.regex.full_match"_lang, &settings.fullMatch); + + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("hex.builtin.view.find.binary_pattern"_lang)) { + auto &settings = this->m_searchSettings.binaryPattern; + + mode = SearchSettings::Mode::BinaryPattern; + + ImGuiExt::InputTextIcon("hex.builtin.view.find.binary_pattern"_lang, ICON_VS_SYMBOL_NAMESPACE, settings.input); + + constexpr static u32 min = 1, max = 0x1000; + ImGui::SliderScalar("hex.builtin.view.find.binary_pattern.alignment"_lang, ImGuiDataType_U32, &settings.alignment, &min, &max); + + settings.pattern = hex::BinaryPattern(settings.input); + this->m_settingsValid = settings.pattern.isValid() && settings.alignment > 0; + + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("hex.builtin.view.find.value"_lang)) { + auto &settings = this->m_searchSettings.value; + + mode = SearchSettings::Mode::Value; + + bool edited = false; + + if (settings.range) { + if (ImGuiExt::InputTextIcon("hex.builtin.view.find.value.min"_lang, ICON_VS_SYMBOL_NUMERIC, settings.inputMin)) edited = true; + if (ImGuiExt::InputTextIcon("hex.builtin.view.find.value.max"_lang, ICON_VS_SYMBOL_NUMERIC, settings.inputMax)) edited = true; + } else { + if (ImGuiExt::InputTextIcon("hex.builtin.common.value"_lang, ICON_VS_SYMBOL_NUMERIC, settings.inputMin)) { + edited = true; + settings.inputMax = settings.inputMin; } - if (settings.pattern.empty()) - this->m_settingsValid = false; - - ImGui::Checkbox("hex.builtin.view.find.regex.full_match"_lang, &settings.fullMatch); - - ImGui::EndTabItem(); + ImGui::BeginDisabled(); + ImGuiExt::InputTextIcon("##placeholder_value", ICON_VS_SYMBOL_NUMERIC, settings.inputMax); + ImGui::EndDisabled(); } - if (ImGui::BeginTabItem("hex.builtin.view.find.binary_pattern"_lang)) { - auto &settings = this->m_searchSettings.binaryPattern; - mode = SearchSettings::Mode::BinaryPattern; + ImGui::Checkbox("hex.builtin.view.find.value.range"_lang, &settings.range); + ImGui::NewLine(); - ImGuiExt::InputTextIcon("hex.builtin.view.find.binary_pattern"_lang, ICON_VS_SYMBOL_NAMESPACE, settings.input); + const std::array InputTypes = { + "hex.builtin.common.type.u8"_lang, + "hex.builtin.common.type.u16"_lang, + "hex.builtin.common.type.u32"_lang, + "hex.builtin.common.type.u64"_lang, + "hex.builtin.common.type.i8"_lang, + "hex.builtin.common.type.i16"_lang, + "hex.builtin.common.type.i32"_lang, + "hex.builtin.common.type.i64"_lang, + "hex.builtin.common.type.f32"_lang, + "hex.builtin.common.type.f64"_lang + }; - constexpr static u32 min = 1, max = 0x1000; - ImGui::SliderScalar("hex.builtin.view.find.binary_pattern.alignment"_lang, ImGuiDataType_U32, &settings.alignment, &min, &max); + if (ImGui::BeginCombo("hex.builtin.common.type"_lang, InputTypes[std::to_underlying(settings.type)].c_str())) { + for (size_t i = 0; i < InputTypes.size(); i++) { + auto type = static_cast(i); - settings.pattern = hex::BinaryPattern(settings.input); - this->m_settingsValid = settings.pattern.isValid() && settings.alignment > 0; - - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("hex.builtin.view.find.value"_lang)) { - auto &settings = this->m_searchSettings.value; - - mode = SearchSettings::Mode::Value; - - bool edited = false; - - if (settings.range) { - if (ImGuiExt::InputTextIcon("hex.builtin.view.find.value.min"_lang, ICON_VS_SYMBOL_NUMERIC, settings.inputMin)) edited = true; - if (ImGuiExt::InputTextIcon("hex.builtin.view.find.value.max"_lang, ICON_VS_SYMBOL_NUMERIC, settings.inputMax)) edited = true; - } else { - if (ImGuiExt::InputTextIcon("hex.builtin.common.value"_lang, ICON_VS_SYMBOL_NUMERIC, settings.inputMin)) { + if (ImGui::Selectable(InputTypes[i].c_str(), type == settings.type)) { + settings.type = type; edited = true; - settings.inputMax = settings.inputMin; - } - - ImGui::BeginDisabled(); - ImGuiExt::InputTextIcon("##placeholder_value", ICON_VS_SYMBOL_NUMERIC, settings.inputMax); - ImGui::EndDisabled(); - } - - ImGui::Checkbox("hex.builtin.view.find.value.range"_lang, &settings.range); - ImGui::NewLine(); - - const std::array InputTypes = { - "hex.builtin.common.type.u8"_lang, - "hex.builtin.common.type.u16"_lang, - "hex.builtin.common.type.u32"_lang, - "hex.builtin.common.type.u64"_lang, - "hex.builtin.common.type.i8"_lang, - "hex.builtin.common.type.i16"_lang, - "hex.builtin.common.type.i32"_lang, - "hex.builtin.common.type.i64"_lang, - "hex.builtin.common.type.f32"_lang, - "hex.builtin.common.type.f64"_lang - }; - - if (ImGui::BeginCombo("hex.builtin.common.type"_lang, InputTypes[std::to_underlying(settings.type)].c_str())) { - for (size_t i = 0; i < InputTypes.size(); i++) { - auto type = static_cast(i); - - if (ImGui::Selectable(InputTypes[i].c_str(), type == settings.type)) { - settings.type = type; - edited = true; - } - } - ImGui::EndCombo(); - } - - { - int selection = [&] { - switch (settings.endian) { - default: - case std::endian::little: return 0; - case std::endian::big: return 1; - } - }(); - - std::array options = { "hex.builtin.common.little"_lang, "hex.builtin.common.big"_lang }; - if (ImGui::SliderInt("hex.builtin.common.endian"_lang, &selection, 0, options.size() - 1, options[selection], ImGuiSliderFlags_NoInput)) { - edited = true; - switch (selection) { - default: - case 0: settings.endian = std::endian::little; break; - case 1: settings.endian = std::endian::big; break; - } } } + ImGui::EndCombo(); + } - ImGui::Checkbox("hex.builtin.view.find.value.aligned"_lang, &settings.aligned); + { + int selection = [&] { + switch (settings.endian) { + default: + case std::endian::little: return 0; + case std::endian::big: return 1; + } + }(); - if (edited) { - auto [minValid, min, minSize] = parseNumericValueInput(settings.inputMin, settings.type); - auto [maxValid, max, maxSize] = parseNumericValueInput(settings.inputMax, settings.type); - - this->m_settingsValid = minValid && maxValid && minSize == maxSize; + std::array options = { "hex.builtin.common.little"_lang, "hex.builtin.common.big"_lang }; + if (ImGui::SliderInt("hex.builtin.common.endian"_lang, &selection, 0, options.size() - 1, options[selection], ImGuiSliderFlags_NoInput)) { + edited = true; + switch (selection) { + default: + case 0: settings.endian = std::endian::little; break; + case 1: settings.endian = std::endian::big; break; + } } - - if (settings.inputMin.empty()) - this->m_settingsValid = false; - - ImGui::EndTabItem(); } - ImGui::EndTabBar(); - } + ImGui::Checkbox("hex.builtin.view.find.value.aligned"_lang, &settings.aligned); - ImGui::NewLine(); + if (edited) { + auto [minValid, min, minSize] = parseNumericValueInput(settings.inputMin, settings.type); + auto [maxValid, max, maxSize] = parseNumericValueInput(settings.inputMax, settings.type); - ImGui::BeginDisabled(!this->m_settingsValid); - { - if (ImGui::Button("hex.builtin.view.find.search"_lang)) { - this->runSearch(); - - this->m_decodeSettings = this->m_searchSettings; + this->m_settingsValid = minValid && maxValid && minSize == maxSize; } + + if (settings.inputMin.empty()) + this->m_settingsValid = false; + + ImGui::EndTabItem(); } - ImGui::EndDisabled(); - ImGui::SameLine(); - ImGuiExt::TextFormatted("hex.builtin.view.find.search.entries"_lang, this->m_foundOccurrences->size()); + ImGui::EndTabBar(); + } - ImGui::BeginDisabled(this->m_foundOccurrences->empty()); - { - if (ImGui::Button("hex.builtin.view.find.search.reset"_lang)) { - this->m_foundOccurrences->clear(); - this->m_sortedOccurrences->clear(); - this->m_occurrenceTree->clear(); + ImGui::NewLine(); - EventManager::post(); - } + ImGui::BeginDisabled(!this->m_settingsValid); + { + if (ImGui::Button("hex.builtin.view.find.search"_lang)) { + this->runSearch(); + + this->m_decodeSettings = this->m_searchSettings; } - ImGui::EndDisabled(); } ImGui::EndDisabled(); + ImGui::SameLine(); + ImGuiExt::TextFormatted("hex.builtin.view.find.search.entries"_lang, this->m_foundOccurrences->size()); - ImGui::Separator(); - ImGui::NewLine(); + ImGui::BeginDisabled(this->m_foundOccurrences->empty()); + { + if (ImGui::Button("hex.builtin.view.find.search.reset"_lang)) { + this->m_foundOccurrences->clear(); + this->m_sortedOccurrences->clear(); + this->m_occurrenceTree->clear(); - auto &currOccurrences = *this->m_sortedOccurrences; - - ImGui::PushItemWidth(-1); - auto prevFilterLength = this->m_currFilter->length(); - if (ImGuiExt::InputTextIcon("##filter", ICON_VS_FILTER, *this->m_currFilter)) { - if (prevFilterLength > this->m_currFilter->length()) - *this->m_sortedOccurrences = *this->m_foundOccurrences; - - if (this->m_filterTask.isRunning()) - this->m_filterTask.interrupt(); - - if (!this->m_currFilter->empty()) { - this->m_filterTask = TaskManager::createTask("Filtering", currOccurrences.size(), [this, provider, &currOccurrences](Task &task) { - u64 progress = 0; - std::erase_if(currOccurrences, [this, provider, &task, &progress](const auto ®ion) { - task.update(progress); - progress += 1; - - return !hex::containsIgnoreCase(this->decodeValue(provider, region), this->m_currFilter.get(provider)); - }); - }); + EventManager::post(); } } - ImGui::PopItemWidth(); - - if (ImGui::BeginTable("##entries", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Sortable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) { - ImGui::TableSetupScrollFreeze(0, 1); - ImGui::TableSetupColumn("hex.builtin.common.offset"_lang, 0, -1, ImGui::GetID("offset")); - ImGui::TableSetupColumn("hex.builtin.common.size"_lang, 0, -1, ImGui::GetID("size")); - ImGui::TableSetupColumn("hex.builtin.common.value"_lang, 0, -1, ImGui::GetID("value")); - - auto sortSpecs = ImGui::TableGetSortSpecs(); - - if (sortSpecs->SpecsDirty) { - std::sort(currOccurrences.begin(), currOccurrences.end(), [this, &sortSpecs, provider](const Occurrence &left, const Occurrence &right) -> bool { - if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("offset")) { - if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending) - return left.region.getStartAddress() > right.region.getStartAddress(); - else - return left.region.getStartAddress() < right.region.getStartAddress(); - } else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("size")) { - if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending) - return left.region.getSize() > right.region.getSize(); - else - return left.region.getSize() < right.region.getSize(); - } else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("value")) { - if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending) - return this->decodeValue(provider, left) > this->decodeValue(provider, right); - else - return this->decodeValue(provider, left) < this->decodeValue(provider, right); - } - - return false; - }); - - sortSpecs->SpecsDirty = false; - } - - ImGui::TableHeadersRow(); - - ImGuiListClipper clipper; - clipper.Begin(currOccurrences.size(), ImGui::GetTextLineHeightWithSpacing()); - - while (clipper.Step()) { - for (size_t i = clipper.DisplayStart; i < std::min(clipper.DisplayEnd, currOccurrences.size()); i++) { - auto &foundItem = currOccurrences[i]; - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - - ImGuiExt::TextFormatted("0x{:08X}", foundItem.region.getStartAddress()); - ImGui::TableNextColumn(); - ImGuiExt::TextFormatted("{}", hex::toByteString(foundItem.region.getSize())); - ImGui::TableNextColumn(); - - ImGui::PushID(i); - - auto value = this->decodeValue(provider, foundItem, 256); - ImGuiExt::TextFormatted("{}", value); - ImGui::SameLine(); - if (ImGui::Selectable("##line", foundItem.selected, ImGuiSelectableFlags_SpanAllColumns)) { - if (ImGui::GetIO().KeyCtrl) { - foundItem.selected = !foundItem.selected; - } else { - for (auto &occurrence : *this->m_sortedOccurrences) - occurrence.selected = false; - foundItem.selected = true; - ImHexApi::HexEditor::setSelection(foundItem.region.getStartAddress(), foundItem.region.getSize()); - } - } - drawContextMenu(foundItem, value); - - ImGui::PopID(); - } - } - clipper.End(); - - ImGui::EndTable(); - } - + ImGui::EndDisabled(); + } + ImGui::EndDisabled(); + + + ImGui::Separator(); + ImGui::NewLine(); + + auto &currOccurrences = *this->m_sortedOccurrences; + + ImGui::PushItemWidth(-1); + auto prevFilterLength = this->m_currFilter->length(); + if (ImGuiExt::InputTextIcon("##filter", ICON_VS_FILTER, *this->m_currFilter)) { + if (prevFilterLength > this->m_currFilter->length()) + *this->m_sortedOccurrences = *this->m_foundOccurrences; + + if (this->m_filterTask.isRunning()) + this->m_filterTask.interrupt(); + + if (!this->m_currFilter->empty()) { + this->m_filterTask = TaskManager::createTask("Filtering", currOccurrences.size(), [this, provider, &currOccurrences](Task &task) { + u64 progress = 0; + std::erase_if(currOccurrences, [this, provider, &task, &progress](const auto ®ion) { + task.update(progress); + progress += 1; + + return !hex::containsIgnoreCase(this->decodeValue(provider, region), this->m_currFilter.get(provider)); + }); + }); + } + } + ImGui::PopItemWidth(); + + if (ImGui::BeginTable("##entries", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Sortable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) { + ImGui::TableSetupScrollFreeze(0, 1); + ImGui::TableSetupColumn("hex.builtin.common.offset"_lang, 0, -1, ImGui::GetID("offset")); + ImGui::TableSetupColumn("hex.builtin.common.size"_lang, 0, -1, ImGui::GetID("size")); + ImGui::TableSetupColumn("hex.builtin.common.value"_lang, 0, -1, ImGui::GetID("value")); + + auto sortSpecs = ImGui::TableGetSortSpecs(); + + if (sortSpecs->SpecsDirty) { + std::sort(currOccurrences.begin(), currOccurrences.end(), [this, &sortSpecs, provider](const Occurrence &left, const Occurrence &right) -> bool { + if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("offset")) { + if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending) + return left.region.getStartAddress() > right.region.getStartAddress(); + else + return left.region.getStartAddress() < right.region.getStartAddress(); + } else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("size")) { + if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending) + return left.region.getSize() > right.region.getSize(); + else + return left.region.getSize() < right.region.getSize(); + } else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("value")) { + if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending) + return this->decodeValue(provider, left) > this->decodeValue(provider, right); + else + return this->decodeValue(provider, left) < this->decodeValue(provider, right); + } + + return false; + }); + + sortSpecs->SpecsDirty = false; + } + + ImGui::TableHeadersRow(); + + ImGuiListClipper clipper; + clipper.Begin(currOccurrences.size(), ImGui::GetTextLineHeightWithSpacing()); + + while (clipper.Step()) { + for (size_t i = clipper.DisplayStart; i < std::min(clipper.DisplayEnd, currOccurrences.size()); i++) { + auto &foundItem = currOccurrences[i]; + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + + ImGuiExt::TextFormatted("0x{:08X}", foundItem.region.getStartAddress()); + ImGui::TableNextColumn(); + ImGuiExt::TextFormatted("{}", hex::toByteString(foundItem.region.getSize())); + ImGui::TableNextColumn(); + + ImGui::PushID(i); + + auto value = this->decodeValue(provider, foundItem, 256); + ImGuiExt::TextFormatted("{}", value); + ImGui::SameLine(); + if (ImGui::Selectable("##line", foundItem.selected, ImGuiSelectableFlags_SpanAllColumns)) { + if (ImGui::GetIO().KeyCtrl) { + foundItem.selected = !foundItem.selected; + } else { + for (auto &occurrence : *this->m_sortedOccurrences) + occurrence.selected = false; + foundItem.selected = true; + ImHexApi::HexEditor::setSelection(foundItem.region.getStartAddress(), foundItem.region.getSize()); + } + } + drawContextMenu(foundItem, value); + + ImGui::PopID(); + } + } + clipper.End(); + + ImGui::EndTable(); } - ImGui::End(); } } diff --git a/plugins/builtin/source/content/views/view_hashes.cpp b/plugins/builtin/source/content/views/view_hashes.cpp index 7a9aef198..37b708c3a 100644 --- a/plugins/builtin/source/content/views/view_hashes.cpp +++ b/plugins/builtin/source/content/views/view_hashes.cpp @@ -42,21 +42,13 @@ namespace hex::plugin::builtin { 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; std::string m_result; ContentRegistry::Hashes::Hash::Function m_hash; }; - ViewHashes::ViewHashes() : View("hex.builtin.view.hashes.name") { + ViewHashes::ViewHashes() : View::Window("hex.builtin.view.hashes.name") { EventManager::subscribe(this, [this](const auto &providerRegion) { for (auto &function : this->m_hashFunctions.get(providerRegion.getProvider())) function.reset(); @@ -143,112 +135,109 @@ namespace hex::plugin::builtin { this->m_selectedHash = hashes.front().get(); } - if (ImGui::Begin(View::toWindowName("hex.builtin.view.hashes.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) { - if (ImGui::BeginCombo("hex.builtin.view.hashes.function"_lang, this->m_selectedHash != nullptr ? LangEntry(this->m_selectedHash->getUnlocalizedName()) : "")) { + if (ImGui::BeginCombo("hex.builtin.view.hashes.function"_lang, this->m_selectedHash != nullptr ? LangEntry(this->m_selectedHash->getUnlocalizedName()) : "")) { - for (const auto &hash : hashes) { - if (ImGui::Selectable(LangEntry(hash->getUnlocalizedName()), this->m_selectedHash == hash.get())) { - this->m_selectedHash = hash.get(); - this->m_newHashName.clear(); - } - } - - ImGui::EndCombo(); - } - - if (this->m_newHashName.empty() && this->m_selectedHash != nullptr) - this->m_newHashName = hex::format("{} {}", LangEntry(this->m_selectedHash->getUnlocalizedName()), static_cast("hex.builtin.view.hashes.hash"_lang)); - - if (ImGui::BeginChild("##settings", ImVec2(ImGui::GetContentRegionAvail().x, 200_scaled), true)) { - if (this->m_selectedHash != nullptr) { - auto startPos = ImGui::GetCursorPosY(); - this->m_selectedHash->draw(); - - // Check if no elements have been added - if (startPos == ImGui::GetCursorPosY()) { - ImGuiExt::TextFormattedCentered("hex.builtin.view.hashes.no_settings"_lang); - } + for (const auto &hash : hashes) { + if (ImGui::Selectable(LangEntry(hash->getUnlocalizedName()), this->m_selectedHash == hash.get())) { + this->m_selectedHash = hash.get(); + this->m_newHashName.clear(); } } - ImGui::EndChild(); + ImGui::EndCombo(); + } - ImGuiExt::InputTextIcon("##hash_name", ICON_VS_SYMBOL_KEY, this->m_newHashName); - ImGui::SameLine(); + if (this->m_newHashName.empty() && this->m_selectedHash != nullptr) + this->m_newHashName = hex::format("{} {}", LangEntry(this->m_selectedHash->getUnlocalizedName()), static_cast("hex.builtin.view.hashes.hash"_lang)); - ImGui::BeginDisabled(this->m_newHashName.empty() || this->m_selectedHash == nullptr); - if (ImGuiExt::IconButton(ICON_VS_ADD, ImGui::GetStyleColorVec4(ImGuiCol_Text))) { - if (this->m_selectedHash != nullptr) { - this->m_hashFunctions->push_back(this->m_selectedHash->create(this->m_newHashName)); - AchievementManager::unlockAchievement("hex.builtin.achievement.misc", "hex.builtin.achievement.misc.create_hash.name"); + if (ImGui::BeginChild("##settings", ImVec2(ImGui::GetContentRegionAvail().x, 200_scaled), true)) { + if (this->m_selectedHash != nullptr) { + auto startPos = ImGui::GetCursorPosY(); + this->m_selectedHash->draw(); + + // Check if no elements have been added + if (startPos == ImGui::GetCursorPosY()) { + ImGuiExt::TextFormattedCentered("hex.builtin.view.hashes.no_settings"_lang); } } - ImGui::EndDisabled(); - - ImGui::SameLine(); - ImGuiExt::HelpHover("hex.builtin.view.hashes.hover_info"_lang); - - if (ImGui::BeginTable("##hashes", 4, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollY)) { - ImGui::TableSetupColumn("hex.builtin.view.hashes.table.name"_lang); - ImGui::TableSetupColumn("hex.builtin.view.hashes.table.type"_lang); - ImGui::TableSetupColumn("hex.builtin.view.hashes.table.result"_lang, ImGuiTableColumnFlags_WidthStretch); - ImGui::TableSetupColumn("##buttons", ImGuiTableColumnFlags_WidthFixed, 50_scaled); - - ImGui::TableHeadersRow(); - - auto provider = ImHexApi::Provider::get(); - auto selection = ImHexApi::HexEditor::getSelection(); - - std::optional indexToRemove; - for (u32 i = 0; i < this->m_hashFunctions->size(); i++) { - auto &function = (*this->m_hashFunctions)[i]; - - ImGui::PushID(i); - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - - ImGui::PushStyleColor(ImGuiCol_Header, 0x00); - ImGui::PushStyleColor(ImGuiCol_HeaderActive, 0x00); - ImGui::PushStyleColor(ImGuiCol_HeaderHovered, 0x00); - ImGui::Selectable(function.getName().c_str(), false); - ImGui::PopStyleColor(3); - - ImGui::TableNextColumn(); - ImGuiExt::TextFormatted("{}", LangEntry(function.getType()->getUnlocalizedName())); - - ImGui::TableNextColumn(); - std::string result; - if (provider != nullptr && selection.has_value()) - result = crypt::encode16(function.get(*selection, provider)); - else - result = "???"; - - ImGui::PushItemWidth(-1); - ImGui::InputText("##result", result, ImGuiInputTextFlags_ReadOnly); - ImGui::PopItemWidth(); - - ImGui::TableNextColumn(); - - if (ImGuiExt::IconButton(ICON_VS_OPEN_PREVIEW, ImGui::GetStyleColorVec4(ImGuiCol_Text))) { - PopupTextHash::open(function); - } - ImGui::SameLine(); - if (ImGuiExt::IconButton(ICON_VS_X, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed))) { - indexToRemove = i; - } - - ImGui::PopID(); - } - - if (indexToRemove.has_value()) { - this->m_hashFunctions->erase(this->m_hashFunctions->begin() + indexToRemove.value()); - } - - ImGui::EndTable(); - } } - ImGui::End(); + ImGui::EndChild(); + + + ImGuiExt::InputTextIcon("##hash_name", ICON_VS_SYMBOL_KEY, this->m_newHashName); + ImGui::SameLine(); + + ImGui::BeginDisabled(this->m_newHashName.empty() || this->m_selectedHash == nullptr); + if (ImGuiExt::IconButton(ICON_VS_ADD, ImGui::GetStyleColorVec4(ImGuiCol_Text))) { + if (this->m_selectedHash != nullptr) { + this->m_hashFunctions->push_back(this->m_selectedHash->create(this->m_newHashName)); + AchievementManager::unlockAchievement("hex.builtin.achievement.misc", "hex.builtin.achievement.misc.create_hash.name"); + } + } + ImGui::EndDisabled(); + + ImGui::SameLine(); + ImGuiExt::HelpHover("hex.builtin.view.hashes.hover_info"_lang); + + if (ImGui::BeginTable("##hashes", 4, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollY)) { + ImGui::TableSetupColumn("hex.builtin.view.hashes.table.name"_lang); + ImGui::TableSetupColumn("hex.builtin.view.hashes.table.type"_lang); + ImGui::TableSetupColumn("hex.builtin.view.hashes.table.result"_lang, ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("##buttons", ImGuiTableColumnFlags_WidthFixed, 50_scaled); + + ImGui::TableHeadersRow(); + + auto provider = ImHexApi::Provider::get(); + auto selection = ImHexApi::HexEditor::getSelection(); + + std::optional indexToRemove; + for (u32 i = 0; i < this->m_hashFunctions->size(); i++) { + auto &function = (*this->m_hashFunctions)[i]; + + ImGui::PushID(i); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + + ImGui::PushStyleColor(ImGuiCol_Header, 0x00); + ImGui::PushStyleColor(ImGuiCol_HeaderActive, 0x00); + ImGui::PushStyleColor(ImGuiCol_HeaderHovered, 0x00); + ImGui::Selectable(function.getName().c_str(), false); + ImGui::PopStyleColor(3); + + ImGui::TableNextColumn(); + ImGuiExt::TextFormatted("{}", LangEntry(function.getType()->getUnlocalizedName())); + + ImGui::TableNextColumn(); + std::string result; + if (provider != nullptr && selection.has_value()) + result = crypt::encode16(function.get(*selection, provider)); + else + result = "???"; + + ImGui::PushItemWidth(-1); + ImGui::InputText("##result", result, ImGuiInputTextFlags_ReadOnly); + ImGui::PopItemWidth(); + + ImGui::TableNextColumn(); + + if (ImGuiExt::IconButton(ICON_VS_OPEN_PREVIEW, ImGui::GetStyleColorVec4(ImGuiCol_Text))) { + PopupTextHash::open(function); + } + ImGui::SameLine(); + if (ImGuiExt::IconButton(ICON_VS_X, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed))) { + indexToRemove = i; + } + + ImGui::PopID(); + } + + if (indexToRemove.has_value()) { + this->m_hashFunctions->erase(this->m_hashFunctions->begin() + indexToRemove.value()); + } + + ImGui::EndTable(); + } } bool ViewHashes::importHashes(prv::Provider *provider, const nlohmann::json &json) { diff --git a/plugins/builtin/source/content/views/view_hex_editor.cpp b/plugins/builtin/source/content/views/view_hex_editor.cpp index d9b665268..f714de18c 100644 --- a/plugins/builtin/source/content/views/view_hex_editor.cpp +++ b/plugins/builtin/source/content/views/view_hex_editor.cpp @@ -347,7 +347,7 @@ namespace hex::plugin::builtin { editor->closePopup(); } - View::confirmButtons("hex.builtin.common.set"_lang, "hex.builtin.common.cancel"_lang, + ImGuiExt::ConfirmButtons("hex.builtin.common.set"_lang, "hex.builtin.common.cancel"_lang, [&, this]{ setBaseAddress(this->m_baseAddress); editor->closePopup(); @@ -381,7 +381,7 @@ namespace hex::plugin::builtin { editor->closePopup(); } - View::confirmButtons("hex.builtin.common.set"_lang, "hex.builtin.common.cancel"_lang, + ImGuiExt::ConfirmButtons("hex.builtin.common.set"_lang, "hex.builtin.common.cancel"_lang, [&, this]{ setPageSize(this->m_pageSize); editor->closePopup(); @@ -419,7 +419,7 @@ namespace hex::plugin::builtin { editor->closePopup(); } - View::confirmButtons("hex.builtin.common.set"_lang, "hex.builtin.common.cancel"_lang, + ImGuiExt::ConfirmButtons("hex.builtin.common.set"_lang, "hex.builtin.common.cancel"_lang, [&, this]{ this->resize(this->m_size); editor->closePopup(); @@ -449,7 +449,7 @@ namespace hex::plugin::builtin { ImGuiExt::InputHexadecimal("hex.builtin.common.address"_lang, &this->m_address); ImGuiExt::InputHexadecimal("hex.builtin.common.size"_lang, &this->m_size); - View::confirmButtons("hex.builtin.common.set"_lang, "hex.builtin.common.cancel"_lang, + ImGuiExt::ConfirmButtons("hex.builtin.common.set"_lang, "hex.builtin.common.cancel"_lang, [&, this]{ insert(this->m_address, this->m_size); editor->closePopup(); @@ -480,7 +480,7 @@ namespace hex::plugin::builtin { ImGuiExt::InputHexadecimal("hex.builtin.common.address"_lang, &this->m_address); ImGuiExt::InputHexadecimal("hex.builtin.common.size"_lang, &this->m_size); - View::confirmButtons("hex.builtin.common.set"_lang, "hex.builtin.common.cancel"_lang, + ImGuiExt::ConfirmButtons("hex.builtin.common.set"_lang, "hex.builtin.common.cancel"_lang, [&, this]{ remove(this->m_address, this->m_size); editor->closePopup(); @@ -515,7 +515,7 @@ namespace hex::plugin::builtin { ImGuiExt::InputTextIcon("hex.builtin.common.bytes"_lang, ICON_VS_SYMBOL_NAMESPACE, this->m_input); - View::confirmButtons("hex.builtin.common.set"_lang, "hex.builtin.common.cancel"_lang, + ImGuiExt::ConfirmButtons("hex.builtin.common.set"_lang, "hex.builtin.common.cancel"_lang, [&, this] { fill(this->m_address, this->m_size, this->m_input); editor->closePopup(); @@ -552,7 +552,7 @@ namespace hex::plugin::builtin { /* Hex Editor */ - ViewHexEditor::ViewHexEditor() : View("hex.builtin.view.hex_editor.name") { + ViewHexEditor::ViewHexEditor() : View::Window("hex.builtin.view.hex_editor.name") { this->m_hexEditor.setForegroundHighlightCallback([this](u64 address, const u8 *data, size_t size) -> std::optional { if (auto highlight = this->m_foregroundHighlights->find(address); highlight != this->m_foregroundHighlights->end()) return highlight->second; @@ -675,14 +675,11 @@ 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.setProvider(ImHexApi::Provider::get()); + this->m_hexEditor.setProvider(ImHexApi::Provider::get()); - this->m_hexEditor.draw(); + this->m_hexEditor.draw(); - this->drawPopup(); - } - ImGui::End(); + this->drawPopup(); } static void save() { diff --git a/plugins/builtin/source/content/views/view_information.cpp b/plugins/builtin/source/content/views/view_information.cpp index a1d5fbdcb..cb10e7f1e 100644 --- a/plugins/builtin/source/content/views/view_information.cpp +++ b/plugins/builtin/source/content/views/view_information.cpp @@ -20,7 +20,7 @@ namespace hex::plugin::builtin { using namespace hex::literals; - ViewInformation::ViewInformation() : View("hex.builtin.view.information.name") { + ViewInformation::ViewInformation() : View::Window("hex.builtin.view.information.name") { EventManager::subscribe(this, [this] { this->m_dataValid = false; this->m_plainTextCharacterPercentage = -1.0; @@ -136,232 +136,229 @@ namespace hex::plugin::builtin { } void ViewInformation::drawContent() { - if (ImGui::Begin(View::toWindowName("hex.builtin.view.information.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) { - if (ImGui::BeginChild("##scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav)) { + if (ImGui::BeginChild("##scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav)) { - auto provider = ImHexApi::Provider::get(); - if (ImHexApi::Provider::isValid() && provider->isReadable()) { - ImGui::BeginDisabled(this->m_analyzerTask.isRunning()); - { - ImGuiExt::Header("hex.builtin.common.settings"_lang, true); + auto provider = ImHexApi::Provider::get(); + if (ImHexApi::Provider::isValid() && provider->isReadable()) { + ImGui::BeginDisabled(this->m_analyzerTask.isRunning()); + { + ImGuiExt::Header("hex.builtin.common.settings"_lang, true); - ui::regionSelectionPicker(&this->m_analysisRegion, provider, &this->m_selectionType, false); - ImGui::NewLine(); + ui::regionSelectionPicker(&this->m_analysisRegion, provider, &this->m_selectionType, false); + ImGui::NewLine(); - ImGui::InputInt("hex.builtin.view.information.block_size"_lang, &this->m_inputChunkSize, ImGuiInputTextFlags_CharsDecimal); + ImGui::InputInt("hex.builtin.view.information.block_size"_lang, &this->m_inputChunkSize, ImGuiInputTextFlags_CharsDecimal); - if (ImGui::Button("hex.builtin.view.information.analyze"_lang, ImVec2(ImGui::GetContentRegionAvail().x, 0))) - this->analyze(); - } - ImGui::EndDisabled(); + if (ImGui::Button("hex.builtin.view.information.analyze"_lang, ImVec2(ImGui::GetContentRegionAvail().x, 0))) + this->analyze(); + } + ImGui::EndDisabled(); - if (this->m_analyzerTask.isRunning()) { - ImGuiExt::TextSpinner("hex.builtin.view.information.analyzing"_lang); - } else { - ImGui::NewLine(); + if (this->m_analyzerTask.isRunning()) { + ImGuiExt::TextSpinner("hex.builtin.view.information.analyzing"_lang); + } else { + ImGui::NewLine(); + } + + if (!this->m_analyzerTask.isRunning() && this->m_dataValid) { + + // Provider information + ImGuiExt::Header("hex.builtin.view.information.provider_information"_lang, true); + + if (ImGui::BeginTable("information", 2, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoKeepColumnsVisible)) { + ImGui::TableSetupColumn("type"); + ImGui::TableSetupColumn("value", ImGuiTableColumnFlags_WidthStretch); + + ImGui::TableNextRow(); + + for (auto &[name, value] : provider->getDataDescription()) { + ImGui::TableNextColumn(); + ImGuiExt::TextFormatted("{}", name); + ImGui::TableNextColumn(); + ImGuiExt::TextFormattedWrapped("{}", value); + } + + ImGui::TableNextColumn(); + ImGuiExt::TextFormatted("{}", "hex.builtin.view.information.region"_lang); + ImGui::TableNextColumn(); + ImGuiExt::TextFormatted("0x{:X} - 0x{:X}", this->m_analyzedRegion.getStartAddress(), this->m_analyzedRegion.getEndAddress()); + + ImGui::EndTable(); } - if (!this->m_analyzerTask.isRunning() && this->m_dataValid) { + // Magic information + if (!(this->m_dataDescription.empty() && this->m_dataMimeType.empty())) { + ImGuiExt::Header("hex.builtin.view.information.magic"_lang); - // Provider information - ImGuiExt::Header("hex.builtin.view.information.provider_information"_lang, true); - - if (ImGui::BeginTable("information", 2, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoKeepColumnsVisible)) { + if (ImGui::BeginTable("magic", 2, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg)) { ImGui::TableSetupColumn("type"); ImGui::TableSetupColumn("value", ImGuiTableColumnFlags_WidthStretch); ImGui::TableNextRow(); - for (auto &[name, value] : provider->getDataDescription()) { + if (!this->m_dataDescription.empty()) { ImGui::TableNextColumn(); - ImGuiExt::TextFormatted("{}", name); + ImGui::TextUnformatted("hex.builtin.view.information.description"_lang); ImGui::TableNextColumn(); - ImGuiExt::TextFormattedWrapped("{}", value); + + if (this->m_dataDescription == "data") { + ImGuiExt::TextFormattedColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "{} ({})", "hex.builtin.view.information.octet_stream_text"_lang, this->m_dataDescription); + } else { + ImGuiExt::TextFormattedWrapped("{}", this->m_dataDescription); + } } - ImGui::TableNextColumn(); - ImGuiExt::TextFormatted("{}", "hex.builtin.view.information.region"_lang); - ImGui::TableNextColumn(); - ImGuiExt::TextFormatted("0x{:X} - 0x{:X}", this->m_analyzedRegion.getStartAddress(), this->m_analyzedRegion.getEndAddress()); + if (!this->m_dataMimeType.empty()) { + ImGui::TableNextColumn(); + ImGui::TextUnformatted("hex.builtin.view.information.mime"_lang); + ImGui::TableNextColumn(); + + if (this->m_dataMimeType == "application/octet-stream") { + ImGuiExt::TextFormattedColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "{} ({})", "hex.builtin.view.information.octet_stream_text"_lang, this->m_dataMimeType); + ImGui::SameLine(); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); + ImGuiExt::HelpHover("hex.builtin.view.information.octet_stream_warning"_lang); + ImGui::PopStyleVar(); + } else { + ImGuiExt::TextFormatted("{}", this->m_dataMimeType); + } + } ImGui::EndTable(); } - // Magic information - if (!(this->m_dataDescription.empty() && this->m_dataMimeType.empty())) { - ImGuiExt::Header("hex.builtin.view.information.magic"_lang); + ImGui::NewLine(); + } - if (ImGui::BeginTable("magic", 2, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg)) { - ImGui::TableSetupColumn("type"); - ImGui::TableSetupColumn("value", ImGuiTableColumnFlags_WidthStretch); + // Information analysis + if (this->m_analyzedRegion.getSize() > 0) { - ImGui::TableNextRow(); + ImGuiExt::Header("hex.builtin.view.information.info_analysis"_lang); - if (!this->m_dataDescription.empty()) { - ImGui::TableNextColumn(); - ImGui::TextUnformatted("hex.builtin.view.information.description"_lang); - ImGui::TableNextColumn(); + ImGui::PushStyleColor(ImGuiCol_FrameBg, ImGui::GetColorU32(ImGuiCol_WindowBg)); + ImPlot::PushStyleColor(ImPlotCol_FrameBg, ImGui::GetColorU32(ImGuiCol_WindowBg)); - if (this->m_dataDescription == "data") { - ImGuiExt::TextFormattedColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "{} ({})", "hex.builtin.view.information.octet_stream_text"_lang, this->m_dataDescription); - } else { - ImGuiExt::TextFormattedWrapped("{}", this->m_dataDescription); - } - } + // Display byte distribution analysis + ImGui::TextUnformatted("hex.builtin.view.information.distribution"_lang); + this->m_byteDistribution.draw( + ImVec2(-1, 0), + ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect + ); - if (!this->m_dataMimeType.empty()) { - ImGui::TableNextColumn(); - ImGui::TextUnformatted("hex.builtin.view.information.mime"_lang); - ImGui::TableNextColumn(); - - if (this->m_dataMimeType == "application/octet-stream") { - ImGuiExt::TextFormattedColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "{} ({})", "hex.builtin.view.information.octet_stream_text"_lang, this->m_dataMimeType); - ImGui::SameLine(); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); - ImGuiExt::HelpHover("hex.builtin.view.information.octet_stream_warning"_lang); - ImGui::PopStyleVar(); - } else { - ImGuiExt::TextFormatted("{}", this->m_dataMimeType); - } - } - - ImGui::EndTable(); - } - - ImGui::NewLine(); - } - - // Information analysis - if (this->m_analyzedRegion.getSize() > 0) { - - ImGuiExt::Header("hex.builtin.view.information.info_analysis"_lang); - - ImGui::PushStyleColor(ImGuiCol_FrameBg, ImGui::GetColorU32(ImGuiCol_WindowBg)); - ImPlot::PushStyleColor(ImPlotCol_FrameBg, ImGui::GetColorU32(ImGuiCol_WindowBg)); - - // Display byte distribution analysis - ImGui::TextUnformatted("hex.builtin.view.information.distribution"_lang); - this->m_byteDistribution.draw( - ImVec2(-1, 0), - ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect - ); - - // Display byte types distribution analysis - ImGui::TextUnformatted("hex.builtin.view.information.byte_types"_lang); - this->m_byteTypesDistribution.draw( - ImVec2(-1, 0), - ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect, - true - ); - - // Display chunk-based entropy analysis - ImGui::TextUnformatted("hex.builtin.view.information.entropy"_lang); - this->m_chunkBasedEntropy.draw( - ImVec2(-1, 0), - ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect, + // Display byte types distribution analysis + ImGui::TextUnformatted("hex.builtin.view.information.byte_types"_lang); + this->m_byteTypesDistribution.draw( + ImVec2(-1, 0), + ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect, true - ); + ); - ImPlot::PopStyleColor(); - ImGui::PopStyleColor(); + // Display chunk-based entropy analysis + ImGui::TextUnformatted("hex.builtin.view.information.entropy"_lang); + this->m_chunkBasedEntropy.draw( + ImVec2(-1, 0), + ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect, + true + ); - ImGui::NewLine(); - } - - // Entropy information - if (ImGui::BeginTable("entropy_info", 2, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg)) { - ImGui::TableSetupColumn("type"); - ImGui::TableSetupColumn("value", ImGuiTableColumnFlags_WidthStretch); - - ImGui::TableNextRow(); - - ImGui::TableNextColumn(); - ImGuiExt::TextFormatted("{}", "hex.builtin.view.information.block_size"_lang); - ImGui::TableNextColumn(); - ImGuiExt::TextFormatted("hex.builtin.view.information.block_size.desc"_lang, this->m_chunkBasedEntropy.getSize(), this->m_chunkBasedEntropy.getChunkSize()); - - ImGui::TableNextColumn(); - ImGuiExt::TextFormatted("{}", "hex.builtin.view.information.file_entropy"_lang); - ImGui::TableNextColumn(); - if (this->m_averageEntropy < 0) ImGui::TextUnformatted("???"); - else ImGuiExt::TextFormatted("{:.5f}", std::abs(this->m_averageEntropy)); - - ImGui::TableNextColumn(); - ImGuiExt::TextFormatted("{}", "hex.builtin.view.information.highest_entropy"_lang); - ImGui::TableNextColumn(); - ImGuiExt::TextFormatted("{:.5f} @", this->m_highestBlockEntropy); - ImGui::SameLine(); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); - if (ImGui::Button(hex::format("0x{:06X}", this->m_highestBlockEntropyAddress).c_str())) { - ImHexApi::HexEditor::setSelection(this->m_highestBlockEntropyAddress, this->m_inputChunkSize); - } - ImGui::PopStyleColor(); - ImGui::PopStyleVar(); - - ImGui::TableNextColumn(); - ImGuiExt::TextFormatted("{}", "hex.builtin.view.information.lowest_entropy"_lang); - ImGui::TableNextColumn(); - ImGuiExt::TextFormatted("{:.5f} @", this->m_lowestBlockEntropy); - ImGui::SameLine(); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); - if (ImGui::Button(hex::format("0x{:06X}", this->m_lowestBlockEntropyAddress).c_str())) { - ImHexApi::HexEditor::setSelection(this->m_lowestBlockEntropyAddress, this->m_inputChunkSize); - } - ImGui::PopStyleColor(); - ImGui::PopStyleVar(); - - ImGui::TableNextColumn(); - ImGuiExt::TextFormatted("{}", "hex.builtin.view.information.plain_text_percentage"_lang); - ImGui::TableNextColumn(); - if (this->m_plainTextCharacterPercentage < 0) ImGui::TextUnformatted("???"); - else ImGuiExt::TextFormatted("{:.2f}%", this->m_plainTextCharacterPercentage); - - ImGui::EndTable(); - } + ImPlot::PopStyleColor(); + ImGui::PopStyleColor(); ImGui::NewLine(); - - // General information - if (ImGui::BeginTable("info", 1, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg)) { - ImGui::TableSetupColumn("value", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableNextRow(); - - if (this->m_averageEntropy > 0.83 && this->m_highestBlockEntropy > 0.9) { - ImGui::TableNextColumn(); - ImGuiExt::TextFormattedColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "{}", "hex.builtin.view.information.encrypted"_lang); - } - - if (this->m_plainTextCharacterPercentage > 95) { - ImGui::TableNextColumn(); - ImGuiExt::TextFormattedColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "{}", "hex.builtin.view.information.plain_text"_lang); - } - - ImGui::EndTable(); - } - - ImGui::BeginGroup(); - { - ImGui::TextUnformatted("hex.builtin.view.information.digram"_lang); - this->m_digram.draw(scaled(ImVec2(300, 300))); - } - ImGui::EndGroup(); - - ImGui::SameLine(); - - ImGui::BeginGroup(); - { - ImGui::TextUnformatted("hex.builtin.view.information.layered_distribution"_lang); - this->m_layeredDistribution.draw(scaled(ImVec2(300, 300))); - } - ImGui::EndGroup(); } + + // Entropy information + if (ImGui::BeginTable("entropy_info", 2, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg)) { + ImGui::TableSetupColumn("type"); + ImGui::TableSetupColumn("value", ImGuiTableColumnFlags_WidthStretch); + + ImGui::TableNextRow(); + + ImGui::TableNextColumn(); + ImGuiExt::TextFormatted("{}", "hex.builtin.view.information.block_size"_lang); + ImGui::TableNextColumn(); + ImGuiExt::TextFormatted("hex.builtin.view.information.block_size.desc"_lang, this->m_chunkBasedEntropy.getSize(), this->m_chunkBasedEntropy.getChunkSize()); + + ImGui::TableNextColumn(); + ImGuiExt::TextFormatted("{}", "hex.builtin.view.information.file_entropy"_lang); + ImGui::TableNextColumn(); + if (this->m_averageEntropy < 0) ImGui::TextUnformatted("???"); + else ImGuiExt::TextFormatted("{:.5f}", std::abs(this->m_averageEntropy)); + + ImGui::TableNextColumn(); + ImGuiExt::TextFormatted("{}", "hex.builtin.view.information.highest_entropy"_lang); + ImGui::TableNextColumn(); + ImGuiExt::TextFormatted("{:.5f} @", this->m_highestBlockEntropy); + ImGui::SameLine(); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); + if (ImGui::Button(hex::format("0x{:06X}", this->m_highestBlockEntropyAddress).c_str())) { + ImHexApi::HexEditor::setSelection(this->m_highestBlockEntropyAddress, this->m_inputChunkSize); + } + ImGui::PopStyleColor(); + ImGui::PopStyleVar(); + + ImGui::TableNextColumn(); + ImGuiExt::TextFormatted("{}", "hex.builtin.view.information.lowest_entropy"_lang); + ImGui::TableNextColumn(); + ImGuiExt::TextFormatted("{:.5f} @", this->m_lowestBlockEntropy); + ImGui::SameLine(); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); + if (ImGui::Button(hex::format("0x{:06X}", this->m_lowestBlockEntropyAddress).c_str())) { + ImHexApi::HexEditor::setSelection(this->m_lowestBlockEntropyAddress, this->m_inputChunkSize); + } + ImGui::PopStyleColor(); + ImGui::PopStyleVar(); + + ImGui::TableNextColumn(); + ImGuiExt::TextFormatted("{}", "hex.builtin.view.information.plain_text_percentage"_lang); + ImGui::TableNextColumn(); + if (this->m_plainTextCharacterPercentage < 0) ImGui::TextUnformatted("???"); + else ImGuiExt::TextFormatted("{:.2f}%", this->m_plainTextCharacterPercentage); + + ImGui::EndTable(); + } + + ImGui::NewLine(); + + // General information + if (ImGui::BeginTable("info", 1, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg)) { + ImGui::TableSetupColumn("value", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableNextRow(); + + if (this->m_averageEntropy > 0.83 && this->m_highestBlockEntropy > 0.9) { + ImGui::TableNextColumn(); + ImGuiExt::TextFormattedColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "{}", "hex.builtin.view.information.encrypted"_lang); + } + + if (this->m_plainTextCharacterPercentage > 95) { + ImGui::TableNextColumn(); + ImGuiExt::TextFormattedColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "{}", "hex.builtin.view.information.plain_text"_lang); + } + + ImGui::EndTable(); + } + + ImGui::BeginGroup(); + { + ImGui::TextUnformatted("hex.builtin.view.information.digram"_lang); + this->m_digram.draw(scaled(ImVec2(300, 300))); + } + ImGui::EndGroup(); + + ImGui::SameLine(); + + ImGui::BeginGroup(); + { + ImGui::TextUnformatted("hex.builtin.view.information.layered_distribution"_lang); + this->m_layeredDistribution.draw(scaled(ImVec2(300, 300))); + } + ImGui::EndGroup(); } } - ImGui::EndChild(); } - ImGui::End(); + ImGui::EndChild(); } } diff --git a/plugins/builtin/source/content/views/view_logs.cpp b/plugins/builtin/source/content/views/view_logs.cpp index ffc0ff909..f4183e438 100644 --- a/plugins/builtin/source/content/views/view_logs.cpp +++ b/plugins/builtin/source/content/views/view_logs.cpp @@ -5,9 +5,8 @@ namespace hex::plugin::builtin { - ViewLogs::ViewLogs() : View("hex.builtin.view.logs.name") { + ViewLogs::ViewLogs() : View::Floating("hex.builtin.view.logs.name") { ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.extras", "hex.builtin.view.logs.name" }, 2500, Shortcut::None, [&, this] { - this->m_viewOpen = true; this->getWindowOpenState() = true; }); } @@ -41,48 +40,42 @@ namespace hex::plugin::builtin { } void ViewLogs::drawContent() { - if (ImGui::Begin(View::toWindowName("hex.builtin.view.logs.name").c_str(), &this->m_viewOpen, ImGuiWindowFlags_NoCollapse)) { + ImGui::Combo("hex.builtin.view.logs.log_level"_lang, &this->m_logLevel, "DEBUG\0INFO\0WARNING\0ERROR\0FATAL\0"); - ImGui::Combo("hex.builtin.view.logs.log_level"_lang, &this->m_logLevel, "DEBUG\0INFO\0WARNING\0ERROR\0FATAL\0"); + if (ImGui::BeginTable("##logs", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollY)) { + ImGui::TableSetupColumn("hex.builtin.view.logs.component"_lang, ImGuiTableColumnFlags_WidthFixed, 100_scaled); + ImGui::TableSetupColumn("hex.builtin.view.logs.message"_lang); + ImGui::TableSetupScrollFreeze(0, 1); - if (ImGui::BeginTable("##logs", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollY)) { - ImGui::TableSetupColumn("hex.builtin.view.logs.component"_lang, ImGuiTableColumnFlags_WidthFixed, 100_scaled); - ImGui::TableSetupColumn("hex.builtin.view.logs.message"_lang); - ImGui::TableSetupScrollFreeze(0, 1); + ImGui::TableHeadersRow(); - ImGui::TableHeadersRow(); + const auto &logs = log::impl::getLogEntries(); + ImGuiListClipper clipper; + clipper.Begin(logs.size()); - const auto &logs = log::impl::getLogEntries(); - ImGuiListClipper clipper; - clipper.Begin(logs.size()); + while (clipper.Step()) { + auto end = 0; + for (size_t i = clipper.DisplayStart; i < std::min(clipper.DisplayEnd + end, logs.size()); i++) { + const auto &log = logs[logs.size() - 1 - i]; - while (clipper.Step()) { - auto end = 0; - for (size_t i = clipper.DisplayStart; i < std::min(clipper.DisplayEnd + end, logs.size()); i++) { - const auto &log = logs[logs.size() - 1 - i]; - - if (!shouldDisplay(log.level, this->m_logLevel)) { - end++; - clipper.ItemsCount--; - continue; - } - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::TextUnformatted(log.project.c_str()); - ImGui::TableNextColumn(); - ImGui::PushStyleColor(ImGuiCol_Text, getColor(log.level).Value); - ImGui::TextUnformatted(log.message.c_str()); - ImGui::PopStyleColor(); + if (!shouldDisplay(log.level, this->m_logLevel)) { + end++; + clipper.ItemsCount--; + continue; } + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::TextUnformatted(log.project.c_str()); + ImGui::TableNextColumn(); + ImGui::PushStyleColor(ImGuiCol_Text, getColor(log.level).Value); + ImGui::TextUnformatted(log.message.c_str()); + ImGui::PopStyleColor(); } - - ImGui::EndTable(); } - } - ImGui::End(); - this->getWindowOpenState() = this->m_viewOpen; + ImGui::EndTable(); + } } } \ No newline at end of file diff --git a/plugins/builtin/source/content/views/view_patches.cpp b/plugins/builtin/source/content/views/view_patches.cpp index 4e3d0690f..1cb9018ea 100644 --- a/plugins/builtin/source/content/views/view_patches.cpp +++ b/plugins/builtin/source/content/views/view_patches.cpp @@ -11,7 +11,7 @@ using namespace std::literals::string_literals; namespace hex::plugin::builtin { - ViewPatches::ViewPatches() : View("hex.builtin.view.patches.name") { + ViewPatches::ViewPatches() : View::Window("hex.builtin.view.patches.name") { ProjectFile::registerPerProviderHandler({ .basePath = "patches.json", @@ -54,74 +54,71 @@ namespace hex::plugin::builtin { } void ViewPatches::drawContent() { - if (ImGui::Begin(View::toWindowName("hex.builtin.view.patches.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) { - auto provider = ImHexApi::Provider::get(); + auto provider = ImHexApi::Provider::get(); - if (ImHexApi::Provider::isValid() && provider->isReadable()) { + if (ImHexApi::Provider::isValid() && provider->isReadable()) { - if (ImGui::BeginTable("##patchesTable", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) { - ImGui::TableSetupScrollFreeze(0, 1); - ImGui::TableSetupColumn("hex.builtin.view.patches.offset"_lang); - ImGui::TableSetupColumn("hex.builtin.view.patches.orig"_lang); - ImGui::TableSetupColumn("hex.builtin.view.patches.patch"_lang); + if (ImGui::BeginTable("##patchesTable", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) { + ImGui::TableSetupScrollFreeze(0, 1); + ImGui::TableSetupColumn("hex.builtin.view.patches.offset"_lang); + ImGui::TableSetupColumn("hex.builtin.view.patches.orig"_lang); + ImGui::TableSetupColumn("hex.builtin.view.patches.patch"_lang); - ImGui::TableHeadersRow(); + ImGui::TableHeadersRow(); - auto &patches = provider->getPatches(); - u32 index = 0; + auto &patches = provider->getPatches(); + u32 index = 0; - ImGuiListClipper clipper; + ImGuiListClipper clipper; - clipper.Begin(patches.size()); - while (clipper.Step()) { - auto iter = patches.begin(); - for (auto i = 0; i < clipper.DisplayStart; i++) - ++iter; + clipper.Begin(patches.size()); + while (clipper.Step()) { + auto iter = patches.begin(); + for (auto i = 0; i < clipper.DisplayStart; i++) + ++iter; - for (auto i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { - const auto &[address, patch] = *iter; + for (auto i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { + const auto &[address, patch] = *iter; - ImGui::TableNextRow(); - ImGui::TableNextColumn(); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); - if (ImGui::Selectable(("##patchLine" + std::to_string(index)).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) { - ImHexApi::HexEditor::setSelection(address, 1); - } - if (ImGui::IsMouseReleased(1) && ImGui::IsItemHovered()) { - ImGui::OpenPopup("PatchContextMenu"); - this->m_selectedPatch = address; - } - ImGui::SameLine(); - ImGuiExt::TextFormatted("0x{0:08X}", address); - - ImGui::TableNextColumn(); - u8 previousValue = 0x00; - provider->readRaw(address, &previousValue, sizeof(u8)); - ImGuiExt::TextFormatted("0x{0:02X}", previousValue); - - ImGui::TableNextColumn(); - ImGuiExt::TextFormatted("0x{0:02X}", patch); - index += 1; - - iter++; + if (ImGui::Selectable(("##patchLine" + std::to_string(index)).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) { + ImHexApi::HexEditor::setSelection(address, 1); } - } - - if (ImGui::BeginPopup("PatchContextMenu")) { - if (ImGui::MenuItem("hex.builtin.view.patches.remove"_lang)) { - patches.erase(this->m_selectedPatch); + if (ImGui::IsMouseReleased(1) && ImGui::IsItemHovered()) { + ImGui::OpenPopup("PatchContextMenu"); + this->m_selectedPatch = address; } - ImGui::EndPopup(); - } + ImGui::SameLine(); + ImGuiExt::TextFormatted("0x{0:08X}", address); - ImGui::EndTable(); + ImGui::TableNextColumn(); + u8 previousValue = 0x00; + provider->readRaw(address, &previousValue, sizeof(u8)); + ImGuiExt::TextFormatted("0x{0:02X}", previousValue); + + ImGui::TableNextColumn(); + ImGuiExt::TextFormatted("0x{0:02X}", patch); + index += 1; + + iter++; + } } + + if (ImGui::BeginPopup("PatchContextMenu")) { + if (ImGui::MenuItem("hex.builtin.view.patches.remove"_lang)) { + patches.erase(this->m_selectedPatch); + } + ImGui::EndPopup(); + } + + ImGui::EndTable(); } } - ImGui::End(); } - void ViewPatches::drawAlwaysVisible() { + void ViewPatches::drawAlwaysVisibleContent() { if (auto provider = ImHexApi::Provider::get(); provider != nullptr) { const auto &patches = provider->getPatches(); if (this->m_numPatches.get(provider) != patches.size()) { diff --git a/plugins/builtin/source/content/views/view_pattern_data.cpp b/plugins/builtin/source/content/views/view_pattern_data.cpp index 005cd8d3b..2ddb7a6e8 100644 --- a/plugins/builtin/source/content/views/view_pattern_data.cpp +++ b/plugins/builtin/source/content/views/view_pattern_data.cpp @@ -8,7 +8,7 @@ namespace hex::plugin::builtin { - ViewPatternData::ViewPatternData() : View("hex.builtin.view.pattern_data.name") { + ViewPatternData::ViewPatternData() : View::Window("hex.builtin.view.pattern_data.name") { this->m_patternDrawer = std::make_unique(); // Handle tree style setting changes @@ -42,22 +42,19 @@ namespace hex::plugin::builtin { } void ViewPatternData::drawContent() { - if (ImGui::Begin(View::toWindowName("hex.builtin.view.pattern_data.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) { - // Draw the pattern tree if the provider is valid - if (ImHexApi::Provider::isValid()) { - // Make sure the runtime has finished evaluating and produced valid patterns - auto &runtime = ContentRegistry::PatternLanguage::getRuntime(); - if (!runtime.arePatternsValid()) { - this->m_patternDrawer->draw({ }); - } else { - // If the runtime has finished evaluating, draw the patterns - if (TRY_LOCK(ContentRegistry::PatternLanguage::getRuntimeLock())) { - this->m_patternDrawer->draw(runtime.getPatterns(), &runtime); - } + // Draw the pattern tree if the provider is valid + if (ImHexApi::Provider::isValid()) { + // Make sure the runtime has finished evaluating and produced valid patterns + auto &runtime = ContentRegistry::PatternLanguage::getRuntime(); + if (!runtime.arePatternsValid()) { + this->m_patternDrawer->draw({ }); + } else { + // If the runtime has finished evaluating, draw the patterns + if (TRY_LOCK(ContentRegistry::PatternLanguage::getRuntimeLock())) { + this->m_patternDrawer->draw(runtime.getPatterns(), &runtime); } } } - ImGui::End(); } } diff --git a/plugins/builtin/source/content/views/view_pattern_editor.cpp b/plugins/builtin/source/content/views/view_pattern_editor.cpp index 764719410..849400569 100644 --- a/plugins/builtin/source/content/views/view_pattern_editor.cpp +++ b/plugins/builtin/source/content/views/view_pattern_editor.cpp @@ -122,7 +122,7 @@ namespace hex::plugin::builtin { return langDef; } - ViewPatternEditor::ViewPatternEditor() : View("hex.builtin.view.pattern_editor.name") { + ViewPatternEditor::ViewPatternEditor() : View::Window("hex.builtin.view.pattern_editor.name") { this->m_parserRuntime = std::make_unique(); ContentRegistry::PatternLanguage::configureRuntime(*this->m_parserRuntime, nullptr); @@ -148,202 +148,199 @@ namespace hex::plugin::builtin { } void ViewPatternEditor::drawContent() { - if (ImGui::Begin(View::toWindowName("hex.builtin.view.pattern_editor.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_None | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) { - auto provider = ImHexApi::Provider::get(); + auto provider = ImHexApi::Provider::get(); - if (ImHexApi::Provider::isValid() && provider->isAvailable()) { - static float height = 0; - static bool dragging = false; + if (ImHexApi::Provider::isValid() && provider->isAvailable()) { + static float height = 0; + static bool dragging = false; - auto availableSize = ImGui::GetContentRegionAvail(); - auto textEditorSize = availableSize; - textEditorSize.y *= 3.5 / 5.0; - textEditorSize.y -= ImGui::GetTextLineHeightWithSpacing(); - textEditorSize.y += height; + auto availableSize = ImGui::GetContentRegionAvail(); + auto textEditorSize = availableSize; + textEditorSize.y *= 3.5 / 5.0; + textEditorSize.y -= ImGui::GetTextLineHeightWithSpacing(); + textEditorSize.y += height; - if (availableSize.y > 1) - textEditorSize.y = std::clamp(textEditorSize.y, 1.0F, availableSize.y - ImGui::GetTextLineHeightWithSpacing() * 3); + if (availableSize.y > 1) + textEditorSize.y = std::clamp(textEditorSize.y, 1.0F, availableSize.y - ImGui::GetTextLineHeightWithSpacing() * 3); - this->m_textEditor.Render("hex.builtin.view.pattern_editor.name"_lang, textEditorSize, true); + this->m_textEditor.Render("hex.builtin.view.pattern_editor.name"_lang, textEditorSize, true); - ImGui::Button("##settings_drag_bar", ImVec2(ImGui::GetContentRegionAvail().x, 2_scaled)); - if (ImGui::IsMouseDragging(ImGuiMouseButton_Left, 0)) { - if (ImGui::IsItemHovered()) - dragging = true; - } else { - dragging = false; + ImGui::Button("##settings_drag_bar", ImVec2(ImGui::GetContentRegionAvail().x, 2_scaled)); + if (ImGui::IsMouseDragging(ImGuiMouseButton_Left, 0)) { + if (ImGui::IsItemHovered()) + dragging = true; + } else { + dragging = false; + } + if (ImGui::IsItemHovered()) { + ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS); + } + + if (dragging) { + height += ImGui::GetMouseDragDelta(ImGuiMouseButton_Left, 0).y; + ImGui::ResetMouseDragDelta(ImGuiMouseButton_Left); + } + + auto settingsSize = ImGui::GetContentRegionAvail(); + settingsSize.y -= ImGui::GetTextLineHeightWithSpacing() * 2.5F; + + if (ImGui::BeginTabBar("##settings")) { + if (ImGui::BeginTabItem("hex.builtin.view.pattern_editor.console"_lang)) { + this->drawConsole(settingsSize); + ImGui::EndTabItem(); } - if (ImGui::IsItemHovered()) { - ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS); + if (ImGui::BeginTabItem("hex.builtin.view.pattern_editor.env_vars"_lang)) { + this->drawEnvVars(settingsSize, *this->m_envVarEntries); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("hex.builtin.view.pattern_editor.settings"_lang)) { + this->drawVariableSettings(settingsSize, *this->m_patternVariables); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("hex.builtin.view.pattern_editor.sections"_lang)) { + this->drawSectionSelector(settingsSize, *this->m_sections); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("hex.builtin.view.pattern_editor.debugger"_lang)) { + this->drawDebugger(settingsSize); + ImGui::EndTabItem(); } - if (dragging) { - height += ImGui::GetMouseDragDelta(ImGuiMouseButton_Left, 0).y; - ImGui::ResetMouseDragDelta(ImGuiMouseButton_Left); - } + ImGui::EndTabBar(); + } - auto settingsSize = ImGui::GetContentRegionAvail(); - settingsSize.y -= ImGui::GetTextLineHeightWithSpacing() * 2.5F; + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1); - if (ImGui::BeginTabBar("##settings")) { - if (ImGui::BeginTabItem("hex.builtin.view.pattern_editor.console"_lang)) { - this->drawConsole(settingsSize); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("hex.builtin.view.pattern_editor.env_vars"_lang)) { - this->drawEnvVars(settingsSize, *this->m_envVarEntries); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("hex.builtin.view.pattern_editor.settings"_lang)) { - this->drawVariableSettings(settingsSize, *this->m_patternVariables); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("hex.builtin.view.pattern_editor.sections"_lang)) { - this->drawSectionSelector(settingsSize, *this->m_sections); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("hex.builtin.view.pattern_editor.debugger"_lang)) { - this->drawDebugger(settingsSize); - ImGui::EndTabItem(); - } - - ImGui::EndTabBar(); - } - - ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1); - - { - auto &runtime = ContentRegistry::PatternLanguage::getRuntime(); - if (runtime.isRunning()) { - if (this->m_breakpointHit) { - if (ImGuiExt::IconButton(ICON_VS_DEBUG_CONTINUE, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarYellow))) - this->m_breakpointHit = false; - ImGui::SameLine(); - if (ImGuiExt::IconButton(ICON_VS_DEBUG_STEP_INTO, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarYellow))) { - runtime.getInternals().evaluator->pauseNextLine(); - this->m_breakpointHit = false; - } - } else { - if (ImGuiExt::IconButton(ICON_VS_DEBUG_STOP, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed))) - runtime.abort(); + { + auto &runtime = ContentRegistry::PatternLanguage::getRuntime(); + if (runtime.isRunning()) { + if (this->m_breakpointHit) { + if (ImGuiExt::IconButton(ICON_VS_DEBUG_CONTINUE, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarYellow))) + this->m_breakpointHit = false; + ImGui::SameLine(); + if (ImGuiExt::IconButton(ICON_VS_DEBUG_STEP_INTO, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarYellow))) { + runtime.getInternals().evaluator->pauseNextLine(); + this->m_breakpointHit = false; } } else { - if (ImGuiExt::IconButton(ICON_VS_DEBUG_START, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarGreen)) || this->m_triggerEvaluation) { - this->m_triggerEvaluation = false; - this->evaluatePattern(this->m_textEditor.GetText(), provider); - } + if (ImGuiExt::IconButton(ICON_VS_DEBUG_STOP, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed))) + runtime.abort(); } + } else { + if (ImGuiExt::IconButton(ICON_VS_DEBUG_START, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarGreen)) || this->m_triggerEvaluation) { + this->m_triggerEvaluation = false; + this->evaluatePattern(this->m_textEditor.GetText(), provider); + } + } - ImGui::PopStyleVar(); + ImGui::PopStyleVar(); + + ImGui::SameLine(); + if (this->m_runningEvaluators > 0) { + if (this->m_breakpointHit) { + ImGuiExt::TextFormatted("hex.builtin.view.pattern_editor.breakpoint_hit"_lang, runtime.getInternals().evaluator->getPauseLine().value_or(0)); + } else { + ImGuiExt::TextSpinner("hex.builtin.view.pattern_editor.evaluating"_lang); + } ImGui::SameLine(); - if (this->m_runningEvaluators > 0) { - if (this->m_breakpointHit) { - ImGuiExt::TextFormatted("hex.builtin.view.pattern_editor.breakpoint_hit"_lang, runtime.getInternals().evaluator->getPauseLine().value_or(0)); - } else { - ImGuiExt::TextSpinner("hex.builtin.view.pattern_editor.evaluating"_lang); - } + ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); + ImGui::SameLine(); - ImGui::SameLine(); - ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); - ImGui::SameLine(); + const auto padding = ImGui::GetStyle().FramePadding.y; - const auto padding = ImGui::GetStyle().FramePadding.y; + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2()); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2()); + if (ImGui::BeginChild("##read_cursor", ImGui::GetContentRegionAvail() + ImVec2(0, padding), true)) { + const auto startPos = ImGui::GetCursorScreenPos(); + const auto size = ImGui::GetContentRegionAvail(); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2()); - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2()); - if (ImGui::BeginChild("##read_cursor", ImGui::GetContentRegionAvail() + ImVec2(0, padding), true)) { - const auto startPos = ImGui::GetCursorScreenPos(); - const auto size = ImGui::GetContentRegionAvail(); + const auto dataBaseAddress = runtime.getInternals().evaluator->getDataBaseAddress(); + const auto dataSize = runtime.getInternals().evaluator->getDataSize(); - const auto dataBaseAddress = runtime.getInternals().evaluator->getDataBaseAddress(); - const auto dataSize = runtime.getInternals().evaluator->getDataSize(); + const auto insertPos = [&, this](u64 address, u32 color) { + const auto progress = (address - dataBaseAddress) / float(dataSize); - const auto insertPos = [&, this](u64 address, u32 color) { - const auto progress = (address - dataBaseAddress) / float(dataSize); + this->m_accessHistory[this->m_accessHistoryIndex] = { progress, color }; + this->m_accessHistoryIndex = (this->m_accessHistoryIndex + 1) % this->m_accessHistory.size(); + }; - this->m_accessHistory[this->m_accessHistoryIndex] = { progress, color }; - this->m_accessHistoryIndex = (this->m_accessHistoryIndex + 1) % this->m_accessHistory.size(); - }; + insertPos(runtime.getLastReadAddress(), ImGuiExt::GetCustomColorU32(ImGuiCustomCol_ToolbarBlue)); + insertPos(runtime.getLastWriteAddress(), ImGuiExt::GetCustomColorU32(ImGuiCustomCol_ToolbarRed)); + insertPos(runtime.getLastPatternPlaceAddress(), ImGuiExt::GetCustomColorU32(ImGuiCustomCol_ToolbarGreen)); - insertPos(runtime.getLastReadAddress(), ImGuiExt::GetCustomColorU32(ImGuiCustomCol_ToolbarBlue)); - insertPos(runtime.getLastWriteAddress(), ImGuiExt::GetCustomColorU32(ImGuiCustomCol_ToolbarRed)); - insertPos(runtime.getLastPatternPlaceAddress(), ImGuiExt::GetCustomColorU32(ImGuiCustomCol_ToolbarGreen)); + auto drawList = ImGui::GetWindowDrawList(); + for (const auto &[progress, color] : this->m_accessHistory) { + if (progress <= 0) continue; - auto drawList = ImGui::GetWindowDrawList(); - for (const auto &[progress, color] : this->m_accessHistory) { - if (progress <= 0) continue; + const auto linePos = startPos + ImVec2(size.x * progress, 0); - const auto linePos = startPos + ImVec2(size.x * progress, 0); - - drawList->AddLine(linePos, linePos + ImVec2(0, size.y), color, 2_scaled); - } - } - ImGui::EndChild(); - ImGui::PopStyleVar(2); - - } else { - if (ImGui::Checkbox("hex.builtin.view.pattern_editor.auto"_lang, &this->m_runAutomatically)) { - if (this->m_runAutomatically) - this->m_hasUnevaluatedChanges = true; - } - - ImGui::SameLine(); - ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); - ImGui::SameLine(); - - if (auto max = runtime.getMaximumPatternCount(); max >= std::numeric_limits::max()) { - ImGuiExt::TextFormatted("{} / {}", - runtime.getCreatedPatternCount(), - ICON_FA_INFINITY); - } else { - ImGuiExt::TextFormatted("{} / {}", - runtime.getCreatedPatternCount(), - runtime.getMaximumPatternCount()); + drawList->AddLine(linePos, linePos + ImVec2(0, size.y), color, 2_scaled); } } - } - - if (this->m_textEditor.IsTextChanged()) { - this->m_hasUnevaluatedChanges = true; - ImHexApi::Provider::markDirty(); - } - - if (this->m_hasUnevaluatedChanges && this->m_runningEvaluators == 0 && this->m_runningParsers == 0) { - this->m_hasUnevaluatedChanges = false; - - auto code = this->m_textEditor.GetText(); - EventManager::post(code); - - TaskManager::createBackgroundTask("Pattern Parsing", [this, code, provider](auto &){ - this->parsePattern(code, provider); + ImGui::EndChild(); + ImGui::PopStyleVar(2); + } else { + if (ImGui::Checkbox("hex.builtin.view.pattern_editor.auto"_lang, &this->m_runAutomatically)) { if (this->m_runAutomatically) - this->m_triggerAutoEvaluate = true; - }); - } - - if (this->m_triggerAutoEvaluate.exchange(false)) { - this->evaluatePattern(this->m_textEditor.GetText(), provider); - } - } - - if (this->m_dangerousFunctionCalled && !ImGui::IsPopupOpen(ImGuiID(0), ImGuiPopupFlags_AnyPopup)) { - PopupQuestion::open("hex.builtin.view.pattern_editor.dangerous_function.desc"_lang, - [this] { - this->m_dangerousFunctionsAllowed = DangerousFunctionPerms::Allow; - }, [this] { - this->m_dangerousFunctionsAllowed = DangerousFunctionPerms::Deny; + this->m_hasUnevaluatedChanges = true; } - ); - this->m_dangerousFunctionCalled = false; + ImGui::SameLine(); + ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); + ImGui::SameLine(); + + if (auto max = runtime.getMaximumPatternCount(); max >= std::numeric_limits::max()) { + ImGuiExt::TextFormatted("{} / {}", + runtime.getCreatedPatternCount(), + ICON_FA_INFINITY); + } else { + ImGuiExt::TextFormatted("{} / {}", + runtime.getCreatedPatternCount(), + runtime.getMaximumPatternCount()); + } + } } - View::discardNavigationRequests(); + if (this->m_textEditor.IsTextChanged()) { + this->m_hasUnevaluatedChanges = true; + ImHexApi::Provider::markDirty(); + } + + if (this->m_hasUnevaluatedChanges && this->m_runningEvaluators == 0 && this->m_runningParsers == 0) { + this->m_hasUnevaluatedChanges = false; + + auto code = this->m_textEditor.GetText(); + EventManager::post(code); + + TaskManager::createBackgroundTask("Pattern Parsing", [this, code, provider](auto &){ + this->parsePattern(code, provider); + + if (this->m_runAutomatically) + this->m_triggerAutoEvaluate = true; + }); + } + + if (this->m_triggerAutoEvaluate.exchange(false)) { + this->evaluatePattern(this->m_textEditor.GetText(), provider); + } } - ImGui::End(); + + if (this->m_dangerousFunctionCalled && !ImGui::IsPopupOpen(ImGuiID(0), ImGuiPopupFlags_AnyPopup)) { + PopupQuestion::open("hex.builtin.view.pattern_editor.dangerous_function.desc"_lang, + [this] { + this->m_dangerousFunctionsAllowed = DangerousFunctionPerms::Allow; + }, [this] { + this->m_dangerousFunctionsAllowed = DangerousFunctionPerms::Deny; + } + ); + + this->m_dangerousFunctionCalled = false; + } + + View::discardNavigationRequests(); } void ViewPatternEditor::drawConsole(ImVec2 size) { @@ -689,7 +686,7 @@ namespace hex::plugin::builtin { ImGui::EndChild(); } - void ViewPatternEditor::drawAlwaysVisible() { + void ViewPatternEditor::drawAlwaysVisibleContent() { auto provider = ImHexApi::Provider::get(); auto open = this->m_sectionWindowDrawer.contains(provider); diff --git a/plugins/builtin/source/content/views/view_provider_settings.cpp b/plugins/builtin/source/content/views/view_provider_settings.cpp index 03ac64251..2a0e293da 100644 --- a/plugins/builtin/source/content/views/view_provider_settings.cpp +++ b/plugins/builtin/source/content/views/view_provider_settings.cpp @@ -5,7 +5,7 @@ namespace hex::plugin::builtin { - ViewProviderSettings::ViewProviderSettings() : hex::View("hex.builtin.view.provider_settings.name") { + ViewProviderSettings::ViewProviderSettings() : View::Modal("hex.builtin.view.provider_settings.name") { EventManager::subscribe(this, [](const hex::prv::Provider *provider) { if (provider->hasLoadInterface() && !provider->shouldSkipLoadInterface()) EventManager::post(View::toWindowName("hex.builtin.view.provider_settings.load_popup")); @@ -29,48 +29,38 @@ namespace hex::plugin::builtin { } void ViewProviderSettings::drawContent() { + auto provider = hex::ImHexApi::Provider::get(); + if (provider != nullptr) { + bool settingsValid = provider->drawLoadInterface(); - } + ImGui::NewLine(); + ImGui::Separator(); - void ViewProviderSettings::drawAlwaysVisible() { - ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5F, 0.5F)); - if (ImGui::BeginPopupModal(View::toWindowName("hex.builtin.view.provider_settings.load_popup").c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { - - auto provider = hex::ImHexApi::Provider::get(); - if (provider != nullptr) { - bool settingsValid = provider->drawLoadInterface(); - - ImGui::NewLine(); - ImGui::Separator(); - - ImGui::BeginDisabled(!settingsValid); - if (ImGui::Button("hex.builtin.common.open"_lang)) { - if (provider->open()) { - EventManager::post(provider); - ImGui::CloseCurrentPopup(); - } - else { - ImGui::CloseCurrentPopup(); - auto errorMessage = provider->getErrorMessage(); - if (errorMessage.empty()) { - PopupError::open("hex.builtin.view.provider_settings.load_error"_lang); - } else { - PopupError::open(hex::format("hex.builtin.view.provider_settings.load_error_details"_lang, errorMessage)); - } - TaskManager::doLater([=] { ImHexApi::Provider::remove(provider); }); - } - } - ImGui::EndDisabled(); - - ImGui::SameLine(); - - if (ImGui::Button("hex.builtin.common.cancel"_lang)) { + ImGui::BeginDisabled(!settingsValid); + if (ImGui::Button("hex.builtin.common.open"_lang)) { + if (provider->open()) { + EventManager::post(provider); ImGui::CloseCurrentPopup(); + } + else { + ImGui::CloseCurrentPopup(); + auto errorMessage = provider->getErrorMessage(); + if (errorMessage.empty()) { + PopupError::open("hex.builtin.view.provider_settings.load_error"_lang); + } else { + PopupError::open(hex::format("hex.builtin.view.provider_settings.load_error_details"_lang, errorMessage)); + } TaskManager::doLater([=] { ImHexApi::Provider::remove(provider); }); } } + ImGui::EndDisabled(); - ImGui::EndPopup(); + ImGui::SameLine(); + + if (ImGui::Button("hex.builtin.common.cancel"_lang)) { + ImGui::CloseCurrentPopup(); + TaskManager::doLater([=] { ImHexApi::Provider::remove(provider); }); + } } } @@ -78,8 +68,4 @@ namespace hex::plugin::builtin { return false; } - bool ViewProviderSettings::isAvailable() const { - return false; - } - } diff --git a/plugins/builtin/source/content/views/view_settings.cpp b/plugins/builtin/source/content/views/view_settings.cpp index b702d304b..2f05c0dc3 100644 --- a/plugins/builtin/source/content/views/view_settings.cpp +++ b/plugins/builtin/source/content/views/view_settings.cpp @@ -10,12 +10,12 @@ namespace hex::plugin::builtin { - ViewSettings::ViewSettings() : View("hex.builtin.view.settings.name") { + ViewSettings::ViewSettings() : View::Floating("hex.builtin.view.settings.name") { // Handle window open requests EventManager::subscribe(this, [this](const std::string &name) { if (name == "Settings") { TaskManager::doLater([this] { - ImGui::OpenPopup(View::toWindowName("hex.builtin.view.settings.name").c_str()); + ImGui::OpenPopup(View::toWindowName(this->getUnlocalizedName()).c_str()); this->getWindowOpenState() = true; }); } @@ -24,7 +24,7 @@ namespace hex::plugin::builtin { // Add the settings menu item to the Extras menu ContentRegistry::Interface::addMenuItemSeparator({ "hex.builtin.menu.extras" }, 3000); ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.extras", "hex.builtin.view.settings.name"_lang }, 4000, Shortcut::None, [&, this] { - TaskManager::doLater([] { ImGui::OpenPopup(View::toWindowName("hex.builtin.view.settings.name").c_str()); }); + TaskManager::doLater([this] { ImGui::OpenPopup(View::toWindowName(this->getUnlocalizedName()).c_str()); }); this->getWindowOpenState() = true; }); } @@ -33,9 +33,8 @@ namespace hex::plugin::builtin { EventManager::unsubscribe(this); } - void ViewSettings::drawContent() { - - if (ImGui::BeginPopupModal(View::toWindowName("hex.builtin.view.settings.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoResize)) { + void ViewSettings::drawAlwaysVisibleContent() { + if (ImGui::BeginPopupModal(View::toWindowName(this->getUnlocalizedName()).c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoResize)) { if (ImGui::BeginTabBar("settings")) { auto &categories = ContentRegistry::Settings::impl::getSettings(); diff --git a/plugins/builtin/source/content/views/view_store.cpp b/plugins/builtin/source/content/views/view_store.cpp index f69d10c87..75ffcc8fa 100644 --- a/plugins/builtin/source/content/views/view_store.cpp +++ b/plugins/builtin/source/content/views/view_store.cpp @@ -27,11 +27,11 @@ namespace hex::plugin::builtin { using namespace std::literals::string_literals; using namespace std::literals::chrono_literals; - ViewStore::ViewStore() : View("hex.builtin.view.store.name") { + ViewStore::ViewStore() : View::Floating("hex.builtin.view.store.name") { ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.extras", "hex.builtin.view.store.name" }, 1000, Shortcut::None, [&, this] { if (this->m_requestStatus == RequestStatus::NotAttempted) this->refresh(); - TaskManager::doLater([] { ImGui::OpenPopup(View::toWindowName("hex.builtin.view.store.name").c_str()); }); + TaskManager::doLater([this] { ImGui::OpenPopup(View::toWindowName(this->getUnlocalizedName()).c_str()); }); this->getWindowOpenState() = true; }); @@ -278,16 +278,10 @@ namespace hex::plugin::builtin { } void ViewStore::drawContent() { - if (ImGui::BeginPopupModal(View::toWindowName("hex.builtin.view.store.name").c_str(), &this->getWindowOpenState())) { - if (this->m_requestStatus == RequestStatus::Failed) - ImGuiExt::TextFormattedColored(ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed), "hex.builtin.view.store.netfailed"_lang); - - this->drawStore(); + if (this->m_requestStatus == RequestStatus::Failed) + ImGuiExt::TextFormattedColored(ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed), "hex.builtin.view.store.netfailed"_lang); - ImGui::EndPopup(); - } else { - this->getWindowOpenState() = false; - } + this->drawStore(); } bool ViewStore::download(fs::ImHexPath pathType, const std::string &fileName, const std::string &url, bool update) { diff --git a/plugins/builtin/source/content/views/view_theme_manager.cpp b/plugins/builtin/source/content/views/view_theme_manager.cpp index f16bf3f56..5e1ea9069 100644 --- a/plugins/builtin/source/content/views/view_theme_manager.cpp +++ b/plugins/builtin/source/content/views/view_theme_manager.cpp @@ -7,129 +7,122 @@ namespace hex::plugin::builtin { - ViewThemeManager::ViewThemeManager() : View("hex.builtin.view.theme_manager.name") { + ViewThemeManager::ViewThemeManager() : View::Floating("hex.builtin.view.theme_manager.name") { ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.extras", "hex.builtin.view.theme_manager.name" }, 2000, Shortcut::None, [&, this] { - this->m_viewOpen = true; this->getWindowOpenState() = true; }); } void ViewThemeManager::drawContent() { - if (ImGui::Begin(View::toWindowName("hex.builtin.view.theme_manager.name").c_str(), &this->m_viewOpen, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoDocking)) { - ImGuiExt::Header("hex.builtin.view.theme_manager.colors"_lang, true); + ImGuiExt::Header("hex.builtin.view.theme_manager.colors"_lang, true); - // Draw theme handlers - ImGui::PushID(1); + // Draw theme handlers + ImGui::PushID(1); - // Loop over each theme handler - bool anyColorHovered = false; - for (auto &[name, handler] : ThemeManager::getThemeHandlers()) { - // Create a new collapsable header for each category - if (ImGui::CollapsingHeader(name.c_str())) { + // Loop over each theme handler + bool anyColorHovered = false; + for (auto &[name, handler] : ThemeManager::getThemeHandlers()) { + // Create a new collapsable header for each category + if (ImGui::CollapsingHeader(name.c_str())) { - // Loop over all the individual theme settings - for (auto &[colorName, colorId] : handler.colorMap) { - if (this->m_startingColor.has_value()) { - if (this->m_hoveredColorId == colorId && this->m_hoveredHandlerName == name) { - handler.setFunction(colorId, this->m_startingColor.value()); - } + // Loop over all the individual theme settings + for (auto &[colorName, colorId] : handler.colorMap) { + if (this->m_startingColor.has_value()) { + if (this->m_hoveredColorId == colorId && this->m_hoveredHandlerName == name) { + handler.setFunction(colorId, this->m_startingColor.value()); } + } - // Get the current color value - auto color = handler.getFunction(colorId); + // Get the current color value + auto color = handler.getFunction(colorId); - // Draw a color picker for the color - if (ImGui::ColorEdit4(colorName.c_str(), reinterpret_cast(&color.Value), ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_AlphaPreviewHalf)) { - // Update the color value - handler.setFunction(colorId, color); + // Draw a color picker for the color + if (ImGui::ColorEdit4(colorName.c_str(), reinterpret_cast(&color.Value), ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_AlphaPreviewHalf)) { + // Update the color value + handler.setFunction(colorId, color); + EventManager::post(); + } + + if (ImGui::IsItemHovered()) { + anyColorHovered = true; + + if (!this->m_hoveredColorId.has_value()) { + this->m_hoveredColorId = colorId; + this->m_startingColor = color; + this->m_hoveredHandlerName = name; + } + } + } + } + + if (this->m_hoveredHandlerName == name && this->m_startingColor.has_value() && this->m_hoveredColorId.has_value()) { + auto flashingColor = this->m_startingColor.value(); + + const float flashProgress = std::min(1.0F, (1.0F + sinf(ImGui::GetTime() * 6)) / 2.0F); + flashingColor.Value.x = std::lerp(flashingColor.Value.x / 2, 1.0F, flashProgress); + flashingColor.Value.y = std::lerp(flashingColor.Value.y / 2, 1.0F, flashProgress); + flashingColor.Value.z = flashingColor.Value.z / 2; + flashingColor.Value.w = 1.0F; + + handler.setFunction(*this->m_hoveredColorId, flashingColor); + + if (!anyColorHovered) { + handler.setFunction(this->m_hoveredColorId.value(), this->m_startingColor.value()); + this->m_startingColor.reset(); + this->m_hoveredColorId.reset(); + } + } + } + ImGui::PopID(); + + + ImGuiExt::Header("hex.builtin.view.theme_manager.styles"_lang); + + // Draw style handlers + ImGui::PushID(2); + + // Loop over each style handler + for (auto &[name, handler] : ThemeManager::getStyleHandlers()) { + // Create a new collapsable header for each category + if (ImGui::CollapsingHeader(name.c_str())) { + + // Loop over all the individual style settings + for (auto &[styleName, style] : handler.styleMap) { + // Get the current style value + auto &[value, min, max, needsScaling] = style; + + // Styles can either be floats or ImVec2s + // Determine which one it is and draw the appropriate slider + if (auto floatValue = std::get_if(&value); floatValue != nullptr) { + if (ImGui::SliderFloat(styleName.c_str(), *floatValue, min, max, "%.1f")) { EventManager::post(); } - - if (ImGui::IsItemHovered()) { - anyColorHovered = true; - - if (!this->m_hoveredColorId.has_value()) { - this->m_hoveredColorId = colorId; - this->m_startingColor = color; - this->m_hoveredHandlerName = name; - } - } - } - } - - if (this->m_hoveredHandlerName == name && this->m_startingColor.has_value() && this->m_hoveredColorId.has_value()) { - auto flashingColor = this->m_startingColor.value(); - - const float flashProgress = std::min(1.0F, (1.0F + sinf(ImGui::GetTime() * 6)) / 2.0F); - flashingColor.Value.x = std::lerp(flashingColor.Value.x / 2, 1.0F, flashProgress); - flashingColor.Value.y = std::lerp(flashingColor.Value.y / 2, 1.0F, flashProgress); - flashingColor.Value.z = flashingColor.Value.z / 2; - flashingColor.Value.w = 1.0F; - - handler.setFunction(*this->m_hoveredColorId, flashingColor); - - if (!anyColorHovered) { - handler.setFunction(this->m_hoveredColorId.value(), this->m_startingColor.value()); - this->m_startingColor.reset(); - this->m_hoveredColorId.reset(); - } - } - } - ImGui::PopID(); - - - ImGuiExt::Header("hex.builtin.view.theme_manager.styles"_lang); - - // Draw style handlers - ImGui::PushID(2); - - // Loop over each style handler - for (auto &[name, handler] : ThemeManager::getStyleHandlers()) { - // Create a new collapsable header for each category - if (ImGui::CollapsingHeader(name.c_str())) { - - // Loop over all the individual style settings - for (auto &[styleName, style] : handler.styleMap) { - // Get the current style value - auto &[value, min, max, needsScaling] = style; - - // Styles can either be floats or ImVec2s - // Determine which one it is and draw the appropriate slider - if (auto floatValue = std::get_if(&value); floatValue != nullptr) { - if (ImGui::SliderFloat(styleName.c_str(), *floatValue, min, max, "%.1f")) { - EventManager::post(); - } - } else if (auto vecValue = std::get_if(&value); vecValue != nullptr) { - if (ImGui::SliderFloat2(styleName.c_str(), &(*vecValue)->x, min, max, "%.1f")) { - EventManager::post(); - } + } else if (auto vecValue = std::get_if(&value); vecValue != nullptr) { + if (ImGui::SliderFloat2(styleName.c_str(), &(*vecValue)->x, min, max, "%.1f")) { + EventManager::post(); } } } } - ImGui::PopID(); - - // Draw export settings - ImGuiExt::Header("hex.builtin.view.theme_manager.export"_lang); - ImGuiExt::InputTextIcon("hex.builtin.view.theme_manager.export.name"_lang, ICON_VS_SYMBOL_KEY, this->m_themeName); - - // Draw the export buttons - if (ImGui::Button("hex.builtin.view.theme_manager.save_theme"_lang, ImVec2(ImGui::GetContentRegionAvail().x, 0))) { - fs::openFileBrowser(fs::DialogMode::Save, { { "ImHex Theme", "json" } }, [this](const std::fs::path &path){ - // Export the current theme as json - auto json = ThemeManager::exportCurrentTheme(this->m_themeName); - - // Write the json to the file - wolv::io::File outputFile(path, wolv::io::File::Mode::Create); - outputFile.writeString(json.dump(4)); - }); - } - } - ImGui::End(); + ImGui::PopID(); - this->getWindowOpenState() = this->m_viewOpen; + // Draw export settings + ImGuiExt::Header("hex.builtin.view.theme_manager.export"_lang); + ImGuiExt::InputTextIcon("hex.builtin.view.theme_manager.export.name"_lang, ICON_VS_SYMBOL_KEY, this->m_themeName); + + // Draw the export buttons + if (ImGui::Button("hex.builtin.view.theme_manager.save_theme"_lang, ImVec2(ImGui::GetContentRegionAvail().x, 0))) { + fs::openFileBrowser(fs::DialogMode::Save, { { "ImHex Theme", "json" } }, [this](const std::fs::path &path){ + // Export the current theme as json + auto json = ThemeManager::exportCurrentTheme(this->m_themeName); + + // Write the json to the file + wolv::io::File outputFile(path, wolv::io::File::Mode::Create); + outputFile.writeString(json.dump(4)); + }); + } } } \ No newline at end of file diff --git a/plugins/builtin/source/content/views/view_tools.cpp b/plugins/builtin/source/content/views/view_tools.cpp index e014df2a2..02808d343 100644 --- a/plugins/builtin/source/content/views/view_tools.cpp +++ b/plugins/builtin/source/content/views/view_tools.cpp @@ -5,49 +5,45 @@ namespace hex::plugin::builtin { - ViewTools::ViewTools() : View("hex.builtin.view.tools.name") { + ViewTools::ViewTools() : View::Window("hex.builtin.view.tools.name") { this->m_dragStartIterator = ContentRegistry::Tools::impl::getEntries().end(); } void ViewTools::drawContent() { auto &tools = ContentRegistry::Tools::impl::getEntries(); - if (ImGui::Begin(View::toWindowName("hex.builtin.view.tools.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) { + // Draw all tools + for (auto iter = tools.begin(); iter != tools.end(); ++iter) { + auto &[name, function, detached] = *iter; - // Draw all tools - for (auto iter = tools.begin(); iter != tools.end(); ++iter) { - auto &[name, function, detached] = *iter; + // If the tool has been detached from the main window, don't draw it here anymore + if (detached) continue; - // If the tool has been detached from the main window, don't draw it here anymore - if (detached) continue; + // Draw the tool + if (ImGui::CollapsingHeader(LangEntry(name))) { + function(); + ImGui::NewLine(); + } else { + // Handle dragging the tool out of the main window - // Draw the tool - if (ImGui::CollapsingHeader(LangEntry(name))) { - function(); - ImGui::NewLine(); - } else { - // Handle dragging the tool out of the main window + // If the user clicks on the header, start dragging the tool remember the iterator + if (ImGui::IsMouseClicked(0) && ImGui::IsItemActivated() && this->m_dragStartIterator == tools.end()) + this->m_dragStartIterator = iter; - // If the user clicks on the header, start dragging the tool remember the iterator - if (ImGui::IsMouseClicked(0) && ImGui::IsItemActivated() && this->m_dragStartIterator == tools.end()) - this->m_dragStartIterator = iter; - - // If the user released the mouse button, stop dragging the tool - if (!ImGui::IsMouseDown(0)) - this->m_dragStartIterator = tools.end(); - - // Detach the tool if the user dragged it out of the main window - if (!ImGui::IsItemHovered() && this->m_dragStartIterator == iter) { - detached = true; - } + // If the user released the mouse button, stop dragging the tool + if (!ImGui::IsMouseDown(0)) + this->m_dragStartIterator = tools.end(); + // Detach the tool if the user dragged it out of the main window + if (!ImGui::IsItemHovered() && this->m_dragStartIterator == iter) { + detached = true; } + } } - ImGui::End(); } - void ViewTools::drawAlwaysVisible() { + void ViewTools::drawAlwaysVisibleContent() { // Make sure the tool windows never get drawn over the welcome screen if (!ImHexApi::Provider::isValid()) return; diff --git a/plugins/builtin/source/content/views/view_yara.cpp b/plugins/builtin/source/content/views/view_yara.cpp index bba708880..ee259b7c8 100644 --- a/plugins/builtin/source/content/views/view_yara.cpp +++ b/plugins/builtin/source/content/views/view_yara.cpp @@ -27,7 +27,7 @@ namespace hex::plugin::builtin { using namespace wolv::literals; - ViewYara::ViewYara() : View("hex.builtin.view.yara.name") { + ViewYara::ViewYara() : View::Window("hex.builtin.view.yara.name") { yr_initialize(); ContentRegistry::FileHandler::add({ ".yar", ".yara" }, [](const auto &path) { @@ -99,153 +99,149 @@ namespace hex::plugin::builtin { } void ViewYara::drawContent() { - if (ImGui::Begin(View::toWindowName("hex.builtin.view.yara.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) { + ImGuiExt::Header("hex.builtin.view.yara.header.rules"_lang, true); - ImGuiExt::Header("hex.builtin.view.yara.header.rules"_lang, true); - - if (ImGui::BeginListBox("##rules", ImVec2(-FLT_MIN, ImGui::GetTextLineHeightWithSpacing() * 5))) { - for (u32 i = 0; i < this->m_rules->size(); i++) { - const bool selected = (this->m_selectedRule == i); - if (ImGui::Selectable(wolv::util::toUTF8String((*this->m_rules)[i].first).c_str(), selected)) { - this->m_selectedRule = i; - } + if (ImGui::BeginListBox("##rules", ImVec2(-FLT_MIN, ImGui::GetTextLineHeightWithSpacing() * 5))) { + for (u32 i = 0; i < this->m_rules->size(); i++) { + const bool selected = (this->m_selectedRule == i); + if (ImGui::Selectable(wolv::util::toUTF8String((*this->m_rules)[i].first).c_str(), selected)) { + this->m_selectedRule = i; + } + } + ImGui::EndListBox(); + } + + if (ImGuiExt::IconButton(ICON_VS_ADD, ImGui::GetStyleColorVec4(ImGuiCol_Text))) { + const auto basePaths = fs::getDefaultPaths(fs::ImHexPath::Yara); + std::vector paths; + for (const auto &path : basePaths) { + std::error_code error; + for (const auto &entry : std::fs::recursive_directory_iterator(path, error)) { + if (!entry.is_regular_file()) continue; + if (entry.path().extension() != ".yara" && entry.path().extension() != ".yar") continue; + + paths.push_back(entry); } - ImGui::EndListBox(); } - if (ImGuiExt::IconButton(ICON_VS_ADD, ImGui::GetStyleColorVec4(ImGuiCol_Text))) { - const auto basePaths = fs::getDefaultPaths(fs::ImHexPath::Yara); - std::vector paths; - for (const auto &path : basePaths) { - std::error_code error; - for (const auto &entry : std::fs::recursive_directory_iterator(path, error)) { - if (!entry.is_regular_file()) continue; - if (entry.path().extension() != ".yara" && entry.path().extension() != ".yar") continue; + PopupFileChooser::open(basePaths, paths, std::vector{ { "Yara File", "yara" }, { "Yara File", "yar" } }, true, + [&](const auto &path) { + this->m_rules->push_back({ path.filename(), path }); + }); + } - paths.push_back(entry); - } - } - - PopupFileChooser::open(basePaths, paths, std::vector{ { "Yara File", "yara" }, { "Yara File", "yar" } }, true, - [&](const auto &path) { - this->m_rules->push_back({ path.filename(), path }); - }); + ImGui::SameLine(); + if (ImGuiExt::IconButton(ICON_VS_REMOVE, ImGui::GetStyleColorVec4(ImGuiCol_Text))) { + if (this->m_selectedRule < this->m_rules->size()) { + this->m_rules->erase(this->m_rules->begin() + this->m_selectedRule); + this->m_selectedRule = std::min(this->m_selectedRule, u32(this->m_rules->size() - 1)); } + } + ImGui::NewLine(); + if (ImGui::Button("hex.builtin.view.yara.match"_lang)) this->applyRules(); + ImGui::SameLine(); + + if (this->m_matcherTask.isRunning()) { ImGui::SameLine(); - if (ImGuiExt::IconButton(ICON_VS_REMOVE, ImGui::GetStyleColorVec4(ImGuiCol_Text))) { - if (this->m_selectedRule < this->m_rules->size()) { - this->m_rules->erase(this->m_rules->begin() + this->m_selectedRule); - this->m_selectedRule = std::min(this->m_selectedRule, u32(this->m_rules->size() - 1)); - } + ImGuiExt::TextSpinner("hex.builtin.view.yara.matching"_lang); + } + + ImGuiExt::Header("hex.builtin.view.yara.header.matches"_lang); + + auto matchesTableSize = ImGui::GetContentRegionAvail(); + matchesTableSize.y *= 3.75 / 5.0; + matchesTableSize.y -= ImGui::GetTextLineHeightWithSpacing(); + + if (ImGui::BeginTable("matches", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY, matchesTableSize)) { + ImGui::TableSetupScrollFreeze(0, 1); + ImGui::TableSetupColumn("hex.builtin.view.yara.matches.identifier"_lang, ImGuiTableColumnFlags_PreferSortAscending, 0, ImGui::GetID("identifier")); + ImGui::TableSetupColumn("hex.builtin.view.yara.matches.variable"_lang, ImGuiTableColumnFlags_PreferSortAscending, 0, ImGui::GetID("variable")); + ImGui::TableSetupColumn("hex.builtin.common.address"_lang, ImGuiTableColumnFlags_PreferSortAscending, 0, ImGui::GetID("address")); + ImGui::TableSetupColumn("hex.builtin.common.size"_lang, ImGuiTableColumnFlags_PreferSortAscending, 0, ImGui::GetID("size")); + + ImGui::TableHeadersRow(); + + auto sortSpecs = ImGui::TableGetSortSpecs(); + if (!this->m_matches->empty() && (sortSpecs->SpecsDirty || this->m_sortedMatches->empty())) { + this->m_sortedMatches->clear(); + std::transform(this->m_matches->begin(), this->m_matches->end(), std::back_inserter(*this->m_sortedMatches), [](auto &match) { + return &match; + }); + + std::sort(this->m_sortedMatches->begin(), this->m_sortedMatches->end(), [&sortSpecs](const YaraMatch *left, const YaraMatch *right) -> bool { + if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("identifier")) + return left->identifier < right->identifier; + else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("variable")) + return left->variable < right->variable; + else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("address")) + return left->address < right->address; + else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("size")) + return left->size < right->size; + else + return false; + }); + + if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Descending) + std::reverse(this->m_sortedMatches->begin(), this->m_sortedMatches->end()); + + sortSpecs->SpecsDirty = false; } - ImGui::NewLine(); - if (ImGui::Button("hex.builtin.view.yara.match"_lang)) this->applyRules(); - ImGui::SameLine(); + if (!this->m_matcherTask.isRunning()) { + ImGuiListClipper clipper; + clipper.Begin(this->m_sortedMatches->size()); - if (this->m_matcherTask.isRunning()) { - ImGui::SameLine(); - ImGuiExt::TextSpinner("hex.builtin.view.yara.matching"_lang); - } + while (clipper.Step()) { + for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { + auto &[identifier, variableName, address, size, wholeDataMatch, highlightId, tooltipId] = *(*this->m_sortedMatches)[i]; + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::PushID(i); + if (ImGui::Selectable("match", false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap)) { + ImHexApi::HexEditor::setSelection(address, size); + } + ImGui::PopID(); + ImGui::SameLine(); + ImGui::TextUnformatted(identifier.c_str()); + ImGui::TableNextColumn(); + ImGui::TextUnformatted(variableName.c_str()); - ImGuiExt::Header("hex.builtin.view.yara.header.matches"_lang); - - auto matchesTableSize = ImGui::GetContentRegionAvail(); - matchesTableSize.y *= 3.75 / 5.0; - matchesTableSize.y -= ImGui::GetTextLineHeightWithSpacing(); - - if (ImGui::BeginTable("matches", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY, matchesTableSize)) { - ImGui::TableSetupScrollFreeze(0, 1); - ImGui::TableSetupColumn("hex.builtin.view.yara.matches.identifier"_lang, ImGuiTableColumnFlags_PreferSortAscending, 0, ImGui::GetID("identifier")); - ImGui::TableSetupColumn("hex.builtin.view.yara.matches.variable"_lang, ImGuiTableColumnFlags_PreferSortAscending, 0, ImGui::GetID("variable")); - ImGui::TableSetupColumn("hex.builtin.common.address"_lang, ImGuiTableColumnFlags_PreferSortAscending, 0, ImGui::GetID("address")); - ImGui::TableSetupColumn("hex.builtin.common.size"_lang, ImGuiTableColumnFlags_PreferSortAscending, 0, ImGui::GetID("size")); - - ImGui::TableHeadersRow(); - - auto sortSpecs = ImGui::TableGetSortSpecs(); - if (!this->m_matches->empty() && (sortSpecs->SpecsDirty || this->m_sortedMatches->empty())) { - this->m_sortedMatches->clear(); - std::transform(this->m_matches->begin(), this->m_matches->end(), std::back_inserter(*this->m_sortedMatches), [](auto &match) { - return &match; - }); - - std::sort(this->m_sortedMatches->begin(), this->m_sortedMatches->end(), [&sortSpecs](const YaraMatch *left, const YaraMatch *right) -> bool { - if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("identifier")) - return left->identifier < right->identifier; - else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("variable")) - return left->variable < right->variable; - else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("address")) - return left->address < right->address; - else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("size")) - return left->size < right->size; - else - return false; - }); - - if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Descending) - std::reverse(this->m_sortedMatches->begin(), this->m_sortedMatches->end()); - - sortSpecs->SpecsDirty = false; - } - - if (!this->m_matcherTask.isRunning()) { - ImGuiListClipper clipper; - clipper.Begin(this->m_sortedMatches->size()); - - while (clipper.Step()) { - for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { - auto &[identifier, variableName, address, size, wholeDataMatch, highlightId, tooltipId] = *(*this->m_sortedMatches)[i]; - ImGui::TableNextRow(); + if (!wholeDataMatch) { ImGui::TableNextColumn(); - ImGui::PushID(i); - if (ImGui::Selectable("match", false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap)) { - ImHexApi::HexEditor::setSelection(address, size); - } - ImGui::PopID(); - ImGui::SameLine(); - ImGui::TextUnformatted(identifier.c_str()); + ImGuiExt::TextFormatted("0x{0:X} : 0x{1:X}", address, address + size - 1); ImGui::TableNextColumn(); - ImGui::TextUnformatted(variableName.c_str()); - - if (!wholeDataMatch) { - ImGui::TableNextColumn(); - ImGuiExt::TextFormatted("0x{0:X} : 0x{1:X}", address, address + size - 1); - ImGui::TableNextColumn(); - ImGuiExt::TextFormatted("0x{0:X}", size); - } else { - ImGui::TableNextColumn(); - ImGuiExt::TextFormattedColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "{}", "hex.builtin.view.yara.whole_data"_lang); - ImGui::TableNextColumn(); - ImGui::TextUnformatted(""); - } + ImGuiExt::TextFormatted("0x{0:X}", size); + } else { + ImGui::TableNextColumn(); + ImGuiExt::TextFormattedColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "{}", "hex.builtin.view.yara.whole_data"_lang); + ImGui::TableNextColumn(); + ImGui::TextUnformatted(""); } } - - clipper.End(); } - ImGui::EndTable(); + clipper.End(); } - auto consoleSize = ImGui::GetContentRegionAvail(); - - if (ImGui::BeginChild("##console", consoleSize, true, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_HorizontalScrollbar)) { - ImGuiListClipper clipper; - - clipper.Begin(this->m_consoleMessages.size()); - while (clipper.Step()) - for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { - const auto &message = this->m_consoleMessages[i]; - - if (ImGui::Selectable(message.c_str())) - ImGui::SetClipboardText(message.c_str()); - } - } - ImGui::EndChild(); + ImGui::EndTable(); } - ImGui::End(); + + auto consoleSize = ImGui::GetContentRegionAvail(); + + if (ImGui::BeginChild("##console", consoleSize, true, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_HorizontalScrollbar)) { + ImGuiListClipper clipper; + + clipper.Begin(this->m_consoleMessages.size()); + while (clipper.Step()) + for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { + const auto &message = this->m_consoleMessages[i]; + + if (ImGui::Selectable(message.c_str())) + ImGui::SetClipboardText(message.c_str()); + } + } + ImGui::EndChild(); } void ViewYara::clearResult() { diff --git a/plugins/windows/include/views/view_tty_console.hpp b/plugins/windows/include/views/view_tty_console.hpp index 3d149a0c8..3eed893d6 100644 --- a/plugins/windows/include/views/view_tty_console.hpp +++ b/plugins/windows/include/views/view_tty_console.hpp @@ -11,7 +11,7 @@ namespace hex::plugin::windows { - class ViewTTYConsole : public View { + class ViewTTYConsole : public View::Window { public: ViewTTYConsole(); ~ViewTTYConsole() override = default; diff --git a/plugins/windows/source/views/view_tty_console.cpp b/plugins/windows/source/views/view_tty_console.cpp index 5720db31a..787823acb 100644 --- a/plugins/windows/source/views/view_tty_console.cpp +++ b/plugins/windows/source/views/view_tty_console.cpp @@ -9,7 +9,7 @@ namespace hex::plugin::windows { - ViewTTYConsole::ViewTTYConsole() : View("hex.windows.view.tty_console.name") { + ViewTTYConsole::ViewTTYConsole() : View::Window("hex.windows.view.tty_console.name") { this->m_comPorts = getAvailablePorts(); this->m_transmitDataBuffer.resize(0xFFF, 0x00); this->m_receiveDataBuffer.reserve(0xFFF); @@ -17,156 +17,152 @@ namespace hex::plugin::windows { } void ViewTTYConsole::drawContent() { - if (ImGui::Begin(View::toWindowName("hex.windows.view.tty_console.name").c_str(), &this->getWindowOpenState())) { + ImGuiExt::Header("hex.windows.view.tty_console.config"_lang, true); - ImGuiExt::Header("hex.windows.view.tty_console.config"_lang, true); + bool connected = this->m_portHandle != INVALID_HANDLE_VALUE; - bool connected = this->m_portHandle != INVALID_HANDLE_VALUE; + ImGui::PushItemFlag(ImGuiItemFlags_Disabled, connected); + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, connected ? 0.5F : 1.0F); - ImGui::PushItemFlag(ImGuiItemFlags_Disabled, connected); - ImGui::PushStyleVar(ImGuiStyleVar_Alpha, connected ? 0.5F : 1.0F); + ImGui::Combo( + "hex.windows.view.tty_console.port"_lang, &this->m_selectedPort, [](void *data, int idx) { + auto &ports = *static_cast> *>(data); - ImGui::Combo( - "hex.windows.view.tty_console.port"_lang, &this->m_selectedPort, [](void *data, int idx) { - auto &ports = *static_cast> *>(data); + return ports[idx].first.c_str(); + }, + &this->m_comPorts, + this->m_comPorts.size()); - return ports[idx].first.c_str(); - }, - &this->m_comPorts, - this->m_comPorts.size()); + ImGui::SameLine(); + if (ImGui::Button("hex.windows.view.tty_console.reload"_lang)) + this->m_comPorts = getAvailablePorts(); - ImGui::SameLine(); - if (ImGui::Button("hex.windows.view.tty_console.reload"_lang)) - this->m_comPorts = getAvailablePorts(); + ImGui::Combo( + "hex.windows.view.tty_console.baud"_lang, &this->m_selectedBaudRate, [](void *data, int idx) { + hex::unused(data); - ImGui::Combo( - "hex.windows.view.tty_console.baud"_lang, &this->m_selectedBaudRate, [](void *data, int idx) { - hex::unused(data); + return ViewTTYConsole::BaudRates[idx]; + }, + nullptr, + ViewTTYConsole::BaudRates.size()); - return ViewTTYConsole::BaudRates[idx]; - }, - nullptr, - ViewTTYConsole::BaudRates.size()); + ImGui::Combo( + "hex.windows.view.tty_console.num_bits"_lang, &this->m_selectedNumBits, [](void *data, int idx) { + hex::unused(data); - ImGui::Combo( - "hex.windows.view.tty_console.num_bits"_lang, &this->m_selectedNumBits, [](void *data, int idx) { - hex::unused(data); + return ViewTTYConsole::NumBits[idx]; + }, + nullptr, + ViewTTYConsole::NumBits.size()); - return ViewTTYConsole::NumBits[idx]; - }, - nullptr, - ViewTTYConsole::NumBits.size()); + ImGui::Combo( + "hex.windows.view.tty_console.stop_bits"_lang, &this->m_selectedStopBits, [](void *data, int idx) { + hex::unused(data); - ImGui::Combo( - "hex.windows.view.tty_console.stop_bits"_lang, &this->m_selectedStopBits, [](void *data, int idx) { - hex::unused(data); + return ViewTTYConsole::StopBits[idx]; + }, + nullptr, + ViewTTYConsole::StopBits.size()); - return ViewTTYConsole::StopBits[idx]; - }, - nullptr, - ViewTTYConsole::StopBits.size()); + ImGui::Combo( + "hex.windows.view.tty_console.parity_bits"_lang, &this->m_selectedParityBits, [](void *data, int idx) { + hex::unused(data); - ImGui::Combo( - "hex.windows.view.tty_console.parity_bits"_lang, &this->m_selectedParityBits, [](void *data, int idx) { - hex::unused(data); + return ViewTTYConsole::ParityBits[idx]; + }, + nullptr, + ViewTTYConsole::ParityBits.size()); - return ViewTTYConsole::ParityBits[idx]; - }, - nullptr, - ViewTTYConsole::ParityBits.size()); + ImGui::Checkbox("hex.windows.view.tty_console.cts"_lang, &this->m_hasCTSFlowControl); - ImGui::Checkbox("hex.windows.view.tty_console.cts"_lang, &this->m_hasCTSFlowControl); + ImGui::PopStyleVar(); + ImGui::PopItemFlag(); - ImGui::PopStyleVar(); - ImGui::PopItemFlag(); + ImGui::NewLine(); - ImGui::NewLine(); + if (this->m_portHandle == INVALID_HANDLE_VALUE) { + if (ImGui::Button("hex.windows.view.tty_console.connect"_lang)) + if (!this->connect()) + EventManager::post("hex.windows.view.tty_console.connect_error"_lang); + } else { + if (ImGui::Button("hex.windows.view.tty_console.disconnect"_lang)) + this->disconnect(); + } - if (this->m_portHandle == INVALID_HANDLE_VALUE) { - if (ImGui::Button("hex.windows.view.tty_console.connect"_lang)) - if (!this->connect()) - EventManager::post("hex.windows.view.tty_console.connect_error"_lang); - } else { - if (ImGui::Button("hex.windows.view.tty_console.disconnect"_lang)) - this->disconnect(); - } + ImGui::NewLine(); - ImGui::NewLine(); + if (ImGui::Button("hex.windows.view.tty_console.clear"_lang)) { + std::scoped_lock lock(this->m_receiveBufferMutex); - if (ImGui::Button("hex.windows.view.tty_console.clear"_lang)) { + this->m_receiveDataBuffer.clear(); + this->m_wrapPositions.clear(); + } + + ImGui::SameLine(); + + ImGui::Checkbox("hex.windows.view.tty_console.auto_scroll"_lang, &this->m_shouldAutoScroll); + + ImGuiExt::Header("hex.windows.view.tty_console.console"_lang); + + auto consoleSize = ImGui::GetContentRegionAvail(); + consoleSize.y -= ImGui::GetTextLineHeight() + ImGui::GetStyle().FramePadding.y * 4; + if (ImGui::BeginChild("##scrolling", consoleSize, true, ImGuiWindowFlags_HorizontalScrollbar)) { + ImGuiListClipper clipper; + clipper.Begin(this->m_wrapPositions.size(), ImGui::GetTextLineHeight()); + + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 1)); + while (clipper.Step()) { std::scoped_lock lock(this->m_receiveBufferMutex); - this->m_receiveDataBuffer.clear(); - this->m_wrapPositions.clear(); - } - - ImGui::SameLine(); - - ImGui::Checkbox("hex.windows.view.tty_console.auto_scroll"_lang, &this->m_shouldAutoScroll); - - ImGuiExt::Header("hex.windows.view.tty_console.console"_lang); - - auto consoleSize = ImGui::GetContentRegionAvail(); - consoleSize.y -= ImGui::GetTextLineHeight() + ImGui::GetStyle().FramePadding.y * 4; - if (ImGui::BeginChild("##scrolling", consoleSize, true, ImGuiWindowFlags_HorizontalScrollbar)) { - ImGuiListClipper clipper; - clipper.Begin(this->m_wrapPositions.size(), ImGui::GetTextLineHeight()); - - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 1)); - while (clipper.Step()) { - std::scoped_lock lock(this->m_receiveBufferMutex); - - for (int i = clipper.DisplayStart + 1; i < clipper.DisplayEnd; i++) { - ImGui::TextUnformatted(this->m_receiveDataBuffer.data() + this->m_wrapPositions[i - 1], this->m_receiveDataBuffer.data() + this->m_wrapPositions[i]); - } - - if (!this->m_receiveDataBuffer.empty() && !this->m_wrapPositions.empty()) - if (static_cast(clipper.DisplayEnd) >= this->m_wrapPositions.size() - 1) - ImGui::TextUnformatted(this->m_receiveDataBuffer.data() + this->m_wrapPositions.back()); - - if (this->m_shouldAutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) { - ImGui::SetScrollHereY(0.0F); - } - } - ImGui::PopStyleVar(); - } - ImGui::EndChild(); - - ImGui::PushItemWidth(-1); - if (ImGui::InputText("##transmit", this->m_transmitDataBuffer.data(), this->m_transmitDataBuffer.size() - 2, ImGuiInputTextFlags_EnterReturnsTrue)) { - auto size = strlen(this->m_transmitDataBuffer.data()); - - this->m_transmitDataBuffer[size + 0] = '\n'; - this->m_transmitDataBuffer[size + 1] = 0x00; - - this->transmitData(this->m_transmitDataBuffer); - ImGui::SetKeyboardFocusHere(0); - } - ImGui::PopItemWidth(); - - if (ImGui::IsMouseReleased(ImGuiMouseButton_Right) && ImGui::IsItemHovered() && this->m_portHandle != INVALID_HANDLE_VALUE && !this->m_transmitting) - ImGui::OpenPopup("ConsoleMenu"); - - if (ImGui::BeginPopup("ConsoleMenu")) { - - static std::vector buffer(2, 0x00); - if (ImGui::MenuItem("hex.windows.view.tty_console.send_etx"_lang, "CTRL + C")) { - buffer[0] = 0x03; - this->transmitData(buffer); - } - if (ImGui::MenuItem("hex.windows.view.tty_console.send_eot"_lang, "CTRL + D")) { - buffer[0] = 0x04; - this->transmitData(buffer); - } - if (ImGui::MenuItem("hex.windows.view.tty_console.send_sub"_lang, "CTRL + Z")) { - buffer[0] = 0x1A; - this->transmitData(buffer); + for (int i = clipper.DisplayStart + 1; i < clipper.DisplayEnd; i++) { + ImGui::TextUnformatted(this->m_receiveDataBuffer.data() + this->m_wrapPositions[i - 1], this->m_receiveDataBuffer.data() + this->m_wrapPositions[i]); } - ImGui::EndPopup(); + if (!this->m_receiveDataBuffer.empty() && !this->m_wrapPositions.empty()) + if (static_cast(clipper.DisplayEnd) >= this->m_wrapPositions.size() - 1) + ImGui::TextUnformatted(this->m_receiveDataBuffer.data() + this->m_wrapPositions.back()); + + if (this->m_shouldAutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) { + ImGui::SetScrollHereY(0.0F); + } } + ImGui::PopStyleVar(); + } + ImGui::EndChild(); + + ImGui::PushItemWidth(-1); + if (ImGui::InputText("##transmit", this->m_transmitDataBuffer.data(), this->m_transmitDataBuffer.size() - 2, ImGuiInputTextFlags_EnterReturnsTrue)) { + auto size = strlen(this->m_transmitDataBuffer.data()); + + this->m_transmitDataBuffer[size + 0] = '\n'; + this->m_transmitDataBuffer[size + 1] = 0x00; + + this->transmitData(this->m_transmitDataBuffer); + ImGui::SetKeyboardFocusHere(0); + } + ImGui::PopItemWidth(); + + if (ImGui::IsMouseReleased(ImGuiMouseButton_Right) && ImGui::IsItemHovered() && this->m_portHandle != INVALID_HANDLE_VALUE && !this->m_transmitting) + ImGui::OpenPopup("ConsoleMenu"); + + if (ImGui::BeginPopup("ConsoleMenu")) { + + static std::vector buffer(2, 0x00); + if (ImGui::MenuItem("hex.windows.view.tty_console.send_etx"_lang, "CTRL + C")) { + buffer[0] = 0x03; + this->transmitData(buffer); + } + if (ImGui::MenuItem("hex.windows.view.tty_console.send_eot"_lang, "CTRL + D")) { + buffer[0] = 0x04; + this->transmitData(buffer); + } + if (ImGui::MenuItem("hex.windows.view.tty_console.send_sub"_lang, "CTRL + Z")) { + buffer[0] = 0x1A; + this->transmitData(buffer); + } + + ImGui::EndPopup(); } - ImGui::End(); } std::vector> ViewTTYConsole::getAvailablePorts() const {