Skip to content

Instantly share code, notes, and snippets.

@ZacharyPatten
Last active July 31, 2024 15:42
Show Gist options
  • Save ZacharyPatten/3426656cafe23dc1fd23836ebe43c9d5 to your computer and use it in GitHub Desktop.
Save ZacharyPatten/3426656cafe23dc1fd23836ebe43c9d5 to your computer and use it in GitHub Desktop.
C# custom Console.ReadLine with hidden characters
using System;
using Towel;
namespace ConsoleApp1
{
class Program
{
static void Main()
{
Console.WriteLine(
"This is an example of a custom \"Console.ReadLine\" with masked characters " +
"for use cases such as passwords. Type \"exit\" to close the program. It supports" +
"Backspace, Delete, Left/Right Arrows, Escape, Home, and End keypresses and the CTRL modifier.");
while (true)
{
Console.Write("Input: ");
string input = ConsoleHelper.HiddenReadLine();
Console.WriteLine(input);
if (input.ToLower() == "exit")
{
break;
}
}
}
}
}
namespace Towel
{
using System.Collections.Generic;
public static class ConsoleHelper
{
public static string HiddenReadLine(char shownCharacter = '*')
{
List<char> list = new List<char>();
HiddenReadLineBase(
shownCharacter: shownCharacter,
GetLength: () => list.Count,
Append: list.Add,
InsertAt: list.Insert,
RemoveAt: list.RemoveAt,
RemoveRange: list.RemoveRange,
Clear: list.Clear);
return string.Concat(list);
}
internal static void HiddenReadLineBase(
char shownCharacter,
Func<int> GetLength,
Action<char> Append,
Action<int, char> InsertAt,
Action<int> RemoveAt,
Action<int, int> RemoveRange = null,
Action Clear = null)
{
int position = 0;
RemoveRange ??= (index, length) =>
{
for (int i = 0; i < length; i++)
{
RemoveAt(index);
}
};
Clear ??= () => RemoveRange(0, GetLength());
static void MoveNegative(int count)
{
int bufferWidth = Console.BufferWidth;
int left = Console.CursorLeft;
int top = Console.CursorTop;
for (int i = 0; i < count; i++)
{
if (left > 0)
{
left--;
}
else
{
top--;
left = bufferWidth - 1;
}
}
Console.CursorLeft = left;
Console.CursorTop = top;
}
static void MovePositive(int count)
{
int bufferWidth = Console.BufferWidth;
int left = Console.CursorLeft;
int top = Console.CursorTop;
for (int i = 0; i < count; i++)
{
if (left == bufferWidth - 1)
{
top++;
left = 0;
}
else
{
left++;
}
}
Console.CursorLeft = left;
Console.CursorTop = top;
}
void MoveToOrigin() => MoveNegative(position);
void MoveToTail() => MovePositive(GetLength() - position);
static void ConsoleWriteChar(char @char)
{
int temp = Console.CursorLeft;
Console.Write(@char);
if (Console.CursorLeft == temp)
{
MovePositive(1);
}
}
static void ConsoleWriteString(string @string)
{
foreach (char c in @string)
{
ConsoleWriteChar(c);
}
}
while (true)
{
ConsoleKeyInfo keyInfo = Console.ReadKey(true);
if (keyInfo.Key is ConsoleKey.Enter)
{
if (!keyInfo.Modifiers.HasFlag(ConsoleModifiers.Control) &&
!keyInfo.Modifiers.HasFlag(ConsoleModifiers.Shift) &&
!keyInfo.Modifiers.HasFlag(ConsoleModifiers.Alt))
{
MovePositive(GetLength() - position);
Console.WriteLine();
break;
}
}
else if (keyInfo.Key is ConsoleKey.Backspace)
{
if (keyInfo.Modifiers.HasFlag(ConsoleModifiers.Control))
{
MoveToOrigin();
ConsoleWriteString(new string(shownCharacter, GetLength() - position) + new string(' ', position));
MoveNegative(GetLength());
RemoveRange(0, position);
position = 0;
}
else if (position > 0)
{
if (position == GetLength())
{
MoveNegative(1);
ConsoleWriteChar(' ');
MoveNegative(1);
}
else
{
MoveToTail();
MoveNegative(1);
ConsoleWriteChar(' ');
MoveNegative(GetLength() - position + 1);
}
RemoveAt(position - 1);
position--;
}
}
else if (keyInfo.Key is ConsoleKey.Delete)
{
if (!keyInfo.Modifiers.HasFlag(ConsoleModifiers.Control) &&
!keyInfo.Modifiers.HasFlag(ConsoleModifiers.Shift) &&
!keyInfo.Modifiers.HasFlag(ConsoleModifiers.Alt))
{
if (position < GetLength())
{
int left = Console.CursorLeft;
int top = Console.CursorTop;
MoveToTail();
MoveNegative(1);
ConsoleWriteChar(' ');
Console.CursorLeft = left;
Console.CursorTop = top;
RemoveAt(position);
continue;
}
}
}
else if (keyInfo.Key is ConsoleKey.Escape)
{
if (!keyInfo.Modifiers.HasFlag(ConsoleModifiers.Control) &&
!keyInfo.Modifiers.HasFlag(ConsoleModifiers.Shift) &&
!keyInfo.Modifiers.HasFlag(ConsoleModifiers.Alt))
{
MoveToOrigin();
int left = Console.CursorLeft;
int top = Console.CursorTop;
ConsoleWriteString(new string(' ', GetLength()));
Console.CursorLeft = left;
Console.CursorTop = top;
Clear();
position = 0;
}
}
else if (keyInfo.Key is ConsoleKey.Home)
{
if (!keyInfo.Modifiers.HasFlag(ConsoleModifiers.Shift) &&
!keyInfo.Modifiers.HasFlag(ConsoleModifiers.Alt))
{
if (keyInfo.Modifiers.HasFlag(ConsoleModifiers.Control))
{
MoveToOrigin();
ConsoleWriteString(new string(shownCharacter, GetLength() - position) + new string(' ', position));
MoveNegative(GetLength());
RemoveRange(0, position);
position = 0;
}
else
{
MoveToOrigin();
position = 0;
}
}
}
else if (keyInfo.Key is ConsoleKey.End)
{
if (!keyInfo.Modifiers.HasFlag(ConsoleModifiers.Shift) &&
!keyInfo.Modifiers.HasFlag(ConsoleModifiers.Alt))
{
if (keyInfo.Modifiers.HasFlag(ConsoleModifiers.Control))
{
MoveToOrigin();
ConsoleWriteString(new string(shownCharacter, position) + new string(' ', GetLength() - position));
MoveNegative(GetLength() - position);
RemoveRange(position, GetLength() - position);
}
else
{
MoveToTail();
position = GetLength();
}
}
}
else if (keyInfo.Key is ConsoleKey.LeftArrow)
{
if (!keyInfo.Modifiers.HasFlag(ConsoleModifiers.Shift) &&
!keyInfo.Modifiers.HasFlag(ConsoleModifiers.Alt))
{
if (keyInfo.Modifiers.HasFlag(ConsoleModifiers.Control))
{
MoveToOrigin();
position = 0;
}
else
{
if (position > 0)
{
MoveNegative(1);
position--;
}
}
}
}
else if (keyInfo.Key is ConsoleKey.RightArrow)
{
if (!keyInfo.Modifiers.HasFlag(ConsoleModifiers.Shift) &&
!keyInfo.Modifiers.HasFlag(ConsoleModifiers.Alt))
{
if (keyInfo.Modifiers.HasFlag(ConsoleModifiers.Control))
{
MoveToTail();
position = GetLength();
}
else
{
if (position < GetLength())
{
MovePositive(1);
position++;
}
}
}
}
else
{
if (!(keyInfo.KeyChar is '\0'))
{
if (position == GetLength())
{
ConsoleWriteChar(shownCharacter);
Append(keyInfo.KeyChar);
position++;
}
else
{
int left = Console.CursorLeft;
int top = Console.CursorTop;
MoveToTail();
ConsoleWriteChar(shownCharacter);
Console.CursorLeft = left;
Console.CursorTop = top;
MovePositive(1);
InsertAt(position, keyInfo.KeyChar);
position++;
}
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment