Skip to content

Commit

Permalink
[Keyboard Manager] Add app-specific shortcuts to Remap shortcuts UI (#…
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
arjunbalgovind committed Jul 8, 2020
1 parent 411140c commit 4634085
Show file tree
Hide file tree
Showing 8 changed files with 217 additions and 75 deletions.
13 changes: 9 additions & 4 deletions src/modules/keyboardmanager/common/KeyboardManagerConstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;

Expand All @@ -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;
Expand All @@ -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";
}
11 changes: 11 additions & 0 deletions src/modules/keyboardmanager/common/trace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
}
3 changes: 3 additions & 0 deletions src/modules/keyboardmanager/common/trace.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,7 @@ class Trace

// 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;
};
106 changes: 89 additions & 17 deletions src/modules/keyboardmanager/ui/EditShortcutsWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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?"))
Expand Down Expand Up @@ -166,13 +192,16 @@ 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 });
shortcutTable.HorizontalAlignment(HorizontalAlignment::Stretch);
shortcutTable.ColumnDefinitions().Append(originalColumn);
shortcutTable.ColumnDefinitions().Append(arrowColumn);
shortcutTable.ColumnDefinitions().Append(newColumn);
shortcutTable.ColumnDefinitions().Append(targetAppColumn);
shortcutTable.ColumnDefinitions().Append(removeColumn);
shortcutTable.RowDefinitions().Append(RowDefinition());

Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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
Expand All @@ -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)
Expand Down

0 comments on commit 4634085

Please sign in to comment.