Repository navigation
[QuickAccent] Update Topmost logic to attempt to fix hybrid graphics issues - #41044
Merged
Merged
Conversation
Topmost was set to true for the application window on start. This change removes that, and dynamically toggles Topmost for the selection window on activation/deactivation. This is an attempt to fix an issue for laptops with hybrid graphics.
Gordon Lam (yeelam-gordon)
requested a review
from Gleb Khmyznikov (khmyznikov)
August 20, 2025 05:05
Contributor
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Gleb Khmyznikov (khmyznikov)
approved these changes
Aug 21, 2025
Gleb Khmyznikov (khmyznikov)
left a comment
Contributor
There was a problem hiding this comment.
Can't verify as well, but this change looks reasonable to me.
Dustin L. Howett (DHowett)
approved these changes
Aug 21, 2025
Member
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
Contributor
There was a problem hiding this comment.
Pull Request Overview
This PR addresses hybrid graphics issues on laptops with Optimus technology by modifying the Topmost window behavior in Quick Accent. Instead of keeping the main application window permanently topmost, the change dynamically sets the Topmost property only when the selection window is active.
- Removes permanent Topmost setting from main application window initialization
- Adds conditional Topmost behavior that activates only when the selection window is displayed
- Includes explanatory comment linking the change to hybrid graphics issue resolution
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
Collaborator
Author
|
After requesting feedback from users with hybrid graphics laptops, it was confirmed that this did not fix #34849, which I've reopened. |
Yu Leng (moooyo)
pushed a commit
that referenced
this pull request
Jun 29, 2026
The WinUI 3 selector set IsAlwaysOnTop once at construction and left it on for the life of the (hidden but never-destroyed) process, reintroducing the hybrid-graphics regression that PR #41044 fixed for the WPF original by toggling Topmost = isActive (issue #34849, still open): a permanently always-on-top overlay can keep a discrete GPU awake. Toggle the WinUIEx WindowEx.IsAlwaysOnTop property in PowerAccent_OnChangeDisplay (true on show, false on hide) instead, matching the sibling PowerDisplay's use of the same property. This drops the cached presenter field, the constructor null-check/warning and the SetAlwaysOnTop helper (WindowEx.IsAlwaysOnTop is null-safe internally), and the now-unused ManagedCommon using. Also fix the dev-doc "WinUI 3 Selector Notes" to reference the renamed PowerAccentXAML/MainWindow.xaml(.cs) instead of the removed Selector.xaml(.cs). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
10 of 11 tasks
Yu Leng (moooyo)
added a commit
that referenced
this pull request
Jul 3, 2026
## Summary of the Pull Request Migrates the **Quick Accent (PowerAccent)** module's UI from WPF (`System.Windows.*`) to **WinUI 3 (Windows App SDK)**, following the pattern used by other migrated modules (ImageResizer, PowerDisplay). The accent selector is now a self-contained WinUI 3 app (`PowerToys.PowerAccent.exe`) shipped under `WinUI3Apps`, and `PowerAccent.Core` is UI-framework-agnostic. demo: https://github.com/user-attachments/assets/400c33ee-0fc0-491e-841b-a546438edf91 ## PR Checklist - [x] Closes: #48889 - [x] **Communication:** Tracked task (#48889) agreed with core contributors - [x] **Tests:** Added/updated and all pass — new `PowerAccent.Core.UnitTests` (21 tests for the positioning / DPI math); existing `PowerAccent.Common.UnitTests` unaffected - [x] **Localization:** No new localizable end-user strings — new accessibility metadata uses non-localized `AutomationProperties.AutomationId`, and the window title is the brand name `"Quick Accent"` (literal, matching ColorPicker) - [x] **Dev docs:** Updated `doc/devdocs/modules/quickaccent.md` - [x] **New binaries:** Added on the required places - [x] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json): the `WinUI3Apps\` PowerAccent payloads are listed in `ESRPSigning_core.json` - [x] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs): no manual `Product.wxs` entry — the self-contained `WinUI3Apps` output (exe + `.pri` + Windows App SDK runtime) is harvested by the `WinUI3ApplicationsFiles` glob (same as ImageResizer) - [x] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml): no change needed — `PowerAccent.Core.UnitTests` is discovered by the existing `**\*UnitTest*.dll` VSTest glob - [x] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml): covered by `ESRPSigning_core.json`; no `release.yml` change needed - [ ] **Documentation updated:** N/A — internal UI-framework migration with no user-facing behavior change ## Detailed Description of the Pull Request / Additional comments The migration spans three areas (tracked in #48889): **UI (WPF → WinUI 3)** - `PowerAccent.UI` is now a WinUI 3 app shell (custom `Program.Main`, `WindowsPackageType=None`, `WindowsAppSDKSelfContained=true`). - The accent selector is a non-activating `TransparentWindow` overlay shown with `SW_SHOWNA` (never steals focus). It is made always-on-top only while shown — the WinUIEx `WindowEx.IsAlwaysOnTop` property is toggled `true` on show / `false` on hide in `OnChangeDisplay` (matching the WPF original's `Topmost = isActive`), so the dormant, never-destroyed overlay does not pin a discrete GPU awake on hybrid-graphics laptops (issue #34849 / PR #41044). - The accent "pill" selection visual is reproduced with `VisualStateManager` (WinUI 3 has no `Style.Triggers`). - **WinUI 3 gotcha:** x:Bind on a Window-rooted XAML initializes only on `Window.Activated`, which never fires for this `SW_SHOWNA` overlay — so the selector calls `Bindings.Update()` after `InitializeComponent()`; without it the `ListView` renders empty. - **Theme:** the long-lived, never-activated process follows the system app theme automatically — `App.xaml` leaves `Application.RequestedTheme` unset, so WinUI re-resolves the `{ThemeResource}` brushes (and retints the acrylic) on a live light/dark switch with no manual `ThemeListener` needed. - **Layout parity with the WPF original:** the bar width hugs its content (`itemCount × 48`, clamped to the monitor width — computed, not measured, to avoid a racy `ListView` measure), and each cell pins `MinWidth=48` (WinUI's `ListViewItem` defaults to 88, which would otherwise leave wide gaps). - **Accessibility:** UIA window name + `AutomationId`s on the character list and description. **Dependency** - `PowerAccent.Core` no longer depends on WPF — it raises events and takes an injected UI-thread marshaller. - WinForms `SendKeys` → `SendInput` (CsWin32 P/Invoke); WPF-UI (Lepo) removed; language data moved to the UI-/WinRT-agnostic `PowerAccent.Common`. - MVVM via CommunityToolkit.Mvvm with `[ObservableProperty]` **partial properties** (WinRT-correct, clears MVVMTK0045). **CI / Build / Installer** - Signing config, WinUI3Apps glob harvest, and the new unit-test project — see the checklist above. ## Validation Steps Performed - **Build:** `x64 Debug` builds with **0 warnings / 0 errors**. - **Unit tests:** `PowerAccent.Core.UnitTests` — **21/21 pass** (9 anchor positions × DPI 1.0/1.5/2.0, the offset and negative-origin monitors, caret centering + edge clamping + flip-below). - **XamlStyler:** `PowerAccentXAML/MainWindow.xaml` passes the passive format check (CI mode). - **Manual (single monitor, Top-center, light theme):** - Accent popup appears with the full accent list rendered. - Bar hugs the characters and is centered; cell spacing matches the WPF original. - Switching the system theme (light/dark) is followed live by the popup. - With `show_description` enabled, the description row is wide (≥600px) and readable, with the accent bar centered above it. - **Remaining manual validation** (tracked in #48889): multi-monitor, per-monitor DPI, all 9 `toolbar_position` values, and high-contrast theme. --------- Co-authored-by: Yu Leng <yuleng@microsoft.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Niels Laute <niels.laute@live.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
7 tasks done
Yu Leng (moooyo)
added a commit
that referenced
this pull request
Aug 11, 2026
## Summary of the Pull Request Follow-up to #49633: replace the way the accent bar's first frame is protected. #49633 fixed the blank/stale first frame (#49489) by masking it — `Selector.Opacity = 0`, unveil after two `CompositionTarget.Rendering` ticks, backed by a 150 ms watchdog. This PR removes the cause instead, using the technique the Command Palette and Quick Access already ship: **DWM-cloak the overlay instead of hiding it**, so it never stops rendering and there is no stale frame to put back on screen. No user-visible behaviour change is intended beyond removing the fixed two-render-tick reveal delay; this is a mechanism swap plus the cleanup it enables. ## PR Checklist - [x] **Closes:** N/A — #49489 was already closed by #49633; this replaces that fix's mechanism - [x] **Communication:** follow-up to a merged PR in the same module, no new feature surface - [x] **Tests:** `PowerAccent.Core.UnitTests` 32/32 still pass (the pure width logic from #49633 is untouched). The current-head CI status is tracked in the PR checks; the compositor-specific manual measurements below were captured before the final commit-fence follow-up and are labeled accordingly - [x] **Localization:** no new end-user-facing strings - [x] **Dev docs:** N/A - [x] **New binaries:** none - [x] **Documentation updated:** N/A ## Detailed Description of the Pull Request / Additional comments ### Why cloaking A hidden WinUI 3 window renders nothing. Its composition surface therefore still holds the frame it was showing when it was hidden, and `ShowWindow` puts that stale frame back on screen before the rebuilt accent list has been laid out — that is #49489. Everything downstream of that follows from "the window does not render while hidden": * the bar cannot be measured before it is shown (a `Collapsed` subtree is never measured), hence #49633's measure-twice workaround; * #49633 deferred reveal using rendering ticks because it did not have a composition-commit fence; * this PR calls `Microsoft.UI.Composition.Compositor.RequestCommitAsync()` after the bar has been laid out, sized, positioned and scrolled, and only reveals after that commit completes. This replaces the frame counter and watchdog with an explicit compositor fence. A cloaked window is equally invisible to the user but stays `SW_SHOWNA`-shown, so XAML keeps laying it out and painting it. This is exactly what `Microsoft.CmdPal.UI\MainWindow.xaml.cs` does, and its comment names the same symptom: ```csharp // TRICKY: show our HWND again. This will trick XAML into painting our // HWND again, so that we avoid the "flicker" caused by a WinUI3 app // window being first shown ``` `QuickAccess.UI\QuickAccessXAML\MainWindow.xaml.cs` uses the same pattern, including the "warm up the window while cloaked" prewarm that this PR also picks up — which is what removes the *first summon of the process* case that #49633's second measurement existed for. ### What the summon looks like now `Show()` still raises `Showing`, so the surface leaves `Collapsed` and the bar lays out — but the window is still cloaked, so nothing reaches the screen. The bar is then measured **once** (on a templated, laid-out, non-collapsed subtree), sized, positioned and scrolled to the selection. The compositor commit is then awaited, and only after it completes does `Reveal()` uncloak the window. The first visible frame is a finished bar by construction rather than by timing. Removed as a result: `RevealTimeoutMs`, `FramesBeforeReveal`, `_revealTimer`, `_revealGeneration`, `_renderedFrames`, `_measuredContentWidthDip`, `ArmRevealTimeout`, `CancelPendingReveal`, `WaitForFirstFrameThenReveal`, `OnRenderingBeforeReveal`, the local `Reveal`, and the `Selector.Opacity` dance — 87 net lines out of `MainWindow`. `_showGeneration` stays: a layout callback queued by a dismissed summon still has to be dropped. ### `TransparentWindow` The cloak lives in the shared window because `Hide()` owns the `AppWindow.Hide()` that has to be replaced. It is **opt-in** (`EnableCloakedHide()`), so Shortcut Guide's overlay and CmdPal's toast keep hiding exactly as they do today; only Quick Accent enables it. `Reveal()` is a no-op for them. Two details worth review attention: * **Hit-testing.** Cloaking takes a window out of composition but *not* out of hit-testing, and this HWND sits exactly where the user is typing. While cloaked the window is therefore made click-through (`WS_EX_TRANSPARENT`), restored on reveal. Without this, an invisible accent bar would swallow clicks meant for the app underneath. * **`SW_HIDE` then `SW_SHOWNA`.** Same order as CmdPal: the hide is what hands the foreground back to whatever window should own it, and the show that follows leaves the window "shown" — which is what keeps XAML painting — while the cloak keeps it off screen. If DWM refuses to cloak, the HWND remains hidden; a later `Show()` retries instead of exposing an un-laid-out frame. ### Relationship to #34849 / #41044 Always-on-top is still released on hide, so the dormant overlay is `WS_EX_TOPMOST=False` exactly as before — verified below. Cloaking is orthogonal to topmost. The one honest trade-off is that the HWND is now permanently `WS_VISIBLE` (cloaked), so it keeps participating in composition while dormant, the same as CmdPal and Quick Access already do; it stays out of Alt-Tab and the taskbar via a hidden owner plus `WS_EX_TOOLWINDOW`. ## Validation Steps Performed > The build/test/live-state results below were recorded at `e372cdf`. Current head `ea658bb` adds the explicit `RequestCommitAsync` fence after that validation. Current-head CI is tracked by the PR checks, and the live window-state/frame-capture checks should be repeated before merge. * `build-essentials`, `Common.UI.Controls`, `PowerAccent.UI` and `PowerAccent.Core.UnitTests` all build clean (Debug|x64), 0 warnings. * `PowerAccent.Core.UnitTests`: 32/32 pass. * Live window-state measurement against the built `PowerToys.PowerAccent.exe` (`DwmGetWindowAttribute(DWMWA_CLOAKED)` + `IsWindowVisible` + ex-styles), driving a real summon of the <kbd>R</kbd> bar with **All languages** selected: | phase | state | |---|---| | dormant (prewarmed, before any summon) | `visible=True cloaked=1 topmost=False clickThrough=True` | | summoned | `visible=True cloaked=0 topmost=True clickThrough=False` | | dismissed | `visible=True cloaked=1 topmost=False clickThrough=True` | i.e. the window is shown-and-painting the whole time, invisible and click-through while dormant, and topmost/interactive only while summoned. * Screen-captured the summoned bar: all 22 characters for <kbd>R</kbd> render (including the wide `₹ ៛ ﷼`), leading and trailing padding are symmetric, nothing is clipped and the selection is on the first cell. --------- Co-authored-by: Yu Leng <yuleng@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Boliang Zhang (LegendaryBlair)
pushed a commit
that referenced
this pull request
Aug 11, 2026
## Summary of the Pull Request Follow-up to #49633: replace the way the accent bar's first frame is protected. #49633 fixed the blank/stale first frame (#49489) by masking it — `Selector.Opacity = 0`, unveil after two `CompositionTarget.Rendering` ticks, backed by a 150 ms watchdog. This PR removes the cause instead, using the technique the Command Palette and Quick Access already ship: **DWM-cloak the overlay instead of hiding it**, so it never stops rendering and there is no stale frame to put back on screen. No user-visible behaviour change is intended beyond removing the fixed two-render-tick reveal delay; this is a mechanism swap plus the cleanup it enables. ## PR Checklist - [x] **Closes:** N/A — #49489 was already closed by #49633; this replaces that fix's mechanism - [x] **Communication:** follow-up to a merged PR in the same module, no new feature surface - [x] **Tests:** `PowerAccent.Core.UnitTests` 32/32 still pass (the pure width logic from #49633 is untouched). The current-head CI status is tracked in the PR checks; the compositor-specific manual measurements below were captured before the final commit-fence follow-up and are labeled accordingly - [x] **Localization:** no new end-user-facing strings - [x] **Dev docs:** N/A - [x] **New binaries:** none - [x] **Documentation updated:** N/A ## Detailed Description of the Pull Request / Additional comments ### Why cloaking A hidden WinUI 3 window renders nothing. Its composition surface therefore still holds the frame it was showing when it was hidden, and `ShowWindow` puts that stale frame back on screen before the rebuilt accent list has been laid out — that is #49489. Everything downstream of that follows from "the window does not render while hidden": * the bar cannot be measured before it is shown (a `Collapsed` subtree is never measured), hence #49633's measure-twice workaround; * #49633 deferred reveal using rendering ticks because it did not have a composition-commit fence; * this PR calls `Microsoft.UI.Composition.Compositor.RequestCommitAsync()` after the bar has been laid out, sized, positioned and scrolled, and only reveals after that commit completes. This replaces the frame counter and watchdog with an explicit compositor fence. A cloaked window is equally invisible to the user but stays `SW_SHOWNA`-shown, so XAML keeps laying it out and painting it. This is exactly what `Microsoft.CmdPal.UI\MainWindow.xaml.cs` does, and its comment names the same symptom: ```csharp // TRICKY: show our HWND again. This will trick XAML into painting our // HWND again, so that we avoid the "flicker" caused by a WinUI3 app // window being first shown ``` `QuickAccess.UI\QuickAccessXAML\MainWindow.xaml.cs` uses the same pattern, including the "warm up the window while cloaked" prewarm that this PR also picks up — which is what removes the *first summon of the process* case that #49633's second measurement existed for. ### What the summon looks like now `Show()` still raises `Showing`, so the surface leaves `Collapsed` and the bar lays out — but the window is still cloaked, so nothing reaches the screen. The bar is then measured **once** (on a templated, laid-out, non-collapsed subtree), sized, positioned and scrolled to the selection. The compositor commit is then awaited, and only after it completes does `Reveal()` uncloak the window. The first visible frame is a finished bar by construction rather than by timing. Removed as a result: `RevealTimeoutMs`, `FramesBeforeReveal`, `_revealTimer`, `_revealGeneration`, `_renderedFrames`, `_measuredContentWidthDip`, `ArmRevealTimeout`, `CancelPendingReveal`, `WaitForFirstFrameThenReveal`, `OnRenderingBeforeReveal`, the local `Reveal`, and the `Selector.Opacity` dance — 87 net lines out of `MainWindow`. `_showGeneration` stays: a layout callback queued by a dismissed summon still has to be dropped. ### `TransparentWindow` The cloak lives in the shared window because `Hide()` owns the `AppWindow.Hide()` that has to be replaced. It is **opt-in** (`EnableCloakedHide()`), so Shortcut Guide's overlay and CmdPal's toast keep hiding exactly as they do today; only Quick Accent enables it. `Reveal()` is a no-op for them. Two details worth review attention: * **Hit-testing.** Cloaking takes a window out of composition but *not* out of hit-testing, and this HWND sits exactly where the user is typing. While cloaked the window is therefore made click-through (`WS_EX_TRANSPARENT`), restored on reveal. Without this, an invisible accent bar would swallow clicks meant for the app underneath. * **`SW_HIDE` then `SW_SHOWNA`.** Same order as CmdPal: the hide is what hands the foreground back to whatever window should own it, and the show that follows leaves the window "shown" — which is what keeps XAML painting — while the cloak keeps it off screen. If DWM refuses to cloak, the HWND remains hidden; a later `Show()` retries instead of exposing an un-laid-out frame. ### Relationship to #34849 / #41044 Always-on-top is still released on hide, so the dormant overlay is `WS_EX_TOPMOST=False` exactly as before — verified below. Cloaking is orthogonal to topmost. The one honest trade-off is that the HWND is now permanently `WS_VISIBLE` (cloaked), so it keeps participating in composition while dormant, the same as CmdPal and Quick Access already do; it stays out of Alt-Tab and the taskbar via a hidden owner plus `WS_EX_TOOLWINDOW`. ## Validation Steps Performed > The build/test/live-state results below were recorded at `e372cdf`. Current head `ea658bb` adds the explicit `RequestCommitAsync` fence after that validation. Current-head CI is tracked by the PR checks, and the live window-state/frame-capture checks should be repeated before merge. * `build-essentials`, `Common.UI.Controls`, `PowerAccent.UI` and `PowerAccent.Core.UnitTests` all build clean (Debug|x64), 0 warnings. * `PowerAccent.Core.UnitTests`: 32/32 pass. * Live window-state measurement against the built `PowerToys.PowerAccent.exe` (`DwmGetWindowAttribute(DWMWA_CLOAKED)` + `IsWindowVisible` + ex-styles), driving a real summon of the <kbd>R</kbd> bar with **All languages** selected: | phase | state | |---|---| | dormant (prewarmed, before any summon) | `visible=True cloaked=1 topmost=False clickThrough=True` | | summoned | `visible=True cloaked=0 topmost=True clickThrough=False` | | dismissed | `visible=True cloaked=1 topmost=False clickThrough=True` | i.e. the window is shown-and-painting the whole time, invisible and click-through while dormant, and topmost/interactive only while summoned. * Screen-captured the summoned bar: all 22 characters for <kbd>R</kbd> render (including the wide `₹ ៛ ﷼`), leading and trailing padding are symmetric, nothing is clipped and the selection is on the first cell. --------- Co-authored-by: Yu Leng <yuleng@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
An attempt to fix a Quick Accent issue affecting laptops with 'Optimus' hybrid graphics modes, where the utility locks the machine into discrete graphics mode permanently.
Summary of the Pull Request
This PR changes the Topmost behaviour for Quick Accent from always true to only being true when the selection window is displayed.
PR Checklist
Detailed Description of the Pull Request / Additional comments
Topmost was set to
truefor the main application window on start, which persisted for the lifetime of the application. This PR change removes that, and instead dynamically toggles Topmost for the selection window as it is activated/deactivated. The assumption is that the FluentWindow-derived window is retaining graphics resources and presenting as an active GPU consumer because of the main application window's Topmost status, even if the selection window is hidden.Validation Steps Performed
I've confirmed with manual tests that the existing QuickAccent functionality appears to function identically with this change. However, I do not own a laptop with a hybrid graphics capability, so am unable to test whether this fixes the underlying problem. I will keep my fingers crossed though 🤞😊