2020-03-24 01:44:02 +08:00
|
|
|
#include "pch.h"
|
2020-03-06 07:52:59 +08:00
|
|
|
#include "EditKeyboardWindow.h"
|
2020-03-27 23:38:58 +08:00
|
|
|
#include "SingleKeyRemapControl.h"
|
2020-04-19 07:12:26 +08:00
|
|
|
#include "KeyDropDownControl.h"
|
2020-04-30 08:58:31 +08:00
|
|
|
#include "XamlBridge.h"
|
2020-05-06 03:30:50 +08:00
|
|
|
#include <keyboardmanager/common/trace.h>
|
2020-05-12 03:45:55 +08:00
|
|
|
#include <keyboardmanager/common/KeyboardManagerConstants.h>
|
|
|
|
#include <set>
|
|
|
|
#include <common/windows_colors.h>
|
2020-05-13 01:17:27 +08:00
|
|
|
#include <common/dpi_aware.h>
|
2020-05-12 03:45:55 +08:00
|
|
|
#include "Styles.h"
|
|
|
|
#include "Dialog.h"
|
2020-05-12 11:13:07 +08:00
|
|
|
#include <keyboardmanager/dll/resource.h>
|
2020-05-12 03:45:55 +08:00
|
|
|
|
|
|
|
using namespace winrt::Windows::Foundation;
|
2020-03-06 07:52:59 +08:00
|
|
|
|
|
|
|
LRESULT CALLBACK EditKeyboardWindowProc(HWND, UINT, WPARAM, LPARAM);
|
|
|
|
|
2020-03-24 01:44:02 +08:00
|
|
|
// This Hwnd will be the window handler for the Xaml Island: A child window that contains Xaml.
|
2020-03-06 07:52:59 +08:00
|
|
|
HWND hWndXamlIslandEditKeyboardWindow = nullptr;
|
2020-03-24 01:44:02 +08:00
|
|
|
// This variable is used to check if window registration has been done to avoid repeated registration leading to an error.
|
|
|
|
bool isEditKeyboardWindowRegistrationCompleted = false;
|
2020-04-15 00:24:11 +08:00
|
|
|
// Holds the native window handle of EditKeyboard Window.
|
|
|
|
HWND hwndEditKeyboardNativeWindow = nullptr;
|
|
|
|
std::mutex editKeyboardWindowMutex;
|
2020-04-30 08:58:31 +08:00
|
|
|
// Stores a pointer to the Xaml Bridge object so that it can be accessed from the window procedure
|
|
|
|
static XamlBridge* xamlBridgePtr = nullptr;
|
2020-03-06 07:52:59 +08:00
|
|
|
|
2020-05-12 03:45:55 +08:00
|
|
|
static std::vector<DWORD> GetOrphanedKeys()
|
|
|
|
{
|
|
|
|
std::set<DWORD> ogKeys;
|
|
|
|
std::set<DWORD> newKeys;
|
|
|
|
|
|
|
|
for (int i = 0; i < SingleKeyRemapControl::singleKeyRemapBuffer.size(); i++)
|
|
|
|
{
|
|
|
|
DWORD ogKey = SingleKeyRemapControl::singleKeyRemapBuffer[i][0];
|
|
|
|
DWORD newKey = SingleKeyRemapControl::singleKeyRemapBuffer[i][1];
|
|
|
|
if (ogKey != 0 && newKey != 0)
|
|
|
|
{
|
|
|
|
ogKeys.insert(ogKey);
|
|
|
|
newKeys.insert(newKey);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto& k : newKeys)
|
|
|
|
{
|
|
|
|
ogKeys.erase(k);
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::vector(ogKeys.begin(), ogKeys.end());
|
|
|
|
}
|
|
|
|
|
|
|
|
static IAsyncOperation<bool> OrphanKeysConfirmationDialog(
|
|
|
|
KeyboardManagerState& state,
|
|
|
|
const std::vector<DWORD>& keys,
|
|
|
|
XamlRoot root)
|
|
|
|
{
|
|
|
|
ContentDialog confirmationDialog;
|
|
|
|
confirmationDialog.XamlRoot(root);
|
|
|
|
confirmationDialog.Title(box_value(L"The following keys are unassigned and you won't be able to use them:"));
|
|
|
|
confirmationDialog.Content(nullptr);
|
|
|
|
confirmationDialog.IsPrimaryButtonEnabled(true);
|
|
|
|
confirmationDialog.DefaultButton(ContentDialogButton::Primary);
|
|
|
|
confirmationDialog.PrimaryButtonText(winrt::hstring(L"Continue Anyway"));
|
|
|
|
confirmationDialog.IsSecondaryButtonEnabled(true);
|
|
|
|
confirmationDialog.SecondaryButtonText(winrt::hstring(L"Cancel"));
|
|
|
|
|
|
|
|
TextBlock orphanKeysBlock;
|
|
|
|
std::wstring orphanKeyString;
|
|
|
|
for (auto k : keys)
|
|
|
|
{
|
|
|
|
orphanKeyString.append(state.keyboardMap.GetKeyName(k));
|
|
|
|
orphanKeyString.append(L", ");
|
|
|
|
}
|
|
|
|
orphanKeyString = orphanKeyString.substr(0, max(0, orphanKeyString.length() - 2));
|
|
|
|
orphanKeysBlock.Text(winrt::hstring(orphanKeyString));
|
|
|
|
orphanKeysBlock.TextWrapping(TextWrapping::Wrap);
|
|
|
|
confirmationDialog.Content(orphanKeysBlock);
|
|
|
|
|
|
|
|
ContentDialogResult res = co_await confirmationDialog.ShowAsync();
|
|
|
|
|
|
|
|
co_return res == ContentDialogResult::Primary;
|
|
|
|
}
|
|
|
|
|
|
|
|
static IAsyncAction OnClickAccept(KeyboardManagerState& keyboardManagerState, XamlRoot root, std::function<void()> ApplyRemappings)
|
|
|
|
{
|
|
|
|
KeyboardManagerHelper::ErrorType isSuccess = Dialog::CheckIfRemappingsAreValid<DWORD>(
|
|
|
|
SingleKeyRemapControl::singleKeyRemapBuffer,
|
|
|
|
[](DWORD key) {
|
|
|
|
return key != 0;
|
|
|
|
});
|
|
|
|
if (isSuccess != KeyboardManagerHelper::ErrorType::NoError)
|
|
|
|
{
|
|
|
|
if (!co_await Dialog::PartialRemappingConfirmationDialog(root))
|
|
|
|
{
|
|
|
|
co_return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Check for orphaned keys
|
|
|
|
// Draw content Dialog
|
|
|
|
std::vector<DWORD> orphanedKeys = GetOrphanedKeys();
|
|
|
|
if (orphanedKeys.size() > 0)
|
|
|
|
{
|
|
|
|
if (!co_await OrphanKeysConfirmationDialog(keyboardManagerState, orphanedKeys, root))
|
|
|
|
{
|
|
|
|
co_return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ApplyRemappings();
|
|
|
|
}
|
2020-05-12 08:18:12 +08:00
|
|
|
|
|
|
|
// Function to combine remappings if the L and R version of the modifier is mapped to the same key
|
|
|
|
void CombineRemappings(std::unordered_map<DWORD, DWORD>& table, DWORD leftKey, DWORD rightKey, DWORD combinedKey)
|
|
|
|
{
|
|
|
|
if (table.find(leftKey) != table.end() && table.find(rightKey) != table.end())
|
|
|
|
{
|
|
|
|
// If they are mapped to the same key, delete those entries and set the common version
|
|
|
|
if (table[leftKey] == table[rightKey])
|
|
|
|
{
|
|
|
|
table[combinedKey] = table[leftKey];
|
|
|
|
table.erase(leftKey);
|
|
|
|
table.erase(rightKey);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function to pre process the remap table before loading it into the UI
|
|
|
|
void PreProcessRemapTable(std::unordered_map<DWORD, DWORD>& table)
|
|
|
|
{
|
|
|
|
// Pre process the table to combine L and R versions of Ctrl/Alt/Shift/Win that are mapped to the same key
|
|
|
|
CombineRemappings(table, VK_LCONTROL, VK_RCONTROL, VK_CONTROL);
|
|
|
|
CombineRemappings(table, VK_LMENU, VK_RMENU, VK_MENU);
|
|
|
|
CombineRemappings(table, VK_LSHIFT, VK_RSHIFT, VK_SHIFT);
|
|
|
|
CombineRemappings(table, VK_LWIN, VK_RWIN, CommonSharedConstants::VK_WIN_BOTH);
|
|
|
|
}
|
|
|
|
|
2020-03-24 01:44:02 +08:00
|
|
|
// Function to create the Edit Keyboard Window
|
|
|
|
void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState)
|
2020-03-06 07:52:59 +08:00
|
|
|
{
|
2020-03-24 01:44:02 +08:00
|
|
|
// Window Registration
|
2020-03-06 07:52:59 +08:00
|
|
|
const wchar_t szWindowClass[] = L"EditKeyboardWindow";
|
2020-03-24 01:44:02 +08:00
|
|
|
if (!isEditKeyboardWindowRegistrationCompleted)
|
2020-03-06 07:52:59 +08:00
|
|
|
{
|
2020-03-24 01:44:02 +08:00
|
|
|
WNDCLASSEX windowClass = {};
|
|
|
|
windowClass.cbSize = sizeof(WNDCLASSEX);
|
|
|
|
windowClass.lpfnWndProc = EditKeyboardWindowProc;
|
|
|
|
windowClass.hInstance = hInst;
|
|
|
|
windowClass.lpszClassName = szWindowClass;
|
|
|
|
windowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW);
|
2020-05-13 06:58:11 +08:00
|
|
|
windowClass.hIcon = (HICON)LoadImageW(
|
|
|
|
windowClass.hInstance,
|
|
|
|
MAKEINTRESOURCE(IDS_KEYBOARDMANAGER_ICON),
|
|
|
|
IMAGE_ICON,
|
|
|
|
48,
|
|
|
|
48,
|
2020-05-12 11:13:07 +08:00
|
|
|
LR_DEFAULTCOLOR);
|
2020-03-24 01:44:02 +08:00
|
|
|
if (RegisterClassEx(&windowClass) == NULL)
|
|
|
|
{
|
|
|
|
MessageBox(NULL, L"Windows registration failed!", L"Error", NULL);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
isEditKeyboardWindowRegistrationCompleted = true;
|
2020-03-06 07:52:59 +08:00
|
|
|
}
|
|
|
|
|
2020-05-09 08:34:24 +08:00
|
|
|
// Find center screen coordinates
|
|
|
|
RECT desktopRect;
|
|
|
|
GetClientRect(GetDesktopWindow(), &desktopRect);
|
2020-05-13 01:17:27 +08:00
|
|
|
// Calculate DPI dependent window size
|
|
|
|
int windowWidth = KeyboardManagerConstants::DefaultEditKeyboardWindowWidth;
|
|
|
|
int windowHeight = KeyboardManagerConstants::DefaultEditKeyboardWindowHeight;
|
|
|
|
DPIAware::Convert(nullptr, windowWidth, windowHeight);
|
2020-05-09 08:34:24 +08:00
|
|
|
|
2020-03-24 01:44:02 +08:00
|
|
|
// Window Creation
|
2020-03-06 07:52:59 +08:00
|
|
|
HWND _hWndEditKeyboardWindow = CreateWindow(
|
|
|
|
szWindowClass,
|
2020-03-31 02:05:29 +08:00
|
|
|
L"Remap Keyboard",
|
2020-05-13 06:58:11 +08:00
|
|
|
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MAXIMIZEBOX,
|
2020-05-09 08:34:24 +08:00
|
|
|
(desktopRect.right / 2) - (windowWidth / 2),
|
|
|
|
(desktopRect.bottom / 2) - (windowHeight / 2),
|
|
|
|
windowWidth,
|
|
|
|
windowHeight,
|
2020-03-06 07:52:59 +08:00
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
hInst,
|
|
|
|
NULL);
|
|
|
|
if (_hWndEditKeyboardWindow == NULL)
|
|
|
|
{
|
|
|
|
MessageBox(NULL, L"Call to CreateWindow failed!", L"Error", NULL);
|
|
|
|
return;
|
|
|
|
}
|
2020-05-09 08:34:24 +08:00
|
|
|
// Ensures the window is in foreground on first startup. If this is not done, the window appears behind because the thread is not on the foreground.
|
|
|
|
if (_hWndEditKeyboardWindow)
|
|
|
|
{
|
|
|
|
SetForegroundWindow(_hWndEditKeyboardWindow);
|
|
|
|
}
|
2020-03-06 07:52:59 +08:00
|
|
|
|
2020-04-15 00:24:11 +08:00
|
|
|
// Store the newly created Edit Keyboard window's handle.
|
|
|
|
std::unique_lock<std::mutex> hwndLock(editKeyboardWindowMutex);
|
|
|
|
hwndEditKeyboardNativeWindow = _hWndEditKeyboardWindow;
|
|
|
|
hwndLock.unlock();
|
|
|
|
|
2020-04-30 08:58:31 +08:00
|
|
|
// Create the xaml bridge object
|
|
|
|
XamlBridge xamlBridge(_hWndEditKeyboardWindow);
|
|
|
|
// DesktopSource needs to be declared before the RelativePanel xamlContainer object to avoid errors
|
|
|
|
winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource desktopSource;
|
|
|
|
// Create the desktop window xaml source object and set its content
|
|
|
|
hWndXamlIslandEditKeyboardWindow = xamlBridge.InitDesktopWindowsXamlSource(desktopSource);
|
2020-03-06 07:52:59 +08:00
|
|
|
|
2020-04-30 08:58:31 +08:00
|
|
|
// Set the pointer to the xaml bridge object
|
|
|
|
xamlBridgePtr = &xamlBridge;
|
2020-03-06 07:52:59 +08:00
|
|
|
|
2020-03-27 23:38:58 +08:00
|
|
|
// Header for the window
|
2020-04-24 00:14:16 +08:00
|
|
|
Windows::UI::Xaml::Controls::RelativePanel header;
|
2020-03-27 23:38:58 +08:00
|
|
|
header.Margin({ 10, 10, 10, 30 });
|
|
|
|
|
|
|
|
// Header text
|
|
|
|
TextBlock headerText;
|
2020-04-22 05:14:50 +08:00
|
|
|
headerText.Text(L"Remap Keyboard");
|
2020-03-27 23:38:58 +08:00
|
|
|
headerText.FontSize(30);
|
2020-04-24 00:14:16 +08:00
|
|
|
headerText.Margin({ 0, 0, 0, 0 });
|
|
|
|
header.SetAlignLeftWithPanel(headerText, true);
|
2020-03-27 23:38:58 +08:00
|
|
|
|
|
|
|
// Header Cancel button
|
|
|
|
Button cancelButton;
|
2020-04-22 05:14:50 +08:00
|
|
|
cancelButton.Content(winrt::box_value(L"Cancel"));
|
2020-05-12 03:45:55 +08:00
|
|
|
cancelButton.Margin({ 10, 0, 0, 0 });
|
2020-04-20 23:22:36 +08:00
|
|
|
cancelButton.Click([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
2020-03-27 23:38:58 +08:00
|
|
|
// Close the window since settings do not need to be saved
|
|
|
|
PostMessage(_hWndEditKeyboardWindow, WM_CLOSE, 0, 0);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Text block for information about remap key section.
|
|
|
|
TextBlock keyRemapInfoHeader;
|
2020-05-21 05:52:10 +08:00
|
|
|
keyRemapInfoHeader.Text(L"Select the key you want to change (Key) and the key you want it to become (Mapped To).");
|
2020-04-19 07:12:26 +08:00
|
|
|
keyRemapInfoHeader.Margin({ 10, 0, 0, 10 });
|
2020-05-06 23:34:26 +08:00
|
|
|
keyRemapInfoHeader.FontWeight(Text::FontWeights::SemiBold());
|
2020-05-09 08:34:24 +08:00
|
|
|
keyRemapInfoHeader.TextWrapping(TextWrapping::Wrap);
|
2020-05-06 23:34:26 +08:00
|
|
|
|
|
|
|
TextBlock keyRemapInfoExample;
|
2020-05-21 05:52:10 +08:00
|
|
|
keyRemapInfoExample.Text(L"For example, if you want to press A and get B, Key A would be your \"Key\" and Key B would be your \"Mapped To\".");
|
2020-05-06 23:34:26 +08:00
|
|
|
keyRemapInfoExample.Margin({ 10, 0, 0, 20 });
|
|
|
|
keyRemapInfoExample.FontStyle(Text::FontStyle::Italic);
|
2020-05-09 08:34:24 +08:00
|
|
|
keyRemapInfoExample.TextWrapping(TextWrapping::Wrap);
|
2020-03-27 23:38:58 +08:00
|
|
|
|
|
|
|
// Table to display the key remaps
|
2020-04-24 00:14:16 +08:00
|
|
|
Grid keyRemapTable;
|
2020-05-09 08:34:24 +08:00
|
|
|
ColumnDefinition originalColumn;
|
|
|
|
originalColumn.MinWidth(KeyboardManagerConstants::RemapTableDropDownWidth);
|
|
|
|
originalColumn.MaxWidth(KeyboardManagerConstants::RemapTableDropDownWidth);
|
|
|
|
ColumnDefinition arrowColumn;
|
|
|
|
arrowColumn.MinWidth(KeyboardManagerConstants::TableArrowColWidth);
|
|
|
|
ColumnDefinition newColumn;
|
|
|
|
newColumn.MinWidth(KeyboardManagerConstants::RemapTableDropDownWidth);
|
|
|
|
newColumn.MaxWidth(KeyboardManagerConstants::RemapTableDropDownWidth);
|
|
|
|
ColumnDefinition removeColumn;
|
|
|
|
removeColumn.MinWidth(KeyboardManagerConstants::TableRemoveColWidth);
|
2020-03-27 23:38:58 +08:00
|
|
|
keyRemapTable.Margin({ 10, 10, 10, 20 });
|
2020-04-24 00:14:16 +08:00
|
|
|
keyRemapTable.HorizontalAlignment(HorizontalAlignment::Stretch);
|
2020-05-09 08:34:24 +08:00
|
|
|
keyRemapTable.ColumnDefinitions().Append(originalColumn);
|
|
|
|
keyRemapTable.ColumnDefinitions().Append(arrowColumn);
|
|
|
|
keyRemapTable.ColumnDefinitions().Append(newColumn);
|
|
|
|
keyRemapTable.ColumnDefinitions().Append(removeColumn);
|
2020-04-24 00:14:16 +08:00
|
|
|
keyRemapTable.RowDefinitions().Append(RowDefinition());
|
2020-03-27 23:38:58 +08:00
|
|
|
|
|
|
|
// First header textblock in the header row of the keys remap table
|
|
|
|
TextBlock originalKeyRemapHeader;
|
2020-05-21 05:52:10 +08:00
|
|
|
originalKeyRemapHeader.Text(L"Key:");
|
2020-03-27 23:38:58 +08:00
|
|
|
originalKeyRemapHeader.FontWeight(Text::FontWeights::Bold());
|
|
|
|
originalKeyRemapHeader.Margin({ 0, 0, 0, 10 });
|
|
|
|
|
|
|
|
// Second header textblock in the header row of the keys remap table
|
|
|
|
TextBlock newKeyRemapHeader;
|
2020-05-21 05:52:10 +08:00
|
|
|
newKeyRemapHeader.Text(L"Mapped To:");
|
2020-03-27 23:38:58 +08:00
|
|
|
newKeyRemapHeader.FontWeight(Text::FontWeights::Bold());
|
|
|
|
newKeyRemapHeader.Margin({ 0, 0, 0, 10 });
|
|
|
|
|
2020-05-09 08:34:24 +08:00
|
|
|
keyRemapTable.SetColumn(originalKeyRemapHeader, KeyboardManagerConstants::RemapTableOriginalColIndex);
|
2020-04-24 00:14:16 +08:00
|
|
|
keyRemapTable.SetRow(originalKeyRemapHeader, 0);
|
2020-05-09 08:34:24 +08:00
|
|
|
keyRemapTable.SetColumn(newKeyRemapHeader, KeyboardManagerConstants::RemapTableNewColIndex);
|
2020-04-24 00:14:16 +08:00
|
|
|
keyRemapTable.SetRow(newKeyRemapHeader, 0);
|
|
|
|
|
|
|
|
keyRemapTable.Children().Append(originalKeyRemapHeader);
|
|
|
|
keyRemapTable.Children().Append(newKeyRemapHeader);
|
2020-03-27 23:38:58 +08:00
|
|
|
|
2020-04-10 00:20:19 +08:00
|
|
|
// Store handle of edit keyboard window
|
|
|
|
SingleKeyRemapControl::EditKeyboardWindowHandle = _hWndEditKeyboardWindow;
|
|
|
|
// Store keyboard manager state
|
|
|
|
SingleKeyRemapControl::keyboardManagerState = &keyboardManagerState;
|
2020-04-19 07:12:26 +08:00
|
|
|
KeyDropDownControl::keyboardManagerState = &keyboardManagerState;
|
2020-04-10 00:20:19 +08:00
|
|
|
// Clear the single key remap buffer
|
|
|
|
SingleKeyRemapControl::singleKeyRemapBuffer.clear();
|
2020-04-19 07:12:26 +08:00
|
|
|
// Vector to store dynamically allocated control objects to avoid early destruction
|
|
|
|
std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>> keyboardRemapControlObjects;
|
2020-04-10 00:20:19 +08:00
|
|
|
|
2020-05-05 06:49:37 +08:00
|
|
|
// Set keyboard manager UI state so that remaps are not applied while on this window
|
|
|
|
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, _hWndEditKeyboardWindow);
|
|
|
|
|
2020-04-10 00:20:19 +08:00
|
|
|
// Load existing remaps into UI
|
|
|
|
std::unique_lock<std::mutex> lock(keyboardManagerState.singleKeyReMap_mutex);
|
2020-04-22 04:40:31 +08:00
|
|
|
std::unordered_map<DWORD, DWORD> singleKeyRemapCopy = keyboardManagerState.singleKeyReMap;
|
|
|
|
lock.unlock();
|
2020-05-12 08:18:12 +08:00
|
|
|
PreProcessRemapTable(singleKeyRemapCopy);
|
2020-04-22 04:40:31 +08:00
|
|
|
|
|
|
|
for (const auto& it : singleKeyRemapCopy)
|
2020-04-10 00:20:19 +08:00
|
|
|
{
|
2020-04-19 07:12:26 +08:00
|
|
|
SingleKeyRemapControl::AddNewControlKeyRemapRow(keyRemapTable, keyboardRemapControlObjects, it.first, it.second);
|
2020-04-10 00:20:19 +08:00
|
|
|
}
|
|
|
|
|
2020-03-27 23:38:58 +08:00
|
|
|
// Main Header Apply button
|
|
|
|
Button applyButton;
|
2020-05-12 03:45:55 +08:00
|
|
|
applyButton.Content(winrt::box_value(L"OK"));
|
|
|
|
applyButton.Style(AccentButtonStyle());
|
|
|
|
applyButton.MinWidth(KeyboardManagerConstants::HeaderButtonWidth);
|
|
|
|
cancelButton.MinWidth(KeyboardManagerConstants::HeaderButtonWidth);
|
|
|
|
header.SetAlignRightWithPanel(cancelButton, true);
|
|
|
|
header.SetLeftOf(applyButton, cancelButton);
|
|
|
|
|
|
|
|
auto ApplyRemappings = [&keyboardManagerState, _hWndEditKeyboardWindow]() {
|
2020-04-27 06:09:40 +08:00
|
|
|
KeyboardManagerHelper::ErrorType isSuccess = KeyboardManagerHelper::ErrorType::NoError;
|
2020-03-27 23:38:58 +08:00
|
|
|
// Clear existing Key Remaps
|
|
|
|
keyboardManagerState.ClearSingleKeyRemaps();
|
2020-05-06 03:30:50 +08:00
|
|
|
DWORD successfulRemapCount = 0;
|
2020-04-10 00:20:19 +08:00
|
|
|
for (int i = 0; i < SingleKeyRemapControl::singleKeyRemapBuffer.size(); i++)
|
2020-03-27 23:38:58 +08:00
|
|
|
{
|
2020-04-10 00:20:19 +08:00
|
|
|
DWORD originalKey = SingleKeyRemapControl::singleKeyRemapBuffer[i][0];
|
|
|
|
DWORD newKey = SingleKeyRemapControl::singleKeyRemapBuffer[i][1];
|
2020-04-04 01:57:46 +08:00
|
|
|
|
2020-04-10 00:20:19 +08:00
|
|
|
if (originalKey != NULL && newKey != NULL)
|
|
|
|
{
|
2020-04-22 04:40:31 +08:00
|
|
|
// If Ctrl/Alt/Shift are added, add their L and R versions instead to the same key
|
|
|
|
bool result = false;
|
|
|
|
bool res1, res2;
|
|
|
|
switch (originalKey)
|
|
|
|
{
|
|
|
|
case VK_CONTROL:
|
|
|
|
res1 = keyboardManagerState.AddSingleKeyRemap(VK_LCONTROL, newKey);
|
|
|
|
res2 = keyboardManagerState.AddSingleKeyRemap(VK_RCONTROL, newKey);
|
|
|
|
result = res1 && res2;
|
|
|
|
break;
|
|
|
|
case VK_MENU:
|
|
|
|
res1 = keyboardManagerState.AddSingleKeyRemap(VK_LMENU, newKey);
|
|
|
|
res2 = keyboardManagerState.AddSingleKeyRemap(VK_RMENU, newKey);
|
|
|
|
result = res1 && res2;
|
|
|
|
break;
|
|
|
|
case VK_SHIFT:
|
|
|
|
res1 = keyboardManagerState.AddSingleKeyRemap(VK_LSHIFT, newKey);
|
|
|
|
res2 = keyboardManagerState.AddSingleKeyRemap(VK_RSHIFT, newKey);
|
|
|
|
result = res1 && res2;
|
|
|
|
break;
|
2020-04-23 23:37:52 +08:00
|
|
|
case CommonSharedConstants::VK_WIN_BOTH:
|
|
|
|
res1 = keyboardManagerState.AddSingleKeyRemap(VK_LWIN, newKey);
|
|
|
|
res2 = keyboardManagerState.AddSingleKeyRemap(VK_RWIN, newKey);
|
|
|
|
result = res1 && res2;
|
|
|
|
break;
|
2020-04-22 04:40:31 +08:00
|
|
|
default:
|
|
|
|
result = keyboardManagerState.AddSingleKeyRemap(originalKey, newKey);
|
|
|
|
}
|
|
|
|
|
2020-04-04 01:57:46 +08:00
|
|
|
if (!result)
|
|
|
|
{
|
2020-04-27 06:09:40 +08:00
|
|
|
isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful;
|
|
|
|
// Tooltip is already shown for this row
|
2020-04-04 01:57:46 +08:00
|
|
|
}
|
2020-05-06 03:30:50 +08:00
|
|
|
else
|
|
|
|
{
|
|
|
|
successfulRemapCount += 1;
|
|
|
|
}
|
2020-03-27 23:38:58 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-04-27 06:09:40 +08:00
|
|
|
isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful;
|
2020-03-27 23:38:58 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-12 03:45:55 +08:00
|
|
|
Trace::KeyRemapCount(successfulRemapCount);
|
2020-04-20 23:22:36 +08:00
|
|
|
// Save the updated shortcuts remaps to file.
|
2020-04-27 06:09:40 +08:00
|
|
|
bool saveResult = keyboardManagerState.SaveConfigToFile();
|
|
|
|
if (!saveResult)
|
2020-04-20 23:22:36 +08:00
|
|
|
{
|
2020-04-27 06:09:40 +08:00
|
|
|
isSuccess = KeyboardManagerHelper::ErrorType::SaveFailed;
|
2020-04-20 23:22:36 +08:00
|
|
|
}
|
2020-05-12 03:45:55 +08:00
|
|
|
|
|
|
|
PostMessage(_hWndEditKeyboardWindow, WM_CLOSE, 0, 0);
|
|
|
|
};
|
|
|
|
|
|
|
|
applyButton.Click([&keyboardManagerState, ApplyRemappings, applyButton](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
|
|
|
OnClickAccept(keyboardManagerState, applyButton.XamlRoot(), ApplyRemappings);
|
2020-03-27 23:38:58 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
header.Children().Append(headerText);
|
|
|
|
header.Children().Append(applyButton);
|
2020-05-14 01:13:44 +08:00
|
|
|
header.Children().Append(cancelButton);
|
2020-03-27 23:38:58 +08:00
|
|
|
|
2020-05-15 00:24:50 +08:00
|
|
|
ScrollViewer scrollViewer;
|
|
|
|
|
2020-03-27 23:38:58 +08:00
|
|
|
// Add remap key button
|
|
|
|
Windows::UI::Xaml::Controls::Button addRemapKey;
|
|
|
|
FontIcon plusSymbol;
|
|
|
|
plusSymbol.FontFamily(Xaml::Media::FontFamily(L"Segoe MDL2 Assets"));
|
|
|
|
plusSymbol.Glyph(L"\xE109");
|
|
|
|
addRemapKey.Content(plusSymbol);
|
2020-04-27 12:57:51 +08:00
|
|
|
addRemapKey.Margin({ 10, 0, 0, 25 });
|
2020-04-20 23:22:36 +08:00
|
|
|
addRemapKey.Click([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
2020-04-19 07:12:26 +08:00
|
|
|
SingleKeyRemapControl::AddNewControlKeyRemapRow(keyRemapTable, keyboardRemapControlObjects);
|
2020-05-15 00:24:50 +08:00
|
|
|
// Whenever a remap is added move to the bottom of the screen
|
|
|
|
scrollViewer.ChangeView(nullptr, scrollViewer.ScrollableHeight(), nullptr);
|
2020-03-27 23:38:58 +08:00
|
|
|
});
|
|
|
|
|
2020-04-27 12:57:51 +08:00
|
|
|
StackPanel mappingsPanel;
|
|
|
|
mappingsPanel.Children().Append(keyRemapInfoHeader);
|
2020-05-06 23:34:26 +08:00
|
|
|
mappingsPanel.Children().Append(keyRemapInfoExample);
|
2020-04-27 12:57:51 +08:00
|
|
|
mappingsPanel.Children().Append(keyRemapTable);
|
|
|
|
mappingsPanel.Children().Append(addRemapKey);
|
|
|
|
|
|
|
|
scrollViewer.Content(mappingsPanel);
|
|
|
|
|
|
|
|
// Creating the Xaml content. xamlContainer is the parent UI element
|
|
|
|
RelativePanel xamlContainer;
|
|
|
|
xamlContainer.SetBelow(scrollViewer, header);
|
|
|
|
xamlContainer.SetAlignLeftWithPanel(header, true);
|
|
|
|
xamlContainer.SetAlignRightWithPanel(header, true);
|
|
|
|
xamlContainer.SetAlignLeftWithPanel(scrollViewer, true);
|
|
|
|
xamlContainer.SetAlignRightWithPanel(scrollViewer, true);
|
2020-03-27 23:38:58 +08:00
|
|
|
xamlContainer.Children().Append(header);
|
2020-04-27 12:57:51 +08:00
|
|
|
xamlContainer.Children().Append(scrollViewer);
|
2020-03-06 07:52:59 +08:00
|
|
|
xamlContainer.UpdateLayout();
|
2020-03-27 23:38:58 +08:00
|
|
|
|
2020-04-30 08:58:31 +08:00
|
|
|
desktopSource.Content(xamlContainer);
|
2020-03-06 07:52:59 +08:00
|
|
|
////End XAML Island section
|
|
|
|
if (_hWndEditKeyboardWindow)
|
|
|
|
{
|
|
|
|
ShowWindow(_hWndEditKeyboardWindow, SW_SHOW);
|
|
|
|
UpdateWindow(_hWndEditKeyboardWindow);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Message loop:
|
2020-04-30 08:58:31 +08:00
|
|
|
xamlBridge.MessageLoop();
|
2020-04-15 00:24:11 +08:00
|
|
|
|
2020-04-30 08:58:31 +08:00
|
|
|
// Reset pointers to nullptr
|
|
|
|
xamlBridgePtr = nullptr;
|
2020-04-15 00:24:11 +08:00
|
|
|
hWndXamlIslandEditKeyboardWindow = nullptr;
|
|
|
|
hwndLock.lock();
|
|
|
|
hwndEditKeyboardNativeWindow = nullptr;
|
2020-05-05 06:49:37 +08:00
|
|
|
keyboardManagerState.ResetUIState();
|
2020-04-30 08:58:31 +08:00
|
|
|
|
|
|
|
// Cannot be done in WM_DESTROY because that causes crashes due to fatal app exit
|
|
|
|
xamlBridge.ClearXamlIslands();
|
2020-03-06 07:52:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
LRESULT CALLBACK EditKeyboardWindowProc(HWND hWnd, UINT messageCode, WPARAM wParam, LPARAM lParam)
|
|
|
|
{
|
|
|
|
RECT rcClient;
|
|
|
|
switch (messageCode)
|
|
|
|
{
|
2020-04-24 00:14:16 +08:00
|
|
|
// Resize the XAML window whenever the parent window is painted or resized
|
2020-03-06 07:52:59 +08:00
|
|
|
case WM_PAINT:
|
2020-04-24 00:14:16 +08:00
|
|
|
case WM_SIZE:
|
2020-03-06 07:52:59 +08:00
|
|
|
GetClientRect(hWnd, &rcClient);
|
|
|
|
SetWindowPos(hWndXamlIslandEditKeyboardWindow, 0, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom, SWP_SHOWWINDOW);
|
|
|
|
break;
|
|
|
|
default:
|
2020-04-30 08:58:31 +08:00
|
|
|
// If the Xaml Bridge object exists, then use it's message handler to handle keyboard focus operations
|
|
|
|
if (xamlBridgePtr != nullptr)
|
|
|
|
{
|
|
|
|
return xamlBridgePtr->MessageHandler(messageCode, wParam, lParam);
|
|
|
|
}
|
2020-05-02 08:34:42 +08:00
|
|
|
else if (messageCode == WM_NCDESTROY)
|
2020-04-30 08:58:31 +08:00
|
|
|
{
|
|
|
|
PostQuitMessage(0);
|
|
|
|
break;
|
|
|
|
}
|
2020-03-06 07:52:59 +08:00
|
|
|
return DefWindowProc(hWnd, messageCode, wParam, lParam);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2020-04-15 00:24:11 +08:00
|
|
|
|
2020-04-30 08:58:31 +08:00
|
|
|
// Function to check if there is already a window active if yes bring to foreground
|
2020-04-15 00:24:11 +08:00
|
|
|
bool CheckEditKeyboardWindowActive()
|
|
|
|
{
|
|
|
|
bool result = false;
|
|
|
|
std::unique_lock<std::mutex> hwndLock(editKeyboardWindowMutex);
|
|
|
|
if (hwndEditKeyboardNativeWindow != nullptr)
|
|
|
|
{
|
|
|
|
// Check if the window is minimized if yes then restore the window.
|
|
|
|
if (IsIconic(hwndEditKeyboardNativeWindow))
|
|
|
|
{
|
|
|
|
ShowWindow(hwndEditKeyboardNativeWindow, SW_RESTORE);
|
|
|
|
}
|
|
|
|
// If there is an already existing window no need to create a new open bring it on foreground.
|
|
|
|
SetForegroundWindow(hwndEditKeyboardNativeWindow);
|
|
|
|
result = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2020-05-13 06:58:11 +08:00
|
|
|
|
|
|
|
// Function to close any active Edit Keyboard window
|
|
|
|
void CloseActiveEditKeyboardWindow()
|
|
|
|
{
|
|
|
|
std::unique_lock<std::mutex> hwndLock(editKeyboardWindowMutex);
|
|
|
|
if (hwndEditKeyboardNativeWindow != nullptr)
|
|
|
|
{
|
|
|
|
PostMessage(hwndEditKeyboardNativeWindow, WM_CLOSE, 0, 0);
|
|
|
|
}
|
|
|
|
}
|