2019-08-10 19:23:08 +02:00
|
|
|
using System;
|
2019-08-18 17:59:14 +02:00
|
|
|
using System.Linq;
|
2019-08-10 19:23:08 +02:00
|
|
|
using System.Text;
|
|
|
|
using Microsoft.Xna.Framework;
|
|
|
|
using Microsoft.Xna.Framework.Graphics;
|
|
|
|
using Microsoft.Xna.Framework.Input;
|
2019-08-12 19:44:16 +02:00
|
|
|
using MLEM.Extensions;
|
2019-08-10 19:23:08 +02:00
|
|
|
using MLEM.Font;
|
2019-08-30 19:05:27 +02:00
|
|
|
using MLEM.Input;
|
2019-08-10 19:23:08 +02:00
|
|
|
using MLEM.Textures;
|
2019-08-10 21:37:10 +02:00
|
|
|
using MLEM.Ui.Style;
|
2019-08-10 19:23:08 +02:00
|
|
|
|
|
|
|
namespace MLEM.Ui.Elements {
|
|
|
|
public class TextField : Element {
|
|
|
|
|
2019-08-18 17:59:14 +02:00
|
|
|
public static readonly Rule DefaultRule = (field, add) => !add.Any(char.IsControl);
|
|
|
|
public static readonly Rule OnlyLetters = (field, add) => add.All(char.IsLetter);
|
|
|
|
public static readonly Rule OnlyNumbers = (field, add) => add.All(char.IsNumber);
|
|
|
|
public static readonly Rule LettersNumbers = (field, add) => add.All(c => char.IsLetter(c) || char.IsNumber(c));
|
|
|
|
|
2019-08-10 21:37:10 +02:00
|
|
|
public NinePatch Texture;
|
|
|
|
public NinePatch HoveredTexture;
|
|
|
|
public Color HoveredColor;
|
2019-08-10 19:23:08 +02:00
|
|
|
public float TextScale;
|
2019-08-24 12:40:20 +02:00
|
|
|
private readonly StringBuilder text = new StringBuilder();
|
|
|
|
public string Text => this.text.ToString();
|
2019-08-23 18:56:39 +02:00
|
|
|
public string PlaceholderText;
|
2019-08-10 19:23:08 +02:00
|
|
|
public TextChanged OnTextChange;
|
|
|
|
public float TextOffsetX = 4;
|
2019-08-10 21:37:10 +02:00
|
|
|
private IGenericFont font;
|
2019-08-10 19:23:08 +02:00
|
|
|
private double caretBlinkTimer;
|
2019-08-11 00:39:40 +02:00
|
|
|
private int textStartIndex;
|
2019-08-18 17:59:14 +02:00
|
|
|
public Rule InputRule;
|
2019-08-30 19:05:27 +02:00
|
|
|
public string MobileTitle;
|
|
|
|
public string MobileDescription;
|
2019-08-10 19:23:08 +02:00
|
|
|
|
2019-08-18 17:59:14 +02:00
|
|
|
public TextField(Anchor anchor, Vector2 size, Rule rule = null, IGenericFont font = null) : base(anchor, size) {
|
|
|
|
this.InputRule = rule ?? DefaultRule;
|
2019-08-10 21:37:10 +02:00
|
|
|
this.font = font;
|
2019-08-30 19:05:27 +02:00
|
|
|
|
2019-09-01 19:33:33 +02:00
|
|
|
if (WindowExtensions.SupportsTextInput()) {
|
2019-08-30 19:05:27 +02:00
|
|
|
this.OnTextInput += (element, key, character) => {
|
|
|
|
if (!this.IsSelected)
|
|
|
|
return;
|
|
|
|
if (key == Keys.Back) {
|
|
|
|
if (this.text.Length > 0) {
|
|
|
|
this.RemoveText(this.text.Length - 1, 1);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this.AppendText(character);
|
2019-08-10 19:23:08 +02:00
|
|
|
}
|
2019-08-30 19:05:27 +02:00
|
|
|
};
|
|
|
|
} else {
|
|
|
|
this.OnPressed += async e => {
|
|
|
|
if (!KeyboardInput.IsVisible) {
|
|
|
|
var title = this.MobileTitle ?? this.PlaceholderText;
|
|
|
|
var result = await KeyboardInput.Show(title, this.MobileDescription, this.Text);
|
|
|
|
if (result != null)
|
2019-09-01 19:50:17 +02:00
|
|
|
this.SetText(result.Replace('\n', ' '), true);
|
2019-08-30 19:05:27 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
2019-08-24 12:40:20 +02:00
|
|
|
}
|
2019-08-11 00:39:40 +02:00
|
|
|
|
2019-08-24 12:40:20 +02:00
|
|
|
private void HandleTextChange() {
|
|
|
|
// not initialized yet
|
|
|
|
if (this.font == null)
|
|
|
|
return;
|
|
|
|
var length = this.font.MeasureString(this.text).X * this.TextScale;
|
|
|
|
var maxWidth = this.DisplayArea.Width / this.Scale - this.TextOffsetX * 2;
|
|
|
|
if (length > maxWidth) {
|
|
|
|
for (var i = 0; i < this.text.Length; i++) {
|
|
|
|
var substring = this.text.ToString(i, this.text.Length - i);
|
|
|
|
if (this.font.MeasureString(substring).X * this.TextScale <= maxWidth) {
|
|
|
|
this.textStartIndex = i;
|
|
|
|
break;
|
|
|
|
}
|
2019-08-11 00:39:40 +02:00
|
|
|
}
|
2019-08-24 12:40:20 +02:00
|
|
|
} else {
|
|
|
|
this.textStartIndex = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.OnTextChange?.Invoke(this, this.text.ToString());
|
2019-08-10 19:23:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public override void Update(GameTime time) {
|
|
|
|
base.Update(time);
|
|
|
|
|
|
|
|
this.caretBlinkTimer += time.ElapsedGameTime.TotalSeconds;
|
|
|
|
if (this.caretBlinkTimer >= 1)
|
|
|
|
this.caretBlinkTimer = 0;
|
|
|
|
}
|
|
|
|
|
2019-09-04 17:19:31 +02:00
|
|
|
public override void Draw(GameTime time, SpriteBatch batch, float alpha) {
|
2019-08-10 19:23:08 +02:00
|
|
|
var tex = this.Texture;
|
|
|
|
var color = Color.White * alpha;
|
|
|
|
if (this.IsMouseOver) {
|
|
|
|
if (this.HoveredTexture != null)
|
|
|
|
tex = this.HoveredTexture;
|
|
|
|
color = this.HoveredColor * alpha;
|
|
|
|
}
|
2019-09-04 17:19:31 +02:00
|
|
|
batch.Draw(tex, this.DisplayArea, color, this.Scale);
|
2019-08-23 18:56:39 +02:00
|
|
|
|
2019-09-04 17:19:31 +02:00
|
|
|
var textPos = this.DisplayArea.Location.ToVector2() + new Vector2(this.TextOffsetX * this.Scale, this.DisplayArea.Height / 2);
|
2019-08-24 12:40:20 +02:00
|
|
|
if (this.text.Length > 0 || this.IsSelected) {
|
2019-08-23 18:56:39 +02:00
|
|
|
var caret = this.IsSelected && this.caretBlinkTimer >= 0.5F ? "|" : "";
|
2019-08-24 12:40:20 +02:00
|
|
|
var display = this.text.ToString(this.textStartIndex, this.text.Length - this.textStartIndex) + caret;
|
|
|
|
this.font.DrawCenteredString(batch, display, textPos, this.TextScale * this.Scale, Color.White * alpha, false, true);
|
2019-08-23 18:56:39 +02:00
|
|
|
} else if (this.PlaceholderText != null) {
|
|
|
|
this.font.DrawCenteredString(batch, this.PlaceholderText, textPos, this.TextScale * this.Scale, Color.Gray * alpha, false, true);
|
|
|
|
}
|
2019-09-04 17:19:31 +02:00
|
|
|
base.Draw(time, batch, alpha);
|
2019-08-10 19:23:08 +02:00
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:17 +02:00
|
|
|
public void SetText(object text, bool removeMismatching = false) {
|
|
|
|
if (removeMismatching) {
|
|
|
|
var result = new StringBuilder();
|
|
|
|
foreach (var c in text.ToString()) {
|
|
|
|
if (this.InputRule(this, c.ToString()))
|
|
|
|
result.Append(c);
|
|
|
|
}
|
|
|
|
text = result.ToString();
|
|
|
|
} else if (!this.InputRule(this, text.ToString()))
|
2019-08-24 12:40:20 +02:00
|
|
|
return;
|
|
|
|
this.text.Clear();
|
|
|
|
this.text.Append(text);
|
|
|
|
this.HandleTextChange();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void AppendText(object text) {
|
2019-08-30 19:05:27 +02:00
|
|
|
if (!this.InputRule(this, text.ToString()))
|
2019-08-24 12:40:20 +02:00
|
|
|
return;
|
|
|
|
this.text.Append(text);
|
|
|
|
this.HandleTextChange();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void RemoveText(int index, int length) {
|
|
|
|
this.text.Remove(index, length);
|
|
|
|
this.HandleTextChange();
|
|
|
|
}
|
|
|
|
|
2019-08-10 21:37:10 +02:00
|
|
|
protected override void InitStyle(UiStyle style) {
|
|
|
|
base.InitStyle(style);
|
|
|
|
this.TextScale = style.TextScale;
|
|
|
|
this.font = style.Font;
|
|
|
|
this.Texture = style.TextFieldTexture;
|
|
|
|
this.HoveredTexture = style.TextFieldHoveredTexture;
|
|
|
|
this.HoveredColor = style.TextFieldHoveredColor;
|
|
|
|
}
|
|
|
|
|
2019-08-10 19:23:08 +02:00
|
|
|
public delegate void TextChanged(TextField field, string text);
|
|
|
|
|
2019-08-18 17:59:14 +02:00
|
|
|
public delegate bool Rule(TextField field, string textToAdd);
|
|
|
|
|
2019-08-10 19:23:08 +02:00
|
|
|
}
|
|
|
|
}
|