Files
FSI.BT.IR.Tools/Kalk/Kalk.Core/Model/KalkConsoleKey.cs
Maier Stephan SI b684704bf8 Sicherung
2023-01-20 16:09:00 +01:00

233 lines
8.7 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using Scriban.Runtime;
namespace Kalk.Core
{
public readonly struct KalkConsoleKey : IEquatable<KalkConsoleKey>
{
// Maps of name to ConsoleKey
private static readonly Dictionary<string, ConsoleKey> MapNameToConsoleKey = new Dictionary<string, ConsoleKey>(StringComparer.OrdinalIgnoreCase);
// Maps of ConsoleKey to name
private static readonly Dictionary<ConsoleKey, string> MapConsoleKeyToName = new Dictionary<ConsoleKey, string>();
static KalkConsoleKey()
{
var names = Enum.GetNames<ConsoleKey>();
var values = Enum.GetValues<ConsoleKey>();
for (var i = 0; i < names.Length; i++)
{
var name = StandardMemberRenamer.Rename(names[i]);
var key = values[i];
if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9)
{
name = ('0' + (key - ConsoleKey.D0)).ToString(CultureInfo.InvariantCulture);
}
MapKey(name, key);
}
MapKey("left", ConsoleKey.LeftArrow);
MapKey("right", ConsoleKey.RightArrow);
MapKey("up", ConsoleKey.UpArrow);
MapKey("down", ConsoleKey.DownArrow);
}
private static void MapKey(string name, ConsoleKey key)
{
MapNameToConsoleKey.Add(name, key);
MapConsoleKeyToName[key] = name;
}
public KalkConsoleKey(ConsoleModifiers modifiers, ConsoleKey key, char keyChar)
{
Modifiers = modifiers;
Key = key;
// Normalize keychar, whenever there is a modifier, we don't store the character
if (modifiers == ConsoleModifiers.Shift && key >= ConsoleKey.A && key <= ConsoleKey.Z)
{
Modifiers = 0;
KeyChar = keyChar;
}
else
{
// We need to force char to 0 when backspace, as it is not consistent between Windows and macOS/Linux
KeyChar = Modifiers != 0 || key == ConsoleKey.Backspace ? (char)0 : keyChar;
}
}
public ConsoleModifiers Modifiers { get; }
public ConsoleKey Key { get; }
public char KeyChar { get; }
public bool Equals(KalkConsoleKey other)
{
return Key == other.Key &&
KeyChar == other.KeyChar &&
Modifiers == other.Modifiers;
}
public override bool Equals(object obj)
{
return obj is KalkConsoleKey other && Equals(other);
}
public override int GetHashCode()
{
var hashCode = (int)Modifiers;
hashCode = (hashCode * 397) ^ (int) Key;
hashCode = (hashCode * 397) ^ (int) KeyChar;
return hashCode;
}
public static bool operator ==(KalkConsoleKey left, KalkConsoleKey right)
{
return left.Equals(right);
}
public static bool operator !=(KalkConsoleKey left, KalkConsoleKey right)
{
return !left.Equals(right);
}
public static KalkConsoleKey Parse(string keyText)
{
if (keyText == null) throw new ArgumentNullException(nameof(keyText));
ConsoleModifiers modifiers = 0;
ConsoleKey key = 0;
char keyChar = (char)0;
int index = 0;
bool expectingPlus = false;
while (index < keyText.Length)
{
if (expectingPlus)
{
if (keyText[index] != '+')
{
throw new ArgumentException($"Unexpected character `{keyText[index]}`. Expecting a + between keys.", nameof(keyText));
}
index++;
expectingPlus = false;
}
else
{
if (string.Compare(keyText, index, "CTRL", 0, "CTRL".Length, true) == 0)
{
if ((modifiers & ConsoleModifiers.Control) != 0) throw new ArgumentException("Cannot have multiple CTRL keys in a shortcut.", nameof(keyText));
modifiers |= ConsoleModifiers.Control;
index += "CTRL".Length;
expectingPlus = true;
}
else if (string.Compare(keyText, index, "ALT", 0, "ALT".Length, true) == 0)
{
if ((modifiers & ConsoleModifiers.Alt) != 0) throw new ArgumentException("Cannot have multiple ALT keys in a shortcut.", nameof(keyText));
modifiers |= ConsoleModifiers.Alt;
index += "ALT".Length;
expectingPlus = true;
}
else if (string.Compare(keyText, index, "SHIFT", 0, "SHIFT".Length, true) == 0)
{
// Skip the shift but don't expect it's modifiers
index += "SHIFT".Length;
expectingPlus = true;
}
else
{
var remainingText = keyText.Substring(index);
expectingPlus = false;
if (!MapNameToConsoleKey.TryGetValue(remainingText, out key))
{
throw new ArgumentException($"Invalid key found `{remainingText}`. Expecting: {string.Join(", ", Enum.GetNames(typeof(ConsoleKey)))}.", nameof(keyText));
}
if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9)
{
keyChar = (char) ('0' + (int) key - (int) ConsoleKey.D0);
}
else if (key >= ConsoleKey.A && key <= ConsoleKey.Z)
{
keyChar = remainingText[0];
}
else
{
keyChar = key switch
{
ConsoleKey.Add => '+',
ConsoleKey.Attention => '!',
ConsoleKey.Divide => '/',
ConsoleKey.Multiply => '*',
ConsoleKey.Subtract => '-',
ConsoleKey.OemPlus => '=',
ConsoleKey.OemMinus => '-',
ConsoleKey.OemPeriod => ';',
ConsoleKey.OemComma => ',',
ConsoleKey.Tab => '\t',
ConsoleKey.Enter => '\r',
ConsoleKey.Spacebar => ' ',
ConsoleKey.Backspace => (char)0,
ConsoleKey.Escape => (char) 0x1b,
_ => keyChar
};
}
break;
}
}
}
return new KalkConsoleKey(modifiers, key, keyChar);
}
public override string ToString()
{
var builder = new StringBuilder();
var modifiers = Modifiers;
if ((modifiers & ConsoleModifiers.Control) != 0)
{
builder.Append("ctrl");
}
if ((modifiers & ConsoleModifiers.Alt) != 0)
{
if (builder.Length > 0) builder.Append('+');
builder.Append("alt");
}
if ((modifiers & ConsoleModifiers.Shift) != 0)
{
if (builder.Length > 0) builder.Append('+');
builder.Append("shift");
}
if (builder.Length > 0)
{
builder.Append('+');
}
if (Key >= ConsoleKey.A && Key <= ConsoleKey.Z && KeyChar != 0)
{
builder.Append(KeyChar);
}
else
{
builder.Append(MapConsoleKeyToName[Key]);
}
return builder.ToString();
}
public static implicit operator KalkConsoleKey(ConsoleKeyInfo keyInfo)
{
return new KalkConsoleKey(keyInfo.Modifiers, keyInfo.Key, keyInfo.KeyChar);
}
public static implicit operator ConsoleKeyInfo(KalkConsoleKey key)
{
return new ConsoleKeyInfo(key.KeyChar, key.Key, (key.Modifiers & ConsoleModifiers.Shift) != 0, (key.Modifiers & ConsoleModifiers.Alt) != 0, (key.Modifiers & ConsoleModifiers.Control) != 0);
}
}
}