Enhance hotkey recording functionality in MainWindow.xaml.cs by adding escape key support to cancel recording and improving error handling. Introduce a delayed registration mechanism to prevent immediate re-entry of hotkeys, ensuring stability during hotkey updates.

This commit is contained in:
2026-02-22 12:33:55 -05:00
parent ae744f1077
commit 7f5d80ff68

View File

@@ -255,7 +255,7 @@ namespace ClipForge
{ {
if (_isRecordingHotkey) return; if (_isRecordingHotkey) return;
_isRecordingHotkey = true; _isRecordingHotkey = true;
HotkeyRecorderButton.Content = "Press any key..."; HotkeyRecorderButton.Content = "Press any key... (Esc to cancel)";
HotkeyRecorderButton.Focus(FocusState.Programmatic); HotkeyRecorderButton.Focus(FocusState.Programmatic);
HotkeyRecorderButton.KeyDown += OnHotkeyCaptureKeyDown; HotkeyRecorderButton.KeyDown += OnHotkeyCaptureKeyDown;
} }
@@ -265,26 +265,72 @@ namespace ClipForge
if (!_isRecordingHotkey) return; if (!_isRecordingHotkey) return;
e.Handled = true; e.Handled = true;
var key = e.Key; try
if (HotkeyHelper.IsModifierKey(key)) {
return; // wait for a non-modifier key var key = e.Key;
// Escape cancels without changing the hotkey
if (key == VirtualKey.Escape)
{
StopRecordingHotkey(restoreDisplay: true);
return;
}
if (HotkeyHelper.IsModifierKey(key))
return; // wait for a non-modifier key
if (key == VirtualKey.None)
return;
uint mod = 0; uint mod = 0;
if ((GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0) mod |= HotkeyHelper.MOD_CONTROL; if ((GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0) mod |= HotkeyHelper.MOD_CONTROL;
if ((GetAsyncKeyState(VK_MENU) & 0x8000) != 0) mod |= HotkeyHelper.MOD_ALT; if ((GetAsyncKeyState(VK_MENU) & 0x8000) != 0) mod |= HotkeyHelper.MOD_ALT;
if ((GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0) mod |= HotkeyHelper.MOD_SHIFT; if ((GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0) mod |= HotkeyHelper.MOD_SHIFT;
if ((GetAsyncKeyState(VK_LWIN) & 0x8000) != 0) mod |= HotkeyHelper.MOD_WIN; if ((GetAsyncKeyState(VK_LWIN) & 0x8000) != 0) mod |= HotkeyHelper.MOD_WIN;
uint vk = (uint)key; uint vk = (uint)key;
_settingsService.Settings.HotkeyModifiers = (int)mod; _settingsService.Settings.HotkeyModifiers = (int)mod;
_settingsService.Settings.HotkeyVirtualKey = (int)vk; _settingsService.Settings.HotkeyVirtualKey = (int)vk;
var ok = _hotkeyService.UpdateHotkey(mod, vk); var display = HotkeyHelper.ToDisplayString(mod, vk);
var display = HotkeyHelper.ToDisplayString(mod, vk); StopRecordingHotkey(restoreDisplay: false);
HotkeyRecorderButton.Content = ok ? display : display + " (in use?)"; HotkeyRecorderButton.Content = display;
// Defer registration so the key is released first; otherwise the new hotkey can fire
// immediately and re-enter (e.g. OnClipRequested) and crash.
_ = DelayedHotkeyRegister(mod, vk, display);
}
catch (Exception ex)
{
StopRecordingHotkey(restoreDisplay: true);
_ = ShowToastAsync("⚠ " + ex.Message);
}
}
private void StopRecordingHotkey(bool restoreDisplay)
{
HotkeyRecorderButton.KeyDown -= OnHotkeyCaptureKeyDown; HotkeyRecorderButton.KeyDown -= OnHotkeyCaptureKeyDown;
_isRecordingHotkey = false; _isRecordingHotkey = false;
if (restoreDisplay)
{
var s = _settingsService.Settings;
HotkeyRecorderButton.Content = HotkeyHelper.ToDisplayString((uint)s.HotkeyModifiers, (uint)s.HotkeyVirtualKey);
}
}
private async System.Threading.Tasks.Task DelayedHotkeyRegister(uint mod, uint vk, string display)
{
await System.Threading.Tasks.Task.Delay(300);
App.MainQueue?.TryEnqueue(() =>
{
try
{
var ok = _hotkeyService.UpdateHotkey(mod, vk);
HotkeyRecorderButton.Content = ok ? display : display + " (in use?)";
}
catch (Exception ex)
{
HotkeyRecorderButton.Content = display;
_ = ShowToastAsync("⚠ " + ex.Message);
}
});
} }
// --- NAV --- // --- NAV ---