namespace VideoDownloader.App.Services; /// 단일 단축키 정의(명령 키 + 표시 조합). public sealed record ShortcutBinding(string Command, string Gesture); /// /// UC-K02/K03 단축키 관리 — 기본 조합표 위에 사용자 재배치(AppConfig.Shortcuts)를 얹고, /// 저장 전에 같은 조합을 쓰는 명령 충돌을 검증한다. /// public sealed class ShortcutManager { private readonly Dictionary _user; /// 기본 단축키표(명령 키 → 기본 조합). public static readonly IReadOnlyDictionary Defaults = new Dictionary { ["newtab"] = "Ctrl+T", ["closetab"] = "Ctrl+W", ["reopentab"] = "Ctrl+Shift+T", ["find"] = "Ctrl+F", ["address"] = "Ctrl+L", ["downloads"] = "Ctrl+J", ["palette"] = "Ctrl+K", ["devtools"] = "F12", ["fullscreen"] = "F11", ["refresh"] = "F5", ["print"] = "Ctrl+P", ["pauseall"] = "Ctrl+Shift+P", }; public ShortcutManager(IDictionary? userShortcuts = null) { _user = new Dictionary(userShortcuts ?? new Dictionary(), StringComparer.OrdinalIgnoreCase); } /// 명령의 현재 조합(사용자 지정 우선, 없으면 기본값). public string GestureFor(string command) => _user.TryGetValue(command, out var g) ? g : Defaults.GetValueOrDefault(command, ""); /// 전체 목록(기본 전체 + 사용자 오버라이드 반영). public List All() => Defaults.Keys .Select(c => new ShortcutBinding(c, GestureFor(c))) .Concat(_user.Where(kv => !Defaults.ContainsKey(kv.Key)).Select(kv => new ShortcutBinding(kv.Key, kv.Value))) .ToList(); /// UC-K03 충돌 검증 — 같은 조합에 묶인 명령 그룹(2개 이상일 때만 반환). public List> FindConflicts() => All() .GroupBy(b => Normalize(b.Gesture)) .Where(g => g.Count() > 1 && g.Key.Length > 0) .Select(g => g.ToList()) .ToList(); /// 조합 문자열 파싱("Ctrl+Shift+P" → ModifierKeys+Key). 파싱 실패 시 null. public static (System.Windows.Input.ModifierKeys Mods, System.Windows.Input.Key Key)? Parse(string gesture) { if (string.IsNullOrWhiteSpace(gesture)) return null; var mods = System.Windows.Input.ModifierKeys.None; var key = System.Windows.Input.Key.None; foreach (var part in gesture.Split('+')) { switch (part.Trim().ToLowerInvariant()) { case "ctrl": mods |= System.Windows.Input.ModifierKeys.Control; continue; case "shift": mods |= System.Windows.Input.ModifierKeys.Shift; continue; case "alt": mods |= System.Windows.Input.ModifierKeys.Alt; continue; case "win": mods |= System.Windows.Input.ModifierKeys.Windows; continue; } if (!Enum.TryParse(part.Trim(), ignoreCase: true, out key)) return null; } return key == System.Windows.Input.Key.None ? null : (mods, key); } /// 조합 비교 정규화(대소문자·순서 무관). public static string Normalize(string gesture) => string.Join("+", (gesture ?? "").Split('+').Select(p => p.Trim().ToLowerInvariant()).OrderBy(p => p)); }