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
18 changed files with 686 additions and 247 deletions

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);
};