mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 19:57:57 +01:00
Refactored Keyboard Manager UI code and added unit tests for Keyboard Manager UI (#5718)
* Added tests for loading and saving remappings in the UI * Added tests for ApplyRemappings methods * Moved single key remap validation logic to separate method so that it can be tested * Added tests for single key remap validation in UI * Refactored shortcut validation code to be testable * Added some shortcut validation tests * Refactored code to be cleaner * Added tests for action key and modifier key selection and formatted file * Added tests for selecting None * Added tests for selecting Null * Added tests for WinL error * Added CtrlAltDel tests * Added tests for MapToSameKey * Added tests for mapping repeated shortcut * Fixed const correctness * Clean up type names with type alias * Clean up ValidateAndUpdateKeyBufferElement tests and tweak ValidateShortcutBufferElement signature * Fixed bug when None selected * Refactored one test as per test case framework * Cleaned up more tests * Cleaned up buffer validation tests * Added tests for KBM Common Helpers and Shortcut
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
#include <sstream>
|
||||
#include "../common/shared_constants.h"
|
||||
#include <shlwapi.h>
|
||||
#include "../common/keyboard_layout.h"
|
||||
|
||||
using namespace winrt::Windows::Foundation;
|
||||
|
||||
@@ -325,4 +326,59 @@ namespace KeyboardManagerHelper
|
||||
return first.Size() > second.Size();
|
||||
});
|
||||
}
|
||||
|
||||
// Function to check if a modifier has been repeated in the previous drop downs
|
||||
bool CheckRepeatedModifier(std::vector<DWORD>& currentKeys, int selectedKeyIndex, const std::vector<DWORD>& keyCodeList)
|
||||
{
|
||||
// check if modifier has already been added before in a previous drop down
|
||||
int currentDropDownIndex = -1;
|
||||
|
||||
// Find the key index of the current drop down selection so that we skip that index while searching for repeated modifiers
|
||||
for (int i = 0; i < currentKeys.size(); i++)
|
||||
{
|
||||
if (currentKeys[i] == keyCodeList[selectedKeyIndex])
|
||||
{
|
||||
currentDropDownIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool matchPreviousModifier = false;
|
||||
for (int i = 0; i < currentKeys.size(); i++)
|
||||
{
|
||||
// Skip the current drop down
|
||||
if (i != currentDropDownIndex)
|
||||
{
|
||||
// If the key type for the newly added key matches any of the existing keys in the shortcut
|
||||
if (KeyboardManagerHelper::GetKeyType(keyCodeList[selectedKeyIndex]) == KeyboardManagerHelper::GetKeyType(currentKeys[i]))
|
||||
{
|
||||
matchPreviousModifier = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matchPreviousModifier;
|
||||
}
|
||||
|
||||
// Function to get the selected key codes from the list of selected indices
|
||||
std::vector<DWORD> GetKeyCodesFromSelectedIndices(const std::vector<int32_t>& selectedIndices, const std::vector<DWORD>& keyCodeList)
|
||||
{
|
||||
std::vector<DWORD> keys;
|
||||
|
||||
for (int i = 0; i < selectedIndices.size(); i++)
|
||||
{
|
||||
int selectedKeyIndex = selectedIndices[i];
|
||||
if (selectedKeyIndex != -1 && keyCodeList.size() > selectedKeyIndex)
|
||||
{
|
||||
// If None is not the selected key
|
||||
if (keyCodeList[selectedKeyIndex] != 0)
|
||||
{
|
||||
keys.push_back(keyCodeList[selectedKeyIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ namespace winrt
|
||||
}
|
||||
}
|
||||
|
||||
class LayoutMap;
|
||||
|
||||
namespace KeyboardManagerHelper
|
||||
{
|
||||
// Type to distinguish between keys
|
||||
@@ -99,4 +101,10 @@ namespace KeyboardManagerHelper
|
||||
|
||||
// Function to sort a vector of shortcuts based on it's size
|
||||
void SortShortcutVectorBasedOnSize(std::vector<Shortcut>& shortcutVector);
|
||||
|
||||
// Function to check if a modifier has been repeated in the previous drop downs
|
||||
bool CheckRepeatedModifier(std::vector<DWORD>& currentKeys, int selectedKeyIndex, const std::vector<DWORD>& keyCodeList);
|
||||
|
||||
// Function to get the selected key codes from the list of selected indices
|
||||
std::vector<DWORD> GetKeyCodesFromSelectedIndices(const std::vector<int32_t>& selectedIndices, const std::vector<DWORD>& keyCodeList);
|
||||
}
|
||||
@@ -19,4 +19,9 @@ public:
|
||||
targetShortcut(Shortcut()), isShortcutInvoked(false), winKeyInvoked(ModifierKey::Disabled)
|
||||
{
|
||||
}
|
||||
|
||||
inline bool operator==(const RemapShortcut& sc) const
|
||||
{
|
||||
return targetShortcut == sc.targetShortcut && isShortcutInvoked == sc.isShortcutInvoked && winKeyInvoked == sc.winKeyInvoked;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -17,6 +17,12 @@ Shortcut::Shortcut(const std::wstring& shortcutVK) :
|
||||
}
|
||||
}
|
||||
|
||||
// Constructor to initialize shortcut from a list of keys
|
||||
Shortcut::Shortcut(const std::vector<DWORD>& keys)
|
||||
{
|
||||
SetKeyCodes(keys);
|
||||
}
|
||||
|
||||
// Function to return the number of keys in the shortcut
|
||||
int Shortcut::Size() const
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "ModifierKey.h"
|
||||
#include <variant>
|
||||
class InputInterface;
|
||||
class LayoutMap;
|
||||
namespace KeyboardManagerHelper
|
||||
@@ -26,6 +27,9 @@ public:
|
||||
// Constructor to initialize Shortcut from it's virtual key code string representation.
|
||||
Shortcut(const std::wstring& shortcutVK);
|
||||
|
||||
// Constructor to initialize shortcut from a list of keys
|
||||
Shortcut(const std::vector<DWORD>& keys);
|
||||
|
||||
// == operator
|
||||
inline bool operator==(const Shortcut& sc) const
|
||||
{
|
||||
@@ -166,3 +170,7 @@ public:
|
||||
// Function to check if the shortcut is illegal (i.e. Win+L or Ctrl+Alt+Del)
|
||||
KeyboardManagerHelper::ErrorType IsShortcutIllegal() const;
|
||||
};
|
||||
|
||||
using RemapBufferItem = std::vector<std::variant<DWORD, Shortcut>>;
|
||||
using RemapBufferRow = std::pair<RemapBufferItem, std::wstring>;
|
||||
using RemapBuffer = std::vector<RemapBufferRow>;
|
||||
|
||||
1442
src/modules/keyboardmanager/test/BufferValidationTests.cpp
Normal file
1442
src/modules/keyboardmanager/test/BufferValidationTests.cpp
Normal file
File diff suppressed because it is too large
Load Diff
173
src/modules/keyboardmanager/test/KeyboardManagerHelperTests.cpp
Normal file
173
src/modules/keyboardmanager/test/KeyboardManagerHelperTests.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
#include "pch.h"
|
||||
#include "CppUnitTest.h"
|
||||
#include <keyboardmanager/common/Helpers.h>
|
||||
#include "TestHelpers.h"
|
||||
#include <common/keyboard_layout.h>
|
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||
|
||||
namespace KeyboardManagerCommonTests
|
||||
{
|
||||
// Tests for methods in the KeyboardManagerHelper namespace
|
||||
TEST_CLASS (KeyboardManagerHelperTests)
|
||||
{
|
||||
public:
|
||||
TEST_METHOD_INITIALIZE(InitializeTestEnv)
|
||||
{
|
||||
}
|
||||
|
||||
// Test if the DoKeysOverlap method returns SameKeyPreviouslyMapped on passing the same key for both arguments
|
||||
TEST_METHOD (DoKeysOverlap_ShouldReturnSameKeyPreviouslyMapped_OnPassingSameKeyForBothArguments)
|
||||
{
|
||||
// Arrange
|
||||
DWORD key1 = 0x41;
|
||||
DWORD key2 = key1;
|
||||
|
||||
// Act
|
||||
auto result = KeyboardManagerHelper::DoKeysOverlap(key1, key2);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::SameKeyPreviouslyMapped);
|
||||
}
|
||||
|
||||
// Test if the DoKeysOverlap method returns ConflictingModifierKey on passing left modifier and common modifier
|
||||
TEST_METHOD (DoKeysOverlap_ShouldReturnConflictingModifierKey_OnPassingLeftModifierAndCommonModifierOfSameType)
|
||||
{
|
||||
// Arrange
|
||||
DWORD key1 = VK_LCONTROL;
|
||||
DWORD key2 = VK_CONTROL;
|
||||
|
||||
// Act
|
||||
auto result = KeyboardManagerHelper::DoKeysOverlap(key1, key2);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::ConflictingModifierKey);
|
||||
}
|
||||
|
||||
// Test if the DoKeysOverlap method returns ConflictingModifierKey on passing right modifier and common modifier
|
||||
TEST_METHOD (DoKeysOverlap_ShouldReturnConflictingModifierKey_OnPassingRightModifierAndCommonModifierOfSameType)
|
||||
{
|
||||
// Arrange
|
||||
DWORD key1 = VK_RCONTROL;
|
||||
DWORD key2 = VK_CONTROL;
|
||||
|
||||
// Act
|
||||
auto result = KeyboardManagerHelper::DoKeysOverlap(key1, key2);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::ConflictingModifierKey);
|
||||
}
|
||||
|
||||
// Test if the DoKeysOverlap method returns NoError on passing left modifier and right modifier
|
||||
TEST_METHOD (DoKeysOverlap_ShouldReturnNoError_OnPassingLeftModifierAndRightModifierOfSameType)
|
||||
{
|
||||
// Arrange
|
||||
DWORD key1 = VK_LCONTROL;
|
||||
DWORD key2 = VK_RCONTROL;
|
||||
|
||||
// Act
|
||||
auto result = KeyboardManagerHelper::DoKeysOverlap(key1, key2);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::NoError);
|
||||
}
|
||||
|
||||
// Test if the DoKeysOverlap method returns NoError on passing keys of different types
|
||||
TEST_METHOD (DoKeysOverlap_ShouldReturnNoError_OnPassingKeysOfDifferentTypes)
|
||||
{
|
||||
// Arrange
|
||||
DWORD key1 = VK_CONTROL;
|
||||
DWORD key2 = VK_SHIFT;
|
||||
|
||||
// Act
|
||||
auto result = KeyboardManagerHelper::DoKeysOverlap(key1, key2);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::NoError);
|
||||
}
|
||||
|
||||
// Test if the DoKeysOverlap method returns NoError on passing different action keys
|
||||
TEST_METHOD (DoKeysOverlap_ShouldReturnNoError_OnPassingDifferentActionKeys)
|
||||
{
|
||||
// Arrange
|
||||
DWORD key1 = 0x41;
|
||||
DWORD key2 = 0x42;
|
||||
|
||||
// Act
|
||||
auto result = KeyboardManagerHelper::DoKeysOverlap(key1, key2);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::NoError);
|
||||
}
|
||||
|
||||
// Test if the CheckRepeatedModifier method returns true on passing vector with same modifier repeated
|
||||
TEST_METHOD (CheckRepeatedModifier_ShouldReturnTrue_OnPassingSameModifierRepeated)
|
||||
{
|
||||
// Arrange
|
||||
std::vector<DWORD> keyList = LayoutMap().GetKeyCodeList(true);
|
||||
std::vector<DWORD> keys = { VK_CONTROL, VK_CONTROL, 0x41 };
|
||||
|
||||
// Act
|
||||
bool result = KeyboardManagerHelper::CheckRepeatedModifier(keys, TestHelpers::GetDropDownIndexFromDropDownList(VK_CONTROL, keyList), keyList);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result);
|
||||
}
|
||||
|
||||
// Test if the CheckRepeatedModifier method returns true on passing vector with conflicting modifier repeated
|
||||
TEST_METHOD (CheckRepeatedModifier_ShouldReturnTrue_OnPassingConflictingModifierRepeated)
|
||||
{
|
||||
// Arrange
|
||||
std::vector<DWORD> keyList = LayoutMap().GetKeyCodeList(true);
|
||||
std::vector<DWORD> keys = { VK_CONTROL, VK_LCONTROL, 0x41 };
|
||||
|
||||
// Act
|
||||
bool result = KeyboardManagerHelper::CheckRepeatedModifier(keys, TestHelpers::GetDropDownIndexFromDropDownList(VK_LCONTROL, keyList), keyList);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result);
|
||||
}
|
||||
|
||||
// Test if the CheckRepeatedModifier method returns false on passing vector with different modifiers
|
||||
TEST_METHOD (CheckRepeatedModifier_ShouldReturnFalse_OnPassingDifferentModifiers)
|
||||
{
|
||||
// Arrange
|
||||
std::vector<DWORD> keyList = LayoutMap().GetKeyCodeList(true);
|
||||
std::vector<DWORD> keys = { VK_CONTROL, VK_SHIFT, 0x41 };
|
||||
|
||||
// Act
|
||||
bool result = KeyboardManagerHelper::CheckRepeatedModifier(keys, TestHelpers::GetDropDownIndexFromDropDownList(VK_SHIFT, keyList), keyList);
|
||||
|
||||
// Assert
|
||||
Assert::IsFalse(result);
|
||||
}
|
||||
|
||||
// Test if the GetKeyCodesFromSelectedIndices method ignores -1 and 0 from argument in return value
|
||||
TEST_METHOD (GetKeyCodesFromSelectedIndices_ShouldIgnoreMinus1And0_OnPassingArgumentWithMinus1Or0)
|
||||
{
|
||||
// Arrange
|
||||
std::vector<DWORD> keyList = LayoutMap().GetKeyCodeList(true);
|
||||
std::vector<int32_t> indices = { -1, 0, -1, 0 };
|
||||
|
||||
// Act
|
||||
auto result = KeyboardManagerHelper::GetKeyCodesFromSelectedIndices(indices, keyList);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result.empty());
|
||||
}
|
||||
|
||||
// Test if the GetKeyCodesFromSelectedIndices method returns vector with key codes from vector with indices as per key code list
|
||||
TEST_METHOD (GetKeyCodesFromSelectedIndices_ShouldReturnVectorWithKeyCodes_OnPassingVectorWithIndicesAsPerKeyCodeList)
|
||||
{
|
||||
// Arrange
|
||||
std::vector<DWORD> keyList = LayoutMap().GetKeyCodeList(true);
|
||||
std::vector<int32_t> indices = { TestHelpers::GetDropDownIndexFromDropDownList(0x41, keyList), TestHelpers::GetDropDownIndexFromDropDownList(0x42, keyList) };
|
||||
|
||||
// Act
|
||||
auto result = KeyboardManagerHelper::GetKeyCodesFromSelectedIndices(indices, keyList);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result == std::vector<DWORD>{ 0x41, 0x42 });
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -107,6 +107,8 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AppSpecificShortcutRemappingTests.cpp" />
|
||||
<ClCompile Include="BufferValidationTests.cpp" />
|
||||
<ClCompile Include="LoadingAndSavingRemappingTests.cpp" />
|
||||
<ClCompile Include="MockedInputSanityTests.cpp" />
|
||||
<ClCompile Include="SetKeyEventTests.cpp" />
|
||||
<ClCompile Include="OSLevelShortcutRemappingTests.cpp" />
|
||||
@@ -114,7 +116,9 @@
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ShortcutTests.cpp" />
|
||||
<ClCompile Include="SingleKeyRemappingTests.cpp" />
|
||||
<ClCompile Include="KeyboardManagerHelperTests.cpp" />
|
||||
<ClCompile Include="TestHelpers.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -39,6 +39,18 @@
|
||||
<ClCompile Include="AppSpecificShortcutRemappingTests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="LoadingAndSavingRemappingTests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="BufferValidationTests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="KeyboardManagerHelperTests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ShortcutTests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h">
|
||||
|
||||
@@ -0,0 +1,521 @@
|
||||
#include "pch.h"
|
||||
#include "CppUnitTest.h"
|
||||
#include <keyboardmanager/common/KeyboardManagerState.h>
|
||||
#include <keyboardmanager/ui/LoadingAndSavingRemappingHelper.h>
|
||||
#include "TestHelpers.h"
|
||||
#include "../common/shared_constants.h"
|
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||
|
||||
namespace RemappingUITests
|
||||
{
|
||||
// Tests for methods in the LoadingAndSavingRemappingHelper namespace
|
||||
TEST_CLASS (LoadingAndSavingRemappingTests)
|
||||
{
|
||||
std::wstring testApp1 = L"testtrocess1.exe";
|
||||
std::wstring testApp2 = L"testprocess2.exe";
|
||||
|
||||
public:
|
||||
TEST_METHOD_INITIALIZE(InitializeTestEnv)
|
||||
{
|
||||
}
|
||||
|
||||
// Test if the CheckIfRemappingsAreValid method is successful when no remaps are passed
|
||||
TEST_METHOD (CheckIfRemappingsAreValid_ShouldReturnNoError_OnPassingNoRemaps)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Assert that remapping set is valid
|
||||
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::NoError);
|
||||
Assert::AreEqual(true, isSuccess);
|
||||
}
|
||||
|
||||
// Test if the CheckIfRemappingsAreValid method is successful when valid key to key remaps are passed
|
||||
TEST_METHOD (CheckIfRemappingsAreValid_ShouldReturnNoError_OnPassingValidKeyToKeyRemaps)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap A to B and B to C
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x41, 0x42 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x42, 0x43 }), std::wstring()));
|
||||
|
||||
// Assert that remapping set is valid
|
||||
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::NoError);
|
||||
Assert::AreEqual(true, isSuccess);
|
||||
}
|
||||
|
||||
// Test if the CheckIfRemappingsAreValid method is successful when valid key to shortcut remaps are passed
|
||||
TEST_METHOD (CheckIfRemappingsAreValid_ShouldReturnNoError_OnPassingValidKeyToShortcutRemaps)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap A to Ctrl+V and B to Alt+Tab
|
||||
Shortcut s1;
|
||||
s1.SetKey(VK_CONTROL);
|
||||
s1.SetKey(0x56);
|
||||
Shortcut s2;
|
||||
s2.SetKey(VK_MENU);
|
||||
s2.SetKey(VK_TAB);
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x41, s1 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x42, s2 }), std::wstring()));
|
||||
|
||||
// Assert that remapping set is valid
|
||||
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::NoError);
|
||||
Assert::AreEqual(true, isSuccess);
|
||||
}
|
||||
|
||||
// Test if the CheckIfRemappingsAreValid method is successful when valid shortcut to key remaps are passed
|
||||
TEST_METHOD (CheckIfRemappingsAreValid_ShouldReturnNoError_OnPassingValidShortcutToKeyRemaps)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap Ctrl+V to A and Alt+Tab to B
|
||||
Shortcut s1;
|
||||
s1.SetKey(VK_CONTROL);
|
||||
s1.SetKey(0x56);
|
||||
Shortcut s2;
|
||||
s2.SetKey(VK_MENU);
|
||||
s2.SetKey(VK_TAB);
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ s1, 0x41 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ s2, 0x42 }), std::wstring()));
|
||||
|
||||
// Assert that remapping set is valid
|
||||
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::NoError);
|
||||
Assert::AreEqual(true, isSuccess);
|
||||
}
|
||||
|
||||
// Test if the CheckIfRemappingsAreValid method is successful when valid shortcut to shortcut remaps are passed
|
||||
TEST_METHOD (CheckIfRemappingsAreValid_ShouldReturnNoError_OnPassingValidShortcutToShortcutRemaps)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap Ctrl+V to Ctrl+D and Alt+Tab to Win+A
|
||||
Shortcut src1;
|
||||
src1.SetKey(VK_CONTROL);
|
||||
src1.SetKey(0x56);
|
||||
Shortcut dest1;
|
||||
dest1.SetKey(VK_CONTROL);
|
||||
dest1.SetKey(0x44);
|
||||
Shortcut src2;
|
||||
src2.SetKey(VK_MENU);
|
||||
src2.SetKey(VK_TAB);
|
||||
Shortcut dest2;
|
||||
dest2.SetKey(CommonSharedConstants::VK_WIN_BOTH);
|
||||
dest2.SetKey(0x41);
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src1, dest1 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src2, dest2 }), std::wstring()));
|
||||
|
||||
// Assert that remapping set is valid
|
||||
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::NoError);
|
||||
Assert::AreEqual(true, isSuccess);
|
||||
}
|
||||
|
||||
// Test if the CheckIfRemappingsAreValid method is successful when valid remaps are passed
|
||||
TEST_METHOD (CheckIfRemappingsAreValid_ShouldReturnNoError_OnPassingValidRemapsOfAllTypes)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap Ctrl+V to Ctrl+D, Alt+Tab to A, A to B and B to Win+A
|
||||
Shortcut src1;
|
||||
src1.SetKey(VK_CONTROL);
|
||||
src1.SetKey(0x56);
|
||||
Shortcut dest1;
|
||||
dest1.SetKey(VK_CONTROL);
|
||||
dest1.SetKey(0x44);
|
||||
Shortcut src2;
|
||||
src2.SetKey(VK_MENU);
|
||||
src2.SetKey(VK_TAB);
|
||||
Shortcut dest2;
|
||||
dest2.SetKey(CommonSharedConstants::VK_WIN_BOTH);
|
||||
dest2.SetKey(0x41);
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src1, dest1 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src2, 0x41 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x41, 0x42 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x42, dest2 }), std::wstring()));
|
||||
|
||||
// Assert that remapping set is valid
|
||||
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::NoError);
|
||||
Assert::AreEqual(true, isSuccess);
|
||||
}
|
||||
|
||||
// Test if the CheckIfRemappingsAreValid method is unsuccessful when remaps with null keys are passed
|
||||
TEST_METHOD (CheckIfRemappingsAreValid_ShouldReturnRemapUnsuccessful_OnPassingRemapsWithNullKeys)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap A to NULL
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x41, NULL }), std::wstring()));
|
||||
|
||||
// Assert that remapping set is invalid
|
||||
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::RemapUnsuccessful);
|
||||
Assert::AreEqual(true, isSuccess);
|
||||
}
|
||||
|
||||
// Test if the CheckIfRemappingsAreValid method is unsuccessful when remaps with invalid shortcuts are passed
|
||||
TEST_METHOD (CheckIfRemappingsAreValid_ShouldReturnRemapUnsuccessful_OnPassingRemapsWithInvalidShortcut)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap A to incomplete shortcut (Ctrl)
|
||||
Shortcut src1;
|
||||
src1.SetKey(VK_CONTROL);
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x41, src1 }), std::wstring()));
|
||||
|
||||
// Assert that remapping set is invalid
|
||||
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::RemapUnsuccessful);
|
||||
Assert::AreEqual(true, isSuccess);
|
||||
}
|
||||
|
||||
// Test if the CheckIfRemappingsAreValid method is unsuccessful when remaps with the same key remapped twice are passed
|
||||
TEST_METHOD (CheckIfRemappingsAreValid_ShouldReturnRemapUnsuccessful_OnPassingRemapsWithSameKeyRemappedTwice)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap A to B and A to Ctrl+C
|
||||
Shortcut src1;
|
||||
src1.SetKey(VK_CONTROL);
|
||||
src1.SetKey(0x43);
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x41, 0x42 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x41, src1 }), std::wstring()));
|
||||
|
||||
// Assert that remapping set is invalid
|
||||
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::RemapUnsuccessful);
|
||||
Assert::AreEqual(true, isSuccess);
|
||||
}
|
||||
|
||||
// Test if the CheckIfRemappingsAreValid method is unsuccessful when remaps with the same shortcut remapped twice are passed
|
||||
TEST_METHOD (CheckIfRemappingsAreValid_ShouldReturnRemapUnsuccessful_OnPassingRemapsWithSameShortcutRemappedTwice)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap Ctrl+A to B and Ctrl+A to Ctrl+V
|
||||
Shortcut src1;
|
||||
src1.SetKey(VK_CONTROL);
|
||||
src1.SetKey(0x41);
|
||||
Shortcut dest1;
|
||||
dest1.SetKey(VK_CONTROL);
|
||||
dest1.SetKey(0x56);
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src1, 0x42 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src1, dest1 }), std::wstring()));
|
||||
|
||||
// Assert that remapping set is invalid
|
||||
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::RemapUnsuccessful);
|
||||
Assert::AreEqual(true, isSuccess);
|
||||
}
|
||||
|
||||
// Test if the CheckIfRemappingsAreValid method is unsuccessful when app specific remaps with the same shortcut remapped twice for the same target app are passed
|
||||
TEST_METHOD (CheckIfRemappingsAreValid_ShouldReturnRemapUnsuccessful_OnPassingAppSpecificRemapsWithSameShortcutRemappedTwiceForTheSameTargetApp)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap Ctrl+A to B and Ctrl+A to Ctrl+V for testApp1
|
||||
Shortcut src1;
|
||||
src1.SetKey(VK_CONTROL);
|
||||
src1.SetKey(0x41);
|
||||
Shortcut dest1;
|
||||
dest1.SetKey(VK_CONTROL);
|
||||
dest1.SetKey(0x56);
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src1, 0x42 }), testApp1));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src1, dest1 }), testApp1));
|
||||
|
||||
// Assert that remapping set is invalid
|
||||
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::RemapUnsuccessful);
|
||||
Assert::AreEqual(true, isSuccess);
|
||||
}
|
||||
|
||||
// Test if the CheckIfRemappingsAreValid method is successful when app specific remaps with the same shortcut remapped twice for different target apps are passed
|
||||
TEST_METHOD (CheckIfRemappingsAreValid_ShouldReturnNoError_OnPassingAppSpecificRemapsWithSameShortcutRemappedTwiceForDifferentTargetApps)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap Ctrl+A to B for testApp1 and Ctrl+A to Ctrl+V for testApp2
|
||||
Shortcut src1;
|
||||
src1.SetKey(VK_CONTROL);
|
||||
src1.SetKey(0x41);
|
||||
Shortcut dest1;
|
||||
dest1.SetKey(VK_CONTROL);
|
||||
dest1.SetKey(0x56);
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src1, 0x42 }), testApp1));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src1, dest1 }), testApp2));
|
||||
|
||||
// Assert that remapping set is valid
|
||||
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::NoError);
|
||||
Assert::AreEqual(true, isSuccess);
|
||||
}
|
||||
|
||||
// Test if the GetOrphanedKeys method return an empty vector on passing no remaps
|
||||
TEST_METHOD (GetOrphanedKeys_ShouldReturnEmptyVector_OnPassingNoRemaps)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Assert that there are no orphaned keys
|
||||
Assert::AreEqual(true, LoadingAndSavingRemappingHelper::GetOrphanedKeys(remapBuffer).empty());
|
||||
}
|
||||
|
||||
// Test if the GetOrphanedKeys method return one orphaned on passing one key remap
|
||||
TEST_METHOD (GetOrphanedKeys_ShouldReturnOneOrphanedKey_OnPassingOneKeyRemap)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap A to B
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x41, 0x42 }), std::wstring()));
|
||||
|
||||
// Assert that only A is orphaned
|
||||
Assert::AreEqual((size_t)1, LoadingAndSavingRemappingHelper::GetOrphanedKeys(remapBuffer).size());
|
||||
Assert::AreEqual((DWORD)0x41, LoadingAndSavingRemappingHelper::GetOrphanedKeys(remapBuffer)[0]);
|
||||
}
|
||||
|
||||
// Test if the GetOrphanedKeys method return an empty vector on passing swapped key remaps
|
||||
TEST_METHOD (GetOrphanedKeys_ShouldReturnEmptyVector_OnPassingSwappedKeyRemap)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap A to B and B to A
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x41, 0x42 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x42, 0x41 }), std::wstring()));
|
||||
|
||||
// Assert that there are no orphaned keys
|
||||
Assert::AreEqual(true, LoadingAndSavingRemappingHelper::GetOrphanedKeys(remapBuffer).empty());
|
||||
}
|
||||
|
||||
// Test if the GetOrphanedKeys method return one orphaned on passing two key remaps where one key is mapped to a remapped key
|
||||
TEST_METHOD (GetOrphanedKeys_ShouldReturnOneOrphanedKey_OnPassingTwoKeyRemapsWhereOneKeyIsMappedToARemappedKey)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap A to Ctrl+B and C to A
|
||||
Shortcut dest1;
|
||||
dest1.SetKey(VK_CONTROL);
|
||||
dest1.SetKey(0x42);
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x41, dest1 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x43, 0x41 }), std::wstring()));
|
||||
|
||||
// Assert that only C is orphaned
|
||||
Assert::AreEqual((size_t)1, LoadingAndSavingRemappingHelper::GetOrphanedKeys(remapBuffer).size());
|
||||
Assert::AreEqual((DWORD)0x43, LoadingAndSavingRemappingHelper::GetOrphanedKeys(remapBuffer)[0]);
|
||||
}
|
||||
|
||||
// Test if the PreProcessRemapTable method combines all the modifier pairs when the left and right modifiers are remapped to the same target
|
||||
TEST_METHOD (PreProcessRemapTable_ShouldCombineAllPairs_OnPassingLeftAndRightModifiersRemappedToTheSameTarget)
|
||||
{
|
||||
std::unordered_map<DWORD, std::variant<DWORD, Shortcut>> remapTable;
|
||||
|
||||
// Remap LCtrl and RCtrl to A, LAlt and RAlt to B, LShift and RShift to C, LWin and RWin to D
|
||||
remapTable[VK_LCONTROL] = 0x41;
|
||||
remapTable[VK_RCONTROL] = 0x41;
|
||||
remapTable[VK_LMENU] = 0x42;
|
||||
remapTable[VK_RMENU] = 0x42;
|
||||
remapTable[VK_LSHIFT] = 0x43;
|
||||
remapTable[VK_RSHIFT] = 0x43;
|
||||
remapTable[VK_LWIN] = 0x44;
|
||||
remapTable[VK_RWIN] = 0x44;
|
||||
|
||||
// Pre process table
|
||||
LoadingAndSavingRemappingHelper::PreProcessRemapTable(remapTable);
|
||||
|
||||
// Expected Ctrl remapped to A, Alt to B, Shift to C, Win to D
|
||||
std::unordered_map<DWORD, std::variant<DWORD, Shortcut>> expectedTable;
|
||||
expectedTable[VK_CONTROL] = 0x41;
|
||||
expectedTable[VK_MENU] = 0x42;
|
||||
expectedTable[VK_SHIFT] = 0x43;
|
||||
expectedTable[CommonSharedConstants::VK_WIN_BOTH] = 0x44;
|
||||
|
||||
bool areTablesEqual = (expectedTable == remapTable);
|
||||
Assert::AreEqual(true, areTablesEqual);
|
||||
}
|
||||
|
||||
// Test if the PreProcessRemapTable method does not combines any of the modifier pairs when the left and right modifiers are remapped to different targets
|
||||
TEST_METHOD (PreProcessRemapTable_ShouldNotCombineAnyPairs_OnPassingLeftAndRightModifiersRemappedToTheDifferentTargets)
|
||||
{
|
||||
std::unordered_map<DWORD, std::variant<DWORD, Shortcut>> remapTable;
|
||||
|
||||
// Remap left modifiers to A and right modifiers to B
|
||||
remapTable[VK_LCONTROL] = 0x41;
|
||||
remapTable[VK_RCONTROL] = 0x42;
|
||||
remapTable[VK_LMENU] = 0x41;
|
||||
remapTable[VK_RMENU] = 0x42;
|
||||
remapTable[VK_LSHIFT] = 0x41;
|
||||
remapTable[VK_RSHIFT] = 0x42;
|
||||
remapTable[VK_LWIN] = 0x41;
|
||||
remapTable[VK_RWIN] = 0x42;
|
||||
|
||||
// Pre process table
|
||||
LoadingAndSavingRemappingHelper::PreProcessRemapTable(remapTable);
|
||||
|
||||
// Expected unchanged table
|
||||
std::unordered_map<DWORD, std::variant<DWORD, Shortcut>> expectedTable;
|
||||
expectedTable[VK_LCONTROL] = 0x41;
|
||||
expectedTable[VK_RCONTROL] = 0x42;
|
||||
expectedTable[VK_LMENU] = 0x41;
|
||||
expectedTable[VK_RMENU] = 0x42;
|
||||
expectedTable[VK_LSHIFT] = 0x41;
|
||||
expectedTable[VK_RSHIFT] = 0x42;
|
||||
expectedTable[VK_LWIN] = 0x41;
|
||||
expectedTable[VK_RWIN] = 0x42;
|
||||
|
||||
bool areTablesEqual = (expectedTable == remapTable);
|
||||
Assert::AreEqual(true, areTablesEqual);
|
||||
}
|
||||
|
||||
// Test if the ApplySingleKeyRemappings method resets the keyboard manager state's single key remappings on passing an empty buffer
|
||||
TEST_METHOD (ApplySingleKeyRemappings_ShouldResetSingleKeyRemappings_OnPassingEmptyBuffer)
|
||||
{
|
||||
KeyboardManagerState testState;
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap A to B
|
||||
testState.AddSingleKeyRemap(0x41, 0x42);
|
||||
|
||||
// Apply the single key remaps from the buffer to the keyboard manager state variable
|
||||
LoadingAndSavingRemappingHelper::ApplySingleKeyRemappings(testState, remapBuffer, false);
|
||||
|
||||
// Assert that single key remapping in the kbm state variable is empty
|
||||
Assert::AreEqual((size_t)0, testState.singleKeyReMap.size());
|
||||
}
|
||||
|
||||
// Test if the ApplySingleKeyRemappings method copies only the valid remappings to the keyboard manager state variable when some of the remappings are invalid
|
||||
TEST_METHOD (ApplySingleKeyRemappings_ShouldCopyOnlyValidRemappings_OnPassingBufferWithSomeInvalidRemappings)
|
||||
{
|
||||
KeyboardManagerState testState;
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Add A->B, B->Ctrl+V, C to incomplete shortcut and D to incomplete key remappings to the buffer
|
||||
Shortcut s1;
|
||||
s1.SetKey(VK_CONTROL);
|
||||
s1.SetKey(0x56);
|
||||
Shortcut s2;
|
||||
s2.SetKey(VK_LMENU);
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x41, 0x42 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x42, s1 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x43, NULL }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x44, s2 }), std::wstring()));
|
||||
|
||||
// Apply the single key remaps from the buffer to the keyboard manager state variable
|
||||
LoadingAndSavingRemappingHelper::ApplySingleKeyRemappings(testState, remapBuffer, false);
|
||||
|
||||
// Expected A remapped to B, B remapped to Ctrl+V
|
||||
std::unordered_map<DWORD, std::variant<DWORD, Shortcut>> expectedTable;
|
||||
expectedTable[0x41] = 0x42;
|
||||
expectedTable[0x42] = s1;
|
||||
|
||||
bool areTablesEqual = (expectedTable == testState.singleKeyReMap);
|
||||
Assert::AreEqual(true, areTablesEqual);
|
||||
}
|
||||
|
||||
// Test if the ApplySingleKeyRemappings method splits common modifiers to their left and right version when copying to the keyboard manager state variable if remappings from common modifiers are passed
|
||||
TEST_METHOD (ApplySingleKeyRemappings_ShouldSplitRemappingsFromCommonModifiers_OnPassingBufferWithSomeMappingsFromCommonModifiers)
|
||||
{
|
||||
KeyboardManagerState testState;
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Add Ctrl->A, Alt->B, Shift->C and Win->D remappings to the buffer
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ VK_CONTROL, 0x41 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ VK_MENU, 0x42 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ VK_SHIFT, 0x43 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ CommonSharedConstants::VK_WIN_BOTH, 0x44 }), std::wstring()));
|
||||
|
||||
// Apply the single key remaps from the buffer to the keyboard manager state variable
|
||||
LoadingAndSavingRemappingHelper::ApplySingleKeyRemappings(testState, remapBuffer, false);
|
||||
|
||||
// Expected LCtrl/RCtrl remapped to A, LAlt/RAlt to B, LShift/RShift to C, LWin/RWin to D
|
||||
std::unordered_map<DWORD, std::variant<DWORD, Shortcut>> expectedTable;
|
||||
expectedTable[VK_LCONTROL] = 0x41;
|
||||
expectedTable[VK_RCONTROL] = 0x41;
|
||||
expectedTable[VK_LMENU] = 0x42;
|
||||
expectedTable[VK_RMENU] = 0x42;
|
||||
expectedTable[VK_LSHIFT] = 0x43;
|
||||
expectedTable[VK_RSHIFT] = 0x43;
|
||||
expectedTable[VK_LWIN] = 0x44;
|
||||
expectedTable[VK_RWIN] = 0x44;
|
||||
|
||||
bool areTablesEqual = (expectedTable == testState.singleKeyReMap);
|
||||
Assert::AreEqual(true, areTablesEqual);
|
||||
}
|
||||
|
||||
// Test if the ApplyShortcutRemappings method resets the keyboard manager state's os level and app specific shortcut remappings on passing an empty buffer
|
||||
TEST_METHOD (ApplyShortcutRemappings_ShouldResetShortcutRemappings_OnPassingEmptyBuffer)
|
||||
{
|
||||
KeyboardManagerState testState;
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap Ctrl+A to Ctrl+B for all apps and Ctrl+C to Alt+V for testApp1
|
||||
Shortcut src1;
|
||||
src1.SetKey(VK_CONTROL);
|
||||
src1.SetKey(0x41);
|
||||
Shortcut dest1;
|
||||
dest1.SetKey(VK_CONTROL);
|
||||
dest1.SetKey(0x42);
|
||||
Shortcut src2;
|
||||
src2.SetKey(VK_CONTROL);
|
||||
src2.SetKey(0x43);
|
||||
Shortcut dest2;
|
||||
dest2.SetKey(VK_MENU);
|
||||
dest2.SetKey(0x56);
|
||||
testState.AddOSLevelShortcut(src1, dest1);
|
||||
testState.AddAppSpecificShortcut(testApp1, src1, dest1);
|
||||
|
||||
// Apply the shortcut remaps from the buffer to the keyboard manager state variable
|
||||
LoadingAndSavingRemappingHelper::ApplyShortcutRemappings(testState, remapBuffer, false);
|
||||
|
||||
// Assert that shortcut remappings in the kbm state variable is empty
|
||||
Assert::AreEqual((size_t)0, testState.osLevelShortcutReMap.size());
|
||||
Assert::AreEqual((size_t)0, testState.appSpecificShortcutReMap.size());
|
||||
}
|
||||
|
||||
// Test if the ApplyShortcutRemappings method copies only the valid remappings to the keyboard manager state variable when some of the remappings are invalid
|
||||
TEST_METHOD (ApplyShortcutRemappings_ShouldCopyOnlyValidRemappings_OnPassingBufferWithSomeInvalidRemappings)
|
||||
{
|
||||
KeyboardManagerState testState;
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Add Ctrl+A->Ctrl+B, Ctrl+C->Alt+V, Ctrl+F->incomplete shortcut and Ctrl+G->incomplete key os level remappings to buffer
|
||||
// Add Ctrl+F->Alt+V, Ctrl+G->Ctrl+B, Ctrl+A->incomplete shortcut and Ctrl+C->incomplete key app specific remappings to buffer
|
||||
Shortcut src1;
|
||||
src1.SetKey(VK_CONTROL);
|
||||
src1.SetKey(0x41);
|
||||
Shortcut dest1;
|
||||
dest1.SetKey(VK_CONTROL);
|
||||
dest1.SetKey(0x42);
|
||||
Shortcut src2;
|
||||
src2.SetKey(VK_CONTROL);
|
||||
src2.SetKey(0x43);
|
||||
Shortcut dest2;
|
||||
dest2.SetKey(VK_MENU);
|
||||
dest2.SetKey(0x56);
|
||||
Shortcut src3;
|
||||
src3.SetKey(VK_CONTROL);
|
||||
src3.SetKey(0x46);
|
||||
Shortcut src4;
|
||||
src4.SetKey(VK_CONTROL);
|
||||
src4.SetKey(0x47);
|
||||
Shortcut dest4;
|
||||
dest4.SetKey(VK_CONTROL);
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src1, dest1 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src2, dest2 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src3, NULL }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src4, dest4 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src3, dest2 }), testApp1));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src4, dest1 }), testApp1));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src1, NULL }), testApp1));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src2, dest4 }), testApp1));
|
||||
|
||||
// Apply the shortcut remaps from the buffer to the keyboard manager state variable
|
||||
LoadingAndSavingRemappingHelper::ApplyShortcutRemappings(testState, remapBuffer, false);
|
||||
|
||||
// Ctrl+A->Ctrl+B and Ctrl+C->Alt+V
|
||||
std::map<Shortcut, RemapShortcut> expectedOSLevelTable;
|
||||
expectedOSLevelTable[src1] = RemapShortcut(dest1);
|
||||
expectedOSLevelTable[src2] = RemapShortcut(dest2);
|
||||
|
||||
// Ctrl+F->Alt+V and Ctrl+G->Ctrl+B for testApp1
|
||||
std::map<std::wstring, std::map<Shortcut, RemapShortcut>> expectedAppSpecificLevelTable;
|
||||
expectedAppSpecificLevelTable[testApp1][src3] = RemapShortcut(dest2);
|
||||
expectedAppSpecificLevelTable[testApp1][src4] = RemapShortcut(dest1);
|
||||
|
||||
bool areOSLevelTablesEqual = (expectedOSLevelTable == testState.osLevelShortcutReMap);
|
||||
bool areAppSpecificTablesEqual = (expectedAppSpecificLevelTable == testState.appSpecificShortcutReMap);
|
||||
Assert::AreEqual(true, areOSLevelTablesEqual);
|
||||
Assert::AreEqual(true, areAppSpecificTablesEqual);
|
||||
}
|
||||
};
|
||||
}
|
||||
176
src/modules/keyboardmanager/test/ShortcutTests.cpp
Normal file
176
src/modules/keyboardmanager/test/ShortcutTests.cpp
Normal file
@@ -0,0 +1,176 @@
|
||||
#include "pch.h"
|
||||
#include "CppUnitTest.h"
|
||||
#include <keyboardmanager/common/Shortcut.h>
|
||||
#include <keyboardmanager/common/Helpers.h>
|
||||
#include "TestHelpers.h"
|
||||
#include "../common/keyboard_layout.h"
|
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||
|
||||
namespace KeyboardManagerCommonTests
|
||||
{
|
||||
// Tests for methods in the Shortcut class
|
||||
TEST_CLASS (KeyboardManagerHelperTests)
|
||||
{
|
||||
public:
|
||||
TEST_METHOD_INITIALIZE(InitializeTestEnv)
|
||||
{
|
||||
}
|
||||
|
||||
// Test if the IsValidShortcut method returns false on passing shortcut with null action key
|
||||
TEST_METHOD (IsValidShortcut_ShouldReturnFalse_OnPassingShortcutWithNullActionKey)
|
||||
{
|
||||
// Arrange
|
||||
Shortcut s;
|
||||
s.SetKey(NULL);
|
||||
|
||||
// Act
|
||||
bool result = s.IsValidShortcut();
|
||||
|
||||
// Assert
|
||||
Assert::IsFalse(result);
|
||||
}
|
||||
|
||||
// Test if the IsValidShortcut method returns false on passing shortcut with only action key
|
||||
TEST_METHOD (IsValidShortcut_ShouldReturnFalse_OnPassingShortcutWithOnlyActionKey)
|
||||
{
|
||||
// Arrange
|
||||
Shortcut s;
|
||||
s.SetKey(0x41);
|
||||
|
||||
// Act
|
||||
bool result = s.IsValidShortcut();
|
||||
|
||||
// Assert
|
||||
Assert::IsFalse(result);
|
||||
}
|
||||
|
||||
// Test if the IsValidShortcut method returns false on passing shortcut with only modifier keys
|
||||
TEST_METHOD (IsValidShortcut_ShouldReturnFalse_OnPassingShortcutWithOnlyModifierKeys)
|
||||
{
|
||||
// Arrange
|
||||
Shortcut s;
|
||||
s.SetKey(VK_CONTROL);
|
||||
s.SetKey(VK_SHIFT);
|
||||
|
||||
// Act
|
||||
bool result = s.IsValidShortcut();
|
||||
|
||||
// Assert
|
||||
Assert::IsFalse(result);
|
||||
}
|
||||
|
||||
// Test if the IsValidShortcut method returns true on passing shortcut with modifier and action key
|
||||
TEST_METHOD (IsValidShortcut_ShouldReturnFalse_OnPassingShortcutWithModifierAndActionKey)
|
||||
{
|
||||
// Arrange
|
||||
Shortcut s;
|
||||
s.SetKey(VK_CONTROL);
|
||||
s.SetKey(0x41);
|
||||
|
||||
// Act
|
||||
bool result = s.IsValidShortcut();
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result);
|
||||
}
|
||||
|
||||
// Test if the DoKeysOverlap method returns NoError on passing invalid shortcut for one of the arguments
|
||||
TEST_METHOD (DoKeysOverlap_ShouldReturnNoError_OnPassingInvalidShortcutForOneOfTheArguments)
|
||||
{
|
||||
// Arrange
|
||||
Shortcut s1(std::vector<DWORD>{ NULL });
|
||||
Shortcut s2(std::vector<DWORD>{ VK_CONTROL, 0x41 });
|
||||
|
||||
// Act
|
||||
auto result = Shortcut::DoKeysOverlap(s1, s2);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::NoError);
|
||||
}
|
||||
|
||||
// Test if the DoKeysOverlap method returns SameShortcutPreviouslyMapped on passing same shortcut for both arguments
|
||||
TEST_METHOD (DoKeysOverlap_ShouldReturnSameShortcutPreviouslyMapped_OnPassingSameShortcutForBothArguments)
|
||||
{
|
||||
// Arrange
|
||||
Shortcut s1(std::vector<DWORD>{ VK_CONTROL, 0x41 });
|
||||
Shortcut s2 = s1;
|
||||
|
||||
// Act
|
||||
auto result = Shortcut::DoKeysOverlap(s1, s2);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::SameShortcutPreviouslyMapped);
|
||||
}
|
||||
|
||||
// Test if the DoKeysOverlap method returns NoError on passing shortcuts with different action keys
|
||||
TEST_METHOD (DoKeysOverlap_ShouldReturnNoError_OnPassingShortcutsWithDifferentActionKeys)
|
||||
{
|
||||
// Arrange
|
||||
Shortcut s1(std::vector<DWORD>{ VK_CONTROL, 0x42 });
|
||||
Shortcut s2(std::vector<DWORD>{ VK_CONTROL, 0x41 });
|
||||
|
||||
// Act
|
||||
auto result = Shortcut::DoKeysOverlap(s1, s2);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::NoError);
|
||||
}
|
||||
|
||||
// Test if the DoKeysOverlap method returns NoError on passing shortcuts with different modifiers
|
||||
TEST_METHOD (DoKeysOverlap_ShouldReturnNoError_OnPassingShortcutsWithDifferentModifiers)
|
||||
{
|
||||
// Arrange
|
||||
Shortcut s1(std::vector<DWORD>{ VK_CONTROL, 0x42 });
|
||||
Shortcut s2(std::vector<DWORD>{ VK_SHIFT, 0x42 });
|
||||
|
||||
// Act
|
||||
auto result = Shortcut::DoKeysOverlap(s1, s2);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::NoError);
|
||||
}
|
||||
|
||||
// Test if the DoKeysOverlap method returns ConflictingModifierShortcut on passing shortcuts with left modifier and common modifier
|
||||
TEST_METHOD (DoKeysOverlap_ShouldReturnConflictingModifierShortcut_OnPassingShortcutsWithLeftModifierAndCommonModifierOfSameType)
|
||||
{
|
||||
// Arrange
|
||||
Shortcut s1(std::vector<DWORD>{ VK_LCONTROL, 0x42 });
|
||||
Shortcut s2(std::vector<DWORD>{ VK_CONTROL, 0x42 });
|
||||
|
||||
// Act
|
||||
auto result = Shortcut::DoKeysOverlap(s1, s2);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::ConflictingModifierShortcut);
|
||||
}
|
||||
|
||||
// Test if the DoKeysOverlap method returns ConflictingModifierShortcut on passing shortcuts with right modifier and common modifier
|
||||
TEST_METHOD (DoKeysOverlap_ShouldReturnConflictingModifierShortcut_OnPassingShortcutsWithRightModifierAndCommonModifierOfSameType)
|
||||
{
|
||||
// Arrange
|
||||
Shortcut s1(std::vector<DWORD>{ VK_RCONTROL, 0x42 });
|
||||
Shortcut s2(std::vector<DWORD>{ VK_CONTROL, 0x42 });
|
||||
|
||||
// Act
|
||||
auto result = Shortcut::DoKeysOverlap(s1, s2);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::ConflictingModifierShortcut);
|
||||
}
|
||||
|
||||
// Test if the DoKeysOverlap method returns ConflictingModifierShortcut on passing shortcuts with left modifier and right modifier
|
||||
TEST_METHOD (DoKeysOverlap_ShouldReturnConflictingModifierShortcut_OnPassingShortcutsWithLeftModifierAndRightModifierOfSameType)
|
||||
{
|
||||
// Arrange
|
||||
Shortcut s1(std::vector<DWORD>{ VK_LCONTROL, 0x42 });
|
||||
Shortcut s2(std::vector<DWORD>{ VK_RCONTROL, 0x42 });
|
||||
|
||||
// Act
|
||||
auto result = Shortcut::DoKeysOverlap(s1, s2);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::NoError);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -22,4 +22,10 @@ namespace TestHelpers
|
||||
state.SetActivatedApp(maxLengthString);
|
||||
state.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp);
|
||||
}
|
||||
|
||||
// Function to return the index of the given key code from the drop down key list
|
||||
int GetDropDownIndexFromDropDownList(DWORD key, const std::vector<DWORD>& keyList)
|
||||
{
|
||||
return (int)std::distance(keyList.begin(), std::find(keyList.begin(), keyList.end(), key));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,4 +6,7 @@ namespace TestHelpers
|
||||
{
|
||||
// Function to reset the environment variables for tests
|
||||
void ResetTestEnv(MockedInput& input, KeyboardManagerState& state);
|
||||
|
||||
// Function to return the index of the given key code from the drop down key list
|
||||
int GetDropDownIndexFromDropDownList(DWORD key, const std::vector<DWORD>& keyList);
|
||||
}
|
||||
|
||||
@@ -6,3 +6,4 @@
|
||||
#include <shlwapi.h>
|
||||
#include <stdexcept>
|
||||
#include <unordered_set>
|
||||
#include "winrt/Windows.Foundation.h"
|
||||
|
||||
294
src/modules/keyboardmanager/ui/BufferValidationHelpers.cpp
Normal file
294
src/modules/keyboardmanager/ui/BufferValidationHelpers.cpp
Normal file
@@ -0,0 +1,294 @@
|
||||
#include "pch.h"
|
||||
#include "BufferValidationHelpers.h"
|
||||
#include <keyboardmanager/common/KeyboardManagerConstants.h>
|
||||
|
||||
namespace BufferValidationHelpers
|
||||
{
|
||||
// Function to validate and update an element of the key remap buffer when the selection has changed
|
||||
KeyboardManagerHelper::ErrorType ValidateAndUpdateKeyBufferElement(int rowIndex, int colIndex, int selectedKeyIndex, const std::vector<DWORD>& keyCodeList, RemapBuffer& remapBuffer)
|
||||
{
|
||||
KeyboardManagerHelper::ErrorType errorType = KeyboardManagerHelper::ErrorType::NoError;
|
||||
|
||||
// Check if the element was not found or the index exceeds the known keys
|
||||
if (selectedKeyIndex != -1 && keyCodeList.size() > selectedKeyIndex)
|
||||
{
|
||||
// Check if the value being set is the same as the other column
|
||||
if (remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)].index() == 0)
|
||||
{
|
||||
if (std::get<DWORD>(remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]) == keyCodeList[selectedKeyIndex])
|
||||
{
|
||||
errorType = KeyboardManagerHelper::ErrorType::MapToSameKey;
|
||||
}
|
||||
}
|
||||
|
||||
// If one column is shortcut and other is key no warning required
|
||||
|
||||
if (errorType == KeyboardManagerHelper::ErrorType::NoError && colIndex == 0)
|
||||
{
|
||||
// Check if the key is already remapped to something else
|
||||
for (int i = 0; i < remapBuffer.size(); i++)
|
||||
{
|
||||
if (i != rowIndex)
|
||||
{
|
||||
if (remapBuffer[i].first[colIndex].index() == 0)
|
||||
{
|
||||
KeyboardManagerHelper::ErrorType result = KeyboardManagerHelper::DoKeysOverlap(std::get<DWORD>(remapBuffer[i].first[colIndex]), keyCodeList[selectedKeyIndex]);
|
||||
if (result != KeyboardManagerHelper::ErrorType::NoError)
|
||||
{
|
||||
errorType = result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If one column is shortcut and other is key no warning required
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no error, set the buffer
|
||||
if (errorType == KeyboardManagerHelper::ErrorType::NoError)
|
||||
{
|
||||
remapBuffer[rowIndex].first[colIndex] = keyCodeList[selectedKeyIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
remapBuffer[rowIndex].first[colIndex] = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reset to null if the key is not found
|
||||
remapBuffer[rowIndex].first[colIndex] = NULL;
|
||||
}
|
||||
|
||||
return errorType;
|
||||
}
|
||||
|
||||
// Function to validate an element of the shortcut remap buffer when the selection has changed
|
||||
std::pair<KeyboardManagerHelper::ErrorType, DropDownAction> ValidateShortcutBufferElement(int rowIndex, int colIndex, uint32_t dropDownIndex, const std::vector<int32_t>& selectedIndices, std::wstring appName, bool isHybridControl, const std::vector<DWORD>& keyCodeList, const RemapBuffer& remapBuffer, bool dropDownFound)
|
||||
{
|
||||
BufferValidationHelpers::DropDownAction dropDownAction = BufferValidationHelpers::DropDownAction::NoAction;
|
||||
KeyboardManagerHelper::ErrorType errorType = KeyboardManagerHelper::ErrorType::NoError;
|
||||
size_t dropDownCount = selectedIndices.size();
|
||||
std::vector<DWORD> selectedKeyCodes = KeyboardManagerHelper::GetKeyCodesFromSelectedIndices(selectedIndices, keyCodeList);
|
||||
int selectedKeyIndex = dropDownFound ? selectedIndices[dropDownIndex] : -1;
|
||||
|
||||
if (selectedKeyIndex != -1 && keyCodeList.size() > selectedKeyIndex && dropDownFound)
|
||||
{
|
||||
// If only 1 drop down and action key is chosen: Warn that a modifier must be chosen (if the drop down is not for a hybrid scenario)
|
||||
if (dropDownCount == 1 && !KeyboardManagerHelper::IsModifierKey(keyCodeList[selectedKeyIndex]) && !isHybridControl)
|
||||
{
|
||||
// warn and reset the drop down
|
||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutStartWithModifier;
|
||||
}
|
||||
// If it is the last drop down
|
||||
else if (dropDownIndex == dropDownCount - 1)
|
||||
{
|
||||
// If last drop down and a modifier is selected: add a new drop down (max drop down count should be enforced)
|
||||
if (KeyboardManagerHelper::IsModifierKey(keyCodeList[selectedKeyIndex]) && dropDownCount < KeyboardManagerConstants::MaxShortcutSize)
|
||||
{
|
||||
// If it matched any of the previous modifiers then reset that drop down
|
||||
if (KeyboardManagerHelper::CheckRepeatedModifier(selectedKeyCodes, selectedKeyIndex, keyCodeList))
|
||||
{
|
||||
// warn and reset the drop down
|
||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutCannotHaveRepeatedModifier;
|
||||
}
|
||||
// If not, add a new drop down
|
||||
else
|
||||
{
|
||||
dropDownAction = BufferValidationHelpers::DropDownAction::AddDropDown;
|
||||
}
|
||||
}
|
||||
// If last drop down and a modifier is selected but there are already max drop downs: warn the user
|
||||
else if (KeyboardManagerHelper::IsModifierKey(keyCodeList[selectedKeyIndex]) && dropDownCount >= KeyboardManagerConstants::MaxShortcutSize)
|
||||
{
|
||||
// warn and reset the drop down
|
||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutMaxShortcutSizeOneActionKey;
|
||||
}
|
||||
// If None is selected but it's the last index: warn
|
||||
else if (keyCodeList[selectedKeyIndex] == 0)
|
||||
{
|
||||
// If it is a hybrid control and there are 2 drop downs then deletion is allowed
|
||||
if (isHybridControl && dropDownCount == KeyboardManagerConstants::MinShortcutSize)
|
||||
{
|
||||
// set delete drop down flag
|
||||
dropDownAction = BufferValidationHelpers::DropDownAction::DeleteDropDown;
|
||||
// do not delete the drop down now since there may be some other error which would cause the drop down to be invalid after removal
|
||||
}
|
||||
else
|
||||
{
|
||||
// warn and reset the drop down
|
||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutOneActionKey;
|
||||
}
|
||||
}
|
||||
// If none of the above, then the action key will be set
|
||||
}
|
||||
// If it is the not the last drop down
|
||||
else
|
||||
{
|
||||
if (KeyboardManagerHelper::IsModifierKey(keyCodeList[selectedKeyIndex]))
|
||||
{
|
||||
// If it matched any of the previous modifiers then reset that drop down
|
||||
if (KeyboardManagerHelper::CheckRepeatedModifier(selectedKeyCodes, selectedKeyIndex, keyCodeList))
|
||||
{
|
||||
// warn and reset the drop down
|
||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutCannotHaveRepeatedModifier;
|
||||
}
|
||||
// If not, the modifier key will be set
|
||||
}
|
||||
// If None is selected and there are more than 2 drop downs
|
||||
else if (keyCodeList[selectedKeyIndex] == 0 && dropDownCount > KeyboardManagerConstants::MinShortcutSize)
|
||||
{
|
||||
// set delete drop down flag
|
||||
dropDownAction = BufferValidationHelpers::DropDownAction::DeleteDropDown;
|
||||
// do not delete the drop down now since there may be some other error which would cause the drop down to be invalid after removal
|
||||
}
|
||||
else if (keyCodeList[selectedKeyIndex] == 0 && dropDownCount <= KeyboardManagerConstants::MinShortcutSize)
|
||||
{
|
||||
// If it is a hybrid control and there are 2 drop downs then deletion is allowed
|
||||
if (isHybridControl && dropDownCount == KeyboardManagerConstants::MinShortcutSize)
|
||||
{
|
||||
// set delete drop down flag
|
||||
dropDownAction = BufferValidationHelpers::DropDownAction::DeleteDropDown;
|
||||
// do not delete the drop down now since there may be some other error which would cause the drop down to be invalid after removal
|
||||
}
|
||||
else
|
||||
{
|
||||
// warn and reset the drop down
|
||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutAtleast2Keys;
|
||||
}
|
||||
}
|
||||
// If the user tries to set an action key check if all drop down menus after this are empty if it is not the first key. If it is a hybrid control, this can be done even on the first key
|
||||
else if (dropDownIndex != 0 || isHybridControl)
|
||||
{
|
||||
bool isClear = true;
|
||||
for (int i = dropDownIndex + 1; i < (int)dropDownCount; i++)
|
||||
{
|
||||
if (selectedIndices[i] != -1)
|
||||
{
|
||||
isClear = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isClear)
|
||||
{
|
||||
dropDownAction = BufferValidationHelpers::DropDownAction::ClearUnusedDropDowns;
|
||||
}
|
||||
else
|
||||
{
|
||||
// warn and reset the drop down
|
||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutNotMoreThanOneActionKey;
|
||||
}
|
||||
}
|
||||
// If there an action key is chosen on the first drop down and there are more than one drop down menus
|
||||
else
|
||||
{
|
||||
// warn and reset the drop down
|
||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutStartWithModifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// After validating the shortcut, now for errors like remap to same shortcut, remap shortcut more than once, Win L and Ctrl Alt Del
|
||||
if (errorType == KeyboardManagerHelper::ErrorType::NoError)
|
||||
{
|
||||
std::variant<DWORD, Shortcut> tempShortcut;
|
||||
if (isHybridControl && selectedKeyCodes.size() == 1)
|
||||
{
|
||||
tempShortcut = selectedKeyCodes[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
tempShortcut = Shortcut();
|
||||
std::get<Shortcut>(tempShortcut).SetKeyCodes(selectedKeyCodes);
|
||||
}
|
||||
|
||||
// Convert app name to lower case
|
||||
std::transform(appName.begin(), appName.end(), appName.begin(), towlower);
|
||||
std::wstring lowercaseDefAppName = KeyboardManagerConstants::DefaultAppName;
|
||||
std::transform(lowercaseDefAppName.begin(), lowercaseDefAppName.end(), lowercaseDefAppName.begin(), towlower);
|
||||
if (appName == lowercaseDefAppName)
|
||||
{
|
||||
appName = L"";
|
||||
}
|
||||
|
||||
// Check if the value being set is the same as the other column - index of other column does not have to be checked since only one column is hybrid
|
||||
if (tempShortcut.index() == 1)
|
||||
{
|
||||
// If shortcut to shortcut
|
||||
if (remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)].index() == 1)
|
||||
{
|
||||
if (std::get<Shortcut>(remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]) == std::get<Shortcut>(tempShortcut) && std::get<Shortcut>(remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]).IsValidShortcut() && std::get<Shortcut>(tempShortcut).IsValidShortcut())
|
||||
{
|
||||
errorType = KeyboardManagerHelper::ErrorType::MapToSameShortcut;
|
||||
}
|
||||
}
|
||||
|
||||
// If one column is shortcut and other is key no warning required
|
||||
}
|
||||
else
|
||||
{
|
||||
// If key to key
|
||||
if (remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)].index() == 0)
|
||||
{
|
||||
if (std::get<DWORD>(remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]) == std::get<DWORD>(tempShortcut) && std::get<DWORD>(remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]) != NULL && std::get<DWORD>(tempShortcut) != NULL)
|
||||
{
|
||||
errorType = KeyboardManagerHelper::ErrorType::MapToSameKey;
|
||||
}
|
||||
}
|
||||
|
||||
// If one column is shortcut and other is key no warning required
|
||||
}
|
||||
|
||||
if (errorType == KeyboardManagerHelper::ErrorType::NoError && colIndex == 0)
|
||||
{
|
||||
// Check if the key is already remapped to something else for the same target app
|
||||
for (int i = 0; i < remapBuffer.size(); i++)
|
||||
{
|
||||
std::wstring currAppName = remapBuffer[i].second;
|
||||
std::transform(currAppName.begin(), currAppName.end(), currAppName.begin(), towlower);
|
||||
|
||||
if (i != rowIndex && currAppName == appName)
|
||||
{
|
||||
KeyboardManagerHelper::ErrorType result = KeyboardManagerHelper::ErrorType::NoError;
|
||||
if (!isHybridControl)
|
||||
{
|
||||
result = Shortcut::DoKeysOverlap(std::get<Shortcut>(remapBuffer[i].first[colIndex]), std::get<Shortcut>(tempShortcut));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tempShortcut.index() == 0 && remapBuffer[i].first[colIndex].index() == 0)
|
||||
{
|
||||
if (std::get<DWORD>(tempShortcut) != NULL && std::get<DWORD>(remapBuffer[i].first[colIndex]) != NULL)
|
||||
{
|
||||
result = KeyboardManagerHelper::DoKeysOverlap(std::get<DWORD>(remapBuffer[i].first[colIndex]), std::get<DWORD>(tempShortcut));
|
||||
}
|
||||
}
|
||||
else if (tempShortcut.index() == 1 && remapBuffer[i].first[colIndex].index() == 1)
|
||||
{
|
||||
if (std::get<Shortcut>(tempShortcut).IsValidShortcut() && std::get<Shortcut>(remapBuffer[i].first[colIndex]).IsValidShortcut())
|
||||
{
|
||||
result = Shortcut::DoKeysOverlap(std::get<Shortcut>(remapBuffer[i].first[colIndex]), std::get<Shortcut>(tempShortcut));
|
||||
}
|
||||
}
|
||||
// Other scenarios not possible since key to shortcut is with key to key, and shortcut to key is with shortcut to shortcut
|
||||
}
|
||||
if (result != KeyboardManagerHelper::ErrorType::NoError)
|
||||
{
|
||||
errorType = result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (errorType == KeyboardManagerHelper::ErrorType::NoError && tempShortcut.index() == 1)
|
||||
{
|
||||
errorType = std::get<Shortcut>(tempShortcut).IsShortcutIllegal();
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_pair(errorType, dropDownAction);
|
||||
}
|
||||
}
|
||||
22
src/modules/keyboardmanager/ui/BufferValidationHelpers.h
Normal file
22
src/modules/keyboardmanager/ui/BufferValidationHelpers.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include "keyboardmanager/common/Helpers.h"
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
#include "keyboardmanager/common/Shortcut.h"
|
||||
|
||||
namespace BufferValidationHelpers
|
||||
{
|
||||
enum class DropDownAction
|
||||
{
|
||||
NoAction,
|
||||
AddDropDown,
|
||||
DeleteDropDown,
|
||||
ClearUnusedDropDowns
|
||||
};
|
||||
|
||||
// Function to validate and update an element of the key remap buffer when the selection has changed
|
||||
KeyboardManagerHelper::ErrorType ValidateAndUpdateKeyBufferElement(int rowIndex, int colIndex, int selectedKeyIndex, const std::vector<DWORD>& keyCodeList, RemapBuffer& remapBuffer);
|
||||
|
||||
// Function to validate an element of the shortcut remap buffer when the selection has changed
|
||||
std::pair<KeyboardManagerHelper::ErrorType, DropDownAction> ValidateShortcutBufferElement(int rowIndex, int colIndex, uint32_t dropDownIndex, const std::vector<int32_t>& selectedIndices, std::wstring appName, bool isHybridControl, const std::vector<DWORD>& keyCodeList, const RemapBuffer& remapBuffer, bool dropDownFound);
|
||||
}
|
||||
@@ -1,44 +1,8 @@
|
||||
#include "pch.h"
|
||||
#include "Dialog.h"
|
||||
#include <set>
|
||||
|
||||
using namespace winrt::Windows::Foundation;
|
||||
|
||||
KeyboardManagerHelper::ErrorType Dialog::CheckIfRemappingsAreValid(const std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& remappings)
|
||||
{
|
||||
KeyboardManagerHelper::ErrorType isSuccess = KeyboardManagerHelper::ErrorType::NoError;
|
||||
std::map<std::wstring, std::set<std::variant<DWORD, Shortcut>>> ogKeys;
|
||||
for (int i = 0; i < remappings.size(); i++)
|
||||
{
|
||||
std::variant<DWORD, Shortcut> ogKey = remappings[i].first[0];
|
||||
std::variant<DWORD, Shortcut> newKey = remappings[i].first[1];
|
||||
std::wstring appName = remappings[i].second;
|
||||
|
||||
bool ogKeyValidity = (ogKey.index() == 0 && std::get<DWORD>(ogKey) != NULL) || (ogKey.index() == 1 && std::get<Shortcut>(ogKey).IsValidShortcut());
|
||||
bool newKeyValidity = (newKey.index() == 0 && std::get<DWORD>(newKey) != NULL) || (newKey.index() == 1 && std::get<Shortcut>(newKey).IsValidShortcut());
|
||||
|
||||
// Add new set for a new target app name
|
||||
if (ogKeys.find(appName) == ogKeys.end())
|
||||
{
|
||||
ogKeys[appName] = std::set<std::variant<DWORD, Shortcut>>();
|
||||
}
|
||||
|
||||
if (ogKeyValidity && newKeyValidity && ogKeys[appName].find(ogKey) == ogKeys[appName].end())
|
||||
{
|
||||
ogKeys[appName].insert(ogKey);
|
||||
}
|
||||
else if (ogKeyValidity && newKeyValidity && ogKeys[appName].find(ogKey) != ogKeys[appName].end())
|
||||
{
|
||||
isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful;
|
||||
}
|
||||
else
|
||||
{
|
||||
isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful;
|
||||
}
|
||||
}
|
||||
return isSuccess;
|
||||
}
|
||||
|
||||
IAsyncOperation<bool> Dialog::PartialRemappingConfirmationDialog(XamlRoot root, std::wstring dialogTitle)
|
||||
{
|
||||
ContentDialog confirmationDialog;
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <keyboardmanager/common/Helpers.h>
|
||||
#include <variant>
|
||||
|
||||
namespace winrt::Windows::UI::Xaml
|
||||
{
|
||||
@@ -18,7 +15,5 @@ namespace winrt::Windows::UI::Xaml
|
||||
|
||||
namespace Dialog
|
||||
{
|
||||
KeyboardManagerHelper::ErrorType CheckIfRemappingsAreValid(const std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& remappings);
|
||||
|
||||
winrt::Windows::Foundation::IAsyncOperation<bool> PartialRemappingConfirmationDialog(winrt::Windows::UI::Xaml::XamlRoot root, std::wstring dialogTitle);
|
||||
};
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <keyboardmanager/dll/resource.h>
|
||||
#include "../common/shared_constants.h"
|
||||
#include "keyboardmanager/common/KeyboardManagerState.h"
|
||||
#include "LoadingAndSavingRemappingHelper.h"
|
||||
|
||||
using namespace winrt::Windows::Foundation;
|
||||
|
||||
@@ -28,36 +29,6 @@ std::mutex editKeyboardWindowMutex;
|
||||
// Stores a pointer to the Xaml Bridge object so that it can be accessed from the window procedure
|
||||
static XamlBridge* xamlBridgePtr = nullptr;
|
||||
|
||||
static std::vector<DWORD> GetOrphanedKeys()
|
||||
{
|
||||
std::set<DWORD> ogKeys;
|
||||
std::set<DWORD> newKeys;
|
||||
|
||||
for (int i = 0; i < SingleKeyRemapControl::singleKeyRemapBuffer.size(); i++)
|
||||
{
|
||||
DWORD ogKey = std::get<DWORD>(SingleKeyRemapControl::singleKeyRemapBuffer[i].first[0]);
|
||||
std::variant<DWORD, Shortcut> newKey = SingleKeyRemapControl::singleKeyRemapBuffer[i].first[1];
|
||||
|
||||
if (ogKey != NULL && ((newKey.index() == 0 && std::get<DWORD>(newKey) != 0) || (newKey.index() == 1 && std::get<Shortcut>(newKey).IsValidShortcut())))
|
||||
{
|
||||
ogKeys.insert(ogKey);
|
||||
|
||||
// newKey should be added only if the target is a key
|
||||
if (SingleKeyRemapControl::singleKeyRemapBuffer[i].first[1].index() == 0)
|
||||
{
|
||||
newKeys.insert(std::get<DWORD>(newKey));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& k : newKeys)
|
||||
{
|
||||
ogKeys.erase(k);
|
||||
}
|
||||
|
||||
return std::vector(ogKeys.begin(), ogKeys.end());
|
||||
}
|
||||
|
||||
static IAsyncOperation<bool> OrphanKeysConfirmationDialog(
|
||||
KeyboardManagerState& state,
|
||||
const std::vector<DWORD>& keys,
|
||||
@@ -92,7 +63,7 @@ static IAsyncOperation<bool> OrphanKeysConfirmationDialog(
|
||||
|
||||
static IAsyncAction OnClickAccept(KeyboardManagerState& keyboardManagerState, XamlRoot root, std::function<void()> ApplyRemappings)
|
||||
{
|
||||
KeyboardManagerHelper::ErrorType isSuccess = Dialog::CheckIfRemappingsAreValid(SingleKeyRemapControl::singleKeyRemapBuffer);
|
||||
KeyboardManagerHelper::ErrorType isSuccess = LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(SingleKeyRemapControl::singleKeyRemapBuffer);
|
||||
|
||||
if (isSuccess != KeyboardManagerHelper::ErrorType::NoError)
|
||||
{
|
||||
@@ -104,7 +75,7 @@ static IAsyncAction OnClickAccept(KeyboardManagerState& keyboardManagerState, Xa
|
||||
|
||||
// Check for orphaned keys
|
||||
// Draw content Dialog
|
||||
std::vector<DWORD> orphanedKeys = GetOrphanedKeys();
|
||||
std::vector<DWORD> orphanedKeys = LoadingAndSavingRemappingHelper::GetOrphanedKeys(SingleKeyRemapControl::singleKeyRemapBuffer);
|
||||
if (orphanedKeys.size() > 0)
|
||||
{
|
||||
if (!co_await OrphanKeysConfirmationDialog(keyboardManagerState, orphanedKeys, root))
|
||||
@@ -115,31 +86,6 @@ static IAsyncAction OnClickAccept(KeyboardManagerState& keyboardManagerState, Xa
|
||||
ApplyRemappings();
|
||||
}
|
||||
|
||||
// Function to combine remappings if the L and R version of the modifier is mapped to the same key
|
||||
void CombineRemappings(std::unordered_map<DWORD, std::variant<DWORD, Shortcut>>& table, DWORD leftKey, DWORD rightKey, DWORD combinedKey)
|
||||
{
|
||||
if (table.find(leftKey) != table.end() && table.find(rightKey) != table.end())
|
||||
{
|
||||
// If they are mapped to the same key, delete those entries and set the common version
|
||||
if (table[leftKey] == table[rightKey])
|
||||
{
|
||||
table[combinedKey] = table[leftKey];
|
||||
table.erase(leftKey);
|
||||
table.erase(rightKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function to pre process the remap table before loading it into the UI
|
||||
void PreProcessRemapTable(std::unordered_map<DWORD, std::variant<DWORD, Shortcut>>& table)
|
||||
{
|
||||
// Pre process the table to combine L and R versions of Ctrl/Alt/Shift/Win that are mapped to the same key
|
||||
CombineRemappings(table, VK_LCONTROL, VK_RCONTROL, VK_CONTROL);
|
||||
CombineRemappings(table, VK_LMENU, VK_RMENU, VK_MENU);
|
||||
CombineRemappings(table, VK_LSHIFT, VK_RSHIFT, VK_SHIFT);
|
||||
CombineRemappings(table, VK_LWIN, VK_RWIN, CommonSharedConstants::VK_WIN_BOTH);
|
||||
}
|
||||
|
||||
// Function to create the Edit Keyboard Window
|
||||
void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState)
|
||||
{
|
||||
@@ -306,7 +252,7 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
||||
std::unique_lock<std::mutex> lock(keyboardManagerState.singleKeyReMap_mutex);
|
||||
std::unordered_map<DWORD, std::variant<DWORD, Shortcut>> singleKeyRemapCopy = keyboardManagerState.singleKeyReMap;
|
||||
lock.unlock();
|
||||
PreProcessRemapTable(singleKeyRemapCopy);
|
||||
LoadingAndSavingRemappingHelper::PreProcessRemapTable(singleKeyRemapCopy);
|
||||
|
||||
for (const auto& it : singleKeyRemapCopy)
|
||||
{
|
||||
@@ -323,78 +269,9 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
||||
header.SetLeftOf(applyButton, cancelButton);
|
||||
|
||||
auto ApplyRemappings = [&keyboardManagerState, _hWndEditKeyboardWindow]() {
|
||||
KeyboardManagerHelper::ErrorType isSuccess = KeyboardManagerHelper::ErrorType::NoError;
|
||||
// Clear existing Key Remaps
|
||||
keyboardManagerState.ClearSingleKeyRemaps();
|
||||
DWORD successfulKeyToKeyRemapCount = 0;
|
||||
DWORD successfulKeyToShortcutRemapCount = 0;
|
||||
for (int i = 0; i < SingleKeyRemapControl::singleKeyRemapBuffer.size(); i++)
|
||||
{
|
||||
DWORD originalKey = std::get<DWORD>(SingleKeyRemapControl::singleKeyRemapBuffer[i].first[0]);
|
||||
std::variant<DWORD, Shortcut> newKey = SingleKeyRemapControl::singleKeyRemapBuffer[i].first[1];
|
||||
|
||||
if (originalKey != NULL && !(newKey.index() == 0 && std::get<DWORD>(newKey) == NULL) && !(newKey.index() == 1 && !std::get<Shortcut>(newKey).IsValidShortcut()))
|
||||
{
|
||||
// If Ctrl/Alt/Shift are added, add their L and R versions instead to the same key
|
||||
bool result = false;
|
||||
bool res1, res2;
|
||||
switch (originalKey)
|
||||
{
|
||||
case VK_CONTROL:
|
||||
res1 = keyboardManagerState.AddSingleKeyRemap(VK_LCONTROL, newKey);
|
||||
res2 = keyboardManagerState.AddSingleKeyRemap(VK_RCONTROL, newKey);
|
||||
result = res1 && res2;
|
||||
break;
|
||||
case VK_MENU:
|
||||
res1 = keyboardManagerState.AddSingleKeyRemap(VK_LMENU, newKey);
|
||||
res2 = keyboardManagerState.AddSingleKeyRemap(VK_RMENU, newKey);
|
||||
result = res1 && res2;
|
||||
break;
|
||||
case VK_SHIFT:
|
||||
res1 = keyboardManagerState.AddSingleKeyRemap(VK_LSHIFT, newKey);
|
||||
res2 = keyboardManagerState.AddSingleKeyRemap(VK_RSHIFT, newKey);
|
||||
result = res1 && res2;
|
||||
break;
|
||||
case CommonSharedConstants::VK_WIN_BOTH:
|
||||
res1 = keyboardManagerState.AddSingleKeyRemap(VK_LWIN, newKey);
|
||||
res2 = keyboardManagerState.AddSingleKeyRemap(VK_RWIN, newKey);
|
||||
result = res1 && res2;
|
||||
break;
|
||||
default:
|
||||
result = keyboardManagerState.AddSingleKeyRemap(originalKey, newKey);
|
||||
}
|
||||
|
||||
if (!result)
|
||||
{
|
||||
isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful;
|
||||
// Tooltip is already shown for this row
|
||||
}
|
||||
else
|
||||
{
|
||||
if (newKey.index() == 0)
|
||||
{
|
||||
successfulKeyToKeyRemapCount += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
successfulKeyToShortcutRemapCount += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful;
|
||||
}
|
||||
}
|
||||
|
||||
Trace::KeyRemapCount(successfulKeyToKeyRemapCount, successfulKeyToShortcutRemapCount);
|
||||
LoadingAndSavingRemappingHelper::ApplySingleKeyRemappings(keyboardManagerState, SingleKeyRemapControl::singleKeyRemapBuffer, true);
|
||||
// Save the updated shortcuts remaps to file.
|
||||
bool saveResult = keyboardManagerState.SaveConfigToFile();
|
||||
if (!saveResult)
|
||||
{
|
||||
isSuccess = KeyboardManagerHelper::ErrorType::SaveFailed;
|
||||
}
|
||||
|
||||
PostMessage(_hWndEditKeyboardWindow, WM_CLOSE, 0, 0);
|
||||
};
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "Dialog.h"
|
||||
#include <keyboardmanager/dll/resource.h>
|
||||
#include <keyboardmanager/common/KeyboardManagerState.h>
|
||||
#include "LoadingAndSavingRemappingHelper.h"
|
||||
|
||||
using namespace winrt::Windows::Foundation;
|
||||
|
||||
@@ -31,7 +32,7 @@ static IAsyncAction OnClickAccept(
|
||||
XamlRoot root,
|
||||
std::function<void()> ApplyRemappings)
|
||||
{
|
||||
KeyboardManagerHelper::ErrorType isSuccess = Dialog::CheckIfRemappingsAreValid(ShortcutControl::shortcutRemapBuffer);
|
||||
KeyboardManagerHelper::ErrorType isSuccess = LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(ShortcutControl::shortcutRemapBuffer);
|
||||
|
||||
if (isSuccess != KeyboardManagerHelper::ErrorType::NoError)
|
||||
{
|
||||
@@ -252,78 +253,9 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
|
||||
header.SetLeftOf(applyButton, cancelButton);
|
||||
|
||||
auto ApplyRemappings = [&keyboardManagerState, _hWndEditShortcutsWindow]() {
|
||||
KeyboardManagerHelper::ErrorType isSuccess = KeyboardManagerHelper::ErrorType::NoError;
|
||||
// Clear existing shortcuts
|
||||
keyboardManagerState.ClearOSLevelShortcuts();
|
||||
keyboardManagerState.ClearAppSpecificShortcuts();
|
||||
DWORD successfulOSLevelShortcutToShortcutRemapCount = 0;
|
||||
DWORD successfulOSLevelShortcutToKeyRemapCount = 0;
|
||||
DWORD successfulAppSpecificShortcutToShortcutRemapCount = 0;
|
||||
DWORD successfulAppSpecificShortcutToKeyRemapCount = 0;
|
||||
// Save the shortcuts that are valid and report if any of them were invalid
|
||||
for (int i = 0; i < ShortcutControl::shortcutRemapBuffer.size(); i++)
|
||||
{
|
||||
Shortcut originalShortcut = std::get<Shortcut>(ShortcutControl::shortcutRemapBuffer[i].first[0]);
|
||||
std::variant<DWORD, Shortcut> newShortcut = ShortcutControl::shortcutRemapBuffer[i].first[1];
|
||||
|
||||
if (originalShortcut.IsValidShortcut() && ((newShortcut.index() == 0 && std::get<DWORD>(newShortcut) != NULL) || (newShortcut.index() == 1 && std::get<Shortcut>(newShortcut).IsValidShortcut())))
|
||||
{
|
||||
if (ShortcutControl::shortcutRemapBuffer[i].second == L"")
|
||||
{
|
||||
bool result = keyboardManagerState.AddOSLevelShortcut(originalShortcut, newShortcut);
|
||||
if (!result)
|
||||
{
|
||||
isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (newShortcut.index() == 0)
|
||||
{
|
||||
successfulOSLevelShortcutToKeyRemapCount += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
successfulOSLevelShortcutToShortcutRemapCount += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool result = keyboardManagerState.AddAppSpecificShortcut(ShortcutControl::shortcutRemapBuffer[i].second, originalShortcut, newShortcut);
|
||||
if (!result)
|
||||
{
|
||||
isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (newShortcut.index() == 0)
|
||||
{
|
||||
successfulAppSpecificShortcutToKeyRemapCount += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
successfulAppSpecificShortcutToShortcutRemapCount += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful;
|
||||
}
|
||||
}
|
||||
|
||||
// Telemetry events
|
||||
Trace::OSLevelShortcutRemapCount(successfulOSLevelShortcutToShortcutRemapCount, successfulOSLevelShortcutToKeyRemapCount);
|
||||
Trace::AppSpecificShortcutRemapCount(successfulAppSpecificShortcutToShortcutRemapCount, successfulAppSpecificShortcutToKeyRemapCount);
|
||||
|
||||
LoadingAndSavingRemappingHelper::ApplyShortcutRemappings(keyboardManagerState, ShortcutControl::shortcutRemapBuffer, true);
|
||||
// Save the updated key remaps to file.
|
||||
bool saveResult = keyboardManagerState.SaveConfigToFile();
|
||||
if (!saveResult)
|
||||
{
|
||||
isSuccess = KeyboardManagerHelper::ErrorType::SaveFailed;
|
||||
}
|
||||
|
||||
PostMessage(_hWndEditShortcutsWindow, WM_CLOSE, 0, 0);
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "KeyDropDownControl.h"
|
||||
#include "keyboardmanager/common/Helpers.h"
|
||||
#include <keyboardmanager/common/KeyboardManagerState.h>
|
||||
#include "BufferValidationHelpers.h"
|
||||
|
||||
// Initialized to null
|
||||
KeyboardManagerState* KeyDropDownControl::keyboardManagerState = nullptr;
|
||||
@@ -53,7 +54,7 @@ void KeyDropDownControl::CheckAndUpdateKeyboardLayout(ComboBox currentDropDown,
|
||||
}
|
||||
|
||||
// Function to set selection handler for single key remap drop down. Needs to be called after the constructor since the singleKeyControl StackPanel is null if called in the constructor
|
||||
void KeyDropDownControl::SetSelectionHandler(Grid& table, StackPanel singleKeyControl, int colIndex, std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& singleKeyRemapBuffer)
|
||||
void KeyDropDownControl::SetSelectionHandler(Grid& table, StackPanel singleKeyControl, int colIndex, RemapBuffer& singleKeyRemapBuffer)
|
||||
{
|
||||
// drop down selection handler
|
||||
auto onSelectionChange = [&, table, singleKeyControl, colIndex](winrt::Windows::Foundation::IInspectable const& sender) {
|
||||
@@ -64,62 +65,12 @@ void KeyDropDownControl::SetSelectionHandler(Grid& table, StackPanel singleKeyCo
|
||||
bool indexFound = table.Children().IndexOf(singleKeyControl, controlIndex);
|
||||
if (indexFound)
|
||||
{
|
||||
KeyboardManagerHelper::ErrorType errorType = KeyboardManagerHelper::ErrorType::NoError;
|
||||
int rowIndex = (controlIndex - KeyboardManagerConstants::RemapTableHeaderCount) / KeyboardManagerConstants::RemapTableColCount;
|
||||
// Check if the element was not found or the index exceeds the known keys
|
||||
if (selectedKeyIndex != -1 && keyCodeList.size() > selectedKeyIndex)
|
||||
{
|
||||
// Check if the value being set is the same as the other column
|
||||
if (singleKeyRemapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)].index() == 0)
|
||||
{
|
||||
if (std::get<DWORD>(singleKeyRemapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]) == keyCodeList[selectedKeyIndex])
|
||||
{
|
||||
errorType = KeyboardManagerHelper::ErrorType::MapToSameKey;
|
||||
}
|
||||
}
|
||||
|
||||
// If one column is shortcut and other is key no warning required
|
||||
|
||||
if (errorType == KeyboardManagerHelper::ErrorType::NoError && colIndex == 0)
|
||||
{
|
||||
// Check if the key is already remapped to something else
|
||||
for (int i = 0; i < singleKeyRemapBuffer.size(); i++)
|
||||
{
|
||||
if (i != rowIndex)
|
||||
{
|
||||
if (singleKeyRemapBuffer[i].first[colIndex].index() == 0)
|
||||
{
|
||||
KeyboardManagerHelper::ErrorType result = KeyboardManagerHelper::DoKeysOverlap(std::get<DWORD>(singleKeyRemapBuffer[i].first[colIndex]), keyCodeList[selectedKeyIndex]);
|
||||
if (result != KeyboardManagerHelper::ErrorType::NoError)
|
||||
{
|
||||
errorType = result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// check key to shortcut error
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no error, set the buffer
|
||||
if (errorType == KeyboardManagerHelper::ErrorType::NoError)
|
||||
{
|
||||
singleKeyRemapBuffer[rowIndex].first[colIndex] = keyCodeList[selectedKeyIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
singleKeyRemapBuffer[rowIndex].first[colIndex] = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reset to null if the key is not found
|
||||
singleKeyRemapBuffer[rowIndex].first[colIndex] = NULL;
|
||||
}
|
||||
// Validate current remap selection
|
||||
KeyboardManagerHelper::ErrorType errorType = BufferValidationHelpers::ValidateAndUpdateKeyBufferElement(rowIndex, colIndex, selectedKeyIndex, keyCodeList, singleKeyRemapBuffer);
|
||||
|
||||
// If there is an error set the warning flyout
|
||||
if (errorType != KeyboardManagerHelper::ErrorType::NoError)
|
||||
{
|
||||
SetDropDownError(currentDropDown, KeyboardManagerHelper::GetErrorMessage(errorType));
|
||||
@@ -142,7 +93,7 @@ void KeyDropDownControl::SetSelectionHandler(Grid& table, StackPanel singleKeyCo
|
||||
});
|
||||
}
|
||||
|
||||
std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateShortcutSelection(Grid table, StackPanel shortcutControl, StackPanel parent, int colIndex, std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow)
|
||||
std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateShortcutSelection(Grid table, StackPanel shortcutControl, StackPanel parent, int colIndex, RemapBuffer& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow)
|
||||
{
|
||||
ComboBox currentDropDown = dropDown.as<ComboBox>();
|
||||
int selectedKeyIndex = currentDropDown.SelectedIndex();
|
||||
@@ -151,9 +102,8 @@ std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateSho
|
||||
// Get row index of the single key control
|
||||
uint32_t controlIndex;
|
||||
bool controlIindexFound = table.Children().IndexOf(shortcutControl, controlIndex);
|
||||
KeyboardManagerHelper::ErrorType errorType = KeyboardManagerHelper::ErrorType::NoError;
|
||||
bool IsDeleteDropDownRequired = false;
|
||||
int rowIndex = -1;
|
||||
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> validationResult = std::make_pair(KeyboardManagerHelper::ErrorType::NoError, BufferValidationHelpers::DropDownAction::NoAction);
|
||||
|
||||
if (controlIindexFound)
|
||||
{
|
||||
@@ -165,242 +115,42 @@ std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateSho
|
||||
{
|
||||
rowIndex = (controlIndex - KeyboardManagerConstants::ShortcutTableHeaderCount) / KeyboardManagerConstants::ShortcutTableColCount;
|
||||
}
|
||||
if (selectedKeyIndex != -1 && keyCodeList.size() > selectedKeyIndex && dropDownFound)
|
||||
{
|
||||
// If only 1 drop down and action key is chosen: Warn that a modifier must be chosen (if the drop down is not for a hybrid scenario)
|
||||
if (parent.Children().Size() == 1 && !KeyboardManagerHelper::IsModifierKey(keyCodeList[selectedKeyIndex]) && !isHybridControl)
|
||||
{
|
||||
// warn and reset the drop down
|
||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutStartWithModifier;
|
||||
}
|
||||
// If it is the last drop down
|
||||
else if (dropDownIndex == parent.Children().Size() - 1)
|
||||
{
|
||||
// If last drop down and a modifier is selected: add a new drop down (max drop down count should be enforced)
|
||||
if (KeyboardManagerHelper::IsModifierKey(keyCodeList[selectedKeyIndex]) && parent.Children().Size() < KeyboardManagerConstants::MaxShortcutSize)
|
||||
{
|
||||
// If it matched any of the previous modifiers then reset that drop down
|
||||
if (CheckRepeatedModifier(parent, selectedKeyIndex, keyCodeList))
|
||||
{
|
||||
// warn and reset the drop down
|
||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutCannotHaveRepeatedModifier;
|
||||
}
|
||||
// If not, add a new drop down
|
||||
else
|
||||
{
|
||||
AddDropDown(table, shortcutControl, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, isSingleKeyWindow);
|
||||
}
|
||||
}
|
||||
// If last drop down and a modifier is selected but there are already max drop downs: warn the user
|
||||
else if (KeyboardManagerHelper::IsModifierKey(keyCodeList[selectedKeyIndex]) && parent.Children().Size() >= KeyboardManagerConstants::MaxShortcutSize)
|
||||
{
|
||||
// warn and reset the drop down
|
||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutMaxShortcutSizeOneActionKey;
|
||||
}
|
||||
// If None is selected but it's the last index: warn
|
||||
else if (keyCodeList[selectedKeyIndex] == 0)
|
||||
{
|
||||
// If it is a hybrid control and there are 2 drop downs then deletion is allowed
|
||||
if (isHybridControl && parent.Children().Size() == KeyboardManagerConstants::MinShortcutSize)
|
||||
{
|
||||
// set delete drop down flag
|
||||
IsDeleteDropDownRequired = true;
|
||||
// do not delete the drop down now since there may be some other error which would cause the drop down to be invalid after removal
|
||||
}
|
||||
else
|
||||
{
|
||||
// warn and reset the drop down
|
||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutOneActionKey;
|
||||
}
|
||||
}
|
||||
// If none of the above, then the action key will be set
|
||||
}
|
||||
// If it is the not the last drop down
|
||||
else
|
||||
{
|
||||
if (KeyboardManagerHelper::IsModifierKey(keyCodeList[selectedKeyIndex]))
|
||||
{
|
||||
// If it matched any of the previous modifiers then reset that drop down
|
||||
if (CheckRepeatedModifier(parent, selectedKeyIndex, keyCodeList))
|
||||
{
|
||||
// warn and reset the drop down
|
||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutCannotHaveRepeatedModifier;
|
||||
}
|
||||
// If not, the modifier key will be set
|
||||
}
|
||||
// If None is selected and there are more than 2 drop downs
|
||||
else if (keyCodeList[selectedKeyIndex] == 0 && parent.Children().Size() > KeyboardManagerConstants::MinShortcutSize)
|
||||
{
|
||||
// set delete drop down flag
|
||||
IsDeleteDropDownRequired = true;
|
||||
// do not delete the drop down now since there may be some other error which would cause the drop down to be invalid after removal
|
||||
}
|
||||
else if (keyCodeList[selectedKeyIndex] == 0 && parent.Children().Size() <= KeyboardManagerConstants::MinShortcutSize)
|
||||
{
|
||||
// If it is a hybrid control and there are 2 drop downs then deletion is allowed
|
||||
if (isHybridControl && parent.Children().Size() == KeyboardManagerConstants::MinShortcutSize)
|
||||
{
|
||||
// set delete drop down flag
|
||||
IsDeleteDropDownRequired = true;
|
||||
// do not delete the drop down now since there may be some other error which would cause the drop down to be invalid after removal
|
||||
}
|
||||
else
|
||||
{
|
||||
// warn and reset the drop down
|
||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutAtleast2Keys;
|
||||
}
|
||||
}
|
||||
// If the user tries to set an action key check if all drop down menus after this are empty if it is not the first key. If it is a hybrid control, this can be done even on the first key
|
||||
else if (dropDownIndex != 0 || isHybridControl)
|
||||
{
|
||||
bool isClear = true;
|
||||
for (int i = dropDownIndex + 1; i < (int)parent.Children().Size(); i++)
|
||||
{
|
||||
ComboBox ItDropDown = parent.Children().GetAt(i).as<ComboBox>();
|
||||
if (ItDropDown.SelectedIndex() != -1)
|
||||
{
|
||||
isClear = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isClear)
|
||||
{
|
||||
// remove all the drop down
|
||||
int elementsToBeRemoved = parent.Children().Size() - dropDownIndex - 1;
|
||||
for (int i = 0; i < elementsToBeRemoved; i++)
|
||||
{
|
||||
parent.Children().RemoveAtEnd();
|
||||
keyDropDownControlObjects.erase(keyDropDownControlObjects.end() - 1);
|
||||
}
|
||||
parent.UpdateLayout();
|
||||
}
|
||||
else
|
||||
{
|
||||
// warn and reset the drop down
|
||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutNotMoreThanOneActionKey;
|
||||
}
|
||||
}
|
||||
// If there an action key is chosen on the first drop down and there are more than one drop down menus
|
||||
else
|
||||
{
|
||||
// warn and reset the drop down
|
||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutStartWithModifier;
|
||||
}
|
||||
}
|
||||
std::vector<int32_t> selectedIndices = GetSelectedIndicesFromStackPanel(parent);
|
||||
|
||||
std::wstring appName;
|
||||
if (targetApp != nullptr)
|
||||
{
|
||||
appName = targetApp.Text().c_str();
|
||||
}
|
||||
|
||||
// After validating the shortcut, now for errors like remap to same shortcut, remap shortcut more than once, Win L and Ctrl Alt Del
|
||||
if (errorType == KeyboardManagerHelper::ErrorType::NoError)
|
||||
// Validate shortcut element
|
||||
validationResult = BufferValidationHelpers::ValidateShortcutBufferElement(rowIndex, colIndex, dropDownIndex, selectedIndices, appName, isHybridControl, keyCodeList, shortcutRemapBuffer, dropDownFound);
|
||||
|
||||
// Add or clear unused drop downs
|
||||
if (validationResult.second == BufferValidationHelpers::DropDownAction::AddDropDown)
|
||||
{
|
||||
std::variant<DWORD, Shortcut> tempShortcut;
|
||||
std::vector<DWORD> selectedKeyCodes = GetKeysFromStackPanel(parent);
|
||||
if (isHybridControl && selectedKeyCodes.size() == 1)
|
||||
AddDropDown(table, shortcutControl, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, isSingleKeyWindow);
|
||||
}
|
||||
else if (validationResult.second == BufferValidationHelpers::DropDownAction::ClearUnusedDropDowns)
|
||||
{
|
||||
// remove all the drop downs after the current index
|
||||
int elementsToBeRemoved = parent.Children().Size() - dropDownIndex - 1;
|
||||
for (int i = 0; i < elementsToBeRemoved; i++)
|
||||
{
|
||||
tempShortcut = selectedKeyCodes[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
tempShortcut = Shortcut();
|
||||
std::get<Shortcut>(tempShortcut).SetKeyCodes(GetKeysFromStackPanel(parent));
|
||||
}
|
||||
std::wstring appName;
|
||||
if (targetApp != nullptr)
|
||||
{
|
||||
appName = targetApp.Text().c_str();
|
||||
}
|
||||
// Convert app name to lower case
|
||||
std::transform(appName.begin(), appName.end(), appName.begin(), towlower);
|
||||
std::wstring lowercaseDefAppName = KeyboardManagerConstants::DefaultAppName;
|
||||
std::transform(lowercaseDefAppName.begin(), lowercaseDefAppName.end(), lowercaseDefAppName.begin(), towlower);
|
||||
if (appName == lowercaseDefAppName)
|
||||
{
|
||||
appName = L"";
|
||||
}
|
||||
|
||||
// Check if the value being set is the same as the other column - index of other column does not have to be checked since only one column is hybrid
|
||||
if (tempShortcut.index() == 1)
|
||||
{
|
||||
// If shortcut to shortcut
|
||||
if (shortcutRemapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)].index() == 1)
|
||||
{
|
||||
if (std::get<Shortcut>(shortcutRemapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]) == std::get<Shortcut>(tempShortcut) && std::get<Shortcut>(shortcutRemapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]).IsValidShortcut() && std::get<Shortcut>(tempShortcut).IsValidShortcut())
|
||||
{
|
||||
errorType = KeyboardManagerHelper::ErrorType::MapToSameShortcut;
|
||||
}
|
||||
}
|
||||
|
||||
// If one column is shortcut and other is key no warning required
|
||||
}
|
||||
else
|
||||
{
|
||||
// If key to key
|
||||
if (shortcutRemapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)].index() == 0)
|
||||
{
|
||||
if (std::get<DWORD>(shortcutRemapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]) == std::get<DWORD>(tempShortcut) && std::get<DWORD>(shortcutRemapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]) != NULL && std::get<DWORD>(tempShortcut) != NULL)
|
||||
{
|
||||
errorType = KeyboardManagerHelper::ErrorType::MapToSameKey;
|
||||
}
|
||||
}
|
||||
|
||||
// If one column is shortcut and other is key no warning required
|
||||
}
|
||||
|
||||
if (errorType == KeyboardManagerHelper::ErrorType::NoError && colIndex == 0)
|
||||
{
|
||||
// Check if the key is already remapped to something else for the same target app
|
||||
for (int i = 0; i < shortcutRemapBuffer.size(); i++)
|
||||
{
|
||||
std::wstring currAppName = shortcutRemapBuffer[i].second;
|
||||
std::transform(currAppName.begin(), currAppName.end(), currAppName.begin(), towlower);
|
||||
|
||||
if (i != rowIndex && currAppName == appName)
|
||||
{
|
||||
KeyboardManagerHelper::ErrorType result = KeyboardManagerHelper::ErrorType::NoError;
|
||||
if (!isHybridControl)
|
||||
{
|
||||
result = Shortcut::DoKeysOverlap(std::get<Shortcut>(shortcutRemapBuffer[i].first[colIndex]), std::get<Shortcut>(tempShortcut));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tempShortcut.index() == 0 && shortcutRemapBuffer[i].first[colIndex].index() == 0)
|
||||
{
|
||||
if (std::get<DWORD>(tempShortcut) != NULL && std::get<DWORD>(shortcutRemapBuffer[i].first[colIndex]) != NULL)
|
||||
{
|
||||
result = KeyboardManagerHelper::DoKeysOverlap(std::get<DWORD>(shortcutRemapBuffer[i].first[colIndex]), std::get<DWORD>(tempShortcut));
|
||||
}
|
||||
}
|
||||
else if (tempShortcut.index() == 1 && shortcutRemapBuffer[i].first[colIndex].index() == 1)
|
||||
{
|
||||
if (std::get<Shortcut>(tempShortcut).IsValidShortcut() && std::get<Shortcut>(shortcutRemapBuffer[i].first[colIndex]).IsValidShortcut())
|
||||
{
|
||||
result = Shortcut::DoKeysOverlap(std::get<Shortcut>(shortcutRemapBuffer[i].first[colIndex]), std::get<Shortcut>(tempShortcut));
|
||||
}
|
||||
}
|
||||
// Other scenarios not possible since key to shortcut is with key to key, and shortcut to key is with shortcut to shortcut
|
||||
}
|
||||
if (result != KeyboardManagerHelper::ErrorType::NoError)
|
||||
{
|
||||
errorType = result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (errorType == KeyboardManagerHelper::ErrorType::NoError && tempShortcut.index() == 1)
|
||||
{
|
||||
errorType = std::get<Shortcut>(tempShortcut).IsShortcutIllegal();
|
||||
parent.Children().RemoveAtEnd();
|
||||
keyDropDownControlObjects.erase(keyDropDownControlObjects.end() - 1);
|
||||
}
|
||||
parent.UpdateLayout();
|
||||
}
|
||||
|
||||
if (errorType != KeyboardManagerHelper::ErrorType::NoError)
|
||||
if (validationResult.first != KeyboardManagerHelper::ErrorType::NoError)
|
||||
{
|
||||
SetDropDownError(currentDropDown, KeyboardManagerHelper::GetErrorMessage(errorType));
|
||||
SetDropDownError(currentDropDown, KeyboardManagerHelper::GetErrorMessage(validationResult.first));
|
||||
}
|
||||
|
||||
// Handle None case if there are no other errors
|
||||
else if (IsDeleteDropDownRequired)
|
||||
else if (validationResult.second == BufferValidationHelpers::DropDownAction::DeleteDropDown)
|
||||
{
|
||||
parent.Children().RemoveAt(dropDownIndex);
|
||||
// delete drop down control object from the vector so that it can be destructed
|
||||
@@ -409,11 +159,11 @@ std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateSho
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_pair(errorType, rowIndex);
|
||||
return std::make_pair(validationResult.first, rowIndex);
|
||||
}
|
||||
|
||||
// Function to set selection handler for shortcut drop down. Needs to be called after the constructor since the shortcutControl StackPanel is null if called in the constructor
|
||||
void KeyDropDownControl::SetSelectionHandler(Grid& table, StackPanel shortcutControl, StackPanel parent, int colIndex, std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox& targetApp, bool isHybridControl, bool isSingleKeyWindow)
|
||||
void KeyDropDownControl::SetSelectionHandler(Grid& table, StackPanel shortcutControl, StackPanel parent, int colIndex, RemapBuffer& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox& targetApp, bool isHybridControl, bool isSingleKeyWindow)
|
||||
{
|
||||
auto onSelectionChange = [&, table, shortcutControl, colIndex, parent, targetApp, isHybridControl, isSingleKeyWindow](winrt::Windows::Foundation::IInspectable const& sender) {
|
||||
std::pair<KeyboardManagerHelper::ErrorType, int> validationResult = ValidateShortcutSelection(table, shortcutControl, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, isSingleKeyWindow);
|
||||
@@ -428,8 +178,8 @@ void KeyDropDownControl::SetSelectionHandler(Grid& table, StackPanel shortcutCon
|
||||
ValidateShortcutFromDropDownList(table, shortcutControl, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, isSingleKeyWindow);
|
||||
}
|
||||
|
||||
// Reset the buffer based on the new selected drop down items
|
||||
std::vector selectedKeyCodes = GetKeysFromStackPanel(parent);
|
||||
// Reset the buffer based on the new selected drop down items. Use static key code list since the KeyDropDownControl object might be deleted
|
||||
std::vector selectedKeyCodes = KeyboardManagerHelper::GetKeyCodesFromSelectedIndices(GetSelectedIndicesFromStackPanel(parent), KeyDropDownControl::keyboardManagerState->keyboardMap.GetKeyCodeList(true));
|
||||
if (!isHybridControl)
|
||||
{
|
||||
std::get<Shortcut>(shortcutRemapBuffer[validationResult.second].first[colIndex]).SetKeyCodes(selectedKeyCodes);
|
||||
@@ -507,7 +257,7 @@ ComboBox KeyDropDownControl::GetComboBox()
|
||||
}
|
||||
|
||||
// Function to add a drop down to the shortcut stack panel
|
||||
void KeyDropDownControl::AddDropDown(Grid table, StackPanel shortcutControl, StackPanel parent, const int colIndex, std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow)
|
||||
void KeyDropDownControl::AddDropDown(Grid table, StackPanel shortcutControl, StackPanel parent, const int colIndex, RemapBuffer& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow)
|
||||
{
|
||||
keyDropDownControlObjects.push_back(std::move(std::unique_ptr<KeyDropDownControl>(new KeyDropDownControl(true))));
|
||||
parent.Children().Append(keyDropDownControlObjects[keyDropDownControlObjects.size() - 1]->GetComboBox());
|
||||
@@ -516,70 +266,28 @@ void KeyDropDownControl::AddDropDown(Grid table, StackPanel shortcutControl, Sta
|
||||
}
|
||||
|
||||
// Function to get the list of key codes from the shortcut combo box stack panel
|
||||
std::vector<DWORD> KeyDropDownControl::GetKeysFromStackPanel(StackPanel parent)
|
||||
std::vector<int32_t> KeyDropDownControl::GetSelectedIndicesFromStackPanel(StackPanel parent)
|
||||
{
|
||||
std::vector<DWORD> keys;
|
||||
std::vector<DWORD> keyCodeList = keyboardManagerState->keyboardMap.GetKeyCodeList(true);
|
||||
std::vector<int32_t> selectedIndices;
|
||||
|
||||
// Get selected indices for each drop down
|
||||
for (int i = 0; i < (int)parent.Children().Size(); i++)
|
||||
{
|
||||
ComboBox currentDropDown = parent.Children().GetAt(i).as<ComboBox>();
|
||||
int selectedKeyIndex = currentDropDown.SelectedIndex();
|
||||
if (selectedKeyIndex != -1 && keyCodeList.size() > selectedKeyIndex)
|
||||
{
|
||||
// If None is not the selected key
|
||||
if (keyCodeList[selectedKeyIndex] != 0)
|
||||
{
|
||||
keys.push_back(keyCodeList[selectedKeyIndex]);
|
||||
}
|
||||
}
|
||||
ComboBox ItDropDown = parent.Children().GetAt(i).as<ComboBox>();
|
||||
selectedIndices.push_back(ItDropDown.SelectedIndex());
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
// Function to check if a modifier has been repeated in the previous drop downs
|
||||
bool KeyDropDownControl::CheckRepeatedModifier(StackPanel parent, int selectedKeyIndex, const std::vector<DWORD>& keyCodeList)
|
||||
{
|
||||
// check if modifier has already been added before in a previous drop down
|
||||
std::vector<DWORD> currentKeys = GetKeysFromStackPanel(parent);
|
||||
int currentDropDownIndex = -1;
|
||||
|
||||
// Find the key index of the current drop down selection so that we skip that index while searching for repeated modifiers
|
||||
for (int i = 0; i < currentKeys.size(); i++)
|
||||
{
|
||||
if (currentKeys[i] == keyCodeList[selectedKeyIndex])
|
||||
{
|
||||
currentDropDownIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool matchPreviousModifier = false;
|
||||
for (int i = 0; i < currentKeys.size(); i++)
|
||||
{
|
||||
// Skip the current drop down
|
||||
if (i != currentDropDownIndex)
|
||||
{
|
||||
// If the key type for the newly added key matches any of the existing keys in the shortcut
|
||||
if (KeyboardManagerHelper::GetKeyType(keyCodeList[selectedKeyIndex]) == KeyboardManagerHelper::GetKeyType(currentKeys[i]))
|
||||
{
|
||||
matchPreviousModifier = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matchPreviousModifier;
|
||||
return selectedIndices;
|
||||
}
|
||||
|
||||
// Function for validating the selection of shortcuts for all the associated drop downs
|
||||
void KeyDropDownControl::ValidateShortcutFromDropDownList(Grid table, StackPanel shortcutControl, StackPanel parent, int colIndex, std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow)
|
||||
void KeyDropDownControl::ValidateShortcutFromDropDownList(Grid table, StackPanel shortcutControl, StackPanel parent, int colIndex, RemapBuffer& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow)
|
||||
{
|
||||
// Iterate over all drop downs from left to right in that row/col and validate if there is an error in any of the drop downs. After this the state should be error-free (if it is a valid shortcut)
|
||||
for (int i = 0; i < keyDropDownControlObjects.size(); i++)
|
||||
{
|
||||
// Check for errors only if the current selection is a valid shortcut
|
||||
std::vector<DWORD> selectedKeyCodes = keyDropDownControlObjects[i]->GetKeysFromStackPanel(parent);
|
||||
std::vector<DWORD> selectedKeyCodes = KeyboardManagerHelper::GetKeyCodesFromSelectedIndices(keyDropDownControlObjects[i]->GetSelectedIndicesFromStackPanel(parent), KeyDropDownControl::keyboardManagerState->keyboardMap.GetKeyCodeList(true));
|
||||
std::variant<DWORD, Shortcut> currentShortcut;
|
||||
if (selectedKeyCodes.size() == 1 && isHybridControl)
|
||||
{
|
||||
@@ -609,7 +317,7 @@ void KeyDropDownControl::SetDropDownError(ComboBox currentDropDown, hstring mess
|
||||
}
|
||||
|
||||
// Function to add a shortcut to the UI control as combo boxes
|
||||
void KeyDropDownControl::AddShortcutToControl(Shortcut shortcut, Grid table, StackPanel parent, KeyboardManagerState& keyboardManagerState, const int colIndex, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& remapBuffer, StackPanel controlLayout, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow)
|
||||
void KeyDropDownControl::AddShortcutToControl(Shortcut shortcut, Grid table, StackPanel parent, KeyboardManagerState& keyboardManagerState, const int colIndex, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, RemapBuffer& remapBuffer, StackPanel controlLayout, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow)
|
||||
{
|
||||
// Delete the existing drop down menus
|
||||
parent.Children().Clear();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
#include <keyboardmanager/common/Shortcut.h>
|
||||
#include <variant>
|
||||
class KeyboardManagerState;
|
||||
|
||||
namespace winrt::Windows
|
||||
@@ -57,13 +56,13 @@ public:
|
||||
}
|
||||
|
||||
// Function to set selection handler for single key remap drop down. Needs to be called after the constructor since the singleKeyControl StackPanel is null if called in the constructor
|
||||
void SetSelectionHandler(winrt::Windows::UI::Xaml::Controls::Grid& table, winrt::Windows::UI::Xaml::Controls::StackPanel singleKeyControl, int colIndex, std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& singleKeyRemapBuffer);
|
||||
void SetSelectionHandler(winrt::Windows::UI::Xaml::Controls::Grid& table, winrt::Windows::UI::Xaml::Controls::StackPanel singleKeyControl, int colIndex, RemapBuffer& singleKeyRemapBuffer);
|
||||
|
||||
// Function for validating the selection of shortcuts for the drop down
|
||||
std::pair<KeyboardManagerHelper::ErrorType, int> ValidateShortcutSelection(winrt::Windows::UI::Xaml::Controls::Grid table, winrt::Windows::UI::Xaml::Controls::StackPanel shortcutControl, winrt::Windows::UI::Xaml::Controls::StackPanel parent, int colIndex, std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, winrt::Windows::UI::Xaml::Controls::TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow);
|
||||
std::pair<KeyboardManagerHelper::ErrorType, int> ValidateShortcutSelection(winrt::Windows::UI::Xaml::Controls::Grid table, winrt::Windows::UI::Xaml::Controls::StackPanel shortcutControl, winrt::Windows::UI::Xaml::Controls::StackPanel parent, int colIndex, RemapBuffer& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, winrt::Windows::UI::Xaml::Controls::TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow);
|
||||
|
||||
// Function to set selection handler for shortcut drop down. Needs to be called after the constructor since the shortcutControl StackPanel is null if called in the constructor
|
||||
void SetSelectionHandler(winrt::Windows::UI::Xaml::Controls::Grid& table, winrt::Windows::UI::Xaml::Controls::StackPanel shortcutControl, winrt::Windows::UI::Xaml::Controls::StackPanel parent, int colIndex, std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, winrt::Windows::UI::Xaml::Controls::TextBox& targetApp, bool isHybridControl, bool isSingleKeyWindow);
|
||||
void SetSelectionHandler(winrt::Windows::UI::Xaml::Controls::Grid& table, winrt::Windows::UI::Xaml::Controls::StackPanel shortcutControl, winrt::Windows::UI::Xaml::Controls::StackPanel parent, int colIndex, RemapBuffer& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, winrt::Windows::UI::Xaml::Controls::TextBox& targetApp, bool isHybridControl, bool isSingleKeyWindow);
|
||||
|
||||
// Function to set the selected index of the drop down
|
||||
void SetSelectedIndex(int32_t index);
|
||||
@@ -72,20 +71,17 @@ public:
|
||||
ComboBox GetComboBox();
|
||||
|
||||
// Function to add a drop down to the shortcut stack panel
|
||||
static void AddDropDown(winrt::Windows::UI::Xaml::Controls::Grid table, winrt::Windows::UI::Xaml::Controls::StackPanel shortcutControl, winrt::Windows::UI::Xaml::Controls::StackPanel parent, const int colIndex, std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, winrt::Windows::UI::Xaml::Controls::TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow);
|
||||
static void AddDropDown(winrt::Windows::UI::Xaml::Controls::Grid table, winrt::Windows::UI::Xaml::Controls::StackPanel shortcutControl, winrt::Windows::UI::Xaml::Controls::StackPanel parent, const int colIndex, RemapBuffer& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, winrt::Windows::UI::Xaml::Controls::TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow);
|
||||
|
||||
// Function to get the list of key codes from the shortcut combo box stack panel
|
||||
static std::vector<DWORD> GetKeysFromStackPanel(StackPanel parent);
|
||||
|
||||
// Function to check if a modifier has been repeated in the previous drop downs
|
||||
static bool CheckRepeatedModifier(winrt::Windows::UI::Xaml::Controls::StackPanel parent, int selectedKeyIndex, const std::vector<DWORD>& keyCodeList);
|
||||
static std::vector<int32_t> GetSelectedIndicesFromStackPanel(StackPanel parent);
|
||||
|
||||
// Function for validating the selection of shortcuts for all the associated drop downs
|
||||
static void ValidateShortcutFromDropDownList(Grid table, StackPanel shortcutControl, StackPanel parent, int colIndex, std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow);
|
||||
static void ValidateShortcutFromDropDownList(Grid table, StackPanel shortcutControl, StackPanel parent, int colIndex, RemapBuffer& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow);
|
||||
|
||||
// Function to set the warning message
|
||||
void SetDropDownError(winrt::Windows::UI::Xaml::Controls::ComboBox currentDropDown, winrt::hstring message);
|
||||
|
||||
// Function to add a shortcut to the UI control as combo boxes
|
||||
static void AddShortcutToControl(Shortcut shortcut, Grid table, StackPanel parent, KeyboardManagerState& keyboardManagerState, const int colIndex, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& remapBuffer, StackPanel controlLayout, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow);
|
||||
static void AddShortcutToControl(Shortcut shortcut, Grid table, StackPanel parent, KeyboardManagerState& keyboardManagerState, const int colIndex, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, RemapBuffer& remapBuffer, StackPanel controlLayout, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow);
|
||||
};
|
||||
|
||||
@@ -112,10 +112,12 @@
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="BufferValidationHelpers.cpp" />
|
||||
<ClCompile Include="Dialog.cpp" />
|
||||
<ClCompile Include="EditKeyboardWindow.cpp" />
|
||||
<ClCompile Include="EditShortcutsWindow.cpp" />
|
||||
<ClCompile Include="KeyDropDownControl.cpp" />
|
||||
<ClCompile Include="LoadingAndSavingRemappingHelper.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
@@ -125,9 +127,11 @@
|
||||
<ClCompile Include="XamlBridge.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="BufferValidationHelpers.h" />
|
||||
<ClInclude Include="Dialog.h" />
|
||||
<ClInclude Include="EditKeyboardWindow.h" />
|
||||
<ClInclude Include="EditShortcutsWindow.h" />
|
||||
<ClInclude Include="LoadingAndSavingRemappingHelper.h" />
|
||||
<ClInclude Include="Styles.h" />
|
||||
<ClInclude Include="KeyDropDownControl.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
|
||||
@@ -28,6 +28,12 @@
|
||||
<ClCompile Include="XamlBridge.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="LoadingAndSavingRemappingHelper.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="BufferValidationHelpers.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Dialog.h">
|
||||
@@ -57,6 +63,12 @@
|
||||
<ClInclude Include="pch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="LoadingAndSavingRemappingHelper.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="BufferValidationHelpers.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
#include "pch.h"
|
||||
#include "LoadingAndSavingRemappingHelper.h"
|
||||
#include <set>
|
||||
#include "../common/shared_constants.h"
|
||||
#include <keyboardmanager/common/KeyboardManagerState.h>
|
||||
#include <keyboardmanager/common/trace.h>
|
||||
|
||||
namespace LoadingAndSavingRemappingHelper
|
||||
{
|
||||
// Function to check if the set of remappings in the buffer are valid
|
||||
KeyboardManagerHelper::ErrorType CheckIfRemappingsAreValid(const RemapBuffer& remappings)
|
||||
{
|
||||
KeyboardManagerHelper::ErrorType isSuccess = KeyboardManagerHelper::ErrorType::NoError;
|
||||
std::map<std::wstring, std::set<std::variant<DWORD, Shortcut>>> ogKeys;
|
||||
for (int i = 0; i < remappings.size(); i++)
|
||||
{
|
||||
std::variant<DWORD, Shortcut> ogKey = remappings[i].first[0];
|
||||
std::variant<DWORD, Shortcut> newKey = remappings[i].first[1];
|
||||
std::wstring appName = remappings[i].second;
|
||||
|
||||
bool ogKeyValidity = (ogKey.index() == 0 && std::get<DWORD>(ogKey) != NULL) || (ogKey.index() == 1 && std::get<Shortcut>(ogKey).IsValidShortcut());
|
||||
bool newKeyValidity = (newKey.index() == 0 && std::get<DWORD>(newKey) != NULL) || (newKey.index() == 1 && std::get<Shortcut>(newKey).IsValidShortcut());
|
||||
|
||||
// Add new set for a new target app name
|
||||
if (ogKeys.find(appName) == ogKeys.end())
|
||||
{
|
||||
ogKeys[appName] = std::set<std::variant<DWORD, Shortcut>>();
|
||||
}
|
||||
|
||||
if (ogKeyValidity && newKeyValidity && ogKeys[appName].find(ogKey) == ogKeys[appName].end())
|
||||
{
|
||||
ogKeys[appName].insert(ogKey);
|
||||
}
|
||||
else if (ogKeyValidity && newKeyValidity && ogKeys[appName].find(ogKey) != ogKeys[appName].end())
|
||||
{
|
||||
isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful;
|
||||
}
|
||||
else
|
||||
{
|
||||
isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful;
|
||||
}
|
||||
}
|
||||
return isSuccess;
|
||||
}
|
||||
|
||||
// Function to return the set of keys that have been orphaned from the remap buffer
|
||||
std::vector<DWORD> GetOrphanedKeys(const RemapBuffer& remappings)
|
||||
{
|
||||
std::set<DWORD> ogKeys;
|
||||
std::set<DWORD> newKeys;
|
||||
|
||||
for (int i = 0; i < remappings.size(); i++)
|
||||
{
|
||||
DWORD ogKey = std::get<DWORD>(remappings[i].first[0]);
|
||||
std::variant<DWORD, Shortcut> newKey = remappings[i].first[1];
|
||||
|
||||
if (ogKey != NULL && ((newKey.index() == 0 && std::get<DWORD>(newKey) != 0) || (newKey.index() == 1 && std::get<Shortcut>(newKey).IsValidShortcut())))
|
||||
{
|
||||
ogKeys.insert(ogKey);
|
||||
|
||||
// newKey should be added only if the target is a key
|
||||
if (remappings[i].first[1].index() == 0)
|
||||
{
|
||||
newKeys.insert(std::get<DWORD>(newKey));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& k : newKeys)
|
||||
{
|
||||
ogKeys.erase(k);
|
||||
}
|
||||
|
||||
return std::vector(ogKeys.begin(), ogKeys.end());
|
||||
}
|
||||
|
||||
// Function to combine remappings if the L and R version of the modifier is mapped to the same key
|
||||
void CombineRemappings(std::unordered_map<DWORD, std::variant<DWORD, Shortcut>>& table, DWORD leftKey, DWORD rightKey, DWORD combinedKey)
|
||||
{
|
||||
if (table.find(leftKey) != table.end() && table.find(rightKey) != table.end())
|
||||
{
|
||||
// If they are mapped to the same key, delete those entries and set the common version
|
||||
if (table[leftKey] == table[rightKey])
|
||||
{
|
||||
table[combinedKey] = table[leftKey];
|
||||
table.erase(leftKey);
|
||||
table.erase(rightKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function to pre process the remap table before loading it into the UI
|
||||
void PreProcessRemapTable(std::unordered_map<DWORD, std::variant<DWORD, Shortcut>>& table)
|
||||
{
|
||||
// Pre process the table to combine L and R versions of Ctrl/Alt/Shift/Win that are mapped to the same key
|
||||
CombineRemappings(table, VK_LCONTROL, VK_RCONTROL, VK_CONTROL);
|
||||
CombineRemappings(table, VK_LMENU, VK_RMENU, VK_MENU);
|
||||
CombineRemappings(table, VK_LSHIFT, VK_RSHIFT, VK_SHIFT);
|
||||
CombineRemappings(table, VK_LWIN, VK_RWIN, CommonSharedConstants::VK_WIN_BOTH);
|
||||
}
|
||||
|
||||
// Function to apply the single key remappings from the buffer to the KeyboardManagerState variable
|
||||
void ApplySingleKeyRemappings(KeyboardManagerState& keyboardManagerState, const RemapBuffer& remappings, bool isTelemetryRequired)
|
||||
{
|
||||
// Clear existing Key Remaps
|
||||
keyboardManagerState.ClearSingleKeyRemaps();
|
||||
DWORD successfulKeyToKeyRemapCount = 0;
|
||||
DWORD successfulKeyToShortcutRemapCount = 0;
|
||||
for (int i = 0; i < remappings.size(); i++)
|
||||
{
|
||||
DWORD originalKey = std::get<DWORD>(remappings[i].first[0]);
|
||||
std::variant<DWORD, Shortcut> newKey = remappings[i].first[1];
|
||||
|
||||
if (originalKey != NULL && !(newKey.index() == 0 && std::get<DWORD>(newKey) == NULL) && !(newKey.index() == 1 && !std::get<Shortcut>(newKey).IsValidShortcut()))
|
||||
{
|
||||
// If Ctrl/Alt/Shift are added, add their L and R versions instead to the same key
|
||||
bool result = false;
|
||||
bool res1, res2;
|
||||
switch (originalKey)
|
||||
{
|
||||
case VK_CONTROL:
|
||||
res1 = keyboardManagerState.AddSingleKeyRemap(VK_LCONTROL, newKey);
|
||||
res2 = keyboardManagerState.AddSingleKeyRemap(VK_RCONTROL, newKey);
|
||||
result = res1 && res2;
|
||||
break;
|
||||
case VK_MENU:
|
||||
res1 = keyboardManagerState.AddSingleKeyRemap(VK_LMENU, newKey);
|
||||
res2 = keyboardManagerState.AddSingleKeyRemap(VK_RMENU, newKey);
|
||||
result = res1 && res2;
|
||||
break;
|
||||
case VK_SHIFT:
|
||||
res1 = keyboardManagerState.AddSingleKeyRemap(VK_LSHIFT, newKey);
|
||||
res2 = keyboardManagerState.AddSingleKeyRemap(VK_RSHIFT, newKey);
|
||||
result = res1 && res2;
|
||||
break;
|
||||
case CommonSharedConstants::VK_WIN_BOTH:
|
||||
res1 = keyboardManagerState.AddSingleKeyRemap(VK_LWIN, newKey);
|
||||
res2 = keyboardManagerState.AddSingleKeyRemap(VK_RWIN, newKey);
|
||||
result = res1 && res2;
|
||||
break;
|
||||
default:
|
||||
result = keyboardManagerState.AddSingleKeyRemap(originalKey, newKey);
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
if (newKey.index() == 0)
|
||||
{
|
||||
successfulKeyToKeyRemapCount += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
successfulKeyToShortcutRemapCount += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If telemetry is to be logged, log the key remap counts
|
||||
if (isTelemetryRequired)
|
||||
{
|
||||
Trace::KeyRemapCount(successfulKeyToKeyRemapCount, successfulKeyToShortcutRemapCount);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to apply the shortcut remappings from the buffer to the KeyboardManagerState variable
|
||||
void ApplyShortcutRemappings(KeyboardManagerState& keyboardManagerState, const RemapBuffer& remappings, bool isTelemetryRequired)
|
||||
{
|
||||
// Clear existing shortcuts
|
||||
keyboardManagerState.ClearOSLevelShortcuts();
|
||||
keyboardManagerState.ClearAppSpecificShortcuts();
|
||||
DWORD successfulOSLevelShortcutToShortcutRemapCount = 0;
|
||||
DWORD successfulOSLevelShortcutToKeyRemapCount = 0;
|
||||
DWORD successfulAppSpecificShortcutToShortcutRemapCount = 0;
|
||||
DWORD successfulAppSpecificShortcutToKeyRemapCount = 0;
|
||||
// Save the shortcuts that are valid and report if any of them were invalid
|
||||
for (int i = 0; i < remappings.size(); i++)
|
||||
{
|
||||
Shortcut originalShortcut = std::get<Shortcut>(remappings[i].first[0]);
|
||||
std::variant<DWORD, Shortcut> newShortcut = remappings[i].first[1];
|
||||
|
||||
if (originalShortcut.IsValidShortcut() && ((newShortcut.index() == 0 && std::get<DWORD>(newShortcut) != NULL) || (newShortcut.index() == 1 && std::get<Shortcut>(newShortcut).IsValidShortcut())))
|
||||
{
|
||||
if (remappings[i].second == L"")
|
||||
{
|
||||
bool result = keyboardManagerState.AddOSLevelShortcut(originalShortcut, newShortcut);
|
||||
if (result)
|
||||
{
|
||||
if (newShortcut.index() == 0)
|
||||
{
|
||||
successfulOSLevelShortcutToKeyRemapCount += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
successfulOSLevelShortcutToShortcutRemapCount += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool result = keyboardManagerState.AddAppSpecificShortcut(remappings[i].second, originalShortcut, newShortcut);
|
||||
if (result)
|
||||
{
|
||||
if (newShortcut.index() == 0)
|
||||
{
|
||||
successfulAppSpecificShortcutToKeyRemapCount += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
successfulAppSpecificShortcutToShortcutRemapCount += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If telemetry is to be logged, log the shortcut remap counts
|
||||
if (isTelemetryRequired)
|
||||
{
|
||||
Trace::OSLevelShortcutRemapCount(successfulOSLevelShortcutToShortcutRemapCount, successfulOSLevelShortcutToKeyRemapCount);
|
||||
Trace::AppSpecificShortcutRemapCount(successfulAppSpecificShortcutToShortcutRemapCount, successfulAppSpecificShortcutToKeyRemapCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <keyboardmanager/common/Helpers.h>
|
||||
#include <variant>
|
||||
|
||||
class KeyboardManagerState;
|
||||
|
||||
namespace LoadingAndSavingRemappingHelper
|
||||
{
|
||||
// Function to check if the set of remappings in the buffer are valid
|
||||
KeyboardManagerHelper::ErrorType CheckIfRemappingsAreValid(const RemapBuffer& remappings);
|
||||
|
||||
// Function to return the set of keys that have been orphaned from the remap buffer
|
||||
std::vector<DWORD> GetOrphanedKeys(const RemapBuffer& remappings);
|
||||
|
||||
// Function to combine remappings if the L and R version of the modifier is mapped to the same key
|
||||
void CombineRemappings(std::unordered_map<DWORD, std::variant<DWORD, Shortcut>>& table, DWORD leftKey, DWORD rightKey, DWORD combinedKey);
|
||||
|
||||
// Function to pre process the remap table before loading it into the UI
|
||||
void PreProcessRemapTable(std::unordered_map<DWORD, std::variant<DWORD, Shortcut>>& table);
|
||||
|
||||
// Function to apply the single key remappings from the buffer to the KeyboardManagerState variable
|
||||
void ApplySingleKeyRemappings(KeyboardManagerState& keyboardManagerState, const RemapBuffer& remappings, bool isTelemetryRequired);
|
||||
|
||||
// Function to apply the shortcut remappings from the buffer to the KeyboardManagerState variable
|
||||
void ApplyShortcutRemappings(KeyboardManagerState& keyboardManagerState, const RemapBuffer& remappings, bool isTelemetryRequired);
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
HWND ShortcutControl::EditShortcutsWindowHandle = nullptr;
|
||||
KeyboardManagerState* ShortcutControl::keyboardManagerState = nullptr;
|
||||
// Initialized as new vector
|
||||
std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>> ShortcutControl::shortcutRemapBuffer;
|
||||
RemapBuffer ShortcutControl::shortcutRemapBuffer;
|
||||
|
||||
ShortcutControl::ShortcutControl(Grid table, const int colIndex, TextBox targetApp)
|
||||
{
|
||||
@@ -93,9 +93,9 @@ void ShortcutControl::AddNewShortcutControlRow(Grid& parent, std::vector<std::ve
|
||||
KeyDropDownControl::ValidateShortcutFromDropDownList(parent, keyboardRemapControlObjects[rowIndex][1]->getShortcutControl(), keyboardRemapControlObjects[rowIndex][1]->shortcutDropDownStackPanel.as<StackPanel>(), 1, ShortcutControl::shortcutRemapBuffer, keyboardRemapControlObjects[rowIndex][1]->keyDropDownControlObjects, targetAppTextBox, true, false);
|
||||
|
||||
// Reset the buffer based on the selected drop down items
|
||||
std::get<Shortcut>(shortcutRemapBuffer[rowIndex].first[0]).SetKeyCodes(KeyDropDownControl::GetKeysFromStackPanel(keyboardRemapControlObjects[rowIndex][0]->shortcutDropDownStackPanel.as<StackPanel>()));
|
||||
std::get<Shortcut>(shortcutRemapBuffer[rowIndex].first[0]).SetKeyCodes(KeyboardManagerHelper::GetKeyCodesFromSelectedIndices(KeyDropDownControl::GetSelectedIndicesFromStackPanel(keyboardRemapControlObjects[rowIndex][0]->shortcutDropDownStackPanel.as<StackPanel>()), KeyDropDownControl::keyboardManagerState->keyboardMap.GetKeyCodeList(true)));
|
||||
// second column is a hybrid column
|
||||
std::vector<DWORD> selectedKeyCodes = KeyDropDownControl::GetKeysFromStackPanel(keyboardRemapControlObjects[rowIndex][1]->shortcutDropDownStackPanel.as<StackPanel>());
|
||||
std::vector<DWORD> selectedKeyCodes = KeyboardManagerHelper::GetKeyCodesFromSelectedIndices(KeyDropDownControl::GetSelectedIndicesFromStackPanel(keyboardRemapControlObjects[rowIndex][1]->shortcutDropDownStackPanel.as<StackPanel>()), KeyDropDownControl::keyboardManagerState->keyboardMap.GetKeyCodeList(true));
|
||||
|
||||
// If exactly one key is selected consider it to be a key remap
|
||||
if (selectedKeyCodes.size() == 1)
|
||||
@@ -172,7 +172,7 @@ void ShortcutControl::AddNewShortcutControlRow(Grid& parent, std::vector<std::ve
|
||||
if (originalKeys.IsValidShortcut() && !(newKeys.index() == 0 && std::get<DWORD>(newKeys) == NULL) && !(newKeys.index() == 1 && !std::get<Shortcut>(newKeys).IsValidShortcut()))
|
||||
{
|
||||
// change to load app name
|
||||
shortcutRemapBuffer.push_back(std::make_pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>(std::vector<std::variant<DWORD, Shortcut>>{ Shortcut(), Shortcut() }, std::wstring(targetAppName)));
|
||||
shortcutRemapBuffer.push_back(std::make_pair<RemapBufferItem, std::wstring>(RemapBufferItem{ Shortcut(), Shortcut() }, std::wstring(targetAppName)));
|
||||
KeyDropDownControl::AddShortcutToControl(originalKeys, parent, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->shortcutDropDownStackPanel.as<StackPanel>(), *keyboardManagerState, 0, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->keyDropDownControlObjects, shortcutRemapBuffer, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->shortcutControlLayout.as<StackPanel>(), targetAppTextBox, false, false);
|
||||
|
||||
if (newKeys.index() == 0)
|
||||
@@ -192,7 +192,7 @@ void ShortcutControl::AddNewShortcutControlRow(Grid& parent, std::vector<std::ve
|
||||
else
|
||||
{
|
||||
// Initialize both shortcuts as empty shortcuts
|
||||
shortcutRemapBuffer.push_back(std::make_pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>(std::vector<std::variant<DWORD, Shortcut>>{ Shortcut(), Shortcut() }, std::wstring(targetAppName)));
|
||||
shortcutRemapBuffer.push_back(std::make_pair<RemapBufferItem, std::wstring>(RemapBufferItem{ Shortcut(), Shortcut() }, std::wstring(targetAppName)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,7 +203,7 @@ StackPanel ShortcutControl::getShortcutControl()
|
||||
}
|
||||
|
||||
// Function to create the detect shortcut UI window
|
||||
void ShortcutControl::createDetectShortcutWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KeyboardManagerState& keyboardManagerState, const int colIndex, Grid table, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, StackPanel controlLayout, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow, HWND parentWindow, std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& remapBuffer)
|
||||
void ShortcutControl::createDetectShortcutWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KeyboardManagerState& keyboardManagerState, const int colIndex, Grid table, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, StackPanel controlLayout, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow, HWND parentWindow, RemapBuffer& remapBuffer)
|
||||
{
|
||||
// ContentDialog for detecting shortcuts. This is the parent UI element.
|
||||
ContentDialog detectShortcutBox;
|
||||
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
// Pointer to the keyboard manager state
|
||||
static KeyboardManagerState* keyboardManagerState;
|
||||
// Stores the current list of remappings
|
||||
static std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>> shortcutRemapBuffer;
|
||||
static RemapBuffer shortcutRemapBuffer;
|
||||
// Vector to store dynamically allocated KeyDropDownControl objects to avoid early destruction
|
||||
std::vector<std::unique_ptr<KeyDropDownControl>> keyDropDownControlObjects;
|
||||
|
||||
@@ -47,5 +47,5 @@ public:
|
||||
StackPanel getShortcutControl();
|
||||
|
||||
// Function to create the detect shortcut UI window
|
||||
static void createDetectShortcutWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KeyboardManagerState& keyboardManagerState, const int colIndex, Grid table, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, StackPanel controlLayout, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow, HWND parentWindow, std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& remapBuffer);
|
||||
static void createDetectShortcutWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KeyboardManagerState& keyboardManagerState, const int colIndex, Grid table, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, StackPanel controlLayout, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow, HWND parentWindow, RemapBuffer& remapBuffer);
|
||||
};
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
HWND SingleKeyRemapControl::EditKeyboardWindowHandle = nullptr;
|
||||
KeyboardManagerState* SingleKeyRemapControl::keyboardManagerState = nullptr;
|
||||
// Initialized as new vector
|
||||
std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>> SingleKeyRemapControl::singleKeyRemapBuffer;
|
||||
RemapBuffer SingleKeyRemapControl::singleKeyRemapBuffer;
|
||||
|
||||
SingleKeyRemapControl::SingleKeyRemapControl(Grid table, const int colIndex)
|
||||
{
|
||||
@@ -93,7 +93,7 @@ void SingleKeyRemapControl::AddNewControlKeyRemapRow(Grid& parent, std::vector<s
|
||||
// Set the key text if the two keys are not null (i.e. default args)
|
||||
if (originalKey != NULL && !(newKey.index() == 0 && std::get<DWORD>(newKey) == NULL) && !(newKey.index() == 1 && !std::get<Shortcut>(newKey).IsValidShortcut()))
|
||||
{
|
||||
singleKeyRemapBuffer.push_back(std::make_pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>(std::vector<std::variant<DWORD, Shortcut>>{ originalKey, newKey }, L""));
|
||||
singleKeyRemapBuffer.push_back(std::make_pair<RemapBufferItem, std::wstring>(RemapBufferItem{ originalKey, newKey }, L""));
|
||||
std::vector<DWORD> keyCodes = keyboardManagerState->keyboardMap.GetKeyCodeList();
|
||||
std::vector<DWORD> shortcutListKeyCodes = keyboardManagerState->keyboardMap.GetKeyCodeList(true);
|
||||
auto it = std::find(keyCodes.begin(), keyCodes.end(), originalKey);
|
||||
@@ -117,7 +117,7 @@ void SingleKeyRemapControl::AddNewControlKeyRemapRow(Grid& parent, std::vector<s
|
||||
else
|
||||
{
|
||||
// Initialize both keys to NULL
|
||||
singleKeyRemapBuffer.push_back(std::make_pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>(std::vector<std::variant<DWORD, Shortcut>>{ NULL, NULL }, L""));
|
||||
singleKeyRemapBuffer.push_back(std::make_pair<RemapBufferItem, std::wstring>(RemapBufferItem{ NULL, NULL }, L""));
|
||||
}
|
||||
|
||||
// Delete row button
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
#include "KeyDropDownControl.h"
|
||||
#include <keyboardmanager/common/Shortcut.h>
|
||||
#include <variant>
|
||||
|
||||
class KeyboardManagerState;
|
||||
namespace winrt::Windows::UI::Xaml
|
||||
@@ -34,7 +33,7 @@ public:
|
||||
// Pointer to the keyboard manager state
|
||||
static KeyboardManagerState* keyboardManagerState;
|
||||
// Stores the current list of remappings
|
||||
static std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>> singleKeyRemapBuffer;
|
||||
static RemapBuffer singleKeyRemapBuffer;
|
||||
|
||||
// constructor
|
||||
SingleKeyRemapControl(Grid table, const int colIndex);
|
||||
|
||||
Reference in New Issue
Block a user