Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 21 additions & 15 deletions src/ClipDiff.Windows/Hotkeys/HotKeyGesture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<uint> 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(
Expand Down Expand Up @@ -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);
}
Expand Down
40 changes: 38 additions & 2 deletions tests/ClipDiff.Windows.Tests/HotKeyGestureTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down