Detect Shortcut: Hold Esc/Enter to Cancel/Accept (#2135)

* Detect Shortcut: Hold Esc/Enter to Discard/Apply changes

Bypass shorcut/single key remapping by holding the navigation keys
This commit is contained in:
Tomas Agustin Raies
2020-04-16 09:16:48 -07:00
committed by GitHub
parent 5d9b71b038
commit c37884bdb7
10 changed files with 594 additions and 38 deletions

View File

@@ -72,15 +72,31 @@ void ShortcutControl::createDetectShortcutWindow(IInspectable const& sender, Xam
// 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.IsPrimaryButtonEnabled(false);
detectShortcutBox.IsSecondaryButtonEnabled(false);
detectShortcutBox.CloseButtonText(to_hstring(L"Cancel"));
// Get the linked text block for the "Type shortcut" button that was clicked
TextBlock linkedShortcutText = getSiblingElement(sender).as<TextBlock>();
// OK button
detectShortcutBox.PrimaryButtonClick([=, &shortcutRemapBuffer, &keyboardManagerState](Windows::UI::Xaml::Controls::ContentDialog const& sender, ContentDialogButtonClickEventArgs const&) {
auto unregisterKeys = [&keyboardManagerState]() {
std::thread t1(&KeyboardManagerState::UnregisterKeyDelay, &keyboardManagerState, VK_ESCAPE);
std::thread t2(&KeyboardManagerState::UnregisterKeyDelay, &keyboardManagerState, VK_RETURN);
t1.detach();
t2.detach();
};
auto selectDetectedShortcutAndResetKeys = [&keyboardManagerState](DWORD key) {
keyboardManagerState.SelectDetectedShortcut(key);
keyboardManagerState.ResetDetectedShortcutKey(key);
};
auto onAccept = [linkedShortcutText,
detectShortcutBox,
&keyboardManagerState,
&shortcutRemapBuffer,
unregisterKeys,
rowIndex,
colIndex] {
// Save the detected shortcut in the linked text block
Shortcut detectedShortcutKeys = keyboardManagerState.GetDetectedShortcut();
@@ -92,14 +108,71 @@ void ShortcutControl::createDetectShortcutWindow(IInspectable const& sender, Xam
// Reset the keyboard manager UI state
keyboardManagerState.ResetUIState();
unregisterKeys();
detectShortcutBox.Hide();
};
TextBlock primaryButtonText;
primaryButtonText.Text(to_hstring(L"OK"));
Button primaryButton;
primaryButton.HorizontalAlignment(HorizontalAlignment::Stretch);
primaryButton.Margin({ 2, 2, 2, 2 });
primaryButton.Content(primaryButtonText);
// OK button
primaryButton.Click([onAccept](IInspectable const& sender, RoutedEventArgs const&) {
onAccept();
});
keyboardManagerState.RegisterKeyDelay(
VK_RETURN,
selectDetectedShortcutAndResetKeys,
[primaryButton, detectShortcutBox](DWORD) {
detectShortcutBox.Dispatcher().RunAsync(
Windows::UI::Core::CoreDispatcherPriority::Normal,
[primaryButton] {
primaryButton.Background(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::DarkGray() });
});
},
[onAccept, detectShortcutBox](DWORD) {
detectShortcutBox.Dispatcher().RunAsync(
Windows::UI::Core::CoreDispatcherPriority::Normal,
[onAccept] {
onAccept();
});
});
TextBlock cancelButtonText;
cancelButtonText.Text(to_hstring(L"Cancel"));
Button cancelButton;
cancelButton.HorizontalAlignment(HorizontalAlignment::Stretch);
cancelButton.Margin({ 2, 2, 2, 2 });
cancelButton.Content(cancelButtonText);
// Cancel button
detectShortcutBox.CloseButtonClick([&keyboardManagerState](Windows::UI::Xaml::Controls::ContentDialog const& sender, ContentDialogButtonClickEventArgs const&) {
cancelButton.Click([detectShortcutBox, unregisterKeys, &keyboardManagerState](IInspectable const& sender, RoutedEventArgs const&) {
// Reset the keyboard manager UI state
keyboardManagerState.ResetUIState();
unregisterKeys();
detectShortcutBox.Hide();
});
keyboardManagerState.RegisterKeyDelay(
VK_ESCAPE,
selectDetectedShortcutAndResetKeys,
[&keyboardManagerState, detectShortcutBox, unregisterKeys](DWORD) {
detectShortcutBox.Dispatcher().RunAsync(
Windows::UI::Core::CoreDispatcherPriority::Normal,
[detectShortcutBox] {
detectShortcutBox.Hide();
});
keyboardManagerState.ResetUIState();
unregisterKeys();
},
nullptr);
// StackPanel parent for the displayed text in the dialog
Windows::UI::Xaml::Controls::StackPanel stackPanel;
detectShortcutBox.Content(stackPanel);
@@ -112,9 +185,36 @@ void ShortcutControl::createDetectShortcutWindow(IInspectable const& sender, Xam
// Target StackPanel to place the selected key
Windows::UI::Xaml::Controls::StackPanel keyStackPanel;
stackPanel.Children().Append(keyStackPanel);
keyStackPanel.Orientation(Orientation::Horizontal);
stackPanel.Children().Append(keyStackPanel);
TextBlock holdEscInfo;
holdEscInfo.Text(winrt::to_hstring("Hold Esc to discard"));
holdEscInfo.FontSize(12);
holdEscInfo.Margin({ 0, 20, 0, 0 });
stackPanel.Children().Append(holdEscInfo);
TextBlock holdEnterInfo;
holdEnterInfo.Text(winrt::to_hstring("Hold Enter to apply"));
holdEnterInfo.FontSize(12);
holdEnterInfo.Margin({ 0, 0, 0, 0 });
stackPanel.Children().Append(holdEnterInfo);
ColumnDefinition primaryButtonColumn;
ColumnDefinition cancelButtonColumn;
Grid buttonPanel;
buttonPanel.Margin({ 0, 20, 0, 0 });
buttonPanel.HorizontalAlignment(HorizontalAlignment::Stretch);
buttonPanel.ColumnDefinitions().Append(primaryButtonColumn);
buttonPanel.ColumnDefinitions().Append(cancelButtonColumn);
buttonPanel.SetColumn(primaryButton, 0);
buttonPanel.SetColumn(cancelButton, 1);
buttonPanel.Children().Append(primaryButton);
buttonPanel.Children().Append(cancelButton);
stackPanel.Children().Append(buttonPanel);
stackPanel.UpdateLayout();
// Configure the keyboardManagerState to store the UI information.

View File

@@ -68,21 +68,32 @@ 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.IsPrimaryButtonEnabled(false);
detectRemapKeyBox.IsSecondaryButtonEnabled(false);
detectRemapKeyBox.CloseButtonText(to_hstring(L"Cancel"));
// Get the linked text block for the "Type Key" button that was clicked
TextBlock linkedRemapText = getSiblingElement(sender).as<TextBlock>();
// OK button
detectRemapKeyBox.PrimaryButtonClick([=, &singleKeyRemapBuffer, &keyboardManagerState](Windows::UI::Xaml::Controls::ContentDialog const& sender, ContentDialogButtonClickEventArgs const&) {
auto unregisterKeys = [&keyboardManagerState]() {
std::thread t1(&KeyboardManagerState::UnregisterKeyDelay, &keyboardManagerState, VK_ESCAPE);
std::thread t2(&KeyboardManagerState::UnregisterKeyDelay, &keyboardManagerState, VK_RETURN);
t1.detach();
t2.detach();
};
auto onAccept = [linkedRemapText,
detectRemapKeyBox,
&keyboardManagerState,
&singleKeyRemapBuffer,
unregisterKeys,
rowIndex,
colIndex] {
// Save the detected key in the linked text block
DWORD detectedKey = keyboardManagerState.GetDetectedSingleRemapKey();
@@ -94,14 +105,69 @@ void SingleKeyRemapControl::createDetectKeyWindow(IInspectable const& sender, Xa
// Reset the keyboard manager UI state
keyboardManagerState.ResetUIState();
unregisterKeys();
detectRemapKeyBox.Hide();
};
TextBlock primaryButtonText;
primaryButtonText.Text(to_hstring(L"OK"));
Button primaryButton;
primaryButton.HorizontalAlignment(HorizontalAlignment::Stretch);
primaryButton.Margin({ 2, 2, 2, 2 });
primaryButton.Content(primaryButtonText);
primaryButton.Click([onAccept](IInspectable const& sender, RoutedEventArgs const&) {
onAccept();
});
keyboardManagerState.RegisterKeyDelay(
VK_RETURN,
std::bind(&KeyboardManagerState::SelectDetectedRemapKey, &keyboardManagerState, std::placeholders::_1),
[primaryButton, detectRemapKeyBox](DWORD) {
detectRemapKeyBox.Dispatcher().RunAsync(
Windows::UI::Core::CoreDispatcherPriority::Normal,
[primaryButton] {
primaryButton.Background(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::DarkGray() });
});
},
[onAccept, detectRemapKeyBox](DWORD) {
detectRemapKeyBox.Dispatcher().RunAsync(
Windows::UI::Core::CoreDispatcherPriority::Normal,
[onAccept] {
onAccept();
});
});
TextBlock cancelButtonText;
cancelButtonText.Text(to_hstring(L"Cancel"));
Button cancelButton;
cancelButton.HorizontalAlignment(HorizontalAlignment::Stretch);
cancelButton.Margin({ 2, 2, 2, 2 });
cancelButton.Content(cancelButtonText);
// Cancel button
detectRemapKeyBox.CloseButtonClick([&keyboardManagerState](Windows::UI::Xaml::Controls::ContentDialog const& sender, ContentDialogButtonClickEventArgs const&) {
cancelButton.Click([detectRemapKeyBox, unregisterKeys, &keyboardManagerState](IInspectable const& sender, RoutedEventArgs const&) {
// Reset the keyboard manager UI state
keyboardManagerState.ResetUIState();
unregisterKeys();
detectRemapKeyBox.Hide();
});
keyboardManagerState.RegisterKeyDelay(
VK_ESCAPE,
std::bind(&KeyboardManagerState::SelectDetectedRemapKey, &keyboardManagerState, std::placeholders::_1),
[&keyboardManagerState, detectRemapKeyBox, unregisterKeys](DWORD) {
detectRemapKeyBox.Dispatcher().RunAsync(
Windows::UI::Core::CoreDispatcherPriority::Normal,
[detectRemapKeyBox] {
detectRemapKeyBox.Hide();
});
keyboardManagerState.ResetUIState();
unregisterKeys();
},
nullptr);
// StackPanel parent for the displayed text in the dialog
Windows::UI::Xaml::Controls::StackPanel stackPanel;
detectRemapKeyBox.Content(stackPanel);
@@ -114,8 +180,36 @@ void SingleKeyRemapControl::createDetectKeyWindow(IInspectable const& sender, Xa
// Target StackPanel to place the selected key
Windows::UI::Xaml::Controls::StackPanel keyStackPanel;
stackPanel.Children().Append(keyStackPanel);
keyStackPanel.Orientation(Orientation::Horizontal);
stackPanel.Children().Append(keyStackPanel);
TextBlock holdEscInfo;
holdEscInfo.Text(winrt::to_hstring("Hold Esc to discard"));
holdEscInfo.FontSize(12);
holdEscInfo.Margin({ 0, 20, 0, 0 });
stackPanel.Children().Append(holdEscInfo);
TextBlock holdEnterInfo;
holdEnterInfo.Text(winrt::to_hstring("Hold Enter to apply"));
holdEnterInfo.FontSize(12);
holdEnterInfo.Margin({ 0, 0, 0, 0 });
stackPanel.Children().Append(holdEnterInfo);
ColumnDefinition primaryButtonColumn;
ColumnDefinition cancelButtonColumn;
Grid buttonPanel;
buttonPanel.Margin({ 0, 20, 0, 0 });
buttonPanel.HorizontalAlignment(HorizontalAlignment::Stretch);
buttonPanel.ColumnDefinitions().Append(primaryButtonColumn);
buttonPanel.ColumnDefinitions().Append(cancelButtonColumn);
buttonPanel.SetColumn(primaryButton, 0);
buttonPanel.SetColumn(cancelButton, 1);
buttonPanel.Children().Append(primaryButton);
buttonPanel.Children().Append(cancelButton);
stackPanel.Children().Append(buttonPanel);
stackPanel.UpdateLayout();
// Configure the keyboardManagerState to store the UI information.