Improve warnings for KBM (dev/build-features) (#2386)

* Handled invalid input for single key remaps

* Added some functions

* Added better error message functions and finished warnings for edit keyboard

* Updated dropdown order of keys

* Added new warning template for shortcuts

* Added show warning on Apply code

* Removed old flyout code

* Fixed issue where tooltips were replaced on Apply

* Simplified == operator
This commit is contained in:
Arjun Balgovind 2020-04-26 15:09:40 -07:00 committed by GitHub
parent 7ec8d02c1f
commit 38ecc82e97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 439 additions and 125 deletions

View File

@ -215,19 +215,6 @@ std::vector<DWORD> LayoutMap::LayoutMapImpl::GetKeyCodeList(const bool isShortcu
std::vector<DWORD> keyCodes; std::vector<DWORD> keyCodes;
if (!isKeyCodeListGenerated) if (!isKeyCodeListGenerated)
{ {
// Add modifier keys
keyCodes.push_back(CommonSharedConstants::VK_WIN_BOTH);
keyCodes.push_back(VK_LWIN);
keyCodes.push_back(VK_RWIN);
keyCodes.push_back(VK_CONTROL);
keyCodes.push_back(VK_LCONTROL);
keyCodes.push_back(VK_RCONTROL);
keyCodes.push_back(VK_MENU);
keyCodes.push_back(VK_LMENU);
keyCodes.push_back(VK_RMENU);
keyCodes.push_back(VK_SHIFT);
keyCodes.push_back(VK_LSHIFT);
keyCodes.push_back(VK_RSHIFT);
// Add character keys // Add character keys
for (auto& it : unicodeKeys) for (auto& it : unicodeKeys)
{ {
@ -237,7 +224,23 @@ std::vector<DWORD> LayoutMap::LayoutMapImpl::GetKeyCodeList(const bool isShortcu
keyCodes.push_back(it.first); keyCodes.push_back(it.first);
} }
} }
// Add modifier keys in alphabetical order
keyCodes.push_back(VK_MENU);
keyCodes.push_back(VK_LMENU);
keyCodes.push_back(VK_RMENU);
keyCodes.push_back(VK_CONTROL);
keyCodes.push_back(VK_LCONTROL);
keyCodes.push_back(VK_RCONTROL);
keyCodes.push_back(VK_SHIFT);
keyCodes.push_back(VK_LSHIFT);
keyCodes.push_back(VK_RSHIFT);
keyCodes.push_back(CommonSharedConstants::VK_WIN_BOTH);
keyCodes.push_back(VK_LWIN);
keyCodes.push_back(VK_RWIN);
// Add all other special keys // Add all other special keys
std::vector<DWORD> specialKeys;
for (int i = 1; i < 256; i++) for (int i = 1; i < 256; i++)
{ {
// If it is not already been added (i.e. it was either a modifier or had a unicode representation) // If it is not already been added (i.e. it was either a modifier or had a unicode representation)
@ -247,14 +250,24 @@ std::vector<DWORD> LayoutMap::LayoutMapImpl::GetKeyCodeList(const bool isShortcu
auto it = unknownKeys.find(i); auto it = unknownKeys.find(i);
if (it == unknownKeys.end()) if (it == unknownKeys.end())
{ {
keyCodes.push_back(i); specialKeys.push_back(i);
} }
else if (unknownKeys[i] != keyboardLayoutMap[i]) else if (unknownKeys[i] != keyboardLayoutMap[i])
{ {
keyCodes.push_back(i); specialKeys.push_back(i);
} }
} }
} }
// Sort the special keys in alphabetical order
std::sort(specialKeys.begin(), specialKeys.end(), [&](const DWORD& lhs, const DWORD& rhs) {
return keyboardLayoutMap[lhs] < keyboardLayoutMap[rhs];
});
for (int i = 0; i < specialKeys.size(); i++)
{
keyCodes.push_back(specialKeys[i]);
}
// Add unknown keys // Add unknown keys
for (auto& it : unknownKeys) for (auto& it : unknownKeys)
{ {
@ -285,20 +298,21 @@ std::vector<std::wstring> LayoutMap::LayoutMapImpl::GetKeyNameList(const bool is
{ {
std::vector<std::wstring> keyNames; std::vector<std::wstring> keyNames;
std::vector<DWORD> keyCodes = GetKeyCodeList(isShortcut); std::vector<DWORD> keyCodes = GetKeyCodeList(isShortcut);
std::lock_guard<std::mutex> lock(keyboardLayoutMap_mutex);
// If it is a key list for the shortcut control then we add a "None" key at the start // If it is a key list for the shortcut control then we add a "None" key at the start
if (isShortcut) if (isShortcut)
{ {
keyNames.push_back(L"None"); keyNames.push_back(L"None");
for (int i = 1; i < keyCodes.size(); i++) for (int i = 1; i < keyCodes.size(); i++)
{ {
keyNames.push_back(GetKeyName(keyCodes[i])); keyNames.push_back(keyboardLayoutMap[keyCodes[i]]);
} }
} }
else else
{ {
for (int i = 0; i < keyCodes.size(); i++) for (int i = 0; i < keyCodes.size(); i++)
{ {
keyNames.push_back(GetKeyName(keyCodes[i])); keyNames.push_back(keyboardLayoutMap[keyCodes[i]]);
} }
} }

View File

@ -65,7 +65,7 @@ namespace KeyboardManagerHelper
} }
// Function to return if the key is an extended key which requires the use of the extended key flag // Function to return if the key is an extended key which requires the use of the extended key flag
bool isExtendedKey(DWORD key) bool IsExtendedKey(DWORD key)
{ {
switch (key) switch (key)
{ {
@ -79,6 +79,7 @@ namespace KeyboardManagerHelper
return false; return false;
} }
} }
Collections::IVector<IInspectable> ToBoxValue(const std::vector<std::wstring>& list) Collections::IVector<IInspectable> ToBoxValue(const std::vector<std::wstring>& list)
{ {
Collections::IVector<IInspectable> boxList = single_threaded_vector<IInspectable>(); Collections::IVector<IInspectable> boxList = single_threaded_vector<IInspectable>();
@ -89,4 +90,73 @@ namespace KeyboardManagerHelper
return boxList; return boxList;
} }
// Function to check if two keys are equal or cover the same set of keys. Return value depends on type of overlap
ErrorType DoKeysOverlap(DWORD first, DWORD second)
{
// If the keys are same
if (first == second)
{
return ErrorType::SameKeyPreviouslyMapped;
}
else if ((GetKeyType(first) == GetKeyType(second)) && GetKeyType(first) != KeyType::Action)
{
// If the keys are of the same modifier type and overlapping, i.e. one is L/R and other is common
if ((first == VK_LWIN && second == VK_RWIN) || (first == VK_LCONTROL && second == VK_RCONTROL) || (first == VK_LMENU && second == VK_RMENU) || (first == VK_LSHIFT && second == VK_RSHIFT))
{
return ErrorType::NoError;
}
else
{
return ErrorType::ConflictingModifierKey;
}
}
// If no overlap
else
{
return ErrorType::NoError;
}
}
// Function to return the error message
winrt::hstring GetErrorMessage(ErrorType errorType)
{
switch (errorType)
{
case ErrorType::NoError:
return L"Remapping successful";
case ErrorType::SameKeyPreviouslyMapped:
return L"Cannot remap a key more than once";
case ErrorType::MapToSameKey:
return L"Cannot remap a key to itself";
case ErrorType::ConflictingModifierKey:
return L"Cannot remap this key as it conflicts with another remapped key";
case ErrorType::SameShortcutPreviouslyMapped:
return L"Cannot remap a shortcut more than once";
case ErrorType::MapToSameShortcut:
return L"Cannot remap a shortcut to itself";
case ErrorType::ConflictingModifierShortcut:
return L"Cannot remap this shortcut as it conflicts with another remapped shortcut";
case ErrorType::WinL:
return L"Cannot remap from/to Win L";
case ErrorType::CtrlAltDel:
return L"Cannot remap from/to Ctrl Alt Del";
case ErrorType::RemapUnsuccessful:
return L"Some remappings were not applied";
case ErrorType::SaveFailed:
return L"Failed to save the remappings";
case ErrorType::MissingKey:
return L"Incomplete remapping";
case ErrorType::ShortcutStartWithModifier:
return L"Shortcut must start with a modifier key";
case ErrorType::ShortcutCannotHaveRepeatedModifier:
return L"Shortcut cannot contain a repeated modifier";
case ErrorType::ShortcutAtleast2Keys:
return L"Shortcut must have atleast 2 keys";
case ErrorType::ShortcutOneActionKey:
return L"Shortcut must contain an action key";
case ErrorType::ShortcutNotMoreThanOneActionKey:
return L"Shortcut cannot have more than one action key";
}
}
} }

View File

@ -15,6 +15,28 @@ namespace KeyboardManagerHelper
Action Action
}; };
// Type to store codes for different errors
enum class ErrorType
{
NoError,
SameKeyPreviouslyMapped,
MapToSameKey,
ConflictingModifierKey,
SameShortcutPreviouslyMapped,
MapToSameShortcut,
ConflictingModifierShortcut,
WinL,
CtrlAltDel,
RemapUnsuccessful,
SaveFailed,
MissingKey,
ShortcutStartWithModifier,
ShortcutCannotHaveRepeatedModifier,
ShortcutAtleast2Keys,
ShortcutOneActionKey,
ShortcutNotMoreThanOneActionKey
};
// Function to split a wstring based on a delimiter and return a vector of split strings // Function to split a wstring based on a delimiter and return a vector of split strings
std::vector<std::wstring> splitwstring(const std::wstring& input, wchar_t delimiter); std::vector<std::wstring> splitwstring(const std::wstring& input, wchar_t delimiter);
@ -22,7 +44,7 @@ namespace KeyboardManagerHelper
winrt::Windows::Foundation::IInspectable getSiblingElement(winrt::Windows::Foundation::IInspectable const& element); winrt::Windows::Foundation::IInspectable getSiblingElement(winrt::Windows::Foundation::IInspectable const& element);
// Function to return if the key is an extended key which requires the use of the extended key flag // Function to return if the key is an extended key which requires the use of the extended key flag
bool isExtendedKey(DWORD key); bool IsExtendedKey(DWORD key);
// Function to check if the key is a modifier key // Function to check if the key is a modifier key
bool IsModifierKey(DWORD key); bool IsModifierKey(DWORD key);
@ -30,8 +52,11 @@ namespace KeyboardManagerHelper
// Function to get the type of the key // Function to get the type of the key
KeyType GetKeyType(DWORD key); KeyType GetKeyType(DWORD key);
// Function to return if the key is an extended key which requires the use of the extended key flag // Function to check if two keys are equal or cover the same set of keys. Return value depends on type of overlap
bool isExtendedKey(DWORD key); ErrorType DoKeysOverlap(DWORD first, DWORD second);
// Function to return the error message
winrt::hstring GetErrorMessage(ErrorType errorType);
// Function to return the list of key name in the order for the drop down based on the key codes // Function to return the list of key name in the order for the drop down based on the key codes
winrt::Windows::Foundation::Collections::IVector<winrt::Windows::Foundation::IInspectable> ToBoxValue(const std::vector<std::wstring>& list); winrt::Windows::Foundation::Collections::IVector<winrt::Windows::Foundation::IInspectable> ToBoxValue(const std::vector<std::wstring>& list);

View File

@ -35,4 +35,7 @@ namespace KeyboardManagerConstants
// Name of the dummy update file. // Name of the dummy update file.
inline const std::wstring DummyUpdateFileName = L"settings-updated.json"; inline const std::wstring DummyUpdateFileName = L"settings-updated.json";
// Initial value for tooltip
inline const winrt::hstring ToolTipInitialContent = L"Initialised";
} }

View File

@ -764,3 +764,56 @@ int Shortcut::GetCommonModifiersCount(const Shortcut& input) const
return commonElements; return commonElements;
} }
// Function to check if the two shortcuts are equal or cover the same set of keys. Return value depends on type of overlap
KeyboardManagerHelper::ErrorType Shortcut::DoKeysOverlap(const Shortcut& first, const Shortcut& second)
{
if (first.IsValidShortcut() && second.IsValidShortcut())
{
// If the shortcuts are equal
if (first == second)
{
return KeyboardManagerHelper::ErrorType::SameShortcutPreviouslyMapped;
}
// If both have win key modifiers and one is the both version then there will be an overlap
else if (first.winKey != ModifierKey::Disabled && second.winKey != ModifierKey::Disabled && (first.winKey == ModifierKey::Both || second.winKey == ModifierKey::Both))
{
return KeyboardManagerHelper::ErrorType::ConflictingModifierShortcut;
}
// If both have ctrl key modifiers and one is the both version then there will be an overlap
else if (first.ctrlKey != ModifierKey::Disabled && second.ctrlKey != ModifierKey::Disabled && (first.ctrlKey == ModifierKey::Both || second.ctrlKey == ModifierKey::Both))
{
return KeyboardManagerHelper::ErrorType::ConflictingModifierShortcut;
}
// If both have alt key modifiers and one is the both version then there will be an overlap
else if (first.altKey != ModifierKey::Disabled && second.altKey != ModifierKey::Disabled && (first.altKey == ModifierKey::Both || second.altKey == ModifierKey::Both))
{
return KeyboardManagerHelper::ErrorType::ConflictingModifierShortcut;
}
// If both have shift key modifiers and one is the both version then there will be an overlap
else if (first.shiftKey != ModifierKey::Disabled && second.shiftKey != ModifierKey::Disabled && (first.shiftKey == ModifierKey::Both || second.shiftKey == ModifierKey::Both))
{
return KeyboardManagerHelper::ErrorType::ConflictingModifierShortcut;
}
}
return KeyboardManagerHelper::ErrorType::NoError;
}
// Function to check if the shortcut is illegal (i.e. Win+L or Ctrl+Alt+Del)
KeyboardManagerHelper::ErrorType Shortcut::IsShortcutIllegal() const
{
// Win+L
if (winKey != ModifierKey::Disabled && ctrlKey == ModifierKey::Disabled && altKey == ModifierKey::Disabled && shiftKey == ModifierKey::Disabled && actionKey == 0x4C)
{
return KeyboardManagerHelper::ErrorType::WinL;
}
// Ctrl+Alt+Del
if (winKey == ModifierKey::Disabled && ctrlKey != ModifierKey::Disabled && altKey != ModifierKey::Disabled && shiftKey == ModifierKey::Disabled && actionKey == VK_DELETE)
{
return KeyboardManagerHelper::ErrorType::CtrlAltDel;
}
return KeyboardManagerHelper::ErrorType::NoError;
}

View File

@ -41,6 +41,12 @@ public:
} }
} }
// == operator
inline bool operator==(const Shortcut& sc) const
{
return (winKey == sc.winKey && ctrlKey == sc.ctrlKey && altKey == sc.altKey && shiftKey == sc.shiftKey && actionKey == sc.actionKey);
}
// Less than operator must be defined to use with std::map. // Less than operator must be defined to use with std::map.
inline bool operator<(const Shortcut& sc) const inline bool operator<(const Shortcut& sc) const
{ {
@ -168,4 +174,10 @@ public:
// Function to get the number of modifiers that are common between the current shortcut and the shortcut in the argument // Function to get the number of modifiers that are common between the current shortcut and the shortcut in the argument
int GetCommonModifiersCount(const Shortcut& input) const; int GetCommonModifiersCount(const Shortcut& input) const;
// Function to check if the two shortcuts are equal or cover the same set of keys. Return value depends on type of overlap
static KeyboardManagerHelper::ErrorType DoKeysOverlap(const Shortcut& first, const Shortcut& second);
// Function to check if the shortcut is illegal (i.e. Win+L or Ctrl+Alt+Del)
KeyboardManagerHelper::ErrorType IsShortcutIllegal() const;
}; };

View File

@ -396,7 +396,7 @@ public:
keyEventArray[index].type = inputType; keyEventArray[index].type = inputType;
keyEventArray[index].ki.wVk = keyCode; keyEventArray[index].ki.wVk = keyCode;
keyEventArray[index].ki.dwFlags = flags; keyEventArray[index].ki.dwFlags = flags;
if (KeyboardManagerHelper::isExtendedKey(keyCode)) if (KeyboardManagerHelper::IsExtendedKey(keyCode))
{ {
keyEventArray[index].ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; keyEventArray[index].ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
} }

View File

@ -106,12 +106,16 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
ColumnDefinition firstColumn; ColumnDefinition firstColumn;
ColumnDefinition secondColumn; ColumnDefinition secondColumn;
ColumnDefinition thirdColumn; ColumnDefinition thirdColumn;
thirdColumn.MaxWidth(100);
ColumnDefinition fourthColumn;
fourthColumn.MaxWidth(100);
keyRemapTable.Margin({ 10, 10, 10, 20 }); keyRemapTable.Margin({ 10, 10, 10, 20 });
keyRemapTable.HorizontalAlignment(HorizontalAlignment::Stretch); keyRemapTable.HorizontalAlignment(HorizontalAlignment::Stretch);
keyRemapTable.ColumnSpacing(10); keyRemapTable.ColumnSpacing(10);
keyRemapTable.ColumnDefinitions().Append(firstColumn); keyRemapTable.ColumnDefinitions().Append(firstColumn);
keyRemapTable.ColumnDefinitions().Append(secondColumn); keyRemapTable.ColumnDefinitions().Append(secondColumn);
keyRemapTable.ColumnDefinitions().Append(thirdColumn); keyRemapTable.ColumnDefinitions().Append(thirdColumn);
keyRemapTable.ColumnDefinitions().Append(fourthColumn);
keyRemapTable.RowDefinitions().Append(RowDefinition()); keyRemapTable.RowDefinitions().Append(RowDefinition());
// First header textblock in the header row of the keys remap table // First header textblock in the header row of the keys remap table
@ -208,7 +212,7 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
header.SetLeftOf(cancelButton, applyButton); header.SetLeftOf(cancelButton, applyButton);
applyButton.Flyout(applyFlyout); applyButton.Flyout(applyFlyout);
applyButton.Click([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { applyButton.Click([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
bool isSuccess = true; KeyboardManagerHelper::ErrorType isSuccess = KeyboardManagerHelper::ErrorType::NoError;
// Clear existing Key Remaps // Clear existing Key Remaps
keyboardManagerState.ClearSingleKeyRemaps(); keyboardManagerState.ClearSingleKeyRemaps();
@ -250,30 +254,31 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
if (!result) if (!result)
{ {
isSuccess = false; isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful;
// Tooltip is already shown for this row
} }
} }
else else
{ {
isSuccess = false; isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful;
// Show tooltip warning on the problematic row
uint32_t warningIndex;
// 2 at start, 4 in each row, and last element of each row
warningIndex = 1 + (i + 1) * 4;
FontIcon warning = keyRemapTable.Children().GetAt(warningIndex).as<FontIcon>();
ToolTip t = ToolTipService::GetToolTip(warning).as<ToolTip>();
t.Content(box_value(KeyboardManagerHelper::GetErrorMessage(KeyboardManagerHelper::ErrorType::MissingKey)));
warning.Visibility(Visibility::Visible);
} }
} }
// Save the updated shortcuts remaps to file. // Save the updated shortcuts remaps to file.
auto saveResult = keyboardManagerState.SaveConfigToFile(); bool saveResult = keyboardManagerState.SaveConfigToFile();
if (!saveResult)
if (isSuccess && saveResult)
{ {
settingsMessage.Text(L"Remapping successful!"); isSuccess = KeyboardManagerHelper::ErrorType::SaveFailed;
}
else if (!isSuccess && saveResult)
{
settingsMessage.Text(L"All remappings were not successfully applied.");
}
else
{
settingsMessage.Text(L"Failed to save the remappings.");
} }
settingsMessage.Text(KeyboardManagerHelper::GetErrorMessage(isSuccess));
}); });
header.Children().Append(headerText); header.Children().Append(headerText);

View File

@ -102,12 +102,16 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
ColumnDefinition firstColumn; ColumnDefinition firstColumn;
ColumnDefinition secondColumn; ColumnDefinition secondColumn;
ColumnDefinition thirdColumn; ColumnDefinition thirdColumn;
thirdColumn.MaxWidth(100);
ColumnDefinition fourthColumn;
fourthColumn.MaxWidth(100);
shortcutTable.Margin({ 10, 10, 10, 20 }); shortcutTable.Margin({ 10, 10, 10, 20 });
shortcutTable.HorizontalAlignment(HorizontalAlignment::Stretch); shortcutTable.HorizontalAlignment(HorizontalAlignment::Stretch);
shortcutTable.ColumnSpacing(10); shortcutTable.ColumnSpacing(10);
shortcutTable.ColumnDefinitions().Append(firstColumn); shortcutTable.ColumnDefinitions().Append(firstColumn);
shortcutTable.ColumnDefinitions().Append(secondColumn); shortcutTable.ColumnDefinitions().Append(secondColumn);
shortcutTable.ColumnDefinitions().Append(thirdColumn); shortcutTable.ColumnDefinitions().Append(thirdColumn);
shortcutTable.ColumnDefinitions().Append(fourthColumn);
shortcutTable.RowDefinitions().Append(RowDefinition()); shortcutTable.RowDefinitions().Append(RowDefinition());
// First header textblock in the header row of the shortcut table // First header textblock in the header row of the shortcut table
@ -123,8 +127,8 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
newShortcutHeader.Margin({ 0, 0, 0, 10 }); newShortcutHeader.Margin({ 0, 0, 0, 10 });
shortcutTable.SetColumn(originalShortcutHeader, 0); shortcutTable.SetColumn(originalShortcutHeader, 0);
shortcutTable.SetRow(newShortcutHeader, 0); shortcutTable.SetRow(originalShortcutHeader, 0);
shortcutTable.SetColumn(originalShortcutHeader, 1); shortcutTable.SetColumn(newShortcutHeader, 1);
shortcutTable.SetRow(newShortcutHeader, 0); shortcutTable.SetRow(newShortcutHeader, 0);
shortcutTable.Children().Append(originalShortcutHeader); shortcutTable.Children().Append(originalShortcutHeader);
@ -160,7 +164,7 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
header.SetLeftOf(cancelButton, applyButton); header.SetLeftOf(cancelButton, applyButton);
applyButton.Flyout(applyFlyout); applyButton.Flyout(applyFlyout);
applyButton.Click([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { applyButton.Click([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
bool isSuccess = true; KeyboardManagerHelper::ErrorType isSuccess = KeyboardManagerHelper::ErrorType::NoError;
// Clear existing shortcuts // Clear existing shortcuts
keyboardManagerState.ClearOSLevelShortcuts(); keyboardManagerState.ClearOSLevelShortcuts();
@ -175,30 +179,31 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
bool result = keyboardManagerState.AddOSLevelShortcut(originalShortcut, newShortcut); bool result = keyboardManagerState.AddOSLevelShortcut(originalShortcut, newShortcut);
if (!result) if (!result)
{ {
isSuccess = false; isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful;
// Tooltip is already shown for this row
} }
} }
else else
{ {
isSuccess = false; isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful;
// Show tooltip warning on the problematic row
uint32_t warningIndex;
// 2 at start, 4 in each row, and last element of each row
warningIndex = 1 + (i + 1) * 4;
FontIcon warning = shortcutTable.Children().GetAt(warningIndex).as<FontIcon>();
ToolTip t = ToolTipService::GetToolTip(warning).as<ToolTip>();
t.Content(box_value(KeyboardManagerHelper::GetErrorMessage(KeyboardManagerHelper::ErrorType::MissingKey)));
warning.Visibility(Visibility::Visible);
} }
} }
// Save the updated key remaps to file. // Save the updated key remaps to file.
auto saveResult = keyboardManagerState.SaveConfigToFile(); bool saveResult = keyboardManagerState.SaveConfigToFile();
if (!saveResult)
if (isSuccess && saveResult)
{ {
settingsMessage.Text(L"Remapping successful!"); isSuccess = KeyboardManagerHelper::ErrorType::SaveFailed;
}
else if (!isSuccess && saveResult)
{
settingsMessage.Text(L"All remappings were not successfully applied.");
}
else
{
settingsMessage.Text(L"Failed to save the remappings.");
} }
settingsMessage.Text(KeyboardManagerHelper::GetErrorMessage(isSuccess));
}); });
header.Children().Append(headerText); header.Children().Append(headerText);

View File

@ -37,8 +37,9 @@ void KeyDropDownControl::CheckAndUpdateKeyboardLayout(ComboBox currentDropDown,
} }
// Function to set selection handler for single key remap drop down. Needs to be called after the constructor since the singleKeyControl StackPanel is null if called in the constructor // Function to set selection handler for single key remap drop down. Needs to be called after the constructor since the singleKeyControl StackPanel is null if called in the constructor
void KeyDropDownControl::SetSelectionHandler(Grid& table, StackPanel& singleKeyControl, size_t colIndex, std::vector<std::vector<DWORD>>& singleKeyRemapBuffer) void KeyDropDownControl::SetSelectionHandler(Grid& table, StackPanel& singleKeyControl, int colIndex, std::vector<std::vector<DWORD>>& singleKeyRemapBuffer)
{ {
// drop down selection handler
dropDown.SelectionChanged([&, table, singleKeyControl, colIndex](winrt::Windows::Foundation::IInspectable const& sender, SelectionChangedEventArgs const& args) { dropDown.SelectionChanged([&, table, singleKeyControl, colIndex](winrt::Windows::Foundation::IInspectable const& sender, SelectionChangedEventArgs const& args) {
ComboBox currentDropDown = sender.as<ComboBox>(); ComboBox currentDropDown = sender.as<ComboBox>();
int selectedKeyIndex = currentDropDown.SelectedIndex(); int selectedKeyIndex = currentDropDown.SelectedIndex();
@ -47,31 +48,73 @@ void KeyDropDownControl::SetSelectionHandler(Grid& table, StackPanel& singleKeyC
bool indexFound = table.Children().IndexOf(singleKeyControl, controlIndex); bool indexFound = table.Children().IndexOf(singleKeyControl, controlIndex);
if (indexFound) if (indexFound)
{ {
int rowIndex = (controlIndex - 2) / 3; KeyboardManagerHelper::ErrorType errorType = KeyboardManagerHelper::ErrorType::NoError;
int rowIndex = (controlIndex - 2) / 4;
// Check if the element was not found or the index exceeds the known keys // Check if the element was not found or the index exceeds the known keys
if (selectedKeyIndex != -1 && keyCodeList.size() > selectedKeyIndex) if (selectedKeyIndex != -1 && keyCodeList.size() > selectedKeyIndex)
{ {
singleKeyRemapBuffer[rowIndex][colIndex] = keyCodeList[selectedKeyIndex]; // Check if the value being set is the same as the other column
if (singleKeyRemapBuffer[rowIndex][std::abs(int(colIndex) - 1)] == keyCodeList[selectedKeyIndex])
{
errorType = KeyboardManagerHelper::ErrorType::MapToSameKey;
}
if (errorType == KeyboardManagerHelper::ErrorType::NoError && colIndex == 0)
{
// Check if the key is already remapped to something else
for (int i = 0; i < singleKeyRemapBuffer.size(); i++)
{
if (i != rowIndex)
{
KeyboardManagerHelper::ErrorType result = KeyboardManagerHelper::DoKeysOverlap(singleKeyRemapBuffer[i][0], keyCodeList[selectedKeyIndex]);
if (result != KeyboardManagerHelper::ErrorType::NoError)
{
errorType = result;
break;
}
}
}
}
// If there is no error, set the buffer
if (errorType == KeyboardManagerHelper::ErrorType::NoError)
{
singleKeyRemapBuffer[rowIndex][colIndex] = keyCodeList[selectedKeyIndex];
}
else
{
singleKeyRemapBuffer[rowIndex][colIndex] = NULL;
}
} }
else else
{ {
// Reset to null if the key is not found // Reset to null if the key is not found
singleKeyRemapBuffer[rowIndex][colIndex] = NULL; singleKeyRemapBuffer[rowIndex][colIndex] = NULL;
} }
if (errorType != KeyboardManagerHelper::ErrorType::NoError)
{
SetDropDownError(currentDropDown, KeyboardManagerHelper::GetErrorMessage(errorType));
}
// If either of the keys are invalid and the tooltip content has been modified after initialization, display the warning
if ((singleKeyRemapBuffer[rowIndex][0] == NULL || singleKeyRemapBuffer[rowIndex][1] == NULL) && toolTip.Content().as<winrt::Windows::Foundation::IPropertyValue>().GetString() != KeyboardManagerConstants::ToolTipInitialContent)
{
warning.Visibility(Visibility::Visible);
}
else
{
warning.Visibility(Visibility::Collapsed);
}
} }
}); });
} }
// Function to set selection handler for shortcut drop down. Needs to be called after the constructor since the shortcutControl StackPanel is null if called in the constructor // Function to set selection handler for shortcut drop down. Needs to be called after the constructor since the shortcutControl StackPanel is null if called in the constructor
void KeyDropDownControl::SetSelectionHandler(Grid& table, StackPanel& shortcutControl, StackPanel parent, size_t colIndex, std::vector<std::vector<Shortcut>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects) void KeyDropDownControl::SetSelectionHandler(Grid& table, StackPanel& shortcutControl, StackPanel parent, int colIndex, std::vector<std::vector<Shortcut>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects)
{ {
Flyout warningFlyout;
TextBlock warningMessage;
warningFlyout.Content(warningMessage);
dropDown.ContextFlyout().SetAttachedFlyout((FrameworkElement)dropDown, warningFlyout);
// drop down selection handler // drop down selection handler
dropDown.SelectionChanged([&, table, shortcutControl, colIndex, parent, warningMessage](winrt::Windows::Foundation::IInspectable const& sender, SelectionChangedEventArgs const&) { dropDown.SelectionChanged([&, table, shortcutControl, colIndex, parent](winrt::Windows::Foundation::IInspectable const& sender, SelectionChangedEventArgs const&) {
ComboBox currentDropDown = sender.as<ComboBox>(); ComboBox currentDropDown = sender.as<ComboBox>();
int selectedKeyIndex = currentDropDown.SelectedIndex(); int selectedKeyIndex = currentDropDown.SelectedIndex();
uint32_t dropDownIndex = -1; uint32_t dropDownIndex = -1;
@ -79,47 +122,48 @@ void KeyDropDownControl::SetSelectionHandler(Grid& table, StackPanel& shortcutCo
// Get row index of the single key control // Get row index of the single key control
uint32_t controlIndex; uint32_t controlIndex;
bool controlIindexFound = table.Children().IndexOf(shortcutControl, controlIndex); bool controlIindexFound = table.Children().IndexOf(shortcutControl, controlIndex);
KeyboardManagerHelper::ErrorType errorType = KeyboardManagerHelper::ErrorType::NoError;
if (controlIindexFound) if (controlIindexFound)
{ {
int rowIndex = (controlIndex - 2) / 3; int rowIndex = (controlIndex - 2) / 4;
if (selectedKeyIndex != -1 && keyCodeList.size() > selectedKeyIndex && dropDownFound) if (selectedKeyIndex != -1 && keyCodeList.size() > selectedKeyIndex && dropDownFound)
{ {
// If only 1 drop down and action key is chosen: Warn that a modifier must be chosen // If only 1 drop down and action key is chosen: Warn that a modifier must be chosen
if (parent.Children().Size() == 1 && !KeyboardManagerHelper::IsModifierKey(keyCodeList[selectedKeyIndex])) if (parent.Children().Size() == 1 && !KeyboardManagerHelper::IsModifierKey(keyCodeList[selectedKeyIndex]))
{ {
// warn and reset the drop down // warn and reset the drop down
SetDropDownError(currentDropDown, warningMessage, L"Shortcut must start with a modifier key"); errorType = KeyboardManagerHelper::ErrorType::ShortcutStartWithModifier;
} }
// If it is the last drop down // If it is the last drop down
else if (dropDownIndex == parent.Children().Size() - 1) else if (dropDownIndex == parent.Children().Size() - 1)
{ {
// If last drop down and a modifier is selected: add a new drop down (max of 5 drop downs should be enforced) // If last drop down and a modifier is selected: add a new drop down (max of 5 drop downs should be enforced)
if (KeyboardManagerHelper::IsModifierKey(keyCodeList[selectedKeyIndex]) && parent.Children().Size() < 5) if (KeyboardManagerHelper::IsModifierKey(keyCodeList[selectedKeyIndex]) && parent.Children().Size() < 3)
{ {
// If it matched any of the previous modifiers then reset that drop down // If it matched any of the previous modifiers then reset that drop down
if (CheckRepeatedModifier(parent, dropDownIndex, selectedKeyIndex, keyCodeList)) if (CheckRepeatedModifier(parent, dropDownIndex, selectedKeyIndex, keyCodeList))
{ {
// warn and reset the drop down // warn and reset the drop down
SetDropDownError(currentDropDown, warningMessage, L"Shortcut cannot contain a repeated modifier"); errorType = KeyboardManagerHelper::ErrorType::ShortcutCannotHaveRepeatedModifier;
} }
// If not, add a new drop down // If not, add a new drop down
else else
{ {
AddDropDown(table, shortcutControl, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects); AddDropDown(table, shortcutControl, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, warning, toolTip);
} }
} }
// If last drop down and a modifier is selected but there are already 5 drop downs: warn the user // If last drop down and a modifier is selected but there are already 5 drop downs: warn the user
else if (KeyboardManagerHelper::IsModifierKey(keyCodeList[selectedKeyIndex]) && parent.Children().Size() >= 5) else if (KeyboardManagerHelper::IsModifierKey(keyCodeList[selectedKeyIndex]) && parent.Children().Size() >= 3)
{ {
// warn and reset the drop down // warn and reset the drop down
SetDropDownError(currentDropDown, warningMessage, L"Shortcut must contain an action key"); errorType = KeyboardManagerHelper::ErrorType::ShortcutOneActionKey;
} }
// If None is selected but it's the last index: warn // If None is selected but it's the last index: warn
else if (keyCodeList[selectedKeyIndex] == 0) else if (keyCodeList[selectedKeyIndex] == 0)
{ {
// warn and reset the drop down // warn and reset the drop down
SetDropDownError(currentDropDown, warningMessage, L"Shortcut must contain an action key"); errorType = KeyboardManagerHelper::ErrorType::ShortcutOneActionKey;
} }
// If none of the above, then the action key will be set // If none of the above, then the action key will be set
} }
@ -132,7 +176,7 @@ void KeyDropDownControl::SetSelectionHandler(Grid& table, StackPanel& shortcutCo
if (CheckRepeatedModifier(parent, dropDownIndex, selectedKeyIndex, keyCodeList)) if (CheckRepeatedModifier(parent, dropDownIndex, selectedKeyIndex, keyCodeList))
{ {
// warn and reset the drop down // warn and reset the drop down
SetDropDownError(currentDropDown, warningMessage, L"Shortcut cannot contain a repeated modifier"); errorType = KeyboardManagerHelper::ErrorType::ShortcutCannotHaveRepeatedModifier;
} }
// If not, the modifier key will be set // If not, the modifier key will be set
} }
@ -148,7 +192,7 @@ void KeyDropDownControl::SetSelectionHandler(Grid& table, StackPanel& shortcutCo
else if (keyCodeList[selectedKeyIndex] == 0 && parent.Children().Size() <= 2) else if (keyCodeList[selectedKeyIndex] == 0 && parent.Children().Size() <= 2)
{ {
// warn and reset the drop down // warn and reset the drop down
SetDropDownError(currentDropDown, warningMessage, L"Shortcut must have atleast 2 keys"); errorType = KeyboardManagerHelper::ErrorType::ShortcutAtleast2Keys;
} }
// If the user tries to set an action key check if all drop down menus after this are empty if it is not the first key // If the user tries to set an action key check if all drop down menus after this are empty if it is not the first key
else if (dropDownIndex != 0) else if (dropDownIndex != 0)
@ -156,8 +200,8 @@ void KeyDropDownControl::SetSelectionHandler(Grid& table, StackPanel& shortcutCo
bool isClear = true; bool isClear = true;
for (int i = dropDownIndex + 1; i < (int)parent.Children().Size(); i++) for (int i = dropDownIndex + 1; i < (int)parent.Children().Size(); i++)
{ {
ComboBox currentDropDown = parent.Children().GetAt(i).as<ComboBox>(); ComboBox ItDropDown = parent.Children().GetAt(i).as<ComboBox>();
if (currentDropDown.SelectedIndex() != -1) if (ItDropDown.SelectedIndex() != -1)
{ {
isClear = false; isClear = false;
break; break;
@ -178,20 +222,68 @@ void KeyDropDownControl::SetSelectionHandler(Grid& table, StackPanel& shortcutCo
else else
{ {
// warn and reset the drop down // warn and reset the drop down
SetDropDownError(currentDropDown, warningMessage, L"Shortcut cannot have more than one action key"); errorType = KeyboardManagerHelper::ErrorType::ShortcutNotMoreThanOneActionKey;
} }
} }
// If there an action key is chosen on the first drop down and there are more than one drop down menus // If there an action key is chosen on the first drop down and there are more than one drop down menus
else else
{ {
// warn and reset the drop down // warn and reset the drop down
SetDropDownError(currentDropDown, warningMessage, L"Shortcut must start with a modifier key"); errorType = KeyboardManagerHelper::ErrorType::ShortcutStartWithModifier;
} }
} }
} }
// After validating the shortcut, now for errors like remap to same shortcut, remap shortcut more than once, Win L and Ctrl Alt Del
if (errorType == KeyboardManagerHelper::ErrorType::NoError)
{
Shortcut tempShortcut;
tempShortcut.SetKeyCodes(GetKeysFromStackPanel(parent));
// Check if the value being set is the same as the other column
if (shortcutRemapBuffer[rowIndex][std::abs(int(colIndex) - 1)] == tempShortcut)
{
errorType = KeyboardManagerHelper::ErrorType::MapToSameShortcut;
}
if (errorType == KeyboardManagerHelper::ErrorType::NoError && colIndex == 0)
{
// Check if the key is already remapped to something else
for (int i = 0; i < shortcutRemapBuffer.size(); i++)
{
if (i != rowIndex)
{
KeyboardManagerHelper::ErrorType result = Shortcut::DoKeysOverlap(shortcutRemapBuffer[i][0], tempShortcut);
if (result != KeyboardManagerHelper::ErrorType::NoError)
{
errorType = result;
break;
}
}
}
}
if (errorType == KeyboardManagerHelper::ErrorType::NoError)
{
errorType = tempShortcut.IsShortcutIllegal();
}
}
if (errorType != KeyboardManagerHelper::ErrorType::NoError)
{
SetDropDownError(currentDropDown, KeyboardManagerHelper::GetErrorMessage(errorType));
}
// Reset the buffer based on the new selected drop down items // Reset the buffer based on the new selected drop down items
shortcutRemapBuffer[rowIndex][colIndex].SetKeyCodes(GetKeysFromStackPanel(parent)); shortcutRemapBuffer[rowIndex][colIndex].SetKeyCodes(GetKeysFromStackPanel(parent));
// If either of the shortcuts are invalid and the tooltip content has been modified after initialization, display the warning
if ((!shortcutRemapBuffer[rowIndex][0].IsValidShortcut() || !shortcutRemapBuffer[rowIndex][1].IsValidShortcut()) && toolTip.Content().as<winrt::Windows::Foundation::IPropertyValue>().GetString() != KeyboardManagerConstants::ToolTipInitialContent)
{
warning.Visibility(Visibility::Visible);
}
else
{
warning.Visibility(Visibility::Collapsed);
}
} }
// If the user searches for a key the selection handler gets invoked however if they click away it reverts back to the previous state. This can result in dangling references to added drop downs which were then reset. // If the user searches for a key the selection handler gets invoked however if they click away it reverts back to the previous state. This can result in dangling references to added drop downs which were then reset.
@ -221,13 +313,9 @@ ComboBox KeyDropDownControl::GetComboBox()
} }
// Function to add a drop down to the shortcut stack panel // Function to add a drop down to the shortcut stack panel
void KeyDropDownControl::AddDropDown(Grid table, StackPanel shortcutControl, StackPanel parent, const size_t colIndex, std::vector<std::vector<Shortcut>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects) void KeyDropDownControl::AddDropDown(Grid table, StackPanel shortcutControl, StackPanel parent, const int colIndex, std::vector<std::vector<Shortcut>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, FontIcon warning, ToolTip toolTip)
{ {
keyDropDownControlObjects.push_back(std::move(std::unique_ptr<KeyDropDownControl>(new KeyDropDownControl(true)))); keyDropDownControlObjects.push_back(std::move(std::unique_ptr<KeyDropDownControl>(new KeyDropDownControl(true, warning, toolTip))));
// Flyout to display the warning on the drop down element
Flyout warningFlyout;
TextBlock warningMessage;
warningFlyout.Content(warningMessage);
parent.Children().Append(keyDropDownControlObjects[keyDropDownControlObjects.size() - 1]->GetComboBox()); parent.Children().Append(keyDropDownControlObjects[keyDropDownControlObjects.size() - 1]->GetComboBox());
keyDropDownControlObjects[keyDropDownControlObjects.size() - 1]->SetSelectionHandler(table, shortcutControl, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects); keyDropDownControlObjects[keyDropDownControlObjects.size() - 1]->SetSelectionHandler(table, shortcutControl, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects);
parent.UpdateLayout(); parent.UpdateLayout();
@ -278,10 +366,9 @@ bool KeyDropDownControl::CheckRepeatedModifier(StackPanel parent, uint32_t dropD
return matchPreviousModifier; return matchPreviousModifier;
} }
// Function to set the flyout warning message // Function to set the warning message
void KeyDropDownControl::SetDropDownError(ComboBox dropDown, TextBlock messageBlock, hstring message) void KeyDropDownControl::SetDropDownError(ComboBox currentDropDown, hstring message)
{ {
messageBlock.Text(message); currentDropDown.SelectedIndex(-1);
dropDown.ContextFlyout().ShowAttachedFlyout((FrameworkElement)dropDown); toolTip.Content(box_value(message));
dropDown.SelectedIndex(-1);
} }

View File

@ -11,6 +11,10 @@ private:
HKL previousLayout = 0; HKL previousLayout = 0;
// Stores the key code list // Stores the key code list
std::vector<DWORD> keyCodeList; std::vector<DWORD> keyCodeList;
// Stores the warning control
FontIcon warning;
// Stores the tooltip for the warning control
ToolTip toolTip;
// Function to set properties apart from the SelectionChanged event handler // Function to set properties apart from the SelectionChanged event handler
void SetDefaultProperties(bool isShortcut); void SetDefaultProperties(bool isShortcut);
@ -22,17 +26,18 @@ public:
// Pointer to the keyboard manager state // Pointer to the keyboard manager state
static KeyboardManagerState* keyboardManagerState; static KeyboardManagerState* keyboardManagerState;
// Constructor for single key drop down // Constructor
KeyDropDownControl(bool isShortcut) KeyDropDownControl(bool isShortcut, FontIcon warning, ToolTip toolTip) :
warning(warning), toolTip(toolTip)
{ {
SetDefaultProperties(isShortcut); SetDefaultProperties(isShortcut);
} }
// Function to set selection handler for single key remap drop down. Needs to be called after the constructor since the singleKeyControl StackPanel is null if called in the constructor // Function to set selection handler for single key remap drop down. Needs to be called after the constructor since the singleKeyControl StackPanel is null if called in the constructor
void SetSelectionHandler(Grid& table, StackPanel& singleKeyControl, size_t colIndex, std::vector<std::vector<DWORD>>& singleKeyRemapBuffer); void SetSelectionHandler(Grid& table, StackPanel& singleKeyControl, int colIndex, std::vector<std::vector<DWORD>>& singleKeyRemapBuffer);
// Function to set selection handler for shortcut drop down. Needs to be called after the constructor since the shortcutControl StackPanel is null if called in the constructor // Function to set selection handler for shortcut drop down. Needs to be called after the constructor since the shortcutControl StackPanel is null if called in the constructor
void SetSelectionHandler(Grid& table, StackPanel& shortcutControl, StackPanel parent, size_t colIndex, std::vector<std::vector<Shortcut>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects); void SetSelectionHandler(Grid& table, StackPanel& shortcutControl, StackPanel parent, int colIndex, std::vector<std::vector<Shortcut>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects);
// Function to set the selected index of the drop down // Function to set the selected index of the drop down
void SetSelectedIndex(int32_t index); void SetSelectedIndex(int32_t index);
@ -41,7 +46,7 @@ public:
ComboBox GetComboBox(); ComboBox GetComboBox();
// Function to add a drop down to the shortcut stack panel // Function to add a drop down to the shortcut stack panel
static void AddDropDown(Grid table, StackPanel shortcutControl, StackPanel parent, const size_t colIndex, std::vector<std::vector<Shortcut>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects); static void AddDropDown(Grid table, StackPanel shortcutControl, StackPanel parent, const int colIndex, std::vector<std::vector<Shortcut>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, FontIcon warning, ToolTip toolTip);
// Function to get the list of key codes from the shortcut combo box stack panel // Function to get the list of key codes from the shortcut combo box stack panel
std::vector<DWORD> GetKeysFromStackPanel(StackPanel parent); std::vector<DWORD> GetKeysFromStackPanel(StackPanel parent);
@ -49,6 +54,6 @@ public:
// Function to check if a modifier has been repeated in the previous drop downs // Function to check if a modifier has been repeated in the previous drop downs
bool CheckRepeatedModifier(StackPanel parent, uint32_t dropDownIndex, int selectedKeyIndex, const std::vector<DWORD>& keyCodeList); bool CheckRepeatedModifier(StackPanel parent, uint32_t dropDownIndex, int selectedKeyIndex, const std::vector<DWORD>& keyCodeList);
// Function to set the flyout warning message // Function to set the warning message
void SetDropDownError(ComboBox dropDown, TextBlock messageBlock, hstring message); void SetDropDownError(ComboBox currentDropDown, hstring message);
}; };

View File

@ -11,10 +11,16 @@ std::vector<std::vector<Shortcut>> ShortcutControl::shortcutRemapBuffer;
// Function to add a new row to the shortcut table. If the originalKeys and newKeys args are provided, then the displayed shortcuts are set to those values. // Function to add a new row to the shortcut table. If the originalKeys and newKeys args are provided, then the displayed shortcuts are set to those values.
void ShortcutControl::AddNewShortcutControlRow(Grid& parent, std::vector<std::vector<std::unique_ptr<ShortcutControl>>>& keyboardRemapControlObjects, Shortcut originalKeys, Shortcut newKeys) void ShortcutControl::AddNewShortcutControlRow(Grid& parent, std::vector<std::vector<std::unique_ptr<ShortcutControl>>>& keyboardRemapControlObjects, Shortcut originalKeys, Shortcut newKeys)
{ {
// Warning icon for the row
ToolTip warningMessage;
FontIcon warningIcon;
warningIcon.Visibility(Visibility::Collapsed);
warningMessage.Content(box_value(KeyboardManagerConstants::ToolTipInitialContent));
// Create new ShortcutControl objects dynamically so that we does not get destructed // Create new ShortcutControl objects dynamically so that we does not get destructed
std::vector<std::unique_ptr<ShortcutControl>> newrow; std::vector<std::unique_ptr<ShortcutControl>> newrow;
newrow.push_back(std::move(std::unique_ptr<ShortcutControl>(new ShortcutControl(parent, 0)))); newrow.push_back(std::move(std::unique_ptr<ShortcutControl>(new ShortcutControl(parent, 0, warningIcon, warningMessage))));
newrow.push_back(std::move(std::unique_ptr<ShortcutControl>(new ShortcutControl(parent, 1)))); newrow.push_back(std::move(std::unique_ptr<ShortcutControl>(new ShortcutControl(parent, 1, warningIcon, warningMessage))));
keyboardRemapControlObjects.push_back(std::move(newrow)); keyboardRemapControlObjects.push_back(std::move(newrow));
// Add to grid // Add to grid
@ -40,18 +46,20 @@ void ShortcutControl::AddNewShortcutControlRow(Grid& parent, std::vector<std::ve
// Get index of delete button // Get index of delete button
UIElementCollection children = parent.Children(); UIElementCollection children = parent.Children();
children.IndexOf(currentButton, index); children.IndexOf(currentButton, index);
uint32_t lastIndexInRow = index + 1;
// Change the row index of elements appearing after the current row, as we will delete the row definition // Change the row index of elements appearing after the current row, as we will delete the row definition
for (uint32_t i = index + 1; i < children.Size(); i++) for (uint32_t i = lastIndexInRow + 1; i < children.Size(); i++)
{ {
int32_t elementRowIndex = parent.GetRow(children.GetAt(i).as<FrameworkElement>()); int32_t elementRowIndex = parent.GetRow(children.GetAt(i).as<FrameworkElement>());
parent.SetRow(children.GetAt(i).as<FrameworkElement>(), elementRowIndex - 1); parent.SetRow(children.GetAt(i).as<FrameworkElement>(), elementRowIndex - 1);
} }
parent.Children().RemoveAt(index); parent.Children().RemoveAt(lastIndexInRow);
parent.Children().RemoveAt(index - 1); parent.Children().RemoveAt(lastIndexInRow - 1);
parent.Children().RemoveAt(index - 2); parent.Children().RemoveAt(lastIndexInRow - 2);
parent.Children().RemoveAt(lastIndexInRow - 3);
// Calculate row index in the buffer from the grid child index (first two children are header elements and then three children in each row) // Calculate row index in the buffer from the grid child index (first two children are header elements and then three children in each row)
int bufferIndex = (index - 2) / 3; int bufferIndex = (lastIndexInRow - 2) / 4;
// Delete the row definition // Delete the row definition
parent.RowDefinitions().RemoveAt(bufferIndex + 1); parent.RowDefinitions().RemoveAt(bufferIndex + 1);
// delete the row from the buffer // delete the row from the buffer
@ -62,14 +70,22 @@ void ShortcutControl::AddNewShortcutControlRow(Grid& parent, std::vector<std::ve
parent.SetColumn(deleteShortcut, 2); parent.SetColumn(deleteShortcut, 2);
parent.SetRow(deleteShortcut, parent.RowDefinitions().Size() - 1); parent.SetRow(deleteShortcut, parent.RowDefinitions().Size() - 1);
parent.Children().Append(deleteShortcut); parent.Children().Append(deleteShortcut);
warningIcon.FontFamily(Xaml::Media::FontFamily(L"Segoe MDL2 Assets"));
warningIcon.Glyph(L"\xE783");
warningIcon.HorizontalAlignment(HorizontalAlignment::Left);
ToolTipService::SetToolTip(warningIcon, warningMessage);
parent.SetColumn(warningIcon, 3);
parent.SetRow(warningIcon, parent.RowDefinitions().Size() - 1);
parent.Children().Append(warningIcon);
parent.UpdateLayout(); parent.UpdateLayout();
// Set the shortcut text if the two vectors are not empty (i.e. default args) // Set the shortcut text if the two vectors are not empty (i.e. default args)
if (originalKeys.IsValidShortcut() && newKeys.IsValidShortcut()) if (originalKeys.IsValidShortcut() && newKeys.IsValidShortcut())
{ {
shortcutRemapBuffer.push_back(std::vector<Shortcut>{ Shortcut(), Shortcut() }); shortcutRemapBuffer.push_back(std::vector<Shortcut>{ Shortcut(), Shortcut() });
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->AddShortcutToControl(originalKeys, parent, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->shortcutDropDownStackPanel, *keyboardManagerState, 0); keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->AddShortcutToControl(originalKeys, parent, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->shortcutDropDownStackPanel, *keyboardManagerState, 0, warningIcon, warningMessage);
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->AddShortcutToControl(newKeys, parent, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->shortcutDropDownStackPanel, *keyboardManagerState, 1); keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->AddShortcutToControl(newKeys, parent, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->shortcutDropDownStackPanel, *keyboardManagerState, 1, warningIcon, warningMessage);
} }
else else
{ {
@ -79,7 +95,7 @@ void ShortcutControl::AddNewShortcutControlRow(Grid& parent, std::vector<std::ve
} }
// Function to add a shortcut to the shortcut control as combo boxes // Function to add a shortcut to the shortcut control as combo boxes
void ShortcutControl::AddShortcutToControl(Shortcut& shortcut, Grid table, StackPanel parent, KeyboardManagerState& keyboardManagerState, const size_t colIndex) void ShortcutControl::AddShortcutToControl(Shortcut& shortcut, Grid table, StackPanel parent, KeyboardManagerState& keyboardManagerState, const int colIndex, FontIcon warning, ToolTip toolTip)
{ {
// Delete the existing drop down menus // Delete the existing drop down menus
parent.Children().Clear(); parent.Children().Clear();
@ -90,7 +106,7 @@ void ShortcutControl::AddShortcutToControl(Shortcut& shortcut, Grid table, Stack
std::vector<DWORD> keyCodeList = keyboardManagerState.keyboardMap.GetKeyCodeList(true); std::vector<DWORD> keyCodeList = keyboardManagerState.keyboardMap.GetKeyCodeList(true);
if (shortcutKeyCodes.size() != 0) if (shortcutKeyCodes.size() != 0)
{ {
KeyDropDownControl::AddDropDown(table, shortcutControlLayout, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects); KeyDropDownControl::AddDropDown(table, shortcutControlLayout, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, warning, toolTip);
for (int i = 0; i < shortcutKeyCodes.size(); i++) for (int i = 0; i < shortcutKeyCodes.size(); i++)
{ {
// New drop down gets added automatically when the SelectedIndex is set // New drop down gets added automatically when the SelectedIndex is set
@ -115,7 +131,7 @@ StackPanel ShortcutControl::getShortcutControl()
} }
// Function to create the detect shortcut UI window // Function to create the detect shortcut UI window
void ShortcutControl::createDetectShortcutWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, std::vector<std::vector<Shortcut>>& shortcutRemapBuffer, KeyboardManagerState& keyboardManagerState, const size_t colIndex, Grid table) void ShortcutControl::createDetectShortcutWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, std::vector<std::vector<Shortcut>>& shortcutRemapBuffer, KeyboardManagerState& keyboardManagerState, const int colIndex, Grid table, FontIcon warning, ToolTip toolTip)
{ {
// ContentDialog for detecting shortcuts. This is the parent UI element. // ContentDialog for detecting shortcuts. This is the parent UI element.
ContentDialog detectShortcutBox; ContentDialog detectShortcutBox;
@ -148,14 +164,16 @@ void ShortcutControl::createDetectShortcutWindow(winrt::Windows::Foundation::IIn
&shortcutRemapBuffer, &shortcutRemapBuffer,
unregisterKeys, unregisterKeys,
colIndex, colIndex,
table] { table,
warning,
toolTip] {
// Save the detected shortcut in the linked text block // Save the detected shortcut in the linked text block
Shortcut detectedShortcutKeys = keyboardManagerState.GetDetectedShortcut(); Shortcut detectedShortcutKeys = keyboardManagerState.GetDetectedShortcut();
if (!detectedShortcutKeys.IsEmpty()) if (!detectedShortcutKeys.IsEmpty())
{ {
// The shortcut buffer gets set in this function // The shortcut buffer gets set in this function
AddShortcutToControl(detectedShortcutKeys, table, linkedShortcutStackPanel, keyboardManagerState, colIndex); AddShortcutToControl(detectedShortcutKeys, table, linkedShortcutStackPanel, keyboardManagerState, colIndex, warning, toolTip);
} }
// Reset the keyboard manager UI state // Reset the keyboard manager UI state

View File

@ -29,16 +29,16 @@ public:
// Vector to store dynamically allocated KeyDropDownControl objects to avoid early destruction // Vector to store dynamically allocated KeyDropDownControl objects to avoid early destruction
std::vector<std::unique_ptr<KeyDropDownControl>> keyDropDownControlObjects; std::vector<std::unique_ptr<KeyDropDownControl>> keyDropDownControlObjects;
ShortcutControl(Grid table, const size_t colIndex) ShortcutControl(Grid table, const int colIndex, FontIcon warning, ToolTip toolTip)
{ {
shortcutDropDownStackPanel.Spacing(10); shortcutDropDownStackPanel.Spacing(10);
shortcutDropDownStackPanel.Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal); shortcutDropDownStackPanel.Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
typeShortcut.Content(winrt::box_value(L"Type Shortcut")); typeShortcut.Content(winrt::box_value(L"Type Shortcut"));
typeShortcut.Click([&, table, colIndex](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { typeShortcut.Click([&, table, colIndex, warning, toolTip](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
keyboardManagerState->SetUIState(KeyboardManagerUIState::DetectShortcutWindowActivated, EditShortcutsWindowHandle); keyboardManagerState->SetUIState(KeyboardManagerUIState::DetectShortcutWindowActivated, EditShortcutsWindowHandle);
// Using the XamlRoot of the typeShortcut to get the root of the XAML host // Using the XamlRoot of the typeShortcut to get the root of the XAML host
createDetectShortcutWindow(sender, sender.as<Button>().XamlRoot(), shortcutRemapBuffer, *keyboardManagerState, colIndex, table); createDetectShortcutWindow(sender, sender.as<Button>().XamlRoot(), shortcutRemapBuffer, *keyboardManagerState, colIndex, table, warning, toolTip);
}); });
shortcutControlLayout.Margin({ 0, 0, 0, 10 }); shortcutControlLayout.Margin({ 0, 0, 0, 10 });
@ -46,7 +46,7 @@ public:
shortcutControlLayout.Children().Append(typeShortcut); shortcutControlLayout.Children().Append(typeShortcut);
shortcutControlLayout.Children().Append(shortcutDropDownStackPanel); shortcutControlLayout.Children().Append(shortcutDropDownStackPanel);
KeyDropDownControl::AddDropDown(table, shortcutControlLayout, shortcutDropDownStackPanel, colIndex, shortcutRemapBuffer, keyDropDownControlObjects); KeyDropDownControl::AddDropDown(table, shortcutControlLayout, shortcutDropDownStackPanel, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, warning, toolTip);
shortcutControlLayout.UpdateLayout(); shortcutControlLayout.UpdateLayout();
} }
@ -54,11 +54,11 @@ public:
static void AddNewShortcutControlRow(Grid& parent, std::vector<std::vector<std::unique_ptr<ShortcutControl>>>& keyboardRemapControlObjects, Shortcut originalKeys = Shortcut(), Shortcut newKeys = Shortcut()); static void AddNewShortcutControlRow(Grid& parent, std::vector<std::vector<std::unique_ptr<ShortcutControl>>>& keyboardRemapControlObjects, Shortcut originalKeys = Shortcut(), Shortcut newKeys = Shortcut());
// Function to add a shortcut to the shortcut control as combo boxes // Function to add a shortcut to the shortcut control as combo boxes
void AddShortcutToControl(Shortcut& shortcut, Grid table, StackPanel parent, KeyboardManagerState& keyboardManagerState, const size_t colIndex); void AddShortcutToControl(Shortcut& shortcut, Grid table, StackPanel parent, KeyboardManagerState& keyboardManagerState, const int colIndex, FontIcon warning, ToolTip toolTip);
// Function to return the stack panel element of the ShortcutControl. This is the externally visible UI element which can be used to add it to other layouts // Function to return the stack panel element of the ShortcutControl. This is the externally visible UI element which can be used to add it to other layouts
StackPanel getShortcutControl(); StackPanel getShortcutControl();
// Function to create the detect shortcut UI window // Function to create the detect shortcut UI window
void createDetectShortcutWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, std::vector<std::vector<Shortcut>>& shortcutRemapBuffer, KeyboardManagerState& keyboardManagerState, const size_t colIndex, Grid table); void createDetectShortcutWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, std::vector<std::vector<Shortcut>>& shortcutRemapBuffer, KeyboardManagerState& keyboardManagerState, const int colIndex, Grid table, FontIcon warning, ToolTip toolTip);
}; };

View File

@ -11,10 +11,16 @@ std::vector<std::vector<DWORD>> SingleKeyRemapControl::singleKeyRemapBuffer;
// 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. // 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(Grid& parent, std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>>& keyboardRemapControlObjects, const DWORD originalKey, const DWORD newKey) void SingleKeyRemapControl::AddNewControlKeyRemapRow(Grid& parent, std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>>& keyboardRemapControlObjects, const DWORD originalKey, const DWORD newKey)
{ {
// Warning icon for the row
ToolTip warningMessage;
FontIcon warningIcon;
warningIcon.Visibility(Visibility::Collapsed);
warningMessage.Content(box_value(KeyboardManagerConstants::ToolTipInitialContent));
// Create new SingleKeyRemapControl objects dynamically so that we does not get destructed // Create new SingleKeyRemapControl objects dynamically so that we does not get destructed
std::vector<std::unique_ptr<SingleKeyRemapControl>> newrow; std::vector<std::unique_ptr<SingleKeyRemapControl>> newrow;
newrow.push_back(std::move(std::unique_ptr<SingleKeyRemapControl>(new SingleKeyRemapControl(parent, 0)))); newrow.push_back(std::move(std::unique_ptr<SingleKeyRemapControl>(new SingleKeyRemapControl(parent, 0, warningIcon, warningMessage))));
newrow.push_back(std::move(std::unique_ptr<SingleKeyRemapControl>(new SingleKeyRemapControl(parent, 1)))); newrow.push_back(std::move(std::unique_ptr<SingleKeyRemapControl>(new SingleKeyRemapControl(parent, 1, warningIcon, warningMessage))));
keyboardRemapControlObjects.push_back(std::move(newrow)); keyboardRemapControlObjects.push_back(std::move(newrow));
// Add to grid // Add to grid
@ -63,18 +69,20 @@ void SingleKeyRemapControl::AddNewControlKeyRemapRow(Grid& parent, std::vector<s
// Get index of delete button // Get index of delete button
UIElementCollection children = parent.Children(); UIElementCollection children = parent.Children();
children.IndexOf(currentButton, index); children.IndexOf(currentButton, index);
uint32_t lastIndexInRow = index + 1;
// Change the row index of elements appearing after the current row, as we will delete the row definition // Change the row index of elements appearing after the current row, as we will delete the row definition
for (uint32_t i = index + 1; i < children.Size(); i++) for (uint32_t i = lastIndexInRow + 1; i < children.Size(); i++)
{ {
int32_t elementRowIndex = parent.GetRow(children.GetAt(i).as<FrameworkElement>()); int32_t elementRowIndex = parent.GetRow(children.GetAt(i).as<FrameworkElement>());
parent.SetRow(children.GetAt(i).as<FrameworkElement>(), elementRowIndex - 1); parent.SetRow(children.GetAt(i).as<FrameworkElement>(), elementRowIndex - 1);
} }
parent.Children().RemoveAt(index); parent.Children().RemoveAt(lastIndexInRow);
parent.Children().RemoveAt(index - 1); parent.Children().RemoveAt(lastIndexInRow - 1);
parent.Children().RemoveAt(index - 2); parent.Children().RemoveAt(lastIndexInRow - 2);
parent.Children().RemoveAt(lastIndexInRow - 3);
// Calculate row index in the buffer from the grid child index (first two children are header elements and then three children in each row) // Calculate row index in the buffer from the grid child index (first two children are header elements and then three children in each row)
int bufferIndex = (index - 2) / 3; int bufferIndex = (lastIndexInRow - 2) / 4;
// Delete the row definition // Delete the row definition
parent.RowDefinitions().RemoveAt(bufferIndex + 1); parent.RowDefinitions().RemoveAt(bufferIndex + 1);
// delete the row from the buffer. // delete the row from the buffer.
@ -85,6 +93,15 @@ void SingleKeyRemapControl::AddNewControlKeyRemapRow(Grid& parent, std::vector<s
parent.SetColumn(deleteRemapKeys, 2); parent.SetColumn(deleteRemapKeys, 2);
parent.SetRow(deleteRemapKeys, parent.RowDefinitions().Size() - 1); parent.SetRow(deleteRemapKeys, parent.RowDefinitions().Size() - 1);
parent.Children().Append(deleteRemapKeys); parent.Children().Append(deleteRemapKeys);
warningIcon.FontFamily(Xaml::Media::FontFamily(L"Segoe MDL2 Assets"));
warningIcon.Glyph(L"\xE783");
warningIcon.HorizontalAlignment(HorizontalAlignment::Left);
ToolTipService::SetToolTip(warningIcon, warningMessage);
parent.SetColumn(warningIcon, 3);
parent.SetRow(warningIcon, parent.RowDefinitions().Size() - 1);
parent.Children().Append(warningIcon);
parent.UpdateLayout();
} }
// 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 // 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

View File

@ -22,8 +22,8 @@ public:
// Stores the current list of remappings // Stores the current list of remappings
static std::vector<std::vector<DWORD>> singleKeyRemapBuffer; static std::vector<std::vector<DWORD>> singleKeyRemapBuffer;
SingleKeyRemapControl(Grid table, const size_t colIndex) : SingleKeyRemapControl(Grid table, const int colIndex, FontIcon warning, ToolTip toolTip) :
singleKeyRemapDropDown(false) singleKeyRemapDropDown(false, warning, toolTip)
{ {
typeKey.Content(winrt::box_value(L"Type Key")); typeKey.Content(winrt::box_value(L"Type Key"));
typeKey.Click([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { typeKey.Click([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {