From 5f2eb1845fbd01abff2e2a2d411fdf249b3afdec Mon Sep 17 00:00:00 2001 From: Ellpeck Date: Tue, 12 Oct 2021 19:58:31 +0200 Subject: [PATCH] Fixed text not being pasted into a text field at all if it contains characters that don't match the input rule --- CHANGELOG.md | 1 + MLEM.Ui/Elements/TextField.cs | 273 +++++++++++++++++----------------- 2 files changed, 141 insertions(+), 133 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33263eb..ca657d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ Improvements Fixes - Fixed VerticalSpace height parameter being an integer +- Fixed text not being pasted into a text field at all if it contains characters that don't match the input rule ## 5.1.0 ### MLEM diff --git a/MLEM.Ui/Elements/TextField.cs b/MLEM.Ui/Elements/TextField.cs index ea2ec16..677f7f3 100644 --- a/MLEM.Ui/Elements/TextField.cs +++ b/MLEM.Ui/Elements/TextField.cs @@ -248,6 +248,146 @@ namespace MLEM.Ui.Elements { } } + /// + public override void Update(GameTime time) { + base.Update(time); + + // handle first initialization if not done + if (this.displayedText == null) + this.HandleTextChange(false); + + if (!this.IsSelected || this.IsHidden) + return; + + if (this.Input.IsKeyPressed(Keys.Left)) { + this.CaretPos--; + } else if (this.Input.IsKeyPressed(Keys.Right)) { + this.CaretPos++; + } else if (this.Input.IsKeyPressed(Keys.Home)) { + this.CaretPos = 0; + } else if (this.Input.IsKeyPressed(Keys.End)) { + this.CaretPos = this.text.Length; + } else if (this.Input.IsModifierKeyDown(ModifierKey.Control)) { + if (this.Input.IsKeyPressed(Keys.V)) { + var clip = ClipboardService.GetText(); + if (clip != null) + this.InsertText(clip, true); + } else if (this.Input.IsKeyPressed(Keys.C)) { + // until there is text selection, just copy the whole content + ClipboardService.SetText(this.Text); + } + } + + this.caretBlinkTimer += time.ElapsedGameTime.TotalSeconds; + if (this.caretBlinkTimer >= 1) + this.caretBlinkTimer = 0; + } + + /// + public override void Draw(GameTime time, SpriteBatch batch, float alpha, BlendState blendState, SamplerState samplerState, Matrix matrix) { + var tex = this.Texture; + var color = Color.White * alpha; + if (this.IsMouseOver) { + tex = this.HoveredTexture.OrDefault(tex); + color = (Color) this.HoveredColor * alpha; + } + batch.Draw(tex, this.DisplayArea, color, this.Scale); + + if (this.displayedText != null) { + var lineHeight = this.Font.Value.LineHeight * this.TextScale * this.Scale; + var offset = new Vector2( + this.TextOffsetX * this.Scale, + this.Multiline ? this.TextOffsetX * this.Scale : this.DisplayArea.Height / 2 - lineHeight / 2); + var textPos = this.DisplayArea.Location + offset; + if (this.text.Length > 0 || this.IsSelected) { + var textColor = this.TextColor.OrDefault(Color.White); + this.Font.Value.DrawString(batch, this.displayedText, textPos, textColor * alpha, 0, Vector2.Zero, this.TextScale * this.Scale, SpriteEffects.None, 0); + + if (this.IsSelected && this.caretBlinkTimer < 0.5F) { + var caretDrawPos = textPos + new Vector2(this.caretDrawOffset * this.TextScale * this.Scale, 0); + if (this.Multiline) + caretDrawPos.Y += this.Font.Value.LineHeight * (this.CaretLine - this.lineOffset) * this.TextScale * this.Scale; + batch.Draw(batch.GetBlankTexture(), new RectangleF(caretDrawPos, new Vector2(this.CaretWidth * this.Scale, lineHeight)), null, textColor * alpha); + } + } else if (this.PlaceholderText != null) { + this.Font.Value.DrawString(batch, this.PlaceholderText, textPos, this.PlaceholderColor.OrDefault(Color.Gray) * alpha, 0, Vector2.Zero, this.TextScale * this.Scale, SpriteEffects.None, 0); + } + } + base.Draw(time, batch, alpha, blendState, samplerState, matrix); + } + + /// + /// Replaces this text field's text with the given text. + /// If the resulting exceeds , the end will be cropped to fit. + /// + /// The new text + /// If any characters that don't match the should be left out + public void SetText(object text, bool removeMismatching = false) { + var strg = text?.ToString() ?? string.Empty; + if (!this.FilterText(ref strg, removeMismatching)) + return; + if (this.MaximumCharacters != null && strg.Length > this.MaximumCharacters) + strg = strg.Substring(0, this.MaximumCharacters.Value); + this.text.Clear(); + this.text.Append(strg); + this.CaretPos = this.text.Length; + this.HandleTextChange(); + } + + /// + /// Inserts the given text at the . + /// If the resulting exceeds , the end will be cropped to fit. + /// + /// The text to insert + /// If any characters that don't match the should be left out + public void InsertText(object text, bool removeMismatching = false) { + var strg = text?.ToString() ?? string.Empty; + if (!this.FilterText(ref strg, removeMismatching)) + return; + if (this.MaximumCharacters != null && this.text.Length + strg.Length > this.MaximumCharacters) + strg = strg.Substring(0, this.MaximumCharacters.Value - this.text.Length); + this.text.Insert(this.CaretPos, strg); + this.CaretPos += strg.Length; + this.HandleTextChange(); + } + + /// + /// Removes the given amount of text at the given index + /// + /// The index + /// The amount of text to remove + public void RemoveText(int index, int length) { + if (index < 0 || index >= this.text.Length) + return; + this.text.Remove(index, length); + // ensure that caret pos is still in bounds + this.CaretPos = this.CaretPos; + this.HandleTextChange(); + } + + /// + protected override void InitStyle(UiStyle style) { + base.InitStyle(style); + this.TextScale.SetFromStyle(style.TextScale); + this.Font.SetFromStyle(style.Font); + this.Texture.SetFromStyle(style.TextFieldTexture); + this.HoveredTexture.SetFromStyle(style.TextFieldHoveredTexture); + this.HoveredColor.SetFromStyle(style.TextFieldHoveredColor); + } + + private bool FilterText(ref string text, bool removeMismatching) { + if (removeMismatching) { + var result = new StringBuilder(); + foreach (var c in text) { + if (this.InputRule(this, c.ToCachedString())) + result.Append(c); + } + text = result.ToString(); + } else if (!this.InputRule(this, text)) + return false; + return true; + } + private void HandleTextChange(bool textChanged = true) { // not initialized yet if (!this.Font.HasValue()) @@ -358,139 +498,6 @@ namespace MLEM.Ui.Elements { } } - /// - public override void Update(GameTime time) { - base.Update(time); - - // handle first initialization if not done - if (this.displayedText == null) - this.HandleTextChange(false); - - if (!this.IsSelected || this.IsHidden) - return; - - if (this.Input.IsKeyPressed(Keys.Left)) { - this.CaretPos--; - } else if (this.Input.IsKeyPressed(Keys.Right)) { - this.CaretPos++; - } else if (this.Input.IsKeyPressed(Keys.Home)) { - this.CaretPos = 0; - } else if (this.Input.IsKeyPressed(Keys.End)) { - this.CaretPos = this.text.Length; - } else if (this.Input.IsModifierKeyDown(ModifierKey.Control)) { - if (this.Input.IsKeyPressed(Keys.V)) { - var clip = ClipboardService.GetText(); - if (clip != null) - this.InsertText(clip); - } else if (this.Input.IsKeyPressed(Keys.C)) { - // until there is text selection, just copy the whole content - ClipboardService.SetText(this.Text); - } - } - - this.caretBlinkTimer += time.ElapsedGameTime.TotalSeconds; - if (this.caretBlinkTimer >= 1) - this.caretBlinkTimer = 0; - } - - /// - public override void Draw(GameTime time, SpriteBatch batch, float alpha, BlendState blendState, SamplerState samplerState, Matrix matrix) { - var tex = this.Texture; - var color = Color.White * alpha; - if (this.IsMouseOver) { - tex = this.HoveredTexture.OrDefault(tex); - color = (Color) this.HoveredColor * alpha; - } - batch.Draw(tex, this.DisplayArea, color, this.Scale); - - if (this.displayedText != null) { - var lineHeight = this.Font.Value.LineHeight * this.TextScale * this.Scale; - var offset = new Vector2( - this.TextOffsetX * this.Scale, - this.Multiline ? this.TextOffsetX * this.Scale : this.DisplayArea.Height / 2 - lineHeight / 2); - var textPos = this.DisplayArea.Location + offset; - if (this.text.Length > 0 || this.IsSelected) { - var textColor = this.TextColor.OrDefault(Color.White); - this.Font.Value.DrawString(batch, this.displayedText, textPos, textColor * alpha, 0, Vector2.Zero, this.TextScale * this.Scale, SpriteEffects.None, 0); - - if (this.IsSelected && this.caretBlinkTimer < 0.5F) { - var caretDrawPos = textPos + new Vector2(this.caretDrawOffset * this.TextScale * this.Scale, 0); - if (this.Multiline) - caretDrawPos.Y += this.Font.Value.LineHeight * (this.CaretLine - this.lineOffset) * this.TextScale * this.Scale; - batch.Draw(batch.GetBlankTexture(), new RectangleF(caretDrawPos, new Vector2(this.CaretWidth * this.Scale, lineHeight)), null, textColor * alpha); - } - } else if (this.PlaceholderText != null) { - this.Font.Value.DrawString(batch, this.PlaceholderText, textPos, this.PlaceholderColor.OrDefault(Color.Gray) * alpha, 0, Vector2.Zero, this.TextScale * this.Scale, SpriteEffects.None, 0); - } - } - base.Draw(time, batch, alpha, blendState, samplerState, matrix); - } - - /// - /// Replaces this text field's text with the given text. - /// If the resulting exceeds , the end will be cropped to fit. - /// - /// The new text - /// If any characters that don't match the should be left out - public void SetText(object text, bool removeMismatching = false) { - var strg = text?.ToString() ?? string.Empty; - if (removeMismatching) { - var result = new StringBuilder(); - foreach (var c in strg) { - if (this.InputRule(this, c.ToCachedString())) - result.Append(c); - } - strg = result.ToString(); - } else if (!this.InputRule(this, strg)) - return; - if (this.MaximumCharacters != null && strg.Length > this.MaximumCharacters) - strg = strg.Substring(0, this.MaximumCharacters.Value); - this.text.Clear(); - this.text.Append(strg); - this.CaretPos = this.text.Length; - this.HandleTextChange(); - } - - /// - /// Inserts the given text at the . - /// If the resulting exceeds , the end will be cropped to fit. - /// - /// The text to insert - public void InsertText(object text) { - var strg = text.ToString(); - if (!this.InputRule(this, strg)) - return; - if (this.MaximumCharacters != null && this.text.Length + strg.Length > this.MaximumCharacters) - strg = strg.Substring(0, this.MaximumCharacters.Value - this.text.Length); - this.text.Insert(this.CaretPos, strg); - this.CaretPos += strg.Length; - this.HandleTextChange(); - } - - /// - /// Removes the given amount of text at the given index - /// - /// The index - /// The amount of text to remove - public void RemoveText(int index, int length) { - if (index < 0 || index >= this.text.Length) - return; - this.text.Remove(index, length); - // ensure that caret pos is still in bounds - this.CaretPos = this.CaretPos; - this.HandleTextChange(); - } - - /// - protected override void InitStyle(UiStyle style) { - base.InitStyle(style); - this.TextScale.SetFromStyle(style.TextScale); - this.Font.SetFromStyle(style.Font); - this.Texture.SetFromStyle(style.TextFieldTexture); - this.HoveredTexture.SetFromStyle(style.TextFieldHoveredTexture); - this.HoveredColor.SetFromStyle(style.TextFieldHoveredColor); - } - /// /// A delegate method used for ///