using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; namespace Dalamud.Bindings.ImGui; [SuppressMessage("ReSharper", "InconsistentNaming")] public static unsafe partial class ImGui { public delegate bool PopulateAutoUtf8BufferDelegate(int index, out Utf8Buffer outText); public delegate bool PopulateAutoUtf8BufferDelegate(scoped in T context, int index, out Utf8Buffer outText); public static bool Combo( Utf8Buffer label, ref int currentItem, scoped in T items, int popupMaxHeightInItems = -1) where T : IList => Combo( label, ref currentItem, static (scoped in T items, int index, out Utf8Buffer outText) => { outText = items[index]; return true; }, items, items.Count, popupMaxHeightInItems); public static bool Combo( Utf8Buffer label, ref int currentItem, IReadOnlyList items, int popupMaxHeightInItems = -1) => Combo( label, ref currentItem, static (scoped in IReadOnlyList items, int index, out Utf8Buffer outText) => { outText = items[index]; return true; }, items, items.Count, popupMaxHeightInItems); public static bool Combo( Utf8Buffer label, ref int currentItem, Utf8Buffer itemsSeparatedByZeros, int popupMaxHeightInItems = -1) { if (!itemsSeparatedByZeros.Span.EndsWith("\0\0"u8)) itemsSeparatedByZeros.AppendFormatted("\0\0"u8); fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) fixed (int* currentItemPtr = ¤tItem) fixed (byte* itemsSeparatedByZerosPtr = itemsSeparatedByZeros.Span) { var r = ImGuiNative.Combo(labelPtr, currentItemPtr, itemsSeparatedByZerosPtr, popupMaxHeightInItems) != 0; label.Dispose(); itemsSeparatedByZeros.Dispose(); return r; } } public static bool Combo( Utf8Buffer label, ref int currentItem, PopulateAutoUtf8BufferDelegate itemsGetter, scoped in TContext context, int itemsCount, int popupMaxHeightInItems = -1) { Utf8Buffer textBuffer = default; var dataBuffer = stackalloc void*[3]; fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) fixed (int* currentItemPtr = ¤tItem) #pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type fixed (TContext* contextPtr = &context) { dataBuffer[0] = &textBuffer; dataBuffer[1] = &itemsGetter; dataBuffer[2] = contextPtr; #pragma warning restore CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type var r = ImGuiNative.Combo( labelPtr, currentItemPtr, (delegate*, void*, int, int, bool>) (nint)(delegate* unmanaged)&PopulateUtf8BufferDelegateWithContext, dataBuffer, itemsCount, popupMaxHeightInItems) != 0; label.Dispose(); textBuffer.Dispose(); return r; } } public static bool Combo( Utf8Buffer label, ref int currentItem, PopulateAutoUtf8BufferDelegate itemsGetter, int itemsCount, int popupMaxHeightInItems = -1) { Utf8Buffer textBuffer = default; var dataBuffer = stackalloc void*[2]; fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) fixed (int* currentItemPtr = ¤tItem) #pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type { dataBuffer[0] = &textBuffer; dataBuffer[1] = &itemsGetter; #pragma warning restore CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type var r = ImGuiNative.Combo( labelPtr, currentItemPtr, (delegate*, void*, int, int, bool>) (nint)(delegate* unmanaged)&PopulateUtf8BufferDelegateWithoutContext, dataBuffer, itemsCount, popupMaxHeightInItems) != 0; label.Dispose(); textBuffer.Dispose(); return r; } } public static bool ListBox( Utf8Buffer label, ref int currentItem, scoped in T items, int popupMaxHeightInItems = -1) where T : IList => ListBox( label, ref currentItem, static (scoped in T items, int index, out Utf8Buffer outText) => { outText = items[index]; return true; }, items, items.Count, popupMaxHeightInItems); public static bool ListBox( Utf8Buffer label, ref int currentItem, IReadOnlyList items, int popupMaxHeightInItems = -1) => ListBox( label, ref currentItem, static (scoped in IReadOnlyList items, int index, out Utf8Buffer outText) => { outText = items[index]; return true; }, items, items.Count, popupMaxHeightInItems); public static bool ListBox( Utf8Buffer label, ref int currentItem, PopulateAutoUtf8BufferDelegate itemsGetter, scoped in TContext context, int itemsCount, int popupMaxHeightInItems = -1) { Utf8Buffer textBuffer = default; var dataBuffer = stackalloc void*[3]; fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) fixed (int* currentItemPtr = ¤tItem) #pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type fixed (TContext* contextPtr = &context) { dataBuffer[0] = &textBuffer; dataBuffer[1] = &itemsGetter; dataBuffer[2] = contextPtr; #pragma warning restore CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type var r = ImGuiNative.ListBox( labelPtr, currentItemPtr, (delegate*, void*, int, int, bool>) (nint)(delegate* unmanaged)&PopulateUtf8BufferDelegateWithContext, dataBuffer, itemsCount, popupMaxHeightInItems) != 0; label.Dispose(); textBuffer.Dispose(); return r; } } public static bool ListBox( Utf8Buffer label, ref int currentItem, PopulateAutoUtf8BufferDelegate itemsGetter, int itemsCount, int popupMaxHeightInItems = -1) { Utf8Buffer textBuffer = default; var dataBuffer = stackalloc void*[2]; fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) fixed (int* currentItemPtr = ¤tItem) #pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type { dataBuffer[0] = &textBuffer; dataBuffer[1] = &itemsGetter; #pragma warning restore CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type var r = ImGuiNative.ListBox( labelPtr, currentItemPtr, (delegate*, void*, int, int, bool>) (nint)(delegate* unmanaged)&PopulateUtf8BufferDelegateWithoutContext, dataBuffer, itemsCount, popupMaxHeightInItems) != 0; label.Dispose(); textBuffer.Dispose(); return r; } } [UnmanagedCallersOnly] private static bool PopulateUtf8BufferDelegateWithContext(void* data, int index, byte** text) { #pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type ref var textBuffer = ref *(Utf8Buffer*)((void**)data)[0]; return ((PopulateAutoUtf8BufferDelegate*)((void**)data)[1])->Invoke( *(object*)((void**)data)[2], index, out textBuffer); #pragma warning restore CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type } [UnmanagedCallersOnly] private static bool PopulateUtf8BufferDelegateWithoutContext(void* data, int index, byte** text) { #pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type ref var textBuffer = ref *(Utf8Buffer*)((void**)data)[0]; return ((PopulateAutoUtf8BufferDelegate*)((void**)data)[1])->Invoke( index, out textBuffer); #pragma warning restore CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type } }