diff --git a/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/CommandViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/CommandViewModel.cs index cfa316b80e..111d27589a 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/CommandViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/CommandViewModel.cs @@ -83,7 +83,7 @@ public partial class CommandViewModel : ExtensionObjectViewModel UpdateProperty(nameof(Icon)); } - if (model is ICommandWithProperties command2) + if (model is IHaveProperties command2) { Properties = command2.Properties; } @@ -121,8 +121,8 @@ public partial class CommandViewModel : ExtensionObjectViewModel Icon = new(iconInfo); Icon.InitializeProperties(); break; - case nameof(ICommandWithProperties.Properties): - if (model is ICommandWithProperties command2) + case nameof(IHaveProperties.Properties): + if (model is IHaveProperties command2) { Properties = command2.Properties; } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/IconDataViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/IconDataViewModel.cs index 70b143864c..5d4ffff3f1 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/IconDataViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/IconDataViewModel.cs @@ -27,6 +27,8 @@ public partial class IconDataViewModel : ObservableObject, IIconData IRandomAccessStreamReference? IIconData.Data => Data.Unsafe; + public string? FontFamily { get; private set; } + public IconDataViewModel(IIconData? icon) { _model = new(icon); @@ -43,5 +45,13 @@ public partial class IconDataViewModel : ObservableObject, IIconData Icon = model.Icon; Data = new(model.Data); + + if (model is IHaveProperties icon2) + { + if (icon2.Properties?.TryGetValue("FontFamily", out var family) ?? false) + { + FontFamily = family as string; + } + } } } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/CommandProviderWrapper.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/CommandProviderWrapper.cs index b83d57ea7d..2bbfb05aa4 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/CommandProviderWrapper.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/CommandProviderWrapper.cs @@ -214,9 +214,9 @@ public sealed class CommandProviderWrapper Logger.LogDebug($"Provider supports {apiExtensions.Length} extensions"); foreach (var a in apiExtensions) { - if (a is ICommandWithProperties command2) + if (a is IHaveProperties command2) { - Logger.LogDebug($"{ProviderId}: Found an ICommand2"); + Logger.LogDebug($"{ProviderId}: Found an IHaveProperties"); } } } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Helpers/IconCacheService.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/Helpers/IconCacheService.cs index 59a6d04ca4..fc535b012a 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Helpers/IconCacheService.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Helpers/IconCacheService.cs @@ -25,7 +25,7 @@ public sealed class IconCacheService(DispatcherQueue dispatcherQueue) { if (!string.IsNullOrEmpty(icon.Icon)) { - var source = IconPathConverter.IconSourceMUX(icon.Icon, false); + var source = IconPathConverter.IconSourceMUX(icon.Icon, false, icon.FontFamily); return source; } else if (icon.Data != null) diff --git a/src/modules/cmdpal/Microsoft.Terminal.UI/IconPathConverter.cpp b/src/modules/cmdpal/Microsoft.Terminal.UI/IconPathConverter.cpp index 935bcb936f..62a704f390 100644 --- a/src/modules/cmdpal/Microsoft.Terminal.UI/IconPathConverter.cpp +++ b/src/modules/cmdpal/Microsoft.Terminal.UI/IconPathConverter.cpp @@ -158,7 +158,7 @@ namespace winrt::Microsoft::Terminal::UI::implementation // Return Value: // - An IconElement with its IconSource set, if possible. template - TIconSource _getIconSource(const winrt::hstring& iconPath, bool monochrome, const int targetSize) + TIconSource _getIconSource(const winrt::hstring& iconPath, bool monochrome, const winrt::hstring& fontFamily, const int targetSize) { TIconSource iconSource{ nullptr }; @@ -187,6 +187,11 @@ namespace winrt::Microsoft::Terminal::UI::implementation { icon.FontFamily(winrt::Microsoft::UI::Xaml::Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" }); } + else if (!fontFamily.empty()) + { + icon.FontFamily(winrt::Microsoft::UI::Xaml::Media::FontFamily{ fontFamily }); + + } else { // Note: you _do_ need to manually set the font here. @@ -225,9 +230,9 @@ namespace winrt::Microsoft::Terminal::UI::implementation // return _getIconSource(path, false); // } - static Microsoft::UI::Xaml::Controls::IconSource _IconSourceMUX(const hstring& path, bool monochrome, const int targetSize) + static Microsoft::UI::Xaml::Controls::IconSource _IconSourceMUX(const hstring& path, bool monochrome, const winrt::hstring& fontFamily, const int targetSize) { - return _getIconSource(path, monochrome, targetSize); + return _getIconSource(path, monochrome, fontFamily, targetSize); } static SoftwareBitmap _convertToSoftwareBitmap(HICON hicon, @@ -343,13 +348,14 @@ namespace winrt::Microsoft::Terminal::UI::implementation MUX::Controls::IconSource IconPathConverter::IconSourceMUX(const winrt::hstring& iconPath, const bool monochrome, + const winrt::hstring& fontFamily, const int targetSize) { std::wstring_view iconPathWithoutIndex; const auto indexOpt = _getIconIndex(iconPath, iconPathWithoutIndex); if (!indexOpt.has_value()) { - return _IconSourceMUX(iconPath, monochrome, targetSize); + return _IconSourceMUX(iconPath, monochrome, fontFamily, targetSize); } const auto bitmapSource = _getImageIconSourceForBinary(iconPathWithoutIndex, indexOpt.value(), targetSize); @@ -369,7 +375,7 @@ namespace winrt::Microsoft::Terminal::UI::implementation const auto indexOpt = _getIconIndex(iconPath, iconPathWithoutIndex); if (!indexOpt.has_value()) { - auto source = IconSourceMUX(iconPath, false, targetSize); + auto source = IconSourceMUX(iconPath, false, L"", targetSize); Microsoft::UI::Xaml::Controls::IconSourceElement icon; icon.IconSource(source); return icon; diff --git a/src/modules/cmdpal/Microsoft.Terminal.UI/IconPathConverter.h b/src/modules/cmdpal/Microsoft.Terminal.UI/IconPathConverter.h index 74f6311a23..8f504eb8c7 100644 --- a/src/modules/cmdpal/Microsoft.Terminal.UI/IconPathConverter.h +++ b/src/modules/cmdpal/Microsoft.Terminal.UI/IconPathConverter.h @@ -10,7 +10,7 @@ namespace winrt::Microsoft::Terminal::UI::implementation //static Windows::UI::Xaml::Controls::IconElement IconWUX(const winrt::hstring& iconPath); //static Windows::UI::Xaml::Controls::IconSource IconSourceWUX(const winrt::hstring& iconPath); - static Microsoft::UI::Xaml::Controls::IconSource IconSourceMUX(const winrt::hstring& iconPath, bool convertToGrayscale, const int targetSize=24); + static Microsoft::UI::Xaml::Controls::IconSource IconSourceMUX(const winrt::hstring& iconPath, bool convertToGrayscale, const winrt::hstring& fontFamily, const int targetSize=24); static Microsoft::UI::Xaml::Controls::IconElement IconMUX(const winrt::hstring& iconPath); static Microsoft::UI::Xaml::Controls::IconElement IconMUX(const winrt::hstring& iconPath, const int targetSize); diff --git a/src/modules/cmdpal/Microsoft.Terminal.UI/IconPathConverter.idl b/src/modules/cmdpal/Microsoft.Terminal.UI/IconPathConverter.idl index 5b6f677003..63f2aa93f4 100644 --- a/src/modules/cmdpal/Microsoft.Terminal.UI/IconPathConverter.idl +++ b/src/modules/cmdpal/Microsoft.Terminal.UI/IconPathConverter.idl @@ -7,7 +7,7 @@ namespace Microsoft.Terminal.UI { // static Windows.UI.Xaml.Controls.IconElement IconWUX(String path); // static Windows.UI.Xaml.Controls.IconSource IconSourceWUX(String path); - static Microsoft.UI.Xaml.Controls.IconSource IconSourceMUX(String path, Boolean convertToGrayscale); + static Microsoft.UI.Xaml.Controls.IconSource IconSourceMUX(String path, Boolean convertToGrayscale, String fontFamily); static Microsoft.UI.Xaml.Controls.IconElement IconMUX(String path); static Microsoft.UI.Xaml.Controls.IconElement IconMUX(String path, Int32 targetSize); }; diff --git a/src/modules/cmdpal/doc/initial-sdk-spec/initial-sdk-spec.md b/src/modules/cmdpal/doc/initial-sdk-spec/initial-sdk-spec.md index 46a24a7d37..40393fa9b1 100644 --- a/src/modules/cmdpal/doc/initial-sdk-spec/initial-sdk-spec.md +++ b/src/modules/cmdpal/doc/initial-sdk-spec/initial-sdk-spec.md @@ -1969,7 +1969,7 @@ So that's exactly what we're going to do, because it works. As an example, we're going to add the following interface to our API: ```csharp -interface ICommandWithProperties requires ICommand +interface IHaveProperties { Windows.Foundation.Collections.IPropertySet Properties { get; }; }; @@ -1980,30 +1980,30 @@ interface ICommandProvider2 requires ICommandProvider }; ``` -`ICommandWithProperties` is just a simple interface, indicating that there's -some property bag of additional values that the host could read. `ICommand` -proves uniquely challenging to extend, because it has both the -`IInvokableCommand` and `IPage` family trees of interfaces which extend from -it. -Typically, it would be impossible for a class to be defined as +`IHaveProperties` is just a simple interface, indicating that there's +some property bag of additional values that the host could read. + +AS an example, `ICommand` proves uniquely challenging to extend, because it has +both the `IInvokableCommand` and `IPage` family trees of interfaces which +extend from it. Typically, it would be impossible for a class to be defined as ```cs -class MyCommandWithProperties : IInvokableCommand, ICommandWithProperties { ... } +class MyCommandWithProperties : IInvokableCommand, IHaveProperties { ... } ``` because Command Palette would only ever see the _first_ interface (`IInvokableCommand`) via MBM, and would never be able to check if an extension -object was an `ICommandWithProperties`. But a class defined like +object was an `IHaveProperties`. But a class defined like ```cs -class CommandWithOnlyProperties : ICommandWithProperties { ... } +class CommandWithOnlyProperties : IHaveProperties { ... } ``` will populate the WinRT type cache in Command Palette with the type information for `ICommandWithProperties`. In fact, if Command Palette has the -`ICommandWithProperties` type info in it's cache, and then later recieves a new +`IHaveProperties` type info in it's cache, and then later recieves a new `MyCommandWithProperties` object, it'll actually be able to know that -`MyCommandWithProperties` is an `ICommandWithProperties`. WinRT is just weird +`MyCommandWithProperties` is an `IHaveProperties`. WinRT is just weird like that some times. `ICommandProvider2` is where the magic happens. This is a _linear_ addition to @@ -2031,7 +2031,7 @@ public partial class SamplePagesCommandsProvider : CommandProvider, ICommandProv public object[] GetApiExtensionStubs() { return [new SupportCommandsWithProperties()]; } - private sealed partial class SupportCommandsWithProperties : ICommand2 { + private sealed partial class SupportCommandsWithProperties : IHaveProperties { public IPropertySet OtherProperties => null; public IIconInfo Icon => null; public string Id => string.Empty; diff --git a/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleListPage.cs b/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleListPage.cs index d790019ef2..b5b3874a14 100644 --- a/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleListPage.cs +++ b/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleListPage.cs @@ -172,10 +172,6 @@ internal sealed partial class SampleListPage : ListPage Title = "Get the name of the Foreground window", }, - // new ListItem(new JustHasProps()) - // { - // Title = "Not actually invokable", - // }, new ListItem(new CommandWithProperties()) { Title = "I have properties", @@ -187,8 +183,12 @@ internal sealed partial class SampleListPage : ListPage ]; } - internal sealed partial class CommandWithProperties : InvokableCommand, ICommandWithProperties + internal sealed partial class CommandWithProperties : InvokableCommand, IHaveProperties { + private FontIconData _icon = new("\u0026", "Wingdings"); + + public override IconInfo Icon => new IconInfo(_icon, _icon); + public override string Name => "Whatever"; public IPropertySet Properties => new PropertySet() @@ -200,7 +200,7 @@ internal sealed partial class SampleListPage : ListPage } #nullable enable - internal sealed partial class OtherCommandWithProperties : ICommandWithProperties, IInvokableCommand + internal sealed partial class OtherCommandWithProperties : IHaveProperties, IInvokableCommand { public string Name => "Whatever 2"; @@ -216,13 +216,11 @@ internal sealed partial class SampleListPage : ListPage return CommandResult.ShowToast("whoop"); } - public IPropertySet OtherProperties => new PropertySet() + public IPropertySet Properties => new PropertySet() { { "yo", "dawg" }, { "Secret", 12345 }, { "hmm?", null }, }; - - public IPropertySet Properties => throw new System.NotImplementedException(); } } diff --git a/src/modules/cmdpal/ext/SamplePagesExtension/SamplePagesCommandsProvider.cs b/src/modules/cmdpal/ext/SamplePagesExtension/SamplePagesCommandsProvider.cs index e78360a578..e500c25d8d 100644 --- a/src/modules/cmdpal/ext/SamplePagesExtension/SamplePagesCommandsProvider.cs +++ b/src/modules/cmdpal/ext/SamplePagesExtension/SamplePagesCommandsProvider.cs @@ -4,12 +4,10 @@ using Microsoft.CommandPalette.Extensions; using Microsoft.CommandPalette.Extensions.Toolkit; -using Windows.Foundation; -using Windows.Foundation.Collections; namespace SamplePagesExtension; -public partial class SamplePagesCommandsProvider : CommandProvider, ICommandProvider2 +public partial class SamplePagesCommandsProvider : CommandProvider { public SamplePagesCommandsProvider() { @@ -29,26 +27,4 @@ public partial class SamplePagesCommandsProvider : CommandProvider, ICommandProv { return _commands; } - - public object[] GetApiExtensionStubs() - { - return [new SupportCommandsWithProperties()]; - } - - private sealed partial class SupportCommandsWithProperties : ICommandWithProperties - { - public IPropertySet Properties => null; - - public IIconInfo Icon => null; - - public string Id => string.Empty; - - public string Name => string.Empty; - - public event TypedEventHandler PropChanged - { - add { } - remove { } - } - } } diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/CommandProvider.cs b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/CommandProvider.cs index 1efc9475a7..62970d2766 100644 --- a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/CommandProvider.cs +++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/CommandProvider.cs @@ -3,10 +3,11 @@ // See the LICENSE file in the project root for more information. using Windows.Foundation; +using Windows.Foundation.Collections; namespace Microsoft.CommandPalette.Extensions.Toolkit; -public abstract partial class CommandProvider : ICommandProvider +public abstract partial class CommandProvider : ICommandProvider, ICommandProvider2 { public virtual string Id { get; protected set; } = string.Empty; @@ -47,4 +48,26 @@ public abstract partial class CommandProvider : ICommandProvider { } } + + /// + /// This is used to manually populate the WinRT type cache in CmdPal with + /// any interfaces that might not follow a straight linear path of requires. + /// + /// You don't need to call this as an extension author. + /// + /// an array of objects that implement all the leaf interfaces we support + public object[] GetApiExtensionStubs() + { + return [new SupportCommandsWithProperties()]; + } + + /// + /// A stub class which implements IHaveProperties. Just marshalling this + /// across the ABI will be enough for CmdPal to store IHaveProperties in + /// its type cache. + /// + private sealed partial class SupportCommandsWithProperties : IHaveProperties + { + public IPropertySet? Properties => null; + } } diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/FontIconData.cs b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/FontIconData.cs new file mode 100644 index 0000000000..ea79ef3122 --- /dev/null +++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/FontIconData.cs @@ -0,0 +1,24 @@ +// 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 Windows.Foundation.Collections; +using Windows.Storage.Streams; + +namespace Microsoft.CommandPalette.Extensions.Toolkit; + +public partial class FontIconData : IconData, IHaveProperties +{ + public string FontFace { get; set; } + + public FontIconData(string glyph, string fontFace) + : base(glyph) + { + FontFace = fontFace; + } + + public IPropertySet Properties => new PropertySet() + { + { "FontFamily", FontFace }, + }; +} diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/IconInfo.cs b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/IconInfo.cs index 674a96ec4b..731b529903 100644 --- a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/IconInfo.cs +++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/IconInfo.cs @@ -27,6 +27,12 @@ public partial class IconInfo : IIconInfo Dark = dark; } + public IconInfo(IconData icon) + { + Light = icon; + Dark = icon; + } + internal IconInfo() : this(string.Empty) { diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.idl b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.idl index cb6fe97169..c608bf7d84 100644 --- a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.idl +++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.idl @@ -365,7 +365,7 @@ namespace Microsoft.CommandPalette.Extensions }; [contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)] - interface ICommandWithProperties requires ICommand + interface IHaveProperties { Windows.Foundation.Collections.IPropertySet Properties { get; }; };