mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-05 18:57:19 +02:00
Remap Keyboard UI (dev/keyboardManager) (#1698)
* Added initial UI implementation * Added backend logic for remap key interaction * Added single key remap control * Updated Edit keyboardWindow UI * Commented out ui logic * Fixed issue with remap window UI and uncommented the code * Updated customdialog window foreground color * Updated buttons foreground color * Added info header * Added null check for detected key * Removed fully qualified namespaces * updated the background color as ligtht gray
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
#include "pch.h"
|
||||
#include "EditKeyboardWindow.h"
|
||||
#include "SingleKeyRemapControl.h"
|
||||
|
||||
LRESULT CALLBACK EditKeyboardWindowProc(HWND, UINT, WPARAM, LPARAM);
|
||||
|
||||
@@ -38,8 +39,8 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
||||
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
400,
|
||||
400,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
NULL,
|
||||
NULL,
|
||||
hInst,
|
||||
@@ -63,14 +64,148 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
||||
// Update the xaml island window size becuase initially is 0,0
|
||||
SetWindowPos(hWndXamlIslandEditKeyboardWindow, 0, 0, 0, 400, 400, SWP_SHOWWINDOW);
|
||||
|
||||
//Creating the Xaml content
|
||||
// Creating the Xaml content. xamlContainer is the parent UI element
|
||||
Windows::UI::Xaml::Controls::StackPanel xamlContainer;
|
||||
xamlContainer.Background(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::LightGray() });
|
||||
Windows::UI::Xaml::Controls::Button bt;
|
||||
bt.Content(winrt::box_value(winrt::to_hstring("Don't Type key")));
|
||||
xamlContainer.Children().Append(bt);
|
||||
|
||||
// Header for the window
|
||||
Windows::UI::Xaml::Controls::StackPanel header;
|
||||
header.Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
|
||||
header.Margin({ 10, 10, 10, 30 });
|
||||
header.Spacing(10);
|
||||
|
||||
// Header text
|
||||
TextBlock headerText;
|
||||
headerText.Text(winrt::to_hstring("Remap Keyboard"));
|
||||
headerText.Foreground(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::Black() });
|
||||
headerText.FontSize(30);
|
||||
headerText.Margin({ 0, 0, 1000, 0 });
|
||||
|
||||
// Header Cancel button
|
||||
Button cancelButton;
|
||||
cancelButton.Background(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::LightGray() });
|
||||
cancelButton.Foreground(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::Black() });
|
||||
cancelButton.Content(winrt::box_value(winrt::to_hstring("Cancel")));
|
||||
cancelButton.Click([&](IInspectable const& sender, RoutedEventArgs const&) {
|
||||
// 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;
|
||||
keyRemapInfoHeader.Text(winrt::to_hstring("Select the key you want to remap, original key, and it's new output when pressed, the new key"));
|
||||
keyRemapInfoHeader.Foreground(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::Black() });
|
||||
keyRemapInfoHeader.Margin({ 0, 0, 0, 10 });
|
||||
|
||||
// Table to display the key remaps
|
||||
Windows::UI::Xaml::Controls::StackPanel keyRemapTable;
|
||||
keyRemapTable.Background(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::LightGray() });
|
||||
keyRemapTable.Margin({ 10, 10, 10, 20 });
|
||||
keyRemapTable.Spacing(10);
|
||||
|
||||
// Header row of the keys remap table
|
||||
Windows::UI::Xaml::Controls::StackPanel tableHeaderRow;
|
||||
tableHeaderRow.Background(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::LightGray() });
|
||||
tableHeaderRow.Spacing(100);
|
||||
tableHeaderRow.Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
|
||||
|
||||
// First header textblock in the header row of the keys remap table
|
||||
TextBlock originalKeyRemapHeader;
|
||||
originalKeyRemapHeader.Text(winrt::to_hstring("Original Key:"));
|
||||
originalKeyRemapHeader.Foreground(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::Black() });
|
||||
originalKeyRemapHeader.FontWeight(Text::FontWeights::Bold());
|
||||
originalKeyRemapHeader.Margin({ 0, 0, 0, 10 });
|
||||
tableHeaderRow.Children().Append(originalKeyRemapHeader);
|
||||
|
||||
// Second header textblock in the header row of the keys remap table
|
||||
TextBlock newKeyRemapHeader;
|
||||
newKeyRemapHeader.Text(winrt::to_hstring("New Key:"));
|
||||
newKeyRemapHeader.Foreground(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::Black() });
|
||||
newKeyRemapHeader.FontWeight(Text::FontWeights::Bold());
|
||||
newKeyRemapHeader.Margin({ 0, 0, 0, 10 });
|
||||
tableHeaderRow.Children().Append(newKeyRemapHeader);
|
||||
|
||||
keyRemapTable.Children().Append(tableHeaderRow);
|
||||
|
||||
// Message to display success/failure of saving settings.
|
||||
TextBlock settingsMessage;
|
||||
|
||||
// Main Header Apply button
|
||||
Button applyButton;
|
||||
applyButton.Background(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::LightGray() });
|
||||
applyButton.Foreground(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::Black() });
|
||||
applyButton.Content(winrt::box_value(winrt::to_hstring("Apply")));
|
||||
applyButton.Click([&](IInspectable const& sender, RoutedEventArgs const&) {
|
||||
bool isSuccess = true;
|
||||
// Clear existing Key Remaps
|
||||
keyboardManagerState.ClearSingleKeyRemaps();
|
||||
|
||||
// Save the keys that are valid and report if any of them were invalid
|
||||
for (unsigned int i = 1; i < keyRemapTable.Children().Size(); i++)
|
||||
{
|
||||
StackPanel currentRow = keyRemapTable.Children().GetAt(i).as<StackPanel>();
|
||||
hstring originalKeyString = currentRow.Children().GetAt(0).as<StackPanel>().Children().GetAt(1).as<TextBlock>().Text();
|
||||
hstring newKeyString = currentRow.Children().GetAt(1).as<StackPanel>().Children().GetAt(1).as<TextBlock>().Text();
|
||||
if (!originalKeyString.empty() && !newKeyString.empty())
|
||||
{
|
||||
DWORD originalKey = std::stoi(originalKeyString.c_str());
|
||||
DWORD newKey = std::stoi(newKeyString.c_str());
|
||||
keyboardManagerState.AddSingleKeyRemap(originalKey, newKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
isSuccess = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isSuccess)
|
||||
{
|
||||
settingsMessage.Foreground(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::Green() });
|
||||
settingsMessage.Text(winrt::to_hstring("Remapping successful!"));
|
||||
}
|
||||
else
|
||||
{
|
||||
settingsMessage.Foreground(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::Red() });
|
||||
settingsMessage.Text(winrt::to_hstring("All remappings were not successfully applied."));
|
||||
}
|
||||
});
|
||||
|
||||
header.Children().Append(headerText);
|
||||
header.Children().Append(cancelButton);
|
||||
header.Children().Append(applyButton);
|
||||
header.Children().Append(settingsMessage);
|
||||
|
||||
// Store handle of edit keyboard window
|
||||
SingleKeyRemapControl::EditKeyboardWindowHandle = _hWndEditKeyboardWindow;
|
||||
// Store keyboard manager state
|
||||
SingleKeyRemapControl::keyboardManagerState = &keyboardManagerState;
|
||||
|
||||
// Load existing remaps into UI
|
||||
for (const auto& it : keyboardManagerState.singleKeyReMap)
|
||||
{
|
||||
SingleKeyRemapControl::AddNewControlKeyRemapRow(keyRemapTable, it.first, it.second);
|
||||
}
|
||||
|
||||
// Add remap key button
|
||||
Windows::UI::Xaml::Controls::Button addRemapKey;
|
||||
addRemapKey.Background(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::LightGray() });
|
||||
addRemapKey.Foreground(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::Black() });
|
||||
FontIcon plusSymbol;
|
||||
plusSymbol.FontFamily(Xaml::Media::FontFamily(L"Segoe MDL2 Assets"));
|
||||
plusSymbol.Glyph(L"\xE109");
|
||||
addRemapKey.Content(plusSymbol);
|
||||
addRemapKey.Margin({ 10 });
|
||||
addRemapKey.Click([&](IInspectable const& sender, RoutedEventArgs const&) {
|
||||
SingleKeyRemapControl::AddNewControlKeyRemapRow(keyRemapTable);
|
||||
});
|
||||
|
||||
xamlContainer.Children().Append(header);
|
||||
xamlContainer.Children().Append(keyRemapInfoHeader);
|
||||
xamlContainer.Children().Append(keyRemapTable);
|
||||
xamlContainer.Children().Append(addRemapKey);
|
||||
xamlContainer.UpdateLayout();
|
||||
desktopSource.Content(xamlContainer);
|
||||
|
||||
////End XAML Island section
|
||||
if (_hWndEditKeyboardWindow)
|
||||
{
|
||||
|
||||
@@ -104,6 +104,7 @@
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ShortcutControl.cpp" />
|
||||
<ClCompile Include="SingleKeyRemapControl.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="EditKeyboardWindow.h" />
|
||||
@@ -111,6 +112,7 @@
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="MainWindow.h" />
|
||||
<ClInclude Include="ShortcutControl.h" />
|
||||
<ClInclude Include="SingleKeyRemapControl.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<ClCompile Include="EditShortcutsWindow.cpp" />
|
||||
<ClCompile Include="ShortcutControl.cpp" />
|
||||
<ClCompile Include="pch.cpp" />
|
||||
<ClCompile Include="SingleKeyRemapControl.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="MainWindow.h" />
|
||||
@@ -13,6 +14,7 @@
|
||||
<ClInclude Include="EditShortcutsWindow.h" />
|
||||
<ClInclude Include="ShortcutControl.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="SingleKeyRemapControl.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
||||
115
src/modules/keyboardmanager/ui/SingleKeyRemapControl.cpp
Normal file
115
src/modules/keyboardmanager/ui/SingleKeyRemapControl.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
#include "pch.h"
|
||||
#include "SingleKeyRemapControl.h"
|
||||
|
||||
//Both static members are initialized to null
|
||||
HWND SingleKeyRemapControl::EditKeyboardWindowHandle = nullptr;
|
||||
KeyboardManagerState* SingleKeyRemapControl::keyboardManagerState = nullptr;
|
||||
|
||||
// Function to add a new row to the remap keys table. If the originalKey and newKey args are provided, then the displayed remap keys are set to those values.
|
||||
void SingleKeyRemapControl::AddNewControlKeyRemapRow(StackPanel& parent, const DWORD& originalKey, const WORD& newKey)
|
||||
{
|
||||
// Parent element for the row
|
||||
Windows::UI::Xaml::Controls::StackPanel tableRow;
|
||||
tableRow.Background(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::LightGray() });
|
||||
tableRow.Spacing(100);
|
||||
tableRow.Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
|
||||
|
||||
// SingleKeyRemapControl for the original key.
|
||||
SingleKeyRemapControl originalRemapKeyControl;
|
||||
tableRow.Children().Append(originalRemapKeyControl.getSingleKeyRemapControl());
|
||||
|
||||
// SingleKeyRemapControl for the new remap key.
|
||||
SingleKeyRemapControl newRemapKeyControl;
|
||||
tableRow.Children().Append(newRemapKeyControl.getSingleKeyRemapControl());
|
||||
|
||||
// Set the key text if the two keys are not null (i.e. default args)
|
||||
if (originalKey != NULL && newKey != NULL)
|
||||
{
|
||||
originalRemapKeyControl.singleKeyRemapText.Text(winrt::to_hstring((unsigned int)originalKey));
|
||||
newRemapKeyControl.singleKeyRemapText.Text(winrt::to_hstring((unsigned int)newKey));
|
||||
}
|
||||
|
||||
// Delete row button
|
||||
Windows::UI::Xaml::Controls::Button deleteRemapKeys;
|
||||
deleteRemapKeys.Background(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::LightGray() });
|
||||
deleteRemapKeys.Foreground(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::Black() });
|
||||
FontIcon deleteSymbol;
|
||||
deleteSymbol.FontFamily(Xaml::Media::FontFamily(L"Segoe MDL2 Assets"));
|
||||
deleteSymbol.Glyph(L"\xE74D");
|
||||
deleteRemapKeys.Content(deleteSymbol);
|
||||
deleteRemapKeys.Click([&](IInspectable const& sender, RoutedEventArgs const&) {
|
||||
StackPanel currentRow = sender.as<Button>().Parent().as<StackPanel>();
|
||||
uint32_t index;
|
||||
parent.Children().IndexOf(currentRow, index);
|
||||
parent.Children().RemoveAt(index);
|
||||
});
|
||||
tableRow.Children().Append(deleteRemapKeys);
|
||||
parent.Children().Append(tableRow);
|
||||
}
|
||||
|
||||
// Function to return the stack panel element of the SingleKeyRemapControl. This is the externally visible UI element which can be used to add it to other layouts
|
||||
StackPanel SingleKeyRemapControl::getSingleKeyRemapControl()
|
||||
{
|
||||
return singleKeyRemapControlLayout;
|
||||
}
|
||||
|
||||
// Function to create the detect remap key UI window
|
||||
void SingleKeyRemapControl::createDetectKeyWindow(IInspectable const& sender, XamlRoot xamlRoot, KeyboardManagerState& keyboardManagerState)
|
||||
{
|
||||
// ContentDialog for detecting remap key. This is the parent UI element.
|
||||
ContentDialog detectRemapKeyBox;
|
||||
|
||||
// ContentDialog requires manually setting the XamlRoot (https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.contentdialog#contentdialog-in-appwindow-or-xaml-islands)
|
||||
detectRemapKeyBox.XamlRoot(xamlRoot);
|
||||
detectRemapKeyBox.Title(box_value(L"Press a key on selected keyboard:"));
|
||||
detectRemapKeyBox.PrimaryButtonText(to_hstring(L"OK"));
|
||||
detectRemapKeyBox.IsSecondaryButtonEnabled(false);
|
||||
detectRemapKeyBox.CloseButtonText(to_hstring(L"Cancel"));
|
||||
detectRemapKeyBox.Background(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::LightGray() });
|
||||
detectRemapKeyBox.Foreground(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::Black() });
|
||||
|
||||
// Get the linked text block for the "Type Key" button that was clicked
|
||||
TextBlock linkedRemapText = getSiblingElement(sender).as<TextBlock>();
|
||||
|
||||
// OK button
|
||||
detectRemapKeyBox.PrimaryButtonClick([=, &keyboardManagerState](Windows::UI::Xaml::Controls::ContentDialog const& sender, ContentDialogButtonClickEventArgs const&) {
|
||||
// Save the detected key in the linked text block
|
||||
DWORD detectedKey = keyboardManagerState.GetDetectedSingleRemapKey();
|
||||
if (detectedKey != NULL)
|
||||
{
|
||||
linkedRemapText.Text(winrt::to_hstring((unsigned int)detectedKey));
|
||||
}
|
||||
|
||||
// Reset the keyboard manager UI state
|
||||
keyboardManagerState.ResetUIState();
|
||||
});
|
||||
|
||||
// Cancel button
|
||||
detectRemapKeyBox.CloseButtonClick([&keyboardManagerState](Windows::UI::Xaml::Controls::ContentDialog const& sender, ContentDialogButtonClickEventArgs const&) {
|
||||
// Reset the keyboard manager UI state
|
||||
keyboardManagerState.ResetUIState();
|
||||
});
|
||||
|
||||
// StackPanel parent for the displayed text in the dialog
|
||||
Windows::UI::Xaml::Controls::StackPanel stackPanel;
|
||||
stackPanel.Background(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::LightGray() });
|
||||
|
||||
// Header textblock
|
||||
TextBlock text;
|
||||
text.Text(winrt::to_hstring("Key Pressed:"));
|
||||
text.Margin({ 0, 0, 0, 10 });
|
||||
|
||||
// Textblock to display the detected key
|
||||
TextBlock remapKey;
|
||||
|
||||
stackPanel.Children().Append(text);
|
||||
stackPanel.Children().Append(remapKey);
|
||||
stackPanel.UpdateLayout();
|
||||
detectRemapKeyBox.Content(stackPanel);
|
||||
|
||||
// Configure the keyboardManagerState to store the UI information.
|
||||
keyboardManagerState.ConfigureDetectSingleKeyRemapUI(remapKey);
|
||||
|
||||
// Show the dialog
|
||||
detectRemapKeyBox.ShowAsync();
|
||||
}
|
||||
49
src/modules/keyboardmanager/ui/SingleKeyRemapControl.h
Normal file
49
src/modules/keyboardmanager/ui/SingleKeyRemapControl.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#include <keyboardmanager/common/KeyboardManagerState.h>
|
||||
|
||||
class SingleKeyRemapControl
|
||||
{
|
||||
private:
|
||||
// Textblock to display the selected remap key
|
||||
TextBlock singleKeyRemapText;
|
||||
|
||||
// Button to type the remap key
|
||||
Button typeKey;
|
||||
|
||||
// StackPanel to parent the above controls
|
||||
StackPanel singleKeyRemapControlLayout;
|
||||
|
||||
public:
|
||||
// Handle to the current Edit Keyboard Window
|
||||
static HWND EditKeyboardWindowHandle;
|
||||
// Pointer to the keyboard manager state
|
||||
static KeyboardManagerState* keyboardManagerState;
|
||||
|
||||
SingleKeyRemapControl()
|
||||
{
|
||||
typeKey.Content(winrt::box_value(winrt::to_hstring("Type Key")));
|
||||
typeKey.Background(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::LightGray() });
|
||||
typeKey.Foreground(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::Black() });
|
||||
typeKey.Click([&](IInspectable const& sender, RoutedEventArgs const&) {
|
||||
keyboardManagerState->SetUIState(KeyboardManagerUIState::DetectSingleKeyRemapWindowActivated, EditKeyboardWindowHandle);
|
||||
// Using the XamlRoot of the typeKey to get the root of the XAML host
|
||||
createDetectKeyWindow(sender, sender.as<Button>().XamlRoot(), *keyboardManagerState);
|
||||
});
|
||||
|
||||
singleKeyRemapControlLayout.Background(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::LightGray() });
|
||||
singleKeyRemapControlLayout.Margin({ 0, 0, 0, 10 });
|
||||
singleKeyRemapControlLayout.Spacing(10);
|
||||
|
||||
singleKeyRemapControlLayout.Children().Append(typeKey);
|
||||
singleKeyRemapControlLayout.Children().Append(singleKeyRemapText);
|
||||
}
|
||||
|
||||
// Function to add a new row to the remap keys table. If the originalKey and newKey args are provided, then the displayed remap keys are set to those values.
|
||||
static void AddNewControlKeyRemapRow(StackPanel& parent, const DWORD& originalKey = NULL, const WORD& newKey = NULL);
|
||||
|
||||
// Function to return the stack panel element of the SingleKeyRemapControl. This is the externally visible UI element which can be used to add it to other layouts
|
||||
StackPanel getSingleKeyRemapControl();
|
||||
|
||||
// Function to create the detect remap keys UI window
|
||||
void createDetectKeyWindow(IInspectable const& sender, XamlRoot xamlRoot, KeyboardManagerState& keyboardManagerState);
|
||||
};
|
||||
Reference in New Issue
Block a user