From d2e6d8e4d9b36886b44e062dafbeea482db4ed21 Mon Sep 17 00:00:00 2001
From: Lennard Fonteijn <lennard@lennardf1989.com>
Date: Sun, 13 Aug 2023 17:08:17 +0200
Subject: [PATCH] =?UTF-8?q?Added=20ImGui=20extension=20to=20create=20singl?=
 =?UTF-8?q?e-=20and=20multiline=20formatted=20selec=E2=80=A6=20(#1249)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

(Partial) implementation of #1245 with additional functionality to
supersede PR #1240.
---
 .../include/hex/ui/imgui_imhex_extensions.h   | 49 +++++++++++++++++++
 plugins/builtin/source/ui/hex_editor.cpp      | 21 +++++---
 2 files changed, 64 insertions(+), 6 deletions(-)

diff --git a/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h b/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h
index 9c086c6e2..c871cd210 100644
--- a/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h
+++ b/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h
@@ -10,6 +10,8 @@
 
 #include <hex/helpers/fmt.hpp>
 
+#include <wolv/utils/string.hpp>
+
 enum ImGuiCustomCol {
     ImGuiCustomCol_DescButton,
     ImGuiCustomCol_DescButtonHovered,
@@ -141,6 +143,24 @@ namespace ImGui {
         ImGui::TextUnformatted(hex::format(fmt, std::forward<decltype(args)>(args)...).c_str());
     }
 
+    inline void TextFormattedSelectable(const std::string &fmt, auto &&...args) {
+        auto text = hex::format(fmt, std::forward<decltype(args)>(args)...);
+
+        ImGui::PushID(text.c_str());
+
+        ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2());
+        ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4());
+
+        ImGui::PushItemWidth(-FLT_MIN);
+        ImGui::InputText("##", const_cast<char *>(text.c_str()), text.size(), ImGuiInputTextFlags_ReadOnly);
+        ImGui::PopItemWidth();
+
+        ImGui::PopStyleColor();
+        ImGui::PopStyleVar();
+
+        ImGui::PopID();
+    }
+
     inline void TextFormattedColored(ImColor color, const std::string &fmt, auto &&...args) {
         ImGui::TextColored(color, "%s", hex::format(fmt, std::forward<decltype(args)>(args)...).c_str());
     }
@@ -153,6 +173,35 @@ namespace ImGui {
         ImGui::TextWrapped("%s", hex::format(fmt, std::forward<decltype(args)>(args)...).c_str());
     }
 
+    inline void TextFormattedWrappedSelectable(const std::string &fmt, auto &&...args) {
+        //Manually wrap text, using the letter M (generally the widest character in non-monospaced fonts) to calculate the character width to use.
+        auto text = wolv::util::wrapMonospacedString(
+                hex::format(fmt, std::forward<decltype(args)>(args)...),
+                ImGui::CalcTextSize("M").x,
+                ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ScrollbarSize - ImGui::GetStyle().FrameBorderSize
+        );
+
+        ImGui::PushID(text.c_str());
+
+        ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2());
+        ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4());
+
+        ImGui::PushItemWidth(-FLT_MIN);
+        ImGui::InputTextMultiline(
+                "##",
+                const_cast<char *>(text.c_str()),
+                text.size(),
+                ImVec2(0, -FLT_MIN),
+                ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_NoHorizontalScroll
+        );
+        ImGui::PopItemWidth();
+
+        ImGui::PopStyleColor();
+        ImGui::PopStyleVar();
+
+        ImGui::PopID();
+    }
+
     inline void TextFormattedCentered(const std::string &fmt, auto &&...args) {
         auto text = hex::format(fmt, std::forward<decltype(args)>(args)...);
         auto availableSpace = ImGui::GetContentRegionAvail();
diff --git a/plugins/builtin/source/ui/hex_editor.cpp b/plugins/builtin/source/ui/hex_editor.cpp
index 6f273b312..40053f6bd 100644
--- a/plugins/builtin/source/ui/hex_editor.cpp
+++ b/plugins/builtin/source/ui/hex_editor.cpp
@@ -694,7 +694,12 @@ namespace hex::plugin::builtin::ui {
                     ImGui::TableNextColumn();
                     {
                         auto pageAddress = this->m_provider->getCurrentPageAddress();
-                        ImGui::TextFormatted("{0}: 0x{1:08X} - 0x{2:08X} ({1} - {2})", "hex.builtin.hex_editor.region"_lang, pageAddress, pageAddress + this->m_provider->getSize() - 1);
+                        ImGui::TextFormatted("{}:", "hex.builtin.hex_editor.region"_lang);
+                        ImGui::SameLine();
+                        ImGui::TextFormattedSelectable("0x{0:08X} - 0x{1:08X} ({0} - {1})",
+                                                       pageAddress,
+                                                       pageAddress + this->m_provider->getSize() - 1
+                       );
                     }
 
                     ImGui::TableNextRow();
@@ -715,16 +720,20 @@ namespace hex::plugin::builtin::ui {
                         else
                             value = std::string("hex.builtin.hex_editor.selection.none"_lang);
 
-                        ImGui::TextFormatted("{0}: {1}", "hex.builtin.hex_editor.selection"_lang, value);
+                        ImGui::TextFormatted("{}:", "hex.builtin.hex_editor.selection"_lang);
+                        ImGui::SameLine();
+                        ImGui::TextFormattedSelectable(value);
                     }
 
                     // Loaded data size
                     ImGui::TableNextColumn();
                     {
-                        ImGui::TextFormatted("{0}: 0x{1:08X} (0x{2:X} | {3})", "hex.builtin.hex_editor.data_size"_lang,
-                                             this->m_provider->getActualSize(),
-                                             this->m_provider->getActualSize(),
-                                             hex::toByteString(this->m_provider->getActualSize())
+                        ImGui::TextFormatted("{}:", "hex.builtin.hex_editor.data_size"_lang);
+                        ImGui::SameLine();
+                        ImGui::TextFormattedSelectable("0x{0:08X} (0x{1:X} | {2})",
+                                                       this->m_provider->getActualSize(),
+                                                       this->m_provider->getActualSize(),
+                                                       hex::toByteString(this->m_provider->getActualSize())
                         );
                     }