mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-15 13:14:17 +01:00
Temporarily fix IME
This commit is contained in:
parent
245e1614e8
commit
20842a64e2
2 changed files with 142 additions and 219 deletions
|
|
@ -46,24 +46,24 @@ internal sealed unsafe class DalamudIme : IInternalDisposableService
|
||||||
.ToDictionary(x => x.Item1, x => x.Name);
|
.ToDictionary(x => x.Item1, x => x.Name);
|
||||||
|
|
||||||
private static readonly UnicodeRange[] HanRange =
|
private static readonly UnicodeRange[] HanRange =
|
||||||
{
|
[
|
||||||
UnicodeRanges.CjkRadicalsSupplement,
|
UnicodeRanges.CjkRadicalsSupplement,
|
||||||
UnicodeRanges.CjkSymbolsandPunctuation,
|
UnicodeRanges.CjkSymbolsandPunctuation,
|
||||||
UnicodeRanges.CjkUnifiedIdeographsExtensionA,
|
UnicodeRanges.CjkUnifiedIdeographsExtensionA,
|
||||||
UnicodeRanges.CjkUnifiedIdeographs,
|
UnicodeRanges.CjkUnifiedIdeographs,
|
||||||
UnicodeRanges.CjkCompatibilityIdeographs,
|
UnicodeRanges.CjkCompatibilityIdeographs,
|
||||||
UnicodeRanges.CjkCompatibilityForms,
|
UnicodeRanges.CjkCompatibilityForms
|
||||||
// No more; Extension B~ are outside BMP range
|
// No more; Extension B~ are outside BMP range
|
||||||
};
|
];
|
||||||
|
|
||||||
private static readonly UnicodeRange[] HangulRange =
|
private static readonly UnicodeRange[] HangulRange =
|
||||||
{
|
[
|
||||||
UnicodeRanges.HangulJamo,
|
UnicodeRanges.HangulJamo,
|
||||||
UnicodeRanges.HangulSyllables,
|
UnicodeRanges.HangulSyllables,
|
||||||
UnicodeRanges.HangulCompatibilityJamo,
|
UnicodeRanges.HangulCompatibilityJamo,
|
||||||
UnicodeRanges.HangulJamoExtendedA,
|
UnicodeRanges.HangulJamoExtendedA,
|
||||||
UnicodeRanges.HangulJamoExtendedB,
|
UnicodeRanges.HangulJamoExtendedB
|
||||||
};
|
];
|
||||||
|
|
||||||
[ServiceManager.ServiceDependency]
|
[ServiceManager.ServiceDependency]
|
||||||
private readonly DalamudConfiguration dalamudConfiguration = Service<DalamudConfiguration>.Get();
|
private readonly DalamudConfiguration dalamudConfiguration = Service<DalamudConfiguration>.Get();
|
||||||
|
|
@ -109,24 +109,6 @@ internal sealed unsafe class DalamudIme : IInternalDisposableService
|
||||||
private bool updateInputLanguage = true;
|
private bool updateInputLanguage = true;
|
||||||
private bool updateImeStatusAgain;
|
private bool updateImeStatusAgain;
|
||||||
|
|
||||||
[SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1003:Symbols should be spaced correctly", Justification = ".")]
|
|
||||||
static DalamudIme()
|
|
||||||
{
|
|
||||||
nint cimgui;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_ = ImGui.GetCurrentContext();
|
|
||||||
|
|
||||||
cimgui = Process.GetCurrentProcess().Modules.Cast<ProcessModule>()
|
|
||||||
.First(x => x.ModuleName == "cimgui.dll")
|
|
||||||
.BaseAddress;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[ServiceManager.ServiceConstructor]
|
[ServiceManager.ServiceConstructor]
|
||||||
private DalamudIme(InterfaceManager.InterfaceManagerWithScene imws)
|
private DalamudIme(InterfaceManager.InterfaceManagerWithScene imws)
|
||||||
{
|
{
|
||||||
|
|
@ -170,11 +152,11 @@ internal sealed unsafe class DalamudIme : IInternalDisposableService
|
||||||
if (!ImGui.GetIO().ConfigInputTextCursorBlink)
|
if (!ImGui.GetIO().ConfigInputTextCursorBlink)
|
||||||
return true;
|
return true;
|
||||||
var textState = GetInputTextState();
|
var textState = GetInputTextState();
|
||||||
if (textState->Id == 0 || (textState->Flags & ImGuiInputTextFlags.ReadOnly) != 0)
|
if (textState.ID == 0 || (textState.Flags & ImGuiInputTextFlags.ReadOnly) != 0)
|
||||||
return true;
|
return true;
|
||||||
if (textState->CursorAnim <= 0)
|
if (textState.CursorAnim <= 0)
|
||||||
return true;
|
return true;
|
||||||
return textState->CursorAnim % 1.2f <= 0.8f;
|
return textState.CursorAnim % 1.2f <= 0.8f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -227,11 +209,8 @@ internal sealed unsafe class DalamudIme : IInternalDisposableService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ImGuiInputTextStateWrapper* GetInputTextState()
|
private static ImGuiInputTextStatePtr GetInputTextState() =>
|
||||||
{
|
(ImGuiInputTextState*)((nint)ImGui.GetCurrentContext().Handle + 0x4588);
|
||||||
var ctx = ImGui.GetCurrentContext();
|
|
||||||
return (ImGuiInputTextStateWrapper*)&ctx.Handle->InputTextState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static (string String, bool Supported) ToUcs2(char* data, int nc = -1)
|
private static (string String, bool Supported) ToUcs2(char* data, int nc = -1)
|
||||||
{
|
{
|
||||||
|
|
@ -332,7 +311,7 @@ internal sealed unsafe class DalamudIme : IInternalDisposableService
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var textState = GetInputTextState();
|
var textState = GetInputTextState();
|
||||||
var invalidTarget = textState->Id == 0 || (textState->Flags & ImGuiInputTextFlags.ReadOnly) != 0;
|
var invalidTarget = textState.ID == 0 || (textState.Flags & ImGuiInputTextFlags.ReadOnly) != 0;
|
||||||
|
|
||||||
#if IMEDEBUG
|
#if IMEDEBUG
|
||||||
switch (args.Message)
|
switch (args.Message)
|
||||||
|
|
@ -564,17 +543,17 @@ internal sealed unsafe class DalamudIme : IInternalDisposableService
|
||||||
var textState = GetInputTextState();
|
var textState = GetInputTextState();
|
||||||
if (this.temporaryUndoSelection is not null)
|
if (this.temporaryUndoSelection is not null)
|
||||||
{
|
{
|
||||||
textState->Undo();
|
textState.Undo();
|
||||||
textState->SelectionTuple = this.temporaryUndoSelection.Value;
|
textState.SetSelectionTuple(this.temporaryUndoSelection.Value);
|
||||||
this.temporaryUndoSelection = null;
|
this.temporaryUndoSelection = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
textState->SanitizeSelectionRange();
|
textState.SanitizeSelectionRange();
|
||||||
if (textState->ReplaceSelectionAndPushUndo(newString))
|
if (textState.ReplaceSelectionAndPushUndo(newString))
|
||||||
this.temporaryUndoSelection = textState->SelectionTuple;
|
this.temporaryUndoSelection = textState.GetSelectionTuple();
|
||||||
|
|
||||||
// Put the cursor at the beginning, so that the candidate window appears aligned with the text.
|
// Put the cursor at the beginning, so that the candidate window appears aligned with the text.
|
||||||
textState->SetSelectionRange(textState->SelectionTuple.Start, newString.Length, 0);
|
textState.SetSelectionRange(textState.GetSelectionTuple().Start, newString.Length, 0);
|
||||||
|
|
||||||
if (finalCommit)
|
if (finalCommit)
|
||||||
{
|
{
|
||||||
|
|
@ -621,7 +600,7 @@ internal sealed unsafe class DalamudIme : IInternalDisposableService
|
||||||
this.temporaryUndoSelection = null;
|
this.temporaryUndoSelection = null;
|
||||||
|
|
||||||
var textState = GetInputTextState();
|
var textState = GetInputTextState();
|
||||||
textState->Stb.SelectStart = textState->Stb.Cursor = textState->Stb.SelectEnd;
|
textState.Stb.SelectStart = textState.Stb.Cursor = textState.Stb.SelectEnd;
|
||||||
|
|
||||||
this.candidateStrings.Clear();
|
this.candidateStrings.Clear();
|
||||||
this.immCandNative = default;
|
this.immCandNative = default;
|
||||||
|
|
@ -931,185 +910,6 @@ internal sealed unsafe class DalamudIme : IInternalDisposableService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Ported from imstb_textedit.h.
|
|
||||||
/// </summary>
|
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 0xE2C)]
|
|
||||||
private struct StbTextEditState
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Position of the text cursor within the string.
|
|
||||||
/// </summary>
|
|
||||||
public int Cursor;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Selection start point.
|
|
||||||
/// </summary>
|
|
||||||
public int SelectStart;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// selection start and end point in characters; if equal, no selection.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Note that start may be less than or greater than end (e.g. when dragging the mouse,
|
|
||||||
/// start is where the initial click was, and you can drag in either direction.)
|
|
||||||
/// </remarks>
|
|
||||||
public int SelectEnd;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Each text field keeps its own insert mode state.
|
|
||||||
/// To keep an app-wide insert mode, copy this value in/out of the app state.
|
|
||||||
/// </summary>
|
|
||||||
public byte InsertMode;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Page size in number of row.
|
|
||||||
/// This value MUST be set to >0 for pageup or pagedown in multilines documents.
|
|
||||||
/// </summary>
|
|
||||||
public int RowCountPerPage;
|
|
||||||
|
|
||||||
// Remainder is stb-private data.
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
private struct ImGuiInputTextStateWrapper
|
|
||||||
{
|
|
||||||
public uint Id;
|
|
||||||
public int CurLenW;
|
|
||||||
public int CurLenA;
|
|
||||||
public ImVector<char> TextWRaw;
|
|
||||||
public ImVector<byte> TextARaw;
|
|
||||||
public ImVector<byte> InitialTextARaw;
|
|
||||||
public bool TextAIsValid;
|
|
||||||
public int BufCapacityA;
|
|
||||||
public float ScrollX;
|
|
||||||
public StbTextEditState Stb;
|
|
||||||
public float CursorAnim;
|
|
||||||
public bool CursorFollow;
|
|
||||||
public bool SelectedAllMouseLock;
|
|
||||||
public bool Edited;
|
|
||||||
public ImGuiInputTextFlags Flags;
|
|
||||||
|
|
||||||
public ImVectorWrapper<char> TextW => new((ImVector*)&this.ThisWrapperPtr->TextWRaw);
|
|
||||||
|
|
||||||
public (int Start, int End, int Cursor) SelectionTuple
|
|
||||||
{
|
|
||||||
get => (this.Stb.SelectStart, this.Stb.SelectEnd, this.Stb.Cursor);
|
|
||||||
set => (this.Stb.SelectStart, this.Stb.SelectEnd, this.Stb.Cursor) = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ImGuiInputTextStateWrapper* ThisWrapperPtr => (ImGuiInputTextStateWrapper*)Unsafe.AsPointer(ref this);
|
|
||||||
|
|
||||||
private ImGuiInputTextState* ThisPtr => (ImGuiInputTextState*)Unsafe.AsPointer(ref this);
|
|
||||||
|
|
||||||
public void SetSelectionRange(int offset, int length, int relativeCursorOffset)
|
|
||||||
{
|
|
||||||
this.Stb.SelectStart = offset;
|
|
||||||
this.Stb.SelectEnd = offset + length;
|
|
||||||
if (relativeCursorOffset >= 0)
|
|
||||||
this.Stb.Cursor = this.Stb.SelectStart + relativeCursorOffset;
|
|
||||||
else
|
|
||||||
this.Stb.Cursor = this.Stb.SelectEnd + 1 + relativeCursorOffset;
|
|
||||||
this.SanitizeSelectionRange();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SanitizeSelectionRange()
|
|
||||||
{
|
|
||||||
ref var s = ref this.Stb.SelectStart;
|
|
||||||
ref var e = ref this.Stb.SelectEnd;
|
|
||||||
ref var c = ref this.Stb.Cursor;
|
|
||||||
s = Math.Clamp(s, 0, this.CurLenW);
|
|
||||||
e = Math.Clamp(e, 0, this.CurLenW);
|
|
||||||
c = Math.Clamp(c, 0, this.CurLenW);
|
|
||||||
if (s == e)
|
|
||||||
s = e = c;
|
|
||||||
if (s > e)
|
|
||||||
(s, e) = (e, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Undo() => ImGuiP.Custom_StbTextUndo(this.ThisPtr);
|
|
||||||
|
|
||||||
public bool MakeUndoReplace(int offset, int oldLength, int newLength)
|
|
||||||
{
|
|
||||||
if (oldLength == 0 && newLength == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
ImGuiP.Custom_StbTextMakeUndoReplace(this.ThisPtr, offset, oldLength, newLength);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ReplaceSelectionAndPushUndo(ReadOnlySpan<char> newText)
|
|
||||||
{
|
|
||||||
var off = this.Stb.SelectStart;
|
|
||||||
var len = this.Stb.SelectEnd - this.Stb.SelectStart;
|
|
||||||
return this.MakeUndoReplace(off, len, newText.Length) && this.ReplaceChars(off, len, newText);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ReplaceChars(int pos, int len, ReadOnlySpan<char> newText)
|
|
||||||
{
|
|
||||||
this.DeleteChars(pos, len);
|
|
||||||
return this.InsertChars(pos, newText);
|
|
||||||
}
|
|
||||||
|
|
||||||
// See imgui_widgets.cpp: STB_TEXTEDIT_DELETECHARS
|
|
||||||
public void DeleteChars(int pos, int n)
|
|
||||||
{
|
|
||||||
if (n == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var dst = this.TextW.Data + pos;
|
|
||||||
|
|
||||||
// We maintain our buffer length in both UTF-8 and wchar formats
|
|
||||||
this.Edited = true;
|
|
||||||
this.CurLenA -= Encoding.UTF8.GetByteCount(dst, n);
|
|
||||||
this.CurLenW -= n;
|
|
||||||
|
|
||||||
// Offset remaining text (FIXME-OPT: Use memmove)
|
|
||||||
var src = this.TextW.Data + pos + n;
|
|
||||||
int i;
|
|
||||||
for (i = 0; src[i] != 0; i++)
|
|
||||||
dst[i] = src[i];
|
|
||||||
dst[i] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
// See imgui_widgets.cpp: STB_TEXTEDIT_INSERTCHARS
|
|
||||||
public bool InsertChars(int pos, ReadOnlySpan<char> newText)
|
|
||||||
{
|
|
||||||
if (newText.Length == 0)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
var isResizable = (this.Flags & ImGuiInputTextFlags.CallbackResize) != 0;
|
|
||||||
var textLen = this.CurLenW;
|
|
||||||
Debug.Assert(pos <= textLen, "pos <= text_len");
|
|
||||||
|
|
||||||
var newTextLenUtf8 = Encoding.UTF8.GetByteCount(newText);
|
|
||||||
if (!isResizable && newTextLenUtf8 + this.CurLenA + 1 > this.BufCapacityA)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Grow internal buffer if needed
|
|
||||||
if (newText.Length + textLen + 1 > this.TextW.Length)
|
|
||||||
{
|
|
||||||
if (!isResizable)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Debug.Assert(textLen < this.TextW.Length, "text_len < this.TextW.Length");
|
|
||||||
this.TextW.Resize(textLen + Math.Clamp(newText.Length * 4, 32, Math.Max(256, newText.Length)) + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
var text = this.TextW.DataSpan;
|
|
||||||
if (pos != textLen)
|
|
||||||
text.Slice(pos, textLen - pos).CopyTo(text[(pos + newText.Length)..]);
|
|
||||||
newText.CopyTo(text[pos..]);
|
|
||||||
|
|
||||||
this.Edited = true;
|
|
||||||
this.CurLenW += newText.Length;
|
|
||||||
this.CurLenA += newTextLenUtf8;
|
|
||||||
this.TextW[this.CurLenW] = '\0';
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if IMEDEBUG
|
#if IMEDEBUG
|
||||||
private static class ImeDebug
|
private static class ImeDebug
|
||||||
{
|
{
|
||||||
|
|
|
||||||
123
Dalamud/Interface/Internal/ImGuiInputTextStatePtrExtensions.cs
Normal file
123
Dalamud/Interface/Internal/ImGuiInputTextStatePtrExtensions.cs
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using Dalamud.Bindings.ImGui;
|
||||||
|
|
||||||
|
namespace Dalamud.Interface.Internal;
|
||||||
|
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
internal static unsafe class ImGuiInputTextStatePtrExtensions
|
||||||
|
{
|
||||||
|
public static (int Start, int End, int Cursor) GetSelectionTuple(this ImGuiInputTextStatePtr self) =>
|
||||||
|
(self.Stb.SelectStart, self.Stb.SelectEnd, self.Stb.Cursor);
|
||||||
|
|
||||||
|
public static void SetSelectionTuple(this ImGuiInputTextStatePtr self, (int Start, int End, int Cursor) value) =>
|
||||||
|
(self.Stb.SelectStart, self.Stb.SelectEnd, self.Stb.Cursor) = value;
|
||||||
|
|
||||||
|
public static void SetSelectionRange(this ImGuiInputTextStatePtr self, int offset, int length, int relativeCursorOffset)
|
||||||
|
{
|
||||||
|
self.Stb.SelectStart = offset;
|
||||||
|
self.Stb.SelectEnd = offset + length;
|
||||||
|
if (relativeCursorOffset >= 0)
|
||||||
|
self.Stb.Cursor = self.Stb.SelectStart + relativeCursorOffset;
|
||||||
|
else
|
||||||
|
self.Stb.Cursor = self.Stb.SelectEnd + 1 + relativeCursorOffset;
|
||||||
|
self.SanitizeSelectionRange();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SanitizeSelectionRange(this ImGuiInputTextStatePtr self)
|
||||||
|
{
|
||||||
|
ref var s = ref self.Stb.SelectStart;
|
||||||
|
ref var e = ref self.Stb.SelectEnd;
|
||||||
|
ref var c = ref self.Stb.Cursor;
|
||||||
|
s = Math.Clamp(s, 0, self.CurLenW);
|
||||||
|
e = Math.Clamp(e, 0, self.CurLenW);
|
||||||
|
c = Math.Clamp(c, 0, self.CurLenW);
|
||||||
|
if (s == e)
|
||||||
|
s = e = c;
|
||||||
|
if (s > e)
|
||||||
|
(s, e) = (e, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Undo(this ImGuiInputTextStatePtr self) => ImGuiP.Custom_StbTextUndo(self);
|
||||||
|
|
||||||
|
public static bool MakeUndoReplace(this ImGuiInputTextStatePtr self, int offset, int oldLength, int newLength)
|
||||||
|
{
|
||||||
|
if (oldLength == 0 && newLength == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ImGuiP.Custom_StbTextMakeUndoReplace(self, offset, oldLength, newLength);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool ReplaceSelectionAndPushUndo(this ImGuiInputTextStatePtr self, ReadOnlySpan<char> newText)
|
||||||
|
{
|
||||||
|
var off = self.Stb.SelectStart;
|
||||||
|
var len = self.Stb.SelectEnd - self.Stb.SelectStart;
|
||||||
|
return self.MakeUndoReplace(off, len, newText.Length) && self.ReplaceChars(off, len, newText);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool ReplaceChars(this ImGuiInputTextStatePtr self, int pos, int len, ReadOnlySpan<char> newText)
|
||||||
|
{
|
||||||
|
self.DeleteChars(pos, len);
|
||||||
|
return self.InsertChars(pos, newText);
|
||||||
|
}
|
||||||
|
|
||||||
|
// See imgui_widgets.cpp: STB_TEXTEDIT_DELETECHARS
|
||||||
|
public static void DeleteChars(this ImGuiInputTextStatePtr self, int pos, int n)
|
||||||
|
{
|
||||||
|
if (n == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var dst = (char*)self.TextW.Data + pos;
|
||||||
|
|
||||||
|
// We maintain our buffer length in both UTF-8 and wchar formats
|
||||||
|
self.Edited = true;
|
||||||
|
self.CurLenA -= Encoding.UTF8.GetByteCount(dst, n);
|
||||||
|
self.CurLenW -= n;
|
||||||
|
|
||||||
|
// Offset remaining text (FIXME-OPT: Use memmove)
|
||||||
|
var src = (char*)self.TextW.Data + pos + n;
|
||||||
|
int i;
|
||||||
|
for (i = 0; src[i] != 0; i++)
|
||||||
|
dst[i] = src[i];
|
||||||
|
dst[i] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// See imgui_widgets.cpp: STB_TEXTEDIT_INSERTCHARS
|
||||||
|
public static bool InsertChars(this ImGuiInputTextStatePtr self, int pos, ReadOnlySpan<char> newText)
|
||||||
|
{
|
||||||
|
if (newText.Length == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
var isResizable = (self.Flags & ImGuiInputTextFlags.CallbackResize) != 0;
|
||||||
|
var textLen = self.CurLenW;
|
||||||
|
Debug.Assert(pos <= textLen, "pos <= text_len");
|
||||||
|
|
||||||
|
var newTextLenUtf8 = Encoding.UTF8.GetByteCount(newText);
|
||||||
|
if (!isResizable && newTextLenUtf8 + self.CurLenA + 1 > self.BufCapacityA)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Grow internal buffer if needed
|
||||||
|
if (newText.Length + textLen + 1 > self.TextW.Size)
|
||||||
|
{
|
||||||
|
if (!isResizable)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Debug.Assert(textLen < self.TextW.Size, "text_len < self.TextW.Length");
|
||||||
|
self.TextW.Resize(textLen + Math.Clamp(newText.Length * 4, 32, Math.Max(256, newText.Length)) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var text = new Span<char>(self.TextW.Data, self.TextW.Size);
|
||||||
|
if (pos != textLen)
|
||||||
|
text.Slice(pos, textLen - pos).CopyTo(text[(pos + newText.Length)..]);
|
||||||
|
newText.CopyTo(text[pos..]);
|
||||||
|
|
||||||
|
self.Edited = true;
|
||||||
|
self.CurLenW += newText.Length;
|
||||||
|
self.CurLenA += newTextLenUtf8;
|
||||||
|
self.TextW[self.CurLenW] = '\0';
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue