From 69d706560f530961e09f393a5c9e48513134f244 Mon Sep 17 00:00:00 2001 From: Michael Jolley Date: Thu, 11 Dec 2025 19:08:35 -0600 Subject: [PATCH] More deux, two --- PowerToys.slnx | 1 + .../CmdPalKeyboardService.def | 3 + .../CmdPalKeyboardService.vcxproj | 176 +++++++ .../CmdPalKeyboardService.vcxproj.filters | 30 ++ .../KeyboardListener.cpp | 165 +++++++ .../CmdPalKeyboardService/KeyboardListener.h | 67 +++ .../KeyboardListener.idl | 16 + .../KeyboardListener_h.h | 453 ++++++++++++++++++ .../CmdPalKeyboardService/PropertySheet.props | 17 + .../UI/CmdPalKeyboardService/packages.config | 4 + .../Deux/UI/CmdPalKeyboardService/pch.cpp | 1 + .../Deux/UI/CmdPalKeyboardService/pch.h | 4 + .../CommandHotKey.cs | 12 + .../{HotkeySettings.cs => Hotkey.cs} | 8 +- .../KeyPressedEventArgs.cs | 12 + .../SettingsModel.cs | 6 +- .../Extensions/IExtensionService.cs | 1 - .../KeyboardService.cs | 257 ++++++++++ ...icrosoft.CommandPalette.UI.Services.csproj | 12 + .../Microsoft.CommandPalette.UI/App.xaml.cs | 1 + .../MainWindow.xaml.cs | 105 +--- .../Microsoft.CommandPalette.UI.csproj | 1 + .../Pages/ShellPage.xaml.cs | 267 ++++++++++- .../Pages/ShellPage.xaml.cs | 6 - 24 files changed, 1515 insertions(+), 110 deletions(-) create mode 100644 src/modules/Deux/UI/CmdPalKeyboardService/CmdPalKeyboardService.def create mode 100644 src/modules/Deux/UI/CmdPalKeyboardService/CmdPalKeyboardService.vcxproj create mode 100644 src/modules/Deux/UI/CmdPalKeyboardService/CmdPalKeyboardService.vcxproj.filters create mode 100644 src/modules/Deux/UI/CmdPalKeyboardService/KeyboardListener.cpp create mode 100644 src/modules/Deux/UI/CmdPalKeyboardService/KeyboardListener.h create mode 100644 src/modules/Deux/UI/CmdPalKeyboardService/KeyboardListener.idl create mode 100644 src/modules/Deux/UI/CmdPalKeyboardService/KeyboardListener_h.h create mode 100644 src/modules/Deux/UI/CmdPalKeyboardService/PropertySheet.props create mode 100644 src/modules/Deux/UI/CmdPalKeyboardService/packages.config create mode 100644 src/modules/Deux/UI/CmdPalKeyboardService/pch.cpp create mode 100644 src/modules/Deux/UI/CmdPalKeyboardService/pch.h create mode 100644 src/modules/Deux/UI/Microsoft.CommandPalette.UI.Models/CommandHotKey.cs rename src/modules/Deux/UI/Microsoft.CommandPalette.UI.Models/{HotkeySettings.cs => Hotkey.cs} (96%) create mode 100644 src/modules/Deux/UI/Microsoft.CommandPalette.UI.Models/KeyPressedEventArgs.cs create mode 100644 src/modules/Deux/UI/Microsoft.CommandPalette.UI.Services/KeyboardService.cs diff --git a/PowerToys.slnx b/PowerToys.slnx index 57ac4fff86..b3e10e22de 100644 --- a/PowerToys.slnx +++ b/PowerToys.slnx @@ -365,6 +365,7 @@ + diff --git a/src/modules/Deux/UI/CmdPalKeyboardService/CmdPalKeyboardService.def b/src/modules/Deux/UI/CmdPalKeyboardService/CmdPalKeyboardService.def new file mode 100644 index 0000000000..24e7c1235c --- /dev/null +++ b/src/modules/Deux/UI/CmdPalKeyboardService/CmdPalKeyboardService.def @@ -0,0 +1,3 @@ +EXPORTS +DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE +DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE diff --git a/src/modules/Deux/UI/CmdPalKeyboardService/CmdPalKeyboardService.vcxproj b/src/modules/Deux/UI/CmdPalKeyboardService/CmdPalKeyboardService.vcxproj new file mode 100644 index 0000000000..5b40718214 --- /dev/null +++ b/src/modules/Deux/UI/CmdPalKeyboardService/CmdPalKeyboardService.vcxproj @@ -0,0 +1,176 @@ + + + + + true + true + {FC192BCE-BFBB-40F5-8C2D-9858DE12C31F} + CmdPalKeyboardService + CmdPalKeyboardService + false + + + + true + false + + + + + true + true + en-US + 17.0 + 10.0 + + + true + true + true + Windows Store + false + + + + + <_VC_Target_Library_Platform>Desktop + <_NoWinAPIFamilyApp>true + + + + + DynamicLibrary + v143 + Unicode + false + + + true + true + + + false + true + false + + + + + + + + + + + + + + + CmdPalKeyboardService + ..\..\..\..\..\$(Platform)\$(Configuration)\ + + + + $(IntDir)pch.pch + Level4 + %(AdditionalOptions) /bigobj + _WINRT_DLL;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions) + ../../../..;%(AdditionalIncludeDirectories) + $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) + + + Console + false + CmdPalKeyboardService.def + Shell32.lib;user32.lib;%(AdditionalDependencies) + + + + + _DEBUG;%(PreprocessorDefinitions) + + + + + NDEBUG;%(PreprocessorDefinitions) + + + true + true + + + + + + KeyboardListener.idl + + + + + Create + + + KeyboardListener.idl + + + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + + + + MultiThreadedDebug + + + + %(IgnoreSpecificDefaultLibraries);libucrtd.lib + %(AdditionalOptions) /defaultlib:ucrtd.lib + + + + + + MultiThreaded + + + + %(IgnoreSpecificDefaultLibraries);libucrt.lib + %(AdditionalOptions) /defaultlib:ucrt.lib + + + + \ No newline at end of file diff --git a/src/modules/Deux/UI/CmdPalKeyboardService/CmdPalKeyboardService.vcxproj.filters b/src/modules/Deux/UI/CmdPalKeyboardService/CmdPalKeyboardService.vcxproj.filters new file mode 100644 index 0000000000..2309326e71 --- /dev/null +++ b/src/modules/Deux/UI/CmdPalKeyboardService/CmdPalKeyboardService.vcxproj.filters @@ -0,0 +1,30 @@ + + + + + accd3aa8-1ba0-4223-9bbe-0c431709210b + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms + + + {926ab91d-31b4-48c3-b9a4-e681349f27f0} + + + + + + + + + + + + + + + + + + + + diff --git a/src/modules/Deux/UI/CmdPalKeyboardService/KeyboardListener.cpp b/src/modules/Deux/UI/CmdPalKeyboardService/KeyboardListener.cpp new file mode 100644 index 0000000000..cf52fe1863 --- /dev/null +++ b/src/modules/Deux/UI/CmdPalKeyboardService/KeyboardListener.cpp @@ -0,0 +1,165 @@ +#include "pch.h" +#include "KeyboardListener.h" +#include "KeyboardListener.g.cpp" + +// #include +// #include +#include + +namespace +{ +} + +namespace winrt::CmdPalKeyboardService::implementation +{ + KeyboardListener::KeyboardListener() + { + s_instance = this; + } + + void KeyboardListener::Start() + { +#if defined(DISABLE_LOWLEVEL_HOOKS_WHEN_DEBUGGED) + const bool hook_disabled = IsDebuggerPresent(); +#else + const bool hook_disabled = false; +#endif + if (!hook_disabled) + { + if (!s_llKeyboardHook) + { + s_llKeyboardHook = SetWindowsHookExW(WH_KEYBOARD_LL, LowLevelKeyboardProc, NULL, NULL); + if (!s_llKeyboardHook) + { + DWORD errorCode = GetLastError(); + show_last_error_message(L"SetWindowsHookEx", errorCode, L"CmdPalKeyboardService"); + } + } + } + } + + void KeyboardListener::Stop() + { + if (s_llKeyboardHook && UnhookWindowsHookEx(s_llKeyboardHook)) + { + s_llKeyboardHook = NULL; + } + } + + void KeyboardListener::SetHotkeyAction(bool win, bool ctrl, bool shift, bool alt, uint8_t key, hstring const& id) + { + Hotkey hotkey = { .win = win, .ctrl = ctrl, .shift = shift, .alt = alt, .key = key }; + std::unique_lock lock{ mutex }; + + HotkeyDescriptor desc = { .hotkey = hotkey, .id = std::wstring(id) }; + hotkeyDescriptors.insert(desc); + } + + void KeyboardListener::ClearHotkey(hstring const& id) + { + { + std::unique_lock lock{ mutex }; + auto it = hotkeyDescriptors.begin(); + while (it != hotkeyDescriptors.end()) + { + if (it->id == id) + { + it = hotkeyDescriptors.erase(it); + } + else + { + ++it; + } + } + } + } + + void KeyboardListener::ClearHotkeys() + { + { + std::unique_lock lock{ mutex }; + auto it = hotkeyDescriptors.begin(); + while (it != hotkeyDescriptors.end()) + { + it = hotkeyDescriptors.erase(it); + } + } + } + + void KeyboardListener::SetProcessCommand(ProcessCommand processCommand) + { + m_processCommandCb = [trigger = std::move(processCommand)](hstring const& id) { + trigger(id); + }; + } + + LRESULT KeyboardListener::DoLowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) + { + const auto& keyPressInfo = *reinterpret_cast(lParam); + + if ((wParam != WM_KEYDOWN) && (wParam != WM_SYSKEYDOWN)) + { + return CallNextHookEx(NULL, nCode, wParam, lParam); + } + + Hotkey hotkey{ + .win = (GetAsyncKeyState(VK_LWIN) & 0x8000) || (GetAsyncKeyState(VK_RWIN) & 0x8000), + .ctrl = static_cast(GetAsyncKeyState(VK_CONTROL) & 0x8000), + .shift = static_cast(GetAsyncKeyState(VK_SHIFT) & 0x8000), + .alt = static_cast(GetAsyncKeyState(VK_MENU) & 0x8000), + .key = static_cast(keyPressInfo.vkCode) + }; + + if (hotkey == Hotkey{}) + { + return CallNextHookEx(NULL, nCode, wParam, lParam); + } + + bool do_action = false; + std::wstring actionId{}; + + { + // Hold the lock for the shortest possible duration + std::unique_lock lock{ mutex }; + HotkeyDescriptor dummy{ .hotkey = hotkey }; + auto it = hotkeyDescriptors.find(dummy); + if (it != hotkeyDescriptors.end()) + { + do_action = true; + actionId = it->id; + } + } + + if (do_action) + { + m_processCommandCb(hstring{ actionId }); + + // After invoking the hotkey send a dummy key to prevent Start Menu from activating + INPUT dummyEvent[1] = {}; + dummyEvent[0].type = INPUT_KEYBOARD; + dummyEvent[0].ki.wVk = 0xFF; + dummyEvent[0].ki.dwFlags = KEYEVENTF_KEYUP; + SendInput(1, dummyEvent, sizeof(INPUT)); + + // Swallow the key press + return 1; + } + + return CallNextHookEx(NULL, nCode, wParam, lParam); + } + + LRESULT KeyboardListener::LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) + { + if (s_instance == nullptr) + { + return CallNextHookEx(NULL, nCode, wParam, lParam); + } + + if (nCode < 0) + { + return CallNextHookEx(NULL, nCode, wParam, lParam); + } + + return s_instance->DoLowLevelKeyboardProc(nCode, wParam, lParam); + } +} diff --git a/src/modules/Deux/UI/CmdPalKeyboardService/KeyboardListener.h b/src/modules/Deux/UI/CmdPalKeyboardService/KeyboardListener.h new file mode 100644 index 0000000000..7cd82ba77c --- /dev/null +++ b/src/modules/Deux/UI/CmdPalKeyboardService/KeyboardListener.h @@ -0,0 +1,67 @@ +#pragma once + +#include "KeyboardListener.g.h" +#include +#include +#include + +namespace winrt::CmdPalKeyboardService::implementation +{ + struct KeyboardListener : KeyboardListenerT + { + struct Hotkey + { + bool win = false; + bool ctrl = false; + bool shift = false; + bool alt = false; + unsigned char key = 0; + + std::strong_ordering operator<=>(const Hotkey&) const = default; + }; + + struct HotkeyDescriptor + { + Hotkey hotkey; + std::wstring id; + + bool operator<(const HotkeyDescriptor& other) const + { + return hotkey < other.hotkey; + }; + }; + + KeyboardListener(); + + void Start(); + void Stop(); + void SetHotkeyAction(bool win, bool ctrl, bool shift, bool alt, uint8_t key, hstring const& id); + void ClearHotkey(hstring const& id); + void ClearHotkeys(); + void SetProcessCommand(ProcessCommand processCommand); + + static LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam); + + private: + LRESULT CALLBACK DoLowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam); + + static inline KeyboardListener* s_instance; + HHOOK s_llKeyboardHook = nullptr; + + // Max DWORD for key code to disable keys. + const DWORD VK_DISABLED = 0x100; + DWORD vkCodePressed = VK_DISABLED; + + std::multiset hotkeyDescriptors; + std::mutex mutex; + + std::function m_processCommandCb; + }; +} + +namespace winrt::CmdPalKeyboardService::factory_implementation +{ + struct KeyboardListener : KeyboardListenerT + { + }; +} diff --git a/src/modules/Deux/UI/CmdPalKeyboardService/KeyboardListener.idl b/src/modules/Deux/UI/CmdPalKeyboardService/KeyboardListener.idl new file mode 100644 index 0000000000..8d808e386c --- /dev/null +++ b/src/modules/Deux/UI/CmdPalKeyboardService/KeyboardListener.idl @@ -0,0 +1,16 @@ + +namespace CmdPalKeyboardService +{ + [version(1.0), uuid(78ab07cd-e128-4e73-86aa-e48e6b6d01ff)] delegate void ProcessCommand(String id); + + [default_interface] runtimeclass KeyboardListener { + KeyboardListener(); + void Start(); + void Stop(); + + void SetHotkeyAction(Boolean win, Boolean ctrl, Boolean shift, Boolean alt, UInt8 key, String id); + void ClearHotkey(String id); + void ClearHotkeys(); + void SetProcessCommand(ProcessCommand processCommand); + } +} diff --git a/src/modules/Deux/UI/CmdPalKeyboardService/KeyboardListener_h.h b/src/modules/Deux/UI/CmdPalKeyboardService/KeyboardListener_h.h new file mode 100644 index 0000000000..1e5358ec08 --- /dev/null +++ b/src/modules/Deux/UI/CmdPalKeyboardService/KeyboardListener_h.h @@ -0,0 +1,453 @@ +/* Header file automatically generated from KeyboardListener.idl */ +/* + * File built with Microsoft(R) MIDLRT Compiler Engine Version 10.00.0231 + */ + +#pragma warning( disable: 4049 ) /* more than 64k source lines */ + +/* verify that the version is high enough to compile this file*/ +#ifndef __REQUIRED_RPCNDR_H_VERSION__ +#define __REQUIRED_RPCNDR_H_VERSION__ 500 +#endif + +/* verify that the version is high enough to compile this file*/ +#ifndef __REQUIRED_RPCSAL_H_VERSION__ +#define __REQUIRED_RPCSAL_H_VERSION__ 100 +#endif + +#include +#include + +#ifndef __RPCNDR_H_VERSION__ +#error this stub requires an updated version of +#endif /* __RPCNDR_H_VERSION__ */ + +#ifndef COM_NO_WINDOWS_H +#include +#include +#endif /*COM_NO_WINDOWS_H*/ +#ifndef __KeyboardListener_h_h__ +#define __KeyboardListener_h_h__ +#ifndef __KeyboardListener_h_p_h__ +#define __KeyboardListener_h_p_h__ + + +#pragma once + +// Ensure that the setting of the /ns_prefix command line switch is consistent for all headers. +// If you get an error from the compiler indicating "warning C4005: 'CHECK_NS_PREFIX_STATE': macro redefinition", this +// indicates that you have included two different headers with different settings for the /ns_prefix MIDL command line switch +#if !defined(DISABLE_NS_PREFIX_CHECKS) +#define CHECK_NS_PREFIX_STATE "always" +#endif // !defined(DISABLE_NS_PREFIX_CHECKS) + + +#pragma push_macro("MIDL_CONST_ID") +#undef MIDL_CONST_ID +#define MIDL_CONST_ID const __declspec(selectany) + + +// Header files for imported files +#include "winrtbase.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.AI.MachineLearning.MachineLearningContract\5.0.0.0\Windows.AI.MachineLearning.MachineLearningContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.AI.MachineLearning.Preview.MachineLearningPreviewContract\2.0.0.0\Windows.AI.MachineLearning.Preview.MachineLearningPreviewContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.ApplicationModel.Calls.Background.CallsBackgroundContract\4.0.0.0\Windows.ApplicationModel.Calls.Background.CallsBackgroundContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.ApplicationModel.Calls.CallsPhoneContract\7.0.0.0\Windows.ApplicationModel.Calls.CallsPhoneContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.ApplicationModel.Calls.CallsVoipContract\5.0.0.0\Windows.ApplicationModel.Calls.CallsVoipContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.ApplicationModel.CommunicationBlocking.CommunicationBlockingContract\2.0.0.0\Windows.ApplicationModel.CommunicationBlocking.CommunicationBlockingContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.ApplicationModel.SocialInfo.SocialInfoContract\2.0.0.0\Windows.ApplicationModel.SocialInfo.SocialInfoContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.ApplicationModel.StartupTaskContract\3.0.0.0\Windows.ApplicationModel.StartupTaskContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.Devices.Custom.CustomDeviceContract\1.0.0.0\Windows.Devices.Custom.CustomDeviceContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.Devices.DevicesLowLevelContract\3.0.0.0\Windows.Devices.DevicesLowLevelContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.Devices.Printers.PrintersContract\1.0.0.0\Windows.Devices.Printers.PrintersContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.Devices.SmartCards.SmartCardBackgroundTriggerContract\3.0.0.0\Windows.Devices.SmartCards.SmartCardBackgroundTriggerContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.Devices.SmartCards.SmartCardEmulatorContract\6.0.0.0\Windows.Devices.SmartCards.SmartCardEmulatorContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.Foundation.FoundationContract\4.0.0.0\Windows.Foundation.FoundationContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.Foundation.UniversalApiContract\19.0.0.0\Windows.Foundation.UniversalApiContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.Gaming.XboxLive.StorageApiContract\1.0.0.0\Windows.Gaming.XboxLive.StorageApiContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.Graphics.Printing3D.Printing3DContract\4.0.0.0\Windows.Graphics.Printing3D.Printing3DContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.Networking.Connectivity.WwanContract\3.0.0.0\Windows.Networking.Connectivity.WwanContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.Networking.Sockets.ControlChannelTriggerContract\3.0.0.0\Windows.Networking.Sockets.ControlChannelTriggerContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.Security.Isolation.IsolatedWindowsEnvironmentContract\5.0.0.0\Windows.Security.Isolation.Isolatedwindowsenvironmentcontract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.Services.Maps.GuidanceContract\3.0.0.0\Windows.Services.Maps.GuidanceContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.Services.Maps.LocalSearchContract\4.0.0.0\Windows.Services.Maps.LocalSearchContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.Services.Store.StoreContract\4.0.0.0\Windows.Services.Store.StoreContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.Services.TargetedContent.TargetedContentContract\1.0.0.0\Windows.Services.TargetedContent.TargetedContentContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.Storage.Provider.CloudFilesContract\7.0.0.0\Windows.Storage.Provider.CloudFilesContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.System.Profile.ProfileHardwareTokenContract\1.0.0.0\Windows.System.Profile.ProfileHardwareTokenContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.System.Profile.ProfileRetailInfoContract\1.0.0.0\Windows.System.Profile.ProfileRetailInfoContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.System.Profile.ProfileSharedModeContract\2.0.0.0\Windows.System.Profile.ProfileSharedModeContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.System.Profile.SystemManufacturers.SystemManufacturersContract\3.0.0.0\Windows.System.Profile.SystemManufacturers.SystemManufacturersContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.System.SystemManagementContract\7.0.0.0\Windows.System.SystemManagementContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.UI.UIAutomation.UIAutomationContract\2.0.0.0\Windows.UI.UIAutomation.UIAutomationContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.UI.ViewManagement.ViewManagementViewScalingContract\1.0.0.0\Windows.UI.ViewManagement.ViewManagementViewScalingContract.h" +#include "C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.UI.Xaml.Core.Direct.XamlDirectContract\5.0.0.0\Windows.UI.Xaml.Core.Direct.XamlDirectContract.h" + +#if defined(__cplusplus) && !defined(CINTERFACE) +#if defined(__MIDL_USE_C_ENUM) +#define MIDL_ENUM enum +#else +#define MIDL_ENUM enum class +#endif +/* Forward Declarations */ +#ifndef ____x_ABI_CCmdPalKeyboardService_CIProcessCommand_FWD_DEFINED__ +#define ____x_ABI_CCmdPalKeyboardService_CIProcessCommand_FWD_DEFINED__ +namespace ABI { + namespace CmdPalKeyboardService { + interface IProcessCommand; + } /* CmdPalKeyboardService */ +} /* ABI */ +#define __x_ABI_CCmdPalKeyboardService_CIProcessCommand ABI::CmdPalKeyboardService::IProcessCommand + +#endif // ____x_ABI_CCmdPalKeyboardService_CIProcessCommand_FWD_DEFINED__ + +#ifndef ____x_ABI_CCmdPalKeyboardService_CIKeyboardListener_FWD_DEFINED__ +#define ____x_ABI_CCmdPalKeyboardService_CIKeyboardListener_FWD_DEFINED__ +namespace ABI { + namespace CmdPalKeyboardService { + interface IKeyboardListener; + } /* CmdPalKeyboardService */ +} /* ABI */ +#define __x_ABI_CCmdPalKeyboardService_CIKeyboardListener ABI::CmdPalKeyboardService::IKeyboardListener + +#endif // ____x_ABI_CCmdPalKeyboardService_CIKeyboardListener_FWD_DEFINED__ + + + +/* + * + * Delegate CmdPalKeyboardService.ProcessCommand + * + */ +#if !defined(____x_ABI_CCmdPalKeyboardService_CIProcessCommand_INTERFACE_DEFINED__) +#define ____x_ABI_CCmdPalKeyboardService_CIProcessCommand_INTERFACE_DEFINED__ +namespace ABI { + namespace CmdPalKeyboardService { + /* [object, uuid("78ab07cd-e128-4e73-86aa-e48e6b6d01ff"), version] */ + MIDL_INTERFACE("78ab07cd-e128-4e73-86aa-e48e6b6d01ff") + IProcessCommand : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */HSTRING id + ) = 0; + + }; + + MIDL_CONST_ID IID & IID_IProcessCommand=__uuidof(IProcessCommand); + + } /* CmdPalKeyboardService */ +} /* ABI */ + +EXTERN_C const IID IID___x_ABI_CCmdPalKeyboardService_CIProcessCommand; +#endif /* !defined(____x_ABI_CCmdPalKeyboardService_CIProcessCommand_INTERFACE_DEFINED__) */ + +namespace ABI { + namespace CmdPalKeyboardService { + class KeyboardListener; + } /* CmdPalKeyboardService */ +} /* ABI */ + + + +/* + * + * Interface CmdPalKeyboardService.IKeyboardListener + * + * Interface is a part of the implementation of type CmdPalKeyboardService.KeyboardListener + * + * + * The IID for this interface was automatically generated by MIDLRT. + * + * Interface IID generation seed: CmdPalKeyboardService.IKeyboardListener:HRESULT Start();HRESULT Stop();HRESULT SetHotkeyAction(Boolean,Boolean,Boolean,Boolean,UInt8,String);HRESULT ClearHotkey(String);HRESULT ClearHotkeys();HRESULT SetProcessCommand(CmdPalKeyboardService.ProcessCommand*); + * + * + */ +#if !defined(____x_ABI_CCmdPalKeyboardService_CIKeyboardListener_INTERFACE_DEFINED__) +#define ____x_ABI_CCmdPalKeyboardService_CIKeyboardListener_INTERFACE_DEFINED__ +extern const __declspec(selectany) _Null_terminated_ WCHAR InterfaceName_CmdPalKeyboardService_IKeyboardListener[] = L"CmdPalKeyboardService.IKeyboardListener"; +namespace ABI { + namespace CmdPalKeyboardService { + /* [uuid("2ae4bb1c-96bd-5c41-a41b-f25b9523efe9"), version, object, exclusiveto] */ + MIDL_INTERFACE("2ae4bb1c-96bd-5c41-a41b-f25b9523efe9") + IKeyboardListener : public IInspectable + { + public: + virtual HRESULT STDMETHODCALLTYPE Start(void) = 0; + virtual HRESULT STDMETHODCALLTYPE Stop(void) = 0; + virtual HRESULT STDMETHODCALLTYPE SetHotkeyAction( + /* [in] */::boolean win, + /* [in] */::boolean ctrl, + /* [in] */::boolean shift, + /* [in] */::boolean alt, + /* [in] */::byte key, + /* [in] */HSTRING id + ) = 0; + virtual HRESULT STDMETHODCALLTYPE ClearHotkey( + /* [in] */HSTRING id + ) = 0; + virtual HRESULT STDMETHODCALLTYPE ClearHotkeys(void) = 0; + virtual HRESULT STDMETHODCALLTYPE SetProcessCommand( + /* [in] */ABI::CmdPalKeyboardService::IProcessCommand * processCommand + ) = 0; + + }; + + MIDL_CONST_ID IID & IID_IKeyboardListener=__uuidof(IKeyboardListener); + + } /* CmdPalKeyboardService */ +} /* ABI */ + +EXTERN_C const IID IID___x_ABI_CCmdPalKeyboardService_CIKeyboardListener; +#endif /* !defined(____x_ABI_CCmdPalKeyboardService_CIKeyboardListener_INTERFACE_DEFINED__) */ + + +/* + * + * Class CmdPalKeyboardService.KeyboardListener + * + * RuntimeClass can be activated. + * + * Class implements the following interfaces: + * CmdPalKeyboardService.IKeyboardListener ** Default Interface ** + * + * Class Threading Model: Both Single and Multi Threaded Apartment + * + * Class Marshaling Behavior: Agile - Class is agile + * + */ + +#ifndef RUNTIMECLASS_CmdPalKeyboardService_KeyboardListener_DEFINED +#define RUNTIMECLASS_CmdPalKeyboardService_KeyboardListener_DEFINED +extern const __declspec(selectany) _Null_terminated_ WCHAR RuntimeClass_CmdPalKeyboardService_KeyboardListener[] = L"CmdPalKeyboardService.KeyboardListener"; +#endif + + +#else // !defined(__cplusplus) +/* Forward Declarations */ +#ifndef ____x_ABI_CCmdPalKeyboardService_CIProcessCommand_FWD_DEFINED__ +#define ____x_ABI_CCmdPalKeyboardService_CIProcessCommand_FWD_DEFINED__ +typedef interface __x_ABI_CCmdPalKeyboardService_CIProcessCommand __x_ABI_CCmdPalKeyboardService_CIProcessCommand; + +#endif // ____x_ABI_CCmdPalKeyboardService_CIProcessCommand_FWD_DEFINED__ + +#ifndef ____x_ABI_CCmdPalKeyboardService_CIKeyboardListener_FWD_DEFINED__ +#define ____x_ABI_CCmdPalKeyboardService_CIKeyboardListener_FWD_DEFINED__ +typedef interface __x_ABI_CCmdPalKeyboardService_CIKeyboardListener __x_ABI_CCmdPalKeyboardService_CIKeyboardListener; + +#endif // ____x_ABI_CCmdPalKeyboardService_CIKeyboardListener_FWD_DEFINED__ + + +/* + * + * Delegate CmdPalKeyboardService.ProcessCommand + * + */ +#if !defined(____x_ABI_CCmdPalKeyboardService_CIProcessCommand_INTERFACE_DEFINED__) +#define ____x_ABI_CCmdPalKeyboardService_CIProcessCommand_INTERFACE_DEFINED__ +/* [object, uuid("78ab07cd-e128-4e73-86aa-e48e6b6d01ff"), version] */ +typedef struct __x_ABI_CCmdPalKeyboardService_CIProcessCommandVtbl +{ + BEGIN_INTERFACE + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + __RPC__in __x_ABI_CCmdPalKeyboardService_CIProcessCommand * This, + /* [in] */ __RPC__in REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + +ULONG ( STDMETHODCALLTYPE *AddRef )( + __RPC__in __x_ABI_CCmdPalKeyboardService_CIProcessCommand * This); + +ULONG ( STDMETHODCALLTYPE *Release )( + __RPC__in __x_ABI_CCmdPalKeyboardService_CIProcessCommand * This); +HRESULT ( STDMETHODCALLTYPE *Invoke )( + __x_ABI_CCmdPalKeyboardService_CIProcessCommand * This, + /* [in] */HSTRING id + ); + END_INTERFACE + +} __x_ABI_CCmdPalKeyboardService_CIProcessCommandVtbl; + +interface __x_ABI_CCmdPalKeyboardService_CIProcessCommand +{ + CONST_VTBL struct __x_ABI_CCmdPalKeyboardService_CIProcessCommandVtbl *lpVtbl; +}; + +#ifdef COBJMACROS +#define __x_ABI_CCmdPalKeyboardService_CIProcessCommand_QueryInterface(This,riid,ppvObject) \ +( (This)->lpVtbl->QueryInterface(This,riid,ppvObject) ) + +#define __x_ABI_CCmdPalKeyboardService_CIProcessCommand_AddRef(This) \ + ( (This)->lpVtbl->AddRef(This) ) + +#define __x_ABI_CCmdPalKeyboardService_CIProcessCommand_Release(This) \ + ( (This)->lpVtbl->Release(This) ) + +#define __x_ABI_CCmdPalKeyboardService_CIProcessCommand_Invoke(This,id) \ + ( (This)->lpVtbl->Invoke(This,id) ) + + +#endif /* COBJMACROS */ + + +EXTERN_C const IID IID___x_ABI_CCmdPalKeyboardService_CIProcessCommand; +#endif /* !defined(____x_ABI_CCmdPalKeyboardService_CIProcessCommand_INTERFACE_DEFINED__) */ + + + +/* + * + * Interface CmdPalKeyboardService.IKeyboardListener + * + * Interface is a part of the implementation of type CmdPalKeyboardService.KeyboardListener + * + * + * The IID for this interface was automatically generated by MIDLRT. + * + * Interface IID generation seed: CmdPalKeyboardService.IKeyboardListener:HRESULT Start();HRESULT Stop();HRESULT SetHotkeyAction(Boolean,Boolean,Boolean,Boolean,UInt8,String);HRESULT ClearHotkey(String);HRESULT ClearHotkeys();HRESULT SetProcessCommand(CmdPalKeyboardService.ProcessCommand*); + * + * + */ +#if !defined(____x_ABI_CCmdPalKeyboardService_CIKeyboardListener_INTERFACE_DEFINED__) +#define ____x_ABI_CCmdPalKeyboardService_CIKeyboardListener_INTERFACE_DEFINED__ +extern const __declspec(selectany) _Null_terminated_ WCHAR InterfaceName_CmdPalKeyboardService_IKeyboardListener[] = L"CmdPalKeyboardService.IKeyboardListener"; +/* [uuid("2ae4bb1c-96bd-5c41-a41b-f25b9523efe9"), version, object, exclusiveto] */ +typedef struct __x_ABI_CCmdPalKeyboardService_CIKeyboardListenerVtbl +{ + BEGIN_INTERFACE + HRESULT ( STDMETHODCALLTYPE *QueryInterface)( + __RPC__in __x_ABI_CCmdPalKeyboardService_CIKeyboardListener * This, + /* [in] */ __RPC__in REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject + ); + +ULONG ( STDMETHODCALLTYPE *AddRef )( + __RPC__in __x_ABI_CCmdPalKeyboardService_CIKeyboardListener * This + ); + +ULONG ( STDMETHODCALLTYPE *Release )( + __RPC__in __x_ABI_CCmdPalKeyboardService_CIKeyboardListener * This + ); + +HRESULT ( STDMETHODCALLTYPE *GetIids )( + __RPC__in __x_ABI_CCmdPalKeyboardService_CIKeyboardListener * This, + /* [out] */ __RPC__out ULONG *iidCount, + /* [size_is][size_is][out] */ __RPC__deref_out_ecount_full_opt(*iidCount) IID **iids + ); + +HRESULT ( STDMETHODCALLTYPE *GetRuntimeClassName )( + __RPC__in __x_ABI_CCmdPalKeyboardService_CIKeyboardListener * This, + /* [out] */ __RPC__deref_out_opt HSTRING *className + ); + +HRESULT ( STDMETHODCALLTYPE *GetTrustLevel )( + __RPC__in __x_ABI_CCmdPalKeyboardService_CIKeyboardListener * This, + /* [OUT ] */ __RPC__out TrustLevel *trustLevel + ); +HRESULT ( STDMETHODCALLTYPE *Start )( + __x_ABI_CCmdPalKeyboardService_CIKeyboardListener * This + ); + HRESULT ( STDMETHODCALLTYPE *Stop )( + __x_ABI_CCmdPalKeyboardService_CIKeyboardListener * This + ); + HRESULT ( STDMETHODCALLTYPE *SetHotkeyAction )( + __x_ABI_CCmdPalKeyboardService_CIKeyboardListener * This, + /* [in] */boolean win, + /* [in] */boolean ctrl, + /* [in] */boolean shift, + /* [in] */boolean alt, + /* [in] */byte key, + /* [in] */HSTRING id + ); + HRESULT ( STDMETHODCALLTYPE *ClearHotkey )( + __x_ABI_CCmdPalKeyboardService_CIKeyboardListener * This, + /* [in] */HSTRING id + ); + HRESULT ( STDMETHODCALLTYPE *ClearHotkeys )( + __x_ABI_CCmdPalKeyboardService_CIKeyboardListener * This + ); + HRESULT ( STDMETHODCALLTYPE *SetProcessCommand )( + __x_ABI_CCmdPalKeyboardService_CIKeyboardListener * This, + /* [in] */__x_ABI_CCmdPalKeyboardService_CIProcessCommand * processCommand + ); + END_INTERFACE + +} __x_ABI_CCmdPalKeyboardService_CIKeyboardListenerVtbl; + +interface __x_ABI_CCmdPalKeyboardService_CIKeyboardListener +{ + CONST_VTBL struct __x_ABI_CCmdPalKeyboardService_CIKeyboardListenerVtbl *lpVtbl; +}; + +#ifdef COBJMACROS +#define __x_ABI_CCmdPalKeyboardService_CIKeyboardListener_QueryInterface(This,riid,ppvObject) \ +( (This)->lpVtbl->QueryInterface(This,riid,ppvObject) ) + +#define __x_ABI_CCmdPalKeyboardService_CIKeyboardListener_AddRef(This) \ + ( (This)->lpVtbl->AddRef(This) ) + +#define __x_ABI_CCmdPalKeyboardService_CIKeyboardListener_Release(This) \ + ( (This)->lpVtbl->Release(This) ) + +#define __x_ABI_CCmdPalKeyboardService_CIKeyboardListener_GetIids(This,iidCount,iids) \ + ( (This)->lpVtbl->GetIids(This,iidCount,iids) ) + +#define __x_ABI_CCmdPalKeyboardService_CIKeyboardListener_GetRuntimeClassName(This,className) \ + ( (This)->lpVtbl->GetRuntimeClassName(This,className) ) + +#define __x_ABI_CCmdPalKeyboardService_CIKeyboardListener_GetTrustLevel(This,trustLevel) \ + ( (This)->lpVtbl->GetTrustLevel(This,trustLevel) ) + +#define __x_ABI_CCmdPalKeyboardService_CIKeyboardListener_Start(This) \ + ( (This)->lpVtbl->Start(This) ) + +#define __x_ABI_CCmdPalKeyboardService_CIKeyboardListener_Stop(This) \ + ( (This)->lpVtbl->Stop(This) ) + +#define __x_ABI_CCmdPalKeyboardService_CIKeyboardListener_SetHotkeyAction(This,win,ctrl,shift,alt,key,id) \ + ( (This)->lpVtbl->SetHotkeyAction(This,win,ctrl,shift,alt,key,id) ) + +#define __x_ABI_CCmdPalKeyboardService_CIKeyboardListener_ClearHotkey(This,id) \ + ( (This)->lpVtbl->ClearHotkey(This,id) ) + +#define __x_ABI_CCmdPalKeyboardService_CIKeyboardListener_ClearHotkeys(This) \ + ( (This)->lpVtbl->ClearHotkeys(This) ) + +#define __x_ABI_CCmdPalKeyboardService_CIKeyboardListener_SetProcessCommand(This,processCommand) \ + ( (This)->lpVtbl->SetProcessCommand(This,processCommand) ) + + +#endif /* COBJMACROS */ + + +EXTERN_C const IID IID___x_ABI_CCmdPalKeyboardService_CIKeyboardListener; +#endif /* !defined(____x_ABI_CCmdPalKeyboardService_CIKeyboardListener_INTERFACE_DEFINED__) */ + + +/* + * + * Class CmdPalKeyboardService.KeyboardListener + * + * RuntimeClass can be activated. + * + * Class implements the following interfaces: + * CmdPalKeyboardService.IKeyboardListener ** Default Interface ** + * + * Class Threading Model: Both Single and Multi Threaded Apartment + * + * Class Marshaling Behavior: Agile - Class is agile + * + */ + +#ifndef RUNTIMECLASS_CmdPalKeyboardService_KeyboardListener_DEFINED +#define RUNTIMECLASS_CmdPalKeyboardService_KeyboardListener_DEFINED +extern const __declspec(selectany) _Null_terminated_ WCHAR RuntimeClass_CmdPalKeyboardService_KeyboardListener[] = L"CmdPalKeyboardService.KeyboardListener"; +#endif + + +#endif // defined(__cplusplus) +#pragma pop_macro("MIDL_CONST_ID") +#endif // __KeyboardListener_h_p_h__ + +#endif // __KeyboardListener_h_h__ diff --git a/src/modules/Deux/UI/CmdPalKeyboardService/PropertySheet.props b/src/modules/Deux/UI/CmdPalKeyboardService/PropertySheet.props new file mode 100644 index 0000000000..27ad40e537 --- /dev/null +++ b/src/modules/Deux/UI/CmdPalKeyboardService/PropertySheet.props @@ -0,0 +1,17 @@ + + + + + + + + \ No newline at end of file diff --git a/src/modules/Deux/UI/CmdPalKeyboardService/packages.config b/src/modules/Deux/UI/CmdPalKeyboardService/packages.config new file mode 100644 index 0000000000..09bfc449e2 --- /dev/null +++ b/src/modules/Deux/UI/CmdPalKeyboardService/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/modules/Deux/UI/CmdPalKeyboardService/pch.cpp b/src/modules/Deux/UI/CmdPalKeyboardService/pch.cpp new file mode 100644 index 0000000000..bcb5590be1 --- /dev/null +++ b/src/modules/Deux/UI/CmdPalKeyboardService/pch.cpp @@ -0,0 +1 @@ +#include "pch.h" diff --git a/src/modules/Deux/UI/CmdPalKeyboardService/pch.h b/src/modules/Deux/UI/CmdPalKeyboardService/pch.h new file mode 100644 index 0000000000..b10d0155ca --- /dev/null +++ b/src/modules/Deux/UI/CmdPalKeyboardService/pch.h @@ -0,0 +1,4 @@ +#pragma once +#include +#include +#include diff --git a/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Models/CommandHotKey.cs b/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Models/CommandHotKey.cs new file mode 100644 index 0000000000..3f7854554d --- /dev/null +++ b/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Models/CommandHotKey.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CommandPalette.UI.Models; + +public class CommandHotKey(Hotkey? hotkey, string commandId) +{ + public string CommandId { get; set; } = commandId; + + public Hotkey? Hotkey { get; set; } = hotkey; +} diff --git a/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Models/HotkeySettings.cs b/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Models/Hotkey.cs similarity index 96% rename from src/modules/Deux/UI/Microsoft.CommandPalette.UI.Models/HotkeySettings.cs rename to src/modules/Deux/UI/Microsoft.CommandPalette.UI.Models/Hotkey.cs index 6b0085340b..7bb5312fdb 100644 --- a/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Models/HotkeySettings.cs +++ b/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Models/Hotkey.cs @@ -9,11 +9,11 @@ using Microsoft.CommandPalette.UI.Models.Helpers; namespace Microsoft.CommandPalette.UI.Models; -public record HotkeySettings +public record Hotkey { private const int VKTAB = 0x09; - public HotkeySettings() + public Hotkey() { Win = false; Ctrl = false; @@ -23,14 +23,14 @@ public record HotkeySettings } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Should Windows key be used /// Should Ctrl key be used /// Should Alt key be used /// Should Shift key be used /// Go to https://learn.microsoft.com/windows/win32/inputdev/virtual-key-codes to see list of v-keys - public HotkeySettings(bool win, bool ctrl, bool alt, bool shift, int code) + public Hotkey(bool win, bool ctrl, bool alt, bool shift, int code) { Win = win; Ctrl = ctrl; diff --git a/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Models/KeyPressedEventArgs.cs b/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Models/KeyPressedEventArgs.cs new file mode 100644 index 0000000000..f41dcab5fd --- /dev/null +++ b/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Models/KeyPressedEventArgs.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Windows.System; + +namespace Microsoft.CommandPalette.UI.Models; + +public class KeyPressedEventArgs(VirtualKey key) : EventArgs +{ + public VirtualKey Key { get; } = key; +} diff --git a/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Models/SettingsModel.cs b/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Models/SettingsModel.cs index 70ec6c21d2..c7c13b3216 100644 --- a/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Models/SettingsModel.cs +++ b/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Models/SettingsModel.cs @@ -11,9 +11,9 @@ public partial class SettingsModel * Make sure that any new types you add are added to JsonSerializationContext! *************************************************************************/ - public static HotkeySettings DefaultActivationShortcut { get; } = new HotkeySettings(true, false, true, false, 0x20); // win+alt+space + public static Hotkey DefaultActivationShortcut { get; } = new Hotkey(true, false, true, false, 0x20); // win+alt+space - public HotkeySettings? Hotkey { get; set; } = DefaultActivationShortcut; + public Hotkey? Hotkey { get; set; } = DefaultActivationShortcut; public bool UseLowLevelGlobalHotkey { get; set; } @@ -40,4 +40,6 @@ public partial class SettingsModel public WindowPosition? LastWindowPosition { get; set; } public TimeSpan AutoGoHomeInterval { get; set; } = Timeout.InfiniteTimeSpan; + + public List CommandHotkeys { get; set; } = []; } diff --git a/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Services/Extensions/IExtensionService.cs b/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Services/Extensions/IExtensionService.cs index e61a4f2737..023cb82c9b 100644 --- a/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Services/Extensions/IExtensionService.cs +++ b/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Services/Extensions/IExtensionService.cs @@ -6,5 +6,4 @@ namespace Microsoft.CommandPalette.UI.Services.Extensions; public interface IExtensionService { - CommandViewModel } diff --git a/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Services/KeyboardService.cs b/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Services/KeyboardService.cs new file mode 100644 index 0000000000..a6e38287a5 --- /dev/null +++ b/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Services/KeyboardService.cs @@ -0,0 +1,257 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using CmdPalKeyboardService; +using Microsoft.CommandPalette.UI.Models; +using Microsoft.Extensions.Logging; +using Windows.System; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.UI.Input.KeyboardAndMouse; +using Windows.Win32.UI.WindowsAndMessaging; + +namespace Microsoft.CommandPalette.UI.Services; + +public partial class KeyboardService : IDisposable +{ + private readonly KeyboardListener _keyboardListener; + private readonly SettingsService _settingsService; + private readonly ILogger _logger; + private readonly List _hotkeys = []; + + private HWND _hwnd; + private bool _disposed; + private UnhookWindowsHookExSafeHandle? _handle; + private HOOKPROC? _hookProc; // Keep reference to prevent GC collection + + /// + /// Event that is raised when a key is pressed down. + /// + public event EventHandler? KeyPressed; + + public KeyboardService(SettingsService settingsService, ILogger logger) + { + _logger = logger; + _settingsService = settingsService; + _keyboardListener = new KeyboardListener(); + _keyboardListener.Start(); + + StartListening(); + + _settingsService.SettingsChanged += SettingsChanged; + } + + public void SetProcessCommand(HWND hwnd, ProcessCommand processCommand) + { + _hwnd = hwnd; + _keyboardListener.SetProcessCommand(processCommand); + SetupHotkeys(); + } + + private void SettingsChanged(SettingsModel sender, object? e) + { + SetupHotkeys(); + } + + private void UnregisterHotkeys() + { + _keyboardListener.ClearHotkeys(); + + while (_hotkeys.Count > 0) + { + PInvoke.UnregisterHotKey(_hwnd, _hotkeys.Count - 1); + _hotkeys.RemoveAt(_hotkeys.Count - 1); + } + } + + private void SetupHotkeys() + { + UnregisterHotkeys(); + + var globalHotkey = _settingsService.CurrentSettings.Hotkey; + if (globalHotkey is not null) + { + if (_settingsService.CurrentSettings.UseLowLevelGlobalHotkey) + { + _keyboardListener.SetHotkeyAction(globalHotkey.Win, globalHotkey.Ctrl, globalHotkey.Shift, globalHotkey.Alt, (byte)globalHotkey.Code, string.Empty); + _hotkeys.Add(new(globalHotkey, string.Empty)); + } + else + { + var vk = globalHotkey.Code; + var modifiers = + (globalHotkey.Alt ? HOT_KEY_MODIFIERS.MOD_ALT : 0) | + (globalHotkey.Ctrl ? HOT_KEY_MODIFIERS.MOD_CONTROL : 0) | + (globalHotkey.Shift ? HOT_KEY_MODIFIERS.MOD_SHIFT : 0) | + (globalHotkey.Win ? HOT_KEY_MODIFIERS.MOD_WIN : 0) + ; + + var success = PInvoke.RegisterHotKey(_hwnd, _hotkeys.Count, modifiers, (uint)vk); + if (success) + { + _hotkeys.Add(new(globalHotkey, string.Empty)); + } + } + } + + foreach (var commandHotkey in _settingsService.CurrentSettings.CommandHotkeys) + { + var key = commandHotkey.Hotkey; + if (key is not null) + { + if (_settingsService.CurrentSettings.UseLowLevelGlobalHotkey) + { + _keyboardListener.SetHotkeyAction(key.Win, key.Ctrl, key.Shift, key.Alt, (byte)key.Code, commandHotkey.CommandId); + _hotkeys.Add(new(commandHotkey.Hotkey, commandHotkey.CommandId)); + } + else + { + var vk = key.Code; + var modifiers = + (key.Alt ? HOT_KEY_MODIFIERS.MOD_ALT : 0) | + (key.Ctrl ? HOT_KEY_MODIFIERS.MOD_CONTROL : 0) | + (key.Shift ? HOT_KEY_MODIFIERS.MOD_SHIFT : 0) | + (key.Win ? HOT_KEY_MODIFIERS.MOD_WIN : 0) + ; + var success = PInvoke.RegisterHotKey(_hwnd, _hotkeys.Count, modifiers, (uint)vk); + if (success) + { + _hotkeys.Add(commandHotkey); + } + } + } + } + } + + private bool StartListening() + { + if (_disposed) + { + return false; + } + + try + { + RegisterKeyboardHook(); + return true; + } + catch (Exception ex) + { + Log_FailedToRegisterHook(ex); + return false; + } + } + + private void RegisterKeyboardHook() + { + ObjectDisposedException.ThrowIf(_disposed, this); + + if (_handle is not null && !_handle.IsInvalid) + { + // Hook is already set + return; + } + + _hookProc = KeyEventHook; + if (!SetWindowKeyHook(_hookProc)) + { + throw new InvalidOperationException("Failed to register keyboard hook."); + } + } + + private bool SetWindowKeyHook(HOOKPROC hookProc) + { + if (_handle is not null && !_handle.IsInvalid) + { + // Hook is already set + return false; + } + + _handle = PInvoke.SetWindowsHookEx( + WINDOWS_HOOK_ID.WH_KEYBOARD, + hookProc, + PInvoke.GetModuleHandle(null), + PInvoke.GetCurrentThreadId()); + + // Check if the hook was successfully set + return _handle is not null && !_handle.IsInvalid; + } + + private static bool IsKeyDownHook(LPARAM lParam) + { + // The 30th bit tells what the previous key state is with 0 being the "UP" state + // For more info see https://learn.microsoft.com/windows/win32/winmsg/keyboardproc#lparam-in + return ((lParam.Value >> 30) & 1) == 0; + } + + private LRESULT KeyEventHook(int nCode, WPARAM wParam, LPARAM lParam) + { + try + { + if (nCode >= 0 && IsKeyDownHook(lParam)) + { + InvokeKeyDown((VirtualKey)wParam.Value); + } + } + catch (Exception ex) + { + Log_ErrorInvokingKeyDownHook(ex); + } + + // Call next hook in chain - pass null as first parameter for current hook + return PInvoke.CallNextHookEx(null, nCode, wParam, lParam); + } + + private void InvokeKeyDown(VirtualKey virtualKey) + { + if (!_disposed) + { + KeyPressed?.Invoke(this, new KeyPressedEventArgs(virtualKey)); + } + } + + public void Dispose() + { + UnregisterHotkeys(); + + if (_settingsService is not null) + { + _settingsService.SettingsChanged -= SettingsChanged; + } + + Dispose(true); + GC.SuppressFinalize(this); + } + + private void UnregisterKeyboardHook() + { + if (_handle is not null && !_handle.IsInvalid) + { + // The SafeHandle should automatically call UnhookWindowsHookEx when disposed + _handle.Dispose(); + _handle = null; + } + + _hookProc = null; + } + + private void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + UnregisterKeyboardHook(); + } + + _disposed = true; + } + } + + [LoggerMessage(Level = LogLevel.Error, Message = "Failed to register hook")] + partial void Log_FailedToRegisterHook(Exception ex); + + [LoggerMessage(Level = LogLevel.Error, Message = "Failed when invoking key down keyboard hook event")] + partial void Log_ErrorInvokingKeyDownHook(Exception ex); +} diff --git a/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Services/Microsoft.CommandPalette.UI.Services.csproj b/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Services/Microsoft.CommandPalette.UI.Services.csproj index d357131640..66b856461e 100644 --- a/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Services/Microsoft.CommandPalette.UI.Services.csproj +++ b/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Services/Microsoft.CommandPalette.UI.Services.csproj @@ -26,10 +26,22 @@ + + + Microsoft.Terminal.UI;CmdPalKeyboardService + $(OutDir) + + + + + True + True + True + \ No newline at end of file diff --git a/src/modules/Deux/UI/Microsoft.CommandPalette.UI/App.xaml.cs b/src/modules/Deux/UI/Microsoft.CommandPalette.UI/App.xaml.cs index eccbdbf917..96ca7c9676 100644 --- a/src/modules/Deux/UI/Microsoft.CommandPalette.UI/App.xaml.cs +++ b/src/modules/Deux/UI/Microsoft.CommandPalette.UI/App.xaml.cs @@ -80,6 +80,7 @@ public partial class App : Application services.AddSingleton(); // Register services + services.AddSingleton(); services.AddSingleton(); // Register view models diff --git a/src/modules/Deux/UI/Microsoft.CommandPalette.UI/MainWindow.xaml.cs b/src/modules/Deux/UI/Microsoft.CommandPalette.UI/MainWindow.xaml.cs index 87f62534cd..24b535d0b1 100644 --- a/src/modules/Deux/UI/Microsoft.CommandPalette.UI/MainWindow.xaml.cs +++ b/src/modules/Deux/UI/Microsoft.CommandPalette.UI/MainWindow.xaml.cs @@ -32,7 +32,6 @@ using Windows.Win32.Foundation; using Windows.Win32.Graphics.Dwm; using Windows.Win32.Graphics.Gdi; using Windows.Win32.UI.HiDpi; -using Windows.Win32.UI.Input.KeyboardAndMouse; using Windows.Win32.UI.WindowsAndMessaging; using WinRT; using WinUIEx; @@ -50,6 +49,7 @@ public sealed partial class MainWindow : WindowEx, private readonly ShellPage _shellPage; private readonly SettingsService _settingsService; private readonly TrayIconService _trayIconService; + private readonly KeyboardService _keyboardService; private const int DefaultWidth = 800; private const int DefaultHeight = 480; @@ -61,9 +61,6 @@ public sealed partial class MainWindow : WindowEx, private readonly WNDPROC? _hotkeyWndProc; private readonly WNDPROC? _originalWndProc; - // private readonly List _hotkeys = []; - // private readonly KeyboardListener _keyboardListener; - // private readonly LocalKeyboardListener _localKeyboardListener; private readonly HiddenOwnerWindowBehavior _hiddenOwnerBehavior = new(); private bool _ignoreHotKeyWhenFullScreen = true; @@ -73,7 +70,7 @@ public sealed partial class MainWindow : WindowEx, private WindowPosition _currentWindowPosition = new(); - public MainWindow(ShellPage shellPage, SettingsService settingsService, TrayIconService trayIconService, ILogger logger) + public MainWindow(ShellPage shellPage, SettingsService settingsService, TrayIconService trayIconService, KeyboardService keyboardService, ILogger logger) { InitializeComponent(); @@ -81,6 +78,7 @@ public sealed partial class MainWindow : WindowEx, _shellPage = shellPage; _settingsService = settingsService; _trayIconService = trayIconService; + _keyboardService = keyboardService; RootElement.Children.Add(_shellPage); @@ -95,9 +93,9 @@ public sealed partial class MainWindow : WindowEx, // } _hiddenOwnerBehavior.ShowInTaskbar(this, Debugger.IsAttached); - // _keyboardListener = new KeyboardListener(); - // _keyboardListener.Start(); - // _keyboardListener.SetProcessCommand(new CmdPalKeyboardService.ProcessCommand(HandleSummon)); + _keyboardService.KeyPressed += KeyboardService_OnKeyPressed; + _keyboardService.SetProcessCommand(_hwnd, new CmdPalKeyboardService.ProcessCommand(HandleSummon)); + this.SetIcon(); AppWindow.Title = RS_.GetString("AppName"); RestoreWindowPosition(); @@ -143,13 +141,18 @@ public sealed partial class MainWindow : WindowEx, Summon(string.Empty); }); - // _localKeyboardListener = new LocalKeyboardListener(); - // _localKeyboardListener.KeyPressed += LocalKeyboardListener_OnKeyPressed; - // _localKeyboardListener.Start(); // Force window to be created, and then cloaked. This will offset initial animation when the window is shown. HideWindow(); } + private void KeyboardService_OnKeyPressed(object? sender, KeyPressedEventArgs e) + { + if (e.Key == VirtualKey.GoBack) + { + WeakReferenceMessenger.Default.Send(new GoBackMessage()); + } + } + public void Receive(ShowWindowMessage message) { ShowHwnd(message.Hwnd, _settingsService.CurrentSettings.SummonOn); @@ -187,7 +190,7 @@ public sealed partial class MainWindow : WindowEx, WeakReferenceMessenger.Default.Send(new GoHomeMessage(WithAnimation: false, FocusSearch: false)); } - private static void LocalKeyboardListener_OnKeyPressed(object? sender, LocalKeyboardListenerKeyPressedEventArgs e) + private static void KeyboardService_OnKeyPressed(object? sender, LocalKeyboardListenerKeyPressedEventArgs e) { if (e.Key == VirtualKey.GoBack) { @@ -266,8 +269,6 @@ public sealed partial class MainWindow : WindowEx, private void HotReloadSettings() { - SetupHotkey(_settingsService.CurrentSettings); - _ignoreHotKeyWhenFullScreen = _settingsService.CurrentSettings.IgnoreShortcutWhenFullscreen; _autoGoHomeInterval = _settingsService.CurrentSettings.AutoGoHomeInterval; @@ -634,13 +635,13 @@ public sealed partial class MainWindow : WindowEx, // var extensionService = serviceProvider.GetService()!; // extensionService.SignalStopExtensionsAsync(); - // _trayIconService.Destroy(); + _trayIconService.Destroy(); // WinUI bug is causing a crash on shutdown when FailFastOnErrors is set to true (#51773592). // Workaround by turning it off before shutdown. App.Current.DebugSettings.FailFastOnErrors = false; - // _localKeyboardListener.Dispose(); + _keyboardService.Dispose(); DisposeAcrylic(); // _keyboardListener.Stop(); @@ -817,76 +818,6 @@ public sealed partial class MainWindow : WindowEx, // know till the message is being handled. WeakReferenceMessenger.Default.Send(new(commandId, _hwnd)); - private void UnregisterHotkeys() - { - // _keyboardListener.ClearHotkeys(); - - // while (_hotkeys.Count > 0) - // { - // PInvoke.UnregisterHotKey(_hwnd, _hotkeys.Count - 1); - // _hotkeys.RemoveAt(_hotkeys.Count - 1); - // } - } - - private void SetupHotkey(SettingsModel settings) - { - UnregisterHotkeys(); - - var globalHotkey = settings.Hotkey; - if (globalHotkey is not null) - { - if (settings.UseLowLevelGlobalHotkey) - { - // _keyboardListener.SetHotkeyAction(globalHotkey.Win, globalHotkey.Ctrl, globalHotkey.Shift, globalHotkey.Alt, (byte)globalHotkey.Code, string.Empty); - // _hotkeys.Add(new(globalHotkey, string.Empty)); - } - else - { - var vk = globalHotkey.Code; - var modifiers = - (globalHotkey.Alt ? HOT_KEY_MODIFIERS.MOD_ALT : 0) | - (globalHotkey.Ctrl ? HOT_KEY_MODIFIERS.MOD_CONTROL : 0) | - (globalHotkey.Shift ? HOT_KEY_MODIFIERS.MOD_SHIFT : 0) | - (globalHotkey.Win ? HOT_KEY_MODIFIERS.MOD_WIN : 0) - ; - - // var success = PInvoke.RegisterHotKey(_hwnd, _hotkeys.Count, modifiers, (uint)vk); - // if (success) - // { - // _hotkeys.Add(new(globalHotkey, string.Empty)); - // } - } - } - - // foreach (var commandHotkey in settings.CommandHotkeys) - // { - // var key = commandHotkey.Hotkey; - // if (key is not null) - // { - // if (settings.UseLowLevelGlobalHotkey) - // { - // _keyboardListener.SetHotkeyAction(key.Win, key.Ctrl, key.Shift, key.Alt, (byte)key.Code, commandHotkey.CommandId); - // _hotkeys.Add(new(globalHotkey, string.Empty)); - // } - // else - // { - // var vk = key.Code; - // var modifiers = - // (key.Alt ? HOT_KEY_MODIFIERS.MOD_ALT : 0) | - // (key.Ctrl ? HOT_KEY_MODIFIERS.MOD_CONTROL : 0) | - // (key.Shift ? HOT_KEY_MODIFIERS.MOD_SHIFT : 0) | - // (key.Win ? HOT_KEY_MODIFIERS.MOD_WIN : 0) - // ; - // var success = PInvoke.RegisterHotKey(_hwnd, _hotkeys.Count, modifiers, (uint)vk); - // if (success) - // { - // _hotkeys.Add(commandHotkey); - // } - // } - // } - // } - } - private void HandleSummon(string commandId) { if (_ignoreHotKeyWhenFullScreen) @@ -982,7 +913,7 @@ public sealed partial class MainWindow : WindowEx, public void Dispose() { - // _localKeyboardListener.Dispose(); + _keyboardService.Dispose(); DisposeAcrylic(); } diff --git a/src/modules/Deux/UI/Microsoft.CommandPalette.UI/Microsoft.CommandPalette.UI.csproj b/src/modules/Deux/UI/Microsoft.CommandPalette.UI/Microsoft.CommandPalette.UI.csproj index 9f188f551d..3ac0c347eb 100644 --- a/src/modules/Deux/UI/Microsoft.CommandPalette.UI/Microsoft.CommandPalette.UI.csproj +++ b/src/modules/Deux/UI/Microsoft.CommandPalette.UI/Microsoft.CommandPalette.UI.csproj @@ -161,6 +161,7 @@ + diff --git a/src/modules/Deux/UI/Microsoft.CommandPalette.UI/Pages/ShellPage.xaml.cs b/src/modules/Deux/UI/Microsoft.CommandPalette.UI/Pages/ShellPage.xaml.cs index 25edc14a66..65c5d3b0ab 100644 --- a/src/modules/Deux/UI/Microsoft.CommandPalette.UI/Pages/ShellPage.xaml.cs +++ b/src/modules/Deux/UI/Microsoft.CommandPalette.UI/Pages/ShellPage.xaml.cs @@ -2,24 +2,45 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.ComponentModel; using System.Text; using CommunityToolkit.Mvvm.Messaging; -using Microsoft.CommandPalette.UI.Models; -using Microsoft.CommandPalette.UI.Models.Helpers; +using CommunityToolkit.WinUI; +using ManagedCommon; +using Microsoft.CommandPalette.Extensions; +using Microsoft.CommandPalette.UI.Models.Events; using Microsoft.CommandPalette.UI.Models.Messages; +using Microsoft.CommandPalette.UI.Services; using Microsoft.CommandPalette.UI.ViewModels; using Microsoft.CommandPalette.ViewModels; using Microsoft.Extensions.Logging; +using Microsoft.PowerToys.Telemetry; +using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media.Animation; using Microsoft.UI.Xaml.Navigation; using DispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue; namespace Microsoft.CommandPalette.UI.Pages; -public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page +public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page, + IRecipient, + IRecipient, + IRecipient, + IRecipient, + IRecipient, + IRecipient, + IRecipient, + IRecipient, + IRecipient, + IRecipient, + IRecipient, + IRecipient, + IRecipient, + INotifyPropertyChanged, + IDisposable { private readonly ShellViewModel viewModel; - private readonly SettingsModel _settingsModel; + private readonly SettingsService _settingsService; private readonly ILogger logger; private readonly DispatcherQueue _queue = DispatcherQueue.GetForCurrentThread(); private readonly Microsoft.UI.Dispatching.DispatcherQueueTimer _debounceTimer = DispatcherQueue.GetForCurrentThread().CreateTimer(); @@ -30,13 +51,31 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page private readonly CompositeFormat _pageNavigatedAnnouncement; - public ShellPage(ShellViewModel viewModel, SettingsModel settingsModel, ILogger logger) + public ShellPage(ShellViewModel viewModel, SettingsService settingsService, ILogger logger) { InitializeComponent(); this.viewModel = viewModel; - _settingsModel = settingsModel; + _settingsService = settingsService; this.logger = logger; + // how we are doing navigation around + WeakReferenceMessenger.Default.Register(this); + WeakReferenceMessenger.Default.Register(this); + WeakReferenceMessenger.Default.Register(this); + WeakReferenceMessenger.Default.Register(this); + + WeakReferenceMessenger.Default.Register(this); + WeakReferenceMessenger.Default.Register(this); + + WeakReferenceMessenger.Default.Register(this); + WeakReferenceMessenger.Default.Register(this); + + WeakReferenceMessenger.Default.Register(this); + WeakReferenceMessenger.Default.Register(this); + WeakReferenceMessenger.Default.Register(this); + WeakReferenceMessenger.Default.Register(this); + WeakReferenceMessenger.Default.Register(this); + // AddHandler(PreviewKeyDownEvent, new KeyEventHandler(ShellPage_OnPreviewKeyDown), true); // AddHandler(KeyDownEvent, new KeyEventHandler(ShellPage_OnKeyDown), false); // AddHandler(PointerPressedEvent, new PointerEventHandler(ShellPage_OnPointerPressed), true); @@ -53,10 +92,218 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page { get { - return _settingsModel.DisableAnimations ? _noAnimation : _slideRightTransition; + return _settingsService.CurrentSettings.DisableAnimations ? _noAnimation : _slideRightTransition; } } + public void Receive(NavigateBackMessage message) + { + if (RootFrame.CanGoBack) + { + if (!message.FromBackspace || + _settingsService.CurrentSettings.BackspaceGoesBack) + { + GoBack(); + } + } + else + { + if (!message.FromBackspace) + { + // If we can't go back then we must be at the top and thus escape again should quit. + WeakReferenceMessenger.Default.Send(); + + PowerToysTelemetry.Log.WriteEvent(new DismissedOnEscEvent()); + } + } + } + + public void Receive(NavigateToPageMessage message) + { + // TODO GH #526 This needs more better locking too + _ = _queue.TryEnqueue(() => + { + // Also hide our details pane about here, if we had one + // HideDetails(); + + // Navigate to the appropriate host page for that VM + // RootFrame.Navigate( + // message.Page switch + // { + // ListViewModel => typeof(ListPage), + // ContentPageViewModel => typeof(ContentPage), + // _ => throw new NotSupportedException(), + // }, + // new AsyncNavigationRequest(message.Page, message.CancellationToken), + // message.WithAnimation ? DefaultPageAnimation : _noAnimation); + // PowerToysTelemetry.Log.WriteEvent(new OpenPageEvent(RootFrame.BackStackDepth, message.Page.Id)); + // if (!viewModel.IsNested) + // { + // // todo BODGY + // RootFrame.BackStack.Clear(); + // } + }); + } + + public void Receive(ShowConfirmationMessage message) + { + DispatcherQueue.TryEnqueue(async () => + { + try + { + await HandleConfirmArgsOnUiThread(message.Args); + } + catch (Exception ex) + { + Logger.LogError(ex.ToString()); + } + }); + } + + public void Receive(ShowToastMessage message) + { + DispatcherQueue.TryEnqueue(() => + { + // _toast.ShowToast(message.Message); + }); + } + + // This gets called from the UI thread + private async Task HandleConfirmArgsOnUiThread(IConfirmationArgs? args) + { + if (args is null) + { + return; + } + + ConfirmResultViewModel vm = new(args, new(ViewModel.CurrentPage)); + var initializeDialogTask = Task.Run(() => { InitializeConfirmationDialog(vm); }); + await initializeDialogTask; + + var resourceLoader = Microsoft.CmdPal.UI.Helpers.ResourceLoaderInstance.ResourceLoader; + var confirmText = resourceLoader.GetString("ConfirmationDialog_ConfirmButtonText"); + var cancelText = resourceLoader.GetString("ConfirmationDialog_CancelButtonText"); + + var name = string.IsNullOrEmpty(vm.PrimaryCommand.Name) ? confirmText : vm.PrimaryCommand.Name; + ContentDialog dialog = new() + { + Title = vm.Title, + Content = vm.Description, + PrimaryButtonText = name, + CloseButtonText = cancelText, + XamlRoot = this.XamlRoot, + }; + + if (vm.IsPrimaryCommandCritical) + { + dialog.DefaultButton = ContentDialogButton.Close; + + // TODO: Maybe we need to style the primary button to be red? + // dialog.PrimaryButtonStyle = new Style(typeof(Button)) + // { + // Setters = + // { + // new Setter(Button.ForegroundProperty, new SolidColorBrush(Colors.Red)), + // new Setter(Button.BackgroundProperty, new SolidColorBrush(Colors.Red)), + // }, + // }; + } + + var result = await dialog.ShowAsync(); + if (result == ContentDialogResult.Primary) + { + var performMessage = new PerformCommandMessage(vm); + WeakReferenceMessenger.Default.Send(performMessage); + } + else + { + // cancel + } + } + + public void Receive(OpenSettingsMessage message) + { + _ = DispatcherQueue.TryEnqueue(() => + { + OpenSettings(); + }); + } + + public void OpenSettings() + { + if (_settingsWindow is null) + { + _settingsWindow = new SettingsWindow(); + } + + _settingsWindow.Activate(); + _settingsWindow.BringToFront(); + } + + public void Receive(ShowDetailsMessage message) + { + if (ViewModel is not null && + ViewModel.CurrentPage is not null) + { + if (ViewModel.CurrentPage.PageContext.TryGetTarget(out var pageContext)) + { + Task.Factory.StartNew( + () => + { + // TERRIBLE HACK TODO GH #245 + // There's weird wacky bugs with debounce currently. + if (!ViewModel.IsDetailsVisible) + { + ViewModel.Details = message.Details; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(HasHeroImage))); + ViewModel.IsDetailsVisible = true; + return; + } + + // GH #322: + // For inexplicable reasons, if you try to change the details too fast, + // we'll explode. This seemingly only happens if you change the details + // while we're also scrolling a new list view item into view. + _debounceTimer.Debounce( + () => + { + ViewModel.Details = message.Details; + + // Trigger a re-evaluation of whether we have a hero image based on + // the current theme + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(HasHeroImage))); + }, + interval: TimeSpan.FromMilliseconds(50), + immediate: ViewModel.IsDetailsVisible == false); + ViewModel.IsDetailsVisible = true; + }, + CancellationToken.None, + TaskCreationOptions.None, + pageContext.Scheduler); + } + } + } + + public void Receive(HideDetailsMessage message) => HideDetails(); + + public void Receive(LaunchUriMessage message) => _ = global::Windows.System.Launcher.LaunchUriAsync(message.Uri); + + private void HideDetails() + { + ViewModel.Details = null; + ViewModel.IsDetailsVisible = false; + } + + public void Receive(ClearSearchMessage message) => SearchBox.ClearSearch(); + + public void Receive(HotkeySummonMessage message) + { + _ = DispatcherQueue.TryEnqueue(() => SummonOnUiThread(message)); + } + + public void Receive(SettingsWindowClosedMessage message) => _settingsWindow = null; + + private void SummonOnUiThread(HotkeySummonMessage message) { var commandId = message.CommandId; @@ -69,11 +316,11 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page // Depending on the settings, either // * Go home, or // * Select the search text (if we should remain open on this page) - if (_settingsModel.AutoGoHomeInterval == TimeSpan.Zero) + if (_settingsService.CurrentSettings.AutoGoHomeInterval == TimeSpan.Zero) { - // GoHome(false); + GoHome(false); } - else if (_settingsModel.HighlightSearchOnActivate) + else if (_settingsService.CurrentSettings.HighlightSearchOnActivate) { // SearchBox.SelectSearch(); } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Pages/ShellPage.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/Pages/ShellPage.xaml.cs index dc12cf142b..db16b66900 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Pages/ShellPage.xaml.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Pages/ShellPage.xaml.cs @@ -7,17 +7,11 @@ using System.Globalization; using System.Text; using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.WinUI; -using ManagedCommon; -using Microsoft.CmdPal.Core.ViewModels; -using Microsoft.CmdPal.Core.ViewModels.Messages; using Microsoft.CmdPal.UI.Events; using Microsoft.CmdPal.UI.Helpers; using Microsoft.CmdPal.UI.Messages; using Microsoft.CmdPal.UI.Settings; -using Microsoft.CmdPal.UI.ViewModels; -using Microsoft.CommandPalette.Extensions; using Microsoft.Extensions.DependencyInjection; -using Microsoft.PowerToys.Telemetry; using Microsoft.UI.Dispatching; using Microsoft.UI.Input; using Microsoft.UI.Xaml;