mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-06-07 09:28:03 +08:00
[Keyboard Manager] Confirmation Dialog for orphaned keys and partial remappings. (#2811)
* WIP Confirmation dialog for orphaned keys * Confirmation Dialog for orphaned keys * White OK button, Anyways capitalizef * Change Apply to Ok for shortcuts * Validate that mappings can be made before changing keyboardManagerState * Set fixed MinWidth for OK button * Fix typo * Partial remappings confirmation dialog Both for Shortcuts and SingleKey * Remove warning icon callback in OnClickAccept * Add text wrapping for OrphanKeys dialog
This commit is contained in:
parent
c39be3dbc9
commit
8c04421387
@ -73,4 +73,7 @@ namespace KeyboardManagerConstants
|
||||
inline const long TableArrowColWidth = 20;
|
||||
inline const long TableRemoveColWidth = 20;
|
||||
inline const long TableWarningColWidth = 20;
|
||||
|
||||
// Shared style constants for both Remap Table and Shortcut Table
|
||||
inline const double HeaderButtonWidth = 100;
|
||||
}
|
17
src/modules/keyboardmanager/ui/Dialog.cpp
Normal file
17
src/modules/keyboardmanager/ui/Dialog.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
#include "pch.h"
|
||||
#include "Dialog.h"
|
||||
|
||||
IAsyncOperation<bool> Dialog::PartialRemappingConfirmationDialog(XamlRoot root)
|
||||
{
|
||||
ContentDialog confirmationDialog;
|
||||
confirmationDialog.XamlRoot(root);
|
||||
confirmationDialog.Title(box_value(L"Some of the keys could not be remapped. Do you want to continue anyway?"));
|
||||
confirmationDialog.IsPrimaryButtonEnabled(true);
|
||||
confirmationDialog.DefaultButton(ContentDialogButton::Primary);
|
||||
confirmationDialog.PrimaryButtonText(winrt::hstring(L"Continue Anyway"));
|
||||
confirmationDialog.IsSecondaryButtonEnabled(true);
|
||||
confirmationDialog.SecondaryButtonText(winrt::hstring(L"Cancel"));
|
||||
|
||||
ContentDialogResult res = co_await confirmationDialog.ShowAsync();
|
||||
co_return res == ContentDialogResult::Primary;
|
||||
}
|
41
src/modules/keyboardmanager/ui/Dialog.h
Normal file
41
src/modules/keyboardmanager/ui/Dialog.h
Normal file
@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <keyboardmanager/common/Helpers.h>
|
||||
#include <set>
|
||||
#include <winrt/Windows.UI.Xaml.h>
|
||||
|
||||
using namespace winrt::Windows::Foundation;
|
||||
|
||||
namespace Dialog
|
||||
{
|
||||
template<typename T>
|
||||
KeyboardManagerHelper::ErrorType CheckIfRemappingsAreValid(
|
||||
const std::vector<std::vector<T>>& remappings,
|
||||
std::function<bool(T)> isValid)
|
||||
{
|
||||
KeyboardManagerHelper::ErrorType isSuccess = KeyboardManagerHelper::ErrorType::NoError;
|
||||
std::set<T> ogKeys;
|
||||
for (int i = 0; i < remappings.size(); i++)
|
||||
{
|
||||
T ogKey = remappings[i][0];
|
||||
T newKey = remappings[i][1];
|
||||
|
||||
if (isValid(ogKey) && isValid(newKey) && ogKeys.find(ogKey) == ogKeys.end())
|
||||
{
|
||||
ogKeys.insert(ogKey);
|
||||
}
|
||||
else if (isValid(ogKey) && isValid(newKey) && ogKeys.find(ogKey) != ogKeys.end())
|
||||
{
|
||||
isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful;
|
||||
}
|
||||
else
|
||||
{
|
||||
isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful;
|
||||
}
|
||||
}
|
||||
return isSuccess;
|
||||
}
|
||||
|
||||
IAsyncOperation<bool> PartialRemappingConfirmationDialog(winrt::Windows::UI::Xaml::XamlRoot root);
|
||||
};
|
@ -4,6 +4,13 @@
|
||||
#include "KeyDropDownControl.h"
|
||||
#include "XamlBridge.h"
|
||||
#include <keyboardmanager/common/trace.h>
|
||||
#include <keyboardmanager/common/KeyboardManagerConstants.h>
|
||||
#include <set>
|
||||
#include <common/windows_colors.h>
|
||||
#include "Styles.h"
|
||||
#include "Dialog.h"
|
||||
|
||||
using namespace winrt::Windows::Foundation;
|
||||
|
||||
LRESULT CALLBACK EditKeyboardWindowProc(HWND, UINT, WPARAM, LPARAM);
|
||||
|
||||
@ -17,6 +24,88 @@ std::mutex editKeyboardWindowMutex;
|
||||
// Stores a pointer to the Xaml Bridge object so that it can be accessed from the window procedure
|
||||
static XamlBridge* xamlBridgePtr = nullptr;
|
||||
|
||||
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();
|
||||
}
|
||||
// Function to create the Edit Keyboard Window
|
||||
void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState)
|
||||
{
|
||||
@ -100,7 +189,7 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
||||
// Header Cancel button
|
||||
Button cancelButton;
|
||||
cancelButton.Content(winrt::box_value(L"Cancel"));
|
||||
cancelButton.Margin({ 0, 0, 10, 0 });
|
||||
cancelButton.Margin({ 10, 0, 0, 0 });
|
||||
cancelButton.Click([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
// Close the window since settings do not need to be saved
|
||||
PostMessage(_hWndEditKeyboardWindow, WM_CLOSE, 0, 0);
|
||||
@ -159,11 +248,6 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
||||
keyRemapTable.Children().Append(originalKeyRemapHeader);
|
||||
keyRemapTable.Children().Append(newKeyRemapHeader);
|
||||
|
||||
// Message to display success/failure of saving settings.
|
||||
Flyout applyFlyout;
|
||||
TextBlock settingsMessage;
|
||||
applyFlyout.Content(settingsMessage);
|
||||
|
||||
// Store handle of edit keyboard window
|
||||
SingleKeyRemapControl::EditKeyboardWindowHandle = _hWndEditKeyboardWindow;
|
||||
// Store keyboard manager state
|
||||
@ -231,11 +315,14 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
||||
|
||||
// Main Header Apply button
|
||||
Button applyButton;
|
||||
applyButton.Content(winrt::box_value(L"Apply"));
|
||||
header.SetAlignRightWithPanel(applyButton, true);
|
||||
header.SetLeftOf(cancelButton, applyButton);
|
||||
applyButton.Flyout(applyFlyout);
|
||||
applyButton.Click([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
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]() {
|
||||
KeyboardManagerHelper::ErrorType isSuccess = KeyboardManagerHelper::ErrorType::NoError;
|
||||
// Clear existing Key Remaps
|
||||
keyboardManagerState.ClearSingleKeyRemaps();
|
||||
@ -292,14 +379,19 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
||||
}
|
||||
}
|
||||
|
||||
Trace::KeyRemapCount(successfulRemapCount);
|
||||
// Save the updated shortcuts remaps to file.
|
||||
bool saveResult = keyboardManagerState.SaveConfigToFile();
|
||||
if (!saveResult)
|
||||
{
|
||||
isSuccess = KeyboardManagerHelper::ErrorType::SaveFailed;
|
||||
}
|
||||
Trace::KeyRemapCount(successfulRemapCount);
|
||||
settingsMessage.Text(KeyboardManagerHelper::GetErrorMessage(isSuccess));
|
||||
|
||||
PostMessage(_hWndEditKeyboardWindow, WM_CLOSE, 0, 0);
|
||||
};
|
||||
|
||||
applyButton.Click([&keyboardManagerState, ApplyRemappings, applyButton](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
OnClickAccept(keyboardManagerState, applyButton.XamlRoot(), ApplyRemappings);
|
||||
});
|
||||
|
||||
header.Children().Append(headerText);
|
||||
|
@ -4,6 +4,12 @@
|
||||
#include "KeyDropDownControl.h"
|
||||
#include "XamlBridge.h"
|
||||
#include <keyboardmanager/common/trace.h>
|
||||
#include <keyboardmanager/common/KeyboardManagerConstants.h>
|
||||
#include <common/windows_colors.h>
|
||||
#include "Styles.h"
|
||||
#include "Dialog.h"
|
||||
|
||||
using namespace winrt::Windows::Foundation;
|
||||
|
||||
LRESULT CALLBACK EditShortcutsWindowProc(HWND, UINT, WPARAM, LPARAM);
|
||||
|
||||
@ -17,6 +23,26 @@ std::mutex editShortcutsWindowMutex;
|
||||
// Stores a pointer to the Xaml Bridge object so that it can be accessed from the window procedure
|
||||
static XamlBridge* xamlBridgePtr = nullptr;
|
||||
|
||||
static IAsyncAction OnClickAccept(
|
||||
KeyboardManagerState& keyboardManagerState,
|
||||
XamlRoot root,
|
||||
std::function<void()> ApplyRemappings)
|
||||
{
|
||||
KeyboardManagerHelper::ErrorType isSuccess = Dialog::CheckIfRemappingsAreValid<Shortcut>(
|
||||
ShortcutControl::shortcutRemapBuffer,
|
||||
[](Shortcut shortcut) {
|
||||
return shortcut.IsValidShortcut();
|
||||
});
|
||||
if (isSuccess != KeyboardManagerHelper::ErrorType::NoError)
|
||||
{
|
||||
if (!co_await Dialog::PartialRemappingConfirmationDialog(root))
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
}
|
||||
ApplyRemappings();
|
||||
}
|
||||
|
||||
// Function to create the Edit Shortcuts Window
|
||||
void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState)
|
||||
{
|
||||
@ -101,7 +127,7 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
|
||||
// Cancel button
|
||||
Button cancelButton;
|
||||
cancelButton.Content(winrt::box_value(L"Cancel"));
|
||||
cancelButton.Margin({ 0, 0, 10, 0 });
|
||||
cancelButton.Margin({ 10, 0, 0, 0 });
|
||||
cancelButton.Click([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
// Close the window since settings do not need to be saved
|
||||
PostMessage(_hWndEditShortcutsWindow, WM_CLOSE, 0, 0);
|
||||
@ -161,11 +187,6 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
|
||||
shortcutTable.Children().Append(originalShortcutHeader);
|
||||
shortcutTable.Children().Append(newShortcutHeader);
|
||||
|
||||
// Message to display success/failure of saving settings.
|
||||
Flyout applyFlyout;
|
||||
TextBlock settingsMessage;
|
||||
applyFlyout.Content(settingsMessage);
|
||||
|
||||
// Store handle of edit shortcuts window
|
||||
ShortcutControl::EditShortcutsWindowHandle = _hWndEditShortcutsWindow;
|
||||
// Store keyboard manager state
|
||||
@ -189,11 +210,14 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
|
||||
|
||||
// Apply button
|
||||
Button applyButton;
|
||||
applyButton.Content(winrt::box_value(L"Apply"));
|
||||
header.SetAlignRightWithPanel(applyButton, true);
|
||||
header.SetLeftOf(cancelButton, applyButton);
|
||||
applyButton.Flyout(applyFlyout);
|
||||
applyButton.Click([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
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, _hWndEditShortcutsWindow]() {
|
||||
KeyboardManagerHelper::ErrorType isSuccess = KeyboardManagerHelper::ErrorType::NoError;
|
||||
// Clear existing shortcuts
|
||||
keyboardManagerState.ClearOSLevelShortcuts();
|
||||
@ -223,14 +247,19 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
|
||||
}
|
||||
}
|
||||
|
||||
Trace::OSLevelShortcutRemapCount(successfulRemapCount);
|
||||
// Save the updated key remaps to file.
|
||||
bool saveResult = keyboardManagerState.SaveConfigToFile();
|
||||
if (!saveResult)
|
||||
{
|
||||
isSuccess = KeyboardManagerHelper::ErrorType::SaveFailed;
|
||||
}
|
||||
Trace::OSLevelShortcutRemapCount(successfulRemapCount);
|
||||
settingsMessage.Text(KeyboardManagerHelper::GetErrorMessage(isSuccess));
|
||||
|
||||
PostMessage(_hWndEditShortcutsWindow, WM_CLOSE, 0, 0);
|
||||
};
|
||||
|
||||
applyButton.Click([&keyboardManagerState, applyButton, ApplyRemappings](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
OnClickAccept(keyboardManagerState, applyButton.XamlRoot(), ApplyRemappings);
|
||||
});
|
||||
|
||||
header.Children().Append(headerText);
|
||||
|
@ -98,6 +98,7 @@
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Dialog.cpp" />
|
||||
<ClCompile Include="EditKeyboardWindow.cpp" />
|
||||
<ClCompile Include="EditShortcutsWindow.cpp" />
|
||||
<ClCompile Include="KeyDropDownControl.cpp" />
|
||||
@ -107,11 +108,14 @@
|
||||
</ClCompile>
|
||||
<ClCompile Include="ShortcutControl.cpp" />
|
||||
<ClCompile Include="SingleKeyRemapControl.cpp" />
|
||||
<ClCompile Include="Styles.cpp" />
|
||||
<ClCompile Include="XamlBridge.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Dialog.h" />
|
||||
<ClInclude Include="EditKeyboardWindow.h" />
|
||||
<ClInclude Include="EditShortcutsWindow.h" />
|
||||
<ClInclude Include="Styles.h" />
|
||||
<ClInclude Include="KeyDropDownControl.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="ShortcutControl.h" />
|
||||
|
@ -8,6 +8,8 @@
|
||||
<ClCompile Include="SingleKeyRemapControl.cpp" />
|
||||
<ClCompile Include="KeyDropDownControl.cpp" />
|
||||
<ClCompile Include="XamlBridge.cpp" />
|
||||
<ClCompile Include="Styles.cpp" />
|
||||
<ClCompile Include="Dialog.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="EditKeyboardWindow.h" />
|
||||
@ -17,6 +19,8 @@
|
||||
<ClInclude Include="SingleKeyRemapControl.h" />
|
||||
<ClInclude Include="KeyDropDownControl.h" />
|
||||
<ClInclude Include="XamlBridge.h" />
|
||||
<ClInclude Include="Styles.h" />
|
||||
<ClInclude Include="Dialog.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
17
src/modules/keyboardmanager/ui/Styles.cpp
Normal file
17
src/modules/keyboardmanager/ui/Styles.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
#include "pch.h"
|
||||
#include "Styles.h"
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.UI.Xaml.Interop.h>
|
||||
#include <common/windows_colors.h>
|
||||
|
||||
Style AccentButtonStyle()
|
||||
{
|
||||
Style style{ winrt::xaml_typename<Controls::Button>() };
|
||||
style.Setters().Append(Setter{
|
||||
Controls::Control::BackgroundProperty(),
|
||||
winrt::Windows::UI::Xaml::Media::SolidColorBrush{ WindowsColors::get_accent_color() } });
|
||||
style.Setters().Append(Setter{
|
||||
Controls::Control::ForegroundProperty(),
|
||||
winrt::Windows::UI::Xaml::Media::SolidColorBrush{ winrt::Windows::UI::Colors::White() } });
|
||||
return style;
|
||||
}
|
6
src/modules/keyboardmanager/ui/Styles.h
Normal file
6
src/modules/keyboardmanager/ui/Styles.h
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
#include <winrt/Windows.UI.Xaml.h>
|
||||
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
|
||||
Style AccentButtonStyle();
|
Loading…
Reference in New Issue
Block a user