Files
PowerToys/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ProviderSettingsViewModel.cs
Mike Griese 14919dff10 CmdPal: Fix SUI crash ; Allow extensions to be disabled (#38040)
Both `TopLevelCommandItemWrapper` and `TopLevelViewModel` were really the same thing. The latter was from an earlier prototype, and the former is a more correct, safer abstraction. We really should have only ever used the former, but alas, we only used it for the SUI, and it piggy-backed off the latter, and that meant the latter's bugs became the former's.


tldr: I made the icon access safe in the SUI. 

And while I was doing this, because we now have a cleaner VM abstraction here in the host, we can actually cleanly disable extensions, because the `CommandProviderWrapper` knows which `ViewModel`s it made. 

Closes https://github.com/zadjii-msft/PowerToys/issues/426
Closes https://github.com/zadjii-msft/PowerToys/issues/478
Closes https://github.com/zadjii-msft/PowerToys/issues/577
2025-03-20 13:36:10 -07:00

94 lines
3.2 KiB
C#

// 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.CodeAnalysis;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.CmdPal.Common.Services;
using Microsoft.CmdPal.UI.ViewModels.Messages;
using Microsoft.CmdPal.UI.ViewModels.Properties;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.CmdPal.UI.ViewModels;
public partial class ProviderSettingsViewModel(
CommandProviderWrapper _provider,
ProviderSettings _providerSettings,
IServiceProvider _serviceProvider) : ObservableObject
{
private readonly SettingsModel _settings = _serviceProvider.GetService<SettingsModel>()!;
public string DisplayName => _provider.DisplayName;
public string ExtensionName => _provider.Extension?.ExtensionDisplayName ?? "Built-in";
public string ExtensionSubtext => IsEnabled ? $"{ExtensionName}, {TopLevelCommands.Count} commands" : Resources.builtin_disabled_extension;
[MemberNotNullWhen(true, nameof(Extension))]
public bool IsFromExtension => _provider.Extension != null;
public IExtensionWrapper? Extension => _provider.Extension;
public string ExtensionVersion => IsFromExtension ? $"{Extension.Version.Major}.{Extension.Version.Minor}.{Extension.Version.Build}.{Extension.Version.Revision}" : string.Empty;
public IconInfoViewModel Icon => _provider.Icon;
public bool IsEnabled
{
get => _providerSettings.IsEnabled;
set
{
if (value != _providerSettings.IsEnabled)
{
_providerSettings.IsEnabled = value;
Save();
WeakReferenceMessenger.Default.Send<ReloadCommandsMessage>(new());
OnPropertyChanged(nameof(IsEnabled));
OnPropertyChanged(nameof(ExtensionSubtext));
}
if (value == true)
{
_provider.CommandsChanged -= Provider_CommandsChanged;
_provider.CommandsChanged += Provider_CommandsChanged;
}
}
}
private void Provider_CommandsChanged(CommandProviderWrapper sender, CommandPalette.Extensions.IItemsChangedEventArgs args)
{
OnPropertyChanged(nameof(ExtensionSubtext));
OnPropertyChanged(nameof(TopLevelCommands));
}
public bool HasSettings => _provider.Settings != null && _provider.Settings.SettingsPage != null;
public ContentPageViewModel? SettingsPage => HasSettings ? _provider?.Settings?.SettingsPage : null;
[field: AllowNull]
public List<TopLevelViewModel> TopLevelCommands
{
get
{
if (field == null)
{
field = BuildTopLevelViewModels();
}
return field;
}
}
private List<TopLevelViewModel> BuildTopLevelViewModels()
{
var thisProvider = _provider;
var providersCommands = thisProvider.TopLevelItems;
// Remember! This comes in on the UI thread!
return [.. providersCommands];
}
private void Save() => SettingsModel.SaveSettings(_settings);
}