CmdPal: Reduce DockWindow backdrop switching and visual artifacts (#46309)

<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

This PR reduces "blinking" of dock when (any) CmdPal settings changes.
It handles only backdrop, not icons.

- Avoids recreating the acrylic controller when the effective backdrop
parameters have not changed.
- Reuses the transparent backdrop instead of reassigning it during dock
refreshes.
- Cleans up backdrop controllers only when switching backdrop modes or
disposing the window.
- Removes obsolete dock-specific backdrop helper logic now handled
directly in DockWindow.


<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #46308
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
This commit is contained in:
Jiří Polášek
2026-03-24 19:17:51 +01:00
committed by GitHub
parent 21f06b8bd0
commit 93f80f5f61
2 changed files with 84 additions and 41 deletions

View File

@@ -4,7 +4,6 @@
using Microsoft.CmdPal.UI.ViewModels.Settings; using Microsoft.CmdPal.UI.ViewModels.Settings;
using Windows.Win32; using Windows.Win32;
using WinUIEx;
namespace Microsoft.CmdPal.UI.Dock; namespace Microsoft.CmdPal.UI.Dock;
@@ -32,16 +31,6 @@ internal static class DockSettingsToViews
}; };
} }
public static Microsoft.UI.Xaml.Media.SystemBackdrop? GetSystemBackdrop(DockBackdrop backdrop)
{
return backdrop switch
{
DockBackdrop.Transparent => new TransparentTintBackdrop(),
DockBackdrop.Acrylic => null,
_ => throw new NotImplementedException(),
};
}
public static uint GetAppBarEdge(DockSide side) public static uint GetAppBarEdge(DockSide side)
{ {
return side switch return side switch

View File

@@ -59,6 +59,8 @@ public sealed partial class DockWindow : WindowEx,
private DockControl _dock; private DockControl _dock;
private DesktopAcrylicController? _acrylicController; private DesktopAcrylicController? _acrylicController;
private SystemBackdropConfiguration? _configurationSource; private SystemBackdropConfiguration? _configurationSource;
private bool _isUpdatingBackdrop;
private BackdropParameters? _lastAppliedAcrylicBackdrop;
private DockSize _lastSize; private DockSize _lastSize;
// Store the original WndProc // Store the original WndProc
@@ -78,6 +80,7 @@ public sealed partial class DockWindow : WindowEx,
viewModel = serviceProvider.GetService<DockViewModel>()!; viewModel = serviceProvider.GetService<DockViewModel>()!;
_themeService = serviceProvider.GetRequiredService<IThemeService>(); _themeService = serviceProvider.GetRequiredService<IThemeService>();
_themeService.ThemeChanged += ThemeService_ThemeChanged; _themeService.ThemeChanged += ThemeService_ThemeChanged;
InitializeBackdropSupport();
_windowViewModel = new DockWindowViewModel(_themeService); _windowViewModel = new DockWindowViewModel(_themeService);
_dock = new DockControl(viewModel); _dock = new DockControl(viewModel);
@@ -156,14 +159,7 @@ public sealed partial class DockWindow : WindowEx,
private void UpdateSettingsOnUiThread() private void UpdateSettingsOnUiThread()
{ {
this.viewModel.UpdateSettings(_settings); this.viewModel.UpdateSettings(_settings);
UpdateBackdrop();
SystemBackdrop = DockSettingsToViews.GetSystemBackdrop(_settings.Backdrop);
// If the backdrop is acrylic, things are more complicated
if (_settings.Backdrop == DockBackdrop.Acrylic)
{
SetAcrylic();
}
_dock.UpdateSettings(_settings); _dock.UpdateSettings(_settings);
var side = DockSettingsToViews.GetAppBarEdge(_settings.Side); var side = DockSettingsToViews.GetAppBarEdge(_settings.Side);
@@ -183,31 +179,98 @@ public sealed partial class DockWindow : WindowEx,
CreateAppBar(_hwnd); CreateAppBar(_hwnd);
} }
// We want to use DesktopAcrylicKind.Thin and custom colors as this is the default material private void InitializeBackdropSupport()
// other Shell surfaces are using, this cannot be set in XAML however.
private void SetAcrylic()
{ {
if (DesktopAcrylicController.IsSupported()) if (DesktopAcrylicController.IsSupported())
{ {
// Hooking up the policy object.
_configurationSource = new SystemBackdropConfiguration _configurationSource = new SystemBackdropConfiguration
{ {
// Initial configuration state.
IsInputActive = true, IsInputActive = true,
}; };
UpdateAcrylic();
} }
} }
private void UpdateAcrylic() private void UpdateBackdrop()
{ {
if (_acrylicController != null) // Prevent re-entrance when backdrop changes trigger theme refresh work.
if (_isUpdatingBackdrop)
{
return;
}
_isUpdatingBackdrop = true;
try
{
switch (_settings.Backdrop)
{
case DockBackdrop.Transparent:
if (SystemBackdrop is not TransparentTintBackdrop)
{
CleanupBackdropControllers();
SetupTransparentBackdrop();
}
break;
case DockBackdrop.Acrylic:
default:
SetupDesktopAcrylic(_themeService.CurrentDockTheme.BackdropParameters);
break;
}
}
catch (Exception ex)
{
Logger.LogError("Failed to update dock backdrop", ex);
}
finally
{
_isUpdatingBackdrop = false;
}
}
private void SetupTransparentBackdrop()
{
if (SystemBackdrop is not TransparentTintBackdrop)
{
SystemBackdrop = new TransparentTintBackdrop();
}
_lastAppliedAcrylicBackdrop = null;
}
private void CleanupBackdropControllers()
{
if (_acrylicController is not null)
{ {
_acrylicController.RemoveAllSystemBackdropTargets(); _acrylicController.RemoveAllSystemBackdropTargets();
_acrylicController.Dispose(); _acrylicController.Dispose();
_acrylicController = null;
} }
var backdrop = _themeService.CurrentDockTheme.BackdropParameters; _lastAppliedAcrylicBackdrop = null;
}
private void SetupDesktopAcrylic(BackdropParameters backdrop)
{
var needsAcrylicUpdate = _acrylicController is null || _lastAppliedAcrylicBackdrop != backdrop;
if (!needsAcrylicUpdate)
{
return;
}
CleanupBackdropControllers();
// Fall back to the transparent backdrop if acrylic is not supported.
if (_configurationSource is null || !DesktopAcrylicController.IsSupported())
{
SetupTransparentBackdrop();
return;
}
// DesktopAcrylicController and SystemBackdrop can't be active simultaneously.
SystemBackdrop = null;
_acrylicController = new DesktopAcrylicController _acrylicController = new DesktopAcrylicController
{ {
Kind = DesktopAcrylicKind.Thin, Kind = DesktopAcrylicKind.Thin,
@@ -221,29 +284,20 @@ public sealed partial class DockWindow : WindowEx,
// Note: Be sure to have "using WinRT;" to support the Window.As<...>() call. // Note: Be sure to have "using WinRT;" to support the Window.As<...>() call.
_acrylicController.AddSystemBackdropTarget(this.As<ICompositionSupportsSystemBackdrop>()); _acrylicController.AddSystemBackdropTarget(this.As<ICompositionSupportsSystemBackdrop>());
_acrylicController.SetSystemBackdropConfiguration(_configurationSource); _acrylicController.SetSystemBackdropConfiguration(_configurationSource);
_lastAppliedAcrylicBackdrop = backdrop;
} }
private void DisposeAcrylic() private void DisposeAcrylic()
{ {
if (_acrylicController is not null) CleanupBackdropControllers();
{ _configurationSource = null;
_acrylicController.Dispose();
_acrylicController = null!;
_configurationSource = null!;
}
} }
private void ThemeService_ThemeChanged(object? sender, ThemeChangedEventArgs e) private void ThemeService_ThemeChanged(object? sender, ThemeChangedEventArgs e)
{ {
DispatcherQueue.TryEnqueue(() => DispatcherQueue.TryEnqueue(() =>
{ {
// We only need to handle acrylic here. UpdateBackdrop();
// Transparent background is handled directly in XAML by binding to
// the DockWindowViewModel's ColorizationColor properties.
if (_settings.Backdrop == DockBackdrop.Acrylic)
{
UpdateAcrylic();
}
// ActualTheme / RequestedTheme sync, // ActualTheme / RequestedTheme sync,
// as pilfered from WindowThemeSynchronizer // as pilfered from WindowThemeSynchronizer