TASK 20 step 2: hotkey config UI + modifier chord persistence + conflict detection

This commit is contained in:
2026-08-26 06:48:14 -07:00
parent 89ab83be2e
commit 3a1484b3c5
12 changed files with 770 additions and 42 deletions
+29
View File
@@ -158,6 +158,9 @@ public class MainViewModel : ViewModelBase
private bool _brandFlashEnabled = true;
private bool _brandFlashActive;
// TASK 20 step 2 — hotkey bindings (custom chord per action)
private Dictionary<HotkeyId, HotkeyChord> _hotkeyBindings = new();
// TASK 10 — Polar license validation
private string _licenseKey = string.Empty;
private string _licenseMessage = string.Empty;
@@ -352,6 +355,15 @@ public class MainViewModel : ViewModelBase
// ─── Global hotkeys (TASK 20) — dispatch a registered hotkey to its action ───
/// <summary>
/// Callback set by MainWindow so the VM can trigger hotkey re-registration
/// after the user saves custom bindings from the config dialog.
/// </summary>
public Action<Dictionary<HotkeyId, HotkeyChord>>? HotkeyBindingsChanged { get; set; }
/// <summary>The current hotkey bindings (loaded from DB, updated on dialog save).</summary>
public Dictionary<HotkeyId, HotkeyChord> CurrentHotkeyBindings => _hotkeyBindings;
public void HandleHotkey(Services.HotkeyId id)
{
switch (id)
@@ -383,6 +395,18 @@ public class MainViewModel : ViewModelBase
if (scene != null) TransitionToScene(scene);
}
private void OpenHotkeyConfig()
{
var vm = new HotkeyConfigViewModel(_layoutStore);
var dialog = new HotkeyConfigDialog(vm) { Owner = Application.Current.MainWindow };
if (dialog.ShowDialog() == true)
{
// Reload bindings from DB (the dialog already saved them).
_hotkeyBindings = _layoutStore.LoadHotkeyBindings();
HotkeyBindingsChanged?.Invoke(_hotkeyBindings);
}
}
/// <summary>
/// Right-click-on-preview → "Show Webcam": offered when the active scene has no
/// webcam config (add one) or hides it (unhide — keeps the config row).
@@ -1485,6 +1509,7 @@ public class MainViewModel : ViewModelBase
public ICommand OpenLayoutCommand { get; }
public ICommand ActivateLicenseCommand { get; }
public ICommand OpenLicenseEntryCommand { get; }
public ICommand OpenHotkeyConfigCommand { get; }
public MainViewModel()
{
@@ -1558,10 +1583,14 @@ public class MainViewModel : ViewModelBase
OpenLayoutCommand = new RelayCommand(_ => OpenLayoutFile());
ActivateLicenseCommand = new RelayCommand(_ => _ = ActivateLicenseAsync(), _ => !IsLicenseValidating);
OpenLicenseEntryCommand = new RelayCommand(_ => IsLicenseEntryOpen = !IsLicenseEntryOpen);
OpenHotkeyConfigCommand = new RelayCommand(_ => OpenHotkeyConfig());
_layoutStore = new LayoutStore(LayoutPathOverride ?? DefaultLayoutPath);
_activeLayoutPath = _layoutStore.ActivePath;
// Load saved hotkey bindings (filled with defaults for any unbound actions).
_hotkeyBindings = _layoutStore.LoadHotkeyBindings();
BroadcastForm = new Services.LiveBroadcastFormViewModel(
() => _layoutStore,
() => _youtubeStream,