From 97a8aeb11849f9b459124816e89b2c201e13a4c5 Mon Sep 17 00:00:00 2001 From: Dylan Briedis Date: Tue, 7 Nov 2023 07:26:19 -0700 Subject: [PATCH] [KeyboardManager]Modernize the editor UI (#28473) * Fix dark title bar for shortcuts window * Adjust editor sizes * Fetch accent button style from resources instead * Modernize the editor UI Reimplemented the XAML bridge to support Mica * Use fluent icons * Modernize the preview key visuals * Implement teaching tips for key drop-down messages * Fix spelling * Fix delete button alignment in keys editor * Remove trace log from bridge message handler * Add WinUI depends to installer script * Hide icon and caption from editor title bar * Update remap entries to look like cards * Use built-in content dialog buttons * Update add button * Fix spelling * Fix installer script for ARM64 * Fix spelling AGAIN * Update dev documentation * Prevent white flash on dark mode * Revert 3-key layout but make window wider * f: align webview versions * f: add pipeline exceptions for Microsoft DLLs that are not versioned * f: add vcruntime140_1_app.dll to the exception list * f: update webview versions --- .github/actions/spell-check/expect.txt | 9 +++ .pipelines/versionAndSignCheck.ps1 | 4 + .../keyboardmanager/keyboardmanagerui.md | 33 +++----- installer/PowerToysSetup/KeyboardManager.wxs | 8 ++ .../KeyboardManagerEditor.vcxproj | 20 ++++- .../KeyboardManagerEditor/packages.config | 4 + .../EditKeyboardWindow.cpp | 60 ++++++++------ .../EditShortcutsWindow.cpp | 73 ++++++++++++----- .../EditorConstants.h | 12 +-- .../KeyDropDownControl.cpp | 22 ++++++ .../KeyDropDownControl.h | 8 ++ .../KeyboardManagerEditorLibrary.vcxproj | 12 +++ ...yboardManagerEditorLibrary.vcxproj.filters | 6 ++ .../KeyboardManagerState.cpp | 11 ++- .../ShortcutControl.cpp | 77 ++++++------------ .../SingleKeyRemapControl.cpp | 79 ++++++------------- .../KeyboardManagerEditorLibrary/Styles.cpp | 10 +-- .../XamlBridge2.cpp | 76 ++++++++++++++++++ .../XamlBridge2.h | 46 +++++++++++ .../packages.config | 3 + .../KeyboardManagerEditorLibrary/pch.h | 6 +- 21 files changed, 386 insertions(+), 193 deletions(-) create mode 100644 src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge2.cpp create mode 100644 src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge2.h diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index fbfdd418c5..b05ffcc32f 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -174,6 +174,7 @@ BTNFACE Bto buf bugreport +BUILDARCH BUILDNUMBER buildtask buildtransitive @@ -1055,6 +1056,7 @@ majortype MAJORVERSION makecab MAKEINTRESOURCE +MAKEINTRESOURCEA MAKEINTRESOURCEW makepri manifestdependency @@ -1182,10 +1184,12 @@ msrc msstore mst msvc +msvcp MTND Mul MULTIPLEUSE multizone +muxc mvvm mwb MWBEx @@ -1251,6 +1255,8 @@ NOCOPYBITS nodeca nodiscard nodoc +NODRAWCAPTION +NODRAWICON NOINHERITLAYOUT NOINTERFACE NOLINKINFO @@ -2094,6 +2100,7 @@ VCINSTALLDIR vcm Vcpkg VCRT +vcruntime vcvars VDesktop vdi @@ -2276,6 +2283,8 @@ wsl wss wstr wsz +WTA +WTNCA wtoi WTS wtsapi diff --git a/.pipelines/versionAndSignCheck.ps1 b/.pipelines/versionAndSignCheck.ps1 index 8cc31f7567..42b66ed8bc 100644 --- a/.pipelines/versionAndSignCheck.ps1 +++ b/.pipelines/versionAndSignCheck.ps1 @@ -25,7 +25,11 @@ $nullVersionExceptions = @( "codicon.ttf", "e_sqlite3.dll", "vcamp140_app.dll", + "vcruntime140_app.dll", + "vcruntime140_1_app.dll", + "msvcp140_app.dll", "marshal.dll", + "Microsoft.Toolkit.Win32.UI.XamlHost.dll", "Microsoft.UI.Composition.OSSupport.dll", "Microsoft.UI.Windowing.dll", "Microsoft.UI.Xaml.Internal.dll", diff --git a/doc/devdocs/modules/keyboardmanager/keyboardmanagerui.md b/doc/devdocs/modules/keyboardmanager/keyboardmanagerui.md index 1f55198b4b..a94c9f0b36 100644 --- a/doc/devdocs/modules/keyboardmanager/keyboardmanagerui.md +++ b/doc/devdocs/modules/keyboardmanager/keyboardmanagerui.md @@ -1,10 +1,7 @@ # Keyboard Manager UI ## Table of Contents: -1. [C++ XAML Islands](#c-xaml-islands) - 1. [Debugging exceptions in XAML Islands](#debugging-exceptions-in-xaml-islands) - 2. [Build times](#build-times) - 3. [Setting custom backgrounds for Xaml Controls using brushes](#setting-custom-backgrounds-for-xaml-controls-using-brushes) +1. [XAML Implementation](#xaml-implementation) 2. [UI Structure](#ui-structure) 3. [EditKeyboardWindow / EditShortcutsWindow](#editkeyboardwindow--editshortcutswindow) 1. [OK and Cancel button](#ok-and-cancel-button) @@ -17,28 +14,18 @@ 2. [Single Key ComboBox Selection Handler](#single-key-combobox-selection-handler) 3. [Shortcut ComboBox Selection Handler](#shortcut-combobox-selection-handler) -## C++ XAML Islands -The KBM UI is implemented as a C++ XAML Island, but all the controls are implemented in code behind rather than .xaml and .xaml.cs files. This was done as per a XAML Island Code sample and it didn't require a separate UWP project, which could be limited in terms of using hooks. There is a [tech debt item](https://github.com/microsoft/PowerToys/issues/2027) for moving this to XAML. The reason it wasn't implemented in the C# Settings was because it required communication with the low level hook thread, which could be too slow if IPC is used, since the UI needs to update on every key event. +## XAML Implementation +The KBM UI was originally implemented as a XAML Island, but in order to easily support Mica, which is [still an issue](https://github.com/microsoft/microsoft-ui-xaml/issues/5319), the [`XamlBridge`](https://github.com/microsoft/PowerToys/blob/b3f27057d43445abc59aa04405f7c24bb895a61c/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge2.cpp) was rewritten to use a [`FrameworkView`](https://github.com/microsoft/PowerToys/blob/b3f27057d43445abc59aa04405f7c24bb895a61c/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge2.cpp#L47C11-L49) object which is how a traditional UWP app behaves: +1. A `CoreWindow` is created by [calling a function inside of `Windows.UI.dll` with an ordinal number of `1500`](https://github.com/microsoft/PowerToys/blob/b3f27057d43445abc59aa04405f7c24bb895a61c/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge2.cpp#L35-L42). +2. A [stubbed implementation of `CoreApplicationView`](https://github.com/microsoft/PowerToys/blob/b3f27057d43445abc59aa04405f7c24bb895a61c/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge2.cpp#L10-L18) was created. +3. Then [both objects are passed on to the `FrameworkView`](https://github.com/microsoft/PowerToys/blob/b3f27057d43445abc59aa04405f7c24bb895a61c/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge2.cpp#L47-L49) to initialize the XAML framework. +4. Lastly, the `CoreWindow` is [attached to the editor window](https://github.com/microsoft/PowerToys/blob/b3f27057d43445abc59aa04405f7c24bb895a61c/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge2.cpp#L54-L61) and its `HWND` is used in-place of `DesktopWindowXamlSource`'s. -**Note:** For functions which take a XAML component as argument, pass it by value and not by reference. This is because `winrt` WinUI classes store their own internal references, so they are supposed to be passed by value (and internally ref counts are incremented). Passing by reference can lead to weird behavior where the object is `null`. +Mica is then achieved by calling [`BackdropMaterial::SetApplyToRootOrPageBackground()`](https://github.com/microsoft/PowerToys/blob/b3f27057d43445abc59aa04405f7c24bb895a61c/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp#L388-L400) in both of the editor windows, or falls back to the `ApplicationPageBackgroundThemeBrush` background if Mica isn't available. -The windows are [created as C++ windows](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp#L128-L140) and the window sizes are set to default by [scaling them as per DPI](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp#L120-L126) using the `DPIAware::Convert` API from common lib. Since the UI is launched on a new thread, the window may not be in the foreground, so [we call `SetForegroundWindow`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp#L146-L150). +The UI was also updated to use WinUI 2.8 to match the look and feel of the Fluent design language of Windows 11 and the rest of PowerToys. There has been talk about [migrating the implementation to XAML files instead of code-behind](https://github.com/microsoft/PowerToys/issues/2027) and [utilizing WinUI 3 going forward](https://github.com/microsoft/PowerToys/issues/15870). More about the update can be read in [here](https://github.com/microsoft/PowerToys/pull/28473). -`DesktopWindowXamlSource` has to be declared and [it is initialized](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp#L159-L162) using the [`XamlBridge`](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/ui/XamlBridge.cpp), and [a second window handle](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp#L161-L162) is generated for the internal Xaml Island window. Most of the code was based on the [Xaml Island Sample](https://github.com/microsoft/Xaml-Islands-Samples/blob/master/Samples/Win32/SampleCppApp/XamlBridge.cpp). The `XamlBridge` class contains code which handles initializing the Xaml Island containers as well as handling special messages like keyboard navigation, and focus between islands and between the C++ window and the island. It also has methods for clearing the xaml islands and closing the window. - -Once the UI controls are created, the parent container is set as the content for the `DesktopWindowXamlSource` and the `XamlBridge.MessageLoop` is executed. Messages are processed by the C++ window handler like [`EditKeyboardWindowProc`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp#L364-L404). The general structure we use for this is, for any `WM_PAINT` or `WM_SIZE` message we resize the Xaml Island window. For `WM_GETMINMAXINFO` we set minimum widths so that the window cannot be resized beyond a minimum height and width. This is done to prevent the WinUI elements from overlapping and getting cropped. If it is neither of these cases we send the message to the [`XamlBridge.MessageHandler`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/XamlBridge.cpp#L291-L301) which handles Destroy, Activation and Focus. If `WM_NCDESTROY` is received when the `XamlBridge` is `nullptr`, the window thread is terminated. - -**Note:** `ContentDialog` in Xaml Islands requires manually settings a `XamlRoot`. This can generally be done by passing the XamlRoot from a component in the main window, such as the button used to open the dialog ([`sender.as