Add drop down key selection support to Keyboard Manager UI (dev/build-features) (#2140)

* Added combobox

* Formatted and removed unused code

* Added drop down support for Edit Keyboard window

* Reordered the displayed key list

* Add shortcut stack panels and drop downs linked to detect shortcut

* Add more selected item logic

* Added complete dropdown support for edit shortcuts window

* Added Flyout warning for incorrect drop down input

* Tweaked warnings

* Removed MainWindow code

* Changed SelectedValue toSelectedIndex

* Removed unnecessary assignments

* Added a warning for two dropdowns and the first one is changed to an action key

* Added function comments in cpp file

* Fixed some comments

* Fixed all allocation and out of scope issues

* Fix most issues except reloading shortcuts

* Fixed issue while reloading shortcuts

* Fixed type cast warnings

* Changed delete to delete[]

* tweaked
This commit is contained in:
Arjun Balgovind 2020-04-18 16:12:26 -07:00 committed by GitHub
parent fc7103f56e
commit 0417b6266a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 686 additions and 247 deletions

View File

@ -27,6 +27,37 @@ IInspectable getSiblingElement(IInspectable const& element)
return parentElement.Children().GetAt(index + 1);
}
// Function to check if the key is a modifier key
bool IsModifierKey(DWORD key)
{
return (GetKeyType(key) != KeyType::Action);
}
// Function to get the type of the key
KeyType GetKeyType(DWORD key)
{
switch (key)
{
case VK_LWIN:
case VK_RWIN:
return KeyType::Win;
case VK_CONTROL:
case VK_LCONTROL:
case VK_RCONTROL:
return KeyType::Ctrl;
case VK_MENU:
case VK_LMENU:
case VK_RMENU:
return KeyType::Alt;
case VK_SHIFT:
case VK_LSHIFT:
case VK_RSHIFT:
return KeyType::Shift;
default:
return KeyType::Action;
}
}
// Function to return if the key is an extended key which requires the use of the extended key flag
bool isExtendedKey(DWORD key)
{

View File

@ -2,6 +2,16 @@
#include <vector>
#include <winrt/Windows.System.h>
// Type to distinguish between keys
enum class KeyType
{
Win,
Ctrl,
Alt,
Shift,
Action
};
// Function to split a wstring based on a delimiter and return a vector of split strings
std::vector<std::wstring> splitwstring(const std::wstring& input, wchar_t delimiter);
@ -33,5 +43,11 @@ std::vector<T> convertWStringVectorToIntegerVector(const std::vector<std::wstrin
return typeVector;
}
// Function to check if the key is a modifier key
bool IsModifierKey(DWORD key);
// Function to get the type of the key
KeyType GetKeyType(DWORD key);
// Function to return if the key is an extended key which requires the use of the extended key flag
bool isExtendedKey(DWORD key);

View File

@ -1,6 +1,7 @@
#include "pch.h"
#include "LayoutMap.h"
// Function to return the unicode string name of the key
std::wstring LayoutMap::GetKeyName(DWORD key)
{
std::wstring result = L"Undefined";
@ -15,30 +16,41 @@ std::wstring LayoutMap::GetKeyName(DWORD key)
return result;
}
// Update Keyboard layout according to input locale identifier
void LayoutMap::UpdateLayout()
{
// Get keyboard layout for current thread
HKL layout = GetKeyboardLayout(0);
if (layout == previousLayout) {
if (layout == previousLayout)
{
return;
}
previousLayout = layout;
if (!isKeyCodeListGenerated)
{
unicodeKeys.clear();
unknownKeys.clear();
}
unsigned char btKeys[256] = { 0 };
unsigned char* btKeys = new unsigned char[256]{ 0 };
GetKeyboardState(btKeys);
// Iterate over all the virtual key codes
for (int i = 0; i < 256; i++)
// Iterate over all the virtual key codes. virtual key 0 is not used
for (int i = 1; i < 256; i++)
{
// Get the scan code from the virtual key code
UINT scanCode = MapVirtualKeyExW(i, MAPVK_VK_TO_VSC, layout);
// Get the unicode representation from the virtual key code and scan code pair to
// Get the unicode representation from the virtual key code and scan code pair to
wchar_t szBuffer[3] = { 0 };
int result = ToUnicodeEx(i, scanCode, (BYTE*)btKeys, szBuffer, 3, 0, layout);
// If a representation is returned
if (result > 0)
{
keyboardLayoutMap[i] = szBuffer;
if (!isKeyCodeListGenerated)
{
unicodeKeys[i] = szBuffer;
}
}
else
{
@ -46,9 +58,15 @@ void LayoutMap::UpdateLayout()
std::wstring vk = L"VK ";
vk += std::to_wstring(i);
keyboardLayoutMap[i] = vk;
if (!isKeyCodeListGenerated)
{
unknownKeys[i] = vk;
}
}
}
delete[] btKeys;
// Override special key names like Shift, Ctrl etc because they don't have unicode mappings and key names like Enter, Space as they appear as "\r", " "
// To do: localization
keyboardLayoutMap[VK_CANCEL] = L"Break";
@ -155,3 +173,104 @@ void LayoutMap::UpdateLayout()
keyboardLayoutMap[0xFF] = L"Undefined";
// To do: Add IME key names
}
// Function to return the list of key codes in the order for the drop down. It creates it if it doesn't exist
std::vector<DWORD> LayoutMap::GetKeyCodeList(const bool isShortcut)
{
std::lock_guard<std::mutex> lock(keyboardLayoutMap_mutex);
UpdateLayout();
std::vector<DWORD> keyCodes;
if (!isKeyCodeListGenerated)
{
// Add modifier keys
keyCodes.push_back(VK_LWIN);
keyCodes.push_back(VK_RWIN);
keyCodes.push_back(VK_CONTROL);
keyCodes.push_back(VK_LCONTROL);
keyCodes.push_back(VK_RCONTROL);
keyCodes.push_back(VK_MENU);
keyCodes.push_back(VK_LMENU);
keyCodes.push_back(VK_RMENU);
keyCodes.push_back(VK_SHIFT);
keyCodes.push_back(VK_LSHIFT);
keyCodes.push_back(VK_RSHIFT);
// Add character keys
for (auto& it : unicodeKeys)
{
// If it was not renamed with a special name
if (it.second == keyboardLayoutMap[it.first])
{
keyCodes.push_back(it.first);
}
}
// Add all other special keys
for (int i = 1; i < 256; i++)
{
// If it is not already been added (i.e. it was either a modifier or had a unicode representation)
if (std::find(keyCodes.begin(), keyCodes.end(), i) == keyCodes.end())
{
// If it is any other key but it is not named as VK #
auto it = unknownKeys.find(i);
if (it == unknownKeys.end())
{
keyCodes.push_back(i);
}
else if (unknownKeys[i] != keyboardLayoutMap[i])
{
keyCodes.push_back(i);
}
}
}
// Add unknown keys
for (auto& it : unknownKeys)
{
// If it was not renamed with a special name
if (it.second == keyboardLayoutMap[it.first])
{
keyCodes.push_back(it.first);
}
}
keyCodeList = keyCodes;
isKeyCodeListGenerated = true;
}
else
{
keyCodes = keyCodeList;
}
// If it is a key list for the shortcut control then we add a "None" key at the start
if (isShortcut)
{
keyCodes.insert(keyCodes.begin(), 0);
}
return keyCodes;
}
Windows::Foundation::Collections::IVector<Windows::Foundation::IInspectable> LayoutMap::GetKeyNameList(const bool isShortcut)
{
std::unique_lock<std::mutex> lock(keyboardLayoutMap_mutex);
UpdateLayout();
lock.unlock();
Windows::Foundation::Collections::IVector<Windows::Foundation::IInspectable> keyNames = single_threaded_vector<Windows::Foundation::IInspectable>();
std::vector<DWORD> keyCodes = GetKeyCodeList(isShortcut);
lock.lock();
// If it is a key list for the shortcut control then we add a "None" key at the start
if (isShortcut)
{
keyNames.Append(winrt::box_value(L"None"));
for (int i = 1; i < keyCodes.size(); i++)
{
keyNames.Append(winrt::box_value(keyboardLayoutMap[keyCodes[i]].c_str()));
}
}
else
{
for (int i = 0; i < keyCodes.size(); i++)
{
keyNames.Append(winrt::box_value(keyboardLayoutMap[keyCodes[i]].c_str()));
}
}
return keyNames;
}

View File

@ -5,16 +5,33 @@
#include <mutex>
#include <windows.h>
using namespace winrt;
// Wrapper class to handle keyboard layout
class LayoutMap
{
private:
// Stores mappings for all the virtual key codes to the name of the key
std::map<DWORD, std::wstring> keyboardLayoutMap;
std::mutex keyboardLayoutMap_mutex;
// Stores the previous layout
HKL previousLayout = 0;
// Stores the keys which have a unicode representation
std::map<DWORD, std::wstring> unicodeKeys;
// Stores the keys which do not have a name
std::map<DWORD, std::wstring> unknownKeys;
// Stores true if the fixed ordering key code list has already been set
bool isKeyCodeListGenerated = false;
// Stores a fixed order key code list for the drop down menus. It is kept fixed to change in ordering due to languages
std::vector<DWORD> keyCodeList;
public:
std::map<DWORD, std::wstring> keyboardLayoutMap;
// Update Keyboard layout according to input locale identifier
void UpdateLayout();
@ -26,4 +43,9 @@ public:
// Function to return the unicode string name of the key
std::wstring GetKeyName(DWORD key);
// Function to return the list of key codes in the order for the drop down. It creates it if it doesn't exist
std::vector<DWORD> GetKeyCodeList(const bool isShortcut = false);
// Function to return the list of key name in the order for the drop down based on the key codes
Windows::Foundation::Collections::IVector<Windows::Foundation::IInspectable> GetKeyNameList(const bool isShortcut = false);
};

View File

@ -391,26 +391,7 @@ void Shortcut::ResetKey(const DWORD& input, const bool& isWinBoth)
}
}
// Function to return the string representation of the shortcut
winrt::hstring Shortcut::ToHstring(LayoutMap& keyboardMap)
{
std::vector<winrt::hstring> keys = GetKeyVector(keyboardMap);
winrt::hstring output;
for (auto& key : keys)
{
output = output + key + winrt::to_hstring(L" ");
}
if (keys.size() > 1)
{
return winrt::hstring(output.c_str(), output.size() - 1);
}
else
{
return output;
}
}
// Function to return a vector of hstring for each key in the display order
std::vector<winrt::hstring> Shortcut::GetKeyVector(LayoutMap& keyboardMap) const
{
std::vector<winrt::hstring> keys;
@ -437,6 +418,43 @@ std::vector<winrt::hstring> Shortcut::GetKeyVector(LayoutMap& keyboardMap) const
return keys;
}
// Function to return a vector of key codes in the display order
std::vector<DWORD> Shortcut::GetKeyCodes()
{
std::vector<DWORD> keys;
if (winKey != ModifierKey::Disabled)
{
keys.push_back(GetWinKey(ModifierKey::Left));
}
if (ctrlKey != ModifierKey::Disabled)
{
keys.push_back(GetCtrlKey());
}
if (altKey != ModifierKey::Disabled)
{
keys.push_back(GetAltKey());
}
if (shiftKey != ModifierKey::Disabled)
{
keys.push_back(GetShiftKey());
}
if (actionKey != NULL)
{
keys.push_back(actionKey);
}
return keys;
}
// Function to set a shortcut from a vector of key codes
void Shortcut::SetKeyCodes(const std::vector<DWORD>& keys)
{
Reset();
for (int i = 0; i < keys.size(); i++)
{
SetKey(keys[i]);
}
}
// Function to check if all the modifiers in the shortcut have been pressed down
bool Shortcut::CheckModifiersKeyboardState() const
{
@ -540,7 +558,7 @@ bool Shortcut::CheckModifiersKeyboardState() const
bool Shortcut::IsKeyboardStateClearExceptShortcut() const
{
// Iterate through all the virtual key codes - 0xFF is set to key down because of the Num Lock
for (int keyVal = 0; keyVal < 0xFF; keyVal++)
for (int keyVal = 1; keyVal < 0xFF; keyVal++)
{
// Skip mouse buttons. Keeping this could cause a remapping to fail if a mouse button is also pressed at the same time
if (keyVal == VK_LBUTTON || keyVal == VK_RBUTTON || keyVal == VK_MBUTTON || keyVal == VK_XBUTTON1 || keyVal == VK_XBUTTON2)

View File

@ -22,7 +22,6 @@ private:
DWORD actionKey;
public:
// By default create an empty shortcut
Shortcut() :
winKey(ModifierKey::Disabled), ctrlKey(ModifierKey::Disabled), altKey(ModifierKey::Disabled), shiftKey(ModifierKey::Disabled), actionKey(NULL)
@ -136,12 +135,15 @@ public:
// Function to reset the state of a shortcut key based on the passed key code argument. Since there is no VK_WIN code, use the second argument for setting common win key.
void ResetKey(const DWORD& input, const bool& isWinBoth = false);
// Function to return the string representation of the shortcut
winrt::hstring ToHstring(LayoutMap& keyboardMap);
// Function to return a vector of hstring for each key, in the same order as ToHstring()
// Function to return a vector of hstring for each key in the display order
std::vector<winrt::hstring> GetKeyVector(LayoutMap& keyboardMap) const;
// Function to return a vector of key codes in the display order
std::vector<DWORD> GetKeyCodes();
// Function to set a shortcut from a vector of key codes
void SetKeyCodes(const std::vector<DWORD>& keys);
// Function to check if all the modifiers in the shortcut have been pressed down
bool CheckModifiersKeyboardState() const;

View File

@ -1,6 +1,7 @@
#include "pch.h"
#include "EditKeyboardWindow.h"
#include "SingleKeyRemapControl.h"
#include "KeyDropDownControl.h"
LRESULT CALLBACK EditKeyboardWindowProc(HWND, UINT, WPARAM, LPARAM);
@ -15,7 +16,6 @@ std::mutex editKeyboardWindowMutex;
// Function to create the Edit Keyboard Window
void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState)
{
// Window Registration
const wchar_t szWindowClass[] = L"EditKeyboardWindow";
if (!isEditKeyboardWindowRegistrationCompleted)
@ -104,7 +104,7 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
TextBlock keyRemapInfoHeader;
keyRemapInfoHeader.Text(winrt::to_hstring("Select the key you want to remap, original key, and it's new output when pressed, the new key"));
keyRemapInfoHeader.Foreground(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::Black() });
keyRemapInfoHeader.Margin({ 0, 0, 0, 10 });
keyRemapInfoHeader.Margin({ 10, 0, 0, 10 });
// Table to display the key remaps
Windows::UI::Xaml::Controls::StackPanel keyRemapTable;
@ -143,14 +143,17 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
SingleKeyRemapControl::EditKeyboardWindowHandle = _hWndEditKeyboardWindow;
// Store keyboard manager state
SingleKeyRemapControl::keyboardManagerState = &keyboardManagerState;
KeyDropDownControl::keyboardManagerState = &keyboardManagerState;
// Clear the single key remap buffer
SingleKeyRemapControl::singleKeyRemapBuffer.clear();
// Vector to store dynamically allocated control objects to avoid early destruction
std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>> keyboardRemapControlObjects;
// Load existing remaps into UI
std::unique_lock<std::mutex> lock(keyboardManagerState.singleKeyReMap_mutex);
for (const auto& it : keyboardManagerState.singleKeyReMap)
{
SingleKeyRemapControl::AddNewControlKeyRemapRow(keyRemapTable, it.first, it.second);
SingleKeyRemapControl::AddNewControlKeyRemapRow(keyRemapTable, keyboardRemapControlObjects, it.first, it.second);
}
lock.unlock();
@ -210,7 +213,7 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
addRemapKey.Content(plusSymbol);
addRemapKey.Margin({ 10 });
addRemapKey.Click([&](IInspectable const& sender, RoutedEventArgs const&) {
SingleKeyRemapControl::AddNewControlKeyRemapRow(keyRemapTable);
SingleKeyRemapControl::AddNewControlKeyRemapRow(keyRemapTable, keyboardRemapControlObjects);
});
xamlContainer.Children().Append(header);

View File

@ -1,6 +1,7 @@
#include "pch.h"
#include "EditShortcutsWindow.h"
#include "ShortcutControl.h"
#include "KeyDropDownControl.h"
LRESULT CALLBACK EditShortcutsWindowProc(HWND, UINT, WPARAM, LPARAM);
@ -133,14 +134,17 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
ShortcutControl::EditShortcutsWindowHandle = _hWndEditShortcutsWindow;
// Store keyboard manager state
ShortcutControl::keyboardManagerState = &keyboardManagerState;
KeyDropDownControl::keyboardManagerState = &keyboardManagerState;
// Clear the shortcut remap buffer
ShortcutControl::shortcutRemapBuffer.clear();
// Vector to store dynamically allocated control objects to avoid early destruction
std::vector<std::vector<std::unique_ptr<ShortcutControl>>> keyboardRemapControlObjects;
// Load existing shortcuts into UI
std::unique_lock<std::mutex> lock(keyboardManagerState.osLevelShortcutReMap_mutex);
for (const auto& it : keyboardManagerState.osLevelShortcutReMap)
{
ShortcutControl::AddNewShortcutControlRow(shortcutTable, it.first, it.second.targetShortcut);
ShortcutControl::AddNewShortcutControlRow(shortcutTable, keyboardRemapControlObjects, it.first, it.second.targetShortcut);
}
lock.unlock();
@ -158,7 +162,7 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
Shortcut originalShortcut = ShortcutControl::shortcutRemapBuffer[i][0];
Shortcut newShortcut = ShortcutControl::shortcutRemapBuffer[i][1];
if (originalShortcut.IsValidShortcut() && originalShortcut.IsValidShortcut())
if (originalShortcut.IsValidShortcut() && newShortcut.IsValidShortcut())
{
bool result = keyboardManagerState.AddOSLevelShortcut(originalShortcut, newShortcut);
if (!result)
@ -197,7 +201,7 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
addShortcut.Content(plusSymbol);
addShortcut.Margin({ 10 });
addShortcut.Click([&](IInspectable const& sender, RoutedEventArgs const&) {
ShortcutControl::AddNewShortcutControlRow(shortcutTable);
ShortcutControl::AddNewShortcutControlRow(shortcutTable, keyboardRemapControlObjects);
});
xamlContainer.Children().Append(header);

View File

@ -0,0 +1,113 @@
#include "pch.h"
#include "KeyDropDownControl.h"
// Initialized to null
KeyboardManagerState* KeyDropDownControl::keyboardManagerState = nullptr;
// Function to set properties apart from the SelectionChanged event handler
void KeyDropDownControl::SetDefaultProperties(bool isShortcut)
{
dropDown.Width(100);
dropDown.MaxDropDownHeight(200);
// Initialise layout attribute
previousLayout = GetKeyboardLayout(0);
keyCodeList = keyboardManagerState->keyboardMap.GetKeyCodeList(isShortcut);
dropDown.ItemsSource(keyboardManagerState->keyboardMap.GetKeyNameList(isShortcut));
// drop down open handler - to reload the items with the latest layout
dropDown.DropDownOpened([&, isShortcut](IInspectable const& sender, auto args) {
ComboBox currentDropDown = sender.as<ComboBox>();
CheckAndUpdateKeyboardLayout(currentDropDown, isShortcut);
});
}
// Function to check if the layout has changed and accordingly update the drop down list
void KeyDropDownControl::CheckAndUpdateKeyboardLayout(ComboBox currentDropDown, bool isShortcut)
{
// Get keyboard layout for current thread
HKL layout = GetKeyboardLayout(0);
// Check if the layout has changed
if (previousLayout != layout)
{
keyCodeList = keyboardManagerState->keyboardMap.GetKeyCodeList(isShortcut);
currentDropDown.ItemsSource(keyboardManagerState->keyboardMap.GetKeyNameList(isShortcut));
previousLayout = layout;
}
}
// Function to set the selected index of the drop down
void KeyDropDownControl::SetSelectedIndex(int32_t index)
{
dropDown.SelectedIndex(index);
}
// Function to return the combo box element of the drop down
ComboBox KeyDropDownControl::GetComboBox()
{
return dropDown;
}
// Function to add a drop down to the shortcut stack panel
void KeyDropDownControl::AddDropDown(StackPanel parent, const size_t rowIndex, const size_t colIndex, std::vector<std::vector<Shortcut>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects)
{
keyDropDownControlObjects.push_back(std::move(std::unique_ptr<KeyDropDownControl>(new KeyDropDownControl(rowIndex, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, parent))));
// Flyout to display the warning on the drop down element
Flyout warningFlyout;
TextBlock warningMessage;
warningFlyout.Content(warningMessage);
parent.Children().Append(keyDropDownControlObjects[keyDropDownControlObjects.size() - 1]->GetComboBox());
parent.UpdateLayout();
}
// Function to get the list of key codes from the shortcut combo box stack panel
std::vector<DWORD> KeyDropDownControl::GetKeysFromStackPanel(StackPanel parent)
{
std::vector<DWORD> keys;
std::vector<DWORD> keyCodeList = keyboardManagerState->keyboardMap.GetKeyCodeList(true);
for (int i = 0; i < (int)parent.Children().Size(); i++)
{
ComboBox currentDropDown = parent.Children().GetAt(i).as<ComboBox>();
int selectedKeyIndex = currentDropDown.SelectedIndex();
if (selectedKeyIndex != -1 && keyCodeList.size() > selectedKeyIndex)
{
// If None is not the selected key
if (keyCodeList[selectedKeyIndex] != 0)
{
keys.push_back(keyCodeList[selectedKeyIndex]);
}
}
}
return keys;
}
// Function to check if a modifier has been repeated in the previous drop downs
bool KeyDropDownControl::CheckRepeatedModifier(StackPanel parent, uint32_t dropDownIndex, int selectedKeyIndex, const std::vector<DWORD>& keyCodeList)
{
// check if modifier has already been added before in a previous drop down
std::vector<DWORD> currentKeys = GetKeysFromStackPanel(parent);
bool matchPreviousModifier = false;
for (int i = 0; i < currentKeys.size(); i++)
{
// Skip the current drop down
if (i != dropDownIndex)
{
// If the key type for the newly added key matches any of the existing keys in the shortcut
if (GetKeyType(keyCodeList[selectedKeyIndex]) == GetKeyType(currentKeys[i]))
{
matchPreviousModifier = true;
break;
}
}
}
return matchPreviousModifier;
}
// Function to set the flyout warning message
void KeyDropDownControl::SetDropDownError(ComboBox dropDown, TextBlock messageBlock, hstring message)
{
messageBlock.Text(message);
dropDown.ContextFlyout().ShowAttachedFlyout((FrameworkElement)dropDown);
dropDown.SelectedIndex(-1);
}

View File

@ -0,0 +1,190 @@
#pragma once
#include <keyboardmanager/common/KeyboardManagerState.h>
// Wrapper class for the key drop down menu
class KeyDropDownControl
{
private:
// Stores the drop down combo box
ComboBox dropDown;
// Stores the previous layout
HKL previousLayout = 0;
// Stores the key code list
std::vector<DWORD> keyCodeList;
// Function to set properties apart from the SelectionChanged event handler
void SetDefaultProperties(bool isShortcut);
// Function to check if the layout has changed and accordingly update the drop down list
void CheckAndUpdateKeyboardLayout(ComboBox currentDropDown, bool isShortcut);
public:
// Pointer to the keyboard manager state
static KeyboardManagerState* keyboardManagerState;
// Constructor for single key drop down
KeyDropDownControl(size_t rowIndex, size_t colIndex, std::vector<std::vector<DWORD>>& singleKeyRemapBuffer)
{
SetDefaultProperties(false);
dropDown.SelectionChanged([&, rowIndex, colIndex](IInspectable const& sender, SelectionChangedEventArgs const& args) {
ComboBox currentDropDown = sender.as<ComboBox>();
int selectedKeyIndex = currentDropDown.SelectedIndex();
// Check if the element was not found or the index exceeds the known keys
if (selectedKeyIndex != -1 && keyCodeList.size() > selectedKeyIndex)
{
singleKeyRemapBuffer[rowIndex][colIndex] = keyCodeList[selectedKeyIndex];
}
else
{
// Reset to null if the key is not found
singleKeyRemapBuffer[rowIndex][colIndex] = NULL;
}
});
}
// Constructor for shortcut drop down
KeyDropDownControl(size_t rowIndex, size_t colIndex, std::vector<std::vector<Shortcut>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, StackPanel parent)
{
SetDefaultProperties(true);
Flyout warningFlyout;
TextBlock warningMessage;
warningFlyout.Content(warningMessage);
dropDown.ContextFlyout().SetAttachedFlyout((FrameworkElement)dropDown, warningFlyout);
// drop down selection handler
dropDown.SelectionChanged([&, rowIndex, colIndex, parent, warningMessage](IInspectable const& sender, SelectionChangedEventArgs const&) {
ComboBox currentDropDown = sender.as<ComboBox>();
int selectedKeyIndex = currentDropDown.SelectedIndex();
uint32_t dropDownIndex = -1;
bool dropDownFound = parent.Children().IndexOf(currentDropDown, dropDownIndex);
if (selectedKeyIndex != -1 && keyCodeList.size() > selectedKeyIndex && dropDownFound)
{
// If only 1 drop down and action key is chosen: Warn that a modifier must be chosen
if (parent.Children().Size() == 1 && !IsModifierKey(keyCodeList[selectedKeyIndex]))
{
// warn and reset the drop down
SetDropDownError(currentDropDown, warningMessage, L"Shortcut must start with a modifier key");
}
// If it is the last drop down
else if (dropDownIndex == parent.Children().Size() - 1)
{
// If last drop down and a modifier is selected: add a new drop down (max of 5 drop downs should be enforced)
if (IsModifierKey(keyCodeList[selectedKeyIndex]) && parent.Children().Size() < 5)
{
// If it matched any of the previous modifiers then reset that drop down
if (CheckRepeatedModifier(parent, dropDownIndex, selectedKeyIndex, keyCodeList))
{
// warn and reset the drop down
SetDropDownError(currentDropDown, warningMessage, L"Shortcut cannot contain a repeated modifier");
}
// If not, add a new drop down
else
{
AddDropDown(parent, rowIndex, colIndex, shortcutRemapBuffer, keyDropDownControlObjects);
}
}
// If last drop down and a modifier is selected but there are already 5 drop downs: warn the user
else if (IsModifierKey(keyCodeList[selectedKeyIndex]) && parent.Children().Size() >= 5)
{
// warn and reset the drop down
SetDropDownError(currentDropDown, warningMessage, L"Shortcut must contain an action key");
}
// If None is selected but it's the last index: warn
else if (keyCodeList[selectedKeyIndex] == 0)
{
// warn and reset the drop down
SetDropDownError(currentDropDown, warningMessage, L"Shortcut must contain an action key");
}
// If none of the above, then the action key will be set
}
// If it is the not the last drop down
else
{
if (IsModifierKey(keyCodeList[selectedKeyIndex]))
{
// If it matched any of the previous modifiers then reset that drop down
if (CheckRepeatedModifier(parent, dropDownIndex, selectedKeyIndex, keyCodeList))
{
// warn and reset the drop down
SetDropDownError(currentDropDown, warningMessage, L"Shortcut cannot contain a repeated modifier");
}
// If not, the modifier key will be set
}
// If None is selected and there are more than 2 drop downs
else if (keyCodeList[selectedKeyIndex] == 0 && parent.Children().Size() > 2)
{
// delete drop down
parent.Children().RemoveAt(dropDownIndex);
// delete drop down control object from the vector so that it can be destructed
keyDropDownControlObjects.erase(keyDropDownControlObjects.begin() + dropDownIndex);
parent.UpdateLayout();
}
else if (keyCodeList[selectedKeyIndex] == 0 && parent.Children().Size() <= 2)
{
// warn and reset the drop down
SetDropDownError(currentDropDown, warningMessage, L"Shortcut must have atleast 2 keys");
}
// 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
else if (dropDownIndex != 0)
{
bool isClear = true;
for (int i = dropDownIndex + 1; i < (int)parent.Children().Size(); i++)
{
ComboBox currentDropDown = parent.Children().GetAt(i).as<ComboBox>();
if (currentDropDown.SelectedIndex() != -1)
{
isClear = false;
break;
}
}
if (isClear)
{
// remove all the drop down
int elementsToBeRemoved = parent.Children().Size() - dropDownIndex - 1;
for (int i = 0; i < elementsToBeRemoved; i++)
{
parent.Children().RemoveAtEnd();
}
parent.UpdateLayout();
}
else
{
// warn and reset the drop down
SetDropDownError(currentDropDown, warningMessage, L"Shortcut cannot have more than one action key");
}
}
// If there an action key is chosen on the first drop down and there are more than one drop down menus
else
{
// warn and reset the drop down
SetDropDownError(currentDropDown, warningMessage, L"Shortcut must start with a modifier key");
}
}
}
// Reset the buffer based on the new selected drop down items
shortcutRemapBuffer[rowIndex][colIndex].SetKeyCodes(GetKeysFromStackPanel(parent));
});
}
// Function to set the selected index of the drop down
void SetSelectedIndex(int32_t index);
// Function to return the combo box element of the drop down
ComboBox GetComboBox();
// Function to add a drop down to the shortcut stack panel
static void AddDropDown(StackPanel parent, const size_t rowIndex, const size_t colIndex, std::vector<std::vector<Shortcut>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects);
// Function to get the list of key codes from the shortcut combo box stack panel
std::vector<DWORD> GetKeysFromStackPanel(StackPanel parent);
// Function to check if a modifier has been repeated in the previous drop downs
bool CheckRepeatedModifier(StackPanel parent, uint32_t dropDownIndex, int selectedKeyIndex, const std::vector<DWORD>& keyCodeList);
// Function to set the flyout warning message
void SetDropDownError(ComboBox dropDown, TextBlock messageBlock, hstring message);
};

View File

@ -98,7 +98,7 @@
<ItemGroup>
<ClCompile Include="EditKeyboardWindow.cpp" />
<ClCompile Include="EditShortcutsWindow.cpp" />
<ClCompile Include="MainWindow.cpp" />
<ClCompile Include="KeyDropDownControl.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
@ -109,8 +109,8 @@
<ItemGroup>
<ClInclude Include="EditKeyboardWindow.h" />
<ClInclude Include="EditShortcutsWindow.h" />
<ClInclude Include="KeyDropDownControl.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="MainWindow.h" />
<ClInclude Include="ShortcutControl.h" />
<ClInclude Include="SingleKeyRemapControl.h" />
</ItemGroup>

View File

@ -1,20 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="MainWindow.cpp" />
<ClCompile Include="EditKeyboardWindow.cpp" />
<ClCompile Include="EditShortcutsWindow.cpp" />
<ClCompile Include="ShortcutControl.cpp" />
<ClCompile Include="pch.cpp" />
<ClCompile Include="SingleKeyRemapControl.cpp" />
<ClCompile Include="KeyDropDownControl.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="MainWindow.h" />
<ClInclude Include="EditKeyboardWindow.h" />
<ClInclude Include="EditShortcutsWindow.h" />
<ClInclude Include="ShortcutControl.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="SingleKeyRemapControl.h" />
<ClInclude Include="KeyDropDownControl.h" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View File

@ -1,150 +0,0 @@
#include "pch.h"
#include "MainWindow.h"
#include "EditKeyboardWindow.h"
#include "EditShortcutsWindow.h"
HWND _hWndMain;
HINSTANCE _hInstance;
// This Hwnd will be the window handler for the Xaml Island: A child window that contains Xaml.
HWND hWndXamlIslandMain = nullptr;
// This variable is used to check if window registration has been done to avoid repeated registration leading to an error.
bool isMainWindowRegistrationCompleted = false;
// Function to create the Main Window
void createMainWindow(HINSTANCE hInstance, KeyboardManagerState& keyboardManagerState)
{
_hInstance = hInstance;
// Window Registration
const wchar_t szWindowClass[] = L"MainWindowClass";
if (!isMainWindowRegistrationCompleted)
{
WNDCLASSEX windowClass = {};
windowClass.cbSize = sizeof(WNDCLASSEX);
windowClass.lpfnWndProc = MainWindowProc;
windowClass.hInstance = hInstance;
windowClass.lpszClassName = szWindowClass;
windowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
windowClass.hIconSm = LoadIcon(windowClass.hInstance, IDI_APPLICATION);
if (RegisterClassEx(&windowClass) == NULL)
{
MessageBox(NULL, L"Windows registration failed!", L"Error", NULL);
return;
}
isMainWindowRegistrationCompleted = true;
}
// Window Creation
_hWndMain = CreateWindow(
szWindowClass,
L"Keyboard Manager Settings",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
if (_hWndMain == NULL)
{
MessageBox(NULL, L"Call to CreateWindow failed!", L"Error", NULL);
return;
}
//XAML Island section
// This DesktopWindowXamlSource is the object that enables a non-UWP desktop application
// to host UWP controls in any UI element that is associated with a window handle (HWND).
DesktopWindowXamlSource desktopSource;
// Get handle to corewindow
auto interop = desktopSource.as<IDesktopWindowXamlSourceNative>();
// Parent the DesktopWindowXamlSource object to current window
check_hresult(interop->AttachToWindow(_hWndMain));
// Get the new child window's hwnd
interop->get_WindowHandle(&hWndXamlIslandMain);
// Update the xaml island window size becuase initially is 0,0
SetWindowPos(hWndXamlIslandMain, 0, 0, 0, 800, 800, SWP_SHOWWINDOW);
//Creating the Xaml content
Windows::UI::Xaml::Controls::StackPanel xamlContainer;
xamlContainer.Background(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::LightGray() });
Windows::UI::Xaml::Controls::StackPanel keyRow;
keyRow.Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
keyRow.Spacing(10);
keyRow.Margin({ 10 });
Windows::Foundation::Collections::IVector<Windows::Foundation::IInspectable> keyNames{ single_threaded_vector<Windows::Foundation::IInspectable>(
{ winrt::box_value(L"Alt"),
winrt::box_value(L"Delete"),
winrt::box_value(L"LAlt"),
winrt::box_value(L"LWin"),
winrt::box_value(L"Shift"),
winrt::box_value(L"NumLock"),
winrt::box_value(L"LCtrl") }) };
Windows::UI::Xaml::Controls::ComboBox cb;
cb.IsEditable(true);
cb.Width(200);
cb.ItemsSource(keyNames);
Windows::UI::Xaml::Controls::Button bt;
bt.Content(winrt::box_value(winrt::to_hstring("Edit Keyboard")));
bt.Click([&](IInspectable const& sender, RoutedEventArgs const&) {
std::thread th(createEditKeyboardWindow, _hInstance, std::ref(keyboardManagerState));
th.join();
});
Windows::UI::Xaml::Controls::Button bt2;
bt2.Content(winrt::box_value(winrt::to_hstring("Edit Shortcuts")));
bt2.Click([&](IInspectable const& sender, RoutedEventArgs const&) {
std::thread th(createEditShortcutsWindow, _hInstance, std::ref(keyboardManagerState));
th.join();
});
keyRow.Children().Append(cb);
keyRow.Children().Append(bt);
keyRow.Children().Append(bt2);
xamlContainer.Children().Append(keyRow);
xamlContainer.UpdateLayout();
desktopSource.Content(xamlContainer);
//End XAML Island section
if (_hWndMain)
{
ShowWindow(_hWndMain, SW_SHOW);
UpdateWindow(_hWndMain);
}
//Message loop:
MSG msg = {};
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
desktopSource.Close();
}
LRESULT CALLBACK MainWindowProc(HWND hWnd, UINT messageCode, WPARAM wParam, LPARAM lParam)
{
RECT rcClient;
switch (messageCode)
{
case WM_PAINT:
GetClientRect(hWnd, &rcClient);
SetWindowPos(hWndXamlIslandMain, 0, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom, SWP_SHOWWINDOW);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, messageCode, wParam, lParam);
break;
}
return 0;
}

View File

@ -1,6 +0,0 @@
#pragma once
#include <keyboardmanager/common/KeyboardManagerState.h>
// Function to create the Main Window
__declspec(dllexport) void createMainWindow(HINSTANCE hInstance, KeyboardManagerState& keyboardManagerState);
LRESULT CALLBACK MainWindowProc(HWND, UINT, WPARAM, LPARAM);

View File

@ -1,5 +1,6 @@
#include "pch.h"
#include "ShortcutControl.h"
#include "KeyDropDownControl.h"
//Both static members are initialized to null
HWND ShortcutControl::EditShortcutsWindowHandle = nullptr;
@ -8,7 +9,7 @@ KeyboardManagerState* ShortcutControl::keyboardManagerState = nullptr;
std::vector<std::vector<Shortcut>> ShortcutControl::shortcutRemapBuffer;
// 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, Shortcut originalKeys, Shortcut newKeys)
void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vector<std::vector<std::unique_ptr<ShortcutControl>>>& keyboardRemapControlObjects, Shortcut originalKeys, Shortcut newKeys)
{
// Parent element for the row
Windows::UI::Xaml::Controls::StackPanel tableRow;
@ -16,27 +17,17 @@ void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, Shortcut orig
tableRow.Spacing(100);
tableRow.Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
// Create new ShortcutControl objects dynamically so that we does not get destructed
std::vector<std::unique_ptr<ShortcutControl>> newrow;
newrow.push_back(std::move(std::unique_ptr<ShortcutControl>(new ShortcutControl(shortcutRemapBuffer.size(), 0))));
newrow.push_back(std::move(std::unique_ptr<ShortcutControl>(new ShortcutControl(shortcutRemapBuffer.size(), 1))));
keyboardRemapControlObjects.push_back(std::move(newrow));
// ShortcutControl for the original shortcut
ShortcutControl originalSC(shortcutRemapBuffer.size(), 0);
tableRow.Children().Append(originalSC.getShortcutControl());
tableRow.Children().Append(keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->getShortcutControl());
// ShortcutControl for the new shortcut
ShortcutControl newSC(shortcutRemapBuffer.size(), 1);
tableRow.Children().Append(newSC.getShortcutControl());
// Set the shortcut text if the two vectors are not empty (i.e. default args)
if (!originalKeys.IsEmpty() && !newKeys.IsEmpty())
{
shortcutRemapBuffer.push_back(std::vector<Shortcut>{ originalKeys, newKeys });
originalSC.shortcutText.Text(originalKeys.ToHstring(keyboardManagerState->keyboardMap));
newSC.shortcutText.Text(newKeys.ToHstring(keyboardManagerState->keyboardMap));
}
else
{
// Initialize both shortcuts as empty shortcuts
shortcutRemapBuffer.push_back(std::vector<Shortcut>{ Shortcut(), Shortcut() });
}
tableRow.Children().Append(keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->getShortcutControl());
// Delete row button
Windows::UI::Xaml::Controls::Button deleteShortcut;
FontIcon deleteSymbol;
@ -50,9 +41,57 @@ void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, Shortcut orig
parent.Children().RemoveAt(index);
// delete the row from the buffer. Since first child of the stackpanel is the header, the effective index starts from 1
shortcutRemapBuffer.erase(shortcutRemapBuffer.begin() + (index - 1));
// delete the ShortcutControl objects so that they get destructed
keyboardRemapControlObjects.erase(keyboardRemapControlObjects.begin() + (index - 1));
});
tableRow.Children().Append(deleteShortcut);
tableRow.UpdateLayout();
parent.Children().Append(tableRow);
parent.UpdateLayout();
// Set the shortcut text if the two vectors are not empty (i.e. default args)
if (originalKeys.IsValidShortcut() && newKeys.IsValidShortcut())
{
shortcutRemapBuffer.push_back(std::vector<Shortcut>{ Shortcut(), Shortcut() });
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->AddShortcutToControl(originalKeys, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->shortcutDropDownStackPanel, *keyboardManagerState, shortcutRemapBuffer.size() - 1, 0);
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->AddShortcutToControl(newKeys, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->shortcutDropDownStackPanel, *keyboardManagerState, shortcutRemapBuffer.size() - 1, 1);
}
else
{
// Initialize both shortcuts as empty shortcuts
shortcutRemapBuffer.push_back(std::vector<Shortcut>{ Shortcut(), Shortcut() });
}
}
// Function to add a shortcut to the shortcut control as combo boxes
void ShortcutControl::AddShortcutToControl(Shortcut& shortcut, StackPanel parent, KeyboardManagerState& keyboardManagerState, const size_t rowIndex, const size_t colIndex)
{
// 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();
std::vector<DWORD> keyCodeList = keyboardManagerState.keyboardMap.GetKeyCodeList(true);
if (shortcutKeyCodes.size() != 0)
{
KeyDropDownControl::AddDropDown(parent, rowIndex, colIndex, shortcutRemapBuffer, keyDropDownControlObjects);
for (int i = 0; i < shortcutKeyCodes.size(); i++)
{
// New drop down gets added automatically when the SelectedIndex is set
if (i < (int)parent.Children().Size())
{
ComboBox currentDropDown = parent.Children().GetAt(i).as<ComboBox>();
auto it = std::find(keyCodeList.begin(), keyCodeList.end(), shortcutKeyCodes[i]);
if (it != keyCodeList.end())
{
currentDropDown.SelectedIndex((int32_t)std::distance(keyCodeList.begin(), it));
}
}
}
}
parent.UpdateLayout();
}
// 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
@ -62,7 +101,7 @@ StackPanel ShortcutControl::getShortcutControl()
}
// Function to create the detect shortcut UI window
void ShortcutControl::createDetectShortcutWindow(IInspectable const& sender, XamlRoot xamlRoot, std::vector<std::vector<Shortcut>>& shortcutRemapBuffer, KeyboardManagerState& keyboardManagerState, const int& rowIndex, const int& colIndex)
void ShortcutControl::createDetectShortcutWindow(IInspectable const& sender, XamlRoot xamlRoot, std::vector<std::vector<Shortcut>>& shortcutRemapBuffer, KeyboardManagerState& keyboardManagerState, const size_t rowIndex, const size_t colIndex)
{
// ContentDialog for detecting shortcuts. This is the parent UI element.
ContentDialog detectShortcutBox;
@ -76,7 +115,7 @@ void ShortcutControl::createDetectShortcutWindow(IInspectable const& sender, Xam
detectShortcutBox.IsSecondaryButtonEnabled(false);
// Get the linked text block for the "Type shortcut" button that was clicked
TextBlock linkedShortcutText = getSiblingElement(sender).as<TextBlock>();
StackPanel linkedShortcutStackPanel = getSiblingElement(sender).as<StackPanel>();
auto unregisterKeys = [&keyboardManagerState]() {
std::thread t1(&KeyboardManagerState::UnregisterKeyDelay, &keyboardManagerState, VK_ESCAPE);
@ -90,7 +129,8 @@ void ShortcutControl::createDetectShortcutWindow(IInspectable const& sender, Xam
keyboardManagerState.ResetDetectedShortcutKey(key);
};
auto onAccept = [linkedShortcutText,
auto onAccept = [this,
linkedShortcutStackPanel,
detectShortcutBox,
&keyboardManagerState,
&shortcutRemapBuffer,
@ -102,8 +142,8 @@ void ShortcutControl::createDetectShortcutWindow(IInspectable const& sender, Xam
if (!detectedShortcutKeys.IsEmpty())
{
shortcutRemapBuffer[rowIndex][colIndex] = detectedShortcutKeys;
linkedShortcutText.Text(detectedShortcutKeys.ToHstring(keyboardManagerState.keyboardMap));
// The shortcut buffer gets set in this function
AddShortcutToControl(detectedShortcutKeys, linkedShortcutStackPanel, keyboardManagerState, rowIndex, colIndex);
}
// Reset the keyboard manager UI state

View File

@ -2,6 +2,7 @@
#include <keyboardmanager/common/KeyboardManagerState.h>
#include <keyboardManager/common/Helpers.h>
#include <keyboardmanager/common/Shortcut.h>
#include "KeyDropDownControl.h"
class ShortcutControl
{
@ -9,6 +10,9 @@ private:
// Textblock to display the selected shortcut
TextBlock shortcutText;
// Stack panel for the drop downs to display the selected shortcut
StackPanel shortcutDropDownStackPanel;
// Button to type the shortcut
Button typeShortcut;
@ -22,9 +26,16 @@ public:
static KeyboardManagerState* keyboardManagerState;
// Stores the current list of remappings
static std::vector<std::vector<Shortcut>> shortcutRemapBuffer;
// Vector to store dynamically allocated KeyDropDownControl objects to avoid early destruction
std::vector<std::unique_ptr<KeyDropDownControl>> keyDropDownControlObjects;
ShortcutControl(const int& rowIndex, const int& colIndex)
ShortcutControl(const size_t rowIndex, const size_t colIndex)
{
shortcutDropDownStackPanel.RequestedTheme(ElementTheme::Light);
shortcutDropDownStackPanel.Spacing(10);
shortcutDropDownStackPanel.Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
KeyDropDownControl::AddDropDown(shortcutDropDownStackPanel, rowIndex, colIndex, shortcutRemapBuffer, keyDropDownControlObjects);
typeShortcut.Content(winrt::box_value(winrt::to_hstring("Type Shortcut")));
typeShortcut.Click([&, rowIndex, colIndex](IInspectable const& sender, RoutedEventArgs const&) {
keyboardManagerState->SetUIState(KeyboardManagerUIState::DetectShortcutWindowActivated, EditShortcutsWindowHandle);
@ -37,15 +48,19 @@ public:
shortcutControlLayout.Spacing(10);
shortcutControlLayout.Children().Append(typeShortcut);
shortcutControlLayout.Children().Append(shortcutText);
shortcutControlLayout.Children().Append(shortcutDropDownStackPanel);
shortcutControlLayout.UpdateLayout();
}
// 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, Shortcut originalKeys = Shortcut(), Shortcut newKeys = Shortcut());
static void AddNewShortcutControlRow(StackPanel& parent, std::vector<std::vector<std::unique_ptr<ShortcutControl>>>& keyboardRemapControlObjects, Shortcut originalKeys = Shortcut(), Shortcut newKeys = Shortcut());
// Function to add a shortcut to the shortcut control as combo boxes
void AddShortcutToControl(Shortcut& shortcut, StackPanel parent, KeyboardManagerState& keyboardManagerState, const size_t rowIndex, const size_t colIndex);
// 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();
// Function to create the detect shortcut UI window
void createDetectShortcutWindow(IInspectable const& sender, XamlRoot xamlRoot, std::vector<std::vector<Shortcut>>& shortcutRemapBuffer, KeyboardManagerState& keyboardManagerState, const int& rowIndex, const int& colIndex);
void createDetectShortcutWindow(IInspectable const& sender, XamlRoot xamlRoot, std::vector<std::vector<Shortcut>>& shortcutRemapBuffer, KeyboardManagerState& keyboardManagerState, const size_t rowIndex, const size_t colIndex);
};

View File

@ -8,7 +8,7 @@ KeyboardManagerState* SingleKeyRemapControl::keyboardManagerState = nullptr;
std::vector<std::vector<DWORD>> SingleKeyRemapControl::singleKeyRemapBuffer;
// 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, const DWORD& originalKey, const DWORD& newKey)
void SingleKeyRemapControl::AddNewControlKeyRemapRow(StackPanel& parent, std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>>& keyboardRemapControlObjects, const DWORD originalKey, const DWORD newKey)
{
// Parent element for the row
Windows::UI::Xaml::Controls::StackPanel tableRow;
@ -16,20 +16,31 @@ void SingleKeyRemapControl::AddNewControlKeyRemapRow(StackPanel& parent, const D
tableRow.Spacing(100);
tableRow.Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
// Create new SingleKeyRemapControl objects dynamically so that we does not get destructed
std::vector<std::unique_ptr<SingleKeyRemapControl>> newrow;
newrow.push_back(std::move(std::unique_ptr<SingleKeyRemapControl>(new SingleKeyRemapControl(singleKeyRemapBuffer.size(), 0))));
newrow.push_back(std::move(std::unique_ptr<SingleKeyRemapControl>(new SingleKeyRemapControl(singleKeyRemapBuffer.size(), 1))));
keyboardRemapControlObjects.push_back(std::move(newrow));
// SingleKeyRemapControl for the original key.
SingleKeyRemapControl originalRemapKeyControl(singleKeyRemapBuffer.size(), 0);
tableRow.Children().Append(originalRemapKeyControl.getSingleKeyRemapControl());
// SingleKeyRemapControl for the new remap key.
SingleKeyRemapControl newRemapKeyControl(singleKeyRemapBuffer.size(), 1);
tableRow.Children().Append(newRemapKeyControl.getSingleKeyRemapControl());
tableRow.Children().Append(keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->getSingleKeyRemapControl());
// SingleKeyRemapControl for the new remap key
tableRow.Children().Append(keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->getSingleKeyRemapControl());
// Set the key text if the two keys are not null (i.e. default args)
if (originalKey != NULL && newKey != NULL)
{
singleKeyRemapBuffer.push_back(std::vector<DWORD>{ originalKey, newKey });
originalRemapKeyControl.singleKeyRemapText.Text(winrt::to_hstring(keyboardManagerState->keyboardMap.GetKeyName(originalKey).c_str()));
newRemapKeyControl.singleKeyRemapText.Text(winrt::to_hstring(keyboardManagerState->keyboardMap.GetKeyName(newKey).c_str()));
std::vector<DWORD> keyCodes = keyboardManagerState->keyboardMap.GetKeyCodeList();
auto it = std::find(keyCodes.begin(), keyCodes.end(), originalKey);
if (it != keyCodes.end())
{
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->singleKeyRemapDropDown.SetSelectedIndex((int32_t)std::distance(keyCodes.begin(), it));
}
it = std::find(keyCodes.begin(), keyCodes.end(), newKey);
if (it != keyCodes.end())
{
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->singleKeyRemapDropDown.SetSelectedIndex((int32_t)std::distance(keyCodes.begin(), it));
}
}
else
{
@ -52,6 +63,8 @@ void SingleKeyRemapControl::AddNewControlKeyRemapRow(StackPanel& parent, const D
parent.Children().RemoveAt(index);
// delete the row from the buffer. Since first child of the stackpanel is the header, the effective index starts from 1
singleKeyRemapBuffer.erase(singleKeyRemapBuffer.begin() + (index - 1));
// delete the SingleKeyRemapControl objects so that they get destructed
keyboardRemapControlObjects.erase(keyboardRemapControlObjects.begin() + (index - 1));
});
tableRow.Children().Append(deleteRemapKeys);
parent.Children().Append(tableRow);
@ -64,7 +77,7 @@ StackPanel SingleKeyRemapControl::getSingleKeyRemapControl()
}
// Function to create the detect remap key UI window
void SingleKeyRemapControl::createDetectKeyWindow(IInspectable const& sender, XamlRoot xamlRoot, std::vector<std::vector<DWORD>>& singleKeyRemapBuffer, KeyboardManagerState& keyboardManagerState, const int& rowIndex, const int& colIndex)
void SingleKeyRemapControl::createDetectKeyWindow(IInspectable const& sender, XamlRoot xamlRoot, std::vector<std::vector<DWORD>>& singleKeyRemapBuffer, KeyboardManagerState& keyboardManagerState, const size_t rowIndex, const size_t colIndex)
{
// ContentDialog for detecting remap key. This is the parent UI element.
ContentDialog detectRemapKeyBox;
@ -78,7 +91,7 @@ void SingleKeyRemapControl::createDetectKeyWindow(IInspectable const& sender, Xa
detectRemapKeyBox.IsSecondaryButtonEnabled(false);
// Get the linked text block for the "Type Key" button that was clicked
TextBlock linkedRemapText = getSiblingElement(sender).as<TextBlock>();
ComboBox linkedRemapDropDown = getSiblingElement(sender).as<ComboBox>();
auto unregisterKeys = [&keyboardManagerState]() {
std::thread t1(&KeyboardManagerState::UnregisterKeyDelay, &keyboardManagerState, VK_ESCAPE);
@ -87,7 +100,7 @@ void SingleKeyRemapControl::createDetectKeyWindow(IInspectable const& sender, Xa
t2.detach();
};
auto onAccept = [linkedRemapText,
auto onAccept = [linkedRemapDropDown,
detectRemapKeyBox,
&keyboardManagerState,
&singleKeyRemapBuffer,
@ -99,8 +112,14 @@ void SingleKeyRemapControl::createDetectKeyWindow(IInspectable const& sender, Xa
if (detectedKey != NULL)
{
singleKeyRemapBuffer[rowIndex][colIndex] = detectedKey;
linkedRemapText.Text(winrt::to_hstring(keyboardManagerState.keyboardMap.GetKeyName(detectedKey).c_str()));
std::vector<DWORD> keyCodeList = keyboardManagerState.keyboardMap.GetKeyCodeList();
// Update the drop down list with the new language to ensure that the correct key is displayed
linkedRemapDropDown.ItemsSource(keyboardManagerState.keyboardMap.GetKeyNameList());
auto it = std::find(keyCodeList.begin(), keyCodeList.end(), detectedKey);
if (it != keyCodeList.end())
{
linkedRemapDropDown.SelectedIndex((int32_t)std::distance(keyCodeList.begin(), it));
}
}
// Reset the keyboard manager UI state

View File

@ -1,11 +1,12 @@
#pragma once
#include <keyboardmanager/common/KeyboardManagerState.h>
#include "KeyDropDownControl.h"
class SingleKeyRemapControl
{
private:
// Textblock to display the selected remap key
TextBlock singleKeyRemapText;
// Drop down to display the selected remap key
KeyDropDownControl singleKeyRemapDropDown;
// Button to type the remap key
Button typeKey;
@ -21,7 +22,8 @@ public:
// Stores the current list of remappings
static std::vector<std::vector<DWORD>> singleKeyRemapBuffer;
SingleKeyRemapControl(const int& rowIndex, const int& colIndex)
SingleKeyRemapControl(const size_t rowIndex, const size_t colIndex) :
singleKeyRemapDropDown(rowIndex, colIndex, singleKeyRemapBuffer)
{
typeKey.Content(winrt::box_value(winrt::to_hstring("Type Key")));
typeKey.Background(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::LightGray() });
@ -37,15 +39,16 @@ public:
singleKeyRemapControlLayout.Spacing(10);
singleKeyRemapControlLayout.Children().Append(typeKey);
singleKeyRemapControlLayout.Children().Append(singleKeyRemapText);
singleKeyRemapControlLayout.Children().Append(singleKeyRemapDropDown.GetComboBox());
singleKeyRemapControlLayout.UpdateLayout();
}
// 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, const DWORD& originalKey = NULL, const DWORD& newKey = NULL);
static void AddNewControlKeyRemapRow(StackPanel& parent, std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>>& keyboardRemapControlObjects, const DWORD originalKey = NULL, const DWORD newKey = NULL);
// 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
StackPanel getSingleKeyRemapControl();
// Function to create the detect remap keys UI window
void createDetectKeyWindow(IInspectable const& sender, XamlRoot xamlRoot, std::vector<std::vector<DWORD>>& singleKeyRemapBuffer, KeyboardManagerState& keyboardManagerState, const int& rowIndex, const int& colIndex);
void createDetectKeyWindow(IInspectable const& sender, XamlRoot xamlRoot, std::vector<std::vector<DWORD>>& singleKeyRemapBuffer, KeyboardManagerState& keyboardManagerState, const size_t rowIndex, const size_t colIndex);
};