diff --git a/src/modules/keyboardmanager/KeyboardManagerEditor/Resources.resx b/src/modules/keyboardmanager/KeyboardManagerEditor/Resources.resx index d3dd047fd8..e7451c8f72 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditor/Resources.resx +++ b/src/modules/keyboardmanager/KeyboardManagerEditor/Resources.resx @@ -275,6 +275,9 @@ Hold Space to toggle allow chord + + Hold BackSpace to toggle exact match + Hold Enter to continue @@ -287,6 +290,9 @@ Allow chords + + Exact Match + Path to program diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDropDownControl.h b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDropDownControl.h index 14299316fb..363f1c205d 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDropDownControl.h +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDropDownControl.h @@ -115,4 +115,6 @@ public: // Get number of selected keys. Do not count -1 and 0 values as they stand for Not selected and None static int GetNumberOfSelectedKeys(std::vector keys); + + bool exactMatch = false; }; diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerState.h b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerState.h index 1687fdabdf..2d02de55d7 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerState.h +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerState.h @@ -90,6 +90,8 @@ namespace KBMEditor // flag to set if we want to allow building a chord bool AllowChord = false; + bool exactMatch = false; + // Stores the keyboard layout LayoutMap keyboardMap; diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/ShortcutControl.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/ShortcutControl.cpp index 54722164de..41e23f36d8 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/ShortcutControl.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/ShortcutControl.cpp @@ -496,11 +496,11 @@ ShortcutControl& ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, s { // not sure why by we need to add the current item in here, so we have it even if does not change. auto newShortcut = std::get(newKeys); - shortcutRemapBuffer.push_back(RemapBufferRow{ RemapBufferItem{ Shortcut(), newShortcut }, std::wstring(targetAppName) }); + shortcutRemapBuffer.push_back(RemapBufferRow{ RemapBufferItem{ originalKeys, newShortcut }, std::wstring(targetAppName) }); } else { - shortcutRemapBuffer.push_back(RemapBufferRow{ RemapBufferItem{ Shortcut(), Shortcut() }, std::wstring(targetAppName) }); + shortcutRemapBuffer.push_back(RemapBufferRow{ RemapBufferItem{ originalKeys, newKeys }, std::wstring(targetAppName) }); } KeyDropDownControl::AddShortcutToControl(originalKeys, parent, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->shortcutDropDownVariableSizedWrapGrid.as(), *keyboardManagerState, 0, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->keyDropDownControlObjects, shortcutRemapBuffer, row, targetAppTextBox, false, false); @@ -956,11 +956,12 @@ void ShortcutControl::CreateDetectShortcutWindow(winrt::Windows::Foundation::IIn shortcut = std::get(shortcutRemapBuffer[rowIndex].mapping[1]); } - if (!shortcut.IsEmpty() && shortcut.HasChord()) + keyboardManagerState.AllowChord = false; + keyboardManagerState.exactMatch = false; + if (!shortcut.IsEmpty()) { - keyboardManagerState.AllowChord = true; - } else { - keyboardManagerState.AllowChord = false; + keyboardManagerState.AllowChord = shortcut.HasChord(); + keyboardManagerState.exactMatch = shortcut.exactMatch; } } @@ -969,6 +970,7 @@ void ShortcutControl::CreateDetectShortcutWindow(winrt::Windows::Foundation::IIn // ContentDialog for detecting shortcuts. This is the parent UI element. ContentDialog detectShortcutBox; ToggleSwitch allowChordSwitch; + ToggleSwitch exactMatchSwitch; // ContentDialog requires manually setting the XamlRoot (https://learn.microsoft.com/uwp/api/windows.ui.xaml.controls.contentdialog#contentdialog-in-appwindow-or-xaml-islands) detectShortcutBox.XamlRoot(xamlRoot); @@ -1038,6 +1040,11 @@ void ShortcutControl::CreateDetectShortcutWindow(winrt::Windows::Foundation::IIn allowChordSwitch.IsOn(keyboardManagerState.AllowChord); }; + auto onReleaseBack = [&keyboardManagerState, + exactMatchSwitch] { + keyboardManagerState.exactMatch = !keyboardManagerState.exactMatch; + exactMatchSwitch.IsOn(keyboardManagerState.exactMatch); + }; auto onAccept = [onPressEnter, onReleaseEnter] { onPressEnter(); @@ -1109,6 +1116,17 @@ void ShortcutControl::CreateDetectShortcutWindow(winrt::Windows::Foundation::IIn }); }, nullptr); + keyboardManagerState.RegisterKeyDelay( + VK_BACK, + selectDetectedShortcutAndResetKeys, + [onReleaseBack, detectShortcutBox](DWORD) { + detectShortcutBox.Dispatcher().RunAsync( + Windows::UI::Core::CoreDispatcherPriority::Normal, + [onReleaseBack] { + onReleaseBack(); + }); + }, + nullptr); } // Cancel button @@ -1157,6 +1175,7 @@ void ShortcutControl::CreateDetectShortcutWindow(winrt::Windows::Foundation::IIn // Detect Chord Windows::UI::Xaml::Controls::StackPanel chordStackPanel; + Windows::UI::Xaml::Controls::StackPanel exactMatchStackPanel; if (isOrigShortcut) { @@ -1189,6 +1208,32 @@ void ShortcutControl::CreateDetectShortcutWindow(winrt::Windows::Foundation::IIn }; allowChordSwitch.Toggled(toggleHandler); + + TextBlock exactMatchText; + exactMatchText.Text(GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_EXACT_MATCH)); + exactMatchText.FontSize(12); + exactMatchText.Margin({ 0, 12 + verticalMargin, 0, 0 }); + exactMatchText.TextAlignment(TextAlignment::Center); + exactMatchStackPanel.VerticalAlignment(VerticalAlignment::Center); + exactMatchStackPanel.Orientation(Orientation::Horizontal); + + exactMatchSwitch.OnContent(nullptr); + exactMatchSwitch.OffContent(nullptr); + exactMatchSwitch.Margin({ 12, verticalMargin, 0, 0 }); + exactMatchSwitch.IsOn(keyboardManagerState.exactMatch); + + exactMatchStackPanel.Children().Append(exactMatchText); + exactMatchStackPanel.Children().Append(exactMatchSwitch); + + stackPanel.Children().Append(exactMatchStackPanel); + exactMatchSwitch.IsOn(keyboardManagerState.exactMatch); + + auto toggleHandlerExactMatch = [exactMatchSwitch, &keyboardManagerState, rowIndex, &remapBuffer](auto const& sender, auto const& e) { + Shortcut& shortcut = std::get(remapBuffer[rowIndex].mapping[0]); + shortcut.exactMatch = exactMatchSwitch.IsOn(); + }; + + exactMatchSwitch.Toggled(toggleHandlerExactMatch); } TextBlock holdEscInfo; @@ -1211,6 +1256,12 @@ void ShortcutControl::CreateDetectShortcutWindow(winrt::Windows::Foundation::IIn holdSpaceInfo.FontSize(12); holdSpaceInfo.Margin({ 0, 0, 0, 0 }); stackPanel.Children().Append(holdSpaceInfo); + + TextBlock holdCtrInfo; + holdCtrInfo.Text(GET_RESOURCE_STRING(IDS_TYPE_HOLDBACKSPACE)); + holdCtrInfo.FontSize(12); + holdCtrInfo.Margin({ 0, 0, 0, 0 }); + stackPanel.Children().Append(holdCtrInfo); } try diff --git a/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.cpp b/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.cpp index 2bf9d302a7..b4754cd9c9 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.cpp @@ -261,6 +261,11 @@ namespace KeyboardEventHandlers // if not a mod key, check for chord stuff if (!resetChordsResults.CurrentKeyIsModifierKey && (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)) { + if (it->first.exactMatch == true && !it->first.IsKeyboardStateClearExceptShortcut(ii)) + { + continue; + } + if (itShortcut.HasChord()) { if (!resetChordsResults.AnyChordStarted && data->lParam->vkCode == itShortcut.GetActionKey() && !itShortcut.IsChordStarted() && itShortcut.HasChord()) diff --git a/src/modules/keyboardmanager/common/KeyboardManagerConstants.h b/src/modules/keyboardmanager/common/KeyboardManagerConstants.h index e0e0cffed1..474fecea59 100644 --- a/src/modules/keyboardmanager/common/KeyboardManagerConstants.h +++ b/src/modules/keyboardmanager/common/KeyboardManagerConstants.h @@ -64,15 +64,15 @@ namespace KeyboardManagerConstants // Name of the property use to store runProgramFilePath. inline const std::wstring RunProgramFilePathSettingName = L"runProgramFilePath"; - // Name of the property use to store secondKeyOfChord. - inline const std::wstring ShortcutSecondKeyOfChordSettingName = L"secondKeyOfChord"; - // Name of the property use to store openUri. inline const std::wstring ShortcutOpenURI = L"openUri"; // Name of the property use to store shortcutOperationType. inline const std::wstring ShortcutOperationType = L"operationType"; + // Name of the property use to store exactMatch. + inline const std::wstring ShortcutExactMatch = L"exactMatch"; + // Name of the property use to store the target application. inline const std::wstring TargetAppSettingName = L"targetApp"; diff --git a/src/modules/keyboardmanager/common/MappingConfiguration.cpp b/src/modules/keyboardmanager/common/MappingConfiguration.cpp index e1e542dbf7..5eb15f3d98 100644 --- a/src/modules/keyboardmanager/common/MappingConfiguration.cpp +++ b/src/modules/keyboardmanager/common/MappingConfiguration.cpp @@ -220,7 +220,9 @@ bool MappingConfiguration::LoadAppSpecificShortcutRemaps(const json::JsonObject& auto newRemapText = it.GetObjectW().GetNamedString(KeyboardManagerConstants::NewTextSettingName, {}); auto targetApp = it.GetObjectW().GetNamedString(KeyboardManagerConstants::TargetAppSettingName); auto operationType = it.GetObjectW().GetNamedNumber(KeyboardManagerConstants::ShortcutOperationType, 0); - + auto exactMatch = it.GetObjectW().GetNamedBoolean(KeyboardManagerConstants::ShortcutExactMatch, false); + auto originalShortcut = Shortcut(originalKeys.c_str()); + originalShortcut.exactMatch = exactMatch; // undo dummy data for backwards compatibility if (newRemapText == L"*Unsupported*") { @@ -236,7 +238,6 @@ bool MappingConfiguration::LoadAppSpecificShortcutRemaps(const json::JsonObject& auto runProgramElevationLevel = it.GetObjectW().GetNamedNumber(KeyboardManagerConstants::RunProgramElevationLevelSettingName, 0); auto runProgramAlreadyRunningAction = it.GetObjectW().GetNamedNumber(KeyboardManagerConstants::RunProgramAlreadyRunningAction, 0); auto runProgramStartWindowType = it.GetObjectW().GetNamedNumber(KeyboardManagerConstants::RunProgramStartWindowType, 0); - auto secondKeyOfChord = it.GetObjectW().GetNamedNumber(KeyboardManagerConstants::ShortcutSecondKeyOfChordSettingName, 0); auto tempShortcut = Shortcut(newRemapKeys.c_str()); tempShortcut.operationType = Shortcut::OperationType::RunProgram; @@ -247,16 +248,15 @@ bool MappingConfiguration::LoadAppSpecificShortcutRemaps(const json::JsonObject& tempShortcut.alreadyRunningAction = static_cast(runProgramAlreadyRunningAction); tempShortcut.startWindowType = static_cast(runProgramStartWindowType); - AddAppSpecificShortcut(targetApp.c_str(), Shortcut(originalKeys.c_str(), static_cast(secondKeyOfChord)), tempShortcut); + AddAppSpecificShortcut(targetApp.c_str(), originalShortcut, tempShortcut); } else if (operationType == 2) { - auto secondKeyOfChord = it.GetObjectW().GetNamedNumber(KeyboardManagerConstants::ShortcutSecondKeyOfChordSettingName, 0); auto tempShortcut = Shortcut(newRemapKeys.c_str()); tempShortcut.operationType = Shortcut::OperationType::OpenURI; tempShortcut.uriToOpen = it.GetObjectW().GetNamedString(KeyboardManagerConstants::ShortcutOpenURI, L""); - AddAppSpecificShortcut(targetApp.c_str(), Shortcut(originalKeys.c_str(), static_cast(secondKeyOfChord)), tempShortcut); + AddAppSpecificShortcut(targetApp.c_str(), originalShortcut, tempShortcut); } if (!newRemapKeys.empty()) @@ -264,18 +264,18 @@ bool MappingConfiguration::LoadAppSpecificShortcutRemaps(const json::JsonObject& // If remapped to a shortcut if (std::wstring(newRemapKeys).find(L";") != std::string::npos) { - AddAppSpecificShortcut(targetApp.c_str(), Shortcut(originalKeys.c_str()), Shortcut(newRemapKeys.c_str())); + AddAppSpecificShortcut(targetApp.c_str(), originalShortcut, Shortcut(newRemapKeys.c_str())); } // If remapped to a key else { - AddAppSpecificShortcut(targetApp.c_str(), Shortcut(originalKeys.c_str()), std::stoul(newRemapKeys.c_str())); + AddAppSpecificShortcut(targetApp.c_str(), originalShortcut, std::stoul(newRemapKeys.c_str())); } } else { - AddAppSpecificShortcut(targetApp.c_str(), Shortcut(originalKeys.c_str()), newRemapText.c_str()); + AddAppSpecificShortcut(targetApp.c_str(), originalShortcut, newRemapText.c_str()); } } catch (...) @@ -316,6 +316,8 @@ bool MappingConfiguration::LoadShortcutRemaps(const json::JsonObject& jsonData, auto newRemapText = it.GetObjectW().GetNamedString(KeyboardManagerConstants::NewTextSettingName, {}); auto operationType = it.GetObjectW().GetNamedNumber(KeyboardManagerConstants::ShortcutOperationType, 0); + auto originalShortcut = Shortcut(originalKeys.c_str()); + originalShortcut.exactMatch = it.GetObjectW().GetNamedBoolean(KeyboardManagerConstants::ShortcutExactMatch, false); // undo dummy data for backwards compatibility if (newRemapText == L"*Unsupported*") { @@ -330,8 +332,6 @@ bool MappingConfiguration::LoadShortcutRemaps(const json::JsonObject& jsonData, auto runProgramStartInDir = it.GetObjectW().GetNamedString(KeyboardManagerConstants::RunProgramStartInDirSettingName, L""); auto runProgramElevationLevel = it.GetObjectW().GetNamedNumber(KeyboardManagerConstants::RunProgramElevationLevelSettingName, 0); auto runProgramStartWindowType = it.GetObjectW().GetNamedNumber(KeyboardManagerConstants::RunProgramStartWindowType, 0); - auto secondKeyOfChord = it.GetObjectW().GetNamedNumber(KeyboardManagerConstants::ShortcutSecondKeyOfChordSettingName, 0); - auto runProgramAlreadyRunningAction = it.GetObjectW().GetNamedNumber(KeyboardManagerConstants::RunProgramAlreadyRunningAction, 0); auto tempShortcut = Shortcut(newRemapKeys.c_str()); @@ -343,33 +343,32 @@ bool MappingConfiguration::LoadShortcutRemaps(const json::JsonObject& jsonData, tempShortcut.alreadyRunningAction = static_cast(runProgramAlreadyRunningAction); tempShortcut.startWindowType = static_cast(runProgramStartWindowType); - AddOSLevelShortcut(Shortcut(originalKeys.c_str(), static_cast(secondKeyOfChord)), tempShortcut); + AddOSLevelShortcut(originalShortcut, tempShortcut); } else if (operationType == 2) { - auto secondKeyOfChord = it.GetObjectW().GetNamedNumber(KeyboardManagerConstants::ShortcutSecondKeyOfChordSettingName, 0); auto tempShortcut = Shortcut(newRemapKeys.c_str()); tempShortcut.operationType = Shortcut::OperationType::OpenURI; tempShortcut.uriToOpen = it.GetObjectW().GetNamedString(KeyboardManagerConstants::ShortcutOpenURI, L""); - AddOSLevelShortcut(Shortcut(originalKeys.c_str(), static_cast(secondKeyOfChord)), tempShortcut); + AddOSLevelShortcut(originalShortcut, tempShortcut); } else if (!newRemapKeys.empty()) { // If remapped to a shortcut if (std::wstring(newRemapKeys).find(L";") != std::string::npos) { - AddOSLevelShortcut(Shortcut(originalKeys.c_str()), Shortcut(newRemapKeys.c_str())); + AddOSLevelShortcut(originalShortcut, Shortcut(newRemapKeys.c_str())); } // If remapped to a key else { - AddOSLevelShortcut(Shortcut(originalKeys.c_str()), std::stoul(newRemapKeys.c_str())); + AddOSLevelShortcut(originalShortcut, std::stoul(newRemapKeys.c_str())); } } else { - AddOSLevelShortcut(Shortcut(originalKeys.c_str()), newRemapText.c_str()); + AddOSLevelShortcut(originalShortcut, newRemapText.c_str()); } } catch (...) @@ -491,6 +490,7 @@ bool MappingConfiguration::SaveSettingsToFile() keys.SetNamedValue(KeyboardManagerConstants::OriginalKeysSettingName, json::value(it.first.ToHstringVK())); + keys.SetNamedValue(KeyboardManagerConstants::ShortcutExactMatch, json::JsonValue::CreateBooleanValue(it.first.exactMatch)); bool remapToText = false; // For shortcut to key remapping @@ -506,7 +506,6 @@ bool MappingConfiguration::SaveSettingsToFile() if (targetShortcut.operationType == Shortcut::OperationType::RunProgram) { keys.SetNamedValue(KeyboardManagerConstants::RunProgramElevationLevelSettingName, json::value(static_cast(targetShortcut.elevationLevel))); - keys.SetNamedValue(KeyboardManagerConstants::ShortcutSecondKeyOfChordSettingName, json::value(static_cast(it.first.secondKey))); keys.SetNamedValue(KeyboardManagerConstants::ShortcutOperationType, json::value(static_cast(targetShortcut.operationType))); keys.SetNamedValue(KeyboardManagerConstants::RunProgramAlreadyRunningAction, json::value(static_cast(targetShortcut.alreadyRunningAction))); @@ -522,7 +521,6 @@ bool MappingConfiguration::SaveSettingsToFile() else if (targetShortcut.operationType == Shortcut::OperationType::OpenURI) { keys.SetNamedValue(KeyboardManagerConstants::RunProgramElevationLevelSettingName, json::value(static_cast(targetShortcut.elevationLevel))); - keys.SetNamedValue(KeyboardManagerConstants::ShortcutSecondKeyOfChordSettingName, json::value(static_cast(it.first.secondKey))); keys.SetNamedValue(KeyboardManagerConstants::ShortcutOperationType, json::value(static_cast(targetShortcut.operationType))); keys.SetNamedValue(KeyboardManagerConstants::ShortcutOpenURI, json::value(targetShortcut.uriToOpen)); @@ -533,7 +531,6 @@ bool MappingConfiguration::SaveSettingsToFile() else { keys.SetNamedValue(KeyboardManagerConstants::ShortcutOperationType, json::value(static_cast(targetShortcut.operationType))); - keys.SetNamedValue(KeyboardManagerConstants::ShortcutSecondKeyOfChordSettingName, json::value(static_cast(it.first.secondKey))); keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(targetShortcut.ToHstringVK())); } } @@ -542,7 +539,6 @@ bool MappingConfiguration::SaveSettingsToFile() { remapToText = true; keys.SetNamedValue(KeyboardManagerConstants::NewTextSettingName, json::value(std::get(it.second.targetShortcut))); - keys.SetNamedValue(KeyboardManagerConstants::ShortcutSecondKeyOfChordSettingName, json::value(static_cast(it.first.secondKey))); } if (!remapToText) @@ -558,7 +554,7 @@ bool MappingConfiguration::SaveSettingsToFile() { json::JsonObject keys; keys.SetNamedValue(KeyboardManagerConstants::OriginalKeysSettingName, json::value(itKeys.first.ToHstringVK())); - + keys.SetNamedValue(KeyboardManagerConstants::ShortcutExactMatch, json::JsonValue::CreateBooleanValue(itKeys.first.exactMatch)); bool remapToText = false; // For shortcut to key remapping @@ -575,7 +571,6 @@ bool MappingConfiguration::SaveSettingsToFile() if (targetShortcut.operationType == Shortcut::OperationType::RunProgram) { keys.SetNamedValue(KeyboardManagerConstants::RunProgramElevationLevelSettingName, json::value(static_cast(targetShortcut.elevationLevel))); - keys.SetNamedValue(KeyboardManagerConstants::ShortcutSecondKeyOfChordSettingName, json::value(static_cast(itKeys.first.secondKey))); keys.SetNamedValue(KeyboardManagerConstants::ShortcutOperationType, json::value(static_cast(targetShortcut.operationType))); keys.SetNamedValue(KeyboardManagerConstants::RunProgramAlreadyRunningAction, json::value(static_cast(targetShortcut.alreadyRunningAction))); @@ -591,7 +586,6 @@ bool MappingConfiguration::SaveSettingsToFile() else if (targetShortcut.operationType == Shortcut::OperationType::OpenURI) { keys.SetNamedValue(KeyboardManagerConstants::RunProgramElevationLevelSettingName, json::value(static_cast(targetShortcut.elevationLevel))); - keys.SetNamedValue(KeyboardManagerConstants::ShortcutSecondKeyOfChordSettingName, json::value(static_cast(itKeys.first.secondKey))); keys.SetNamedValue(KeyboardManagerConstants::ShortcutOperationType, json::value(static_cast(targetShortcut.operationType))); keys.SetNamedValue(KeyboardManagerConstants::ShortcutOpenURI, json::value(targetShortcut.uriToOpen)); diff --git a/src/modules/keyboardmanager/common/Shortcut.h b/src/modules/keyboardmanager/common/Shortcut.h index 0bbe00bb4f..5ca0623e63 100644 --- a/src/modules/keyboardmanager/common/Shortcut.h +++ b/src/modules/keyboardmanager/common/Shortcut.h @@ -26,6 +26,9 @@ private: std::vector Shortcut::ConvertToNumbers(std::vector& keys); public: + + bool exactMatch = false; + enum ElevationLevel { NonElevated = 0,