From c6c950dc51743dbd5c7b8233521c5ea9779b7a25 Mon Sep 17 00:00:00 2001 From: Asriel Camora Date: Wed, 25 Oct 2023 12:44:03 -0700 Subject: [PATCH] Add internal InputTextEx --- Craftimizer/ImGuiExtras.cs | 139 +++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 Craftimizer/ImGuiExtras.cs diff --git a/Craftimizer/ImGuiExtras.cs b/Craftimizer/ImGuiExtras.cs new file mode 100644 index 0000000..bc60e1a --- /dev/null +++ b/Craftimizer/ImGuiExtras.cs @@ -0,0 +1,139 @@ +using ImGuiNET; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; + +namespace Craftimizer; + +internal static unsafe class ImGuiExtras +{ + // https://github.com/ImGuiNET/ImGui.NET/blob/069363672fed940ebdaa02f9b032c282b66467c7/src/CodeGenerator/definitions/cimgui/definitions.json#L25394 + [DllImport("cimgui", CallingConvention = CallingConvention.Cdecl)] + private static extern unsafe byte igInputTextEx(byte* label, byte* hint, byte* buf, int buf_size, Vector2 size, ImGuiInputTextFlags flags, ImGuiInputTextCallback? callback, void* user_data); + + // https://github.com/ImGuiNET/ImGui.NET/blob/069363672fed940ebdaa02f9b032c282b66467c7/src/ImGui.NET/Util.cs + #region Util + + private const int StackAllocationSizeLimit = 2048; + + private static unsafe byte* Allocate(int byteCount) => (byte*)Marshal.AllocHGlobal(byteCount); + + private static unsafe void Free(byte* ptr) => Marshal.FreeHGlobal((IntPtr)ptr); + + private static unsafe int GetUtf8(ReadOnlySpan s, byte* utf8Bytes, int utf8ByteCount) + { + if (s.IsEmpty) + return 0; + + fixed (char* utf16Ptr = s) + return Encoding.UTF8.GetBytes(utf16Ptr, s.Length, utf8Bytes, utf8ByteCount); + } + + private static unsafe bool AreStringsEqual(byte* a, int aLength, byte* b) + { + for (var i = 0; i < aLength; i++) + { + if (a[i] != b[i]) + return false; + } + + return b[aLength] == 0; + } + + private static unsafe string StringFromPtr(byte* ptr) + { + var characters = 0; + while (ptr[characters] != 0) + { + characters++; + } + + return Encoding.UTF8.GetString(ptr, characters); + } + + #endregion + + // Based off of code from InputTextWithHint: https://github.com/ImGuiNET/ImGui.NET/blob/069363672fed940ebdaa02f9b032c282b66467c7/src/ImGui.NET/ImGui.Manual.cs#L271 + public static unsafe bool InputTextEx(string label, string hint, ref string input, int maxLength, Vector2 size, ImGuiInputTextFlags flags = ImGuiInputTextFlags.None, ImGuiInputTextCallback? callback = null, IntPtr user_data = default) + { + var utf8LabelByteCount = Encoding.UTF8.GetByteCount(label); + byte* utf8LabelBytes; + if (utf8LabelByteCount > StackAllocationSizeLimit) + { + utf8LabelBytes = Allocate(utf8LabelByteCount + 1); + } + else + { + var stackPtr = stackalloc byte[utf8LabelByteCount + 1]; + utf8LabelBytes = stackPtr; + } + GetUtf8(label, utf8LabelBytes, utf8LabelByteCount); + + var utf8HintByteCount = Encoding.UTF8.GetByteCount(hint); + byte* utf8HintBytes; + if (utf8HintByteCount > StackAllocationSizeLimit) + { + utf8HintBytes = Allocate(utf8HintByteCount + 1); + } + else + { + var stackPtr = stackalloc byte[utf8HintByteCount + 1]; + utf8HintBytes = stackPtr; + } + GetUtf8(hint, utf8HintBytes, utf8HintByteCount); + + var utf8InputByteCount = Encoding.UTF8.GetByteCount(input); + var inputBufSize = Math.Max(maxLength + 1, utf8InputByteCount + 1); + + byte* utf8InputBytes; + byte* originalUtf8InputBytes; + if (inputBufSize > StackAllocationSizeLimit) + { + utf8InputBytes = Allocate(inputBufSize); + originalUtf8InputBytes = Allocate(inputBufSize); + } + else + { + var inputStackBytes = stackalloc byte[inputBufSize]; + utf8InputBytes = inputStackBytes; + var originalInputStackBytes = stackalloc byte[inputBufSize]; + originalUtf8InputBytes = originalInputStackBytes; + } + GetUtf8(input, utf8InputBytes, inputBufSize); + var clearBytesCount = (uint)(inputBufSize - utf8InputByteCount); + Unsafe.InitBlockUnaligned(utf8InputBytes + utf8InputByteCount, 0, clearBytesCount); + Unsafe.CopyBlock(originalUtf8InputBytes, utf8InputBytes, (uint)inputBufSize); + + var result = igInputTextEx( + utf8LabelBytes, + utf8HintBytes, + utf8InputBytes, + inputBufSize, + size, + flags, + callback, + user_data.ToPointer()); + if (!AreStringsEqual(originalUtf8InputBytes, inputBufSize, utf8InputBytes)) + { + input = StringFromPtr(utf8InputBytes); + } + + if (utf8LabelByteCount > StackAllocationSizeLimit) + { + Free(utf8LabelBytes); + } + if (utf8HintByteCount > StackAllocationSizeLimit) + { + Free(utf8HintBytes); + } + if (inputBufSize > StackAllocationSizeLimit) + { + Free(utf8InputBytes); + Free(originalUtf8InputBytes); + } + + return result != 0; + } +}