From fda22de83dd4633d5b938f2cf6c9e76975f9cb0d Mon Sep 17 00:00:00 2001 From: Ellpeck Date: Mon, 17 Jul 2023 15:20:36 +0200 Subject: [PATCH] Fixed TextInput not working correctly when using surrogate pairs --- CHANGELOG.md | 3 +++ MLEM/Font/CodePointSource.cs | 12 ++++++++++++ MLEM/Input/TextInput.cs | 10 ++++++---- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90c3afa..562a04c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,9 @@ Jump to version: Additions - Added GraphicsExtensions.WithRenderTargets, a multi-target version of WithRenderTarget +Fixes +- Fixed TextInput not working correctly when using surrogate pairs + ## 6.2.0 ### MLEM diff --git a/MLEM/Font/CodePointSource.cs b/MLEM/Font/CodePointSource.cs index fe4e3d2..cae77b2 100644 --- a/MLEM/Font/CodePointSource.cs +++ b/MLEM/Font/CodePointSource.cs @@ -63,6 +63,18 @@ namespace MLEM.Font { return (curr, 1); } + /// + /// Returns an index in this code point source that is as close to as possible, but not between two members of a surrogate pair. If the is already not between surrogate pairs, it is returned unchanged. + /// + /// The index to ensure is not between surrogates. + /// Whether the returned index should be increased by 1 (instead of decreased by 1) when it is between surrogates. + /// An index close to , but not between surrogates. + public int EnsureSurrogateBoundary(int index, bool increase) { + if (index < this.Length && char.IsLowSurrogate(this[index])) + return increase && index < this.Length - 1 || index <= 0 ? index + 1 : index - 1; + return index; + } + /// Returns an enumerator that iterates through the collection. /// A that can be used to iterate through the collection. /// 1 diff --git a/MLEM/Input/TextInput.cs b/MLEM/Input/TextInput.cs index f91fc87..2061b05 100644 --- a/MLEM/Input/TextInput.cs +++ b/MLEM/Input/TextInput.cs @@ -105,7 +105,8 @@ namespace MLEM.Input { set { var val = (int) MathHelper.Clamp(value, 0F, this.text.Length); if (this.caretPos != val) { - this.caretPos = val; + // ensure that we don't move to a location that is between high and low surrogates + this.caretPos = new CodePointSource(this.text).EnsureSurrogateBoundary(val, val > this.caretPos); this.caretBlinkTimer = 0; this.SetTextDataDirty(false); } @@ -360,7 +361,7 @@ namespace MLEM.Input { if (!this.FilterText(ref strg, removeMismatching)) return; if (this.MaximumCharacters != null && strg.Length > this.MaximumCharacters) - strg = strg.Substring(0, this.MaximumCharacters.Value); + strg = strg.Substring(0, new CodePointSource(strg).EnsureSurrogateBoundary(this.MaximumCharacters.Value, false)); this.text.Clear(); this.text.Append(strg); this.CaretPos = this.text.Length; @@ -378,7 +379,7 @@ namespace MLEM.Input { if (!this.FilterText(ref strg, removeMismatching)) return false; if (this.MaximumCharacters != null && this.text.Length + strg.Length > this.MaximumCharacters) - strg = strg.Substring(0, this.MaximumCharacters.Value - this.text.Length); + strg = strg.Substring(0, new CodePointSource(strg).EnsureSurrogateBoundary(this.MaximumCharacters.Value - this.text.Length, false)); this.text.Insert(this.CaretPos, strg); this.CaretPos += strg.Length; this.SetTextDataDirty(); @@ -393,7 +394,8 @@ namespace MLEM.Input { public bool RemoveText(int index, int length) { if (index < 0 || index >= this.text.Length) return false; - this.text.Remove(index, length); + var source = new CodePointSource(this.text); + this.text.Remove(source.EnsureSurrogateBoundary(index, false), source.EnsureSurrogateBoundary(index + length, true) - index); // ensure that caret pos is still in bounds this.CaretPos = this.CaretPos; this.SetTextDataDirty();