mirror of
https://github.com/microsoft/PowerToys.git
synced 2024-11-23 19:49:17 +08:00
[KBM]Allow remapping keys and shortcuts to arbitrary unicode sequences (#29399)
* [KBM] Allow remapping keys and shortcuts to arbitrary unicode sequences * f: spelling * f: tests * f: split shortcut configuration * f: address ui layout comments * [BugReport]Don't report personal info * f: fix crash in KBME * f: add missed type button * f: fix shortcut line UI elements alignment * f: align elements size * f: add warning about non-mapped keys
This commit is contained in:
parent
2543dee1f1
commit
f742d3c1c3
@ -287,6 +287,16 @@
|
||||
<value>Key</value>
|
||||
<comment>Key on a keyboard</comment>
|
||||
</data>
|
||||
<data name="Mapping_Type_DropDown_Text" xml:space="preserve">
|
||||
<value>Text</value>
|
||||
</data>
|
||||
<data name="Mapping_Type_DropDown_Shortcut" xml:space="preserve">
|
||||
<value>Shortcut</value>
|
||||
</data>
|
||||
<data name="Mapping_Type_DropDown_Key" xml:space="preserve">
|
||||
<value>Key</value>
|
||||
<comment>Key on a keyboard</comment>
|
||||
</data>
|
||||
<data name="Add_Key_Remap_Button" xml:space="preserve">
|
||||
<value>Add key remapping</value>
|
||||
<comment>Key on a keyboard</comment>
|
||||
|
@ -220,7 +220,7 @@ namespace BufferValidationHelpers
|
||||
// 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 == ShortcutErrorType::NoError)
|
||||
{
|
||||
KeyShortcutUnion tempShortcut;
|
||||
KeyShortcutTextUnion tempShortcut;
|
||||
if (isHybridControl && KeyDropDownControl::GetNumberOfSelectedKeys(selectedCodes) == 1)
|
||||
{
|
||||
tempShortcut = (DWORD)*std::find_if(selectedCodes.begin(), selectedCodes.end(), [](int32_t a) { return a != -1 && a != 0; });
|
||||
|
@ -285,14 +285,21 @@ inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardMan
|
||||
|
||||
// Load existing remaps into UI
|
||||
SingleKeyRemapTable singleKeyRemapCopy = mappingConfiguration.singleKeyReMap;
|
||||
SingleKeyToTextRemapTable singleKeyToTextRemapCopy = mappingConfiguration.singleKeyToTextReMap;
|
||||
|
||||
LoadingAndSavingRemappingHelper::PreProcessRemapTable(singleKeyRemapCopy);
|
||||
LoadingAndSavingRemappingHelper::PreProcessRemapTable(singleKeyToTextRemapCopy);
|
||||
|
||||
for (const auto& it : singleKeyRemapCopy)
|
||||
{
|
||||
SingleKeyRemapControl::AddNewControlKeyRemapRow(keyRemapTable, keyboardRemapControlObjects, it.first, it.second);
|
||||
}
|
||||
|
||||
for (const auto& it : singleKeyToTextRemapCopy)
|
||||
{
|
||||
SingleKeyRemapControl::AddNewControlKeyRemapRow(keyRemapTable, keyboardRemapControlObjects, it.first, it.second);
|
||||
}
|
||||
|
||||
// Main Header Apply button
|
||||
Button applyButton;
|
||||
applyButton.Content(winrt::box_value(GET_RESOURCE_STRING(IDS_OK_BUTTON)));
|
||||
|
@ -35,7 +35,7 @@ namespace EditorConstants
|
||||
inline const long ShortcutTableRemoveColIndex = 4;
|
||||
inline const long ShortcutArrowColumnWidth = 90;
|
||||
inline const DWORD64 ShortcutTableDropDownWidth = 160;
|
||||
inline const DWORD64 ShortcutTableDropDownSpacing = 10;
|
||||
inline const long ShortcutTableDropDownSpacing = 10;
|
||||
inline const long ShortcutOriginColumnWidth = 3 * ShortcutTableDropDownWidth + 3 * ShortcutTableDropDownSpacing;
|
||||
inline const long ShortcutTargetColumnWidth = 3 * ShortcutTableDropDownWidth + 3 * ShortcutTableDropDownSpacing + 15;
|
||||
|
||||
|
@ -66,7 +66,7 @@ void KeyDropDownControl::SetDefaultProperties(bool isShortcut, bool renderDisabl
|
||||
}
|
||||
|
||||
dropDown.as<ComboBox>().MaxDropDownHeight(EditorConstants::TableDropDownHeight);
|
||||
|
||||
|
||||
// Initialise layout attribute
|
||||
previousLayout = GetKeyboardLayout(0);
|
||||
dropDown.as<ComboBox>().SelectedValuePath(L"DataContext");
|
||||
@ -83,7 +83,20 @@ void KeyDropDownControl::SetDefaultProperties(bool isShortcut, bool renderDisabl
|
||||
// Attach the tip to the drop down
|
||||
warningTip.Target(dropDown.as<ComboBox>());
|
||||
dropDown.as<ComboBox>().Loaded([&](winrt::Windows::Foundation::IInspectable const& sender, auto args) {
|
||||
Media::VisualTreeHelper::GetChild(dropDown.as<ComboBox>(), 0).as<Grid>().Children().Append(warningTip);
|
||||
auto combo = dropDown.as<ComboBox>();
|
||||
auto child0 = Media::VisualTreeHelper::GetChild(combo, 0);
|
||||
if (!child0)
|
||||
return;
|
||||
|
||||
auto grid = child0.as<Grid>();
|
||||
if (!grid)
|
||||
return;
|
||||
|
||||
auto& gridChildren = grid.Children();
|
||||
if (!gridChildren)
|
||||
return;
|
||||
|
||||
gridChildren.Append(warningTip);
|
||||
});
|
||||
|
||||
// Tip properties
|
||||
@ -102,7 +115,7 @@ void KeyDropDownControl::SetDefaultProperties(bool isShortcut, bool renderDisabl
|
||||
warningFlyout.as<Flyout>().FlyoutPresenterStyle(style);
|
||||
dropDown.as<ComboBox>().ContextFlyout().SetAttachedFlyout((FrameworkElement)dropDown.as<ComboBox>(), warningFlyout.as<Flyout>());
|
||||
#endif
|
||||
|
||||
|
||||
// To set the accessible name of the combo-box (by default index 1)
|
||||
SetAccessibleNameForComboBox(dropDown.as<ComboBox>(), 1);
|
||||
}
|
||||
@ -141,7 +154,7 @@ void KeyDropDownControl::SetSelectionHandler(StackPanel& table, StackPanel row,
|
||||
|
||||
ComboBox currentDropDown = sender.as<ComboBox>();
|
||||
int selectedKeyCode = GetSelectedValue(currentDropDown);
|
||||
|
||||
|
||||
// Validate current remap selection
|
||||
ShortcutErrorType errorType = BufferValidationHelpers::ValidateAndUpdateKeyBufferElement(rowIndex, colIndex, selectedKeyCode, singleKeyRemapBuffer);
|
||||
|
||||
@ -228,7 +241,7 @@ std::pair<ShortcutErrorType, int> KeyDropDownControl::ValidateShortcutSelection(
|
||||
}
|
||||
|
||||
parent.Children().RemoveAt(dropDownIndex);
|
||||
|
||||
|
||||
// delete drop down control object from the vector so that it can be destructed
|
||||
keyDropDownControlObjects.erase(keyDropDownControlObjects.begin() + dropDownIndex);
|
||||
}
|
||||
@ -368,7 +381,7 @@ void KeyDropDownControl::ValidateShortcutFromDropDownList(StackPanel table, Stac
|
||||
{
|
||||
// Check for errors only if the current selection is a valid shortcut
|
||||
std::vector<int32_t> selectedKeyCodes = GetSelectedCodesFromStackPanel(parent);
|
||||
KeyShortcutUnion currentShortcut;
|
||||
KeyShortcutTextUnion currentShortcut;
|
||||
if (GetNumberOfSelectedKeys(selectedKeyCodes) == 1 && isHybridControl)
|
||||
{
|
||||
currentShortcut = (DWORD)selectedKeyCodes[0];
|
||||
@ -415,7 +428,7 @@ void KeyDropDownControl::AddShortcutToControl(Shortcut shortcut, StackPanel tabl
|
||||
{
|
||||
// Delete the existing drop down menus
|
||||
parent.Children().Clear();
|
||||
|
||||
|
||||
// Remove references to the old drop down objects to destroy them
|
||||
keyDropDownControlObjects.clear();
|
||||
std::vector<DWORD> shortcutKeyCodes = shortcut.GetKeyCodes();
|
||||
|
@ -11,7 +11,22 @@ namespace KeyboardManagerEditorStrings
|
||||
{
|
||||
return GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_ALLAPPS);
|
||||
}
|
||||
|
||||
|
||||
inline std::wstring MappingTypeText()
|
||||
{
|
||||
return GET_RESOURCE_STRING(IDS_MAPPING_TYPE_DROPDOWN_TEXT);
|
||||
}
|
||||
|
||||
inline std::wstring MappingTypeShortcut()
|
||||
{
|
||||
return GET_RESOURCE_STRING(IDS_MAPPING_TYPE_DROPDOWN_SHORTCUT);
|
||||
}
|
||||
|
||||
inline std::wstring MappingTypeKey()
|
||||
{
|
||||
return GET_RESOURCE_STRING(IDS_MAPPING_TYPE_DROPDOWN_KEY);
|
||||
}
|
||||
|
||||
// Function to return the error message
|
||||
winrt::hstring GetErrorMessage(ShortcutErrorType errorType);
|
||||
}
|
||||
|
@ -17,20 +17,20 @@ namespace LoadingAndSavingRemappingHelper
|
||||
ShortcutErrorType CheckIfRemappingsAreValid(const RemapBuffer& remappings)
|
||||
{
|
||||
ShortcutErrorType isSuccess = ShortcutErrorType::NoError;
|
||||
std::map<std::wstring, std::set<KeyShortcutUnion>> ogKeys;
|
||||
std::map<std::wstring, std::set<KeyShortcutTextUnion>> ogKeys;
|
||||
for (int i = 0; i < remappings.size(); i++)
|
||||
{
|
||||
KeyShortcutUnion ogKey = remappings[i].first[0];
|
||||
KeyShortcutUnion newKey = remappings[i].first[1];
|
||||
KeyShortcutTextUnion ogKey = remappings[i].first[0];
|
||||
KeyShortcutTextUnion newKey = remappings[i].first[1];
|
||||
std::wstring appName = remappings[i].second;
|
||||
|
||||
bool ogKeyValidity = (ogKey.index() == 0 && std::get<DWORD>(ogKey) != NULL) || (ogKey.index() == 1 && EditorHelpers::IsValidShortcut(std::get<Shortcut>(ogKey)));
|
||||
bool newKeyValidity = (newKey.index() == 0 && std::get<DWORD>(newKey) != NULL) || (newKey.index() == 1 && EditorHelpers::IsValidShortcut(std::get<Shortcut>(newKey)));
|
||||
const bool ogKeyValidity = (ogKey.index() == 0 && std::get<DWORD>(ogKey) != NULL) || (ogKey.index() == 1 && EditorHelpers::IsValidShortcut(std::get<Shortcut>(ogKey)));
|
||||
const bool newKeyValidity = (newKey.index() == 0 && std::get<DWORD>(newKey) != NULL) || (newKey.index() == 1 && EditorHelpers::IsValidShortcut(std::get<Shortcut>(newKey))) || (newKey.index() == 2 && !std::get<std::wstring>(newKey).empty());
|
||||
|
||||
// Add new set for a new target app name
|
||||
if (ogKeys.find(appName) == ogKeys.end())
|
||||
{
|
||||
ogKeys[appName] = std::set<KeyShortcutUnion>();
|
||||
ogKeys[appName] = std::set<KeyShortcutTextUnion>();
|
||||
}
|
||||
|
||||
if (ogKeyValidity && newKeyValidity && ogKeys[appName].find(ogKey) == ogKeys[appName].end())
|
||||
@ -59,9 +59,12 @@ namespace LoadingAndSavingRemappingHelper
|
||||
for (int i = 0; i < remappings.size(); i++)
|
||||
{
|
||||
DWORD ogKey = std::get<DWORD>(remappings[i].first[0]);
|
||||
KeyShortcutUnion newKey = remappings[i].first[1];
|
||||
KeyShortcutTextUnion newKey = remappings[i].first[1];
|
||||
|
||||
if (ogKey != NULL && ((newKey.index() == 0 && std::get<DWORD>(newKey) != 0) || (newKey.index() == 1 && EditorHelpers::IsValidShortcut(std::get<Shortcut>(newKey)))))
|
||||
const bool hasValidKeyRemapping = newKey.index() == 0 && std::get<DWORD>(newKey) != 0;
|
||||
const bool hasValidShortcutRemapping = newKey.index() == 1 && EditorHelpers::IsValidShortcut(std::get<Shortcut>(newKey));
|
||||
const bool hasValidTextRemapping = newKey.index() == 2 && !std::get<std::wstring>(newKey).empty();
|
||||
if (ogKey != NULL && (hasValidKeyRemapping || hasValidShortcutRemapping || hasValidTextRemapping))
|
||||
{
|
||||
ogKeys.insert(ogKey);
|
||||
|
||||
@ -116,53 +119,64 @@ namespace LoadingAndSavingRemappingHelper
|
||||
{
|
||||
// Clear existing Key Remaps
|
||||
mappingConfiguration.ClearSingleKeyRemaps();
|
||||
mappingConfiguration.ClearSingleKeyToTextRemaps();
|
||||
DWORD successfulKeyToKeyRemapCount = 0;
|
||||
DWORD successfulKeyToShortcutRemapCount = 0;
|
||||
DWORD successfulKeyToTextRemapCount = 0;
|
||||
for (int i = 0; i < remappings.size(); i++)
|
||||
{
|
||||
DWORD originalKey = std::get<DWORD>(remappings[i].first[0]);
|
||||
KeyShortcutUnion newKey = remappings[i].first[1];
|
||||
const DWORD originalKey = std::get<DWORD>(remappings[i].first[0]);
|
||||
KeyShortcutTextUnion newKey = remappings[i].first[1];
|
||||
|
||||
if (originalKey != NULL && !(newKey.index() == 0 && std::get<DWORD>(newKey) == NULL) && !(newKey.index() == 1 && !EditorHelpers::IsValidShortcut(std::get<Shortcut>(newKey))))
|
||||
if (originalKey != NULL && !(newKey.index() == 0 && std::get<DWORD>(newKey) == NULL) && !(newKey.index() == 1 && !EditorHelpers::IsValidShortcut(std::get<Shortcut>(newKey))) && !(newKey.index() == 2 && std::get<std::wstring>(newKey).empty()))
|
||||
{
|
||||
// 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)
|
||||
std::vector<DWORD> originalKeysWithModifiers;
|
||||
if (originalKey == VK_CONTROL)
|
||||
{
|
||||
case VK_CONTROL:
|
||||
res1 = mappingConfiguration.AddSingleKeyRemap(VK_LCONTROL, newKey);
|
||||
res2 = mappingConfiguration.AddSingleKeyRemap(VK_RCONTROL, newKey);
|
||||
result = res1 && res2;
|
||||
break;
|
||||
case VK_MENU:
|
||||
res1 = mappingConfiguration.AddSingleKeyRemap(VK_LMENU, newKey);
|
||||
res2 = mappingConfiguration.AddSingleKeyRemap(VK_RMENU, newKey);
|
||||
result = res1 && res2;
|
||||
break;
|
||||
case VK_SHIFT:
|
||||
res1 = mappingConfiguration.AddSingleKeyRemap(VK_LSHIFT, newKey);
|
||||
res2 = mappingConfiguration.AddSingleKeyRemap(VK_RSHIFT, newKey);
|
||||
result = res1 && res2;
|
||||
break;
|
||||
case CommonSharedConstants::VK_WIN_BOTH:
|
||||
res1 = mappingConfiguration.AddSingleKeyRemap(VK_LWIN, newKey);
|
||||
res2 = mappingConfiguration.AddSingleKeyRemap(VK_RWIN, newKey);
|
||||
result = res1 && res2;
|
||||
break;
|
||||
default:
|
||||
result = mappingConfiguration.AddSingleKeyRemap(originalKey, newKey);
|
||||
originalKeysWithModifiers.push_back(VK_LCONTROL);
|
||||
originalKeysWithModifiers.push_back(VK_RCONTROL);
|
||||
}
|
||||
else if (originalKey == VK_MENU)
|
||||
{
|
||||
originalKeysWithModifiers.push_back(VK_LMENU);
|
||||
originalKeysWithModifiers.push_back(VK_RMENU);
|
||||
}
|
||||
else if (originalKey == VK_SHIFT)
|
||||
{
|
||||
originalKeysWithModifiers.push_back(VK_LSHIFT);
|
||||
originalKeysWithModifiers.push_back(VK_RSHIFT);
|
||||
}
|
||||
else if (originalKey == CommonSharedConstants::VK_WIN_BOTH)
|
||||
{
|
||||
originalKeysWithModifiers.push_back(VK_LWIN);
|
||||
originalKeysWithModifiers.push_back(VK_RWIN);
|
||||
}
|
||||
else
|
||||
{
|
||||
originalKeysWithModifiers.push_back(originalKey);
|
||||
}
|
||||
|
||||
for (const DWORD key : originalKeysWithModifiers)
|
||||
{
|
||||
const bool mappedToText = newKey.index() == 2;
|
||||
result = mappedToText ? mappingConfiguration.AddSingleKeyToTextRemap(key, std::get<std::wstring>(newKey)) : mappingConfiguration.AddSingleKeyRemap(key, newKey) && result;
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
if (newKey.index() == 0)
|
||||
{
|
||||
successfulKeyToKeyRemapCount += 1;
|
||||
++successfulKeyToKeyRemapCount;
|
||||
}
|
||||
else
|
||||
else if (newKey.index() == 1)
|
||||
{
|
||||
successfulKeyToShortcutRemapCount += 1;
|
||||
++successfulKeyToShortcutRemapCount;
|
||||
}
|
||||
else if (newKey.index() == 2)
|
||||
{
|
||||
++successfulKeyToTextRemapCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -171,7 +185,7 @@ namespace LoadingAndSavingRemappingHelper
|
||||
// If telemetry is to be logged, log the key remap counts
|
||||
if (isTelemetryRequired)
|
||||
{
|
||||
Trace::KeyRemapCount(successfulKeyToKeyRemapCount, successfulKeyToShortcutRemapCount);
|
||||
Trace::KeyRemapCount(successfulKeyToKeyRemapCount, successfulKeyToShortcutRemapCount, successfulKeyToTextRemapCount);
|
||||
}
|
||||
}
|
||||
|
||||
@ -185,14 +199,14 @@ namespace LoadingAndSavingRemappingHelper
|
||||
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]);
|
||||
KeyShortcutUnion newShortcut = remappings[i].first[1];
|
||||
KeyShortcutTextUnion newShortcut = remappings[i].first[1];
|
||||
|
||||
if (EditorHelpers::IsValidShortcut(originalShortcut) && ((newShortcut.index() == 0 && std::get<DWORD>(newShortcut) != NULL) || (newShortcut.index() == 1 && EditorHelpers::IsValidShortcut(std::get<Shortcut>(newShortcut)))))
|
||||
if (EditorHelpers::IsValidShortcut(originalShortcut) && ((newShortcut.index() == 0 && std::get<DWORD>(newShortcut) != NULL) || (newShortcut.index() == 1 && EditorHelpers::IsValidShortcut(std::get<Shortcut>(newShortcut))) || (newShortcut.index() == 2 && !std::get<std::wstring>(newShortcut).empty())))
|
||||
{
|
||||
if (remappings[i].second == L"")
|
||||
{
|
||||
|
@ -15,10 +15,10 @@ namespace LoadingAndSavingRemappingHelper
|
||||
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, KeyShortcutUnion>& table, DWORD leftKey, DWORD rightKey, DWORD combinedKey);
|
||||
void CombineRemappings(std::unordered_map<DWORD, KeyShortcutTextUnion>& 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, KeyShortcutUnion>& table);
|
||||
void PreProcessRemapTable(std::unordered_map<DWORD, KeyShortcutTextUnion>& table);
|
||||
|
||||
// Function to apply the single key remappings from the buffer to the KeyboardManagerState variable
|
||||
void ApplySingleKeyRemappings(MappingConfiguration& mappingConfiguration, const RemapBuffer& remappings, bool isTelemetryRequired);
|
||||
|
@ -21,7 +21,7 @@ ShortcutControl::ShortcutControl(StackPanel table, StackPanel row, const int col
|
||||
shortcutDropDownVariableSizedWrapGrid = VariableSizedWrapGrid();
|
||||
typeShortcut = Button();
|
||||
shortcutControlLayout = StackPanel();
|
||||
bool isHybridControl = colIndex == 1 ? true : false;
|
||||
const bool isHybridControl = colIndex == 1;
|
||||
|
||||
// TODO: Check if there is a VariableSizedWrapGrid equivalent.
|
||||
// shortcutDropDownVariableSizedWrapGrid.as<VariableSizedWrapGrid>().Spacing(EditorConstants::ShortcutTableDropDownSpacing);
|
||||
@ -41,7 +41,13 @@ ShortcutControl::ShortcutControl(StackPanel table, StackPanel row, const int col
|
||||
|
||||
shortcutControlLayout.as<StackPanel>().Spacing(EditorConstants::ShortcutTableDropDownSpacing);
|
||||
|
||||
shortcutControlLayout.as<StackPanel>().Children().Append(typeShortcut.as<Button>());
|
||||
keyComboAndSelectStackPanel = StackPanel();
|
||||
keyComboAndSelectStackPanel.as<StackPanel>().Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
|
||||
keyComboAndSelectStackPanel.as<StackPanel>().Spacing(EditorConstants::ShortcutTableDropDownSpacing);
|
||||
|
||||
keyComboAndSelectStackPanel.as<StackPanel>().Children().Append(typeShortcut.as<Button>());
|
||||
shortcutControlLayout.as<StackPanel>().Children().InsertAt(0, keyComboAndSelectStackPanel.as<StackPanel>());
|
||||
|
||||
shortcutControlLayout.as<StackPanel>().Children().Append(shortcutDropDownVariableSizedWrapGrid.as<VariableSizedWrapGrid>());
|
||||
KeyDropDownControl::AddDropDown(table, row, shortcutDropDownVariableSizedWrapGrid.as<VariableSizedWrapGrid>(), colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, false);
|
||||
try
|
||||
@ -63,7 +69,7 @@ void ShortcutControl::SetAccessibleNameForTextBox(TextBox targetAppTextBox, int
|
||||
{
|
||||
targetAppTextBoxAccessibleName += GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_ALLAPPS);
|
||||
}
|
||||
|
||||
|
||||
targetAppTextBox.SetValue(Automation::AutomationProperties::NameProperty(), box_value(targetAppTextBoxAccessibleName));
|
||||
}
|
||||
|
||||
@ -77,7 +83,7 @@ void ShortcutControl::UpdateAccessibleNames(StackPanel sourceColumn, StackPanel
|
||||
}
|
||||
|
||||
// Function to add a new row to the shortcut table. If the originalKeys and newKeys args are provided, then the displayed shortcuts are set to those values.
|
||||
void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vector<std::vector<std::unique_ptr<ShortcutControl>>>& keyboardRemapControlObjects, const Shortcut& originalKeys, const KeyShortcutUnion& newKeys, const std::wstring& targetAppName)
|
||||
void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vector<std::vector<std::unique_ptr<ShortcutControl>>>& keyboardRemapControlObjects, const Shortcut& originalKeys, const KeyShortcutTextUnion& newKeys, const std::wstring& targetAppName)
|
||||
{
|
||||
// Textbox for target application
|
||||
TextBox targetAppTextBox;
|
||||
@ -115,6 +121,60 @@ void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vector<s
|
||||
// ShortcutControl for the new shortcut
|
||||
auto target = keyboardRemapControlObjects.back()[1]->GetShortcutControl();
|
||||
target.Width(EditorConstants::ShortcutTargetColumnWidth);
|
||||
|
||||
auto typeCombo = ComboBox();
|
||||
typeCombo.Width(EditorConstants::RemapTableDropDownWidth);
|
||||
typeCombo.Items().Append(winrt::box_value(KeyboardManagerEditorStrings::MappingTypeShortcut()));
|
||||
typeCombo.Items().Append(winrt::box_value(KeyboardManagerEditorStrings::MappingTypeText()));
|
||||
auto controlStackPanel = keyboardRemapControlObjects.back()[1]->shortcutControlLayout.as<StackPanel>();
|
||||
auto firstLineStackPanel = keyboardRemapControlObjects.back()[1]->keyComboAndSelectStackPanel.as<StackPanel>();
|
||||
firstLineStackPanel.Children().InsertAt(0, typeCombo);
|
||||
|
||||
auto textInput = TextBox();
|
||||
auto textInputMargin = Windows::UI::Xaml::Thickness();
|
||||
textInputMargin.Top = -EditorConstants::ShortcutTableDropDownSpacing;
|
||||
textInputMargin.Bottom = EditorConstants::ShortcutTableDropDownSpacing; // compensate for a collapsed UIElement
|
||||
textInput.Margin(textInputMargin);
|
||||
|
||||
textInput.AcceptsReturn(false);
|
||||
textInput.Visibility(Visibility::Collapsed);
|
||||
textInput.Width(EditorConstants::TableDropDownHeight);
|
||||
controlStackPanel.Children().Append(textInput);
|
||||
textInput.HorizontalAlignment(HorizontalAlignment::Left);
|
||||
textInput.TextChanged([parent, row](winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::Controls::TextChangedEventArgs const& e) mutable {
|
||||
auto textbox = sender.as<TextBox>();
|
||||
auto text = textbox.Text();
|
||||
uint32_t rowIndex = -1;
|
||||
|
||||
if (!parent.Children().IndexOf(row, rowIndex))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
shortcutRemapBuffer[rowIndex].first[1] = text.c_str();
|
||||
});
|
||||
|
||||
auto grid = keyboardRemapControlObjects.back()[1]->shortcutDropDownVariableSizedWrapGrid.as<VariableSizedWrapGrid>();
|
||||
auto gridMargin = Windows::UI::Xaml::Thickness();
|
||||
gridMargin.Bottom = -EditorConstants::ShortcutTableDropDownSpacing; // compensate for a collapsed textInput
|
||||
grid.Margin(gridMargin);
|
||||
auto button = keyboardRemapControlObjects.back()[1]->typeShortcut.as<Button>();
|
||||
|
||||
typeCombo.SelectionChanged([typeCombo, grid, button, textInput](winrt::Windows::Foundation::IInspectable const&, SelectionChangedEventArgs const&) {
|
||||
const bool textSelected = typeCombo.SelectedIndex() == 1;
|
||||
|
||||
const auto shortcutInputVisibility = textSelected ? Visibility::Collapsed : Visibility::Visible;
|
||||
|
||||
grid.Visibility(shortcutInputVisibility);
|
||||
button.Visibility(shortcutInputVisibility);
|
||||
|
||||
const auto textInputVisibility = textSelected ? Visibility::Visible : Visibility::Collapsed;
|
||||
textInput.Visibility(textInputVisibility);
|
||||
});
|
||||
|
||||
const bool textSelected = newKeys.index() == 2;
|
||||
typeCombo.SelectedIndex(textSelected);
|
||||
|
||||
row.Children().Append(target);
|
||||
|
||||
targetAppTextBox.Width(EditorConstants::ShortcutTableDropDownWidth);
|
||||
@ -129,7 +189,7 @@ void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vector<s
|
||||
});
|
||||
|
||||
// LostFocus handler will be called whenever text is updated by a user and then they click something else or tab to another control. Does not get called if Text is updated while the TextBox isn't in focus (i.e. from code)
|
||||
targetAppTextBox.LostFocus([&keyboardRemapControlObjects, parent, row, targetAppTextBox](auto const& sender, auto const& e) {
|
||||
targetAppTextBox.LostFocus([&keyboardRemapControlObjects, parent, row, targetAppTextBox, typeCombo, textInput](auto const& sender, auto const& e) {
|
||||
// Get index of targetAppTextBox button
|
||||
uint32_t rowIndex;
|
||||
if (!parent.Children().IndexOf(row, rowIndex))
|
||||
@ -151,19 +211,27 @@ void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vector<s
|
||||
std::get<Shortcut>(shortcutRemapBuffer[rowIndex].first[0]).SetKeyCodes(KeyDropDownControl::GetSelectedCodesFromStackPanel(keyboardRemapControlObjects[rowIndex][0]->shortcutDropDownVariableSizedWrapGrid.as<VariableSizedWrapGrid>()));
|
||||
// second column is a hybrid column
|
||||
|
||||
std::vector<int32_t> selectedKeyCodes = KeyDropDownControl::GetSelectedCodesFromStackPanel(keyboardRemapControlObjects[rowIndex][1]->shortcutDropDownVariableSizedWrapGrid.as<VariableSizedWrapGrid>());
|
||||
|
||||
// If exactly one key is selected consider it to be a key remap
|
||||
if (selectedKeyCodes.size() == 1)
|
||||
const bool textSelected = typeCombo.SelectedIndex() == 1;
|
||||
if (textSelected)
|
||||
{
|
||||
shortcutRemapBuffer[rowIndex].first[1] = (DWORD)selectedKeyCodes[0];
|
||||
shortcutRemapBuffer[rowIndex].first[1] = textInput.Text().c_str();
|
||||
}
|
||||
else
|
||||
{
|
||||
Shortcut tempShortcut;
|
||||
tempShortcut.SetKeyCodes(selectedKeyCodes);
|
||||
// Assign instead of setting the value in the buffer since the previous value may not be a Shortcut
|
||||
shortcutRemapBuffer[rowIndex].first[1] = tempShortcut;
|
||||
std::vector<int32_t> selectedKeyCodes = KeyDropDownControl::GetSelectedCodesFromStackPanel(keyboardRemapControlObjects[rowIndex][1]->shortcutDropDownVariableSizedWrapGrid.as<VariableSizedWrapGrid>());
|
||||
|
||||
// If exactly one key is selected consider it to be a key remap
|
||||
if (selectedKeyCodes.size() == 1)
|
||||
{
|
||||
shortcutRemapBuffer[rowIndex].first[1] = (DWORD)selectedKeyCodes[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
Shortcut tempShortcut;
|
||||
tempShortcut.SetKeyCodes(selectedKeyCodes);
|
||||
// Assign instead of setting the value in the buffer since the previous value may not be a Shortcut
|
||||
shortcutRemapBuffer[rowIndex].first[1] = tempShortcut;
|
||||
}
|
||||
}
|
||||
std::wstring newText = targetAppTextBox.Text().c_str();
|
||||
std::wstring lowercaseDefAppName = KeyboardManagerEditorStrings::DefaultAppName();
|
||||
@ -263,10 +331,19 @@ void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vector<s
|
||||
{
|
||||
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->keyDropDownControlObjects[0]->SetSelectedValue(std::to_wstring(std::get<DWORD>(newKeys)));
|
||||
}
|
||||
else
|
||||
else if (newKeys.index() == 1)
|
||||
{
|
||||
KeyDropDownControl::AddShortcutToControl(std::get<Shortcut>(newKeys), parent, keyboardRemapControlObjects.back()[1]->shortcutDropDownVariableSizedWrapGrid.as<VariableSizedWrapGrid>(), *keyboardManagerState, 1, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->keyDropDownControlObjects, shortcutRemapBuffer, row, targetAppTextBox, true, false);
|
||||
}
|
||||
else if (newKeys.index() == 2)
|
||||
{
|
||||
shortcutRemapBuffer.back().first[1] = std::get<std::wstring>(newKeys);
|
||||
const auto& remapControl = keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1];
|
||||
const auto& controlChildren = remapControl->GetShortcutControl().Children();
|
||||
const auto& topLineChildren = controlChildren.GetAt(0).as<StackPanel>();
|
||||
topLineChildren.Children().GetAt(0).as<ComboBox>().SelectedIndex(1);
|
||||
controlChildren.GetAt(2).as<TextBox>().Text(std::get<std::wstring>(newKeys));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -291,8 +368,8 @@ void ShortcutControl::CreateDetectShortcutWindow(winrt::Windows::Foundation::IIn
|
||||
detectShortcutBox.XamlRoot(xamlRoot);
|
||||
detectShortcutBox.Title(box_value(GET_RESOURCE_STRING(IDS_TYPESHORTCUT_TITLE)));
|
||||
|
||||
// Get the linked stack panel for the "Type shortcut" button that was clicked
|
||||
VariableSizedWrapGrid linkedShortcutVariableSizedWrapGrid = UIHelpers::GetSiblingElement(sender).as<VariableSizedWrapGrid>();
|
||||
// Get the parent linked stack panel for the "Type shortcut" button that was clicked
|
||||
VariableSizedWrapGrid linkedShortcutVariableSizedWrapGrid = UIHelpers::GetSiblingElement(sender.as<FrameworkElement>().Parent()).as<VariableSizedWrapGrid>();
|
||||
|
||||
auto unregisterKeys = [&keyboardManagerState]() {
|
||||
keyboardManagerState.ClearRegisteredKeyDelays();
|
||||
|
@ -31,6 +31,9 @@ private:
|
||||
// StackPanel to parent the above controls
|
||||
winrt::Windows::Foundation::IInspectable shortcutControlLayout;
|
||||
|
||||
// StackPanel to parent the first line of "To" Column
|
||||
winrt::Windows::Foundation::IInspectable keyComboAndSelectStackPanel;
|
||||
|
||||
// Function to set the accessible name of the target app text box
|
||||
static void SetAccessibleNameForTextBox(TextBox targetAppTextBox, int rowIndex);
|
||||
|
||||
@ -54,7 +57,7 @@ public:
|
||||
ShortcutControl(StackPanel table, StackPanel row, const int colIndex, TextBox targetApp);
|
||||
|
||||
// Function to add a new row to the shortcut table. If the originalKeys and newKeys args are provided, then the displayed shortcuts are set to those values.
|
||||
static void AddNewShortcutControlRow(StackPanel& parent, std::vector<std::vector<std::unique_ptr<ShortcutControl>>>& keyboardRemapControlObjects, const Shortcut& originalKeys = Shortcut(), const KeyShortcutUnion& newKeys = Shortcut(), const std::wstring& targetAppName = L"");
|
||||
static void AddNewShortcutControlRow(StackPanel& parent, std::vector<std::vector<std::unique_ptr<ShortcutControl>>>& keyboardRemapControlObjects, const Shortcut& originalKeys = Shortcut(), const KeyShortcutTextUnion& newKeys = Shortcut(), const std::wstring& targetAppName = L"");
|
||||
|
||||
// Function to return the stack panel element of the ShortcutControl. This is the externally visible UI element which can be used to add it to other layouts
|
||||
StackPanel GetShortcutControl();
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "SingleKeyRemapControl.h"
|
||||
|
||||
#include "KeyboardManagerState.h"
|
||||
#include "KeyboardManagerEditorStrings.h"
|
||||
#include "ShortcutControl.h"
|
||||
#include "UIHelpers.h"
|
||||
#include "EditorHelpers.h"
|
||||
@ -20,25 +21,80 @@ SingleKeyRemapControl::SingleKeyRemapControl(StackPanel table, StackPanel row, c
|
||||
typeKey.as<Button>().Content(winrt::box_value(GET_RESOURCE_STRING(IDS_TYPE_BUTTON)));
|
||||
|
||||
singleKeyRemapControlLayout = StackPanel();
|
||||
singleKeyRemapControlLayout.as<StackPanel>().Spacing(10);
|
||||
singleKeyRemapControlLayout.as<StackPanel>().Children().Append(typeKey.as<Button>());
|
||||
singleKeyRemapControlLayout.as<StackPanel>().Spacing(EditorConstants::ShortcutTableDropDownSpacing);
|
||||
|
||||
// Key column
|
||||
// Key column (From key)
|
||||
if (colIndex == 0)
|
||||
{
|
||||
singleKeyRemapControlLayout.as<StackPanel>().Children().Append(typeKey.as<Button>());
|
||||
|
||||
keyDropDownControlObjects.emplace_back(std::make_unique<KeyDropDownControl>(false));
|
||||
singleKeyRemapControlLayout.as<StackPanel>().Children().Append(keyDropDownControlObjects[0]->GetComboBox());
|
||||
// Set selection handler for the drop down
|
||||
keyDropDownControlObjects[0]->SetSelectionHandler(table, row, colIndex, singleKeyRemapBuffer);
|
||||
}
|
||||
|
||||
// Hybrid column
|
||||
// Hybrid column (To Key/Shortcut/Text)
|
||||
else
|
||||
{
|
||||
StackPanel keyComboAndSelectStackPanel;
|
||||
keyComboAndSelectStackPanel.Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
|
||||
keyComboAndSelectStackPanel.Spacing(EditorConstants::ShortcutTableDropDownSpacing);
|
||||
|
||||
hybridDropDownVariableSizedWrapGrid = VariableSizedWrapGrid();
|
||||
hybridDropDownVariableSizedWrapGrid.as<VariableSizedWrapGrid>().Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
|
||||
KeyDropDownControl::AddDropDown(table, row, hybridDropDownVariableSizedWrapGrid.as<VariableSizedWrapGrid>(), colIndex, singleKeyRemapBuffer, keyDropDownControlObjects, nullptr, true, true);
|
||||
singleKeyRemapControlLayout.as<StackPanel>().Children().Append(hybridDropDownVariableSizedWrapGrid.as<VariableSizedWrapGrid>());
|
||||
auto grid = hybridDropDownVariableSizedWrapGrid.as<VariableSizedWrapGrid>();
|
||||
grid.Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
|
||||
auto gridMargin = Windows::UI::Xaml::Thickness();
|
||||
gridMargin.Bottom = -EditorConstants::ShortcutTableDropDownSpacing; // compensate for a collapsed textInput
|
||||
grid.Margin(gridMargin);
|
||||
|
||||
KeyDropDownControl::AddDropDown(table, row, grid, colIndex, singleKeyRemapBuffer, keyDropDownControlObjects, nullptr, true, true);
|
||||
|
||||
singleKeyRemapControlLayout.as<StackPanel>().Children().Append(grid);
|
||||
|
||||
auto textInput = TextBox();
|
||||
|
||||
auto textBoxMargin = Windows::UI::Xaml::Thickness();
|
||||
textBoxMargin.Top = -EditorConstants::ShortcutTableDropDownSpacing; // compensate for a collapsed grid
|
||||
textBoxMargin.Bottom = EditorConstants::ShortcutTableDropDownSpacing;
|
||||
textInput.Margin(textBoxMargin);
|
||||
textInput.AcceptsReturn(false);
|
||||
textInput.Visibility(Visibility::Collapsed);
|
||||
textInput.Width(EditorConstants::TableDropDownHeight);
|
||||
singleKeyRemapControlLayout.as<StackPanel>().Children().Append(textInput);
|
||||
textInput.HorizontalAlignment(HorizontalAlignment::Left);
|
||||
textInput.TextChanged([this, row, table](winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::Controls::TextChangedEventArgs const& e) mutable {
|
||||
auto textbox = sender.as<TextBox>();
|
||||
auto text = textbox.Text();
|
||||
uint32_t rowIndex = -1;
|
||||
if (!table.Children().IndexOf(row, rowIndex))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
singleKeyRemapBuffer[rowIndex].first[1] = text.c_str();
|
||||
});
|
||||
|
||||
auto typeCombo = ComboBox();
|
||||
typeCombo.Width(EditorConstants::RemapTableDropDownWidth);
|
||||
typeCombo.Items().Append(winrt::box_value(KeyboardManagerEditorStrings::MappingTypeKey()));
|
||||
typeCombo.Items().Append(winrt::box_value(KeyboardManagerEditorStrings::MappingTypeText()));
|
||||
keyComboAndSelectStackPanel.Children().Append(typeCombo);
|
||||
keyComboAndSelectStackPanel.Children().Append(typeKey.as<Button>());
|
||||
singleKeyRemapControlLayout.as<StackPanel>().Children().InsertAt(0, keyComboAndSelectStackPanel);
|
||||
|
||||
typeCombo.SelectedIndex(0);
|
||||
typeCombo.SelectionChanged([this, typeCombo, grid, textInput](winrt::Windows::Foundation::IInspectable const&, SelectionChangedEventArgs const&) {
|
||||
const bool textSelected = typeCombo.SelectedIndex() == 1;
|
||||
|
||||
const auto keyInputVisibility = textSelected ? Visibility::Collapsed : Visibility::Visible;
|
||||
|
||||
grid.Visibility(keyInputVisibility);
|
||||
typeKey.as<Button>().Visibility(keyInputVisibility);
|
||||
|
||||
const auto textInputVisibility = textSelected ? Visibility::Visible : Visibility::Collapsed;
|
||||
textInput.Visibility(textInputVisibility);
|
||||
});
|
||||
}
|
||||
|
||||
typeKey.as<Button>().Click([&, table, colIndex, row](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
@ -73,8 +129,15 @@ void SingleKeyRemapControl::UpdateAccessibleNames(StackPanel sourceColumn, Stack
|
||||
deleteButton.SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_AUTOMATIONPROPERTIES_ROW) + std::to_wstring(rowIndex) + L", " + GET_RESOURCE_STRING(IDS_DELETE_REMAPPING_BUTTON)));
|
||||
}
|
||||
|
||||
void SingleKeyRemapControl::TextToMapChangedHandler(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::Controls::TextChangedEventArgs const& e) // TODO: remove
|
||||
{
|
||||
auto textbox = sender.as<TextBox>();
|
||||
auto text = textbox.Text();
|
||||
(void)text;
|
||||
}
|
||||
|
||||
// Function to add a new row to the remap keys table. If the originalKey and newKey args are provided, then the displayed remap keys are set to those values.
|
||||
void SingleKeyRemapControl::AddNewControlKeyRemapRow(StackPanel& parent, std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>>& keyboardRemapControlObjects, const DWORD originalKey, const KeyShortcutUnion newKey)
|
||||
void SingleKeyRemapControl::AddNewControlKeyRemapRow(StackPanel& parent, std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>>& keyboardRemapControlObjects, const DWORD originalKey, const KeyShortcutTextUnion newKey)
|
||||
{
|
||||
// Create new SingleKeyRemapControl objects dynamically so that we does not get destructed
|
||||
std::vector<std::unique_ptr<SingleKeyRemapControl>> newrow;
|
||||
@ -112,7 +175,7 @@ void SingleKeyRemapControl::AddNewControlKeyRemapRow(StackPanel& parent, std::ve
|
||||
row.Children().Append(targetElement);
|
||||
|
||||
// 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 && !EditorHelpers::IsValidShortcut(std::get<Shortcut>(newKey))))
|
||||
if (originalKey != NULL && !(newKey.index() == 0 && std::get<DWORD>(newKey) == NULL) && !(newKey.index() == 1 && !EditorHelpers::IsValidShortcut(std::get<Shortcut>(newKey))) && !(newKey.index() == 2 && std::get<std::wstring>(newKey).empty()))
|
||||
{
|
||||
singleKeyRemapBuffer.push_back(std::make_pair<RemapBufferItem, std::wstring>(RemapBufferItem{ originalKey, newKey }, L""));
|
||||
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->keyDropDownControlObjects[0]->SetSelectedValue(std::to_wstring(originalKey));
|
||||
@ -120,10 +183,20 @@ void SingleKeyRemapControl::AddNewControlKeyRemapRow(StackPanel& parent, std::ve
|
||||
{
|
||||
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->keyDropDownControlObjects[0]->SetSelectedValue(std::to_wstring(std::get<DWORD>(newKey)));
|
||||
}
|
||||
else
|
||||
else if (newKey.index() == 1)
|
||||
{
|
||||
KeyDropDownControl::AddShortcutToControl(std::get<Shortcut>(newKey), parent, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->hybridDropDownVariableSizedWrapGrid.as<VariableSizedWrapGrid>(), *keyboardManagerState, 1, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->keyDropDownControlObjects, singleKeyRemapBuffer, row, nullptr, true, true);
|
||||
}
|
||||
else if (newKey.index() == 2)
|
||||
{
|
||||
auto& singleKeyRemapControl = keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1];
|
||||
|
||||
const auto& firstLineStackPanel = singleKeyRemapControl->singleKeyRemapControlLayout.as<StackPanel>().Children().GetAt(0).as<StackPanel>();
|
||||
|
||||
firstLineStackPanel.Children().GetAt(0).as<ComboBox>().SelectedIndex(1);
|
||||
|
||||
singleKeyRemapControl->singleKeyRemapControlLayout.as<StackPanel>().Children().GetAt(2).as<TextBox>().Text(std::get<std::wstring>(newKey));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -178,7 +251,7 @@ void SingleKeyRemapControl::AddNewControlKeyRemapRow(StackPanel& parent, std::ve
|
||||
{
|
||||
}
|
||||
singleKeyRemapBuffer.erase(singleKeyRemapBuffer.begin() + rowIndex);
|
||||
|
||||
|
||||
// delete the SingleKeyRemapControl objects so that they get destructed
|
||||
keyboardRemapControlObjects.erase(keyboardRemapControlObjects.begin() + rowIndex);
|
||||
});
|
||||
|
@ -35,6 +35,8 @@ private:
|
||||
// Function to set the accessible names for all the controls in a row
|
||||
static void UpdateAccessibleNames(StackPanel sourceColumn, StackPanel mappedToColumn, Button deleteButton, int rowIndex);
|
||||
|
||||
void TextToMapChangedHandler(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::Controls::TextChangedEventArgs const& e);
|
||||
|
||||
public:
|
||||
// Vector to store dynamically allocated KeyDropDownControl objects to avoid early destruction
|
||||
std::vector<std::unique_ptr<KeyDropDownControl>> keyDropDownControlObjects;
|
||||
@ -52,7 +54,7 @@ public:
|
||||
SingleKeyRemapControl(StackPanel table, StackPanel row, const int colIndex);
|
||||
|
||||
// Function to add a new row to the remap keys table. If the originalKey and newKey args are provided, then the displayed remap keys are set to those values.
|
||||
static void AddNewControlKeyRemapRow(StackPanel& parent, std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>>& keyboardRemapControlObjects, const DWORD originalKey = 0, const KeyShortcutUnion newKey = (DWORD)0);
|
||||
static void AddNewControlKeyRemapRow(StackPanel& parent, std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>>& keyboardRemapControlObjects, const DWORD originalKey = 0, const KeyShortcutTextUnion newKey = (DWORD)0);
|
||||
|
||||
// Function to return the stack panel element of the SingleKeyRemapControl. This is the externally visible UI element which can be used to add it to other layouts
|
||||
winrt::Windows::UI::Xaml::Controls::StackPanel getSingleKeyRemapControl();
|
||||
|
@ -9,13 +9,19 @@ namespace UIHelpers
|
||||
void SetFocusOnTypeButtonInLastRow(StackPanel& parent, long colCount)
|
||||
{
|
||||
// First element in the last row (StackPanel)
|
||||
StackPanel firstElementInLastRow = parent.Children().GetAt(parent.Children().Size() - 1).as<StackPanel>().Children().GetAt(0).as<StackPanel>();
|
||||
auto lastHotKeyLine = parent.Children().GetAt(parent.Children().Size() - 1).as<StackPanel>();
|
||||
|
||||
// Type button is the first child in the StackPanel
|
||||
Button firstTypeButtonInLastRow = firstElementInLastRow.Children().GetAt(0).as<Button>();
|
||||
// Get "To" Column
|
||||
auto toColumn = lastHotKeyLine.Children().GetAt(2).as<StackPanel>();
|
||||
|
||||
// Get first line in "To" Column
|
||||
auto firstLineIntoColumn = toColumn.Children().GetAt(0).as<StackPanel>();
|
||||
|
||||
// Get Type Button from the first line
|
||||
Button typeButton = firstLineIntoColumn.Children().GetAt(1).as<Button>();
|
||||
|
||||
// Set programmatic focus on the button
|
||||
firstTypeButtonInLastRow.Focus(FocusState::Programmatic);
|
||||
typeButton.Focus(FocusState::Programmatic);
|
||||
}
|
||||
|
||||
RECT GetForegroundWindowDesktopRect()
|
||||
@ -68,4 +74,22 @@ namespace UIHelpers
|
||||
|
||||
return boxList;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
std::vector<std::wstring> GetChildrenNames(StackPanel& s)
|
||||
{
|
||||
std::vector<std::wstring> result;
|
||||
for (auto child : s.Children())
|
||||
{
|
||||
std::wstring nameAndClass =
|
||||
child.as<IFrameworkElement>().Name().c_str();
|
||||
|
||||
nameAndClass += L" ";
|
||||
nameAndClass += winrt::get_class_name(child.try_as<winrt::Windows::Foundation::IInspectable>()).c_str();
|
||||
result.push_back(nameAndClass);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -29,4 +29,9 @@ namespace UIHelpers
|
||||
|
||||
// Function to return the list of key name in the order for the drop down based on the key codes
|
||||
winrt::Windows::Foundation::Collections::IVector<winrt::Windows::Foundation::IInspectable> ToBoxValue(const std::vector<std::pair<DWORD, std::wstring>>& list);
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Useful For debugging issues
|
||||
std::vector<std::wstring> GetChildrenNames(StackPanel& s);
|
||||
#endif
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ void Trace::UnregisterProvider() noexcept
|
||||
}
|
||||
|
||||
// Log number of key remaps when the user uses Edit Keyboard and saves settings
|
||||
void Trace::KeyRemapCount(const DWORD keyToKeyCount, const DWORD keyToShortcutCount) noexcept
|
||||
void Trace::KeyRemapCount(const DWORD keyToKeyCount, const DWORD keyToShortcutCount, const DWORD keyToTextCount) noexcept
|
||||
{
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
@ -28,7 +28,8 @@ void Trace::KeyRemapCount(const DWORD keyToKeyCount, const DWORD keyToShortcutCo
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingValue(keyToKeyCount + keyToShortcutCount, "KeyRemapCount"),
|
||||
TraceLoggingValue(keyToKeyCount, "KeyToKeyRemapCount"),
|
||||
TraceLoggingValue(keyToShortcutCount, "KeyToShortcutRemapCount"));
|
||||
TraceLoggingValue(keyToShortcutCount, "KeyToShortcutRemapCount"),
|
||||
TraceLoggingValue(keyToTextCount, "KeyToTextRemapCount"));
|
||||
}
|
||||
|
||||
// Log number of os level shortcut remaps when the user uses Edit Shortcuts and saves settings
|
||||
|
@ -7,14 +7,14 @@ public:
|
||||
static void UnregisterProvider() noexcept;
|
||||
|
||||
// Log number of key remaps when the user uses Edit Keyboard and saves settings
|
||||
static void KeyRemapCount(const DWORD keyToKeyCount, const DWORD keyToShortcutCount) noexcept;
|
||||
static void KeyRemapCount(const DWORD keyToKeyCount, const DWORD keyToShortcutCount, const DWORD keyToTextCount) noexcept;
|
||||
|
||||
// Log number of os level shortcut remaps when the user uses Edit Shortcuts and saves settings
|
||||
static void OSLevelShortcutRemapCount(const DWORD shortcutToShortcutCount, const DWORD shortcutToKeyCount) noexcept;
|
||||
|
||||
// Log number of app specific shortcut remaps when the user uses Edit Shortcuts and saves settings
|
||||
static void AppSpecificShortcutRemapCount(const DWORD shortcutToShortcutCount, const DWORD shortcutToKeyCount) noexcept;
|
||||
|
||||
|
||||
// Log if an error occurs in KBM
|
||||
static void Error(const DWORD errorCode, std::wstring errorMessage, std::wstring methodName) noexcept;
|
||||
};
|
||||
|
@ -7,13 +7,21 @@
|
||||
#include <keyboardmanager/common/Helpers.h>
|
||||
#include <keyboardmanager/KeyboardManagerEngineLibrary/trace.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
bool GeneratedByKBM(const LowlevelKeyboardEvent* data)
|
||||
{
|
||||
return data->lParam->dwExtraInfo & CommonSharedConstants::KEYBOARDMANAGER_INJECTED_FLAG;
|
||||
}
|
||||
}
|
||||
|
||||
namespace KeyboardEventHandlers
|
||||
{
|
||||
// Function to a handle a single key remap
|
||||
intptr_t HandleSingleKeyRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, State& state) noexcept
|
||||
{
|
||||
// Check if the key event was generated by KeyboardManager to avoid remapping events generated by us.
|
||||
if (!(data->lParam->dwExtraInfo & CommonSharedConstants::KEYBOARDMANAGER_INJECTED_FLAG))
|
||||
if (!GeneratedByKBM(data))
|
||||
{
|
||||
const auto remapping = state.GetSingleKeyRemap(data->lParam->vkCode);
|
||||
if (remapping)
|
||||
@ -21,7 +29,7 @@ namespace KeyboardEventHandlers
|
||||
auto it = remapping.value();
|
||||
|
||||
// Check if the remap is to a key or a shortcut
|
||||
bool remapToKey = (it->second.index() == 0);
|
||||
const bool remapToKey = it->second.index() == 0;
|
||||
|
||||
// If mapped to VK_DISABLED then the key is disabled
|
||||
if (remapToKey)
|
||||
@ -191,7 +199,9 @@ namespace KeyboardEventHandlers
|
||||
}
|
||||
|
||||
// Check if the remap is to a key or a shortcut
|
||||
bool remapToShortcut = (it->second.targetShortcut.index() == 1);
|
||||
const bool remapToKey = it->second.targetShortcut.index() == 0;
|
||||
const bool remapToShortcut = it->second.targetShortcut.index() == 1;
|
||||
const bool remapToText = it->second.targetShortcut.index() == 2;
|
||||
|
||||
const size_t src_size = it->first.Size();
|
||||
const size_t dest_size = remapToShortcut ? std::get<Shortcut>(it->second.targetShortcut).Size() : 1;
|
||||
@ -202,13 +212,13 @@ namespace KeyboardEventHandlers
|
||||
if (data->lParam->vkCode == it->first.GetActionKey() && (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN))
|
||||
{
|
||||
// Check if any other keys have been pressed apart from the shortcut. If true, then check for the next shortcut. This is to be done only for shortcut to shortcut remaps
|
||||
if (!it->first.IsKeyboardStateClearExceptShortcut(ii) && (remapToShortcut || std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED))
|
||||
if (!it->first.IsKeyboardStateClearExceptShortcut(ii) && (remapToShortcut || (remapToKey && std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t key_count;
|
||||
LPINPUT keyEventList;
|
||||
size_t key_count = 0;
|
||||
LPINPUT keyEventList = nullptr;
|
||||
|
||||
// Remember which win key was pressed initially
|
||||
if (ii.GetVirtualKeyState(VK_RWIN))
|
||||
@ -265,7 +275,7 @@ namespace KeyboardEventHandlers
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (remapToKey)
|
||||
{
|
||||
// Dummy key, key up for all the original shortcut modifier keys and key down for remapped key
|
||||
key_count = KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE + (src_size - 1) + dest_size;
|
||||
@ -300,6 +310,33 @@ namespace KeyboardEventHandlers
|
||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, static_cast<WORD>(Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut))), data->lParam->vkCode);
|
||||
}
|
||||
}
|
||||
// Remapped to text
|
||||
else
|
||||
{
|
||||
key_count = KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE + src_size;
|
||||
|
||||
auto remapping = std::get<std::wstring>(it->second.targetShortcut);
|
||||
const UINT inputEventCount = static_cast<UINT>(remapping.length() * 2);
|
||||
key_count += inputEventCount;
|
||||
|
||||
keyEventList = new INPUT[key_count]{};
|
||||
|
||||
int i = 0;
|
||||
Helpers::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
|
||||
// Release original shortcut state (release in reverse order of shortcut to be accurate)
|
||||
Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
|
||||
for (size_t idx = 0; idx < inputEventCount; ++idx)
|
||||
{
|
||||
auto& input = keyEventList[idx + i];
|
||||
input.type = INPUT_KEYBOARD;
|
||||
const bool upEvent = idx & 0x1;
|
||||
input.ki.dwFlags = KEYEVENTF_UNICODE | (upEvent ? KEYEVENTF_KEYUP : 0);
|
||||
input.ki.dwExtraInfo = KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG;
|
||||
input.ki.wScan = remapping[idx >> 1];
|
||||
}
|
||||
}
|
||||
|
||||
it->second.isShortcutInvoked = true;
|
||||
// If app specific shortcut is invoked, store the target application
|
||||
@ -332,8 +369,8 @@ namespace KeyboardEventHandlers
|
||||
if ((it->first.CheckWinKey(data->lParam->vkCode) || it->first.CheckCtrlKey(data->lParam->vkCode) || it->first.CheckAltKey(data->lParam->vkCode) || it->first.CheckShiftKey(data->lParam->vkCode)) && (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP))
|
||||
{
|
||||
// Release new shortcut, and set original shortcut keys except the one released
|
||||
size_t key_count;
|
||||
LPINPUT keyEventList;
|
||||
size_t key_count = 0;
|
||||
LPINPUT keyEventList = nullptr;
|
||||
if (remapToShortcut)
|
||||
{
|
||||
// if the released key is present in both shortcuts' modifiers (i.e part of the common modifiers)
|
||||
@ -373,7 +410,7 @@ namespace KeyboardEventHandlers
|
||||
// Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+Ctrl+A->Ctrl+V, press Win+Ctrl+A and release A then Ctrl, since Win will be pressed here we need to send a dummy event after it
|
||||
Helpers::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
}
|
||||
else
|
||||
else if (remapToKey)
|
||||
{
|
||||
// 1 for releasing new key and original shortcut modifiers except the one released and dummy key
|
||||
key_count = dest_size + src_size - 2 + KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE;
|
||||
@ -432,13 +469,13 @@ namespace KeyboardEventHandlers
|
||||
}
|
||||
|
||||
// The system will see the modifiers of the new shortcut as being held down because of the shortcut remap
|
||||
if (!remapToShortcut || std::get<Shortcut>(it->second.targetShortcut).CheckModifiersKeyboardState(ii))
|
||||
if (!remapToShortcut || (remapToShortcut && std::get<Shortcut>(it->second.targetShortcut).CheckModifiersKeyboardState(ii)))
|
||||
{
|
||||
// Case 2: If the original shortcut is still held down the keyboard will get a key down message of the action key in the original shortcut and the new shortcut's modifiers will be held down (keys held down send repeated keydown messages)
|
||||
if (data->lParam->vkCode == it->first.GetActionKey() && (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN))
|
||||
{
|
||||
// In case of mapping to disable do not send anything
|
||||
if (!remapToShortcut && std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)
|
||||
if (remapToKey && std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)
|
||||
{
|
||||
// Since the original shortcut's action key is pressed, set it to true
|
||||
it->second.isOriginalActionKeyPressed = true;
|
||||
@ -446,15 +483,34 @@ namespace KeyboardEventHandlers
|
||||
}
|
||||
|
||||
size_t key_count = 1;
|
||||
LPINPUT keyEventList = new INPUT[key_count]{};
|
||||
LPINPUT keyEventList = nullptr;
|
||||
if (remapToShortcut)
|
||||
{
|
||||
keyEventList = new INPUT[key_count]{};
|
||||
Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
}
|
||||
else
|
||||
else if (remapToKey)
|
||||
{
|
||||
keyEventList = new INPUT[key_count]{};
|
||||
Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, static_cast<WORD>(Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut))), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
}
|
||||
else if (remapToText)
|
||||
{
|
||||
auto remapping = std::get<std::wstring>(it->second.targetShortcut);
|
||||
const UINT inputEventCount = static_cast<UINT>(remapping.length() * 2);
|
||||
key_count = inputEventCount;
|
||||
keyEventList = new INPUT[key_count]{};
|
||||
|
||||
for (size_t idx = 0; idx < inputEventCount; ++idx)
|
||||
{
|
||||
auto& input = keyEventList[idx];
|
||||
input.type = INPUT_KEYBOARD;
|
||||
const bool upEvent = idx & 0x1;
|
||||
input.ki.dwFlags = KEYEVENTF_UNICODE | (upEvent ? KEYEVENTF_KEYUP : 0);
|
||||
input.ki.dwExtraInfo = KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG;
|
||||
input.ki.wScan = remapping[idx >> 1];
|
||||
}
|
||||
}
|
||||
|
||||
UINT res = ii.SendVirtualInput(static_cast<UINT>(key_count), keyEventList, sizeof(INPUT));
|
||||
delete[] keyEventList;
|
||||
@ -462,10 +518,10 @@ namespace KeyboardEventHandlers
|
||||
}
|
||||
|
||||
// Case 3: If the action key is released from the original shortcut, keep modifiers of the new shortcut until some other key event which doesn't apply to the original shortcut
|
||||
if (data->lParam->vkCode == it->first.GetActionKey() && (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP))
|
||||
if (!remapToText && data->lParam->vkCode == it->first.GetActionKey() && (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP))
|
||||
{
|
||||
size_t key_count = 1;
|
||||
LPINPUT keyEventList;
|
||||
LPINPUT keyEventList = nullptr;
|
||||
if (remapToShortcut)
|
||||
{
|
||||
keyEventList = new INPUT[key_count]{};
|
||||
@ -683,16 +739,22 @@ namespace KeyboardEventHandlers
|
||||
// For remap to key, if the original action key is not currently pressed, we should revert the keyboard state to the physical keys. If it is pressed we should not suppress the event so that shortcut to key remaps can be pressed with other keys. Example use-case: Alt+D->Win, allows Alt+D+A to perform Win+A
|
||||
|
||||
// Modifier state reset might be required for this key depending on the target key - ex: Ctrl+A -> Caps, Shift is pressed. System should not see Shift and Caps pressed together
|
||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, data->lParam->vkCode, Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)));
|
||||
|
||||
auto maybeTargetKey = std::get_if<DWORD>(&it->second.targetShortcut);
|
||||
|
||||
if (maybeTargetKey)
|
||||
{
|
||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, data->lParam->vkCode, Helpers::FilterArtificialKeys(*maybeTargetKey));
|
||||
}
|
||||
|
||||
// If the shortcut is remapped to Disable then we have to revert the keyboard state to the physical keys
|
||||
bool isRemapToDisable = (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED);
|
||||
bool isRemapToDisable = maybeTargetKey && (*maybeTargetKey == CommonSharedConstants::VK_DISABLED);
|
||||
bool isOriginalActionKeyPressed = false;
|
||||
|
||||
if (!isRemapToDisable)
|
||||
if (maybeTargetKey && !isRemapToDisable)
|
||||
{
|
||||
// If the remap target key is currently pressed, then we do not have to revert the keyboard state to the physical keys
|
||||
if (ii.GetVirtualKeyState((Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)))))
|
||||
if (ii.GetVirtualKeyState((Helpers::FilterArtificialKeys(*maybeTargetKey))))
|
||||
{
|
||||
isOriginalActionKeyPressed = true;
|
||||
}
|
||||
@ -850,4 +912,43 @@ namespace KeyboardEventHandlers
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function to generate a unicode string in response to a single keypress
|
||||
intptr_t HandleSingleKeyToTextRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, State& state)
|
||||
{
|
||||
if (GeneratedByKBM(data))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Only send the text on keydown event
|
||||
if (data->wParam != WM_KEYDOWN)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto remapping = state.GetSingleKeyToTextRemapEvent(data->lParam->vkCode);
|
||||
if (!remapping)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const size_t keyCount = remapping->length();
|
||||
const UINT eventCount = static_cast<UINT>(keyCount * 2);
|
||||
auto keyEventList = std::make_unique<INPUT[]>(keyCount * 2);
|
||||
|
||||
for (size_t i = 0; i < eventCount; ++i)
|
||||
{
|
||||
auto& input = keyEventList[i];
|
||||
input.type = INPUT_KEYBOARD;
|
||||
const bool upEvent = i & 0x1;
|
||||
input.ki.dwFlags = KEYEVENTF_UNICODE | (upEvent ? KEYEVENTF_KEYUP : 0);
|
||||
input.ki.dwExtraInfo = KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG;
|
||||
input.ki.wScan = (*remapping)[i >> 1];
|
||||
}
|
||||
|
||||
UINT res = ii.SendVirtualInput(eventCount, keyEventList.get(), sizeof(INPUT));
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,9 @@ namespace KeyboardEventHandlers
|
||||
// Function to a handle an app-specific shortcut remap
|
||||
intptr_t HandleAppSpecificShortcutRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, State& state) noexcept;
|
||||
|
||||
// Function to generate a unicode string in response to a single keypress
|
||||
intptr_t HandleSingleKeyToTextRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, State& state);
|
||||
|
||||
// Function to ensure Ctrl/Shift/Alt modifier key state is not detected as pressed down by applications which detect keys at a lower level than hooks when it is remapped for scenarios where its required
|
||||
void ResetIfModifierKeyForLowerLevelKeyHandlers(KeyboardManagerInput::InputInterface& ii, DWORD key, DWORD target);
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "pch.h"
|
||||
#include "pch.h"
|
||||
#include "KeyboardManager.h"
|
||||
#include <interface/powertoy_module_interface.h>
|
||||
#include <common/SettingsAPI/settings_objects.h>
|
||||
@ -68,12 +68,13 @@ void KeyboardManager::LoadSettings()
|
||||
|
||||
LRESULT CALLBACK KeyboardManager::HookProc(int nCode, const WPARAM wParam, const LPARAM lParam)
|
||||
{
|
||||
LowlevelKeyboardEvent event;
|
||||
LowlevelKeyboardEvent event{};
|
||||
if (nCode == HC_ACTION)
|
||||
{
|
||||
event.lParam = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
|
||||
event.wParam = wParam;
|
||||
event.lParam->vkCode = Helpers::EncodeKeyNumpadOrigin(event.lParam->vkCode, event.lParam->flags & LLKHF_EXTENDED);
|
||||
|
||||
if (keyboardManagerObjectPtr->HandleKeyboardHookEvent(&event) == 1)
|
||||
{
|
||||
// Reset Num Lock whenever a NumLock key down event is suppressed since Num Lock key state change occurs before it is intercepted by low level hooks
|
||||
@ -162,6 +163,13 @@ intptr_t KeyboardManager::HandleKeyboardHookEvent(LowlevelKeyboardEvent* data) n
|
||||
return 1;
|
||||
}
|
||||
|
||||
intptr_t SingleKeyToTextRemapResult = KeyboardEventHandlers::HandleSingleKeyToTextRemapEvent(inputHandler, data, state);
|
||||
|
||||
if (SingleKeyToTextRemapResult == 1)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Handle an os-level shortcut remapping
|
||||
return KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent(inputHandler, data, state);
|
||||
}
|
||||
|
@ -14,6 +14,18 @@ std::optional<SingleKeyRemapTable::iterator> State::GetSingleKeyRemap(const DWOR
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::wstring> State::GetSingleKeyToTextRemapEvent(const DWORD originalKey) const
|
||||
{
|
||||
if (auto it = singleKeyToTextReMap.find(originalKey); it != end(singleKeyToTextReMap))
|
||||
{
|
||||
return std::get<std::wstring>(it->second);
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
bool State::CheckShortcutRemapInvoked(const std::optional<std::wstring>& appName)
|
||||
{
|
||||
// Assumes appName exists in the app-specific remap table
|
||||
|
@ -11,6 +11,9 @@ public:
|
||||
// Function to get the iterator of a single key remap given the source key. Returns nullopt if it isn't remapped
|
||||
std::optional<SingleKeyRemapTable::iterator> GetSingleKeyRemap(const DWORD& originalKey);
|
||||
|
||||
// Function to get a unicode string remap given the source key. Returns nullopt if it isn't remapped
|
||||
std::optional<std::wstring> GetSingleKeyToTextRemapEvent(const DWORD originalKey) const;
|
||||
|
||||
bool CheckShortcutRemapInvoked(const std::optional<std::wstring>& appName);
|
||||
|
||||
// Function to get the source and target of a shortcut remap given the source shortcut. Returns nullopt if it isn't remapped
|
||||
|
@ -3,9 +3,9 @@
|
||||
namespace KeyboardManagerConstants
|
||||
{
|
||||
// Event name for signaling settings changes
|
||||
inline const std::wstring SettingsEventName = L"PowerToys_KeyboardManager_Event_Settings";
|
||||
inline const std::wstring SettingsEventName = L"PowerToys_KeyboardManager_Event_Settings";
|
||||
|
||||
inline const std::wstring EditorWindowEventName = L"PowerToys_KeyboardManager_Event_EditorWindow";
|
||||
inline const std::wstring EditorWindowEventName = L"PowerToys_KeyboardManager_Event_EditorWindow";
|
||||
|
||||
// Name of the powertoy module.
|
||||
inline const std::wstring ModuleName = L"Keyboard Manager";
|
||||
@ -16,12 +16,18 @@ namespace KeyboardManagerConstants
|
||||
// Name of the property use to store single keyremaps.
|
||||
inline const std::wstring RemapKeysSettingName = L"remapKeys";
|
||||
|
||||
// Name of the property use to store single to text keyremaps.
|
||||
inline const std::wstring RemapKeysToTextSettingName = L"remapKeysToText";
|
||||
|
||||
// Name of the property use to store single keyremaps array in case of in process approach.
|
||||
inline const std::wstring InProcessRemapKeysSettingName = L"inProcess";
|
||||
|
||||
// Name of the property use to store shortcut remaps.
|
||||
inline const std::wstring RemapShortcutsSettingName = L"remapShortcuts";
|
||||
|
||||
// Name of the property use to store shortcut to text remaps.
|
||||
inline const std::wstring RemapShortcutsToTextSettingName = L"remapShortcutsToText";
|
||||
|
||||
// Name of the property use to store global shortcut remaps array.
|
||||
inline const std::wstring GlobalRemapShortcutsSettingName = L"global";
|
||||
|
||||
@ -34,6 +40,9 @@ namespace KeyboardManagerConstants
|
||||
// Name of the property use to store new remap keys.
|
||||
inline const std::wstring NewRemapKeysSettingName = L"newRemapKeys";
|
||||
|
||||
// Name of the property use to store new remapped string.
|
||||
inline const std::wstring NewTextSettingName = L"unicodeText";
|
||||
|
||||
// Name of the property use to store the target application.
|
||||
inline const std::wstring TargetAppSettingName = L"targetApp";
|
||||
|
||||
@ -44,7 +53,6 @@ namespace KeyboardManagerConstants
|
||||
inline const int MinimumEditKeyboardWindowWidth = 200;
|
||||
inline const int MinimumEditKeyboardWindowHeight = 200;
|
||||
|
||||
|
||||
// Flags used for distinguishing key events sent by Keyboard Manager
|
||||
inline const ULONG_PTR KEYBOARDMANAGER_SINGLEKEY_FLAG = 0x11; // Single key remaps
|
||||
inline const ULONG_PTR KEYBOARDMANAGER_SHORTCUT_FLAG = 0x101; // Shortcut remaps
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "pch.h"
|
||||
#include "pch.h"
|
||||
#include "MappingConfiguration.h"
|
||||
|
||||
#include <common/SettingsAPI/settings_objects.h>
|
||||
@ -17,13 +17,18 @@ void MappingConfiguration::ClearOSLevelShortcuts()
|
||||
osLevelShortcutReMapSortedKeys.clear();
|
||||
}
|
||||
|
||||
|
||||
// Function to clear the Keys remapping table.
|
||||
void MappingConfiguration::ClearSingleKeyRemaps()
|
||||
{
|
||||
singleKeyReMap.clear();
|
||||
}
|
||||
|
||||
// Function to clear the Keys remapping table.
|
||||
void MappingConfiguration::ClearSingleKeyToTextRemaps()
|
||||
{
|
||||
singleKeyToTextReMap.clear();
|
||||
}
|
||||
|
||||
// Function to clear the App specific shortcut remapping table
|
||||
void MappingConfiguration::ClearAppSpecificShortcuts()
|
||||
{
|
||||
@ -32,7 +37,7 @@ void MappingConfiguration::ClearAppSpecificShortcuts()
|
||||
}
|
||||
|
||||
// Function to add a new OS level shortcut remapping
|
||||
bool MappingConfiguration::AddOSLevelShortcut(const Shortcut& originalSC, const KeyShortcutUnion& newSC)
|
||||
bool MappingConfiguration::AddOSLevelShortcut(const Shortcut& originalSC, const KeyShortcutTextUnion& newSC)
|
||||
{
|
||||
// Check if the shortcut is already remapped
|
||||
auto it = osLevelShortcutReMap.find(originalSC);
|
||||
@ -49,7 +54,7 @@ bool MappingConfiguration::AddOSLevelShortcut(const Shortcut& originalSC, const
|
||||
}
|
||||
|
||||
// Function to add a new single key to key/shortcut remapping
|
||||
bool MappingConfiguration::AddSingleKeyRemap(const DWORD& originalKey, const KeyShortcutUnion& newRemapKey)
|
||||
bool MappingConfiguration::AddSingleKeyRemap(const DWORD& originalKey, const KeyShortcutTextUnion& newRemapKey)
|
||||
{
|
||||
// Check if the key is already remapped
|
||||
auto it = singleKeyReMap.find(originalKey);
|
||||
@ -62,8 +67,21 @@ bool MappingConfiguration::AddSingleKeyRemap(const DWORD& originalKey, const Key
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MappingConfiguration::AddSingleKeyToTextRemap(const DWORD originalKey, const std::wstring& text)
|
||||
{
|
||||
if (auto it = singleKeyToTextReMap.find(originalKey); it != end(singleKeyToTextReMap))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
singleKeyToTextReMap[originalKey] = text;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to add a new App specific shortcut remapping
|
||||
bool MappingConfiguration::AddAppSpecificShortcut(const std::wstring& app, const Shortcut& originalSC, const KeyShortcutUnion& newSC)
|
||||
bool MappingConfiguration::AddAppSpecificShortcut(const std::wstring& app, const Shortcut& originalSC, const KeyShortcutTextUnion& newSC)
|
||||
{
|
||||
// Convert app name to lower case
|
||||
std::wstring process_name;
|
||||
@ -92,7 +110,6 @@ bool MappingConfiguration::AddAppSpecificShortcut(const std::wstring& app, const
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool MappingConfiguration::LoadSingleKeyRemaps(const json::JsonObject& jsonData)
|
||||
{
|
||||
bool result = true;
|
||||
@ -141,6 +158,45 @@ bool MappingConfiguration::LoadSingleKeyRemaps(const json::JsonObject& jsonData)
|
||||
return result;
|
||||
}
|
||||
|
||||
bool MappingConfiguration::LoadSingleKeyToTextRemaps(const json::JsonObject& jsonData)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
try
|
||||
{
|
||||
auto remapKeysData = jsonData.GetNamedObject(KeyboardManagerConstants::RemapKeysToTextSettingName);
|
||||
ClearSingleKeyToTextRemaps();
|
||||
|
||||
if (!remapKeysData)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
auto inProcessRemapKeys = remapKeysData.GetNamedArray(KeyboardManagerConstants::InProcessRemapKeysSettingName);
|
||||
for (const auto& it : inProcessRemapKeys)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto originalKey = it.GetObjectW().GetNamedString(KeyboardManagerConstants::OriginalKeysSettingName);
|
||||
auto newText = it.GetObjectW().GetNamedString(KeyboardManagerConstants::NewTextSettingName);
|
||||
AddSingleKeyToTextRemap(std::stoul(originalKey.c_str()), newText.c_str());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Logger::error(L"Improper Key Data JSON. Try the next remap.");
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Logger::error(L"Improper JSON format for single key to text remaps. Skip to next remap type");
|
||||
result = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool MappingConfiguration::LoadAppSpecificShortcutRemaps(const json::JsonObject& remapShortcutsData)
|
||||
{
|
||||
bool result = true;
|
||||
@ -153,19 +209,27 @@ bool MappingConfiguration::LoadAppSpecificShortcutRemaps(const json::JsonObject&
|
||||
try
|
||||
{
|
||||
auto originalKeys = it.GetObjectW().GetNamedString(KeyboardManagerConstants::OriginalKeysSettingName);
|
||||
auto newRemapKeys = it.GetObjectW().GetNamedString(KeyboardManagerConstants::NewRemapKeysSettingName);
|
||||
auto newRemapKeys = it.GetObjectW().GetNamedString(KeyboardManagerConstants::NewRemapKeysSettingName, {});
|
||||
auto newRemapText = it.GetObjectW().GetNamedString(KeyboardManagerConstants::NewTextSettingName, {});
|
||||
auto targetApp = it.GetObjectW().GetNamedString(KeyboardManagerConstants::TargetAppSettingName);
|
||||
|
||||
// If remapped to a shortcut
|
||||
if (std::wstring(newRemapKeys).find(L";") != std::string::npos)
|
||||
if (!newRemapKeys.empty())
|
||||
{
|
||||
AddAppSpecificShortcut(targetApp.c_str(), Shortcut(originalKeys.c_str()), Shortcut(newRemapKeys.c_str()));
|
||||
}
|
||||
// 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()));
|
||||
}
|
||||
|
||||
// If remapped to a key
|
||||
// If remapped to a key
|
||||
else
|
||||
{
|
||||
AddAppSpecificShortcut(targetApp.c_str(), Shortcut(originalKeys.c_str()), std::stoul(newRemapKeys.c_str()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddAppSpecificShortcut(targetApp.c_str(), Shortcut(originalKeys.c_str()), std::stoul(newRemapKeys.c_str()));
|
||||
AddAppSpecificShortcut(targetApp.c_str(), Shortcut(originalKeys.c_str()), newRemapText.c_str());
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
@ -184,15 +248,13 @@ bool MappingConfiguration::LoadAppSpecificShortcutRemaps(const json::JsonObject&
|
||||
return result;
|
||||
}
|
||||
|
||||
bool MappingConfiguration::LoadShortcutRemaps(const json::JsonObject& jsonData)
|
||||
bool MappingConfiguration::LoadShortcutRemaps(const json::JsonObject& jsonData, const std::wstring& objectName)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
try
|
||||
{
|
||||
auto remapShortcutsData = jsonData.GetNamedObject(KeyboardManagerConstants::RemapShortcutsSettingName);
|
||||
ClearOSLevelShortcuts();
|
||||
ClearAppSpecificShortcuts();
|
||||
auto remapShortcutsData = jsonData.GetNamedObject(objectName);
|
||||
if (remapShortcutsData)
|
||||
{
|
||||
// Load os level shortcut remaps
|
||||
@ -204,18 +266,25 @@ bool MappingConfiguration::LoadShortcutRemaps(const json::JsonObject& jsonData)
|
||||
try
|
||||
{
|
||||
auto originalKeys = it.GetObjectW().GetNamedString(KeyboardManagerConstants::OriginalKeysSettingName);
|
||||
auto newRemapKeys = it.GetObjectW().GetNamedString(KeyboardManagerConstants::NewRemapKeysSettingName);
|
||||
auto newRemapKeys = it.GetObjectW().GetNamedString(KeyboardManagerConstants::NewRemapKeysSettingName, {});
|
||||
auto newRemapText = it.GetObjectW().GetNamedString(KeyboardManagerConstants::NewTextSettingName, {});
|
||||
|
||||
// If remapped to a shortcut
|
||||
if (std::wstring(newRemapKeys).find(L";") != std::string::npos)
|
||||
if (!newRemapKeys.empty())
|
||||
{
|
||||
AddOSLevelShortcut(Shortcut(originalKeys.c_str()), Shortcut(newRemapKeys.c_str()));
|
||||
// If remapped to a shortcut
|
||||
if (std::wstring(newRemapKeys).find(L";") != std::string::npos)
|
||||
{
|
||||
AddOSLevelShortcut(Shortcut(originalKeys.c_str()), Shortcut(newRemapKeys.c_str()));
|
||||
}
|
||||
// If remapped to a key
|
||||
else
|
||||
{
|
||||
AddOSLevelShortcut(Shortcut(originalKeys.c_str()), std::stoul(newRemapKeys.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
// If remapped to a key
|
||||
else
|
||||
{
|
||||
AddOSLevelShortcut(Shortcut(originalKeys.c_str()), std::stoul(newRemapKeys.c_str()));
|
||||
AddOSLevelShortcut(Shortcut(originalKeys.c_str()), newRemapText.c_str());
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
@ -244,10 +313,6 @@ bool MappingConfiguration::LoadShortcutRemaps(const json::JsonObject& jsonData)
|
||||
return result;
|
||||
}
|
||||
|
||||
MappingConfiguration::MappingConfiguration()
|
||||
{
|
||||
}
|
||||
|
||||
bool MappingConfiguration::LoadSettings()
|
||||
{
|
||||
Logger::trace(L"SettingsHelper::LoadSettings()");
|
||||
@ -271,7 +336,11 @@ bool MappingConfiguration::LoadSettings()
|
||||
}
|
||||
|
||||
bool result = LoadSingleKeyRemaps(*configFile);
|
||||
result = result && LoadShortcutRemaps(*configFile);
|
||||
ClearOSLevelShortcuts();
|
||||
ClearAppSpecificShortcuts();
|
||||
result = LoadShortcutRemaps(*configFile, KeyboardManagerConstants::RemapShortcutsSettingName) && result;
|
||||
result = LoadShortcutRemaps(*configFile, KeyboardManagerConstants::RemapShortcutsToTextSettingName) && result;
|
||||
result = LoadSingleKeyToTextRemaps(*configFile) && result;
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -289,10 +358,20 @@ bool MappingConfiguration::SaveSettingsToFile()
|
||||
bool result = true;
|
||||
json::JsonObject configJson;
|
||||
json::JsonObject remapShortcuts;
|
||||
json::JsonObject remapShortcutsToText;
|
||||
|
||||
json::JsonObject remapKeys;
|
||||
json::JsonObject remapKeysToText;
|
||||
|
||||
json::JsonArray inProcessRemapKeysArray;
|
||||
json::JsonArray inProcessRemapKeysToTextArray;
|
||||
|
||||
json::JsonArray appSpecificRemapShortcutsArray;
|
||||
json::JsonArray appSpecificRemapShortcutsToTextArray;
|
||||
|
||||
json::JsonArray globalRemapShortcutsArray;
|
||||
json::JsonArray globalRemapShortcutsToTextArray;
|
||||
|
||||
for (const auto& it : singleKeyReMap)
|
||||
{
|
||||
json::JsonObject keys;
|
||||
@ -313,24 +392,43 @@ bool MappingConfiguration::SaveSettingsToFile()
|
||||
inProcessRemapKeysArray.Append(keys);
|
||||
}
|
||||
|
||||
for (const auto& [code, text] : singleKeyToTextReMap)
|
||||
{
|
||||
json::JsonObject keys;
|
||||
keys.SetNamedValue(KeyboardManagerConstants::OriginalKeysSettingName, json::value(winrt::to_hstring(static_cast<unsigned int>(code))));
|
||||
keys.SetNamedValue(KeyboardManagerConstants::NewTextSettingName, json::value(std::get<std::wstring>(text)));
|
||||
inProcessRemapKeysToTextArray.Append(keys);
|
||||
}
|
||||
|
||||
for (const auto& it : osLevelShortcutReMap)
|
||||
{
|
||||
json::JsonObject keys;
|
||||
|
||||
keys.SetNamedValue(KeyboardManagerConstants::OriginalKeysSettingName, json::value(it.first.ToHstringVK()));
|
||||
|
||||
bool remapToText = false;
|
||||
|
||||
// For shortcut to key remapping
|
||||
if (it.second.targetShortcut.index() == 0)
|
||||
{
|
||||
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(winrt::to_hstring((unsigned int)std::get<DWORD>(it.second.targetShortcut))));
|
||||
}
|
||||
|
||||
// For shortcut to shortcut remapping
|
||||
else
|
||||
else if (it.second.targetShortcut.index() == 1)
|
||||
{
|
||||
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(std::get<Shortcut>(it.second.targetShortcut).ToHstringVK()));
|
||||
}
|
||||
// For shortcut to text remapping
|
||||
else if (it.second.targetShortcut.index() == 2)
|
||||
{
|
||||
remapToText = true;
|
||||
keys.SetNamedValue(KeyboardManagerConstants::NewTextSettingName, json::value(std::get<std::wstring>(it.second.targetShortcut)));
|
||||
}
|
||||
|
||||
globalRemapShortcutsArray.Append(keys);
|
||||
if (!remapToText)
|
||||
globalRemapShortcutsArray.Append(keys);
|
||||
else
|
||||
globalRemapShortcutsToTextArray.Append(keys);
|
||||
}
|
||||
|
||||
for (const auto& itApp : appSpecificShortcutReMap)
|
||||
@ -341,6 +439,8 @@ bool MappingConfiguration::SaveSettingsToFile()
|
||||
json::JsonObject keys;
|
||||
keys.SetNamedValue(KeyboardManagerConstants::OriginalKeysSettingName, json::value(itKeys.first.ToHstringVK()));
|
||||
|
||||
bool remapToText = false;
|
||||
|
||||
// For shortcut to key remapping
|
||||
if (itKeys.second.targetShortcut.index() == 0)
|
||||
{
|
||||
@ -348,22 +448,37 @@ bool MappingConfiguration::SaveSettingsToFile()
|
||||
}
|
||||
|
||||
// For shortcut to shortcut remapping
|
||||
else
|
||||
else if (itKeys.second.targetShortcut.index() == 1)
|
||||
{
|
||||
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(std::get<Shortcut>(itKeys.second.targetShortcut).ToHstringVK()));
|
||||
}
|
||||
else if (itKeys.second.targetShortcut.index() == 2)
|
||||
{
|
||||
keys.SetNamedValue(KeyboardManagerConstants::NewTextSettingName, json::value(std::get<std::wstring>(itKeys.second.targetShortcut)));
|
||||
remapToText = true;
|
||||
}
|
||||
|
||||
keys.SetNamedValue(KeyboardManagerConstants::TargetAppSettingName, json::value(itApp.first));
|
||||
|
||||
appSpecificRemapShortcutsArray.Append(keys);
|
||||
if (!remapToText)
|
||||
appSpecificRemapShortcutsArray.Append(keys);
|
||||
else
|
||||
appSpecificRemapShortcutsToTextArray.Append(keys);
|
||||
}
|
||||
}
|
||||
|
||||
remapShortcuts.SetNamedValue(KeyboardManagerConstants::GlobalRemapShortcutsSettingName, globalRemapShortcutsArray);
|
||||
remapShortcuts.SetNamedValue(KeyboardManagerConstants::AppSpecificRemapShortcutsSettingName, appSpecificRemapShortcutsArray);
|
||||
|
||||
remapShortcutsToText.SetNamedValue(KeyboardManagerConstants::GlobalRemapShortcutsSettingName, globalRemapShortcutsToTextArray);
|
||||
remapShortcutsToText.SetNamedValue(KeyboardManagerConstants::AppSpecificRemapShortcutsSettingName, appSpecificRemapShortcutsToTextArray);
|
||||
|
||||
remapKeys.SetNamedValue(KeyboardManagerConstants::InProcessRemapKeysSettingName, inProcessRemapKeysArray);
|
||||
remapKeysToText.SetNamedValue(KeyboardManagerConstants::InProcessRemapKeysSettingName, inProcessRemapKeysToTextArray);
|
||||
configJson.SetNamedValue(KeyboardManagerConstants::RemapKeysSettingName, remapKeys);
|
||||
configJson.SetNamedValue(KeyboardManagerConstants::RemapKeysToTextSettingName, remapKeysToText);
|
||||
configJson.SetNamedValue(KeyboardManagerConstants::RemapShortcutsSettingName, remapShortcuts);
|
||||
configJson.SetNamedValue(KeyboardManagerConstants::RemapShortcutsToTextSettingName, remapShortcutsToText);
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -6,15 +6,14 @@
|
||||
#include <keyboardmanager/common/Shortcut.h>
|
||||
#include <keyboardmanager/common/RemapShortcut.h>
|
||||
|
||||
using SingleKeyRemapTable = std::unordered_map<DWORD, KeyShortcutUnion>;
|
||||
using SingleKeyRemapTable = std::unordered_map<DWORD, KeyShortcutTextUnion>;
|
||||
using SingleKeyToTextRemapTable = SingleKeyRemapTable;
|
||||
using ShortcutRemapTable = std::map<Shortcut, RemapShortcut>;
|
||||
using AppSpecificShortcutRemapTable = std::map<std::wstring, ShortcutRemapTable>;
|
||||
|
||||
class MappingConfiguration
|
||||
{
|
||||
public:
|
||||
MappingConfiguration();
|
||||
|
||||
~MappingConfiguration() = default;
|
||||
|
||||
// Load the configuration.
|
||||
@ -29,22 +28,31 @@ public:
|
||||
// Function to clear the Keys remapping table
|
||||
void ClearSingleKeyRemaps();
|
||||
|
||||
// Function to clear the Keys to text remapping table
|
||||
void ClearSingleKeyToTextRemaps();
|
||||
|
||||
// Function to clear the App specific shortcut remapping table
|
||||
void ClearAppSpecificShortcuts();
|
||||
|
||||
// Function to add a new single key to key remapping
|
||||
bool AddSingleKeyRemap(const DWORD& originalKey, const KeyShortcutUnion& newRemapKey);
|
||||
bool AddSingleKeyRemap(const DWORD& originalKey, const KeyShortcutTextUnion& newRemapKey);
|
||||
|
||||
// Function to add a new single key to unicode string remapping
|
||||
bool AddSingleKeyToTextRemap(const DWORD originalKey, const std::wstring& text);
|
||||
|
||||
// Function to add a new OS level shortcut remapping
|
||||
bool AddOSLevelShortcut(const Shortcut& originalSC, const KeyShortcutUnion& newSC);
|
||||
bool AddOSLevelShortcut(const Shortcut& originalSC, const KeyShortcutTextUnion& newSC);
|
||||
|
||||
// Function to add a new App specific level shortcut remapping
|
||||
bool AddAppSpecificShortcut(const std::wstring& app, const Shortcut& originalSC, const KeyShortcutUnion& newSC);
|
||||
bool AddAppSpecificShortcut(const std::wstring& app, const Shortcut& originalSC, const KeyShortcutTextUnion& newSC);
|
||||
|
||||
// The map members and their mutexes are left as public since the maps are used extensively in dllmain.cpp.
|
||||
// Maps which store the remappings for each of the features. The bool fields should be initialized to false. They are used to check the current state of the shortcut (i.e is that particular shortcut currently pressed down or not).
|
||||
// Stores single key remappings
|
||||
std::unordered_map<DWORD, KeyShortcutUnion> singleKeyReMap;
|
||||
SingleKeyRemapTable singleKeyReMap;
|
||||
|
||||
// Stores single key to text remappings
|
||||
SingleKeyToTextRemapTable singleKeyToTextReMap;
|
||||
|
||||
// Stores the os level shortcut remappings
|
||||
ShortcutRemapTable osLevelShortcutReMap;
|
||||
@ -57,9 +65,9 @@ public:
|
||||
// Stores the current configuration name.
|
||||
std::wstring currentConfig = KeyboardManagerConstants::DefaultConfiguration;
|
||||
|
||||
|
||||
private:
|
||||
bool LoadSingleKeyRemaps(const json::JsonObject& jsonData);
|
||||
bool LoadShortcutRemaps(const json::JsonObject& jsonData);
|
||||
bool LoadSingleKeyToTextRemaps(const json::JsonObject& jsonData);
|
||||
bool LoadShortcutRemaps(const json::JsonObject& jsonData, const std::wstring& objectName);
|
||||
bool LoadAppSpecificShortcutRemaps(const json::JsonObject& remapShortcutsData);
|
||||
};
|
@ -6,13 +6,13 @@
|
||||
class RemapShortcut
|
||||
{
|
||||
public:
|
||||
KeyShortcutUnion targetShortcut;
|
||||
KeyShortcutTextUnion targetShortcut;
|
||||
bool isShortcutInvoked;
|
||||
ModifierKey winKeyInvoked;
|
||||
// This bool value is only required for remapping shortcuts to Disable
|
||||
bool isOriginalActionKeyPressed;
|
||||
|
||||
RemapShortcut(const KeyShortcutUnion& sc) :
|
||||
RemapShortcut(const KeyShortcutTextUnion& sc) :
|
||||
targetShortcut(sc), isShortcutInvoked(false), winKeyInvoked(ModifierKey::Disabled), isOriginalActionKeyPressed(false)
|
||||
{
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ public:
|
||||
int GetCommonModifiersCount(const Shortcut& input) const;
|
||||
};
|
||||
|
||||
using KeyShortcutUnion = std::variant<DWORD, Shortcut>;
|
||||
using RemapBufferItem = std::vector<KeyShortcutUnion>;
|
||||
using KeyShortcutTextUnion = std::variant<DWORD, Shortcut, std::wstring>;
|
||||
using RemapBufferItem = std::vector<KeyShortcutTextUnion>;
|
||||
using RemapBufferRow = std::pair<RemapBufferItem, std::wstring>;
|
||||
using RemapBuffer = std::vector<RemapBufferRow>;
|
||||
|
@ -28,9 +28,10 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
ArgumentNullException.ThrowIfNull(arg);
|
||||
|
||||
// Using Ordinal comparison for internal text
|
||||
return OriginalKeys.Equals(arg.OriginalKeys, StringComparison.Ordinal) &&
|
||||
NewRemapKeys.Equals(arg.NewRemapKeys, StringComparison.Ordinal) &&
|
||||
TargetApp.Equals(arg.TargetApp, StringComparison.Ordinal);
|
||||
return string.Equals(OriginalKeys, arg.OriginalKeys, StringComparison.Ordinal) &&
|
||||
string.Equals(NewRemapKeys, arg.NewRemapKeys, StringComparison.Ordinal) &&
|
||||
string.Equals(NewRemapString, arg.NewRemapString, StringComparison.Ordinal) &&
|
||||
string.Equals(TargetApp, arg.TargetApp, StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,13 +13,22 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
[JsonPropertyName("remapKeys")]
|
||||
public RemapKeysDataModel RemapKeys { get; set; }
|
||||
|
||||
[JsonPropertyName("remapKeysToText")]
|
||||
public RemapKeysDataModel RemapKeysToText { get; set; }
|
||||
|
||||
[JsonPropertyName("remapShortcuts")]
|
||||
public ShortcutsKeyDataModel RemapShortcuts { get; set; }
|
||||
|
||||
[JsonPropertyName("remapShortcutsToText")]
|
||||
public ShortcutsKeyDataModel RemapShortcutsToText { get; set; }
|
||||
|
||||
public KeyboardManagerProfile()
|
||||
{
|
||||
RemapKeys = new RemapKeysDataModel();
|
||||
RemapKeysToText = new RemapKeysDataModel();
|
||||
|
||||
RemapShortcuts = new ShortcutsKeyDataModel();
|
||||
RemapShortcutsToText = new ShortcutsKeyDataModel();
|
||||
}
|
||||
|
||||
public string ToJsonString()
|
||||
|
@ -18,6 +18,9 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
[JsonPropertyName("newRemapKeys")]
|
||||
public string NewRemapKeys { get; set; }
|
||||
|
||||
[JsonPropertyName("unicodeText")]
|
||||
public string NewRemapString { get; set; }
|
||||
|
||||
private static List<string> MapKeys(string stringOfKeys)
|
||||
{
|
||||
return stringOfKeys
|
||||
@ -34,7 +37,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
|
||||
public List<string> GetMappedNewRemapKeys()
|
||||
{
|
||||
return MapKeys(NewRemapKeys);
|
||||
return string.IsNullOrEmpty(NewRemapString) ? MapKeys(NewRemapKeys) : new List<string> { NewRemapString };
|
||||
}
|
||||
|
||||
public string ToJsonString()
|
||||
|
@ -306,6 +306,13 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
{
|
||||
KeyboardManagerProfile kbmProfile = GetKBMProfile();
|
||||
_kbmItem = new DashboardModuleKBMItem() { RemapKeys = kbmProfile?.RemapKeys.InProcessRemapKeys, RemapShortcuts = KeyboardManagerViewModel.CombineShortcutLists(kbmProfile?.RemapShortcuts.GlobalRemapShortcuts, kbmProfile?.RemapShortcuts.AppSpecificRemapShortcuts) };
|
||||
|
||||
_kbmItem.RemapKeys = _kbmItem.RemapKeys.Concat(kbmProfile?.RemapKeysToText.InProcessRemapKeys).ToList();
|
||||
|
||||
var shortcutsToTextRemappings = KeyboardManagerViewModel.CombineShortcutLists(kbmProfile?.RemapShortcutsToText.GlobalRemapShortcuts, kbmProfile?.RemapShortcutsToText.AppSpecificRemapShortcuts);
|
||||
|
||||
_kbmItem.RemapShortcuts = _kbmItem.RemapShortcuts.Concat(shortcutsToTextRemappings).ToList();
|
||||
|
||||
var list = new List<DashboardModuleItem>
|
||||
{
|
||||
_kbmItem,
|
||||
|
@ -158,7 +158,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
{
|
||||
if (_profile != null)
|
||||
{
|
||||
return _profile.RemapKeys.InProcessRemapKeys;
|
||||
return _profile.RemapKeys.InProcessRemapKeys.Concat(_profile.RemapKeysToText.InProcessRemapKeys).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -190,11 +190,11 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
}
|
||||
else if (appSpecificShortcutList == null)
|
||||
{
|
||||
return globalShortcutList.ConvertAll(x => new AppSpecificKeysDataModel { OriginalKeys = x.OriginalKeys, NewRemapKeys = x.NewRemapKeys, TargetApp = allAppsDescription }).ToList();
|
||||
return globalShortcutList.ConvertAll(x => new AppSpecificKeysDataModel { OriginalKeys = x.OriginalKeys, NewRemapKeys = x.NewRemapKeys, NewRemapString = x.NewRemapString, TargetApp = allAppsDescription }).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
return globalShortcutList.ConvertAll(x => new AppSpecificKeysDataModel { OriginalKeys = x.OriginalKeys, NewRemapKeys = x.NewRemapKeys, TargetApp = allAppsDescription }).Concat(appSpecificShortcutList).ToList();
|
||||
return globalShortcutList.ConvertAll(x => new AppSpecificKeysDataModel { OriginalKeys = x.OriginalKeys, NewRemapKeys = x.NewRemapKeys, NewRemapString = x.NewRemapString, TargetApp = allAppsDescription }).Concat(appSpecificShortcutList).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
@ -204,7 +204,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
{
|
||||
if (_profile != null)
|
||||
{
|
||||
return CombineShortcutLists(_profile.RemapShortcuts.GlobalRemapShortcuts, _profile.RemapShortcuts.AppSpecificRemapShortcuts);
|
||||
return CombineShortcutLists(_profile.RemapShortcuts.GlobalRemapShortcuts, _profile.RemapShortcuts.AppSpecificRemapShortcuts).Concat(CombineShortcutLists(_profile.RemapShortcutsToText.GlobalRemapShortcuts, _profile.RemapShortcutsToText.AppSpecificRemapShortcuts)).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -333,6 +333,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
if (readSuccessfully)
|
||||
{
|
||||
FilterRemapKeysList(_profile?.RemapKeys?.InProcessRemapKeys);
|
||||
FilterRemapKeysList(_profile?.RemapKeysToText?.InProcessRemapKeys);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -27,7 +27,9 @@ using namespace winrt::Windows::Data::Json;
|
||||
|
||||
map<wstring, vector<wstring>> escapeInfo = {
|
||||
{ L"FancyZones\\app-zone-history.json", { L"app-zone-history/app-path" } },
|
||||
{ L"FancyZones\\settings.json", { L"properties/fancyzones_excluded_apps" } }
|
||||
{ L"FancyZones\\settings.json", { L"properties/fancyzones_excluded_apps" } },
|
||||
{ L"MouseWithoutBorders\\settings.json", { L"properties/SecurityKey" } }, // avoid leaking connection key
|
||||
{ L"Keyboard Manager\\default.json", { L"remapKeysToText", L"remapShortcutsToText" } }, // avoid leaking personal information from text mappings
|
||||
};
|
||||
|
||||
vector<wstring> filesToDelete = {
|
||||
|
Loading…
Reference in New Issue
Block a user