mirror of
https://github.com/Phantop/LADXHD.git
synced 2024-11-01 04:14:22 +00:00
402 lines
14 KiB
C#
402 lines
14 KiB
C#
using System;
|
|
using Microsoft.Xna.Framework;
|
|
using Microsoft.Xna.Framework.Graphics;
|
|
using Microsoft.Xna.Framework.Input;
|
|
using ProjectZ.InGame.Things;
|
|
|
|
namespace ProjectZ.Base.UI
|
|
{
|
|
public class UiTextInput : UiElement
|
|
{
|
|
public Type InputType;
|
|
public float MaxLength;
|
|
|
|
public string StrValue
|
|
{
|
|
get => _strValue;
|
|
set
|
|
{
|
|
_strValue = value;
|
|
_cursorIndex = StrValue.Length;
|
|
_selectionStart = -1;
|
|
}
|
|
}
|
|
|
|
private readonly UiFunction _onTextUpdate;
|
|
private string _strValue = "";
|
|
|
|
private int _cursorIndex;
|
|
|
|
private int _selectionStart = -1;
|
|
private int _selectionEnd = -1;
|
|
private bool _mouseSelecting;
|
|
|
|
private float _buttonDownCounter;
|
|
private int _buttonResetTime = 35;
|
|
private int _buttonResetInitTime = 250;
|
|
|
|
private int _maxCharacterCount;
|
|
private float _cursorCounter;
|
|
private Vector2 _fontSize;
|
|
|
|
private double _lastMouseClick;
|
|
|
|
public UiTextInput(Rectangle rectangle, SpriteFont font, float maxLength,
|
|
string elementId, string screen, UiFunction update, UiFunction onTextUpdate) : base(elementId, screen)
|
|
{
|
|
Rectangle = rectangle;
|
|
UpdateFunction = update;
|
|
_onTextUpdate = onTextUpdate;
|
|
MaxLength = maxLength;
|
|
Font = font;
|
|
|
|
_fontSize = font.MeasureString("A");
|
|
_maxCharacterCount = (int)((rectangle.Width - 10) / _fontSize.X);
|
|
}
|
|
|
|
public override void Update()
|
|
{
|
|
base.Update();
|
|
|
|
_cursorCounter += Game1.DeltaTime;
|
|
|
|
if (InputHandler.KeyDown(Keys.Left) || InputHandler.KeyDown(Keys.Right) ||
|
|
InputHandler.KeyDown(Keys.Up) || InputHandler.KeyDown(Keys.Down) ||
|
|
InputHandler.KeyDown(Keys.Back) || InputHandler.KeyDown(Keys.Delete))
|
|
{
|
|
_buttonDownCounter -= Game1.DeltaTime;
|
|
}
|
|
else
|
|
{
|
|
_buttonDownCounter = _buttonResetInitTime;
|
|
}
|
|
|
|
if (!Selected) return;
|
|
|
|
var mousePosition = InputHandler.MousePosition().ToVector2();
|
|
var newCursorIndex = SetCursor(mousePosition);
|
|
var mouseReleased = InputHandler.MouseLeftReleased();
|
|
|
|
if (InputHandler.MouseLeftPressed())
|
|
{
|
|
// set the cursor to the position the user clicked on
|
|
if (_lastMouseClick < Game1.TotalGameTime - 550 || newCursorIndex != _cursorIndex)
|
|
{
|
|
_mouseSelecting = false;
|
|
_lastMouseClick = Game1.TotalGameTime;
|
|
_cursorIndex = newCursorIndex;
|
|
_selectionStart = -1;
|
|
}
|
|
// select the word the user clicked on
|
|
else
|
|
{
|
|
_selectionStart = CursorPositionSkip(-1);
|
|
_selectionEnd = CursorPositionSkip(1);
|
|
_cursorIndex = _selectionEnd;
|
|
}
|
|
}
|
|
else if (InputHandler.MouseLeftDown() &&
|
|
(_mouseSelecting || (_selectionStart == -1 && newCursorIndex != _cursorIndex)))
|
|
{
|
|
_mouseSelecting = true;
|
|
_selectionStart = Math.Min(_cursorIndex, newCursorIndex);
|
|
_selectionEnd = Math.Max(_cursorIndex, newCursorIndex);
|
|
}
|
|
|
|
if (_mouseSelecting && InputHandler.MouseLeftReleased())
|
|
{
|
|
_mouseSelecting = false;
|
|
_cursorIndex = newCursorIndex;
|
|
}
|
|
|
|
var inputString = InputHandler.ReturnCharacter();
|
|
if (_strValue.Length < MaxLength && inputString != "")
|
|
{
|
|
// delete the selection first
|
|
if (_selectionStart != -1)
|
|
DeleteSelection();
|
|
|
|
_strValue = _strValue.Substring(0, _cursorIndex) + inputString +
|
|
_strValue.Substring(_cursorIndex, _strValue.Length - _cursorIndex);
|
|
|
|
_cursorIndex++;
|
|
}
|
|
|
|
//delete last position
|
|
if ((InputHandler.KeyPressed(Keys.Back) || InputHandler.KeyDown(Keys.Back) &&
|
|
_buttonDownCounter <= 0) && _strValue.Length > 0 && _cursorIndex > 0)
|
|
{
|
|
// delete selection
|
|
if (_selectionStart != -1)
|
|
{
|
|
DeleteSelection();
|
|
}
|
|
else
|
|
{
|
|
_strValue = _strValue.Remove(_cursorIndex - 1, 1);
|
|
_cursorIndex--;
|
|
}
|
|
|
|
_buttonDownCounter += _buttonResetTime;
|
|
_cursorCounter = 0;
|
|
}
|
|
|
|
if ((InputHandler.KeyPressed(Keys.Delete) || InputHandler.KeyDown(Keys.Delete) &&
|
|
_buttonDownCounter <= 0) && _strValue.Length > 0)
|
|
{
|
|
// delete selection
|
|
if (_selectionStart != -1)
|
|
DeleteSelection();
|
|
else if (_cursorIndex < _strValue.Length)
|
|
_strValue = _strValue.Remove(_cursorIndex, 1);
|
|
|
|
_buttonDownCounter += _buttonResetTime;
|
|
_cursorCounter = 0;
|
|
}
|
|
|
|
if (InputHandler.KeyPressed(Keys.Up))
|
|
{
|
|
_cursorIndex = 0;
|
|
_cursorCounter = 0;
|
|
}
|
|
|
|
if (InputHandler.KeyDown(Keys.Down))
|
|
{
|
|
_cursorIndex = _strValue.Length;
|
|
_cursorCounter = 0;
|
|
}
|
|
|
|
// move the cursor to the end of the word
|
|
if (InputHandler.KeyPressed(Keys.Left) || InputHandler.KeyDown(Keys.Left) && _buttonDownCounter <= 0)
|
|
{
|
|
if (InputHandler.KeyDown(Keys.LeftControl))
|
|
_cursorIndex = CursorPositionSkip(-1);
|
|
else
|
|
MoveCursor(-1);
|
|
|
|
if (!InputHandler.KeyDown(Keys.LeftShift))
|
|
_selectionStart = -1;
|
|
|
|
ResetCursorTimer();
|
|
}
|
|
|
|
if (InputHandler.KeyPressed(Keys.Right) || InputHandler.KeyDown(Keys.Right) && _buttonDownCounter <= 0)
|
|
{
|
|
if (InputHandler.KeyDown(Keys.LeftControl))
|
|
_cursorIndex = CursorPositionSkip(1);
|
|
else
|
|
MoveCursor(1);
|
|
|
|
if (!InputHandler.KeyDown(Keys.LeftShift))
|
|
_selectionStart = -1;
|
|
|
|
ResetCursorTimer();
|
|
}
|
|
|
|
if (InputType == typeof(int))
|
|
{
|
|
if (InputHandler.MouseWheelDown())
|
|
AddValue(-1);
|
|
if (InputHandler.MouseWheelUp())
|
|
AddValue(1);
|
|
}
|
|
if (InputType == typeof(bool))
|
|
{
|
|
if (InputHandler.MouseWheelDown() || InputHandler.MouseWheelUp())
|
|
ToggleBool();
|
|
}
|
|
|
|
_cursorIndex = MathHelper.Clamp(_cursorIndex, 0, _strValue.Length);
|
|
|
|
InputHandler.ResetInputState();
|
|
|
|
_onTextUpdate?.Invoke(this);
|
|
}
|
|
|
|
private void DeleteSelection()
|
|
{
|
|
_strValue = _strValue.Remove(_selectionStart, _selectionEnd - _selectionStart);
|
|
|
|
if (_cursorIndex > _selectionStart)
|
|
_cursorIndex -= _selectionEnd - _selectionStart;
|
|
|
|
_selectionStart = -1;
|
|
}
|
|
|
|
private int SetCursor(Vector2 position)
|
|
{
|
|
var textPosition = GetTextPosition();
|
|
|
|
// cursor will be set in front or behind the character depending on where you click
|
|
var posX = (int)((position.X - textPosition.X + _fontSize.X / 2) / _fontSize.X);
|
|
var posY = (int)(position.Y - textPosition.Y) / (int)_fontSize.Y;
|
|
|
|
var xIndex = MathHelper.Clamp(posX, 0, _maxCharacterCount);
|
|
var yIndex = MathHelper.Clamp(posY, 0, _maxCharacterCount);
|
|
|
|
var positionIndex = xIndex + yIndex * _maxCharacterCount;
|
|
|
|
_cursorCounter = 0;
|
|
return MathHelper.Clamp(positionIndex, 0, _strValue.Length);
|
|
}
|
|
|
|
private void MoveCursor(int offset)
|
|
{
|
|
_cursorIndex += offset;
|
|
}
|
|
|
|
private void ResetCursorTimer()
|
|
{
|
|
_buttonDownCounter += _buttonResetTime;
|
|
_cursorCounter = 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Move the cursor in the given direction.
|
|
/// This will try to skip over words.
|
|
/// </summary>
|
|
/// <param name="direction"></param>
|
|
private int CursorPositionSkip(int direction)
|
|
{
|
|
if (_strValue.Length == 0)
|
|
return 0;
|
|
|
|
var offset = direction < 0 ? -1 : 0;
|
|
var characterIndex = _cursorIndex + offset;
|
|
var lastCharacter = -1;
|
|
|
|
while (0 <= characterIndex && characterIndex < _strValue.Length)
|
|
{
|
|
var characterType = GetCharacterType(_strValue[characterIndex]);
|
|
// we break if there is a new character type at the cursor position
|
|
if (lastCharacter != -1 && characterType != 0 && lastCharacter != characterType)
|
|
{
|
|
characterIndex -= offset;
|
|
return characterIndex;
|
|
}
|
|
lastCharacter = characterType;
|
|
|
|
characterIndex += direction;
|
|
}
|
|
|
|
return direction < 0 ? 0 : _strValue.Length;
|
|
}
|
|
|
|
private int GetCharacterType(char character)
|
|
{
|
|
if (character == ' ')
|
|
return 0;
|
|
|
|
if (('a' <= character && character <= 'z') ||
|
|
('A' <= character && character <= 'Z') ||
|
|
('0' <= character && character <= '9') ||
|
|
character == '_')
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
return 2;
|
|
}
|
|
|
|
public void AddValue(int diff)
|
|
{
|
|
var converted = ConvertToType();
|
|
|
|
if (converted == null) return;
|
|
|
|
var intValue = (int)converted + diff;
|
|
_strValue = intValue.ToString();
|
|
}
|
|
|
|
public void ToggleBool()
|
|
{
|
|
var converted = ConvertToType();
|
|
|
|
if (converted == null) return;
|
|
|
|
var boolValue = !(bool)converted;
|
|
_strValue = boolValue.ToString();
|
|
}
|
|
|
|
public object ConvertToType()
|
|
{
|
|
object output = null;
|
|
|
|
if (InputType == typeof(int))
|
|
{
|
|
int.TryParse(_strValue, out var intResult);
|
|
output = intResult;
|
|
}
|
|
else if (InputType == typeof(bool))
|
|
{
|
|
bool.TryParse(_strValue, out var boolResult);
|
|
output = boolResult;
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
public override void Draw(SpriteBatch spriteBatch)
|
|
{
|
|
spriteBatch.Draw(Resources.SprWhite, Rectangle, BackgroundColor);
|
|
|
|
Label = StrValue;
|
|
|
|
// add line breaks if needed
|
|
if (_maxCharacterCount >= 1)
|
|
{
|
|
var breakCount = 1;
|
|
var length = Label.Length;
|
|
while (length > _maxCharacterCount)
|
|
{
|
|
length -= _maxCharacterCount;
|
|
Label = Label.Insert(breakCount * _maxCharacterCount + breakCount - 1, "\n");
|
|
breakCount++;
|
|
}
|
|
}
|
|
|
|
// draw the text
|
|
var textPosition = GetTextPosition();
|
|
|
|
// draw the selection
|
|
if (Selected && _selectionStart != -1)
|
|
{
|
|
var drawIndex = _selectionStart;
|
|
|
|
// draw the selection line by line
|
|
while (drawIndex < _selectionEnd)
|
|
{
|
|
var drawIndexEnd = MathHelper.Min(
|
|
drawIndex + _maxCharacterCount - (drawIndex % _maxCharacterCount), _selectionEnd);
|
|
var drawPosition = new Vector2(
|
|
(drawIndex % _maxCharacterCount) * _fontSize.X,
|
|
(drawIndex / _maxCharacterCount) * _fontSize.Y);
|
|
spriteBatch.Draw(Resources.SprWhite,
|
|
new Rectangle(
|
|
(int)(textPosition.X + drawPosition.X), (int)(textPosition.Y + drawPosition.Y),
|
|
(drawIndexEnd - drawIndex) * (int)_fontSize.X, (int)_fontSize.Y), Color.Blue);
|
|
drawIndex = drawIndexEnd;
|
|
}
|
|
}
|
|
|
|
spriteBatch.DrawString(Font, Label, textPosition, FontColor);
|
|
|
|
// draw the cursor
|
|
if (Selected && _cursorCounter % 700 < 350 && _maxCharacterCount > 0)
|
|
{
|
|
var offset = _cursorIndex != 0 && _cursorIndex % _maxCharacterCount == 0 ? 1 : 0;
|
|
var cursorPosition = new Vector2(
|
|
textPosition.X + (_cursorIndex % _maxCharacterCount + offset * _maxCharacterCount) * _fontSize.X - 3,
|
|
textPosition.Y + (_cursorIndex / _maxCharacterCount - offset) * _fontSize.Y);
|
|
|
|
spriteBatch.DrawString(Font, "|", cursorPosition, FontColor);
|
|
}
|
|
}
|
|
|
|
private Vector2 GetTextPosition()
|
|
{
|
|
var label = Label.Length > 0 ? Label : "0";
|
|
return new Vector2(Rectangle.X + 5, (int)(Rectangle.Y + Rectangle.Height / 2 - Font.MeasureString(label).Y / 2));
|
|
}
|
|
}
|
|
} |