using System; using System.Collections; using System.Linq; using System.Security; using System.Text; using System.Threading; using System.Windows.Forms; using JetBrains.Annotations; namespace Ameliorated.ConsoleUtils { /// /// Represents a input prompt to be used with the Start method. /// public class ChoicePrompt : Prompt { public string Choices { get; set; } = "YN"; public bool BeepSound { get; set; } = true; public bool CaseSensitive { get; set; } = false; public bool AllowEscape { get; set; } = true; public bool AnyKey { get; set; } = false; public ConsoleColor? TextForeground { get; set; } private bool _bindToOpenFrame; [CanBeNull] public new int? Start() { if (Choices.Length < 1 && !AnyKey) throw new ArgumentException("There must be at least 1 choice."); _bindToOpenFrame = ConsoleTUI.OpenFrame != null && BindToOpenFrame; if (_bindToOpenFrame && ConsoleTUI.OpenFrame.AvailableLines() < 1) ConsoleTUI.OpenFrame.Clear(); if (TextForeground.HasValue) ConsoleUtils.SetColor(TextForeground); if (_bindToOpenFrame) ConsoleTUI.OpenFrame.Write(Text); else Console.Write(Text); if (TextForeground.HasValue) ConsoleUtils.ResetColor(); var cursorVisibility = Console.CursorVisible; int? result; while (true) { Console.CursorVisible = true; ConsoleUtils.ClearInputBuffer(); var key = Console.ReadKey(true); if (AnyKey) { Console.CursorVisible = cursorVisibility; return key.KeyChar; } if (key.Key == ConsoleKey.Escape && AllowEscape) { Console.CursorVisible = cursorVisibility; return null; } if (CaseSensitive) result = Choices.IndexOf(key.KeyChar.ToString(), StringComparison.Ordinal); else result = Choices.IndexOf(key.KeyChar.ToString(), StringComparison.OrdinalIgnoreCase); if (result >= 0) { if (InputForeground.HasValue && InputBackground.HasValue) ConsoleUtils.SetColor(InputForeground.Value, InputBackground.Value); else if (InputForeground.HasValue) ConsoleUtils.SetColor(InputForeground.Value); else if (InputBackground.HasValue) ConsoleUtils.SetColor(Console.ForegroundColor, InputBackground.Value); if (!CaseSensitive) Console.Write(key.KeyChar.ToString().ToUpper()); else Console.Write(key.KeyChar.ToString()); if (InputForeground.HasValue || InputBackground.HasValue) ConsoleUtils.ResetColor(); break; } else if (BeepSound) Console.Beep(); } Console.CursorVisible = cursorVisibility; Console.WriteLine(); return result.Value; } } public class InputPrompt : Prompt { [Optional] public ConsoleColor? BoxBackground { get; set; } public bool MaskInput { get; set; } = false; public bool AlignInput { get; set; } = true; [Optional] public int? SplitWidth { get; set; } private bool _bindToOpenFrame; private int? _splitWidth; [CanBeNull] public new string Start() { if (SplitWidth.HasValue && !AlignInput) throw new ArgumentException("Property SplitWidth must not be used without property AlignInput set to true."); _bindToOpenFrame = ConsoleTUI.OpenFrame != null && BindToOpenFrame; if (_bindToOpenFrame && ConsoleTUI.OpenFrame.AvailableLines() < 1) ConsoleTUI.OpenFrame.Clear(); if (!MaxLength.HasValue) MaxLength = AlignInput ? ConsoleTUI.OpenFrame.AvailableChars() - (Text.LastLine().Length * ConsoleTUI.OpenFrame.AvailableLines()) : ConsoleTUI.OpenFrame.AvailableChars(); int startLeft = Console.CursorLeft + Text.LastLine().Length; _splitWidth = null; if (AlignInput && !_bindToOpenFrame) { if (Text.LastLine().Length > Console.WindowWidth - 3) throw new ArgumentException("Last line of property Text must not be within 3 characters of the available width."); _splitWidth = Console.WindowWidth - startLeft; } if (_bindToOpenFrame) { if (SplitWidth.HasValue && (SplitWidth.Value + startLeft + ConsoleTUI.OpenFrame.DisplayOffset >= Console.WindowWidth)) throw new ArgumentException($"Property SplitWidth must be less than the available width."); // TODO: BAD WORDING if (Text.LastLine().Length > ConsoleTUI.OpenFrame.DisplayWidth - 3) throw new ArgumentException("Last line of property Text must not be within 3 characters of the available width."); _splitWidth = ConsoleTUI.OpenFrame.DisplayWidth - (startLeft); if (!AlignInput) _splitWidth = ConsoleTUI.OpenFrame.DisplayWidth; } if (SplitWidth.HasValue) { if (SplitWidth.Value + startLeft >= Console.WindowWidth) throw new ArgumentException($"Property SplitWidth must be less than the available width."); if (Text.LastLine().Length > Console.WindowWidth - 3) throw new ArgumentException("Last line of property Text must not be within 3 characters of the available width."); _splitWidth = SplitWidth.Value; } /* int maxLines = Console.WindowHeight - Console.CursorTop; if (_bindToOpenFrame) { maxLines = ConsoleTUI.OpenFrame.DisplayHeight - (Console.CursorTop - 6) - Text.SplitByLine().Length; if (maxLines < 2) { ConsoleTUI.OpenFrame.Clear(); if (_bindToOpenFrame) maxLines = ConsoleTUI.OpenFrame.DisplayHeight - (Console.CursorTop - 6) - Text.SplitByLine().Length; } } */ ConsoleUtils.ClearInputBuffer(); if (_bindToOpenFrame) ConsoleTUI.OpenFrame.Write(Text); else Console.Write(Text); if (BoxBackground.HasValue && _splitWidth.HasValue) { if (AlignInput) WriteBackground(_splitWidth.Value, BoxBackground.Value); else WriteBackground(_splitWidth.Value - Text.LastLine().Length, BoxBackground.Value); } if (BoxBackground.HasValue && !_splitWidth.HasValue) WriteBackground(Console.WindowWidth - Console.CursorLeft, BoxBackground.Value); var cursorVisibility = Console.CursorVisible; var input = new StringBuilder(); ConsoleKeyInfo key; do { Console.CursorVisible = true; key = Console.ReadKey(true); if (key.Key == ConsoleKey.Backspace) { if (input.Length <= 0) continue; input.Remove(input.Length - 1, 1); bool movedLines = false; if (_bindToOpenFrame) { if ((((input.Length + 1) - _splitWidth.Value + Text.LastLine().Length).IsDivisibleBy(ConsoleTUI.OpenFrame.DisplayWidth) || input.Length + 1 == _splitWidth.Value - Text.LastLine().Length) && !AlignInput) { if (BoxBackground.HasValue) WriteBackground(_splitWidth.Value - (Console.CursorLeft - ConsoleTUI.OpenFrame.DisplayOffset), Console.BackgroundColor); Console.SetCursorPosition((ConsoleTUI.OpenFrame.DisplayOffset) + _splitWidth.Value - 1, Console.CursorTop - 1); movedLines = true; } else if ((input.Length + 1).IsDivisibleBy(_splitWidth.Value) && AlignInput) { if (BoxBackground.HasValue) WriteBackground(_splitWidth.Value, Console.BackgroundColor); Console.SetCursorPosition(ConsoleTUI.OpenFrame.DisplayOffset + startLeft + _splitWidth.Value - 1, Console.CursorTop - 1); movedLines = true; } } else if (AlignInput) { if ((input.Length + 1).IsDivisibleBy(_splitWidth.Value)) { if (BoxBackground.HasValue) WriteBackground(_splitWidth.Value, Console.BackgroundColor); Console.SetCursorPosition(startLeft + _splitWidth.Value, Console.CursorTop - 1); movedLines = true; } } else { if (((input.Length + 1) - (Console.WindowWidth - startLeft)).IsDivisibleBy(Console.WindowWidth) || input.Length + 1 == Console.WindowWidth - startLeft) { if (BoxBackground.HasValue) WriteBackground(Console.WindowWidth, Console.BackgroundColor); Console.SetCursorPosition(Console.WindowWidth - 1, Console.CursorTop - 1); movedLines = true; } } if (movedLines) { if (BoxBackground.HasValue) ConsoleUtils.SetColor(Console.ForegroundColor, BoxBackground.Value); Console.Write(" \b"); if (BoxBackground.HasValue) ConsoleUtils.ResetColor(); } else { if (BoxBackground.HasValue) ConsoleUtils.SetColor(Console.ForegroundColor, BoxBackground.Value); Console.Write("\b \b"); if (BoxBackground.HasValue) ConsoleUtils.ResetColor(); } if (MaxLength.HasValue && MaxLength.Value - 1 == input.Length) Console.CursorVisible = true; continue; } if (Char.IsControl(key.KeyChar)) continue; if (MaxLength.HasValue && MaxLength.Value <= input.Length) { ConsoleUtils.SetColor(ConsoleColor.DarkRed, InputBackground); Console.CursorVisible = false; Console.Write("!"); Thread.Sleep(200); ConsoleUtils.ClearInputBuffer(); ConsoleUtils.ResetColor(); ConsoleUtils.SetColor(null, BoxBackground); Console.Write("\b \b"); ConsoleUtils.ResetColor(); continue; } input.Append(key.KeyChar); ConsoleUtils.SetColor(InputForeground, InputBackground); if (MaskInput) Console.Write("*"); else Console.Write(key.KeyChar); ConsoleUtils.ResetColor(); if (MaxLength.HasValue && MaxLength.Value <= input.Length + 1) continue; if (_bindToOpenFrame) { if (((input.Length - _splitWidth.Value + Text.LastLine().Length).IsDivisibleBy(_splitWidth.Value) || input.Length == _splitWidth.Value - Text.LastLine().Length) && !AlignInput) { Console.SetCursorPosition(ConsoleTUI.OpenFrame.DisplayOffset, Console.CursorTop + 1); if (BoxBackground.HasValue) WriteBackground(_splitWidth.Value, BoxBackground.Value); } else if (input.Length.IsDivisibleBy(_splitWidth.Value) && AlignInput) { Console.SetCursorPosition(startLeft + ConsoleTUI.OpenFrame.DisplayOffset, Console.CursorTop + 1); if (BoxBackground.HasValue) WriteBackground(_splitWidth.Value, BoxBackground.Value); } } else if (AlignInput) { if (input.Length.IsDivisibleBy(_splitWidth.Value)) { if (SplitWidth.HasValue) Console.SetCursorPosition(startLeft, Console.CursorTop + 1); else // Console will have automatically moved the cursor down Console.SetCursorPosition(startLeft, Console.CursorTop); if (BoxBackground.HasValue) WriteBackground(_splitWidth.Value, BoxBackground.Value); } } else { if ((input.Length - (Console.WindowWidth - startLeft)).IsDivisibleBy(Console.WindowWidth) || input.Length == Console.WindowWidth - startLeft) { // Console will have automatically moved the cursor down Console.SetCursorPosition(startLeft - Text.LastLine().Length, Console.CursorTop); if (BoxBackground.HasValue) WriteBackground(Console.WindowWidth - (startLeft - Text.LastLine().Length), BoxBackground.Value); } } } while (key.Key != ConsoleKey.Enter && (!AllowEscape || (AllowEscape && key.Key != ConsoleKey.Escape))); if (input.Length == 0) { ConsoleUtils.SetColor(ConsoleColor.DarkGray); Console.Write("None"); ConsoleUtils.ResetColor(); } Console.CursorVisible = cursorVisibility; Console.WriteLine(); if (key.Key == ConsoleKey.Escape && AllowEscape) return null; return input.ToString(); } } public class SecureInputPrompt : Prompt { [Optional] public ConsoleColor? BoxBackground { get; set; } public bool MaskInput { get; set; } = true; public bool AlignInput { get; set; } = true; [Optional] public int? SplitWidth { get; set; } private bool _bindToOpenFrame; private int? _splitWidth; [CanBeNull] public new SecureString Start() {if (SplitWidth.HasValue && !AlignInput) throw new ArgumentException("Property SplitWidth must not be used without property AlignInput set to true."); _bindToOpenFrame = ConsoleTUI.OpenFrame != null && BindToOpenFrame; if (_bindToOpenFrame && ConsoleTUI.OpenFrame.AvailableLines() < 1) ConsoleTUI.OpenFrame.Clear(); if (!MaxLength.HasValue) MaxLength = AlignInput ? ConsoleTUI.OpenFrame.AvailableChars() - (Text.LastLine().Length * ConsoleTUI.OpenFrame.AvailableLines()) : ConsoleTUI.OpenFrame.AvailableChars(); int startLeft = Console.CursorLeft + Text.LastLine().Length; _splitWidth = null; if (AlignInput && !_bindToOpenFrame) { if (Text.LastLine().Length > Console.WindowWidth - 3) throw new ArgumentException("Last line of property Text must not be within 3 characters of the available width."); _splitWidth = Console.WindowWidth - startLeft; } if (_bindToOpenFrame) { if (SplitWidth.HasValue && (SplitWidth.Value + startLeft + ConsoleTUI.OpenFrame.DisplayOffset >= Console.WindowWidth)) throw new ArgumentException($"Property SplitWidth must be less than the available width."); // TODO: BAD WORDING if (Text.LastLine().Length > ConsoleTUI.OpenFrame.DisplayWidth - 3) throw new ArgumentException("Last line of property Text must not be within 3 characters of the available width."); _splitWidth = ConsoleTUI.OpenFrame.DisplayWidth - (startLeft); if (!AlignInput) _splitWidth = ConsoleTUI.OpenFrame.DisplayWidth; } if (SplitWidth.HasValue) { if (SplitWidth.Value + startLeft >= Console.WindowWidth) throw new ArgumentException($"Property SplitWidth must be less than the available width."); if (Text.LastLine().Length > Console.WindowWidth - 3) throw new ArgumentException("Last line of property Text must not be within 3 characters of the available width."); _splitWidth = SplitWidth.Value; } /* int maxLines = Console.WindowHeight - Console.CursorTop; if (_bindToOpenFrame) { maxLines = ConsoleTUI.OpenFrame.DisplayHeight - (Console.CursorTop - 6) - Text.SplitByLine().Length; if (maxLines < 2) { ConsoleTUI.OpenFrame.Clear(); if (_bindToOpenFrame) maxLines = ConsoleTUI.OpenFrame.DisplayHeight - (Console.CursorTop - 6) - Text.SplitByLine().Length; } } */ ConsoleUtils.ClearInputBuffer(); if (_bindToOpenFrame) ConsoleTUI.OpenFrame.Write(Text); else Console.Write(Text); if (BoxBackground.HasValue && _splitWidth.HasValue) { if (AlignInput) WriteBackground(_splitWidth.Value, BoxBackground.Value); else WriteBackground(_splitWidth.Value - Text.LastLine().Length, BoxBackground.Value); } if (BoxBackground.HasValue && !_splitWidth.HasValue) WriteBackground(Console.WindowWidth - Console.CursorLeft, BoxBackground.Value); var cursorVisibility = Console.CursorVisible; var input = new SecureString(); ConsoleKeyInfo key; do { Console.CursorVisible = true; key = Console.ReadKey(true); if (key.Key == ConsoleKey.Backspace) { if (input.Length <= 0) continue; input.RemoveAt(input.Length - 1); bool movedLines = false; if (_bindToOpenFrame) { if ((((input.Length + 1) - _splitWidth.Value + Text.LastLine().Length).IsDivisibleBy(ConsoleTUI.OpenFrame.DisplayWidth) || input.Length + 1 == _splitWidth.Value - Text.LastLine().Length) && !AlignInput) { if (BoxBackground.HasValue) WriteBackground(_splitWidth.Value - (Console.CursorLeft - ConsoleTUI.OpenFrame.DisplayOffset), Console.BackgroundColor); Console.SetCursorPosition((ConsoleTUI.OpenFrame.DisplayOffset) + _splitWidth.Value - 1, Console.CursorTop - 1); movedLines = true; } else if ((input.Length + 1).IsDivisibleBy(_splitWidth.Value) && AlignInput) { if (BoxBackground.HasValue) WriteBackground(_splitWidth.Value, Console.BackgroundColor); Console.SetCursorPosition(ConsoleTUI.OpenFrame.DisplayOffset + startLeft + _splitWidth.Value - 1, Console.CursorTop - 1); movedLines = true; } } else if (AlignInput) { if ((input.Length + 1).IsDivisibleBy(_splitWidth.Value)) { if (BoxBackground.HasValue) WriteBackground(_splitWidth.Value, Console.BackgroundColor); Console.SetCursorPosition(startLeft + _splitWidth.Value, Console.CursorTop - 1); movedLines = true; } } else { if (((input.Length + 1) - (Console.WindowWidth - startLeft)).IsDivisibleBy(Console.WindowWidth) || input.Length + 1 == Console.WindowWidth - startLeft) { if (BoxBackground.HasValue) WriteBackground(Console.WindowWidth, Console.BackgroundColor); Console.SetCursorPosition(Console.WindowWidth - 1, Console.CursorTop - 1); movedLines = true; } } if (movedLines) { if (BoxBackground.HasValue) ConsoleUtils.SetColor(Console.ForegroundColor, BoxBackground.Value); Console.Write(" \b"); if (BoxBackground.HasValue) ConsoleUtils.ResetColor(); } else { if (BoxBackground.HasValue) ConsoleUtils.SetColor(Console.ForegroundColor, BoxBackground.Value); Console.Write("\b \b"); if (BoxBackground.HasValue) ConsoleUtils.ResetColor(); } if (MaxLength.HasValue && MaxLength.Value - 1 == input.Length) Console.CursorVisible = true; } if (Char.IsControl(key.KeyChar)) continue; if (MaxLength.HasValue && MaxLength.Value <= input.Length) { ConsoleUtils.SetColor(ConsoleColor.DarkRed, InputBackground); Console.CursorVisible = false; Console.Write("!"); Thread.Sleep(200); ConsoleUtils.ClearInputBuffer(); ConsoleUtils.ResetColor(); ConsoleUtils.SetColor(null, BoxBackground); Console.Write("\b \b"); ConsoleUtils.ResetColor(); continue; } input.AppendChar(key.KeyChar); ConsoleUtils.SetColor(InputForeground, InputBackground); if (MaskInput) Console.Write("*"); else Console.Write(key.KeyChar); ConsoleUtils.ResetColor(); if (MaxLength.HasValue && MaxLength.Value <= input.Length + 1) continue; if (_bindToOpenFrame) { if (((input.Length - _splitWidth.Value + Text.LastLine().Length).IsDivisibleBy(_splitWidth.Value) || input.Length == _splitWidth.Value - Text.LastLine().Length) && !AlignInput) { Console.SetCursorPosition(ConsoleTUI.OpenFrame.DisplayOffset, Console.CursorTop + 1); if (BoxBackground.HasValue) WriteBackground(_splitWidth.Value, BoxBackground.Value); } else if (input.Length.IsDivisibleBy(_splitWidth.Value) && AlignInput) { Console.SetCursorPosition(startLeft + ConsoleTUI.OpenFrame.DisplayOffset, Console.CursorTop + 1); if (BoxBackground.HasValue) WriteBackground(_splitWidth.Value, BoxBackground.Value); } } else if (AlignInput) { if (input.Length.IsDivisibleBy(_splitWidth.Value)) { if (SplitWidth.HasValue) Console.SetCursorPosition(startLeft, Console.CursorTop + 1); else // Console will have automatically moved the cursor down Console.SetCursorPosition(startLeft, Console.CursorTop); if (BoxBackground.HasValue) WriteBackground(_splitWidth.Value, BoxBackground.Value); } } else { if ((input.Length - (Console.WindowWidth - startLeft)).IsDivisibleBy(Console.WindowWidth) || input.Length == Console.WindowWidth - startLeft) { // Console will have automatically moved the cursor down Console.SetCursorPosition(startLeft - Text.LastLine().Length, Console.CursorTop); if (BoxBackground.HasValue) WriteBackground(Console.WindowWidth - (startLeft - Text.LastLine().Length), BoxBackground.Value); } } } while (key.Key != ConsoleKey.Enter && (!AllowEscape || (AllowEscape && key.Key != ConsoleKey.Escape))); if (input.Length == 0) { ConsoleUtils.SetColor(ConsoleColor.Gray); Console.Write("None"); ConsoleUtils.ResetColor(); } Console.CursorVisible = cursorVisibility; Console.WriteLine(); if (key.Key == ConsoleKey.Escape && AllowEscape) return null; return input; } } public abstract class Prompt { /// /// Text to be displayed before the input. /// [Optional] public string Text { get; set; } = ""; [Optional] public int? MaxLength { get; set; } /// /// (Optional) /// [Optional] public ConsoleColor? InputForeground { get; set; } = null; [Optional] public ConsoleColor? InputBackground { get; set; } = null; public bool BindToOpenFrame { get; set; } = true; public bool AllowEscape { get; set; } = true; internal static void WriteBackground(int length, ConsoleColor color) { if (Console.CursorLeft + length > Console.WindowWidth) throw new Exception("Critical Error"); int leftStart = Console.CursorLeft; int topStart = Console.CursorTop; ConsoleUtils.SetColor(Console.ForegroundColor, color); Console.Write(new string(' ', length)); ConsoleUtils.ResetColor(); Console.SetCursorPosition(leftStart, topStart); } } }