using System; using System.Collections.Generic; namespace Unosquare.Swan { /// <summary> /// A console terminal helper to create nicer output and receive input from the user /// This class is thread-safe :). /// </summary> public static partial class Terminal { #region ReadKey /// <summary> /// Reads a key from the Terminal. This is the closest equivalent to Console.ReadKey. /// </summary> /// <param name="intercept">if set to <c>true</c> the pressed key will not be rendered to the output.</param> /// <param name="disableLocking">if set to <c>true</c> the output will continue to be shown. /// This is useful for services and daemons that are running as console applications and wait for a key to exit the program.</param> /// <returns>The console key information.</returns> public static ConsoleKeyInfo ReadKey(Boolean intercept, Boolean disableLocking = false) { if(IsConsolePresent == false) { return default; } if(disableLocking) { return Console.ReadKey(intercept); } lock(SyncLock) { Flush(); InputDone.Reset(); try { Console.CursorVisible = true; return Console.ReadKey(intercept); } finally { Console.CursorVisible = false; InputDone.Set(); } } } /// <summary> /// Reads a key from the Terminal. /// </summary> /// <param name="prompt">The prompt.</param> /// <param name="preventEcho">if set to <c>true</c> [prevent echo].</param> /// <returns>The console key information.</returns> public static ConsoleKeyInfo ReadKey(this String prompt, Boolean preventEcho) { if(IsConsolePresent == false) { return default; } lock(SyncLock) { if(prompt != null) { ($" {(String.IsNullOrWhiteSpace(Settings.LoggingTimeFormat) ? String.Empty : DateTime.Now.ToString(Settings.LoggingTimeFormat) + " ")}" + $"{Settings.UserInputPrefix} << {prompt} ").Write(ConsoleColor.White); } ConsoleKeyInfo input = ReadKey(true); String echo = preventEcho ? String.Empty : input.Key.ToString(); echo.WriteLine(); return input; } } /// <summary> /// Reads a key from the terminal preventing the key from being echoed. /// </summary> /// <param name="prompt">The prompt.</param> /// <returns>A value that identifies the console key.</returns> public static ConsoleKeyInfo ReadKey(this String prompt) => prompt.ReadKey(true); #endregion #region Other Terminal Read Methods /// <summary> /// Reads a line of text from the console. /// </summary> /// <returns>The read line.</returns> public static String ReadLine() { if(IsConsolePresent == false) { return default; } lock(SyncLock) { Flush(); InputDone.Reset(); try { Console.CursorVisible = true; return Console.ReadLine(); } finally { Console.CursorVisible = false; InputDone.Set(); } } } /// <summary> /// Reads a number from the input. If unable to parse, it returns the default number. /// </summary> /// <param name="prompt">The prompt.</param> /// <param name="defaultNumber">The default number.</param> /// <returns> /// Conversions of string representation of a number to its 32-bit signed integer equivalent. /// </returns> public static Int32 ReadNumber(this String prompt, Int32 defaultNumber) { if(IsConsolePresent == false) { return defaultNumber; } lock(SyncLock) { $" {DateTime.Now:HH:mm:ss} USR << {prompt} (default is {defaultNumber}): ".Write(ConsoleColor.White); String input = ReadLine(); return Int32.TryParse(input, out Int32 parsedInt) ? parsedInt : defaultNumber; } } /// <summary> /// Creates a table prompt where the user can enter an option based on the options dictionary provided. /// </summary> /// <param name="title">The title.</param> /// <param name="options">The options.</param> /// <param name="anyKeyOption">Any key option.</param> /// <returns>A value that identifies the console key that was pressed.</returns> public static ConsoleKeyInfo ReadPrompt(this String title, Dictionary<ConsoleKey, String> options, String anyKeyOption) { if(IsConsolePresent == false) { return default; } const ConsoleColor textColor = ConsoleColor.White; Int32 lineLength = Console.BufferWidth; Int32 lineAlign = -(lineLength - 2); String textFormat = "{0," + lineAlign + "}"; // lock the output as an atomic operation lock(SyncLock) { { // Top border Table.TopLeft(); Table.Horizontal(-lineAlign); Table.TopRight(); } { // Title Table.Vertical(); String titleText = String.Format( textFormat, String.IsNullOrWhiteSpace(title) ? " Select an option from the list below." : $" {title}"); titleText.Write(textColor); Table.Vertical(); } { // Title Bottom Table.LeftTee(); Table.Horizontal(lineLength - 2); Table.RightTee(); } // Options foreach(KeyValuePair<ConsoleKey, String> kvp in options) { Table.Vertical(); String.Format(textFormat, $" {"[ " + kvp.Key + " ]",-10} {kvp.Value}").Write(textColor); Table.Vertical(); } // Any Key Options if(String.IsNullOrWhiteSpace(anyKeyOption) == false) { Table.Vertical(); String.Format(textFormat, " ").Write(ConsoleColor.Gray); Table.Vertical(); Table.Vertical(); String.Format(textFormat, $" {" ",-10} {anyKeyOption}").Write(ConsoleColor.Gray); Table.Vertical(); } { // Input section Table.LeftTee(); Table.Horizontal(lineLength - 2); Table.RightTee(); Table.Vertical(); String.Format(textFormat, Settings.UserOptionText).Write(ConsoleColor.Green); Table.Vertical(); Table.BottomLeft(); Table.Horizontal(lineLength - 2); Table.BottomRight(); } } Int32 inputLeft = Settings.UserOptionText.Length + 3; SetCursorPosition(inputLeft, CursorTop - 2); ConsoleKeyInfo userInput = ReadKey(true); userInput.Key.ToString().Write(ConsoleColor.Gray); SetCursorPosition(0, CursorTop + 2); return userInput; } #endregion } }