Keyboard disable (#6874)

Added disable key/shortcut functionality
This commit is contained in:
Mykhailo Pylyp 2020-10-02 15:36:36 +03:00 committed by GitHub
parent b2e72e1ca4
commit 3f25d7ccc8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 298 additions and 34 deletions

View file

@ -215,6 +215,7 @@ void LayoutMap::LayoutMapImpl::UpdateLayout()
keyboardLayoutMap[VK_NONCONVERT] = L"IME Non-Convert";
keyboardLayoutMap[VK_ACCEPT] = L"IME Kana";
keyboardLayoutMap[VK_MODECHANGE] = L"IME Mode Change";
keyboardLayoutMap[CommonSharedConstants::VK_DISABLED] = L"Disable";
}
// Function to return the list of key codes in the order for the drop down. It creates it if it doesn't exist

View file

@ -11,4 +11,7 @@ namespace CommonSharedConstants
// Path to the event used by PowerLauncher
const wchar_t POWER_LAUNCHER_SHARED_EVENT[] = L"Local\\PowerToysRunInvokeEvent-30f26ad7-d36d-4c0e-ab02-68bb5ff3c4ab";
}
// Max DWORD for key code to disable keys.
const DWORD VK_DISABLED = 0x100;
}

View file

@ -173,6 +173,8 @@ namespace KeyboardManagerHelper
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SHORTCUTMAXONEACTIONKEY).c_str();
case ErrorType::ShortcutMaxShortcutSizeOneActionKey:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_MAXSHORTCUTSIZE).c_str();
case ErrorType::ShortcutDisableAsActionKey:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_DISABLEASACTIONKEY).c_str();
default:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_DEFAULT).c_str();
}

View file

@ -48,7 +48,8 @@ namespace KeyboardManagerHelper
ShortcutAtleast2Keys,
ShortcutOneActionKey,
ShortcutNotMoreThanOneActionKey,
ShortcutMaxShortcutSizeOneActionKey
ShortcutMaxShortcutSizeOneActionKey,
ShortcutDisableAsActionKey
};
// Enum type to store possible decision for input in the low level hook

View file

@ -24,10 +24,10 @@ namespace KeyboardEventHandlers
// Check if the remap is to a key or a shortcut
bool remapToKey = (it->second.index() == 0);
// If mapped to 0x0 then the key is disabled
// If mapped to VK_DISABLED then the key is disabled
if (remapToKey)
{
if (std::get<DWORD>(it->second) == 0x0)
if (std::get<DWORD>(it->second) == CommonSharedConstants::VK_DISABLED)
{
return 1;
}
@ -214,7 +214,7 @@ 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)
if (!it->first.IsKeyboardStateClearExceptShortcut(ii) && (remapToShortcut || std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED))
{
continue;
}
@ -284,6 +284,12 @@ namespace KeyboardEventHandlers
{
// Dummy key, key up for all the original shortcut modifier keys and key down for remapped key
key_count = 1 + (src_size - 1) + dest_size;
// Do not send Disable key
if (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)
{
key_count--;
}
keyEventList = new INPUT[key_count]();
memset(keyEventList, 0, sizeof(keyEventList));
@ -296,8 +302,11 @@ namespace KeyboardEventHandlers
KeyboardManagerHelper::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
// Set target key down state
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++;
if (std::get<DWORD>(it->second.targetShortcut) != CommonSharedConstants::VK_DISABLED)
{
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++;
}
// Modifier state reset might be required for this key depending on the shortcut's action and target modifier - ex: Win+Caps -> Ctrl
if (it->first.GetCtrlKey() == NULL && it->first.GetAltKey() == NULL && it->first.GetShiftKey() == NULL)
@ -382,13 +391,22 @@ namespace KeyboardEventHandlers
{
// 1 for releasing new key and original shortcut modifiers except the one released
key_count = dest_size + src_size - 2;
// Do not send Disable key up
if (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)
{
key_count--;
}
keyEventList = new INPUT[key_count]();
memset(keyEventList, 0, sizeof(keyEventList));
// Release new key state
int i = 0;
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++;
if (std::get<DWORD>(it->second.targetShortcut) != CommonSharedConstants::VK_DISABLED)
{
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++;
}
// Set original shortcut key down state except the action key and the released modifier since the original action key may or may not be held down. If it is held down it will generate it's own key message
KeyboardManagerHelper::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, Shortcut(), data->lParam->vkCode);
@ -418,6 +436,12 @@ namespace KeyboardEventHandlers
// 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)
{
return 1;
}
size_t key_count = 1;
LPINPUT keyEventList = new INPUT[key_count]();
memset(keyEventList, 0, sizeof(keyEventList));
@ -455,13 +479,23 @@ namespace KeyboardEventHandlers
{
// 1 for releasing new key and original shortcut modifiers, and dummy key
key_count = dest_size + src_size;
// Do not send Disable key
if (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)
{
key_count--;
}
keyEventList = new INPUT[key_count]();
memset(keyEventList, 0, sizeof(keyEventList));
// Release new key state
int i = 0;
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++;
// Do not send Disable key
if (std::get<DWORD>(it->second.targetShortcut) != CommonSharedConstants::VK_DISABLED)
{
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++;
}
// Set original shortcut key down state except the action key and the released modifier
KeyboardManagerHelper::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
@ -499,6 +533,10 @@ namespace KeyboardEventHandlers
return 1;
}
}
else if (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)
{
return 1;
}
}
// Case 5: If any key apart from the action key or a modifier key in the original shortcut is pressed then revert the keyboard state to just the original modifiers being held down along with the current key press
@ -598,6 +636,43 @@ namespace KeyboardEventHandlers
i++;
}
it->second.isShortcutInvoked = false;
it->second.winKeyInvoked = ModifierKey::Disabled;
// If app specific shortcut has finished invoking, reset the target application
if (activatedApp != KeyboardManagerConstants::NoActivatedApp)
{
keyboardManagerState.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp);
}
lock.unlock();
UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT));
delete[] keyEventList;
return 1;
}
// All modifier keys and action key will be pressed down because if they are not pressed that means that handler has already been invoked on key release
else if (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)
{
// Key down for original shortcut modifiers and action key, dummy key, and current key press
size_t key_count = src_size + 1 + 1;
LPINPUT keyEventList = new INPUT[key_count]();
memset(keyEventList, 0, sizeof(keyEventList));
// Set old shortcut key down state
int i = 0;
KeyboardManagerHelper::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
// Set old action key
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it->first.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++;
// Send current key pressed without shortcut flag so that it can be reprocessed in case the physical keys pressed are a different remapped shortcut
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, 0);
i++;
// Send dummy key
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)KeyboardManagerConstants::DUMMY_KEY, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++;
it->second.isShortcutInvoked = false;
it->second.winKeyInvoked = ModifierKey::Disabled;
// If app specific shortcut has finished invoking, reset the target application

View file

@ -279,4 +279,7 @@
<data name="AutomationProperties_Row" xml:space="preserve">
<value>Row </value>
</data>
<data name="ERRORMESSAGE_DISABLEASACTIONKEY" xml:space="preserve">
<value>Disable can not be an action or a modifier key</value>
</data>
</root>

View file

@ -4,12 +4,14 @@
#include <keyboardmanager/common/KeyboardManagerState.h>
#include <keyboardmanager/dll/KeyboardEventHandlers.h>
#include "TestHelpers.h"
#include <common\shared_constants.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace RemappingLogicTests
{
TEST_CLASS (AppSpecificShortcutRemappingTests)
{
private:
MockedInput mockedInputHandler;
@ -312,5 +314,33 @@ namespace RemappingLogicTests
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), false);
}
// Disable app specific shortcut
TEST_METHOD (AppSpecificShortcutToDisable_ShouldDisable_WhenAppIsOnForeground)
{
Shortcut src;
src.SetKey(VK_CONTROL);
WORD actionKey = 0x41;
src.SetKey(actionKey);
WORD disableKey = CommonSharedConstants::VK_DISABLED;
testState.AddAppSpecificShortcut(testApp1, src, disableKey);
// Set the testApp as the foreground process
mockedInputHandler.SetForegroundProcess(testApp1);
const int nInputs = 2;
INPUT input[nInputs] = {};
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = VK_CONTROL;
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = actionKey;
// Send Ctrl+A keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
// Check if Ctrl+A is released and disable key was not send
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(actionKey), false);
}
};
}

View file

@ -1438,5 +1438,26 @@ namespace RemappingUITests
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::NoError);
});
}
// Return error on Disable as second modifier key or action key
TEST_METHOD (ValidateShortcutBufferElement_ShouldReturnDisableAsActionKeyError_OnSettingSecondDropdownAsDisable)
{
std::vector<DWORD> keyList = keyboardLayout.GetKeyCodeList(true);
keyList.insert(keyList.begin(), CommonSharedConstants::VK_DISABLED);
// Arrange
RemapBuffer remapBuffer;
remapBuffer.push_back(std::make_pair(RemapBufferItem{ std::vector<DWORD>{ VK_SHIFT, CommonSharedConstants::VK_DISABLED }, Shortcut() }, testApp1));
std::vector<int32_t> selectedIndices = {
GetDropDownIndexFromDropDownList(VK_SHIFT, keyList),
GetDropDownIndexFromDropDownList(CommonSharedConstants::VK_DISABLED, keyList)
};
// Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(0, 1, 1, selectedIndices, testApp1, true, keyList, remapBuffer, true);
// Assert
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::ShortcutDisableAsActionKey);
Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction);
}
};
}

View file

@ -2004,5 +2004,88 @@ namespace RemappingLogicTests
// Shortcut invoked state should be true
Assert::AreEqual(true, testState.osLevelShortcutReMap[src].isShortcutInvoked);
}
TEST_METHOD (ShortcutDisable_ShouldDisableShortcut_OnExactMatch)
{
Shortcut src;
src.SetKey(VK_CONTROL);
WORD actionKey = 0x41;
src.SetKey(actionKey);
WORD disableKey = CommonSharedConstants::VK_DISABLED;
testState.AddOSLevelShortcut(src, disableKey);
const int nInputs = 2;
INPUT input[nInputs] = {};
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = VK_CONTROL;
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = actionKey;
// send Ctrl+A
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
// Check that Ctrl+A was released and Disable key was not sent
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(actionKey), false);
}
TEST_METHOD (ShortcutDisable_ShouldNotDisableShortcut_OnSubsetMatch)
{
Shortcut src;
src.SetKey(VK_CONTROL);
WORD actionKey = 0x41;
src.SetKey(actionKey);
WORD disableKey = CommonSharedConstants::VK_DISABLED;
testState.AddOSLevelShortcut(src, disableKey);
const int nInputs = 3;
INPUT input[nInputs] = {};
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = VK_CONTROL;
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = VK_SHIFT;
input[2].type = INPUT_KEYBOARD;
input[2].ki.wVk = actionKey;
// send Ctrl+Shift+A
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
// Check that Ctrl+A was not released and Disable key was not sent
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(actionKey), true);
}
TEST_METHOD (ShortcutDisable_ShouldNotDisableShortcutSuperset_AfterShorcutWasDisabled)
{
Shortcut src;
src.SetKey(VK_CONTROL);
WORD actionKey = 0x41;
src.SetKey(actionKey);
WORD disableKey = CommonSharedConstants::VK_DISABLED;
testState.AddOSLevelShortcut(src, disableKey);
const int nInputs = 2;
INPUT input[nInputs] = {};
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = VK_CONTROL;
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = actionKey;
// send Ctrl+A
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = 0x42;
// send B
mockedInputHandler.SendVirtualInput(1, input, sizeof(INPUT));
// Check that Ctrl+A+B was pressed
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(actionKey), true);
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x42), true);
}
};
}

View file

@ -58,8 +58,8 @@ namespace RemappingLogicTests
// Test if key is suppressed if a key is disabled by single key remap
TEST_METHOD (RemappedKeyDisabled_ShouldNotChangeKeyState_OnKeyEvent)
{
// Remap A to 0x0 (disabled)
testState.AddSingleKeyRemap(0x41, 0x0);
// Remap A to VK_DISABLE (disabled)
testState.AddSingleKeyRemap(0x41, CommonSharedConstants::VK_DISABLED);
const int nInputs = 1;
INPUT input[nInputs] = {};

View file

@ -1,6 +1,7 @@
#include "pch.h"
#include "BufferValidationHelpers.h"
#include <keyboardmanager/common/KeyboardManagerConstants.h>
#include <common\shared_constants.h>
namespace BufferValidationHelpers
{
@ -121,9 +122,14 @@ namespace BufferValidationHelpers
errorType = KeyboardManagerHelper::ErrorType::ShortcutOneActionKey;
}
}
// Disable can not be selected if one modifier key has already been selected
else if (keyCodeList[selectedKeyIndex] == CommonSharedConstants::VK_DISABLED && dropDownIndex)
{
errorType = KeyboardManagerHelper::ErrorType::ShortcutDisableAsActionKey;
}
// If none of the above, then the action key will be set
}
// If it is the not the last drop down
// If it is not the last drop down
else
{
if (KeyboardManagerHelper::IsModifierKey(keyCodeList[selectedKeyIndex]))
@ -158,6 +164,11 @@ namespace BufferValidationHelpers
errorType = KeyboardManagerHelper::ErrorType::ShortcutAtleast2Keys;
}
}
// Allow selection of VK_DISABLE only in first dropdown
else if (keyCodeList[selectedKeyIndex] == CommonSharedConstants::VK_DISABLED && dropDownIndex)
{
errorType = KeyboardManagerHelper::ErrorType::ShortcutDisableAsActionKey;
}
// If the user tries to set an action key check if all drop down menus after this are empty if it is not the first key. If it is a hybrid control, this can be done even on the first key
else if (dropDownIndex != 0 || isHybridControl)
{

View file

@ -3,12 +3,38 @@
#include "keyboardmanager/common/Helpers.h"
#include <keyboardmanager/common/KeyboardManagerState.h>
#include "BufferValidationHelpers.h"
#include <common\shared_constants.h>
#include <common\keyboard_layout_impl.h>
// Initialized to null
KeyboardManagerState* KeyDropDownControl::keyboardManagerState = nullptr;
// Get keys code list depending if Disable is in dropdown
std::vector<DWORD> KeyDropDownControl::GetKeyCodeList(bool isShortcut, bool renderDisable)
{
auto list = keyboardManagerState->keyboardMap.GetKeyCodeList(isShortcut);
if (renderDisable)
{
list.insert(list.begin(), CommonSharedConstants::VK_DISABLED);
}
return list;
}
// Get keys name list depending if Disable is in dropdown
std::vector<std::wstring> KeyDropDownControl::GetKeyNameList(bool isShortcut, bool renderDisable)
{
auto list = keyboardManagerState->keyboardMap.GetKeyNameList(isShortcut);
if (renderDisable)
{
list.insert(list.begin(), keyboardManagerState->keyboardMap.GetKeyName(CommonSharedConstants::VK_DISABLED));
}
return list;
}
// Function to set properties apart from the SelectionChanged event handler
void KeyDropDownControl::SetDefaultProperties(bool isShortcut)
void KeyDropDownControl::SetDefaultProperties(bool isShortcut, bool renderDisable)
{
dropDown = ComboBox();
warningFlyout = Flyout();
@ -25,12 +51,12 @@ void KeyDropDownControl::SetDefaultProperties(bool isShortcut)
dropDown.as<ComboBox>().MaxDropDownHeight(KeyboardManagerConstants::TableDropDownHeight);
// Initialise layout attribute
previousLayout = GetKeyboardLayout(0);
keyCodeList = keyboardManagerState->keyboardMap.GetKeyCodeList(isShortcut);
dropDown.as<ComboBox>().ItemsSource(KeyboardManagerHelper::ToBoxValue(keyboardManagerState->keyboardMap.GetKeyNameList(isShortcut)));
keyCodeList = GetKeyCodeList(isShortcut, renderDisable);
dropDown.as<ComboBox>().ItemsSource(KeyboardManagerHelper::ToBoxValue(GetKeyNameList(isShortcut, renderDisable)));
// drop down open handler - to reload the items with the latest layout
dropDown.as<ComboBox>().DropDownOpened([&, isShortcut](winrt::Windows::Foundation::IInspectable const& sender, auto args) {
ComboBox currentDropDown = sender.as<ComboBox>();
CheckAndUpdateKeyboardLayout(currentDropDown, isShortcut);
CheckAndUpdateKeyboardLayout(currentDropDown, isShortcut, renderDisable);
});
// Attach flyout to the drop down
@ -48,7 +74,7 @@ void KeyDropDownControl::SetAccessibleNameForComboBox(ComboBox dropDown, int ind
}
// Function to check if the layout has changed and accordingly update the drop down list
void KeyDropDownControl::CheckAndUpdateKeyboardLayout(ComboBox currentDropDown, bool isShortcut)
void KeyDropDownControl::CheckAndUpdateKeyboardLayout(ComboBox currentDropDown, bool isShortcut, bool renderDisable)
{
// Get keyboard layout for current thread
HKL layout = GetKeyboardLayout(0);
@ -56,8 +82,8 @@ void KeyDropDownControl::CheckAndUpdateKeyboardLayout(ComboBox currentDropDown,
// Check if the layout has changed
if (previousLayout != layout)
{
keyCodeList = keyboardManagerState->keyboardMap.GetKeyCodeList(isShortcut);
currentDropDown.ItemsSource(KeyboardManagerHelper::ToBoxValue(keyboardManagerState->keyboardMap.GetKeyNameList(isShortcut)));
keyCodeList = GetKeyCodeList(isShortcut, renderDisable);
currentDropDown.ItemsSource(KeyboardManagerHelper::ToBoxValue(GetKeyNameList(isShortcut, renderDisable)));
previousLayout = layout;
}
}
@ -203,7 +229,7 @@ void KeyDropDownControl::SetSelectionHandler(Grid& table, StackPanel shortcutCon
}
// Reset the buffer based on the new selected drop down items. Use static key code list since the KeyDropDownControl object might be deleted
std::vector selectedKeyCodes = KeyboardManagerHelper::GetKeyCodesFromSelectedIndices(GetSelectedIndicesFromStackPanel(parent), KeyDropDownControl::keyboardManagerState->keyboardMap.GetKeyCodeList(true));
std::vector selectedKeyCodes = KeyboardManagerHelper::GetKeyCodesFromSelectedIndices(GetSelectedIndicesFromStackPanel(parent), GetKeyCodeList(true, colIndex == 1));
if (!isHybridControl)
{
std::get<Shortcut>(shortcutRemapBuffer[validationResult.second].first[colIndex]).SetKeyCodes(selectedKeyCodes);
@ -283,7 +309,7 @@ ComboBox KeyDropDownControl::GetComboBox()
// Function to add a drop down to the shortcut stack panel
void KeyDropDownControl::AddDropDown(Grid table, StackPanel shortcutControl, StackPanel parent, const int colIndex, RemapBuffer& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow, bool ignoreWarning)
{
keyDropDownControlObjects.push_back(std::move(std::unique_ptr<KeyDropDownControl>(new KeyDropDownControl(true, ignoreWarning))));
keyDropDownControlObjects.push_back(std::move(std::unique_ptr<KeyDropDownControl>(new KeyDropDownControl(true, ignoreWarning, colIndex == 1))));
parent.Children().Append(keyDropDownControlObjects[keyDropDownControlObjects.size() - 1]->GetComboBox());
keyDropDownControlObjects[keyDropDownControlObjects.size() - 1]->SetSelectionHandler(table, shortcutControl, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, isSingleKeyWindow);
parent.UpdateLayout();
@ -314,7 +340,7 @@ void KeyDropDownControl::ValidateShortcutFromDropDownList(Grid table, StackPanel
for (int i = 0; i < keyDropDownControlObjects.size(); i++)
{
// Check for errors only if the current selection is a valid shortcut
std::vector<DWORD> selectedKeyCodes = KeyboardManagerHelper::GetKeyCodesFromSelectedIndices(keyDropDownControlObjects[i]->GetSelectedIndicesFromStackPanel(parent), KeyDropDownControl::keyboardManagerState->keyboardMap.GetKeyCodeList(true));
std::vector<DWORD> selectedKeyCodes = KeyboardManagerHelper::GetKeyCodesFromSelectedIndices(keyDropDownControlObjects[i]->GetSelectedIndicesFromStackPanel(parent), GetKeyCodeList(true, colIndex == 1));
std::variant<DWORD, Shortcut> currentShortcut;
if (selectedKeyCodes.size() == 1 && isHybridControl)
{
@ -352,7 +378,7 @@ void KeyDropDownControl::AddShortcutToControl(Shortcut shortcut, Grid table, Sta
keyDropDownControlObjects.clear();
std::vector<DWORD> shortcutKeyCodes = shortcut.GetKeyCodes();
std::vector<DWORD> keyCodeList = keyboardManagerState.keyboardMap.GetKeyCodeList(true);
std::vector<DWORD> keyCodeList = GetKeyCodeList(true, colIndex == 1);
if (shortcutKeyCodes.size() != 0)
{
bool ignoreWarning = false;

View file

@ -1,5 +1,6 @@
#pragma once
#include <keyboardmanager/common/Shortcut.h>
#include <vector>
class KeyboardManagerState;
namespace winrt::Windows
@ -42,23 +43,22 @@ private:
bool ignoreKeyToShortcutWarning;
// Function to set properties apart from the SelectionChanged event handler
void SetDefaultProperties(bool isShortcut);
void SetDefaultProperties(bool isShortcut, bool renderDisable);
// Function to check if the layout has changed and accordingly update the drop down list
void CheckAndUpdateKeyboardLayout(ComboBox currentDropDown, bool isShortcut);
void CheckAndUpdateKeyboardLayout(ComboBox currentDropDown, bool isShortcut, bool renderDisable);
// Function to set accessible name for combobox
static void SetAccessibleNameForComboBox(ComboBox dropDown, int index);
public:
// Pointer to the keyboard manager state
static KeyboardManagerState* keyboardManagerState;
// Constructor - the last default parameter should be passed as false only if it originates from Type shortcut or when an old shortcut is reloaded
KeyDropDownControl(bool isShortcut, bool fromAddShortcutToControl = false) :
KeyDropDownControl(bool isShortcut, bool fromAddShortcutToControl = false, bool renderDisable = false) :
ignoreKeyToShortcutWarning(fromAddShortcutToControl)
{
SetDefaultProperties(isShortcut);
SetDefaultProperties(isShortcut, renderDisable);
}
// Function to set selection handler for single key remap drop down. Needs to be called after the constructor since the singleKeyControl StackPanel is null if called in the constructor
@ -90,4 +90,10 @@ public:
// Function to add a shortcut to the UI control as combo boxes
static void AddShortcutToControl(Shortcut shortcut, Grid table, StackPanel parent, KeyboardManagerState& keyboardManagerState, const int colIndex, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, RemapBuffer& remapBuffer, StackPanel controlLayout, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow);
// Get keys code list depending if Disable is in dropdown
static std::vector<DWORD> GetKeyCodeList(bool isShortcut, bool renderDisable);
// Get keys name list depending if Disable is in dropdown
static std::vector<std::wstring> GetKeyNameList(bool isShortcut, bool renderDisable);
};

View file

@ -5,6 +5,7 @@
#include "keyboardmanager/common/Helpers.h"
#include "common/common.h"
#include "keyboardmanager/dll/Generated Files/resource.h"
#include <common\shared_constants.h>
extern "C" IMAGE_DOS_HEADER __ImageBase;
//Both static members are initialized to null
@ -132,9 +133,9 @@ void ShortcutControl::AddNewShortcutControlRow(Grid& parent, std::vector<std::ve
KeyDropDownControl::ValidateShortcutFromDropDownList(parent, keyboardRemapControlObjects[rowIndex][1]->getShortcutControl(), keyboardRemapControlObjects[rowIndex][1]->shortcutDropDownStackPanel.as<StackPanel>(), 1, ShortcutControl::shortcutRemapBuffer, keyboardRemapControlObjects[rowIndex][1]->keyDropDownControlObjects, targetAppTextBox, true, false);
// Reset the buffer based on the selected drop down items
std::get<Shortcut>(shortcutRemapBuffer[rowIndex].first[0]).SetKeyCodes(KeyboardManagerHelper::GetKeyCodesFromSelectedIndices(KeyDropDownControl::GetSelectedIndicesFromStackPanel(keyboardRemapControlObjects[rowIndex][0]->shortcutDropDownStackPanel.as<StackPanel>()), KeyDropDownControl::keyboardManagerState->keyboardMap.GetKeyCodeList(true)));
std::get<Shortcut>(shortcutRemapBuffer[rowIndex].first[0]).SetKeyCodes(KeyboardManagerHelper::GetKeyCodesFromSelectedIndices(KeyDropDownControl::GetSelectedIndicesFromStackPanel(keyboardRemapControlObjects[rowIndex][0]->shortcutDropDownStackPanel.as<StackPanel>()), KeyDropDownControl::GetKeyCodeList(true, false)));
// second column is a hybrid column
std::vector<DWORD> selectedKeyCodes = KeyboardManagerHelper::GetKeyCodesFromSelectedIndices(KeyDropDownControl::GetSelectedIndicesFromStackPanel(keyboardRemapControlObjects[rowIndex][1]->shortcutDropDownStackPanel.as<StackPanel>()), KeyDropDownControl::keyboardManagerState->keyboardMap.GetKeyCodeList(true));
std::vector<DWORD> selectedKeyCodes = KeyboardManagerHelper::GetKeyCodesFromSelectedIndices(KeyDropDownControl::GetSelectedIndicesFromStackPanel(keyboardRemapControlObjects[rowIndex][1]->shortcutDropDownStackPanel.as<StackPanel>()), KeyDropDownControl::GetKeyCodeList(true, true));
// If exactly one key is selected consider it to be a key remap
if (selectedKeyCodes.size() == 1)
@ -243,7 +244,7 @@ void ShortcutControl::AddNewShortcutControlRow(Grid& parent, std::vector<std::ve
if (newKeys.index() == 0)
{
std::vector<DWORD> shortcutListKeyCodes = keyboardManagerState->keyboardMap.GetKeyCodeList(true);
std::vector<DWORD> shortcutListKeyCodes = KeyDropDownControl::GetKeyCodeList(true, true);
auto it = std::find(shortcutListKeyCodes.begin(), shortcutListKeyCodes.end(), std::get<DWORD>(newKeys));
if (it != shortcutListKeyCodes.end())
{

View file

@ -6,6 +6,7 @@
#include "ShortcutControl.h"
#include "common/common.h"
#include "keyboardmanager/dll/Generated Files/resource.h"
#include <common\shared_constants.h>
extern "C" IMAGE_DOS_HEADER __ImageBase;
//Both static members are initialized to null
@ -106,7 +107,7 @@ void SingleKeyRemapControl::AddNewControlKeyRemapRow(Grid& parent, std::vector<s
{
singleKeyRemapBuffer.push_back(std::make_pair<RemapBufferItem, std::wstring>(RemapBufferItem{ originalKey, newKey }, L""));
std::vector<DWORD> keyCodes = keyboardManagerState->keyboardMap.GetKeyCodeList();
std::vector<DWORD> shortcutListKeyCodes = keyboardManagerState->keyboardMap.GetKeyCodeList(true);
std::vector<DWORD> shortcutListKeyCodes = KeyDropDownControl::GetKeyCodeList(true, true);
auto it = std::find(keyCodes.begin(), keyCodes.end(), originalKey);
if (it != keyCodes.end())
{