mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-04 18:26:39 +02:00
Display keys in Shorcut modal as buttons (#1996)
* Display keys in Shorcut modal as buttons * Refactor: rename currentShortcutUI and currentSingleKeyUI * Change GetKeyVector signature
This commit is contained in:
committed by
Udit Singh
parent
52c12731cb
commit
70495d9ce9
@@ -3,7 +3,7 @@
|
||||
|
||||
// Constructor
|
||||
KeyboardManagerState::KeyboardManagerState() :
|
||||
uiState(KeyboardManagerUIState::Deactivated), currentUIWindow(nullptr), currentShortcutTextBlock(nullptr), currentSingleKeyRemapTextBlock(nullptr), detectedRemapKey(NULL)
|
||||
uiState(KeyboardManagerUIState::Deactivated), currentUIWindow(nullptr), currentShortcutUI(nullptr), currentSingleKeyUI(nullptr), detectedRemapKey(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -50,18 +50,18 @@ void KeyboardManagerState::ResetUIState()
|
||||
SetUIState(KeyboardManagerUIState::Deactivated);
|
||||
|
||||
// Reset the shortcut UI stored variables
|
||||
std::unique_lock<std::mutex> currentShortcutTextBlock_lock(currentShortcutTextBlock_mutex);
|
||||
currentShortcutTextBlock = nullptr;
|
||||
currentShortcutTextBlock_lock.unlock();
|
||||
std::unique_lock<std::mutex> currentShortcutUI_lock(currentShortcutUI_mutex);
|
||||
currentShortcutUI = nullptr;
|
||||
currentShortcutUI_lock.unlock();
|
||||
|
||||
std::unique_lock<std::mutex> detectedShortcut_lock(detectedShortcut_mutex);
|
||||
detectedShortcut.Reset();
|
||||
detectedShortcut_lock.unlock();
|
||||
|
||||
// Reset all the single key remap UI stored variables.
|
||||
std::unique_lock<std::mutex> currentSingleKeyRemapTextBlock_lock(currentSingleKeyRemapTextBlock_mutex);
|
||||
currentSingleKeyRemapTextBlock = nullptr;
|
||||
currentSingleKeyRemapTextBlock_lock.unlock();
|
||||
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;
|
||||
@@ -115,81 +115,120 @@ bool KeyboardManagerState::AddSingleKeyRemap(const DWORD& originalKey, const DWO
|
||||
}
|
||||
|
||||
// Function to set the textblock of the detect shortcut UI so that it can be accessed by the hook
|
||||
void KeyboardManagerState::ConfigureDetectShortcutUI(const TextBlock& textBlock)
|
||||
void KeyboardManagerState::ConfigureDetectShortcutUI(const StackPanel& textBlock)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(currentShortcutTextBlock_mutex);
|
||||
currentShortcutTextBlock = textBlock;
|
||||
std::lock_guard<std::mutex> lock(currentShortcutUI_mutex);
|
||||
currentShortcutUI = textBlock;
|
||||
}
|
||||
|
||||
// Function to set the textblock of the detect remap key UI so that it can be accessed by the hook
|
||||
void KeyboardManagerState::ConfigureDetectSingleKeyRemapUI(const TextBlock& textBlock)
|
||||
void KeyboardManagerState::ConfigureDetectSingleKeyRemapUI(const StackPanel& textBlock)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(currentSingleKeyRemapTextBlock_mutex);
|
||||
currentSingleKeyRemapTextBlock = textBlock;
|
||||
std::lock_guard<std::mutex> lock(currentSingleKeyUI_mutex);
|
||||
currentSingleKeyUI = textBlock;
|
||||
}
|
||||
|
||||
|
||||
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 });
|
||||
border.Background(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::LightGray() });
|
||||
remapKey.Foreground(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::Black() });
|
||||
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> currentShortcutTextBlock_lock(currentShortcutTextBlock_mutex);
|
||||
if (currentShortcutTextBlock == nullptr)
|
||||
std::lock_guard<std::mutex> currentShortcutUI_lock(currentShortcutUI_mutex);
|
||||
if (currentShortcutUI == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
std::unique_lock<std::mutex> detectedShortcut_lock(detectedShortcut_mutex);
|
||||
hstring shortcutString = detectedShortcut.ToHstring();
|
||||
|
||||
std::vector<hstring> shortcut = detectedShortcut.GetKeyVector();
|
||||
|
||||
detectedShortcut_lock.unlock();
|
||||
|
||||
// Since this function is invoked from the back-end thread, in order to update the UI the dispatcher must be used.
|
||||
currentShortcutTextBlock.Dispatcher().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [=]() {
|
||||
currentShortcutTextBlock.Text(shortcutString);
|
||||
currentShortcutUI.Dispatcher().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [this, shortcut]() {
|
||||
currentShortcutUI.Children().Clear();
|
||||
for (auto& key : shortcut)
|
||||
{
|
||||
AddKeyToLayout(currentShortcutUI, key);
|
||||
}
|
||||
currentShortcutUI.UpdateLayout();
|
||||
});
|
||||
}
|
||||
|
||||
// Function to update the detect remap key UI based on the entered key.
|
||||
void KeyboardManagerState::UpdateDetectSingleKeyRemapUI()
|
||||
{
|
||||
std::lock_guard<std::mutex> currentSingleKeyRemapTextBlock_lock(currentSingleKeyRemapTextBlock_mutex);
|
||||
if (currentSingleKeyRemapTextBlock == nullptr)
|
||||
std::lock_guard<std::mutex> currentSingleKeyUI_lock(currentSingleKeyUI_mutex);
|
||||
if (currentSingleKeyUI == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> detectedRemapKey_lock(detectedRemapKey_mutex);
|
||||
hstring remapKeyString = winrt::to_hstring((unsigned int)detectedRemapKey);
|
||||
hstring key = winrt::to_hstring((unsigned int)detectedRemapKey);
|
||||
detectedRemapKey_lock.unlock();
|
||||
|
||||
// Since this function is invoked from the back-end thread, in order to update the UI the dispatcher must be used.
|
||||
currentSingleKeyRemapTextBlock.Dispatcher().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [=]() {
|
||||
currentSingleKeyRemapTextBlock.Text(remapKeyString);
|
||||
currentSingleKeyUI.Dispatcher().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [this, key]() {
|
||||
currentSingleKeyUI.Children().Clear();
|
||||
AddKeyToLayout(currentSingleKeyUI, key);
|
||||
currentSingleKeyUI.UpdateLayout();
|
||||
});
|
||||
}
|
||||
|
||||
// Function to return the currently detected shortcut which is displayed on the UI
|
||||
Shortcut KeyboardManagerState::GetDetectedShortcut()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(currentShortcutTextBlock_mutex);
|
||||
hstring detectedShortcutString = currentShortcutTextBlock.Text();
|
||||
std::unique_lock<std::mutex> lock(currentShortcutUI_mutex);
|
||||
|
||||
std::vector<winrt::hstring> keys;
|
||||
if (currentShortcutUI.Children().Size() > 0)
|
||||
{
|
||||
for (auto border : currentShortcutUI.Children())
|
||||
{
|
||||
auto keyString = border.as<Border>().Child().as<TextBlock>().Text();
|
||||
keys.push_back(keyString);
|
||||
}
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
return Shortcut::CreateShortcut(detectedShortcutString);
|
||||
return Shortcut::CreateShortcut(keys);
|
||||
}
|
||||
|
||||
// Function to return the currently detected remap key which is displayed on the UI
|
||||
DWORD KeyboardManagerState::GetDetectedSingleRemapKey()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(currentSingleKeyRemapTextBlock_mutex);
|
||||
hstring remapKeyString = currentSingleKeyRemapTextBlock.Text();
|
||||
lock.unlock();
|
||||
|
||||
std::wstring remapKeyWString = remapKeyString.c_str();
|
||||
DWORD remapKey = NULL;
|
||||
if (!remapKeyString.empty())
|
||||
std::unique_lock<std::mutex> lock(currentSingleKeyUI_mutex);
|
||||
DWORD key = 0;
|
||||
if (currentSingleKeyUI.Children().Size() > 0)
|
||||
{
|
||||
remapKey = std::stoul(remapKeyWString);
|
||||
auto border = currentSingleKeyUI.Children().GetAt(0);
|
||||
auto keyString = border.as<Border>().Child().as<TextBlock>().Text();
|
||||
key = std::stoul(keyString.c_str());
|
||||
}
|
||||
|
||||
return remapKey;
|
||||
lock.unlock();
|
||||
|
||||
return 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.
|
||||
|
||||
@@ -39,13 +39,15 @@ private:
|
||||
std::mutex detectedRemapKey_mutex;
|
||||
|
||||
// Stores the UI element which is to be updated based on the remap key entered.
|
||||
TextBlock currentSingleKeyRemapTextBlock;
|
||||
std::mutex currentSingleKeyRemapTextBlock_mutex;
|
||||
StackPanel currentSingleKeyUI;
|
||||
std::mutex currentSingleKeyUI_mutex;
|
||||
|
||||
// Stores the UI element which is to be updated based on the shortcut entered
|
||||
TextBlock currentShortcutTextBlock;
|
||||
std::mutex currentShortcutTextBlock_mutex;
|
||||
StackPanel currentShortcutUI;
|
||||
std::mutex currentShortcutUI_mutex;
|
||||
|
||||
// Display a key by appending a border Control as a child of the panel.
|
||||
void AddKeyToLayout(const StackPanel& panel, const winrt::hstring& key);
|
||||
public:
|
||||
// The map members and their mutexes are left as public since the maps are used extensively in dllmain.cpp.
|
||||
// Maps which store the remappings for each of the features. The bool fields should be initalised to false. They are used to check the current state of the shortcut (i.e is that particular shortcut currently pressed down or not).
|
||||
@@ -93,10 +95,10 @@ public:
|
||||
bool AddOSLevelShortcut(const Shortcut& originalSC, const Shortcut& newSC);
|
||||
|
||||
// Function to set the textblock of the detect shortcut UI so that it can be accessed by the hook
|
||||
void ConfigureDetectShortcutUI(const TextBlock& textBlock);
|
||||
void ConfigureDetectShortcutUI(const StackPanel& textBlock);
|
||||
|
||||
// Function to set the textblock of the detect remap key UI so that it can be accessed by the hook
|
||||
void ConfigureDetectSingleKeyRemapUI(const TextBlock& textBlock);
|
||||
void ConfigureDetectSingleKeyRemapUI(const StackPanel& textBlock);
|
||||
|
||||
// Function to update the detect shortcut UI based on the entered keys
|
||||
void UpdateDetectShortcutUI();
|
||||
|
||||
@@ -395,28 +395,47 @@ void Shortcut::ResetKey(const DWORD& input, const bool& isWinBoth)
|
||||
// Function to return the string representation of the shortcut
|
||||
winrt::hstring Shortcut::ToHstring() const
|
||||
{
|
||||
std::vector<winrt::hstring> keys = GetKeyVector();
|
||||
|
||||
winrt::hstring output;
|
||||
for (auto& key : keys)
|
||||
{
|
||||
output = output + key + winrt::to_hstring(L" ");
|
||||
}
|
||||
if (keys.size() > 1)
|
||||
{
|
||||
return winrt::hstring(output.c_str(), output.size() - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<winrt::hstring> Shortcut::GetKeyVector() const
|
||||
{
|
||||
std::vector<winrt::hstring> keys;
|
||||
if (winKey != ModifierKey::Disabled)
|
||||
{
|
||||
output = output + ModifierKeyNameWithSide(winKey, L"Win") + winrt::to_hstring(L" ");
|
||||
keys.push_back(ModifierKeyNameWithSide(winKey, L"Win"));
|
||||
}
|
||||
if (ctrlKey != ModifierKey::Disabled)
|
||||
{
|
||||
output = output + ModifierKeyNameWithSide(ctrlKey, L"Ctrl") + winrt::to_hstring(L" ");
|
||||
keys.push_back(ModifierKeyNameWithSide(ctrlKey, L"Ctrl"));
|
||||
}
|
||||
if (altKey != ModifierKey::Disabled)
|
||||
{
|
||||
output = output + ModifierKeyNameWithSide(altKey, L"Alt") + winrt::to_hstring(L" ");
|
||||
keys.push_back(ModifierKeyNameWithSide(altKey, L"Alt"));
|
||||
}
|
||||
if (shiftKey != ModifierKey::Disabled)
|
||||
{
|
||||
output = output + ModifierKeyNameWithSide(shiftKey, L"Shift") + winrt::to_hstring(L" ");
|
||||
keys.push_back(ModifierKeyNameWithSide(shiftKey, L"Shift"));
|
||||
}
|
||||
if (actionKey != NULL)
|
||||
{
|
||||
output = output + winrt::to_hstring((unsigned int)actionKey);
|
||||
keys.push_back(winrt::to_hstring((unsigned int)actionKey));
|
||||
}
|
||||
return output;
|
||||
return keys;
|
||||
}
|
||||
|
||||
// Function to check if all the modifiers in the shortcut have been pressed down
|
||||
@@ -765,23 +784,27 @@ DWORD Shortcut::DecodeKey(const std::wstring& keyName)
|
||||
}
|
||||
|
||||
// Function to create a shortcut object from its string representation
|
||||
Shortcut Shortcut::CreateShortcut(const winrt::hstring& input)
|
||||
Shortcut Shortcut::CreateShortcut(const std::vector<winrt::hstring>& keys)
|
||||
{
|
||||
Shortcut newShortcut;
|
||||
std::wstring shortcutWstring = input.c_str();
|
||||
std::vector<std::wstring> shortcutVector = splitwstring(shortcutWstring, L' ');
|
||||
for (int i = 0; i < shortcutVector.size(); i++)
|
||||
for (int i = 0; i < keys.size(); i++)
|
||||
{
|
||||
if (shortcutVector[i] == L"Win")
|
||||
if (keys[i] == L"Win")
|
||||
{
|
||||
newShortcut.SetKey(NULL, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
DWORD keyCode = DecodeKey(shortcutVector[i]);
|
||||
DWORD keyCode = DecodeKey(keys[i].c_str());
|
||||
newShortcut.SetKey(keyCode);
|
||||
}
|
||||
}
|
||||
|
||||
return newShortcut;
|
||||
}
|
||||
|
||||
Shortcut Shortcut::CreateShortcut(const winrt::hstring& input)
|
||||
{
|
||||
std::vector<std::wstring> shortcut = splitwstring(input.c_str(), L' ');
|
||||
return CreateShortcut(std::vector<winrt::hstring>(shortcut.begin(), shortcut.end()));
|
||||
}
|
||||
@@ -140,6 +140,9 @@ public:
|
||||
// Function to return the string representation of the shortcut
|
||||
winrt::hstring ToHstring() const;
|
||||
|
||||
// Function to return a vector of hstring for each key, in the same order as ToHstring()
|
||||
std::vector<winrt::hstring> GetKeyVector() const;
|
||||
|
||||
// Function to check if all the modifiers in the shortcut have been pressed down
|
||||
bool CheckModifiersKeyboardState() const;
|
||||
|
||||
@@ -152,6 +155,9 @@ public:
|
||||
// Function to return the virtual key code from the name of the key
|
||||
static DWORD DecodeKey(const std::wstring& keyName);
|
||||
|
||||
// Function to create a shortcut object from its string vector representation
|
||||
static Shortcut CreateShortcut(const std::vector<winrt::hstring>& keys);
|
||||
|
||||
// Function to create a shortcut object from its string representation
|
||||
static Shortcut CreateShortcut(const winrt::hstring& input);
|
||||
};
|
||||
|
||||
@@ -57,13 +57,14 @@ void ShortcutControl::createDetectShortcutWindow(IInspectable const& sender, Xam
|
||||
// ContentDialog for detecting shortcuts. This is the parent UI element.
|
||||
ContentDialog detectShortcutBox;
|
||||
|
||||
// TODO: Hardcoded light theme, since the app is not theme aware ATM.
|
||||
detectShortcutBox.RequestedTheme(ElementTheme::Light);
|
||||
// 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)
|
||||
detectShortcutBox.XamlRoot(xamlRoot);
|
||||
detectShortcutBox.Title(box_value(L"Press the keys in shortcut:"));
|
||||
detectShortcutBox.PrimaryButtonText(to_hstring(L"OK"));
|
||||
detectShortcutBox.IsSecondaryButtonEnabled(false);
|
||||
detectShortcutBox.CloseButtonText(to_hstring(L"Cancel"));
|
||||
detectShortcutBox.Background(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::LightGray() });
|
||||
|
||||
// Get the linked text block for the "Type shortcut" button that was clicked
|
||||
TextBlock linkedShortcutText = getSiblingElement(sender).as<TextBlock>();
|
||||
@@ -86,23 +87,23 @@ void ShortcutControl::createDetectShortcutWindow(IInspectable const& sender, Xam
|
||||
|
||||
// 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() });
|
||||
detectShortcutBox.Content(stackPanel);
|
||||
|
||||
// Header textblock
|
||||
TextBlock text;
|
||||
text.Text(winrt::to_hstring("Keys Pressed:"));
|
||||
text.Margin({ 0, 0, 0, 10 });
|
||||
|
||||
// Textblock to display the detected shortcut
|
||||
TextBlock shortcutKeys;
|
||||
|
||||
stackPanel.Children().Append(text);
|
||||
stackPanel.Children().Append(shortcutKeys);
|
||||
|
||||
// Target StackPanel to place the selected key
|
||||
Windows::UI::Xaml::Controls::StackPanel keyStackPanel;
|
||||
stackPanel.Children().Append(keyStackPanel);
|
||||
keyStackPanel.Orientation(Orientation::Horizontal);
|
||||
|
||||
stackPanel.UpdateLayout();
|
||||
detectShortcutBox.Content(stackPanel);
|
||||
|
||||
// Configure the keyboardManagerState to store the UI information.
|
||||
keyboardManagerState.ConfigureDetectShortcutUI(shortcutKeys);
|
||||
keyboardManagerState.ConfigureDetectShortcutUI(keyStackPanel);
|
||||
|
||||
// Show the dialog
|
||||
detectShortcutBox.ShowAsync();
|
||||
|
||||
@@ -58,15 +58,15 @@ void SingleKeyRemapControl::createDetectKeyWindow(IInspectable const& sender, Xa
|
||||
{
|
||||
// ContentDialog for detecting remap key. This is the parent UI element.
|
||||
ContentDialog detectRemapKeyBox;
|
||||
|
||||
|
||||
// TODO: Hardcoded light theme, since the app is not theme aware ATM.
|
||||
detectRemapKeyBox.RequestedTheme(ElementTheme::Light);
|
||||
// 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>();
|
||||
@@ -92,23 +92,22 @@ void SingleKeyRemapControl::createDetectKeyWindow(IInspectable const& sender, Xa
|
||||
|
||||
// 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() });
|
||||
detectRemapKeyBox.Content(stackPanel);
|
||||
|
||||
// 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);
|
||||
|
||||
// Target StackPanel to place the selected key
|
||||
Windows::UI::Xaml::Controls::StackPanel keyStackPanel;
|
||||
stackPanel.Children().Append(keyStackPanel);
|
||||
keyStackPanel.Orientation(Orientation::Horizontal);
|
||||
stackPanel.UpdateLayout();
|
||||
detectRemapKeyBox.Content(stackPanel);
|
||||
|
||||
// Configure the keyboardManagerState to store the UI information.
|
||||
keyboardManagerState.ConfigureDetectSingleKeyRemapUI(remapKey);
|
||||
keyboardManagerState.ConfigureDetectSingleKeyRemapUI(keyStackPanel);
|
||||
|
||||
// Show the dialog
|
||||
detectRemapKeyBox.ShowAsync();
|
||||
|
||||
Reference in New Issue
Block a user