Login Menu with user inputs

I need to add two user inputs (username and password) to my login form in monogame. Is there any package or class that does this?

No, lul. There were some generic GUI packages, but nothing like this is built in.

I made some progress. Right now I have a menu with two inputs and a button. My problem now is that when I press a key(for example “a”) it writes more than one char.


Another problem that I have is that it is writing in both inputs. I am thinking about solving this using a boolean .Is it viable?

Here is my Input and _Keyboard classes

  public class Input : Basic2D
    {
        private _Keyboard keyboard;
        private string inputText;
        private SpriteFont font;

        public Input( Vector2 _position, Vector2 _dimensions): base("2D\\UI\\inputBox", _position, _dimensions)
        {
            keyboard = new _Keyboard();
            inputText = "";
            font = Globals.content.Load<SpriteFont>("Fonts\\Arial16");
            position = _position;
        }

        public override void Update(Vector2 _offset)
        {

            keyboard.Update();
            if (keyboard.GetPress("A"))
            {
                inputText += "a";

            }
            if (keyboard.GetPress("D"))
            {
                inputText += "d";
            }
            if (keyboard.GetPress("W"))
            {
                inputText += "m";

            }

            if (keyboard.GetPress("S"))
            {
                inputText += "s";

            }


            keyboard.UpdateOld();
            mouseControl.Update();
        }

      

        public override void Draw(Vector2 _offset)
        {

            string tempStr = inputText;
            Vector2 strDims = font.MeasureString(tempStr);
            Globals.spriteBatch.DrawString(font, tempStr, new Vector2(_offset.X - strDims.X / 2, _offset.Y-10), Color.Black);
            base.Draw(_offset);

        
        }
    }
   public class _Keyboard
    {

        public KeyboardState newKeyboard, oldKeyboard;       //newKeyboard tracks the actual frame while oldKeyboard tracks the previous frame

        public List<_Key> pressedKeys = new List<_Key>(), previousPressedKeys = new List<_Key>();

        public _Keyboard()
        {

        }

        public virtual void Update()
        {
            newKeyboard = Keyboard.GetState();

            GetPressedKeys();

        }

        public void UpdateOld()
        {
            oldKeyboard = newKeyboard;

            previousPressedKeys = new List<_Key>();
            for (int i = 0; i < pressedKeys.Count; i++)
            {
                previousPressedKeys.Add(pressedKeys[i]);
            }
        }


        public bool GetPress(string KEY)
        {

            for (int i = 0; i < pressedKeys.Count; i++)
            {

                if (pressedKeys[i].key == KEY)
                {
                    return true;
                }

            }


            return false;
        }

        public virtual void GetPressedKeys()
        {
            pressedKeys.Clear();
            for (int i = 0; i < newKeyboard.GetPressedKeys().Length; i++)
            {

                pressedKeys.Add(new _Key(newKeyboard.GetPressedKeys()[i].ToString(), 1));

            }
        }

        public virtual bool GetSinglePress(string KEY)
        {
            for (int i = 0; i < pressedKeys.Count; i++)
            {
                bool isIn = false;

                for(int j = 0; j < previousPressedKeys.Count; j++)
                {
                    if(pressedKeys[i].key == previousPressedKeys[j].key)
                    {
                        isIn = true;
                        break;
                    }
                }

                if(!isIn && (pressedKeys[i].key == KEY || pressedKeys[i].print == KEY))
                {
                    return true;
                }
            }

            return false;
        }

    }
'''

Because this is not how you do typing.
Reference: https://github.com/Martenfur/Monofoxe/blob/develop/Monofoxe/Monofoxe.Engine.DesktopGL/TextInputBInderDesktopGL.cs

Depending on which platform you are targeting, there might be other options. If all you are targeting is Windows, then you could add a reference to Forms and just add in a form for that type of input. It would act something like a splash screen, or a pop-over. Of course, if you are targeting multiple platforms, that wouldn’t be the way to go, and adding Xamarin to cover that part of it might be more effort than it is worth.

I made some incredible progress(in my opinion) and now my problem is almost solved. I can already make the player input his username and password. If the Shift Key is being pressed or the CapsLock active is the only thing that is missing to me. Summing up, I am only missing “special characterd” like ! and _ for example and upper case letters. I don´t know if it is of any help but I will leave my code below.

 public class Input : Basic2D
    {
        private _Keyboard keyboard;
        private string inputText;
        private SpriteFont font;
        private bool hovered, pressed, active, confidential;

        public Input( Vector2 _position, Vector2 _dimensions,bool _confidential): base("2D\\UI\\inputBox", _position, _dimensions)
        {
            keyboard = new _Keyboard();
            inputText = "";
            font = Globals.content.Load<SpriteFont>("Fonts\\Arial16");
            position = _position;
            hovered = false;
            pressed = false;
            active = false;
            confidential = _confidential;
        }

        public  void Update(Vector2 _offset,Input _input)
        {
            
            mouseControl.Update();
            if (Hover(_offset))
            {
                hovered = true;

                if (mouseControl.LeftClick())
                {

                    hovered = false;
                    pressed = true;
                }

                else if (mouseControl.LeftClickRelease())
                {
                    active = true;
                    _input.active = false;

                }
            }
            else
            {
                hovered = false;
            }

            if (!mouseControl.LeftClick() && !mouseControl.LeftClickHold())
            {
                pressed = false;
            }

            mouseControl.UpdateOld();
            if (active)
            {
                keyboard.Update();
                List<_Key> pressedKeys = keyboard.ReturnKeysPressed();
                for (int i = 0; i < pressedKeys.Count; i++)
                {
                    if (keyboard.GetSinglePress(pressedKeys[i].key))
                    {
                        inputText += pressedKeys[i].print;
                    }
                }


                keyboard.UpdateOld();
            }
        }

      

        public override void Draw(Vector2 _offset)
        {
            string tempString;
            if (!confidential)
            {
                tempString = inputText;
            }
            else  
            {
                tempString = "";
                for (int i = 0; i < inputText.Length; i++)
                {
                    tempString += "*";
                }
            }
           
            Vector2 strDims = font.MeasureString(tempString);
            Globals.spriteBatch.DrawString(font, tempString, new Vector2(_offset.X - strDims.X / 2, _offset.Y-10), Color.Black);
            base.Draw(_offset);

        
        }
    }

Where you do inputText += you need to test if either the shift keys are down using IsKeyDown and Keys.LeftShift and Keys.RightShift and append the upper case version of the key. For the digits you will have to have a switch because there is no mapping but for letters you could try the ToString method.

I am not using the framework keyboard and key classes so should I implement IsKeyDown in mine or wouldn´t be worth and should I just use the static Keyboard instead?

 public class _Keyboard
    {

        public KeyboardState newKeyboard, oldKeyboard;       //newKeyboard tracks the actual frame while oldKeyboard tracks the previous frame

        public List<_Key> pressedKeys = new List<_Key>(), previousPressedKeys = new List<_Key>();

        public _Keyboard()
        {

        }

        public virtual void Update()
        {
            newKeyboard = Keyboard.GetState();

            GetPressedKeys();

        }

        public void UpdateOld()
        {
            oldKeyboard = newKeyboard;

            previousPressedKeys = new List<_Key>();
            for (int i = 0; i < pressedKeys.Count; i++)
            {
                previousPressedKeys.Add(pressedKeys[i]);
            }
        }


        public bool GetPress(string KEY)
        {

            for (int i = 0; i < pressedKeys.Count; i++)
            {

                if (pressedKeys[i].key == KEY)
                {
                    return true;
                }

            }


            return false;
        }

        public virtual void GetPressedKeys()
        {
            pressedKeys.Clear();
            for (int i = 0; i < newKeyboard.GetPressedKeys().Length; i++)
            {

                pressedKeys.Add(new _Key(newKeyboard.GetPressedKeys()[i].ToString(), 1));

            }
        }

        public List<_Key> ReturnKeysPressed()
        {
            return pressedKeys;
        }

        public virtual bool GetSinglePress(string KEY)
        {
            for (int i = 0; i < pressedKeys.Count; i++)
            {
                bool isIn = false;

                for(int j = 0; j < previousPressedKeys.Count; j++)
                {
                    if(pressedKeys[i].key == previousPressedKeys[j].key)
                    {
                        isIn = true;
                        break;
                    }
                }

                if(!isIn && (pressedKeys[i].key == KEY || pressedKeys[i].print == KEY))
                {
                    return true;
                }
            }

            return false;
        }

    }

 public class _Key
    {
        public int state;
        public string key, print, display;

        public _Key(string _key, int _state)
        {
            key = _key;                             //name of the key
            state = _state;                         //starts as 1, when key is held down it´s value is 2
            MakePrint(key);
        }

        public virtual void Update()
        {
            state = 2;

        }

        public void MakePrint(string KEY)
        {
            display = KEY;

            string tempStr = "";

            if (KEY == "A" || KEY == "B" || KEY == "C" || KEY == "D" || KEY == "E" || KEY == "F" || KEY == "G" || KEY == "H" || KEY == "I" || KEY == "J" || KEY == "K" || KEY == "L" || KEY == "M" || KEY == "N" || KEY == "O" || KEY == "P" || KEY == "Q" || KEY == "R" || KEY == "S" || KEY == "T" || KEY == "U" || KEY == "V" || KEY == "W" || KEY == "X" || KEY == "Y" || KEY == "Z")
            {
                tempStr = KEY.ToLower();
            }
            if (KEY == "Space")
            {
                tempStr = " ";
            }
            if (KEY == "OemCloseBrackets")
            {
                tempStr = "]";
                display = tempStr;
            }
            if (KEY == "OemOpenBrackets")
            {
                tempStr = "[";
                display = tempStr;
            }
            if (KEY == "OemMinus")
            {
                tempStr = "-";
                display = tempStr;
            }
            if (KEY == "OemPeriod" || KEY == "Decimal")
            {
                tempStr = ".";
            }
            if (KEY == "D1" || KEY == "D2" || KEY == "D3" || KEY == "D4" || KEY == "D5" || KEY == "D6" || KEY == "D7" || KEY == "D8" || KEY == "D9" || KEY == "D0")
            {
                tempStr = KEY.Substring(1);
            }
            else if (KEY == "NumPad1" || KEY == "NumPad2" || KEY == "NumPad3" || KEY == "NumPad4" || KEY == "NumPad5" || KEY == "NumPad6" || KEY == "NumPad7" || KEY == "NumPad8" || KEY == "NumPad9" || KEY == "NumPad0")
            {
                tempStr = KEY.Substring(6);
            }


            print = tempStr;
        }
    }

Also is the key name Left Shift and Right Shift ? What about the Backspace?

Based on your code you could try something like this:

        if (Keyboard.GetState().IsKeyDown(Keys.LeftShift) || Keyboard.GetState().IsKeyDown(Keys.RightShift))
        {
            if (KEY == "A" || KEY == "B" || KEY == "C" || KEY == "D" || KEY == "E" || KEY == "F" || KEY == "G" || KEY == "H" || KEY == "I" || KEY == "J" || KEY == "K" || KEY == "L" || KEY == "M" || KEY == "N" || KEY == "O" || KEY == "P" || KEY == "Q" || KEY == "R" || KEY == "S" || KEY == "T" || KEY == "U" || KEY == "V" || KEY == "W" || KEY == "X" || KEY == "Y" || KEY == "Z")
            {
                tempStr = KEY;
            }
        }
        else
        {
            if (KEY == "A" || KEY == "B" || KEY == "C" || KEY == "D" || KEY == "E" || KEY == "F" || KEY == "G" || KEY == "H" || KEY == "I" || KEY == "J" || KEY == "K" || KEY == "L" || KEY == "M" || KEY == "N" || KEY == "O" || KEY == "P" || KEY == "Q" || KEY == "R" || KEY == "S" || KEY == "T" || KEY == "U" || KEY == "V" || KEY == "W" || KEY == "X" || KEY == "Y" || KEY == "Z")
            {
                tempStr = KEY.ToLower();
            }
        }

You will need to handle the digits differently because there is no mapping between digits and special characters. It will be the same idea though. For backspace you just do a substring of the string with -1 length.

What I was asking is if the name of the backspace key is backspace ,sorry if my question wasn´t clear :sweat_smile:

It is Keys.Back so I guess Back would be the string name for it.

Thanks :grin:

For the special characters do I have to put each of the keys in the first if condition?
Example:

 if (Keyboard.GetState().IsKeyDown(Keys.LeftShift) || Keyboard.GetState().IsKeyDown(Keys.RightShift))
            {
                if (KEY == "A" || KEY == "B" || KEY == "C" || KEY == "D" || KEY == "E" || KEY == "F" || KEY == "G" || KEY == "H" || KEY == "I" || KEY == "J" || KEY == "K" || KEY == "L" || KEY == "M" || KEY == "N" || KEY == "O" || KEY == "P" || KEY == "Q" || KEY == "R" || KEY == "S" || KEY == "T" || KEY == "U" || KEY == "V" || KEY == "W" || KEY == "X" || KEY == "Y" || KEY == "Z")
                {
                    tempStr = KEY;
                }

                if(KEY == "D1" )
                {
                    tempStr = "!";
                }
                else if (KEY == "D3")
                {
                    tempStr = "#";
                }
            }

If you’re interested you can try out my UI library. As it turns out, I’ve used a login screen as a test UI to make a lot of it. If you let me know where something doesn’t make sense it would help me out a lot to make it easier to use.

I’m definitely going to need to make a video tutorial, the house is pretty noisy though.

If not, making your own UI is a lot of fun! I’ve been doing it for 3 years now. :slight_smile: Mine is based off behaviors, so each box has a list of interchangeable behaviors. One of them allows you to edit text.

Here is the code I use to edit text. It uses this behavior as well to draw the text. Feel free to take and use any of it.

using DownUnder.UI.Widgets.DataTypes;
using DownUnder.Utilities;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using MonoGame.Extended;
using System;
using System.Collections.Generic;
using System.Text;

namespace DownUnder.UI.Widgets.Behaviors.Visual.DrawTextBehaviors
{
    public class DrawEditableText : WidgetBehavior, ISubWidgetBehavior<DrawText>
    {
        public DrawText BaseBehavior => Parent.Behaviors.GetFirst<DrawText>();

        public override string[] BehaviorIDs { get; protected set; } = new string[] { DownUnderBehaviorIDs.FUNCTION };

        private bool Active => active_backing;

        readonly float caret_blink_time;
        StringBuilder edit_text = new StringBuilder();
        int caret_position = 0;
        int highlight_start = 0;
        int highlight_end = 0;
        /// <summary> If this is less than half _CaretBlinkTime then the caret won't be drawn. </summary>
        float caret_blink_timer = 0f;
        bool active_backing = false;
        List<RectangleF> highlight_area;
        List<RectangleF> text_area;
        bool allow_draw = false;
        bool clicking = false;
        bool editing_enabled_backing = true;

        public DrawEditableTextSettings Settings { get; set; } = new DrawEditableTextSettings();

        public bool EditingEnabled
        {
            get => editing_enabled_backing;
            set
            {
                if (!value && Active)
                {
                    editing_enabled_backing = value;
                    active_backing = value;
                    DeactivateEditing();
                    return;
                }
                editing_enabled_backing = value;
            }
        }

        public void ActivateEditing()
        {
            if (active_backing) return;
            edit_text.Clear();
            edit_text.Append(BaseBehavior.Text);
            if (Settings.HighlightTextOnActivation) HighlightRange(0, edit_text.Length);
            else MoveCaretTo(Parent.ParentDWindow.WindowFont.IndexFromPoint(edit_text.ToString(), Parent.CursorPosition, true));
            BaseBehavior.EnableDefaultDraw = false;
            active_backing = true;
        }

        public void DeactivateEditing(bool keep_changes = false)
        {
            if (!active_backing) return;
            if (keep_changes) BaseBehavior.Text = edit_text.ToString();
            //BaseBehavior.TextEntryRules.ApplyFinalCheck(edit_text); ??
            edit_text.Clear();
            BaseBehavior.EnableDefaultDraw = true;
            active_backing = false;
            caret_blink_timer = 0f;
        }

        /// <summary> The start of the highlighted text (if active). </summary>
        int _HighlightPosition => (highlight_start > highlight_end) ? highlight_end : highlight_start;

        /// <summary> The length of the highlighted text (if active). </summary>
        int _HighlightLength => (highlight_start > highlight_end) ? highlight_start - highlight_end : highlight_end - highlight_start;

        bool _CaretCurrentlyDrawn => caret_blink_timer / caret_blink_time < 0.5f;

        int _CaretLine
        {
            get
            {
                if (caret_position == 0) return 0;
                string source = edit_text.ToString().Substring(0, caret_position);
                int count = 0;
                foreach (char c in source)
                    if (c == '\n') count++;
                return count;
            }
        }

        /// <summary> The number of lines in this text. </summary>
        public int NumOfLines
        {
            get
            {
                string source = edit_text.ToString();
                int count = 0;
                foreach (char c in source)
                    if (c == '\n') count++;
                return count;
            }
        }

        int _BeginningOfCurrentLine {
            get {
                for (int i = caret_position - 1; i >= 0; i--)
                    if (edit_text[i] == '\n') return i + 1;
                return 0;
            }
        }

        int _EndOfCurrentLine {
            get {
                for (int i = caret_position; i < edit_text.Length; i++)
                    if (edit_text[i] == '\n') return i;
                return edit_text.Length;
            }
        }

        /// <summary> Returns the start and end index of the word the caret is over. Includes the following space. </summary>
        public Tuple<int, int> CurrentWord {
            get {
                int start = 0;

                for (int i = caret_position - 1; i >= 0; i--) {
                    if (edit_text[i] == '\n' || edit_text[i] == ' ') {
                        start = i + 1;
                        break;
                    }
                }

                for (int i = caret_position; i < edit_text.Length; i++) {
                    if (edit_text[i] == '\n' || edit_text[i] == ' ') {
                        if (edit_text[i] == ' ') i++;
                        return new Tuple<int, int>(start, i);
                    }
                }

                return new Tuple<int, int>(start, edit_text.Length);
            }
        }

        public DrawEditableText() : base() {
            caret_blink_time = OSInterface.CaretBlinkTime;
        }

        protected override void Initialize()
        {
        }

        protected override void ConnectEvents()
        {
            Parent.OnUpdate += Update;
            Parent.OnDraw += Draw;
            Parent.OnClick += ClickAction;
            Parent.OnDoubleClick += DoubleClickAction;
            Parent.OnTripleClick += TripleClickAction;
            Parent.OnConfirm += ConfirmAction;
            Parent.OnSelectOff += FocusOffAction;
        }

        protected override void DisconnectEvents()
        {
            Parent.OnUpdate -= Update;
            Parent.OnDraw -= Draw;
            Parent.OnClick -= ClickAction;
            Parent.OnDoubleClick -= DoubleClickAction;
            Parent.OnTripleClick -= TripleClickAction;
            Parent.OnConfirm -= ConfirmAction;
            Parent.OnSelectOff -= FocusOffAction;
        }

        public override object Clone()
        {
            throw new NotImplementedException();
        }

        public void Update(object sender, EventArgs args)
        {
            if (!Parent.UpdateData.UIInputState.PrimaryClick) clicking = false;
            if (!Settings.RequireDoubleClick && Parent.IsPrimaryHovered) Parent.ParentDWindow.UICursor = MouseCursor.IBeam;
            if (!Active) return;
            Vector2 offset = Parent.PositionInWindow.ToVector2().Floored();
            UIInputState inp = Parent.UpdateData.UIInputState;

            if (clicking) MoveCaretTo(Parent.ParentDWindow.WindowFont.IndexFromPoint(edit_text.ToString(), Parent.CursorPosition, true));
            
            // Movement of the caret
            if (inp.TextCursorMovement.Left && caret_position != 0) MoveCaretTo(caret_position - 1);
            if (inp.TextCursorMovement.Right && caret_position != edit_text.Length) MoveCaretTo(caret_position + 1);
            if (inp.TextCursorMovement.Up)
            {
                if (_CaretLine == 0) MoveCaretTo(0);
                else
                {
                    MoveCaretTo(
                        Parent.ParentDWindow.WindowFont.IndexFromPoint
                        (
                            edit_text.ToString(),
                            Parent.ParentDWindow.WindowFont.GetCharacterPosition(edit_text.ToString(), caret_position) - new Vector2(0f, Parent.ParentDWindow.WindowFont.MeasureString("|").Y),
                            true
                        )
                    );
                }
            }

            if (inp.TextCursorMovement.Down)
            {
                if (_CaretLine == NumOfLines) MoveCaretTo(edit_text.Length);
                else
                {
                    MoveCaretTo(
                        Parent.ParentDWindow.WindowFont.IndexFromPoint
                        (
                            edit_text.ToString(),
                            Parent.ParentDWindow.WindowFont.GetCharacterPosition(edit_text.ToString(), caret_position) + new Vector2(0f, Parent.ParentDWindow.WindowFont.MeasureString("|").Y),
                            true
                        )
                    );
                }
            }

            if (inp.Home) MoveCaretTo(_BeginningOfCurrentLine);
            if (inp.End) MoveCaretTo(_EndOfCurrentLine);

            // Editing the text
            bool text_changed = false;

            if (inp.BackSpace)
            {
                if (_HighlightLength != 0)
                {
                    text_changed |= DeleteHighlightedText();
                }
                else if (edit_text.Length > 0 && caret_position != 0)
                {
                    edit_text.Remove(caret_position - 1, 1);
                    MoveCaretTo(caret_position - 1);
                    text_changed = true;
                }
            }

            if (inp.Delete)
            {
                if (_HighlightLength != 0)
                {
                    text_changed |= DeleteHighlightedText();
                }
                else if (edit_text.Length > 0 && caret_position != edit_text.Length)
                {
                    edit_text.Remove(caret_position, 1);
                    MoveCaretTo(caret_position);
                    text_changed = true;
                }
            }

            if (inp.SelectAll) HighlightRange(0, edit_text.Length);
            
            if (inp.Copy || inp.Cut)
            {
                if (_HighlightLength > 0)
                {
                    char[] t = new char[_HighlightLength];
                    edit_text.CopyTo(_HighlightPosition, t, 0, _HighlightLength);
                    OSInterface.CopyTextToClipBoard(new string(t));
                }
                if (inp.Cut) text_changed |= DeleteHighlightedText();
            }

            if (inp.Paste) text_changed |= InsertText(OSInterface.GetTextFromClipboard(), caret_position, true);
            
            // Insert typed text
            text_changed |= InsertText(Parent.UpdateData.UIInputState.Text, caret_position, true);

            if (text_changed)
            {
                if (Settings.LiveUpdate) BaseBehavior.Text = edit_text.ToString();
            }

            text_area = Parent.ParentDWindow.WindowFont.MeasureStringAreas(edit_text.ToString());
            highlight_area = Parent.ParentDWindow.WindowFont.MeasureSubStringAreas(edit_text.ToString(), _HighlightPosition, _HighlightLength, true);
            caret_blink_timer += Parent.UpdateData.ElapsedSeconds;

            bool over_highlighted_text = false;
            foreach (RectangleF text in highlight_area) {
                text.Offset(offset);
                if (text.Contains(Parent.CursorPosition)) over_highlighted_text = true;
            }

            if (Parent.IsPrimaryHovered && (!over_highlighted_text || clicking)) Parent.ParentDWindow.UICursor = MouseCursor.IBeam;
            if (caret_blink_timer >= caret_blink_time) caret_blink_timer -= caret_blink_time;

            allow_draw = true;
        }

        private void MoveCaretTo(int index, bool no_highlight = false)
        {
            if (caret_position != index) caret_blink_timer = 0f;
            caret_position = index;
            if (!Parent.UpdateData.UIInputState.Shift && !clicking || no_highlight) highlight_start = caret_position;
            highlight_end = caret_position;
        }

        private void HighlightRange(int start, int end)
        {
            caret_position = end;
            highlight_start = start;
            highlight_end = end;
            caret_blink_timer = 0f;
        }

        private bool InsertText(string text, int index, bool no_highlight = false)
        {
            bool text_changed = false;
            if (text == "") return text_changed;
            if (DeleteHighlightedText())
            {
                index = caret_position;
                text_changed = true;
            }
            int added_chars = Settings.TextEntryRules.CheckAndInsert(edit_text, text, index);
            if (added_chars != 0)
            {
                MoveCaretTo(index + added_chars, no_highlight);
                text_changed = true;
            }
            return text_changed;
        }

        private bool DeleteHighlightedText()
        {
            int highlight_length = _HighlightLength;
            if (highlight_length == 0) return false;
            int highlight_position = _HighlightPosition;
            edit_text.Remove(highlight_position, highlight_length);
            highlight_start = highlight_position;
            highlight_end = highlight_position;
            caret_position = highlight_position;
            return true;
        }

        /// <summary> Is called when the parent is clicked. </summary>
        private void ClickAction(object sender, EventArgs args)
        {
            if (Active)
            {
                MoveCaretTo(Parent.ParentDWindow.WindowFont.IndexFromPoint(edit_text.ToString(), Parent.CursorPosition, true));
                clicking = true;
            }

            if (!Settings.RequireDoubleClick) ActivateEditing();
        }

        /// <summary> Is called when the parent is double clicked. </summary>
        private void DoubleClickAction(object sender, EventArgs args)
        {
            if (!EditingEnabled) return;

            if (Active)
            {
                clicking = false;
                Tuple<int, int> word = CurrentWord;
                //Console.WriteLine(word);
                HighlightRange(word.Item1, word.Item2);
                return;
            }

            ActivateEditing();
        }

        private void ConfirmAction(object sender, EventArgs args)
        {
            DeactivateEditing(true);
        }

        private void FocusOffAction(object sender, EventArgs args)
        {
            DeactivateEditing(false);
        }

        /// <summary> Is called when the parent is triple clicked. </summary>
        private void TripleClickAction(object sender, EventArgs args)
        {
            if (Active)
            {
                clicking = false;
                HighlightRange(_BeginningOfCurrentLine, _EndOfCurrentLine);
                return;
            }
        }

        public void Draw(object sender, WidgetDrawArgs args)
        {
            if (!Active) return;
            if (!allow_draw) return;
            Vector2 offset = args.DrawingArea.Position.WithOffset(BaseBehavior.TextPosition).Floored();

            if (_HighlightLength > 0)
            {
                foreach (var rect in highlight_area)
                {
                    rect.Offset(offset);
                    Parent.SpriteBatch.FillRectangle(rect, Color.LightBlue);
                }
            }

            BaseBehavior.ForceDrawText(args.DrawingArea.Position, edit_text.ToString());

            if (_CaretCurrentlyDrawn)
            {
                Vector2 position = Parent.ParentDWindow.WindowFont.GetCharacterPosition(edit_text.ToString(), caret_position) + offset + new Vector2(1, 0);
                Vector2 position2 = position + new Vector2(0, 20);
                Parent.SpriteBatch.DrawLine(position, position2, Parent.VisualSettings.TextColor, 1);
            }
        }
    }
}

My project is a university project so I think that using an already made UI would be less valuable than making my own

University, eh? I never went to school, probably why I still work as a deli clerk.

Are you still stuck on the repeating text part? I made a BufferedKeyboard class that basically keeps a list of bools that independently keep track of timing, where each bool represents one key. If you want to go down the easier route, you should definitely look into using MonoGame’s Window.TextInput EventHandler. If you get too crazy parsing input it might get a little too ambitious for a school project.

This event handler will handle timing for you, so no repeating values. The downside is that certain keys are not kept track of correctly, should you just add chars to the end of your string, and you will need to account for them manually.

Yes. You need a separate if for each of the digits you want to use. Same for any other special character that has a shift state associated with it.

I would opt against using Keyboard.GetState(), unless it’s being wrapped by another class, at all since it’s not meant to be used for text input. It’s also bad practice to try to account for every key manually.

General rule of thumb is when you have a line like this

if (KEY == “A” || KEY == “B” || KEY == “C” || KEY == “D” || KEY == “E” || KEY == “F” || KEY == “G” || KEY == “H” || KEY == “I” || KEY == “J” || KEY == “K” || KEY == “L” || KEY == “M” || KEY == “N” || KEY == “O” || KEY == “P” || KEY == “Q” || KEY == “R” || KEY == “S” || KEY == “T” || KEY == “U” || KEY == “V” || KEY == “W” || KEY == “X” || KEY == “Y” || KEY == “Z”)

There’s going to be an easier way to do it. Your professor isn’t going to like that.

It could be simplified to if (KEY >= “A” && KEY <= “Z”)

But then he still has to account for the numbers and special characters, and shift and caps lock, and repeating character timing. Window.TextInput will handle all of that for you by actually interfacing with your OS and your typing settings and keyboard layouts.

Doing it with KeyboardState would be fine… if you had a very specific reason not to use the built in event handler.