diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockSettingsToViews.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockSettingsToViews.cs index 2a1f0396ef..6137685d1c 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockSettingsToViews.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockSettingsToViews.cs @@ -4,7 +4,6 @@ using Microsoft.CmdPal.UI.ViewModels.Settings; using Windows.Win32; -using WinUIEx; 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) { return side switch diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockWindow.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockWindow.xaml.cs index d9803b1403..bf593b3392 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockWindow.xaml.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockWindow.xaml.cs @@ -59,6 +59,8 @@ public sealed partial class DockWindow : WindowEx, private DockControl _dock; private DesktopAcrylicController? _acrylicController; private SystemBackdropConfiguration? _configurationSource; + private bool _isUpdatingBackdrop; + private BackdropParameters? _lastAppliedAcrylicBackdrop; private DockSize _lastSize; // Store the original WndProc @@ -78,6 +80,7 @@ public sealed partial class DockWindow : WindowEx, viewModel = serviceProvider.GetService()!; _themeService = serviceProvider.GetRequiredService(); _themeService.ThemeChanged += ThemeService_ThemeChanged; + InitializeBackdropSupport(); _windowViewModel = new DockWindowViewModel(_themeService); _dock = new DockControl(viewModel); @@ -156,14 +159,7 @@ public sealed partial class DockWindow : WindowEx, private void UpdateSettingsOnUiThread() { this.viewModel.UpdateSettings(_settings); - - SystemBackdrop = DockSettingsToViews.GetSystemBackdrop(_settings.Backdrop); - - // If the backdrop is acrylic, things are more complicated - if (_settings.Backdrop == DockBackdrop.Acrylic) - { - SetAcrylic(); - } + UpdateBackdrop(); _dock.UpdateSettings(_settings); var side = DockSettingsToViews.GetAppBarEdge(_settings.Side); @@ -183,31 +179,98 @@ public sealed partial class DockWindow : WindowEx, CreateAppBar(_hwnd); } - // We want to use DesktopAcrylicKind.Thin and custom colors as this is the default material - // other Shell surfaces are using, this cannot be set in XAML however. - private void SetAcrylic() + private void InitializeBackdropSupport() { if (DesktopAcrylicController.IsSupported()) { - // Hooking up the policy object. _configurationSource = new SystemBackdropConfiguration { - // Initial configuration state. 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.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 { 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. _acrylicController.AddSystemBackdropTarget(this.As()); _acrylicController.SetSystemBackdropConfiguration(_configurationSource); + _lastAppliedAcrylicBackdrop = backdrop; } private void DisposeAcrylic() { - if (_acrylicController is not null) - { - _acrylicController.Dispose(); - _acrylicController = null!; - _configurationSource = null!; - } + CleanupBackdropControllers(); + _configurationSource = null; } private void ThemeService_ThemeChanged(object? sender, ThemeChangedEventArgs e) { DispatcherQueue.TryEnqueue(() => { - // We only need to handle acrylic here. - // Transparent background is handled directly in XAML by binding to - // the DockWindowViewModel's ColorizationColor properties. - if (_settings.Backdrop == DockBackdrop.Acrylic) - { - UpdateAcrylic(); - } + UpdateBackdrop(); // ActualTheme / RequestedTheme sync, // as pilfered from WindowThemeSynchronizer