using Client.ConsoleForms.Parameters; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Tofvesson.Collections; using Tofvesson.Crypto; namespace Client.ConsoleForms.Graphics { public class InputView : TextView { public enum InputType { Any, AlphaNumeric, Integer, Decimal, Alphabet } public sealed class InputField { public const char hide_char = '*'; public string Label { get; private set; } public int MaxLength { get; private set; } public bool ShowText { get; set; } public string Text { get; set; } public int SelectIndex { get; set; } public InputType Input { get; set; } public ConsoleColor TextColor { get; set; } public ConsoleColor BackgroundColor { get; set; } public ConsoleColor SelectTextColor { get; set; } public ConsoleColor SelectBackgroundColor { get; set; } public string InputTypeString { get { switch (Input) { case InputType.Any: return "Any"; case InputType.AlphaNumeric: return "AlphaNumeric"; case InputType.Integer: return "Integer"; case InputType.Decimal: return "Decimal"; case InputType.Alphabet: return "Alphabet"; } throw new SystemException("Invalid system state detected"); } set { switch (value.ToLower()) { case "alphanumeric": Input = InputType.AlphaNumeric; break; case "integer": Input = InputType.Integer; break; case "decimal": Input = InputType.Decimal; break; case "alphabet": Input = InputType.Alphabet; break; default: Input = InputType.Any; break; } } } internal int RenderStart { get; set; } public InputField(string label, int maxLength) { TextColor = ConsoleColor.Black; BackgroundColor = ConsoleColor.DarkGray; SelectTextColor = ConsoleColor.Black; SelectBackgroundColor = ConsoleColor.Gray; Input = InputType.Any; Label = label; MaxLength = maxLength; Text = ""; } public bool IsValidChar(char c) => (Input == InputType.Any) || (Input == InputType.AlphaNumeric && c.IsAlphaNumeric()) || (Input == InputType.Alphabet && c.IsAlphabetical()) || (Input == InputType.Integer && c.IsNumber()) || (Input == InputType.Decimal && c.IsDecimal()); } public delegate void SubmissionListener(InputView view); public delegate bool TextEnteredListener(InputView view, InputField change, ConsoleKeyInfo info); public ConsoleColor DefaultBackgroundColor { get; set; } public ConsoleColor DefaultTextColor { get; set; } public ConsoleColor DefaultSelectBackgroundColor { get; set; } public ConsoleColor DefaultSelectTextColor { get; set; } public InputField[] Inputs { get; private set; } private int selectedField; public int SelectedField { get => selectedField; set { selectedField = value; Dirty = true; } } private string[][] splitInputs; public SubmissionListener SubmissionsListener { protected get; set; } public TextEnteredListener InputListener { protected get; set; } public InputView(ViewData parameters, LangManager lang) : base(parameters, lang) { int sBC = parameters.AttribueAsInt("textfield_select_color", (int)ConsoleColor.Gray), sTC = parameters.AttribueAsInt("text_select_color", (int)ConsoleColor.Black), BC = parameters.AttribueAsInt("field_noselect_color", (int)ConsoleColor.DarkGray), TC = parameters.AttribueAsInt("text_noselect_color", (int)ConsoleColor.Black); DefaultBackgroundColor = (ConsoleColor)BC; DefaultTextColor = (ConsoleColor)TC; DefaultSelectBackgroundColor = (ConsoleColor)sBC; DefaultSelectTextColor = (ConsoleColor)sTC; List fields = new List(); foreach (var data in parameters.nestedData.GetFirst(d => d.Name.Equals("Fields")).nestedData) if (!data.Name.Equals("Field")) continue; else fields.Add(new InputField(data.InnerText, data.AttribueAsInt("max_length", -1)) { ShowText = !data.AttribueAsBool("hide", false), Text = data.GetAttribute("default"), InputTypeString = data.GetAttribute("input_type"), TextColor = (ConsoleColor)data.AttribueAsInt("color_text", TC), BackgroundColor = (ConsoleColor)data.AttribueAsInt("color_background", BC), SelectTextColor = (ConsoleColor)data.AttribueAsInt("color_text_select", sTC), SelectBackgroundColor = (ConsoleColor)data.AttribueAsInt("color_background_select", sBC) }); Inputs = fields.ToArray(); int computedSize = 0; splitInputs = new string[Inputs.Length][]; for (int i = 0; i < Inputs.Length; ++i) { splitInputs[i] = ComputeTextDimensions(Inputs[i].Label.Split(' ')); computedSize += splitInputs[i].Length; } ContentHeight += computedSize + Inputs.Length * 2; //++ContentWidth; // Idk, it works, though... } protected override void _Draw(int left, ref int top) { DrawEmptyPadding(left, ref top, padding.Top()); DrawContent(left, ref top); DrawInputFields(left, ref top, 1); DrawEmptyPadding(left, ref top, padding.Bottom()); } protected void DrawInputFields(int left, ref int top, int spaceHeight) { int pl = padding.Left(), pr = padding.Right(); for (int j = 0; j < Inputs.Length; ++j) { DrawEmptyPadding(left, ref top, spaceHeight); for (int i = 0; i < splitInputs[j].Length; ++i) { Console.SetCursorPosition(left, top++); Console.BackgroundColor = BackgroundColor; Console.Write(Filler(' ', pl) + splitInputs[j][i] + Filler(' ', MaxWidth - splitInputs[j][i].Length) + Filler(' ', pr)); } Console.SetCursorPosition(left, top++); // Draw padding Console.BackgroundColor = BackgroundColor; Console.Write(Filler(' ', pl)); // Draw field Console.BackgroundColor = j == selectedField ? Inputs[j].SelectBackgroundColor : Inputs[j].BackgroundColor; Console.ForegroundColor = j == selectedField ? Inputs[j].SelectTextColor : Inputs[j].TextColor; Console.Write(Inputs[j].ShowText ? Inputs[j].Text.Substring(Inputs[j].RenderStart, Inputs[j].SelectIndex - Inputs[j].RenderStart) : Filler('*', Inputs[j].SelectIndex - Inputs[j].RenderStart)); if (j == selectedField) Console.BackgroundColor = ConsoleColor.DarkGray; Console.Write(Inputs[j].SelectIndex < Inputs[j].Text.Length ? Inputs[j].ShowText ? Inputs[j].Text[Inputs[j].SelectIndex] : '*' : ' '); if (j == selectedField) Console.BackgroundColor = Inputs[j].SelectBackgroundColor; int drawn = 0; if (Inputs[j].SelectIndex < Inputs[j].Text.Length) Console.Write( Inputs[j].ShowText ? Inputs[j].Text.Substring(Inputs[j].SelectIndex + 1, drawn = Math.Min(maxWidth + Inputs[j].SelectIndex - Inputs[j].RenderStart - 1, Inputs[j].Text.Length - Inputs[j].SelectIndex - 1)) : Filler('*', drawn = Math.Min(maxWidth + Inputs[j].SelectIndex - Inputs[j].RenderStart - 1, Inputs[j].Text.Length - Inputs[j].SelectIndex - 1)) ); Console.Write(Filler(' ', maxWidth - 1 - drawn - Inputs[j].SelectIndex + Inputs[j].RenderStart)); Console.ForegroundColor = ConsoleColor.Black; // Draw padding Console.BackgroundColor = BackgroundColor; Console.Write(Filler(' ', pr)); } } public override bool HandleKeyEvent(ConsoleController.KeyEvent evt, bool inFocus) { bool changed = base.HandleKeyEvent(evt, inFocus); ConsoleKeyInfo info = evt.Event; if (!evt.ValidEvent || !inFocus || Inputs.Length == 0) return changed; switch (info.Key) { case ConsoleKey.LeftArrow: if (Inputs[selectedField].SelectIndex > 0) { if (Inputs[selectedField].RenderStart == Inputs[selectedField].SelectIndex--) --Inputs[selectedField].RenderStart; } else return changed; break; case ConsoleKey.RightArrow: if (Inputs[selectedField].SelectIndex < Inputs[selectedField].Text.Length) { if (++Inputs[selectedField].SelectIndex - Inputs[selectedField].RenderStart == maxWidth) ++Inputs[selectedField].RenderStart; } else return changed; break; case ConsoleKey.Tab: case ConsoleKey.DownArrow: if (selectedField < Inputs.Length - 1) ++selectedField; else return changed; break; case ConsoleKey.UpArrow: if (selectedField > 0) --selectedField; else return changed; break; case ConsoleKey.Backspace: if (Inputs[selectedField].SelectIndex > 0) { if (InputListener?.Invoke(this, Inputs[selectedField], info) == false) break; string text = Inputs[selectedField].Text; Inputs[selectedField].Text = text.Substring(0, Inputs[selectedField].SelectIndex - 1); if (Inputs[selectedField].SelectIndex < text.Length) Inputs[selectedField].Text += text.Substring(Inputs[selectedField].SelectIndex); if (Inputs[selectedField].RenderStart == Inputs[selectedField].SelectIndex--) --Inputs[selectedField].RenderStart; } else return changed; break; case ConsoleKey.Delete: if (Inputs[selectedField].SelectIndex < Inputs[selectedField].Text.Length) { if (InputListener?.Invoke(this, Inputs[selectedField], info) == false) break; string text = Inputs[selectedField].Text; Inputs[selectedField].Text = text.Substring(0, Inputs[selectedField].SelectIndex); if (Inputs[selectedField].SelectIndex + 1 < text.Length) Inputs[selectedField].Text += text.Substring(Inputs[selectedField].SelectIndex + 1); } else return changed; break; case ConsoleKey.Enter: SubmissionsListener?.Invoke(this); return changed; default: if (info.KeyChar != 0 && info.KeyChar != '\b' && info.KeyChar != '\r' && (Inputs[selectedField].Text.Length < Inputs[selectedField].MaxLength || Inputs[selectedField].MaxLength < 0) && Inputs[selectedField].IsValidChar(info.KeyChar)) { if (InputListener?.Invoke(this, Inputs[selectedField], info) == false) break; Inputs[selectedField].Text = Inputs[selectedField].Text.Substring(0, Inputs[selectedField].SelectIndex) + info.KeyChar + Inputs[selectedField].Text.Substring(Inputs[selectedField].SelectIndex); if (++Inputs[selectedField].SelectIndex - Inputs[selectedField].RenderStart == maxWidth) ++Inputs[selectedField].RenderStart; } else return changed; break; } return true; } } }