/
githubmirror
/
PowerToys
Обзор
Документация
Войти
/
githubmirror
/
PowerToys
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/AppearanceSettingsViewModel.cs
534 строки
19 KB
Clint Rutkas
Fix SA1623: Fix property documentation summaries (#46717)
01 апр 2026, 19:46
Не верифицирован
01 апр 2026, 19:46
565094a
Код
Авторство
О чём код?
// 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 System.Collections.ObjectModel; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CommunityToolkit.WinUI; using Microsoft.CmdPal.UI.ViewModels.Services; using Microsoft.UI; using Microsoft.UI.Dispatching; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Media; using Windows.UI; using Windows.UI.ViewManagement; namespace Microsoft.CmdPal.UI.ViewModels; public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDisposable { internal static readonly Color DefaultTintColor = Color.FromArgb(255, 0, 120, 212); internal static readonly ObservableCollection<Color> WindowsColorSwatches = [ // row 0 Color.FromArgb(255, 255, 185, 0), // #ffb900 Color.FromArgb(255, 255, 140, 0), // #ff8c00 Color.FromArgb(255, 247, 99, 12), // #f7630c Color.FromArgb(255, 202, 80, 16), // #ca5010 Color.FromArgb(255, 218, 59, 1), // #da3b01 Color.FromArgb(255, 239, 105, 80), // #ef6950 // row 1 Color.FromArgb(255, 209, 52, 56), // #d13438 Color.FromArgb(255, 255, 67, 67), // #ff4343 Color.FromArgb(255, 231, 72, 86), // #e74856 Color.FromArgb(255, 232, 17, 35), // #e81123 Color.FromArgb(255, 234, 0, 94), // #ea005e Color.FromArgb(255, 195, 0, 82), // #c30052 // row 2 Color.FromArgb(255, 227, 0, 140), // #e3008c Color.FromArgb(255, 191, 0, 119), // #bf0077 Color.FromArgb(255, 194, 57, 179), // #c239b3 Color.FromArgb(255, 154, 0, 137), // #9a0089 Color.FromArgb(255, 0, 120, 212), // #0078d4 Color.FromArgb(255, 0, 99, 177), // #0063b1 // row 3 Color.FromArgb(255, 142, 140, 216), // #8e8cd8 Color.FromArgb(255, 107, 105, 214), // #6b69d6 Color.FromArgb(255, 135, 100, 184), // #8764b8 Color.FromArgb(255, 116, 77, 169), // #744da9 Color.FromArgb(255, 177, 70, 194), // #b146c2 Color.FromArgb(255, 136, 23, 152), // #881798 // row 4 Color.FromArgb(255, 0, 153, 188), // #0099bc Color.FromArgb(255, 45, 125, 154), // #2d7d9a Color.FromArgb(255, 0, 183, 195), // #00b7c3 Color.FromArgb(255, 3, 131, 135), // #038387 Color.FromArgb(255, 0, 178, 148), // #00b294 Color.FromArgb(255, 1, 133, 116), // #018574 // row 5 Color.FromArgb(255, 0, 204, 106), // #00cc6a Color.FromArgb(255, 16, 137, 62), // #10893e Color.FromArgb(255, 122, 117, 116), // #7a7574 Color.FromArgb(255, 93, 90, 88), // #5d5a58 Color.FromArgb(255, 104, 118, 138), // #68768a Color.FromArgb(255, 81, 92, 107), // #515c6b // row 6 Color.FromArgb(255, 86, 124, 115), // #567c73 Color.FromArgb(255, 72, 104, 96), // #486860 Color.FromArgb(255, 73, 130, 5), // #498205 Color.FromArgb(255, 16, 124, 16), // #107c10 Color.FromArgb(255, 118, 118, 118), // #767676 Color.FromArgb(255, 76, 74, 72), // #4c4a48 // row 7 Color.FromArgb(255, 105, 121, 126), // #69797e Color.FromArgb(255, 74, 84, 89), // #4a5459 Color.FromArgb(255, 100, 124, 100), // #647c64 Color.FromArgb(255, 82, 94, 84), // #525e54 Color.FromArgb(255, 132, 117, 69), // #847545 Color.FromArgb(255, 126, 115, 95), // #7e735f ]; private readonly ISettingsService _settingsService; private readonly UISettings _uiSettings; private readonly IThemeService _themeService; private readonly DispatcherQueueTimer _saveTimer = DispatcherQueue.GetForCurrentThread().CreateTimer(); private readonly DispatcherQueue _uiDispatcher = DispatcherQueue.GetForCurrentThread(); private ElementTheme? _elementThemeOverride; private Color _currentSystemAccentColor; public ObservableCollection<Color> Swatches => WindowsColorSwatches; public int ThemeIndex { get => (int)_settingsService.Settings.Theme; set => Theme = (UserTheme)value; } public UserTheme Theme { get => _settingsService.Settings.Theme; set { if (_settingsService.Settings.Theme != value) { _settingsService.UpdateSettings(s => s with { Theme = value }); OnPropertyChanged(); OnPropertyChanged(nameof(ThemeIndex)); DebouncedReapply(); } } } public ColorizationMode ColorizationMode { get => _settingsService.Settings.ColorizationMode; set { if (_settingsService.Settings.ColorizationMode != value) { _settingsService.UpdateSettings(s => s with { ColorizationMode = value }); OnPropertyChanged(); OnPropertyChanged(nameof(ColorizationModeIndex)); OnPropertyChanged(nameof(IsCustomTintVisible)); OnPropertyChanged(nameof(IsColorIntensityVisible)); OnPropertyChanged(nameof(IsImageTintIntensityVisible)); OnPropertyChanged(nameof(EffectiveTintIntensity)); OnPropertyChanged(nameof(IsBackgroundControlsVisible)); OnPropertyChanged(nameof(IsNoBackgroundVisible)); OnPropertyChanged(nameof(IsAccentColorControlsVisible)); OnPropertyChanged(nameof(IsResetButtonVisible)); if (value == ColorizationMode.WindowsAccentColor) { ThemeColor = _currentSystemAccentColor; } IsColorizationDetailsExpanded = value != ColorizationMode.None; DebouncedReapply(); } } } public int ColorizationModeIndex { get => (int)_settingsService.Settings.ColorizationMode; set => ColorizationMode = (ColorizationMode)value; } public Color ThemeColor { get => _settingsService.Settings.CustomThemeColor; set { if (_settingsService.Settings.CustomThemeColor != value) { _settingsService.UpdateSettings(s => s with { CustomThemeColor = value }); OnPropertyChanged(); if (ColorIntensity == 0) { ColorIntensity = 100; } DebouncedReapply(); } } } public int ColorIntensity { get => _settingsService.Settings.CustomThemeColorIntensity; set { _settingsService.UpdateSettings(s => s with { CustomThemeColorIntensity = value }); OnPropertyChanged(); OnPropertyChanged(nameof(EffectiveTintIntensity)); DebouncedReapply(); } } public int BackgroundImageTintIntensity { get => _settingsService.Settings.BackgroundImageTintIntensity; set { _settingsService.UpdateSettings(s => s with { BackgroundImageTintIntensity = value }); OnPropertyChanged(); OnPropertyChanged(nameof(EffectiveTintIntensity)); DebouncedReapply(); } } public string BackgroundImagePath { get => _settingsService.Settings.BackgroundImagePath ?? string.Empty; set { if (_settingsService.Settings.BackgroundImagePath != value) { _settingsService.UpdateSettings(s => s with { BackgroundImagePath = value }); OnPropertyChanged(); if (BackgroundImageOpacity == 0) { BackgroundImageOpacity = 100; } DebouncedReapply(); } } } public int BackgroundImageOpacity { get => _settingsService.Settings.BackgroundImageOpacity; set { if (_settingsService.Settings.BackgroundImageOpacity != value) { _settingsService.UpdateSettings(s => s with { BackgroundImageOpacity = value }); OnPropertyChanged(); DebouncedReapply(); } } } public int BackgroundImageBrightness { get => _settingsService.Settings.BackgroundImageBrightness; set { if (_settingsService.Settings.BackgroundImageBrightness != value) { _settingsService.UpdateSettings(s => s with { BackgroundImageBrightness = value }); OnPropertyChanged(); DebouncedReapply(); } } } public int BackgroundImageBlurAmount { get => _settingsService.Settings.BackgroundImageBlurAmount; set { if (_settingsService.Settings.BackgroundImageBlurAmount != value) { _settingsService.UpdateSettings(s => s with { BackgroundImageBlurAmount = value }); OnPropertyChanged(); DebouncedReapply(); } } } public BackgroundImageFit BackgroundImageFit { get => _settingsService.Settings.BackgroundImageFit; set { if (_settingsService.Settings.BackgroundImageFit != value) { _settingsService.UpdateSettings(s => s with { BackgroundImageFit = value }); OnPropertyChanged(); OnPropertyChanged(nameof(BackgroundImageFitIndex)); DebouncedReapply(); } } } public int BackgroundImageFitIndex { // Naming between UI facing string and enum is a bit confusing, but the enum fields // are based on XAML Stretch enum values. So I'm choosing to keep the confusion here, close // to the UI. // - BackgroundImageFit.Fill corresponds to "Stretch" // - BackgroundImageFit.UniformToFill corresponds to "Fill" get => BackgroundImageFit switch { BackgroundImageFit.Fill => 1, _ => 0, }; set => BackgroundImageFit = value switch { 1 => BackgroundImageFit.Fill, _ => BackgroundImageFit.UniformToFill, }; } public int BackdropOpacity { get => _settingsService.Settings.BackdropOpacity; set { if (_settingsService.Settings.BackdropOpacity != value) { _settingsService.UpdateSettings(s => s with { BackdropOpacity = value }); OnPropertyChanged(); OnPropertyChanged(nameof(EffectiveBackdropStyle)); OnPropertyChanged(nameof(EffectiveImageOpacity)); DebouncedReapply(); } } } public int BackdropStyleIndex { get => (int)_settingsService.Settings.BackdropStyle; set { var newStyle = (BackdropStyle)value; if (_settingsService.Settings.BackdropStyle != newStyle) { _settingsService.UpdateSettings(s => s with { BackdropStyle = newStyle }); OnPropertyChanged(); OnPropertyChanged(nameof(IsBackdropOpacityVisible)); OnPropertyChanged(nameof(IsMicaBackdropDescriptionVisible)); OnPropertyChanged(nameof(IsBackgroundSettingsEnabled)); OnPropertyChanged(nameof(IsBackgroundNotAvailableVisible)); if (!IsBackgroundSettingsEnabled) { IsColorizationDetailsExpanded = false; } DebouncedReapply(); } } } /// <summary> /// Gets a value indicating whether the backdrop opacity slider should be visible. /// </summary> public bool IsBackdropOpacityVisible => BackdropStyles.Get(_settingsService.Settings.BackdropStyle).SupportsOpacity; /// <summary> /// Gets a value indicating whether the backdrop description (for styles without options) should be visible. /// </summary> public bool IsMicaBackdropDescriptionVisible => !BackdropStyles.Get(_settingsService.Settings.BackdropStyle).SupportsOpacity; /// <summary> /// Gets a value indicating whether background/colorization settings are available. /// </summary> public bool IsBackgroundSettingsEnabled => BackdropStyles.Get(_settingsService.Settings.BackdropStyle).SupportsColorization; /// <summary> /// Gets a value indicating whether the "not available" message should be shown (inverse of IsBackgroundSettingsEnabled). /// </summary> public bool IsBackgroundNotAvailableVisible => !BackdropStyles.Get(_settingsService.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 (_settingsService.Settings.BackdropStyle != BackdropStyle.Acrylic || _settingsService.Settings.BackdropOpacity < 100) { return _settingsService.Settings.BackdropStyle; } return null; } } public double EffectiveImageOpacity => EffectiveBackdropStyle is not null ? (BackgroundImageOpacity / 100f) * Math.Sqrt(_settingsService.Settings.BackdropOpacity / 100.0) : (BackgroundImageOpacity / 100f); [ObservableProperty] public partial bool IsColorizationDetailsExpanded { get; set; } public bool IsCustomTintVisible => _settingsService.Settings.ColorizationMode is ColorizationMode.CustomColor or ColorizationMode.Image; public bool IsColorIntensityVisible => _settingsService.Settings.ColorizationMode is ColorizationMode.CustomColor or ColorizationMode.WindowsAccentColor; public bool IsImageTintIntensityVisible => _settingsService.Settings.ColorizationMode is ColorizationMode.Image; /// <summary> /// Gets the effective tint intensity for the preview, based on the current colorization mode. /// </summary> public int EffectiveTintIntensity => _settingsService.Settings.ColorizationMode is ColorizationMode.Image ? _settingsService.Settings.BackgroundImageTintIntensity : _settingsService.Settings.CustomThemeColorIntensity; public bool IsBackgroundControlsVisible => _settingsService.Settings.ColorizationMode is ColorizationMode.Image; public bool IsNoBackgroundVisible => _settingsService.Settings.ColorizationMode is ColorizationMode.None; public bool IsAccentColorControlsVisible => _settingsService.Settings.ColorizationMode is ColorizationMode.WindowsAccentColor; public bool IsResetButtonVisible => _settingsService.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 Color EffectiveThemeColor => !BackdropStyles.Get(_settingsService.Settings.BackdropStyle).SupportsColorization ? Colors.Transparent : ColorizationMode switch { 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). public int EffectiveBackgroundImageBlurAmount => (int)Math.Round(BackgroundImageBlurAmount / 4f); public double EffectiveBackgroundImageBrightness => BackgroundImageBrightness / 100.0; public ImageSource? EffectiveBackgroundImageSource => !BackdropStyles.Get(_settingsService.Settings.BackdropStyle).SupportsBackgroundImage ? null : ColorizationMode is ColorizationMode.Image && !string.IsNullOrWhiteSpace(BackgroundImagePath) && Uri.TryCreate(BackgroundImagePath, UriKind.RelativeOrAbsolute, out var uri) ? new Microsoft.UI.Xaml.Media.Imaging.BitmapImage(uri) : null; public AppearanceSettingsViewModel(IThemeService themeService, ISettingsService settingsService) { _themeService = themeService; _themeService.ThemeChanged += ThemeServiceOnThemeChanged; _settingsService = settingsService; _uiSettings = new UISettings(); _uiSettings.ColorValuesChanged += UiSettingsOnColorValuesChanged; UpdateAccentColor(_uiSettings); Reapply(); IsColorizationDetailsExpanded = _settingsService.Settings.ColorizationMode != ColorizationMode.None && IsBackgroundSettingsEnabled; } private void UiSettingsOnColorValuesChanged(UISettings sender, object args) => _uiDispatcher.TryEnqueue(() => UpdateAccentColor(sender)); private void UpdateAccentColor(UISettings sender) { _currentSystemAccentColor = sender.GetColorValue(UIColorType.Accent); if (ColorizationMode == ColorizationMode.WindowsAccentColor) { ThemeColor = _currentSystemAccentColor; } } private void ThemeServiceOnThemeChanged(object? sender, ThemeChangedEventArgs e) { _saveTimer.Debounce(Reapply, TimeSpan.FromMilliseconds(200)); } private void DebouncedReapply() { _saveTimer.Debounce(Reapply, TimeSpan.FromMilliseconds(200)); } private void Reapply() { // Theme services recalculates effective color and opacity based on current settings. EffectiveBackdrop = _themeService.Current.BackdropParameters; OnPropertyChanged(nameof(EffectiveBackdrop)); OnPropertyChanged(nameof(EffectiveBackdropStyle)); OnPropertyChanged(nameof(EffectiveImageOpacity)); OnPropertyChanged(nameof(EffectiveBackgroundImageBrightness)); OnPropertyChanged(nameof(EffectiveBackgroundImageSource)); OnPropertyChanged(nameof(EffectiveThemeColor)); OnPropertyChanged(nameof(EffectiveBackgroundImageBlurAmount)); // LOAD BEARING: // We need to cycle through the EffectiveTheme property to force reload of resources. _elementThemeOverride = ElementTheme.Light; OnPropertyChanged(nameof(EffectiveTheme)); _elementThemeOverride = ElementTheme.Dark; OnPropertyChanged(nameof(EffectiveTheme)); _elementThemeOverride = null; OnPropertyChanged(nameof(EffectiveTheme)); } [RelayCommand] private void ResetBackgroundImageProperties() { BackgroundImageBrightness = 0; BackgroundImageBlurAmount = 0; BackgroundImageFit = BackgroundImageFit.UniformToFill; BackgroundImageOpacity = 100; 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() { _uiSettings.ColorValuesChanged -= UiSettingsOnColorValuesChanged; _themeService.ThemeChanged -= ThemeServiceOnThemeChanged; } }