mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-03 09:46:54 +02:00
CmdPal: Extract persistence services from SettingsModel and AppStateModel (#46312)
## Summary of the Pull Request Extracts persistence (load/save) logic from `SettingsModel` and `AppStateModel` into dedicated service classes, following the single-responsibility principle. Consumers now interact with `ISettingsService` and `IAppStateService` instead of receiving raw model objects through DI. **New services introduced:** - `IPersistenceService` / `PersistenceService` — generic `Load<T>` / `Save<T>` with AOT-compatible `JsonTypeInfo<T>`, ensures target directory exists before writing - `ISettingsService` / `SettingsService` — loads settings on construction, runs migrations, exposes `Settings` property and `SettingsChanged` event - `IAppStateService` / `AppStateService` — loads state on construction, exposes `State` property and `StateChanged` event **Key changes:** - `SettingsModel` and `AppStateModel` are now pure data models — all file I/O, migration, and directory management removed - Raw `SettingsModel` and `AppStateModel` removed from DI container; consumers receive the appropriate service - `IApplicationInfoService.ConfigDirectory` injected into services for config path resolution (no more hardcoded `Utilities.BaseSettingsPath`) - ~30 consumer files updated across `Microsoft.CmdPal.UI.ViewModels` and `Microsoft.CmdPal.UI` projects - All `#pragma warning disable SA1300` suppressions removed — convenience accessors replaced with direct `_settingsService.Settings` / `_appStateService.State` access - Namespace prefixes (`Services.ISettingsService`) replaced with proper `using` directives ## PR Checklist - [ ] **Communication:** I've discussed this with core contributors already. - [x] **Tests:** Added/updated and all pass - [ ] **Localization:** N/A — no end-user-facing strings changed - [ ] **Dev docs:** N/A — internal refactor, no public API changes - [ ] **New binaries:** N/A — no new binaries introduced ## Detailed Description of the Pull Request / Additional comments ### Architecture Services are registered as singletons in `App.xaml.cs`: ```csharp services.AddSingleton<IPersistenceService, PersistenceService>(); services.AddSingleton<ISettingsService, SettingsService>(); services.AddSingleton<IAppStateService, AppStateService>(); ``` `PersistenceService.Save<T>` writes the serialized model directly to disk, creating the target directory if it doesn't exist. It also does not attempt to merge existing and new settings/state. `SettingsService` runs hotkey migrations on load and raises `SettingsChanged` after saves. `AppStateService` always raises `StateChanged` after saves. ### Files changed (41 files, +1169/−660) | Area | Files | What changed | |------|-------|-------------| | New services | `Services/IPersistenceService.cs`, `PersistenceService.cs`, `ISettingsService.cs`, `SettingsService.cs`, `IAppStateService.cs`, `AppStateService.cs` | New service interfaces and implementations | | Models | `SettingsModel.cs`, `AppStateModel.cs` | Stripped to pure data bags | | DI | `App.xaml.cs` | Service registration, removed raw model DI | | ViewModels | 12 files | Constructor injection of services | | UI | 10 files | Service injection replacing model access | | Settings | `DockSettings.cs` | `Colors.Transparent` replaced with struct literal to avoid WinUI3 COM dependency | | Tests | `PersistenceServiceTests.cs`, `SettingsServiceTests.cs`, `AppStateServiceTests.cs` | 38 unit tests covering all three services | | Config | `.gitignore` | Added `.squad/`, `.github/agents/` exclusions | ## Validation Steps Performed - Built `Microsoft.CmdPal.UI` with MSBuild (x64/Debug) — exit code 0, clean build - Ran 38 unit tests via `vstest.console.exe` — all passing - Verified no remaining `#pragma warning disable SA1300` blocks - Verified no remaining `Services.` namespace prefixes --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -5,20 +5,23 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Services;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
public partial class AliasManager : ObservableObject
|
||||
{
|
||||
private readonly TopLevelCommandManager _topLevelCommandManager;
|
||||
private readonly ISettingsService _settingsService;
|
||||
|
||||
// REMEMBER, CommandAlias.SearchPrefix is what we use as keys
|
||||
private readonly Dictionary<string, CommandAlias> _aliases;
|
||||
|
||||
public AliasManager(TopLevelCommandManager tlcManager, SettingsModel settings)
|
||||
public AliasManager(TopLevelCommandManager tlcManager, ISettingsService settingsService)
|
||||
{
|
||||
_topLevelCommandManager = tlcManager;
|
||||
_aliases = settings.Aliases;
|
||||
_settingsService = settingsService;
|
||||
_aliases = _settingsService.Settings.Aliases;
|
||||
|
||||
if (_aliases.Count == 0)
|
||||
{
|
||||
|
||||
@@ -1,26 +1,14 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// 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.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
public partial class AppStateModel : ObservableObject
|
||||
{
|
||||
[JsonIgnore]
|
||||
public static readonly string FilePath;
|
||||
|
||||
public event TypedEventHandler<AppStateModel, object?>? StateChanged;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// STATE HERE
|
||||
// Make sure that you make the setters public (JsonSerializer.Deserialize will fail silently otherwise)!
|
||||
@@ -31,141 +19,4 @@ public partial class AppStateModel : ObservableObject
|
||||
|
||||
// END SETTINGS
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static AppStateModel()
|
||||
{
|
||||
FilePath = StateJsonPath();
|
||||
}
|
||||
|
||||
public static AppStateModel LoadState()
|
||||
{
|
||||
if (string.IsNullOrEmpty(FilePath))
|
||||
{
|
||||
throw new InvalidOperationException($"You must set a valid {nameof(FilePath)} before calling {nameof(LoadState)}");
|
||||
}
|
||||
|
||||
if (!File.Exists(FilePath))
|
||||
{
|
||||
Debug.WriteLine("The provided settings file does not exist");
|
||||
return new();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Read the JSON content from the file
|
||||
var jsonContent = File.ReadAllText(FilePath);
|
||||
|
||||
var loaded = JsonSerializer.Deserialize<AppStateModel>(jsonContent, JsonSerializationContext.Default.AppStateModel);
|
||||
|
||||
Debug.WriteLine(loaded is not null ? "Loaded settings file" : "Failed to parse");
|
||||
|
||||
return loaded ?? new();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(ex.ToString());
|
||||
}
|
||||
|
||||
return new();
|
||||
}
|
||||
|
||||
public static void SaveState(AppStateModel model)
|
||||
{
|
||||
if (string.IsNullOrEmpty(FilePath))
|
||||
{
|
||||
throw new InvalidOperationException($"You must set a valid {nameof(FilePath)} before calling {nameof(SaveState)}");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Serialize the main dictionary to JSON and save it to the file
|
||||
var settingsJson = JsonSerializer.Serialize(model, JsonSerializationContext.Default.AppStateModel!);
|
||||
|
||||
// validate JSON
|
||||
if (JsonNode.Parse(settingsJson) is not JsonObject newSettings)
|
||||
{
|
||||
Logger.LogError("Failed to parse app state as a JsonObject.");
|
||||
return;
|
||||
}
|
||||
|
||||
// read previous settings
|
||||
if (!TryReadSavedState(out var savedSettings))
|
||||
{
|
||||
savedSettings = new JsonObject();
|
||||
}
|
||||
|
||||
// merge new settings into old ones
|
||||
foreach (var item in newSettings)
|
||||
{
|
||||
savedSettings[item.Key] = item.Value?.DeepClone();
|
||||
}
|
||||
|
||||
var serialized = savedSettings.ToJsonString(JsonSerializationContext.Default.AppStateModel!.Options);
|
||||
File.WriteAllText(FilePath, serialized);
|
||||
|
||||
// TODO: Instead of just raising the event here, we should
|
||||
// have a file change watcher on the settings file, and
|
||||
// reload the settings then
|
||||
model.StateChanged?.Invoke(model, null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Failed to save application state to {FilePath}:", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryReadSavedState([NotNullWhen(true)] out JsonObject? savedSettings)
|
||||
{
|
||||
savedSettings = null;
|
||||
|
||||
// read existing content from the file
|
||||
string oldContent;
|
||||
try
|
||||
{
|
||||
if (File.Exists(FilePath))
|
||||
{
|
||||
oldContent = File.ReadAllText(FilePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
// file doesn't exist (might not have been created yet), so consider this a success
|
||||
// and return empty settings
|
||||
savedSettings = new JsonObject();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning($"Failed to read app state file {FilePath}:\n{ex}");
|
||||
return false;
|
||||
}
|
||||
|
||||
// detect empty file, just for sake of logging
|
||||
if (string.IsNullOrWhiteSpace(oldContent))
|
||||
{
|
||||
Logger.LogInfo($"App state file is empty: {FilePath}");
|
||||
return false;
|
||||
}
|
||||
|
||||
// is it valid JSON?
|
||||
try
|
||||
{
|
||||
savedSettings = JsonNode.Parse(oldContent) as JsonObject;
|
||||
return savedSettings != null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning($"Failed to parse app state from {FilePath}:\n{ex}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal static string StateJsonPath()
|
||||
{
|
||||
var directory = Utilities.BaseSettingsPath("Microsoft.CmdPal");
|
||||
Directory.CreateDirectory(directory);
|
||||
|
||||
// now, the settings is just next to the exe
|
||||
return Path.Combine(directory, "state.json");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// 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.
|
||||
|
||||
@@ -87,7 +87,8 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
Color.FromArgb(255, 126, 115, 95), // #7e735f
|
||||
];
|
||||
|
||||
private readonly SettingsModel _settings;
|
||||
private readonly ISettingsService _settingsService;
|
||||
|
||||
private readonly UISettings _uiSettings;
|
||||
private readonly IThemeService _themeService;
|
||||
private readonly DispatcherQueueTimer _saveTimer = DispatcherQueue.GetForCurrentThread().CreateTimer();
|
||||
@@ -100,18 +101,18 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
|
||||
public int ThemeIndex
|
||||
{
|
||||
get => (int)_settings.Theme;
|
||||
get => (int)_settingsService.Settings.Theme;
|
||||
set => Theme = (UserTheme)value;
|
||||
}
|
||||
|
||||
public UserTheme Theme
|
||||
{
|
||||
get => _settings.Theme;
|
||||
get => _settingsService.Settings.Theme;
|
||||
set
|
||||
{
|
||||
if (_settings.Theme != value)
|
||||
if (_settingsService.Settings.Theme != value)
|
||||
{
|
||||
_settings.Theme = value;
|
||||
_settingsService.Settings.Theme = value;
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(ThemeIndex));
|
||||
Save();
|
||||
@@ -121,12 +122,12 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
|
||||
public ColorizationMode ColorizationMode
|
||||
{
|
||||
get => _settings.ColorizationMode;
|
||||
get => _settingsService.Settings.ColorizationMode;
|
||||
set
|
||||
{
|
||||
if (_settings.ColorizationMode != value)
|
||||
if (_settingsService.Settings.ColorizationMode != value)
|
||||
{
|
||||
_settings.ColorizationMode = value;
|
||||
_settingsService.Settings.ColorizationMode = value;
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(ColorizationModeIndex));
|
||||
OnPropertyChanged(nameof(IsCustomTintVisible));
|
||||
@@ -152,18 +153,18 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
|
||||
public int ColorizationModeIndex
|
||||
{
|
||||
get => (int)_settings.ColorizationMode;
|
||||
get => (int)_settingsService.Settings.ColorizationMode;
|
||||
set => ColorizationMode = (ColorizationMode)value;
|
||||
}
|
||||
|
||||
public Color ThemeColor
|
||||
{
|
||||
get => _settings.CustomThemeColor;
|
||||
get => _settingsService.Settings.CustomThemeColor;
|
||||
set
|
||||
{
|
||||
if (_settings.CustomThemeColor != value)
|
||||
if (_settingsService.Settings.CustomThemeColor != value)
|
||||
{
|
||||
_settings.CustomThemeColor = value;
|
||||
_settingsService.Settings.CustomThemeColor = value;
|
||||
|
||||
OnPropertyChanged();
|
||||
|
||||
@@ -179,10 +180,10 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
|
||||
public int ColorIntensity
|
||||
{
|
||||
get => _settings.CustomThemeColorIntensity;
|
||||
get => _settingsService.Settings.CustomThemeColorIntensity;
|
||||
set
|
||||
{
|
||||
_settings.CustomThemeColorIntensity = value;
|
||||
_settingsService.Settings.CustomThemeColorIntensity = value;
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(EffectiveTintIntensity));
|
||||
Save();
|
||||
@@ -191,10 +192,10 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
|
||||
public int BackgroundImageTintIntensity
|
||||
{
|
||||
get => _settings.BackgroundImageTintIntensity;
|
||||
get => _settingsService.Settings.BackgroundImageTintIntensity;
|
||||
set
|
||||
{
|
||||
_settings.BackgroundImageTintIntensity = value;
|
||||
_settingsService.Settings.BackgroundImageTintIntensity = value;
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(EffectiveTintIntensity));
|
||||
Save();
|
||||
@@ -203,12 +204,12 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
|
||||
public string BackgroundImagePath
|
||||
{
|
||||
get => _settings.BackgroundImagePath ?? string.Empty;
|
||||
get => _settingsService.Settings.BackgroundImagePath ?? string.Empty;
|
||||
set
|
||||
{
|
||||
if (_settings.BackgroundImagePath != value)
|
||||
if (_settingsService.Settings.BackgroundImagePath != value)
|
||||
{
|
||||
_settings.BackgroundImagePath = value;
|
||||
_settingsService.Settings.BackgroundImagePath = value;
|
||||
OnPropertyChanged();
|
||||
|
||||
if (BackgroundImageOpacity == 0)
|
||||
@@ -223,12 +224,12 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
|
||||
public int BackgroundImageOpacity
|
||||
{
|
||||
get => _settings.BackgroundImageOpacity;
|
||||
get => _settingsService.Settings.BackgroundImageOpacity;
|
||||
set
|
||||
{
|
||||
if (_settings.BackgroundImageOpacity != value)
|
||||
if (_settingsService.Settings.BackgroundImageOpacity != value)
|
||||
{
|
||||
_settings.BackgroundImageOpacity = value;
|
||||
_settingsService.Settings.BackgroundImageOpacity = value;
|
||||
OnPropertyChanged();
|
||||
Save();
|
||||
}
|
||||
@@ -237,12 +238,12 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
|
||||
public int BackgroundImageBrightness
|
||||
{
|
||||
get => _settings.BackgroundImageBrightness;
|
||||
get => _settingsService.Settings.BackgroundImageBrightness;
|
||||
set
|
||||
{
|
||||
if (_settings.BackgroundImageBrightness != value)
|
||||
if (_settingsService.Settings.BackgroundImageBrightness != value)
|
||||
{
|
||||
_settings.BackgroundImageBrightness = value;
|
||||
_settingsService.Settings.BackgroundImageBrightness = value;
|
||||
OnPropertyChanged();
|
||||
Save();
|
||||
}
|
||||
@@ -251,12 +252,12 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
|
||||
public int BackgroundImageBlurAmount
|
||||
{
|
||||
get => _settings.BackgroundImageBlurAmount;
|
||||
get => _settingsService.Settings.BackgroundImageBlurAmount;
|
||||
set
|
||||
{
|
||||
if (_settings.BackgroundImageBlurAmount != value)
|
||||
if (_settingsService.Settings.BackgroundImageBlurAmount != value)
|
||||
{
|
||||
_settings.BackgroundImageBlurAmount = value;
|
||||
_settingsService.Settings.BackgroundImageBlurAmount = value;
|
||||
OnPropertyChanged();
|
||||
Save();
|
||||
}
|
||||
@@ -265,12 +266,12 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
|
||||
public BackgroundImageFit BackgroundImageFit
|
||||
{
|
||||
get => _settings.BackgroundImageFit;
|
||||
get => _settingsService.Settings.BackgroundImageFit;
|
||||
set
|
||||
{
|
||||
if (_settings.BackgroundImageFit != value)
|
||||
if (_settingsService.Settings.BackgroundImageFit != value)
|
||||
{
|
||||
_settings.BackgroundImageFit = value;
|
||||
_settingsService.Settings.BackgroundImageFit = value;
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(BackgroundImageFitIndex));
|
||||
Save();
|
||||
@@ -299,12 +300,12 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
|
||||
public int BackdropOpacity
|
||||
{
|
||||
get => _settings.BackdropOpacity;
|
||||
get => _settingsService.Settings.BackdropOpacity;
|
||||
set
|
||||
{
|
||||
if (_settings.BackdropOpacity != value)
|
||||
if (_settingsService.Settings.BackdropOpacity != value)
|
||||
{
|
||||
_settings.BackdropOpacity = value;
|
||||
_settingsService.Settings.BackdropOpacity = value;
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(EffectiveBackdropStyle));
|
||||
OnPropertyChanged(nameof(EffectiveImageOpacity));
|
||||
@@ -315,13 +316,13 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
|
||||
public int BackdropStyleIndex
|
||||
{
|
||||
get => (int)_settings.BackdropStyle;
|
||||
get => (int)_settingsService.Settings.BackdropStyle;
|
||||
set
|
||||
{
|
||||
var newStyle = (BackdropStyle)value;
|
||||
if (_settings.BackdropStyle != newStyle)
|
||||
if (_settingsService.Settings.BackdropStyle != newStyle)
|
||||
{
|
||||
_settings.BackdropStyle = newStyle;
|
||||
_settingsService.Settings.BackdropStyle = newStyle;
|
||||
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(IsBackdropOpacityVisible));
|
||||
@@ -343,25 +344,25 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
/// Gets whether the backdrop opacity slider should be visible.
|
||||
/// </summary>
|
||||
public bool IsBackdropOpacityVisible =>
|
||||
BackdropStyles.Get(_settings.BackdropStyle).SupportsOpacity;
|
||||
BackdropStyles.Get(_settingsService.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;
|
||||
!BackdropStyles.Get(_settingsService.Settings.BackdropStyle).SupportsOpacity;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether background/colorization settings are available.
|
||||
/// </summary>
|
||||
public bool IsBackgroundSettingsEnabled =>
|
||||
BackdropStyles.Get(_settings.BackdropStyle).SupportsColorization;
|
||||
BackdropStyles.Get(_settingsService.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;
|
||||
!BackdropStyles.Get(_settingsService.Settings.BackdropStyle).SupportsColorization;
|
||||
|
||||
public BackdropStyle? EffectiveBackdropStyle
|
||||
{
|
||||
@@ -370,9 +371,9 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
// 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)
|
||||
if (_settingsService.Settings.BackdropStyle != BackdropStyle.Acrylic || _settingsService.Settings.BackdropOpacity < 100)
|
||||
{
|
||||
return _settings.BackdropStyle;
|
||||
return _settingsService.Settings.BackdropStyle;
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -381,39 +382,39 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
|
||||
public double EffectiveImageOpacity =>
|
||||
EffectiveBackdropStyle is not null
|
||||
? (BackgroundImageOpacity / 100f) * Math.Sqrt(_settings.BackdropOpacity / 100.0)
|
||||
? (BackgroundImageOpacity / 100f) * Math.Sqrt(_settingsService.Settings.BackdropOpacity / 100.0)
|
||||
: (BackgroundImageOpacity / 100f);
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool IsColorizationDetailsExpanded { get; set; }
|
||||
|
||||
public bool IsCustomTintVisible => _settings.ColorizationMode is ColorizationMode.CustomColor or ColorizationMode.Image;
|
||||
public bool IsCustomTintVisible => _settingsService.Settings.ColorizationMode is ColorizationMode.CustomColor or ColorizationMode.Image;
|
||||
|
||||
public bool IsColorIntensityVisible => _settings.ColorizationMode is ColorizationMode.CustomColor or ColorizationMode.WindowsAccentColor;
|
||||
public bool IsColorIntensityVisible => _settingsService.Settings.ColorizationMode is ColorizationMode.CustomColor or ColorizationMode.WindowsAccentColor;
|
||||
|
||||
public bool IsImageTintIntensityVisible => _settings.ColorizationMode is ColorizationMode.Image;
|
||||
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 => _settings.ColorizationMode is ColorizationMode.Image
|
||||
? _settings.BackgroundImageTintIntensity
|
||||
: _settings.CustomThemeColorIntensity;
|
||||
public int EffectiveTintIntensity => _settingsService.Settings.ColorizationMode is ColorizationMode.Image
|
||||
? _settingsService.Settings.BackgroundImageTintIntensity
|
||||
: _settingsService.Settings.CustomThemeColorIntensity;
|
||||
|
||||
public bool IsBackgroundControlsVisible => _settings.ColorizationMode is ColorizationMode.Image;
|
||||
public bool IsBackgroundControlsVisible => _settingsService.Settings.ColorizationMode is ColorizationMode.Image;
|
||||
|
||||
public bool IsNoBackgroundVisible => _settings.ColorizationMode is ColorizationMode.None;
|
||||
public bool IsNoBackgroundVisible => _settingsService.Settings.ColorizationMode is ColorizationMode.None;
|
||||
|
||||
public bool IsAccentColorControlsVisible => _settings.ColorizationMode is ColorizationMode.WindowsAccentColor;
|
||||
public bool IsAccentColorControlsVisible => _settingsService.Settings.ColorizationMode is ColorizationMode.WindowsAccentColor;
|
||||
|
||||
public bool IsResetButtonVisible => _settings.ColorizationMode is ColorizationMode.Image;
|
||||
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(_settings.BackdropStyle).SupportsColorization
|
||||
!BackdropStyles.Get(_settingsService.Settings.BackdropStyle).SupportsColorization
|
||||
? Colors.Transparent
|
||||
: ColorizationMode switch
|
||||
{
|
||||
@@ -428,7 +429,7 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
public double EffectiveBackgroundImageBrightness => BackgroundImageBrightness / 100.0;
|
||||
|
||||
public ImageSource? EffectiveBackgroundImageSource =>
|
||||
!BackdropStyles.Get(_settings.BackdropStyle).SupportsBackgroundImage
|
||||
!BackdropStyles.Get(_settingsService.Settings.BackdropStyle).SupportsBackgroundImage
|
||||
? null
|
||||
: ColorizationMode is ColorizationMode.Image
|
||||
&& !string.IsNullOrWhiteSpace(BackgroundImagePath)
|
||||
@@ -436,11 +437,11 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
? new Microsoft.UI.Xaml.Media.Imaging.BitmapImage(uri)
|
||||
: null;
|
||||
|
||||
public AppearanceSettingsViewModel(IThemeService themeService, SettingsModel settings)
|
||||
public AppearanceSettingsViewModel(IThemeService themeService, ISettingsService settingsService)
|
||||
{
|
||||
_themeService = themeService;
|
||||
_themeService.ThemeChanged += ThemeServiceOnThemeChanged;
|
||||
_settings = settings;
|
||||
_settingsService = settingsService;
|
||||
|
||||
_uiSettings = new UISettings();
|
||||
_uiSettings.ColorValuesChanged += UiSettingsOnColorValuesChanged;
|
||||
@@ -448,7 +449,7 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
|
||||
Reapply();
|
||||
|
||||
IsColorizationDetailsExpanded = _settings.ColorizationMode != ColorizationMode.None && IsBackgroundSettingsEnabled;
|
||||
IsColorizationDetailsExpanded = _settingsService.Settings.ColorizationMode != ColorizationMode.None && IsBackgroundSettingsEnabled;
|
||||
}
|
||||
|
||||
private void UiSettingsOnColorValuesChanged(UISettings sender, object args) => _uiDispatcher.TryEnqueue(() => UpdateAccentColor(sender));
|
||||
@@ -469,7 +470,7 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
|
||||
private void Save()
|
||||
{
|
||||
SettingsModel.SaveSettings(_settings);
|
||||
_settingsService.Save();
|
||||
_saveTimer.Debounce(Reapply, TimeSpan.FromMilliseconds(200));
|
||||
}
|
||||
|
||||
|
||||
@@ -139,7 +139,8 @@ public sealed class CommandProviderWrapper : ICommandProviderContext
|
||||
return;
|
||||
}
|
||||
|
||||
var settings = serviceProvider.GetService<SettingsModel>()!;
|
||||
var settingsService = serviceProvider.GetRequiredService<ISettingsService>();
|
||||
var settings = settingsService.Settings;
|
||||
|
||||
var providerSettings = GetProviderSettings(settings);
|
||||
IsActive = providerSettings.IsEnabled;
|
||||
@@ -249,16 +250,15 @@ public sealed class CommandProviderWrapper : ICommandProviderContext
|
||||
IServiceProvider serviceProvider,
|
||||
ICommandProvider4? four)
|
||||
{
|
||||
var settings = serviceProvider.GetService<SettingsModel>()!;
|
||||
var settings = serviceProvider.GetRequiredService<ISettingsService>().Settings;
|
||||
var contextMenuFactory = serviceProvider.GetService<IContextMenuFactory>()!;
|
||||
var state = serviceProvider.GetService<AppStateModel>()!;
|
||||
var providerSettings = GetProviderSettings(settings);
|
||||
var ourContext = GetProviderContext();
|
||||
WeakReference<IPageContext> pageContext = new(this.TopLevelPageContext);
|
||||
var make = (ICommandItem? i, TopLevelType t) =>
|
||||
{
|
||||
CommandItemViewModel commandItemViewModel = new(new(i), pageContext, contextMenuFactory: contextMenuFactory);
|
||||
TopLevelViewModel topLevelViewModel = new(commandItemViewModel, t, ExtensionHost, ourContext, settings, providerSettings, serviceProvider, i, contextMenuFactory: contextMenuFactory);
|
||||
TopLevelViewModel topLevelViewModel = new(commandItemViewModel, t, ExtensionHost, ourContext, providerSettings, serviceProvider, i, contextMenuFactory: contextMenuFactory);
|
||||
topLevelViewModel.InitializeProperties();
|
||||
|
||||
return topLevelViewModel;
|
||||
@@ -407,7 +407,8 @@ public sealed class CommandProviderWrapper : ICommandProviderContext
|
||||
|
||||
public void PinCommand(string commandId, IServiceProvider serviceProvider)
|
||||
{
|
||||
var settings = serviceProvider.GetService<SettingsModel>()!;
|
||||
var settingsService = serviceProvider.GetRequiredService<ISettingsService>();
|
||||
var settings = settingsService.Settings;
|
||||
var providerSettings = GetProviderSettings(settings);
|
||||
|
||||
if (!providerSettings.PinnedCommandIds.Contains(commandId))
|
||||
@@ -416,13 +417,14 @@ public sealed class CommandProviderWrapper : ICommandProviderContext
|
||||
|
||||
// Raise CommandsChanged so the TopLevelCommandManager reloads our commands
|
||||
this.CommandsChanged?.Invoke(this, new ItemsChangedEventArgs(-1));
|
||||
SettingsModel.SaveSettings(settings, false);
|
||||
settingsService.Save(hotReload: false);
|
||||
}
|
||||
}
|
||||
|
||||
public void UnpinCommand(string commandId, IServiceProvider serviceProvider)
|
||||
{
|
||||
var settings = serviceProvider.GetService<SettingsModel>()!;
|
||||
var settingsService = serviceProvider.GetRequiredService<ISettingsService>();
|
||||
var settings = settingsService.Settings;
|
||||
var providerSettings = GetProviderSettings(settings);
|
||||
|
||||
if (providerSettings.PinnedCommandIds.Remove(commandId))
|
||||
@@ -430,13 +432,14 @@ public sealed class CommandProviderWrapper : ICommandProviderContext
|
||||
// Raise CommandsChanged so the TopLevelCommandManager reloads our commands
|
||||
this.CommandsChanged?.Invoke(this, new ItemsChangedEventArgs(-1));
|
||||
|
||||
SettingsModel.SaveSettings(settings, false);
|
||||
settingsService.Save(hotReload: false);
|
||||
}
|
||||
}
|
||||
|
||||
public void PinDockBand(string commandId, IServiceProvider serviceProvider)
|
||||
{
|
||||
var settings = serviceProvider.GetService<SettingsModel>()!;
|
||||
var settingsService = serviceProvider.GetRequiredService<ISettingsService>();
|
||||
var settings = settingsService.Settings;
|
||||
var bandSettings = new DockBandSettings
|
||||
{
|
||||
CommandId = commandId,
|
||||
@@ -447,19 +450,20 @@ public sealed class CommandProviderWrapper : ICommandProviderContext
|
||||
// Raise CommandsChanged so the TopLevelCommandManager reloads our commands
|
||||
this.CommandsChanged?.Invoke(this, new ItemsChangedEventArgs(-1));
|
||||
|
||||
SettingsModel.SaveSettings(settings, false);
|
||||
settingsService.Save(hotReload: false);
|
||||
}
|
||||
|
||||
public void UnpinDockBand(string commandId, IServiceProvider serviceProvider)
|
||||
{
|
||||
var settings = serviceProvider.GetService<SettingsModel>()!;
|
||||
var settingsService = serviceProvider.GetRequiredService<ISettingsService>();
|
||||
var settings = settingsService.Settings;
|
||||
settings.DockSettings.StartBands.RemoveAll(b => b.CommandId == commandId && b.ProviderId == ProviderId);
|
||||
settings.DockSettings.CenterBands.RemoveAll(b => b.CommandId == commandId && b.ProviderId == ProviderId);
|
||||
settings.DockSettings.EndBands.RemoveAll(b => b.CommandId == commandId && b.ProviderId == ProviderId);
|
||||
|
||||
// Raise CommandsChanged so the TopLevelCommandManager reloads our commands
|
||||
this.CommandsChanged?.Invoke(this, new ItemsChangedEventArgs(-1));
|
||||
SettingsModel.SaveSettings(settings, false);
|
||||
settingsService.Save(hotReload: false);
|
||||
}
|
||||
|
||||
public ICommandProviderContext GetProviderContext() => this;
|
||||
|
||||
@@ -18,6 +18,7 @@ using Microsoft.CmdPal.Ext.Apps.Programs;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Commands;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Properties;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Services;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
@@ -42,8 +43,8 @@ public sealed partial class MainListPage : DynamicListPage,
|
||||
private readonly ThrottledDebouncedAction _refreshThrottledDebouncedAction;
|
||||
private readonly TopLevelCommandManager _tlcManager;
|
||||
private readonly AliasManager _aliasManager;
|
||||
private readonly SettingsModel _settings;
|
||||
private readonly AppStateModel _appStateModel;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IAppStateService _appStateService;
|
||||
private readonly ScoringFunction<IListItem> _scoringFunction;
|
||||
private readonly ScoringFunction<IListItem> _fallbackScoringFunction;
|
||||
private readonly IFuzzyMatcherProvider _fuzzyMatcherProvider;
|
||||
@@ -79,23 +80,23 @@ public sealed partial class MainListPage : DynamicListPage,
|
||||
|
||||
public MainListPage(
|
||||
TopLevelCommandManager topLevelCommandManager,
|
||||
SettingsModel settings,
|
||||
AliasManager aliasManager,
|
||||
AppStateModel appStateModel,
|
||||
IFuzzyMatcherProvider fuzzyMatcherProvider)
|
||||
IFuzzyMatcherProvider fuzzyMatcherProvider,
|
||||
ISettingsService settingsService,
|
||||
IAppStateService appStateService)
|
||||
{
|
||||
Id = "com.microsoft.cmdpal.home";
|
||||
Title = Resources.builtin_home_name;
|
||||
Icon = IconHelpers.FromRelativePath("Assets\\Square44x44Logo.altform-unplated_targetsize-256.png");
|
||||
PlaceholderText = Properties.Resources.builtin_main_list_page_searchbar_placeholder;
|
||||
|
||||
_settings = settings;
|
||||
_settingsService = settingsService;
|
||||
_aliasManager = aliasManager;
|
||||
_appStateModel = appStateModel;
|
||||
_appStateService = appStateService;
|
||||
_tlcManager = topLevelCommandManager;
|
||||
_fuzzyMatcherProvider = fuzzyMatcherProvider;
|
||||
_scoringFunction = (in query, item) => ScoreTopLevelItem(in query, item, _appStateModel.RecentCommands, _fuzzyMatcherProvider.Current);
|
||||
_fallbackScoringFunction = (in _, item) => ScoreFallbackItem(item, _settings.FallbackRanks);
|
||||
_scoringFunction = (in query, item) => ScoreTopLevelItem(in query, item, _appStateService.State.RecentCommands, _fuzzyMatcherProvider.Current);
|
||||
_fallbackScoringFunction = (in _, item) => ScoreFallbackItem(item, _settingsService.Settings.FallbackRanks);
|
||||
|
||||
_tlcManager.PropertyChanged += TlcManager_PropertyChanged;
|
||||
_tlcManager.TopLevelCommands.CollectionChanged += Commands_CollectionChanged;
|
||||
@@ -150,8 +151,8 @@ public sealed partial class MainListPage : DynamicListPage,
|
||||
WeakReferenceMessenger.Default.Register<ClearSearchMessage>(this);
|
||||
WeakReferenceMessenger.Default.Register<UpdateFallbackItemsMessage>(this);
|
||||
|
||||
settings.SettingsChanged += SettingsChangedHandler;
|
||||
HotReloadSettings(settings);
|
||||
_settingsService.SettingsChanged += SettingsChangedHandler;
|
||||
HotReloadSettings(_settingsService.Settings);
|
||||
_includeApps = _tlcManager.IsProviderActive(AllAppsCommandProvider.WellKnownId);
|
||||
|
||||
IsLoading = true;
|
||||
@@ -364,7 +365,7 @@ public sealed partial class MainListPage : DynamicListPage,
|
||||
}
|
||||
|
||||
// prefilter fallbacks
|
||||
var globalFallbacks = _settings.GetGlobalFallbacks();
|
||||
var globalFallbacks = _settingsService.Settings.GetGlobalFallbacks();
|
||||
var specialFallbacks = new List<TopLevelViewModel>(globalFallbacks.Length);
|
||||
var commonFallbacks = new List<TopLevelViewModel>(commands.Count - globalFallbacks.Length);
|
||||
|
||||
@@ -479,7 +480,7 @@ public sealed partial class MainListPage : DynamicListPage,
|
||||
|
||||
// We need to remove pinned apps from allNewApps so they don't show twice.
|
||||
// Pinned app command IDs are stored in ProviderSettings.PinnedCommandIds.
|
||||
_settings.ProviderSettings.TryGetValue(AllAppsCommandProvider.WellKnownId, out var providerSettings);
|
||||
_settingsService.Settings.ProviderSettings.TryGetValue(AllAppsCommandProvider.WellKnownId, out var providerSettings);
|
||||
var pinnedCommandIds = providerSettings?.PinnedCommandIds;
|
||||
|
||||
if (pinnedCommandIds is not null && pinnedCommandIds.Count > 0)
|
||||
@@ -678,9 +679,9 @@ public sealed partial class MainListPage : DynamicListPage,
|
||||
public void UpdateHistory(IListItem topLevelOrAppItem)
|
||||
{
|
||||
var id = IdForTopLevelOrAppItem(topLevelOrAppItem);
|
||||
var history = _appStateModel.RecentCommands;
|
||||
var history = _appStateService.State.RecentCommands;
|
||||
history.AddHistoryItem(id);
|
||||
AppStateModel.SaveState(_appStateModel);
|
||||
_appStateService.Save();
|
||||
}
|
||||
|
||||
private static string IdForTopLevelOrAppItem(IListItem topLevelOrAppItem)
|
||||
@@ -703,7 +704,7 @@ public sealed partial class MainListPage : DynamicListPage,
|
||||
RequestRefresh(fullRefresh: false);
|
||||
}
|
||||
|
||||
private void SettingsChangedHandler(SettingsModel sender, object? args) => HotReloadSettings(sender);
|
||||
private void SettingsChangedHandler(ISettingsService sender, SettingsModel args) => HotReloadSettings(args);
|
||||
|
||||
private void HotReloadSettings(SettingsModel settings) => ShowDetails = settings.ShowAppDetails;
|
||||
|
||||
@@ -716,9 +717,9 @@ public sealed partial class MainListPage : DynamicListPage,
|
||||
_tlcManager.PropertyChanged -= TlcManager_PropertyChanged;
|
||||
_tlcManager.TopLevelCommands.CollectionChanged -= Commands_CollectionChanged;
|
||||
|
||||
if (_settings is not null)
|
||||
if (_settingsService is not null)
|
||||
{
|
||||
_settings.SettingsChanged -= SettingsChangedHandler;
|
||||
_settingsService.SettingsChanged -= SettingsChangedHandler;
|
||||
}
|
||||
|
||||
WeakReferenceMessenger.Default.UnregisterAll(this);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Services;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Settings;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels.Dock;
|
||||
@@ -12,7 +13,7 @@ namespace Microsoft.CmdPal.UI.ViewModels.Dock;
|
||||
public partial class DockBandSettingsViewModel : ObservableObject
|
||||
{
|
||||
private static readonly CompositeFormat PluralItemsFormatString = CompositeFormat.Parse(Properties.Resources.dock_item_count_plural);
|
||||
private readonly SettingsModel _settingsModel;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly DockBandSettings _dockSettingsModel;
|
||||
private readonly TopLevelViewModel _adapter;
|
||||
private readonly DockBandViewModel? _bandViewModel;
|
||||
@@ -128,19 +129,19 @@ public partial class DockBandSettingsViewModel : ObservableObject
|
||||
DockBandSettings dockSettingsModel,
|
||||
TopLevelViewModel topLevelAdapter,
|
||||
DockBandViewModel? bandViewModel,
|
||||
SettingsModel settingsModel)
|
||||
ISettingsService settingsService)
|
||||
{
|
||||
_dockSettingsModel = dockSettingsModel;
|
||||
_adapter = topLevelAdapter;
|
||||
_bandViewModel = bandViewModel;
|
||||
_settingsModel = settingsModel;
|
||||
_settingsService = settingsService;
|
||||
_pinSide = FetchPinSide();
|
||||
_showLabels = FetchShowLabels();
|
||||
}
|
||||
|
||||
private DockPinSide FetchPinSide()
|
||||
{
|
||||
var dockSettings = _settingsModel.DockSettings;
|
||||
var dockSettings = _settingsService.Settings.DockSettings;
|
||||
var inStart = dockSettings.StartBands.Any(b => b.CommandId == _dockSettingsModel.CommandId);
|
||||
if (inStart)
|
||||
{
|
||||
@@ -175,7 +176,7 @@ public partial class DockBandSettingsViewModel : ObservableObject
|
||||
|
||||
private void Save()
|
||||
{
|
||||
SettingsModel.SaveSettings(_settingsModel);
|
||||
_settingsService.Save();
|
||||
}
|
||||
|
||||
private void UpdatePinSide(DockPinSide value)
|
||||
@@ -188,7 +189,7 @@ public partial class DockBandSettingsViewModel : ObservableObject
|
||||
|
||||
public void SetBandPosition(DockPinSide side, int? index)
|
||||
{
|
||||
var dockSettings = _settingsModel.DockSettings;
|
||||
var dockSettings = _settingsService.Settings.DockSettings;
|
||||
|
||||
// Remove from all sides first
|
||||
dockSettings.StartBands.RemoveAll(b => b.CommandId == _dockSettingsModel.CommandId);
|
||||
|
||||
@@ -7,6 +7,7 @@ using CommunityToolkit.Mvvm.Messaging;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.UI.Messages;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Services;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Settings;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
@@ -15,7 +16,7 @@ namespace Microsoft.CmdPal.UI.ViewModels.Dock;
|
||||
public sealed partial class DockViewModel
|
||||
{
|
||||
private readonly TopLevelCommandManager _topLevelCommandManager;
|
||||
private readonly SettingsModel _settingsModel;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly DockPageContext _pageContext; // only to be used for our own context menu - not for dock bands themselves
|
||||
private readonly IContextMenuFactory _contextMenuFactory;
|
||||
|
||||
@@ -34,13 +35,13 @@ public sealed partial class DockViewModel
|
||||
public DockViewModel(
|
||||
TopLevelCommandManager tlcManager,
|
||||
IContextMenuFactory contextMenuFactory,
|
||||
SettingsModel settings,
|
||||
TaskScheduler scheduler)
|
||||
TaskScheduler scheduler,
|
||||
ISettingsService settingsService)
|
||||
{
|
||||
_topLevelCommandManager = tlcManager;
|
||||
_contextMenuFactory = contextMenuFactory;
|
||||
_settingsModel = settings;
|
||||
_settings = settings.DockSettings;
|
||||
_settingsService = settingsService;
|
||||
_settings = _settingsService.Settings.DockSettings;
|
||||
Scheduler = scheduler;
|
||||
_pageContext = new(this);
|
||||
|
||||
@@ -148,7 +149,7 @@ public sealed partial class DockViewModel
|
||||
|
||||
private void SaveSettings()
|
||||
{
|
||||
SettingsModel.SaveSettings(_settingsModel);
|
||||
_settingsService.Save();
|
||||
}
|
||||
|
||||
public DockBandViewModel? FindBandByTopLevel(TopLevelViewModel tlc)
|
||||
@@ -193,7 +194,7 @@ public sealed partial class DockViewModel
|
||||
public void SyncBandPosition(DockBandViewModel band, DockPinSide targetSide, int targetIndex)
|
||||
{
|
||||
var bandId = band.Id;
|
||||
var dockSettings = _settingsModel.DockSettings;
|
||||
var dockSettings = _settingsService.Settings.DockSettings;
|
||||
|
||||
var bandSettings = dockSettings.StartBands.FirstOrDefault(b => b.CommandId == bandId)
|
||||
?? dockSettings.CenterBands.FirstOrDefault(b => b.CommandId == bandId)
|
||||
@@ -228,7 +229,7 @@ public sealed partial class DockViewModel
|
||||
public void MoveBandWithoutSaving(DockBandViewModel band, DockPinSide targetSide, int targetIndex)
|
||||
{
|
||||
var bandId = band.Id;
|
||||
var dockSettings = _settingsModel.DockSettings;
|
||||
var dockSettings = _settingsService.Settings.DockSettings;
|
||||
|
||||
var bandSettings = dockSettings.StartBands.FirstOrDefault(b => b.CommandId == bandId)
|
||||
?? dockSettings.CenterBands.FirstOrDefault(b => b.CommandId == bandId)
|
||||
@@ -301,7 +302,7 @@ public sealed partial class DockViewModel
|
||||
_snapshotCenterBands = null;
|
||||
_snapshotEndBands = null;
|
||||
_snapshotBandViewModels = null;
|
||||
SettingsModel.SaveSettings(_settingsModel);
|
||||
_settingsService.Save();
|
||||
Logger.LogDebug("Saved band order to settings");
|
||||
}
|
||||
|
||||
@@ -316,7 +317,7 @@ public sealed partial class DockViewModel
|
||||
/// </summary>
|
||||
public void SnapshotBandOrder()
|
||||
{
|
||||
var dockSettings = _settingsModel.DockSettings;
|
||||
var dockSettings = _settingsService.Settings.DockSettings;
|
||||
_snapshotStartBands = dockSettings.StartBands.Select(b => b.Clone()).ToList();
|
||||
_snapshotCenterBands = dockSettings.CenterBands.Select(b => b.Clone()).ToList();
|
||||
_snapshotEndBands = dockSettings.EndBands.Select(b => b.Clone()).ToList();
|
||||
@@ -358,7 +359,7 @@ public sealed partial class DockViewModel
|
||||
band.RestoreShowLabels();
|
||||
}
|
||||
|
||||
var dockSettings = _settingsModel.DockSettings;
|
||||
var dockSettings = _settingsService.Settings.DockSettings;
|
||||
|
||||
// Restore settings from snapshot
|
||||
dockSettings.StartBands.Clear();
|
||||
@@ -400,7 +401,7 @@ public sealed partial class DockViewModel
|
||||
return;
|
||||
}
|
||||
|
||||
var dockSettings = _settingsModel.DockSettings;
|
||||
var dockSettings = _settingsService.Settings.DockSettings;
|
||||
|
||||
StartItems.Clear();
|
||||
CenterItems.Clear();
|
||||
@@ -433,7 +434,7 @@ public sealed partial class DockViewModel
|
||||
|
||||
private void RebuildUICollections()
|
||||
{
|
||||
var dockSettings = _settingsModel.DockSettings;
|
||||
var dockSettings = _settingsService.Settings.DockSettings;
|
||||
|
||||
// Create a lookup of all current band ViewModels
|
||||
var allBands = StartItems.Concat(CenterItems).Concat(EndItems).ToDictionary(b => b.Id);
|
||||
@@ -510,7 +511,7 @@ public sealed partial class DockViewModel
|
||||
|
||||
// Create settings for the new band
|
||||
var bandSettings = new DockBandSettings { ProviderId = topLevel.CommandProviderId, CommandId = bandId, ShowLabels = null };
|
||||
var dockSettings = _settingsModel.DockSettings;
|
||||
var dockSettings = _settingsService.Settings.DockSettings;
|
||||
|
||||
// Create the band view model
|
||||
var bandVm = CreateBandItem(bandSettings, topLevel.ItemViewModel);
|
||||
@@ -550,7 +551,7 @@ public sealed partial class DockViewModel
|
||||
public void UnpinBand(DockBandViewModel band)
|
||||
{
|
||||
var bandId = band.Id;
|
||||
var dockSettings = _settingsModel.DockSettings;
|
||||
var dockSettings = _settingsService.Settings.DockSettings;
|
||||
|
||||
// Remove from settings
|
||||
dockSettings.StartBands.RemoveAll(b => b.CommandId == bandId);
|
||||
@@ -616,7 +617,7 @@ public sealed partial class DockViewModel
|
||||
|
||||
private void EmitDockConfiguration()
|
||||
{
|
||||
var isDockEnabled = _settingsModel.EnableDock;
|
||||
var isDockEnabled = _settingsService.Settings.EnableDock;
|
||||
var dockSide = isDockEnabled ? _settings.Side.ToString().ToLowerInvariant() : "none";
|
||||
|
||||
static string FormatBands(List<DockBandSettings> bands) =>
|
||||
|
||||
@@ -23,8 +23,7 @@ namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
/// </summary>
|
||||
public sealed partial class DockAppearanceSettingsViewModel : ObservableObject, IDisposable
|
||||
{
|
||||
private readonly SettingsModel _settings;
|
||||
private readonly DockSettings _dockSettings;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly UISettings _uiSettings;
|
||||
private readonly IThemeService _themeService;
|
||||
private readonly DispatcherQueueTimer _saveTimer = DispatcherQueue.GetForCurrentThread().CreateTimer();
|
||||
@@ -37,18 +36,18 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
|
||||
|
||||
public int ThemeIndex
|
||||
{
|
||||
get => (int)_dockSettings.Theme;
|
||||
get => (int)_settingsService.Settings.DockSettings.Theme;
|
||||
set => Theme = (UserTheme)value;
|
||||
}
|
||||
|
||||
public UserTheme Theme
|
||||
{
|
||||
get => _dockSettings.Theme;
|
||||
get => _settingsService.Settings.DockSettings.Theme;
|
||||
set
|
||||
{
|
||||
if (_dockSettings.Theme != value)
|
||||
if (_settingsService.Settings.DockSettings.Theme != value)
|
||||
{
|
||||
_dockSettings.Theme = value;
|
||||
_settingsService.Settings.DockSettings.Theme = value;
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(ThemeIndex));
|
||||
Save();
|
||||
@@ -58,18 +57,18 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
|
||||
|
||||
public int BackdropIndex
|
||||
{
|
||||
get => (int)_dockSettings.Backdrop;
|
||||
get => (int)_settingsService.Settings.DockSettings.Backdrop;
|
||||
set => Backdrop = (DockBackdrop)value;
|
||||
}
|
||||
|
||||
public DockBackdrop Backdrop
|
||||
{
|
||||
get => _dockSettings.Backdrop;
|
||||
get => _settingsService.Settings.DockSettings.Backdrop;
|
||||
set
|
||||
{
|
||||
if (_dockSettings.Backdrop != value)
|
||||
if (_settingsService.Settings.DockSettings.Backdrop != value)
|
||||
{
|
||||
_dockSettings.Backdrop = value;
|
||||
_settingsService.Settings.DockSettings.Backdrop = value;
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(BackdropIndex));
|
||||
Save();
|
||||
@@ -79,12 +78,12 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
|
||||
|
||||
public ColorizationMode ColorizationMode
|
||||
{
|
||||
get => _dockSettings.ColorizationMode;
|
||||
get => _settingsService.Settings.DockSettings.ColorizationMode;
|
||||
set
|
||||
{
|
||||
if (_dockSettings.ColorizationMode != value)
|
||||
if (_settingsService.Settings.DockSettings.ColorizationMode != value)
|
||||
{
|
||||
_dockSettings.ColorizationMode = value;
|
||||
_settingsService.Settings.DockSettings.ColorizationMode = value;
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(ColorizationModeIndex));
|
||||
OnPropertyChanged(nameof(IsCustomTintVisible));
|
||||
@@ -107,18 +106,18 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
|
||||
|
||||
public int ColorizationModeIndex
|
||||
{
|
||||
get => (int)_dockSettings.ColorizationMode;
|
||||
get => (int)_settingsService.Settings.DockSettings.ColorizationMode;
|
||||
set => ColorizationMode = (ColorizationMode)value;
|
||||
}
|
||||
|
||||
public Color ThemeColor
|
||||
{
|
||||
get => _dockSettings.CustomThemeColor;
|
||||
get => _settingsService.Settings.DockSettings.CustomThemeColor;
|
||||
set
|
||||
{
|
||||
if (_dockSettings.CustomThemeColor != value)
|
||||
if (_settingsService.Settings.DockSettings.CustomThemeColor != value)
|
||||
{
|
||||
_dockSettings.CustomThemeColor = value;
|
||||
_settingsService.Settings.DockSettings.CustomThemeColor = value;
|
||||
|
||||
OnPropertyChanged();
|
||||
|
||||
@@ -134,10 +133,10 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
|
||||
|
||||
public int ColorIntensity
|
||||
{
|
||||
get => _dockSettings.CustomThemeColorIntensity;
|
||||
get => _settingsService.Settings.DockSettings.CustomThemeColorIntensity;
|
||||
set
|
||||
{
|
||||
_dockSettings.CustomThemeColorIntensity = value;
|
||||
_settingsService.Settings.DockSettings.CustomThemeColorIntensity = value;
|
||||
OnPropertyChanged();
|
||||
Save();
|
||||
}
|
||||
@@ -145,12 +144,12 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
|
||||
|
||||
public string BackgroundImagePath
|
||||
{
|
||||
get => _dockSettings.BackgroundImagePath ?? string.Empty;
|
||||
get => _settingsService.Settings.DockSettings.BackgroundImagePath ?? string.Empty;
|
||||
set
|
||||
{
|
||||
if (_dockSettings.BackgroundImagePath != value)
|
||||
if (_settingsService.Settings.DockSettings.BackgroundImagePath != value)
|
||||
{
|
||||
_dockSettings.BackgroundImagePath = value;
|
||||
_settingsService.Settings.DockSettings.BackgroundImagePath = value;
|
||||
OnPropertyChanged();
|
||||
|
||||
if (BackgroundImageOpacity == 0)
|
||||
@@ -165,12 +164,12 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
|
||||
|
||||
public int BackgroundImageOpacity
|
||||
{
|
||||
get => _dockSettings.BackgroundImageOpacity;
|
||||
get => _settingsService.Settings.DockSettings.BackgroundImageOpacity;
|
||||
set
|
||||
{
|
||||
if (_dockSettings.BackgroundImageOpacity != value)
|
||||
if (_settingsService.Settings.DockSettings.BackgroundImageOpacity != value)
|
||||
{
|
||||
_dockSettings.BackgroundImageOpacity = value;
|
||||
_settingsService.Settings.DockSettings.BackgroundImageOpacity = value;
|
||||
OnPropertyChanged();
|
||||
Save();
|
||||
}
|
||||
@@ -179,12 +178,12 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
|
||||
|
||||
public int BackgroundImageBrightness
|
||||
{
|
||||
get => _dockSettings.BackgroundImageBrightness;
|
||||
get => _settingsService.Settings.DockSettings.BackgroundImageBrightness;
|
||||
set
|
||||
{
|
||||
if (_dockSettings.BackgroundImageBrightness != value)
|
||||
if (_settingsService.Settings.DockSettings.BackgroundImageBrightness != value)
|
||||
{
|
||||
_dockSettings.BackgroundImageBrightness = value;
|
||||
_settingsService.Settings.DockSettings.BackgroundImageBrightness = value;
|
||||
OnPropertyChanged();
|
||||
Save();
|
||||
}
|
||||
@@ -193,12 +192,12 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
|
||||
|
||||
public int BackgroundImageBlurAmount
|
||||
{
|
||||
get => _dockSettings.BackgroundImageBlurAmount;
|
||||
get => _settingsService.Settings.DockSettings.BackgroundImageBlurAmount;
|
||||
set
|
||||
{
|
||||
if (_dockSettings.BackgroundImageBlurAmount != value)
|
||||
if (_settingsService.Settings.DockSettings.BackgroundImageBlurAmount != value)
|
||||
{
|
||||
_dockSettings.BackgroundImageBlurAmount = value;
|
||||
_settingsService.Settings.DockSettings.BackgroundImageBlurAmount = value;
|
||||
OnPropertyChanged();
|
||||
Save();
|
||||
}
|
||||
@@ -207,12 +206,12 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
|
||||
|
||||
public BackgroundImageFit BackgroundImageFit
|
||||
{
|
||||
get => _dockSettings.BackgroundImageFit;
|
||||
get => _settingsService.Settings.DockSettings.BackgroundImageFit;
|
||||
set
|
||||
{
|
||||
if (_dockSettings.BackgroundImageFit != value)
|
||||
if (_settingsService.Settings.DockSettings.BackgroundImageFit != value)
|
||||
{
|
||||
_dockSettings.BackgroundImageFit = value;
|
||||
_settingsService.Settings.DockSettings.BackgroundImageFit = value;
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(BackgroundImageFitIndex));
|
||||
Save();
|
||||
@@ -237,15 +236,15 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
|
||||
[ObservableProperty]
|
||||
public partial bool IsColorizationDetailsExpanded { get; set; }
|
||||
|
||||
public bool IsCustomTintVisible => _dockSettings.ColorizationMode is ColorizationMode.CustomColor or ColorizationMode.Image;
|
||||
public bool IsCustomTintVisible => _settingsService.Settings.DockSettings.ColorizationMode is ColorizationMode.CustomColor or ColorizationMode.Image;
|
||||
|
||||
public bool IsCustomTintIntensityVisible => _dockSettings.ColorizationMode is ColorizationMode.CustomColor or ColorizationMode.WindowsAccentColor or ColorizationMode.Image;
|
||||
public bool IsCustomTintIntensityVisible => _settingsService.Settings.DockSettings.ColorizationMode is ColorizationMode.CustomColor or ColorizationMode.WindowsAccentColor or ColorizationMode.Image;
|
||||
|
||||
public bool IsBackgroundControlsVisible => _dockSettings.ColorizationMode is ColorizationMode.Image;
|
||||
public bool IsBackgroundControlsVisible => _settingsService.Settings.DockSettings.ColorizationMode is ColorizationMode.Image;
|
||||
|
||||
public bool IsNoBackgroundVisible => _dockSettings.ColorizationMode is ColorizationMode.None;
|
||||
public bool IsNoBackgroundVisible => _settingsService.Settings.DockSettings.ColorizationMode is ColorizationMode.None;
|
||||
|
||||
public bool IsAccentColorControlsVisible => _dockSettings.ColorizationMode is ColorizationMode.WindowsAccentColor;
|
||||
public bool IsAccentColorControlsVisible => _settingsService.Settings.DockSettings.ColorizationMode is ColorizationMode.WindowsAccentColor;
|
||||
|
||||
public ElementTheme EffectiveTheme => _elementThemeOverride ?? _themeService.Current.Theme;
|
||||
|
||||
@@ -268,12 +267,11 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
|
||||
? new Microsoft.UI.Xaml.Media.Imaging.BitmapImage(uri)
|
||||
: null;
|
||||
|
||||
public DockAppearanceSettingsViewModel(IThemeService themeService, SettingsModel settings)
|
||||
public DockAppearanceSettingsViewModel(IThemeService themeService, ISettingsService settingsService)
|
||||
{
|
||||
_themeService = themeService;
|
||||
_themeService.ThemeChanged += ThemeServiceOnThemeChanged;
|
||||
_settings = settings;
|
||||
_dockSettings = settings.DockSettings;
|
||||
_settingsService = settingsService;
|
||||
|
||||
_uiSettings = new UISettings();
|
||||
_uiSettings.ColorValuesChanged += UiSettingsOnColorValuesChanged;
|
||||
@@ -281,7 +279,7 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
|
||||
|
||||
Reapply();
|
||||
|
||||
IsColorizationDetailsExpanded = _dockSettings.ColorizationMode != ColorizationMode.None;
|
||||
IsColorizationDetailsExpanded = _settingsService.Settings.DockSettings.ColorizationMode != ColorizationMode.None;
|
||||
}
|
||||
|
||||
private void UiSettingsOnColorValuesChanged(UISettings sender, object args) => _uiDispatcher.TryEnqueue(() => UpdateAccentColor(sender));
|
||||
@@ -302,7 +300,7 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
|
||||
|
||||
private void Save()
|
||||
{
|
||||
SettingsModel.SaveSettings(_settings);
|
||||
_settingsService.Save();
|
||||
_saveTimer.Debounce(Reapply, TimeSpan.FromMilliseconds(200));
|
||||
}
|
||||
|
||||
|
||||
@@ -5,12 +5,13 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Services;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
public partial class FallbackSettingsViewModel : ObservableObject
|
||||
{
|
||||
private readonly SettingsModel _settings;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly FallbackSettings _fallbackSettings;
|
||||
|
||||
public string DisplayName { get; private set; } = string.Empty;
|
||||
@@ -62,10 +63,10 @@ public partial class FallbackSettingsViewModel : ObservableObject
|
||||
public FallbackSettingsViewModel(
|
||||
TopLevelViewModel fallback,
|
||||
FallbackSettings fallbackSettings,
|
||||
SettingsModel settingsModel,
|
||||
ProviderSettingsViewModel providerSettings)
|
||||
ProviderSettingsViewModel providerSettings,
|
||||
ISettingsService settingsService)
|
||||
{
|
||||
_settings = settingsModel;
|
||||
_settingsService = settingsService;
|
||||
_fallbackSettings = fallbackSettings;
|
||||
|
||||
Id = fallback.Id;
|
||||
@@ -79,7 +80,7 @@ public partial class FallbackSettingsViewModel : ObservableObject
|
||||
|
||||
private void Save()
|
||||
{
|
||||
SettingsModel.SaveSettings(_settings);
|
||||
_settingsService.Save();
|
||||
WeakReferenceMessenger.Default.Send<ReloadCommandsMessage>(new());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Services;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Settings;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
@@ -12,10 +13,10 @@ public partial class HotkeyManager : ObservableObject
|
||||
private readonly TopLevelCommandManager _topLevelCommandManager;
|
||||
private readonly List<TopLevelHotkey> _commandHotkeys;
|
||||
|
||||
public HotkeyManager(TopLevelCommandManager tlcManager, SettingsModel settings)
|
||||
public HotkeyManager(TopLevelCommandManager tlcManager, ISettingsService settingsService)
|
||||
{
|
||||
_topLevelCommandManager = tlcManager;
|
||||
_commandHotkeys = settings.CommandHotkeys;
|
||||
_commandHotkeys = settingsService.Settings.CommandHotkeys;
|
||||
}
|
||||
|
||||
public void UpdateHotkey(string commandId, HotkeySettings? hotkey)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// 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.
|
||||
|
||||
@@ -10,6 +10,7 @@ using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.CmdPal.Common.Services;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Properties;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Services;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
@@ -22,7 +23,7 @@ public partial class ProviderSettingsViewModel : ObservableObject
|
||||
|
||||
private readonly CommandProviderWrapper _provider;
|
||||
private readonly ProviderSettings _providerSettings;
|
||||
private readonly SettingsModel _settings;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly Lock _initializeSettingsLock = new();
|
||||
|
||||
private Task? _initializeSettingsTask;
|
||||
@@ -30,11 +31,11 @@ public partial class ProviderSettingsViewModel : ObservableObject
|
||||
public ProviderSettingsViewModel(
|
||||
CommandProviderWrapper provider,
|
||||
ProviderSettings providerSettings,
|
||||
SettingsModel settings)
|
||||
ISettingsService settingsService)
|
||||
{
|
||||
_provider = provider;
|
||||
_providerSettings = providerSettings;
|
||||
_settings = settings;
|
||||
_settingsService = settingsService;
|
||||
|
||||
LoadingSettings = _provider.Settings?.HasSettings ?? false;
|
||||
|
||||
@@ -179,18 +180,18 @@ public partial class ProviderSettingsViewModel : ObservableObject
|
||||
{
|
||||
if (_providerSettings.FallbackCommands.TryGetValue(fallbackItem.Id, out var fallbackSettings))
|
||||
{
|
||||
fallbackViewModels.Add(new FallbackSettingsViewModel(fallbackItem, fallbackSettings, _settings, this));
|
||||
fallbackViewModels.Add(new FallbackSettingsViewModel(fallbackItem, fallbackSettings, this, _settingsService));
|
||||
}
|
||||
else
|
||||
{
|
||||
fallbackViewModels.Add(new FallbackSettingsViewModel(fallbackItem, new(), _settings, this));
|
||||
fallbackViewModels.Add(new FallbackSettingsViewModel(fallbackItem, new(), this, _settingsService));
|
||||
}
|
||||
}
|
||||
|
||||
FallbackCommands = fallbackViewModels;
|
||||
}
|
||||
|
||||
private void Save() => SettingsModel.SaveSettings(_settings);
|
||||
private void Save() => _settingsService.Save();
|
||||
|
||||
private void InitializeSettingsPage()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// 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 Microsoft.CmdPal.Common.Services;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation of <see cref="IAppStateService"/>.
|
||||
/// Handles loading, saving, and change notification for <see cref="AppStateModel"/>.
|
||||
/// </summary>
|
||||
public sealed class AppStateService : IAppStateService
|
||||
{
|
||||
private readonly IPersistenceService _persistence;
|
||||
private readonly IApplicationInfoService _appInfoService;
|
||||
private readonly string _filePath;
|
||||
|
||||
public AppStateService(IPersistenceService persistence, IApplicationInfoService appInfoService)
|
||||
{
|
||||
_persistence = persistence;
|
||||
_appInfoService = appInfoService;
|
||||
_filePath = StateJsonPath();
|
||||
State = _persistence.Load(_filePath, JsonSerializationContext.Default.AppStateModel);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public AppStateModel State { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event TypedEventHandler<IAppStateService, AppStateModel>? StateChanged;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Save()
|
||||
{
|
||||
_persistence.Save(State, _filePath, JsonSerializationContext.Default.AppStateModel);
|
||||
StateChanged?.Invoke(this, State);
|
||||
}
|
||||
|
||||
private string StateJsonPath()
|
||||
{
|
||||
var directory = _appInfoService.ConfigDirectory;
|
||||
return Path.Combine(directory, "state.json");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// 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.Foundation;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Manages the lifecycle of <see cref="AppStateModel"/>: load, save, and change notification.
|
||||
/// </summary>
|
||||
public interface IAppStateService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the current application state instance.
|
||||
/// </summary>
|
||||
AppStateModel State { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Persists the current state to disk and raises <see cref="StateChanged"/>.
|
||||
/// </summary>
|
||||
void Save();
|
||||
|
||||
/// <summary>
|
||||
/// Raised after state has been saved to disk.
|
||||
/// </summary>
|
||||
event TypedEventHandler<IAppStateService, AppStateModel> StateChanged;
|
||||
}
|
||||
@@ -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 System.Text.Json.Serialization.Metadata;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Provides AOT-compatible JSON file persistence with shallow-merge strategy.
|
||||
/// </summary>
|
||||
public interface IPersistenceService
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads and deserializes a model from the specified JSON file.
|
||||
/// Returns a new <typeparamref name="T"/> instance when the file is missing or unreadable.
|
||||
/// </summary>
|
||||
T Load<T>(string filePath, JsonTypeInfo<T> typeInfo)
|
||||
where T : new();
|
||||
|
||||
/// <summary>
|
||||
/// Serializes <paramref name="model"/>, shallow-merges into the existing file
|
||||
/// (preserving unknown keys), and writes the result back to disk.
|
||||
/// </summary>
|
||||
/// <param name="model">The model to persist.</param>
|
||||
/// <param name="filePath">Target JSON file path.</param>
|
||||
/// <param name="typeInfo">AOT-compatible type metadata.</param>
|
||||
void Save<T>(T model, string filePath, JsonTypeInfo<T> typeInfo);
|
||||
}
|
||||
@@ -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.Foundation;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Manages the lifecycle of <see cref="SettingsModel"/>: load, save, migration, and change notification.
|
||||
/// </summary>
|
||||
public interface ISettingsService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the current settings instance.
|
||||
/// </summary>
|
||||
SettingsModel Settings { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Persists the current settings to disk.
|
||||
/// </summary>
|
||||
/// <param name="hotReload">When <see langword="true"/>, raises <see cref="SettingsChanged"/> after saving.</param>
|
||||
void Save(bool hotReload = true);
|
||||
|
||||
/// <summary>
|
||||
/// Raised after settings are saved with <paramref name="hotReload"/> enabled, or after <see cref="Reload"/>.
|
||||
/// </summary>
|
||||
event TypedEventHandler<ISettingsService, SettingsModel> SettingsChanged;
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
// 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.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization.Metadata;
|
||||
using ManagedCommon;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation of <see cref="IPersistenceService"/> that reads/writes
|
||||
/// JSON files with a shallow-merge strategy to preserve unknown keys.
|
||||
/// </summary>
|
||||
public sealed class PersistenceService : IPersistenceService
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public T Load<T>(string filePath, JsonTypeInfo<T> typeInfo)
|
||||
where T : new()
|
||||
{
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
Logger.LogDebug("Settings file not found at {FilePath}", filePath);
|
||||
return new T();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var jsonContent = File.ReadAllText(filePath);
|
||||
var loaded = JsonSerializer.Deserialize(jsonContent, typeInfo);
|
||||
|
||||
if (loaded is null)
|
||||
{
|
||||
Logger.LogDebug("Failed to parse settings file at {FilePath}", filePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogDebug("Successfully loaded settings file from {FilePath}", filePath);
|
||||
}
|
||||
|
||||
return loaded ?? new T();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Failed to load settings from {filePath}:", ex);
|
||||
}
|
||||
|
||||
return new T();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Save<T>(T model, string filePath, JsonTypeInfo<T> typeInfo)
|
||||
{
|
||||
try
|
||||
{
|
||||
var settingsJson = JsonSerializer.Serialize(model, typeInfo);
|
||||
|
||||
if (JsonNode.Parse(settingsJson) is not JsonObject newSettings)
|
||||
{
|
||||
Logger.LogError("Failed to parse serialized model as JsonObject.");
|
||||
return;
|
||||
}
|
||||
|
||||
var directory = Path.GetDirectoryName(filePath);
|
||||
if (!string.IsNullOrEmpty(directory))
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
|
||||
var serialized = newSettings.ToJsonString(typeInfo.Options);
|
||||
File.WriteAllText(filePath, serialized);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Failed to save to {filePath}:", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
// 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.Diagnostics;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization.Metadata;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.Common.Services;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation of <see cref="ISettingsService"/>.
|
||||
/// Handles loading, saving, migration, and change notification for <see cref="SettingsModel"/>.
|
||||
/// </summary>
|
||||
public sealed class SettingsService : ISettingsService
|
||||
{
|
||||
private const string DeprecatedHotkeyGoesHomeKey = "HotkeyGoesHome";
|
||||
|
||||
private readonly IPersistenceService _persistence;
|
||||
private readonly IApplicationInfoService _appInfoService;
|
||||
private readonly string _filePath;
|
||||
|
||||
public SettingsService(IPersistenceService persistence, IApplicationInfoService appInfoService)
|
||||
{
|
||||
_persistence = persistence;
|
||||
_appInfoService = appInfoService;
|
||||
_filePath = SettingsJsonPath();
|
||||
Settings = _persistence.Load(_filePath, JsonSerializationContext.Default.SettingsModel);
|
||||
ApplyMigrations();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public SettingsModel Settings { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event TypedEventHandler<ISettingsService, SettingsModel>? SettingsChanged;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Save(bool hotReload = true)
|
||||
{
|
||||
_persistence.Save(
|
||||
Settings,
|
||||
_filePath,
|
||||
JsonSerializationContext.Default.SettingsModel);
|
||||
|
||||
if (hotReload)
|
||||
{
|
||||
SettingsChanged?.Invoke(this, Settings);
|
||||
}
|
||||
}
|
||||
|
||||
private string SettingsJsonPath()
|
||||
{
|
||||
var directory = _appInfoService.ConfigDirectory;
|
||||
return Path.Combine(directory, "settings.json");
|
||||
}
|
||||
|
||||
private void ApplyMigrations()
|
||||
{
|
||||
var migratedAny = false;
|
||||
|
||||
try
|
||||
{
|
||||
var jsonContent = File.Exists(_filePath) ? File.ReadAllText(_filePath) : null;
|
||||
if (jsonContent is not null && JsonNode.Parse(jsonContent) is JsonObject root)
|
||||
{
|
||||
migratedAny |= TryMigrate(
|
||||
"Migration #1: HotkeyGoesHome (bool) -> AutoGoHomeInterval (TimeSpan)",
|
||||
root,
|
||||
Settings,
|
||||
nameof(SettingsModel.AutoGoHomeInterval),
|
||||
DeprecatedHotkeyGoesHomeKey,
|
||||
(model, goesHome) => model.AutoGoHomeInterval = goesHome ? TimeSpan.Zero : Timeout.InfiniteTimeSpan,
|
||||
JsonSerializationContext.Default.Boolean);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Migration check failed: {ex}");
|
||||
}
|
||||
|
||||
if (migratedAny)
|
||||
{
|
||||
Save(hotReload: false);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryMigrate<T>(
|
||||
string migrationName,
|
||||
JsonObject root,
|
||||
SettingsModel model,
|
||||
string newKey,
|
||||
string oldKey,
|
||||
Action<SettingsModel, T> apply,
|
||||
JsonTypeInfo<T> jsonTypeInfo)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (root.ContainsKey(newKey) && root[newKey] is not null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (root.TryGetPropertyValue(oldKey, out var oldNode) && oldNode is not null)
|
||||
{
|
||||
var value = oldNode.Deserialize(jsonTypeInfo);
|
||||
apply(model, value!);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Error during migration {migrationName}.", ex);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.UI;
|
||||
using Windows.UI;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels.Settings;
|
||||
@@ -29,7 +28,7 @@ public class DockSettings
|
||||
|
||||
public ColorizationMode ColorizationMode { get; set; }
|
||||
|
||||
public Color CustomThemeColor { get; set; } = Colors.Transparent;
|
||||
public Color CustomThemeColor { get; set; } = new() { A = 0, R = 255, G = 255, B = 255 }; // Transparent — avoids WinUI3 COM dependency on Colors.Transparent and COM in class init
|
||||
|
||||
public int CustomThemeColorIntensity { get; set; } = 100;
|
||||
|
||||
|
||||
@@ -2,30 +2,15 @@
|
||||
// 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.Diagnostics;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.Json.Serialization.Metadata;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Settings;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Microsoft.UI;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
public partial class SettingsModel : ObservableObject
|
||||
{
|
||||
private const string DeprecatedHotkeyGoesHomeKey = "HotkeyGoesHome";
|
||||
|
||||
[JsonIgnore]
|
||||
public static readonly string FilePath;
|
||||
|
||||
public event TypedEventHandler<SettingsModel, object?>? SettingsChanged;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// SETTINGS HERE
|
||||
public static HotkeySettings DefaultActivationShortcut { get; } = new HotkeySettings(true, false, true, false, 0x20); // win+alt+space
|
||||
@@ -77,7 +62,7 @@ public partial class SettingsModel : ObservableObject
|
||||
|
||||
public ColorizationMode ColorizationMode { get; set; }
|
||||
|
||||
public Color CustomThemeColor { get; set; } = Colors.Transparent;
|
||||
public Color CustomThemeColor { get; set; } = new() { A = 0, R = 255, G = 255, B = 255 }; // Transparent — avoids WinUI3 COM dependency on Colors.Transparent
|
||||
|
||||
public int CustomThemeColorIntensity { get; set; } = 100;
|
||||
|
||||
@@ -102,11 +87,6 @@ public partial class SettingsModel : ObservableObject
|
||||
// END SETTINGS
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static SettingsModel()
|
||||
{
|
||||
FilePath = SettingsJsonPath();
|
||||
}
|
||||
|
||||
public ProviderSettings GetProviderSettings(CommandProviderWrapper provider)
|
||||
{
|
||||
ProviderSettings? settings;
|
||||
@@ -143,165 +123,6 @@ public partial class SettingsModel : ObservableObject
|
||||
return globalFallbacks.ToArray();
|
||||
}
|
||||
|
||||
public static SettingsModel LoadSettings()
|
||||
{
|
||||
if (string.IsNullOrEmpty(FilePath))
|
||||
{
|
||||
throw new InvalidOperationException($"You must set a valid {nameof(SettingsModel.FilePath)} before calling {nameof(LoadSettings)}");
|
||||
}
|
||||
|
||||
if (!File.Exists(FilePath))
|
||||
{
|
||||
Debug.WriteLine("The provided settings file does not exist");
|
||||
return new();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Read the JSON content from the file
|
||||
var jsonContent = File.ReadAllText(FilePath);
|
||||
var loaded = JsonSerializer.Deserialize<SettingsModel>(jsonContent, JsonSerializationContext.Default.SettingsModel) ?? new();
|
||||
|
||||
var migratedAny = false;
|
||||
try
|
||||
{
|
||||
if (JsonNode.Parse(jsonContent) is JsonObject root)
|
||||
{
|
||||
migratedAny |= ApplyMigrations(root, loaded);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Migration check failed: {ex}");
|
||||
}
|
||||
|
||||
Debug.WriteLine("Loaded settings file");
|
||||
|
||||
if (migratedAny)
|
||||
{
|
||||
SaveSettings(loaded);
|
||||
}
|
||||
|
||||
return loaded;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(ex.ToString());
|
||||
}
|
||||
|
||||
return new();
|
||||
}
|
||||
|
||||
private static bool ApplyMigrations(JsonObject root, SettingsModel model)
|
||||
{
|
||||
var migrated = false;
|
||||
|
||||
// Migration #1: HotkeyGoesHome (bool) -> AutoGoHomeInterval (TimeSpan)
|
||||
// The old 'HotkeyGoesHome' boolean indicated whether the "go home" action should happen immediately (true) or never (false).
|
||||
// The new 'AutoGoHomeInterval' uses a TimeSpan: 'TimeSpan.Zero' means immediate, 'Timeout.InfiniteTimeSpan' means never.
|
||||
migrated |= TryMigrate(
|
||||
"Migration #1: HotkeyGoesHome (bool) -> AutoGoHomeInterval (TimeSpan)",
|
||||
root,
|
||||
model,
|
||||
nameof(AutoGoHomeInterval),
|
||||
DeprecatedHotkeyGoesHomeKey,
|
||||
(settingsModel, goesHome) => settingsModel.AutoGoHomeInterval = goesHome ? TimeSpan.Zero : Timeout.InfiniteTimeSpan,
|
||||
JsonSerializationContext.Default.Boolean);
|
||||
|
||||
return migrated;
|
||||
}
|
||||
|
||||
private static bool TryMigrate<T>(string migrationName, JsonObject root, SettingsModel model, string newKey, string oldKey, Action<SettingsModel, T> apply, JsonTypeInfo<T> jsonTypeInfo)
|
||||
{
|
||||
try
|
||||
{
|
||||
// If new key already present, skip migration
|
||||
if (root.ContainsKey(newKey) && root[newKey] is not null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If old key present, try to deserialize and apply
|
||||
if (root.TryGetPropertyValue(oldKey, out var oldNode) && oldNode is not null)
|
||||
{
|
||||
var value = oldNode.Deserialize<T>(jsonTypeInfo);
|
||||
apply(model, value!);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Error during migration {migrationName}.", ex);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void SaveSettings(SettingsModel model, bool hotReload = true)
|
||||
{
|
||||
if (string.IsNullOrEmpty(FilePath))
|
||||
{
|
||||
throw new InvalidOperationException($"You must set a valid {nameof(FilePath)} before calling {nameof(SaveSettings)}");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Serialize the main dictionary to JSON and save it to the file
|
||||
var settingsJson = JsonSerializer.Serialize(model, JsonSerializationContext.Default.SettingsModel);
|
||||
|
||||
// Is it valid JSON?
|
||||
if (JsonNode.Parse(settingsJson) is JsonObject newSettings)
|
||||
{
|
||||
// Now, read the existing content from the file
|
||||
var oldContent = File.Exists(FilePath) ? File.ReadAllText(FilePath) : "{}";
|
||||
|
||||
// Is it valid JSON?
|
||||
if (JsonNode.Parse(oldContent) is JsonObject savedSettings)
|
||||
{
|
||||
foreach (var item in newSettings)
|
||||
{
|
||||
savedSettings[item.Key] = item.Value?.DeepClone();
|
||||
}
|
||||
|
||||
// Remove deprecated keys
|
||||
savedSettings.Remove(DeprecatedHotkeyGoesHomeKey);
|
||||
|
||||
var serialized = savedSettings.ToJsonString(JsonSerializationContext.Default.Options);
|
||||
File.WriteAllText(FilePath, serialized);
|
||||
|
||||
// TODO: Instead of just raising the event here, we should
|
||||
// have a file change watcher on the settings file, and
|
||||
// reload the settings then
|
||||
if (hotReload)
|
||||
{
|
||||
model.SettingsChanged?.Invoke(model, null);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.WriteLine("Failed to parse settings file as JsonObject.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.WriteLine("Failed to parse settings file as JsonObject.");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
internal static string SettingsJsonPath()
|
||||
{
|
||||
var directory = Utilities.BaseSettingsPath("Microsoft.CmdPal");
|
||||
Directory.CreateDirectory(directory);
|
||||
|
||||
// now, the settings is just next to the exe
|
||||
return Path.Combine(directory, "settings.json");
|
||||
}
|
||||
|
||||
// [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "<Pending>")]
|
||||
// private static readonly JsonSerializerOptions _serializerOptions = new()
|
||||
// {
|
||||
|
||||
@@ -27,7 +27,7 @@ public partial class SettingsViewModel : INotifyPropertyChanged
|
||||
TimeSpan.FromSeconds(180),
|
||||
];
|
||||
|
||||
private readonly SettingsModel _settings;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly TopLevelCommandManager _topLevelCommandManager;
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
@@ -38,10 +38,10 @@ public partial class SettingsViewModel : INotifyPropertyChanged
|
||||
|
||||
public HotkeySettings? Hotkey
|
||||
{
|
||||
get => _settings.Hotkey;
|
||||
get => _settingsService.Settings.Hotkey;
|
||||
set
|
||||
{
|
||||
_settings.Hotkey = value ?? SettingsModel.DefaultActivationShortcut;
|
||||
_settingsService.Settings.Hotkey = value ?? SettingsModel.DefaultActivationShortcut;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Hotkey)));
|
||||
Save();
|
||||
}
|
||||
@@ -49,10 +49,10 @@ public partial class SettingsViewModel : INotifyPropertyChanged
|
||||
|
||||
public bool UseLowLevelGlobalHotkey
|
||||
{
|
||||
get => _settings.UseLowLevelGlobalHotkey;
|
||||
get => _settingsService.Settings.UseLowLevelGlobalHotkey;
|
||||
set
|
||||
{
|
||||
_settings.UseLowLevelGlobalHotkey = value;
|
||||
_settingsService.Settings.UseLowLevelGlobalHotkey = value;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Hotkey)));
|
||||
Save();
|
||||
}
|
||||
@@ -60,100 +60,100 @@ public partial class SettingsViewModel : INotifyPropertyChanged
|
||||
|
||||
public bool AllowExternalReload
|
||||
{
|
||||
get => _settings.AllowExternalReload;
|
||||
get => _settingsService.Settings.AllowExternalReload;
|
||||
set
|
||||
{
|
||||
_settings.AllowExternalReload = value;
|
||||
_settingsService.Settings.AllowExternalReload = value;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShowAppDetails
|
||||
{
|
||||
get => _settings.ShowAppDetails;
|
||||
get => _settingsService.Settings.ShowAppDetails;
|
||||
set
|
||||
{
|
||||
_settings.ShowAppDetails = value;
|
||||
_settingsService.Settings.ShowAppDetails = value;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
public bool BackspaceGoesBack
|
||||
{
|
||||
get => _settings.BackspaceGoesBack;
|
||||
get => _settingsService.Settings.BackspaceGoesBack;
|
||||
set
|
||||
{
|
||||
_settings.BackspaceGoesBack = value;
|
||||
_settingsService.Settings.BackspaceGoesBack = value;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
public bool SingleClickActivates
|
||||
{
|
||||
get => _settings.SingleClickActivates;
|
||||
get => _settingsService.Settings.SingleClickActivates;
|
||||
set
|
||||
{
|
||||
_settings.SingleClickActivates = value;
|
||||
_settingsService.Settings.SingleClickActivates = value;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
public bool HighlightSearchOnActivate
|
||||
{
|
||||
get => _settings.HighlightSearchOnActivate;
|
||||
get => _settingsService.Settings.HighlightSearchOnActivate;
|
||||
set
|
||||
{
|
||||
_settings.HighlightSearchOnActivate = value;
|
||||
_settingsService.Settings.HighlightSearchOnActivate = value;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
public bool KeepPreviousQuery
|
||||
{
|
||||
get => _settings.KeepPreviousQuery;
|
||||
get => _settingsService.Settings.KeepPreviousQuery;
|
||||
set
|
||||
{
|
||||
_settings.KeepPreviousQuery = value;
|
||||
_settingsService.Settings.KeepPreviousQuery = value;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
public int MonitorPositionIndex
|
||||
{
|
||||
get => (int)_settings.SummonOn;
|
||||
get => (int)_settingsService.Settings.SummonOn;
|
||||
set
|
||||
{
|
||||
_settings.SummonOn = (MonitorBehavior)value;
|
||||
_settingsService.Settings.SummonOn = (MonitorBehavior)value;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShowSystemTrayIcon
|
||||
{
|
||||
get => _settings.ShowSystemTrayIcon;
|
||||
get => _settingsService.Settings.ShowSystemTrayIcon;
|
||||
set
|
||||
{
|
||||
_settings.ShowSystemTrayIcon = value;
|
||||
_settingsService.Settings.ShowSystemTrayIcon = value;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IgnoreShortcutWhenFullscreen
|
||||
{
|
||||
get => _settings.IgnoreShortcutWhenFullscreen;
|
||||
get => _settingsService.Settings.IgnoreShortcutWhenFullscreen;
|
||||
set
|
||||
{
|
||||
_settings.IgnoreShortcutWhenFullscreen = value;
|
||||
_settingsService.Settings.IgnoreShortcutWhenFullscreen = value;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
public bool DisableAnimations
|
||||
{
|
||||
get => _settings.DisableAnimations;
|
||||
get => _settingsService.Settings.DisableAnimations;
|
||||
set
|
||||
{
|
||||
_settings.DisableAnimations = value;
|
||||
_settingsService.Settings.DisableAnimations = value;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
@@ -162,7 +162,7 @@ public partial class SettingsViewModel : INotifyPropertyChanged
|
||||
{
|
||||
get
|
||||
{
|
||||
var index = AutoGoHomeIntervals.IndexOf(_settings.AutoGoHomeInterval);
|
||||
var index = AutoGoHomeIntervals.IndexOf(_settingsService.Settings.AutoGoHomeInterval);
|
||||
return index >= 0 ? index : 0;
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ public partial class SettingsViewModel : INotifyPropertyChanged
|
||||
{
|
||||
if (value >= 0 && value < AutoGoHomeIntervals.Count)
|
||||
{
|
||||
_settings.AutoGoHomeInterval = AutoGoHomeIntervals[value];
|
||||
_settingsService.Settings.AutoGoHomeInterval = AutoGoHomeIntervals[value];
|
||||
}
|
||||
|
||||
Save();
|
||||
@@ -179,60 +179,60 @@ public partial class SettingsViewModel : INotifyPropertyChanged
|
||||
|
||||
public int EscapeKeyBehaviorIndex
|
||||
{
|
||||
get => (int)_settings.EscapeKeyBehaviorSetting;
|
||||
get => (int)_settingsService.Settings.EscapeKeyBehaviorSetting;
|
||||
set
|
||||
{
|
||||
_settings.EscapeKeyBehaviorSetting = (EscapeKeyBehavior)value;
|
||||
_settingsService.Settings.EscapeKeyBehaviorSetting = (EscapeKeyBehavior)value;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
public DockSide Dock_Side
|
||||
{
|
||||
get => _settings.DockSettings.Side;
|
||||
get => _settingsService.Settings.DockSettings.Side;
|
||||
set
|
||||
{
|
||||
_settings.DockSettings.Side = value;
|
||||
_settingsService.Settings.DockSettings.Side = value;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
public DockSize Dock_DockSize
|
||||
{
|
||||
get => _settings.DockSettings.DockSize;
|
||||
get => _settingsService.Settings.DockSettings.DockSize;
|
||||
set
|
||||
{
|
||||
_settings.DockSettings.DockSize = value;
|
||||
_settingsService.Settings.DockSettings.DockSize = value;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
public DockBackdrop Dock_Backdrop
|
||||
{
|
||||
get => _settings.DockSettings.Backdrop;
|
||||
get => _settingsService.Settings.DockSettings.Backdrop;
|
||||
set
|
||||
{
|
||||
_settings.DockSettings.Backdrop = value;
|
||||
_settingsService.Settings.DockSettings.Backdrop = value;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Dock_ShowLabels
|
||||
{
|
||||
get => _settings.DockSettings.ShowLabels;
|
||||
get => _settingsService.Settings.DockSettings.ShowLabels;
|
||||
set
|
||||
{
|
||||
_settings.DockSettings.ShowLabels = value;
|
||||
_settingsService.Settings.DockSettings.ShowLabels = value;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
public bool EnableDock
|
||||
{
|
||||
get => _settings.EnableDock;
|
||||
get => _settingsService.Settings.EnableDock;
|
||||
set
|
||||
{
|
||||
_settings.EnableDock = value;
|
||||
_settingsService.Settings.EnableDock = value;
|
||||
Save();
|
||||
WeakReferenceMessenger.Default.Send(new ShowHideDockMessage(value));
|
||||
WeakReferenceMessenger.Default.Send(new ReloadCommandsMessage()); // TODO! we need to update the MoreCommands of all top level items, but we don't _really_ want to reload
|
||||
@@ -245,26 +245,26 @@ public partial class SettingsViewModel : INotifyPropertyChanged
|
||||
|
||||
public SettingsExtensionsViewModel Extensions { get; }
|
||||
|
||||
public SettingsViewModel(SettingsModel settings, TopLevelCommandManager topLevelCommandManager, TaskScheduler scheduler, IThemeService themeService)
|
||||
public SettingsViewModel(TopLevelCommandManager topLevelCommandManager, TaskScheduler scheduler, IThemeService themeService, ISettingsService settingsService)
|
||||
{
|
||||
_settings = settings;
|
||||
_settingsService = settingsService;
|
||||
_topLevelCommandManager = topLevelCommandManager;
|
||||
|
||||
Appearance = new AppearanceSettingsViewModel(themeService, _settings);
|
||||
DockAppearance = new DockAppearanceSettingsViewModel(themeService, _settings);
|
||||
Appearance = new AppearanceSettingsViewModel(themeService, settingsService);
|
||||
DockAppearance = new DockAppearanceSettingsViewModel(themeService, settingsService);
|
||||
|
||||
var activeProviders = GetCommandProviders();
|
||||
var allProviderSettings = _settings.ProviderSettings;
|
||||
var allProviderSettings = _settingsService.Settings.ProviderSettings;
|
||||
|
||||
var fallbacks = new List<FallbackSettingsViewModel>();
|
||||
var currentRankings = _settings.FallbackRanks;
|
||||
var currentRankings = _settingsService.Settings.FallbackRanks;
|
||||
var needsSave = false;
|
||||
|
||||
foreach (var item in activeProviders)
|
||||
{
|
||||
var providerSettings = settings.GetProviderSettings(item);
|
||||
var providerSettings = _settingsService.Settings.GetProviderSettings(item);
|
||||
|
||||
var settingsModel = new ProviderSettingsViewModel(item, providerSettings, _settings);
|
||||
var settingsModel = new ProviderSettingsViewModel(item, providerSettings, settingsService);
|
||||
CommandProviders.Add(settingsModel);
|
||||
|
||||
fallbacks.AddRange(settingsModel.FallbackCommands);
|
||||
@@ -306,10 +306,10 @@ public partial class SettingsViewModel : INotifyPropertyChanged
|
||||
|
||||
public void ApplyFallbackSort()
|
||||
{
|
||||
_settings.FallbackRanks = FallbackRankings.Select(s => s.Id).ToArray();
|
||||
_settingsService.Settings.FallbackRanks = FallbackRankings.Select(s => s.Id).ToArray();
|
||||
Save();
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(FallbackRankings)));
|
||||
}
|
||||
|
||||
private void Save() => SettingsModel.SaveSettings(_settings);
|
||||
private void Save() => _settingsService.Save();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// 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.
|
||||
|
||||
@@ -9,6 +9,7 @@ using ManagedCommon;
|
||||
using Microsoft.CmdPal.Common.Helpers;
|
||||
using Microsoft.CmdPal.Common.Text;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Services;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Settings;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
@@ -21,7 +22,7 @@ namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
[DebuggerDisplay($"{{{nameof(GetDebuggerDisplay)}(),nq}}")]
|
||||
public sealed partial class TopLevelViewModel : ObservableObject, IListItem, IExtendedAttributesProvider, IPrecomputedListItem
|
||||
{
|
||||
private readonly SettingsModel _settings;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly ProviderSettings _providerSettings;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly CommandItemViewModel _commandItemViewModel;
|
||||
@@ -185,9 +186,9 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem, IEx
|
||||
return null;
|
||||
}
|
||||
|
||||
var bandSettings = _settings.DockSettings.StartBands
|
||||
.Concat(_settings.DockSettings.CenterBands)
|
||||
.Concat(_settings.DockSettings.EndBands)
|
||||
var bandSettings = _settingsService.Settings.DockSettings.StartBands
|
||||
.Concat(_settingsService.Settings.DockSettings.CenterBands)
|
||||
.Concat(_settingsService.Settings.DockSettings.EndBands)
|
||||
.FirstOrDefault(band => band.CommandId == this.Id);
|
||||
if (bandSettings is null)
|
||||
{
|
||||
@@ -208,14 +209,13 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem, IEx
|
||||
TopLevelType topLevelType,
|
||||
CommandPaletteHost extensionHost,
|
||||
ICommandProviderContext commandProviderContext,
|
||||
SettingsModel settings,
|
||||
ProviderSettings providerSettings,
|
||||
IServiceProvider serviceProvider,
|
||||
ICommandItem? commandItem,
|
||||
IContextMenuFactory? contextMenuFactory)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_settings = settings;
|
||||
_settingsService = serviceProvider.GetRequiredService<ISettingsService>();
|
||||
_providerSettings = providerSettings;
|
||||
ProviderContext = commandProviderContext;
|
||||
_commandItemViewModel = item;
|
||||
@@ -313,7 +313,7 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem, IEx
|
||||
}
|
||||
}
|
||||
|
||||
private void Save() => SettingsModel.SaveSettings(_settings);
|
||||
private void Save() => _settingsService.Save();
|
||||
|
||||
private void HandleChangeAlias()
|
||||
{
|
||||
@@ -347,7 +347,7 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem, IEx
|
||||
|
||||
private void UpdateHotkey()
|
||||
{
|
||||
var hotkey = _settings.CommandHotkeys.Where(hk => hk.CommandId == Id).FirstOrDefault();
|
||||
var hotkey = _settingsService.Settings.CommandHotkeys.Where(hk => hk.CommandId == Id).FirstOrDefault();
|
||||
if (hotkey is not null)
|
||||
{
|
||||
_hotkey = hotkey.Hotkey;
|
||||
|
||||
Reference in New Issue
Block a user