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));
}