2019-12-08 19:54:52 +01:00
#nullable enable
using System ;
2019-12-04 18:57:18 +01:00
using System.Collections.Generic ;
using System.Globalization ;
2019-12-08 19:54:52 +01:00
using Swan.Lite.Logging ;
2019-12-04 18:57:18 +01:00
2019-12-08 19:54:52 +01:00
namespace 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
2019-12-04 18:57:18 +01:00
/// <summary>
2019-12-08 19:54:52 +01:00
/// Reads a key from the Terminal. This is the closest equivalent to Console.ReadKey.
2019-12-04 18:57:18 +01:00
/// </summary>
2019-12-08 19:54:52 +01:00
/// <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 ) {
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 ( String prompt , Boolean preventEcho = true ) {
if ( ! IsConsolePresent ) {
return default ;
}
lock ( SyncLock ) {
if ( prompt ! = null ) {
Write ( $"{GetNowFormatted()}{Settings.UserInputPrefix} << {prompt} " , ConsoleColor . White ) ;
}
ConsoleKeyInfo input = ReadKey ( true ) ;
String echo = preventEcho ? String . Empty : input . Key . ToString ( ) ;
WriteLine ( echo ) ;
return input ;
}
}
#endregion
#region Other Terminal Read Methods
/// <summary>
/// Clears the screen.
/// </summary>
public static void Clear ( ) {
Flush ( ) ;
Console . Clear ( ) ;
}
/// <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 line from the input.
/// </summary>
/// <param name="prompt">The prompt.</param>
/// <returns>The read line.</returns>
public static String ? ReadLine ( String prompt ) {
if ( ! IsConsolePresent ) {
return null ;
}
lock ( SyncLock ) {
Write ( $"{GetNowFormatted()}{Settings.UserInputPrefix} << {prompt}: " , ConsoleColor . White ) ;
return ReadLine ( ) ;
}
}
/// <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 ( String prompt , Int32 defaultNumber ) {
if ( ! IsConsolePresent ) {
return defaultNumber ;
}
lock ( SyncLock ) {
Write ( $"{GetNowFormatted()}{Settings.UserInputPrefix} << {prompt} (default is {defaultNumber}): " , 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>
/// <exception cref="ArgumentNullException">options.</exception>
public static ConsoleKeyInfo ReadPrompt ( String title , IDictionary < ConsoleKey , String > options , String anyKeyOption ) {
if ( ! IsConsolePresent ) {
return default ;
}
if ( options = = null ) {
throw new ArgumentNullException ( nameof ( options ) ) ;
}
const ConsoleColor textColor = ConsoleColor . White ;
Int32 lineLength = Console . WindowWidth ;
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 ( CultureInfo . CurrentCulture , textFormat , String . IsNullOrWhiteSpace ( title ) ? " Select an option from the list below." : $" {title}" ) ;
Write ( titleText , textColor ) ;
Table . Vertical ( ) ;
}
{
// Title Bottom
Table . LeftTee ( ) ;
Table . Horizontal ( lineLength - 2 ) ;
Table . RightTee ( ) ;
}
// Options
foreach ( KeyValuePair < ConsoleKey , String > kvp in options ) {
Table . Vertical ( ) ;
Write ( String . Format ( CultureInfo . CurrentCulture , textFormat , $" {" [ " + kvp.Key + " ] ",-10} {kvp.Value}" ) , textColor ) ;
Table . Vertical ( ) ;
}
// Any Key Options
if ( String . IsNullOrWhiteSpace ( anyKeyOption ) = = false ) {
Table . Vertical ( ) ;
Write ( String . Format ( CultureInfo . CurrentCulture , textFormat , " " ) , ConsoleColor . Gray ) ;
Table . Vertical ( ) ;
Table . Vertical ( ) ;
Write ( String . Format ( CultureInfo . CurrentCulture , textFormat , $" {" ",-10} {anyKeyOption}" ) , ConsoleColor . Gray ) ;
Table . Vertical ( ) ;
}
{
// Input section
Table . LeftTee ( ) ;
Table . Horizontal ( lineLength - 2 ) ;
Table . RightTee ( ) ;
Table . Vertical ( ) ;
Write ( String . Format ( CultureInfo . CurrentCulture , textFormat , Settings . UserOptionText ) , ConsoleColor . Green ) ;
Table . Vertical ( ) ;
Table . BottomLeft ( ) ;
Table . Horizontal ( lineLength - 2 ) ;
Table . BottomRight ( ) ;
}
}
Int32 inputLeft = Settings . UserOptionText . Length + 3 ;
SetCursorPosition ( inputLeft , CursorTop - 1 ) ;
ConsoleKeyInfo userInput = ReadKey ( true ) ;
Write ( userInput . Key . ToString ( ) , ConsoleColor . Gray ) ;
SetCursorPosition ( 0 , CursorTop + 2 ) ;
return userInput ;
}
#endregion
private static String GetNowFormatted ( ) = > $" {(String.IsNullOrWhiteSpace(TextLogger.LoggingTimeFormat) ? String.Empty : DateTime.Now.ToString(TextLogger.LoggingTimeFormat) + " ")}" ;
}
2019-12-04 18:57:18 +01:00
}