[KBM] decoupling editor and engine (#11133)

This commit is contained in:
Mykhailo Pylyp
2021-05-07 11:16:31 +03:00
committed by GitHub
parent 9461909321
commit 8785fca309
77 changed files with 2509 additions and 2775 deletions

View File

@@ -2,17 +2,20 @@
#include "BufferValidationHelpers.h"
#include <common/interop/shared_constants.h>
#include <keyboardmanager/common/KeyboardManagerConstants.h>
#include <KeyboardManagerEditorStrings.h>
#include <KeyboardManagerConstants.h>
#include <KeyDropDownControl.h>
#include "KeyboardManagerEditorStrings.h"
#include "KeyDropDownControl.h"
#include "UIHelpers.h"
#include "EditorHelpers.h"
#include "EditorConstants.h"
namespace BufferValidationHelpers
{
// Function to validate and update an element of the key remap buffer when the selection has changed
KeyboardManagerHelper::ErrorType ValidateAndUpdateKeyBufferElement(int rowIndex, int colIndex, int selectedKeyCode, RemapBuffer& remapBuffer)
ShortcutErrorType ValidateAndUpdateKeyBufferElement(int rowIndex, int colIndex, int selectedKeyCode, RemapBuffer& remapBuffer)
{
KeyboardManagerHelper::ErrorType errorType = KeyboardManagerHelper::ErrorType::NoError;
ShortcutErrorType errorType = ShortcutErrorType::NoError;
// Check if the element was not found or the index exceeds the known keys
if (selectedKeyCode != -1)
@@ -22,13 +25,13 @@ namespace BufferValidationHelpers
{
if (std::get<DWORD>(remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]) == selectedKeyCode)
{
errorType = KeyboardManagerHelper::ErrorType::MapToSameKey;
errorType = ShortcutErrorType::MapToSameKey;
}
}
// If one column is shortcut and other is key no warning required
if (errorType == KeyboardManagerHelper::ErrorType::NoError && colIndex == 0)
if (errorType == ShortcutErrorType::NoError && colIndex == 0)
{
// Check if the key is already remapped to something else
for (int i = 0; i < remapBuffer.size(); i++)
@@ -37,8 +40,8 @@ namespace BufferValidationHelpers
{
if (remapBuffer[i].first[colIndex].index() == 0)
{
KeyboardManagerHelper::ErrorType result = KeyboardManagerHelper::DoKeysOverlap(std::get<DWORD>(remapBuffer[i].first[colIndex]), selectedKeyCode);
if (result != KeyboardManagerHelper::ErrorType::NoError)
ShortcutErrorType result = EditorHelpers::DoKeysOverlap(std::get<DWORD>(remapBuffer[i].first[colIndex]), selectedKeyCode);
if (result != ShortcutErrorType::NoError)
{
errorType = result;
break;
@@ -51,7 +54,7 @@ namespace BufferValidationHelpers
}
// If there is no error, set the buffer
if (errorType == KeyboardManagerHelper::ErrorType::NoError)
if (errorType == ShortcutErrorType::NoError)
{
remapBuffer[rowIndex].first[colIndex] = selectedKeyCode;
}
@@ -70,32 +73,32 @@ namespace BufferValidationHelpers
}
// Function to validate an element of the shortcut remap buffer when the selection has changed
std::pair<KeyboardManagerHelper::ErrorType, DropDownAction> ValidateShortcutBufferElement(int rowIndex, int colIndex, uint32_t dropDownIndex, const std::vector<int32_t>& selectedCodes, std::wstring appName, bool isHybridControl, const RemapBuffer& remapBuffer, bool dropDownFound)
std::pair<ShortcutErrorType, DropDownAction> ValidateShortcutBufferElement(int rowIndex, int colIndex, uint32_t dropDownIndex, const std::vector<int32_t>& selectedCodes, std::wstring appName, bool isHybridControl, const RemapBuffer& remapBuffer, bool dropDownFound)
{
BufferValidationHelpers::DropDownAction dropDownAction = BufferValidationHelpers::DropDownAction::NoAction;
KeyboardManagerHelper::ErrorType errorType = KeyboardManagerHelper::ErrorType::NoError;
ShortcutErrorType errorType = ShortcutErrorType::NoError;
size_t dropDownCount = selectedCodes.size();
DWORD selectedKeyCode = dropDownFound ? selectedCodes[dropDownIndex] : -1;
if (selectedKeyCode != -1 && dropDownFound)
{
// If only 1 drop down and action key is chosen: Warn that a modifier must be chosen (if the drop down is not for a hybrid scenario)
if (dropDownCount == 1 && !KeyboardManagerHelper::IsModifierKey(selectedKeyCode) && !isHybridControl)
if (dropDownCount == 1 && !Helpers::IsModifierKey(selectedKeyCode) && !isHybridControl)
{
// warn and reset the drop down
errorType = KeyboardManagerHelper::ErrorType::ShortcutStartWithModifier;
errorType = ShortcutErrorType::ShortcutStartWithModifier;
}
else if (dropDownIndex == dropDownCount - 1)
{
// If it is the last drop down
// If last drop down and a modifier is selected: add a new drop down (max drop down count should be enforced)
if (KeyboardManagerHelper::IsModifierKey(selectedKeyCode) && dropDownCount < KeyboardManagerConstants::MaxShortcutSize)
if (Helpers::IsModifierKey(selectedKeyCode) && dropDownCount < EditorConstants::MaxShortcutSize)
{
// If it matched any of the previous modifiers then reset that drop down
if (KeyboardManagerHelper::CheckRepeatedModifier(selectedCodes, selectedKeyCode))
if (EditorHelpers::CheckRepeatedModifier(selectedCodes, selectedKeyCode))
{
// warn and reset the drop down
errorType = KeyboardManagerHelper::ErrorType::ShortcutCannotHaveRepeatedModifier;
errorType = ShortcutErrorType::ShortcutCannotHaveRepeatedModifier;
}
else
{
@@ -103,17 +106,17 @@ namespace BufferValidationHelpers
dropDownAction = BufferValidationHelpers::DropDownAction::AddDropDown;
}
}
else if (KeyboardManagerHelper::IsModifierKey(selectedKeyCode) && dropDownCount >= KeyboardManagerConstants::MaxShortcutSize)
else if (Helpers::IsModifierKey(selectedKeyCode) && dropDownCount >= EditorConstants::MaxShortcutSize)
{
// If last drop down and a modifier is selected but there are already max drop downs: warn the user
// warn and reset the drop down
errorType = KeyboardManagerHelper::ErrorType::ShortcutMaxShortcutSizeOneActionKey;
errorType = ShortcutErrorType::ShortcutMaxShortcutSizeOneActionKey;
}
else if (selectedKeyCode == 0)
{
// If None is selected but it's the last index: warn
// If it is a hybrid control and there are 2 drop downs then deletion is allowed
if (isHybridControl && dropDownCount == KeyboardManagerConstants::MinShortcutSize)
if (isHybridControl && dropDownCount == EditorConstants::MinShortcutSize)
{
// set delete drop down flag
dropDownAction = BufferValidationHelpers::DropDownAction::DeleteDropDown;
@@ -122,40 +125,40 @@ namespace BufferValidationHelpers
else
{
// warn and reset the drop down
errorType = KeyboardManagerHelper::ErrorType::ShortcutOneActionKey;
errorType = ShortcutErrorType::ShortcutOneActionKey;
}
}
else if (selectedKeyCode == CommonSharedConstants::VK_DISABLED && dropDownIndex)
{
// Disable can not be selected if one modifier key has already been selected
errorType = KeyboardManagerHelper::ErrorType::ShortcutDisableAsActionKey;
errorType = ShortcutErrorType::ShortcutDisableAsActionKey;
}
// If none of the above, then the action key will be set
}
else
{
// If it is not the last drop down
if (KeyboardManagerHelper::IsModifierKey(selectedKeyCode))
if (Helpers::IsModifierKey(selectedKeyCode))
{
// If it matched any of the previous modifiers then reset that drop down
if (KeyboardManagerHelper::CheckRepeatedModifier(selectedCodes, selectedKeyCode))
if (EditorHelpers::CheckRepeatedModifier(selectedCodes, selectedKeyCode))
{
// warn and reset the drop down
errorType = KeyboardManagerHelper::ErrorType::ShortcutCannotHaveRepeatedModifier;
errorType = ShortcutErrorType::ShortcutCannotHaveRepeatedModifier;
}
// If not, the modifier key will be set
}
else if (selectedKeyCode == 0 && dropDownCount > KeyboardManagerConstants::MinShortcutSize)
else if (selectedKeyCode == 0 && dropDownCount > EditorConstants::MinShortcutSize)
{
// If None is selected and there are more than 2 drop downs
// set delete drop down flag
dropDownAction = BufferValidationHelpers::DropDownAction::DeleteDropDown;
// do not delete the drop down now since there may be some other error which would cause the drop down to be invalid after removal
}
else if (selectedKeyCode == 0 && dropDownCount <= KeyboardManagerConstants::MinShortcutSize)
else if (selectedKeyCode == 0 && dropDownCount <= EditorConstants::MinShortcutSize)
{
// If it is a hybrid control and there are 2 drop downs then deletion is allowed
if (isHybridControl && dropDownCount == KeyboardManagerConstants::MinShortcutSize)
if (isHybridControl && dropDownCount == EditorConstants::MinShortcutSize)
{
// set delete drop down flag
dropDownAction = BufferValidationHelpers::DropDownAction::DeleteDropDown;
@@ -164,13 +167,13 @@ namespace BufferValidationHelpers
else
{
// warn and reset the drop down
errorType = KeyboardManagerHelper::ErrorType::ShortcutAtleast2Keys;
errorType = ShortcutErrorType::ShortcutAtleast2Keys;
}
}
else if (selectedKeyCode == CommonSharedConstants::VK_DISABLED && dropDownIndex)
{
// Allow selection of VK_DISABLE only in first dropdown
errorType = KeyboardManagerHelper::ErrorType::ShortcutDisableAsActionKey;
errorType = ShortcutErrorType::ShortcutDisableAsActionKey;
}
else if (dropDownIndex != 0 || isHybridControl)
{
@@ -193,20 +196,20 @@ namespace BufferValidationHelpers
else
{
// warn and reset the drop down
errorType = KeyboardManagerHelper::ErrorType::ShortcutNotMoreThanOneActionKey;
errorType = ShortcutErrorType::ShortcutNotMoreThanOneActionKey;
}
}
else
{
// If there an action key is chosen on the first drop down and there are more than one drop down menus
// warn and reset the drop down
errorType = KeyboardManagerHelper::ErrorType::ShortcutStartWithModifier;
errorType = ShortcutErrorType::ShortcutStartWithModifier;
}
}
}
// After validating the shortcut, now for errors like remap to same shortcut, remap shortcut more than once, Win L and Ctrl Alt Del
if (errorType == KeyboardManagerHelper::ErrorType::NoError)
if (errorType == ShortcutErrorType::NoError)
{
KeyShortcutUnion tempShortcut;
if (isHybridControl && KeyDropDownControl::GetNumberOfSelectedKeys(selectedCodes) == 1)
@@ -234,9 +237,10 @@ namespace BufferValidationHelpers
// If shortcut to shortcut
if (remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)].index() == 1)
{
if (std::get<Shortcut>(remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]) == std::get<Shortcut>(tempShortcut) && std::get<Shortcut>(remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]).IsValidShortcut() && std::get<Shortcut>(tempShortcut).IsValidShortcut())
auto& shortcut = std::get<Shortcut>(remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]);
if (shortcut == std::get<Shortcut>(tempShortcut) && EditorHelpers::IsValidShortcut(shortcut) && EditorHelpers::IsValidShortcut(std::get<Shortcut>(tempShortcut)))
{
errorType = KeyboardManagerHelper::ErrorType::MapToSameShortcut;
errorType = ShortcutErrorType::MapToSameShortcut;
}
}
@@ -249,14 +253,14 @@ namespace BufferValidationHelpers
{
if (std::get<DWORD>(remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]) == std::get<DWORD>(tempShortcut) && std::get<DWORD>(remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]) != NULL && std::get<DWORD>(tempShortcut) != NULL)
{
errorType = KeyboardManagerHelper::ErrorType::MapToSameKey;
errorType = ShortcutErrorType::MapToSameKey;
}
}
// If one column is shortcut and other is key no warning required
}
if (errorType == KeyboardManagerHelper::ErrorType::NoError && colIndex == 0)
if (errorType == ShortcutErrorType::NoError && colIndex == 0)
{
// Check if the key is already remapped to something else for the same target app
for (int i = 0; i < remapBuffer.size(); i++)
@@ -266,10 +270,10 @@ namespace BufferValidationHelpers
if (i != rowIndex && currAppName == appName)
{
KeyboardManagerHelper::ErrorType result = KeyboardManagerHelper::ErrorType::NoError;
ShortcutErrorType result = ShortcutErrorType::NoError;
if (!isHybridControl)
{
result = Shortcut::DoKeysOverlap(std::get<Shortcut>(remapBuffer[i].first[colIndex]), std::get<Shortcut>(tempShortcut));
result = EditorHelpers::DoShortcutsOverlap(std::get<Shortcut>(remapBuffer[i].first[colIndex]), std::get<Shortcut>(tempShortcut));
}
else
{
@@ -277,19 +281,20 @@ namespace BufferValidationHelpers
{
if (std::get<DWORD>(tempShortcut) != NULL && std::get<DWORD>(remapBuffer[i].first[colIndex]) != NULL)
{
result = KeyboardManagerHelper::DoKeysOverlap(std::get<DWORD>(remapBuffer[i].first[colIndex]), std::get<DWORD>(tempShortcut));
result = EditorHelpers::DoKeysOverlap(std::get<DWORD>(remapBuffer[i].first[colIndex]), std::get<DWORD>(tempShortcut));
}
}
else if (tempShortcut.index() == 1 && remapBuffer[i].first[colIndex].index() == 1)
{
if (std::get<Shortcut>(tempShortcut).IsValidShortcut() && std::get<Shortcut>(remapBuffer[i].first[colIndex]).IsValidShortcut())
auto& shortcut = std::get<Shortcut>(remapBuffer[i].first[colIndex]);
if (EditorHelpers::IsValidShortcut(std::get<Shortcut>(tempShortcut)) && EditorHelpers::IsValidShortcut(shortcut))
{
result = Shortcut::DoKeysOverlap(std::get<Shortcut>(remapBuffer[i].first[colIndex]), std::get<Shortcut>(tempShortcut));
result = EditorHelpers::DoShortcutsOverlap(std::get<Shortcut>(remapBuffer[i].first[colIndex]), std::get<Shortcut>(tempShortcut));
}
}
// Other scenarios not possible since key to shortcut is with key to key, and shortcut to key is with shortcut to shortcut
}
if (result != KeyboardManagerHelper::ErrorType::NoError)
if (result != ShortcutErrorType::NoError)
{
errorType = result;
break;
@@ -298,9 +303,9 @@ namespace BufferValidationHelpers
}
}
if (errorType == KeyboardManagerHelper::ErrorType::NoError && tempShortcut.index() == 1)
if (errorType == ShortcutErrorType::NoError && tempShortcut.index() == 1)
{
errorType = std::get<Shortcut>(tempShortcut).IsShortcutIllegal();
errorType = EditorHelpers::IsShortcutIllegal(std::get<Shortcut>(tempShortcut));
}
}

View File

@@ -2,6 +2,8 @@
#include <keyboardmanager/common/Helpers.h>
#include "ShortcutErrorType.h"
namespace BufferValidationHelpers
{
enum class DropDownAction
@@ -13,8 +15,8 @@ namespace BufferValidationHelpers
};
// Function to validate and update an element of the key remap buffer when the selection has changed
KeyboardManagerHelper::ErrorType ValidateAndUpdateKeyBufferElement(int rowIndex, int colIndex, int selectedKeyCode, RemapBuffer& remapBuffer);
ShortcutErrorType ValidateAndUpdateKeyBufferElement(int rowIndex, int colIndex, int selectedKeyCode, RemapBuffer& remapBuffer);
// Function to validate an element of the shortcut remap buffer when the selection has changed
std::pair<KeyboardManagerHelper::ErrorType, DropDownAction> ValidateShortcutBufferElement(int rowIndex, int colIndex, uint32_t dropDownIndex, const std::vector<int32_t>& selectedCodes, std::wstring appName, bool isHybridControl, const RemapBuffer& remapBuffer, bool dropDownFound);
std::pair<ShortcutErrorType, DropDownAction> ValidateShortcutBufferElement(int rowIndex, int colIndex, uint32_t dropDownIndex, const std::vector<int32_t>& selectedCodes, std::wstring appName, bool isHybridControl, const RemapBuffer& remapBuffer, bool dropDownFound);
}

View File

@@ -6,12 +6,13 @@
#include <common/interop/shared_constants.h>
#include <common/themes/windows_colors.h>
#include <common/utils/EventLocker.h>
#include <common/utils/winapi_error.h>
#include <keyboardmanager/common/KeyboardManagerConstants.h>
#include <keyboardmanager/common/MappingConfiguration.h>
#include <KeyboardManagerConstants.h>
#include <KeyboardManagerState.h>
#include "EditKeyboardWindow.h"
#include "ErrorTypes.h"
#include "SingleKeyRemapControl.h"
#include "KeyDropDownControl.h"
#include "XamlBridge.h"
@@ -19,7 +20,8 @@
#include "Dialog.h"
#include "LoadingAndSavingRemappingHelper.h"
#include "UIHelpers.h"
#include <common/utils/winapi_error.h>
#include "ShortcutErrorType.h"
#include "EditorConstants.h"
using namespace winrt::Windows::Foundation;
@@ -41,7 +43,7 @@ std::mutex editKeyboardWindowMutex;
static XamlBridge* xamlBridgePtr = nullptr;
static IAsyncOperation<bool> OrphanKeysConfirmationDialog(
KeyboardManagerState& state,
KBMEditor::KeyboardManagerState& state,
const std::vector<DWORD>& keys,
XamlRoot root)
{
@@ -73,11 +75,11 @@ static IAsyncOperation<bool> OrphanKeysConfirmationDialog(
co_return res == ContentDialogResult::Primary;
}
static IAsyncAction OnClickAccept(KeyboardManagerState& keyboardManagerState, XamlRoot root, std::function<void()> ApplyRemappings)
static IAsyncAction OnClickAccept(KBMEditor::KeyboardManagerState& keyboardManagerState, XamlRoot root, std::function<void()> ApplyRemappings)
{
KeyboardManagerHelper::ErrorType isSuccess = LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(SingleKeyRemapControl::singleKeyRemapBuffer);
ShortcutErrorType isSuccess = LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(SingleKeyRemapControl::singleKeyRemapBuffer);
if (isSuccess != KeyboardManagerHelper::ErrorType::NoError)
if (isSuccess != ShortcutErrorType::NoError)
{
if (!co_await Dialog::PartialRemappingConfirmationDialog(root, GET_RESOURCE_STRING(IDS_EDITKEYBOARD_PARTIALCONFIRMATIONDIALOGTITLE)))
{
@@ -100,7 +102,7 @@ static IAsyncAction OnClickAccept(KeyboardManagerState& keyboardManagerState, Xa
}
// Function to create the Edit Keyboard Window
inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState)
inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardManagerState& keyboardManagerState, MappingConfiguration& mappingConfiguration)
{
Logger::trace("CreateEditKeyboardWindowImpl()");
auto locker = EventLocker::Get(KeyboardManagerConstants::EditorWindowEventName.c_str());
@@ -142,8 +144,8 @@ inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KeyboardManagerState&
RECT desktopRect = UIHelpers::GetForegroundWindowDesktopRect();
// Calculate DPI dependent window size
int windowWidth = KeyboardManagerConstants::DefaultEditKeyboardWindowWidth;
int windowHeight = KeyboardManagerConstants::DefaultEditKeyboardWindowHeight;
int windowWidth = EditorConstants::DefaultEditKeyboardWindowWidth;
int windowHeight = EditorConstants::DefaultEditKeyboardWindowHeight;
DPIAware::ConvertByCursorPosition(windowWidth, windowHeight);
DPIAware::GetScreenDPIForCursor(g_currentDPI);
@@ -231,7 +233,7 @@ inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KeyboardManagerState&
TextBlock originalKeyRemapHeader;
originalKeyRemapHeader.Text(GET_RESOURCE_STRING(IDS_EDITKEYBOARD_SOURCEHEADER));
originalKeyRemapHeader.FontWeight(Text::FontWeights::Bold());
StackPanel originalKeyHeaderContainer = UIHelpers::GetWrapped(originalKeyRemapHeader, KeyboardManagerConstants::RemapTableDropDownWidth + KeyboardManagerConstants::TableArrowColWidth).as<StackPanel>();
StackPanel originalKeyHeaderContainer = UIHelpers::GetWrapped(originalKeyRemapHeader, EditorConstants::RemapTableDropDownWidth + EditorConstants::TableArrowColWidth).as<StackPanel>();
// Second header textblock in the header row of the keys remap table
TextBlock newKeyRemapHeader;
@@ -250,6 +252,7 @@ inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KeyboardManagerState&
// Store keyboard manager state
SingleKeyRemapControl::keyboardManagerState = &keyboardManagerState;
KeyDropDownControl::keyboardManagerState = &keyboardManagerState;
KeyDropDownControl::mappingConfiguration = &mappingConfiguration;
// Clear the single key remap buffer
SingleKeyRemapControl::singleKeyRemapBuffer.clear();
@@ -258,10 +261,10 @@ inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KeyboardManagerState&
std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>> keyboardRemapControlObjects;
// Set keyboard manager UI state so that remaps are not applied while on this window
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, _hWndEditKeyboardWindow);
keyboardManagerState.SetUIState(KBMEditor::KeyboardManagerUIState::EditKeyboardWindowActivated, _hWndEditKeyboardWindow);
// Load existing remaps into UI
SingleKeyRemapTable singleKeyRemapCopy = keyboardManagerState.singleKeyReMap;
SingleKeyRemapTable singleKeyRemapCopy = mappingConfiguration.singleKeyReMap;
LoadingAndSavingRemappingHelper::PreProcessRemapTable(singleKeyRemapCopy);
@@ -274,14 +277,14 @@ inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KeyboardManagerState&
Button applyButton;
applyButton.Content(winrt::box_value(GET_RESOURCE_STRING(IDS_OK_BUTTON)));
applyButton.Style(AccentButtonStyle());
applyButton.MinWidth(KeyboardManagerConstants::HeaderButtonWidth);
cancelButton.MinWidth(KeyboardManagerConstants::HeaderButtonWidth);
applyButton.MinWidth(EditorConstants::HeaderButtonWidth);
cancelButton.MinWidth(EditorConstants::HeaderButtonWidth);
header.SetAlignRightWithPanel(cancelButton, true);
header.SetLeftOf(applyButton, cancelButton);
auto ApplyRemappings = [&keyboardManagerState, _hWndEditKeyboardWindow]() {
LoadingAndSavingRemappingHelper::ApplySingleKeyRemappings(keyboardManagerState, SingleKeyRemapControl::singleKeyRemapBuffer, true);
bool saveResult = keyboardManagerState.SaveConfigToFile();
auto ApplyRemappings = [&mappingConfiguration, _hWndEditKeyboardWindow]() {
LoadingAndSavingRemappingHelper::ApplySingleKeyRemappings(mappingConfiguration, SingleKeyRemapControl::singleKeyRemapBuffer, true);
bool saveResult = mappingConfiguration.SaveSettingsToFile();
PostMessage(_hWndEditKeyboardWindow, WM_CLOSE, 0, 0);
};
@@ -313,7 +316,7 @@ inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KeyboardManagerState&
scrollViewer.ChangeView(nullptr, scrollViewer.ScrollableHeight(), nullptr);
// Set focus to the first Type Button in the newly added row
UIHelpers::SetFocusOnTypeButtonInLastRow(keyRemapTable, KeyboardManagerConstants::RemapTableColCount);
UIHelpers::SetFocusOnTypeButtonInLastRow(keyRemapTable, EditorConstants::RemapTableColCount);
});
// Set accessible name for the addRemapKey button
@@ -376,10 +379,10 @@ inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KeyboardManagerState&
xamlBridge.ClearXamlIslands();
}
void CreateEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState)
void CreateEditKeyboardWindow(HINSTANCE hInst, KBMEditor::KeyboardManagerState& keyboardManagerState, MappingConfiguration& mappingConfiguration)
{
// Move implementation into the separate method so resources get destroyed correctly
CreateEditKeyboardWindowImpl(hInst, keyboardManagerState);
CreateEditKeyboardWindowImpl(hInst, keyboardManagerState, mappingConfiguration);
// Calling ClearXamlIslands() outside of the message loop is not enough to prevent
// Microsoft.UI.XAML.dll from crashing during deinitialization, see https://github.com/microsoft/PowerToys/issues/10906
@@ -405,8 +408,8 @@ LRESULT CALLBACK EditKeyboardWindowProc(HWND hWnd, UINT messageCode, WPARAM wPar
case WM_GETMINMAXINFO:
{
LPMINMAXINFO mmi = (LPMINMAXINFO)lParam;
int minWidth = KeyboardManagerConstants::MinimumEditKeyboardWindowWidth;
int minHeight = KeyboardManagerConstants::MinimumEditKeyboardWindowHeight;
int minWidth = EditorConstants::MinimumEditKeyboardWindowWidth;
int minHeight = EditorConstants::MinimumEditKeyboardWindowHeight;
DPIAware::Convert(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONULL), minWidth, minHeight);
mmi->ptMinTrackSize.x = minWidth;
mmi->ptMinTrackSize.y = minHeight;

View File

@@ -1,8 +1,14 @@
#pragma once
class KeyboardManagerState;
namespace KBMEditor
{
class KeyboardManagerState;
}
class MappingConfiguration;
// Function to create the Edit Keyboard Window
void CreateEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState);
void CreateEditKeyboardWindow(HINSTANCE hInst, KBMEditor::KeyboardManagerState& keyboardManagerState, MappingConfiguration& mappingConfiguration);
// Function to check if there is already a window active if yes bring to foreground
bool CheckEditKeyboardWindowActive();

View File

@@ -3,18 +3,19 @@
#include <common/Display/dpi_aware.h>
#include <common/utils/EventLocker.h>
#include <KeyboardManagerState.h>
#include <Dialog.h>
#include <ErrorTypes.h>
#include <KeyDropDownControl.h>
#include <LoadingAndSavingRemappingHelper.h>
#include <ShortcutControl.h>
#include <Styles.h>
#include <UIHelpers.h>
#include <XamlBridge.h>
#include <common/utils/winapi_error.h>
#include <keyboardmanager/common/MappingConfiguration.h>
#include "KeyboardManagerState.h"
#include "Dialog.h"
#include "KeyDropDownControl.h"
#include "LoadingAndSavingRemappingHelper.h"
#include "ShortcutControl.h"
#include "Styles.h"
#include "UIHelpers.h"
#include "XamlBridge.h"
#include "ShortcutErrorType.h"
#include "EditorConstants.h"
using namespace winrt::Windows::Foundation;
@@ -36,13 +37,13 @@ std::mutex editShortcutsWindowMutex;
static XamlBridge* xamlBridgePtr = nullptr;
static IAsyncAction OnClickAccept(
KeyboardManagerState& keyboardManagerState,
KBMEditor::KeyboardManagerState& keyboardManagerState,
XamlRoot root,
std::function<void()> ApplyRemappings)
{
KeyboardManagerHelper::ErrorType isSuccess = LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(ShortcutControl::shortcutRemapBuffer);
ShortcutErrorType isSuccess = LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(ShortcutControl::shortcutRemapBuffer);
if (isSuccess != KeyboardManagerHelper::ErrorType::NoError)
if (isSuccess != ShortcutErrorType::NoError)
{
if (!co_await Dialog::PartialRemappingConfirmationDialog(root, GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_PARTIALCONFIRMATIONDIALOGTITLE)))
{
@@ -53,7 +54,7 @@ static IAsyncAction OnClickAccept(
}
// Function to create the Edit Shortcuts Window
inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState)
inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardManagerState& keyboardManagerState, MappingConfiguration& mappingConfiguration)
{
Logger::trace("CreateEditShortcutsWindowImpl()");
auto locker = EventLocker::Get(KeyboardManagerConstants::EditorWindowEventName.c_str());
@@ -95,8 +96,8 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KeyboardManagerState&
RECT desktopRect = UIHelpers::GetForegroundWindowDesktopRect();
// Calculate DPI dependent window size
int windowWidth = KeyboardManagerConstants::DefaultEditShortcutsWindowWidth;
int windowHeight = KeyboardManagerConstants::DefaultEditShortcutsWindowHeight;
int windowWidth = EditorConstants::DefaultEditShortcutsWindowWidth;
int windowHeight = EditorConstants::DefaultEditShortcutsWindowHeight;
DPIAware::ConvertByCursorPosition(windowWidth, windowHeight);
DPIAware::GetScreenDPIForCursor(g_currentDPI);
@@ -198,9 +199,9 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KeyboardManagerState&
StackPanel tableHeader = StackPanel();
tableHeader.Orientation(Orientation::Horizontal);
tableHeader.Margin({ 10, 0, 0, 10 });
auto originalShortcutContainer = UIHelpers::GetWrapped(originalShortcutHeader, KeyboardManagerConstants::ShortcutOriginColumnWidth + (double)KeyboardManagerConstants::ShortcutArrowColumnWidth);
auto originalShortcutContainer = UIHelpers::GetWrapped(originalShortcutHeader, EditorConstants::ShortcutOriginColumnWidth + (double)EditorConstants::ShortcutArrowColumnWidth);
tableHeader.Children().Append(originalShortcutContainer.as<FrameworkElement>());
auto newShortcutHeaderContainer = UIHelpers::GetWrapped(newShortcutHeader, KeyboardManagerConstants::ShortcutTargetColumnWidth);
auto newShortcutHeaderContainer = UIHelpers::GetWrapped(newShortcutHeader, EditorConstants::ShortcutTargetColumnWidth);
tableHeader.Children().Append(newShortcutHeaderContainer.as<FrameworkElement>());
tableHeader.Children().Append(targetAppHeader);
@@ -210,6 +211,7 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KeyboardManagerState&
// Store keyboard manager state
ShortcutControl::keyboardManagerState = &keyboardManagerState;
KeyDropDownControl::keyboardManagerState = &keyboardManagerState;
KeyDropDownControl::mappingConfiguration = &mappingConfiguration;
// Clear the shortcut remap buffer
ShortcutControl::shortcutRemapBuffer.clear();
@@ -218,11 +220,11 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KeyboardManagerState&
std::vector<std::vector<std::unique_ptr<ShortcutControl>>> keyboardRemapControlObjects;
// Set keyboard manager UI state so that shortcut remaps are not applied while on this window
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, _hWndEditShortcutsWindow);
keyboardManagerState.SetUIState(KBMEditor::KeyboardManagerUIState::EditShortcutsWindowActivated, _hWndEditShortcutsWindow);
// Load existing os level shortcuts into UI
// Create copy of the remaps to avoid concurrent access
ShortcutRemapTable osLevelShortcutReMapCopy = keyboardManagerState.osLevelShortcutReMap;
ShortcutRemapTable osLevelShortcutReMapCopy = mappingConfiguration.osLevelShortcutReMap;
for (const auto& it : osLevelShortcutReMapCopy)
{
@@ -231,7 +233,7 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KeyboardManagerState&
// Load existing app-specific shortcuts into UI
// Create copy of the remaps to avoid concurrent access
AppSpecificShortcutRemapTable appSpecificShortcutReMapCopy = keyboardManagerState.appSpecificShortcutReMap;
AppSpecificShortcutRemapTable appSpecificShortcutReMapCopy = mappingConfiguration.appSpecificShortcutReMap;
// Iterate through all the apps
for (const auto& itApp : appSpecificShortcutReMapCopy)
@@ -247,14 +249,14 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KeyboardManagerState&
Button applyButton;
applyButton.Content(winrt::box_value(GET_RESOURCE_STRING(IDS_OK_BUTTON)));
applyButton.Style(AccentButtonStyle());
applyButton.MinWidth(KeyboardManagerConstants::HeaderButtonWidth);
cancelButton.MinWidth(KeyboardManagerConstants::HeaderButtonWidth);
applyButton.MinWidth(EditorConstants::HeaderButtonWidth);
cancelButton.MinWidth(EditorConstants::HeaderButtonWidth);
header.SetAlignRightWithPanel(cancelButton, true);
header.SetLeftOf(applyButton, cancelButton);
auto ApplyRemappings = [&keyboardManagerState, _hWndEditShortcutsWindow]() {
LoadingAndSavingRemappingHelper::ApplyShortcutRemappings(keyboardManagerState, ShortcutControl::shortcutRemapBuffer, true);
bool saveResult = keyboardManagerState.SaveConfigToFile();
auto ApplyRemappings = [&mappingConfiguration, _hWndEditShortcutsWindow]() {
LoadingAndSavingRemappingHelper::ApplyShortcutRemappings(mappingConfiguration, ShortcutControl::shortcutRemapBuffer, true);
bool saveResult = mappingConfiguration.SaveSettingsToFile();
PostMessage(_hWndEditShortcutsWindow, WM_CLOSE, 0, 0);
};
@@ -286,7 +288,7 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KeyboardManagerState&
scrollViewer.ChangeView(nullptr, scrollViewer.ScrollableHeight(), nullptr);
// Set focus to the first Type Button in the newly added row
UIHelpers::SetFocusOnTypeButtonInLastRow(shortcutTable, KeyboardManagerConstants::ShortcutTableColCount);
UIHelpers::SetFocusOnTypeButtonInLastRow(shortcutTable, EditorConstants::ShortcutTableColCount);
});
// Set accessible name for the add shortcut button
@@ -349,10 +351,10 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KeyboardManagerState&
xamlBridge.ClearXamlIslands();
}
void CreateEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState)
void CreateEditShortcutsWindow(HINSTANCE hInst, KBMEditor::KeyboardManagerState& keyboardManagerState, MappingConfiguration& mappingConfiguration)
{
// Move implementation into the separate method so resources get destroyed correctly
CreateEditShortcutsWindowImpl(hInst, keyboardManagerState);
CreateEditShortcutsWindowImpl(hInst, keyboardManagerState, mappingConfiguration);
// Calling ClearXamlIslands() outside of the message loop is not enough to prevent
// Microsoft.UI.XAML.dll from crashing during deinitialization, see https://github.com/microsoft/PowerToys/issues/10906
@@ -378,8 +380,8 @@ LRESULT CALLBACK EditShortcutsWindowProc(HWND hWnd, UINT messageCode, WPARAM wPa
case WM_GETMINMAXINFO:
{
LPMINMAXINFO mmi = (LPMINMAXINFO)lParam;
int minWidth = KeyboardManagerConstants::MinimumEditShortcutsWindowWidth;
int minHeight = KeyboardManagerConstants::MinimumEditShortcutsWindowHeight;
int minWidth = EditorConstants::MinimumEditShortcutsWindowWidth;
int minHeight = EditorConstants::MinimumEditShortcutsWindowHeight;
DPIAware::Convert(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONULL), minWidth, minHeight);
mmi->ptMinTrackSize.x = minWidth;
mmi->ptMinTrackSize.y = minHeight;

View File

@@ -1,8 +1,14 @@
#pragma once
class KeyboardManagerState;
namespace KBMEditor
{
class KeyboardManagerState;
}
class MappingConfiguration;
// Function to create the Edit Shortcuts Window
void CreateEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState);
void CreateEditShortcutsWindow(HINSTANCE hInst, KBMEditor::KeyboardManagerState& keyboardManagerState, MappingConfiguration& mappingConfiguration);
// Function to check if there is already a window active if yes bring to foreground
bool CheckEditShortcutsWindowActive();

View File

@@ -0,0 +1,53 @@
#pragma once
namespace EditorConstants
{
// Default window sizes
inline const int DefaultEditKeyboardWindowWidth = 800;
inline const int DefaultEditKeyboardWindowHeight = 600;
inline const int MinimumEditKeyboardWindowWidth = 500;
inline const int MinimumEditKeyboardWindowHeight = 450;
inline const int EditKeyboardTableMinWidth = 700;
inline const int DefaultEditShortcutsWindowWidth = 1050;
inline const int DefaultEditShortcutsWindowHeight = 600;
inline const int MinimumEditShortcutsWindowWidth = 500;
inline const int MinimumEditShortcutsWindowHeight = 500;
inline const int EditShortcutsTableMinWidth = 1000;
// Key Remap table constants
inline const long RemapTableColCount = 4;
inline const long RemapTableHeaderCount = 2;
inline const long RemapTableOriginalColIndex = 0;
inline const long RemapTableArrowColIndex = 1;
inline const long RemapTableNewColIndex = 2;
inline const long RemapTableRemoveColIndex = 3;
inline const DWORD64 RemapTableDropDownWidth = 110;
// Shortcut table constants
inline const long ShortcutTableColCount = 5;
inline const long ShortcutTableHeaderCount = 3;
inline const long ShortcutTableOriginalColIndex = 0;
inline const long ShortcutTableArrowColIndex = 1;
inline const long ShortcutTableNewColIndex = 2;
inline const long ShortcutTableTargetAppColIndex = 3;
inline const long ShortcutTableRemoveColIndex = 4;
inline const long ShortcutArrowColumnWidth = 90;
inline const DWORD64 ShortcutTableDropDownWidth = 110;
inline const DWORD64 ShortcutTableDropDownSpacing = 10;
inline const long ShortcutOriginColumnWidth = 3 * ShortcutTableDropDownWidth + 2 * ShortcutTableDropDownSpacing;
inline const long ShortcutTargetColumnWidth = 3 * ShortcutTableDropDownWidth + 2 * ShortcutTableDropDownSpacing + 25;
// Drop down height used for both Edit Keyboard and Edit Shortcuts
inline const DWORD64 TableDropDownHeight = 200;
inline const DWORD64 TableArrowColWidth = 230;
inline const DWORD64 TableRemoveColWidth = 20;
inline const DWORD64 TableWarningColWidth = 20;
inline const DWORD64 TableTargetAppColWidth = ShortcutTableDropDownWidth + TableRemoveColWidth * 2;
// Shared style constants for both Remap Table and Shortcut Table
inline const DWORD64 HeaderButtonWidth = 100;
// Minimum and maximum size of a shortcut
inline const long MinShortcutSize = 2;
inline const long MaxShortcutSize = 3;
}

View File

@@ -0,0 +1,144 @@
#include "pch.h"
#include <common/interop/keyboard_layout.h>
#include <keyboardmanager/common/Helpers.h>
#include "ShortcutErrorType.h"
using Helpers::GetKeyType;
namespace EditorHelpers
{
// Function to check if two keys are equal or cover the same set of keys. Return value depends on type of overlap
ShortcutErrorType DoKeysOverlap(DWORD first, DWORD second)
{
// If the keys are same
if (first == second)
{
return ShortcutErrorType::SameKeyPreviouslyMapped;
}
else if ((GetKeyType(first) == GetKeyType(second)) && GetKeyType(first) != Helpers::KeyType::Action)
{
// If the keys are of the same modifier type and overlapping, i.e. one is L/R and other is common
if (((first == VK_LWIN && second == VK_RWIN) || (first == VK_RWIN && second == VK_LWIN)) || ((first == VK_LCONTROL && second == VK_RCONTROL) || (first == VK_RCONTROL && second == VK_LCONTROL)) || ((first == VK_LMENU && second == VK_RMENU) || (first == VK_RMENU && second == VK_LMENU)) || ((first == VK_LSHIFT && second == VK_RSHIFT) || (first == VK_RSHIFT && second == VK_LSHIFT)))
{
return ShortcutErrorType::NoError;
}
else
{
return ShortcutErrorType::ConflictingModifierKey;
}
}
// If no overlap
else
{
return ShortcutErrorType::NoError;
}
}
// Function to check if a modifier has been repeated in the previous drop downs
bool CheckRepeatedModifier(const std::vector<int32_t>& currentKeys, int selectedKeyCode)
{
// Count the number of keys that are equal to 'selectedKeyCode'
int numberOfSameType = 0;
for (int i = 0; i < currentKeys.size(); i++)
{
numberOfSameType += Helpers::GetKeyType(selectedKeyCode) == Helpers::GetKeyType(currentKeys[i]);
}
// If we have at least two keys equal to 'selectedKeyCode' than modifier was repeated
return numberOfSameType > 1;
}
// Function to return true if the shortcut is valid. A valid shortcut has atleast one modifier, as well as an action key
bool IsValidShortcut(Shortcut shortcut)
{
if (shortcut.actionKey != NULL)
{
if (shortcut.winKey != ModifierKey::Disabled || shortcut.ctrlKey != ModifierKey::Disabled || shortcut.altKey != ModifierKey::Disabled || shortcut.shiftKey != ModifierKey::Disabled)
{
return true;
}
}
return false;
}
// Function to check if the two shortcuts are equal or cover the same set of keys. Return value depends on type of overlap
ShortcutErrorType DoShortcutsOverlap(const Shortcut& first, const Shortcut& second)
{
if (IsValidShortcut(first) && IsValidShortcut(second))
{
// If the shortcuts are equal
if (first == second)
{
return ShortcutErrorType::SameShortcutPreviouslyMapped;
}
// action keys match
else if (first.actionKey == second.actionKey)
{
// corresponding modifiers are either both disabled or both not disabled - this ensures that both match in types of modifiers i.e. Ctrl(l/r/c) Shift (l/r/c) A matches Ctrl(l/r/c) Shift (l/r/c) A
if (((first.winKey != ModifierKey::Disabled && second.winKey != ModifierKey::Disabled) || (first.winKey == ModifierKey::Disabled && second.winKey == ModifierKey::Disabled)) &&
((first.ctrlKey != ModifierKey::Disabled && second.ctrlKey != ModifierKey::Disabled) || (first.ctrlKey == ModifierKey::Disabled && second.ctrlKey == ModifierKey::Disabled)) &&
((first.altKey != ModifierKey::Disabled && second.altKey != ModifierKey::Disabled) || (first.altKey == ModifierKey::Disabled && second.altKey == ModifierKey::Disabled)) &&
((first.shiftKey != ModifierKey::Disabled && second.shiftKey != ModifierKey::Disabled) || (first.shiftKey == ModifierKey::Disabled && second.shiftKey == ModifierKey::Disabled)))
{
// If one of the modifier is common
if ((first.winKey == ModifierKey::Both || second.winKey == ModifierKey::Both) ||
(first.ctrlKey == ModifierKey::Both || second.ctrlKey == ModifierKey::Both) ||
(first.altKey == ModifierKey::Both || second.altKey == ModifierKey::Both) ||
(first.shiftKey == ModifierKey::Both || second.shiftKey == ModifierKey::Both))
{
return ShortcutErrorType::ConflictingModifierShortcut;
}
}
}
}
return ShortcutErrorType::NoError;
}
// Function to return a vector of hstring for each key in the display order
std::vector<winrt::hstring> GetKeyVector(Shortcut shortcut, LayoutMap& keyboardMap)
{
std::vector<winrt::hstring> keys;
if (shortcut.winKey != ModifierKey::Disabled)
{
keys.push_back(winrt::to_hstring(keyboardMap.GetKeyName(shortcut.GetWinKey(ModifierKey::Both)).c_str()));
}
if (shortcut.ctrlKey != ModifierKey::Disabled)
{
keys.push_back(winrt::to_hstring(keyboardMap.GetKeyName(shortcut.GetCtrlKey()).c_str()));
}
if (shortcut.altKey != ModifierKey::Disabled)
{
keys.push_back(winrt::to_hstring(keyboardMap.GetKeyName(shortcut.GetAltKey()).c_str()));
}
if (shortcut.shiftKey != ModifierKey::Disabled)
{
keys.push_back(winrt::to_hstring(keyboardMap.GetKeyName(shortcut.GetShiftKey()).c_str()));
}
if (shortcut.actionKey != NULL)
{
keys.push_back(winrt::to_hstring(keyboardMap.GetKeyName(shortcut.actionKey).c_str()));
}
return keys;
}
// Function to check if the shortcut is illegal (i.e. Win+L or Ctrl+Alt+Del)
ShortcutErrorType IsShortcutIllegal(Shortcut shortcut)
{
// Win+L
if (shortcut.winKey != ModifierKey::Disabled && shortcut.ctrlKey == ModifierKey::Disabled && shortcut.altKey == ModifierKey::Disabled && shortcut.shiftKey == ModifierKey::Disabled && shortcut.actionKey == 0x4C)
{
return ShortcutErrorType::WinL;
}
// Ctrl+Alt+Del
if (shortcut.winKey == ModifierKey::Disabled && shortcut.ctrlKey != ModifierKey::Disabled && shortcut.altKey != ModifierKey::Disabled && shortcut.shiftKey == ModifierKey::Disabled && shortcut.actionKey == VK_DELETE)
{
return ShortcutErrorType::CtrlAltDel;
}
return ShortcutErrorType::NoError;
}
}

View File

@@ -0,0 +1,25 @@
#pragma once
#include <keyboardmanager/common/Shortcut.h>
#include "ShortcutErrorType.h"
namespace EditorHelpers
{
// Function to check if two keys are equal or cover the same set of keys. Return value depends on type of overlap
ShortcutErrorType DoKeysOverlap(DWORD first, DWORD second);
// Function to check if a modifier has been repeated in the previous drop downs
bool CheckRepeatedModifier(const std::vector<int32_t>& currentKeys, int selectedKeyCodes);
// Function to return true if the shortcut is valid. A valid shortcut has atleast one modifier, as well as an action key
bool IsValidShortcut(Shortcut shortcut);
// Function to check if the two shortcuts are equal or cover the same set of keys. Return value depends on type of overlap
ShortcutErrorType DoShortcutsOverlap(const Shortcut& first, const Shortcut& second);
// Function to return a vector of hstring for each key in the display order
std::vector<winrt::hstring> GetKeyVector(Shortcut shortcut, LayoutMap& keyboardMap);
// Function to check if the shortcut is illegal (i.e. Win+L or Ctrl+Alt+Del)
ShortcutErrorType IsShortcutIllegal(Shortcut shortcut);
}

View File

@@ -0,0 +1,166 @@
#include "pch.h"
#include "KeyDelay.h"
// NOTE: The destructor should never be called on the DelayThread, i.e. from any of shortPress, longPress or longPressReleased, as it will re-enter the mutex. Even if the mutex is removed it will deadlock because of the join statement
KeyDelay::~KeyDelay()
{
std::unique_lock<std::mutex> l(_queueMutex);
_quit = true;
_cv.notify_all();
l.unlock();
_delayThread.join();
}
void KeyDelay::KeyEvent(LowlevelKeyboardEvent* ev)
{
std::lock_guard guard(_queueMutex);
_queue.push({ ev->lParam->time, ev->wParam });
_cv.notify_all();
}
KeyTimedEvent KeyDelay::NextEvent()
{
auto ev = _queue.front();
_queue.pop();
return ev;
}
bool KeyDelay::CheckIfMillisHaveElapsed(DWORD64 first, DWORD64 last, DWORD64 duration)
{
if (first < last && first <= first + duration)
{
return first + duration < last;
}
else
{
first += ULLONG_MAX / 2;
last += ULLONG_MAX / 2;
return first + duration < last;
}
}
bool KeyDelay::HasNextEvent()
{
return !_queue.empty();
}
bool KeyDelay::HandleRelease()
{
while (HasNextEvent())
{
auto ev = NextEvent();
switch (ev.message)
{
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
_state = KeyDelayState::ON_HOLD;
_initialHoldKeyDown = ev.time;
return false;
case WM_KEYUP:
case WM_SYSKEYUP:
break;
}
}
return true;
}
bool KeyDelay::HandleOnHold(std::unique_lock<std::mutex>& cvLock)
{
while (HasNextEvent())
{
auto ev = NextEvent();
switch (ev.message)
{
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
break;
case WM_KEYUP:
case WM_SYSKEYUP:
if (CheckIfMillisHaveElapsed(_initialHoldKeyDown, ev.time, LONG_PRESS_DELAY_MILLIS))
{
if (_onLongPressDetected != nullptr)
{
_onLongPressDetected(_key);
}
if (_onLongPressReleased != nullptr)
{
_onLongPressReleased(_key);
}
}
else
{
if (_onShortPress != nullptr)
{
_onShortPress(_key);
}
}
_state = KeyDelayState::RELEASED;
return false;
}
}
if (CheckIfMillisHaveElapsed(_initialHoldKeyDown, GetTickCount64(), LONG_PRESS_DELAY_MILLIS))
{
if (_onLongPressDetected != nullptr)
{
_onLongPressDetected(_key);
}
_state = KeyDelayState::ON_HOLD_TIMEOUT;
}
else
{
_cv.wait_for(cvLock, std::chrono::milliseconds(ON_HOLD_WAIT_TIMEOUT_MILLIS));
}
return false;
}
bool KeyDelay::HandleOnHoldTimeout()
{
while (HasNextEvent())
{
auto ev = NextEvent();
switch (ev.message)
{
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
break;
case WM_KEYUP:
case WM_SYSKEYUP:
if (_onLongPressReleased != nullptr)
{
_onLongPressReleased(_key);
}
_state = KeyDelayState::RELEASED;
return false;
}
}
return true;
}
void KeyDelay::DelayThread()
{
std::unique_lock<std::mutex> qLock(_queueMutex);
bool shouldWait = true;
while (!_quit)
{
if (shouldWait)
{
_cv.wait(qLock);
}
switch (_state)
{
case KeyDelayState::RELEASED:
shouldWait = HandleRelease();
break;
case KeyDelayState::ON_HOLD:
shouldWait = HandleOnHold(qLock);
break;
case KeyDelayState::ON_HOLD_TIMEOUT:
shouldWait = HandleOnHoldTimeout();
break;
}
}
}

View File

@@ -0,0 +1,93 @@
#pragma once
#include <functional>
#include <thread>
#include <queue>
#include <mutex>
#include <common/hooks/LowlevelKeyboardEvent.h>
// Available states for the KeyDelay state machine.
enum class KeyDelayState
{
RELEASED,
ON_HOLD,
ON_HOLD_TIMEOUT,
};
// Virtual key + timestamp (in millis since Windows startup)
struct KeyTimedEvent
{
DWORD64 time;
WPARAM message;
};
// Handles delayed key inputs.
// Implemented as a state machine running on its own thread.
// Thread stops on destruction.
class KeyDelay
{
public:
KeyDelay(
DWORD key,
std::function<void(DWORD)> onShortPress,
std::function<void(DWORD)> onLongPressDetected,
std::function<void(DWORD)> onLongPressReleased) :
_quit(false),
_state(KeyDelayState::RELEASED),
_initialHoldKeyDown(0),
_key(key),
_onShortPress(onShortPress),
_onLongPressDetected(onLongPressDetected),
_onLongPressReleased(onLongPressReleased),
_delayThread(&KeyDelay::DelayThread, this){};
// Enque new KeyTimedEvent and notify the condition variable.
void KeyEvent(LowlevelKeyboardEvent* ev);
~KeyDelay();
private:
// Runs the state machine, waits if there is no events to process.
// Checks for _quit condition.
void DelayThread();
// Manage state transitions and trigger callbacks on certain events.
// Returns whether or not the thread should wait on new events.
bool HandleRelease();
bool HandleOnHold(std::unique_lock<std::mutex>& cvLock);
bool HandleOnHoldTimeout();
// Get next key event in queue.
KeyTimedEvent NextEvent();
bool HasNextEvent();
// Check if <duration> milliseconds passed since <first> millisecond.
// Also checks for overflow conditions.
bool CheckIfMillisHaveElapsed(DWORD64 first, DWORD64 last, DWORD64 duration);
bool _quit;
KeyDelayState _state;
// Callback functions, the key provided in the constructor is passed as an argument.
std::function<void(DWORD)> _onLongPressDetected;
std::function<void(DWORD)> _onLongPressReleased;
std::function<void(DWORD)> _onShortPress;
// Queue holding key events that are not processed yet. Should be kept synchronized
// using _queueMutex
std::queue<KeyTimedEvent> _queue;
std::mutex _queueMutex;
// DelayThread waits on this condition variable when there is no events to process.
std::condition_variable _cv;
// Keeps track of the time at which the initial KEY_DOWN event happened.
DWORD64 _initialHoldKeyDown;
// Virtual Key provided in the constructor. Passed to callback functions.
DWORD _key;
// Declare _delayThread after all other members so that it is the last to be initialized by the constructor
std::thread _delayThread;
static const DWORD64 LONG_PRESS_DELAY_MILLIS = 900;
static const DWORD64 ON_HOLD_WAIT_TIMEOUT_MILLIS = 50;
};

View File

@@ -2,15 +2,20 @@
#include "KeyDropDownControl.h"
#include <common/interop/shared_constants.h>
#include <KeyboardManagerState.h>
#include <BufferValidationHelpers.h>
#include <KeyboardManagerEditorStrings.h>
#include <ErrorTypes.h>
#include <UIHelpers.h>
#include <keyboardmanager/common/MappingConfiguration.h>
#include "KeyboardManagerState.h"
#include "BufferValidationHelpers.h"
#include "KeyboardManagerEditorStrings.h"
#include "UIHelpers.h"
#include "EditorHelpers.h"
#include "ShortcutErrorType.h"
#include "EditorConstants.h"
// Initialized to null
KeyboardManagerState* KeyDropDownControl::keyboardManagerState = nullptr;
KBMEditor::KeyboardManagerState* KeyDropDownControl::keyboardManagerState = nullptr;
MappingConfiguration* KeyDropDownControl::mappingConfiguration = nullptr;
// Get selected value of dropdown or -1 if nothing is selected
DWORD KeyDropDownControl::GetSelectedValue(ComboBox comboBox)
@@ -51,14 +56,14 @@ void KeyDropDownControl::SetDefaultProperties(bool isShortcut, bool renderDisabl
if (!isShortcut)
{
dropDown.as<ComboBox>().Width(KeyboardManagerConstants::RemapTableDropDownWidth);
dropDown.as<ComboBox>().Width(EditorConstants::RemapTableDropDownWidth);
}
else
{
dropDown.as<ComboBox>().Width(KeyboardManagerConstants::ShortcutTableDropDownWidth);
dropDown.as<ComboBox>().Width(EditorConstants::ShortcutTableDropDownWidth);
}
dropDown.as<ComboBox>().MaxDropDownHeight(KeyboardManagerConstants::TableDropDownHeight);
dropDown.as<ComboBox>().MaxDropDownHeight(EditorConstants::TableDropDownHeight);
// Initialise layout attribute
previousLayout = GetKeyboardLayout(0);
@@ -121,10 +126,10 @@ void KeyDropDownControl::SetSelectionHandler(StackPanel& table, StackPanel row,
int selectedKeyCode = GetSelectedValue(currentDropDown);
// Validate current remap selection
KeyboardManagerHelper::ErrorType errorType = BufferValidationHelpers::ValidateAndUpdateKeyBufferElement(rowIndex, colIndex, selectedKeyCode, singleKeyRemapBuffer);
ShortcutErrorType errorType = BufferValidationHelpers::ValidateAndUpdateKeyBufferElement(rowIndex, colIndex, selectedKeyCode, singleKeyRemapBuffer);
// If there is an error set the warning flyout
if (errorType != KeyboardManagerHelper::ErrorType::NoError)
if (errorType != ShortcutErrorType::NoError)
{
SetDropDownError(currentDropDown, KeyboardManagerEditorStrings::GetErrorMessage(errorType));
}
@@ -145,12 +150,12 @@ void KeyDropDownControl::SetSelectionHandler(StackPanel& table, StackPanel row,
});
}
std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateShortcutSelection(StackPanel table, StackPanel row, StackPanel parent, int colIndex, RemapBuffer& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow)
std::pair<ShortcutErrorType, int> KeyDropDownControl::ValidateShortcutSelection(StackPanel table, StackPanel row, StackPanel parent, int colIndex, RemapBuffer& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow)
{
ComboBox currentDropDown = dropDown.as<ComboBox>();
uint32_t dropDownIndex = -1;
bool dropDownFound = parent.Children().IndexOf(currentDropDown, dropDownIndex);
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> validationResult = std::make_pair(KeyboardManagerHelper::ErrorType::NoError, BufferValidationHelpers::DropDownAction::NoAction);
std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> validationResult = std::make_pair(ShortcutErrorType::NoError, BufferValidationHelpers::DropDownAction::NoAction);
uint32_t rowIndex;
bool controlIindexFound = table.Children().IndexOf(row, rowIndex);
@@ -185,13 +190,13 @@ std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateSho
}
// If ignore key to shortcut warning flag is true and it is a hybrid control in SingleKeyRemapControl, then skip MapToSameKey error
if (isHybridControl && isSingleKeyWindow && ignoreKeyToShortcutWarning && (validationResult.first == KeyboardManagerHelper::ErrorType::MapToSameKey))
if (isHybridControl && isSingleKeyWindow && ignoreKeyToShortcutWarning && (validationResult.first == ShortcutErrorType::MapToSameKey))
{
validationResult.first = KeyboardManagerHelper::ErrorType::NoError;
validationResult.first = ShortcutErrorType::NoError;
}
// If the remapping is invalid display an error message
if (validationResult.first != KeyboardManagerHelper::ErrorType::NoError)
if (validationResult.first != ShortcutErrorType::NoError)
{
SetDropDownError(currentDropDown, KeyboardManagerEditorStrings::GetErrorMessage(validationResult.first));
}
@@ -227,13 +232,13 @@ std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateSho
void KeyDropDownControl::SetSelectionHandler(StackPanel& table, StackPanel row, StackPanel parent, int colIndex, RemapBuffer& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox& targetApp, bool isHybridControl, bool isSingleKeyWindow)
{
auto onSelectionChange = [&, table, row, colIndex, parent, targetApp, isHybridControl, isSingleKeyWindow](winrt::Windows::Foundation::IInspectable const& sender) {
std::pair<KeyboardManagerHelper::ErrorType, int> validationResult = ValidateShortcutSelection(table, row, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, isSingleKeyWindow);
std::pair<ShortcutErrorType, int> validationResult = ValidateShortcutSelection(table, row, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, isSingleKeyWindow);
// Check if the drop down row index was identified from the return value of validateSelection
if (validationResult.second != -1)
{
// If an error occurred
if (validationResult.first != KeyboardManagerHelper::ErrorType::NoError)
if (validationResult.first != ShortcutErrorType::NoError)
{
// Validate all the drop downs
ValidateShortcutFromDropDownList(table, row, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, isSingleKeyWindow);
@@ -362,7 +367,7 @@ void KeyDropDownControl::ValidateShortcutFromDropDownList(StackPanel table, Stac
}
// If the key/shortcut is valid and that drop down is not empty
if (((currentShortcut.index() == 0 && std::get<DWORD>(currentShortcut) != NULL) || (currentShortcut.index() == 1 && std::get<Shortcut>(currentShortcut).IsValidShortcut())) && GetSelectedValue(keyDropDownControlObjects[i]->GetComboBox()) != -1)
if (((currentShortcut.index() == 0 && std::get<DWORD>(currentShortcut) != NULL) || (currentShortcut.index() == 1 && EditorHelpers::IsValidShortcut(std::get<Shortcut>(currentShortcut)))) && GetSelectedValue(keyDropDownControlObjects[i]->GetComboBox()) != -1)
{
keyDropDownControlObjects[i]->ValidateShortcutSelection(table, row, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, isSingleKeyWindow);
}
@@ -378,7 +383,7 @@ void KeyDropDownControl::SetDropDownError(ComboBox currentDropDown, hstring mess
}
// Function to add a shortcut to the UI control as combo boxes
void KeyDropDownControl::AddShortcutToControl(Shortcut shortcut, StackPanel table, StackPanel parent, KeyboardManagerState& keyboardManagerState, const int colIndex, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, RemapBuffer& remapBuffer, StackPanel row, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow)
void KeyDropDownControl::AddShortcutToControl(Shortcut shortcut, StackPanel table, StackPanel parent, KBMEditor::KeyboardManagerState& keyboardManagerState, const int colIndex, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, RemapBuffer& remapBuffer, StackPanel row, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow)
{
// Delete the existing drop down menus
parent.Children().Clear();

View File

@@ -1,8 +1,13 @@
#pragma once
#include <Shortcut.h>
#include <keyboardmanager/common/Shortcut.h>
class KeyboardManagerState;
namespace KBMEditor
{
class KeyboardManagerState;
}
class MappingConfiguration;
namespace winrt::Windows
{
@@ -20,10 +25,7 @@ namespace winrt::Windows
}
}
namespace KeyboardManagerHelper
{
enum class ErrorType;
}
enum class ShortcutErrorType;
// Wrapper class for the key drop down menu
class KeyDropDownControl
@@ -58,7 +60,8 @@ private:
public:
// Pointer to the keyboard manager state
static KeyboardManagerState* keyboardManagerState;
static KBMEditor::KeyboardManagerState* keyboardManagerState;
static MappingConfiguration* mappingConfiguration;
// Constructor - the last default parameter should be passed as false only if it originates from Type shortcut or when an old shortcut is reloaded
KeyDropDownControl(bool isShortcut, bool fromAddShortcutToControl = false, bool renderDisable = false) :
@@ -71,7 +74,7 @@ public:
void SetSelectionHandler(StackPanel& table, StackPanel row, int colIndex, RemapBuffer& singleKeyRemapBuffer);
// Function for validating the selection of shortcuts for the drop down
std::pair<KeyboardManagerHelper::ErrorType, int> ValidateShortcutSelection(StackPanel table, StackPanel row, StackPanel parent, int colIndex, RemapBuffer& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, winrt::Windows::UI::Xaml::Controls::TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow);
std::pair<ShortcutErrorType, int> ValidateShortcutSelection(StackPanel table, StackPanel row, StackPanel parent, int colIndex, RemapBuffer& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, winrt::Windows::UI::Xaml::Controls::TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow);
// Function to set selection handler for shortcut drop down.
void SetSelectionHandler(StackPanel& table, StackPanel row, winrt::Windows::UI::Xaml::Controls::StackPanel parent, int colIndex, RemapBuffer& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, winrt::Windows::UI::Xaml::Controls::TextBox& targetApp, bool isHybridControl, bool isSingleKeyWindow);
@@ -95,7 +98,7 @@ public:
void SetSelectedValue(std::wstring value);
// Function to add a shortcut to the UI control as combo boxes
static void AddShortcutToControl(Shortcut shortcut, StackPanel table, StackPanel parent, KeyboardManagerState& keyboardManagerState, const int colIndex, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, RemapBuffer& remapBuffer, StackPanel row, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow);
static void AddShortcutToControl(Shortcut shortcut, StackPanel table, StackPanel parent, KBMEditor::KeyboardManagerState& keyboardManagerState, const int colIndex, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, RemapBuffer& remapBuffer, StackPanel row, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow);
// Get keys name list depending if Disable is in dropdown
static std::vector<std::pair<DWORD,std::wstring>> GetKeyList(bool isShortcut, bool renderDisable);

View File

@@ -27,7 +27,7 @@
<ItemDefinitionGroup>
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<AdditionalIncludeDirectories>./;$(SolutionDir)src\modules\;$(SolutionDir)src\common\Display;$(SolutionDir)src\common\inc;$(SolutionDir)src\common\Telemetry;$(SolutionDir)src;./../common;./../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>./;$(SolutionDir)src\modules\;$(SolutionDir)src\common\Display;$(SolutionDir)src\common\inc;$(SolutionDir)src\common\Telemetry;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
@@ -36,12 +36,17 @@
<ClInclude Include="BufferValidationHelpers.h" />
<ClInclude Include="Dialog.h" />
<ClInclude Include="EditKeyboardWindow.h" />
<ClInclude Include="EditorConstants.h" />
<ClInclude Include="EditorHelpers.h" />
<ClInclude Include="EditShortcutsWindow.h" />
<ClInclude Include="KeyboardManagerEditorStrings.h" />
<ClInclude Include="KeyboardManagerState.h" />
<ClInclude Include="KeyDelay.h" />
<ClInclude Include="KeyDropDownControl.h" />
<ClInclude Include="LoadingAndSavingRemappingHelper.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="ShortcutControl.h" />
<ClInclude Include="ShortcutErrorType.h" />
<ClInclude Include="SingleKeyRemapControl.h" />
<ClInclude Include="Styles.h" />
<ClInclude Include="targetver.h" />
@@ -53,8 +58,11 @@
<ClCompile Include="BufferValidationHelpers.cpp" />
<ClCompile Include="Dialog.cpp" />
<ClCompile Include="EditKeyboardWindow.cpp" />
<ClCompile Include="EditorHelpers.cpp" />
<ClCompile Include="EditShortcutsWindow.cpp" />
<ClCompile Include="KeyboardManagerEditorStrings.cpp" />
<ClCompile Include="KeyboardManagerState.cpp" />
<ClCompile Include="KeyDelay.cpp" />
<ClCompile Include="KeyDropDownControl.cpp" />
<ClCompile Include="LoadingAndSavingRemappingHelper.cpp" />
<ClCompile Include="pch.cpp">

View File

@@ -60,6 +60,21 @@
<ClInclude Include="trace.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="KeyboardManagerState.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="EditorHelpers.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ShortcutErrorType.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="KeyDelay.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="EditorConstants.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
@@ -104,6 +119,15 @@
<ClCompile Include="trace.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="KeyboardManagerState.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="EditorHelpers.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="KeyDelay.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View File

@@ -2,47 +2,45 @@
#include "KeyboardManagerEditorStrings.h"
// Function to return the error message
winrt::hstring KeyboardManagerEditorStrings::GetErrorMessage(KeyboardManagerHelper::ErrorType errorType)
winrt::hstring KeyboardManagerEditorStrings::GetErrorMessage(ShortcutErrorType errorType)
{
using namespace KeyboardManagerHelper;
switch (errorType)
{
case ErrorType::NoError:
case ShortcutErrorType::NoError:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_REMAPSUCCESSFUL).c_str();
case ErrorType::SameKeyPreviouslyMapped:
case ShortcutErrorType::SameKeyPreviouslyMapped:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SAMEKEYPREVIOUSLYMAPPED).c_str();
case ErrorType::MapToSameKey:
case ShortcutErrorType::MapToSameKey:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_MAPPEDTOSAMEKEY).c_str();
case ErrorType::ConflictingModifierKey:
case ShortcutErrorType::ConflictingModifierKey:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_CONFLICTINGMODIFIERKEY).c_str();
case ErrorType::SameShortcutPreviouslyMapped:
case ShortcutErrorType::SameShortcutPreviouslyMapped:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SAMESHORTCUTPREVIOUSLYMAPPED).c_str();
case ErrorType::MapToSameShortcut:
case ShortcutErrorType::MapToSameShortcut:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_MAPTOSAMESHORTCUT).c_str();
case ErrorType::ConflictingModifierShortcut:
case ShortcutErrorType::ConflictingModifierShortcut:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_CONFLICTINGMODIFIERSHORTCUT).c_str();
case ErrorType::WinL:
case ShortcutErrorType::WinL:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_WINL).c_str();
case ErrorType::CtrlAltDel:
case ShortcutErrorType::CtrlAltDel:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_CTRLALTDEL).c_str();
case ErrorType::RemapUnsuccessful:
case ShortcutErrorType::RemapUnsuccessful:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_REMAPUNSUCCESSFUL).c_str();
case ErrorType::SaveFailed:
case ShortcutErrorType::SaveFailed:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SAVEFAILED).c_str();
case ErrorType::ShortcutStartWithModifier:
case ShortcutErrorType::ShortcutStartWithModifier:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SHORTCUTSTARTWITHMODIFIER).c_str();
case ErrorType::ShortcutCannotHaveRepeatedModifier:
case ShortcutErrorType::ShortcutCannotHaveRepeatedModifier:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SHORTCUTNOREPEATEDMODIFIER).c_str();
case ErrorType::ShortcutAtleast2Keys:
case ShortcutErrorType::ShortcutAtleast2Keys:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SHORTCUTATLEAST2KEYS).c_str();
case ErrorType::ShortcutOneActionKey:
case ShortcutErrorType::ShortcutOneActionKey:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SHORTCUTONEACTIONKEY).c_str();
case ErrorType::ShortcutNotMoreThanOneActionKey:
case ShortcutErrorType::ShortcutNotMoreThanOneActionKey:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SHORTCUTMAXONEACTIONKEY).c_str();
case ErrorType::ShortcutMaxShortcutSizeOneActionKey:
case ShortcutErrorType::ShortcutMaxShortcutSizeOneActionKey:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_MAXSHORTCUTSIZE).c_str();
case ErrorType::ShortcutDisableAsActionKey:
case ShortcutErrorType::ShortcutDisableAsActionKey:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_DISABLEASACTIONKEY).c_str();
default:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_DEFAULT).c_str();

View File

@@ -2,7 +2,7 @@
#include "pch.h"
#include <ErrorTypes.h>
#include "ShortcutErrorType.h"
namespace KeyboardManagerEditorStrings
{
@@ -10,5 +10,5 @@ namespace KeyboardManagerEditorStrings
inline const std::wstring DefaultAppName = GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_ALLAPPS);
// Function to return the error message
winrt::hstring GetErrorMessage(KeyboardManagerHelper::ErrorType errorType);
}
winrt::hstring GetErrorMessage(ShortcutErrorType errorType);
}

View File

@@ -0,0 +1,363 @@
#include "pch.h"
#include "KeyboardManagerState.h"
#include <keyboardmanager/common/Helpers.h>
#include "EditorHelpers.h"
#include "KeyDelay.h"
using namespace KBMEditor;
// Constructor
KeyboardManagerState::KeyboardManagerState() :
uiState(KeyboardManagerUIState::Deactivated), currentUIWindow(nullptr), currentShortcutUI1(nullptr), currentShortcutUI2(nullptr), currentSingleKeyUI(nullptr), detectedRemapKey(NULL)
{
}
// Destructor
KeyboardManagerState::~KeyboardManagerState()
{
}
// Function to check the if the UI state matches the argument state. For states with detect windows it also checks if the window is in focus.
bool KeyboardManagerState::CheckUIState(KeyboardManagerUIState state)
{
std::lock_guard<std::mutex> lock(uiState_mutex);
if (uiState == state)
{
std::unique_lock<std::mutex> lock(currentUIWindow_mutex);
if (uiState == KeyboardManagerUIState::Deactivated || uiState == KeyboardManagerUIState::EditKeyboardWindowActivated || uiState == KeyboardManagerUIState::EditShortcutsWindowActivated)
{
return true;
}
// If the UI state is a detect window then we also have to ensure that the UI window is in focus.
// GetForegroundWindow can be used here since we only need to check the main parent window and not the sub windows within the content dialog. Using GUIThreadInfo will give more specific sub-windows within the XAML window which is not needed.
else if (currentUIWindow == GetForegroundWindow())
{
return true;
}
}
// If we are checking for EditKeyboardWindowActivated then it's possible the state could be DetectSingleKeyRemapWindowActivated but not in focus
else if (state == KeyboardManagerUIState::EditKeyboardWindowActivated && uiState == KeyboardManagerUIState::DetectSingleKeyRemapWindowActivated)
{
return true;
}
// If we are checking for EditShortcutsWindowActivated then it's possible the state could be DetectShortcutWindowActivated but not in focus
else if (state == KeyboardManagerUIState::EditShortcutsWindowActivated && uiState == KeyboardManagerUIState::DetectShortcutWindowActivated)
{
return true;
}
return false;
}
// Function to set the window handle of the current UI window that is activated
void KeyboardManagerState::SetCurrentUIWindow(HWND windowHandle)
{
std::lock_guard<std::mutex> lock(currentUIWindow_mutex);
currentUIWindow = windowHandle;
}
// Function to set the UI state. When a window is activated, the handle to the window can be passed in the windowHandle argument.
void KeyboardManagerState::SetUIState(KeyboardManagerUIState state, HWND windowHandle)
{
std::lock_guard<std::mutex> lock(uiState_mutex);
uiState = state;
SetCurrentUIWindow(windowHandle);
}
// Function to reset the UI state members
void KeyboardManagerState::ResetUIState()
{
SetUIState(KeyboardManagerUIState::Deactivated);
// Reset the shortcut UI stored variables
std::unique_lock<std::mutex> currentShortcutUI_lock(currentShortcutUI_mutex);
currentShortcutUI1 = nullptr;
currentShortcutUI2 = nullptr;
currentShortcutUI_lock.unlock();
std::unique_lock<std::mutex> detectedShortcut_lock(detectedShortcut_mutex);
detectedShortcut.Reset();
detectedShortcut_lock.unlock();
std::unique_lock<std::mutex> currentShortcut_lock(currentShortcut_mutex);
currentShortcut.Reset();
currentShortcut_lock.unlock();
// Reset all the single key remap UI stored variables.
std::unique_lock<std::mutex> currentSingleKeyUI_lock(currentSingleKeyUI_mutex);
currentSingleKeyUI = nullptr;
currentSingleKeyUI_lock.unlock();
std::unique_lock<std::mutex> detectedRemapKey_lock(detectedRemapKey_mutex);
detectedRemapKey = NULL;
detectedRemapKey_lock.unlock();
}
// Function to set the textblock of the detect shortcut UI so that it can be accessed by the hook
void KeyboardManagerState::ConfigureDetectShortcutUI(const StackPanel& textBlock1, const StackPanel& textBlock2)
{
std::lock_guard<std::mutex> lock(currentShortcutUI_mutex);
currentShortcutUI1 = textBlock1.as<winrt::Windows::Foundation::IInspectable>();
currentShortcutUI2 = textBlock2.as<winrt::Windows::Foundation::IInspectable>();
}
// Function to set the textblock of the detect remap key UI so that it can be accessed by the hook
void KeyboardManagerState::ConfigureDetectSingleKeyRemapUI(const StackPanel& textBlock)
{
std::lock_guard<std::mutex> lock(currentSingleKeyUI_mutex);
currentSingleKeyUI = textBlock.as<winrt::Windows::Foundation::IInspectable>();
}
void KeyboardManagerState::AddKeyToLayout(const StackPanel& panel, const hstring& key)
{
// Textblock to display the detected key
TextBlock remapKey;
Border border;
border.Padding({ 20, 10, 20, 10 });
border.Margin({ 0, 0, 10, 0 });
// Use the base low brush to be consistent with the theme
border.Background(Windows::UI::Xaml::Application::Current().Resources().Lookup(box_value(L"SystemControlBackgroundBaseLowBrush")).as<Windows::UI::Xaml::Media::SolidColorBrush>());
remapKey.FontSize(20);
border.HorizontalAlignment(HorizontalAlignment::Left);
border.Child(remapKey);
remapKey.Text(key);
panel.Children().Append(border);
}
// Function to update the detect shortcut UI based on the entered keys
void KeyboardManagerState::UpdateDetectShortcutUI()
{
std::lock_guard<std::mutex> currentShortcutUI_lock(currentShortcutUI_mutex);
if (currentShortcutUI1 == nullptr)
{
return;
}
std::unique_lock<std::mutex> detectedShortcut_lock(detectedShortcut_mutex);
std::unique_lock<std::mutex> currentShortcut_lock(currentShortcut_mutex);
// Save the latest displayed shortcut
currentShortcut = detectedShortcut;
auto detectedShortcutCopy = detectedShortcut;
currentShortcut_lock.unlock();
detectedShortcut_lock.unlock();
// Since this function is invoked from the back-end thread, in order to update the UI the dispatcher must be used.
currentShortcutUI1.as<StackPanel>().Dispatcher().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [this, detectedShortcutCopy]() {
std::vector<hstring> shortcut = EditorHelpers::GetKeyVector(detectedShortcutCopy, keyboardMap);
currentShortcutUI1.as<StackPanel>().Children().Clear();
currentShortcutUI2.as<StackPanel>().Children().Clear();
// The second row should be hidden if there are 3 keys or lesser to avoid an extra margin
if (shortcut.size() > 3)
{
currentShortcutUI2.as<StackPanel>().Visibility(Visibility::Visible);
}
else
{
currentShortcutUI2.as<StackPanel>().Visibility(Visibility::Collapsed);
}
for (int i = 0; i < shortcut.size(); i++)
{
if (i < 3)
{
AddKeyToLayout(currentShortcutUI1.as<StackPanel>(), shortcut[i]);
}
else
{
AddKeyToLayout(currentShortcutUI2.as<StackPanel>(), shortcut[i]);
}
}
currentShortcutUI1.as<StackPanel>().UpdateLayout();
currentShortcutUI2.as<StackPanel>().UpdateLayout();
});
}
// Function to update the detect remap key UI based on the entered key.
void KeyboardManagerState::UpdateDetectSingleKeyRemapUI()
{
std::lock_guard<std::mutex> currentSingleKeyUI_lock(currentSingleKeyUI_mutex);
if (currentSingleKeyUI == nullptr)
{
return;
}
// Since this function is invoked from the back-end thread, in order to update the UI the dispatcher must be used.
currentSingleKeyUI.as<StackPanel>().Dispatcher().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [this]() {
currentSingleKeyUI.as<StackPanel>().Children().Clear();
hstring key = winrt::to_hstring(keyboardMap.GetKeyName(detectedRemapKey).c_str());
AddKeyToLayout(currentSingleKeyUI.as<StackPanel>(), key);
currentSingleKeyUI.as<StackPanel>().UpdateLayout();
});
}
// Function to return the currently detected shortcut which is displayed on the UI
Shortcut KeyboardManagerState::GetDetectedShortcut()
{
std::lock_guard<std::mutex> lock(currentShortcut_mutex);
return currentShortcut;
}
// Function to return the currently detected remap key which is displayed on the UI
DWORD KeyboardManagerState::GetDetectedSingleRemapKey()
{
std::lock_guard<std::mutex> lock(detectedRemapKey_mutex);
return detectedRemapKey;
}
void KeyboardManagerState::SelectDetectedRemapKey(DWORD key)
{
std::lock_guard<std::mutex> guard(detectedRemapKey_mutex);
detectedRemapKey = key;
UpdateDetectSingleKeyRemapUI();
return;
}
void KeyboardManagerState::SelectDetectedShortcut(DWORD key)
{
// Set the new key and store if a change occurred
std::unique_lock<std::mutex> lock(detectedShortcut_mutex);
bool updateUI = detectedShortcut.SetKey(key);
lock.unlock();
if (updateUI)
{
// Update the UI. This function is called here because it should store the set of keys pressed till the last key which was pressed down.
UpdateDetectShortcutUI();
}
return;
}
void KeyboardManagerState::ResetDetectedShortcutKey(DWORD key)
{
std::lock_guard<std::mutex> lock(detectedShortcut_mutex);
detectedShortcut.ResetKey(key);
}
// Function which can be used in HandleKeyboardHookEvent before the single key remap event to use the UI and suppress events while the remap window is active.
Helpers::KeyboardHookDecision KeyboardManagerState::DetectSingleRemapKeyUIBackend(LowlevelKeyboardEvent* data)
{
// Check if the detect key UI window has been activated
if (CheckUIState(KeyboardManagerUIState::DetectSingleKeyRemapWindowActivated))
{
if (HandleKeyDelayEvent(data))
{
return Helpers::KeyboardHookDecision::Suppress;
}
// detect the key if it is pressed down
if (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)
{
SelectDetectedRemapKey(data->lParam->vkCode);
}
// Suppress the keyboard event
return Helpers::KeyboardHookDecision::Suppress;
}
// If the settings window is up, remappings should not be applied, but we should not suppress events in the hook
else if (CheckUIState(KeyboardManagerUIState::EditKeyboardWindowActivated))
{
return Helpers::KeyboardHookDecision::SkipHook;
}
return Helpers::KeyboardHookDecision::ContinueExec;
}
// Function which can be used in HandleKeyboardHookEvent before the os level shortcut remap event to use the UI and suppress events while the remap window is active.
Helpers::KeyboardHookDecision KeyboardManagerState::DetectShortcutUIBackend(LowlevelKeyboardEvent* data, bool isRemapKey)
{
// Check if the detect shortcut UI window has been activated
if ((!isRemapKey && CheckUIState(KeyboardManagerUIState::DetectShortcutWindowActivated)) || (isRemapKey && CheckUIState(KeyboardManagerUIState::DetectShortcutWindowInEditKeyboardWindowActivated)))
{
if (HandleKeyDelayEvent(data))
{
return Helpers::KeyboardHookDecision::Suppress;
}
// Add the key if it is pressed down
if (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)
{
SelectDetectedShortcut(data->lParam->vkCode);
}
// Remove the key if it has been released
else if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP)
{
ResetDetectedShortcutKey(data->lParam->vkCode);
}
// Suppress the keyboard event
return Helpers::KeyboardHookDecision::Suppress;
}
// If the detect shortcut UI window is not activated, then clear the shortcut buffer if it isn't empty
else if (!CheckUIState(KeyboardManagerUIState::DetectShortcutWindowActivated) && !CheckUIState(KeyboardManagerUIState::DetectShortcutWindowInEditKeyboardWindowActivated))
{
std::lock_guard<std::mutex> lock(detectedShortcut_mutex);
if (!detectedShortcut.IsEmpty())
{
detectedShortcut.Reset();
}
}
// If the settings window is up, shortcut remappings should not be applied, but we should not suppress events in the hook
if (!isRemapKey && (CheckUIState(KeyboardManagerUIState::EditShortcutsWindowActivated)) || (isRemapKey && uiState == KeyboardManagerUIState::DetectShortcutWindowInEditKeyboardWindowActivated))
{
return Helpers::KeyboardHookDecision::SkipHook;
}
return Helpers::KeyboardHookDecision::ContinueExec;
}
void KeyboardManagerState::RegisterKeyDelay(
DWORD key,
std::function<void(DWORD)> onShortPress,
std::function<void(DWORD)> onLongPressDetected,
std::function<void(DWORD)> onLongPressReleased)
{
std::lock_guard l(keyDelays_mutex);
if (keyDelays.find(key) != keyDelays.end())
{
throw std::invalid_argument("This key was already registered.");
}
keyDelays[key] = std::make_unique<KeyDelay>(key, onShortPress, onLongPressDetected, onLongPressReleased);
}
void KeyboardManagerState::UnregisterKeyDelay(DWORD key)
{
std::lock_guard l(keyDelays_mutex);
auto deleted = keyDelays.erase(key);
if (deleted == 0)
{
throw std::invalid_argument("The key was not previously registered.");
}
}
// Function to clear all the registered key delays
void KeyboardManagerState::ClearRegisteredKeyDelays()
{
std::lock_guard l(keyDelays_mutex);
keyDelays.clear();
}
bool KeyboardManagerState::HandleKeyDelayEvent(LowlevelKeyboardEvent* ev)
{
if (currentUIWindow != GetForegroundWindow())
{
return false;
}
std::lock_guard l(keyDelays_mutex);
if (keyDelays.find(ev->lParam->vkCode) == keyDelays.end())
{
return false;
}
keyDelays[ev->lParam->vkCode]->KeyEvent(ev);
return true;
}

View File

@@ -0,0 +1,161 @@
#pragma once
#include <common/hooks/LowlevelKeyboardEvent.h>
#include <common/interop/keyboard_layout.h>
#include <keyboardmanager/common/KeyboardManagerConstants.h>
#include <keyboardmanager/common/Shortcut.h>
class KeyDelay;
namespace Helpers
{
// Enum type to store possible decision for input in the low level hook
enum class KeyboardHookDecision
{
ContinueExec,
Suppress,
SkipHook
};
}
namespace winrt::Windows::UI::Xaml::Controls
{
struct StackPanel;
}
namespace KBMEditor
{
// Enum type to store different states of the UI
enum class KeyboardManagerUIState
{
// If set to this value then there is no keyboard manager window currently active that requires a hook
Deactivated,
// If set to this value then the detect key window is currently active and it requires a hook
DetectSingleKeyRemapWindowActivated,
// If set to this value then the detect shortcut window in edit keyboard window is currently active and it requires a hook
DetectShortcutWindowInEditKeyboardWindowActivated,
// If set to this value then the edit keyboard window is currently active and remaps should not be applied
EditKeyboardWindowActivated,
// If set to this value then the detect shortcut window is currently active and it requires a hook
DetectShortcutWindowActivated,
// If set to this value then the edit shortcuts window is currently active and remaps should not be applied
EditShortcutsWindowActivated
};
// Class to store the shared state of the keyboard manager between the UI and the hook
class KeyboardManagerState
{
private:
// State variable used to store which UI window is currently active that requires interaction with the hook
KeyboardManagerUIState uiState;
std::mutex uiState_mutex;
// Window handle for the current UI window which is active. Should be set to nullptr if UI is deactivated
HWND currentUIWindow;
std::mutex currentUIWindow_mutex;
// Object to store the shortcut detected in the detect shortcut UI window. Gets cleared on releasing keys. This is used in both the backend and the UI.
Shortcut detectedShortcut;
std::mutex detectedShortcut_mutex;
// Object to store the shortcut state displayed in the UI window. Always stores last displayed shortcut irrespective of releasing keys. This is used in both the backend and the UI.
Shortcut currentShortcut;
std::mutex currentShortcut_mutex;
// Store detected remap key in the remap UI window. This is used in both the backend and the UI.
DWORD detectedRemapKey;
std::mutex detectedRemapKey_mutex;
// Stores the UI element which is to be updated based on the remap key entered.
winrt::Windows::Foundation::IInspectable currentSingleKeyUI;
std::mutex currentSingleKeyUI_mutex;
// Stores the UI element which is to be updated based on the shortcut entered (each stackpanel represents a row of keys)
winrt::Windows::Foundation::IInspectable currentShortcutUI1;
winrt::Windows::Foundation::IInspectable currentShortcutUI2;
std::mutex currentShortcutUI_mutex;
// Registered KeyDelay objects, used to notify delayed key events.
std::map<DWORD, std::unique_ptr<KeyDelay>> keyDelays;
std::mutex keyDelays_mutex;
// Display a key by appending a border Control as a child of the panel.
void AddKeyToLayout(const winrt::Windows::UI::Xaml::Controls::StackPanel& panel, const winrt::hstring& key);
public:
// Stores the keyboard layout
LayoutMap keyboardMap;
// Constructor
KeyboardManagerState();
// Destructor
~KeyboardManagerState();
// Function to reset the UI state members
void ResetUIState();
// Function to check the if the UI state matches the argument state. For states with detect windows it also checks if the window is in focus.
bool CheckUIState(KeyboardManagerUIState state);
// Function to set the window handle of the current UI window that is activated
void SetCurrentUIWindow(HWND windowHandle);
// Function to set the UI state. When a window is activated, the handle to the window can be passed in the windowHandle argument.
void SetUIState(KeyboardManagerUIState state, HWND windowHandle = nullptr);
// Function to set the textblock of the detect shortcut UI so that it can be accessed by the hook
void ConfigureDetectShortcutUI(const winrt::Windows::UI::Xaml::Controls::StackPanel& textBlock1, const winrt::Windows::UI::Xaml::Controls::StackPanel& textBlock2);
// Function to set the textblock of the detect remap key UI so that it can be accessed by the hook
void ConfigureDetectSingleKeyRemapUI(const winrt::Windows::UI::Xaml::Controls::StackPanel& textBlock);
// Function to update the detect shortcut UI based on the entered keys
void UpdateDetectShortcutUI();
// Function to update the detect remap key UI based on the entered key.
void UpdateDetectSingleKeyRemapUI();
// Function to return the currently detected shortcut which is displayed on the UI
Shortcut GetDetectedShortcut();
// Function to return the currently detected remap key which is displayed on the UI
DWORD GetDetectedSingleRemapKey();
// Function which can be used in HandleKeyboardHookEvent before the single key remap event to use the UI and suppress events while the remap window is active.
Helpers::KeyboardHookDecision DetectSingleRemapKeyUIBackend(LowlevelKeyboardEvent* data);
// Function which can be used in HandleKeyboardHookEvent before the os level shortcut remap event to use the UI and suppress events while the remap window is active.
Helpers::KeyboardHookDecision DetectShortcutUIBackend(LowlevelKeyboardEvent* data, bool isRemapKey);
// Add a KeyDelay object to get delayed key presses events for a given virtual key
// NOTE: this will throw an exception if a virtual key is registered twice.
// NOTE*: the virtual key should represent the original, unmapped virtual key.
void RegisterKeyDelay(
DWORD key,
std::function<void(DWORD)> onShortPress,
std::function<void(DWORD)> onLongPressDetected,
std::function<void(DWORD)> onLongPressReleased);
// Remove a KeyDelay.
// NOTE: this method will throw if the virtual key is not registered beforehand.
// NOTE*: the virtual key should represent the original, unmapped virtual key.
void UnregisterKeyDelay(DWORD key);
// Function to clear all the registered key delays
void ClearRegisteredKeyDelays();
// Handle a key event, for a delayed key.
bool HandleKeyDelayEvent(LowlevelKeyboardEvent* ev);
// Update the currently selected single key remap
void SelectDetectedRemapKey(DWORD key);
// Update the currently selected shortcut.
void SelectDetectedShortcut(DWORD key);
// Reset the shortcut (backend) state after releasing a key.
void ResetDetectedShortcutKey(DWORD key);
};
}

View File

@@ -2,19 +2,20 @@
#include "LoadingAndSavingRemappingHelper.h"
#include <set>
#include <common/interop/shared_constants.h>
#include <ErrorTypes.h>
#include <KeyboardManagerState.h>
#include <keyboardmanager/common/MappingConfiguration.h>
#include <keyboardmanager/KeyboardManagerEditorLibrary/trace.h>
#include "KeyboardManagerState.h"
#include "keyboardmanager/KeyboardManagerEditorLibrary/trace.h"
#include "EditorHelpers.h"
#include "ShortcutErrorType.h"
namespace LoadingAndSavingRemappingHelper
{
// Function to check if the set of remappings in the buffer are valid
KeyboardManagerHelper::ErrorType CheckIfRemappingsAreValid(const RemapBuffer& remappings)
ShortcutErrorType CheckIfRemappingsAreValid(const RemapBuffer& remappings)
{
KeyboardManagerHelper::ErrorType isSuccess = KeyboardManagerHelper::ErrorType::NoError;
ShortcutErrorType isSuccess = ShortcutErrorType::NoError;
std::map<std::wstring, std::set<KeyShortcutUnion>> ogKeys;
for (int i = 0; i < remappings.size(); i++)
{
@@ -22,8 +23,8 @@ namespace LoadingAndSavingRemappingHelper
KeyShortcutUnion newKey = remappings[i].first[1];
std::wstring appName = remappings[i].second;
bool ogKeyValidity = (ogKey.index() == 0 && std::get<DWORD>(ogKey) != NULL) || (ogKey.index() == 1 && std::get<Shortcut>(ogKey).IsValidShortcut());
bool newKeyValidity = (newKey.index() == 0 && std::get<DWORD>(newKey) != NULL) || (newKey.index() == 1 && std::get<Shortcut>(newKey).IsValidShortcut());
bool ogKeyValidity = (ogKey.index() == 0 && std::get<DWORD>(ogKey) != NULL) || (ogKey.index() == 1 && EditorHelpers::IsValidShortcut(std::get<Shortcut>(ogKey)));
bool newKeyValidity = (newKey.index() == 0 && std::get<DWORD>(newKey) != NULL) || (newKey.index() == 1 && EditorHelpers::IsValidShortcut(std::get<Shortcut>(newKey)));
// Add new set for a new target app name
if (ogKeys.find(appName) == ogKeys.end())
@@ -37,11 +38,11 @@ namespace LoadingAndSavingRemappingHelper
}
else if (ogKeyValidity && newKeyValidity && ogKeys[appName].find(ogKey) != ogKeys[appName].end())
{
isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful;
isSuccess = ShortcutErrorType::RemapUnsuccessful;
}
else
{
isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful;
isSuccess = ShortcutErrorType::RemapUnsuccessful;
}
}
@@ -59,7 +60,7 @@ namespace LoadingAndSavingRemappingHelper
DWORD ogKey = std::get<DWORD>(remappings[i].first[0]);
KeyShortcutUnion newKey = remappings[i].first[1];
if (ogKey != NULL && ((newKey.index() == 0 && std::get<DWORD>(newKey) != 0) || (newKey.index() == 1 && std::get<Shortcut>(newKey).IsValidShortcut())))
if (ogKey != NULL && ((newKey.index() == 0 && std::get<DWORD>(newKey) != 0) || (newKey.index() == 1 && EditorHelpers::IsValidShortcut(std::get<Shortcut>(newKey)))))
{
ogKeys.insert(ogKey);
@@ -105,10 +106,10 @@ namespace LoadingAndSavingRemappingHelper
}
// Function to apply the single key remappings from the buffer to the KeyboardManagerState variable
void ApplySingleKeyRemappings(KeyboardManagerState& keyboardManagerState, const RemapBuffer& remappings, bool isTelemetryRequired)
void ApplySingleKeyRemappings(MappingConfiguration& mappingConfiguration, const RemapBuffer& remappings, bool isTelemetryRequired)
{
// Clear existing Key Remaps
keyboardManagerState.ClearSingleKeyRemaps();
mappingConfiguration.ClearSingleKeyRemaps();
DWORD successfulKeyToKeyRemapCount = 0;
DWORD successfulKeyToShortcutRemapCount = 0;
for (int i = 0; i < remappings.size(); i++)
@@ -116,7 +117,7 @@ namespace LoadingAndSavingRemappingHelper
DWORD originalKey = std::get<DWORD>(remappings[i].first[0]);
KeyShortcutUnion newKey = remappings[i].first[1];
if (originalKey != NULL && !(newKey.index() == 0 && std::get<DWORD>(newKey) == NULL) && !(newKey.index() == 1 && !std::get<Shortcut>(newKey).IsValidShortcut()))
if (originalKey != NULL && !(newKey.index() == 0 && std::get<DWORD>(newKey) == NULL) && !(newKey.index() == 1 && !EditorHelpers::IsValidShortcut(std::get<Shortcut>(newKey))))
{
// If Ctrl/Alt/Shift are added, add their L and R versions instead to the same key
bool result = false;
@@ -124,27 +125,27 @@ namespace LoadingAndSavingRemappingHelper
switch (originalKey)
{
case VK_CONTROL:
res1 = keyboardManagerState.AddSingleKeyRemap(VK_LCONTROL, newKey);
res2 = keyboardManagerState.AddSingleKeyRemap(VK_RCONTROL, newKey);
res1 = mappingConfiguration.AddSingleKeyRemap(VK_LCONTROL, newKey);
res2 = mappingConfiguration.AddSingleKeyRemap(VK_RCONTROL, newKey);
result = res1 && res2;
break;
case VK_MENU:
res1 = keyboardManagerState.AddSingleKeyRemap(VK_LMENU, newKey);
res2 = keyboardManagerState.AddSingleKeyRemap(VK_RMENU, newKey);
res1 = mappingConfiguration.AddSingleKeyRemap(VK_LMENU, newKey);
res2 = mappingConfiguration.AddSingleKeyRemap(VK_RMENU, newKey);
result = res1 && res2;
break;
case VK_SHIFT:
res1 = keyboardManagerState.AddSingleKeyRemap(VK_LSHIFT, newKey);
res2 = keyboardManagerState.AddSingleKeyRemap(VK_RSHIFT, newKey);
res1 = mappingConfiguration.AddSingleKeyRemap(VK_LSHIFT, newKey);
res2 = mappingConfiguration.AddSingleKeyRemap(VK_RSHIFT, newKey);
result = res1 && res2;
break;
case CommonSharedConstants::VK_WIN_BOTH:
res1 = keyboardManagerState.AddSingleKeyRemap(VK_LWIN, newKey);
res2 = keyboardManagerState.AddSingleKeyRemap(VK_RWIN, newKey);
res1 = mappingConfiguration.AddSingleKeyRemap(VK_LWIN, newKey);
res2 = mappingConfiguration.AddSingleKeyRemap(VK_RWIN, newKey);
result = res1 && res2;
break;
default:
result = keyboardManagerState.AddSingleKeyRemap(originalKey, newKey);
result = mappingConfiguration.AddSingleKeyRemap(originalKey, newKey);
}
if (result)
@@ -169,11 +170,11 @@ namespace LoadingAndSavingRemappingHelper
}
// Function to apply the shortcut remappings from the buffer to the KeyboardManagerState variable
void ApplyShortcutRemappings(KeyboardManagerState& keyboardManagerState, const RemapBuffer& remappings, bool isTelemetryRequired)
void ApplyShortcutRemappings(MappingConfiguration& mappingConfiguration, const RemapBuffer& remappings, bool isTelemetryRequired)
{
// Clear existing shortcuts
keyboardManagerState.ClearOSLevelShortcuts();
keyboardManagerState.ClearAppSpecificShortcuts();
mappingConfiguration.ClearOSLevelShortcuts();
mappingConfiguration.ClearAppSpecificShortcuts();
DWORD successfulOSLevelShortcutToShortcutRemapCount = 0;
DWORD successfulOSLevelShortcutToKeyRemapCount = 0;
DWORD successfulAppSpecificShortcutToShortcutRemapCount = 0;
@@ -185,11 +186,11 @@ namespace LoadingAndSavingRemappingHelper
Shortcut originalShortcut = std::get<Shortcut>(remappings[i].first[0]);
KeyShortcutUnion newShortcut = remappings[i].first[1];
if (originalShortcut.IsValidShortcut() && ((newShortcut.index() == 0 && std::get<DWORD>(newShortcut) != NULL) || (newShortcut.index() == 1 && std::get<Shortcut>(newShortcut).IsValidShortcut())))
if (EditorHelpers::IsValidShortcut(originalShortcut) && ((newShortcut.index() == 0 && std::get<DWORD>(newShortcut) != NULL) || (newShortcut.index() == 1 && EditorHelpers::IsValidShortcut(std::get<Shortcut>(newShortcut)))))
{
if (remappings[i].second == L"")
{
bool result = keyboardManagerState.AddOSLevelShortcut(originalShortcut, newShortcut);
bool result = mappingConfiguration.AddOSLevelShortcut(originalShortcut, newShortcut);
if (result)
{
if (newShortcut.index() == 0)
@@ -204,7 +205,7 @@ namespace LoadingAndSavingRemappingHelper
}
else
{
bool result = keyboardManagerState.AddAppSpecificShortcut(remappings[i].second, originalShortcut, newShortcut);
bool result = mappingConfiguration.AddAppSpecificShortcut(remappings[i].second, originalShortcut, newShortcut);
if (result)
{
if (newShortcut.index() == 0)

View File

@@ -2,12 +2,14 @@
#include <keyboardmanager/common/Helpers.h>
class KeyboardManagerState;
#include "ShortcutErrorType.h"
class MappingConfiguration;
namespace LoadingAndSavingRemappingHelper
{
// Function to check if the set of remappings in the buffer are valid
KeyboardManagerHelper::ErrorType CheckIfRemappingsAreValid(const RemapBuffer& remappings);
ShortcutErrorType CheckIfRemappingsAreValid(const RemapBuffer& remappings);
// Function to return the set of keys that have been orphaned from the remap buffer
std::vector<DWORD> GetOrphanedKeys(const RemapBuffer& remappings);
@@ -19,8 +21,8 @@ namespace LoadingAndSavingRemappingHelper
void PreProcessRemapTable(std::unordered_map<DWORD, KeyShortcutUnion>& table);
// Function to apply the single key remappings from the buffer to the KeyboardManagerState variable
void ApplySingleKeyRemappings(KeyboardManagerState& keyboardManagerState, const RemapBuffer& remappings, bool isTelemetryRequired);
void ApplySingleKeyRemappings(MappingConfiguration& mappingConfiguration, const RemapBuffer& remappings, bool isTelemetryRequired);
// Function to apply the shortcut remappings from the buffer to the KeyboardManagerState variable
void ApplyShortcutRemappings(KeyboardManagerState& keyboardManagerState, const RemapBuffer& remappings, bool isTelemetryRequired);
void ApplyShortcutRemappings(MappingConfiguration& mappingConfiguration, const RemapBuffer& remappings, bool isTelemetryRequired);
}

View File

@@ -3,15 +3,16 @@
#include <common/interop/shared_constants.h>
#include <KeyboardManagerState.h>
#include <KeyboardManagerEditorStrings.h>
#include <KeyDropDownControl.h>
#include <UIHelpers.h>
#include "KeyboardManagerState.h"
#include "KeyboardManagerEditorStrings.h"
#include "KeyDropDownControl.h"
#include "UIHelpers.h"
#include "EditorHelpers.h"
#include "EditorConstants.h"
//Both static members are initialized to null
HWND ShortcutControl::editShortcutsWindowHandle = nullptr;
KeyboardManagerState* ShortcutControl::keyboardManagerState = nullptr;
KBMEditor::KeyboardManagerState* ShortcutControl::keyboardManagerState = nullptr;
// Initialized as new vector
RemapBuffer ShortcutControl::shortcutRemapBuffer;
@@ -22,13 +23,13 @@ ShortcutControl::ShortcutControl(StackPanel table, StackPanel row, const int col
shortcutControlLayout = StackPanel();
bool isHybridControl = colIndex == 1 ? true : false;
shortcutDropDownStackPanel.as<StackPanel>().Spacing(KeyboardManagerConstants::ShortcutTableDropDownSpacing);
shortcutDropDownStackPanel.as<StackPanel>().Spacing(EditorConstants::ShortcutTableDropDownSpacing);
shortcutDropDownStackPanel.as<StackPanel>().Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
typeShortcut.as<Button>().Content(winrt::box_value(GET_RESOURCE_STRING(IDS_TYPE_BUTTON)));
typeShortcut.as<Button>().Width(KeyboardManagerConstants::ShortcutTableDropDownWidth);
typeShortcut.as<Button>().Width(EditorConstants::ShortcutTableDropDownWidth);
typeShortcut.as<Button>().Click([&, table, row, colIndex, isHybridControl, targetApp](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
keyboardManagerState->SetUIState(KeyboardManagerUIState::DetectShortcutWindowActivated, editShortcutsWindowHandle);
keyboardManagerState->SetUIState(KBMEditor::KeyboardManagerUIState::DetectShortcutWindowActivated, editShortcutsWindowHandle);
// Using the XamlRoot of the typeShortcut to get the root of the XAML host
CreateDetectShortcutWindow(sender, sender.as<Button>().XamlRoot(), *keyboardManagerState, colIndex, table, keyDropDownControlObjects, row, targetApp, isHybridControl, false, editShortcutsWindowHandle, shortcutRemapBuffer);
});
@@ -36,7 +37,7 @@ ShortcutControl::ShortcutControl(StackPanel table, StackPanel row, const int col
// Set an accessible name for the type shortcut button
typeShortcut.as<Button>().SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_TYPE_BUTTON)));
shortcutControlLayout.as<StackPanel>().Spacing(KeyboardManagerConstants::ShortcutTableDropDownSpacing);
shortcutControlLayout.as<StackPanel>().Spacing(EditorConstants::ShortcutTableDropDownSpacing);
shortcutControlLayout.as<StackPanel>().Children().Append(typeShortcut.as<Button>());
shortcutControlLayout.as<StackPanel>().Children().Append(shortcutDropDownStackPanel.as<StackPanel>());
@@ -90,7 +91,7 @@ void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vector<s
// ShortcutControl for the original shortcut
auto origin = keyboardRemapControlObjects.back()[0]->GetShortcutControl();
origin.Width(KeyboardManagerConstants::ShortcutOriginColumnWidth);
origin.Width(EditorConstants::ShortcutOriginColumnWidth);
row.Children().Append(origin);
// Arrow icon
@@ -99,17 +100,17 @@ void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vector<s
arrowIcon.Glyph(L"\xE72A");
arrowIcon.VerticalAlignment(VerticalAlignment::Center);
arrowIcon.HorizontalAlignment(HorizontalAlignment::Center);
auto arrowIconContainer = UIHelpers::GetWrapped(arrowIcon, KeyboardManagerConstants::ShortcutArrowColumnWidth).as<StackPanel>();
auto arrowIconContainer = UIHelpers::GetWrapped(arrowIcon, EditorConstants::ShortcutArrowColumnWidth).as<StackPanel>();
arrowIconContainer.Orientation(Orientation::Vertical);
arrowIconContainer.VerticalAlignment(VerticalAlignment::Center);
row.Children().Append(arrowIconContainer);
// ShortcutControl for the new shortcut
auto target = keyboardRemapControlObjects.back()[1]->GetShortcutControl();
target.Width(KeyboardManagerConstants::ShortcutTargetColumnWidth);
target.Width(EditorConstants::ShortcutTargetColumnWidth);
row.Children().Append(target);
targetAppTextBox.Width(KeyboardManagerConstants::ShortcutTableDropDownWidth);
targetAppTextBox.Width(EditorConstants::ShortcutTableDropDownWidth);
targetAppTextBox.PlaceholderText(KeyboardManagerEditorStrings::DefaultAppName);
targetAppTextBox.Text(targetAppName);
@@ -174,10 +175,10 @@ void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vector<s
});
// We need two containers in order to align it horizontally and vertically
StackPanel targetAppHorizontal = UIHelpers::GetWrapped(targetAppTextBox, KeyboardManagerConstants::TableTargetAppColWidth).as<StackPanel>();
StackPanel targetAppHorizontal = UIHelpers::GetWrapped(targetAppTextBox, EditorConstants::TableTargetAppColWidth).as<StackPanel>();
targetAppHorizontal.Orientation(Orientation::Horizontal);
targetAppHorizontal.HorizontalAlignment(HorizontalAlignment::Left);
StackPanel targetAppContainer = UIHelpers::GetWrapped(targetAppHorizontal, KeyboardManagerConstants::TableTargetAppColWidth).as<StackPanel>();
StackPanel targetAppContainer = UIHelpers::GetWrapped(targetAppHorizontal, EditorConstants::TableTargetAppColWidth).as<StackPanel>();
targetAppContainer.Orientation(Orientation::Vertical);
targetAppContainer.VerticalAlignment(VerticalAlignment::Bottom);
row.Children().Append(targetAppContainer);
@@ -240,7 +241,7 @@ void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vector<s
UpdateAccessibleNames(keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->GetShortcutControl(), keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->GetShortcutControl(), targetAppTextBox, deleteShortcut, (int)keyboardRemapControlObjects.size());
// Set the shortcut text if the two vectors are not empty (i.e. default args)
if (originalKeys.IsValidShortcut() && !(newKeys.index() == 0 && std::get<DWORD>(newKeys) == NULL) && !(newKeys.index() == 1 && !std::get<Shortcut>(newKeys).IsValidShortcut()))
if (EditorHelpers::IsValidShortcut(originalKeys) && !(newKeys.index() == 0 && std::get<DWORD>(newKeys) == NULL) && !(newKeys.index() == 1 && !EditorHelpers::IsValidShortcut(std::get<Shortcut>(newKeys))))
{
// change to load app name
shortcutRemapBuffer.push_back(std::make_pair<RemapBufferItem, std::wstring>(RemapBufferItem{ Shortcut(), Shortcut() }, std::wstring(targetAppName)));
@@ -269,7 +270,7 @@ StackPanel ShortcutControl::GetShortcutControl()
}
// Function to create the detect shortcut UI window
void ShortcutControl::CreateDetectShortcutWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KeyboardManagerState& keyboardManagerState, const int colIndex, StackPanel table, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, StackPanel row, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow, HWND parentWindow, RemapBuffer& remapBuffer)
void ShortcutControl::CreateDetectShortcutWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KBMEditor::KeyboardManagerState& keyboardManagerState, const int colIndex, StackPanel table, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, StackPanel row, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow, HWND parentWindow, RemapBuffer& remapBuffer)
{
// ContentDialog for detecting shortcuts. This is the parent UI element.
ContentDialog detectShortcutBox;
@@ -325,12 +326,12 @@ void ShortcutControl::CreateDetectShortcutWindow(winrt::Windows::Foundation::IIn
if (isSingleKeyWindow)
{
// Revert UI state back to Edit Keyboard window
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, parentWindow);
keyboardManagerState.SetUIState(KBMEditor::KeyboardManagerUIState::EditKeyboardWindowActivated, parentWindow);
}
else
{
// Revert UI state back to Edit Shortcut window
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, parentWindow);
keyboardManagerState.SetUIState(KBMEditor::KeyboardManagerUIState::EditShortcutsWindowActivated, parentWindow);
}
unregisterKeys();
@@ -391,12 +392,12 @@ void ShortcutControl::CreateDetectShortcutWindow(winrt::Windows::Foundation::IIn
if (isSingleKeyWindow)
{
// Revert UI state back to Edit Keyboard window
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, parentWindow);
keyboardManagerState.SetUIState(KBMEditor::KeyboardManagerUIState::EditKeyboardWindowActivated, parentWindow);
}
else
{
// Revert UI state back to Edit Shortcut window
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, parentWindow);
keyboardManagerState.SetUIState(KBMEditor::KeyboardManagerUIState::EditShortcutsWindowActivated, parentWindow);
}
unregisterKeys();
};

View File

@@ -1,8 +1,12 @@
#pragma once
#include <Shortcut.h>
#include <keyboardmanager/common/Shortcut.h>
namespace KBMEditor
{
class KeyboardManagerState;
}
class KeyboardManagerState;
class KeyDropDownControl;
namespace winrt::Windows::UI::Xaml
{
@@ -38,7 +42,7 @@ public:
static HWND editShortcutsWindowHandle;
// Pointer to the keyboard manager state
static KeyboardManagerState* keyboardManagerState;
static KBMEditor::KeyboardManagerState* keyboardManagerState;
// Stores the current list of remappings
static RemapBuffer shortcutRemapBuffer;
@@ -56,5 +60,5 @@ public:
StackPanel GetShortcutControl();
// Function to create the detect shortcut UI window
static void CreateDetectShortcutWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KeyboardManagerState& keyboardManagerState, const int colIndex, StackPanel table, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, StackPanel controlLayout, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow, HWND parentWindow, RemapBuffer& remapBuffer);
static void CreateDetectShortcutWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KBMEditor::KeyboardManagerState& keyboardManagerState, const int colIndex, StackPanel table, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, StackPanel controlLayout, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow, HWND parentWindow, RemapBuffer& remapBuffer);
};

View File

@@ -0,0 +1,24 @@
#pragma once
// Type to store codes for different errors
enum class ShortcutErrorType
{
NoError,
SameKeyPreviouslyMapped,
MapToSameKey,
ConflictingModifierKey,
SameShortcutPreviouslyMapped,
MapToSameShortcut,
ConflictingModifierShortcut,
WinL,
CtrlAltDel,
RemapUnsuccessful,
SaveFailed,
ShortcutStartWithModifier,
ShortcutCannotHaveRepeatedModifier,
ShortcutAtleast2Keys,
ShortcutOneActionKey,
ShortcutNotMoreThanOneActionKey,
ShortcutMaxShortcutSizeOneActionKey,
ShortcutDisableAsActionKey
};

View File

@@ -1,21 +1,22 @@
#include "pch.h"
#include "SingleKeyRemapControl.h"
#include <KeyboardManagerState.h>
#include <ShortcutControl.h>
#include <UIHelpers.h>
#include "KeyboardManagerState.h"
#include "ShortcutControl.h"
#include "UIHelpers.h"
#include "EditorHelpers.h"
#include "EditorConstants.h"
//Both static members are initialized to null
HWND SingleKeyRemapControl::EditKeyboardWindowHandle = nullptr;
KeyboardManagerState* SingleKeyRemapControl::keyboardManagerState = nullptr;
KBMEditor::KeyboardManagerState* SingleKeyRemapControl::keyboardManagerState = nullptr;
// Initialized as new vector
RemapBuffer SingleKeyRemapControl::singleKeyRemapBuffer;
SingleKeyRemapControl::SingleKeyRemapControl(StackPanel table, StackPanel row, const int colIndex)
{
typeKey = Button();
typeKey.as<Button>().Width(KeyboardManagerConstants::RemapTableDropDownWidth);
typeKey.as<Button>().Width(EditorConstants::RemapTableDropDownWidth);
typeKey.as<Button>().Content(winrt::box_value(GET_RESOURCE_STRING(IDS_TYPE_BUTTON)));
singleKeyRemapControlLayout = StackPanel();
@@ -35,7 +36,7 @@ SingleKeyRemapControl::SingleKeyRemapControl(StackPanel table, StackPanel row, c
else
{
hybridDropDownStackPanel = StackPanel();
hybridDropDownStackPanel.as<StackPanel>().Spacing(KeyboardManagerConstants::ShortcutTableDropDownSpacing);
hybridDropDownStackPanel.as<StackPanel>().Spacing(EditorConstants::ShortcutTableDropDownSpacing);
hybridDropDownStackPanel.as<StackPanel>().Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
KeyDropDownControl::AddDropDown(table, row, hybridDropDownStackPanel.as<StackPanel>(), colIndex, singleKeyRemapBuffer, keyDropDownControlObjects, nullptr, true, true);
singleKeyRemapControlLayout.as<StackPanel>().Children().Append(hybridDropDownStackPanel.as<StackPanel>());
@@ -45,12 +46,12 @@ SingleKeyRemapControl::SingleKeyRemapControl(StackPanel table, StackPanel row, c
// Using the XamlRoot of the typeKey to get the root of the XAML host
if (colIndex == 0)
{
keyboardManagerState->SetUIState(KeyboardManagerUIState::DetectSingleKeyRemapWindowActivated, EditKeyboardWindowHandle);
keyboardManagerState->SetUIState(KBMEditor::KeyboardManagerUIState::DetectSingleKeyRemapWindowActivated, EditKeyboardWindowHandle);
createDetectKeyWindow(sender, sender.as<Button>().XamlRoot(), *keyboardManagerState);
}
else
{
keyboardManagerState->SetUIState(KeyboardManagerUIState::DetectShortcutWindowInEditKeyboardWindowActivated, EditKeyboardWindowHandle);
keyboardManagerState->SetUIState(KBMEditor::KeyboardManagerUIState::DetectShortcutWindowInEditKeyboardWindowActivated, EditKeyboardWindowHandle);
ShortcutControl::CreateDetectShortcutWindow(sender, sender.as<Button>().XamlRoot(), *keyboardManagerState, colIndex, table, keyDropDownControlObjects, row, nullptr, true, true, EditKeyboardWindowHandle, singleKeyRemapBuffer);
}
});
@@ -87,7 +88,7 @@ void SingleKeyRemapControl::AddNewControlKeyRemapRow(StackPanel& parent, std::ve
// SingleKeyRemapControl for the original key.
auto originalElement = keyboardRemapControlObjects.back()[0]->getSingleKeyRemapControl();
originalElement.Width(KeyboardManagerConstants::RemapTableDropDownWidth);
originalElement.Width(EditorConstants::RemapTableDropDownWidth);
row.Children().Append(originalElement);
// Arrow icon
@@ -96,18 +97,18 @@ void SingleKeyRemapControl::AddNewControlKeyRemapRow(StackPanel& parent, std::ve
arrowIcon.Glyph(L"\xE72A");
arrowIcon.VerticalAlignment(VerticalAlignment::Center);
arrowIcon.HorizontalAlignment(HorizontalAlignment::Center);
auto arrowIconContainer = UIHelpers::GetWrapped(arrowIcon, KeyboardManagerConstants::TableArrowColWidth).as<StackPanel>();
auto arrowIconContainer = UIHelpers::GetWrapped(arrowIcon, EditorConstants::TableArrowColWidth).as<StackPanel>();
arrowIconContainer.Orientation(Orientation::Vertical);
arrowIconContainer.VerticalAlignment(VerticalAlignment::Center);
row.Children().Append(arrowIconContainer);
// SingleKeyRemapControl for the new remap key
auto targetElement = keyboardRemapControlObjects.back()[1]->getSingleKeyRemapControl();
targetElement.Width(KeyboardManagerConstants::ShortcutTargetColumnWidth);
targetElement.Width(EditorConstants::ShortcutTargetColumnWidth);
row.Children().Append(targetElement);
// Set the key text if the two keys are not null (i.e. default args)
if (originalKey != NULL && !(newKey.index() == 0 && std::get<DWORD>(newKey) == NULL) && !(newKey.index() == 1 && !std::get<Shortcut>(newKey).IsValidShortcut()))
if (originalKey != NULL && !(newKey.index() == 0 && std::get<DWORD>(newKey) == NULL) && !(newKey.index() == 1 && !EditorHelpers::IsValidShortcut(std::get<Shortcut>(newKey))))
{
singleKeyRemapBuffer.push_back(std::make_pair<RemapBufferItem, std::wstring>(RemapBufferItem{ originalKey, newKey }, L""));
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->keyDropDownControlObjects[0]->SetSelectedValue(std::to_wstring(originalKey));
@@ -186,7 +187,7 @@ StackPanel SingleKeyRemapControl::getSingleKeyRemapControl()
}
// Function to create the detect remap key UI window
void SingleKeyRemapControl::createDetectKeyWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KeyboardManagerState& keyboardManagerState)
void SingleKeyRemapControl::createDetectKeyWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KBMEditor::KeyboardManagerState& keyboardManagerState)
{
// ContentDialog for detecting remap key. This is the parent UI element.
ContentDialog detectRemapKeyBox;
@@ -229,7 +230,7 @@ void SingleKeyRemapControl::createDetectKeyWindow(winrt::Windows::Foundation::II
// Reset the keyboard manager UI state
keyboardManagerState.ResetUIState();
// Revert UI state back to Edit Keyboard window
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, EditKeyboardWindowHandle);
keyboardManagerState.SetUIState(KBMEditor::KeyboardManagerUIState::EditKeyboardWindowActivated, EditKeyboardWindowHandle);
unregisterKeys();
};
@@ -253,7 +254,7 @@ void SingleKeyRemapControl::createDetectKeyWindow(winrt::Windows::Foundation::II
// NOTE: UnregisterKeys should never be called on the DelayThread, as it will re-enter the mutex. To avoid this it is run on the dispatcher thread
keyboardManagerState.RegisterKeyDelay(
VK_RETURN,
std::bind(&KeyboardManagerState::SelectDetectedRemapKey, &keyboardManagerState, std::placeholders::_1),
std::bind(&KBMEditor::KeyboardManagerState::SelectDetectedRemapKey, &keyboardManagerState, std::placeholders::_1),
[primaryButton, onPressEnter, detectRemapKeyBox](DWORD) {
detectRemapKeyBox.Dispatcher().RunAsync(
Windows::UI::Core::CoreDispatcherPriority::Normal,
@@ -283,7 +284,7 @@ void SingleKeyRemapControl::createDetectKeyWindow(winrt::Windows::Foundation::II
keyboardManagerState.ResetUIState();
// Revert UI state back to Edit Keyboard window
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, EditKeyboardWindowHandle);
keyboardManagerState.SetUIState(KBMEditor::KeyboardManagerUIState::EditKeyboardWindowActivated, EditKeyboardWindowHandle);
unregisterKeys();
};
@@ -300,7 +301,7 @@ void SingleKeyRemapControl::createDetectKeyWindow(winrt::Windows::Foundation::II
// NOTE: UnregisterKeys should never be called on the DelayThread, as it will re-enter the mutex. To avoid this it is run on the dispatcher thread
keyboardManagerState.RegisterKeyDelay(
VK_ESCAPE,
std::bind(&KeyboardManagerState::SelectDetectedRemapKey, &keyboardManagerState, std::placeholders::_1),
std::bind(&KBMEditor::KeyboardManagerState::SelectDetectedRemapKey, &keyboardManagerState, std::placeholders::_1),
[onCancel, detectRemapKeyBox](DWORD) {
detectRemapKeyBox.Dispatcher().RunAsync(
Windows::UI::Core::CoreDispatcherPriority::Normal,

View File

@@ -1,10 +1,14 @@
#pragma once
#include <Shortcut.h>
#include <keyboardmanager/common/Shortcut.h>
#include <KeyDropDownControl.h>
class KeyboardManagerState;
namespace KBMEditor
{
class KeyboardManagerState;
}
namespace winrt::Windows::UI::Xaml
{
struct XamlRoot;
@@ -39,7 +43,7 @@ public:
static HWND EditKeyboardWindowHandle;
// Pointer to the keyboard manager state
static KeyboardManagerState* keyboardManagerState;
static KBMEditor::KeyboardManagerState* keyboardManagerState;
// Stores the current list of remappings
static RemapBuffer singleKeyRemapBuffer;
@@ -54,5 +58,5 @@ public:
winrt::Windows::UI::Xaml::Controls::StackPanel getSingleKeyRemapControl();
// Function to create the detect remap keys UI window
void createDetectKeyWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KeyboardManagerState& keyboardManagerState);
void createDetectKeyWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KBMEditor::KeyboardManagerState& keyboardManagerState);
};