mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-01-18 06:29:44 +08:00
[Keyboard Manager] Add app-specific shortcuts to Remap shortcuts UI (#4804)
* Enable app specific shortcut remapping * Fixed lowercase function call * Add test file * Moved GetForegroundProcess to II and added tests * Fixed runtime error while testing due to heap allocation across dll boundary * Renamed function * Changed shortcutBuffer type * Linked App specific UI to backend * Added shortcut validation logic on TextBox LostFocus handler * Moved Validate function and changed default text * Changed to case insensitive warning check * Changed to case insensitive warning check at OnClickAccept * Fixed alignment and spacing issues
This commit is contained in:
parent
411140c3ea
commit
4634085402
@ -47,7 +47,7 @@ namespace KeyboardManagerConstants
|
||||
// Default window sizes
|
||||
inline const int DefaultEditKeyboardWindowWidth = 800;
|
||||
inline const int DefaultEditKeyboardWindowHeight = 600;
|
||||
inline const int DefaultEditShortcutsWindowWidth = 1000;
|
||||
inline const int DefaultEditShortcutsWindowWidth = 1050;
|
||||
inline const int DefaultEditShortcutsWindowHeight = 600;
|
||||
|
||||
// Key Remap table constants
|
||||
@ -60,12 +60,13 @@ namespace KeyboardManagerConstants
|
||||
inline const DWORD64 RemapTableDropDownWidth = 110;
|
||||
|
||||
// Shortcut table constants
|
||||
inline const long ShortcutTableColCount = 4;
|
||||
inline const long ShortcutTableHeaderCount = 2;
|
||||
inline const long ShortcutTableColCount = 5;
|
||||
inline const long ShortcutTableHeaderCount = 3;
|
||||
inline const long ShortcutTableOriginalColIndex = 0;
|
||||
inline const long ShortcutTableArrowColIndex = 1;
|
||||
inline const long ShortcutTableNewColIndex = 2;
|
||||
inline const long ShortcutTableRemoveColIndex = 3;
|
||||
inline const long ShortcutTableTargetAppColIndex = 3;
|
||||
inline const long ShortcutTableRemoveColIndex = 4;
|
||||
inline const DWORD64 ShortcutTableDropDownWidth = 110;
|
||||
inline const DWORD64 ShortcutTableDropDownSpacing = 10;
|
||||
|
||||
@ -74,6 +75,7 @@ namespace KeyboardManagerConstants
|
||||
inline const DWORD64 TableArrowColWidth = 20;
|
||||
inline const DWORD64 TableRemoveColWidth = 20;
|
||||
inline const DWORD64 TableWarningColWidth = 20;
|
||||
inline const DWORD64 TableTargetAppColWidth = ShortcutTableDropDownWidth + 50;
|
||||
|
||||
// Shared style constants for both Remap Table and Shortcut Table
|
||||
inline const DWORD64 HeaderButtonWidth = 100;
|
||||
@ -85,4 +87,7 @@ namespace KeyboardManagerConstants
|
||||
|
||||
// Dummy key event used in between key up and down events to prevent certain global events from happening
|
||||
inline const DWORD DUMMY_KEY = 0xFF;
|
||||
|
||||
// String constant for the default app name in Remap shortcuts
|
||||
inline const std::wstring DefaultAppName = L"All Apps";
|
||||
}
|
@ -50,3 +50,14 @@ void Trace::OSLevelShortcutRemapCount(const DWORD count) noexcept
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingValue(count, "OSLevelShortcutRemapCount"));
|
||||
}
|
||||
|
||||
// Log number of app specific shortcut remaps when the user uses Edit Shortcuts and saves settings
|
||||
void Trace::AppSpecificShortcutRemapCount(const DWORD count) noexcept
|
||||
{
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"KeyboardManager_AppSpecificShortcutRemapCount",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingValue(count, "AppSpecificShortcutRemapCount"));
|
||||
}
|
||||
|
@ -14,4 +14,7 @@ public:
|
||||
|
||||
// Log number of os level shortcut remaps when the user uses Edit Shortcuts and saves settings
|
||||
static void OSLevelShortcutRemapCount(const DWORD count) noexcept;
|
||||
|
||||
// Log number of app specific shortcut remaps when the user uses Edit Shortcuts and saves settings
|
||||
static void AppSpecificShortcutRemapCount(const DWORD count) noexcept;
|
||||
};
|
||||
|
@ -30,11 +30,37 @@ static IAsyncAction OnClickAccept(
|
||||
XamlRoot root,
|
||||
std::function<void()> ApplyRemappings)
|
||||
{
|
||||
KeyboardManagerHelper::ErrorType isSuccess = Dialog::CheckIfRemappingsAreValid<Shortcut>(
|
||||
ShortcutControl::shortcutRemapBuffer,
|
||||
[](Shortcut shortcut) {
|
||||
return shortcut.IsValidShortcut();
|
||||
});
|
||||
KeyboardManagerHelper::ErrorType isSuccess = KeyboardManagerHelper::ErrorType::NoError;
|
||||
std::map<std::wstring, std::vector<std::vector<Shortcut>>> appSpecificBuffer;
|
||||
|
||||
// Create per app shortcut buffer
|
||||
for (int i = 0; i < ShortcutControl::shortcutRemapBuffer.size(); i++)
|
||||
{
|
||||
std::wstring currAppName = ShortcutControl::shortcutRemapBuffer[i].second;
|
||||
std::transform(currAppName.begin(), currAppName.end(), currAppName.begin(), towlower);
|
||||
|
||||
if (appSpecificBuffer.find(currAppName) == appSpecificBuffer.end())
|
||||
{
|
||||
appSpecificBuffer[currAppName] = std::vector<std::vector<Shortcut>>();
|
||||
}
|
||||
|
||||
appSpecificBuffer[currAppName].push_back(ShortcutControl::shortcutRemapBuffer[i].first);
|
||||
}
|
||||
|
||||
for (auto it : appSpecificBuffer)
|
||||
{
|
||||
KeyboardManagerHelper::ErrorType currentSuccess = Dialog::CheckIfRemappingsAreValid<Shortcut>(
|
||||
it.second,
|
||||
[](Shortcut shortcut) {
|
||||
return shortcut.IsValidShortcut();
|
||||
});
|
||||
if (currentSuccess != KeyboardManagerHelper::ErrorType::NoError)
|
||||
{
|
||||
isSuccess = currentSuccess;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isSuccess != KeyboardManagerHelper::ErrorType::NoError)
|
||||
{
|
||||
if (!co_await Dialog::PartialRemappingConfirmationDialog(root, L"Some of the shortcuts could not be remapped. Do you want to continue anyway?"))
|
||||
@ -166,6 +192,8 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
|
||||
ColumnDefinition newColumn;
|
||||
newColumn.MinWidth(3 * KeyboardManagerConstants::ShortcutTableDropDownWidth + 2 * KeyboardManagerConstants::ShortcutTableDropDownSpacing);
|
||||
newColumn.MaxWidth(3 * KeyboardManagerConstants::ShortcutTableDropDownWidth + 2 * KeyboardManagerConstants::ShortcutTableDropDownSpacing);
|
||||
ColumnDefinition targetAppColumn;
|
||||
targetAppColumn.MinWidth(KeyboardManagerConstants::TableTargetAppColWidth);
|
||||
ColumnDefinition removeColumn;
|
||||
removeColumn.MinWidth(KeyboardManagerConstants::TableRemoveColWidth);
|
||||
shortcutTable.Margin({ 10, 10, 10, 20 });
|
||||
@ -173,6 +201,7 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
|
||||
shortcutTable.ColumnDefinitions().Append(originalColumn);
|
||||
shortcutTable.ColumnDefinitions().Append(arrowColumn);
|
||||
shortcutTable.ColumnDefinitions().Append(newColumn);
|
||||
shortcutTable.ColumnDefinitions().Append(targetAppColumn);
|
||||
shortcutTable.ColumnDefinitions().Append(removeColumn);
|
||||
shortcutTable.RowDefinitions().Append(RowDefinition());
|
||||
|
||||
@ -188,13 +217,24 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
|
||||
newShortcutHeader.FontWeight(Text::FontWeights::Bold());
|
||||
newShortcutHeader.Margin({ 0, 0, 0, 10 });
|
||||
|
||||
// Third header textblock in the header row of the shortcut table
|
||||
TextBlock targetAppHeader;
|
||||
targetAppHeader.Text(L"Target App:");
|
||||
targetAppHeader.Width(KeyboardManagerConstants::ShortcutTableDropDownWidth);
|
||||
targetAppHeader.FontWeight(Text::FontWeights::Bold());
|
||||
targetAppHeader.Margin({ 0, 0, 0, 10 });
|
||||
targetAppHeader.HorizontalAlignment(HorizontalAlignment::Center);
|
||||
|
||||
shortcutTable.SetColumn(originalShortcutHeader, KeyboardManagerConstants::ShortcutTableOriginalColIndex);
|
||||
shortcutTable.SetRow(originalShortcutHeader, 0);
|
||||
shortcutTable.SetColumn(newShortcutHeader, KeyboardManagerConstants::ShortcutTableNewColIndex);
|
||||
shortcutTable.SetRow(newShortcutHeader, 0);
|
||||
shortcutTable.SetColumn(targetAppHeader, KeyboardManagerConstants::ShortcutTableTargetAppColIndex);
|
||||
shortcutTable.SetRow(targetAppHeader, 0);
|
||||
|
||||
shortcutTable.Children().Append(originalShortcutHeader);
|
||||
shortcutTable.Children().Append(newShortcutHeader);
|
||||
shortcutTable.Children().Append(targetAppHeader);
|
||||
|
||||
// Store handle of edit shortcuts window
|
||||
ShortcutControl::EditShortcutsWindowHandle = _hWndEditShortcutsWindow;
|
||||
@ -209,13 +249,26 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
|
||||
// Set keyboard manager UI state so that shortcut remaps are not applied while on this window
|
||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, _hWndEditShortcutsWindow);
|
||||
|
||||
// Load existing shortcuts into UI
|
||||
std::unique_lock<std::mutex> lock(keyboardManagerState.osLevelShortcutReMap_mutex);
|
||||
// Load existing os level shortcuts into UI
|
||||
std::unique_lock<std::mutex> lockOSLevel(keyboardManagerState.osLevelShortcutReMap_mutex);
|
||||
for (const auto& it : keyboardManagerState.osLevelShortcutReMap)
|
||||
{
|
||||
ShortcutControl::AddNewShortcutControlRow(shortcutTable, keyboardRemapControlObjects, it.first, it.second.targetShortcut);
|
||||
}
|
||||
lock.unlock();
|
||||
lockOSLevel.unlock();
|
||||
|
||||
// Load existing app-specific shortcuts into UI
|
||||
std::unique_lock<std::mutex> lockAppSpecific(keyboardManagerState.appSpecificShortcutReMap_mutex);
|
||||
// Iterate through all the apps
|
||||
for (const auto& itApp : keyboardManagerState.appSpecificShortcutReMap)
|
||||
{
|
||||
// Iterate through shortcuts for each app
|
||||
for (const auto& itShortcut : itApp.second)
|
||||
{
|
||||
ShortcutControl::AddNewShortcutControlRow(shortcutTable, keyboardRemapControlObjects, itShortcut.first, itShortcut.second.targetShortcut, itApp.first);
|
||||
}
|
||||
}
|
||||
lockAppSpecific.unlock();
|
||||
|
||||
// Apply button
|
||||
Button applyButton;
|
||||
@ -230,24 +283,40 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
|
||||
KeyboardManagerHelper::ErrorType isSuccess = KeyboardManagerHelper::ErrorType::NoError;
|
||||
// Clear existing shortcuts
|
||||
keyboardManagerState.ClearOSLevelShortcuts();
|
||||
DWORD successfulRemapCount = 0;
|
||||
keyboardManagerState.ClearAppSpecificShortcuts();
|
||||
DWORD successfulOSLevelRemapCount = 0;
|
||||
DWORD successfulAppSpecificRemapCount = 0;
|
||||
// Save the shortcuts that are valid and report if any of them were invalid
|
||||
for (int i = 0; i < ShortcutControl::shortcutRemapBuffer.size(); i++)
|
||||
{
|
||||
Shortcut originalShortcut = ShortcutControl::shortcutRemapBuffer[i][0];
|
||||
Shortcut newShortcut = ShortcutControl::shortcutRemapBuffer[i][1];
|
||||
Shortcut originalShortcut = ShortcutControl::shortcutRemapBuffer[i].first[0];
|
||||
Shortcut newShortcut = ShortcutControl::shortcutRemapBuffer[i].first[1];
|
||||
|
||||
if (originalShortcut.IsValidShortcut() && newShortcut.IsValidShortcut())
|
||||
{
|
||||
bool result = keyboardManagerState.AddOSLevelShortcut(originalShortcut, newShortcut);
|
||||
if (!result)
|
||||
if (ShortcutControl::shortcutRemapBuffer[i].second == L"")
|
||||
{
|
||||
isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful;
|
||||
// Tooltip is already shown for this row
|
||||
bool result = keyboardManagerState.AddOSLevelShortcut(originalShortcut, newShortcut);
|
||||
if (!result)
|
||||
{
|
||||
isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful;
|
||||
}
|
||||
else
|
||||
{
|
||||
successfulOSLevelRemapCount += 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
successfulRemapCount += 1;
|
||||
bool result = keyboardManagerState.AddAppSpecificShortcut(ShortcutControl::shortcutRemapBuffer[i].second, originalShortcut, newShortcut);
|
||||
if (!result)
|
||||
{
|
||||
isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful;
|
||||
}
|
||||
else
|
||||
{
|
||||
successfulAppSpecificRemapCount += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -256,7 +325,10 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
|
||||
}
|
||||
}
|
||||
|
||||
Trace::OSLevelShortcutRemapCount(successfulRemapCount);
|
||||
// Telemetry events
|
||||
Trace::OSLevelShortcutRemapCount(successfulOSLevelRemapCount);
|
||||
Trace::AppSpecificShortcutRemapCount(successfulAppSpecificRemapCount);
|
||||
|
||||
// Save the updated key remaps to file.
|
||||
bool saveResult = keyboardManagerState.SaveConfigToFile();
|
||||
if (!saveResult)
|
||||
|
@ -125,7 +125,7 @@ void KeyDropDownControl::SetSelectionHandler(Grid& table, StackPanel& singleKeyC
|
||||
});
|
||||
}
|
||||
|
||||
std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateShortcutSelection(Grid table, StackPanel shortcutControl, StackPanel parent, int colIndex, std::vector<std::vector<Shortcut>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects)
|
||||
std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateShortcutSelection(Grid table, StackPanel shortcutControl, StackPanel parent, int colIndex, std::vector<std::pair<std::vector<Shortcut>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox targetApp)
|
||||
{
|
||||
ComboBox currentDropDown = dropDown;
|
||||
int selectedKeyIndex = currentDropDown.SelectedIndex();
|
||||
@ -164,7 +164,7 @@ std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateSho
|
||||
// If not, add a new drop down
|
||||
else
|
||||
{
|
||||
AddDropDown(table, shortcutControl, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects);
|
||||
AddDropDown(table, shortcutControl, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp);
|
||||
}
|
||||
}
|
||||
// If last drop down and a modifier is selected but there are already max drop downs: warn the user
|
||||
@ -251,20 +251,27 @@ std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateSho
|
||||
{
|
||||
Shortcut tempShortcut;
|
||||
tempShortcut.SetKeyCodes(GetKeysFromStackPanel(parent));
|
||||
std::wstring appName = targetApp.Text().c_str();
|
||||
// Convert app name to lower case
|
||||
std::transform(appName.begin(), appName.end(), appName.begin(), towlower);
|
||||
|
||||
// Check if the value being set is the same as the other column
|
||||
if (shortcutRemapBuffer[rowIndex][std::abs(int(colIndex) - 1)] == tempShortcut && shortcutRemapBuffer[rowIndex][std::abs(int(colIndex) - 1)].IsValidShortcut() && tempShortcut.IsValidShortcut())
|
||||
if (shortcutRemapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)] == tempShortcut && shortcutRemapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)].IsValidShortcut() && tempShortcut.IsValidShortcut())
|
||||
{
|
||||
errorType = KeyboardManagerHelper::ErrorType::MapToSameShortcut;
|
||||
}
|
||||
|
||||
if (errorType == KeyboardManagerHelper::ErrorType::NoError && colIndex == 0)
|
||||
{
|
||||
// Check if the key is already remapped to something else
|
||||
// Check if the key is already remapped to something else for the same target app
|
||||
for (int i = 0; i < shortcutRemapBuffer.size(); i++)
|
||||
{
|
||||
if (i != rowIndex)
|
||||
std::wstring currAppName = shortcutRemapBuffer[i].second;
|
||||
std::transform(currAppName.begin(), currAppName.end(), currAppName.begin(), towlower);
|
||||
|
||||
if (i != rowIndex && currAppName == appName)
|
||||
{
|
||||
KeyboardManagerHelper::ErrorType result = Shortcut::DoKeysOverlap(shortcutRemapBuffer[i][colIndex], tempShortcut);
|
||||
KeyboardManagerHelper::ErrorType result = Shortcut::DoKeysOverlap(shortcutRemapBuffer[i].first[colIndex], tempShortcut);
|
||||
if (result != KeyboardManagerHelper::ErrorType::NoError)
|
||||
{
|
||||
errorType = result;
|
||||
@ -299,10 +306,10 @@ std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateSho
|
||||
}
|
||||
|
||||
// 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, int 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::pair<std::vector<Shortcut>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox& targetApp)
|
||||
{
|
||||
auto onSelectionChange = [&, table, shortcutControl, colIndex, parent](winrt::Windows::Foundation::IInspectable const& sender) {
|
||||
std::pair<KeyboardManagerHelper::ErrorType, int> validationResult = ValidateShortcutSelection(table, shortcutControl, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects);
|
||||
auto onSelectionChange = [&, table, shortcutControl, colIndex, parent, targetApp](winrt::Windows::Foundation::IInspectable const& sender) {
|
||||
std::pair<KeyboardManagerHelper::ErrorType, int> validationResult = ValidateShortcutSelection(table, shortcutControl, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp);
|
||||
|
||||
// Check if the drop down row index was identified from the return value of validateSelection
|
||||
if (validationResult.second != -1)
|
||||
@ -310,23 +317,13 @@ void KeyDropDownControl::SetSelectionHandler(Grid& table, StackPanel& shortcutCo
|
||||
// If an error occurred
|
||||
if (validationResult.first != KeyboardManagerHelper::ErrorType::NoError)
|
||||
{
|
||||
// Iterate over all drop downs from left to right in that row/col and validate if there is an error in any of the drop downs. After this the state should be error-free (if it is a valid shortcut)
|
||||
for (int i = 0; i < keyDropDownControlObjects.size(); i++)
|
||||
{
|
||||
// Check for errors only if the current selection is a valid shortcut
|
||||
Shortcut tempComputedShortcut;
|
||||
tempComputedShortcut.SetKeyCodes(keyDropDownControlObjects[i]->GetKeysFromStackPanel(parent));
|
||||
|
||||
// If the shortcut is valid and that drop down is not empty
|
||||
if (tempComputedShortcut.IsValidShortcut() && keyDropDownControlObjects[i]->GetComboBox().SelectedIndex() != -1)
|
||||
{
|
||||
keyDropDownControlObjects[i]->ValidateShortcutSelection(table, shortcutControl, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects);
|
||||
}
|
||||
}
|
||||
// Validate all the drop downs
|
||||
ValidateShortcutFromDropDownList(table, shortcutControl, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp);
|
||||
}
|
||||
|
||||
// Reset the buffer based on the new selected drop down items
|
||||
shortcutRemapBuffer[validationResult.second][colIndex].SetKeyCodes(GetKeysFromStackPanel(parent));
|
||||
shortcutRemapBuffer[validationResult.second].first[colIndex].SetKeyCodes(GetKeysFromStackPanel(parent));
|
||||
shortcutRemapBuffer[validationResult.second].second = targetApp.Text().c_str();
|
||||
}
|
||||
|
||||
// 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.
|
||||
@ -370,11 +367,11 @@ ComboBox KeyDropDownControl::GetComboBox()
|
||||
}
|
||||
|
||||
// Function to add a drop down to the shortcut stack panel
|
||||
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)
|
||||
void KeyDropDownControl::AddDropDown(Grid table, StackPanel shortcutControl, StackPanel parent, const int colIndex, std::vector<std::pair<std::vector<Shortcut>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox& targetApp)
|
||||
{
|
||||
keyDropDownControlObjects.push_back(std::move(std::unique_ptr<KeyDropDownControl>(new KeyDropDownControl(true))));
|
||||
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, targetApp);
|
||||
parent.UpdateLayout();
|
||||
}
|
||||
|
||||
@ -435,6 +432,24 @@ bool KeyDropDownControl::CheckRepeatedModifier(StackPanel parent, int selectedKe
|
||||
return matchPreviousModifier;
|
||||
}
|
||||
|
||||
// Function for validating the selection of shortcuts for all the associated drop downs
|
||||
void KeyDropDownControl::ValidateShortcutFromDropDownList(Grid table, StackPanel shortcutControl, StackPanel parent, int colIndex, std::vector<std::pair<std::vector<Shortcut>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox targetApp)
|
||||
{
|
||||
// Iterate over all drop downs from left to right in that row/col and validate if there is an error in any of the drop downs. After this the state should be error-free (if it is a valid shortcut)
|
||||
for (int i = 0; i < keyDropDownControlObjects.size(); i++)
|
||||
{
|
||||
// Check for errors only if the current selection is a valid shortcut
|
||||
Shortcut tempComputedShortcut;
|
||||
tempComputedShortcut.SetKeyCodes(keyDropDownControlObjects[i]->GetKeysFromStackPanel(parent));
|
||||
|
||||
// If the shortcut is valid and that drop down is not empty
|
||||
if (tempComputedShortcut.IsValidShortcut() && keyDropDownControlObjects[i]->GetComboBox().SelectedIndex() != -1)
|
||||
{
|
||||
keyDropDownControlObjects[i]->ValidateShortcutSelection(table, shortcutControl, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function to set the warning message
|
||||
void KeyDropDownControl::SetDropDownError(ComboBox currentDropDown, hstring message)
|
||||
{
|
||||
|
@ -36,10 +36,10 @@ public:
|
||||
void SetSelectionHandler(Grid& table, StackPanel& singleKeyControl, int colIndex, std::vector<std::vector<DWORD>>& singleKeyRemapBuffer);
|
||||
|
||||
// Function for validating the selection of shortcuts for the drop down
|
||||
std::pair<KeyboardManagerHelper::ErrorType, int> ValidateShortcutSelection(Grid table, StackPanel shortcutControl, StackPanel parent, int colIndex, std::vector<std::vector<Shortcut>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects);
|
||||
std::pair<KeyboardManagerHelper::ErrorType, int> ValidateShortcutSelection(Grid table, StackPanel shortcutControl, StackPanel parent, int colIndex, std::vector<std::pair<std::vector<Shortcut>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox targetApp);
|
||||
|
||||
// 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, int 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::pair<std::vector<Shortcut>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox& targetApp);
|
||||
|
||||
// Function to set the selected index of the drop down
|
||||
void SetSelectedIndex(int32_t index);
|
||||
@ -48,7 +48,7 @@ public:
|
||||
ComboBox GetComboBox();
|
||||
|
||||
// Function to add a drop down to the shortcut stack panel
|
||||
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);
|
||||
static void AddDropDown(Grid table, StackPanel shortcutControl, StackPanel parent, const int colIndex, std::vector<std::pair<std::vector<Shortcut>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox& targetApp);
|
||||
|
||||
// Function to get the list of key codes from the shortcut combo box stack panel
|
||||
static std::vector<DWORD> GetKeysFromStackPanel(StackPanel parent);
|
||||
@ -56,6 +56,9 @@ public:
|
||||
// Function to check if a modifier has been repeated in the previous drop downs
|
||||
static bool CheckRepeatedModifier(StackPanel parent, int selectedKeyIndex, const std::vector<DWORD>& keyCodeList);
|
||||
|
||||
// Function for validating the selection of shortcuts for all the associated drop downs
|
||||
static void ValidateShortcutFromDropDownList(Grid table, StackPanel shortcutControl, StackPanel parent, int colIndex, std::vector<std::pair<std::vector<Shortcut>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox targetApp);
|
||||
|
||||
// Function to set the warning message
|
||||
void SetDropDownError(ComboBox currentDropDown, hstring message);
|
||||
};
|
||||
|
@ -6,15 +6,18 @@
|
||||
HWND ShortcutControl::EditShortcutsWindowHandle = nullptr;
|
||||
KeyboardManagerState* ShortcutControl::keyboardManagerState = nullptr;
|
||||
// Initialized as new vector
|
||||
std::vector<std::vector<Shortcut>> ShortcutControl::shortcutRemapBuffer;
|
||||
std::vector<std::pair<std::vector<Shortcut>, std::wstring>> 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.
|
||||
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, std::wstring targetAppName)
|
||||
{
|
||||
// Textbox for target application
|
||||
TextBox targetAppTextBox;
|
||||
|
||||
// Create new ShortcutControl objects dynamically so that we does not get destructed
|
||||
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, 1))));
|
||||
newrow.push_back(std::move(std::unique_ptr<ShortcutControl>(new ShortcutControl(parent, 0, targetAppTextBox))));
|
||||
newrow.push_back(std::move(std::unique_ptr<ShortcutControl>(new ShortcutControl(parent, 1, targetAppTextBox))));
|
||||
keyboardRemapControlObjects.push_back(std::move(newrow));
|
||||
|
||||
// Add to grid
|
||||
@ -39,6 +42,37 @@ void ShortcutControl::AddNewShortcutControlRow(Grid& parent, std::vector<std::ve
|
||||
// ShortcutControl for the new shortcut
|
||||
parent.Children().Append(keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->getShortcutControl());
|
||||
|
||||
targetAppTextBox.Width(KeyboardManagerConstants::ShortcutTableDropDownWidth);
|
||||
targetAppTextBox.Margin({ 0, 0, 0, KeyboardManagerConstants::ShortcutTableDropDownSpacing });
|
||||
targetAppTextBox.VerticalAlignment(VerticalAlignment::Bottom);
|
||||
targetAppTextBox.HorizontalAlignment(HorizontalAlignment::Center);
|
||||
targetAppTextBox.PlaceholderText(KeyboardManagerConstants::DefaultAppName);
|
||||
targetAppTextBox.Text(targetAppName);
|
||||
|
||||
// LostFocus handler will be called whenever text is updated by a user and then they click something else or tab to another control. Does not get called if Text is updated while the TextBox isn't in focus (i.e. from code)
|
||||
targetAppTextBox.LostFocus([&keyboardRemapControlObjects, parent, targetAppTextBox](auto const& sender, auto const& e) {
|
||||
// Get index of targetAppTextBox button
|
||||
UIElementCollection children = parent.Children();
|
||||
uint32_t index;
|
||||
children.IndexOf(targetAppTextBox, index);
|
||||
uint32_t lastIndexInRow = index + ((KeyboardManagerConstants::ShortcutTableColCount - 1) - KeyboardManagerConstants::ShortcutTableTargetAppColIndex);
|
||||
// Calculate row index in the buffer from the grid child index (first set of children are header elements and then three children in each row)
|
||||
int rowIndex = (lastIndexInRow - KeyboardManagerConstants::ShortcutTableHeaderCount) / KeyboardManagerConstants::ShortcutTableColCount;
|
||||
|
||||
// Validate both set of drop downs
|
||||
KeyDropDownControl::ValidateShortcutFromDropDownList(parent, keyboardRemapControlObjects[rowIndex][0]->getShortcutControl(), keyboardRemapControlObjects[rowIndex][0]->shortcutDropDownStackPanel, 0, ShortcutControl::shortcutRemapBuffer, keyboardRemapControlObjects[rowIndex][0]->keyDropDownControlObjects, targetAppTextBox);
|
||||
KeyDropDownControl::ValidateShortcutFromDropDownList(parent, keyboardRemapControlObjects[rowIndex][1]->getShortcutControl(), keyboardRemapControlObjects[rowIndex][1]->shortcutDropDownStackPanel, 1, ShortcutControl::shortcutRemapBuffer, keyboardRemapControlObjects[rowIndex][1]->keyDropDownControlObjects, targetAppTextBox);
|
||||
|
||||
// Reset the buffer based on the selected drop down items
|
||||
shortcutRemapBuffer[rowIndex].first[0].SetKeyCodes(KeyDropDownControl::GetKeysFromStackPanel(keyboardRemapControlObjects[rowIndex][0]->shortcutDropDownStackPanel));
|
||||
shortcutRemapBuffer[rowIndex].first[1].SetKeyCodes(KeyDropDownControl::GetKeysFromStackPanel(keyboardRemapControlObjects[rowIndex][1]->shortcutDropDownStackPanel));
|
||||
shortcutRemapBuffer[rowIndex].second = targetAppTextBox.Text().c_str();
|
||||
});
|
||||
|
||||
parent.SetColumn(targetAppTextBox, KeyboardManagerConstants::ShortcutTableTargetAppColIndex);
|
||||
parent.SetRow(targetAppTextBox, parent.RowDefinitions().Size() - 1);
|
||||
parent.Children().Append(targetAppTextBox);
|
||||
|
||||
// Delete row button
|
||||
Windows::UI::Xaml::Controls::Button deleteShortcut;
|
||||
FontIcon deleteSymbol;
|
||||
@ -66,7 +100,7 @@ void ShortcutControl::AddNewShortcutControlRow(Grid& parent, std::vector<std::ve
|
||||
parent.Children().RemoveAt(lastIndexInRow - i);
|
||||
}
|
||||
|
||||
// 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 set of children are header elements and then three children in each row)
|
||||
int bufferIndex = (lastIndexInRow - KeyboardManagerConstants::ShortcutTableHeaderCount) / KeyboardManagerConstants::ShortcutTableColCount;
|
||||
// Delete the row definition
|
||||
parent.RowDefinitions().RemoveAt(bufferIndex + 1);
|
||||
@ -83,19 +117,20 @@ void ShortcutControl::AddNewShortcutControlRow(Grid& parent, std::vector<std::ve
|
||||
// Set the shortcut text if the two vectors are not empty (i.e. default args)
|
||||
if (originalKeys.IsValidShortcut() && newKeys.IsValidShortcut())
|
||||
{
|
||||
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][1]->AddShortcutToControl(newKeys, parent, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->shortcutDropDownStackPanel, *keyboardManagerState, 1);
|
||||
// change to load app name
|
||||
shortcutRemapBuffer.push_back(std::make_pair<std::vector<Shortcut>, std::wstring>(std::vector<Shortcut>{ Shortcut(), Shortcut() }, std::wstring(targetAppName)));
|
||||
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->AddShortcutToControl(originalKeys, parent, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->shortcutDropDownStackPanel, *keyboardManagerState, 0, targetAppTextBox);
|
||||
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->AddShortcutToControl(newKeys, parent, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->shortcutDropDownStackPanel, *keyboardManagerState, 1, targetAppTextBox);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Initialize both shortcuts as empty shortcuts
|
||||
shortcutRemapBuffer.push_back(std::vector<Shortcut>{ Shortcut(), Shortcut() });
|
||||
shortcutRemapBuffer.push_back(std::make_pair<std::vector<Shortcut>, std::wstring>(std::vector<Shortcut>{ Shortcut(), Shortcut() }, std::wstring(targetAppName)));
|
||||
}
|
||||
}
|
||||
|
||||
// Function to add a shortcut to the shortcut control as combo boxes
|
||||
void ShortcutControl::AddShortcutToControl(Shortcut& shortcut, Grid table, StackPanel parent, KeyboardManagerState& keyboardManagerState, const int colIndex)
|
||||
void ShortcutControl::AddShortcutToControl(Shortcut& shortcut, Grid table, StackPanel parent, KeyboardManagerState& keyboardManagerState, const int colIndex, TextBox targetApp)
|
||||
{
|
||||
// Delete the existing drop down menus
|
||||
parent.Children().Clear();
|
||||
@ -106,7 +141,7 @@ void ShortcutControl::AddShortcutToControl(Shortcut& shortcut, Grid table, Stack
|
||||
std::vector<DWORD> keyCodeList = keyboardManagerState.keyboardMap.GetKeyCodeList(true);
|
||||
if (shortcutKeyCodes.size() != 0)
|
||||
{
|
||||
KeyDropDownControl::AddDropDown(table, shortcutControlLayout, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects);
|
||||
KeyDropDownControl::AddDropDown(table, shortcutControlLayout, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp);
|
||||
for (int i = 0; i < shortcutKeyCodes.size(); i++)
|
||||
{
|
||||
// New drop down gets added automatically when the SelectedIndex is set
|
||||
@ -131,7 +166,7 @@ StackPanel ShortcutControl::getShortcutControl()
|
||||
}
|
||||
|
||||
// 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 int colIndex, Grid table)
|
||||
void ShortcutControl::createDetectShortcutWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, std::vector<std::pair<std::vector<Shortcut>, std::wstring>>& shortcutRemapBuffer, KeyboardManagerState& keyboardManagerState, const int colIndex, Grid table, TextBox targetApp)
|
||||
{
|
||||
// ContentDialog for detecting shortcuts. This is the parent UI element.
|
||||
ContentDialog detectShortcutBox;
|
||||
@ -164,14 +199,15 @@ void ShortcutControl::createDetectShortcutWindow(winrt::Windows::Foundation::IIn
|
||||
&shortcutRemapBuffer,
|
||||
unregisterKeys,
|
||||
colIndex,
|
||||
table] {
|
||||
table,
|
||||
targetApp] {
|
||||
// Save the detected shortcut in the linked text block
|
||||
Shortcut detectedShortcutKeys = keyboardManagerState.GetDetectedShortcut();
|
||||
|
||||
if (!detectedShortcutKeys.IsEmpty())
|
||||
{
|
||||
// The shortcut buffer gets set in this function
|
||||
AddShortcutToControl(detectedShortcutKeys, table, linkedShortcutStackPanel, keyboardManagerState, colIndex);
|
||||
AddShortcutToControl(detectedShortcutKeys, table, linkedShortcutStackPanel, keyboardManagerState, colIndex, targetApp);
|
||||
}
|
||||
// Hide the type shortcut UI
|
||||
detectShortcutBox.Hide();
|
||||
|
@ -7,9 +7,6 @@
|
||||
class ShortcutControl
|
||||
{
|
||||
private:
|
||||
// Textblock to display the selected shortcut
|
||||
TextBlock shortcutText;
|
||||
|
||||
// Stack panel for the drop downs to display the selected shortcut
|
||||
StackPanel shortcutDropDownStackPanel;
|
||||
|
||||
@ -25,21 +22,21 @@ public:
|
||||
// Pointer to the keyboard manager state
|
||||
static KeyboardManagerState* keyboardManagerState;
|
||||
// Stores the current list of remappings
|
||||
static std::vector<std::vector<Shortcut>> shortcutRemapBuffer;
|
||||
static std::vector<std::pair<std::vector<Shortcut>, std::wstring>> shortcutRemapBuffer;
|
||||
// Vector to store dynamically allocated KeyDropDownControl objects to avoid early destruction
|
||||
std::vector<std::unique_ptr<KeyDropDownControl>> keyDropDownControlObjects;
|
||||
|
||||
ShortcutControl(Grid table, const int colIndex)
|
||||
ShortcutControl(Grid table, const int colIndex, TextBox targetApp)
|
||||
{
|
||||
shortcutDropDownStackPanel.Spacing(10);
|
||||
shortcutDropDownStackPanel.Spacing(KeyboardManagerConstants::ShortcutTableDropDownSpacing);
|
||||
shortcutDropDownStackPanel.Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
|
||||
|
||||
typeShortcut.Content(winrt::box_value(L"Type Shortcut"));
|
||||
typeShortcut.Width(KeyboardManagerConstants::ShortcutTableDropDownWidth);
|
||||
typeShortcut.Click([&, table, colIndex](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
typeShortcut.Click([&, table, colIndex, targetApp](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
keyboardManagerState->SetUIState(KeyboardManagerUIState::DetectShortcutWindowActivated, EditShortcutsWindowHandle);
|
||||
// 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, targetApp);
|
||||
});
|
||||
|
||||
shortcutControlLayout.Margin({ 0, 0, 0, 10 });
|
||||
@ -47,19 +44,19 @@ public:
|
||||
|
||||
shortcutControlLayout.Children().Append(typeShortcut);
|
||||
shortcutControlLayout.Children().Append(shortcutDropDownStackPanel);
|
||||
KeyDropDownControl::AddDropDown(table, shortcutControlLayout, shortcutDropDownStackPanel, colIndex, shortcutRemapBuffer, keyDropDownControlObjects);
|
||||
KeyDropDownControl::AddDropDown(table, shortcutControlLayout, shortcutDropDownStackPanel, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp);
|
||||
shortcutControlLayout.UpdateLayout();
|
||||
}
|
||||
|
||||
// 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.
|
||||
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(), std::wstring targetAppName = L"");
|
||||
|
||||
// Function to add a shortcut to the shortcut control as combo boxes
|
||||
void AddShortcutToControl(Shortcut& shortcut, Grid table, StackPanel parent, KeyboardManagerState& keyboardManagerState, const int colIndex);
|
||||
void AddShortcutToControl(Shortcut& shortcut, Grid table, StackPanel parent, KeyboardManagerState& keyboardManagerState, const int colIndex, TextBox targetApp);
|
||||
|
||||
// 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();
|
||||
|
||||
// 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 int colIndex, Grid table);
|
||||
void createDetectShortcutWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, std::vector<std::pair<std::vector<Shortcut>, std::wstring>>& shortcutRemapBuffer, KeyboardManagerState& keyboardManagerState, const int colIndex, Grid table, TextBox targetApp);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user