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

@@ -18,6 +18,8 @@ namespace Microsoft.CmdPal.UI.ViewModels;
public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDisposable public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDisposable
{ {
private static readonly Color DefaultTintColor = Color.FromArgb(255, 0, 120, 212);
private static readonly ObservableCollection<Color> WindowsColorSwatches = [ private static readonly ObservableCollection<Color> WindowsColorSwatches = [
// row 0 // row 0
@@ -128,10 +130,13 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
OnPropertyChanged(); OnPropertyChanged();
OnPropertyChanged(nameof(ColorizationModeIndex)); OnPropertyChanged(nameof(ColorizationModeIndex));
OnPropertyChanged(nameof(IsCustomTintVisible)); OnPropertyChanged(nameof(IsCustomTintVisible));
OnPropertyChanged(nameof(IsCustomTintIntensityVisible)); OnPropertyChanged(nameof(IsColorIntensityVisible));
OnPropertyChanged(nameof(IsImageTintIntensityVisible));
OnPropertyChanged(nameof(EffectiveTintIntensity));
OnPropertyChanged(nameof(IsBackgroundControlsVisible)); OnPropertyChanged(nameof(IsBackgroundControlsVisible));
OnPropertyChanged(nameof(IsNoBackgroundVisible)); OnPropertyChanged(nameof(IsNoBackgroundVisible));
OnPropertyChanged(nameof(IsAccentColorControlsVisible)); OnPropertyChanged(nameof(IsAccentColorControlsVisible));
OnPropertyChanged(nameof(IsResetButtonVisible));
if (value == ColorizationMode.WindowsAccentColor) if (value == ColorizationMode.WindowsAccentColor)
{ {
@@ -179,6 +184,19 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
{ {
_settings.CustomThemeColorIntensity = value; _settings.CustomThemeColorIntensity = value;
OnPropertyChanged(); OnPropertyChanged();
OnPropertyChanged(nameof(EffectiveTintIntensity));
Save();
}
}
public int BackgroundImageTintIntensity
{
get => _settings.BackgroundImageTintIntensity;
set
{
_settings.BackgroundImageTintIntensity = value;
OnPropertyChanged();
OnPropertyChanged(nameof(EffectiveTintIntensity));
Save(); Save();
} }
} }
@@ -279,12 +297,108 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
}; };
} }
public int BackdropOpacity
{
get => _settings.BackdropOpacity;
set
{
if (_settings.BackdropOpacity != value)
{
_settings.BackdropOpacity = value;
OnPropertyChanged();
OnPropertyChanged(nameof(EffectiveBackdropStyle));
OnPropertyChanged(nameof(EffectiveImageOpacity));
Save();
}
}
}
public int BackdropStyleIndex
{
get => (int)_settings.BackdropStyle;
set
{
var newStyle = (BackdropStyle)value;
if (_settings.BackdropStyle != newStyle)
{
_settings.BackdropStyle = newStyle;
OnPropertyChanged();
OnPropertyChanged(nameof(IsBackdropOpacityVisible));
OnPropertyChanged(nameof(IsMicaBackdropDescriptionVisible));
OnPropertyChanged(nameof(IsBackgroundSettingsEnabled));
OnPropertyChanged(nameof(IsBackgroundNotAvailableVisible));
if (!IsBackgroundSettingsEnabled)
{
IsColorizationDetailsExpanded = false;
}
Save();
}
}
}
/// <summary>
/// Gets whether the backdrop opacity slider should be visible.
/// </summary>
public bool IsBackdropOpacityVisible =>
BackdropStyles.Get(_settings.BackdropStyle).SupportsOpacity;
/// <summary>
/// Gets whether the backdrop description (for styles without options) should be visible.
/// </summary>
public bool IsMicaBackdropDescriptionVisible =>
!BackdropStyles.Get(_settings.BackdropStyle).SupportsOpacity;
/// <summary>
/// Gets whether background/colorization settings are available.
/// </summary>
public bool IsBackgroundSettingsEnabled =>
BackdropStyles.Get(_settings.BackdropStyle).SupportsColorization;
/// <summary>
/// Gets whether the "not available" message should be shown (inverse of IsBackgroundSettingsEnabled).
/// </summary>
public bool IsBackgroundNotAvailableVisible =>
!BackdropStyles.Get(_settings.BackdropStyle).SupportsColorization;
public BackdropStyle? EffectiveBackdropStyle
{
get
{
// Return style when transparency/blur is visible (not fully opaque Acrylic)
// - Clear/Mica/MicaAlt/AcrylicThin always show their effect
// - Acrylic shows effect only when opacity < 100
if (_settings.BackdropStyle != BackdropStyle.Acrylic || _settings.BackdropOpacity < 100)
{
return _settings.BackdropStyle;
}
return null;
}
}
public double EffectiveImageOpacity =>
EffectiveBackdropStyle is not null
? (BackgroundImageOpacity / 100f) * Math.Sqrt(_settings.BackdropOpacity / 100.0)
: (BackgroundImageOpacity / 100f);
[ObservableProperty] [ObservableProperty]
public partial bool IsColorizationDetailsExpanded { get; set; } public partial bool IsColorizationDetailsExpanded { get; set; }
public bool IsCustomTintVisible => _settings.ColorizationMode is ColorizationMode.CustomColor or ColorizationMode.Image; public bool IsCustomTintVisible => _settings.ColorizationMode is ColorizationMode.CustomColor or ColorizationMode.Image;
public bool IsCustomTintIntensityVisible => _settings.ColorizationMode is ColorizationMode.CustomColor or ColorizationMode.WindowsAccentColor or ColorizationMode.Image; public bool IsColorIntensityVisible => _settings.ColorizationMode is ColorizationMode.CustomColor or ColorizationMode.WindowsAccentColor;
public bool IsImageTintIntensityVisible => _settings.ColorizationMode is ColorizationMode.Image;
/// <summary>
/// Gets the effective tint intensity for the preview, based on the current colorization mode.
/// </summary>
public int EffectiveTintIntensity => _settings.ColorizationMode is ColorizationMode.Image
? _settings.BackgroundImageTintIntensity
: _settings.CustomThemeColorIntensity;
public bool IsBackgroundControlsVisible => _settings.ColorizationMode is ColorizationMode.Image; public bool IsBackgroundControlsVisible => _settings.ColorizationMode is ColorizationMode.Image;
@@ -292,16 +406,21 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
public bool IsAccentColorControlsVisible => _settings.ColorizationMode is ColorizationMode.WindowsAccentColor; public bool IsAccentColorControlsVisible => _settings.ColorizationMode is ColorizationMode.WindowsAccentColor;
public AcrylicBackdropParameters EffectiveBackdrop { get; private set; } = new(Colors.Black, Colors.Black, 0.5f, 0.5f); public bool IsResetButtonVisible => _settings.ColorizationMode is ColorizationMode.Image;
public BackdropParameters EffectiveBackdrop { get; private set; } = new(Colors.Black, Colors.Black, 0.5f, 0.5f);
public ElementTheme EffectiveTheme => _elementThemeOverride ?? _themeService.Current.Theme; public ElementTheme EffectiveTheme => _elementThemeOverride ?? _themeService.Current.Theme;
public Color EffectiveThemeColor => ColorizationMode switch public Color EffectiveThemeColor =>
{ !BackdropStyles.Get(_settings.BackdropStyle).SupportsColorization
ColorizationMode.WindowsAccentColor => _currentSystemAccentColor, ? Colors.Transparent
ColorizationMode.CustomColor or ColorizationMode.Image => ThemeColor, : ColorizationMode switch
_ => Colors.Transparent, {
}; ColorizationMode.WindowsAccentColor => _currentSystemAccentColor,
ColorizationMode.CustomColor or ColorizationMode.Image => ThemeColor,
_ => Colors.Transparent,
};
// Since the blur amount is absolute, we need to scale it down for the preview (which is smaller than full screen). // Since the blur amount is absolute, we need to scale it down for the preview (which is smaller than full screen).
public int EffectiveBackgroundImageBlurAmount => (int)Math.Round(BackgroundImageBlurAmount / 4f); public int EffectiveBackgroundImageBlurAmount => (int)Math.Round(BackgroundImageBlurAmount / 4f);
@@ -309,11 +428,13 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
public double EffectiveBackgroundImageBrightness => BackgroundImageBrightness / 100.0; public double EffectiveBackgroundImageBrightness => BackgroundImageBrightness / 100.0;
public ImageSource? EffectiveBackgroundImageSource => public ImageSource? EffectiveBackgroundImageSource =>
ColorizationMode is ColorizationMode.Image !BackdropStyles.Get(_settings.BackdropStyle).SupportsBackgroundImage
&& !string.IsNullOrWhiteSpace(BackgroundImagePath) ? null
&& Uri.TryCreate(BackgroundImagePath, UriKind.RelativeOrAbsolute, out var uri) : ColorizationMode is ColorizationMode.Image
? new Microsoft.UI.Xaml.Media.Imaging.BitmapImage(uri) && !string.IsNullOrWhiteSpace(BackgroundImagePath)
: null; && Uri.TryCreate(BackgroundImagePath, UriKind.RelativeOrAbsolute, out var uri)
? new Microsoft.UI.Xaml.Media.Imaging.BitmapImage(uri)
: null;
public AppearanceSettingsViewModel(IThemeService themeService, SettingsModel settings) public AppearanceSettingsViewModel(IThemeService themeService, SettingsModel settings)
{ {
@@ -327,7 +448,7 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
Reapply(); Reapply();
IsColorizationDetailsExpanded = _settings.ColorizationMode != ColorizationMode.None; IsColorizationDetailsExpanded = _settings.ColorizationMode != ColorizationMode.None && IsBackgroundSettingsEnabled;
} }
private void UiSettingsOnColorValuesChanged(UISettings sender, object args) => _uiDispatcher.TryEnqueue(() => UpdateAccentColor(sender)); private void UiSettingsOnColorValuesChanged(UISettings sender, object args) => _uiDispatcher.TryEnqueue(() => UpdateAccentColor(sender));
@@ -357,6 +478,8 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
// Theme services recalculates effective color and opacity based on current settings. // Theme services recalculates effective color and opacity based on current settings.
EffectiveBackdrop = _themeService.Current.BackdropParameters; EffectiveBackdrop = _themeService.Current.BackdropParameters;
OnPropertyChanged(nameof(EffectiveBackdrop)); OnPropertyChanged(nameof(EffectiveBackdrop));
OnPropertyChanged(nameof(EffectiveBackdropStyle));
OnPropertyChanged(nameof(EffectiveImageOpacity));
OnPropertyChanged(nameof(EffectiveBackgroundImageBrightness)); OnPropertyChanged(nameof(EffectiveBackgroundImageBrightness));
OnPropertyChanged(nameof(EffectiveBackgroundImageSource)); OnPropertyChanged(nameof(EffectiveBackgroundImageSource));
OnPropertyChanged(nameof(EffectiveThemeColor)); OnPropertyChanged(nameof(EffectiveThemeColor));
@@ -379,7 +502,28 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
BackgroundImageBlurAmount = 0; BackgroundImageBlurAmount = 0;
BackgroundImageFit = BackgroundImageFit.UniformToFill; BackgroundImageFit = BackgroundImageFit.UniformToFill;
BackgroundImageOpacity = 100; BackgroundImageOpacity = 100;
ColorIntensity = 0; BackgroundImageTintIntensity = 0;
}
[RelayCommand]
private void ResetAppearanceSettings()
{
// Reset theme
Theme = UserTheme.Default;
// Reset backdrop settings
BackdropStyleIndex = (int)BackdropStyle.Acrylic;
BackdropOpacity = 100;
// Reset background image settings
BackgroundImagePath = string.Empty;
ResetBackgroundImageProperties();
// Reset colorization
ColorizationMode = ColorizationMode.None;
ThemeColor = DefaultTintColor;
ColorIntensity = 100;
BackgroundImageTintIntensity = 0;
} }
public void Dispose() public void Dispose()

View File

@@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.CmdPal.UI.ViewModels;
/// <summary>
/// Specifies the type of system backdrop controller to use.
/// </summary>
public enum BackdropControllerKind
{
/// <summary>
/// Solid color with alpha transparency (TransparentTintBackdrop).
/// </summary>
Solid,
/// <summary>
/// Desktop Acrylic with default blur (DesktopAcrylicKind.Default).
/// </summary>
Acrylic,
/// <summary>
/// Desktop Acrylic with thinner blur (DesktopAcrylicKind.Thin).
/// </summary>
AcrylicThin,
/// <summary>
/// Mica effect (MicaKind.Base).
/// </summary>
Mica,
/// <summary>
/// Mica alternate/darker variant (MicaKind.BaseAlt).
/// </summary>
MicaAlt,
/// <summary>
/// Custom backdrop implementation.
/// </summary>
Custom,
}

View File

@@ -0,0 +1,36 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.CmdPal.UI.ViewModels;
/// <summary>
/// Specifies the visual backdrop style for the window.
/// </summary>
public enum BackdropStyle
{
/// <summary>
/// Standard desktop acrylic with blur effect.
/// </summary>
Acrylic,
/// <summary>
/// Solid color with alpha transparency (no blur).
/// </summary>
Clear,
/// <summary>
/// Mica effect that samples the desktop wallpaper.
/// </summary>
Mica,
/// <summary>
/// Thinner acrylic variant with more transparency.
/// </summary>
AcrylicThin,
/// <summary>
/// Mica alternate variant (darker).
/// </summary>
MicaAlt,
}

View File

@@ -0,0 +1,77 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.CmdPal.UI.ViewModels;
/// <summary>
/// Configuration parameters for a backdrop style.
/// </summary>
public sealed record BackdropStyleConfig
{
/// <summary>
/// Gets the type of system backdrop controller to use.
/// </summary>
public required BackdropControllerKind ControllerKind { get; init; }
/// <summary>
/// Gets the base tint opacity before user adjustments.
/// </summary>
public required float BaseTintOpacity { get; init; }
/// <summary>
/// Gets the base luminosity opacity before user adjustments.
/// </summary>
public required float BaseLuminosityOpacity { get; init; }
/// <summary>
/// Gets the brush type to use for preview approximation.
/// </summary>
public required PreviewBrushKind PreviewBrush { get; init; }
/// <summary>
/// Gets the fixed opacity for styles that don't support user adjustment (e.g., Mica).
/// When <see cref="SupportsOpacity"/> is false, this value is used as the effective opacity.
/// </summary>
public float FixedOpacity { get; init; }
/// <summary>
/// Gets whether this backdrop style supports custom colorization (tint colors).
/// </summary>
public bool SupportsColorization { get; init; } = true;
/// <summary>
/// Gets whether this backdrop style supports custom background images.
/// </summary>
public bool SupportsBackgroundImage { get; init; } = true;
/// <summary>
/// Gets whether this backdrop style supports opacity adjustment.
/// </summary>
public bool SupportsOpacity { get; init; } = true;
/// <summary>
/// Computes the effective tint opacity based on this style's configuration.
/// </summary>
/// <param name="userOpacity">User's backdrop opacity setting (0-1 normalized).</param>
/// <param name="baseTintOpacityOverride">Optional override for base tint opacity (used by colorful theme).</param>
/// <returns>The effective opacity to apply.</returns>
public float ComputeEffectiveOpacity(float userOpacity, float? baseTintOpacityOverride = null)
{
// For styles that don't support opacity (Mica), use FixedOpacity
if (!SupportsOpacity && FixedOpacity > 0)
{
return FixedOpacity;
}
// For Solid: only user opacity matters (controls alpha of solid color)
if (ControllerKind == BackdropControllerKind.Solid)
{
return userOpacity;
}
// For blur effects: multiply base opacity with user opacity
var baseTint = baseTintOpacityOverride ?? BaseTintOpacity;
return baseTint * userOpacity;
}
}

View File

@@ -0,0 +1,65 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.CmdPal.UI.ViewModels;
/// <summary>
/// Central registry of backdrop style configurations.
/// </summary>
public static class BackdropStyles
{
private static readonly Dictionary<BackdropStyle, BackdropStyleConfig> Configs = new()
{
[BackdropStyle.Acrylic] = new()
{
ControllerKind = BackdropControllerKind.Acrylic,
BaseTintOpacity = 0.5f,
BaseLuminosityOpacity = 0.9f,
PreviewBrush = PreviewBrushKind.Acrylic,
},
[BackdropStyle.AcrylicThin] = new()
{
ControllerKind = BackdropControllerKind.AcrylicThin,
BaseTintOpacity = 0.0f,
BaseLuminosityOpacity = 0.85f,
PreviewBrush = PreviewBrushKind.Acrylic,
},
[BackdropStyle.Mica] = new()
{
ControllerKind = BackdropControllerKind.Mica,
BaseTintOpacity = 0.0f,
BaseLuminosityOpacity = 1.0f,
PreviewBrush = PreviewBrushKind.Solid,
FixedOpacity = 0.96f,
SupportsOpacity = false,
},
[BackdropStyle.MicaAlt] = new()
{
ControllerKind = BackdropControllerKind.MicaAlt,
BaseTintOpacity = 0.0f,
BaseLuminosityOpacity = 1.0f,
PreviewBrush = PreviewBrushKind.Solid,
FixedOpacity = 0.98f,
SupportsOpacity = false,
},
[BackdropStyle.Clear] = new()
{
ControllerKind = BackdropControllerKind.Solid,
BaseTintOpacity = 1.0f,
BaseLuminosityOpacity = 1.0f,
PreviewBrush = PreviewBrushKind.Solid,
},
};
/// <summary>
/// Gets the configuration for the specified backdrop style.
/// </summary>
public static BackdropStyleConfig Get(BackdropStyle style) =>
Configs.TryGetValue(style, out var config) ? config : Configs[BackdropStyle.Acrylic];
/// <summary>
/// Gets all registered backdrop styles.
/// </summary>
public static IEnumerable<BackdropStyle> All => Configs.Keys;
}

View File

@@ -22,6 +22,7 @@ public partial class MainWindowViewModel : ObservableObject, IDisposable
public partial Stretch BackgroundImageStretch { get; private set; } = Stretch.Fill; public partial Stretch BackgroundImageStretch { get; private set; } = Stretch.Fill;
[ObservableProperty] [ObservableProperty]
[NotifyPropertyChangedFor(nameof(EffectiveImageOpacity))]
public partial double BackgroundImageOpacity { get; private set; } public partial double BackgroundImageOpacity { get; private set; }
[ObservableProperty] [ObservableProperty]
@@ -39,6 +40,30 @@ public partial class MainWindowViewModel : ObservableObject, IDisposable
[ObservableProperty] [ObservableProperty]
public partial bool ShowBackgroundImage { get; private set; } public partial bool ShowBackgroundImage { get; private set; }
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(EffectiveBackdropStyle))]
[NotifyPropertyChangedFor(nameof(EffectiveImageOpacity))]
public partial BackdropStyle BackdropStyle { get; private set; }
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(EffectiveBackdropStyle))]
[NotifyPropertyChangedFor(nameof(EffectiveImageOpacity))]
public partial float BackdropOpacity { get; private set; } = 1.0f;
// Returns null when no transparency needed (BlurImageControl uses this to decide source type)
public BackdropStyle? EffectiveBackdropStyle =>
BackdropStyle == BackdropStyle.Clear ||
BackdropStyle == BackdropStyle.Mica ||
BackdropOpacity < 1.0f
? BackdropStyle
: null;
// When transparency is enabled, use square root curve so image stays visible longer as backdrop fades
public double EffectiveImageOpacity =>
EffectiveBackdropStyle is not null
? BackgroundImageOpacity * Math.Sqrt(BackdropOpacity)
: BackgroundImageOpacity;
public MainWindowViewModel(IThemeService themeService) public MainWindowViewModel(IThemeService themeService)
{ {
_themeService = themeService; _themeService = themeService;
@@ -58,6 +83,9 @@ public partial class MainWindowViewModel : ObservableObject, IDisposable
BackgroundImageTintIntensity = _themeService.Current.TintIntensity; BackgroundImageTintIntensity = _themeService.Current.TintIntensity;
BackgroundImageBlurAmount = _themeService.Current.BlurAmount; BackgroundImageBlurAmount = _themeService.Current.BlurAmount;
BackdropStyle = _themeService.Current.BackdropParameters.Style;
BackdropOpacity = _themeService.Current.BackdropOpacity;
ShowBackgroundImage = BackgroundImageSource != null; ShowBackgroundImage = BackgroundImageSource != null;
}); });
} }

View File

@@ -0,0 +1,21 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.CmdPal.UI.ViewModels;
/// <summary>
/// Specifies the brush type to use for backdrop preview approximation.
/// </summary>
public enum PreviewBrushKind
{
/// <summary>
/// SolidColorBrush with computed alpha.
/// </summary>
Solid,
/// <summary>
/// AcrylicBrush with blur effect.
/// </summary>
Acrylic,
}

View File

@@ -1,9 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Windows.UI;
namespace Microsoft.CmdPal.UI.ViewModels.Services;
public sealed record AcrylicBackdropParameters(Color TintColor, Color FallbackColor, float TintOpacity, float LuminosityOpacity);

View File

@@ -0,0 +1,29 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Windows.UI;
namespace Microsoft.CmdPal.UI.ViewModels.Services;
/// <summary>
/// Parameters for configuring the window backdrop appearance.
/// </summary>
/// <param name="TintColor">The tint color applied to the backdrop.</param>
/// <param name="FallbackColor">The fallback color when backdrop effects are unavailable.</param>
/// <param name="EffectiveOpacity">
/// The effective opacity for the backdrop, pre-computed by the theme provider.
/// For Acrylic style: TintOpacity * BackdropOpacity.
/// For Clear style: BackdropOpacity (controls the solid color alpha).
/// </param>
/// <param name="EffectiveLuminosityOpacity">
/// The effective luminosity opacity for Acrylic backdrop, pre-computed by the theme provider.
/// Computed as LuminosityOpacity * BackdropOpacity.
/// </param>
/// <param name="Style">The backdrop style (Acrylic or Clear).</param>
public sealed record BackdropParameters(
Color TintColor,
Color FallbackColor,
float EffectiveOpacity,
float EffectiveLuminosityOpacity,
BackdropStyle Style = BackdropStyle.Acrylic);

View File

@@ -51,12 +51,23 @@ public sealed class ThemeSnapshot
public required double BackgroundImageOpacity { get; init; } public required double BackgroundImageOpacity { get; init; }
/// <summary> /// <summary>
/// Gets the effective acrylic backdrop parameters based on current settings and theme. /// Gets the effective backdrop parameters based on current settings and theme.
/// </summary> /// </summary>
/// <returns>The resolved <c>AcrylicBackdropParameters</c> to apply.</returns> /// <returns>The resolved <c>BackdropParameters</c> to apply.</returns>
public required AcrylicBackdropParameters BackdropParameters { get; init; } public required BackdropParameters BackdropParameters { get; init; }
/// <summary>
/// Gets the raw backdrop opacity setting (0-1 range).
/// Used for determining if transparency is enabled and for image opacity calculations.
/// </summary>
public required float BackdropOpacity { get; init; }
public required int BlurAmount { get; init; } public required int BlurAmount { get; init; }
public required float BackgroundBrightness { get; init; } public required float BackgroundBrightness { get; init; }
/// <summary>
/// Gets whether colorization is active (accent color, custom color, or image mode).
/// </summary>
public required bool HasColorization { get; init; }
} }

View File

@@ -74,6 +74,8 @@ public partial class SettingsModel : ObservableObject
public int CustomThemeColorIntensity { get; set; } = 100; public int CustomThemeColorIntensity { get; set; } = 100;
public int BackgroundImageTintIntensity { get; set; }
public int BackgroundImageOpacity { get; set; } = 20; public int BackgroundImageOpacity { get; set; } = 20;
public int BackgroundImageBlurAmount { get; set; } public int BackgroundImageBlurAmount { get; set; }
@@ -84,6 +86,10 @@ public partial class SettingsModel : ObservableObject
public string? BackgroundImagePath { get; set; } public string? BackgroundImagePath { get; set; }
public BackdropStyle BackdropStyle { get; set; }
public int BackdropOpacity { get; set; } = 100;
// END SETTINGS // END SETTINGS
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////

View File

@@ -288,7 +288,6 @@ internal sealed partial class BlurImageControl : Control
_effectBrush?.Dispose(); _effectBrush?.Dispose();
_effectBrush = effectFactory.CreateBrush(); _effectBrush = effectFactory.CreateBrush();
// Set initial source
if (ImageSource is not null) if (ImageSource is not null)
{ {
_imageBrush ??= _compositor.CreateSurfaceBrush(); _imageBrush ??= _compositor.CreateSurfaceBrush();

View File

@@ -16,24 +16,38 @@
CornerRadius="8" CornerRadius="8"
Translation="0,0,8"> Translation="0,0,8">
<Grid> <Grid>
<!-- Clear style: SolidColorBrush with computed alpha (window backdrop) -->
<Border <Border
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
BorderBrush="{ThemeResource SurfaceStrokeColorDefaultBrush}" BorderBrush="{ThemeResource SurfaceStrokeColorDefaultBrush}"
BorderThickness="1" BorderThickness="1"
Visibility="{x:Bind h:BindTransformers.NegateVisibility(ShowBackgroundImage), Mode=OneWay}"> Visibility="{x:Bind ClearVisibility, Mode=OneWay}">
<Border.Background>
<SolidColorBrush Color="{x:Bind EffectiveClearColor, Mode=OneWay}" />
</Border.Background>
</Border>
<!-- Acrylic/Mica style: AcrylicBrush with effective opacity (window backdrop) -->
<Border
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
BorderBrush="{ThemeResource SurfaceStrokeColorDefaultBrush}"
BorderThickness="1"
Visibility="{x:Bind AcrylicVisibility, Mode=OneWay}">
<Border.Background> <Border.Background>
<AcrylicBrush <AcrylicBrush
FallbackColor="{x:Bind PreviewBackgroundColor, Mode=OneWay}" FallbackColor="{x:Bind PreviewBackgroundColor, Mode=OneWay}"
TintColor="{x:Bind PreviewBackgroundColor, Mode=OneWay}" TintColor="{x:Bind PreviewBackgroundColor, Mode=OneWay}"
TintOpacity="{x:Bind PreviewBackgroundOpacity, Mode=OneWay}" /> TintOpacity="{x:Bind PreviewEffectiveOpacity, Mode=OneWay}" />
</Border.Background> </Border.Background>
</Border> </Border>
<!-- Background image (inside window, on top of backdrop) -->
<local:BlurImageControl <local:BlurImageControl
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
BlurAmount="{x:Bind PreviewBackgroundImageBlurAmount, Mode=OneWay}" BlurAmount="{x:Bind PreviewBackgroundImageBlurAmount, Mode=OneWay}"
ImageBrightness="{x:Bind PreviewBackgroundImageBrightness, Mode=OneWay}" ImageBrightness="{x:Bind PreviewBackgroundImageBrightness, Mode=OneWay}"
ImageOpacity="{x:Bind PreviewBackgroundImageOpacity, Mode=OneWay}"
ImageSource="{x:Bind PreviewBackgroundImageSource, Mode=OneWay}" ImageSource="{x:Bind PreviewBackgroundImageSource, Mode=OneWay}"
ImageStretch="{x:Bind ToStretch(PreviewBackgroundImageFit), Mode=OneWay}" ImageStretch="{x:Bind ToStretch(PreviewBackgroundImageFit), Mode=OneWay}"
IsHitTestVisible="False" IsHitTestVisible="False"

View File

@@ -12,13 +12,11 @@ namespace Microsoft.CmdPal.UI.Controls;
public sealed partial class CommandPalettePreview : UserControl public sealed partial class CommandPalettePreview : UserControl
{ {
public static readonly DependencyProperty PreviewBackgroundOpacityProperty = DependencyProperty.Register(nameof(PreviewBackgroundOpacity), typeof(double), typeof(CommandPalettePreview), new PropertyMetadata(0d)); public static readonly DependencyProperty PreviewBackgroundColorProperty = DependencyProperty.Register(nameof(PreviewBackgroundColor), typeof(Color), typeof(CommandPalettePreview), new PropertyMetadata(default(Color), OnBackdropPropertyChanged));
public static readonly DependencyProperty PreviewBackgroundColorProperty = DependencyProperty.Register(nameof(PreviewBackgroundColor), typeof(Color), typeof(CommandPalettePreview), new PropertyMetadata(default(Color))); public static readonly DependencyProperty PreviewBackgroundImageSourceProperty = DependencyProperty.Register(nameof(PreviewBackgroundImageSource), typeof(ImageSource), typeof(CommandPalettePreview), new PropertyMetadata(null, OnBackgroundImageSourceChanged));
public static readonly DependencyProperty PreviewBackgroundImageSourceProperty = DependencyProperty.Register(nameof(PreviewBackgroundImageSource), typeof(ImageSource), typeof(CommandPalettePreview), new PropertyMetadata(null, PropertyChangedCallback)); public static readonly DependencyProperty PreviewBackgroundImageOpacityProperty = DependencyProperty.Register(nameof(PreviewBackgroundImageOpacity), typeof(double), typeof(CommandPalettePreview), new PropertyMetadata(1.0));
public static readonly DependencyProperty PreviewBackgroundImageOpacityProperty = DependencyProperty.Register(nameof(PreviewBackgroundImageOpacity), typeof(int), typeof(CommandPalettePreview), new PropertyMetadata(0));
public static readonly DependencyProperty PreviewBackgroundImageFitProperty = DependencyProperty.Register(nameof(PreviewBackgroundImageFit), typeof(BackgroundImageFit), typeof(CommandPalettePreview), new PropertyMetadata(default(BackgroundImageFit))); public static readonly DependencyProperty PreviewBackgroundImageFitProperty = DependencyProperty.Register(nameof(PreviewBackgroundImageFit), typeof(BackgroundImageFit), typeof(CommandPalettePreview), new PropertyMetadata(default(BackgroundImageFit)));
@@ -30,7 +28,18 @@ public sealed partial class CommandPalettePreview : UserControl
public static readonly DependencyProperty PreviewBackgroundImageTintIntensityProperty = DependencyProperty.Register(nameof(PreviewBackgroundImageTintIntensity), typeof(int), typeof(CommandPalettePreview), new PropertyMetadata(0)); public static readonly DependencyProperty PreviewBackgroundImageTintIntensityProperty = DependencyProperty.Register(nameof(PreviewBackgroundImageTintIntensity), typeof(int), typeof(CommandPalettePreview), new PropertyMetadata(0));
public static readonly DependencyProperty ShowBackgroundImageProperty = DependencyProperty.Register(nameof(ShowBackgroundImage), typeof(Visibility), typeof(CommandPalettePreview), new PropertyMetadata(Visibility.Collapsed)); public static readonly DependencyProperty ShowBackgroundImageProperty = DependencyProperty.Register(nameof(ShowBackgroundImage), typeof(Visibility), typeof(CommandPalettePreview), new PropertyMetadata(Visibility.Collapsed, OnVisibilityPropertyChanged));
public static readonly DependencyProperty PreviewBackdropStyleProperty = DependencyProperty.Register(nameof(PreviewBackdropStyle), typeof(BackdropStyle?), typeof(CommandPalettePreview), new PropertyMetadata(null, OnVisibilityPropertyChanged));
public static readonly DependencyProperty PreviewEffectiveOpacityProperty = DependencyProperty.Register(nameof(PreviewEffectiveOpacity), typeof(double), typeof(CommandPalettePreview), new PropertyMetadata(1.0, OnBackdropPropertyChanged));
// Computed read-only dependency properties
public static readonly DependencyProperty EffectiveClearColorProperty = DependencyProperty.Register(nameof(EffectiveClearColor), typeof(Color), typeof(CommandPalettePreview), new PropertyMetadata(default(Color)));
public static readonly DependencyProperty AcrylicVisibilityProperty = DependencyProperty.Register(nameof(AcrylicVisibility), typeof(Visibility), typeof(CommandPalettePreview), new PropertyMetadata(Visibility.Visible));
public static readonly DependencyProperty ClearVisibilityProperty = DependencyProperty.Register(nameof(ClearVisibility), typeof(Visibility), typeof(CommandPalettePreview), new PropertyMetadata(Visibility.Collapsed));
public BackgroundImageFit PreviewBackgroundImageFit public BackgroundImageFit PreviewBackgroundImageFit
{ {
@@ -38,12 +47,6 @@ public sealed partial class CommandPalettePreview : UserControl
set { SetValue(PreviewBackgroundImageFitProperty, value); } set { SetValue(PreviewBackgroundImageFitProperty, value); }
} }
public double PreviewBackgroundOpacity
{
get { return (double)GetValue(PreviewBackgroundOpacityProperty); }
set { SetValue(PreviewBackgroundOpacityProperty, value); }
}
public Color PreviewBackgroundColor public Color PreviewBackgroundColor
{ {
get { return (Color)GetValue(PreviewBackgroundColorProperty); } get { return (Color)GetValue(PreviewBackgroundColorProperty); }
@@ -56,10 +59,10 @@ public sealed partial class CommandPalettePreview : UserControl
set { SetValue(PreviewBackgroundImageSourceProperty, value); } set { SetValue(PreviewBackgroundImageSourceProperty, value); }
} }
public int PreviewBackgroundImageOpacity public double PreviewBackgroundImageOpacity
{ {
get { return (int)GetValue(PreviewBackgroundImageOpacityProperty); } get => (double)GetValue(PreviewBackgroundImageOpacityProperty);
set { SetValue(PreviewBackgroundImageOpacityProperty, value); } set => SetValue(PreviewBackgroundImageOpacityProperty, value);
} }
public double PreviewBackgroundImageBrightness public double PreviewBackgroundImageBrightness
@@ -92,12 +95,48 @@ public sealed partial class CommandPalettePreview : UserControl
set => SetValue(ShowBackgroundImageProperty, value); set => SetValue(ShowBackgroundImageProperty, value);
} }
public BackdropStyle? PreviewBackdropStyle
{
get => (BackdropStyle?)GetValue(PreviewBackdropStyleProperty);
set => SetValue(PreviewBackdropStyleProperty, value);
}
/// <summary>
/// Gets or sets the effective opacity for the backdrop, pre-computed by the theme provider.
/// For Acrylic style: used directly as TintOpacity.
/// For Clear style: used to compute the alpha channel of the solid color.
/// </summary>
public double PreviewEffectiveOpacity
{
get => (double)GetValue(PreviewEffectiveOpacityProperty);
set => SetValue(PreviewEffectiveOpacityProperty, value);
}
// Computed read-only properties
public Color EffectiveClearColor
{
get => (Color)GetValue(EffectiveClearColorProperty);
private set => SetValue(EffectiveClearColorProperty, value);
}
public Visibility AcrylicVisibility
{
get => (Visibility)GetValue(AcrylicVisibilityProperty);
private set => SetValue(AcrylicVisibilityProperty, value);
}
public Visibility ClearVisibility
{
get => (Visibility)GetValue(ClearVisibilityProperty);
private set => SetValue(ClearVisibilityProperty, value);
}
public CommandPalettePreview() public CommandPalettePreview()
{ {
InitializeComponent(); InitializeComponent();
} }
private static void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) private static void OnBackgroundImageSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{ {
if (d is not CommandPalettePreview preview) if (d is not CommandPalettePreview preview)
{ {
@@ -107,7 +146,46 @@ public sealed partial class CommandPalettePreview : UserControl
preview.ShowBackgroundImage = e.NewValue is ImageSource ? Visibility.Visible : Visibility.Collapsed; preview.ShowBackgroundImage = e.NewValue is ImageSource ? Visibility.Visible : Visibility.Collapsed;
} }
private double ToOpacity(int value) => value / 100.0; private static void OnBackdropPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is not CommandPalettePreview preview)
{
return;
}
preview.UpdateComputedClearColor();
}
private static void OnVisibilityPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is not CommandPalettePreview preview)
{
return;
}
preview.UpdateComputedVisibilityProperties();
preview.UpdateComputedClearColor();
}
private void UpdateComputedClearColor()
{
EffectiveClearColor = Color.FromArgb(
(byte)(PreviewEffectiveOpacity * 255),
PreviewBackgroundColor.R,
PreviewBackgroundColor.G,
PreviewBackgroundColor.B);
}
private void UpdateComputedVisibilityProperties()
{
var config = BackdropStyles.Get(PreviewBackdropStyle ?? BackdropStyle.Acrylic);
// Show backdrop effect based on style (on top of any background image)
AcrylicVisibility = config.PreviewBrush == PreviewBrushKind.Acrylic
? Visibility.Visible : Visibility.Collapsed;
ClearVisibility = config.PreviewBrush == PreviewBrushKind.Solid
? Visibility.Visible : Visibility.Collapsed;
}
private double ToTintIntensity(int value) => value / 100.0; private double ToTintIntensity(int value) => value / 100.0;

View File

@@ -22,7 +22,7 @@
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
BlurAmount="{x:Bind ViewModel.BackgroundImageBlurAmount, Mode=OneWay}" BlurAmount="{x:Bind ViewModel.BackgroundImageBlurAmount, Mode=OneWay}"
ImageBrightness="{x:Bind ViewModel.BackgroundImageBrightness, Mode=OneWay}" ImageBrightness="{x:Bind ViewModel.BackgroundImageBrightness, Mode=OneWay}"
ImageOpacity="{x:Bind ViewModel.BackgroundImageOpacity, Mode=OneWay}" ImageOpacity="{x:Bind ViewModel.EffectiveImageOpacity, Mode=OneWay}"
ImageSource="{x:Bind ViewModel.BackgroundImageSource, Mode=OneWay}" ImageSource="{x:Bind ViewModel.BackgroundImageSource, Mode=OneWay}"
ImageStretch="{x:Bind ViewModel.BackgroundImageStretch, Mode=OneWay}" ImageStretch="{x:Bind ViewModel.BackgroundImageStretch, Mode=OneWay}"
IsHitTestVisible="False" IsHitTestVisible="False"

View File

@@ -31,6 +31,7 @@ using Windows.ApplicationModel.Activation;
using Windows.Foundation; using Windows.Foundation;
using Windows.Graphics; using Windows.Graphics;
using Windows.System; using Windows.System;
using Windows.UI;
using Windows.Win32; using Windows.Win32;
using Windows.Win32.Foundation; using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Dwm; using Windows.Win32.Graphics.Dwm;
@@ -80,7 +81,9 @@ public sealed partial class MainWindow : WindowEx,
private int _sessionErrorCount; private int _sessionErrorCount;
private DesktopAcrylicController? _acrylicController; private DesktopAcrylicController? _acrylicController;
private MicaController? _micaController;
private SystemBackdropConfiguration? _configurationSource; private SystemBackdropConfiguration? _configurationSource;
private bool _isUpdatingBackdrop;
private TimeSpan _autoGoHomeInterval = Timeout.InfiniteTimeSpan; private TimeSpan _autoGoHomeInterval = Timeout.InfiniteTimeSpan;
private WindowPosition _currentWindowPosition = new(); private WindowPosition _currentWindowPosition = new();
@@ -109,7 +112,7 @@ public sealed partial class MainWindow : WindowEx,
CommandPaletteHost.SetHostHwnd((ulong)_hwnd.Value); CommandPaletteHost.SetHostHwnd((ulong)_hwnd.Value);
} }
SetAcrylic(); InitializeBackdropSupport();
_hiddenOwnerBehavior.ShowInTaskbar(this, Debugger.IsAttached); _hiddenOwnerBehavior.ShowInTaskbar(this, Debugger.IsAttached);
@@ -158,7 +161,7 @@ public sealed partial class MainWindow : WindowEx,
App.Current.Services.GetService<SettingsModel>()!.SettingsChanged += SettingsChangedHandler; App.Current.Services.GetService<SettingsModel>()!.SettingsChanged += SettingsChangedHandler;
// Make sure that we update the acrylic theme when the OS theme changes // Make sure that we update the acrylic theme when the OS theme changes
RootElement.ActualThemeChanged += (s, e) => DispatcherQueue.TryEnqueue(UpdateAcrylic); RootElement.ActualThemeChanged += (s, e) => DispatcherQueue.TryEnqueue(UpdateBackdrop);
// Hardcoding event name to avoid bringing in the PowerToys.interop dependency. Event name must match CMDPAL_SHOW_EVENT from shared_constants.h // Hardcoding event name to avoid bringing in the PowerToys.interop dependency. Event name must match CMDPAL_SHOW_EVENT from shared_constants.h
NativeEventWaiter.WaitForEventLoop("Local\\PowerToysCmdPal-ShowEvent-62336fcd-8611-4023-9b30-091a6af4cc5a", () => NativeEventWaiter.WaitForEventLoop("Local\\PowerToysCmdPal-ShowEvent-62336fcd-8611-4023-9b30-091a6af4cc5a", () =>
@@ -185,7 +188,7 @@ public sealed partial class MainWindow : WindowEx,
private void ThemeServiceOnThemeChanged(object? sender, ThemeChangedEventArgs e) private void ThemeServiceOnThemeChanged(object? sender, ThemeChangedEventArgs e)
{ {
UpdateAcrylic(); UpdateBackdrop();
} }
private static void LocalKeyboardListener_OnKeyPressed(object? sender, LocalKeyboardListenerKeyPressedEventArgs e) private static void LocalKeyboardListener_OnKeyPressed(object? sender, LocalKeyboardListenerKeyPressedEventArgs e)
@@ -280,48 +283,170 @@ public sealed partial class MainWindow : WindowEx,
_autoGoHomeTimer.Interval = _autoGoHomeInterval; _autoGoHomeTimer.Interval = _autoGoHomeInterval;
} }
private void SetAcrylic() private void InitializeBackdropSupport()
{ {
if (DesktopAcrylicController.IsSupported()) if (DesktopAcrylicController.IsSupported() || MicaController.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()
{ {
// Prevent re-entrance when backdrop changes trigger ActualThemeChanged
if (_isUpdatingBackdrop)
{
return;
}
_isUpdatingBackdrop = true;
var backdrop = _themeService.Current.BackdropParameters;
var isImageMode = ViewModel.ShowBackgroundImage;
var config = BackdropStyles.Get(backdrop.Style);
try try
{ {
if (_acrylicController != null) switch (config.ControllerKind)
{ {
_acrylicController.RemoveAllSystemBackdropTargets(); case BackdropControllerKind.Solid:
_acrylicController.Dispose(); CleanupBackdropControllers();
var tintColor = Color.FromArgb(
(byte)(backdrop.EffectiveOpacity * 255),
backdrop.TintColor.R,
backdrop.TintColor.G,
backdrop.TintColor.B);
SetupTransparentBackdrop(tintColor);
break;
case BackdropControllerKind.Mica:
case BackdropControllerKind.MicaAlt:
SetupMica(backdrop, isImageMode, config.ControllerKind);
break;
case BackdropControllerKind.Acrylic:
case BackdropControllerKind.AcrylicThin:
default:
SetupDesktopAcrylic(backdrop, isImageMode, config.ControllerKind);
break;
} }
var backdrop = _themeService.Current.BackdropParameters;
_acrylicController = new DesktopAcrylicController
{
TintColor = backdrop.TintColor,
TintOpacity = backdrop.TintOpacity,
FallbackColor = backdrop.FallbackColor,
LuminosityOpacity = backdrop.LuminosityOpacity,
};
// Enable the system backdrop.
// Note: Be sure to have "using WinRT;" to support the Window.As<...>() call.
_acrylicController.AddSystemBackdropTarget(this.As<ICompositionSupportsSystemBackdrop>());
_acrylicController.SetSystemBackdropConfiguration(_configurationSource);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogError("Failed to update backdrop", ex); Logger.LogError("Failed to update backdrop", ex);
} }
finally
{
_isUpdatingBackdrop = false;
}
}
private void SetupTransparentBackdrop(Color tintColor)
{
if (SystemBackdrop is TransparentTintBackdrop existingBackdrop)
{
existingBackdrop.TintColor = tintColor;
}
else
{
SystemBackdrop = new TransparentTintBackdrop { TintColor = tintColor };
}
}
private void CleanupBackdropControllers()
{
if (_acrylicController is not null)
{
_acrylicController.RemoveAllSystemBackdropTargets();
_acrylicController.Dispose();
_acrylicController = null;
}
if (_micaController is not null)
{
_micaController.RemoveAllSystemBackdropTargets();
_micaController.Dispose();
_micaController = null;
}
}
private void SetupDesktopAcrylic(BackdropParameters backdrop, bool isImageMode, BackdropControllerKind kind)
{
CleanupBackdropControllers();
// Fall back to solid color if acrylic not supported
if (_configurationSource is null || !DesktopAcrylicController.IsSupported())
{
SetupTransparentBackdrop(backdrop.FallbackColor);
return;
}
// DesktopAcrylicController and SystemBackdrop can't be active simultaneously
SystemBackdrop = null;
// Image mode: no tint here, BlurImageControl handles it (avoids double-tinting)
var effectiveTintOpacity = isImageMode
? 0.0f
: backdrop.EffectiveOpacity;
_acrylicController = new DesktopAcrylicController
{
Kind = kind == BackdropControllerKind.AcrylicThin
? DesktopAcrylicKind.Thin
: DesktopAcrylicKind.Default,
TintColor = backdrop.TintColor,
TintOpacity = effectiveTintOpacity,
FallbackColor = backdrop.FallbackColor,
LuminosityOpacity = backdrop.EffectiveLuminosityOpacity,
};
// Requires "using WinRT;" for Window.As<>()
_acrylicController.AddSystemBackdropTarget(this.As<ICompositionSupportsSystemBackdrop>());
_acrylicController.SetSystemBackdropConfiguration(_configurationSource);
}
private void SetupMica(BackdropParameters backdrop, bool isImageMode, BackdropControllerKind kind)
{
CleanupBackdropControllers();
// Fall back to solid color if Mica not supported
if (_configurationSource is null || !MicaController.IsSupported())
{
SetupTransparentBackdrop(backdrop.FallbackColor);
return;
}
// MicaController and SystemBackdrop can't be active simultaneously
SystemBackdrop = null;
_configurationSource.Theme = _themeService.Current.Theme == ElementTheme.Dark
? SystemBackdropTheme.Dark
: SystemBackdropTheme.Light;
var hasColorization = _themeService.Current.HasColorization || isImageMode;
_micaController = new MicaController
{
Kind = kind == BackdropControllerKind.MicaAlt
? MicaKind.BaseAlt
: MicaKind.Base,
};
// Only set tint properties when colorization is active
// Otherwise let system handle light/dark theme defaults automatically
if (hasColorization)
{
// Image mode: no tint here, BlurImageControl handles it (avoids double-tinting)
_micaController.TintColor = backdrop.TintColor;
_micaController.TintOpacity = isImageMode ? 0.0f : backdrop.EffectiveOpacity;
_micaController.FallbackColor = backdrop.FallbackColor;
_micaController.LuminosityOpacity = backdrop.EffectiveLuminosityOpacity;
}
_micaController.AddSystemBackdropTarget(this.As<ICompositionSupportsSystemBackdrop>());
_micaController.SetSystemBackdropConfiguration(_configurationSource);
} }
private void ShowHwnd(IntPtr hwndValue, MonitorBehavior target) private void ShowHwnd(IntPtr hwndValue, MonitorBehavior target)
@@ -637,12 +762,8 @@ public sealed partial class MainWindow : WindowEx,
private void DisposeAcrylic() private void DisposeAcrylic()
{ {
if (_acrylicController is not null) CleanupBackdropControllers();
{ _configurationSource = null!;
_acrylicController.Dispose();
_acrylicController = null!;
_configurationSource = null!;
}
} }
// Updates our window s.t. the top of the window is draggable. // Updates our window s.t. the top of the window is draggable.

View File

@@ -4,6 +4,7 @@
using CommunityToolkit.WinUI.Helpers; using CommunityToolkit.WinUI.Helpers;
using Microsoft.CmdPal.UI.Helpers; using Microsoft.CmdPal.UI.Helpers;
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.CmdPal.UI.ViewModels.Services; using Microsoft.CmdPal.UI.ViewModels.Services;
using Microsoft.UI.Xaml; using Microsoft.UI.Xaml;
using Windows.UI; using Windows.UI;
@@ -34,7 +35,7 @@ internal sealed class ColorfulThemeProvider : IThemeProvider
_uiSettings = uiSettings; _uiSettings = uiSettings;
} }
public AcrylicBackdropParameters GetAcrylicBackdrop(ThemeContext context) public BackdropParameters GetBackdropParameters(ThemeContext context)
{ {
var isLight = context.Theme == ElementTheme.Light || var isLight = context.Theme == ElementTheme.Light ||
(context.Theme == ElementTheme.Default && (context.Theme == ElementTheme.Default &&
@@ -53,7 +54,26 @@ internal sealed class ColorfulThemeProvider : IThemeProvider
var colorIntensity = isLight ? 0.6f * colorIntensityUser : colorIntensityUser; var colorIntensity = isLight ? 0.6f * colorIntensityUser : colorIntensityUser;
var effectiveBgColor = ColorBlender.Blend(baseColor, blended, colorIntensity); 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 private static class ColorBlender

View File

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

View File

@@ -2,6 +2,7 @@
// The Microsoft Corporation licenses this file to you under the MIT license. // The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.CmdPal.UI.ViewModels.Services; using Microsoft.CmdPal.UI.ViewModels.Services;
using Microsoft.UI.Xaml; using Microsoft.UI.Xaml;
using Windows.UI; using Windows.UI;
@@ -28,16 +29,28 @@ internal sealed class NormalThemeProvider : IThemeProvider
public string ResourcePath => "ms-appx:///Styles/Theme.Normal.xaml"; 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 || var isLight = context.Theme == ElementTheme.Light ||
(context.Theme == ElementTheme.Default && (context.Theme == ElementTheme.Default &&
_uiSettings.GetColorValue(UIColorType.Background).R > 128); _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, TintColor: isLight ? LightBaseColor : DarkBaseColor,
FallbackColor: isLight ? LightBaseColor : DarkBaseColor, FallbackColor: isLight ? LightBaseColor : DarkBaseColor,
TintOpacity: 0.5f, EffectiveOpacity: effectiveOpacity,
LuminosityOpacity: isLight ? 0.9f : 0.96f); EffectiveLuminosityOpacity: effectiveLuminosityOpacity,
Style: backdropStyle);
} }
} }

View File

@@ -2,12 +2,16 @@
// The Microsoft Corporation licenses this file to you under the MIT license. // The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.UI.Xaml; using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Media;
using Windows.UI; using Windows.UI;
namespace Microsoft.CmdPal.UI.Services; namespace Microsoft.CmdPal.UI.Services;
/// <summary>
/// Input parameters for theme computation, passed to theme providers.
/// </summary>
internal sealed record ThemeContext internal sealed record ThemeContext
{ {
public ElementTheme Theme { get; init; } public ElementTheme Theme { get; init; }
@@ -21,4 +25,8 @@ internal sealed record ThemeContext
public double BackgroundImageOpacity { get; init; } public double BackgroundImageOpacity { get; init; }
public int? ColorIntensity { 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 // provider selection
var intensity = Math.Clamp(_settings.CustomThemeColorIntensity, 0, 100); var themeColorIntensity = Math.Clamp(_settings.CustomThemeColorIntensity, 0, 100);
IThemeProvider provider = intensity > 0 && _settings.ColorizationMode is ColorizationMode.CustomColor or ColorizationMode.WindowsAccentColor or ColorizationMode.Image var imageTintIntensity = Math.Clamp(_settings.BackgroundImageTintIntensity, 0, 100);
? _colorfulThemeProvider var effectiveColorIntensity = _settings.ColorizationMode == ColorizationMode.Image
: _normalThemeProvider; ? imageTintIntensity
: themeColorIntensity;
IThemeProvider provider = UseColorfulProvider(effectiveColorIntensity) ? _colorfulThemeProvider : _normalThemeProvider;
// Calculate values // Calculate values
var tint = _settings.ColorizationMode switch 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; 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 var context = new ThemeContext
{ {
Tint = tint, Tint = tint,
ColorIntensity = intensity, ColorIntensity = effectiveColorIntensity,
Theme = effectiveTheme, Theme = effectiveTheme,
BackgroundImageSource = imageSource, BackgroundImageSource = imageSource,
BackgroundImageStretch = stretch, BackgroundImageStretch = stretch,
BackgroundImageOpacity = opacity, 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 blur = _settings.BackgroundImageBlurAmount;
var brightness = _settings.BackgroundImageBrightness; var brightness = _settings.BackgroundImageBrightness;
// Create public snapshot (no provider!) // Create public snapshot (no provider!)
var hasColorization = effectiveColorIntensity > 0
&& _settings.ColorizationMode is ColorizationMode.CustomColor or ColorizationMode.WindowsAccentColor or ColorizationMode.Image;
var snapshot = new ThemeSnapshot var snapshot = new ThemeSnapshot
{ {
Tint = tint, Tint = tint,
TintIntensity = intensity / 100f, TintIntensity = effectiveColorIntensity / 100f,
Theme = effectiveTheme, Theme = effectiveTheme,
BackgroundImageSource = imageSource, BackgroundImageSource = imageSource,
BackgroundImageStretch = stretch, BackgroundImageStretch = stretch,
BackgroundImageOpacity = opacity, BackgroundImageOpacity = opacity,
BackdropParameters = backdrop, BackdropParameters = backdrop,
BackdropOpacity = context.BackdropOpacity,
BlurAmount = blur, BlurAmount = blur,
BackgroundBrightness = brightness / 100f, BackgroundBrightness = brightness / 100f,
HasColorization = hasColorization,
}; };
// Bundle with provider for internal use // Bundle with provider for internal use
@@ -138,6 +148,12 @@ internal sealed partial class ThemeService : IThemeService, IDisposable
ThemeChanged?.Invoke(this, new ThemeChangedEventArgs()); 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) private static BitmapImage? LoadImageSafe(string? path)
{ {
if (string.IsNullOrWhiteSpace(path)) if (string.IsNullOrWhiteSpace(path))
@@ -195,13 +211,15 @@ internal sealed partial class ThemeService : IThemeService, IDisposable
{ {
Tint = Colors.Transparent, Tint = Colors.Transparent,
Theme = ElementTheme.Light, 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, BackgroundImageOpacity = 1,
BackgroundImageSource = null, BackgroundImageSource = null,
BackgroundImageStretch = Stretch.Fill, BackgroundImageStretch = Stretch.Fill,
BlurAmount = 0, BlurAmount = 0,
TintIntensity = 1.0f, TintIntensity = 1.0f,
BackgroundBrightness = 0, BackgroundBrightness = 0,
HasColorization = false,
}, },
Provider = _normalThemeProvider, Provider = _normalThemeProvider,
}; };

View File

@@ -22,18 +22,50 @@
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Spacing="{StaticResource SettingsCardSpacing}"> Spacing="{StaticResource SettingsCardSpacing}">
<ptControls:ScreenPreview Margin="0,0,0,16" HorizontalAlignment="Left"> <StackPanel
<ptControls:CommandPalettePreview Margin="0,0,0,16"
PreviewBackgroundColor="{x:Bind ViewModel.Appearance.EffectiveBackdrop.TintColor, Mode=OneWay}" HorizontalAlignment="Left"
PreviewBackgroundImageBlurAmount="{x:Bind ViewModel.Appearance.EffectiveBackgroundImageBlurAmount, Mode=OneWay}" Orientation="Horizontal"
PreviewBackgroundImageBrightness="{x:Bind ViewModel.Appearance.EffectiveBackgroundImageBrightness, Mode=OneWay}" Spacing="16">
PreviewBackgroundImageFit="{x:Bind ViewModel.Appearance.BackgroundImageFit, Mode=OneWay}" <ptControls:ScreenPreview>
PreviewBackgroundImageSource="{x:Bind ViewModel.Appearance.EffectiveBackgroundImageSource, Mode=OneWay}" <ptControls:CommandPalettePreview
PreviewBackgroundImageTint="{x:Bind ViewModel.Appearance.EffectiveThemeColor, Mode=OneWay}" PreviewBackdropStyle="{x:Bind ViewModel.Appearance.EffectiveBackdropStyle, Mode=OneWay}"
PreviewBackgroundImageTintIntensity="{x:Bind ViewModel.Appearance.ColorIntensity, Mode=OneWay}" PreviewBackgroundColor="{x:Bind ViewModel.Appearance.EffectiveBackdrop.TintColor, Mode=OneWay}"
PreviewBackgroundOpacity="{x:Bind ViewModel.Appearance.EffectiveBackdrop.TintOpacity, Mode=OneWay}" PreviewBackgroundImageBlurAmount="{x:Bind ViewModel.Appearance.EffectiveBackgroundImageBlurAmount, Mode=OneWay}"
RequestedTheme="{x:Bind ViewModel.Appearance.EffectiveTheme, Mode=OneWay}" /> PreviewBackgroundImageBrightness="{x:Bind ViewModel.Appearance.EffectiveBackgroundImageBrightness, Mode=OneWay}"
</ptControls:ScreenPreview> PreviewBackgroundImageFit="{x:Bind ViewModel.Appearance.BackgroundImageFit, Mode=OneWay}"
PreviewBackgroundImageOpacity="{x:Bind ViewModel.Appearance.EffectiveImageOpacity, Mode=OneWay}"
PreviewBackgroundImageSource="{x:Bind ViewModel.Appearance.EffectiveBackgroundImageSource, Mode=OneWay}"
PreviewBackgroundImageTint="{x:Bind ViewModel.Appearance.EffectiveThemeColor, Mode=OneWay}"
PreviewBackgroundImageTintIntensity="{x:Bind ViewModel.Appearance.EffectiveTintIntensity, Mode=OneWay}"
PreviewEffectiveOpacity="{x:Bind ViewModel.Appearance.EffectiveBackdrop.EffectiveOpacity, Mode=OneWay}"
RequestedTheme="{x:Bind ViewModel.Appearance.EffectiveTheme, Mode=OneWay}" />
</ptControls:ScreenPreview>
<StackPanel VerticalAlignment="Bottom" Spacing="8">
<Button
x:Uid="Settings_AppearancePage_OpenCommandPaletteButton"
MinWidth="200"
HorizontalContentAlignment="Left"
Click="OpenCommandPalette_Click"
Style="{StaticResource SubtleButtonStyle}">
<StackPanel Orientation="Horizontal" Spacing="8">
<FontIcon FontSize="16" Glyph="&#xE8A7;" />
<TextBlock x:Uid="Settings_AppearancePage_OpenCommandPaletteButton_Text" />
</StackPanel>
</Button>
<Button
x:Uid="Settings_AppearancePage_ResetAppearanceButton"
MinWidth="200"
HorizontalContentAlignment="Left"
Command="{x:Bind ViewModel.Appearance.ResetAppearanceSettingsCommand}"
Style="{StaticResource SubtleButtonStyle}">
<StackPanel Orientation="Horizontal" Spacing="8">
<FontIcon FontSize="16" Glyph="&#xE72C;" />
<TextBlock x:Uid="Settings_AppearancePage_ResetAppearanceButton_Text" />
</StackPanel>
</Button>
</StackPanel>
</StackPanel>
<controls:SettingsCard x:Uid="Settings_GeneralPage_AppTheme_SettingsCard" HeaderIcon="{ui:FontIcon Glyph=&#xE793;}"> <controls:SettingsCard x:Uid="Settings_GeneralPage_AppTheme_SettingsCard" HeaderIcon="{ui:FontIcon Glyph=&#xE793;}">
<ComboBox MinWidth="{StaticResource SettingActionControlMinWidth}" SelectedIndex="{x:Bind ViewModel.Appearance.ThemeIndex, Mode=TwoWay}"> <ComboBox MinWidth="{StaticResource SettingActionControlMinWidth}" SelectedIndex="{x:Bind ViewModel.Appearance.ThemeIndex, Mode=TwoWay}">
@@ -62,19 +94,67 @@
</ComboBox> </ComboBox>
</controls:SettingsCard> </controls:SettingsCard>
<controls:SettingsExpander
x:Uid="Settings_GeneralPage_BackdropStyle_SettingsCard"
HeaderIcon="{ui:FontIcon Glyph=&#xF5EF;}"
IsExpanded="{x:Bind ViewModel.Appearance.IsBackdropOpacityVisible, Mode=OneWay}">
<ComboBox MinWidth="{StaticResource SettingActionControlMinWidth}" SelectedIndex="{x:Bind ViewModel.Appearance.BackdropStyleIndex, Mode=TwoWay}">
<ComboBoxItem x:Uid="Settings_GeneralPage_BackdropStyle_Acrylic" />
<ComboBoxItem x:Uid="Settings_GeneralPage_BackdropStyle_Transparent" />
<ComboBoxItem x:Uid="Settings_GeneralPage_BackdropStyle_Mica" />
<!-- Hidden: preview not working well, kept to preserve index mapping -->
<ComboBoxItem x:Uid="Settings_GeneralPage_BackdropStyle_AcrylicThin" Visibility="Collapsed" />
<ComboBoxItem x:Uid="Settings_GeneralPage_BackdropStyle_MicaAlt" />
</ComboBox>
<controls:SettingsExpander.Items>
<!-- Mica description (no opacity control) -->
<controls:SettingsCard
x:Uid="Settings_GeneralPage_MicaBackdrop_SettingsCard"
HorizontalContentAlignment="Stretch"
ContentAlignment="Vertical"
Visibility="{x:Bind ViewModel.Appearance.IsMicaBackdropDescriptionVisible, Mode=OneWay}">
<TextBlock
x:Uid="Settings_GeneralPage_MicaBackdrop_DescriptionTextBlock"
Margin="24"
HorizontalAlignment="Stretch"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
HorizontalTextAlignment="Center"
TextAlignment="Center"
TextWrapping="WrapWholeWords" />
</controls:SettingsCard>
<!-- Opacity slider (for non-Mica styles) -->
<controls:SettingsCard x:Uid="Settings_GeneralPage_BackdropOpacity_SettingsCard" Visibility="{x:Bind ViewModel.Appearance.IsBackdropOpacityVisible, Mode=OneWay}">
<Slider
MinWidth="{StaticResource SettingActionControlMinWidth}"
Maximum="100"
Minimum="0"
StepFrequency="1"
Value="{x:Bind ViewModel.Appearance.BackdropOpacity, Mode=TwoWay}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
<controls:SettingsExpander <controls:SettingsExpander
x:Uid="Settings_GeneralPage_Background_SettingsExpander" x:Uid="Settings_GeneralPage_Background_SettingsExpander"
HeaderIcon="{ui:FontIcon Glyph=&#xE790;}" HeaderIcon="{ui:FontIcon Glyph=&#xE790;}"
IsEnabled="{x:Bind ViewModel.Appearance.IsBackgroundSettingsEnabled, Mode=OneWay}"
IsExpanded="{x:Bind ViewModel.Appearance.IsColorizationDetailsExpanded, Mode=TwoWay}"> IsExpanded="{x:Bind ViewModel.Appearance.IsColorizationDetailsExpanded, Mode=TwoWay}">
<ComboBox <Grid>
x:Uid="Settings_GeneralPage_ColorizationMode" <ComboBox
MinWidth="{StaticResource SettingActionControlMinWidth}" x:Uid="Settings_GeneralPage_ColorizationMode"
SelectedIndex="{x:Bind ViewModel.Appearance.ColorizationModeIndex, Mode=TwoWay}"> MinWidth="{StaticResource SettingActionControlMinWidth}"
<ComboBoxItem x:Uid="Settings_GeneralPage_ColorizationMode_None" /> SelectedIndex="{x:Bind ViewModel.Appearance.ColorizationModeIndex, Mode=TwoWay}"
<ComboBoxItem x:Uid="Settings_GeneralPage_ColorizationMode_WindowsAccent" /> Visibility="{x:Bind ViewModel.Appearance.IsBackgroundSettingsEnabled, Mode=OneWay}">
<ComboBoxItem x:Uid="Settings_GeneralPage_ColorizationMode_CustomColor" /> <ComboBoxItem x:Uid="Settings_GeneralPage_ColorizationMode_None" />
<ComboBoxItem x:Uid="Settings_GeneralPage_ColorizationMode_Image" /> <ComboBoxItem x:Uid="Settings_GeneralPage_ColorizationMode_WindowsAccent" />
</ComboBox> <ComboBoxItem x:Uid="Settings_GeneralPage_ColorizationMode_CustomColor" />
<ComboBoxItem x:Uid="Settings_GeneralPage_ColorizationMode_Image" />
</ComboBox>
<TextBlock
x:Uid="Settings_GeneralPage_Background_NotAvailable"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Visibility="{x:Bind ViewModel.Appearance.IsBackgroundNotAvailableVisible, Mode=OneWay}" />
</Grid>
<controls:SettingsExpander.Items> <controls:SettingsExpander.Items>
<!-- none --> <!-- none -->
<controls:SettingsCard <controls:SettingsCard
@@ -155,7 +235,7 @@
PaletteColors="{x:Bind ViewModel.Appearance.Swatches}" PaletteColors="{x:Bind ViewModel.Appearance.Swatches}"
SelectedColor="{x:Bind ViewModel.Appearance.ThemeColor, Mode=TwoWay}" /> SelectedColor="{x:Bind ViewModel.Appearance.ThemeColor, Mode=TwoWay}" />
</controls:SettingsCard> </controls:SettingsCard>
<controls:SettingsCard x:Uid="Settings_GeneralPage_BackgroundTintIntensity_SettingsCard" Visibility="{x:Bind ViewModel.Appearance.IsCustomTintIntensityVisible, Mode=OneWay}"> <controls:SettingsCard x:Uid="Settings_GeneralPage_BackgroundTintIntensity_SettingsCard" Visibility="{x:Bind ViewModel.Appearance.IsColorIntensityVisible, Mode=OneWay}">
<Slider <Slider
MinWidth="{StaticResource SettingActionControlMinWidth}" MinWidth="{StaticResource SettingActionControlMinWidth}"
Maximum="100" Maximum="100"
@@ -163,9 +243,17 @@
StepFrequency="1" StepFrequency="1"
Value="{x:Bind ViewModel.Appearance.ColorIntensity, Mode=TwoWay}" /> Value="{x:Bind ViewModel.Appearance.ColorIntensity, Mode=TwoWay}" />
</controls:SettingsCard> </controls:SettingsCard>
<controls:SettingsCard x:Uid="Settings_GeneralPage_ImageTintIntensity_SettingsCard" Visibility="{x:Bind ViewModel.Appearance.IsImageTintIntensityVisible, Mode=OneWay}">
<Slider
MinWidth="{StaticResource SettingActionControlMinWidth}"
Maximum="100"
Minimum="0"
StepFrequency="1"
Value="{x:Bind ViewModel.Appearance.BackgroundImageTintIntensity, Mode=TwoWay}" />
</controls:SettingsCard>
<!-- Reset background image properties --> <!-- Reset appearance properties -->
<controls:SettingsCard x:Uid="Settings_GeneralPage_BackgroundImage_ResetProperties_SettingsCard" Visibility="{x:Bind ViewModel.Appearance.IsBackgroundControlsVisible, Mode=OneWay}"> <controls:SettingsCard x:Uid="Settings_GeneralPage_BackgroundImage_ResetProperties_SettingsCard" Visibility="{x:Bind ViewModel.Appearance.IsResetButtonVisible, Mode=OneWay}">
<StackPanel Orientation="Horizontal" Spacing="8"> <StackPanel Orientation="Horizontal" Spacing="8">
<Button x:Uid="Settings_GeneralPage_Background_ResetImagePropertiesButton" Command="{x:Bind ViewModel.Appearance.ResetBackgroundImagePropertiesCommand}" /> <Button x:Uid="Settings_GeneralPage_Background_ResetImagePropertiesButton" Command="{x:Bind ViewModel.Appearance.ResetBackgroundImagePropertiesCommand}" />
</StackPanel> </StackPanel>

View File

@@ -3,8 +3,13 @@
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System.Diagnostics; using System.Diagnostics;
using CommunityToolkit.Mvvm.Messaging;
using ManagedCommon; using ManagedCommon;
using Microsoft.CmdPal.Core.ViewModels.Messages;
using Microsoft.CmdPal.UI.Events;
using Microsoft.CmdPal.UI.Messages;
using Microsoft.CmdPal.UI.ViewModels; using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.CmdPal.UI.ViewModels.Messages;
using Microsoft.CmdPal.UI.ViewModels.Services; using Microsoft.CmdPal.UI.ViewModels.Services;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI; using Microsoft.UI;
@@ -12,6 +17,7 @@ using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Documents; using Microsoft.UI.Xaml.Documents;
using Microsoft.Windows.Storage.Pickers; using Microsoft.Windows.Storage.Pickers;
using Windows.Win32.Foundation;
namespace Microsoft.CmdPal.UI.Settings; namespace Microsoft.CmdPal.UI.Settings;
@@ -86,4 +92,9 @@ public sealed partial class AppearancePage : Page
} }
}); });
} }
private void OpenCommandPalette_Click(object sender, RoutedEventArgs e)
{
WeakReferenceMessenger.Default.Send<HotkeySummonMessage>(new(string.Empty, HWND.Null));
}
} }

View File

@@ -577,6 +577,9 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
<data name="Settings_GeneralPage_BackgroundTintIntensity_SettingsCard.Header" xml:space="preserve"> <data name="Settings_GeneralPage_BackgroundTintIntensity_SettingsCard.Header" xml:space="preserve">
<value>Color intensity</value> <value>Color intensity</value>
</data> </data>
<data name="Settings_GeneralPage_ImageTintIntensity_SettingsCard.Header" xml:space="preserve">
<value>Color intensity</value>
</data>
<data name="OptionalColorPickerButton_UnsetTextBlock.Text" xml:space="preserve"> <data name="OptionalColorPickerButton_UnsetTextBlock.Text" xml:space="preserve">
<value>Choose color</value> <value>Choose color</value>
</data> </data>
@@ -668,7 +671,7 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
<value>Background image</value> <value>Background image</value>
</data> </data>
<data name="Settings_GeneralPage_NoBackground_DescriptionTextBlock.Text" xml:space="preserve"> <data name="Settings_GeneralPage_NoBackground_DescriptionTextBlock.Text" xml:space="preserve">
<value>No settings</value> <value>No additional settings are available.</value>
</data> </data>
<data name="Settings_GeneralPage_Background_SettingsExpander.Header" xml:space="preserve"> <data name="Settings_GeneralPage_Background_SettingsExpander.Header" xml:space="preserve">
<value>Background</value> <value>Background</value>
@@ -676,6 +679,9 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
<data name="Settings_GeneralPage_Background_SettingsExpander.Description" xml:space="preserve"> <data name="Settings_GeneralPage_Background_SettingsExpander.Description" xml:space="preserve">
<value>Choose a custom background color or image</value> <value>Choose a custom background color or image</value>
</data> </data>
<data name="Settings_GeneralPage_Background_NotAvailable.Text" xml:space="preserve">
<value>Not available with Mica</value>
</data>
<data name="Settings_GeneralPage_WindowsAccentColor_SettingsCard.Header" xml:space="preserve"> <data name="Settings_GeneralPage_WindowsAccentColor_SettingsCard.Header" xml:space="preserve">
<value>System accent color</value> <value>System accent color</value>
</data> </data>
@@ -692,7 +698,7 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
<value>Restore defaults</value> <value>Restore defaults</value>
</data> </data>
<data name="Settings_GeneralPage_Background_ResetImagePropertiesButton.Content" xml:space="preserve"> <data name="Settings_GeneralPage_Background_ResetImagePropertiesButton.Content" xml:space="preserve">
<value>Reset</value> <value>Reset image settings</value>
</data> </data>
<data name="Settings_GeneralPage_WindowsAccentColor_SettingsCard_Description1.Text" xml:space="preserve"> <data name="Settings_GeneralPage_WindowsAccentColor_SettingsCard_Description1.Text" xml:space="preserve">
<value>Change the system accent in Windows Settings:</value> <value>Change the system accent in Windows Settings:</value>
@@ -727,4 +733,45 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
<data name="Settings_GeneralPage_VersionNo" xml:space="preserve"> <data name="Settings_GeneralPage_VersionNo" xml:space="preserve">
<value>Version {0}</value> <value>Version {0}</value>
</data> </data>
<data name="Settings_GeneralPage_BackdropOpacity_SettingsCard.Header" xml:space="preserve">
<value>Opacity</value>
</data>
<data name="Settings_GeneralPage_BackdropStyle_SettingsCard.Header" xml:space="preserve">
<value>Material</value>
</data>
<data name="Settings_GeneralPage_BackdropStyle_SettingsCard.Description" xml:space="preserve">
<value>Select the visual material used for the window background</value>
</data>
<data name="Settings_GeneralPage_BackdropStyle_Acrylic.Content" xml:space="preserve">
<value>Acrylic (default)</value>
</data>
<data name="Settings_GeneralPage_BackdropStyle_Transparent.Content" xml:space="preserve">
<value>Transparent</value>
</data>
<data name="Settings_GeneralPage_BackdropStyle_Mica.Content" xml:space="preserve">
<value>Mica</value>
</data>
<data name="Settings_GeneralPage_BackdropStyle_AcrylicThin.Content" xml:space="preserve">
<value>Thin Acrylic</value>
</data>
<data name="Settings_GeneralPage_BackdropStyle_MicaAlt.Content" xml:space="preserve">
<value>Mica Alt</value>
</data>
<data name="Settings_GeneralPage_MicaBackdrop_DescriptionTextBlock.Text" xml:space="preserve">
<value>Mica automatically adapts to your desktop wallpaper. Custom backgrounds and opacity settings are not available for this material.</value>
</data>
<data name="Settings_AppearancePage_OpenCommandPaletteButton.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Open Command Palette</value>
<comment>Button to open the Command Palette window to preview appearance changes</comment>
</data>
<data name="Settings_AppearancePage_OpenCommandPaletteButton_Text.Text" xml:space="preserve">
<value>Open Command Palette</value>
</data>
<data name="Settings_AppearancePage_ResetAppearanceButton.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Reset appearance settings</value>
<comment>Button to reset all appearance settings to their default values</comment>
</data>
<data name="Settings_AppearancePage_ResetAppearanceButton_Text.Text" xml:space="preserve">
<value>Reset to defaults</value>
</data>
</root> </root>