Initial commit to new repository

This commit is contained in:
2026-04-03 18:23:52 +09:00
commit deffb33cf9
5248 changed files with 267762 additions and 0 deletions

View File

@@ -0,0 +1,127 @@
namespace AxCopilot.Core;
/// <summary>
/// "Alt+Space", "Ctrl+Shift+K" 형식의 핫키 문자열 파싱/포맷 유틸리티.
/// </summary>
public static class HotkeyParser
{
private static readonly Dictionary<string, int> _keyMap =
new(StringComparer.OrdinalIgnoreCase)
{
// 특수키
["Space"] = 0x20, ["Enter"] = 0x0D, ["Return"] = 0x0D,
["Tab"] = 0x09, ["Esc"] = 0x1B, ["Escape"] = 0x1B,
["Backspace"]= 0x08, ["Back"] = 0x08,
["Delete"] = 0x2E, ["Del"] = 0x2E,
["Insert"] = 0x2D, ["Ins"] = 0x2D,
["Home"] = 0x24, ["End"] = 0x23,
["PageUp"] = 0x21, ["PgUp"] = 0x21,
["PageDown"] = 0x22, ["PgDn"] = 0x22,
["PrintScreen"] = 0x2C, ["PrtSc"] = 0x2C, ["Snapshot"] = 0x2C,
["Pause"] = 0x13, ["Break"] = 0x13,
["ScrollLock"] = 0x91,
// 방향키
["Left"] = 0x25, ["Up"] = 0x26, ["Right"] = 0x27, ["Down"] = 0x28,
// 기호
["`"] = 0xC0, ["Grave"] = 0xC0,
["-"] = 0xBD, ["="] = 0xBB,
["["] = 0xDB, ["]"] = 0xDD,
["\\"] = 0xDC, [";"] = 0xBA,
["'"] = 0xDE, [","] = 0xBC,
["."] = 0xBE, ["/"] = 0xBF,
};
static HotkeyParser()
{
// AZ
for (char c = 'A'; c <= 'Z'; c++)
_keyMap[c.ToString()] = c;
// 09
for (char c = '0'; c <= '9'; c++)
_keyMap[c.ToString()] = c;
// F1F24
for (int i = 1; i <= 24; i++)
_keyMap[$"F{i}"] = 0x6F + i;
// Numpad 09
for (int i = 0; i <= 9; i++)
_keyMap[$"Num{i}"] = 0x60 + i;
}
/// <summary>
/// "Alt+Space" → <see cref="HotkeyDefinition"/>. 실패 시 false 반환.
/// </summary>
public static bool TryParse(string hotkey, out HotkeyDefinition result)
{
result = default;
if (string.IsNullOrWhiteSpace(hotkey)) return false;
var parts = hotkey.Split('+',
StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
bool ctrl = false, alt = false, shift = false, win = false;
int? vkCode = null;
foreach (var p in parts)
{
if (p.Equals("Ctrl", StringComparison.OrdinalIgnoreCase) ||
p.Equals("Control", StringComparison.OrdinalIgnoreCase))
{ ctrl = true; continue; }
if (p.Equals("Alt", StringComparison.OrdinalIgnoreCase))
{ alt = true; continue; }
if (p.Equals("Shift", StringComparison.OrdinalIgnoreCase))
{ shift = true; continue; }
if (p.Equals("Win", StringComparison.OrdinalIgnoreCase) ||
p.Equals("Windows", StringComparison.OrdinalIgnoreCase))
{ win = true; continue; }
if (_keyMap.TryGetValue(p, out int vk))
vkCode = vk;
else
return false; // 알 수 없는 키
}
if (vkCode == null) return false;
result = new HotkeyDefinition(vkCode.Value, ctrl, alt, shift, win);
return true;
}
/// <summary>
/// <see cref="HotkeyDefinition"/> → "Alt+Space" 형식 문자열.
/// </summary>
public static string Format(HotkeyDefinition def)
{
var parts = new List<string>(5);
if (def.Ctrl) parts.Add("Ctrl");
if (def.Alt) parts.Add("Alt");
if (def.Shift) parts.Add("Shift");
if (def.Win) parts.Add("Win");
parts.Add(VkToName(def.VkCode));
return string.Join("+", parts);
}
// VK 코드 → 읽기 좋은 이름 변환
private static string VkToName(int vk)
{
if (vk >= 0x41 && vk <= 0x5A) return ((char)vk).ToString(); // AZ
if (vk >= 0x30 && vk <= 0x39) return ((char)vk).ToString(); // 09
if (vk >= 0x70 && vk <= 0x87) return $"F{vk - 0x6F}"; // F1F24
if (vk >= 0x60 && vk <= 0x69) return $"Num{vk - 0x60}"; // Numpad
// 특수키 테이블에서 긴 이름 우선 검색
string? best = null;
foreach (var (name, code) in _keyMap)
{
if (code == vk && (best == null || name.Length > best.Length))
best = name;
}
return best ?? $"0x{vk:X2}";
}
}
/// <summary>
/// 파싱된 핫키 정의. VK 코드 + 요구 수정자 키.
/// </summary>
public record struct HotkeyDefinition(int VkCode, bool Ctrl, bool Alt, bool Shift, bool Win);