Continuing part deux

This commit is contained in:
Michael Jolley 2025-12-09 19:39:32 -06:00
parent 75f8d3affb
commit 82a63a4517
No known key found for this signature in database
GPG Key ID: F608E47FBB163707
22 changed files with 328 additions and 240 deletions

View File

@ -369,6 +369,10 @@
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/Deux/UI/Microsoft.CommandPalette.UI.Services/Microsoft.CommandPalette.UI.Services.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/Deux/UI/Microsoft.CommandPalette.UI.ViewModels/Microsoft.CommandPalette.UI.ViewModels.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />

View File

@ -2,57 +2,14 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.CommandPalette.UI.Models;
using Microsoft.Extensions.Logging;
using Windows.Foundation;
namespace Microsoft.CommandPalette.UI.Models;
namespace Microsoft.CmdPal.UI.ViewModels;
public partial class AppStateModel : ObservableObject
public partial class AppStateModel
{
private static string _filePath;
/*************************************************************************
* Make sure that you make the setters public (JsonSerializer.Deserialize will fail silently otherwise)!
* Make sure that any new types you add are added to JsonSerializationContext!
*************************************************************************/
public event TypedEventHandler<AppStateModel, object?>? StateChanged;
///////////////////////////////////////////////////////////////////////////
// STATE HERE
// Make sure that you make the setters public (JsonSerializer.Deserialize will fail silently otherwise)!
// Make sure that any new types you add are added to JsonSerializationContext!
public List<string> RunHistory { get; set; } = [];
// END STATE
///////////////////////////////////////////////////////////////////////////
static AppStateModel()
{
_filePath = PersistenceService.SettingsJsonPath("state.json");
}
public static AppStateModel LoadState(ILogger logger)
{
return PersistenceService.LoadObject<AppStateModel>(_filePath, JsonSerializationContext.Default.AppStateModel!, logger);
}
public static void SaveState(AppStateModel model, ILogger logger)
{
try
{
PersistenceService.SaveObject(
model,
_filePath,
JsonSerializationContext.Default.AppStateModel!,
JsonSerializationContext.Default.AppStateModel!.Options,
beforeWriteMutation: null,
afterWriteCallback: m => m.StateChanged?.Invoke(m, null),
logger);
}
catch (Exception ex)
{
Log_SaveStateFailure(logger, _filePath, ex);
}
}
[LoggerMessage(Level = LogLevel.Error, Message = "Failed to save application state to '{filePath}'.")]
static partial void Log_SaveStateFailure(ILogger logger, string filePath, Exception exception);
}

View File

@ -2,27 +2,15 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.Extensions.Logging;
using Windows.Foundation;
namespace Microsoft.CommandPalette.UI.Models;
public partial class SettingsModel : ObservableObject
public partial class SettingsModel
{
private const string DeprecatedHotkeyGoesHomeKey = "HotkeyGoesHome";
/*************************************************************************
* Make sure that you make the setters public (JsonSerializer.Deserialize will fail silently otherwise)!
* Make sure that any new types you add are added to JsonSerializationContext!
*************************************************************************/
[JsonIgnore]
private static readonly string _filePath;
public event TypedEventHandler<SettingsModel, object?>? SettingsChanged;
///////////////////////////////////////////////////////////////////////////
// SETTINGS HERE
public static HotkeySettings DefaultActivationShortcut { get; } = new HotkeySettings(true, false, true, false, 0x20); // win+alt+space
public HotkeySettings? Hotkey { get; set; } = DefaultActivationShortcut;
@ -52,97 +40,4 @@ public partial class SettingsModel : ObservableObject
public WindowPosition? LastWindowPosition { get; set; }
public TimeSpan AutoGoHomeInterval { get; set; } = Timeout.InfiniteTimeSpan;
// END SETTINGS
///////////////////////////////////////////////////////////////////////////
static SettingsModel()
{
_filePath = PersistenceService.SettingsJsonPath("settings.json");
}
private static bool ApplyMigrations(JsonObject root, SettingsModel model, ILogger logger)
{
var migrated = false;
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,
logger);
return migrated;
}
private static bool TryMigrate<T>(string migrationName, JsonObject root, SettingsModel model, string newKey, string oldKey, Action<SettingsModel, T> apply, JsonTypeInfo<T> jsonTypeInfo, ILogger logger)
{
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<T>(jsonTypeInfo);
apply(model, value!);
return true;
}
}
catch (Exception ex)
{
Log_MigrationFailure(logger, migrationName, ex);
}
return false;
}
public static SettingsModel LoadSettings(ILogger logger)
{
var settings = PersistenceService.LoadObject<SettingsModel>(_filePath, JsonSerializationContext.Default.SettingsModel!, logger);
var migratedAny = false;
try
{
var jsonContent = File.Exists(_filePath) ? File.ReadAllText(_filePath) : "{}";
if (JsonNode.Parse(jsonContent) is JsonObject root)
{
migratedAny |= ApplyMigrations(root, settings, logger);
}
}
catch (Exception ex)
{
Log_MigrationCheckFailure(logger, ex);
}
if (migratedAny)
{
SaveSettings(settings, logger);
}
return settings;
}
public static void SaveSettings(SettingsModel model, ILogger logger)
{
PersistenceService.SaveObject(
model,
_filePath,
JsonSerializationContext.Default.SettingsModel,
JsonSerializationContext.Default.Options,
beforeWriteMutation: obj => obj.Remove(DeprecatedHotkeyGoesHomeKey),
afterWriteCallback: m => m.SettingsChanged?.Invoke(m, null),
logger);
}
[LoggerMessage(Level = LogLevel.Error, Message = "Settings migration '{MigrationName}' failed.")]
static partial void Log_MigrationFailure(ILogger logger, string MigrationName, Exception exception);
[LoggerMessage(Level = LogLevel.Error, Message = "Settings migration check failed.")]
static partial void Log_MigrationCheckFailure(ILogger logger, Exception exception);
}

View File

@ -0,0 +1,50 @@
// 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.CommandPalette.UI.Models;
using Microsoft.Extensions.Logging;
using Windows.Foundation;
namespace Microsoft.CommandPalette.UI.Services;
public partial class AppStateService
{
private readonly ILogger logger;
private readonly string _filePath;
private AppStateModel _appStateModel;
public event TypedEventHandler<AppStateModel, object?>? StateChanged;
public AppStateModel CurrentSettings => _appStateModel;
public AppStateService(ILogger<SettingsService> logger)
{
this.logger = logger;
_filePath = PersistenceService.SettingsJsonPath("state.json");
_appStateModel = LoadState();
}
private AppStateModel LoadState()
{
return PersistenceService.LoadObject<AppStateModel>(_filePath, JsonSerializationContext.Default.AppStateModel!, logger);
}
public void SaveSettings(AppStateModel model)
{
PersistenceService.SaveObject(
model,
_filePath,
JsonSerializationContext.Default.AppStateModel,
JsonSerializationContext.Default.Options,
null,
afterWriteCallback: m => FinalizeStateSave(m),
logger);
}
private void FinalizeStateSave(AppStateModel model)
{
_appStateModel = model;
StateChanged?.Invoke(model, null);
}
}

View File

@ -9,7 +9,7 @@ namespace Microsoft.CommandPalette.UI.Services;
// Adapter implementing Microsoft.Extensions.Logging.ILogger,
// delegating to ManagedCommon.Logger.
internal sealed partial class CmdPalLogger : ILogger
public sealed partial class CmdPalLogger : ILogger
{
private static readonly AsyncLocal<Stack<object>> _scopeStack = new();
private readonly LogLevel _minLevel;

View File

@ -0,0 +1,9 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.CommandPalette.UI.Services.Extensions;
internal class BuiltInExtensionService : IExtensionService
{
}

View File

@ -0,0 +1,9 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.CommandPalette.UI.Services.Extensions;
internal interface IExtensionService
{
}

View File

@ -0,0 +1,9 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.CommandPalette.UI.Services.Extensions;
internal class WinRTExtensionService : IExtensionService
{
}

View File

@ -4,7 +4,7 @@
using Microsoft.Windows.ApplicationModel.Resources;
namespace Microsoft.CommandPalette.UI.Models.Helpers;
namespace Microsoft.CommandPalette.UI.Services.Helpers;
public static class ResourceLoaderInstance
{

View File

@ -3,9 +3,9 @@
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.CommandPalette.UI.Models;
namespace Microsoft.CommandPalette.UI.Models;
namespace Microsoft.CommandPalette.UI.Services;
[JsonSerializable(typeof(float))]
[JsonSerializable(typeof(int))]

View File

@ -29,6 +29,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
<ProjectReference Include="..\..\SDK\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
<ProjectReference Include="..\Microsoft.CommandPalette.UI.Models\Microsoft.CommandPalette.UI.Models.csproj" />
</ItemGroup>
</Project>

View File

@ -9,7 +9,7 @@ using System.Text.Json.Serialization.Metadata;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Microsoft.Extensions.Logging;
namespace Microsoft.CommandPalette.UI.Models;
namespace Microsoft.CommandPalette.UI.Services;
public partial class PersistenceService
{

View File

@ -0,0 +1,121 @@
// 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 Microsoft.CommandPalette.UI.Models;
using Microsoft.Extensions.Logging;
using Windows.Foundation;
namespace Microsoft.CommandPalette.UI.Services;
public partial class SettingsService
{
private readonly ILogger logger;
private readonly string _filePath;
private const string DeprecatedHotkeyGoesHomeKey = "HotkeyGoesHome";
private SettingsModel _settingsModel;
public event TypedEventHandler<SettingsModel, object?>? SettingsChanged;
public SettingsModel CurrentSettings => _settingsModel;
public SettingsService(ILogger<SettingsService> logger)
{
this.logger = logger;
_filePath = PersistenceService.SettingsJsonPath("settings.json");
_settingsModel = LoadSettings();
}
private SettingsModel LoadSettings()
{
var settings = PersistenceService.LoadObject<SettingsModel>(_filePath, JsonSerializationContext.Default.SettingsModel!, logger);
var migratedAny = false;
try
{
var jsonContent = File.Exists(_filePath) ? File.ReadAllText(_filePath) : "{}";
if (JsonNode.Parse(jsonContent) is JsonObject root)
{
migratedAny |= ApplyMigrations(root, settings);
}
}
catch (Exception ex)
{
Log_MigrationCheckFailure(ex);
}
if (migratedAny)
{
SaveSettings(settings);
}
return settings;
}
public void SaveSettings(SettingsModel model)
{
PersistenceService.SaveObject(
model,
_filePath,
JsonSerializationContext.Default.SettingsModel,
JsonSerializationContext.Default.Options,
beforeWriteMutation: obj => obj.Remove(DeprecatedHotkeyGoesHomeKey),
afterWriteCallback: m => FinalizeSettingsSave(m),
logger);
}
private void FinalizeSettingsSave(SettingsModel model)
{
_settingsModel = model;
SettingsChanged?.Invoke(model, null);
}
private bool ApplyMigrations(JsonObject root, SettingsModel model)
{
var migrated = false;
migrated |= TryMigrate(
"Migration #1: HotkeyGoesHome (bool) -> AutoGoHomeInterval (TimeSpan)",
root,
model,
nameof(SettingsModel.AutoGoHomeInterval),
DeprecatedHotkeyGoesHomeKey,
(settingsModel, goesHome) => settingsModel.AutoGoHomeInterval = goesHome ? TimeSpan.Zero : Timeout.InfiniteTimeSpan,
JsonSerializationContext.Default.Boolean);
return migrated;
}
private 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<T>(jsonTypeInfo);
apply(model, value!);
return true;
}
}
catch (Exception ex)
{
Log_MigrationFailure(migrationName, ex);
}
return false;
}
[LoggerMessage(Level = LogLevel.Error, Message = "Settings migration '{MigrationName}' failed.")]
partial void Log_MigrationFailure(string MigrationName, Exception exception);
[LoggerMessage(Level = LogLevel.Error, Message = "Settings migration check failed.")]
partial void Log_MigrationCheckFailure(Exception exception);
}

View File

@ -8,19 +8,23 @@ using CommunityToolkit.Mvvm.Messaging;
using Microsoft.CommandPalette.UI.Models;
using Microsoft.CommandPalette.UI.Models.Messages;
using Microsoft.UI.Xaml;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.UI.Shell;
using Windows.Win32.UI.WindowsAndMessaging;
using WinRT.Interop;
using RS_ = Microsoft.CommandPalette.UI.Models.Helpers.ResourceLoaderInstance;
using RS_ = Microsoft.CommandPalette.UI.Services.Helpers.ResourceLoaderInstance;
namespace Microsoft.CommandPalette.UI.Services;
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "Stylistically, window messages are WM_*")]
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1306:Field names should begin with lower-case letter", Justification = "Stylistically, window messages are WM_*")]
public sealed partial class TrayIconService
public sealed partial class TrayIconService : IDisposable
{
private const uint MY_NOTIFY_ID = 1000;
private const uint WM_TRAY_ICON = PInvoke.WM_USER + 1;
private readonly SettingsModel _settingsModel;
private readonly SettingsService _settingsService;
private readonly uint WM_TASKBAR_RESTART;
private Window? _window;
@ -31,9 +35,10 @@ public sealed partial class TrayIconService
private DestroyIconSafeHandle? _largeIcon;
private DestroyMenuSafeHandle? _popupMenu;
public TrayIconService(SettingsModel settingsModel)
public TrayIconService(SettingsService settingsService)
{
_settingsModel = settingsModel;
_settingsService = settingsService;
_settingsService.SettingsChanged += OnSettingsChanged;
// TaskbarCreated is the message that's broadcast when explorer.exe
// restarts. We need to know when that happens to be able to bring our
@ -41,9 +46,14 @@ public sealed partial class TrayIconService
WM_TASKBAR_RESTART = PInvoke.RegisterWindowMessage("TaskbarCreated");
}
public void SetupTrayIcon(bool? showSystemTrayIcon = null)
private void OnSettingsChanged(SettingsModel sender, object? args)
{
if (showSystemTrayIcon ?? _settingsModel.ShowSystemTrayIcon)
SetupTrayIcon();
}
public void SetupTrayIcon()
{
if (_settingsService.CurrentSettings.ShowSystemTrayIcon)
{
if (_window is null)
{
@ -205,4 +215,14 @@ public sealed partial class TrayIconService
return PInvoke.CallWindowProc(_originalWndProc, hwnd, uMsg, wParam, lParam);
}
public void Dispose()
{
if (_settingsService is not null)
{
_settingsService.SettingsChanged -= OnSettingsChanged;
}
Destroy();
}
}

View File

@ -24,6 +24,7 @@
<ItemGroup>
<ProjectReference Include="..\..\SDK\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
<ProjectReference Include="..\Microsoft.CommandPalette.UI.Models\Microsoft.CommandPalette.UI.Models.csproj" />
<ProjectReference Include="..\Microsoft.CommandPalette.UI.Services\Microsoft.CommandPalette.UI.Services.csproj" />
</ItemGroup>
</Project>

View File

@ -4,11 +4,11 @@
using System.ComponentModel;
using Microsoft.CommandPalette.UI.Models;
using Microsoft.Extensions.Logging;
using Microsoft.CommandPalette.UI.Services;
namespace Microsoft.CommandPalette.UI.ViewModels.Settings;
public partial class SettingsViewModel : INotifyPropertyChanged
public partial class SettingsViewModel : INotifyPropertyChanged, IDisposable
{
private static readonly List<TimeSpan> AutoGoHomeIntervals =
[
@ -23,18 +23,17 @@ public partial class SettingsViewModel : INotifyPropertyChanged
TimeSpan.FromSeconds(180),
];
private readonly ILogger logger;
private readonly SettingsModel _settings;
private readonly IServiceProvider _serviceProvider;
private readonly SettingsService _settingsService;
private SettingsModel _settingsModel;
public event PropertyChangedEventHandler? PropertyChanged;
public HotkeySettings? Hotkey
{
get => _settings.Hotkey;
get => _settingsModel.Hotkey;
set
{
_settings.Hotkey = value ?? SettingsModel.DefaultActivationShortcut;
_settingsModel.Hotkey = value ?? SettingsModel.DefaultActivationShortcut;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Hotkey)));
Save();
}
@ -42,10 +41,10 @@ public partial class SettingsViewModel : INotifyPropertyChanged
public bool UseLowLevelGlobalHotkey
{
get => _settings.UseLowLevelGlobalHotkey;
get => _settingsModel.UseLowLevelGlobalHotkey;
set
{
_settings.UseLowLevelGlobalHotkey = value;
_settingsModel.UseLowLevelGlobalHotkey = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Hotkey)));
Save();
}
@ -53,90 +52,90 @@ public partial class SettingsViewModel : INotifyPropertyChanged
public bool AllowExternalReload
{
get => _settings.AllowExternalReload;
get => _settingsModel.AllowExternalReload;
set
{
_settings.AllowExternalReload = value;
_settingsModel.AllowExternalReload = value;
Save();
}
}
public bool ShowAppDetails
{
get => _settings.ShowAppDetails;
get => _settingsModel.ShowAppDetails;
set
{
_settings.ShowAppDetails = value;
_settingsModel.ShowAppDetails = value;
Save();
}
}
public bool BackspaceGoesBack
{
get => _settings.BackspaceGoesBack;
get => _settingsModel.BackspaceGoesBack;
set
{
_settings.BackspaceGoesBack = value;
_settingsModel.BackspaceGoesBack = value;
Save();
}
}
public bool SingleClickActivates
{
get => _settings.SingleClickActivates;
get => _settingsModel.SingleClickActivates;
set
{
_settings.SingleClickActivates = value;
_settingsModel.SingleClickActivates = value;
Save();
}
}
public bool HighlightSearchOnActivate
{
get => _settings.HighlightSearchOnActivate;
get => _settingsModel.HighlightSearchOnActivate;
set
{
_settings.HighlightSearchOnActivate = value;
_settingsModel.HighlightSearchOnActivate = value;
Save();
}
}
public int MonitorPositionIndex
{
get => (int)_settings.SummonOn;
get => (int)_settingsModel.SummonOn;
set
{
_settings.SummonOn = (MonitorBehavior)value;
_settingsModel.SummonOn = (MonitorBehavior)value;
Save();
}
}
public bool ShowSystemTrayIcon
{
get => _settings.ShowSystemTrayIcon;
get => _settingsModel.ShowSystemTrayIcon;
set
{
_settings.ShowSystemTrayIcon = value;
_settingsModel.ShowSystemTrayIcon = value;
Save();
}
}
public bool IgnoreShortcutWhenFullscreen
{
get => _settings.IgnoreShortcutWhenFullscreen;
get => _settingsModel.IgnoreShortcutWhenFullscreen;
set
{
_settings.IgnoreShortcutWhenFullscreen = value;
_settingsModel.IgnoreShortcutWhenFullscreen = value;
Save();
}
}
public bool DisableAnimations
{
get => _settings.DisableAnimations;
get => _settingsModel.DisableAnimations;
set
{
_settings.DisableAnimations = value;
_settingsModel.DisableAnimations = value;
Save();
}
}
@ -145,7 +144,7 @@ public partial class SettingsViewModel : INotifyPropertyChanged
{
get
{
var index = AutoGoHomeIntervals.IndexOf(_settings.AutoGoHomeInterval);
var index = AutoGoHomeIntervals.IndexOf(_settingsModel.AutoGoHomeInterval);
return index >= 0 ? index : 0;
}
@ -153,7 +152,7 @@ public partial class SettingsViewModel : INotifyPropertyChanged
{
if (value >= 0 && value < AutoGoHomeIntervals.Count)
{
_settings.AutoGoHomeInterval = AutoGoHomeIntervals[value];
_settingsModel.AutoGoHomeInterval = AutoGoHomeIntervals[value];
}
Save();
@ -162,11 +161,12 @@ public partial class SettingsViewModel : INotifyPropertyChanged
// public ObservableCollection<ProviderSettingsViewModel> CommandProviders { get; } = [];
// public SettingsExtensionsViewModel Extensions { get; }
public SettingsViewModel(SettingsModel settings, IServiceProvider serviceProvider, TaskScheduler scheduler, ILogger logger)
public SettingsViewModel(SettingsService settingsService)
{
_settings = settings;
_serviceProvider = serviceProvider;
this.logger = logger;
_settingsService = settingsService;
_settingsModel = _settingsService.CurrentSettings;
_settingsService.SettingsChanged += OnSettingsChanged;
// var activeProviders = GetCommandProviders();
// var allProviderSettings = _settings.ProviderSettings;
@ -179,11 +179,27 @@ public partial class SettingsViewModel : INotifyPropertyChanged
// Extensions = new SettingsExtensionsViewModel(CommandProviders, scheduler);
}
private void OnSettingsChanged(SettingsModel sender, object? args)
{
_settingsModel = sender;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
}
// private IEnumerable<CommandProviderWrapper> GetCommandProviders()
// {
// var manager = _serviceProvider.GetService<TopLevelCommandManager>()!;
// var allProviders = manager.CommandProviders;
// return allProviders;
// }
private void Save() => SettingsModel.SaveSettings(_settings, logger);
private void Save() => _settingsService.SaveSettings(_settingsModel);
public void Dispose()
{
if (_settingsService is not null)
{
_settingsService.SettingsChanged -= OnSettingsChanged;
}
GC.SuppressFinalize(this);
}
}

View File

@ -2,9 +2,7 @@
// 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.UI.ViewModels;
using Microsoft.CommandPalette.UI.Helpers;
using Microsoft.CommandPalette.UI.Models;
using Microsoft.CommandPalette.UI.Pages;
using Microsoft.CommandPalette.UI.Services;
using Microsoft.CommandPalette.ViewModels;
@ -22,6 +20,7 @@ public partial class App : Application
{
private readonly ILogger logger;
private readonly GlobalErrorHandler _globalErrorHandler;
private readonly IServiceProvider _services;
/// <summary>
/// Gets the current <see cref="App"/> instance in use.
@ -32,10 +31,6 @@ public partial class App : Application
public ETWTrace EtwTrace { get; private set; } = new ETWTrace();
/// <summary>
/// Gets the <see cref="IServiceProvider"/> instance to resolve application services.
/// </summary>
public IServiceProvider Services { get; }
public App(ILogger logger)
{
@ -46,7 +41,7 @@ public partial class App : Application
_globalErrorHandler.Register(this);
#endif
Services = ConfigureServices();
_services = ConfigureServices();
this.InitializeComponent();
@ -70,12 +65,10 @@ public partial class App : Application
services.AddSingleton(logger);
services.AddSingleton(TaskScheduler.FromCurrentSynchronizationContext());
// Register settings & app state
var settingsModel = SettingsModel.LoadSettings(logger);
services.AddSingleton(settingsModel);
var appStateModel = AppStateModel.LoadState(logger);
services.AddSingleton(appStateModel);
// Register settings & app state services first
// because other services depend on them
services.AddSingleton<SettingsService>();
services.AddSingleton<AppStateService>();
// Register services
services.AddSingleton<TrayIconService>();
@ -99,7 +92,7 @@ public partial class App : Application
{
var activatedEventArgs = Microsoft.Windows.AppLifecycle.AppInstance.GetCurrent().GetActivatedEventArgs();
var mainWindow = Services.GetRequiredService<MainWindow>();
var mainWindow = _services.GetRequiredService<MainWindow>();
AppWindow = mainWindow;
((MainWindow)AppWindow).HandleLaunchNonUI(activatedEventArgs);

View File

@ -3,7 +3,7 @@
// See the LICENSE file in the project root for more information.
using Microsoft.UI.Xaml.Data;
using RS_ = Microsoft.CommandPalette.UI.Helpers.ResourceLoaderInstance;
using RS_ = Microsoft.CommandPalette.UI.Services.Helpers.ResourceLoaderInstance;
namespace Microsoft.CommandPalette.UI.Converters;

View File

@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using Microsoft.CommandPalette.UI.Services;
using Microsoft.CommandPalette.UI.Services.Helpers;
using Microsoft.Extensions.Logging;
using Windows.Win32;
using Windows.Win32.Foundation;

View File

@ -11,6 +11,7 @@ using Microsoft.CommandPalette.UI.Models;
using Microsoft.CommandPalette.UI.Models.Events;
using Microsoft.CommandPalette.UI.Models.Messages;
using Microsoft.CommandPalette.UI.Pages;
using Microsoft.CommandPalette.UI.Services;
using Microsoft.CommandPalette.UI.ViewModels.Helpers;
using Microsoft.Extensions.Logging;
using Microsoft.PowerToys.Telemetry;
@ -35,7 +36,7 @@ using Windows.Win32.UI.Input.KeyboardAndMouse;
using Windows.Win32.UI.WindowsAndMessaging;
using WinRT;
using WinUIEx;
using RS_ = Microsoft.CommandPalette.UI.Helpers.ResourceLoaderInstance;
using RS_ = Microsoft.CommandPalette.UI.Services.Helpers.ResourceLoaderInstance;
namespace Microsoft.CommandPalette.UI;
@ -47,7 +48,7 @@ public sealed partial class MainWindow : WindowEx,
{
private readonly ILogger logger;
private readonly ShellPage _shellPage;
private readonly SettingsModel _settingsModel;
private readonly SettingsService _settingsService;
private readonly TrayIconService _trayIconService;
private const int DefaultWidth = 800;
private const int DefaultHeight = 480;
@ -72,13 +73,13 @@ public sealed partial class MainWindow : WindowEx,
private WindowPosition _currentWindowPosition = new();
public MainWindow(ShellPage shellPage, SettingsModel settingsModel, TrayIconService trayIconService, ILogger logger)
public MainWindow(ShellPage shellPage, SettingsService settingsService, TrayIconService trayIconService, ILogger logger)
{
InitializeComponent();
this.logger = logger;
_shellPage = shellPage;
_settingsModel = settingsModel;
_settingsService = settingsService;
_trayIconService = trayIconService;
RootElement.Children.Add(_shellPage);
@ -128,9 +129,10 @@ public sealed partial class MainWindow : WindowEx,
var hotKeyPrcPointer = Marshal.GetFunctionPointerForDelegate(_hotkeyWndProc);
_originalWndProc = Marshal.GetDelegateForFunctionPointer<WNDPROC>(PInvoke.SetWindowLongPtr(_hwnd, WINDOW_LONG_PTR_INDEX.GWL_WNDPROC, hotKeyPrcPointer));
// Load our settings, and then also wire up a settings changed handler
HotReloadSettings();
_settingsModel.SettingsChanged += SettingsChangedHandler;
// Wire up a settings changed handler
_settingsService.SettingsChanged += SettingsChangedHandler;
_trayIconService.SetupTrayIcon();
// Make sure that we update the acrylic theme when the OS theme changes
RootElement.ActualThemeChanged += (s, e) => DispatcherQueue.TryEnqueue(UpdateAcrylic);
@ -150,7 +152,7 @@ public sealed partial class MainWindow : WindowEx,
public void Receive(ShowWindowMessage message)
{
ShowHwnd(message.Hwnd, _settingsModel.SummonOn);
ShowHwnd(message.Hwnd, _settingsService.CurrentSettings.SummonOn);
}
public void Receive(HideWindowMessage message)
@ -217,7 +219,7 @@ public sealed partial class MainWindow : WindowEx,
private void RestoreWindowPosition()
{
if (_settingsModel.LastWindowPosition is not WindowPosition savedPosition)
if (_settingsService.CurrentSettings.LastWindowPosition is not WindowPosition savedPosition)
{
PositionCentered();
return;
@ -264,12 +266,11 @@ public sealed partial class MainWindow : WindowEx,
private void HotReloadSettings()
{
SetupHotkey(_settingsModel);
_trayIconService.SetupTrayIcon(_settingsModel.ShowSystemTrayIcon);
SetupHotkey(_settingsService.CurrentSettings);
_ignoreHotKeyWhenFullScreen = _settingsModel.IgnoreShortcutWhenFullscreen;
_ignoreHotKeyWhenFullScreen = _settingsService.CurrentSettings.IgnoreShortcutWhenFullscreen;
_autoGoHomeInterval = _settingsModel.AutoGoHomeInterval;
_autoGoHomeInterval = _settingsService.CurrentSettings.AutoGoHomeInterval;
_autoGoHomeTimer.Interval = _autoGoHomeInterval;
}
@ -614,9 +615,10 @@ public sealed partial class MainWindow : WindowEx,
{
UpdateWindowPositionInMemory();
if (_settingsModel is not null)
if (_settingsService is not null)
{
_settingsModel.LastWindowPosition = new WindowPosition
var settings = _settingsService.CurrentSettings;
settings.LastWindowPosition = new WindowPosition
{
X = _currentWindowPosition.X,
Y = _currentWindowPosition.Y,
@ -627,12 +629,12 @@ public sealed partial class MainWindow : WindowEx,
ScreenHeight = _currentWindowPosition.ScreenHeight,
};
SettingsModel.SaveSettings(_settingsModel, logger);
_settingsService.SaveSettings(settings);
}
// var extensionService = serviceProvider.GetService<IExtensionService>()!;
// extensionService.SignalStopExtensionsAsync();
_trayIconService.Destroy();
// _trayIconService.Destroy();
// WinUI bug is causing a crash on shutdown when FailFastOnErrors is set to true (#51773592).
// Workaround by turning it off before shutdown.
@ -770,7 +772,7 @@ public sealed partial class MainWindow : WindowEx,
}
else if (uri.StartsWith("x-cmdpal://reload", StringComparison.OrdinalIgnoreCase))
{
if (_settingsModel.AllowExternalReload == true)
if (_settingsService.CurrentSettings.AllowExternalReload == true)
{
Log_ExternalReloadTriggered();
WeakReferenceMessenger.Default.Send<ReloadCommandsMessage>(new());

View File

@ -161,7 +161,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
<ProjectReference Include="..\Microsoft.CommandPalette.UI.Models\Microsoft.CommandPalette.UI.Models.csproj" />
<ProjectReference Include="..\Microsoft.CommandPalette.UI.Services\Microsoft.CommandPalette.UI.Services.csproj" />
<ProjectReference Include="..\Microsoft.CommandPalette.UI.ViewModels\Microsoft.CommandPalette.UI.ViewModels.csproj" />
</ItemGroup>
<ItemGroup>

View File

@ -4,8 +4,8 @@
using System.Text;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.CommandPalette.UI.Helpers;
using Microsoft.CommandPalette.UI.Models;
using Microsoft.CommandPalette.UI.Models.Helpers;
using Microsoft.CommandPalette.UI.Models.Messages;
using Microsoft.CommandPalette.UI.ViewModels;
using Microsoft.CommandPalette.ViewModels;