mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-04 10:16:24 +02:00
[KBM]Launch apps / URI with keyboard shortcuts, support chords (#30121)
* Working UI update with just runProgram Path and isRunProgram * First working, basic. no args or path, or setting change detections. * Revert and fixed. * Some clean up, working with config file monitor * Args and Start-in should be working. * File monitor, quotes, xaml screens one * Fixed enable/disable toogle from XAML * Code cleanup. * Betting logging. * Cleanup, start of RunProgramDescriptor and usage of run_non_elevated/run_elevated * Code moved to KeyboardEventHandlers, but not enabled since it won't build as is, needs elevation.h. Other testing.. * Key chords working, pretty much * Added gui for elevation level, need to refresh on change... * f: include shellapi.h and reference wil in KBMEL * run_elevated/run_non_elevated sorted out. Working! * Removed lots of old temp code. * Fix some speling errors. * Cleanup before trying to add a UI for the chord * Added "DifferentUser" option * Closer on UI for chords. * Better UI, lots working. * Clean up * Text for “Allow chords” – needs to look better… * Bugs and clean-up * Cleanup * Refactor and clean up. * More clean up * Some localization. * Don’t show “Allow chords“ to the “to” shortcut * Maybe better foreground after opening new app * Better chord matching. * Runprogram fix for stealing existing shortcut. * Better runProgram stuff * Temp commit * Working well * Toast test * More toast * Added File and Folder picker UI * Pre-check on run program file exists. * Refactor to SetupRunProgramControls * Open URI UI is going. * Open URI working well * Open URI stuff working well * Allowed AppSpecific shortcut and fixed backup/restore shortcut dups * Fixed settings screen * Start of code to find by name... * UI fixed * Small fixes * Some single edit code working. * UI getting better. * Fixes * Fixed and merge from main * UI updates * UI updates. * UI stuff * Fixed crash from move ui item locations. * Fixed crash from move ui item locations. * Added delete confirm * Basic sound working. * Localized some stuff * Added sounds * Better experiance when shortcut is in use. * UI tweaks * Fixed KBM ui for unicode shortcut not having "," * Some clean up * Cleanup * Cleanup * Fixed applyXamlStyling * Added back stuff lost in merge * applyXamlStyling, again * Fixed crash on change from non shortcut to shortcut * Update src/modules/keyboardmanager/KeyboardManagerEngineTest/KeyboardManagerEngineTest.vcxproj * Fixed some spelling type issues. * ImplementationLibrary 231216 * Comment bump to see if the Microsoft.Windows.ImplementationLibrary version thing gets picked up * Correct, Microsoft.Windows.ImplementationLibrary, finally? * Fixed two test that failed because we now allow key-chords. * Removed shortcut sounds. * use original behavior when "allow chords" is off in shortcut window * fix crash when editing a shortcut that has apps specified for it * split KBM chords with comma on dashboard page * Fix some spelling items. * More "spelling" * Fix XAML styling * align TextBlock and ToggleSwitch * fix cutoff issue at the top * increase ComboBox width * Added *Unsupported* for backwards compat on config of KBM * fix spellcheck * Fix crash on Remap key screen * Fixed Remap Keys ComboBox width too short. * Removed KBM Single Edit mode, fixed crash. * Fix Xaml with xaml cops * Fix crash on setting "target app" for some types of shortcuts. * Space to toggle chord, combobox back * fix spellcheck * fix some code nits * Code review updates. * Add exclusions to the bug report tool * Code review and kill CloseAndEndTask * Fix alignment / 3 comboboxes per row * Fix daily telemetry events to exclude start app and open URI * Add chords and remove app start and open uri from config telemetry * comma instead of plus in human readable shortcut telemetry data * Code review, restore default-old state when new row added in KBM * Code review, restore default-old state when new row added in KBM, part 2 * Still show target app on Settings * Only allow enabling chords for origin shortcuts --------- Co-authored-by: Andrey Nekrasov <yuyoyuppe@users.noreply.github.com> Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
This commit is contained in:
@@ -204,8 +204,9 @@ namespace BufferValidationHelpers
|
||||
}
|
||||
else
|
||||
{
|
||||
// warn and reset the drop down
|
||||
errorType = ShortcutErrorType::ShortcutNotMoreThanOneActionKey;
|
||||
// this used to "warn and reset the drop down" but for now, since we will allow Chords, we do allow this
|
||||
// leaving the here and commented out for posterity, for now.
|
||||
// errorType = ShortcutErrorType::ShortcutNotMoreThanOneActionKey;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -69,7 +69,7 @@ static IAsyncAction OnClickAccept(
|
||||
}
|
||||
|
||||
// Function to create the Edit Shortcuts Window
|
||||
inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardManagerState& keyboardManagerState, MappingConfiguration& mappingConfiguration)
|
||||
inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardManagerState& keyboardManagerState, MappingConfiguration& mappingConfiguration, std::wstring keysForShortcutToEdit, std::wstring action)
|
||||
{
|
||||
Logger::trace("CreateEditShortcutsWindowImpl()");
|
||||
auto locker = EventLocker::Get(KeyboardManagerConstants::EditorWindowEventName.c_str());
|
||||
@@ -107,12 +107,24 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardMa
|
||||
isEditShortcutsWindowRegistrationCompleted = true;
|
||||
}
|
||||
|
||||
// we might be passed via cmdline some keysForShortcutToEdit, this means we're editing just one shortcut
|
||||
if (!keysForShortcutToEdit.empty())
|
||||
{
|
||||
}
|
||||
|
||||
// Find coordinates of the screen where the settings window is placed.
|
||||
RECT desktopRect = UIHelpers::GetForegroundWindowDesktopRect();
|
||||
|
||||
// Calculate DPI dependent window size
|
||||
float windowWidth = EditorConstants::DefaultEditShortcutsWindowWidth;
|
||||
float windowHeight = EditorConstants::DefaultEditShortcutsWindowHeight;
|
||||
|
||||
if (!keysForShortcutToEdit.empty())
|
||||
{
|
||||
windowWidth = EditorConstants::DefaultEditSingleShortcutsWindowWidth;
|
||||
windowHeight = EditorConstants::DefaultEditSingleShortcutsWindowHeight;
|
||||
}
|
||||
|
||||
DPIAware::ConvertByCursorPosition(windowWidth, windowHeight);
|
||||
DPIAware::GetScreenDPIForCursor(g_currentDPI);
|
||||
|
||||
@@ -129,13 +141,13 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardMa
|
||||
NULL,
|
||||
hInst,
|
||||
NULL);
|
||||
|
||||
|
||||
if (_hWndEditShortcutsWindow == NULL)
|
||||
{
|
||||
MessageBox(NULL, GET_RESOURCE_STRING(IDS_CREATEWINDOWFAILED_ERRORMESSAGE).c_str(), GET_RESOURCE_STRING(IDS_CREATEWINDOWFAILED_ERRORTITLE).c_str(), NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Ensures the window is in foreground on first startup. If this is not done, the window appears behind because the thread is not on the foreground.
|
||||
if (_hWndEditShortcutsWindow)
|
||||
{
|
||||
@@ -157,7 +169,7 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardMa
|
||||
|
||||
// Create the xaml bridge object
|
||||
XamlBridge2 xamlBridge(_hWndEditShortcutsWindow);
|
||||
|
||||
|
||||
// Create the desktop window xaml source object and set its content
|
||||
hWndXamlIslandEditShortcutsWindow = xamlBridge.InitBridge();
|
||||
|
||||
@@ -227,15 +239,15 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardMa
|
||||
|
||||
// Store handle of edit shortcuts window
|
||||
ShortcutControl::editShortcutsWindowHandle = _hWndEditShortcutsWindow;
|
||||
|
||||
|
||||
// Store keyboard manager state
|
||||
ShortcutControl::keyboardManagerState = &keyboardManagerState;
|
||||
KeyDropDownControl::keyboardManagerState = &keyboardManagerState;
|
||||
KeyDropDownControl::mappingConfiguration = &mappingConfiguration;
|
||||
|
||||
|
||||
// Clear the shortcut remap buffer
|
||||
ShortcutControl::shortcutRemapBuffer.clear();
|
||||
|
||||
|
||||
// Vector to store dynamically allocated control objects to avoid early destruction
|
||||
std::vector<std::vector<std::unique_ptr<ShortcutControl>>> keyboardRemapControlObjects;
|
||||
|
||||
@@ -246,27 +258,9 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardMa
|
||||
// Create copy of the remaps to avoid concurrent access
|
||||
ShortcutRemapTable osLevelShortcutReMapCopy = mappingConfiguration.osLevelShortcutReMap;
|
||||
|
||||
for (const auto& it : osLevelShortcutReMapCopy)
|
||||
{
|
||||
ShortcutControl::AddNewShortcutControlRow(shortcutTable, keyboardRemapControlObjects, it.first, it.second.targetShortcut);
|
||||
}
|
||||
|
||||
// Load existing app-specific shortcuts into UI
|
||||
// Create copy of the remaps to avoid concurrent access
|
||||
AppSpecificShortcutRemapTable appSpecificShortcutReMapCopy = mappingConfiguration.appSpecificShortcutReMap;
|
||||
|
||||
// Iterate through all the apps
|
||||
for (const auto& itApp : appSpecificShortcutReMapCopy)
|
||||
{
|
||||
// Iterate through shortcuts for each app
|
||||
for (const auto& itShortcut : itApp.second)
|
||||
{
|
||||
ShortcutControl::AddNewShortcutControlRow(shortcutTable, keyboardRemapControlObjects, itShortcut.first, itShortcut.second.targetShortcut, itApp.first);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply button
|
||||
Button applyButton;
|
||||
applyButton.Name(L"applyButton");
|
||||
applyButton.Content(winrt::box_value(GET_RESOURCE_STRING(IDS_OK_BUTTON)));
|
||||
applyButton.Style(AccentButtonStyle());
|
||||
applyButton.MinWidth(EditorConstants::HeaderButtonWidth);
|
||||
@@ -284,7 +278,44 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardMa
|
||||
OnClickAccept(keyboardManagerState, applyButton.XamlRoot(), ApplyRemappings);
|
||||
});
|
||||
|
||||
auto OnClickAcceptNoCheckFn = ApplyRemappings;
|
||||
|
||||
for (const auto& it : osLevelShortcutReMapCopy)
|
||||
{
|
||||
auto isHidden = false;
|
||||
|
||||
// check to see if this should be hidden because it's NOT the one we are looking for.
|
||||
// It will still be there for backward compatability, just not visible
|
||||
if (!keysForShortcutToEdit.empty())
|
||||
{
|
||||
isHidden = (keysForShortcutToEdit != it.first.ToHstringVK());
|
||||
}
|
||||
|
||||
ShortcutControl::AddNewShortcutControlRow(shortcutTable, keyboardRemapControlObjects, it.first, it.second.targetShortcut, L"");
|
||||
}
|
||||
|
||||
// Load existing app-specific shortcuts into UI
|
||||
// Create copy of the remaps to avoid concurrent access
|
||||
AppSpecificShortcutRemapTable appSpecificShortcutReMapCopy = mappingConfiguration.appSpecificShortcutReMap;
|
||||
|
||||
// Iterate through all the apps
|
||||
for (const auto& itApp : appSpecificShortcutReMapCopy)
|
||||
{
|
||||
// Iterate through shortcuts for each app
|
||||
for (const auto& itShortcut : itApp.second)
|
||||
{
|
||||
auto isHidden = false;
|
||||
if (!keysForShortcutToEdit.empty())
|
||||
{
|
||||
isHidden = (keysForShortcutToEdit != itShortcut.first.ToHstringVK());
|
||||
}
|
||||
|
||||
ShortcutControl::AddNewShortcutControlRow(shortcutTable, keyboardRemapControlObjects, itShortcut.first, itShortcut.second.targetShortcut, itApp.first);
|
||||
}
|
||||
}
|
||||
|
||||
header.Children().Append(headerText);
|
||||
|
||||
header.Children().Append(applyButton);
|
||||
header.Children().Append(cancelButton);
|
||||
|
||||
@@ -299,17 +330,41 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardMa
|
||||
addShortcut.Margin({ 10, 10, 0, 25 });
|
||||
addShortcut.Style(AccentButtonStyle());
|
||||
addShortcut.Click([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
ShortcutControl::AddNewShortcutControlRow(shortcutTable, keyboardRemapControlObjects);
|
||||
ShortcutControl& newShortcut = ShortcutControl::AddNewShortcutControlRow(shortcutTable, keyboardRemapControlObjects);
|
||||
|
||||
// Whenever a remap is added move to the bottom of the screen
|
||||
scrollViewer.ChangeView(nullptr, scrollViewer.ScrollableHeight(), nullptr);
|
||||
|
||||
// Set focus to the first Type Button in the newly added row
|
||||
UIHelpers::SetFocusOnTypeButtonInLastRow(shortcutTable, EditorConstants::ShortcutTableColCount);
|
||||
|
||||
//newShortcut.OpenNewShortcutControlRow(shortcutTable, shortcutTable.Children().GetAt(shortcutTable.Children().Size() - 1).as<StackPanel>());
|
||||
});
|
||||
|
||||
// if this is a delete action we just want to quick load the screen to delete the shortcut and close
|
||||
// this is so we can delete from the KBM settings screen
|
||||
if (action == L"isDelete")
|
||||
{
|
||||
auto indexToDelete = -1;
|
||||
for (int i = 0; i < ShortcutControl::shortcutRemapBuffer.size(); i++)
|
||||
{
|
||||
auto tempShortcut = std::get<Shortcut>(ShortcutControl::shortcutRemapBuffer[i].first[0]);
|
||||
if (tempShortcut.ToHstringVK() == keysForShortcutToEdit)
|
||||
{
|
||||
indexToDelete = i;
|
||||
}
|
||||
}
|
||||
if (indexToDelete >= 0)
|
||||
{
|
||||
ShortcutControl::shortcutRemapBuffer.erase(ShortcutControl::shortcutRemapBuffer.begin() + indexToDelete);
|
||||
}
|
||||
OnClickAcceptNoCheckFn();
|
||||
return;
|
||||
}
|
||||
|
||||
// Remap shortcut button content
|
||||
StackPanel addShortcutContent;
|
||||
|
||||
addShortcutContent.Orientation(Orientation::Horizontal);
|
||||
addShortcutContent.Spacing(10);
|
||||
addShortcutContent.Children().Append(SymbolIcon(Symbol::Add));
|
||||
@@ -333,6 +388,7 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardMa
|
||||
mappingsPanel.Children().Append(addShortcut);
|
||||
|
||||
// Remapping table should be scrollable
|
||||
|
||||
scrollViewer.Content(mappingsPanel);
|
||||
|
||||
RelativePanel xamlContainer;
|
||||
@@ -347,6 +403,7 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardMa
|
||||
xamlContainer.Children().Append(header);
|
||||
xamlContainer.Children().Append(helperText);
|
||||
xamlContainer.Children().Append(scrollViewer);
|
||||
|
||||
try
|
||||
{
|
||||
// If a layout update has been triggered by other methods (e.g.: adapting to zoom level), this may throw an exception.
|
||||
@@ -368,6 +425,7 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardMa
|
||||
// Mica isn't available
|
||||
xamlContainer.Background(Application::Current().Resources().Lookup(box_value(L"ApplicationPageBackgroundThemeBrush")).as<Media::SolidColorBrush>());
|
||||
}
|
||||
|
||||
Window::Current().Content(xamlContent);
|
||||
|
||||
////End XAML Island section
|
||||
@@ -389,10 +447,10 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardMa
|
||||
keyboardManagerState.ClearRegisteredKeyDelays();
|
||||
}
|
||||
|
||||
void CreateEditShortcutsWindow(HINSTANCE hInst, KBMEditor::KeyboardManagerState& keyboardManagerState, MappingConfiguration& mappingConfiguration)
|
||||
void CreateEditShortcutsWindow(HINSTANCE hInst, KBMEditor::KeyboardManagerState& keyboardManagerState, MappingConfiguration& mappingConfiguration, std::wstring keysForShortcutToEdit, std::wstring action)
|
||||
{
|
||||
// Move implementation into the separate method so resources get destroyed correctly
|
||||
CreateEditShortcutsWindowImpl(hInst, keyboardManagerState, mappingConfiguration);
|
||||
CreateEditShortcutsWindowImpl(hInst, keyboardManagerState, mappingConfiguration, keysForShortcutToEdit, action);
|
||||
|
||||
// Calling ClearXamlIslands() outside of the message loop is not enough to prevent
|
||||
// Microsoft.UI.XAML.dll from crashing during deinitialization, see https://github.com/microsoft/PowerToys/issues/10906
|
||||
@@ -420,7 +478,9 @@ LRESULT CALLBACK EditShortcutsWindowProc(HWND hWnd, UINT messageCode, WPARAM wPa
|
||||
LPMINMAXINFO mmi = reinterpret_cast<LPMINMAXINFO>(lParam);
|
||||
float minWidth = EditorConstants::MinimumEditShortcutsWindowWidth;
|
||||
float minHeight = EditorConstants::MinimumEditShortcutsWindowHeight;
|
||||
|
||||
DPIAware::Convert(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONULL), minWidth, minHeight);
|
||||
|
||||
mmi->ptMinTrackSize.x = static_cast<LONG>(minWidth);
|
||||
mmi->ptMinTrackSize.y = static_cast<LONG>(minHeight);
|
||||
}
|
||||
@@ -453,8 +513,7 @@ LRESULT CALLBACK EditShortcutsWindowProc(HWND hWnd, UINT messageCode, WPARAM wPa
|
||||
rect->top,
|
||||
rect->right - rect->left,
|
||||
rect->bottom - rect->top,
|
||||
SWP_NOZORDER | SWP_NOACTIVATE
|
||||
);
|
||||
SWP_NOZORDER | SWP_NOACTIVATE);
|
||||
|
||||
Logger::trace(L"WM_DPICHANGED: new dpi {} rect {} {} ", newDPI, rect->right - rect->left, rect->bottom - rect->top);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace KBMEditor
|
||||
class MappingConfiguration;
|
||||
|
||||
// Function to create the Edit Shortcuts Window
|
||||
void CreateEditShortcutsWindow(HINSTANCE hInst, KBMEditor::KeyboardManagerState& keyboardManagerState, MappingConfiguration& mappingConfiguration);
|
||||
void CreateEditShortcutsWindow(HINSTANCE hInst, KBMEditor::KeyboardManagerState& keyboardManagerState, MappingConfiguration& mappingConfiguration, std::wstring keysForShortcutToEdit, std::wstring action);
|
||||
|
||||
// Function to check if there is already a window active if yes bring to foreground
|
||||
bool CheckEditShortcutsWindowActive();
|
||||
|
||||
@@ -10,8 +10,13 @@ namespace EditorConstants
|
||||
inline const int EditKeyboardTableMinWidth = 700;
|
||||
inline const int DefaultEditShortcutsWindowWidth = 1410;
|
||||
inline const int DefaultEditShortcutsWindowHeight = 600;
|
||||
inline const int DefaultEditSingleShortcutsWindowWidth = 1080;
|
||||
inline const int DefaultEditSingleShortcutsWindowHeight = 400;
|
||||
inline const int MinimumEditShortcutsWindowWidth = 500;
|
||||
inline const int MinimumEditShortcutsWindowHeight = 500;
|
||||
inline const int MinimumEditSingleShortcutsWindowWidth = 500;
|
||||
inline const int MinimumEditSingleShortcutsWindowHeight = 600;
|
||||
|
||||
inline const int EditShortcutsTableMinWidth = 1000;
|
||||
|
||||
// Key Remap table constants
|
||||
|
||||
@@ -52,6 +52,16 @@ namespace EditorHelpers
|
||||
// Function to return true if the shortcut is valid. A valid shortcut has atleast one modifier, as well as an action key
|
||||
bool IsValidShortcut(Shortcut shortcut)
|
||||
{
|
||||
if (shortcut.operationType == Shortcut::OperationType::RunProgram && shortcut.runProgramFilePath.length() > 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (shortcut.operationType == Shortcut::OperationType::OpenURI && shortcut.uriToOpen.length() > 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (shortcut.actionKey != NULL)
|
||||
{
|
||||
if (shortcut.winKey != ModifierKey::Disabled || shortcut.ctrlKey != ModifierKey::Disabled || shortcut.altKey != ModifierKey::Disabled || shortcut.shiftKey != ModifierKey::Disabled)
|
||||
|
||||
@@ -30,6 +30,11 @@ DWORD KeyDropDownControl::GetSelectedValue(ComboBox comboBox)
|
||||
return stoul(std::wstring(value));
|
||||
}
|
||||
|
||||
DWORD KeyDropDownControl::GetSelectedValue(TextBlock text)
|
||||
{
|
||||
return keyboardManagerState->keyboardMap.GetKeyFromName(std::wstring{ text.Text() });
|
||||
}
|
||||
|
||||
void KeyDropDownControl::SetSelectedValue(std::wstring value)
|
||||
{
|
||||
this->dropDown.as<ComboBox>().SelectedValue(winrt::box_value(value));
|
||||
@@ -87,7 +92,7 @@ void KeyDropDownControl::SetDefaultProperties(bool isShortcut, bool renderDisabl
|
||||
auto child0 = Media::VisualTreeHelper::GetChild(combo, 0);
|
||||
if (!child0)
|
||||
return;
|
||||
|
||||
|
||||
auto grid = child0.as<Grid>();
|
||||
if (!grid)
|
||||
return;
|
||||
@@ -95,7 +100,7 @@ void KeyDropDownControl::SetDefaultProperties(bool isShortcut, bool renderDisabl
|
||||
auto& gridChildren = grid.Children();
|
||||
if (!gridChildren)
|
||||
return;
|
||||
|
||||
|
||||
gridChildren.Append(warningTip);
|
||||
});
|
||||
|
||||
@@ -287,10 +292,8 @@ void KeyDropDownControl::SetSelectionHandler(StackPanel& table, StackPanel row,
|
||||
}
|
||||
else
|
||||
{
|
||||
Shortcut tempShortcut;
|
||||
tempShortcut.SetKeyCodes(selectedKeyCodes);
|
||||
// Assign instead of setting the value in the buffer since the previous value may not be a Shortcut
|
||||
shortcutRemapBuffer[validationResult.second].first[colIndex] = tempShortcut;
|
||||
shortcutRemapBuffer[validationResult.second].first[colIndex] = Shortcut(selectedKeyCodes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -364,10 +367,31 @@ std::vector<int32_t> KeyDropDownControl::GetSelectedCodesFromStackPanel(Variable
|
||||
std::vector<int32_t> selectedKeyCodes;
|
||||
|
||||
// Get selected indices for each drop down
|
||||
for (int i = 0; i < (int)parent.Children().Size(); i++)
|
||||
for (uint32_t i = 0; i < parent.Children().Size(); i++)
|
||||
{
|
||||
ComboBox ItDropDown = parent.Children().GetAt(i).as<ComboBox>();
|
||||
selectedKeyCodes.push_back(GetSelectedValue(ItDropDown));
|
||||
if (auto ItDropDown = parent.Children().GetAt(i).try_as<ComboBox>(); ItDropDown)
|
||||
{
|
||||
selectedKeyCodes.push_back(GetSelectedValue(ItDropDown));
|
||||
}
|
||||
|
||||
// If it's a ShortcutControl -> use its layout, see KeyboardManagerState::AddKeyToLayout
|
||||
else if (auto sp = parent.Children().GetAt(i).try_as<StackPanel>(); sp)
|
||||
{
|
||||
for (uint32_t j = 0; j < sp.Children().Size(); ++j)
|
||||
{
|
||||
auto border = sp.Children().GetAt(j).try_as<Border>();
|
||||
|
||||
// if this is null then this is a different layout
|
||||
// likely because this not a shortcut to another shortcut but rather
|
||||
// run app or open uri
|
||||
|
||||
if (border != nullptr)
|
||||
{
|
||||
auto textBlock = border.Child().try_as<TextBlock>();
|
||||
selectedKeyCodes.push_back(GetSelectedValue(textBlock));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return selectedKeyCodes;
|
||||
@@ -432,27 +456,36 @@ void KeyDropDownControl::AddShortcutToControl(Shortcut shortcut, StackPanel tabl
|
||||
// Remove references to the old drop down objects to destroy them
|
||||
keyDropDownControlObjects.clear();
|
||||
std::vector<DWORD> shortcutKeyCodes = shortcut.GetKeyCodes();
|
||||
if (shortcutKeyCodes.size() != 0)
|
||||
|
||||
auto secondKey = shortcut.GetSecondKey();
|
||||
|
||||
bool ignoreWarning = false;
|
||||
|
||||
// If more than one key is to be added, ignore a shortcut to key warning on partially entering the remapping
|
||||
if (shortcutKeyCodes.size() > 1)
|
||||
{
|
||||
bool ignoreWarning = false;
|
||||
ignoreWarning = true;
|
||||
}
|
||||
|
||||
// If more than one key is to be added, ignore a shortcut to key warning on partially entering the remapping
|
||||
if (shortcutKeyCodes.size() > 1)
|
||||
KeyDropDownControl::AddDropDown(table, row, parent, colIndex, remapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, isSingleKeyWindow, ignoreWarning);
|
||||
|
||||
for (int i = 0; i < shortcutKeyCodes.size(); i++)
|
||||
{
|
||||
// New drop down gets added automatically when the SelectedValue(key code) is set
|
||||
if (i < (int)parent.Children().Size())
|
||||
{
|
||||
ignoreWarning = true;
|
||||
ComboBox currentDropDown = parent.Children().GetAt(i).as<ComboBox>();
|
||||
currentDropDown.SelectedValue(winrt::box_value(std::to_wstring(shortcutKeyCodes[i])));
|
||||
}
|
||||
}
|
||||
|
||||
if (shortcut.HasChord())
|
||||
{
|
||||
// if this has a chord, render it last
|
||||
KeyDropDownControl::AddDropDown(table, row, parent, colIndex, remapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, isSingleKeyWindow, ignoreWarning);
|
||||
|
||||
for (int i = 0; i < shortcutKeyCodes.size(); i++)
|
||||
{
|
||||
// New drop down gets added automatically when the SelectedValue(key code) is set
|
||||
if (i < (int)parent.Children().Size())
|
||||
{
|
||||
ComboBox currentDropDown = parent.Children().GetAt(i).as<ComboBox>();
|
||||
currentDropDown.SelectedValue(winrt::box_value(std::to_wstring(shortcutKeyCodes[i])));
|
||||
}
|
||||
}
|
||||
auto nextI = static_cast<int>(shortcutKeyCodes.size());
|
||||
ComboBox currentDropDown = parent.Children().GetAt(nextI).as<ComboBox>();
|
||||
currentDropDown.SelectedValue(winrt::box_value(std::to_wstring(shortcut.GetSecondKey())));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ private:
|
||||
|
||||
// Get selected value of dropdown or -1 if nothing is selected
|
||||
static DWORD GetSelectedValue(ComboBox comboBox);
|
||||
static DWORD GetSelectedValue(TextBlock text);
|
||||
|
||||
// Function to set accessible name for combobox
|
||||
static void SetAccessibleNameForComboBox(ComboBox dropDown, int index);
|
||||
@@ -110,7 +111,7 @@ public:
|
||||
static void AddShortcutToControl(Shortcut shortcut, StackPanel table, VariableSizedWrapGrid parent, KBMEditor::KeyboardManagerState& keyboardManagerState, const int colIndex, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, RemapBuffer& remapBuffer, StackPanel row, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow);
|
||||
|
||||
// Get keys name list depending if Disable is in dropdown
|
||||
static std::vector<std::pair<DWORD,std::wstring>> GetKeyList(bool isShortcut, bool renderDisable);
|
||||
static std::vector<std::pair<DWORD, std::wstring>> GetKeyList(bool isShortcut, bool renderDisable);
|
||||
|
||||
// Get number of selected keys. Do not count -1 and 0 values as they stand for Not selected and None
|
||||
static int GetNumberOfSelectedKeys(std::vector<int32_t> keys);
|
||||
|
||||
@@ -22,6 +22,16 @@ namespace KeyboardManagerEditorStrings
|
||||
return GET_RESOURCE_STRING(IDS_MAPPING_TYPE_DROPDOWN_KEY_SHORTCUT);
|
||||
}
|
||||
|
||||
inline std::wstring MappingTypeRunProgram()
|
||||
{
|
||||
return GET_RESOURCE_STRING(IDS_MAPPING_TYPE_DROPDOWN_RUN_PROGRAM);
|
||||
}
|
||||
|
||||
inline std::wstring MappingTypeOpenUri()
|
||||
{
|
||||
return GET_RESOURCE_STRING(IDS_MAPPING_TYPE_DROPDOWN_OPEN_URI);
|
||||
}
|
||||
|
||||
// Function to return the error message
|
||||
winrt::hstring GetErrorMessage(ShortcutErrorType errorType);
|
||||
}
|
||||
|
||||
@@ -110,13 +110,13 @@ void KeyboardManagerState::ConfigureDetectSingleKeyRemapUI(const StackPanel& tex
|
||||
currentSingleKeyUI = textBlock.as<winrt::Windows::Foundation::IInspectable>();
|
||||
}
|
||||
|
||||
void KeyboardManagerState::AddKeyToLayout(const StackPanel& panel, const hstring& key)
|
||||
TextBlock KeyboardManagerState::AddKeyToLayout(const StackPanel& panel, const hstring& key)
|
||||
{
|
||||
// Textblock to display the detected key
|
||||
TextBlock remapKey;
|
||||
Border border;
|
||||
|
||||
border.Padding({ 20, 10, 20, 10 });
|
||||
border.Padding({ 10, 5, 10, 5 });
|
||||
border.Margin({ 0, 0, 10, 0 });
|
||||
|
||||
// Based on settings-ui\Settings.UI\SettingsXAML\Controls\KeyVisual\KeyVisual.xaml
|
||||
@@ -127,12 +127,15 @@ void KeyboardManagerState::AddKeyToLayout(const StackPanel& panel, const hstring
|
||||
remapKey.Foreground(Application::Current().Resources().Lookup(box_value(L"ButtonForeground")).as<Media::Brush>());
|
||||
remapKey.FontWeight(Text::FontWeights::SemiBold());
|
||||
|
||||
remapKey.FontSize(20);
|
||||
remapKey.FontSize(14);
|
||||
|
||||
border.HorizontalAlignment(HorizontalAlignment::Left);
|
||||
border.Child(remapKey);
|
||||
|
||||
remapKey.Text(key);
|
||||
panel.Children().Append(border);
|
||||
|
||||
return remapKey;
|
||||
}
|
||||
|
||||
// Function to update the detect shortcut UI based on the entered keys
|
||||
@@ -167,17 +170,39 @@ void KeyboardManagerState::UpdateDetectShortcutUI()
|
||||
currentShortcutUI2.as<StackPanel>().Visibility(Visibility::Collapsed);
|
||||
}
|
||||
|
||||
auto lastStackPanel = currentShortcutUI2.as<StackPanel>();
|
||||
for (int i = 0; i < shortcut.size(); i++)
|
||||
{
|
||||
if (i < 3)
|
||||
{
|
||||
AddKeyToLayout(currentShortcutUI1.as<StackPanel>(), shortcut[i]);
|
||||
lastStackPanel = currentShortcutUI1.as<StackPanel>();
|
||||
}
|
||||
else
|
||||
{
|
||||
AddKeyToLayout(currentShortcutUI2.as<StackPanel>(), shortcut[i]);
|
||||
lastStackPanel = currentShortcutUI2.as<StackPanel>();
|
||||
}
|
||||
}
|
||||
|
||||
if (!AllowChord)
|
||||
{
|
||||
detectedShortcut.secondKey = NULL;
|
||||
}
|
||||
|
||||
// add a TextBlock, to show what shortcut in text, e.g.: "CTRL+j, k" OR "CTRL+j, CTRL+k".
|
||||
if (detectedShortcut.HasChord())
|
||||
{
|
||||
TextBlock txtComma;
|
||||
txtComma.Text(L",");
|
||||
txtComma.FontSize(20);
|
||||
txtComma.Padding({ 0, 0, 10, 0 });
|
||||
txtComma.VerticalAlignment(VerticalAlignment::Bottom);
|
||||
txtComma.TextAlignment(TextAlignment::Left);
|
||||
lastStackPanel.Children().Append(txtComma);
|
||||
AddKeyToLayout(lastStackPanel, EditorHelpers::GetKeyVector(Shortcut(detectedShortcutCopy.secondKey), keyboardMap)[0]);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// If a layout update has been triggered by other methods (e.g.: adapting to zoom level), this may throw an exception.
|
||||
@@ -228,6 +253,12 @@ Shortcut KeyboardManagerState::GetDetectedShortcut()
|
||||
return currentShortcut;
|
||||
}
|
||||
|
||||
void KeyboardManagerState::SetDetectedShortcut(Shortcut shortcut)
|
||||
{
|
||||
detectedShortcut = shortcut;
|
||||
UpdateDetectShortcutUI();
|
||||
}
|
||||
|
||||
// Function to return the currently detected remap key which is displayed on the UI
|
||||
DWORD KeyboardManagerState::GetDetectedSingleRemapKey()
|
||||
{
|
||||
@@ -246,9 +277,57 @@ void KeyboardManagerState::SelectDetectedRemapKey(DWORD key)
|
||||
void KeyboardManagerState::SelectDetectedShortcut(DWORD key)
|
||||
{
|
||||
// Set the new key and store if a change occurred
|
||||
std::unique_lock<std::mutex> lock(detectedShortcut_mutex);
|
||||
bool updateUI = detectedShortcut.SetKey(key);
|
||||
lock.unlock();
|
||||
bool updateUI = false;
|
||||
|
||||
if (AllowChord)
|
||||
{
|
||||
// Code to determine if we're building/updating a chord.
|
||||
auto currentFirstKey = detectedShortcut.GetActionKey();
|
||||
auto currentSecondKey = detectedShortcut.GetSecondKey();
|
||||
|
||||
Shortcut tempShortcut = Shortcut(key);
|
||||
bool isKeyActionTypeKey = (tempShortcut.actionKey != NULL);
|
||||
|
||||
if (isKeyActionTypeKey)
|
||||
{
|
||||
// we want a chord and already have the first key set
|
||||
std::unique_lock<std::mutex> lock(detectedShortcut_mutex);
|
||||
|
||||
if (currentFirstKey == NULL)
|
||||
{
|
||||
Logger::trace(L"AllowChord AND no first");
|
||||
updateUI = detectedShortcut.SetKey(key);
|
||||
}
|
||||
else if (currentSecondKey == NULL)
|
||||
{
|
||||
// we don't have the second key, set it now
|
||||
Logger::trace(L"AllowChord AND we have first key of {}, will use {}", currentFirstKey, key);
|
||||
updateUI = detectedShortcut.SetSecondKey(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
// we already have the second key, swap it to first, and use new as second
|
||||
Logger::trace(L"DO have secondKey, will make first {} and second {}", currentSecondKey, key);
|
||||
detectedShortcut.actionKey = currentSecondKey;
|
||||
detectedShortcut.secondKey = key;
|
||||
updateUI = true;
|
||||
}
|
||||
updateUI = true;
|
||||
lock.unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(detectedShortcut_mutex);
|
||||
updateUI = detectedShortcut.SetKey(key);
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(detectedShortcut_mutex);
|
||||
updateUI = detectedShortcut.SetKey(key);
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
if (updateUI)
|
||||
{
|
||||
@@ -261,9 +340,12 @@ void KeyboardManagerState::SelectDetectedShortcut(DWORD key)
|
||||
void KeyboardManagerState::ResetDetectedShortcutKey(DWORD key)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(detectedShortcut_mutex);
|
||||
detectedShortcut.ResetKey(key);
|
||||
// only clear if mod, not if action, since we need to keek actionKey and secondKey for chord
|
||||
if (Shortcut::IsModifier(key))
|
||||
{
|
||||
detectedShortcut.ResetKey(key);
|
||||
}
|
||||
}
|
||||
|
||||
// Function which can be used in HandleKeyboardHookEvent before the single key remap event to use the UI and suppress events while the remap window is active.
|
||||
Helpers::KeyboardHookDecision KeyboardManagerState::DetectSingleRemapKeyUIBackend(LowlevelKeyboardEvent* data)
|
||||
{
|
||||
@@ -372,6 +454,12 @@ void KeyboardManagerState::ClearRegisteredKeyDelays()
|
||||
keyDelays.clear();
|
||||
}
|
||||
|
||||
void KBMEditor::KeyboardManagerState::ClearStoredShortcut()
|
||||
{
|
||||
std::scoped_lock<std::mutex> detectedShortcut_lock(detectedShortcut_mutex);
|
||||
detectedShortcut.Reset();
|
||||
}
|
||||
|
||||
bool KeyboardManagerState::HandleKeyDelayEvent(LowlevelKeyboardEvent* ev)
|
||||
{
|
||||
if (currentUIWindow != GetForegroundWindow())
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace Helpers
|
||||
namespace winrt::Windows::UI::Xaml::Controls
|
||||
{
|
||||
struct StackPanel;
|
||||
struct TextBlock;
|
||||
}
|
||||
|
||||
namespace KBMEditor
|
||||
@@ -80,10 +81,15 @@ namespace KBMEditor
|
||||
std::map<DWORD, std::unique_ptr<KeyDelay>> keyDelays;
|
||||
std::mutex keyDelays_mutex;
|
||||
|
||||
// Display a key by appending a border Control as a child of the panel.
|
||||
void AddKeyToLayout(const winrt::Windows::UI::Xaml::Controls::StackPanel& panel, const winrt::hstring& key);
|
||||
|
||||
public:
|
||||
// Display a key by appending a border Control as a child of the panel.
|
||||
winrt::Windows::UI::Xaml::Controls::TextBlock AddKeyToLayout(const winrt::Windows::UI::Xaml::Controls::StackPanel& panel, const winrt::hstring& key);
|
||||
|
||||
|
||||
|
||||
// flag to set if we want to allow building a chord
|
||||
bool AllowChord = false;
|
||||
|
||||
// Stores the keyboard layout
|
||||
LayoutMap keyboardMap;
|
||||
|
||||
@@ -120,6 +126,9 @@ namespace KBMEditor
|
||||
// Function to return the currently detected shortcut which is displayed on the UI
|
||||
Shortcut GetDetectedShortcut();
|
||||
|
||||
// Function to SetDetectedShortcut and also UpdateDetectShortcutUI
|
||||
void KeyboardManagerState::SetDetectedShortcut(Shortcut shortcut);
|
||||
|
||||
// Function to return the currently detected remap key which is displayed on the UI
|
||||
DWORD GetDetectedSingleRemapKey();
|
||||
|
||||
@@ -146,6 +155,8 @@ namespace KBMEditor
|
||||
// Function to clear all the registered key delays
|
||||
void ClearRegisteredKeyDelays();
|
||||
|
||||
void ClearStoredShortcut();
|
||||
|
||||
// Handle a key event, for a delayed key.
|
||||
bool HandleKeyDelayEvent(LowlevelKeyboardEvent* ev);
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "pch.h"
|
||||
#include "ShortcutControl.h"
|
||||
|
||||
#include <Windows.h>
|
||||
#include <commdlg.h>
|
||||
#include <ShlObj.h>
|
||||
#include <common/interop/shared_constants.h>
|
||||
|
||||
#include "KeyboardManagerState.h"
|
||||
@@ -19,8 +21,9 @@ RemapBuffer ShortcutControl::shortcutRemapBuffer;
|
||||
ShortcutControl::ShortcutControl(StackPanel table, StackPanel row, const int colIndex, TextBox targetApp)
|
||||
{
|
||||
shortcutDropDownVariableSizedWrapGrid = VariableSizedWrapGrid();
|
||||
typeShortcut = Button();
|
||||
btnPickShortcut = Button();
|
||||
shortcutControlLayout = StackPanel();
|
||||
|
||||
const bool isHybridControl = colIndex == 1;
|
||||
|
||||
// TODO: Check if there is a VariableSizedWrapGrid equivalent.
|
||||
@@ -28,28 +31,44 @@ ShortcutControl::ShortcutControl(StackPanel table, StackPanel row, const int col
|
||||
shortcutDropDownVariableSizedWrapGrid.as<VariableSizedWrapGrid>().Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
|
||||
shortcutDropDownVariableSizedWrapGrid.as<VariableSizedWrapGrid>().MaximumRowsOrColumns(3);
|
||||
|
||||
typeShortcut.as<Button>().Content(winrt::box_value(GET_RESOURCE_STRING(IDS_TYPE_BUTTON)));
|
||||
typeShortcut.as<Button>().Width(EditorConstants::ShortcutTableDropDownWidth);
|
||||
typeShortcut.as<Button>().Click([&, table, row, colIndex, isHybridControl, targetApp](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
btnPickShortcut.as<Button>().Content(winrt::box_value(GET_RESOURCE_STRING(IDS_TYPE_BUTTON)));
|
||||
btnPickShortcut.as<Button>().Width(EditorConstants::ShortcutTableDropDownWidth / 2);
|
||||
btnPickShortcut.as<Button>().Click([&, table, row, colIndex, isHybridControl, targetApp](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
keyboardManagerState->SetUIState(KBMEditor::KeyboardManagerUIState::DetectShortcutWindowActivated, editShortcutsWindowHandle);
|
||||
// Using the XamlRoot of the typeShortcut to get the root of the XAML host
|
||||
CreateDetectShortcutWindow(sender, sender.as<Button>().XamlRoot(), *keyboardManagerState, colIndex, table, keyDropDownControlObjects, row, targetApp, isHybridControl, false, editShortcutsWindowHandle, shortcutRemapBuffer);
|
||||
});
|
||||
|
||||
FontIcon fontIcon;
|
||||
fontIcon.Glyph(L"\uE70F"); // Unicode for the accept icon
|
||||
fontIcon.FontFamily(Media::FontFamily(L"Segoe MDL2 Assets")); // Set the font family to Segoe MDL2 Assets
|
||||
// Set the FontIcon as the content of the button
|
||||
btnPickShortcut.as<Button>().Content(fontIcon);
|
||||
|
||||
uint32_t rowIndex;
|
||||
|
||||
UIElementCollection children = table.Children();
|
||||
bool indexFound = children.IndexOf(row, rowIndex);
|
||||
|
||||
auto nameX = L"btnPickShortcut_" + std::to_wstring(colIndex);
|
||||
btnPickShortcut.as<Button>().Name(nameX);
|
||||
|
||||
// Set an accessible name for the type shortcut button
|
||||
typeShortcut.as<Button>().SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_TYPE_BUTTON)));
|
||||
btnPickShortcut.as<Button>().SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_TYPE_BUTTON)));
|
||||
|
||||
shortcutControlLayout.as<StackPanel>().Spacing(EditorConstants::ShortcutTableDropDownSpacing);
|
||||
|
||||
keyComboAndSelectStackPanel = StackPanel();
|
||||
keyComboAndSelectStackPanel.as<StackPanel>().Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
|
||||
keyComboAndSelectStackPanel.as<StackPanel>().Spacing(EditorConstants::ShortcutTableDropDownSpacing);
|
||||
keyComboStackPanel = StackPanel();
|
||||
keyComboStackPanel.as<StackPanel>().Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
|
||||
keyComboStackPanel.as<StackPanel>().Spacing(EditorConstants::ShortcutTableDropDownSpacing);
|
||||
|
||||
keyComboAndSelectStackPanel.as<StackPanel>().Children().Append(typeShortcut.as<Button>());
|
||||
shortcutControlLayout.as<StackPanel>().Children().InsertAt(0, keyComboAndSelectStackPanel.as<StackPanel>());
|
||||
shortcutControlLayout.as<StackPanel>().Children().Append(keyComboStackPanel.as<StackPanel>());
|
||||
|
||||
spBtnPickShortcut = UIHelpers::GetLabelWrapped(btnPickShortcut.as<Button>(), GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_LABEL_SHORTCUT), 80).as<StackPanel>();
|
||||
shortcutControlLayout.as<StackPanel>().Children().Append(spBtnPickShortcut);
|
||||
|
||||
shortcutControlLayout.as<StackPanel>().Children().Append(shortcutDropDownVariableSizedWrapGrid.as<VariableSizedWrapGrid>());
|
||||
KeyDropDownControl::AddDropDown(table, row, shortcutDropDownVariableSizedWrapGrid.as<VariableSizedWrapGrid>(), colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, false);
|
||||
|
||||
try
|
||||
{
|
||||
// If a layout update has been triggered by other methods (e.g.: adapting to zoom level), this may throw an exception.
|
||||
@@ -60,6 +79,13 @@ ShortcutControl::ShortcutControl(StackPanel table, StackPanel row, const int col
|
||||
}
|
||||
}
|
||||
|
||||
void ShortcutControl::OpenNewShortcutControlRow(StackPanel table, StackPanel row)
|
||||
{
|
||||
keyboardManagerState->SetUIState(KBMEditor::KeyboardManagerUIState::DetectShortcutWindowActivated, editShortcutsWindowHandle);
|
||||
// Using the XamlRoot of the typeShortcut to get the root of the XAML host
|
||||
CreateDetectShortcutWindow(btnPickShortcut, btnPickShortcut.XamlRoot(), *keyboardManagerState, 0, table, keyDropDownControlObjects, row, nullptr, false, false, editShortcutsWindowHandle, shortcutRemapBuffer);
|
||||
}
|
||||
|
||||
// Function to set the accessible name of the target App text box
|
||||
void ShortcutControl::SetAccessibleNameForTextBox(TextBox targetAppTextBox, int rowIndex)
|
||||
{
|
||||
@@ -82,18 +108,36 @@ void ShortcutControl::UpdateAccessibleNames(StackPanel sourceColumn, StackPanel
|
||||
deleteButton.SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_AUTOMATIONPROPERTIES_ROW) + std::to_wstring(rowIndex) + L", " + GET_RESOURCE_STRING(IDS_DELETE_REMAPPING_BUTTON)));
|
||||
}
|
||||
|
||||
void ShortcutControl::DeleteShortcutControl(StackPanel& parent, std::vector<std::vector<std::unique_ptr<ShortcutControl>>>& keyboardRemapControlObjects, int rowIndex)
|
||||
{
|
||||
UIElementCollection children = parent.Children();
|
||||
children.RemoveAt(rowIndex);
|
||||
shortcutRemapBuffer.erase(shortcutRemapBuffer.begin() + rowIndex);
|
||||
// delete the SingleKeyRemapControl objects so that they get destructed
|
||||
keyboardRemapControlObjects.erase(keyboardRemapControlObjects.begin() + rowIndex);
|
||||
}
|
||||
|
||||
// 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(StackPanel& parent, std::vector<std::vector<std::unique_ptr<ShortcutControl>>>& keyboardRemapControlObjects, const Shortcut& originalKeys, const KeyShortcutTextUnion& newKeys, const std::wstring& targetAppName)
|
||||
ShortcutControl& ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vector<std::vector<std::unique_ptr<ShortcutControl>>>& keyboardRemapControlObjects, const Shortcut& originalKeys, const KeyShortcutTextUnion& newKeys, const std::wstring& targetAppName)
|
||||
{
|
||||
// Textbox for target application
|
||||
TextBox targetAppTextBox;
|
||||
int runProgramLabelWidth = 80;
|
||||
|
||||
// Create new ShortcutControl objects dynamically so that we does not get destructed
|
||||
std::vector<std::unique_ptr<ShortcutControl>> newrow;
|
||||
StackPanel row = StackPanel();
|
||||
|
||||
row.Name(L"row");
|
||||
|
||||
parent.Children().Append(row);
|
||||
|
||||
newrow.emplace_back(std::make_unique<ShortcutControl>(parent, row, 0, targetAppTextBox));
|
||||
|
||||
ShortcutControl& newShortcutToRemap = *(newrow.back());
|
||||
|
||||
newrow.emplace_back(std::make_unique<ShortcutControl>(parent, row, 1, targetAppTextBox));
|
||||
|
||||
keyboardRemapControlObjects.push_back(std::move(newrow));
|
||||
|
||||
row.Padding({ 10, 15, 10, 5 });
|
||||
@@ -105,7 +149,9 @@ void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vector<s
|
||||
|
||||
// ShortcutControl for the original shortcut
|
||||
auto origin = keyboardRemapControlObjects.back()[0]->GetShortcutControl();
|
||||
|
||||
origin.Width(EditorConstants::ShortcutOriginColumnWidth);
|
||||
|
||||
row.Children().Append(origin);
|
||||
|
||||
// Arrow icon
|
||||
@@ -122,26 +168,46 @@ void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vector<s
|
||||
auto target = keyboardRemapControlObjects.back()[1]->GetShortcutControl();
|
||||
target.Width(EditorConstants::ShortcutTargetColumnWidth);
|
||||
|
||||
auto typeCombo = ComboBox();
|
||||
typeCombo.Width(EditorConstants::RemapTableDropDownWidth);
|
||||
typeCombo.Items().Append(winrt::box_value(KeyboardManagerEditorStrings::MappingTypeKeyShortcut()));
|
||||
typeCombo.Items().Append(winrt::box_value(KeyboardManagerEditorStrings::MappingTypeText()));
|
||||
uint32_t rowIndex = -1;
|
||||
if (!parent.Children().IndexOf(row, rowIndex))
|
||||
{
|
||||
return newShortcutToRemap;
|
||||
}
|
||||
|
||||
// add shortcut type choice
|
||||
auto actionTypeCombo = ComboBox();
|
||||
actionTypeCombo.Name(L"actionTypeCombo_" + std::to_wstring(rowIndex));
|
||||
actionTypeCombo.Width(EditorConstants::RemapTableDropDownWidth);
|
||||
actionTypeCombo.Items().Append(winrt::box_value(KeyboardManagerEditorStrings::MappingTypeKeyShortcut()));
|
||||
actionTypeCombo.Items().Append(winrt::box_value(KeyboardManagerEditorStrings::MappingTypeText()));
|
||||
actionTypeCombo.Items().Append(winrt::box_value(KeyboardManagerEditorStrings::MappingTypeRunProgram()));
|
||||
actionTypeCombo.Items().Append(winrt::box_value(KeyboardManagerEditorStrings::MappingTypeOpenUri()));
|
||||
|
||||
auto controlStackPanel = keyboardRemapControlObjects.back()[1]->shortcutControlLayout.as<StackPanel>();
|
||||
auto firstLineStackPanel = keyboardRemapControlObjects.back()[1]->keyComboAndSelectStackPanel.as<StackPanel>();
|
||||
firstLineStackPanel.Children().InsertAt(0, typeCombo);
|
||||
auto firstLineStackPanel = keyboardRemapControlObjects.back()[1]->keyComboStackPanel.as<StackPanel>();
|
||||
|
||||
firstLineStackPanel.Children().InsertAt(0, UIHelpers::GetLabelWrapped(actionTypeCombo, GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_LABEL_ACTION), runProgramLabelWidth).as<StackPanel>());
|
||||
|
||||
// add textbox for when it's a text input
|
||||
|
||||
auto unicodeTextKeysInput = TextBox();
|
||||
|
||||
unicodeTextKeysInput.Name(L"unicodeTextKeysInput_" + std::to_wstring(rowIndex));
|
||||
|
||||
auto textInput = TextBox();
|
||||
auto textInputMargin = Windows::UI::Xaml::Thickness();
|
||||
textInputMargin.Top = -EditorConstants::ShortcutTableDropDownSpacing;
|
||||
textInputMargin.Bottom = EditorConstants::ShortcutTableDropDownSpacing; // compensate for a collapsed UIElement
|
||||
textInput.Margin(textInputMargin);
|
||||
unicodeTextKeysInput.Margin(textInputMargin);
|
||||
|
||||
textInput.AcceptsReturn(false);
|
||||
textInput.Visibility(Visibility::Collapsed);
|
||||
textInput.Width(EditorConstants::TableDropDownHeight);
|
||||
controlStackPanel.Children().Append(textInput);
|
||||
textInput.HorizontalAlignment(HorizontalAlignment::Left);
|
||||
textInput.TextChanged([parent, row](winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::Controls::TextChangedEventArgs const& e) mutable {
|
||||
unicodeTextKeysInput.AcceptsReturn(false);
|
||||
//unicodeTextKeysInput.Visibility(Visibility::Collapsed);
|
||||
unicodeTextKeysInput.Width(EditorConstants::TableDropDownHeight);
|
||||
|
||||
StackPanel spUnicodeTextKeysInput = UIHelpers::GetLabelWrapped(unicodeTextKeysInput, GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_LABEL_KEYS), runProgramLabelWidth).as<StackPanel>();
|
||||
controlStackPanel.Children().Append(spUnicodeTextKeysInput);
|
||||
|
||||
unicodeTextKeysInput.HorizontalAlignment(HorizontalAlignment::Left);
|
||||
|
||||
unicodeTextKeysInput.TextChanged([parent, row](winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::Controls::TextChangedEventArgs const& e) mutable {
|
||||
auto textbox = sender.as<TextBox>();
|
||||
auto text = textbox.Text();
|
||||
uint32_t rowIndex = -1;
|
||||
@@ -154,29 +220,93 @@ void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vector<s
|
||||
shortcutRemapBuffer[rowIndex].first[1] = text.c_str();
|
||||
});
|
||||
|
||||
auto grid = keyboardRemapControlObjects.back()[1]->shortcutDropDownVariableSizedWrapGrid.as<VariableSizedWrapGrid>();
|
||||
const bool textSelected = newKeys.index() == 2;
|
||||
bool isRunProgram = false;
|
||||
bool isOpenUri = false;
|
||||
Shortcut shortCut;
|
||||
if (!textSelected && newKeys.index() == 1)
|
||||
{
|
||||
shortCut = std::get<Shortcut>(newKeys);
|
||||
isRunProgram = (shortCut.operationType == Shortcut::OperationType::RunProgram);
|
||||
isOpenUri = (shortCut.operationType == Shortcut::OperationType::OpenURI);
|
||||
}
|
||||
|
||||
// add TextBoxes for when it's a runProgram fields
|
||||
|
||||
auto runProgramStackPanel = SetupRunProgramControls(parent, row, shortCut, textInputMargin, controlStackPanel);
|
||||
|
||||
runProgramStackPanel.Margin({ 0, -30, 0, 0 });
|
||||
|
||||
auto openURIStackPanel = SetupOpenURIControls(parent, row, shortCut, textInputMargin, controlStackPanel);
|
||||
|
||||
// add grid for when it's a key/shortcut
|
||||
auto shortcutItemsGrid = keyboardRemapControlObjects.back()[1]->shortcutDropDownVariableSizedWrapGrid.as<VariableSizedWrapGrid>();
|
||||
auto gridMargin = Windows::UI::Xaml::Thickness();
|
||||
gridMargin.Bottom = -EditorConstants::ShortcutTableDropDownSpacing; // compensate for a collapsed textInput
|
||||
grid.Margin(gridMargin);
|
||||
auto button = keyboardRemapControlObjects.back()[1]->typeShortcut.as<Button>();
|
||||
shortcutItemsGrid.Margin(gridMargin);
|
||||
auto shortcutButton = keyboardRemapControlObjects.back()[1]->btnPickShortcut.as<Button>();
|
||||
auto spBtnPickShortcut = keyboardRemapControlObjects.back()[1]->spBtnPickShortcut.as<StackPanel>();
|
||||
|
||||
typeCombo.SelectionChanged([typeCombo, grid, button, textInput](winrt::Windows::Foundation::IInspectable const&, SelectionChangedEventArgs const&) {
|
||||
const bool textSelected = typeCombo.SelectedIndex() == 1;
|
||||
// event code for when type changes
|
||||
actionTypeCombo.SelectionChanged([parent, row, controlStackPanel, actionTypeCombo, shortcutItemsGrid, spBtnPickShortcut, spUnicodeTextKeysInput, runProgramStackPanel, openURIStackPanel](winrt::Windows::Foundation::IInspectable const&, SelectionChangedEventArgs const&) {
|
||||
const auto shortcutType = ShortcutControl::GetShortcutType(actionTypeCombo);
|
||||
|
||||
const auto shortcutInputVisibility = textSelected ? Visibility::Collapsed : Visibility::Visible;
|
||||
|
||||
grid.Visibility(shortcutInputVisibility);
|
||||
button.Visibility(shortcutInputVisibility);
|
||||
|
||||
const auto textInputVisibility = textSelected ? Visibility::Visible : Visibility::Collapsed;
|
||||
textInput.Visibility(textInputVisibility);
|
||||
if (shortcutType == ShortcutControl::ShortcutType::Shortcut)
|
||||
{
|
||||
spBtnPickShortcut.Visibility(Visibility::Visible);
|
||||
shortcutItemsGrid.Visibility(Visibility::Visible);
|
||||
spUnicodeTextKeysInput.Visibility(Visibility::Collapsed);
|
||||
runProgramStackPanel.Visibility(Visibility::Collapsed);
|
||||
openURIStackPanel.Visibility(Visibility::Collapsed);
|
||||
}
|
||||
else if (shortcutType == ShortcutControl::ShortcutType::Text)
|
||||
{
|
||||
spBtnPickShortcut.Visibility(Visibility::Collapsed);
|
||||
shortcutItemsGrid.Visibility(Visibility::Collapsed);
|
||||
spUnicodeTextKeysInput.Visibility(Visibility::Visible);
|
||||
runProgramStackPanel.Visibility(Visibility::Collapsed);
|
||||
openURIStackPanel.Visibility(Visibility::Collapsed);
|
||||
}
|
||||
else if (shortcutType == ShortcutControl::ShortcutType::RunProgram)
|
||||
{
|
||||
spBtnPickShortcut.Visibility(Visibility::Collapsed);
|
||||
shortcutItemsGrid.Visibility(Visibility::Collapsed);
|
||||
spUnicodeTextKeysInput.Visibility(Visibility::Collapsed);
|
||||
runProgramStackPanel.Visibility(Visibility::Visible);
|
||||
openURIStackPanel.Visibility(Visibility::Collapsed);
|
||||
}
|
||||
else
|
||||
{
|
||||
spBtnPickShortcut.Visibility(Visibility::Collapsed);
|
||||
shortcutItemsGrid.Visibility(Visibility::Collapsed);
|
||||
spUnicodeTextKeysInput.Visibility(Visibility::Collapsed);
|
||||
runProgramStackPanel.Visibility(Visibility::Collapsed);
|
||||
openURIStackPanel.Visibility(Visibility::Visible);
|
||||
}
|
||||
});
|
||||
|
||||
const bool textSelected = newKeys.index() == 2;
|
||||
typeCombo.SelectedIndex(textSelected);
|
||||
|
||||
row.Children().Append(target);
|
||||
|
||||
if (textSelected)
|
||||
{
|
||||
actionTypeCombo.SelectedIndex(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (shortCut.operationType == Shortcut::OperationType::RunProgram)
|
||||
{
|
||||
actionTypeCombo.SelectedIndex(2);
|
||||
}
|
||||
else if (shortCut.operationType == Shortcut::OperationType::OpenURI)
|
||||
{
|
||||
actionTypeCombo.SelectedIndex(3);
|
||||
}
|
||||
else
|
||||
{
|
||||
actionTypeCombo.SelectedIndex(0);
|
||||
}
|
||||
}
|
||||
|
||||
targetAppTextBox.Width(EditorConstants::ShortcutTableDropDownWidth);
|
||||
targetAppTextBox.PlaceholderText(KeyboardManagerEditorStrings::DefaultAppName());
|
||||
targetAppTextBox.Text(targetAppName);
|
||||
@@ -189,7 +319,7 @@ void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vector<s
|
||||
});
|
||||
|
||||
// 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, row, targetAppTextBox, typeCombo, textInput](auto const& sender, auto const& e) {
|
||||
targetAppTextBox.LostFocus([&keyboardRemapControlObjects, parent, row, targetAppTextBox, actionTypeCombo, unicodeTextKeysInput](auto const& sender, auto const& e) {
|
||||
// Get index of targetAppTextBox button
|
||||
uint32_t rowIndex;
|
||||
if (!parent.Children().IndexOf(row, rowIndex))
|
||||
@@ -211,28 +341,60 @@ void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vector<s
|
||||
std::get<Shortcut>(shortcutRemapBuffer[rowIndex].first[0]).SetKeyCodes(KeyDropDownControl::GetSelectedCodesFromStackPanel(keyboardRemapControlObjects[rowIndex][0]->shortcutDropDownVariableSizedWrapGrid.as<VariableSizedWrapGrid>()));
|
||||
// second column is a hybrid column
|
||||
|
||||
const bool textSelected = typeCombo.SelectedIndex() == 1;
|
||||
const bool regularShortcut = actionTypeCombo.SelectedIndex() == 0;
|
||||
const bool textSelected = actionTypeCombo.SelectedIndex() == 1;
|
||||
const bool runProgram = actionTypeCombo.SelectedIndex() == 2;
|
||||
const bool openUri = actionTypeCombo.SelectedIndex() == 3;
|
||||
|
||||
if (textSelected)
|
||||
{
|
||||
shortcutRemapBuffer[rowIndex].first[1] = textInput.Text().c_str();
|
||||
shortcutRemapBuffer[rowIndex].first[1] = unicodeTextKeysInput.Text().c_str();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<int32_t> selectedKeyCodes = KeyDropDownControl::GetSelectedCodesFromStackPanel(keyboardRemapControlObjects[rowIndex][1]->shortcutDropDownVariableSizedWrapGrid.as<VariableSizedWrapGrid>());
|
||||
if (regularShortcut)
|
||||
{
|
||||
std::vector<int32_t> selectedKeyCodes = KeyDropDownControl::GetSelectedCodesFromStackPanel(keyboardRemapControlObjects[rowIndex][1]->shortcutDropDownVariableSizedWrapGrid.as<VariableSizedWrapGrid>());
|
||||
|
||||
// If exactly one key is selected consider it to be a key remap
|
||||
if (selectedKeyCodes.size() == 1)
|
||||
{
|
||||
shortcutRemapBuffer[rowIndex].first[1] = (DWORD)selectedKeyCodes[0];
|
||||
// If exactly one key is selected consider it to be a key remap
|
||||
if (selectedKeyCodes.size() == 1)
|
||||
{
|
||||
shortcutRemapBuffer[rowIndex].first[1] = (DWORD)selectedKeyCodes[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
Shortcut tempShortcut;
|
||||
tempShortcut.SetKeyCodes(selectedKeyCodes);
|
||||
// Assign instead of setting the value in the buffer since the previous value may not be a Shortcut
|
||||
shortcutRemapBuffer[rowIndex].first[1] = tempShortcut;
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (runProgram)
|
||||
{
|
||||
auto runProgramPathInput = row.FindName(L"runProgramPathInput_" + std::to_wstring(rowIndex)).as<TextBox>();
|
||||
auto runProgramArgsForProgramInput = row.FindName(L"runProgramArgsForProgramInput_" + std::to_wstring(rowIndex)).as<TextBox>();
|
||||
auto runProgramStartInDirInput = row.FindName(L"runProgramStartInDirInput_" + std::to_wstring(rowIndex)).as<TextBox>();
|
||||
auto runProgramElevationTypeCombo = row.FindName(L"runProgramElevationTypeCombo_" + std::to_wstring(rowIndex)).as<ComboBox>();
|
||||
auto runProgramAlreadyRunningAction = row.FindName(L"runProgramAlreadyRunningAction_" + std::to_wstring(rowIndex)).as<ComboBox>();
|
||||
|
||||
Shortcut tempShortcut;
|
||||
tempShortcut.SetKeyCodes(selectedKeyCodes);
|
||||
tempShortcut.operationType = Shortcut::OperationType::RunProgram;
|
||||
|
||||
tempShortcut.runProgramFilePath = ShortcutControl::RemoveExtraQuotes(runProgramPathInput.Text().c_str());
|
||||
tempShortcut.runProgramArgs = (runProgramArgsForProgramInput.Text().c_str());
|
||||
tempShortcut.runProgramStartInDir = (runProgramStartInDirInput.Text().c_str());
|
||||
|
||||
tempShortcut.elevationLevel = static_cast<Shortcut::ElevationLevel>(runProgramElevationTypeCombo.SelectedIndex());
|
||||
tempShortcut.alreadyRunningAction = static_cast<Shortcut::ProgramAlreadyRunningAction>(runProgramAlreadyRunningAction.SelectedIndex());
|
||||
|
||||
// Assign instead of setting the value in the buffer since the previous value may not be a Shortcut
|
||||
shortcutRemapBuffer[rowIndex].first[1] = tempShortcut;
|
||||
}
|
||||
else if (openUri)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring newText = targetAppTextBox.Text().c_str();
|
||||
std::wstring lowercaseDefAppName = KeyboardManagerEditorStrings::DefaultAppName();
|
||||
std::transform(newText.begin(), newText.end(), newText.begin(), towlower);
|
||||
@@ -251,16 +413,19 @@ void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vector<s
|
||||
});
|
||||
|
||||
// We need two containers in order to align it horizontally and vertically
|
||||
|
||||
StackPanel targetAppHorizontal = UIHelpers::GetWrapped(targetAppTextBox, EditorConstants::TableTargetAppColWidth).as<StackPanel>();
|
||||
targetAppHorizontal.Orientation(Orientation::Horizontal);
|
||||
targetAppHorizontal.HorizontalAlignment(HorizontalAlignment::Left);
|
||||
StackPanel targetAppContainer = UIHelpers::GetWrapped(targetAppHorizontal, EditorConstants::TableTargetAppColWidth).as<StackPanel>();
|
||||
targetAppContainer.Orientation(Orientation::Vertical);
|
||||
targetAppContainer.VerticalAlignment(VerticalAlignment::Center);
|
||||
|
||||
row.Children().Append(targetAppContainer);
|
||||
|
||||
// Delete row button
|
||||
Windows::UI::Xaml::Controls::Button deleteShortcut;
|
||||
|
||||
deleteShortcut.Content(SymbolIcon(Symbol::Delete));
|
||||
deleteShortcut.Background(Media::SolidColorBrush(Colors::Transparent()));
|
||||
deleteShortcut.HorizontalAlignment(HorizontalAlignment::Center);
|
||||
@@ -312,9 +477,11 @@ void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vector<s
|
||||
ToolTipService::SetToolTip(deleteShortcut, deleteShortcuttoolTip);
|
||||
|
||||
StackPanel deleteShortcutContainer = StackPanel();
|
||||
deleteShortcutContainer.Name(L"deleteShortcutContainer");
|
||||
deleteShortcutContainer.Children().Append(deleteShortcut);
|
||||
deleteShortcutContainer.Orientation(Orientation::Vertical);
|
||||
deleteShortcutContainer.VerticalAlignment(VerticalAlignment::Center);
|
||||
|
||||
row.Children().Append(deleteShortcutContainer);
|
||||
|
||||
// Set accessible names
|
||||
@@ -324,12 +491,25 @@ void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vector<s
|
||||
if (EditorHelpers::IsValidShortcut(originalKeys) && !(newKeys.index() == 0 && std::get<DWORD>(newKeys) == NULL) && !(newKeys.index() == 1 && !EditorHelpers::IsValidShortcut(std::get<Shortcut>(newKeys))))
|
||||
{
|
||||
// change to load app name
|
||||
shortcutRemapBuffer.push_back(std::make_pair<RemapBufferItem, std::wstring>(RemapBufferItem{ Shortcut(), Shortcut() }, std::wstring(targetAppName)));
|
||||
|
||||
if (isRunProgram || isOpenUri)
|
||||
{
|
||||
// not sure why by we need to add the current item in here, so we have it even if does not change.
|
||||
auto newShortcut = std::get<Shortcut>(newKeys);
|
||||
shortcutRemapBuffer.push_back(std::make_pair<RemapBufferItem, std::wstring>(RemapBufferItem{ Shortcut(), newShortcut }, std::wstring(targetAppName)));
|
||||
}
|
||||
else
|
||||
{
|
||||
shortcutRemapBuffer.push_back(std::make_pair<RemapBufferItem, std::wstring>(RemapBufferItem{ Shortcut(), Shortcut() }, std::wstring(targetAppName)));
|
||||
}
|
||||
|
||||
KeyDropDownControl::AddShortcutToControl(originalKeys, parent, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->shortcutDropDownVariableSizedWrapGrid.as<VariableSizedWrapGrid>(), *keyboardManagerState, 0, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->keyDropDownControlObjects, shortcutRemapBuffer, row, targetAppTextBox, false, false);
|
||||
|
||||
if (newKeys.index() == 0)
|
||||
{
|
||||
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->keyDropDownControlObjects[0]->SetSelectedValue(std::to_wstring(std::get<DWORD>(newKeys)));
|
||||
auto shortcut = new Shortcut;
|
||||
shortcut->SetKey(std::get<DWORD>(newKeys));
|
||||
KeyDropDownControl::AddShortcutToControl(*shortcut, parent, keyboardRemapControlObjects.back()[1]->shortcutDropDownVariableSizedWrapGrid.as<VariableSizedWrapGrid>(), *keyboardManagerState, 1, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->keyDropDownControlObjects, shortcutRemapBuffer, row, targetAppTextBox, true, false);
|
||||
}
|
||||
else if (newKeys.index() == 1)
|
||||
{
|
||||
@@ -339,16 +519,404 @@ void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vector<s
|
||||
{
|
||||
shortcutRemapBuffer.back().first[1] = std::get<std::wstring>(newKeys);
|
||||
const auto& remapControl = keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1];
|
||||
const auto& controlChildren = remapControl->GetShortcutControl().Children();
|
||||
const auto& topLineChildren = controlChildren.GetAt(0).as<StackPanel>();
|
||||
topLineChildren.Children().GetAt(0).as<ComboBox>().SelectedIndex(1);
|
||||
controlChildren.GetAt(2).as<TextBox>().Text(std::get<std::wstring>(newKeys));
|
||||
actionTypeCombo.SelectedIndex(1);
|
||||
unicodeTextKeysInput.Text(std::get<std::wstring>(newKeys));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Initialize both shortcuts as empty shortcuts
|
||||
shortcutRemapBuffer.push_back(std::make_pair<RemapBufferItem, std::wstring>(RemapBufferItem{ Shortcut(), Shortcut() }, std::wstring(targetAppName)));
|
||||
|
||||
KeyDropDownControl::AddShortcutToControl(originalKeys, parent, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->shortcutDropDownVariableSizedWrapGrid.as<VariableSizedWrapGrid>(), *keyboardManagerState, 0, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->keyDropDownControlObjects, shortcutRemapBuffer, row, targetAppTextBox, false, false);
|
||||
|
||||
KeyDropDownControl::AddShortcutToControl(std::get<Shortcut>(newKeys), parent, keyboardRemapControlObjects.back()[1]->shortcutDropDownVariableSizedWrapGrid.as<VariableSizedWrapGrid>(), *keyboardManagerState, 1, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->keyDropDownControlObjects, shortcutRemapBuffer, row, targetAppTextBox, true, false);
|
||||
|
||||
}
|
||||
|
||||
return newShortcutToRemap;
|
||||
}
|
||||
|
||||
StackPanel SetupOpenURIControls(StackPanel& parent, StackPanel& row, Shortcut& shortCut, winrt::Windows::UI::Xaml::Thickness& textInputMargin, ::StackPanel& _controlStackPanel)
|
||||
{
|
||||
StackPanel openUriStackPanel;
|
||||
auto uriTextBox = TextBox();
|
||||
|
||||
int runProgramLabelWidth = 80;
|
||||
|
||||
uriTextBox.Text(shortCut.uriToOpen);
|
||||
uriTextBox.PlaceholderText(GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_URI_EXAMPLE));
|
||||
uriTextBox.Margin(textInputMargin);
|
||||
uriTextBox.Width(EditorConstants::TableDropDownHeight);
|
||||
uriTextBox.HorizontalAlignment(HorizontalAlignment::Left);
|
||||
|
||||
winrt::Windows::UI::Xaml::Controls::HyperlinkButton hyperlinkButton;
|
||||
hyperlinkButton.NavigateUri(Windows::Foundation::Uri(L"https://learn.microsoft.com/windows/uwp/launch-resume/launch-app-with-uri"));
|
||||
hyperlinkButton.Content(winrt::box_value(GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_WHAT_CAN_I_USE_LINK)));
|
||||
hyperlinkButton.Margin(textInputMargin);
|
||||
|
||||
StackPanel boxAndLink;
|
||||
boxAndLink.Orientation(Orientation::Horizontal);
|
||||
boxAndLink.Children().Append(uriTextBox);
|
||||
boxAndLink.Children().Append(hyperlinkButton);
|
||||
|
||||
openUriStackPanel.Children().Append(UIHelpers::GetLabelWrapped(boxAndLink, GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_LABEL_PATH_URI), runProgramLabelWidth).as<StackPanel>());
|
||||
|
||||
uriTextBox.TextChanged([parent, row, uriTextBox](winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::Controls::TextChangedEventArgs const& e) mutable {
|
||||
uint32_t rowIndex = -1;
|
||||
if (!parent.Children().IndexOf(row, rowIndex))
|
||||
{
|
||||
return;
|
||||
}
|
||||
Shortcut tempShortcut;
|
||||
tempShortcut.operationType = Shortcut::OperationType::OpenURI;
|
||||
tempShortcut.uriToOpen = ShortcutControl::RemoveExtraQuotes(uriTextBox.Text().c_str());
|
||||
ShortcutControl::shortcutRemapBuffer[rowIndex].first[1] = tempShortcut;
|
||||
});
|
||||
|
||||
_controlStackPanel.Children().Append(openUriStackPanel);
|
||||
return openUriStackPanel;
|
||||
}
|
||||
|
||||
StackPanel SetupRunProgramControls(StackPanel& parent, StackPanel& row, Shortcut& shortCut, winrt::Windows::UI::Xaml::Thickness& textInputMargin, ::StackPanel& _controlStackPanel)
|
||||
{
|
||||
uint32_t rowIndex;
|
||||
// Get index of delete button
|
||||
UIElementCollection children = parent.Children();
|
||||
children.IndexOf(row, rowIndex);
|
||||
|
||||
StackPanel controlStackPanel;
|
||||
controlStackPanel.Name(L"RunProgramControls_" + std::to_wstring(rowIndex));
|
||||
|
||||
auto runProgramPathInput = TextBox();
|
||||
runProgramPathInput.Name(L"runProgramPathInput_" + std::to_wstring(rowIndex));
|
||||
auto runProgramArgsForProgramInput = TextBox();
|
||||
runProgramArgsForProgramInput.Name(L"runProgramArgsForProgramInput_" + std::to_wstring(rowIndex));
|
||||
auto runProgramStartInDirInput = TextBox();
|
||||
runProgramStartInDirInput.Name(L"runProgramStartInDirInput_" + std::to_wstring(rowIndex));
|
||||
|
||||
Button pickFileBtn;
|
||||
Button pickPathBtn;
|
||||
auto runProgramElevationTypeCombo = ComboBox();
|
||||
runProgramElevationTypeCombo.Name(L"runProgramElevationTypeCombo_" + std::to_wstring(rowIndex));
|
||||
|
||||
auto runProgramAlreadyRunningAction = ComboBox();
|
||||
runProgramAlreadyRunningAction.Name(L"runProgramAlreadyRunningAction_" + std::to_wstring(rowIndex));
|
||||
|
||||
_controlStackPanel.Children().Append(controlStackPanel);
|
||||
|
||||
StackPanel stackPanelForRunProgramPath;
|
||||
StackPanel stackPanelRunProgramStartInDir;
|
||||
|
||||
runProgramPathInput.PlaceholderText(GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_PATH_TO_PROGRAM));
|
||||
|
||||
runProgramPathInput.Margin(textInputMargin);
|
||||
|
||||
runProgramPathInput.AcceptsReturn(false);
|
||||
runProgramPathInput.IsSpellCheckEnabled(false);
|
||||
runProgramPathInput.Width(EditorConstants::TableDropDownHeight);
|
||||
runProgramPathInput.HorizontalAlignment(HorizontalAlignment::Left);
|
||||
|
||||
runProgramArgsForProgramInput.PlaceholderText(GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_ARGS_FOR_PROGRAM));
|
||||
runProgramArgsForProgramInput.Margin(textInputMargin);
|
||||
runProgramArgsForProgramInput.AcceptsReturn(false);
|
||||
runProgramArgsForProgramInput.IsSpellCheckEnabled(false);
|
||||
runProgramArgsForProgramInput.Width(EditorConstants::TableDropDownHeight);
|
||||
runProgramArgsForProgramInput.HorizontalAlignment(HorizontalAlignment::Left);
|
||||
|
||||
runProgramStartInDirInput.IsSpellCheckEnabled(false);
|
||||
runProgramStartInDirInput.PlaceholderText(GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_START_IN_DIR_FOR_PROGRAM));
|
||||
runProgramStartInDirInput.Margin(textInputMargin);
|
||||
runProgramStartInDirInput.AcceptsReturn(false);
|
||||
runProgramStartInDirInput.Width(EditorConstants::TableDropDownHeight);
|
||||
runProgramStartInDirInput.HorizontalAlignment(HorizontalAlignment::Left);
|
||||
|
||||
stackPanelForRunProgramPath.Orientation(Orientation::Horizontal);
|
||||
stackPanelRunProgramStartInDir.Orientation(Orientation::Horizontal);
|
||||
|
||||
pickFileBtn.Content(winrt::box_value(GET_RESOURCE_STRING(IDS_BROWSE_FOR_PROGRAM_BUTTON)));
|
||||
pickPathBtn.Content(winrt::box_value(GET_RESOURCE_STRING(IDS_BROWSE_FOR_PATH_BUTTON)));
|
||||
pickFileBtn.Margin(textInputMargin);
|
||||
pickPathBtn.Margin(textInputMargin);
|
||||
|
||||
stackPanelForRunProgramPath.Children().Append(runProgramPathInput);
|
||||
stackPanelForRunProgramPath.Children().Append(pickFileBtn);
|
||||
|
||||
stackPanelRunProgramStartInDir.Children().Append(runProgramStartInDirInput);
|
||||
stackPanelRunProgramStartInDir.Children().Append(pickPathBtn);
|
||||
|
||||
int runProgramLabelWidth = 90;
|
||||
|
||||
controlStackPanel.Children().Append(UIHelpers::GetLabelWrapped(stackPanelForRunProgramPath, GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_LABEL_PROGRAM), runProgramLabelWidth).as<StackPanel>());
|
||||
|
||||
controlStackPanel.Children().Append(UIHelpers::GetLabelWrapped(runProgramArgsForProgramInput, GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_LABEL_ARGS), runProgramLabelWidth).as<StackPanel>());
|
||||
|
||||
controlStackPanel.Children().Append(UIHelpers::GetLabelWrapped(stackPanelRunProgramStartInDir, GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_LABEL_START_IN), runProgramLabelWidth).as<StackPanel>());
|
||||
|
||||
// add shortcut type choice
|
||||
runProgramElevationTypeCombo.Width(EditorConstants::TableDropDownHeight);
|
||||
runProgramElevationTypeCombo.Items().Append(winrt::box_value(GET_RESOURCE_STRING(IDS_ELEVATION_TYPE_NORMAL)));
|
||||
runProgramElevationTypeCombo.Items().Append(winrt::box_value(GET_RESOURCE_STRING(IDS_ELEVATION_TYPE_ELEVATED)));
|
||||
runProgramElevationTypeCombo.Items().Append(winrt::box_value(GET_RESOURCE_STRING(IDS_ELEVATION_TYPE_DIFFERENT_USER)));
|
||||
runProgramElevationTypeCombo.SelectedIndex(0);
|
||||
// runProgramAlreadyRunningAction
|
||||
runProgramAlreadyRunningAction.Width(EditorConstants::TableDropDownHeight);
|
||||
runProgramAlreadyRunningAction.Items().Append(winrt::box_value(GET_RESOURCE_STRING(IDS_ALREADY_RUNNING_SHOW_WINDOW)));
|
||||
runProgramAlreadyRunningAction.Items().Append(winrt::box_value(GET_RESOURCE_STRING(IDS_ALREADY_RUNNING_START_ANOTHER)));
|
||||
runProgramAlreadyRunningAction.Items().Append(winrt::box_value(GET_RESOURCE_STRING(IDS_ALREADY_RUNNING_DO_NOTHING)));
|
||||
runProgramAlreadyRunningAction.Items().Append(winrt::box_value(GET_RESOURCE_STRING(IDS_ALREADY_RUNNING_CLOSE)));
|
||||
runProgramAlreadyRunningAction.Items().Append(winrt::box_value(GET_RESOURCE_STRING(IDS_ALREADY_RUNNING_TERMINATE)));
|
||||
|
||||
runProgramAlreadyRunningAction.SelectedIndex(0);
|
||||
|
||||
controlStackPanel.Children().Append(UIHelpers::GetLabelWrapped(runProgramElevationTypeCombo, GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_LABEL_ELEVATION), runProgramLabelWidth).as<StackPanel>());
|
||||
controlStackPanel.Children().Append(UIHelpers::GetLabelWrapped(runProgramAlreadyRunningAction, GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_LABEL_IF_RUNNING), runProgramLabelWidth).as<StackPanel>());
|
||||
|
||||
auto runProgramStartWindow = ComboBox();
|
||||
runProgramStartWindow.Name(L"runProgramStartWindow_" + std::to_wstring(rowIndex));
|
||||
runProgramStartWindow.Width(EditorConstants::TableDropDownHeight);
|
||||
runProgramStartWindow.Items().Append(winrt::box_value(GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_VISIBILITY_NORMAL)));
|
||||
runProgramStartWindow.Items().Append(winrt::box_value(GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_VISIBILITY_HIDDEN)));
|
||||
runProgramStartWindow.SelectedIndex(0);
|
||||
controlStackPanel.Children().Append(UIHelpers::GetLabelWrapped(runProgramStartWindow, GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_LABEL_START_AS), runProgramLabelWidth).as<StackPanel>());
|
||||
|
||||
// add events to TextBoxes for runProgram fields.
|
||||
runProgramPathInput.TextChanged([parent, row](winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::Controls::TextChangedEventArgs const& e) mutable {
|
||||
uint32_t rowIndex = -1;
|
||||
if (!parent.Children().IndexOf(row, rowIndex))
|
||||
{
|
||||
return;
|
||||
}
|
||||
Shortcut tempShortcut;
|
||||
CreateNewTempShortcut(row, tempShortcut, rowIndex);
|
||||
ShortcutControl::shortcutRemapBuffer[rowIndex].first[1] = tempShortcut;
|
||||
});
|
||||
|
||||
runProgramArgsForProgramInput.TextChanged([parent, row](winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::Controls::TextChangedEventArgs const& e) mutable {
|
||||
uint32_t rowIndex = -1;
|
||||
if (!parent.Children().IndexOf(row, rowIndex))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Shortcut tempShortcut;
|
||||
CreateNewTempShortcut(row, tempShortcut, rowIndex);
|
||||
ShortcutControl::shortcutRemapBuffer[rowIndex].first[1] = tempShortcut;
|
||||
});
|
||||
|
||||
runProgramStartInDirInput.TextChanged([parent, row](winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::Controls::TextChangedEventArgs const& e) mutable {
|
||||
uint32_t rowIndex = -1;
|
||||
if (!parent.Children().IndexOf(row, rowIndex))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ShortcutControl::shortcutRemapBuffer.size() <= rowIndex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Shortcut tempShortcut;
|
||||
CreateNewTempShortcut(row, tempShortcut, rowIndex);
|
||||
ShortcutControl::shortcutRemapBuffer[rowIndex].first[1] = tempShortcut;
|
||||
});
|
||||
|
||||
runProgramAlreadyRunningAction.SelectionChanged([parent, row](winrt::Windows::Foundation::IInspectable const&, SelectionChangedEventArgs const&) {
|
||||
uint32_t rowIndex;
|
||||
if (!parent.Children().IndexOf(row, rowIndex))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ShortcutControl::shortcutRemapBuffer.size() <= rowIndex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Shortcut tempShortcut;
|
||||
CreateNewTempShortcut(static_cast<StackPanel>(row), tempShortcut, rowIndex);
|
||||
ShortcutControl::shortcutRemapBuffer[rowIndex].first[1] = tempShortcut;
|
||||
});
|
||||
|
||||
runProgramElevationTypeCombo.SelectionChanged([parent, row](winrt::Windows::Foundation::IInspectable const&, SelectionChangedEventArgs const&) {
|
||||
uint32_t rowIndex;
|
||||
if (!parent.Children().IndexOf(row, rowIndex))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ShortcutControl::shortcutRemapBuffer.size() <= rowIndex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Shortcut tempShortcut;
|
||||
CreateNewTempShortcut(static_cast<StackPanel>(row), tempShortcut, rowIndex);
|
||||
ShortcutControl::shortcutRemapBuffer[rowIndex].first[1] = tempShortcut;
|
||||
});
|
||||
|
||||
runProgramStartWindow.SelectionChanged([parent, row](winrt::Windows::Foundation::IInspectable const&, SelectionChangedEventArgs const&) {
|
||||
uint32_t rowIndex;
|
||||
if (!parent.Children().IndexOf(row, rowIndex))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ShortcutControl::shortcutRemapBuffer.size() <= rowIndex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Shortcut tempShortcut;
|
||||
CreateNewTempShortcut(static_cast<StackPanel>(row), tempShortcut, rowIndex);
|
||||
|
||||
ShortcutControl::shortcutRemapBuffer[rowIndex].first[1] = tempShortcut;
|
||||
});
|
||||
|
||||
pickFileBtn.Click([&, parent, row](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
Button currentButton = sender.as<Button>();
|
||||
uint32_t rowIndex;
|
||||
UIElementCollection children = parent.Children();
|
||||
bool indexFound = children.IndexOf(row, rowIndex);
|
||||
|
||||
if (!indexFound)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OPENFILENAME openFileName;
|
||||
TCHAR szFile[260] = { 0 };
|
||||
|
||||
ZeroMemory(&openFileName, sizeof(openFileName));
|
||||
openFileName.lStructSize = sizeof(openFileName);
|
||||
openFileName.hwndOwner = NULL;
|
||||
openFileName.lpstrFile = szFile;
|
||||
openFileName.nMaxFile = sizeof(szFile);
|
||||
openFileName.lpstrFilter = TEXT("All Files (*.*)\0*.*\0");
|
||||
openFileName.nFilterIndex = 1;
|
||||
openFileName.lpstrFileTitle = NULL;
|
||||
openFileName.nMaxFileTitle = 0;
|
||||
openFileName.lpstrInitialDir = NULL;
|
||||
openFileName.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
|
||||
|
||||
auto runProgramPathInput = row.FindName(L"runProgramPathInput_" + std::to_wstring(rowIndex)).as<TextBox>();
|
||||
|
||||
if (GetOpenFileName(&openFileName) == TRUE)
|
||||
{
|
||||
runProgramPathInput.Text(szFile);
|
||||
}
|
||||
});
|
||||
|
||||
pickPathBtn.Click([&, parent, row](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
Button currentButton = sender.as<Button>();
|
||||
uint32_t rowIndex;
|
||||
UIElementCollection children = parent.Children();
|
||||
bool indexFound = children.IndexOf(row, rowIndex);
|
||||
if (!indexFound)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
||||
if (!FAILED(hr))
|
||||
{
|
||||
// Create a buffer to store the selected folder path
|
||||
wchar_t path[MAX_PATH];
|
||||
ZeroMemory(path, sizeof(path));
|
||||
|
||||
// Initialize the BROWSEINFO structure
|
||||
BROWSEINFO browseInfo = { 0 };
|
||||
browseInfo.hwndOwner = NULL; // Use NULL if there's no owner window
|
||||
browseInfo.pidlRoot = NULL; // Use NULL to start from the desktop
|
||||
browseInfo.pszDisplayName = path; // Buffer to store the display name
|
||||
browseInfo.lpszTitle = L"Select a folder"; // Title of the dialog
|
||||
browseInfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE; // Show only file system directories
|
||||
|
||||
// Show the dialog
|
||||
|
||||
LPITEMIDLIST pidl = SHBrowseForFolder(&browseInfo);
|
||||
if (pidl != NULL)
|
||||
{
|
||||
// Get the selected folder's path
|
||||
if (SHGetPathFromIDList(pidl, path))
|
||||
{
|
||||
auto runProgramStartInDirInput = row.FindName(L"runProgramStartInDirInput_" + std::to_wstring(rowIndex)).as<TextBox>();
|
||||
runProgramStartInDirInput.Text(path);
|
||||
}
|
||||
|
||||
// Free the PIDL
|
||||
CoTaskMemFree(pidl);
|
||||
}
|
||||
|
||||
// Release COM
|
||||
CoUninitialize();
|
||||
|
||||
// Uninitialize COM
|
||||
CoUninitialize();
|
||||
}
|
||||
});
|
||||
|
||||
// this really should not be here, it just works because SelectionChanged is always changed?
|
||||
runProgramPathInput.Text(shortCut.runProgramFilePath);
|
||||
runProgramArgsForProgramInput.Text(shortCut.runProgramArgs);
|
||||
runProgramStartInDirInput.Text(shortCut.runProgramStartInDir);
|
||||
|
||||
runProgramElevationTypeCombo.SelectedIndex(shortCut.elevationLevel);
|
||||
runProgramAlreadyRunningAction.SelectedIndex(shortCut.alreadyRunningAction);
|
||||
runProgramStartWindow.SelectedIndex(shortCut.startWindowType);
|
||||
|
||||
return controlStackPanel;
|
||||
}
|
||||
|
||||
void CreateNewTempShortcut(StackPanel& row, Shortcut& tempShortcut, const uint32_t& rowIndex)
|
||||
{
|
||||
tempShortcut.operationType = Shortcut::OperationType::RunProgram;
|
||||
//tempShortcut.isRunProgram = true;
|
||||
|
||||
auto runProgramPathInput = row.FindName(L"runProgramPathInput_" + std::to_wstring(rowIndex)).as<TextBox>();
|
||||
auto runProgramArgsForProgramInput = row.FindName(L"runProgramArgsForProgramInput_" + std::to_wstring(rowIndex)).as<TextBox>();
|
||||
auto runProgramStartInDirInput = row.FindName(L"runProgramStartInDirInput_" + std::to_wstring(rowIndex)).as<TextBox>();
|
||||
auto runProgramElevationTypeCombo = row.FindName(L"runProgramElevationTypeCombo_" + std::to_wstring(rowIndex)).as<ComboBox>();
|
||||
auto runProgramAlreadyRunningAction = row.FindName(L"runProgramAlreadyRunningAction_" + std::to_wstring(rowIndex)).as<ComboBox>();
|
||||
auto runProgramStartWindow = row.FindName(L"runProgramStartWindow_" + std::to_wstring(rowIndex)).as<ComboBox>();
|
||||
|
||||
tempShortcut.runProgramFilePath = ShortcutControl::RemoveExtraQuotes(runProgramPathInput.Text().c_str());
|
||||
tempShortcut.runProgramArgs = (runProgramArgsForProgramInput.Text().c_str());
|
||||
tempShortcut.runProgramStartInDir = (runProgramStartInDirInput.Text().c_str());
|
||||
|
||||
// Assign instead of setting the value in the buffer since the previous value may not be a Shortcut
|
||||
|
||||
tempShortcut.elevationLevel = static_cast<Shortcut::ElevationLevel>(runProgramElevationTypeCombo.SelectedIndex());
|
||||
tempShortcut.alreadyRunningAction = static_cast<Shortcut::ProgramAlreadyRunningAction>(runProgramAlreadyRunningAction.SelectedIndex());
|
||||
tempShortcut.startWindowType = static_cast<Shortcut::StartWindowType>(runProgramStartWindow.SelectedIndex());
|
||||
}
|
||||
|
||||
std::wstring ShortcutControl::RemoveExtraQuotes(const std::wstring& str)
|
||||
{
|
||||
if (!str.empty() && str.front() == L'"' && str.back() == L'"')
|
||||
{
|
||||
return str.substr(1, str.size() - 2);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
ShortcutControl::ShortcutType ShortcutControl::GetShortcutType(const winrt::Windows::UI::Xaml::Controls::ComboBox& typeCombo)
|
||||
{
|
||||
if (typeCombo.SelectedIndex() == 0)
|
||||
{
|
||||
return ShortcutControl::ShortcutType::Shortcut;
|
||||
}
|
||||
else if (typeCombo.SelectedIndex() == 1)
|
||||
{
|
||||
return ShortcutControl::ShortcutType::Text;
|
||||
}
|
||||
else if (typeCombo.SelectedIndex() == 2)
|
||||
{
|
||||
return ShortcutControl::ShortcutType::RunProgram;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ShortcutControl::ShortcutType::OpenURI;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,14 +929,53 @@ StackPanel ShortcutControl::GetShortcutControl()
|
||||
// Function to create the detect shortcut UI window
|
||||
void ShortcutControl::CreateDetectShortcutWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KBMEditor::KeyboardManagerState& keyboardManagerState, const int colIndex, StackPanel table, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, StackPanel row, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow, HWND parentWindow, RemapBuffer& remapBuffer)
|
||||
{
|
||||
// check to see if this orig or map-to shortcut;
|
||||
bool isOrigShortcut = (colIndex == 0);
|
||||
|
||||
uint32_t rowIndex;
|
||||
|
||||
UIElementCollection children = table.Children();
|
||||
bool indexFound = children.IndexOf(row, rowIndex);
|
||||
|
||||
Shortcut shortcut;
|
||||
|
||||
if (shortcutRemapBuffer.size() > 0)
|
||||
{
|
||||
if (colIndex == 0)
|
||||
{
|
||||
shortcut = std::get<Shortcut>(shortcutRemapBuffer[rowIndex].first[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (shortcutRemapBuffer[rowIndex].first[1].index() != 1)
|
||||
{
|
||||
// not a shortcut, let's fix that.
|
||||
Shortcut newShortcut;
|
||||
shortcutRemapBuffer[rowIndex].first[1] = newShortcut;
|
||||
}
|
||||
shortcut = std::get<Shortcut>(shortcutRemapBuffer[rowIndex].first[1]);
|
||||
}
|
||||
|
||||
if (!shortcut.IsEmpty() && shortcut.HasChord())
|
||||
{
|
||||
keyboardManagerState.AllowChord = true;
|
||||
} else {
|
||||
keyboardManagerState.AllowChord = false;
|
||||
}
|
||||
}
|
||||
|
||||
//remapBuffer[rowIndex].first.
|
||||
|
||||
// ContentDialog for detecting shortcuts. This is the parent UI element.
|
||||
ContentDialog detectShortcutBox;
|
||||
ToggleSwitch allowChordSwitch;
|
||||
|
||||
// ContentDialog requires manually setting the XamlRoot (https://learn.microsoft.com/uwp/api/windows.ui.xaml.controls.contentdialog#contentdialog-in-appwindow-or-xaml-islands)
|
||||
detectShortcutBox.XamlRoot(xamlRoot);
|
||||
detectShortcutBox.Title(box_value(GET_RESOURCE_STRING(IDS_TYPESHORTCUT_TITLE)));
|
||||
|
||||
// Get the parent linked stack panel for the "Type shortcut" button that was clicked
|
||||
|
||||
VariableSizedWrapGrid linkedShortcutVariableSizedWrapGrid = UIHelpers::GetSiblingElement(sender.as<FrameworkElement>().Parent()).as<VariableSizedWrapGrid>();
|
||||
|
||||
auto unregisterKeys = [&keyboardManagerState]() {
|
||||
@@ -424,6 +1031,13 @@ void ShortcutControl::CreateDetectShortcutWindow(winrt::Windows::Foundation::IIn
|
||||
unregisterKeys();
|
||||
};
|
||||
|
||||
auto onReleaseSpace = [&keyboardManagerState,
|
||||
allowChordSwitch] {
|
||||
|
||||
keyboardManagerState.AllowChord = !keyboardManagerState.AllowChord;
|
||||
allowChordSwitch.IsOn(keyboardManagerState.AllowChord);
|
||||
};
|
||||
|
||||
auto onAccept = [onPressEnter,
|
||||
onReleaseEnter] {
|
||||
onPressEnter();
|
||||
@@ -481,6 +1095,22 @@ void ShortcutControl::CreateDetectShortcutWindow(winrt::Windows::Foundation::IIn
|
||||
unregisterKeys();
|
||||
};
|
||||
|
||||
if (isOrigShortcut)
|
||||
{
|
||||
// Hold space to allow chords. Chords are only available for origin shortcuts.
|
||||
keyboardManagerState.RegisterKeyDelay(
|
||||
VK_SPACE,
|
||||
selectDetectedShortcutAndResetKeys,
|
||||
[onReleaseSpace, detectShortcutBox](DWORD) {
|
||||
detectShortcutBox.Dispatcher().RunAsync(
|
||||
Windows::UI::Core::CoreDispatcherPriority::Normal,
|
||||
[onReleaseSpace] {
|
||||
onReleaseSpace();
|
||||
});
|
||||
},
|
||||
nullptr);
|
||||
}
|
||||
|
||||
// Cancel button
|
||||
detectShortcutBox.CloseButtonText(GET_RESOURCE_STRING(IDS_CANCEL_BUTTON));
|
||||
detectShortcutBox.CloseButtonClick([onCancel](winrt::Windows::Foundation::IInspectable const& sender, ContentDialogButtonClickEventArgs const& args) {
|
||||
@@ -525,6 +1155,42 @@ void ShortcutControl::CreateDetectShortcutWindow(winrt::Windows::Foundation::IIn
|
||||
keyStackPanel2.Visibility(Visibility::Collapsed);
|
||||
stackPanel.Children().Append(keyStackPanel2);
|
||||
|
||||
// Detect Chord
|
||||
Windows::UI::Xaml::Controls::StackPanel chordStackPanel;
|
||||
|
||||
if (isOrigShortcut)
|
||||
{
|
||||
constexpr double verticalMargin = 20.f;
|
||||
TextBlock allowChordText;
|
||||
allowChordText.Text(GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_ALLOW_CHORDS));
|
||||
allowChordText.FontSize(12);
|
||||
allowChordText.Margin({ 0, 12 + verticalMargin, 0, 0 });
|
||||
chordStackPanel.VerticalAlignment(VerticalAlignment::Center);
|
||||
allowChordText.TextAlignment(TextAlignment::Center);
|
||||
chordStackPanel.Orientation(Orientation::Horizontal);
|
||||
|
||||
allowChordSwitch.OnContent(nullptr);
|
||||
allowChordSwitch.OffContent(nullptr);
|
||||
allowChordSwitch.Margin({ 12, verticalMargin, 0, 0 });
|
||||
|
||||
chordStackPanel.Children().Append(allowChordText);
|
||||
chordStackPanel.Children().Append(allowChordSwitch);
|
||||
|
||||
stackPanel.Children().Append(chordStackPanel);
|
||||
allowChordSwitch.IsOn(keyboardManagerState.AllowChord);
|
||||
|
||||
auto toggleHandler = [allowChordSwitch, &keyboardManagerState](auto const& sender, auto const& e) {
|
||||
keyboardManagerState.AllowChord = allowChordSwitch.IsOn();
|
||||
|
||||
if (!allowChordSwitch.IsOn())
|
||||
{
|
||||
keyboardManagerState.ClearStoredShortcut();
|
||||
}
|
||||
};
|
||||
|
||||
allowChordSwitch.Toggled(toggleHandler);
|
||||
}
|
||||
|
||||
TextBlock holdEscInfo;
|
||||
holdEscInfo.Text(GET_RESOURCE_STRING(IDS_TYPE_HOLDESC));
|
||||
holdEscInfo.FontSize(12);
|
||||
@@ -537,6 +1203,16 @@ void ShortcutControl::CreateDetectShortcutWindow(winrt::Windows::Foundation::IIn
|
||||
holdEnterInfo.Margin({ 0, 0, 0, 0 });
|
||||
stackPanel.Children().Append(holdEnterInfo);
|
||||
|
||||
if (isOrigShortcut)
|
||||
{
|
||||
// Hold space to allow chords. Chords are only available for origin shortcuts.
|
||||
TextBlock holdSpaceInfo;
|
||||
holdSpaceInfo.Text(GET_RESOURCE_STRING(IDS_TYPE_HOLDSPACE));
|
||||
holdSpaceInfo.FontSize(12);
|
||||
holdSpaceInfo.Margin({ 0, 0, 0, 0 });
|
||||
stackPanel.Children().Append(holdSpaceInfo);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// If a layout update has been triggered by other methods (e.g.: adapting to zoom level), this may throw an exception.
|
||||
@@ -551,4 +1227,9 @@ void ShortcutControl::CreateDetectShortcutWindow(winrt::Windows::Foundation::IIn
|
||||
|
||||
// Show the dialog
|
||||
detectShortcutBox.ShowAsync();
|
||||
|
||||
if (!shortcut.IsEmpty() && keyboardManagerState.AllowChord)
|
||||
{
|
||||
keyboardManagerState.SetDetectedShortcut(shortcut);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,13 +26,16 @@ private:
|
||||
winrt::Windows::Foundation::IInspectable shortcutDropDownVariableSizedWrapGrid;
|
||||
|
||||
// Button to type the shortcut
|
||||
winrt::Windows::Foundation::IInspectable typeShortcut;
|
||||
Button btnPickShortcut;
|
||||
|
||||
// StackPanel to hold the shortcut
|
||||
StackPanel spBtnPickShortcut;
|
||||
|
||||
// StackPanel to parent the above controls
|
||||
winrt::Windows::Foundation::IInspectable shortcutControlLayout;
|
||||
|
||||
// StackPanel to parent the first line of "To" Column
|
||||
winrt::Windows::Foundation::IInspectable keyComboAndSelectStackPanel;
|
||||
winrt::Windows::Foundation::IInspectable keyComboStackPanel;
|
||||
|
||||
// Function to set the accessible name of the target app text box
|
||||
static void SetAccessibleNameForTextBox(TextBox targetAppTextBox, int rowIndex);
|
||||
@@ -40,6 +43,15 @@ private:
|
||||
// Function to set the accessible names for all the controls in a row
|
||||
static void UpdateAccessibleNames(StackPanel sourceColumn, StackPanel mappedToColumn, TextBox targetAppTextBox, Button deleteButton, int rowIndex);
|
||||
|
||||
// enum for the type of shortcut, to make it easier to switch on and read
|
||||
enum class ShortcutType
|
||||
{
|
||||
Shortcut,
|
||||
Text,
|
||||
RunProgram,
|
||||
OpenURI
|
||||
};
|
||||
|
||||
public:
|
||||
// Handle to the current Edit Shortcuts Window
|
||||
static HWND editShortcutsWindowHandle;
|
||||
@@ -56,8 +68,20 @@ public:
|
||||
// constructor
|
||||
ShortcutControl(StackPanel table, StackPanel row, const int colIndex, TextBox targetApp);
|
||||
|
||||
// Function to that will CreateDetectShortcutWindow, created here to it can be done automatically when "new shortcut" is clicked.
|
||||
void OpenNewShortcutControlRow(StackPanel table, StackPanel row);
|
||||
|
||||
// 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(StackPanel& parent, std::vector<std::vector<std::unique_ptr<ShortcutControl>>>& keyboardRemapControlObjects, const Shortcut& originalKeys = Shortcut(), const KeyShortcutTextUnion& newKeys = Shortcut(), const std::wstring& targetAppName = L"");
|
||||
static ShortcutControl& AddNewShortcutControlRow(StackPanel& parent, std::vector<std::vector<std::unique_ptr<ShortcutControl>>>& keyboardRemapControlObjects, const Shortcut& originalKeys = Shortcut(), const KeyShortcutTextUnion& newKeys = Shortcut(), const std::wstring& targetAppName = L"");
|
||||
|
||||
// Function to delete the shortcut control
|
||||
static void ShortcutControl::DeleteShortcutControl(StackPanel& parent, std::vector<std::vector<std::unique_ptr<ShortcutControl>>>& keyboardRemapControlObjects, int index);
|
||||
|
||||
// Function to get the shortcut type
|
||||
static ShortcutType GetShortcutType(const Controls::ComboBox& typeCombo);
|
||||
|
||||
// Function to remove extra quotes from the start and end of the string (used where we will add them as needed later)
|
||||
static std::wstring ShortcutControl::RemoveExtraQuotes(const std::wstring& str);
|
||||
|
||||
// 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();
|
||||
@@ -65,3 +89,9 @@ public:
|
||||
// Function to create the detect shortcut UI window
|
||||
static void CreateDetectShortcutWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KBMEditor::KeyboardManagerState& keyboardManagerState, const int colIndex, StackPanel table, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, StackPanel controlLayout, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow, HWND parentWindow, RemapBuffer& remapBuffer);
|
||||
};
|
||||
|
||||
StackPanel SetupRunProgramControls(StackPanel& parent, StackPanel& row, Shortcut& shortCut, winrt::Windows::UI::Xaml::Thickness& textInputMargin, ::StackPanel& _controlStackPanel);
|
||||
|
||||
void CreateNewTempShortcut(StackPanel& row, Shortcut& tempShortcut, const uint32_t& rowIndex);
|
||||
|
||||
StackPanel SetupOpenURIControls(StackPanel& parent, StackPanel& row, Shortcut& shortCut, winrt::Windows::UI::Xaml::Thickness& textInputMargin, ::StackPanel& _controlStackPanel);
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
|
||||
#include <common/monitor_utils.h>
|
||||
|
||||
using namespace Windows::UI::Xaml::Media;
|
||||
using namespace Windows::UI::Xaml::Automation::Peers;
|
||||
|
||||
namespace UIHelpers
|
||||
{
|
||||
// This method sets focus to the first Type button on the last row of the Grid
|
||||
@@ -19,9 +22,11 @@ namespace UIHelpers
|
||||
|
||||
// Get Type Button from the first line
|
||||
Button typeButton = firstLineIntoColumn.Children().GetAt(1).as<Button>();
|
||||
|
||||
// Set programmatic focus on the button
|
||||
typeButton.Focus(FocusState::Programmatic);
|
||||
if (typeButton != nullptr)
|
||||
{
|
||||
// Set programmatic focus on the button
|
||||
typeButton.Focus(FocusState::Programmatic);
|
||||
}
|
||||
}
|
||||
|
||||
RECT GetForegroundWindowDesktopRect()
|
||||
@@ -53,6 +58,34 @@ namespace UIHelpers
|
||||
return parentElement.Children().GetAt(index + 1);
|
||||
}
|
||||
|
||||
winrt::Windows::Foundation::IInspectable GetLabelWrapped(const winrt::Windows::Foundation::IInspectable& element, std::wstring label, double textWidth, HorizontalAlignment horizontalAlignment)
|
||||
{
|
||||
StackPanel sp = StackPanel();
|
||||
|
||||
try
|
||||
{
|
||||
sp.Name(L"Wrapped_" + element.as<FrameworkElement>().Name());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
|
||||
sp.Orientation(Orientation::Horizontal);
|
||||
sp.HorizontalAlignment(horizontalAlignment);
|
||||
TextBlock text;
|
||||
text.FontWeight(Text::FontWeights::Bold());
|
||||
text.Text(label);
|
||||
|
||||
if (textWidth >= 0)
|
||||
{
|
||||
text.Width(textWidth);
|
||||
}
|
||||
|
||||
sp.Children().Append(text);
|
||||
sp.Children().Append(element.as<FrameworkElement>());
|
||||
return sp;
|
||||
}
|
||||
|
||||
winrt::Windows::Foundation::IInspectable GetWrapped(const winrt::Windows::Foundation::IInspectable& element, double width)
|
||||
{
|
||||
StackPanel sp = StackPanel();
|
||||
|
||||
@@ -27,6 +27,9 @@ namespace UIHelpers
|
||||
|
||||
winrt::Windows::Foundation::IInspectable GetWrapped(const winrt::Windows::Foundation::IInspectable& element, double width);
|
||||
|
||||
// Function to return a StackPanel with an element and a TextBlock label.
|
||||
winrt::Windows::Foundation::IInspectable GetLabelWrapped(const winrt::Windows::Foundation::IInspectable& element, std::wstring label, double width, HorizontalAlignment horizontalAlignment = HorizontalAlignment::Left);
|
||||
|
||||
// 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::pair<DWORD, std::wstring>>& list);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user