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

@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
Version 2.0 Version 2.0
The primary goals of this format is to allow a simple XML format The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes various data types are done through the TypeConverter classes
associated with the data types. associated with the data types.
Example: Example:
... ado.net/XML headers & schema ... ... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader> <resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader> <resheader name="version">2.0</resheader>
@@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment> <comment>This is a comment</comment>
</data> </data>
There are any number of "resheader" rows that contain simple There are any number of "resheader" rows that contain simple
name/value pairs. name/value pairs.
Each data row contains a name, and value. The row also contains a Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture. text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the Classes that don't support this are serialized and stored with the
mimetype set. mimetype set.
The mimetype is used for serialized objects, and tells the The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly: extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below. read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64 mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding. : and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64 mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding. : and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64 mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter : using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding. : and then encoded with base64 encoding.
--> -->
@@ -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>