mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-13 12:14:16 +01:00
Just use win32 APIs
This commit is contained in:
parent
f6d16d5624
commit
06938509e7
4 changed files with 125 additions and 231 deletions
|
|
@ -90,6 +90,7 @@
|
||||||
<PackageReference Include="System.Reactive" Version="5.0.0" />
|
<PackageReference Include="System.Reactive" Version="5.0.0" />
|
||||||
<PackageReference Include="System.Reflection.MetadataLoadContext" Version="7.0.0" />
|
<PackageReference Include="System.Reflection.MetadataLoadContext" Version="7.0.0" />
|
||||||
<PackageReference Include="System.Resources.Extensions" Version="7.0.0" />
|
<PackageReference Include="System.Resources.Extensions" Version="7.0.0" />
|
||||||
|
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.22621.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Dalamud.Common\Dalamud.Common.csproj" />
|
<ProjectReference Include="..\Dalamud.Common\Dalamud.Common.csproj" />
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,19 @@
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using CheapLoc;
|
||||||
|
|
||||||
|
using Dalamud.Game.Gui.Toast;
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
|
using Dalamud.Logging.Internal;
|
||||||
|
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
|
|
||||||
|
using TerraFX.Interop.Windows;
|
||||||
|
|
||||||
|
using static TerraFX.Interop.Windows.Windows;
|
||||||
|
|
||||||
namespace Dalamud.Interface.Internal;
|
namespace Dalamud.Interface.Internal;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -29,9 +37,15 @@ namespace Dalamud.Interface.Internal;
|
||||||
[ServiceManager.EarlyLoadedService]
|
[ServiceManager.EarlyLoadedService]
|
||||||
internal sealed unsafe class ImGuiClipboardFunctionProvider : IServiceType, IDisposable
|
internal sealed unsafe class ImGuiClipboardFunctionProvider : IServiceType, IDisposable
|
||||||
{
|
{
|
||||||
|
private static readonly ModuleLog Log = new(nameof(ImGuiClipboardFunctionProvider));
|
||||||
private readonly nint clipboardUserDataOriginal;
|
private readonly nint clipboardUserDataOriginal;
|
||||||
private readonly delegate* unmanaged<nint, byte*, void> setTextOriginal;
|
private readonly nint setTextOriginal;
|
||||||
private readonly delegate* unmanaged<nint, byte*> getTextOriginal;
|
private readonly nint getTextOriginal;
|
||||||
|
|
||||||
|
[ServiceManager.ServiceDependency]
|
||||||
|
private readonly ToastGui toastGui = Service<ToastGui>.Get();
|
||||||
|
|
||||||
|
private ImVectorWrapper<byte> clipboardData;
|
||||||
private GCHandle clipboardUserData;
|
private GCHandle clipboardUserData;
|
||||||
|
|
||||||
[ServiceManager.ServiceConstructor]
|
[ServiceManager.ServiceConstructor]
|
||||||
|
|
@ -43,11 +57,13 @@ internal sealed unsafe class ImGuiClipboardFunctionProvider : IServiceType, IDis
|
||||||
|
|
||||||
var io = ImGui.GetIO();
|
var io = ImGui.GetIO();
|
||||||
this.clipboardUserDataOriginal = io.ClipboardUserData;
|
this.clipboardUserDataOriginal = io.ClipboardUserData;
|
||||||
this.setTextOriginal = (delegate* unmanaged<nint, byte*, void>)io.SetClipboardTextFn;
|
this.setTextOriginal = io.SetClipboardTextFn;
|
||||||
this.getTextOriginal = (delegate* unmanaged<nint, byte*>)io.GetClipboardTextFn;
|
this.getTextOriginal = io.GetClipboardTextFn;
|
||||||
io.ClipboardUserData = GCHandle.ToIntPtr(this.clipboardUserData = GCHandle.Alloc(this));
|
io.ClipboardUserData = GCHandle.ToIntPtr(this.clipboardUserData = GCHandle.Alloc(this));
|
||||||
io.SetClipboardTextFn = (nint)(delegate* unmanaged<nint, byte*, void>)&StaticSetClipboardTextImpl;
|
io.SetClipboardTextFn = (nint)(delegate* unmanaged<nint, byte*, void>)&StaticSetClipboardTextImpl;
|
||||||
io.GetClipboardTextFn = (nint)(delegate* unmanaged<nint, byte*>)&StaticGetClipboardTextImpl;
|
io.GetClipboardTextFn = (nint)(delegate* unmanaged<nint, byte*>)&StaticGetClipboardTextImpl;
|
||||||
|
|
||||||
|
this.clipboardData = new(0);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
|
@ -59,10 +75,6 @@ internal sealed unsafe class ImGuiClipboardFunctionProvider : IServiceType, IDis
|
||||||
((ImGuiClipboardFunctionProvider)GCHandle.FromIntPtr(userData).Target)!.GetClipboardTextImpl();
|
((ImGuiClipboardFunctionProvider)GCHandle.FromIntPtr(userData).Target)!.GetClipboardTextImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
[SuppressMessage("ReSharper", "AssignNullToNotNullAttribute", Justification = "If it's null, it's crashworthy")]
|
|
||||||
private static ImVectorWrapper<byte> ImGuiCurrentContextClipboardHandlerData =>
|
|
||||||
new((ImVector*)(ImGui.GetCurrentContext() + 0x5520));
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
|
@ -70,30 +82,118 @@ internal sealed unsafe class ImGuiClipboardFunctionProvider : IServiceType, IDis
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var io = ImGui.GetIO();
|
var io = ImGui.GetIO();
|
||||||
io.SetClipboardTextFn = (nint)this.setTextOriginal;
|
io.SetClipboardTextFn = this.setTextOriginal;
|
||||||
io.GetClipboardTextFn = (nint)this.getTextOriginal;
|
io.GetClipboardTextFn = this.getTextOriginal;
|
||||||
io.ClipboardUserData = this.clipboardUserDataOriginal;
|
io.ClipboardUserData = this.clipboardUserDataOriginal;
|
||||||
|
|
||||||
this.clipboardUserData.Free();
|
this.clipboardUserData.Free();
|
||||||
|
this.clipboardData.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool OpenClipboardOrShowError()
|
||||||
|
{
|
||||||
|
if (!OpenClipboard(default))
|
||||||
|
{
|
||||||
|
this.toastGui.ShowError(
|
||||||
|
Loc.Localize(
|
||||||
|
"ImGuiClipboardFunctionProviderClipboardInUse",
|
||||||
|
"Some other application is using the clipboard. Try again later."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetClipboardTextImpl(byte* text)
|
private void SetClipboardTextImpl(byte* text)
|
||||||
{
|
{
|
||||||
var buffer = ImGuiCurrentContextClipboardHandlerData;
|
if (!this.OpenClipboardOrShowError())
|
||||||
buffer.SetFromZeroTerminatedSequence(text);
|
return;
|
||||||
buffer.Utf8Normalize();
|
|
||||||
buffer.AddZeroTerminatorIfMissing();
|
try
|
||||||
this.setTextOriginal(this.clipboardUserDataOriginal, buffer.Data);
|
{
|
||||||
|
var len = 0;
|
||||||
|
while (text[len] != 0)
|
||||||
|
len++;
|
||||||
|
var str = Encoding.UTF8.GetString(text, len);
|
||||||
|
str = str.ReplaceLineEndings("\r\n");
|
||||||
|
var hMem = GlobalAlloc(GMEM.GMEM_MOVEABLE, (nuint)((str.Length + 1) * 2));
|
||||||
|
if (hMem == 0)
|
||||||
|
throw new OutOfMemoryException();
|
||||||
|
|
||||||
|
var ptr = (char*)GlobalLock(hMem);
|
||||||
|
if (ptr == null)
|
||||||
|
{
|
||||||
|
throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error())
|
||||||
|
?? throw new InvalidOperationException($"{nameof(GlobalLock)} failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
str.AsSpan().CopyTo(new(ptr, str.Length));
|
||||||
|
ptr[str.Length] = default;
|
||||||
|
GlobalUnlock(hMem);
|
||||||
|
|
||||||
|
SetClipboardData(CF.CF_UNICODETEXT, hMem);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(e, $"Error in {nameof(this.SetClipboardTextImpl)}");
|
||||||
|
this.toastGui.ShowError(
|
||||||
|
Loc.Localize(
|
||||||
|
"ImGuiClipboardFunctionProviderErrorCopy",
|
||||||
|
"Failed to copy. See logs for details."));
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
CloseClipboard();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte* GetClipboardTextImpl()
|
private byte* GetClipboardTextImpl()
|
||||||
{
|
{
|
||||||
_ = this.getTextOriginal(this.clipboardUserDataOriginal);
|
this.clipboardData.Clear();
|
||||||
|
|
||||||
var buffer = ImGuiCurrentContextClipboardHandlerData;
|
var formats = stackalloc uint[] { CF.CF_UNICODETEXT, CF.CF_TEXT };
|
||||||
buffer.TrimZeroTerminator();
|
if (GetPriorityClipboardFormat(formats, 2) < 1 || !this.OpenClipboardOrShowError())
|
||||||
buffer.Utf8Normalize();
|
{
|
||||||
buffer.AddZeroTerminatorIfMissing();
|
this.clipboardData.Add(0);
|
||||||
return buffer.Data;
|
return this.clipboardData.Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var hMem = (HGLOBAL)GetClipboardData(CF.CF_UNICODETEXT);
|
||||||
|
if (hMem != default)
|
||||||
|
{
|
||||||
|
var ptr = (char*)GlobalLock(hMem);
|
||||||
|
if (ptr == null)
|
||||||
|
{
|
||||||
|
throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error())
|
||||||
|
?? throw new InvalidOperationException($"{nameof(GlobalLock)} failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var str = new string(ptr);
|
||||||
|
str = str.ReplaceLineEndings("\r\n");
|
||||||
|
this.clipboardData.Resize(Encoding.UTF8.GetByteCount(str) + 1);
|
||||||
|
Encoding.UTF8.GetBytes(str, this.clipboardData.DataSpan);
|
||||||
|
this.clipboardData[^1] = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.clipboardData.Add(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(e, $"Error in {nameof(this.GetClipboardTextImpl)}");
|
||||||
|
this.toastGui.ShowError(
|
||||||
|
Loc.Localize(
|
||||||
|
"ImGuiClipboardFunctionProviderErrorPaste",
|
||||||
|
"Failed to paste. See logs for details."));
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
CloseClipboard();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.clipboardData.Data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,207 +0,0 @@
|
||||||
using System.Numerics;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Dalamud.Interface.Utility;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Utility methods for <see cref="ImVectorWrapper{T}"/>.
|
|
||||||
/// </summary>
|
|
||||||
public static partial class ImVectorWrapper
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Appends <paramref name="buf"/> from <paramref name="psz"/>, a zero terminated sequence.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The element type.</typeparam>
|
|
||||||
/// <param name="buf">The target buffer.</param>
|
|
||||||
/// <param name="psz">The pointer to the zero-terminated sequence.</param>
|
|
||||||
public static unsafe void AppendZeroTerminatedSequence<T>(this ref ImVectorWrapper<T> buf, T* psz)
|
|
||||||
where T : unmanaged, INumber<T>
|
|
||||||
{
|
|
||||||
var len = 0;
|
|
||||||
while (psz[len] != default)
|
|
||||||
len++;
|
|
||||||
|
|
||||||
buf.AddRange(new Span<T>(psz, len));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets <paramref name="buf"/> from <paramref name="psz"/>, a zero terminated sequence.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The element type.</typeparam>
|
|
||||||
/// <param name="buf">The target buffer.</param>
|
|
||||||
/// <param name="psz">The pointer to the zero-terminated sequence.</param>
|
|
||||||
public static unsafe void SetFromZeroTerminatedSequence<T>(this ref ImVectorWrapper<T> buf, T* psz)
|
|
||||||
where T : unmanaged, INumber<T>
|
|
||||||
{
|
|
||||||
buf.Clear();
|
|
||||||
buf.AppendZeroTerminatedSequence(psz);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Trims zero terminator(s).
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The element type.</typeparam>
|
|
||||||
/// <param name="buf">The buffer.</param>
|
|
||||||
public static void TrimZeroTerminator<T>(this ref ImVectorWrapper<T> buf)
|
|
||||||
where T : unmanaged, INumber<T>
|
|
||||||
{
|
|
||||||
ref var len = ref buf.LengthUnsafe;
|
|
||||||
while (len > 0 && buf[len - 1] == default)
|
|
||||||
len--;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a zero terminator to the buffer, if missing.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The element type.</typeparam>
|
|
||||||
/// <param name="buf">The buffer.</param>
|
|
||||||
public static void AddZeroTerminatorIfMissing<T>(this ref ImVectorWrapper<T> buf)
|
|
||||||
where T : unmanaged, INumber<T>
|
|
||||||
{
|
|
||||||
if (buf.Length > 0 && buf[^1] == default)
|
|
||||||
return;
|
|
||||||
buf.Add(default);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the codepoint at the given offset.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="buf">The buffer containing bytes in UTF-8.</param>
|
|
||||||
/// <param name="offset">The offset in bytes.</param>
|
|
||||||
/// <param name="numBytes">Number of bytes occupied by the character, invalid or not.</param>
|
|
||||||
/// <param name="invalid">The fallback character, if no valid UTF-8 character could be found.</param>
|
|
||||||
/// <returns>The parsed codepoint, or <paramref name="invalid"/> if it could not be parsed correctly.</returns>
|
|
||||||
public static unsafe int Utf8GetCodepoint(
|
|
||||||
this in ImVectorWrapper<byte> buf,
|
|
||||||
int offset,
|
|
||||||
out int numBytes,
|
|
||||||
int invalid = 0xFFFD)
|
|
||||||
{
|
|
||||||
var cb = buf.LengthUnsafe - offset;
|
|
||||||
if (cb <= 0)
|
|
||||||
{
|
|
||||||
numBytes = 0;
|
|
||||||
return invalid;
|
|
||||||
}
|
|
||||||
|
|
||||||
numBytes = 1;
|
|
||||||
|
|
||||||
var b = buf.DataUnsafe + offset;
|
|
||||||
if ((b[0] & 0x80) == 0)
|
|
||||||
return b[0];
|
|
||||||
|
|
||||||
if (cb < 2 || (b[1] & 0xC0) != 0x80)
|
|
||||||
return invalid;
|
|
||||||
if ((b[0] & 0xE0) == 0xC0)
|
|
||||||
{
|
|
||||||
numBytes = 2;
|
|
||||||
return ((b[0] & 0x1F) << 6) | (b[1] & 0x3F);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cb < 3 || (b[2] & 0xC0) != 0x80)
|
|
||||||
return invalid;
|
|
||||||
if ((b[0] & 0xF0) == 0xE0)
|
|
||||||
{
|
|
||||||
numBytes = 3;
|
|
||||||
return ((b[0] & 0x0F) << 12) | ((b[1] & 0x3F) << 6) | (b[2] & 0x3F);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cb < 4 || (b[3] & 0xC0) != 0x80)
|
|
||||||
return invalid;
|
|
||||||
if ((b[0] & 0xF8) == 0xF0)
|
|
||||||
{
|
|
||||||
numBytes = 4;
|
|
||||||
return ((b[0] & 0x07) << 18) | ((b[1] & 0x3F) << 12) | ((b[2] & 0x3F) << 6) | (b[3] & 0x3F);
|
|
||||||
}
|
|
||||||
|
|
||||||
return invalid;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Normalizes the given UTF-8 string.<br />
|
|
||||||
/// Using the default values will ensure the best interop between the game, ImGui, and Windows.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="buf">The buffer containing bytes in UTF-8.</param>
|
|
||||||
/// <param name="lineEnding">The replacement line ending. If empty, CR LF will be used.</param>
|
|
||||||
/// <param name="invalidChar">The replacement invalid character. If empty, U+FFFD REPLACEMENT CHARACTER will be used.</param>
|
|
||||||
/// <param name="normalizeLineEndings">Specify whether to normalize the line endings.</param>
|
|
||||||
/// <param name="sanitizeInvalidCharacters">Specify whether to replace invalid characters.</param>
|
|
||||||
/// <param name="sanitizeNonUcs2Characters">Specify whether to replace characters that requires the use of surrogate, when encoded in UTF-16.</param>
|
|
||||||
/// <param name="sanitizeSurrogates">Specify whether to make sense out of WTF-8.</param>
|
|
||||||
public static unsafe void Utf8Normalize(
|
|
||||||
this ref ImVectorWrapper<byte> buf,
|
|
||||||
ReadOnlySpan<byte> lineEnding = default,
|
|
||||||
ReadOnlySpan<byte> invalidChar = default,
|
|
||||||
bool normalizeLineEndings = true,
|
|
||||||
bool sanitizeInvalidCharacters = true,
|
|
||||||
bool sanitizeNonUcs2Characters = true,
|
|
||||||
bool sanitizeSurrogates = true)
|
|
||||||
{
|
|
||||||
if (lineEnding.IsEmpty)
|
|
||||||
lineEnding = "\r\n"u8;
|
|
||||||
if (invalidChar.IsEmpty)
|
|
||||||
invalidChar = "\uFFFD"u8;
|
|
||||||
|
|
||||||
// Ensure an implicit null after the end of the string.
|
|
||||||
buf.EnsureCapacity(buf.Length + 1);
|
|
||||||
buf.StorageSpan[buf.Length] = 0;
|
|
||||||
|
|
||||||
Span<char> charsBuf = stackalloc char[2];
|
|
||||||
Span<byte> bytesBuf = stackalloc byte[4];
|
|
||||||
for (var i = 0; i < buf.Length;)
|
|
||||||
{
|
|
||||||
var c1 = buf.Utf8GetCodepoint(i, out var cb, -1);
|
|
||||||
switch (c1)
|
|
||||||
{
|
|
||||||
// Note that buf.Data[i + 1] is always defined. See the beginning of the function.
|
|
||||||
case '\r' when buf.Data[i + 1] == '\n':
|
|
||||||
// If it's already CR LF, it passes all filters.
|
|
||||||
i += 2;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case >= 0xD800 and <= 0xDFFF when sanitizeSurrogates:
|
|
||||||
{
|
|
||||||
var c2 = buf.Utf8GetCodepoint(i + cb, out var cb2);
|
|
||||||
if (c1 is < 0xD800 or >= 0xDC00)
|
|
||||||
goto case -2;
|
|
||||||
if (c2 is < 0xDC00 or >= 0xE000)
|
|
||||||
goto case -2;
|
|
||||||
charsBuf[0] = unchecked((char)c1);
|
|
||||||
charsBuf[1] = unchecked((char)c2);
|
|
||||||
var bytesLen = Encoding.UTF8.GetBytes(charsBuf, bytesBuf);
|
|
||||||
buf.ReplaceRange(i, cb + cb2, bytesBuf[..bytesLen]);
|
|
||||||
// Do not alter i; now that the WTF-8 has been dealt with, apply other filters.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case -2:
|
|
||||||
case -1 or 0xFFFE or 0xFFFF when sanitizeInvalidCharacters:
|
|
||||||
case >= 0xD800 and <= 0xDFFF when sanitizeInvalidCharacters:
|
|
||||||
case > char.MaxValue when sanitizeNonUcs2Characters:
|
|
||||||
{
|
|
||||||
buf.ReplaceRange(i, cb, invalidChar);
|
|
||||||
i += invalidChar.Length;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// See String.Manipulation.cs: IndexOfNewlineChar.
|
|
||||||
// CR; Carriage Return
|
|
||||||
// LF; Line Feed
|
|
||||||
// FF; Form Feed
|
|
||||||
// NEL; Next Line
|
|
||||||
// LS; Line Separator
|
|
||||||
// PS; Paragraph Separator
|
|
||||||
case '\r' or '\n' or '\f' or '\u0085' or '\u2028' or '\u2029' when normalizeLineEndings:
|
|
||||||
{
|
|
||||||
buf.ReplaceRange(i, cb, lineEnding);
|
|
||||||
i += lineEnding.Length;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
i += cb;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -13,7 +13,7 @@ namespace Dalamud.Interface.Utility;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Utility methods for <see cref="ImVectorWrapper{T}"/>.
|
/// Utility methods for <see cref="ImVectorWrapper{T}"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static partial class ImVectorWrapper
|
public static class ImVectorWrapper
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the <see cref="ImVectorWrapper{T}"/> struct, initialized with
|
/// Creates a new instance of the <see cref="ImVectorWrapper{T}"/> struct, initialized with
|
||||||
|
|
@ -208,7 +208,7 @@ public unsafe struct ImVectorWrapper<T> : IList<T>, IList, IReadOnlyList<T>, IDi
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="initialCapacity">The initial capacity.</param>
|
/// <param name="initialCapacity">The initial capacity.</param>
|
||||||
/// <param name="destroyer">The destroyer function to call on item removal.</param>
|
/// <param name="destroyer">The destroyer function to call on item removal.</param>
|
||||||
public ImVectorWrapper(int initialCapacity = 0, ImGuiNativeDestroyDelegate? destroyer = null)
|
public ImVectorWrapper(int initialCapacity, ImGuiNativeDestroyDelegate? destroyer = null)
|
||||||
{
|
{
|
||||||
if (initialCapacity < 0)
|
if (initialCapacity < 0)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue