Instantly share code, notes, and snippets.
Last active
July 31, 2024 15:42
-
Star
2
(2)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
Save ZacharyPatten/3426656cafe23dc1fd23836ebe43c9d5 to your computer and use it in GitHub Desktop.
C# custom Console.ReadLine with hidden characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode 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