UI 복잡 버전 234 RED→GREEN 완료(docs/UI_USECASES.md §A~§J): 라이브 테마 전환(DynamicResource 전환+ThemeResolver+HighContrast 테마, 재시작 없음), 디자인 토큰 체계·WCAG 대비 결함 5건 수정, 설정창 카테고리 내비/검색 재구성+AppConfig 40여 속성, 다운로드 관리자(속도/ETA/세그먼트맵/전체 일시정지-이어받기/CSV 내보내기/예약 게이트/중복 확인), 라이브러리·아카이브·작업공간·재생목록·즐겨찾기 편집 뷰, 접근성(자동화 속성 30여 개·커스텀 피어·docs/ACCESSIBILITY.md), 인터랙션(휠 클릭 새 탭·XButton 탐색·F12·영역 캡처·프로필 전환), 상태/알림(토스트·알림센터·온보딩·CrashReportDialog). 보강 테스트 35건+실창 E2E 4건(StaDispatcher 기반 — 프록시 설정이 기시작 핸들러를 건드리는 크래시 실결함 수정 포함) — 453/453 GREEN. 이전 미커밋 작업(CI 워크플로·Mdns·QueueHub·UpdateChecker·AGENTS/CLAUDE·빌드 스크립트) 일괄 포함
This commit is contained in:
parent
5a0ce730b2
commit
17f2f91152
88 changed files with 9463 additions and 1127 deletions
78
VideoDownloader.App/Services/ShortcutManager.cs
Normal file
78
VideoDownloader.App/Services/ShortcutManager.cs
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
namespace VideoDownloader.App.Services;
|
||||
|
||||
/// <summary>단일 단축키 정의(명령 키 + 표시 조합).</summary>
|
||||
public sealed record ShortcutBinding(string Command, string Gesture);
|
||||
|
||||
/// <summary>
|
||||
/// UC-K02/K03 단축키 관리 — 기본 조합표 위에 사용자 재배치(AppConfig.Shortcuts)를 얹고,
|
||||
/// 저장 전에 같은 조합을 쓰는 명령 충돌을 검증한다.
|
||||
/// </summary>
|
||||
public sealed class ShortcutManager
|
||||
{
|
||||
private readonly Dictionary<string, string> _user;
|
||||
|
||||
/// <summary>기본 단축키표(명령 키 → 기본 조합).</summary>
|
||||
public static readonly IReadOnlyDictionary<string, string> Defaults = new Dictionary<string, string>
|
||||
{
|
||||
["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<string, string>? userShortcuts = null)
|
||||
{
|
||||
_user = new Dictionary<string, string>(userShortcuts ?? new Dictionary<string, string>(), StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>명령의 현재 조합(사용자 지정 우선, 없으면 기본값).</summary>
|
||||
public string GestureFor(string command)
|
||||
=> _user.TryGetValue(command, out var g) ? g : Defaults.GetValueOrDefault(command, "");
|
||||
|
||||
/// <summary>전체 목록(기본 전체 + 사용자 오버라이드 반영).</summary>
|
||||
public List<ShortcutBinding> 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();
|
||||
|
||||
/// <summary>UC-K03 충돌 검증 — 같은 조합에 묶인 명령 그룹(2개 이상일 때만 반환).</summary>
|
||||
public List<List<ShortcutBinding>> FindConflicts()
|
||||
=> All()
|
||||
.GroupBy(b => Normalize(b.Gesture))
|
||||
.Where(g => g.Count() > 1 && g.Key.Length > 0)
|
||||
.Select(g => g.ToList())
|
||||
.ToList();
|
||||
|
||||
/// <summary>조합 문자열 파싱("Ctrl+Shift+P" → ModifierKeys+Key). 파싱 실패 시 null.</summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>조합 비교 정규화(대소문자·순서 무관).</summary>
|
||||
public static string Normalize(string gesture)
|
||||
=> string.Join("+", (gesture ?? "").Split('+').Select(p => p.Trim().ToLowerInvariant()).OrderBy(p => p));
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue