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
.gitignore
vendored
5
.gitignore
vendored
@@ -360,3 +360,8 @@ src/common/Telemetry/*.etl
|
|||||||
# PowerToysInstaller Build Temp Files
|
# PowerToysInstaller Build Temp Files
|
||||||
installer/*/*.wxs.bk
|
installer/*/*.wxs.bk
|
||||||
/src/modules/awake/.claude
|
/src/modules/awake/.claude
|
||||||
|
|
||||||
|
# Squad / Copilot agents — local-only, not committed
|
||||||
|
.squad/
|
||||||
|
.squad-workstream
|
||||||
|
.github/agents/
|
||||||
|
|||||||
@@ -5,20 +5,23 @@
|
|||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||||
|
using Microsoft.CmdPal.UI.ViewModels.Services;
|
||||||
|
|
||||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||||
|
|
||||||
public partial class AliasManager : ObservableObject
|
public partial class AliasManager : ObservableObject
|
||||||
{
|
{
|
||||||
private readonly TopLevelCommandManager _topLevelCommandManager;
|
private readonly TopLevelCommandManager _topLevelCommandManager;
|
||||||
|
private readonly ISettingsService _settingsService;
|
||||||
|
|
||||||
// REMEMBER, CommandAlias.SearchPrefix is what we use as keys
|
// REMEMBER, CommandAlias.SearchPrefix is what we use as keys
|
||||||
private readonly Dictionary<string, CommandAlias> _aliases;
|
private readonly Dictionary<string, CommandAlias> _aliases;
|
||||||
|
|
||||||
public AliasManager(TopLevelCommandManager tlcManager, SettingsModel settings)
|
public AliasManager(TopLevelCommandManager tlcManager, ISettingsService settingsService)
|
||||||
{
|
{
|
||||||
_topLevelCommandManager = tlcManager;
|
_topLevelCommandManager = tlcManager;
|
||||||
_aliases = settings.Aliases;
|
_settingsService = settingsService;
|
||||||
|
_aliases = _settingsService.Settings.Aliases;
|
||||||
|
|
||||||
if (_aliases.Count == 0)
|
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.
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Text.Json;
|
|
||||||
using System.Text.Json.Nodes;
|
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using ManagedCommon;
|
|
||||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
|
||||||
using Windows.Foundation;
|
|
||||||
|
|
||||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||||
|
|
||||||
public partial class AppStateModel : ObservableObject
|
public partial class AppStateModel : ObservableObject
|
||||||
{
|
{
|
||||||
[JsonIgnore]
|
|
||||||
public static readonly string FilePath;
|
|
||||||
|
|
||||||
public event TypedEventHandler<AppStateModel, object?>? StateChanged;
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// STATE HERE
|
// STATE HERE
|
||||||
// Make sure that you make the setters public (JsonSerializer.Deserialize will fail silently otherwise)!
|
// 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
|
// 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.
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
@@ -87,7 +87,8 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
|||||||
Color.FromArgb(255, 126, 115, 95), // #7e735f
|
Color.FromArgb(255, 126, 115, 95), // #7e735f
|
||||||
];
|
];
|
||||||
|
|
||||||
private readonly SettingsModel _settings;
|
private readonly ISettingsService _settingsService;
|
||||||
|
|
||||||
private readonly UISettings _uiSettings;
|
private readonly UISettings _uiSettings;
|
||||||
private readonly IThemeService _themeService;
|
private readonly IThemeService _themeService;
|
||||||
private readonly DispatcherQueueTimer _saveTimer = DispatcherQueue.GetForCurrentThread().CreateTimer();
|
private readonly DispatcherQueueTimer _saveTimer = DispatcherQueue.GetForCurrentThread().CreateTimer();
|
||||||
@@ -100,18 +101,18 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
|||||||
|
|
||||||
public int ThemeIndex
|
public int ThemeIndex
|
||||||
{
|
{
|
||||||
get => (int)_settings.Theme;
|
get => (int)_settingsService.Settings.Theme;
|
||||||
set => Theme = (UserTheme)value;
|
set => Theme = (UserTheme)value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserTheme Theme
|
public UserTheme Theme
|
||||||
{
|
{
|
||||||
get => _settings.Theme;
|
get => _settingsService.Settings.Theme;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_settings.Theme != value)
|
if (_settingsService.Settings.Theme != value)
|
||||||
{
|
{
|
||||||
_settings.Theme = value;
|
_settingsService.Settings.Theme = value;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
OnPropertyChanged(nameof(ThemeIndex));
|
OnPropertyChanged(nameof(ThemeIndex));
|
||||||
Save();
|
Save();
|
||||||
@@ -121,12 +122,12 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
|||||||
|
|
||||||
public ColorizationMode ColorizationMode
|
public ColorizationMode ColorizationMode
|
||||||
{
|
{
|
||||||
get => _settings.ColorizationMode;
|
get => _settingsService.Settings.ColorizationMode;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_settings.ColorizationMode != value)
|
if (_settingsService.Settings.ColorizationMode != value)
|
||||||
{
|
{
|
||||||
_settings.ColorizationMode = value;
|
_settingsService.Settings.ColorizationMode = value;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
OnPropertyChanged(nameof(ColorizationModeIndex));
|
OnPropertyChanged(nameof(ColorizationModeIndex));
|
||||||
OnPropertyChanged(nameof(IsCustomTintVisible));
|
OnPropertyChanged(nameof(IsCustomTintVisible));
|
||||||
@@ -152,18 +153,18 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
|||||||
|
|
||||||
public int ColorizationModeIndex
|
public int ColorizationModeIndex
|
||||||
{
|
{
|
||||||
get => (int)_settings.ColorizationMode;
|
get => (int)_settingsService.Settings.ColorizationMode;
|
||||||
set => ColorizationMode = (ColorizationMode)value;
|
set => ColorizationMode = (ColorizationMode)value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Color ThemeColor
|
public Color ThemeColor
|
||||||
{
|
{
|
||||||
get => _settings.CustomThemeColor;
|
get => _settingsService.Settings.CustomThemeColor;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_settings.CustomThemeColor != value)
|
if (_settingsService.Settings.CustomThemeColor != value)
|
||||||
{
|
{
|
||||||
_settings.CustomThemeColor = value;
|
_settingsService.Settings.CustomThemeColor = value;
|
||||||
|
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
|
|
||||||
@@ -179,10 +180,10 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
|||||||
|
|
||||||
public int ColorIntensity
|
public int ColorIntensity
|
||||||
{
|
{
|
||||||
get => _settings.CustomThemeColorIntensity;
|
get => _settingsService.Settings.CustomThemeColorIntensity;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_settings.CustomThemeColorIntensity = value;
|
_settingsService.Settings.CustomThemeColorIntensity = value;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
OnPropertyChanged(nameof(EffectiveTintIntensity));
|
OnPropertyChanged(nameof(EffectiveTintIntensity));
|
||||||
Save();
|
Save();
|
||||||
@@ -191,10 +192,10 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
|||||||
|
|
||||||
public int BackgroundImageTintIntensity
|
public int BackgroundImageTintIntensity
|
||||||
{
|
{
|
||||||
get => _settings.BackgroundImageTintIntensity;
|
get => _settingsService.Settings.BackgroundImageTintIntensity;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_settings.BackgroundImageTintIntensity = value;
|
_settingsService.Settings.BackgroundImageTintIntensity = value;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
OnPropertyChanged(nameof(EffectiveTintIntensity));
|
OnPropertyChanged(nameof(EffectiveTintIntensity));
|
||||||
Save();
|
Save();
|
||||||
@@ -203,12 +204,12 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
|||||||
|
|
||||||
public string BackgroundImagePath
|
public string BackgroundImagePath
|
||||||
{
|
{
|
||||||
get => _settings.BackgroundImagePath ?? string.Empty;
|
get => _settingsService.Settings.BackgroundImagePath ?? string.Empty;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_settings.BackgroundImagePath != value)
|
if (_settingsService.Settings.BackgroundImagePath != value)
|
||||||
{
|
{
|
||||||
_settings.BackgroundImagePath = value;
|
_settingsService.Settings.BackgroundImagePath = value;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
|
|
||||||
if (BackgroundImageOpacity == 0)
|
if (BackgroundImageOpacity == 0)
|
||||||
@@ -223,12 +224,12 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
|||||||
|
|
||||||
public int BackgroundImageOpacity
|
public int BackgroundImageOpacity
|
||||||
{
|
{
|
||||||
get => _settings.BackgroundImageOpacity;
|
get => _settingsService.Settings.BackgroundImageOpacity;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_settings.BackgroundImageOpacity != value)
|
if (_settingsService.Settings.BackgroundImageOpacity != value)
|
||||||
{
|
{
|
||||||
_settings.BackgroundImageOpacity = value;
|
_settingsService.Settings.BackgroundImageOpacity = value;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
@@ -237,12 +238,12 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
|||||||
|
|
||||||
public int BackgroundImageBrightness
|
public int BackgroundImageBrightness
|
||||||
{
|
{
|
||||||
get => _settings.BackgroundImageBrightness;
|
get => _settingsService.Settings.BackgroundImageBrightness;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_settings.BackgroundImageBrightness != value)
|
if (_settingsService.Settings.BackgroundImageBrightness != value)
|
||||||
{
|
{
|
||||||
_settings.BackgroundImageBrightness = value;
|
_settingsService.Settings.BackgroundImageBrightness = value;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
@@ -251,12 +252,12 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
|||||||
|
|
||||||
public int BackgroundImageBlurAmount
|
public int BackgroundImageBlurAmount
|
||||||
{
|
{
|
||||||
get => _settings.BackgroundImageBlurAmount;
|
get => _settingsService.Settings.BackgroundImageBlurAmount;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_settings.BackgroundImageBlurAmount != value)
|
if (_settingsService.Settings.BackgroundImageBlurAmount != value)
|
||||||
{
|
{
|
||||||
_settings.BackgroundImageBlurAmount = value;
|
_settingsService.Settings.BackgroundImageBlurAmount = value;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
@@ -265,12 +266,12 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
|||||||
|
|
||||||
public BackgroundImageFit BackgroundImageFit
|
public BackgroundImageFit BackgroundImageFit
|
||||||
{
|
{
|
||||||
get => _settings.BackgroundImageFit;
|
get => _settingsService.Settings.BackgroundImageFit;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_settings.BackgroundImageFit != value)
|
if (_settingsService.Settings.BackgroundImageFit != value)
|
||||||
{
|
{
|
||||||
_settings.BackgroundImageFit = value;
|
_settingsService.Settings.BackgroundImageFit = value;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
OnPropertyChanged(nameof(BackgroundImageFitIndex));
|
OnPropertyChanged(nameof(BackgroundImageFitIndex));
|
||||||
Save();
|
Save();
|
||||||
@@ -299,12 +300,12 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
|||||||
|
|
||||||
public int BackdropOpacity
|
public int BackdropOpacity
|
||||||
{
|
{
|
||||||
get => _settings.BackdropOpacity;
|
get => _settingsService.Settings.BackdropOpacity;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_settings.BackdropOpacity != value)
|
if (_settingsService.Settings.BackdropOpacity != value)
|
||||||
{
|
{
|
||||||
_settings.BackdropOpacity = value;
|
_settingsService.Settings.BackdropOpacity = value;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
OnPropertyChanged(nameof(EffectiveBackdropStyle));
|
OnPropertyChanged(nameof(EffectiveBackdropStyle));
|
||||||
OnPropertyChanged(nameof(EffectiveImageOpacity));
|
OnPropertyChanged(nameof(EffectiveImageOpacity));
|
||||||
@@ -315,13 +316,13 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
|||||||
|
|
||||||
public int BackdropStyleIndex
|
public int BackdropStyleIndex
|
||||||
{
|
{
|
||||||
get => (int)_settings.BackdropStyle;
|
get => (int)_settingsService.Settings.BackdropStyle;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
var newStyle = (BackdropStyle)value;
|
var newStyle = (BackdropStyle)value;
|
||||||
if (_settings.BackdropStyle != newStyle)
|
if (_settingsService.Settings.BackdropStyle != newStyle)
|
||||||
{
|
{
|
||||||
_settings.BackdropStyle = newStyle;
|
_settingsService.Settings.BackdropStyle = newStyle;
|
||||||
|
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
OnPropertyChanged(nameof(IsBackdropOpacityVisible));
|
OnPropertyChanged(nameof(IsBackdropOpacityVisible));
|
||||||
@@ -343,25 +344,25 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
|||||||
/// Gets whether the backdrop opacity slider should be visible.
|
/// Gets whether the backdrop opacity slider should be visible.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsBackdropOpacityVisible =>
|
public bool IsBackdropOpacityVisible =>
|
||||||
BackdropStyles.Get(_settings.BackdropStyle).SupportsOpacity;
|
BackdropStyles.Get(_settingsService.Settings.BackdropStyle).SupportsOpacity;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets whether the backdrop description (for styles without options) should be visible.
|
/// Gets whether the backdrop description (for styles without options) should be visible.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsMicaBackdropDescriptionVisible =>
|
public bool IsMicaBackdropDescriptionVisible =>
|
||||||
!BackdropStyles.Get(_settings.BackdropStyle).SupportsOpacity;
|
!BackdropStyles.Get(_settingsService.Settings.BackdropStyle).SupportsOpacity;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets whether background/colorization settings are available.
|
/// Gets whether background/colorization settings are available.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsBackgroundSettingsEnabled =>
|
public bool IsBackgroundSettingsEnabled =>
|
||||||
BackdropStyles.Get(_settings.BackdropStyle).SupportsColorization;
|
BackdropStyles.Get(_settingsService.Settings.BackdropStyle).SupportsColorization;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets whether the "not available" message should be shown (inverse of IsBackgroundSettingsEnabled).
|
/// Gets whether the "not available" message should be shown (inverse of IsBackgroundSettingsEnabled).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsBackgroundNotAvailableVisible =>
|
public bool IsBackgroundNotAvailableVisible =>
|
||||||
!BackdropStyles.Get(_settings.BackdropStyle).SupportsColorization;
|
!BackdropStyles.Get(_settingsService.Settings.BackdropStyle).SupportsColorization;
|
||||||
|
|
||||||
public BackdropStyle? EffectiveBackdropStyle
|
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)
|
// Return style when transparency/blur is visible (not fully opaque Acrylic)
|
||||||
// - Clear/Mica/MicaAlt/AcrylicThin always show their effect
|
// - Clear/Mica/MicaAlt/AcrylicThin always show their effect
|
||||||
// - Acrylic shows effect only when opacity < 100
|
// - 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;
|
return null;
|
||||||
@@ -381,39 +382,39 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
|||||||
|
|
||||||
public double EffectiveImageOpacity =>
|
public double EffectiveImageOpacity =>
|
||||||
EffectiveBackdropStyle is not null
|
EffectiveBackdropStyle is not null
|
||||||
? (BackgroundImageOpacity / 100f) * Math.Sqrt(_settings.BackdropOpacity / 100.0)
|
? (BackgroundImageOpacity / 100f) * Math.Sqrt(_settingsService.Settings.BackdropOpacity / 100.0)
|
||||||
: (BackgroundImageOpacity / 100f);
|
: (BackgroundImageOpacity / 100f);
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
public partial bool IsColorizationDetailsExpanded { get; set; }
|
public partial bool IsColorizationDetailsExpanded { get; set; }
|
||||||
|
|
||||||
public bool IsCustomTintVisible => _settings.ColorizationMode is ColorizationMode.CustomColor or ColorizationMode.Image;
|
public bool IsCustomTintVisible => _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>
|
/// <summary>
|
||||||
/// Gets the effective tint intensity for the preview, based on the current colorization mode.
|
/// Gets the effective tint intensity for the preview, based on the current colorization mode.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int EffectiveTintIntensity => _settings.ColorizationMode is ColorizationMode.Image
|
public int EffectiveTintIntensity => _settingsService.Settings.ColorizationMode is ColorizationMode.Image
|
||||||
? _settings.BackgroundImageTintIntensity
|
? _settingsService.Settings.BackgroundImageTintIntensity
|
||||||
: _settings.CustomThemeColorIntensity;
|
: _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 BackdropParameters EffectiveBackdrop { get; private set; } = new(Colors.Black, Colors.Black, 0.5f, 0.5f);
|
||||||
|
|
||||||
public ElementTheme EffectiveTheme => _elementThemeOverride ?? _themeService.Current.Theme;
|
public ElementTheme EffectiveTheme => _elementThemeOverride ?? _themeService.Current.Theme;
|
||||||
|
|
||||||
public Color EffectiveThemeColor =>
|
public Color EffectiveThemeColor =>
|
||||||
!BackdropStyles.Get(_settings.BackdropStyle).SupportsColorization
|
!BackdropStyles.Get(_settingsService.Settings.BackdropStyle).SupportsColorization
|
||||||
? Colors.Transparent
|
? Colors.Transparent
|
||||||
: ColorizationMode switch
|
: ColorizationMode switch
|
||||||
{
|
{
|
||||||
@@ -428,7 +429,7 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
|||||||
public double EffectiveBackgroundImageBrightness => BackgroundImageBrightness / 100.0;
|
public double EffectiveBackgroundImageBrightness => BackgroundImageBrightness / 100.0;
|
||||||
|
|
||||||
public ImageSource? EffectiveBackgroundImageSource =>
|
public ImageSource? EffectiveBackgroundImageSource =>
|
||||||
!BackdropStyles.Get(_settings.BackdropStyle).SupportsBackgroundImage
|
!BackdropStyles.Get(_settingsService.Settings.BackdropStyle).SupportsBackgroundImage
|
||||||
? null
|
? null
|
||||||
: ColorizationMode is ColorizationMode.Image
|
: ColorizationMode is ColorizationMode.Image
|
||||||
&& !string.IsNullOrWhiteSpace(BackgroundImagePath)
|
&& !string.IsNullOrWhiteSpace(BackgroundImagePath)
|
||||||
@@ -436,11 +437,11 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
|||||||
? new Microsoft.UI.Xaml.Media.Imaging.BitmapImage(uri)
|
? new Microsoft.UI.Xaml.Media.Imaging.BitmapImage(uri)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
public AppearanceSettingsViewModel(IThemeService themeService, SettingsModel settings)
|
public AppearanceSettingsViewModel(IThemeService themeService, ISettingsService settingsService)
|
||||||
{
|
{
|
||||||
_themeService = themeService;
|
_themeService = themeService;
|
||||||
_themeService.ThemeChanged += ThemeServiceOnThemeChanged;
|
_themeService.ThemeChanged += ThemeServiceOnThemeChanged;
|
||||||
_settings = settings;
|
_settingsService = settingsService;
|
||||||
|
|
||||||
_uiSettings = new UISettings();
|
_uiSettings = new UISettings();
|
||||||
_uiSettings.ColorValuesChanged += UiSettingsOnColorValuesChanged;
|
_uiSettings.ColorValuesChanged += UiSettingsOnColorValuesChanged;
|
||||||
@@ -448,7 +449,7 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
|||||||
|
|
||||||
Reapply();
|
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));
|
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()
|
private void Save()
|
||||||
{
|
{
|
||||||
SettingsModel.SaveSettings(_settings);
|
_settingsService.Save();
|
||||||
_saveTimer.Debounce(Reapply, TimeSpan.FromMilliseconds(200));
|
_saveTimer.Debounce(Reapply, TimeSpan.FromMilliseconds(200));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -139,7 +139,8 @@ public sealed class CommandProviderWrapper : ICommandProviderContext
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var settings = serviceProvider.GetService<SettingsModel>()!;
|
var settingsService = serviceProvider.GetRequiredService<ISettingsService>();
|
||||||
|
var settings = settingsService.Settings;
|
||||||
|
|
||||||
var providerSettings = GetProviderSettings(settings);
|
var providerSettings = GetProviderSettings(settings);
|
||||||
IsActive = providerSettings.IsEnabled;
|
IsActive = providerSettings.IsEnabled;
|
||||||
@@ -249,16 +250,15 @@ public sealed class CommandProviderWrapper : ICommandProviderContext
|
|||||||
IServiceProvider serviceProvider,
|
IServiceProvider serviceProvider,
|
||||||
ICommandProvider4? four)
|
ICommandProvider4? four)
|
||||||
{
|
{
|
||||||
var settings = serviceProvider.GetService<SettingsModel>()!;
|
var settings = serviceProvider.GetRequiredService<ISettingsService>().Settings;
|
||||||
var contextMenuFactory = serviceProvider.GetService<IContextMenuFactory>()!;
|
var contextMenuFactory = serviceProvider.GetService<IContextMenuFactory>()!;
|
||||||
var state = serviceProvider.GetService<AppStateModel>()!;
|
|
||||||
var providerSettings = GetProviderSettings(settings);
|
var providerSettings = GetProviderSettings(settings);
|
||||||
var ourContext = GetProviderContext();
|
var ourContext = GetProviderContext();
|
||||||
WeakReference<IPageContext> pageContext = new(this.TopLevelPageContext);
|
WeakReference<IPageContext> pageContext = new(this.TopLevelPageContext);
|
||||||
var make = (ICommandItem? i, TopLevelType t) =>
|
var make = (ICommandItem? i, TopLevelType t) =>
|
||||||
{
|
{
|
||||||
CommandItemViewModel commandItemViewModel = new(new(i), pageContext, contextMenuFactory: contextMenuFactory);
|
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();
|
topLevelViewModel.InitializeProperties();
|
||||||
|
|
||||||
return topLevelViewModel;
|
return topLevelViewModel;
|
||||||
@@ -407,7 +407,8 @@ public sealed class CommandProviderWrapper : ICommandProviderContext
|
|||||||
|
|
||||||
public void PinCommand(string commandId, IServiceProvider serviceProvider)
|
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);
|
var providerSettings = GetProviderSettings(settings);
|
||||||
|
|
||||||
if (!providerSettings.PinnedCommandIds.Contains(commandId))
|
if (!providerSettings.PinnedCommandIds.Contains(commandId))
|
||||||
@@ -416,13 +417,14 @@ public sealed class CommandProviderWrapper : ICommandProviderContext
|
|||||||
|
|
||||||
// Raise CommandsChanged so the TopLevelCommandManager reloads our commands
|
// Raise CommandsChanged so the TopLevelCommandManager reloads our commands
|
||||||
this.CommandsChanged?.Invoke(this, new ItemsChangedEventArgs(-1));
|
this.CommandsChanged?.Invoke(this, new ItemsChangedEventArgs(-1));
|
||||||
SettingsModel.SaveSettings(settings, false);
|
settingsService.Save(hotReload: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UnpinCommand(string commandId, IServiceProvider serviceProvider)
|
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);
|
var providerSettings = GetProviderSettings(settings);
|
||||||
|
|
||||||
if (providerSettings.PinnedCommandIds.Remove(commandId))
|
if (providerSettings.PinnedCommandIds.Remove(commandId))
|
||||||
@@ -430,13 +432,14 @@ public sealed class CommandProviderWrapper : ICommandProviderContext
|
|||||||
// Raise CommandsChanged so the TopLevelCommandManager reloads our commands
|
// Raise CommandsChanged so the TopLevelCommandManager reloads our commands
|
||||||
this.CommandsChanged?.Invoke(this, new ItemsChangedEventArgs(-1));
|
this.CommandsChanged?.Invoke(this, new ItemsChangedEventArgs(-1));
|
||||||
|
|
||||||
SettingsModel.SaveSettings(settings, false);
|
settingsService.Save(hotReload: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PinDockBand(string commandId, IServiceProvider serviceProvider)
|
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
|
var bandSettings = new DockBandSettings
|
||||||
{
|
{
|
||||||
CommandId = commandId,
|
CommandId = commandId,
|
||||||
@@ -447,19 +450,20 @@ public sealed class CommandProviderWrapper : ICommandProviderContext
|
|||||||
// Raise CommandsChanged so the TopLevelCommandManager reloads our commands
|
// Raise CommandsChanged so the TopLevelCommandManager reloads our commands
|
||||||
this.CommandsChanged?.Invoke(this, new ItemsChangedEventArgs(-1));
|
this.CommandsChanged?.Invoke(this, new ItemsChangedEventArgs(-1));
|
||||||
|
|
||||||
SettingsModel.SaveSettings(settings, false);
|
settingsService.Save(hotReload: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UnpinDockBand(string commandId, IServiceProvider serviceProvider)
|
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.StartBands.RemoveAll(b => b.CommandId == commandId && b.ProviderId == ProviderId);
|
||||||
settings.DockSettings.CenterBands.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);
|
settings.DockSettings.EndBands.RemoveAll(b => b.CommandId == commandId && b.ProviderId == ProviderId);
|
||||||
|
|
||||||
// Raise CommandsChanged so the TopLevelCommandManager reloads our commands
|
// Raise CommandsChanged so the TopLevelCommandManager reloads our commands
|
||||||
this.CommandsChanged?.Invoke(this, new ItemsChangedEventArgs(-1));
|
this.CommandsChanged?.Invoke(this, new ItemsChangedEventArgs(-1));
|
||||||
SettingsModel.SaveSettings(settings, false);
|
settingsService.Save(hotReload: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ICommandProviderContext GetProviderContext() => this;
|
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.Commands;
|
||||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||||
using Microsoft.CmdPal.UI.ViewModels.Properties;
|
using Microsoft.CmdPal.UI.ViewModels.Properties;
|
||||||
|
using Microsoft.CmdPal.UI.ViewModels.Services;
|
||||||
using Microsoft.CommandPalette.Extensions;
|
using Microsoft.CommandPalette.Extensions;
|
||||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||||
|
|
||||||
@@ -42,8 +43,8 @@ public sealed partial class MainListPage : DynamicListPage,
|
|||||||
private readonly ThrottledDebouncedAction _refreshThrottledDebouncedAction;
|
private readonly ThrottledDebouncedAction _refreshThrottledDebouncedAction;
|
||||||
private readonly TopLevelCommandManager _tlcManager;
|
private readonly TopLevelCommandManager _tlcManager;
|
||||||
private readonly AliasManager _aliasManager;
|
private readonly AliasManager _aliasManager;
|
||||||
private readonly SettingsModel _settings;
|
private readonly ISettingsService _settingsService;
|
||||||
private readonly AppStateModel _appStateModel;
|
private readonly IAppStateService _appStateService;
|
||||||
private readonly ScoringFunction<IListItem> _scoringFunction;
|
private readonly ScoringFunction<IListItem> _scoringFunction;
|
||||||
private readonly ScoringFunction<IListItem> _fallbackScoringFunction;
|
private readonly ScoringFunction<IListItem> _fallbackScoringFunction;
|
||||||
private readonly IFuzzyMatcherProvider _fuzzyMatcherProvider;
|
private readonly IFuzzyMatcherProvider _fuzzyMatcherProvider;
|
||||||
@@ -79,23 +80,23 @@ public sealed partial class MainListPage : DynamicListPage,
|
|||||||
|
|
||||||
public MainListPage(
|
public MainListPage(
|
||||||
TopLevelCommandManager topLevelCommandManager,
|
TopLevelCommandManager topLevelCommandManager,
|
||||||
SettingsModel settings,
|
|
||||||
AliasManager aliasManager,
|
AliasManager aliasManager,
|
||||||
AppStateModel appStateModel,
|
IFuzzyMatcherProvider fuzzyMatcherProvider,
|
||||||
IFuzzyMatcherProvider fuzzyMatcherProvider)
|
ISettingsService settingsService,
|
||||||
|
IAppStateService appStateService)
|
||||||
{
|
{
|
||||||
Id = "com.microsoft.cmdpal.home";
|
Id = "com.microsoft.cmdpal.home";
|
||||||
Title = Resources.builtin_home_name;
|
Title = Resources.builtin_home_name;
|
||||||
Icon = IconHelpers.FromRelativePath("Assets\\Square44x44Logo.altform-unplated_targetsize-256.png");
|
Icon = IconHelpers.FromRelativePath("Assets\\Square44x44Logo.altform-unplated_targetsize-256.png");
|
||||||
PlaceholderText = Properties.Resources.builtin_main_list_page_searchbar_placeholder;
|
PlaceholderText = Properties.Resources.builtin_main_list_page_searchbar_placeholder;
|
||||||
|
|
||||||
_settings = settings;
|
_settingsService = settingsService;
|
||||||
_aliasManager = aliasManager;
|
_aliasManager = aliasManager;
|
||||||
_appStateModel = appStateModel;
|
_appStateService = appStateService;
|
||||||
_tlcManager = topLevelCommandManager;
|
_tlcManager = topLevelCommandManager;
|
||||||
_fuzzyMatcherProvider = fuzzyMatcherProvider;
|
_fuzzyMatcherProvider = fuzzyMatcherProvider;
|
||||||
_scoringFunction = (in query, item) => ScoreTopLevelItem(in query, item, _appStateModel.RecentCommands, _fuzzyMatcherProvider.Current);
|
_scoringFunction = (in query, item) => ScoreTopLevelItem(in query, item, _appStateService.State.RecentCommands, _fuzzyMatcherProvider.Current);
|
||||||
_fallbackScoringFunction = (in _, item) => ScoreFallbackItem(item, _settings.FallbackRanks);
|
_fallbackScoringFunction = (in _, item) => ScoreFallbackItem(item, _settingsService.Settings.FallbackRanks);
|
||||||
|
|
||||||
_tlcManager.PropertyChanged += TlcManager_PropertyChanged;
|
_tlcManager.PropertyChanged += TlcManager_PropertyChanged;
|
||||||
_tlcManager.TopLevelCommands.CollectionChanged += Commands_CollectionChanged;
|
_tlcManager.TopLevelCommands.CollectionChanged += Commands_CollectionChanged;
|
||||||
@@ -150,8 +151,8 @@ public sealed partial class MainListPage : DynamicListPage,
|
|||||||
WeakReferenceMessenger.Default.Register<ClearSearchMessage>(this);
|
WeakReferenceMessenger.Default.Register<ClearSearchMessage>(this);
|
||||||
WeakReferenceMessenger.Default.Register<UpdateFallbackItemsMessage>(this);
|
WeakReferenceMessenger.Default.Register<UpdateFallbackItemsMessage>(this);
|
||||||
|
|
||||||
settings.SettingsChanged += SettingsChangedHandler;
|
_settingsService.SettingsChanged += SettingsChangedHandler;
|
||||||
HotReloadSettings(settings);
|
HotReloadSettings(_settingsService.Settings);
|
||||||
_includeApps = _tlcManager.IsProviderActive(AllAppsCommandProvider.WellKnownId);
|
_includeApps = _tlcManager.IsProviderActive(AllAppsCommandProvider.WellKnownId);
|
||||||
|
|
||||||
IsLoading = true;
|
IsLoading = true;
|
||||||
@@ -364,7 +365,7 @@ public sealed partial class MainListPage : DynamicListPage,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// prefilter fallbacks
|
// prefilter fallbacks
|
||||||
var globalFallbacks = _settings.GetGlobalFallbacks();
|
var globalFallbacks = _settingsService.Settings.GetGlobalFallbacks();
|
||||||
var specialFallbacks = new List<TopLevelViewModel>(globalFallbacks.Length);
|
var specialFallbacks = new List<TopLevelViewModel>(globalFallbacks.Length);
|
||||||
var commonFallbacks = new List<TopLevelViewModel>(commands.Count - 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.
|
// We need to remove pinned apps from allNewApps so they don't show twice.
|
||||||
// Pinned app command IDs are stored in ProviderSettings.PinnedCommandIds.
|
// 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;
|
var pinnedCommandIds = providerSettings?.PinnedCommandIds;
|
||||||
|
|
||||||
if (pinnedCommandIds is not null && pinnedCommandIds.Count > 0)
|
if (pinnedCommandIds is not null && pinnedCommandIds.Count > 0)
|
||||||
@@ -678,9 +679,9 @@ public sealed partial class MainListPage : DynamicListPage,
|
|||||||
public void UpdateHistory(IListItem topLevelOrAppItem)
|
public void UpdateHistory(IListItem topLevelOrAppItem)
|
||||||
{
|
{
|
||||||
var id = IdForTopLevelOrAppItem(topLevelOrAppItem);
|
var id = IdForTopLevelOrAppItem(topLevelOrAppItem);
|
||||||
var history = _appStateModel.RecentCommands;
|
var history = _appStateService.State.RecentCommands;
|
||||||
history.AddHistoryItem(id);
|
history.AddHistoryItem(id);
|
||||||
AppStateModel.SaveState(_appStateModel);
|
_appStateService.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string IdForTopLevelOrAppItem(IListItem topLevelOrAppItem)
|
private static string IdForTopLevelOrAppItem(IListItem topLevelOrAppItem)
|
||||||
@@ -703,7 +704,7 @@ public sealed partial class MainListPage : DynamicListPage,
|
|||||||
RequestRefresh(fullRefresh: false);
|
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;
|
private void HotReloadSettings(SettingsModel settings) => ShowDetails = settings.ShowAppDetails;
|
||||||
|
|
||||||
@@ -716,9 +717,9 @@ public sealed partial class MainListPage : DynamicListPage,
|
|||||||
_tlcManager.PropertyChanged -= TlcManager_PropertyChanged;
|
_tlcManager.PropertyChanged -= TlcManager_PropertyChanged;
|
||||||
_tlcManager.TopLevelCommands.CollectionChanged -= Commands_CollectionChanged;
|
_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);
|
WeakReferenceMessenger.Default.UnregisterAll(this);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using Microsoft.CmdPal.UI.ViewModels.Services;
|
||||||
using Microsoft.CmdPal.UI.ViewModels.Settings;
|
using Microsoft.CmdPal.UI.ViewModels.Settings;
|
||||||
|
|
||||||
namespace Microsoft.CmdPal.UI.ViewModels.Dock;
|
namespace Microsoft.CmdPal.UI.ViewModels.Dock;
|
||||||
@@ -12,7 +13,7 @@ namespace Microsoft.CmdPal.UI.ViewModels.Dock;
|
|||||||
public partial class DockBandSettingsViewModel : ObservableObject
|
public partial class DockBandSettingsViewModel : ObservableObject
|
||||||
{
|
{
|
||||||
private static readonly CompositeFormat PluralItemsFormatString = CompositeFormat.Parse(Properties.Resources.dock_item_count_plural);
|
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 DockBandSettings _dockSettingsModel;
|
||||||
private readonly TopLevelViewModel _adapter;
|
private readonly TopLevelViewModel _adapter;
|
||||||
private readonly DockBandViewModel? _bandViewModel;
|
private readonly DockBandViewModel? _bandViewModel;
|
||||||
@@ -128,19 +129,19 @@ public partial class DockBandSettingsViewModel : ObservableObject
|
|||||||
DockBandSettings dockSettingsModel,
|
DockBandSettings dockSettingsModel,
|
||||||
TopLevelViewModel topLevelAdapter,
|
TopLevelViewModel topLevelAdapter,
|
||||||
DockBandViewModel? bandViewModel,
|
DockBandViewModel? bandViewModel,
|
||||||
SettingsModel settingsModel)
|
ISettingsService settingsService)
|
||||||
{
|
{
|
||||||
_dockSettingsModel = dockSettingsModel;
|
_dockSettingsModel = dockSettingsModel;
|
||||||
_adapter = topLevelAdapter;
|
_adapter = topLevelAdapter;
|
||||||
_bandViewModel = bandViewModel;
|
_bandViewModel = bandViewModel;
|
||||||
_settingsModel = settingsModel;
|
_settingsService = settingsService;
|
||||||
_pinSide = FetchPinSide();
|
_pinSide = FetchPinSide();
|
||||||
_showLabels = FetchShowLabels();
|
_showLabels = FetchShowLabels();
|
||||||
}
|
}
|
||||||
|
|
||||||
private DockPinSide FetchPinSide()
|
private DockPinSide FetchPinSide()
|
||||||
{
|
{
|
||||||
var dockSettings = _settingsModel.DockSettings;
|
var dockSettings = _settingsService.Settings.DockSettings;
|
||||||
var inStart = dockSettings.StartBands.Any(b => b.CommandId == _dockSettingsModel.CommandId);
|
var inStart = dockSettings.StartBands.Any(b => b.CommandId == _dockSettingsModel.CommandId);
|
||||||
if (inStart)
|
if (inStart)
|
||||||
{
|
{
|
||||||
@@ -175,7 +176,7 @@ public partial class DockBandSettingsViewModel : ObservableObject
|
|||||||
|
|
||||||
private void Save()
|
private void Save()
|
||||||
{
|
{
|
||||||
SettingsModel.SaveSettings(_settingsModel);
|
_settingsService.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdatePinSide(DockPinSide value)
|
private void UpdatePinSide(DockPinSide value)
|
||||||
@@ -188,7 +189,7 @@ public partial class DockBandSettingsViewModel : ObservableObject
|
|||||||
|
|
||||||
public void SetBandPosition(DockPinSide side, int? index)
|
public void SetBandPosition(DockPinSide side, int? index)
|
||||||
{
|
{
|
||||||
var dockSettings = _settingsModel.DockSettings;
|
var dockSettings = _settingsService.Settings.DockSettings;
|
||||||
|
|
||||||
// Remove from all sides first
|
// Remove from all sides first
|
||||||
dockSettings.StartBands.RemoveAll(b => b.CommandId == _dockSettingsModel.CommandId);
|
dockSettings.StartBands.RemoveAll(b => b.CommandId == _dockSettingsModel.CommandId);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using CommunityToolkit.Mvvm.Messaging;
|
|||||||
using ManagedCommon;
|
using ManagedCommon;
|
||||||
using Microsoft.CmdPal.UI.Messages;
|
using Microsoft.CmdPal.UI.Messages;
|
||||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||||
|
using Microsoft.CmdPal.UI.ViewModels.Services;
|
||||||
using Microsoft.CmdPal.UI.ViewModels.Settings;
|
using Microsoft.CmdPal.UI.ViewModels.Settings;
|
||||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||||
|
|
||||||
@@ -15,7 +16,7 @@ namespace Microsoft.CmdPal.UI.ViewModels.Dock;
|
|||||||
public sealed partial class DockViewModel
|
public sealed partial class DockViewModel
|
||||||
{
|
{
|
||||||
private readonly TopLevelCommandManager _topLevelCommandManager;
|
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 DockPageContext _pageContext; // only to be used for our own context menu - not for dock bands themselves
|
||||||
private readonly IContextMenuFactory _contextMenuFactory;
|
private readonly IContextMenuFactory _contextMenuFactory;
|
||||||
|
|
||||||
@@ -34,13 +35,13 @@ public sealed partial class DockViewModel
|
|||||||
public DockViewModel(
|
public DockViewModel(
|
||||||
TopLevelCommandManager tlcManager,
|
TopLevelCommandManager tlcManager,
|
||||||
IContextMenuFactory contextMenuFactory,
|
IContextMenuFactory contextMenuFactory,
|
||||||
SettingsModel settings,
|
TaskScheduler scheduler,
|
||||||
TaskScheduler scheduler)
|
ISettingsService settingsService)
|
||||||
{
|
{
|
||||||
_topLevelCommandManager = tlcManager;
|
_topLevelCommandManager = tlcManager;
|
||||||
_contextMenuFactory = contextMenuFactory;
|
_contextMenuFactory = contextMenuFactory;
|
||||||
_settingsModel = settings;
|
_settingsService = settingsService;
|
||||||
_settings = settings.DockSettings;
|
_settings = _settingsService.Settings.DockSettings;
|
||||||
Scheduler = scheduler;
|
Scheduler = scheduler;
|
||||||
_pageContext = new(this);
|
_pageContext = new(this);
|
||||||
|
|
||||||
@@ -148,7 +149,7 @@ public sealed partial class DockViewModel
|
|||||||
|
|
||||||
private void SaveSettings()
|
private void SaveSettings()
|
||||||
{
|
{
|
||||||
SettingsModel.SaveSettings(_settingsModel);
|
_settingsService.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DockBandViewModel? FindBandByTopLevel(TopLevelViewModel tlc)
|
public DockBandViewModel? FindBandByTopLevel(TopLevelViewModel tlc)
|
||||||
@@ -193,7 +194,7 @@ public sealed partial class DockViewModel
|
|||||||
public void SyncBandPosition(DockBandViewModel band, DockPinSide targetSide, int targetIndex)
|
public void SyncBandPosition(DockBandViewModel band, DockPinSide targetSide, int targetIndex)
|
||||||
{
|
{
|
||||||
var bandId = band.Id;
|
var bandId = band.Id;
|
||||||
var dockSettings = _settingsModel.DockSettings;
|
var dockSettings = _settingsService.Settings.DockSettings;
|
||||||
|
|
||||||
var bandSettings = dockSettings.StartBands.FirstOrDefault(b => b.CommandId == bandId)
|
var bandSettings = dockSettings.StartBands.FirstOrDefault(b => b.CommandId == bandId)
|
||||||
?? dockSettings.CenterBands.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)
|
public void MoveBandWithoutSaving(DockBandViewModel band, DockPinSide targetSide, int targetIndex)
|
||||||
{
|
{
|
||||||
var bandId = band.Id;
|
var bandId = band.Id;
|
||||||
var dockSettings = _settingsModel.DockSettings;
|
var dockSettings = _settingsService.Settings.DockSettings;
|
||||||
|
|
||||||
var bandSettings = dockSettings.StartBands.FirstOrDefault(b => b.CommandId == bandId)
|
var bandSettings = dockSettings.StartBands.FirstOrDefault(b => b.CommandId == bandId)
|
||||||
?? dockSettings.CenterBands.FirstOrDefault(b => b.CommandId == bandId)
|
?? dockSettings.CenterBands.FirstOrDefault(b => b.CommandId == bandId)
|
||||||
@@ -301,7 +302,7 @@ public sealed partial class DockViewModel
|
|||||||
_snapshotCenterBands = null;
|
_snapshotCenterBands = null;
|
||||||
_snapshotEndBands = null;
|
_snapshotEndBands = null;
|
||||||
_snapshotBandViewModels = null;
|
_snapshotBandViewModels = null;
|
||||||
SettingsModel.SaveSettings(_settingsModel);
|
_settingsService.Save();
|
||||||
Logger.LogDebug("Saved band order to settings");
|
Logger.LogDebug("Saved band order to settings");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,7 +317,7 @@ public sealed partial class DockViewModel
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void SnapshotBandOrder()
|
public void SnapshotBandOrder()
|
||||||
{
|
{
|
||||||
var dockSettings = _settingsModel.DockSettings;
|
var dockSettings = _settingsService.Settings.DockSettings;
|
||||||
_snapshotStartBands = dockSettings.StartBands.Select(b => b.Clone()).ToList();
|
_snapshotStartBands = dockSettings.StartBands.Select(b => b.Clone()).ToList();
|
||||||
_snapshotCenterBands = dockSettings.CenterBands.Select(b => b.Clone()).ToList();
|
_snapshotCenterBands = dockSettings.CenterBands.Select(b => b.Clone()).ToList();
|
||||||
_snapshotEndBands = dockSettings.EndBands.Select(b => b.Clone()).ToList();
|
_snapshotEndBands = dockSettings.EndBands.Select(b => b.Clone()).ToList();
|
||||||
@@ -358,7 +359,7 @@ public sealed partial class DockViewModel
|
|||||||
band.RestoreShowLabels();
|
band.RestoreShowLabels();
|
||||||
}
|
}
|
||||||
|
|
||||||
var dockSettings = _settingsModel.DockSettings;
|
var dockSettings = _settingsService.Settings.DockSettings;
|
||||||
|
|
||||||
// Restore settings from snapshot
|
// Restore settings from snapshot
|
||||||
dockSettings.StartBands.Clear();
|
dockSettings.StartBands.Clear();
|
||||||
@@ -400,7 +401,7 @@ public sealed partial class DockViewModel
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var dockSettings = _settingsModel.DockSettings;
|
var dockSettings = _settingsService.Settings.DockSettings;
|
||||||
|
|
||||||
StartItems.Clear();
|
StartItems.Clear();
|
||||||
CenterItems.Clear();
|
CenterItems.Clear();
|
||||||
@@ -433,7 +434,7 @@ public sealed partial class DockViewModel
|
|||||||
|
|
||||||
private void RebuildUICollections()
|
private void RebuildUICollections()
|
||||||
{
|
{
|
||||||
var dockSettings = _settingsModel.DockSettings;
|
var dockSettings = _settingsService.Settings.DockSettings;
|
||||||
|
|
||||||
// Create a lookup of all current band ViewModels
|
// Create a lookup of all current band ViewModels
|
||||||
var allBands = StartItems.Concat(CenterItems).Concat(EndItems).ToDictionary(b => b.Id);
|
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
|
// Create settings for the new band
|
||||||
var bandSettings = new DockBandSettings { ProviderId = topLevel.CommandProviderId, CommandId = bandId, ShowLabels = null };
|
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
|
// Create the band view model
|
||||||
var bandVm = CreateBandItem(bandSettings, topLevel.ItemViewModel);
|
var bandVm = CreateBandItem(bandSettings, topLevel.ItemViewModel);
|
||||||
@@ -550,7 +551,7 @@ public sealed partial class DockViewModel
|
|||||||
public void UnpinBand(DockBandViewModel band)
|
public void UnpinBand(DockBandViewModel band)
|
||||||
{
|
{
|
||||||
var bandId = band.Id;
|
var bandId = band.Id;
|
||||||
var dockSettings = _settingsModel.DockSettings;
|
var dockSettings = _settingsService.Settings.DockSettings;
|
||||||
|
|
||||||
// Remove from settings
|
// Remove from settings
|
||||||
dockSettings.StartBands.RemoveAll(b => b.CommandId == bandId);
|
dockSettings.StartBands.RemoveAll(b => b.CommandId == bandId);
|
||||||
@@ -616,7 +617,7 @@ public sealed partial class DockViewModel
|
|||||||
|
|
||||||
private void EmitDockConfiguration()
|
private void EmitDockConfiguration()
|
||||||
{
|
{
|
||||||
var isDockEnabled = _settingsModel.EnableDock;
|
var isDockEnabled = _settingsService.Settings.EnableDock;
|
||||||
var dockSide = isDockEnabled ? _settings.Side.ToString().ToLowerInvariant() : "none";
|
var dockSide = isDockEnabled ? _settings.Side.ToString().ToLowerInvariant() : "none";
|
||||||
|
|
||||||
static string FormatBands(List<DockBandSettings> bands) =>
|
static string FormatBands(List<DockBandSettings> bands) =>
|
||||||
|
|||||||
@@ -23,8 +23,7 @@ namespace Microsoft.CmdPal.UI.ViewModels;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed partial class DockAppearanceSettingsViewModel : ObservableObject, IDisposable
|
public sealed partial class DockAppearanceSettingsViewModel : ObservableObject, IDisposable
|
||||||
{
|
{
|
||||||
private readonly SettingsModel _settings;
|
private readonly ISettingsService _settingsService;
|
||||||
private readonly DockSettings _dockSettings;
|
|
||||||
private readonly UISettings _uiSettings;
|
private readonly UISettings _uiSettings;
|
||||||
private readonly IThemeService _themeService;
|
private readonly IThemeService _themeService;
|
||||||
private readonly DispatcherQueueTimer _saveTimer = DispatcherQueue.GetForCurrentThread().CreateTimer();
|
private readonly DispatcherQueueTimer _saveTimer = DispatcherQueue.GetForCurrentThread().CreateTimer();
|
||||||
@@ -37,18 +36,18 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
|
|||||||
|
|
||||||
public int ThemeIndex
|
public int ThemeIndex
|
||||||
{
|
{
|
||||||
get => (int)_dockSettings.Theme;
|
get => (int)_settingsService.Settings.DockSettings.Theme;
|
||||||
set => Theme = (UserTheme)value;
|
set => Theme = (UserTheme)value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserTheme Theme
|
public UserTheme Theme
|
||||||
{
|
{
|
||||||
get => _dockSettings.Theme;
|
get => _settingsService.Settings.DockSettings.Theme;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_dockSettings.Theme != value)
|
if (_settingsService.Settings.DockSettings.Theme != value)
|
||||||
{
|
{
|
||||||
_dockSettings.Theme = value;
|
_settingsService.Settings.DockSettings.Theme = value;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
OnPropertyChanged(nameof(ThemeIndex));
|
OnPropertyChanged(nameof(ThemeIndex));
|
||||||
Save();
|
Save();
|
||||||
@@ -58,18 +57,18 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
|
|||||||
|
|
||||||
public int BackdropIndex
|
public int BackdropIndex
|
||||||
{
|
{
|
||||||
get => (int)_dockSettings.Backdrop;
|
get => (int)_settingsService.Settings.DockSettings.Backdrop;
|
||||||
set => Backdrop = (DockBackdrop)value;
|
set => Backdrop = (DockBackdrop)value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DockBackdrop Backdrop
|
public DockBackdrop Backdrop
|
||||||
{
|
{
|
||||||
get => _dockSettings.Backdrop;
|
get => _settingsService.Settings.DockSettings.Backdrop;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_dockSettings.Backdrop != value)
|
if (_settingsService.Settings.DockSettings.Backdrop != value)
|
||||||
{
|
{
|
||||||
_dockSettings.Backdrop = value;
|
_settingsService.Settings.DockSettings.Backdrop = value;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
OnPropertyChanged(nameof(BackdropIndex));
|
OnPropertyChanged(nameof(BackdropIndex));
|
||||||
Save();
|
Save();
|
||||||
@@ -79,12 +78,12 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
|
|||||||
|
|
||||||
public ColorizationMode ColorizationMode
|
public ColorizationMode ColorizationMode
|
||||||
{
|
{
|
||||||
get => _dockSettings.ColorizationMode;
|
get => _settingsService.Settings.DockSettings.ColorizationMode;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_dockSettings.ColorizationMode != value)
|
if (_settingsService.Settings.DockSettings.ColorizationMode != value)
|
||||||
{
|
{
|
||||||
_dockSettings.ColorizationMode = value;
|
_settingsService.Settings.DockSettings.ColorizationMode = value;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
OnPropertyChanged(nameof(ColorizationModeIndex));
|
OnPropertyChanged(nameof(ColorizationModeIndex));
|
||||||
OnPropertyChanged(nameof(IsCustomTintVisible));
|
OnPropertyChanged(nameof(IsCustomTintVisible));
|
||||||
@@ -107,18 +106,18 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
|
|||||||
|
|
||||||
public int ColorizationModeIndex
|
public int ColorizationModeIndex
|
||||||
{
|
{
|
||||||
get => (int)_dockSettings.ColorizationMode;
|
get => (int)_settingsService.Settings.DockSettings.ColorizationMode;
|
||||||
set => ColorizationMode = (ColorizationMode)value;
|
set => ColorizationMode = (ColorizationMode)value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Color ThemeColor
|
public Color ThemeColor
|
||||||
{
|
{
|
||||||
get => _dockSettings.CustomThemeColor;
|
get => _settingsService.Settings.DockSettings.CustomThemeColor;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_dockSettings.CustomThemeColor != value)
|
if (_settingsService.Settings.DockSettings.CustomThemeColor != value)
|
||||||
{
|
{
|
||||||
_dockSettings.CustomThemeColor = value;
|
_settingsService.Settings.DockSettings.CustomThemeColor = value;
|
||||||
|
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
|
|
||||||
@@ -134,10 +133,10 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
|
|||||||
|
|
||||||
public int ColorIntensity
|
public int ColorIntensity
|
||||||
{
|
{
|
||||||
get => _dockSettings.CustomThemeColorIntensity;
|
get => _settingsService.Settings.DockSettings.CustomThemeColorIntensity;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_dockSettings.CustomThemeColorIntensity = value;
|
_settingsService.Settings.DockSettings.CustomThemeColorIntensity = value;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
@@ -145,12 +144,12 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
|
|||||||
|
|
||||||
public string BackgroundImagePath
|
public string BackgroundImagePath
|
||||||
{
|
{
|
||||||
get => _dockSettings.BackgroundImagePath ?? string.Empty;
|
get => _settingsService.Settings.DockSettings.BackgroundImagePath ?? string.Empty;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_dockSettings.BackgroundImagePath != value)
|
if (_settingsService.Settings.DockSettings.BackgroundImagePath != value)
|
||||||
{
|
{
|
||||||
_dockSettings.BackgroundImagePath = value;
|
_settingsService.Settings.DockSettings.BackgroundImagePath = value;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
|
|
||||||
if (BackgroundImageOpacity == 0)
|
if (BackgroundImageOpacity == 0)
|
||||||
@@ -165,12 +164,12 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
|
|||||||
|
|
||||||
public int BackgroundImageOpacity
|
public int BackgroundImageOpacity
|
||||||
{
|
{
|
||||||
get => _dockSettings.BackgroundImageOpacity;
|
get => _settingsService.Settings.DockSettings.BackgroundImageOpacity;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_dockSettings.BackgroundImageOpacity != value)
|
if (_settingsService.Settings.DockSettings.BackgroundImageOpacity != value)
|
||||||
{
|
{
|
||||||
_dockSettings.BackgroundImageOpacity = value;
|
_settingsService.Settings.DockSettings.BackgroundImageOpacity = value;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
@@ -179,12 +178,12 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
|
|||||||
|
|
||||||
public int BackgroundImageBrightness
|
public int BackgroundImageBrightness
|
||||||
{
|
{
|
||||||
get => _dockSettings.BackgroundImageBrightness;
|
get => _settingsService.Settings.DockSettings.BackgroundImageBrightness;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_dockSettings.BackgroundImageBrightness != value)
|
if (_settingsService.Settings.DockSettings.BackgroundImageBrightness != value)
|
||||||
{
|
{
|
||||||
_dockSettings.BackgroundImageBrightness = value;
|
_settingsService.Settings.DockSettings.BackgroundImageBrightness = value;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
@@ -193,12 +192,12 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
|
|||||||
|
|
||||||
public int BackgroundImageBlurAmount
|
public int BackgroundImageBlurAmount
|
||||||
{
|
{
|
||||||
get => _dockSettings.BackgroundImageBlurAmount;
|
get => _settingsService.Settings.DockSettings.BackgroundImageBlurAmount;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_dockSettings.BackgroundImageBlurAmount != value)
|
if (_settingsService.Settings.DockSettings.BackgroundImageBlurAmount != value)
|
||||||
{
|
{
|
||||||
_dockSettings.BackgroundImageBlurAmount = value;
|
_settingsService.Settings.DockSettings.BackgroundImageBlurAmount = value;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
@@ -207,12 +206,12 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
|
|||||||
|
|
||||||
public BackgroundImageFit BackgroundImageFit
|
public BackgroundImageFit BackgroundImageFit
|
||||||
{
|
{
|
||||||
get => _dockSettings.BackgroundImageFit;
|
get => _settingsService.Settings.DockSettings.BackgroundImageFit;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_dockSettings.BackgroundImageFit != value)
|
if (_settingsService.Settings.DockSettings.BackgroundImageFit != value)
|
||||||
{
|
{
|
||||||
_dockSettings.BackgroundImageFit = value;
|
_settingsService.Settings.DockSettings.BackgroundImageFit = value;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
OnPropertyChanged(nameof(BackgroundImageFitIndex));
|
OnPropertyChanged(nameof(BackgroundImageFitIndex));
|
||||||
Save();
|
Save();
|
||||||
@@ -237,15 +236,15 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
|
|||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
public partial bool IsColorizationDetailsExpanded { get; set; }
|
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;
|
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)
|
? new Microsoft.UI.Xaml.Media.Imaging.BitmapImage(uri)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
public DockAppearanceSettingsViewModel(IThemeService themeService, SettingsModel settings)
|
public DockAppearanceSettingsViewModel(IThemeService themeService, ISettingsService settingsService)
|
||||||
{
|
{
|
||||||
_themeService = themeService;
|
_themeService = themeService;
|
||||||
_themeService.ThemeChanged += ThemeServiceOnThemeChanged;
|
_themeService.ThemeChanged += ThemeServiceOnThemeChanged;
|
||||||
_settings = settings;
|
_settingsService = settingsService;
|
||||||
_dockSettings = settings.DockSettings;
|
|
||||||
|
|
||||||
_uiSettings = new UISettings();
|
_uiSettings = new UISettings();
|
||||||
_uiSettings.ColorValuesChanged += UiSettingsOnColorValuesChanged;
|
_uiSettings.ColorValuesChanged += UiSettingsOnColorValuesChanged;
|
||||||
@@ -281,7 +279,7 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
|
|||||||
|
|
||||||
Reapply();
|
Reapply();
|
||||||
|
|
||||||
IsColorizationDetailsExpanded = _dockSettings.ColorizationMode != ColorizationMode.None;
|
IsColorizationDetailsExpanded = _settingsService.Settings.DockSettings.ColorizationMode != ColorizationMode.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UiSettingsOnColorValuesChanged(UISettings sender, object args) => _uiDispatcher.TryEnqueue(() => UpdateAccentColor(sender));
|
private void UiSettingsOnColorValuesChanged(UISettings sender, object args) => _uiDispatcher.TryEnqueue(() => UpdateAccentColor(sender));
|
||||||
@@ -302,7 +300,7 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
|
|||||||
|
|
||||||
private void Save()
|
private void Save()
|
||||||
{
|
{
|
||||||
SettingsModel.SaveSettings(_settings);
|
_settingsService.Save();
|
||||||
_saveTimer.Debounce(Reapply, TimeSpan.FromMilliseconds(200));
|
_saveTimer.Debounce(Reapply, TimeSpan.FromMilliseconds(200));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,12 +5,13 @@
|
|||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||||
|
using Microsoft.CmdPal.UI.ViewModels.Services;
|
||||||
|
|
||||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||||
|
|
||||||
public partial class FallbackSettingsViewModel : ObservableObject
|
public partial class FallbackSettingsViewModel : ObservableObject
|
||||||
{
|
{
|
||||||
private readonly SettingsModel _settings;
|
private readonly ISettingsService _settingsService;
|
||||||
private readonly FallbackSettings _fallbackSettings;
|
private readonly FallbackSettings _fallbackSettings;
|
||||||
|
|
||||||
public string DisplayName { get; private set; } = string.Empty;
|
public string DisplayName { get; private set; } = string.Empty;
|
||||||
@@ -62,10 +63,10 @@ public partial class FallbackSettingsViewModel : ObservableObject
|
|||||||
public FallbackSettingsViewModel(
|
public FallbackSettingsViewModel(
|
||||||
TopLevelViewModel fallback,
|
TopLevelViewModel fallback,
|
||||||
FallbackSettings fallbackSettings,
|
FallbackSettings fallbackSettings,
|
||||||
SettingsModel settingsModel,
|
ProviderSettingsViewModel providerSettings,
|
||||||
ProviderSettingsViewModel providerSettings)
|
ISettingsService settingsService)
|
||||||
{
|
{
|
||||||
_settings = settingsModel;
|
_settingsService = settingsService;
|
||||||
_fallbackSettings = fallbackSettings;
|
_fallbackSettings = fallbackSettings;
|
||||||
|
|
||||||
Id = fallback.Id;
|
Id = fallback.Id;
|
||||||
@@ -79,7 +80,7 @@ public partial class FallbackSettingsViewModel : ObservableObject
|
|||||||
|
|
||||||
private void Save()
|
private void Save()
|
||||||
{
|
{
|
||||||
SettingsModel.SaveSettings(_settings);
|
_settingsService.Save();
|
||||||
WeakReferenceMessenger.Default.Send<ReloadCommandsMessage>(new());
|
WeakReferenceMessenger.Default.Send<ReloadCommandsMessage>(new());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using Microsoft.CmdPal.UI.ViewModels.Services;
|
||||||
using Microsoft.CmdPal.UI.ViewModels.Settings;
|
using Microsoft.CmdPal.UI.ViewModels.Settings;
|
||||||
|
|
||||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||||
@@ -12,10 +13,10 @@ public partial class HotkeyManager : ObservableObject
|
|||||||
private readonly TopLevelCommandManager _topLevelCommandManager;
|
private readonly TopLevelCommandManager _topLevelCommandManager;
|
||||||
private readonly List<TopLevelHotkey> _commandHotkeys;
|
private readonly List<TopLevelHotkey> _commandHotkeys;
|
||||||
|
|
||||||
public HotkeyManager(TopLevelCommandManager tlcManager, SettingsModel settings)
|
public HotkeyManager(TopLevelCommandManager tlcManager, ISettingsService settingsService)
|
||||||
{
|
{
|
||||||
_topLevelCommandManager = tlcManager;
|
_topLevelCommandManager = tlcManager;
|
||||||
_commandHotkeys = settings.CommandHotkeys;
|
_commandHotkeys = settingsService.Settings.CommandHotkeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateHotkey(string commandId, HotkeySettings? hotkey)
|
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.
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
@@ -10,6 +10,7 @@ using CommunityToolkit.Mvvm.Messaging;
|
|||||||
using Microsoft.CmdPal.Common.Services;
|
using Microsoft.CmdPal.Common.Services;
|
||||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||||
using Microsoft.CmdPal.UI.ViewModels.Properties;
|
using Microsoft.CmdPal.UI.ViewModels.Properties;
|
||||||
|
using Microsoft.CmdPal.UI.ViewModels.Services;
|
||||||
|
|
||||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||||
|
|
||||||
@@ -22,7 +23,7 @@ public partial class ProviderSettingsViewModel : ObservableObject
|
|||||||
|
|
||||||
private readonly CommandProviderWrapper _provider;
|
private readonly CommandProviderWrapper _provider;
|
||||||
private readonly ProviderSettings _providerSettings;
|
private readonly ProviderSettings _providerSettings;
|
||||||
private readonly SettingsModel _settings;
|
private readonly ISettingsService _settingsService;
|
||||||
private readonly Lock _initializeSettingsLock = new();
|
private readonly Lock _initializeSettingsLock = new();
|
||||||
|
|
||||||
private Task? _initializeSettingsTask;
|
private Task? _initializeSettingsTask;
|
||||||
@@ -30,11 +31,11 @@ public partial class ProviderSettingsViewModel : ObservableObject
|
|||||||
public ProviderSettingsViewModel(
|
public ProviderSettingsViewModel(
|
||||||
CommandProviderWrapper provider,
|
CommandProviderWrapper provider,
|
||||||
ProviderSettings providerSettings,
|
ProviderSettings providerSettings,
|
||||||
SettingsModel settings)
|
ISettingsService settingsService)
|
||||||
{
|
{
|
||||||
_provider = provider;
|
_provider = provider;
|
||||||
_providerSettings = providerSettings;
|
_providerSettings = providerSettings;
|
||||||
_settings = settings;
|
_settingsService = settingsService;
|
||||||
|
|
||||||
LoadingSettings = _provider.Settings?.HasSettings ?? false;
|
LoadingSettings = _provider.Settings?.HasSettings ?? false;
|
||||||
|
|
||||||
@@ -179,18 +180,18 @@ public partial class ProviderSettingsViewModel : ObservableObject
|
|||||||
{
|
{
|
||||||
if (_providerSettings.FallbackCommands.TryGetValue(fallbackItem.Id, out var fallbackSettings))
|
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
|
else
|
||||||
{
|
{
|
||||||
fallbackViewModels.Add(new FallbackSettingsViewModel(fallbackItem, new(), _settings, this));
|
fallbackViewModels.Add(new FallbackSettingsViewModel(fallbackItem, new(), this, _settingsService));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FallbackCommands = fallbackViewModels;
|
FallbackCommands = fallbackViewModels;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Save() => SettingsModel.SaveSettings(_settings);
|
private void Save() => _settingsService.Save();
|
||||||
|
|
||||||
private void InitializeSettingsPage()
|
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.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using Microsoft.UI;
|
|
||||||
using Windows.UI;
|
using Windows.UI;
|
||||||
|
|
||||||
namespace Microsoft.CmdPal.UI.ViewModels.Settings;
|
namespace Microsoft.CmdPal.UI.ViewModels.Settings;
|
||||||
@@ -29,7 +28,7 @@ public class DockSettings
|
|||||||
|
|
||||||
public ColorizationMode ColorizationMode { get; set; }
|
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;
|
public int CustomThemeColorIntensity { get; set; } = 100;
|
||||||
|
|
||||||
|
|||||||
@@ -2,30 +2,15 @@
|
|||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Text.Json;
|
|
||||||
using System.Text.Json.Nodes;
|
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using System.Text.Json.Serialization.Metadata;
|
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using ManagedCommon;
|
|
||||||
using Microsoft.CmdPal.UI.ViewModels.Settings;
|
using Microsoft.CmdPal.UI.ViewModels.Settings;
|
||||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
|
||||||
using Microsoft.UI;
|
|
||||||
using Windows.Foundation;
|
|
||||||
using Windows.UI;
|
using Windows.UI;
|
||||||
|
|
||||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||||
|
|
||||||
public partial class SettingsModel : ObservableObject
|
public partial class SettingsModel : ObservableObject
|
||||||
{
|
{
|
||||||
private const string DeprecatedHotkeyGoesHomeKey = "HotkeyGoesHome";
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public static readonly string FilePath;
|
|
||||||
|
|
||||||
public event TypedEventHandler<SettingsModel, object?>? SettingsChanged;
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// SETTINGS HERE
|
// SETTINGS HERE
|
||||||
public static HotkeySettings DefaultActivationShortcut { get; } = new HotkeySettings(true, false, true, false, 0x20); // win+alt+space
|
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 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;
|
public int CustomThemeColorIntensity { get; set; } = 100;
|
||||||
|
|
||||||
@@ -102,11 +87,6 @@ public partial class SettingsModel : ObservableObject
|
|||||||
// END SETTINGS
|
// END SETTINGS
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
static SettingsModel()
|
|
||||||
{
|
|
||||||
FilePath = SettingsJsonPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProviderSettings GetProviderSettings(CommandProviderWrapper provider)
|
public ProviderSettings GetProviderSettings(CommandProviderWrapper provider)
|
||||||
{
|
{
|
||||||
ProviderSettings? settings;
|
ProviderSettings? settings;
|
||||||
@@ -143,165 +123,6 @@ public partial class SettingsModel : ObservableObject
|
|||||||
return globalFallbacks.ToArray();
|
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>")]
|
// [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "<Pending>")]
|
||||||
// private static readonly JsonSerializerOptions _serializerOptions = new()
|
// private static readonly JsonSerializerOptions _serializerOptions = new()
|
||||||
// {
|
// {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public partial class SettingsViewModel : INotifyPropertyChanged
|
|||||||
TimeSpan.FromSeconds(180),
|
TimeSpan.FromSeconds(180),
|
||||||
];
|
];
|
||||||
|
|
||||||
private readonly SettingsModel _settings;
|
private readonly ISettingsService _settingsService;
|
||||||
private readonly TopLevelCommandManager _topLevelCommandManager;
|
private readonly TopLevelCommandManager _topLevelCommandManager;
|
||||||
|
|
||||||
public event PropertyChangedEventHandler? PropertyChanged;
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
@@ -38,10 +38,10 @@ public partial class SettingsViewModel : INotifyPropertyChanged
|
|||||||
|
|
||||||
public HotkeySettings? Hotkey
|
public HotkeySettings? Hotkey
|
||||||
{
|
{
|
||||||
get => _settings.Hotkey;
|
get => _settingsService.Settings.Hotkey;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_settings.Hotkey = value ?? SettingsModel.DefaultActivationShortcut;
|
_settingsService.Settings.Hotkey = value ?? SettingsModel.DefaultActivationShortcut;
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Hotkey)));
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Hotkey)));
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
@@ -49,10 +49,10 @@ public partial class SettingsViewModel : INotifyPropertyChanged
|
|||||||
|
|
||||||
public bool UseLowLevelGlobalHotkey
|
public bool UseLowLevelGlobalHotkey
|
||||||
{
|
{
|
||||||
get => _settings.UseLowLevelGlobalHotkey;
|
get => _settingsService.Settings.UseLowLevelGlobalHotkey;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_settings.UseLowLevelGlobalHotkey = value;
|
_settingsService.Settings.UseLowLevelGlobalHotkey = value;
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Hotkey)));
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Hotkey)));
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
@@ -60,100 +60,100 @@ public partial class SettingsViewModel : INotifyPropertyChanged
|
|||||||
|
|
||||||
public bool AllowExternalReload
|
public bool AllowExternalReload
|
||||||
{
|
{
|
||||||
get => _settings.AllowExternalReload;
|
get => _settingsService.Settings.AllowExternalReload;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_settings.AllowExternalReload = value;
|
_settingsService.Settings.AllowExternalReload = value;
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ShowAppDetails
|
public bool ShowAppDetails
|
||||||
{
|
{
|
||||||
get => _settings.ShowAppDetails;
|
get => _settingsService.Settings.ShowAppDetails;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_settings.ShowAppDetails = value;
|
_settingsService.Settings.ShowAppDetails = value;
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool BackspaceGoesBack
|
public bool BackspaceGoesBack
|
||||||
{
|
{
|
||||||
get => _settings.BackspaceGoesBack;
|
get => _settingsService.Settings.BackspaceGoesBack;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_settings.BackspaceGoesBack = value;
|
_settingsService.Settings.BackspaceGoesBack = value;
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool SingleClickActivates
|
public bool SingleClickActivates
|
||||||
{
|
{
|
||||||
get => _settings.SingleClickActivates;
|
get => _settingsService.Settings.SingleClickActivates;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_settings.SingleClickActivates = value;
|
_settingsService.Settings.SingleClickActivates = value;
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HighlightSearchOnActivate
|
public bool HighlightSearchOnActivate
|
||||||
{
|
{
|
||||||
get => _settings.HighlightSearchOnActivate;
|
get => _settingsService.Settings.HighlightSearchOnActivate;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_settings.HighlightSearchOnActivate = value;
|
_settingsService.Settings.HighlightSearchOnActivate = value;
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool KeepPreviousQuery
|
public bool KeepPreviousQuery
|
||||||
{
|
{
|
||||||
get => _settings.KeepPreviousQuery;
|
get => _settingsService.Settings.KeepPreviousQuery;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_settings.KeepPreviousQuery = value;
|
_settingsService.Settings.KeepPreviousQuery = value;
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int MonitorPositionIndex
|
public int MonitorPositionIndex
|
||||||
{
|
{
|
||||||
get => (int)_settings.SummonOn;
|
get => (int)_settingsService.Settings.SummonOn;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_settings.SummonOn = (MonitorBehavior)value;
|
_settingsService.Settings.SummonOn = (MonitorBehavior)value;
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ShowSystemTrayIcon
|
public bool ShowSystemTrayIcon
|
||||||
{
|
{
|
||||||
get => _settings.ShowSystemTrayIcon;
|
get => _settingsService.Settings.ShowSystemTrayIcon;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_settings.ShowSystemTrayIcon = value;
|
_settingsService.Settings.ShowSystemTrayIcon = value;
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IgnoreShortcutWhenFullscreen
|
public bool IgnoreShortcutWhenFullscreen
|
||||||
{
|
{
|
||||||
get => _settings.IgnoreShortcutWhenFullscreen;
|
get => _settingsService.Settings.IgnoreShortcutWhenFullscreen;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_settings.IgnoreShortcutWhenFullscreen = value;
|
_settingsService.Settings.IgnoreShortcutWhenFullscreen = value;
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool DisableAnimations
|
public bool DisableAnimations
|
||||||
{
|
{
|
||||||
get => _settings.DisableAnimations;
|
get => _settingsService.Settings.DisableAnimations;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_settings.DisableAnimations = value;
|
_settingsService.Settings.DisableAnimations = value;
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -162,7 +162,7 @@ public partial class SettingsViewModel : INotifyPropertyChanged
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var index = AutoGoHomeIntervals.IndexOf(_settings.AutoGoHomeInterval);
|
var index = AutoGoHomeIntervals.IndexOf(_settingsService.Settings.AutoGoHomeInterval);
|
||||||
return index >= 0 ? index : 0;
|
return index >= 0 ? index : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,7 +170,7 @@ public partial class SettingsViewModel : INotifyPropertyChanged
|
|||||||
{
|
{
|
||||||
if (value >= 0 && value < AutoGoHomeIntervals.Count)
|
if (value >= 0 && value < AutoGoHomeIntervals.Count)
|
||||||
{
|
{
|
||||||
_settings.AutoGoHomeInterval = AutoGoHomeIntervals[value];
|
_settingsService.Settings.AutoGoHomeInterval = AutoGoHomeIntervals[value];
|
||||||
}
|
}
|
||||||
|
|
||||||
Save();
|
Save();
|
||||||
@@ -179,60 +179,60 @@ public partial class SettingsViewModel : INotifyPropertyChanged
|
|||||||
|
|
||||||
public int EscapeKeyBehaviorIndex
|
public int EscapeKeyBehaviorIndex
|
||||||
{
|
{
|
||||||
get => (int)_settings.EscapeKeyBehaviorSetting;
|
get => (int)_settingsService.Settings.EscapeKeyBehaviorSetting;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_settings.EscapeKeyBehaviorSetting = (EscapeKeyBehavior)value;
|
_settingsService.Settings.EscapeKeyBehaviorSetting = (EscapeKeyBehavior)value;
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DockSide Dock_Side
|
public DockSide Dock_Side
|
||||||
{
|
{
|
||||||
get => _settings.DockSettings.Side;
|
get => _settingsService.Settings.DockSettings.Side;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_settings.DockSettings.Side = value;
|
_settingsService.Settings.DockSettings.Side = value;
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DockSize Dock_DockSize
|
public DockSize Dock_DockSize
|
||||||
{
|
{
|
||||||
get => _settings.DockSettings.DockSize;
|
get => _settingsService.Settings.DockSettings.DockSize;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_settings.DockSettings.DockSize = value;
|
_settingsService.Settings.DockSettings.DockSize = value;
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DockBackdrop Dock_Backdrop
|
public DockBackdrop Dock_Backdrop
|
||||||
{
|
{
|
||||||
get => _settings.DockSettings.Backdrop;
|
get => _settingsService.Settings.DockSettings.Backdrop;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_settings.DockSettings.Backdrop = value;
|
_settingsService.Settings.DockSettings.Backdrop = value;
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Dock_ShowLabels
|
public bool Dock_ShowLabels
|
||||||
{
|
{
|
||||||
get => _settings.DockSettings.ShowLabels;
|
get => _settingsService.Settings.DockSettings.ShowLabels;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_settings.DockSettings.ShowLabels = value;
|
_settingsService.Settings.DockSettings.ShowLabels = value;
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool EnableDock
|
public bool EnableDock
|
||||||
{
|
{
|
||||||
get => _settings.EnableDock;
|
get => _settingsService.Settings.EnableDock;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_settings.EnableDock = value;
|
_settingsService.Settings.EnableDock = value;
|
||||||
Save();
|
Save();
|
||||||
WeakReferenceMessenger.Default.Send(new ShowHideDockMessage(value));
|
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
|
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 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;
|
_topLevelCommandManager = topLevelCommandManager;
|
||||||
|
|
||||||
Appearance = new AppearanceSettingsViewModel(themeService, _settings);
|
Appearance = new AppearanceSettingsViewModel(themeService, settingsService);
|
||||||
DockAppearance = new DockAppearanceSettingsViewModel(themeService, _settings);
|
DockAppearance = new DockAppearanceSettingsViewModel(themeService, settingsService);
|
||||||
|
|
||||||
var activeProviders = GetCommandProviders();
|
var activeProviders = GetCommandProviders();
|
||||||
var allProviderSettings = _settings.ProviderSettings;
|
var allProviderSettings = _settingsService.Settings.ProviderSettings;
|
||||||
|
|
||||||
var fallbacks = new List<FallbackSettingsViewModel>();
|
var fallbacks = new List<FallbackSettingsViewModel>();
|
||||||
var currentRankings = _settings.FallbackRanks;
|
var currentRankings = _settingsService.Settings.FallbackRanks;
|
||||||
var needsSave = false;
|
var needsSave = false;
|
||||||
|
|
||||||
foreach (var item in activeProviders)
|
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);
|
CommandProviders.Add(settingsModel);
|
||||||
|
|
||||||
fallbacks.AddRange(settingsModel.FallbackCommands);
|
fallbacks.AddRange(settingsModel.FallbackCommands);
|
||||||
@@ -306,10 +306,10 @@ public partial class SettingsViewModel : INotifyPropertyChanged
|
|||||||
|
|
||||||
public void ApplyFallbackSort()
|
public void ApplyFallbackSort()
|
||||||
{
|
{
|
||||||
_settings.FallbackRanks = FallbackRankings.Select(s => s.Id).ToArray();
|
_settingsService.Settings.FallbackRanks = FallbackRankings.Select(s => s.Id).ToArray();
|
||||||
Save();
|
Save();
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(FallbackRankings)));
|
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.
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
@@ -9,6 +9,7 @@ using ManagedCommon;
|
|||||||
using Microsoft.CmdPal.Common.Helpers;
|
using Microsoft.CmdPal.Common.Helpers;
|
||||||
using Microsoft.CmdPal.Common.Text;
|
using Microsoft.CmdPal.Common.Text;
|
||||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||||
|
using Microsoft.CmdPal.UI.ViewModels.Services;
|
||||||
using Microsoft.CmdPal.UI.ViewModels.Settings;
|
using Microsoft.CmdPal.UI.ViewModels.Settings;
|
||||||
using Microsoft.CommandPalette.Extensions;
|
using Microsoft.CommandPalette.Extensions;
|
||||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||||
@@ -21,7 +22,7 @@ namespace Microsoft.CmdPal.UI.ViewModels;
|
|||||||
[DebuggerDisplay($"{{{nameof(GetDebuggerDisplay)}(),nq}}")]
|
[DebuggerDisplay($"{{{nameof(GetDebuggerDisplay)}(),nq}}")]
|
||||||
public sealed partial class TopLevelViewModel : ObservableObject, IListItem, IExtendedAttributesProvider, IPrecomputedListItem
|
public sealed partial class TopLevelViewModel : ObservableObject, IListItem, IExtendedAttributesProvider, IPrecomputedListItem
|
||||||
{
|
{
|
||||||
private readonly SettingsModel _settings;
|
private readonly ISettingsService _settingsService;
|
||||||
private readonly ProviderSettings _providerSettings;
|
private readonly ProviderSettings _providerSettings;
|
||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IServiceProvider _serviceProvider;
|
||||||
private readonly CommandItemViewModel _commandItemViewModel;
|
private readonly CommandItemViewModel _commandItemViewModel;
|
||||||
@@ -185,9 +186,9 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem, IEx
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var bandSettings = _settings.DockSettings.StartBands
|
var bandSettings = _settingsService.Settings.DockSettings.StartBands
|
||||||
.Concat(_settings.DockSettings.CenterBands)
|
.Concat(_settingsService.Settings.DockSettings.CenterBands)
|
||||||
.Concat(_settings.DockSettings.EndBands)
|
.Concat(_settingsService.Settings.DockSettings.EndBands)
|
||||||
.FirstOrDefault(band => band.CommandId == this.Id);
|
.FirstOrDefault(band => band.CommandId == this.Id);
|
||||||
if (bandSettings is null)
|
if (bandSettings is null)
|
||||||
{
|
{
|
||||||
@@ -208,14 +209,13 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem, IEx
|
|||||||
TopLevelType topLevelType,
|
TopLevelType topLevelType,
|
||||||
CommandPaletteHost extensionHost,
|
CommandPaletteHost extensionHost,
|
||||||
ICommandProviderContext commandProviderContext,
|
ICommandProviderContext commandProviderContext,
|
||||||
SettingsModel settings,
|
|
||||||
ProviderSettings providerSettings,
|
ProviderSettings providerSettings,
|
||||||
IServiceProvider serviceProvider,
|
IServiceProvider serviceProvider,
|
||||||
ICommandItem? commandItem,
|
ICommandItem? commandItem,
|
||||||
IContextMenuFactory? contextMenuFactory)
|
IContextMenuFactory? contextMenuFactory)
|
||||||
{
|
{
|
||||||
_serviceProvider = serviceProvider;
|
_serviceProvider = serviceProvider;
|
||||||
_settings = settings;
|
_settingsService = serviceProvider.GetRequiredService<ISettingsService>();
|
||||||
_providerSettings = providerSettings;
|
_providerSettings = providerSettings;
|
||||||
ProviderContext = commandProviderContext;
|
ProviderContext = commandProviderContext;
|
||||||
_commandItemViewModel = item;
|
_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()
|
private void HandleChangeAlias()
|
||||||
{
|
{
|
||||||
@@ -347,7 +347,7 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem, IEx
|
|||||||
|
|
||||||
private void UpdateHotkey()
|
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)
|
if (hotkey is not null)
|
||||||
{
|
{
|
||||||
_hotkey = hotkey.Hotkey;
|
_hotkey = hotkey.Hotkey;
|
||||||
|
|||||||
@@ -183,11 +183,10 @@ public partial class App : Application, IDisposable
|
|||||||
|
|
||||||
private static void AddUIServices(ServiceCollection services, DispatcherQueue dispatcherQueue)
|
private static void AddUIServices(ServiceCollection services, DispatcherQueue dispatcherQueue)
|
||||||
{
|
{
|
||||||
// Models
|
// Models & persistence services
|
||||||
var sm = SettingsModel.LoadSettings();
|
services.AddSingleton<IPersistenceService, PersistenceService>();
|
||||||
services.AddSingleton(sm);
|
services.AddSingleton<ISettingsService, SettingsService>();
|
||||||
var state = AppStateModel.LoadState();
|
services.AddSingleton<IAppStateService, AppStateService>();
|
||||||
services.AddSingleton(state);
|
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
services.AddSingleton<ICommandProviderCache, DefaultCommandProviderCache>();
|
services.AddSingleton<ICommandProviderCache, DefaultCommandProviderCache>();
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using CommunityToolkit.Mvvm.Messaging;
|
|||||||
using ManagedCommon;
|
using ManagedCommon;
|
||||||
using Microsoft.CmdPal.UI.ViewModels;
|
using Microsoft.CmdPal.UI.ViewModels;
|
||||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||||
|
using Microsoft.CmdPal.UI.ViewModels.Services;
|
||||||
using Microsoft.CmdPal.UI.ViewModels.Settings;
|
using Microsoft.CmdPal.UI.ViewModels.Settings;
|
||||||
using Microsoft.CommandPalette.Extensions;
|
using Microsoft.CommandPalette.Extensions;
|
||||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||||
@@ -15,12 +16,12 @@ namespace Microsoft.CmdPal.UI;
|
|||||||
|
|
||||||
internal sealed partial class CommandPaletteContextMenuFactory : IContextMenuFactory
|
internal sealed partial class CommandPaletteContextMenuFactory : IContextMenuFactory
|
||||||
{
|
{
|
||||||
private readonly SettingsModel _settingsModel;
|
private readonly ISettingsService _settingsService;
|
||||||
private readonly TopLevelCommandManager _topLevelCommandManager;
|
private readonly TopLevelCommandManager _topLevelCommandManager;
|
||||||
|
|
||||||
public CommandPaletteContextMenuFactory(SettingsModel settingsModel, TopLevelCommandManager topLevelCommandManager)
|
public CommandPaletteContextMenuFactory(ISettingsService settingsService, TopLevelCommandManager topLevelCommandManager)
|
||||||
{
|
{
|
||||||
_settingsModel = settingsModel;
|
_settingsService = settingsService;
|
||||||
_topLevelCommandManager = topLevelCommandManager;
|
_topLevelCommandManager = topLevelCommandManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +66,7 @@ internal sealed partial class CommandPaletteContextMenuFactory : IContextMenuFac
|
|||||||
var providerId = providerContext.ProviderId;
|
var providerId = providerContext.ProviderId;
|
||||||
if (_topLevelCommandManager.LookupProvider(providerId) is CommandProviderWrapper provider)
|
if (_topLevelCommandManager.LookupProvider(providerId) is CommandProviderWrapper provider)
|
||||||
{
|
{
|
||||||
var providerSettings = _settingsModel.GetProviderSettings(provider);
|
var providerSettings = _settingsService.Settings.GetProviderSettings(provider);
|
||||||
|
|
||||||
var alreadyPinnedToTopLevel = providerSettings.PinnedCommandIds.Contains(itemId);
|
var alreadyPinnedToTopLevel = providerSettings.PinnedCommandIds.Contains(itemId);
|
||||||
|
|
||||||
@@ -82,7 +83,7 @@ internal sealed partial class CommandPaletteContextMenuFactory : IContextMenuFac
|
|||||||
providerId: providerId,
|
providerId: providerId,
|
||||||
pin: !alreadyPinnedToTopLevel,
|
pin: !alreadyPinnedToTopLevel,
|
||||||
PinLocation.TopLevel,
|
PinLocation.TopLevel,
|
||||||
_settingsModel,
|
_settingsService,
|
||||||
_topLevelCommandManager);
|
_topLevelCommandManager);
|
||||||
|
|
||||||
var contextItem = new PinToContextItem(pinToTopLevelCommand, commandItem);
|
var contextItem = new PinToContextItem(pinToTopLevelCommand, commandItem);
|
||||||
@@ -132,7 +133,7 @@ internal sealed partial class CommandPaletteContextMenuFactory : IContextMenuFac
|
|||||||
var providerId = providerContext.ProviderId;
|
var providerId = providerContext.ProviderId;
|
||||||
if (_topLevelCommandManager.LookupProvider(providerId) is CommandProviderWrapper provider)
|
if (_topLevelCommandManager.LookupProvider(providerId) is CommandProviderWrapper provider)
|
||||||
{
|
{
|
||||||
var providerSettings = _settingsModel.GetProviderSettings(provider);
|
var providerSettings = _settingsService.Settings.GetProviderSettings(provider);
|
||||||
|
|
||||||
var isPinnedSubCommand = providerSettings.PinnedCommandIds.Contains(itemId);
|
var isPinnedSubCommand = providerSettings.PinnedCommandIds.Contains(itemId);
|
||||||
if (isPinnedSubCommand)
|
if (isPinnedSubCommand)
|
||||||
@@ -142,7 +143,7 @@ internal sealed partial class CommandPaletteContextMenuFactory : IContextMenuFac
|
|||||||
providerId: providerId,
|
providerId: providerId,
|
||||||
pin: !isPinnedSubCommand,
|
pin: !isPinnedSubCommand,
|
||||||
PinLocation.TopLevel,
|
PinLocation.TopLevel,
|
||||||
_settingsModel,
|
_settingsService,
|
||||||
_topLevelCommandManager);
|
_topLevelCommandManager);
|
||||||
|
|
||||||
var contextItem = new PinToContextItem(pinToTopLevelCommand, commandItem);
|
var contextItem = new PinToContextItem(pinToTopLevelCommand, commandItem);
|
||||||
@@ -168,22 +169,22 @@ internal sealed partial class CommandPaletteContextMenuFactory : IContextMenuFac
|
|||||||
List<IContextItem> moreCommands,
|
List<IContextItem> moreCommands,
|
||||||
CommandItemViewModel commandItem)
|
CommandItemViewModel commandItem)
|
||||||
{
|
{
|
||||||
if (!_settingsModel.EnableDock)
|
if (!_settingsService.Settings.EnableDock)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var inStartBands = _settingsModel.DockSettings.StartBands.Any(band => MatchesBand(band, itemId, providerId));
|
var inStartBands = _settingsService.Settings.DockSettings.StartBands.Any(band => MatchesBand(band, itemId, providerId));
|
||||||
var inCenterBands = _settingsModel.DockSettings.CenterBands.Any(band => MatchesBand(band, itemId, providerId));
|
var inCenterBands = _settingsService.Settings.DockSettings.CenterBands.Any(band => MatchesBand(band, itemId, providerId));
|
||||||
var inEndBands = _settingsModel.DockSettings.EndBands.Any(band => MatchesBand(band, itemId, providerId));
|
var inEndBands = _settingsService.Settings.DockSettings.EndBands.Any(band => MatchesBand(band, itemId, providerId));
|
||||||
var alreadyPinned = inStartBands || inCenterBands || inEndBands; /** &&
|
var alreadyPinned = inStartBands || inCenterBands || inEndBands; /** &&
|
||||||
_settingsModel.DockSettings.PinnedCommands.Contains(this.Id)**/
|
_settingsService.Settings.DockSettings.PinnedCommands.Contains(this.Id)**/
|
||||||
var pinToTopLevelCommand = new PinToCommand(
|
var pinToTopLevelCommand = new PinToCommand(
|
||||||
commandId: itemId,
|
commandId: itemId,
|
||||||
providerId: providerId,
|
providerId: providerId,
|
||||||
pin: !alreadyPinned,
|
pin: !alreadyPinned,
|
||||||
PinLocation.Dock,
|
PinLocation.Dock,
|
||||||
_settingsModel,
|
_settingsService,
|
||||||
_topLevelCommandManager);
|
_topLevelCommandManager);
|
||||||
|
|
||||||
var contextItem = new PinToContextItem(pinToTopLevelCommand, commandItem);
|
var contextItem = new PinToContextItem(pinToTopLevelCommand, commandItem);
|
||||||
@@ -231,7 +232,7 @@ internal sealed partial class CommandPaletteContextMenuFactory : IContextMenuFac
|
|||||||
{
|
{
|
||||||
private readonly string _commandId;
|
private readonly string _commandId;
|
||||||
private readonly string _providerId;
|
private readonly string _providerId;
|
||||||
private readonly SettingsModel _settings;
|
private readonly ISettingsService _settingsService;
|
||||||
private readonly TopLevelCommandManager _topLevelCommandManager;
|
private readonly TopLevelCommandManager _topLevelCommandManager;
|
||||||
private readonly bool _pin;
|
private readonly bool _pin;
|
||||||
private readonly PinLocation _pinLocation;
|
private readonly PinLocation _pinLocation;
|
||||||
@@ -251,13 +252,13 @@ internal sealed partial class CommandPaletteContextMenuFactory : IContextMenuFac
|
|||||||
string providerId,
|
string providerId,
|
||||||
bool pin,
|
bool pin,
|
||||||
PinLocation pinLocation,
|
PinLocation pinLocation,
|
||||||
SettingsModel settings,
|
ISettingsService settingsService,
|
||||||
TopLevelCommandManager topLevelCommandManager)
|
TopLevelCommandManager topLevelCommandManager)
|
||||||
{
|
{
|
||||||
_commandId = commandId;
|
_commandId = commandId;
|
||||||
_providerId = providerId;
|
_providerId = providerId;
|
||||||
_pinLocation = pinLocation;
|
_pinLocation = pinLocation;
|
||||||
_settings = settings;
|
_settingsService = settingsService;
|
||||||
_topLevelCommandManager = topLevelCommandManager;
|
_topLevelCommandManager = topLevelCommandManager;
|
||||||
_pin = pin;
|
_pin = pin;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,10 +18,10 @@ public sealed partial class FallbackRanker : UserControl
|
|||||||
{
|
{
|
||||||
this.InitializeComponent();
|
this.InitializeComponent();
|
||||||
|
|
||||||
var settings = App.Current.Services.GetService<SettingsModel>()!;
|
|
||||||
var topLevelCommandManager = App.Current.Services.GetService<TopLevelCommandManager>()!;
|
var topLevelCommandManager = App.Current.Services.GetService<TopLevelCommandManager>()!;
|
||||||
var themeService = App.Current.Services.GetService<IThemeService>()!;
|
var themeService = App.Current.Services.GetService<IThemeService>()!;
|
||||||
viewModel = new SettingsViewModel(settings, topLevelCommandManager, _mainTaskScheduler, themeService);
|
var settingsService = App.Current.Services.GetRequiredService<ISettingsService>();
|
||||||
|
viewModel = new SettingsViewModel(topLevelCommandManager, _mainTaskScheduler, themeService, settingsService);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ListView_DragItemsCompleted(ListViewBase sender, DragItemsCompletedEventArgs args)
|
private void ListView_DragItemsCompleted(ListViewBase sender, DragItemsCompletedEventArgs args)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using Microsoft.CmdPal.UI.Helpers;
|
|||||||
using Microsoft.CmdPal.UI.ViewModels;
|
using Microsoft.CmdPal.UI.ViewModels;
|
||||||
using Microsoft.CmdPal.UI.ViewModels.Commands;
|
using Microsoft.CmdPal.UI.ViewModels.Commands;
|
||||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||||
|
using Microsoft.CmdPal.UI.ViewModels.Services;
|
||||||
using Microsoft.CmdPal.UI.Views;
|
using Microsoft.CmdPal.UI.Views;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.UI.Dispatching;
|
using Microsoft.UI.Dispatching;
|
||||||
@@ -49,7 +50,7 @@ public sealed partial class SearchBar : UserControl,
|
|||||||
// 0.6+ suggestions
|
// 0.6+ suggestions
|
||||||
private string? _textToSuggest;
|
private string? _textToSuggest;
|
||||||
|
|
||||||
private SettingsModel Settings => App.Current.Services.GetRequiredService<SettingsModel>();
|
private SettingsModel Settings => App.Current.Services.GetRequiredService<ISettingsService>().Settings;
|
||||||
|
|
||||||
public PageViewModel? CurrentPageViewModel
|
public PageViewModel? CurrentPageViewModel
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ public sealed partial class DockWindow : WindowEx,
|
|||||||
#pragma warning restore SA1306 // Field names should begin with lower-case letter
|
#pragma warning restore SA1306 // Field names should begin with lower-case letter
|
||||||
|
|
||||||
private readonly IThemeService _themeService;
|
private readonly IThemeService _themeService;
|
||||||
|
private readonly ISettingsService _settingsService;
|
||||||
private readonly DockWindowViewModel _windowViewModel;
|
private readonly DockWindowViewModel _windowViewModel;
|
||||||
private readonly HiddenOwnerWindowBehavior _hiddenOwnerWindowBehavior = new();
|
private readonly HiddenOwnerWindowBehavior _hiddenOwnerWindowBehavior = new();
|
||||||
|
|
||||||
@@ -68,8 +69,9 @@ public sealed partial class DockWindow : WindowEx,
|
|||||||
public DockWindow()
|
public DockWindow()
|
||||||
{
|
{
|
||||||
var serviceProvider = App.Current.Services;
|
var serviceProvider = App.Current.Services;
|
||||||
var mainSettings = serviceProvider.GetService<SettingsModel>()!;
|
var mainSettings = serviceProvider.GetRequiredService<ISettingsService>().Settings;
|
||||||
mainSettings.SettingsChanged += SettingsChangedHandler;
|
_settingsService = serviceProvider.GetRequiredService<ISettingsService>();
|
||||||
|
_settingsService.SettingsChanged += SettingsChangedHandler;
|
||||||
_settings = mainSettings.DockSettings;
|
_settings = mainSettings.DockSettings;
|
||||||
_lastSize = _settings.DockSize;
|
_lastSize = _settings.DockSize;
|
||||||
|
|
||||||
@@ -128,9 +130,9 @@ public sealed partial class DockWindow : WindowEx,
|
|||||||
UpdateSettingsOnUiThread();
|
UpdateSettingsOnUiThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SettingsChangedHandler(SettingsModel sender, object? args)
|
private void SettingsChangedHandler(ISettingsService sender, SettingsModel args)
|
||||||
{
|
{
|
||||||
_settings = sender.DockSettings;
|
_settings = args.DockSettings;
|
||||||
DispatcherQueue.TryEnqueue(UpdateSettingsOnUiThread);
|
DispatcherQueue.TryEnqueue(UpdateSettingsOnUiThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -621,9 +623,7 @@ public sealed partial class DockWindow : WindowEx,
|
|||||||
|
|
||||||
private void DockWindow_Closed(object sender, WindowEventArgs args)
|
private void DockWindow_Closed(object sender, WindowEventArgs args)
|
||||||
{
|
{
|
||||||
var serviceProvider = App.Current.Services;
|
_settingsService.SettingsChanged -= SettingsChangedHandler;
|
||||||
var settings = serviceProvider.GetService<SettingsModel>();
|
|
||||||
settings?.SettingsChanged -= SettingsChangedHandler;
|
|
||||||
_themeService.ThemeChanged -= ThemeService_ThemeChanged;
|
_themeService.ThemeChanged -= ThemeService_ThemeChanged;
|
||||||
DisposeAcrylic();
|
DisposeAcrylic();
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using Microsoft.CmdPal.UI.Messages;
|
|||||||
using Microsoft.CmdPal.UI.ViewModels;
|
using Microsoft.CmdPal.UI.ViewModels;
|
||||||
using Microsoft.CmdPal.UI.ViewModels.Commands;
|
using Microsoft.CmdPal.UI.ViewModels.Commands;
|
||||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||||
|
using Microsoft.CmdPal.UI.ViewModels.Services;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Automation.Peers;
|
using Microsoft.UI.Xaml.Automation.Peers;
|
||||||
@@ -183,7 +184,7 @@ public sealed partial class ListPage : Page,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var settings = App.Current.Services.GetService<SettingsModel>()!;
|
var settings = App.Current.Services.GetRequiredService<ISettingsService>().Settings;
|
||||||
if (settings.SingleClickActivates)
|
if (settings.SingleClickActivates)
|
||||||
{
|
{
|
||||||
ViewModel?.InvokeItemCommand.Execute(item);
|
ViewModel?.InvokeItemCommand.Execute(item);
|
||||||
@@ -203,7 +204,7 @@ public sealed partial class ListPage : Page,
|
|||||||
{
|
{
|
||||||
if (ItemView.SelectedItem is ListItemViewModel vm)
|
if (ItemView.SelectedItem is ListItemViewModel vm)
|
||||||
{
|
{
|
||||||
var settings = App.Current.Services.GetService<SettingsModel>()!;
|
var settings = App.Current.Services.GetRequiredService<ISettingsService>().Settings;
|
||||||
if (!settings.SingleClickActivates)
|
if (!settings.SingleClickActivates)
|
||||||
{
|
{
|
||||||
ViewModel?.InvokeItemCommand.Execute(vm);
|
ViewModel?.InvokeItemCommand.Execute(vm);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using CommunityToolkit.Mvvm.Messaging;
|
|||||||
using Microsoft.CmdPal.UI.Messages;
|
using Microsoft.CmdPal.UI.Messages;
|
||||||
using Microsoft.CmdPal.UI.ViewModels;
|
using Microsoft.CmdPal.UI.ViewModels;
|
||||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||||
|
using Microsoft.CmdPal.UI.ViewModels.Services;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Windows.Win32;
|
using Windows.Win32;
|
||||||
using Windows.Win32.Foundation;
|
using Windows.Win32.Foundation;
|
||||||
@@ -25,7 +26,7 @@ internal sealed partial class TrayIconService
|
|||||||
private const uint MY_NOTIFY_ID = 1000;
|
private const uint MY_NOTIFY_ID = 1000;
|
||||||
private const uint WM_TRAY_ICON = PInvoke.WM_USER + 1;
|
private const uint WM_TRAY_ICON = PInvoke.WM_USER + 1;
|
||||||
|
|
||||||
private readonly SettingsModel _settingsModel;
|
private readonly ISettingsService _settingsService;
|
||||||
private readonly uint WM_TASKBAR_RESTART;
|
private readonly uint WM_TASKBAR_RESTART;
|
||||||
|
|
||||||
private Window? _window;
|
private Window? _window;
|
||||||
@@ -36,9 +37,9 @@ internal sealed partial class TrayIconService
|
|||||||
private DestroyIconSafeHandle? _largeIcon;
|
private DestroyIconSafeHandle? _largeIcon;
|
||||||
private DestroyMenuSafeHandle? _popupMenu;
|
private DestroyMenuSafeHandle? _popupMenu;
|
||||||
|
|
||||||
public TrayIconService(SettingsModel settingsModel)
|
public TrayIconService(ISettingsService settingsService)
|
||||||
{
|
{
|
||||||
_settingsModel = settingsModel;
|
_settingsService = settingsService;
|
||||||
|
|
||||||
// TaskbarCreated is the message that's broadcast when explorer.exe
|
// TaskbarCreated is the message that's broadcast when explorer.exe
|
||||||
// restarts. We need to know when that happens to be able to bring our
|
// restarts. We need to know when that happens to be able to bring our
|
||||||
@@ -48,7 +49,7 @@ internal sealed partial class TrayIconService
|
|||||||
|
|
||||||
public void SetupTrayIcon(bool? showSystemTrayIcon = null)
|
public void SetupTrayIcon(bool? showSystemTrayIcon = null)
|
||||||
{
|
{
|
||||||
if (showSystemTrayIcon ?? _settingsModel.ShowSystemTrayIcon)
|
if (showSystemTrayIcon ?? _settingsService.Settings.ShowSystemTrayIcon)
|
||||||
{
|
{
|
||||||
if (_window is null)
|
if (_window is null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ public sealed partial class MainWindow : WindowEx,
|
|||||||
|
|
||||||
// Load our settings, and then also wire up a settings changed handler
|
// Load our settings, and then also wire up a settings changed handler
|
||||||
HotReloadSettings();
|
HotReloadSettings();
|
||||||
App.Current.Services.GetService<SettingsModel>()!.SettingsChanged += SettingsChangedHandler;
|
App.Current.Services.GetRequiredService<ISettingsService>().SettingsChanged += SettingsChangedHandler;
|
||||||
|
|
||||||
// Make sure that we update the acrylic theme when the OS theme changes
|
// Make sure that we update the acrylic theme when the OS theme changes
|
||||||
RootElement.ActualThemeChanged += (s, e) => DispatcherQueue.TryEnqueue(UpdateBackdrop);
|
RootElement.ActualThemeChanged += (s, e) => DispatcherQueue.TryEnqueue(UpdateBackdrop);
|
||||||
@@ -211,7 +211,7 @@ public sealed partial class MainWindow : WindowEx,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SettingsChangedHandler(SettingsModel sender, object? args)
|
private void SettingsChangedHandler(ISettingsService sender, SettingsModel args)
|
||||||
{
|
{
|
||||||
DispatcherQueue.TryEnqueue(HotReloadSettings);
|
DispatcherQueue.TryEnqueue(HotReloadSettings);
|
||||||
}
|
}
|
||||||
@@ -292,7 +292,7 @@ public sealed partial class MainWindow : WindowEx,
|
|||||||
|
|
||||||
private void RestoreWindowPositionFromSavedSettings()
|
private void RestoreWindowPositionFromSavedSettings()
|
||||||
{
|
{
|
||||||
var settings = App.Current.Services.GetService<SettingsModel>();
|
var settings = App.Current.Services.GetRequiredService<ISettingsService>().Settings;
|
||||||
RestoreWindowPosition(settings?.LastWindowPosition);
|
RestoreWindowPosition(settings?.LastWindowPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,7 +363,7 @@ public sealed partial class MainWindow : WindowEx,
|
|||||||
|
|
||||||
private void HotReloadSettings()
|
private void HotReloadSettings()
|
||||||
{
|
{
|
||||||
var settings = App.Current.Services.GetService<SettingsModel>()!;
|
var settings = App.Current.Services.GetRequiredService<ISettingsService>().Settings;
|
||||||
|
|
||||||
SetupHotkey(settings);
|
SetupHotkey(settings);
|
||||||
App.Current.Services.GetService<TrayIconService>()!.SetupTrayIcon(settings.ShowSystemTrayIcon);
|
App.Current.Services.GetService<TrayIconService>()!.SetupTrayIcon(settings.ShowSystemTrayIcon);
|
||||||
@@ -707,7 +707,7 @@ public sealed partial class MainWindow : WindowEx,
|
|||||||
{
|
{
|
||||||
_isLoadedFromDock = false;
|
_isLoadedFromDock = false;
|
||||||
|
|
||||||
var settings = App.Current.Services.GetService<SettingsModel>()!;
|
var settings = App.Current.Services.GetRequiredService<ISettingsService>().Settings;
|
||||||
|
|
||||||
// Start session tracking
|
// Start session tracking
|
||||||
_sessionStopwatch = Stopwatch.StartNew();
|
_sessionStopwatch = Stopwatch.StartNew();
|
||||||
@@ -902,7 +902,8 @@ public sealed partial class MainWindow : WindowEx,
|
|||||||
UpdateWindowPositionInMemory();
|
UpdateWindowPositionInMemory();
|
||||||
}
|
}
|
||||||
|
|
||||||
var settings = serviceProvider.GetService<SettingsModel>();
|
var settingsService = serviceProvider.GetRequiredService<ISettingsService>();
|
||||||
|
var settings = settingsService.Settings;
|
||||||
if (settings is not null)
|
if (settings is not null)
|
||||||
{
|
{
|
||||||
// If we were last shown from the dock, _currentWindowPosition still holds
|
// If we were last shown from the dock, _currentWindowPosition still holds
|
||||||
@@ -910,7 +911,7 @@ public sealed partial class MainWindow : WindowEx,
|
|||||||
if (_currentWindowPosition.IsSizeValid)
|
if (_currentWindowPosition.IsSizeValid)
|
||||||
{
|
{
|
||||||
settings.LastWindowPosition = _currentWindowPosition;
|
settings.LastWindowPosition = _currentWindowPosition;
|
||||||
SettingsModel.SaveSettings(settings);
|
settingsService.Save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1080,7 +1081,7 @@ public sealed partial class MainWindow : WindowEx,
|
|||||||
}
|
}
|
||||||
else if (uri.StartsWith("x-cmdpal://reload", StringComparison.OrdinalIgnoreCase))
|
else if (uri.StartsWith("x-cmdpal://reload", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
var settings = App.Current.Services.GetService<SettingsModel>();
|
var settings = App.Current.Services.GetRequiredService<ISettingsService>().Settings;
|
||||||
if (settings?.AllowExternalReload == true)
|
if (settings?.AllowExternalReload == true)
|
||||||
{
|
{
|
||||||
Logger.LogInfo("External Reload triggered");
|
Logger.LogInfo("External Reload triggered");
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ using Microsoft.CmdPal.UI.Services;
|
|||||||
using Microsoft.CmdPal.UI.Settings;
|
using Microsoft.CmdPal.UI.Settings;
|
||||||
using Microsoft.CmdPal.UI.ViewModels;
|
using Microsoft.CmdPal.UI.ViewModels;
|
||||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||||
|
using Microsoft.CmdPal.UI.ViewModels.Services;
|
||||||
using Microsoft.CommandPalette.Extensions;
|
using Microsoft.CommandPalette.Extensions;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.PowerToys.Telemetry;
|
using Microsoft.PowerToys.Telemetry;
|
||||||
@@ -111,7 +112,7 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
|
|||||||
var pageAnnouncementFormat = ResourceLoaderInstance.GetString("ScreenReader_Announcement_NavigatedToPage0");
|
var pageAnnouncementFormat = ResourceLoaderInstance.GetString("ScreenReader_Announcement_NavigatedToPage0");
|
||||||
_pageNavigatedAnnouncement = CompositeFormat.Parse(pageAnnouncementFormat);
|
_pageNavigatedAnnouncement = CompositeFormat.Parse(pageAnnouncementFormat);
|
||||||
|
|
||||||
if (App.Current.Services.GetService<SettingsModel>()!.EnableDock)
|
if (App.Current.Services.GetRequiredService<ISettingsService>().Settings.EnableDock)
|
||||||
{
|
{
|
||||||
_dockWindow = new DockWindow();
|
_dockWindow = new DockWindow();
|
||||||
_dockWindow.Show();
|
_dockWindow.Show();
|
||||||
@@ -125,14 +126,14 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var settings = App.Current.Services.GetService<SettingsModel>()!;
|
var settings = App.Current.Services.GetRequiredService<ISettingsService>().Settings;
|
||||||
return settings.DisableAnimations ? _noAnimation : _slideRightTransition;
|
return settings.DisableAnimations ? _noAnimation : _slideRightTransition;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Receive(NavigateBackMessage message)
|
public void Receive(NavigateBackMessage message)
|
||||||
{
|
{
|
||||||
var settings = App.Current.Services.GetService<SettingsModel>()!;
|
var settings = App.Current.Services.GetRequiredService<ISettingsService>().Settings;
|
||||||
|
|
||||||
if (RootFrame.CanGoBack)
|
if (RootFrame.CanGoBack)
|
||||||
{
|
{
|
||||||
@@ -362,7 +363,7 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
|
|||||||
|
|
||||||
private void SummonOnUiThread(HotkeySummonMessage message)
|
private void SummonOnUiThread(HotkeySummonMessage message)
|
||||||
{
|
{
|
||||||
var settings = App.Current.Services.GetService<SettingsModel>()!;
|
var settings = App.Current.Services.GetRequiredService<ISettingsService>().Settings;
|
||||||
var commandId = message.CommandId;
|
var commandId = message.CommandId;
|
||||||
var isRoot = string.IsNullOrEmpty(commandId);
|
var isRoot = string.IsNullOrEmpty(commandId);
|
||||||
if (isRoot)
|
if (isRoot)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using Microsoft.CmdPal.Common.Services;
|
|||||||
using Microsoft.CmdPal.Common.Text;
|
using Microsoft.CmdPal.Common.Text;
|
||||||
using Microsoft.CmdPal.UI.ViewModels;
|
using Microsoft.CmdPal.UI.ViewModels;
|
||||||
using Microsoft.CmdPal.UI.ViewModels.MainPage;
|
using Microsoft.CmdPal.UI.ViewModels.MainPage;
|
||||||
|
using Microsoft.CmdPal.UI.ViewModels.Services;
|
||||||
using Microsoft.CommandPalette.Extensions;
|
using Microsoft.CommandPalette.Extensions;
|
||||||
using WinRT;
|
using WinRT;
|
||||||
|
|
||||||
@@ -23,13 +24,13 @@ internal sealed class PowerToysRootPageService : IRootPageService
|
|||||||
private IExtensionWrapper? _activeExtension;
|
private IExtensionWrapper? _activeExtension;
|
||||||
private Lazy<MainListPage> _mainListPage;
|
private Lazy<MainListPage> _mainListPage;
|
||||||
|
|
||||||
public PowerToysRootPageService(TopLevelCommandManager topLevelCommandManager, SettingsModel settings, AliasManager aliasManager, AppStateModel appStateModel, IFuzzyMatcherProvider fuzzyMatcherProvider)
|
public PowerToysRootPageService(TopLevelCommandManager topLevelCommandManager, AliasManager aliasManager, IFuzzyMatcherProvider fuzzyMatcherProvider, ISettingsService settingsService, IAppStateService appStateService)
|
||||||
{
|
{
|
||||||
_tlcManager = topLevelCommandManager;
|
_tlcManager = topLevelCommandManager;
|
||||||
|
|
||||||
_mainListPage = new Lazy<MainListPage>(() =>
|
_mainListPage = new Lazy<MainListPage>(() =>
|
||||||
{
|
{
|
||||||
return new MainListPage(_tlcManager, settings, aliasManager, appStateModel, fuzzyMatcherProvider);
|
return new MainListPage(_tlcManager, aliasManager, fuzzyMatcherProvider, settingsService, appStateService);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,32 +4,33 @@
|
|||||||
|
|
||||||
using Microsoft.CmdPal.Common.Services;
|
using Microsoft.CmdPal.Common.Services;
|
||||||
using Microsoft.CmdPal.UI.ViewModels;
|
using Microsoft.CmdPal.UI.ViewModels;
|
||||||
|
using Microsoft.CmdPal.UI.ViewModels.Services;
|
||||||
|
|
||||||
namespace Microsoft.CmdPal.UI;
|
namespace Microsoft.CmdPal.UI;
|
||||||
|
|
||||||
internal sealed class RunHistoryService : IRunHistoryService
|
internal sealed class RunHistoryService : IRunHistoryService
|
||||||
{
|
{
|
||||||
private readonly AppStateModel _appStateModel;
|
private readonly IAppStateService _appStateService;
|
||||||
|
|
||||||
public RunHistoryService(AppStateModel appStateModel)
|
public RunHistoryService(IAppStateService appStateService)
|
||||||
{
|
{
|
||||||
_appStateModel = appStateModel;
|
_appStateService = appStateService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadOnlyList<string> GetRunHistory()
|
public IReadOnlyList<string> GetRunHistory()
|
||||||
{
|
{
|
||||||
if (_appStateModel.RunHistory.Count == 0)
|
if (_appStateService.State.RunHistory.Count == 0)
|
||||||
{
|
{
|
||||||
var history = Microsoft.Terminal.UI.RunHistory.CreateRunHistory();
|
var history = Microsoft.Terminal.UI.RunHistory.CreateRunHistory();
|
||||||
_appStateModel.RunHistory.AddRange(history);
|
_appStateService.State.RunHistory.AddRange(history);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _appStateModel.RunHistory;
|
return _appStateService.State.RunHistory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearRunHistory()
|
public void ClearRunHistory()
|
||||||
{
|
{
|
||||||
_appStateModel.RunHistory.Clear();
|
_appStateService.State.RunHistory.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddRunHistoryItem(string item)
|
public void AddRunHistoryItem(string item)
|
||||||
@@ -40,11 +41,11 @@ internal sealed class RunHistoryService : IRunHistoryService
|
|||||||
return; // Do not add empty or whitespace items
|
return; // Do not add empty or whitespace items
|
||||||
}
|
}
|
||||||
|
|
||||||
_appStateModel.RunHistory.Remove(item);
|
_appStateService.State.RunHistory.Remove(item);
|
||||||
|
|
||||||
// Add the item to the front of the history
|
// Add the item to the front of the history
|
||||||
_appStateModel.RunHistory.Insert(0, item);
|
_appStateService.State.RunHistory.Insert(0, item);
|
||||||
|
|
||||||
AppStateModel.SaveState(_appStateModel);
|
_appStateService.Save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ internal sealed partial class ThemeService : IThemeService, IDisposable
|
|||||||
private static readonly TimeSpan ReloadDebounceInterval = TimeSpan.FromMilliseconds(500);
|
private static readonly TimeSpan ReloadDebounceInterval = TimeSpan.FromMilliseconds(500);
|
||||||
|
|
||||||
private readonly UISettings _uiSettings;
|
private readonly UISettings _uiSettings;
|
||||||
private readonly SettingsModel _settings;
|
private readonly ISettingsService _settingsService;
|
||||||
|
|
||||||
private readonly ResourceSwapper _resourceSwapper;
|
private readonly ResourceSwapper _resourceSwapper;
|
||||||
private readonly NormalThemeProvider _normalThemeProvider;
|
private readonly NormalThemeProvider _normalThemeProvider;
|
||||||
private readonly ColorfulThemeProvider _colorfulThemeProvider;
|
private readonly ColorfulThemeProvider _colorfulThemeProvider;
|
||||||
@@ -76,32 +77,32 @@ internal sealed partial class ThemeService : IThemeService, IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// provider selection
|
// provider selection
|
||||||
var themeColorIntensity = Math.Clamp(_settings.CustomThemeColorIntensity, 0, 100);
|
var themeColorIntensity = Math.Clamp(_settingsService.Settings.CustomThemeColorIntensity, 0, 100);
|
||||||
var imageTintIntensity = Math.Clamp(_settings.BackgroundImageTintIntensity, 0, 100);
|
var imageTintIntensity = Math.Clamp(_settingsService.Settings.BackgroundImageTintIntensity, 0, 100);
|
||||||
var effectiveColorIntensity = _settings.ColorizationMode == ColorizationMode.Image
|
var effectiveColorIntensity = _settingsService.Settings.ColorizationMode == ColorizationMode.Image
|
||||||
? imageTintIntensity
|
? imageTintIntensity
|
||||||
: themeColorIntensity;
|
: themeColorIntensity;
|
||||||
|
|
||||||
IThemeProvider provider = UseColorfulProvider(effectiveColorIntensity) ? _colorfulThemeProvider : _normalThemeProvider;
|
IThemeProvider provider = UseColorfulProvider(effectiveColorIntensity) ? _colorfulThemeProvider : _normalThemeProvider;
|
||||||
|
|
||||||
// Calculate values
|
// Calculate values
|
||||||
var tint = _settings.ColorizationMode switch
|
var tint = _settingsService.Settings.ColorizationMode switch
|
||||||
{
|
{
|
||||||
ColorizationMode.CustomColor => _settings.CustomThemeColor,
|
ColorizationMode.CustomColor => _settingsService.Settings.CustomThemeColor,
|
||||||
ColorizationMode.WindowsAccentColor => _uiSettings.GetColorValue(UIColorType.Accent),
|
ColorizationMode.WindowsAccentColor => _uiSettings.GetColorValue(UIColorType.Accent),
|
||||||
ColorizationMode.Image => _settings.CustomThemeColor,
|
ColorizationMode.Image => _settingsService.Settings.CustomThemeColor,
|
||||||
_ => Colors.Transparent,
|
_ => Colors.Transparent,
|
||||||
};
|
};
|
||||||
var effectiveTheme = GetElementTheme((ElementTheme)_settings.Theme);
|
var effectiveTheme = GetElementTheme((ElementTheme)_settingsService.Settings.Theme);
|
||||||
var imageSource = _settings.ColorizationMode == ColorizationMode.Image
|
var imageSource = _settingsService.Settings.ColorizationMode == ColorizationMode.Image
|
||||||
? LoadImageSafe(_settings.BackgroundImagePath)
|
? LoadImageSafe(_settingsService.Settings.BackgroundImagePath)
|
||||||
: null;
|
: null;
|
||||||
var stretch = _settings.BackgroundImageFit switch
|
var stretch = _settingsService.Settings.BackgroundImageFit switch
|
||||||
{
|
{
|
||||||
BackgroundImageFit.Fill => Stretch.Fill,
|
BackgroundImageFit.Fill => Stretch.Fill,
|
||||||
_ => Stretch.UniformToFill,
|
_ => Stretch.UniformToFill,
|
||||||
};
|
};
|
||||||
var opacity = Math.Clamp(_settings.BackgroundImageOpacity, 0, 100) / 100.0;
|
var opacity = Math.Clamp(_settingsService.Settings.BackgroundImageOpacity, 0, 100) / 100.0;
|
||||||
|
|
||||||
// create input and offload to actual theme provider
|
// create input and offload to actual theme provider
|
||||||
var context = new ThemeContext
|
var context = new ThemeContext
|
||||||
@@ -112,16 +113,16 @@ internal sealed partial class ThemeService : IThemeService, IDisposable
|
|||||||
BackgroundImageSource = imageSource,
|
BackgroundImageSource = imageSource,
|
||||||
BackgroundImageStretch = stretch,
|
BackgroundImageStretch = stretch,
|
||||||
BackgroundImageOpacity = opacity,
|
BackgroundImageOpacity = opacity,
|
||||||
BackdropStyle = _settings.BackdropStyle,
|
BackdropStyle = _settingsService.Settings.BackdropStyle,
|
||||||
BackdropOpacity = Math.Clamp(_settings.BackdropOpacity, 0, 100) / 100f,
|
BackdropOpacity = Math.Clamp(_settingsService.Settings.BackdropOpacity, 0, 100) / 100f,
|
||||||
};
|
};
|
||||||
var backdrop = provider.GetBackdropParameters(context);
|
var backdrop = provider.GetBackdropParameters(context);
|
||||||
var blur = _settings.BackgroundImageBlurAmount;
|
var blur = _settingsService.Settings.BackgroundImageBlurAmount;
|
||||||
var brightness = _settings.BackgroundImageBrightness;
|
var brightness = _settingsService.Settings.BackgroundImageBrightness;
|
||||||
|
|
||||||
// Create public snapshot (no provider!)
|
// Create public snapshot (no provider!)
|
||||||
var hasColorization = effectiveColorIntensity > 0
|
var hasColorization = effectiveColorIntensity > 0
|
||||||
&& _settings.ColorizationMode is ColorizationMode.CustomColor or ColorizationMode.WindowsAccentColor or ColorizationMode.Image;
|
&& _settingsService.Settings.ColorizationMode is ColorizationMode.CustomColor or ColorizationMode.WindowsAccentColor or ColorizationMode.Image;
|
||||||
|
|
||||||
var snapshot = new ThemeSnapshot
|
var snapshot = new ThemeSnapshot
|
||||||
{
|
{
|
||||||
@@ -149,7 +150,7 @@ internal sealed partial class ThemeService : IThemeService, IDisposable
|
|||||||
Interlocked.Exchange(ref _currentState, newState);
|
Interlocked.Exchange(ref _currentState, newState);
|
||||||
|
|
||||||
// Compute DockThemeSnapshot from DockSettings
|
// Compute DockThemeSnapshot from DockSettings
|
||||||
var dockSettings = _settings.DockSettings;
|
var dockSettings = _settingsService.Settings.DockSettings;
|
||||||
var dockIntensity = Math.Clamp(dockSettings.CustomThemeColorIntensity, 0, 100);
|
var dockIntensity = Math.Clamp(dockSettings.CustomThemeColorIntensity, 0, 100);
|
||||||
IThemeProvider dockProvider = dockIntensity > 0 && dockSettings.ColorizationMode is ColorizationMode.CustomColor or ColorizationMode.WindowsAccentColor or ColorizationMode.Image
|
IThemeProvider dockProvider = dockIntensity > 0 && dockSettings.ColorizationMode is ColorizationMode.CustomColor or ColorizationMode.WindowsAccentColor or ColorizationMode.Image
|
||||||
? _colorfulThemeProvider
|
? _colorfulThemeProvider
|
||||||
@@ -208,8 +209,8 @@ internal sealed partial class ThemeService : IThemeService, IDisposable
|
|||||||
|
|
||||||
private bool UseColorfulProvider(int effectiveColorIntensity)
|
private bool UseColorfulProvider(int effectiveColorIntensity)
|
||||||
{
|
{
|
||||||
return _settings.ColorizationMode == ColorizationMode.Image
|
return _settingsService.Settings.ColorizationMode == ColorizationMode.Image
|
||||||
|| (effectiveColorIntensity > 0 && _settings.ColorizationMode is ColorizationMode.CustomColor or ColorizationMode.WindowsAccentColor);
|
|| (effectiveColorIntensity > 0 && _settingsService.Settings.ColorizationMode is ColorizationMode.CustomColor or ColorizationMode.WindowsAccentColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BitmapImage? LoadImageSafe(string? path)
|
private static BitmapImage? LoadImageSafe(string? path)
|
||||||
@@ -241,13 +242,12 @@ internal sealed partial class ThemeService : IThemeService, IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ThemeService(SettingsModel settings, ResourceSwapper resourceSwapper)
|
public ThemeService(ResourceSwapper resourceSwapper, ISettingsService settingsService)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(settings);
|
|
||||||
ArgumentNullException.ThrowIfNull(resourceSwapper);
|
ArgumentNullException.ThrowIfNull(resourceSwapper);
|
||||||
|
|
||||||
_settings = settings;
|
_settingsService = settingsService;
|
||||||
_settings.SettingsChanged += SettingsOnSettingsChanged;
|
_settingsService.SettingsChanged += SettingsOnSettingsChanged;
|
||||||
|
|
||||||
_resourceSwapper = resourceSwapper;
|
_resourceSwapper = resourceSwapper;
|
||||||
|
|
||||||
@@ -319,7 +319,7 @@ internal sealed partial class ThemeService : IThemeService, IDisposable
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SettingsOnSettingsChanged(SettingsModel sender, object? args)
|
private void SettingsOnSettingsChanged(ISettingsService sender, SettingsModel args)
|
||||||
{
|
{
|
||||||
RequestReload();
|
RequestReload();
|
||||||
}
|
}
|
||||||
@@ -339,7 +339,7 @@ internal sealed partial class ThemeService : IThemeService, IDisposable
|
|||||||
_disposed = true;
|
_disposed = true;
|
||||||
_dispatcherQueueTimer?.Stop();
|
_dispatcherQueueTimer?.Stop();
|
||||||
_uiSettings.ColorValuesChanged -= UiSettings_ColorValuesChanged;
|
_uiSettings.ColorValuesChanged -= UiSettings_ColorValuesChanged;
|
||||||
_settings.SettingsChanged -= SettingsOnSettingsChanged;
|
_settingsService.SettingsChanged -= SettingsOnSettingsChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class InternalThemeState
|
private sealed class InternalThemeState
|
||||||
|
|||||||
@@ -31,10 +31,10 @@ public sealed partial class AppearancePage : Page
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
var settings = App.Current.Services.GetService<SettingsModel>()!;
|
|
||||||
var themeService = App.Current.Services.GetRequiredService<IThemeService>();
|
var themeService = App.Current.Services.GetRequiredService<IThemeService>();
|
||||||
var topLevelCommandManager = App.Current.Services.GetService<TopLevelCommandManager>()!;
|
var topLevelCommandManager = App.Current.Services.GetService<TopLevelCommandManager>()!;
|
||||||
ViewModel = new SettingsViewModel(settings, topLevelCommandManager, _mainTaskScheduler, themeService);
|
var settingsService = App.Current.Services.GetRequiredService<ISettingsService>();
|
||||||
|
ViewModel = new SettingsViewModel(topLevelCommandManager, _mainTaskScheduler, themeService, settingsService);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void PickBackgroundImage_Click(object sender, RoutedEventArgs e)
|
private async void PickBackgroundImage_Click(object sender, RoutedEventArgs e)
|
||||||
|
|||||||
@@ -28,11 +28,11 @@ public sealed partial class DockSettingsPage : Page
|
|||||||
{
|
{
|
||||||
this.InitializeComponent();
|
this.InitializeComponent();
|
||||||
|
|
||||||
var settings = App.Current.Services.GetService<SettingsModel>()!;
|
|
||||||
var themeService = App.Current.Services.GetService<IThemeService>()!;
|
var themeService = App.Current.Services.GetService<IThemeService>()!;
|
||||||
var topLevelCommandManager = App.Current.Services.GetService<TopLevelCommandManager>()!;
|
var topLevelCommandManager = App.Current.Services.GetService<TopLevelCommandManager>()!;
|
||||||
|
var settingsService = App.Current.Services.GetRequiredService<ISettingsService>();
|
||||||
|
|
||||||
ViewModel = new SettingsViewModel(settings, topLevelCommandManager, _mainTaskScheduler, themeService);
|
ViewModel = new SettingsViewModel(topLevelCommandManager, _mainTaskScheduler, themeService, settingsService);
|
||||||
|
|
||||||
// Initialize UI state
|
// Initialize UI state
|
||||||
InitializeSettings();
|
InitializeSettings();
|
||||||
@@ -195,7 +195,8 @@ public sealed partial class DockSettingsPage : Page
|
|||||||
|
|
||||||
// var allBands = GetAllBands();
|
// var allBands = GetAllBands();
|
||||||
var tlcManager = App.Current.Services.GetService<TopLevelCommandManager>()!;
|
var tlcManager = App.Current.Services.GetService<TopLevelCommandManager>()!;
|
||||||
var settingsModel = App.Current.Services.GetService<SettingsModel>()!;
|
var settingsModel = App.Current.Services.GetRequiredService<ISettingsService>().Settings;
|
||||||
|
var settingsService = App.Current.Services.GetRequiredService<ISettingsService>();
|
||||||
var dockViewModel = App.Current.Services.GetService<DockViewModel>()!;
|
var dockViewModel = App.Current.Services.GetService<DockViewModel>()!;
|
||||||
var allBands = tlcManager.GetDockBandsSnapshot();
|
var allBands = tlcManager.GetDockBandsSnapshot();
|
||||||
foreach (var band in allBands)
|
foreach (var band in allBands)
|
||||||
@@ -208,7 +209,7 @@ public sealed partial class DockSettingsPage : Page
|
|||||||
dockSettingsModel: setting,
|
dockSettingsModel: setting,
|
||||||
topLevelAdapter: band,
|
topLevelAdapter: band,
|
||||||
bandViewModel: bandVm,
|
bandViewModel: bandVm,
|
||||||
settingsModel: settingsModel
|
settingsService: settingsService
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,10 +24,10 @@ public sealed partial class ExtensionsPage : Page
|
|||||||
{
|
{
|
||||||
this.InitializeComponent();
|
this.InitializeComponent();
|
||||||
|
|
||||||
var settings = App.Current.Services.GetService<SettingsModel>()!;
|
|
||||||
var topLevelCommandManager = App.Current.Services.GetService<TopLevelCommandManager>()!;
|
var topLevelCommandManager = App.Current.Services.GetService<TopLevelCommandManager>()!;
|
||||||
var themeService = App.Current.Services.GetService<IThemeService>()!;
|
var themeService = App.Current.Services.GetService<IThemeService>()!;
|
||||||
viewModel = new SettingsViewModel(settings, topLevelCommandManager, _mainTaskScheduler, themeService);
|
var settingsService = App.Current.Services.GetRequiredService<ISettingsService>();
|
||||||
|
viewModel = new SettingsViewModel(topLevelCommandManager, _mainTaskScheduler, themeService, settingsService);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SettingsCard_Click(object sender, RoutedEventArgs e)
|
private void SettingsCard_Click(object sender, RoutedEventArgs e)
|
||||||
|
|||||||
@@ -23,11 +23,11 @@ public sealed partial class GeneralPage : Page
|
|||||||
{
|
{
|
||||||
this.InitializeComponent();
|
this.InitializeComponent();
|
||||||
|
|
||||||
var settings = App.Current.Services.GetService<SettingsModel>()!;
|
|
||||||
var topLevelCommandManager = App.Current.Services.GetService<TopLevelCommandManager>()!;
|
var topLevelCommandManager = App.Current.Services.GetService<TopLevelCommandManager>()!;
|
||||||
var themeService = App.Current.Services.GetService<IThemeService>()!;
|
var themeService = App.Current.Services.GetService<IThemeService>()!;
|
||||||
|
var settingsService = App.Current.Services.GetRequiredService<ISettingsService>();
|
||||||
_appInfoService = App.Current.Services.GetRequiredService<IApplicationInfoService>();
|
_appInfoService = App.Current.Services.GetRequiredService<IApplicationInfoService>();
|
||||||
viewModel = new SettingsViewModel(settings, topLevelCommandManager, _mainTaskScheduler, themeService);
|
viewModel = new SettingsViewModel(topLevelCommandManager, _mainTaskScheduler, themeService, settingsService);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ApplicationVersion
|
public string ApplicationVersion
|
||||||
|
|||||||
@@ -0,0 +1,163 @@
|
|||||||
|
// 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;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using Microsoft.CmdPal.Common.Services;
|
||||||
|
using Microsoft.CmdPal.UI.ViewModels.Services;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using Moq;
|
||||||
|
|
||||||
|
namespace Microsoft.CmdPal.UI.ViewModels.UnitTests;
|
||||||
|
|
||||||
|
[TestClass]
|
||||||
|
public class AppStateServiceTests
|
||||||
|
{
|
||||||
|
private Mock<IPersistenceService> _mockPersistence = null!;
|
||||||
|
private Mock<IApplicationInfoService> _mockAppInfo = null!;
|
||||||
|
private string _testDirectory = null!;
|
||||||
|
|
||||||
|
[TestInitialize]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
_mockPersistence = new Mock<IPersistenceService>();
|
||||||
|
_mockAppInfo = new Mock<IApplicationInfoService>();
|
||||||
|
_testDirectory = Path.Combine(Path.GetTempPath(), $"CmdPalTest_{Guid.NewGuid():N}");
|
||||||
|
_mockAppInfo.Setup(a => a.ConfigDirectory).Returns(_testDirectory);
|
||||||
|
|
||||||
|
// Default: Load returns a new AppStateModel
|
||||||
|
_mockPersistence
|
||||||
|
.Setup(p => p.Load(
|
||||||
|
It.IsAny<string>(),
|
||||||
|
It.IsAny<System.Text.Json.Serialization.Metadata.JsonTypeInfo<AppStateModel>>()))
|
||||||
|
.Returns(new AppStateModel());
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Constructor_LoadsState_ViaPersistenceService()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expectedState = new AppStateModel
|
||||||
|
{
|
||||||
|
RunHistory = new List<string> { "command1", "command2" },
|
||||||
|
};
|
||||||
|
_mockPersistence
|
||||||
|
.Setup(p => p.Load(
|
||||||
|
It.IsAny<string>(),
|
||||||
|
It.IsAny<System.Text.Json.Serialization.Metadata.JsonTypeInfo<AppStateModel>>()))
|
||||||
|
.Returns(expectedState);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var service = new AppStateService(_mockPersistence.Object, _mockAppInfo.Object);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.IsNotNull(service.State);
|
||||||
|
Assert.AreEqual(2, service.State.RunHistory.Count);
|
||||||
|
Assert.AreEqual("command1", service.State.RunHistory[0]);
|
||||||
|
_mockPersistence.Verify(
|
||||||
|
p => p.Load(
|
||||||
|
It.IsAny<string>(),
|
||||||
|
It.IsAny<System.Text.Json.Serialization.Metadata.JsonTypeInfo<AppStateModel>>()),
|
||||||
|
Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void State_ReturnsLoadedModel()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expectedState = new AppStateModel();
|
||||||
|
_mockPersistence
|
||||||
|
.Setup(p => p.Load(
|
||||||
|
It.IsAny<string>(),
|
||||||
|
It.IsAny<System.Text.Json.Serialization.Metadata.JsonTypeInfo<AppStateModel>>()))
|
||||||
|
.Returns(expectedState);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var service = new AppStateService(_mockPersistence.Object, _mockAppInfo.Object);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.AreSame(expectedState, service.State);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Save_DelegatesToPersistenceService()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var service = new AppStateService(_mockPersistence.Object, _mockAppInfo.Object);
|
||||||
|
service.State.RunHistory.Add("test-command");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
service.Save();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_mockPersistence.Verify(
|
||||||
|
p => p.Save(
|
||||||
|
service.State,
|
||||||
|
It.IsAny<string>(),
|
||||||
|
It.IsAny<System.Text.Json.Serialization.Metadata.JsonTypeInfo<AppStateModel>>()),
|
||||||
|
Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Save_RaisesStateChangedEvent()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var service = new AppStateService(_mockPersistence.Object, _mockAppInfo.Object);
|
||||||
|
var eventRaised = false;
|
||||||
|
service.StateChanged += (sender, state) =>
|
||||||
|
{
|
||||||
|
eventRaised = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
service.Save();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.IsTrue(eventRaised);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void StateChanged_PassesCorrectArguments()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var service = new AppStateService(_mockPersistence.Object, _mockAppInfo.Object);
|
||||||
|
IAppStateService? receivedSender = null;
|
||||||
|
AppStateModel? receivedState = null;
|
||||||
|
|
||||||
|
service.StateChanged += (sender, state) =>
|
||||||
|
{
|
||||||
|
receivedSender = sender;
|
||||||
|
receivedState = state;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
service.Save();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.AreSame(service, receivedSender);
|
||||||
|
Assert.AreSame(service.State, receivedState);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Save_Always_RaisesStateChangedEvent()
|
||||||
|
{
|
||||||
|
// Arrange - AppStateService.Save() should always raise StateChanged
|
||||||
|
// (unlike SettingsService which has hotReload parameter)
|
||||||
|
var service = new AppStateService(_mockPersistence.Object, _mockAppInfo.Object);
|
||||||
|
var eventCount = 0;
|
||||||
|
|
||||||
|
service.StateChanged += (sender, state) =>
|
||||||
|
{
|
||||||
|
eventCount++;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
service.Save();
|
||||||
|
service.Save();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.AreEqual(2, eventCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
// 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;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Microsoft.CmdPal.UI.ViewModels.Services;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
|
||||||
|
namespace Microsoft.CmdPal.UI.ViewModels.UnitTests;
|
||||||
|
|
||||||
|
[TestClass]
|
||||||
|
public partial class PersistenceServiceTests
|
||||||
|
{
|
||||||
|
private PersistenceService _service = null!;
|
||||||
|
private string _testDirectory = null!;
|
||||||
|
private string _testFilePath = null!;
|
||||||
|
|
||||||
|
// Simple test model for persistence testing
|
||||||
|
private sealed class TestModel
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public int Value { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonSerializable(typeof(TestModel))]
|
||||||
|
private sealed partial class TestJsonContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestInitialize]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
_service = new PersistenceService();
|
||||||
|
_testDirectory = Path.Combine(Path.GetTempPath(), $"PersistenceServiceTests_{Guid.NewGuid():N}");
|
||||||
|
Directory.CreateDirectory(_testDirectory);
|
||||||
|
_testFilePath = Path.Combine(_testDirectory, "test.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCleanup]
|
||||||
|
public void Cleanup()
|
||||||
|
{
|
||||||
|
if (Directory.Exists(_testDirectory))
|
||||||
|
{
|
||||||
|
Directory.Delete(_testDirectory, recursive: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Load_ReturnsNewInstance_WhenFileDoesNotExist()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var nonExistentPath = Path.Combine(_testDirectory, "nonexistent.json");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = _service.Load(nonExistentPath, TestJsonContext.Default.TestModel);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.IsNotNull(result);
|
||||||
|
Assert.AreEqual(string.Empty, result.Name);
|
||||||
|
Assert.AreEqual(0, result.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Load_ReturnsDeserializedModel_WhenFileContainsValidJson()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expectedModel = new TestModel { Name = "Test", Value = 42 };
|
||||||
|
var json = JsonSerializer.Serialize(expectedModel, TestJsonContext.Default.TestModel);
|
||||||
|
File.WriteAllText(_testFilePath, json);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = _service.Load(_testFilePath, TestJsonContext.Default.TestModel);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.IsNotNull(result);
|
||||||
|
Assert.AreEqual("Test", result.Name);
|
||||||
|
Assert.AreEqual(42, result.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Load_ReturnsNewInstance_WhenFileContainsInvalidJson()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
File.WriteAllText(_testFilePath, "{ invalid json }");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = _service.Load(_testFilePath, TestJsonContext.Default.TestModel);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.IsNotNull(result);
|
||||||
|
Assert.AreEqual(string.Empty, result.Name);
|
||||||
|
Assert.AreEqual(0, result.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Save_CreatesFile_WhenFileDoesNotExist()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var model = new TestModel { Name = "NewFile", Value = 123 };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
_service.Save(model, _testFilePath, TestJsonContext.Default.TestModel);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.IsTrue(File.Exists(_testFilePath));
|
||||||
|
var content = File.ReadAllText(_testFilePath);
|
||||||
|
Assert.IsTrue(content.Contains("NewFile"));
|
||||||
|
Assert.IsTrue(content.Contains("123"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Save_HandlesExistingDirectory()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
// Note: PersistenceService.Save() does NOT create missing directories
|
||||||
|
// It relies on Directory.CreateDirectory being called by the consumer
|
||||||
|
// (e.g., SettingsService calls Directory.CreateDirectory in SettingsJsonPath())
|
||||||
|
var nestedDir = Path.Combine(_testDirectory, "nested");
|
||||||
|
Directory.CreateDirectory(nestedDir); // Create directory beforehand
|
||||||
|
var nestedFilePath = Path.Combine(nestedDir, "test.json");
|
||||||
|
var model = new TestModel { Name = "NestedTest", Value = 321 };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
_service.Save(model, nestedFilePath, TestJsonContext.Default.TestModel);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.IsTrue(File.Exists(nestedFilePath));
|
||||||
|
var result = _service.Load(nestedFilePath, TestJsonContext.Default.TestModel);
|
||||||
|
Assert.AreEqual("NestedTest", result.Name);
|
||||||
|
Assert.AreEqual(321, result.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
// 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;
|
||||||
|
using System.IO;
|
||||||
|
using Microsoft.CmdPal.Common.Services;
|
||||||
|
using Microsoft.CmdPal.UI.ViewModels.Services;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using Moq;
|
||||||
|
|
||||||
|
namespace Microsoft.CmdPal.UI.ViewModels.UnitTests;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests for <see cref="SettingsService"/>.
|
||||||
|
/// NOTE: These tests currently fail in console test runners due to WinUI3 COM dependencies in SettingsModel.
|
||||||
|
/// SettingsModel constructor initializes DockSettings which uses Microsoft.UI.Colors.Transparent,
|
||||||
|
/// requiring WinUI3 runtime registration. Tests pass when run within VS Test Explorer with WinUI3 host.
|
||||||
|
/// </summary>
|
||||||
|
[TestClass]
|
||||||
|
public class SettingsServiceTests
|
||||||
|
{
|
||||||
|
private Mock<IPersistenceService> _mockPersistence = null!;
|
||||||
|
private Mock<IApplicationInfoService> _mockAppInfo = null!;
|
||||||
|
private SettingsModel _testSettings = null!;
|
||||||
|
private string _testDirectory = null!;
|
||||||
|
|
||||||
|
[TestInitialize]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
_mockPersistence = new Mock<IPersistenceService>();
|
||||||
|
_mockAppInfo = new Mock<IApplicationInfoService>();
|
||||||
|
_testDirectory = Path.Combine(Path.GetTempPath(), $"CmdPalTest_{Guid.NewGuid():N}");
|
||||||
|
_mockAppInfo.Setup(a => a.ConfigDirectory).Returns(_testDirectory);
|
||||||
|
|
||||||
|
// Create a minimal test settings instance without triggering WinUI3 dependencies
|
||||||
|
// We'll mock the Load to return this, avoiding SettingsModel constructor that uses Colors.Transparent
|
||||||
|
_testSettings = CreateMinimalSettingsModel();
|
||||||
|
|
||||||
|
// Default: Load returns our test settings
|
||||||
|
_mockPersistence
|
||||||
|
.Setup(p => p.Load(
|
||||||
|
It.IsAny<string>(),
|
||||||
|
It.IsAny<System.Text.Json.Serialization.Metadata.JsonTypeInfo<SettingsModel>>()))
|
||||||
|
.Returns(_testSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SettingsModel CreateMinimalSettingsModel()
|
||||||
|
{
|
||||||
|
// Bypass constructor by using deserialize from minimal JSON
|
||||||
|
// This avoids WinUI3 dependencies (Colors.Transparent)
|
||||||
|
var minimalJson = "{}";
|
||||||
|
var settings = System.Text.Json.JsonSerializer.Deserialize(
|
||||||
|
minimalJson,
|
||||||
|
JsonSerializationContext.Default.SettingsModel) ?? new SettingsModel();
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Constructor_LoadsSettings_ViaPersistenceService()
|
||||||
|
{
|
||||||
|
// Act
|
||||||
|
var service = new SettingsService(_mockPersistence.Object, _mockAppInfo.Object);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.IsNotNull(service.Settings);
|
||||||
|
_mockPersistence.Verify(
|
||||||
|
p => p.Load(
|
||||||
|
It.IsAny<string>(),
|
||||||
|
It.IsAny<System.Text.Json.Serialization.Metadata.JsonTypeInfo<SettingsModel>>()),
|
||||||
|
Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Settings_ReturnsLoadedModel()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
_testSettings.ShowAppDetails = true;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var service = new SettingsService(_mockPersistence.Object, _mockAppInfo.Object);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.IsTrue(service.Settings.ShowAppDetails);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Save_DelegatesToPersistenceService()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var service = new SettingsService(_mockPersistence.Object, _mockAppInfo.Object);
|
||||||
|
service.Settings.SingleClickActivates = true;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
service.Save(hotReload: false);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_mockPersistence.Verify(
|
||||||
|
p => p.Save(
|
||||||
|
service.Settings,
|
||||||
|
It.IsAny<string>(),
|
||||||
|
It.IsAny<System.Text.Json.Serialization.Metadata.JsonTypeInfo<SettingsModel>>()),
|
||||||
|
Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Save_WithHotReloadTrue_RaisesSettingsChangedEvent()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var service = new SettingsService(_mockPersistence.Object, _mockAppInfo.Object);
|
||||||
|
var eventRaised = false;
|
||||||
|
service.SettingsChanged += (sender, settings) =>
|
||||||
|
{
|
||||||
|
eventRaised = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
service.Save(hotReload: true);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.IsTrue(eventRaised);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Save_WithHotReloadFalse_DoesNotRaiseSettingsChangedEvent()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var service = new SettingsService(_mockPersistence.Object, _mockAppInfo.Object);
|
||||||
|
var eventRaised = false;
|
||||||
|
service.SettingsChanged += (sender, settings) =>
|
||||||
|
{
|
||||||
|
eventRaised = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
service.Save(hotReload: false);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.IsFalse(eventRaised);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Save_WithDefaultHotReload_RaisesSettingsChangedEvent()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var service = new SettingsService(_mockPersistence.Object, _mockAppInfo.Object);
|
||||||
|
var eventRaised = false;
|
||||||
|
service.SettingsChanged += (sender, settings) =>
|
||||||
|
{
|
||||||
|
eventRaised = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
service.Save(); // Default is hotReload: true
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.IsTrue(eventRaised);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void SettingsChanged_PassesCorrectArguments()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var service = new SettingsService(_mockPersistence.Object, _mockAppInfo.Object);
|
||||||
|
ISettingsService? receivedSender = null;
|
||||||
|
SettingsModel? receivedSettings = null;
|
||||||
|
|
||||||
|
service.SettingsChanged += (sender, settings) =>
|
||||||
|
{
|
||||||
|
receivedSender = sender;
|
||||||
|
receivedSettings = settings;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
service.Save(hotReload: true);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.AreSame(service, receivedSender);
|
||||||
|
Assert.AreSame(service.Settings, receivedSettings);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user