Files
Craftimizer/Craftimizer/ImGuiExtras.cs
T
2023-10-25 12:44:03 -07:00

140 lines
4.8 KiB
C#

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<char> 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;
}
}