RaspberryIO_26/Swan.Lite/Terminal.Interaction.cs
2019-12-08 19:54:52 +01:00

239 lines
7.4 KiB
C#

#nullable enable
using System;
using System.Collections.Generic;
using System.Globalization;
using Swan.Lite.Logging;
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
/// <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) {
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) + " ")}";
}
}