diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index cd757f1552..1223683f08 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -516,6 +516,7 @@ FARPROC fdx fesf FFFF +Figma FILEEXPLORER fileexploreraddons fileexplorerpreview diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Messages/ReloadFinishedMessage.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Messages/ReloadFinishedMessage.cs new file mode 100644 index 0000000000..a6e2122edd --- /dev/null +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Messages/ReloadFinishedMessage.cs @@ -0,0 +1,7 @@ +// 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.CmdPal.UI.ViewModels.Messages; + +public record ReloadFinishedMessage(); diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Properties/Resources.Designer.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Properties/Resources.Designer.cs index e3a4e31088..be9d103b2d 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Properties/Resources.Designer.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Properties/Resources.Designer.cs @@ -410,5 +410,23 @@ namespace Microsoft.CmdPal.UI.ViewModels.Properties { return ResourceManager.GetString("builtin_reload_subtitle", resourceCulture); } } + + /// + /// Looks up a localized string similar to {0} extensions found. + /// + public static string builtin_settings_extension_n_extensions_found { + get { + return ResourceManager.GetString("builtin_settings_extension_n_extensions_found", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} extensions installed. + /// + public static string builtin_settings_extension_n_extensions_installed { + get { + return ResourceManager.GetString("builtin_settings_extension_n_extensions_installed", resourceCulture); + } + } } } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Properties/Resources.resx b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Properties/Resources.resx index 6279a0bfdc..f0f68b8582 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Properties/Resources.resx +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Properties/Resources.resx @@ -236,4 +236,10 @@ Home + + {0} extensions found + + + {0} extensions installed + \ No newline at end of file diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/SettingsExtensionsViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/SettingsExtensionsViewModel.cs new file mode 100644 index 0000000000..8e14a83d19 --- /dev/null +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/SettingsExtensionsViewModel.cs @@ -0,0 +1,144 @@ +// 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.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Globalization; +using System.Text; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; + +using Microsoft.CmdPal.Core.ViewModels.Messages; +using Microsoft.CmdPal.UI.ViewModels.Messages; +using Microsoft.CommandPalette.Extensions.Toolkit; + +namespace Microsoft.CmdPal.UI.ViewModels; + +/// +/// Provides filtering over the list of provider settings view models. +/// Intended to be used by the UI to bind a TextBox (SearchText) and an ItemsRepeater (FilteredProviders). +/// +public partial class SettingsExtensionsViewModel : ObservableObject +{ + private static readonly CompositeFormat LabelNumberExtensionFound + = CompositeFormat.Parse(Properties.Resources.builtin_settings_extension_n_extensions_found!); + + private static readonly CompositeFormat LabelNumberExtensionInstalled + = CompositeFormat.Parse(Properties.Resources.builtin_settings_extension_n_extensions_installed!); + + private readonly ObservableCollection _source; + private readonly TaskScheduler _uiScheduler; + + public ObservableCollection FilteredProviders { get; } = []; + + private string _searchText = string.Empty; + + public string SearchText + { + get => _searchText; + set + { + if (_searchText != value) + { + _searchText = value; + OnPropertyChanged(); + ApplyFilter(); + } + } + } + + public string ItemCounterText + { + get + { + var hasQuery = !string.IsNullOrWhiteSpace(_searchText); + var count = hasQuery ? FilteredProviders.Count : _source.Count; + var format = hasQuery ? LabelNumberExtensionFound : LabelNumberExtensionInstalled; + return string.Format(CultureInfo.CurrentCulture, format, count); + } + } + + public bool ShowManualReloadOverlay + { + get; + private set + { + if (field != value) + { + field = value; + OnPropertyChanged(); + } + } + } + + public bool ShowNoResultsPanel => !string.IsNullOrWhiteSpace(_searchText) && FilteredProviders.Count == 0; + + public bool HasResults => !ShowNoResultsPanel; + + public IRelayCommand ReloadExtensionsCommand { get; } + + public SettingsExtensionsViewModel(ObservableCollection source, TaskScheduler uiScheduler) + { + _source = source; + _uiScheduler = uiScheduler; + _source.CollectionChanged += Source_CollectionChanged; + ApplyFilter(); + + ReloadExtensionsCommand = new RelayCommand(ReloadExtensions); + + WeakReferenceMessenger.Default.Register(this, (_, _) => + { + Task.Factory.StartNew(() => ShowManualReloadOverlay = false, CancellationToken.None, TaskCreationOptions.None, _uiScheduler); + }); + } + + private void Source_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) + { + ApplyFilter(); + } + + private void ApplyFilter() + { + var query = _searchText; + var filtered = ListHelpers.FilterList(_source, query, Matches); + ListHelpers.InPlaceUpdateList(FilteredProviders, filtered); + OnPropertyChanged(nameof(ItemCounterText)); + OnPropertyChanged(nameof(HasResults)); + OnPropertyChanged(nameof(ShowNoResultsPanel)); + } + + private static int Matches(string query, ProviderSettingsViewModel item) + { + if (string.IsNullOrWhiteSpace(query)) + { + return 100; + } + + return Contains(item.DisplayName, query) + || Contains(item.ExtensionName, query) + || Contains(item.ExtensionSubtext, query) + ? 100 + : 0; + } + + private static bool Contains(string? haystack, string needle) + { + return !string.IsNullOrEmpty(haystack) && haystack.Contains(needle, StringComparison.OrdinalIgnoreCase); + } + + [RelayCommand] + private void OpenStoreWithExtension(string? query) + { + const string extensionsAssocUri = "ms-windows-store://assoc/?Tags=AppExtension-com.microsoft.commandpalette"; + ShellHelpers.OpenInShell(extensionsAssocUri); + } + + private void ReloadExtensions() + { + ShowManualReloadOverlay = true; + WeakReferenceMessenger.Default.Send(); + WeakReferenceMessenger.Default.Send(); + } +} diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/SettingsViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/SettingsViewModel.cs index 8e05660656..380f2340ba 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/SettingsViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/SettingsViewModel.cs @@ -140,6 +140,8 @@ public partial class SettingsViewModel : INotifyPropertyChanged public ObservableCollection CommandProviders { get; } = []; + public SettingsExtensionsViewModel Extensions { get; } + public SettingsViewModel(SettingsModel settings, IServiceProvider serviceProvider, TaskScheduler scheduler) { _settings = settings; @@ -155,6 +157,8 @@ public partial class SettingsViewModel : INotifyPropertyChanged var settingsModel = new ProviderSettingsViewModel(item, providerSettings, _serviceProvider); CommandProviders.Add(settingsModel); } + + Extensions = new SettingsExtensionsViewModel(CommandProviders, scheduler); } private IEnumerable GetCommandProviders() diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/TopLevelCommandManager.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/TopLevelCommandManager.cs index 5dd41280bf..c113b508d3 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/TopLevelCommandManager.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/TopLevelCommandManager.cs @@ -259,6 +259,9 @@ public partial class TopLevelCommandManager : ObservableObject, IsLoading = false; + // Send on the current thread; receivers should marshal to UI if needed + WeakReferenceMessenger.Default.Send(); + return true; } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Assets/StoreLogo.dark.svg b/src/modules/cmdpal/Microsoft.CmdPal.UI/Assets/StoreLogo.dark.svg new file mode 100644 index 0000000000..ec3cb933d8 --- /dev/null +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Assets/StoreLogo.dark.svg @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Assets/StoreLogo.light.svg b/src/modules/cmdpal/Microsoft.CmdPal.UI/Assets/StoreLogo.light.svg new file mode 100644 index 0000000000..9d74683716 --- /dev/null +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Assets/StoreLogo.light.svg @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Assets/WinGetLogo.svg b/src/modules/cmdpal/Microsoft.CmdPal.UI/Assets/WinGetLogo.svg new file mode 100644 index 0000000000..0bf3ebbaff --- /dev/null +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Assets/WinGetLogo.svg @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Microsoft.CmdPal.UI.csproj b/src/modules/cmdpal/Microsoft.CmdPal.UI/Microsoft.CmdPal.UI.csproj index f577f13898..15bc3920fd 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Microsoft.CmdPal.UI.csproj +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Microsoft.CmdPal.UI.csproj @@ -181,6 +181,15 @@ PreserveNewest + + Never + + + Never + + + Never + diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Settings/ExtensionsPage.xaml b/src/modules/cmdpal/Microsoft.CmdPal.UI/Settings/ExtensionsPage.xaml index f798134c10..9e9600bd6e 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Settings/ExtensionsPage.xaml +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Settings/ExtensionsPage.xaml @@ -14,18 +14,204 @@ xmlns:viewModels="using:Microsoft.CmdPal.UI.ViewModels" mc:Ignorable="d"> + + + + + + + + + + ms-appx:///Assets/StoreLogo.dark.svg + + + + + + + + + + + + + + + + ms-appx:///Assets/StoreLogo.light.svg + + + + + ms-appx:///Assets/StoreLogo.dark.svg + + + + + + + - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Settings/ExtensionsPage.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/Settings/ExtensionsPage.xaml.cs index e164638ebb..e99296bad9 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Settings/ExtensionsPage.xaml.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Settings/ExtensionsPage.xaml.cs @@ -8,6 +8,7 @@ using Microsoft.CmdPal.UI.ViewModels; using Microsoft.Extensions.DependencyInjection; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Input; namespace Microsoft.CmdPal.UI.Settings; @@ -35,4 +36,10 @@ public sealed partial class ExtensionsPage : Page } } } + + private void OnFindInvoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args) + { + SearchBox?.Focus(FocusState.Keyboard); + args.Handled = true; + } } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Strings/en-us/Resources.resw b/src/modules/cmdpal/Microsoft.CmdPal.UI/Strings/en-us/Resources.resw index 4eb8be9b2b..506a879761 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Strings/en-us/Resources.resw +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Strings/en-us/Resources.resw @@ -474,4 +474,43 @@ Right-click to remove the key combination, thereby deactivating the shortcut. Settings (Ctrl+,) + + No extensions found + + + Try a different search term + + + More options + + + Reload extensions + + + Reloading extensions.. + + + Discover more extensions + + + Find more extensions on the Microsoft Store or WinGet. + + + Learn how to create your own extensions + + + Find extensions on the Microsoft Store + + + Microsoft Store + + + Find extensions on WinGet + + + Microsoft Store + + + Search extensions + \ No newline at end of file