mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-06 11:16:51 +02:00
[Keyboard Manager] Added in Shortcut to Key and Key to Shortcut remapping (#5070)
* Added union class * Added key to shortcut backend implementation * Added tests * Added tests for CapsLock/modifier workaround for key to shortcut * Added correct JSON loading step * Cleaned shortcut remap code to use helper function for modifier keys * Removed RemapKey class * Enable Key to Shortcut in UI along with Type Shortcut in Remap key window * Fixed orphaning and unsuccessful remap dialog * Fixed column width * Renamed second type key button * Fixed Type Shortcut issues * Fixed shortcut to key backend logic and manually tested most scenarios * Added s2k in UI, manually verified its working * Added one more k2s test * Added tests for s2k * Added tests for Caps Lock workaround in shortcut remaps * Fixed formatting * Fixed formatting * Removed safety code since it can cause issues with code generated key up events * Added test for key up scenario * Tweaked warning text * Tweaked text * Tweaked text to fit in two lines * telemetry additions
This commit is contained in:
@@ -35,12 +35,18 @@ static std::vector<DWORD> GetOrphanedKeys()
|
||||
|
||||
for (int i = 0; i < SingleKeyRemapControl::singleKeyRemapBuffer.size(); i++)
|
||||
{
|
||||
DWORD ogKey = SingleKeyRemapControl::singleKeyRemapBuffer[i][0];
|
||||
DWORD newKey = SingleKeyRemapControl::singleKeyRemapBuffer[i][1];
|
||||
if (ogKey != 0 && newKey != 0)
|
||||
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);
|
||||
newKeys.insert(newKey);
|
||||
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,11 +92,8 @@ static IAsyncOperation<bool> OrphanKeysConfirmationDialog(
|
||||
|
||||
static IAsyncAction OnClickAccept(KeyboardManagerState& keyboardManagerState, XamlRoot root, std::function<void()> ApplyRemappings)
|
||||
{
|
||||
KeyboardManagerHelper::ErrorType isSuccess = Dialog::CheckIfRemappingsAreValid<DWORD>(
|
||||
SingleKeyRemapControl::singleKeyRemapBuffer,
|
||||
[](DWORD key) {
|
||||
return key != 0;
|
||||
});
|
||||
KeyboardManagerHelper::ErrorType isSuccess = Dialog::CheckIfRemappingsAreValid(SingleKeyRemapControl::singleKeyRemapBuffer);
|
||||
|
||||
if (isSuccess != KeyboardManagerHelper::ErrorType::NoError)
|
||||
{
|
||||
if (!co_await Dialog::PartialRemappingConfirmationDialog(root, L"Some of the keys could not be remapped. Do you want to continue anyway?"))
|
||||
@@ -98,6 +101,7 @@ static IAsyncAction OnClickAccept(KeyboardManagerState& keyboardManagerState, Xa
|
||||
co_return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for orphaned keys
|
||||
// Draw content Dialog
|
||||
std::vector<DWORD> orphanedKeys = GetOrphanedKeys();
|
||||
@@ -112,7 +116,7 @@ static IAsyncAction OnClickAccept(KeyboardManagerState& keyboardManagerState, Xa
|
||||
}
|
||||
|
||||
// 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, DWORD>& table, DWORD leftKey, DWORD rightKey, DWORD combinedKey)
|
||||
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())
|
||||
{
|
||||
@@ -127,7 +131,7 @@ void CombineRemappings(std::unordered_map<DWORD, DWORD>& table, DWORD leftKey, D
|
||||
}
|
||||
|
||||
// Function to pre process the remap table before loading it into the UI
|
||||
void PreProcessRemapTable(std::unordered_map<DWORD, DWORD>& table)
|
||||
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);
|
||||
@@ -234,13 +238,13 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
||||
|
||||
// Text block for information about remap key section.
|
||||
TextBlock keyRemapInfoHeader;
|
||||
keyRemapInfoHeader.Text(L"Select the key you want to change (Key) and the key you want it to become (Mapped To).");
|
||||
keyRemapInfoHeader.Text(L"Select the key you want to change (Key) and then the key or shortcut you want it to become (Mapped To).");
|
||||
keyRemapInfoHeader.Margin({ 10, 0, 0, 10 });
|
||||
keyRemapInfoHeader.FontWeight(Text::FontWeights::SemiBold());
|
||||
keyRemapInfoHeader.TextWrapping(TextWrapping::Wrap);
|
||||
|
||||
TextBlock keyRemapInfoExample;
|
||||
keyRemapInfoExample.Text(L"For example, if you want to press A and get B, Key A would be your \"Key\" and Key B would be your \"Mapped To\".");
|
||||
keyRemapInfoExample.Text(L"For example, if you want to press A and get \"Ctrl+C\", key \"A\" would be your \"Key\" column and the shortcut \"Ctrl+C\" would be your \"Mapped To\" column.");
|
||||
keyRemapInfoExample.Margin({ 10, 0, 0, 20 });
|
||||
keyRemapInfoExample.FontStyle(Text::FontStyle::Italic);
|
||||
keyRemapInfoExample.TextWrapping(TextWrapping::Wrap);
|
||||
@@ -253,8 +257,8 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
||||
ColumnDefinition arrowColumn;
|
||||
arrowColumn.MinWidth(KeyboardManagerConstants::TableArrowColWidth);
|
||||
ColumnDefinition newColumn;
|
||||
newColumn.MinWidth(KeyboardManagerConstants::RemapTableDropDownWidth);
|
||||
newColumn.MaxWidth(KeyboardManagerConstants::RemapTableDropDownWidth);
|
||||
newColumn.MinWidth(3 * KeyboardManagerConstants::ShortcutTableDropDownWidth + 2 * KeyboardManagerConstants::ShortcutTableDropDownSpacing);
|
||||
newColumn.MaxWidth(3 * KeyboardManagerConstants::ShortcutTableDropDownWidth + 2 * KeyboardManagerConstants::ShortcutTableDropDownSpacing);
|
||||
ColumnDefinition removeColumn;
|
||||
removeColumn.MinWidth(KeyboardManagerConstants::TableRemoveColWidth);
|
||||
keyRemapTable.Margin({ 10, 10, 10, 20 });
|
||||
@@ -300,7 +304,7 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
||||
|
||||
// Load existing remaps into UI
|
||||
std::unique_lock<std::mutex> lock(keyboardManagerState.singleKeyReMap_mutex);
|
||||
std::unordered_map<DWORD, DWORD> singleKeyRemapCopy = keyboardManagerState.singleKeyReMap;
|
||||
std::unordered_map<DWORD, std::variant<DWORD, Shortcut>> singleKeyRemapCopy = keyboardManagerState.singleKeyReMap;
|
||||
lock.unlock();
|
||||
PreProcessRemapTable(singleKeyRemapCopy);
|
||||
|
||||
@@ -322,13 +326,14 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
||||
KeyboardManagerHelper::ErrorType isSuccess = KeyboardManagerHelper::ErrorType::NoError;
|
||||
// Clear existing Key Remaps
|
||||
keyboardManagerState.ClearSingleKeyRemaps();
|
||||
DWORD successfulRemapCount = 0;
|
||||
DWORD successfulKeyToKeyRemapCount = 0;
|
||||
DWORD successfulKeyToShortcutRemapCount = 0;
|
||||
for (int i = 0; i < SingleKeyRemapControl::singleKeyRemapBuffer.size(); i++)
|
||||
{
|
||||
DWORD originalKey = SingleKeyRemapControl::singleKeyRemapBuffer[i][0];
|
||||
DWORD newKey = SingleKeyRemapControl::singleKeyRemapBuffer[i][1];
|
||||
DWORD originalKey = std::get<DWORD>(SingleKeyRemapControl::singleKeyRemapBuffer[i].first[0]);
|
||||
std::variant<DWORD, Shortcut> newKey = SingleKeyRemapControl::singleKeyRemapBuffer[i].first[1];
|
||||
|
||||
if (originalKey != NULL && newKey != NULL)
|
||||
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;
|
||||
@@ -366,7 +371,14 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
||||
}
|
||||
else
|
||||
{
|
||||
successfulRemapCount += 1;
|
||||
if (newKey.index() == 0)
|
||||
{
|
||||
successfulKeyToKeyRemapCount += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
successfulKeyToShortcutRemapCount += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -375,7 +387,7 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
||||
}
|
||||
}
|
||||
|
||||
Trace::KeyRemapCount(successfulRemapCount);
|
||||
Trace::KeyRemapCount(successfulKeyToKeyRemapCount, successfulKeyToShortcutRemapCount);
|
||||
// Save the updated shortcuts remaps to file.
|
||||
bool saveResult = keyboardManagerState.SaveConfigToFile();
|
||||
if (!saveResult)
|
||||
|
||||
Reference in New Issue
Block a user