2020-03-24 01:44:02 +08:00
|
|
|
#include "pch.h"
|
|
|
|
#include "EditShortcutsWindow.h"
|
2021-04-27 03:01:38 +08:00
|
|
|
|
|
|
|
#include <common/Display/dpi_aware.h>
|
|
|
|
#include <common/utils/EventLocker.h>
|
|
|
|
#include <common/utils/winapi_error.h>
|
2021-05-07 16:16:31 +08:00
|
|
|
#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"
|
2020-05-12 03:45:55 +08:00
|
|
|
|
|
|
|
using namespace winrt::Windows::Foundation;
|
2020-03-24 01:44:02 +08:00
|
|
|
|
2021-04-30 22:04:53 +08:00
|
|
|
static UINT g_currentDPI = DPIAware::DEFAULT_DPI;
|
|
|
|
|
2020-03-24 01:44:02 +08:00
|
|
|
LRESULT CALLBACK EditShortcutsWindowProc(HWND, UINT, WPARAM, LPARAM);
|
|
|
|
|
|
|
|
// This Hwnd will be the window handler for the Xaml Island: A child window that contains Xaml.
|
|
|
|
HWND hWndXamlIslandEditShortcutsWindow = nullptr;
|
2021-04-27 03:01:38 +08:00
|
|
|
|
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 isEditShortcutsWindowRegistrationCompleted = false;
|
2021-04-27 03:01:38 +08:00
|
|
|
|
2020-04-15 00:24:11 +08:00
|
|
|
// Holds the native window handle of EditShortcuts Window.
|
|
|
|
HWND hwndEditShortcutsNativeWindow = nullptr;
|
|
|
|
std::mutex editShortcutsWindowMutex;
|
2021-04-27 03:01:38 +08:00
|
|
|
|
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-24 01:44:02 +08:00
|
|
|
|
2020-05-12 03:45:55 +08:00
|
|
|
static IAsyncAction OnClickAccept(
|
2021-05-07 16:16:31 +08:00
|
|
|
KBMEditor::KeyboardManagerState& keyboardManagerState,
|
2020-05-12 03:45:55 +08:00
|
|
|
XamlRoot root,
|
|
|
|
std::function<void()> ApplyRemappings)
|
|
|
|
{
|
2021-05-07 16:16:31 +08:00
|
|
|
ShortcutErrorType isSuccess = LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(ShortcutControl::shortcutRemapBuffer);
|
2020-07-09 07:24:30 +08:00
|
|
|
|
2021-05-07 16:16:31 +08:00
|
|
|
if (isSuccess != ShortcutErrorType::NoError)
|
2020-05-12 03:45:55 +08:00
|
|
|
{
|
2020-08-18 04:46:50 +08:00
|
|
|
if (!co_await Dialog::PartialRemappingConfirmationDialog(root, GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_PARTIALCONFIRMATIONDIALOGTITLE)))
|
2020-05-12 03:45:55 +08:00
|
|
|
{
|
|
|
|
co_return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ApplyRemappings();
|
|
|
|
}
|
|
|
|
|
2020-03-24 01:44:02 +08:00
|
|
|
// Function to create the Edit Shortcuts Window
|
2021-05-07 16:16:31 +08:00
|
|
|
inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardManagerState& keyboardManagerState, MappingConfiguration& mappingConfiguration)
|
2020-03-24 01:44:02 +08:00
|
|
|
{
|
2021-04-27 03:01:38 +08:00
|
|
|
Logger::trace("CreateEditShortcutsWindowImpl()");
|
|
|
|
auto locker = EventLocker::Get(KeyboardManagerConstants::EditorWindowEventName.c_str());
|
|
|
|
if (!locker.has_value())
|
|
|
|
{
|
|
|
|
Logger::error(L"Failed to lock event {}. {}", KeyboardManagerConstants::EditorWindowEventName, get_last_error_or_default(GetLastError()));
|
|
|
|
}
|
|
|
|
|
|
|
|
Logger::trace(L"Signaled {} event to suspend the KBM engine", KeyboardManagerConstants::EditorWindowEventName);
|
2021-01-19 01:38:42 +08:00
|
|
|
|
2020-03-24 01:44:02 +08:00
|
|
|
// Window Registration
|
|
|
|
const wchar_t szWindowClass[] = L"EditShortcutsWindow";
|
|
|
|
|
|
|
|
if (!isEditShortcutsWindowRegistrationCompleted)
|
|
|
|
{
|
|
|
|
WNDCLASSEX windowClass = {};
|
|
|
|
windowClass.cbSize = sizeof(WNDCLASSEX);
|
|
|
|
windowClass.lpfnWndProc = EditShortcutsWindowProc;
|
|
|
|
windowClass.hInstance = hInst;
|
|
|
|
windowClass.lpszClassName = szWindowClass;
|
2023-02-08 19:01:13 +08:00
|
|
|
windowClass.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW);
|
|
|
|
windowClass.hIcon = static_cast<HICON>(LoadImageW(
|
2020-05-12 11:13:07 +08:00
|
|
|
windowClass.hInstance,
|
2020-05-13 06:58:11 +08:00
|
|
|
MAKEINTRESOURCE(IDS_KEYBOARDMANAGER_ICON),
|
|
|
|
IMAGE_ICON,
|
|
|
|
48,
|
|
|
|
48,
|
2023-02-08 19:01:13 +08:00
|
|
|
LR_DEFAULTCOLOR));
|
2020-03-24 01:44:02 +08:00
|
|
|
if (RegisterClassEx(&windowClass) == NULL)
|
|
|
|
{
|
2020-08-18 04:46:50 +08:00
|
|
|
MessageBox(NULL, GET_RESOURCE_STRING(IDS_REGISTERCLASSFAILED_ERRORMESSAGE).c_str(), GET_RESOURCE_STRING(IDS_REGISTERCLASSFAILED_ERRORTITLE).c_str(), NULL);
|
2020-03-24 01:44:02 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
isEditShortcutsWindowRegistrationCompleted = true;
|
|
|
|
}
|
|
|
|
|
2020-12-10 23:28:44 +08:00
|
|
|
// Find coordinates of the screen where the settings window is placed.
|
|
|
|
RECT desktopRect = UIHelpers::GetForegroundWindowDesktopRect();
|
|
|
|
|
2020-05-13 01:17:27 +08:00
|
|
|
// Calculate DPI dependent window size
|
2022-03-26 00:09:59 +08:00
|
|
|
float windowWidth = EditorConstants::DefaultEditShortcutsWindowWidth;
|
|
|
|
float windowHeight = EditorConstants::DefaultEditShortcutsWindowHeight;
|
2021-04-29 23:39:12 +08:00
|
|
|
DPIAware::ConvertByCursorPosition(windowWidth, windowHeight);
|
2021-04-30 22:04:53 +08:00
|
|
|
DPIAware::GetScreenDPIForCursor(g_currentDPI);
|
2020-05-09 08:34:24 +08:00
|
|
|
|
2020-03-24 01:44:02 +08:00
|
|
|
// Window Creation
|
|
|
|
HWND _hWndEditShortcutsWindow = CreateWindow(
|
|
|
|
szWindowClass,
|
2020-08-18 04:46:50 +08:00
|
|
|
GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_WINDOWNAME).c_str(),
|
2020-05-13 06:58:11 +08:00
|
|
|
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MAXIMIZEBOX,
|
2022-03-26 00:09:59 +08:00
|
|
|
((desktopRect.right + desktopRect.left) / 2) - ((int)windowWidth / 2),
|
|
|
|
((desktopRect.bottom + desktopRect.top) / 2) - ((int)windowHeight / 2),
|
|
|
|
static_cast<int>(windowWidth),
|
|
|
|
static_cast<int>(windowHeight),
|
2020-03-24 01:44:02 +08:00
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
hInst,
|
|
|
|
NULL);
|
2021-04-27 03:01:38 +08:00
|
|
|
|
2020-03-24 01:44:02 +08:00
|
|
|
if (_hWndEditShortcutsWindow == NULL)
|
|
|
|
{
|
2020-08-18 04:46:50 +08:00
|
|
|
MessageBox(NULL, GET_RESOURCE_STRING(IDS_CREATEWINDOWFAILED_ERRORMESSAGE).c_str(), GET_RESOURCE_STRING(IDS_CREATEWINDOWFAILED_ERRORTITLE).c_str(), NULL);
|
2020-03-24 01:44:02 +08:00
|
|
|
return;
|
|
|
|
}
|
2021-04-27 03:01:38 +08:00
|
|
|
|
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 (_hWndEditShortcutsWindow)
|
|
|
|
{
|
|
|
|
SetForegroundWindow(_hWndEditShortcutsWindow);
|
|
|
|
}
|
2020-03-24 01:44:02 +08:00
|
|
|
|
2020-04-15 00:24:11 +08:00
|
|
|
// Store the newly created Edit Shortcuts window's handle.
|
|
|
|
std::unique_lock<std::mutex> hwndLock(editShortcutsWindowMutex);
|
|
|
|
hwndEditShortcutsNativeWindow = _hWndEditShortcutsWindow;
|
|
|
|
hwndLock.unlock();
|
|
|
|
|
2020-04-30 08:58:31 +08:00
|
|
|
// Create the xaml bridge object
|
|
|
|
XamlBridge xamlBridge(_hWndEditShortcutsWindow);
|
2021-04-27 03:01:38 +08:00
|
|
|
|
2020-04-30 08:58:31 +08:00
|
|
|
// DesktopSource needs to be declared before the RelativePanel xamlContainer object to avoid errors
|
|
|
|
winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource desktopSource;
|
2021-04-27 03:01:38 +08:00
|
|
|
|
2020-04-30 08:58:31 +08:00
|
|
|
// Create the desktop window xaml source object and set its content
|
|
|
|
hWndXamlIslandEditShortcutsWindow = xamlBridge.InitDesktopWindowsXamlSource(desktopSource);
|
2020-03-24 01:44:02 +08:00
|
|
|
|
2020-04-30 08:58:31 +08:00
|
|
|
// Set the pointer to the xaml bridge object
|
|
|
|
xamlBridgePtr = &xamlBridge;
|
2020-03-24 01:44:02 +08:00
|
|
|
|
|
|
|
// Header for the window
|
2020-04-24 00:14:16 +08:00
|
|
|
Windows::UI::Xaml::Controls::RelativePanel header;
|
2020-03-24 01:44:02 +08:00
|
|
|
header.Margin({ 10, 10, 10, 30 });
|
|
|
|
|
|
|
|
// Header text
|
|
|
|
TextBlock headerText;
|
2020-08-18 04:46:50 +08:00
|
|
|
headerText.Text(GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_WINDOWNAME));
|
2020-03-24 01:44:02 +08:00
|
|
|
headerText.FontSize(30);
|
2020-05-09 08:34:24 +08:00
|
|
|
headerText.Margin({ 0, 0, 0, 0 });
|
2020-04-24 00:14:16 +08:00
|
|
|
header.SetAlignLeftWithPanel(headerText, true);
|
2020-03-24 01:44:02 +08:00
|
|
|
|
|
|
|
// Cancel button
|
|
|
|
Button cancelButton;
|
2020-08-18 04:46:50 +08:00
|
|
|
cancelButton.Content(winrt::box_value(GET_RESOURCE_STRING(IDS_CANCEL_BUTTON)));
|
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-24 01:44:02 +08:00
|
|
|
// Close the window since settings do not need to be saved
|
|
|
|
PostMessage(_hWndEditShortcutsWindow, WM_CLOSE, 0, 0);
|
|
|
|
});
|
|
|
|
|
2020-05-06 23:34:26 +08:00
|
|
|
// Text block for information about remap key section.
|
|
|
|
TextBlock shortcutRemapInfoHeader;
|
2020-08-18 04:46:50 +08:00
|
|
|
shortcutRemapInfoHeader.Text(GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_INFO));
|
2020-05-06 23:34:26 +08:00
|
|
|
shortcutRemapInfoHeader.Margin({ 10, 0, 0, 10 });
|
|
|
|
shortcutRemapInfoHeader.FontWeight(Text::FontWeights::SemiBold());
|
2020-05-09 08:34:24 +08:00
|
|
|
shortcutRemapInfoHeader.TextWrapping(TextWrapping::Wrap);
|
2020-05-06 23:34:26 +08:00
|
|
|
|
|
|
|
TextBlock shortcutRemapInfoExample;
|
2020-08-18 04:46:50 +08:00
|
|
|
shortcutRemapInfoExample.Text(GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_INFOEXAMPLE));
|
2020-05-06 23:34:26 +08:00
|
|
|
shortcutRemapInfoExample.Margin({ 10, 0, 0, 20 });
|
|
|
|
shortcutRemapInfoExample.FontStyle(Text::FontStyle::Italic);
|
2020-05-09 08:34:24 +08:00
|
|
|
shortcutRemapInfoExample.TextWrapping(TextWrapping::Wrap);
|
2020-05-06 23:34:26 +08:00
|
|
|
|
2020-03-24 01:44:02 +08:00
|
|
|
// Table to display the shortcuts
|
2020-11-06 22:24:11 +08:00
|
|
|
StackPanel shortcutTable;
|
2020-03-24 01:44:02 +08:00
|
|
|
|
|
|
|
// First header textblock in the header row of the shortcut table
|
|
|
|
TextBlock originalShortcutHeader;
|
2020-08-18 04:46:50 +08:00
|
|
|
originalShortcutHeader.Text(GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_SOURCEHEADER));
|
2020-03-24 01:44:02 +08:00
|
|
|
originalShortcutHeader.FontWeight(Text::FontWeights::Bold());
|
|
|
|
|
2020-04-04 01:57:46 +08:00
|
|
|
// Second header textblock in the header row of the shortcut table
|
2020-03-24 01:44:02 +08:00
|
|
|
TextBlock newShortcutHeader;
|
2020-08-18 04:46:50 +08:00
|
|
|
newShortcutHeader.Text(GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_TARGETHEADER));
|
2020-03-24 01:44:02 +08:00
|
|
|
newShortcutHeader.FontWeight(Text::FontWeights::Bold());
|
|
|
|
|
2020-07-09 07:24:30 +08:00
|
|
|
// Third header textblock in the header row of the shortcut table
|
|
|
|
TextBlock targetAppHeader;
|
2020-08-18 04:46:50 +08:00
|
|
|
targetAppHeader.Text(GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_TARGETAPPHEADER));
|
2020-07-09 07:24:30 +08:00
|
|
|
targetAppHeader.FontWeight(Text::FontWeights::Bold());
|
|
|
|
targetAppHeader.HorizontalAlignment(HorizontalAlignment::Center);
|
|
|
|
|
2020-11-06 22:24:11 +08:00
|
|
|
StackPanel tableHeader = StackPanel();
|
|
|
|
tableHeader.Orientation(Orientation::Horizontal);
|
|
|
|
tableHeader.Margin({ 10, 0, 0, 10 });
|
2023-02-08 19:01:13 +08:00
|
|
|
auto originalShortcutContainer = UIHelpers::GetWrapped(originalShortcutHeader, EditorConstants::ShortcutOriginColumnWidth + static_cast<double>(EditorConstants::ShortcutArrowColumnWidth));
|
2020-11-06 22:24:11 +08:00
|
|
|
tableHeader.Children().Append(originalShortcutContainer.as<FrameworkElement>());
|
2021-05-07 16:16:31 +08:00
|
|
|
auto newShortcutHeaderContainer = UIHelpers::GetWrapped(newShortcutHeader, EditorConstants::ShortcutTargetColumnWidth);
|
2020-11-06 22:24:11 +08:00
|
|
|
tableHeader.Children().Append(newShortcutHeaderContainer.as<FrameworkElement>());
|
|
|
|
tableHeader.Children().Append(targetAppHeader);
|
2020-03-24 01:44:02 +08:00
|
|
|
|
2020-04-10 00:20:19 +08:00
|
|
|
// Store handle of edit shortcuts window
|
2021-04-27 03:01:38 +08:00
|
|
|
ShortcutControl::editShortcutsWindowHandle = _hWndEditShortcutsWindow;
|
|
|
|
|
2020-04-10 00:20:19 +08:00
|
|
|
// Store keyboard manager state
|
|
|
|
ShortcutControl::keyboardManagerState = &keyboardManagerState;
|
2020-04-19 07:12:26 +08:00
|
|
|
KeyDropDownControl::keyboardManagerState = &keyboardManagerState;
|
2021-05-07 16:16:31 +08:00
|
|
|
KeyDropDownControl::mappingConfiguration = &mappingConfiguration;
|
2021-04-27 03:01:38 +08:00
|
|
|
|
2020-04-10 00:20:19 +08:00
|
|
|
// Clear the shortcut remap buffer
|
|
|
|
ShortcutControl::shortcutRemapBuffer.clear();
|
2021-04-27 03:01:38 +08:00
|
|
|
|
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<ShortcutControl>>> keyboardRemapControlObjects;
|
2020-04-10 00:20:19 +08:00
|
|
|
|
2020-05-05 06:49:37 +08:00
|
|
|
// Set keyboard manager UI state so that shortcut remaps are not applied while on this window
|
2021-05-07 16:16:31 +08:00
|
|
|
keyboardManagerState.SetUIState(KBMEditor::KeyboardManagerUIState::EditShortcutsWindowActivated, _hWndEditShortcutsWindow);
|
2020-05-05 06:49:37 +08:00
|
|
|
|
2020-07-09 07:24:30 +08:00
|
|
|
// Load existing os level shortcuts into UI
|
2020-10-09 02:28:24 +08:00
|
|
|
// Create copy of the remaps to avoid concurrent access
|
2021-05-07 16:16:31 +08:00
|
|
|
ShortcutRemapTable osLevelShortcutReMapCopy = mappingConfiguration.osLevelShortcutReMap;
|
2020-10-09 02:28:24 +08:00
|
|
|
|
|
|
|
for (const auto& it : osLevelShortcutReMapCopy)
|
2020-04-10 00:20:19 +08:00
|
|
|
{
|
2020-04-19 07:12:26 +08:00
|
|
|
ShortcutControl::AddNewShortcutControlRow(shortcutTable, keyboardRemapControlObjects, it.first, it.second.targetShortcut);
|
2020-04-10 00:20:19 +08:00
|
|
|
}
|
2020-07-09 07:24:30 +08:00
|
|
|
|
|
|
|
// Load existing app-specific shortcuts into UI
|
2020-10-09 02:28:24 +08:00
|
|
|
// Create copy of the remaps to avoid concurrent access
|
2021-05-07 16:16:31 +08:00
|
|
|
AppSpecificShortcutRemapTable appSpecificShortcutReMapCopy = mappingConfiguration.appSpecificShortcutReMap;
|
2020-10-09 02:28:24 +08:00
|
|
|
|
2020-07-09 07:24:30 +08:00
|
|
|
// Iterate through all the apps
|
2020-10-09 02:28:24 +08:00
|
|
|
for (const auto& itApp : appSpecificShortcutReMapCopy)
|
2020-07-09 07:24:30 +08:00
|
|
|
{
|
|
|
|
// Iterate through shortcuts for each app
|
|
|
|
for (const auto& itShortcut : itApp.second)
|
|
|
|
{
|
|
|
|
ShortcutControl::AddNewShortcutControlRow(shortcutTable, keyboardRemapControlObjects, itShortcut.first, itShortcut.second.targetShortcut, itApp.first);
|
|
|
|
}
|
|
|
|
}
|
2020-04-10 00:20:19 +08:00
|
|
|
|
2020-03-24 01:44:02 +08:00
|
|
|
// Apply button
|
|
|
|
Button applyButton;
|
2020-08-18 04:46:50 +08:00
|
|
|
applyButton.Content(winrt::box_value(GET_RESOURCE_STRING(IDS_OK_BUTTON)));
|
2020-05-12 03:45:55 +08:00
|
|
|
applyButton.Style(AccentButtonStyle());
|
2021-05-07 16:16:31 +08:00
|
|
|
applyButton.MinWidth(EditorConstants::HeaderButtonWidth);
|
|
|
|
cancelButton.MinWidth(EditorConstants::HeaderButtonWidth);
|
2020-05-12 03:45:55 +08:00
|
|
|
header.SetAlignRightWithPanel(cancelButton, true);
|
|
|
|
header.SetLeftOf(applyButton, cancelButton);
|
|
|
|
|
2021-05-07 16:16:31 +08:00
|
|
|
auto ApplyRemappings = [&mappingConfiguration, _hWndEditShortcutsWindow]() {
|
|
|
|
LoadingAndSavingRemappingHelper::ApplyShortcutRemappings(mappingConfiguration, ShortcutControl::shortcutRemapBuffer, true);
|
|
|
|
bool saveResult = mappingConfiguration.SaveSettingsToFile();
|
2020-05-12 03:45:55 +08:00
|
|
|
PostMessage(_hWndEditShortcutsWindow, WM_CLOSE, 0, 0);
|
|
|
|
};
|
|
|
|
|
|
|
|
applyButton.Click([&keyboardManagerState, applyButton, ApplyRemappings](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
|
|
|
OnClickAccept(keyboardManagerState, applyButton.XamlRoot(), ApplyRemappings);
|
2020-03-24 01:44:02 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
header.Children().Append(headerText);
|
|
|
|
header.Children().Append(applyButton);
|
2020-05-14 01:13:44 +08:00
|
|
|
header.Children().Append(cancelButton);
|
2020-03-24 01:44:02 +08:00
|
|
|
|
2020-05-15 00:24:50 +08:00
|
|
|
ScrollViewer scrollViewer;
|
2020-09-23 08:46:57 +08:00
|
|
|
scrollViewer.VerticalScrollMode(ScrollMode::Enabled);
|
|
|
|
scrollViewer.HorizontalScrollMode(ScrollMode::Enabled);
|
|
|
|
scrollViewer.VerticalScrollBarVisibility(ScrollBarVisibility::Auto);
|
|
|
|
scrollViewer.HorizontalScrollBarVisibility(ScrollBarVisibility::Auto);
|
2020-05-15 00:24:50 +08:00
|
|
|
|
2020-03-24 01:44:02 +08:00
|
|
|
// Add shortcut button
|
|
|
|
Windows::UI::Xaml::Controls::Button addShortcut;
|
|
|
|
FontIcon plusSymbol;
|
|
|
|
plusSymbol.FontFamily(Xaml::Media::FontFamily(L"Segoe MDL2 Assets"));
|
2022-07-15 17:17:18 +08:00
|
|
|
plusSymbol.Glyph(L"\xE710");
|
2020-03-24 01:44:02 +08:00
|
|
|
addShortcut.Content(plusSymbol);
|
2020-11-06 22:24:11 +08:00
|
|
|
addShortcut.Margin({ 10, 10, 0, 25 });
|
2020-04-20 23:22:36 +08:00
|
|
|
addShortcut.Click([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
2020-04-19 07:12:26 +08:00
|
|
|
ShortcutControl::AddNewShortcutControlRow(shortcutTable, keyboardRemapControlObjects);
|
2020-10-30 04:16:52 +08:00
|
|
|
|
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-10-30 04:16:52 +08:00
|
|
|
|
|
|
|
// Set focus to the first Type Button in the newly added row
|
2021-05-07 16:16:31 +08:00
|
|
|
UIHelpers::SetFocusOnTypeButtonInLastRow(shortcutTable, EditorConstants::ShortcutTableColCount);
|
2020-03-24 01:44:02 +08:00
|
|
|
});
|
2020-10-28 00:13:39 +08:00
|
|
|
|
2020-09-09 03:24:59 +08:00
|
|
|
// Set accessible name for the add shortcut button
|
|
|
|
addShortcut.SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_ADD_SHORTCUT_BUTTON)));
|
2020-03-24 01:44:02 +08:00
|
|
|
|
2020-10-28 00:13:39 +08:00
|
|
|
// Add tooltip for add button which would appear on hover
|
|
|
|
ToolTip addShortcuttoolTip;
|
|
|
|
addShortcuttoolTip.Content(box_value(GET_RESOURCE_STRING(IDS_ADD_SHORTCUT_BUTTON)));
|
|
|
|
ToolTipService::SetToolTip(addShortcut, addShortcuttoolTip);
|
|
|
|
|
2020-09-23 08:46:57 +08:00
|
|
|
// Header and example text at the top of the window
|
|
|
|
StackPanel helperText;
|
|
|
|
helperText.Children().Append(shortcutRemapInfoHeader);
|
|
|
|
helperText.Children().Append(shortcutRemapInfoExample);
|
|
|
|
|
|
|
|
// Remapping table
|
2020-04-27 12:57:51 +08:00
|
|
|
StackPanel mappingsPanel;
|
2020-11-06 22:24:11 +08:00
|
|
|
mappingsPanel.Children().Append(tableHeader);
|
2020-04-27 12:57:51 +08:00
|
|
|
mappingsPanel.Children().Append(shortcutTable);
|
|
|
|
mappingsPanel.Children().Append(addShortcut);
|
|
|
|
|
2020-09-23 08:46:57 +08:00
|
|
|
// Remapping table should be scrollable
|
2020-04-27 12:57:51 +08:00
|
|
|
scrollViewer.Content(mappingsPanel);
|
|
|
|
|
|
|
|
RelativePanel xamlContainer;
|
2020-09-23 08:46:57 +08:00
|
|
|
xamlContainer.SetBelow(helperText, header);
|
|
|
|
xamlContainer.SetBelow(scrollViewer, helperText);
|
2020-04-27 12:57:51 +08:00
|
|
|
xamlContainer.SetAlignLeftWithPanel(header, true);
|
|
|
|
xamlContainer.SetAlignRightWithPanel(header, true);
|
2020-09-23 08:46:57 +08:00
|
|
|
xamlContainer.SetAlignLeftWithPanel(helperText, true);
|
|
|
|
xamlContainer.SetAlignRightWithPanel(helperText, true);
|
2020-04-27 12:57:51 +08:00
|
|
|
xamlContainer.SetAlignLeftWithPanel(scrollViewer, true);
|
|
|
|
xamlContainer.SetAlignRightWithPanel(scrollViewer, true);
|
2020-03-24 01:44:02 +08:00
|
|
|
xamlContainer.Children().Append(header);
|
2020-09-23 08:46:57 +08:00
|
|
|
xamlContainer.Children().Append(helperText);
|
2020-04-27 12:57:51 +08:00
|
|
|
xamlContainer.Children().Append(scrollViewer);
|
2021-10-06 22:54:15 +08:00
|
|
|
try
|
|
|
|
{
|
|
|
|
// If a layout update has been triggered by other methods (e.g.: adapting to zoom level), this may throw an exception.
|
|
|
|
xamlContainer.UpdateLayout();
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
}
|
2020-04-30 08:58:31 +08:00
|
|
|
|
2020-03-24 01:44:02 +08:00
|
|
|
desktopSource.Content(xamlContainer);
|
|
|
|
|
|
|
|
////End XAML Island section
|
|
|
|
if (_hWndEditShortcutsWindow)
|
|
|
|
{
|
|
|
|
ShowWindow(_hWndEditShortcutsWindow, SW_SHOW);
|
|
|
|
UpdateWindow(_hWndEditShortcutsWindow);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
hWndXamlIslandEditShortcutsWindow = nullptr;
|
|
|
|
hwndLock.lock();
|
|
|
|
hwndEditShortcutsNativeWindow = nullptr;
|
2020-05-05 06:49:37 +08:00
|
|
|
keyboardManagerState.ResetUIState();
|
2020-09-12 06:16:34 +08:00
|
|
|
keyboardManagerState.ClearRegisteredKeyDelays();
|
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-24 01:44:02 +08:00
|
|
|
}
|
|
|
|
|
2021-05-07 16:16:31 +08:00
|
|
|
void CreateEditShortcutsWindow(HINSTANCE hInst, KBMEditor::KeyboardManagerState& keyboardManagerState, MappingConfiguration& mappingConfiguration)
|
2021-04-27 03:01:38 +08:00
|
|
|
{
|
|
|
|
// Move implementation into the separate method so resources get destroyed correctly
|
2021-05-07 16:16:31 +08:00
|
|
|
CreateEditShortcutsWindowImpl(hInst, keyboardManagerState, mappingConfiguration);
|
2021-04-27 03:01:38 +08:00
|
|
|
|
|
|
|
// 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
|
|
|
|
Logger::trace("Terminating process {}", GetCurrentProcessId());
|
|
|
|
Logger::flush();
|
|
|
|
TerminateProcess(GetCurrentProcess(), 0);
|
|
|
|
}
|
|
|
|
|
2020-03-24 01:44:02 +08:00
|
|
|
LRESULT CALLBACK EditShortcutsWindowProc(HWND hWnd, UINT messageCode, WPARAM wParam, LPARAM lParam)
|
|
|
|
{
|
|
|
|
switch (messageCode)
|
|
|
|
{
|
2020-04-24 00:14:16 +08:00
|
|
|
// Resize the XAML window whenever the parent window is painted or resized
|
2020-03-24 01:44:02 +08:00
|
|
|
case WM_PAINT:
|
2020-04-24 00:14:16 +08:00
|
|
|
case WM_SIZE:
|
2020-09-23 08:46:57 +08:00
|
|
|
{
|
2021-04-30 22:04:53 +08:00
|
|
|
RECT rect = { 0 };
|
|
|
|
GetClientRect(hWnd, &rect);
|
|
|
|
SetWindowPos(hWndXamlIslandEditShortcutsWindow, 0, rect.left, rect.top, rect.right, rect.bottom, SWP_SHOWWINDOW);
|
2020-09-23 08:46:57 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
// To avoid UI elements overlapping on making the window smaller enforce a minimum window size
|
|
|
|
case WM_GETMINMAXINFO:
|
|
|
|
{
|
2023-02-08 19:01:13 +08:00
|
|
|
LPMINMAXINFO mmi = reinterpret_cast<LPMINMAXINFO>(lParam);
|
2022-03-26 00:09:59 +08:00
|
|
|
float minWidth = EditorConstants::MinimumEditShortcutsWindowWidth;
|
|
|
|
float minHeight = EditorConstants::MinimumEditShortcutsWindowHeight;
|
2021-04-29 23:39:12 +08:00
|
|
|
DPIAware::Convert(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONULL), minWidth, minHeight);
|
2022-03-26 00:09:59 +08:00
|
|
|
mmi->ptMinTrackSize.x = static_cast<LONG>(minWidth);
|
|
|
|
mmi->ptMinTrackSize.y = static_cast<LONG>(minHeight);
|
2021-04-30 22:04:53 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_GETDPISCALEDSIZE:
|
|
|
|
{
|
|
|
|
UINT newDPI = static_cast<UINT>(wParam);
|
|
|
|
SIZE* size = reinterpret_cast<SIZE*>(lParam);
|
|
|
|
Logger::trace(L"WM_GETDPISCALEDSIZE: DPI {} size X {} Y {}", newDPI, size->cx, size->cy);
|
|
|
|
|
|
|
|
float scalingFactor = static_cast<float>(newDPI) / g_currentDPI;
|
|
|
|
Logger::trace(L"WM_GETDPISCALEDSIZE: scaling factor {}", scalingFactor);
|
|
|
|
|
|
|
|
size->cx = static_cast<LONG>(size->cx * scalingFactor);
|
|
|
|
size->cy = static_cast<LONG>(size->cy * scalingFactor);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_DPICHANGED:
|
|
|
|
{
|
|
|
|
UINT newDPI = static_cast<UINT>(LOWORD(wParam));
|
|
|
|
g_currentDPI = newDPI;
|
|
|
|
|
|
|
|
RECT* rect = reinterpret_cast<RECT*>(lParam);
|
|
|
|
SetWindowPos(
|
|
|
|
hWnd,
|
|
|
|
nullptr,
|
|
|
|
rect->left,
|
|
|
|
rect->top,
|
|
|
|
rect->right - rect->left,
|
|
|
|
rect->bottom - rect->top,
|
|
|
|
SWP_NOZORDER | SWP_NOACTIVATE
|
|
|
|
);
|
|
|
|
|
|
|
|
Logger::trace(L"WM_DPICHANGED: new dpi {} rect {} {} ", newDPI, rect->right - rect->left, rect->bottom - rect->top);
|
2020-09-23 08:46:57 +08:00
|
|
|
}
|
|
|
|
break;
|
2020-03-24 01:44:02 +08:00
|
|
|
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-24 01:44:02 +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-20 23:59:35 +08:00
|
|
|
bool CheckEditShortcutsWindowActive()
|
2020-04-15 00:24:11 +08:00
|
|
|
{
|
|
|
|
bool result = false;
|
|
|
|
std::unique_lock<std::mutex> hwndLock(editShortcutsWindowMutex);
|
|
|
|
if (hwndEditShortcutsNativeWindow != nullptr)
|
|
|
|
{
|
|
|
|
// Check if the window is minimized if yes then restore the window.
|
|
|
|
if (IsIconic(hwndEditShortcutsNativeWindow))
|
|
|
|
{
|
|
|
|
ShowWindow(hwndEditShortcutsNativeWindow, SW_RESTORE);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there is an already existing window no need to create a new open bring it on foreground.
|
|
|
|
SetForegroundWindow(hwndEditShortcutsNativeWindow);
|
|
|
|
result = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2020-05-13 06:58:11 +08:00
|
|
|
|
|
|
|
// Function to close any active Edit Shortcuts window
|
|
|
|
void CloseActiveEditShortcutsWindow()
|
|
|
|
{
|
|
|
|
std::unique_lock<std::mutex> hwndLock(editShortcutsWindowMutex);
|
|
|
|
if (hwndEditShortcutsNativeWindow != nullptr)
|
|
|
|
{
|
|
|
|
PostMessage(hwndEditShortcutsNativeWindow, WM_CLOSE, 0, 0);
|
|
|
|
}
|
|
|
|
}
|