mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-10 00:47:58 -06:00
Continuing part deux
This commit is contained in:
parent
75f8d3affb
commit
82a63a4517
@ -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" />
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
{
|
||||
}
|
||||
@ -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
|
||||
{
|
||||
}
|
||||
@ -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
|
||||
{
|
||||
}
|
||||
@ -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
|
||||
{
|
||||
@ -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))]
|
||||
@ -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>
|
||||
@ -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
|
||||
{
|
||||
@ -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);
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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>
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user