CmdPal: Transparent window (#45159)

## Summary

This PR adds:
- Backdrop material customization
  - Alongside acrylic, the following options are now available:
  - Transparent background
  - Mica background
- Background material opacity
  - Lets you control how transparent the background is

## Pictures? Pictures!

<img width="1491" height="928" alt="image"
src="https://github.com/user-attachments/assets/ff4e9e06-fcf1-4f05-bc0a-fb70dc4f39be"
/>



https://github.com/user-attachments/assets/84e83279-afab-481e-b904-f054318c5d2f

<img width="977" height="628" alt="image"
src="https://github.com/user-attachments/assets/241a228d-af3f-448a-94a6-0a282218bd8c"
/>


## PR Checklist

- [x] Closes: #44197
<!-- - [ ] 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-02-09 20:42:01 +01:00
committed by GitHub
parent 7477b561a1
commit 095961402b
24 changed files with 1023 additions and 157 deletions

View File

@@ -4,6 +4,7 @@
using CommunityToolkit.WinUI.Helpers;
using Microsoft.CmdPal.UI.Helpers;
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.CmdPal.UI.ViewModels.Services;
using Microsoft.UI.Xaml;
using Windows.UI;
@@ -34,7 +35,7 @@ internal sealed class ColorfulThemeProvider : IThemeProvider
_uiSettings = uiSettings;
}
public AcrylicBackdropParameters GetAcrylicBackdrop(ThemeContext context)
public BackdropParameters GetBackdropParameters(ThemeContext context)
{
var isLight = context.Theme == ElementTheme.Light ||
(context.Theme == ElementTheme.Default &&
@@ -53,7 +54,26 @@ internal sealed class ColorfulThemeProvider : IThemeProvider
var colorIntensity = isLight ? 0.6f * colorIntensityUser : colorIntensityUser;
var effectiveBgColor = ColorBlender.Blend(baseColor, blended, colorIntensity);
return new AcrylicBackdropParameters(effectiveBgColor, effectiveBgColor, 0.8f, 0.8f);
var transparencyMode = context.BackdropStyle ?? BackdropStyle.Acrylic;
var config = BackdropStyles.Get(transparencyMode);
// For colorful theme, boost tint opacity to show color better through blur
// But not for styles with fixed opacity (Mica) - they handle their own opacity
var baseTintOpacity = config.ControllerKind == BackdropControllerKind.Solid || !config.SupportsOpacity
? (float?)null // Use default
: Math.Max(config.BaseTintOpacity, 0.8f);
var effectiveOpacity = config.ComputeEffectiveOpacity(context.BackdropOpacity, baseTintOpacity);
var effectiveLuminosityOpacity = config.SupportsOpacity
? config.BaseLuminosityOpacity * context.BackdropOpacity
: config.BaseLuminosityOpacity;
return new BackdropParameters(
TintColor: effectiveBgColor,
FallbackColor: effectiveBgColor,
EffectiveOpacity: effectiveOpacity,
EffectiveLuminosityOpacity: effectiveLuminosityOpacity,
Style: transparencyMode);
}
private static class ColorBlender

View File

@@ -8,14 +8,14 @@ using Microsoft.CmdPal.UI.ViewModels.Services;
namespace Microsoft.CmdPal.UI.Services;
/// <summary>
/// Provides theme identification, resource path resolution, and creation of acrylic
/// backdrop parameters based on the current <see cref="ThemeContext"/>.
/// Provides theme identification, resource path resolution, and creation of backdrop
/// parameters based on the current <see cref="ThemeContext"/>.
/// </summary>
/// <remarks>
/// Implementations should expose a stable <see cref="ThemeKey"/> and a valid XAML resource
/// dictionary path via <see cref="ResourcePath"/>. The
/// <see cref="GetAcrylicBackdrop(ThemeContext)"/> method computes
/// <see cref="AcrylicBackdropParameters"/> using the supplied theme context.
/// <see cref="GetBackdropParameters(ThemeContext)"/> method computes
/// <see cref="BackdropParameters"/> using the supplied theme context.
/// </remarks>
internal interface IThemeProvider
{
@@ -30,9 +30,9 @@ internal interface IThemeProvider
string ResourcePath { get; }
/// <summary>
/// Creates acrylic backdrop parameters based on the provided theme context.
/// Creates backdrop parameters based on the provided theme context.
/// </summary>
/// <param name="context">The current theme context, including theme, tint, and optional background details.</param>
/// <returns>The computed <see cref="AcrylicBackdropParameters"/> for the backdrop.</returns>
AcrylicBackdropParameters GetAcrylicBackdrop(ThemeContext context);
/// <param name="context">The current theme context, including theme, tint, transparency mode, and optional background details.</param>
/// <returns>The computed <see cref="BackdropParameters"/> for the backdrop.</returns>
BackdropParameters GetBackdropParameters(ThemeContext context);
}

View File

@@ -2,6 +2,7 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.CmdPal.UI.ViewModels.Services;
using Microsoft.UI.Xaml;
using Windows.UI;
@@ -28,16 +29,28 @@ internal sealed class NormalThemeProvider : IThemeProvider
public string ResourcePath => "ms-appx:///Styles/Theme.Normal.xaml";
public AcrylicBackdropParameters GetAcrylicBackdrop(ThemeContext context)
public BackdropParameters GetBackdropParameters(ThemeContext context)
{
var isLight = context.Theme == ElementTheme.Light ||
(context.Theme == ElementTheme.Default &&
_uiSettings.GetColorValue(UIColorType.Background).R > 128);
return new AcrylicBackdropParameters(
var backdropStyle = context.BackdropStyle ?? BackdropStyle.Acrylic;
var config = BackdropStyles.Get(backdropStyle);
// Apply light/dark theme adjustment to luminosity
var baseLuminosityOpacity = isLight
? config.BaseLuminosityOpacity
: Math.Min(config.BaseLuminosityOpacity + 0.06f, 1.0f);
var effectiveOpacity = config.ComputeEffectiveOpacity(context.BackdropOpacity);
var effectiveLuminosityOpacity = baseLuminosityOpacity * context.BackdropOpacity;
return new BackdropParameters(
TintColor: isLight ? LightBaseColor : DarkBaseColor,
FallbackColor: isLight ? LightBaseColor : DarkBaseColor,
TintOpacity: 0.5f,
LuminosityOpacity: isLight ? 0.9f : 0.96f);
EffectiveOpacity: effectiveOpacity,
EffectiveLuminosityOpacity: effectiveLuminosityOpacity,
Style: backdropStyle);
}
}

View File

@@ -2,12 +2,16 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
using Windows.UI;
namespace Microsoft.CmdPal.UI.Services;
/// <summary>
/// Input parameters for theme computation, passed to theme providers.
/// </summary>
internal sealed record ThemeContext
{
public ElementTheme Theme { get; init; }
@@ -21,4 +25,8 @@ internal sealed record ThemeContext
public double BackgroundImageOpacity { get; init; }
public int? ColorIntensity { get; init; }
public BackdropStyle? BackdropStyle { get; init; }
public float BackdropOpacity { get; init; } = 1.0f;
}

View File

@@ -72,10 +72,13 @@ internal sealed partial class ThemeService : IThemeService, IDisposable
}
// provider selection
var intensity = Math.Clamp(_settings.CustomThemeColorIntensity, 0, 100);
IThemeProvider provider = intensity > 0 && _settings.ColorizationMode is ColorizationMode.CustomColor or ColorizationMode.WindowsAccentColor or ColorizationMode.Image
? _colorfulThemeProvider
: _normalThemeProvider;
var themeColorIntensity = Math.Clamp(_settings.CustomThemeColorIntensity, 0, 100);
var imageTintIntensity = Math.Clamp(_settings.BackgroundImageTintIntensity, 0, 100);
var effectiveColorIntensity = _settings.ColorizationMode == ColorizationMode.Image
? imageTintIntensity
: themeColorIntensity;
IThemeProvider provider = UseColorfulProvider(effectiveColorIntensity) ? _colorfulThemeProvider : _normalThemeProvider;
// Calculate values
var tint = _settings.ColorizationMode switch
@@ -96,32 +99,39 @@ internal sealed partial class ThemeService : IThemeService, IDisposable
};
var opacity = Math.Clamp(_settings.BackgroundImageOpacity, 0, 100) / 100.0;
// create context and offload to actual theme provider
// create input and offload to actual theme provider
var context = new ThemeContext
{
Tint = tint,
ColorIntensity = intensity,
ColorIntensity = effectiveColorIntensity,
Theme = effectiveTheme,
BackgroundImageSource = imageSource,
BackgroundImageStretch = stretch,
BackgroundImageOpacity = opacity,
BackdropStyle = _settings.BackdropStyle,
BackdropOpacity = Math.Clamp(_settings.BackdropOpacity, 0, 100) / 100f,
};
var backdrop = provider.GetAcrylicBackdrop(context);
var backdrop = provider.GetBackdropParameters(context);
var blur = _settings.BackgroundImageBlurAmount;
var brightness = _settings.BackgroundImageBrightness;
// Create public snapshot (no provider!)
var hasColorization = effectiveColorIntensity > 0
&& _settings.ColorizationMode is ColorizationMode.CustomColor or ColorizationMode.WindowsAccentColor or ColorizationMode.Image;
var snapshot = new ThemeSnapshot
{
Tint = tint,
TintIntensity = intensity / 100f,
TintIntensity = effectiveColorIntensity / 100f,
Theme = effectiveTheme,
BackgroundImageSource = imageSource,
BackgroundImageStretch = stretch,
BackgroundImageOpacity = opacity,
BackdropParameters = backdrop,
BackdropOpacity = context.BackdropOpacity,
BlurAmount = blur,
BackgroundBrightness = brightness / 100f,
HasColorization = hasColorization,
};
// Bundle with provider for internal use
@@ -138,6 +148,12 @@ internal sealed partial class ThemeService : IThemeService, IDisposable
ThemeChanged?.Invoke(this, new ThemeChangedEventArgs());
}
private bool UseColorfulProvider(int effectiveColorIntensity)
{
return _settings.ColorizationMode == ColorizationMode.Image
|| (effectiveColorIntensity > 0 && _settings.ColorizationMode is ColorizationMode.CustomColor or ColorizationMode.WindowsAccentColor);
}
private static BitmapImage? LoadImageSafe(string? path)
{
if (string.IsNullOrWhiteSpace(path))
@@ -195,13 +211,15 @@ internal sealed partial class ThemeService : IThemeService, IDisposable
{
Tint = Colors.Transparent,
Theme = ElementTheme.Light,
BackdropParameters = new AcrylicBackdropParameters(Colors.Black, Colors.Black, 0.5f, 0.5f),
BackdropParameters = new BackdropParameters(Colors.Black, Colors.Black, EffectiveOpacity: 0.5f, EffectiveLuminosityOpacity: 0.5f),
BackdropOpacity = 1.0f,
BackgroundImageOpacity = 1,
BackgroundImageSource = null,
BackgroundImageStretch = Stretch.Fill,
BlurAmount = 0,
TintIntensity = 1.0f,
BackgroundBrightness = 0,
HasColorization = false,
},
Provider = _normalThemeProvider,
};