diff --git a/.github/actions/spell-check/allow/code.txt b/.github/actions/spell-check/allow/code.txt index 248009b4f2e4..92331140f8e5 100644 --- a/.github/actions/spell-check/allow/code.txt +++ b/.github/actions/spell-check/allow/code.txt @@ -84,6 +84,7 @@ Ctrls EXSEL HOLDENTER HOLDESC +HOLDSPACE KBDLLHOOKSTRUCT keyevent LAlt diff --git a/.github/actions/spell-check/allow/names.txt b/.github/actions/spell-check/allow/names.txt index 2d9bb89b75d8..223a9a42d284 100644 --- a/.github/actions/spell-check/allow/names.txt +++ b/.github/actions/spell-check/allow/names.txt @@ -99,6 +99,7 @@ Pooja Quriz randyrants ricardosantos +ritchielawrence robmikh Rutkas ryanbodrug @@ -128,6 +129,7 @@ Zykova # OTHERS +cmdow Controlz cortana fancymouse diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index fbef86f40de6..7b4900ccb17b 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -99,6 +99,7 @@ bbwe bck BESTEFFORT bhid +BIF bigbar bigobj binlog @@ -125,6 +126,7 @@ BPBF bpmf bpp Browsable +BROWSEINFO bsd bthprops bti @@ -212,6 +214,7 @@ cominterop commandline COMMANDTITLE commctrl +commdlg compmgmt COMPOSITIONFULL comsupp @@ -473,6 +476,7 @@ FILELOCKSMITHCONTEXTMENU FILELOCKSMITHEXT FILELOCKSMITHLIB FILELOCKSMITHLIBINTEROP +FILEMUSTEXIST FILEOP FILEOS FILESUBTYPE @@ -801,6 +805,7 @@ LPCTSTR lpdw lpfn LPINPUT +LPITEMIDLIST lpmi LPMINMAXINFO LPMONITORINFO @@ -808,6 +813,7 @@ LPOSVERSIONINFOEXW LPQUERY lprc LPSAFEARRAY +lpstr lpsz lpt LPTHREAD @@ -971,6 +977,7 @@ netsetup netsh newcolor newdev +NEWDIALOGSTYLE newitem newpath newrow @@ -1041,6 +1048,7 @@ ocr Ocrsettings odbccp officehubintl +OFN ofs oldcolor olditem @@ -1051,6 +1059,7 @@ OLECHAR onebranch OOBEPT opencode +OPENFILENAME opensource openxmlformats OPTIMIZEFORINVOKE @@ -1085,6 +1094,7 @@ parray PARTIALCONFIRMATIONDIALOGTITLE PATCOPY pathcch +PATHMUSTEXIST Pathto PATINVERT PATPAINT @@ -1173,6 +1183,7 @@ PRINTCLIENT printmanagement prm proactively +PROCESSENTRY PROCESSKEY processthreadsapi PRODEXT @@ -1292,6 +1303,7 @@ RESTORETOMAXIMIZED restrictedcapabilities restrictederrorinfo resultlist +RETURNONLYFSDIRS RGBQUAD rgbs rgelt @@ -1436,6 +1448,7 @@ sln SMALLICON smartphone SMTO +SNAPPROCESS snwprintf softline SOURCECLIENTAREAONLY @@ -1578,6 +1591,7 @@ tlbimp TMPVAR TNP toggleswitch +Toolhelp toolkitconverters Toolset toolwindow @@ -1634,6 +1648,7 @@ Updatelayout UPGRADINGPRODUCTCODE Uptool urld +urlmon Usb USEDEFAULT USEFILEATTRIBUTES @@ -1709,6 +1724,7 @@ wcautil WCE wcex WClass +wcsicmp wcsnicmp WDA wdp diff --git a/src/common/interop/keyboard_layout.cpp b/src/common/interop/keyboard_layout.cpp index 2b74757e666b..5254f1ee78e0 100644 --- a/src/common/interop/keyboard_layout.cpp +++ b/src/common/interop/keyboard_layout.cpp @@ -27,6 +27,17 @@ std::wstring LayoutMap::GetKeyName(DWORD key) return impl->GetKeyName(key); } +DWORD LayoutMap::GetKeyFromName(const std::wstring& name) +{ + auto list = impl->GetKeyNameList(false); + for (const auto& [value, key] : list) + { + if (key == name) + return value; + } + return {}; +} + std::vector LayoutMap::GetKeyCodeList(const bool isShortcut) { return impl->GetKeyCodeList(isShortcut); diff --git a/src/common/interop/keyboard_layout.h b/src/common/interop/keyboard_layout.h index c6b7cf21cc60..c81fc51760ac 100644 --- a/src/common/interop/keyboard_layout.h +++ b/src/common/interop/keyboard_layout.h @@ -11,6 +11,7 @@ class LayoutMap ~LayoutMap(); void UpdateLayout(); std::wstring GetKeyName(DWORD key); + DWORD GetKeyFromName(const std::wstring& name); std::vector GetKeyCodeList(const bool isShortcut = false); std::vector> GetKeyNameList(const bool isShortcut = false); diff --git a/src/common/utils/elevation.h b/src/common/utils/elevation.h index 62ab1c75d5f2..7f2ecbf6df9e 100644 --- a/src/common/utils/elevation.h +++ b/src/common/utils/elevation.h @@ -71,7 +71,7 @@ namespace } result = spView->QueryInterface(riid, ppv); - if (result != S_OK || ppv == nullptr || *ppv == nullptr ) + if (result != S_OK || ppv == nullptr || *ppv == nullptr) { Logger::warn(L"Failed to query interface. {}", GetErrorString(result)); return false; @@ -83,7 +83,7 @@ namespace inline bool GetDesktopAutomationObject(REFIID riid, void** ppv) { CComPtr spsv; - + // Desktop may not be available on startup auto attempts = 5; for (auto i = 1; i <= attempts; i++) @@ -207,8 +207,34 @@ inline bool drop_elevated_privileges() return result; } +// Run command as different user, returns true if succeeded +inline HANDLE run_as_different_user(const std::wstring& file, const std::wstring& params, const wchar_t* workingDir = nullptr, const bool showWindow = true) +{ + Logger::info(L"run_elevated with params={}", params); + SHELLEXECUTEINFOW exec_info = { 0 }; + exec_info.cbSize = sizeof(SHELLEXECUTEINFOW); + exec_info.lpVerb = L"runAsUser"; + exec_info.lpFile = file.c_str(); + exec_info.lpParameters = params.c_str(); + exec_info.hwnd = 0; + exec_info.fMask = SEE_MASK_NOCLOSEPROCESS; + exec_info.lpDirectory = workingDir; + exec_info.hInstApp = 0; + if (showWindow) + { + exec_info.nShow = SW_SHOWDEFAULT; + } + else + { + // might have limited success, but only option with ShellExecuteExW + exec_info.nShow = SW_HIDE; + } + + return ShellExecuteExW(&exec_info) ? exec_info.hProcess : nullptr; +} + // Run command as elevated user, returns true if succeeded -inline HANDLE run_elevated(const std::wstring& file, const std::wstring& params) +inline HANDLE run_elevated(const std::wstring& file, const std::wstring& params, const wchar_t* workingDir = nullptr, const bool showWindow = true) { Logger::info(L"run_elevated with params={}", params); SHELLEXECUTEINFOW exec_info = { 0 }; @@ -218,15 +244,24 @@ inline HANDLE run_elevated(const std::wstring& file, const std::wstring& params) exec_info.lpParameters = params.c_str(); exec_info.hwnd = 0; exec_info.fMask = SEE_MASK_NOCLOSEPROCESS; - exec_info.lpDirectory = 0; + exec_info.lpDirectory = workingDir; exec_info.hInstApp = 0; - exec_info.nShow = SW_SHOWDEFAULT; + + if (showWindow) + { + exec_info.nShow = SW_SHOWDEFAULT; + } + else + { + // might have limited success, but only option with ShellExecuteExW + exec_info.nShow = SW_HIDE; + } return ShellExecuteExW(&exec_info) ? exec_info.hProcess : nullptr; } // Run command as non-elevated user, returns true if succeeded, puts the process id into returnPid if returnPid != NULL -inline bool run_non_elevated(const std::wstring& file, const std::wstring& params, DWORD* returnPid, const wchar_t* workingDir = nullptr) +inline bool run_non_elevated(const std::wstring& file, const std::wstring& params, DWORD* returnPid, const wchar_t* workingDir = nullptr, const bool showWindow = true) { Logger::info(L"run_non_elevated with params={}", params); auto executable_args = L"\"" + file + L"\""; @@ -292,15 +327,22 @@ inline bool run_non_elevated(const std::wstring& file, const std::wstring& param STARTUPINFOEX siex = { 0 }; siex.lpAttributeList = pptal; siex.StartupInfo.cb = sizeof(siex); - PROCESS_INFORMATION pi = { 0 }; + auto dwCreationFlags = EXTENDED_STARTUPINFO_PRESENT; + + if (!showWindow) + { + siex.StartupInfo.dwFlags = STARTF_USESHOWWINDOW; + siex.StartupInfo.wShowWindow = SW_HIDE; + dwCreationFlags = CREATE_NO_WINDOW; + } auto succeeded = CreateProcessW(file.c_str(), &executable_args[0], nullptr, nullptr, FALSE, - EXTENDED_STARTUPINFO_PRESENT, + dwCreationFlags, nullptr, workingDir, &siex.StartupInfo, @@ -341,7 +383,10 @@ inline bool RunNonElevatedEx(const std::wstring& file, const std::wstring& param catch (...) { } - if (SUCCEEDED(co_init)) CoUninitialize(); + if (SUCCEEDED(co_init)) + { + CoUninitialize(); + } return success; } @@ -372,7 +417,7 @@ inline std::optional RunNonElevatedFailsafe(const std::wstring& fil } } - auto handles = getProcessHandlesByName(std::filesystem::path{ file }.filename().wstring(), PROCESS_QUERY_INFORMATION | SYNCHRONIZE | handleAccess ); + auto handles = getProcessHandlesByName(std::filesystem::path{ file }.filename().wstring(), PROCESS_QUERY_INFORMATION | SYNCHRONIZE | handleAccess); if (handles.empty()) return std::nullopt; @@ -385,7 +430,7 @@ inline std::optional RunNonElevatedFailsafe(const std::wstring& fil } // Run command with the same elevation, returns true if succeeded -inline bool run_same_elevation(const std::wstring& file, const std::wstring& params, DWORD* returnPid) +inline bool run_same_elevation(const std::wstring& file, const std::wstring& params, DWORD* returnPid, const wchar_t* workingDir = nullptr) { auto executable_args = L"\"" + file + L"\""; if (!params.empty()) @@ -403,7 +448,7 @@ inline bool run_same_elevation(const std::wstring& file, const std::wstring& par FALSE, 0, nullptr, - nullptr, + workingDir, &si, &pi); diff --git a/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.cpp b/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.cpp index 54e2c7087d05..260e4c1f5cee 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.cpp @@ -77,8 +77,26 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, type = static_cast(_wtoi(cmdArgs[1])); } - if (numArgs == 3) + std::wstring keysForShortcutToEdit = L""; + std::wstring action = L""; + + + // do some parsing of the cmdline arg to see if we need to behave different + // like, single edit mode, or "delete" mode. + // These extra args are from "OpenEditor" in the KeyboardManagerViewModel + if (numArgs >= 3) { + if (numArgs >= 4) + { + keysForShortcutToEdit = std::wstring(cmdArgs[3]); + } + + if (numArgs >= 5) + { + action = std::wstring(cmdArgs[4]); + } + + std::wstring pid = std::wstring(cmdArgs[2]); Logger::trace(L"Editor started from the settings with pid {}", pid); if (!pid.empty()) @@ -108,7 +126,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, return -1; } - editor->OpenEditorWindow(type); + editor->OpenEditorWindow(type, keysForShortcutToEdit, action); editor = nullptr; @@ -149,7 +167,7 @@ bool KeyboardManagerEditor::StartLowLevelKeyboardHook() return (hook != nullptr); } -void KeyboardManagerEditor::OpenEditorWindow(KeyboardManagerEditorType type) +void KeyboardManagerEditor::OpenEditorWindow(KeyboardManagerEditorType type, std::wstring keysForShortcutToEdit, std::wstring action) { switch (type) { @@ -157,7 +175,7 @@ void KeyboardManagerEditor::OpenEditorWindow(KeyboardManagerEditorType type) CreateEditKeyboardWindow(hInstance, keyboardManagerState, mappingConfiguration); break; case KeyboardManagerEditorType::ShortcutEditor: - CreateEditShortcutsWindow(hInstance, keyboardManagerState, mappingConfiguration); + CreateEditShortcutsWindow(hInstance, keyboardManagerState, mappingConfiguration, keysForShortcutToEdit, action); } } diff --git a/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.h b/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.h index e6c64df3fd82..ff914aeb3e64 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.h +++ b/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.h @@ -23,20 +23,20 @@ class KeyboardManagerEditor } bool StartLowLevelKeyboardHook(); - void OpenEditorWindow(KeyboardManagerEditorType type); + void OpenEditorWindow(KeyboardManagerEditorType type, std::wstring keysForShortcutToEdit, std::wstring action); // Function called by the hook procedure to handle the events. This is the starting point function for remapping intptr_t HandleKeyboardHookEvent(LowlevelKeyboardEvent* data) noexcept; private: static LRESULT CALLBACK KeyHookProc(int nCode, WPARAM wParam, LPARAM lParam); - + inline static HHOOK hook; HINSTANCE hInstance; KBMEditor::KeyboardManagerState keyboardManagerState; MappingConfiguration mappingConfiguration; - + // Object of class which implements InputInterface. Required for calling library functions while enabling testing KeyboardManagerInput::Input inputHandler; }; diff --git a/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.vcxproj b/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.vcxproj index 52e32232c0e0..0abafcf2fdb8 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.vcxproj +++ b/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.vcxproj @@ -154,7 +154,9 @@ - + + Designer + diff --git a/src/modules/keyboardmanager/KeyboardManagerEditor/Resources.resx b/src/modules/keyboardmanager/KeyboardManagerEditor/Resources.resx index 1e342b4414f4..fcf3aa2574c8 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditor/Resources.resx +++ b/src/modules/keyboardmanager/KeyboardManagerEditor/Resources.resx @@ -144,6 +144,15 @@ Remap shortcuts + + Edit shortcut + + + Select program + + + Select path + OK @@ -161,12 +170,60 @@ To send: - To send: + To: Target app: - + + Args: + + + Elevation: + + + Visibility: + + + App: + + + Start in: + + + Action: + + + Path/URI: + + + Shortcut: + + + Keys: + + + Normal + + + Hidden + + + If running: + + + Running sound: + + + Start sound: + + + Key in use + + + Select file + + Warning: The following keys do not have assignments: Key on a keyboard @@ -184,7 +241,11 @@ Key on a keyboard - Select the shortcut you want to change ("Select") and then configure the key, shortcut or text you want it to send ("To send"). + Edit shortcuts to activate and then configure the actions to happen when activated. + Key on a keyboard + + + Edit shortcut to activate and then configure the action to happen when activated. Key on a keyboard @@ -211,6 +272,9 @@ Keys selected: Keys on a keyboard + + Hold Space to toggle allow chord + Hold Enter to continue @@ -220,6 +284,24 @@ All apps + + Allow chords + + + Path to program + + + E.g.: https://website.url + + + What can I use? + + + Arguments for program + + + Start in directory + Remapping successful @@ -288,12 +370,20 @@ Key on a keyboard - Text + Send Text - Key/Shortcut + Send Key/Shortcut Key on a keyboard + + Run Program + Run Program + + + Open URI + URI to open. + Add key remapping Key on a keyboard @@ -304,6 +394,33 @@ Delete remapping + + Do nothing + + + Show window + + + Start another + + + Close + + + End task + + + Close and end task + + + Normal + + + Elevated + + + Different User + Remapping deleted diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/BufferValidationHelpers.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/BufferValidationHelpers.cpp index 228e680f2678..98dfdd5ff213 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/BufferValidationHelpers.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/BufferValidationHelpers.cpp @@ -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 diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditShortcutsWindow.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditShortcutsWindow.cpp index e5a80db5a7a4..3f3578aa0282 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditShortcutsWindow.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditShortcutsWindow.cpp @@ -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>> 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()); }); + // 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(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()); } + 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(lParam); float minWidth = EditorConstants::MinimumEditShortcutsWindowWidth; float minHeight = EditorConstants::MinimumEditShortcutsWindowHeight; + DPIAware::Convert(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONULL), minWidth, minHeight); + mmi->ptMinTrackSize.x = static_cast(minWidth); mmi->ptMinTrackSize.y = static_cast(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); } diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditShortcutsWindow.h b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditShortcutsWindow.h index ddd2a2798ff5..dc5f992cf7e5 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditShortcutsWindow.h +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditShortcutsWindow.h @@ -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(); diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditorConstants.h b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditorConstants.h index cd38ad8d83c0..f89049982e07 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditorConstants.h +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditorConstants.h @@ -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 diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditorHelpers.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditorHelpers.cpp index e21f0c9aa914..059d8931cc5b 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditorHelpers.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditorHelpers.cpp @@ -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) diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDropDownControl.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDropDownControl.cpp index 39a7bb2aa321..e83c55a07362 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDropDownControl.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDropDownControl.cpp @@ -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().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(); 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 KeyDropDownControl::GetSelectedCodesFromStackPanel(Variable std::vector 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(); - selectedKeyCodes.push_back(GetSelectedValue(ItDropDown)); + if (auto ItDropDown = parent.Children().GetAt(i).try_as(); 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(); sp) + { + for (uint32_t j = 0; j < sp.Children().Size(); ++j) + { + auto border = sp.Children().GetAt(j).try_as(); + + // 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(); + 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 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; + } + + KeyDropDownControl::AddDropDown(table, row, parent, colIndex, remapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, isSingleKeyWindow, ignoreWarning); - // If more than one key is to be added, ignore a shortcut to key warning on partially entering the remapping - if (shortcutKeyCodes.size() > 1) + 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(); + 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(); - currentDropDown.SelectedValue(winrt::box_value(std::to_wstring(shortcutKeyCodes[i]))); - } - } + auto nextI = static_cast(shortcutKeyCodes.size()); + ComboBox currentDropDown = parent.Children().GetAt(nextI).as(); + currentDropDown.SelectedValue(winrt::box_value(std::to_wstring(shortcut.GetSecondKey()))); } } diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDropDownControl.h b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDropDownControl.h index b9e3c86e9278..14299316fbeb 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDropDownControl.h +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDropDownControl.h @@ -63,6 +63,7 @@ class KeyDropDownControl // 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 @@ class KeyDropDownControl static void AddShortcutToControl(Shortcut shortcut, StackPanel table, VariableSizedWrapGrid parent, KBMEditor::KeyboardManagerState& keyboardManagerState, const int colIndex, std::vector>& keyDropDownControlObjects, RemapBuffer& remapBuffer, StackPanel row, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow); // Get keys name list depending if Disable is in dropdown - static std::vector> GetKeyList(bool isShortcut, bool renderDisable); + static std::vector> 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 keys); diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerEditorStrings.h b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerEditorStrings.h index 360e9fe0bc1e..a4eb0a8d1249 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerEditorStrings.h +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerEditorStrings.h @@ -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); } diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerState.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerState.cpp index 4ed756ba6f6c..69e583bd853c 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerState.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerState.cpp @@ -110,13 +110,13 @@ void KeyboardManagerState::ConfigureDetectSingleKeyRemapUI(const StackPanel& tex currentSingleKeyUI = textBlock.as(); } -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()); 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().Visibility(Visibility::Collapsed); } + auto lastStackPanel = currentShortcutUI2.as(); for (int i = 0; i < shortcut.size(); i++) { if (i < 3) { AddKeyToLayout(currentShortcutUI1.as(), shortcut[i]); + lastStackPanel = currentShortcutUI1.as(); } else { AddKeyToLayout(currentShortcutUI2.as(), shortcut[i]); + lastStackPanel = currentShortcutUI2.as(); } } + + 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 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 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 lock(detectedShortcut_mutex); + updateUI = detectedShortcut.SetKey(key); + lock.unlock(); + } + } + else + { + std::unique_lock 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 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 detectedShortcut_lock(detectedShortcut_mutex); + detectedShortcut.Reset(); +} + bool KeyboardManagerState::HandleKeyDelayEvent(LowlevelKeyboardEvent* ev) { if (currentUIWindow != GetForegroundWindow()) diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerState.h b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerState.h index a62b28d77d7e..1687fdabdf84 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerState.h +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerState.h @@ -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> keyDelays; std::mutex keyDelays_mutex; + public: // 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); + 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; - public: // 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); diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/ShortcutControl.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/ShortcutControl.cpp index 4091c4e4d2d4..6afa4ce349ad 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/ShortcutControl.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/ShortcutControl.cpp @@ -1,6 +1,8 @@ #include "pch.h" #include "ShortcutControl.h" - +#include +#include +#include #include #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().Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal); shortcutDropDownVariableSizedWrapGrid.as().MaximumRowsOrColumns(3); - typeShortcut.as