diff --git a/PowerToys.slnx b/PowerToys.slnx
index f0ac6c974e..645a61167d 100644
--- a/PowerToys.slnx
+++ b/PowerToys.slnx
@@ -532,6 +532,10 @@
+
+
+
+
diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorUI.UnitTests/KeyboardManagerEditorUI.UnitTests.csproj b/src/modules/keyboardmanager/KeyboardManagerEditorUI.UnitTests/KeyboardManagerEditorUI.UnitTests.csproj
new file mode 100644
index 0000000000..d7b146c41d
--- /dev/null
+++ b/src/modules/keyboardmanager/KeyboardManagerEditorUI.UnitTests/KeyboardManagerEditorUI.UnitTests.csproj
@@ -0,0 +1,24 @@
+
+
+
+
+
+ true
+ win-x64
+ win-arm64
+ false
+ false
+ false
+ $(SolutionDir)$(Platform)\$(Configuration)\tests\KeyboardManagerEditorUI.UnitTests\
+ Exe
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorUI.UnitTests/RemappingHelperTests.cs b/src/modules/keyboardmanager/KeyboardManagerEditorUI.UnitTests/RemappingHelperTests.cs
new file mode 100644
index 0000000000..fe85be7d8a
--- /dev/null
+++ b/src/modules/keyboardmanager/KeyboardManagerEditorUI.UnitTests/RemappingHelperTests.cs
@@ -0,0 +1,182 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using KeyboardManagerEditorUI.Helpers;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Windows.System;
+
+namespace KeyboardManagerEditorUI.UnitTests
+{
+ [TestClass]
+ public class RemappingHelperTests
+ {
+ // Test that modifier keys pressed in reverse order (Shift, Alt, Ctrl, Win)
+ // are sorted into the standard display order (Win, Ctrl, Alt, Shift)
+ [TestMethod]
+ public void SortModifierKeys_ShouldSortInCanonicalOrder_WhenPressedInReverseOrder()
+ {
+ // Arrange - keys in reverse of standard order
+ var keys = new List
+ {
+ VirtualKey.Shift,
+ VirtualKey.Menu, // Alt
+ VirtualKey.Control,
+ VirtualKey.LeftWindows,
+ };
+
+ // Act
+ RemappingHelper.SortModifierKeys(keys);
+
+ // Assert - should be Win, Ctrl, Alt, Shift
+ Assert.AreEqual(VirtualKey.LeftWindows, keys[0]);
+ Assert.AreEqual(VirtualKey.Control, keys[1]);
+ Assert.AreEqual(VirtualKey.Menu, keys[2]);
+ Assert.AreEqual(VirtualKey.Shift, keys[3]);
+ }
+
+ // Test the specific bug scenario: Win+Shift+S where Shift was pressed before Win
+ [TestMethod]
+ public void SortModifierKeys_ShouldShowWinBeforeShift_WhenShiftPressedFirst()
+ {
+ // Arrange - Shift pressed before Win (the reported bug scenario)
+ var keys = new List
+ {
+ VirtualKey.Shift,
+ VirtualKey.LeftWindows,
+ };
+
+ // Act
+ RemappingHelper.SortModifierKeys(keys);
+
+ // Assert - Win should come before Shift
+ Assert.AreEqual(VirtualKey.LeftWindows, keys[0]);
+ Assert.AreEqual(VirtualKey.Shift, keys[1]);
+ }
+
+ // Test that keys already in the correct order remain unchanged
+ [TestMethod]
+ public void SortModifierKeys_ShouldPreserveOrder_WhenAlreadyInCanonicalOrder()
+ {
+ // Arrange - already in correct order
+ var keys = new List
+ {
+ VirtualKey.LeftWindows,
+ VirtualKey.Control,
+ VirtualKey.Menu,
+ VirtualKey.Shift,
+ };
+
+ // Act
+ RemappingHelper.SortModifierKeys(keys);
+
+ // Assert - order should be unchanged
+ Assert.AreEqual(VirtualKey.LeftWindows, keys[0]);
+ Assert.AreEqual(VirtualKey.Control, keys[1]);
+ Assert.AreEqual(VirtualKey.Menu, keys[2]);
+ Assert.AreEqual(VirtualKey.Shift, keys[3]);
+ }
+
+ // Test that left/right variants of the same modifier maintain their relative
+ // position but are grouped correctly relative to other modifier types
+ [TestMethod]
+ public void SortModifierKeys_ShouldSortCorrectly_WithLeftRightVariants()
+ {
+ // Arrange - right shift before left control
+ var keys = new List
+ {
+ VirtualKey.RightShift,
+ VirtualKey.LeftControl,
+ };
+
+ // Act
+ RemappingHelper.SortModifierKeys(keys);
+
+ // Assert - Ctrl should come before Shift
+ Assert.AreEqual(VirtualKey.LeftControl, keys[0]);
+ Assert.AreEqual(VirtualKey.RightShift, keys[1]);
+ }
+
+ // Test with a single modifier key (should not throw)
+ [TestMethod]
+ public void SortModifierKeys_ShouldHandleSingleModifier()
+ {
+ // Arrange
+ var keys = new List { VirtualKey.Control };
+
+ // Act
+ RemappingHelper.SortModifierKeys(keys);
+
+ // Assert
+ Assert.AreEqual(1, keys.Count);
+ Assert.AreEqual(VirtualKey.Control, keys[0]);
+ }
+
+ // Test with an empty list (should not throw)
+ [TestMethod]
+ public void SortModifierKeys_ShouldHandleEmptyList()
+ {
+ // Arrange
+ var keys = new List();
+
+ // Act
+ RemappingHelper.SortModifierKeys(keys);
+
+ // Assert
+ Assert.AreEqual(0, keys.Count);
+ }
+
+ // Test GetModifierSortOrder returns correct values
+ [TestMethod]
+ public void GetModifierSortOrder_ShouldReturnCorrectOrder_ForAllModifierTypes()
+ {
+ // Win keys should return 0
+ Assert.AreEqual(0, RemappingHelper.GetModifierSortOrder(VirtualKey.LeftWindows));
+ Assert.AreEqual(0, RemappingHelper.GetModifierSortOrder(VirtualKey.RightWindows));
+
+ // Ctrl keys should return 1
+ Assert.AreEqual(1, RemappingHelper.GetModifierSortOrder(VirtualKey.Control));
+ Assert.AreEqual(1, RemappingHelper.GetModifierSortOrder(VirtualKey.LeftControl));
+ Assert.AreEqual(1, RemappingHelper.GetModifierSortOrder(VirtualKey.RightControl));
+
+ // Alt keys should return 2
+ Assert.AreEqual(2, RemappingHelper.GetModifierSortOrder(VirtualKey.Menu));
+ Assert.AreEqual(2, RemappingHelper.GetModifierSortOrder(VirtualKey.LeftMenu));
+ Assert.AreEqual(2, RemappingHelper.GetModifierSortOrder(VirtualKey.RightMenu));
+
+ // Shift keys should return 3
+ Assert.AreEqual(3, RemappingHelper.GetModifierSortOrder(VirtualKey.Shift));
+ Assert.AreEqual(3, RemappingHelper.GetModifierSortOrder(VirtualKey.LeftShift));
+ Assert.AreEqual(3, RemappingHelper.GetModifierSortOrder(VirtualKey.RightShift));
+ }
+
+ // Test that non-modifier keys get the highest sort order (4)
+ [TestMethod]
+ public void GetModifierSortOrder_ShouldReturnFour_ForNonModifierKeys()
+ {
+ Assert.AreEqual(4, RemappingHelper.GetModifierSortOrder(VirtualKey.A));
+ Assert.AreEqual(4, RemappingHelper.GetModifierSortOrder(VirtualKey.Space));
+ Assert.AreEqual(4, RemappingHelper.GetModifierSortOrder(VirtualKey.Enter));
+ }
+
+ // Test that two modifiers pressed out of order (Alt then Ctrl) get corrected
+ [TestMethod]
+ public void SortModifierKeys_ShouldSortCorrectly_WhenAltPressedBeforeCtrl()
+ {
+ // Arrange
+ var keys = new List
+ {
+ VirtualKey.Menu, // Alt
+ VirtualKey.Control,
+ };
+
+ // Act
+ RemappingHelper.SortModifierKeys(keys);
+
+ // Assert - Ctrl should come before Alt
+ Assert.AreEqual(VirtualKey.Control, keys[0]);
+ Assert.AreEqual(VirtualKey.Menu, keys[1]);
+ }
+ }
+}
diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorUI/Helpers/KeyboardHookHelper.cs b/src/modules/keyboardmanager/KeyboardManagerEditorUI/Helpers/KeyboardHookHelper.cs
index 6f71058111..224e0024b3 100644
--- a/src/modules/keyboardmanager/KeyboardManagerEditorUI/Helpers/KeyboardHookHelper.cs
+++ b/src/modules/keyboardmanager/KeyboardManagerEditorUI/Helpers/KeyboardHookHelper.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft Corporation
+// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
@@ -212,6 +212,11 @@ namespace KeyboardManagerEditorUI.Helpers
}
}
+ // Sort modifier keys by the standard display order: Win → Ctrl → Alt → Shift
+ // This matches the old C++ GetKeyVector behavior where keys are always shown in
+ // canonical order regardless of the order the user pressed them.
+ RemappingHelper.SortModifierKeys(modifierKeys);
+
foreach (var key in modifierKeys)
{
keyList.Add(_mappingService.GetKeyDisplayName((int)key));
diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorUI/Helpers/RemappingHelper.cs b/src/modules/keyboardmanager/KeyboardManagerEditorUI/Helpers/RemappingHelper.cs
index 48c32d853c..515b3a19e4 100644
--- a/src/modules/keyboardmanager/KeyboardManagerEditorUI/Helpers/RemappingHelper.cs
+++ b/src/modules/keyboardmanager/KeyboardManagerEditorUI/Helpers/RemappingHelper.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft Corporation
+// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
@@ -176,5 +176,32 @@ namespace KeyboardManagerEditorUI.Helpers
|| key == VirtualKey.LeftWindows
|| key == VirtualKey.RightWindows;
}
+
+ ///
+ /// Returns the sort order for a modifier key in the standard display order:
+ /// Win(0) → Ctrl(1) → Alt(2) → Shift(3).
+ /// Non-modifier (action) keys return 4.
+ /// This matches the old C++ GetKeyVector behavior.
+ ///
+ public static int GetModifierSortOrder(VirtualKey key)
+ {
+ return key switch
+ {
+ VirtualKey.LeftWindows or VirtualKey.RightWindows => 0,
+ VirtualKey.Control or VirtualKey.LeftControl or VirtualKey.RightControl => 1,
+ VirtualKey.Menu or VirtualKey.LeftMenu or VirtualKey.RightMenu => 2,
+ VirtualKey.Shift or VirtualKey.LeftShift or VirtualKey.RightShift => 3,
+ _ => 4,
+ };
+ }
+
+ ///
+ /// Sorts a list of modifier keys in the standard display order:
+ /// Win → Ctrl → Alt → Shift.
+ ///
+ public static void SortModifierKeys(List modifierKeys)
+ {
+ modifierKeys.Sort((a, b) => GetModifierSortOrder(a).CompareTo(GetModifierSortOrder(b)));
+ }
}
}