mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 19:57:57 +01:00
CmdPal: Add settings to control which fallbacks are enabled (#40505)
This adds settings to each provider to allow us to control if individual fallback items are enabled or not, regardless of the provider being enabled. This is relevant to _all the threads where disabling fallback commands came up_ This just adds another section to each provider's settings page, with a list of the fallback commands. This also has nothing to do with the "top-level apps search", which is not really a fallback command - it's its own thing. Ref #38288. Doesn't close that, because this only controls enable/disable, not ranking. From here, we should be able to add a dedicated page in the SUI that shows all the fallbacks across all providers. That's where we'll enable the ordering.
This commit is contained in:
@@ -177,13 +177,13 @@ public sealed class CommandProviderWrapper
|
||||
private void InitializeCommands(ICommandItem[] commands, IFallbackCommandItem[] fallbacks, IServiceProvider serviceProvider, WeakReference<IPageContext> pageContext)
|
||||
{
|
||||
var settings = serviceProvider.GetService<SettingsModel>()!;
|
||||
var providerSettings = GetProviderSettings(settings);
|
||||
|
||||
Func<ICommandItem?, bool, TopLevelViewModel> makeAndAdd = (ICommandItem? i, bool fallback) =>
|
||||
{
|
||||
CommandItemViewModel commandItemViewModel = new(new(i), pageContext);
|
||||
TopLevelViewModel topLevelViewModel = new(commandItemViewModel, fallback, ExtensionHost, ProviderId, settings, serviceProvider);
|
||||
|
||||
topLevelViewModel.ItemViewModel.SlowInitializeProperties();
|
||||
TopLevelViewModel topLevelViewModel = new(commandItemViewModel, fallback, ExtensionHost, ProviderId, settings, providerSettings, serviceProvider);
|
||||
topLevelViewModel.InitializeProperties();
|
||||
|
||||
return topLevelViewModel;
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ internal sealed partial class FallbackLogItem : FallbackCommandItem
|
||||
private readonly LogMessagesPage _logMessagesPage;
|
||||
|
||||
public FallbackLogItem()
|
||||
: base(new LogMessagesPage(), Resources.builtin_log_subtitle)
|
||||
: base(new LogMessagesPage() { Id = "com.microsoft.cmdpal.log" }, Resources.builtin_log_subtitle)
|
||||
{
|
||||
_logMessagesPage = (LogMessagesPage)Command!;
|
||||
Title = string.Empty;
|
||||
|
||||
@@ -12,7 +12,9 @@ internal sealed partial class FallbackReloadItem : FallbackCommandItem
|
||||
private readonly ReloadExtensionsCommand _reloadCommand;
|
||||
|
||||
public FallbackReloadItem()
|
||||
: base(new ReloadExtensionsCommand(), Properties.Resources.builtin_reload_display_title)
|
||||
: base(
|
||||
new ReloadExtensionsCommand() { Id = "com.microsoft.cmdpal.reload" },
|
||||
Properties.Resources.builtin_reload_display_title)
|
||||
{
|
||||
_reloadCommand = (ReloadExtensionsCommand)Command!;
|
||||
Title = string.Empty;
|
||||
|
||||
@@ -13,6 +13,7 @@ public partial class QuitCommand : InvokableCommand, IFallbackHandler
|
||||
{
|
||||
public QuitCommand()
|
||||
{
|
||||
Id = "com.microsoft.cmdpal.quit";
|
||||
Icon = new IconInfo("\uE711");
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ public class ProviderSettings
|
||||
{
|
||||
public bool IsEnabled { get; set; } = true;
|
||||
|
||||
public Dictionary<string, bool> FallbackCommands { get; set; } = [];
|
||||
|
||||
[JsonIgnore]
|
||||
public string ProviderDisplayName { get; set; } = string.Empty;
|
||||
|
||||
@@ -42,4 +44,14 @@ public class ProviderSettings
|
||||
throw new InvalidDataException("Did you add a built-in command and forget to set the Id? Make sure you do that!");
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsFallbackEnabled(TopLevelViewModel command)
|
||||
{
|
||||
return FallbackCommands.TryGetValue(command.Id, out var enabled) ? enabled : true;
|
||||
}
|
||||
|
||||
public void SetFallbackEnabled(TopLevelViewModel command, bool enabled)
|
||||
{
|
||||
FallbackCommands[command.Id] = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,11 @@ public partial class ProviderSettingsViewModel(
|
||||
|
||||
public string ExtensionName => _provider.Extension?.ExtensionDisplayName ?? "Built-in";
|
||||
|
||||
public string ExtensionSubtext => IsEnabled ? $"{ExtensionName}, {TopLevelCommands.Count} commands" : Resources.builtin_disabled_extension;
|
||||
public string ExtensionSubtext => IsEnabled ?
|
||||
HasFallbackCommands ?
|
||||
$"{ExtensionName}, {TopLevelCommands.Count} commands, {FallbackCommands.Count} fallback commands" :
|
||||
$"{ExtensionName}, {TopLevelCommands.Count} commands" :
|
||||
Resources.builtin_disabled_extension;
|
||||
|
||||
[MemberNotNullWhen(true, nameof(Extension))]
|
||||
public bool IsFromExtension => _provider.Extension != null;
|
||||
@@ -139,6 +143,31 @@ public partial class ProviderSettingsViewModel(
|
||||
return [.. providersCommands];
|
||||
}
|
||||
|
||||
[field: AllowNull]
|
||||
public List<TopLevelViewModel> FallbackCommands
|
||||
{
|
||||
get
|
||||
{
|
||||
if (field == null)
|
||||
{
|
||||
field = BuildFallbackViewModels();
|
||||
}
|
||||
|
||||
return field;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasFallbackCommands => _provider.FallbackItems?.Length > 0;
|
||||
|
||||
private List<TopLevelViewModel> BuildFallbackViewModels()
|
||||
{
|
||||
var thisProvider = _provider;
|
||||
var providersCommands = thisProvider.FallbackItems;
|
||||
|
||||
// Remember! This comes in on the UI thread!
|
||||
return [.. providersCommands];
|
||||
}
|
||||
|
||||
private void Save() => SettingsModel.SaveSettings(_settings);
|
||||
|
||||
private void InitializeSettingsPage()
|
||||
|
||||
@@ -98,19 +98,28 @@ public partial class TopLevelCommandManager : ObservableObject,
|
||||
|
||||
await commandProvider.LoadTopLevelCommands(_serviceProvider, weakSelf);
|
||||
|
||||
var settings = _serviceProvider.GetService<SettingsModel>()!;
|
||||
var commands = await Task.Factory.StartNew(
|
||||
() =>
|
||||
{
|
||||
List<TopLevelViewModel> commands = [];
|
||||
foreach (var item in commandProvider.TopLevelItems)
|
||||
{
|
||||
TopLevelCommands.Add(item);
|
||||
}
|
||||
|
||||
List<TopLevelViewModel> commands = [];
|
||||
foreach (var item in commandProvider.FallbackItems)
|
||||
{
|
||||
if (item.IsEnabled)
|
||||
{
|
||||
TopLevelCommands.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var item in commandProvider.TopLevelItems)
|
||||
{
|
||||
commands.Add(item);
|
||||
}
|
||||
|
||||
foreach (var item in commandProvider.FallbackItems)
|
||||
{
|
||||
commands.Add(item);
|
||||
}
|
||||
return commands;
|
||||
},
|
||||
CancellationToken.None,
|
||||
TaskCreationOptions.None,
|
||||
_taskScheduler);
|
||||
|
||||
commandProvider.CommandsChanged -= CommandProvider_CommandsChanged;
|
||||
commandProvider.CommandsChanged += CommandProvider_CommandsChanged;
|
||||
@@ -176,7 +185,10 @@ public partial class TopLevelCommandManager : ObservableObject,
|
||||
|
||||
foreach (var i in sender.FallbackItems)
|
||||
{
|
||||
newItems.Add(i);
|
||||
if (i.IsEnabled)
|
||||
{
|
||||
newItems.Add(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Slice out the old commands
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
using System.Collections.ObjectModel;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Settings;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
@@ -17,6 +19,7 @@ namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
public sealed partial class TopLevelViewModel : ObservableObject, IListItem
|
||||
{
|
||||
private readonly SettingsModel _settings;
|
||||
private readonly ProviderSettings _providerSettings;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly CommandItemViewModel _commandItemViewModel;
|
||||
|
||||
@@ -77,6 +80,9 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem
|
||||
////// INotifyPropChanged
|
||||
public event TypedEventHandler<object, IPropChangedEventArgs>? PropChanged;
|
||||
|
||||
// Fallback items
|
||||
public string DisplayTitle { get; private set; } = string.Empty;
|
||||
|
||||
public HotkeySettings? Hotkey
|
||||
{
|
||||
get => _hotkey;
|
||||
@@ -133,16 +139,32 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsEnabled
|
||||
{
|
||||
get => _providerSettings.IsFallbackEnabled(this);
|
||||
set
|
||||
{
|
||||
if (value != IsEnabled)
|
||||
{
|
||||
_providerSettings.SetFallbackEnabled(this, value);
|
||||
Save();
|
||||
WeakReferenceMessenger.Default.Send<ReloadCommandsMessage>(new());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TopLevelViewModel(
|
||||
CommandItemViewModel item,
|
||||
bool isFallback,
|
||||
CommandPaletteHost extensionHost,
|
||||
string commandProviderId,
|
||||
SettingsModel settings,
|
||||
ProviderSettings providerSettings,
|
||||
IServiceProvider serviceProvider)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_settings = settings;
|
||||
_providerSettings = providerSettings;
|
||||
_commandProviderId = commandProviderId;
|
||||
_commandItemViewModel = item;
|
||||
|
||||
@@ -156,6 +178,22 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem
|
||||
// UpdateTags();
|
||||
}
|
||||
|
||||
internal void InitializeProperties()
|
||||
{
|
||||
ItemViewModel.SlowInitializeProperties();
|
||||
|
||||
if (IsFallback)
|
||||
{
|
||||
var model = _commandItemViewModel.Model.Unsafe;
|
||||
|
||||
// RPC to check type
|
||||
if (model is IFallbackCommandItem fallback)
|
||||
{
|
||||
DisplayTitle = fallback.DisplayTitle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Item_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(e.PropertyName))
|
||||
@@ -240,7 +278,7 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem
|
||||
{
|
||||
// Use WyHash64 to generate stable ID hashes.
|
||||
// manually seeding with 0, so that the hash is stable across launches
|
||||
var result = WyHash64.ComputeHash64(_commandProviderId + Title + Subtitle, seed: 0);
|
||||
var result = WyHash64.ComputeHash64(_commandProviderId + DisplayTitle + Title + Subtitle, seed: 0);
|
||||
_generatedId = $"{_commandProviderId}{result}";
|
||||
}
|
||||
|
||||
@@ -263,6 +301,11 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsEnabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return UnsafeUpdateFallbackSynchronous(newQuery);
|
||||
|
||||
@@ -106,6 +106,40 @@
|
||||
</ItemsRepeater.ItemTemplate>
|
||||
</ItemsRepeater>
|
||||
|
||||
<TextBlock
|
||||
x:Uid="ExtensionFallbackCommandsHeader"
|
||||
Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
|
||||
Visibility="{x:Bind ViewModel.HasFallbackCommands}" />
|
||||
|
||||
<ItemsRepeater
|
||||
ItemsSource="{x:Bind ViewModel.FallbackCommands, Mode=OneWay}"
|
||||
Layout="{StaticResource VerticalStackLayout}"
|
||||
Visibility="{x:Bind ViewModel.HasFallbackCommands}">
|
||||
<ItemsRepeater.ItemTemplate>
|
||||
<DataTemplate x:DataType="viewModels:TopLevelViewModel">
|
||||
<controls:SettingsCard DataContext="{x:Bind}" Header="{x:Bind DisplayTitle, Mode=OneWay}">
|
||||
<controls:SettingsCard.HeaderIcon>
|
||||
<cpcontrols:ContentIcon>
|
||||
<cpcontrols:ContentIcon.Content>
|
||||
<cpcontrols:IconBox
|
||||
Width="20"
|
||||
Height="20"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
SourceKey="{x:Bind Icon, Mode=OneWay}"
|
||||
SourceRequested="{x:Bind helpers:IconCacheProvider.SourceRequested}" />
|
||||
</cpcontrols:ContentIcon.Content>
|
||||
</cpcontrols:ContentIcon>
|
||||
</controls:SettingsCard.HeaderIcon>
|
||||
|
||||
<!-- Content goes here -->
|
||||
<ToggleSwitch IsOn="{x:Bind IsEnabled, Mode=TwoWay}" />
|
||||
|
||||
|
||||
</controls:SettingsCard>
|
||||
</DataTemplate>
|
||||
</ItemsRepeater.ItemTemplate>
|
||||
</ItemsRepeater>
|
||||
|
||||
<TextBlock
|
||||
x:Uid="ExtensionSettingsHeader"
|
||||
Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
|
||||
|
||||
@@ -241,6 +241,10 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
|
||||
<value>Commands</value>
|
||||
<comment>A section header for information about the app</comment>
|
||||
</data>
|
||||
<data name="ExtensionFallbackCommandsHeader.Text" xml:space="preserve">
|
||||
<value>Fallback commands</value>
|
||||
<comment>A section header for information about the commands presented to the user when the search text doesn't exactly match the name of a command.</comment>
|
||||
</data>
|
||||
<data name="ExtensionDisabledHeader.Text" xml:space="preserve">
|
||||
<value>This extension is disabled</value>
|
||||
<comment>A header to inform the user that an extension is not currently active</comment>
|
||||
|
||||
@@ -15,6 +15,8 @@ namespace Microsoft.CmdPal.Ext.Indexer;
|
||||
|
||||
internal sealed partial class FallbackOpenFileItem : FallbackCommandItem, System.IDisposable
|
||||
{
|
||||
private static readonly NoOpCommand _baseCommandWithId = new() { Id = "com.microsoft.indexer.fallback" };
|
||||
|
||||
private readonly CompositeFormat fallbackItemSearchPageTitleCompositeFormat = CompositeFormat.Parse(Resources.Indexer_fallback_searchPage_title);
|
||||
|
||||
private readonly SearchEngine _searchEngine = new();
|
||||
@@ -22,10 +24,11 @@ internal sealed partial class FallbackOpenFileItem : FallbackCommandItem, System
|
||||
private uint _queryCookie = 10;
|
||||
|
||||
public FallbackOpenFileItem()
|
||||
: base(new NoOpCommand(), Resources.Indexer_Find_Path_fallback_display_title)
|
||||
: base(_baseCommandWithId, Resources.Indexer_Find_Path_fallback_display_title)
|
||||
{
|
||||
Title = string.Empty;
|
||||
Subtitle = string.Empty;
|
||||
Icon = Icons.FileExplorer;
|
||||
}
|
||||
|
||||
public override void UpdateQuery(string query)
|
||||
|
||||
@@ -6,7 +6,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Commands;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Data;
|
||||
|
||||
@@ -15,7 +15,9 @@ internal sealed partial class FallbackExecuteItem : FallbackCommandItem
|
||||
private readonly SettingsManager _settings;
|
||||
|
||||
public FallbackExecuteItem(SettingsManager settings)
|
||||
: base(new ExecuteItem(string.Empty, settings), Resources.shell_command_display_title)
|
||||
: base(
|
||||
new ExecuteItem(string.Empty, settings) { Id = "com.microsoft.run.fallback" },
|
||||
Resources.shell_command_display_title)
|
||||
{
|
||||
_settings = settings;
|
||||
_executeItem = (ExecuteItem)this.Command!;
|
||||
|
||||
@@ -17,7 +17,7 @@ internal sealed partial class FallbackExecuteSearchItem : FallbackCommandItem
|
||||
private static readonly CompositeFormat PluginOpen = System.Text.CompositeFormat.Parse(Properties.Resources.plugin_open);
|
||||
|
||||
public FallbackExecuteSearchItem(SettingsManager settings)
|
||||
: base(new SearchWebCommand(string.Empty, settings), Resources.command_item_title)
|
||||
: base(new SearchWebCommand(string.Empty, settings) { Id = "com.microsoft.websearch.fallback" }, Resources.command_item_title)
|
||||
{
|
||||
_executeItem = (SearchWebCommand)this.Command!;
|
||||
Title = string.Empty;
|
||||
|
||||
@@ -7,7 +7,6 @@ using System.Globalization;
|
||||
using System.Text;
|
||||
using Microsoft.CmdPal.Ext.WebSearch.Commands;
|
||||
using Microsoft.CmdPal.Ext.WebSearch.Helpers;
|
||||
using Microsoft.CmdPal.Ext.WebSearch.Properties;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using BrowserInfo = Microsoft.CmdPal.Ext.WebSearch.Helpers.DefaultBrowserInfo;
|
||||
|
||||
@@ -20,7 +19,7 @@ internal sealed partial class FallbackOpenURLItem : FallbackCommandItem
|
||||
private static readonly CompositeFormat PluginOpenUrlInBrowser = System.Text.CompositeFormat.Parse(Properties.Resources.plugin_open_url_in_browser);
|
||||
|
||||
public FallbackOpenURLItem(SettingsManager settings)
|
||||
: base(new OpenURLCommand(string.Empty, settings), string.Empty)
|
||||
: base(new OpenURLCommand(string.Empty, settings), Properties.Resources.open_url_fallback_title)
|
||||
{
|
||||
_executeItem = (OpenURLCommand)this.Command!;
|
||||
Title = string.Empty;
|
||||
|
||||
@@ -132,6 +132,15 @@ namespace Microsoft.CmdPal.Ext.WebSearch.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Open URL.
|
||||
/// </summary>
|
||||
public static string open_url_fallback_title {
|
||||
get {
|
||||
return ResourceManager.GetString("open_url_fallback_title", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to the default browser.
|
||||
/// </summary>
|
||||
|
||||
@@ -178,4 +178,7 @@
|
||||
<data name="settings_page_name" xml:space="preserve">
|
||||
<value>Settings</value>
|
||||
</data>
|
||||
<data name="open_url_fallback_title" xml:space="preserve">
|
||||
<value>Open URL</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -18,7 +18,7 @@ public partial class Command : BaseObservable, ICommand
|
||||
|
||||
= string.Empty;
|
||||
|
||||
public virtual string Id { get; protected set; } = string.Empty;
|
||||
public virtual string Id { get; set; } = string.Empty;
|
||||
|
||||
public virtual IconInfo Icon
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user