diff --git a/src/ClipDiff.Windows/Hotkeys/HotKeyGesture.cs b/src/ClipDiff.Windows/Hotkeys/HotKeyGesture.cs index 031d1b0..022c8f0 100644 --- a/src/ClipDiff.Windows/Hotkeys/HotKeyGesture.cs +++ b/src/ClipDiff.Windows/Hotkeys/HotKeyGesture.cs @@ -8,27 +8,37 @@ internal enum HotKeyModifiers : uint None = 0, Alt = 0x0001, Control = 0x0002, - Shift = 0x0004, - Windows = 0x0008 + Shift = 0x0004 } internal sealed record HotKeyGesture(HotKeyModifiers Modifiers, uint VirtualKey) { + private const uint VirtualKeyShift = 0x10; + private const uint VirtualKeyControl = 0x11; + private const uint VirtualKeyMenu = 0x12; private const uint VirtualKeyF4 = 0x73; private const uint VirtualKeyD = 0x44; private const uint VirtualKeyLeftWindows = 0x5B; private const uint VirtualKeyRightWindows = 0x5C; + private const uint VirtualKeyLeftShift = 0xA0; + private const uint VirtualKeyRightShift = 0xA1; + private const uint VirtualKeyLeftControl = 0xA2; + private const uint VirtualKeyRightControl = 0xA3; + private const uint VirtualKeyLeftMenu = 0xA4; + private const uint VirtualKeyRightMenu = 0xA5; + + // Win32 calls the Alt key VK_MENU; none of these modifier keys can be a shortcut's primary key. private static readonly HashSet ModifierVirtualKeys = [ - 0x10, - 0x11, - 0x12, - 0xA0, - 0xA1, - 0xA2, - 0xA3, - 0xA4, - 0xA5 + VirtualKeyShift, + VirtualKeyControl, + VirtualKeyMenu, + VirtualKeyLeftShift, + VirtualKeyRightShift, + VirtualKeyLeftControl, + VirtualKeyRightControl, + VirtualKeyLeftMenu, + VirtualKeyRightMenu ]; public static HotKeyGesture Default { get; } = new( @@ -78,10 +88,6 @@ public string DisplayText parts.Add("Shift"); } - // If no modifiers and VirtualKey is 0, treat as invalid for display purposes - if (parts.Count == 0 && VirtualKey == 0) - return "Unassigned"; - parts.Add(FormatVirtualKey(VirtualKey)); return string.Join('+', parts); } diff --git a/tests/ClipDiff.Windows.Tests/HotKeyGestureTests.cs b/tests/ClipDiff.Windows.Tests/HotKeyGestureTests.cs index e178fc1..7f4da0e 100644 --- a/tests/ClipDiff.Windows.Tests/HotKeyGestureTests.cs +++ b/tests/ClipDiff.Windows.Tests/HotKeyGestureTests.cs @@ -28,13 +28,49 @@ public void FormatsTopRowNumpadAndFunctionKeys() } [TestMethod] - public void RejectsUnsafeOrIncompleteShortcuts() + public void RequiresControlOrAltModifier() { Assert.IsFalse(new HotKeyGesture(HotKeyModifiers.None, 0x44).IsValid); Assert.IsFalse(new HotKeyGesture(HotKeyModifiers.Shift, 0x44).IsValid); + } + + [TestMethod] + public void EnforcesVirtualKeyBoundaries() + { + Assert.IsFalse(new HotKeyGesture(HotKeyModifiers.Control, 0).IsValid); + Assert.IsTrue(new HotKeyGesture(HotKeyModifiers.Control, 0xFE).IsValid); + Assert.IsFalse(new HotKeyGesture(HotKeyModifiers.Control, 0xFF).IsValid); + } + + [TestMethod] + public void RejectsModifierAsPrimaryKey() + { Assert.IsFalse(new HotKeyGesture(HotKeyModifiers.Control, 0x11).IsValid); + } + + [TestMethod] + public void RejectsAltF4() + { Assert.IsFalse(new HotKeyGesture(HotKeyModifiers.Alt, 0x73).IsValid); - Assert.IsFalse(new HotKeyGesture(HotKeyModifiers.Windows, 0x44).IsValid); + } + + [TestMethod] + public void RejectsUnsupportedModifierFlags() + { + const HotKeyModifiers unsupportedWindowsModifier = (HotKeyModifiers)0x0008; + + Assert.IsFalse(new HotKeyGesture(unsupportedWindowsModifier, 0x44).IsValid); + Assert.IsFalse( + new HotKeyGesture(HotKeyModifiers.Control | unsupportedWindowsModifier, 0x44).IsValid); + } + + [TestMethod] + public void InvalidShortcutsDisplayAsUnassigned() + { + Assert.AreEqual("Unassigned", new HotKeyGesture(HotKeyModifiers.None, 0x44).DisplayText); + Assert.AreEqual("Unassigned", new HotKeyGesture(HotKeyModifiers.Control, 0).DisplayText); + Assert.AreEqual("Unassigned", new HotKeyGesture(HotKeyModifiers.Control, 0x11).DisplayText); + Assert.AreEqual("Unassigned", new HotKeyGesture(HotKeyModifiers.Alt, 0x73).DisplayText); } [TestMethod]