diff --git a/src/modules/cmdpal/Core/Microsoft.CmdPal.Core.Common/Helpers/PinnedDockItem.cs b/src/modules/cmdpal/Core/Microsoft.CmdPal.Core.Common/Helpers/PinnedDockItem.cs new file mode 100644 index 0000000000..ca1ce06c2c --- /dev/null +++ b/src/modules/cmdpal/Core/Microsoft.CmdPal.Core.Common/Helpers/PinnedDockItem.cs @@ -0,0 +1,23 @@ +// 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.Extensions; +using Microsoft.CommandPalette.Extensions.Toolkit; + +namespace Microsoft.CmdPal.Core.Common.Helpers; + +public partial class PinnedDockItem : WrappedDockItem +{ + public override string Title => $"{base.Title} ({Properties.Resources.PinnedItemSuffix})"; + + public PinnedDockItem(ICommand command) + : base(command, command.Name) + { + } + + public PinnedDockItem(ICommandItem item, string id) + : base(item, id, item.Title) + { + } +} diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/CommandProviderWrapper.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/CommandProviderWrapper.cs index 21e47ca033..b0b6f8c217 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/CommandProviderWrapper.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/CommandProviderWrapper.cs @@ -163,20 +163,27 @@ public sealed class CommandProviderWrapper UnsafePreCacheApiAdditions(two); } - if (model is IExtendedAttributesProvider iHaveProperties) + // if (model is IExtendedAttributesProvider iHaveProperties) + if (model is ICommandProvider3 supportsDockBands) { - var props = iHaveProperties.GetProperties(); - var hasBands = props.TryGetValue("DockBands", out var obj); - if (hasBands && obj is not null) + // var props = iHaveProperties.GetProperties(); + // var hasBands = props.TryGetValue("DockBands", out var obj); + // if (hasBands && obj is not null) + // { + // // CoreLogger.LogDebug($"Found bands object on {DisplayName} ({ProviderId}) "); + // // var bands = (ICommandItem[])obj; + // var bands = obj as ICommandItem[]; + // if (bands is not null) + // { + // CoreLogger.LogDebug($"Found {bands.Length} bands on {DisplayName} ({ProviderId}) "); + // dockBands = bands; + // } + // } + var bands = supportsDockBands.GetDockBands(); + if (bands is not null) { - // CoreLogger.LogDebug($"Found bands object on {DisplayName} ({ProviderId}) "); - // var bands = (ICommandItem[])obj; - var bands = obj as ICommandItem[]; - if (bands is not null) - { - CoreLogger.LogDebug($"Found {bands.Length} bands on {DisplayName} ({ProviderId}) "); - dockBands = bands; - } + CoreLogger.LogDebug($"Found {bands.Length} bands on {DisplayName} ({ProviderId}) "); + dockBands = bands; } } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Commands/BuiltInsCommandProvider.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Commands/BuiltInsCommandProvider.cs index 54b124ded7..aef3bdfd61 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Commands/BuiltInsCommandProvider.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Commands/BuiltInsCommandProvider.cs @@ -13,7 +13,7 @@ namespace Microsoft.CmdPal.UI.ViewModels.BuiltinCommands; /// /// Built-in Provider for a top-level command which can quit the application. Invokes the , which sends a . /// -public sealed partial class BuiltInsCommandProvider : CommandProvider, IExtendedAttributesProvider +public sealed partial class BuiltInsCommandProvider : CommandProvider { private readonly OpenSettingsCommand openSettings = new(); private readonly QuitCommand quitCommand = new(); @@ -45,16 +45,13 @@ public sealed partial class BuiltInsCommandProvider : CommandProvider, IExtended _rootPageService = rootPageService; } - public IDictionary GetProperties() + public override ICommandItem[]? GetDockBands() { var rootPage = _rootPageService.GetRootPage(); List bandItems = new(); bandItems.Add(new WrappedDockItem(rootPage, Properties.Resources.builtin_command_palette_title)); - return new PropertySet() - { - { "DockBands", bandItems.ToArray() }, - }; + return bandItems.ToArray(); } public override void InitializeWithHost(IExtensionHost host) => BuiltinsExtensionHost.Instance.Initialize(host); diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Dock/DockViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Dock/DockViewModel.cs index da5dc2f359..45f969fd57 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Dock/DockViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Dock/DockViewModel.cs @@ -67,12 +67,9 @@ public sealed partial class DockViewModel : IDisposable, var commandId = band.Id; var topLevelCommand = _topLevelCommandManager.LookupDockBand(commandId); - // TODO! temp hack: fallback to looking up a top-level command - // remove this once the API is added if (topLevelCommand is null) { - Logger.LogWarning($"Temporary fallback to loading top-level command '{commandId}'"); - topLevelCommand = _topLevelCommandManager.LookupCommand(commandId); + Logger.LogWarning($"Failed to find band {commandId}"); } if (topLevelCommand is not null) diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/TimeDateCommandsProvider.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/TimeDateCommandsProvider.cs index febaa3c6d0..2bd217838b 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/TimeDateCommandsProvider.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/TimeDateCommandsProvider.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Globalization; using System.Text; using Microsoft.CmdPal.Core.Common.Helpers; @@ -11,11 +10,10 @@ using Microsoft.CmdPal.Ext.TimeDate.Helpers; using Microsoft.CmdPal.Ext.TimeDate.Pages; using Microsoft.CommandPalette.Extensions; using Microsoft.CommandPalette.Extensions.Toolkit; -using Windows.Foundation.Collections; namespace Microsoft.CmdPal.Ext.TimeDate; -public sealed partial class TimeDateCommandsProvider : CommandProvider, IExtendedAttributesProvider +public sealed partial class TimeDateCommandsProvider : CommandProvider { private readonly CommandItem _command; private static readonly SettingsManager _settingsManager = new SettingsManager(); @@ -56,17 +54,14 @@ public sealed partial class TimeDateCommandsProvider : CommandProvider, IExtende public override IFallbackCommandItem[] FallbackCommands() => [_fallbackTimeDateItem]; - public IDictionary GetProperties() + public override ICommandItem[] GetDockBands() { var wrappedBand = new WrappedDockItem( _bandItem, "com.microsoft.cmdpal.timedate.dockband", Resources.Microsoft_plugin_timedate_dock_band_title); - return new PropertySet() - { - { "DockBands", new ICommandItem[] { wrappedBand } }, - }; + return new ICommandItem[] { wrappedBand }; } } #pragma warning disable SA1402 // File may only contain a single type diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/WindowWalkerCommandsProvider.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/WindowWalkerCommandsProvider.cs index ab59228f07..7f5460ab72 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/WindowWalkerCommandsProvider.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/WindowWalkerCommandsProvider.cs @@ -1,15 +1,13 @@ -// Copyright (c) Microsoft Corporation +// 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; -using System.Collections.Generic; using Microsoft.CmdPal.Ext.WindowWalker.Helpers; using Microsoft.CmdPal.Ext.WindowWalker.Pages; using Microsoft.CmdPal.Ext.WindowWalker.Properties; using Microsoft.CommandPalette.Extensions; using Microsoft.CommandPalette.Extensions.Toolkit; -using Windows.Foundation.Collections; using Windows.Win32; using Windows.Win32.Foundation; using Windows.Win32.UI.Accessibility; @@ -17,7 +15,7 @@ using Windows.Win32.UI.WindowsAndMessaging; namespace Microsoft.CmdPal.Ext.WindowWalker; -public partial class WindowWalkerCommandsProvider : CommandProvider, IExtendedAttributesProvider +public partial class WindowWalkerCommandsProvider : CommandProvider { private readonly CommandItem _windowWalkerPageItem; private readonly CommandItem _bandItem; @@ -45,12 +43,9 @@ public partial class WindowWalkerCommandsProvider : CommandProvider, IExtendedAt public override ICommandItem[] TopLevelCommands() => [_windowWalkerPageItem]; - public IDictionary GetProperties() + public override ICommandItem[]? GetDockBands() { - return new PropertySet() - { - { "DockBands", new ICommandItem[] { _bandItem } }, - }; + return new ICommandItem[] { _bandItem }; } } diff --git a/src/modules/cmdpal/ext/SamplePagesExtension/SampleButtonsDockBand.cs b/src/modules/cmdpal/ext/SamplePagesExtension/SampleButtonsDockBand.cs new file mode 100644 index 0000000000..c7012b2d25 --- /dev/null +++ b/src/modules/cmdpal/ext/SamplePagesExtension/SampleButtonsDockBand.cs @@ -0,0 +1,30 @@ +// 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.Generic; +using Microsoft.CommandPalette.Extensions; +using Microsoft.CommandPalette.Extensions.Toolkit; + +namespace SamplePagesExtension; + +/// +/// A sample dock band with multiple buttons. +/// Each button shows a toast message when clicked. +/// +internal sealed partial class SampleButtonsDockBand : WrappedDockItem +{ + public SampleButtonsDockBand() + : base([], "com.microsoft.cmdpal.samples.buttons_band", "Sample Buttons Band") + { + ListItem[] buttons = [ + new(new ShowToastCommand("Button 1")) { Title = "1" }, + new(new ShowToastCommand("Button B")) { Icon = new IconInfo("\uF094") }, // B button + new(new ShowToastCommand("Button 3")) { Title = "Items have Icons &", Icon = new IconInfo("\uED1E"), Subtitle = "titles & subtitles" }, // Subtitles + ]; + Icon = new IconInfo("\uEECA"); // ButtonView2 + Items = buttons; + } +} + +#pragma warning restore SA1402 // File may only contain a single type diff --git a/src/modules/cmdpal/ext/SamplePagesExtension/SampleDockBand.cs b/src/modules/cmdpal/ext/SamplePagesExtension/SampleDockBand.cs new file mode 100644 index 0000000000..b95a50ab69 --- /dev/null +++ b/src/modules/cmdpal/ext/SamplePagesExtension/SampleDockBand.cs @@ -0,0 +1,22 @@ +// 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.Extensions.Toolkit; + +namespace SamplePagesExtension; +#pragma warning disable SA1402 // File may only contain a single type + +/// +/// A sample dock band with one button. +/// Clicking on this button will open the palette to the samples list page +/// +internal sealed partial class SampleDockBand : WrappedDockItem +{ + public SampleDockBand() + : base(new SamplesListPage(), "Command Palette Samples") + { + } +} + +#pragma warning restore SA1402 // File may only contain a single type diff --git a/src/modules/cmdpal/ext/SamplePagesExtension/SamplePagesCommandsProvider.cs b/src/modules/cmdpal/ext/SamplePagesExtension/SamplePagesCommandsProvider.cs index e500c25d8d..d24d4964d3 100644 --- a/src/modules/cmdpal/ext/SamplePagesExtension/SamplePagesCommandsProvider.cs +++ b/src/modules/cmdpal/ext/SamplePagesExtension/SamplePagesCommandsProvider.cs @@ -2,6 +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 System.Collections.Generic; using Microsoft.CommandPalette.Extensions; using Microsoft.CommandPalette.Extensions.Toolkit; @@ -27,4 +28,15 @@ public partial class SamplePagesCommandsProvider : CommandProvider { return _commands; } + + public override ICommandItem[] GetDockBands() + { + List bands = new() + { + new SampleDockBand(), + new SampleButtonsDockBand(), + }; + + return bands.ToArray(); + } } diff --git a/src/modules/cmdpal/ext/SamplePagesExtension/ShowToastCommand.cs b/src/modules/cmdpal/ext/SamplePagesExtension/ShowToastCommand.cs new file mode 100644 index 0000000000..67bbd30fc5 --- /dev/null +++ b/src/modules/cmdpal/ext/SamplePagesExtension/ShowToastCommand.cs @@ -0,0 +1,19 @@ +// 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.Generic; +using Microsoft.CommandPalette.Extensions; +using Microsoft.CommandPalette.Extensions.Toolkit; + +namespace SamplePagesExtension; + +internal sealed partial class ShowToastCommand(string message) : InvokableCommand +{ + public override ICommandResult Invoke() + { + return CommandResult.ShowToast(message); + } +} + +#pragma warning restore SA1402 // File may only contain a single type 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 ca64c87b23..79f952dbe7 100644 --- a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/CommandProvider.cs +++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/CommandProvider.cs @@ -6,7 +6,10 @@ using Windows.Foundation; namespace Microsoft.CommandPalette.Extensions.Toolkit; -public abstract partial class CommandProvider : ICommandProvider, ICommandProvider2 +public abstract partial class CommandProvider : + ICommandProvider, + ICommandProvider2, + ICommandProvider3 { public virtual string Id { get; protected set; } = string.Empty; @@ -48,6 +51,11 @@ public abstract partial class CommandProvider : ICommandProvider, ICommandProvid } } + public virtual ICommandItem[]? GetDockBands() + { + return null; + } + /// /// 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. diff --git a/src/modules/cmdpal/Core/Microsoft.CmdPal.Core.Common/Helpers/WrappedDockItem.cs b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Dock/WrappedDockItem.cs similarity index 50% rename from src/modules/cmdpal/Core/Microsoft.CmdPal.Core.Common/Helpers/WrappedDockItem.cs rename to src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Dock/WrappedDockItem.cs index 6ad2291f6a..8d0d8aecfe 100644 --- a/src/modules/cmdpal/Core/Microsoft.CmdPal.Core.Common/Helpers/WrappedDockItem.cs +++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Dock/WrappedDockItem.cs @@ -2,10 +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.CommandPalette.Extensions; -using Microsoft.CommandPalette.Extensions.Toolkit; - -namespace Microsoft.CmdPal.Core.Common.Helpers; +namespace Microsoft.CommandPalette.Extensions.Toolkit; #pragma warning disable SA1402 // File may only contain a single type @@ -15,14 +12,19 @@ public partial class WrappedDockItem : CommandItem public override IIconInfo? Icon => _icon; + public override ICommand? Command => _backingList; + private readonly string _itemTitle; private readonly IIconInfo? _icon; + private readonly WrappedDockList _backingList; + + public IListItem[] Items { get => _backingList.GetItems(); set => _backingList.SetItems(value); } public WrappedDockItem( ICommand command, string displayTitle) { - Command = new WrappedDockList(command); + _backingList = new WrappedDockList(command); _itemTitle = string.IsNullOrEmpty(displayTitle) ? command.Name : displayTitle; _icon = command.Icon; } @@ -32,55 +34,44 @@ public partial class WrappedDockItem : CommandItem string id, string displayTitle) { - Command = new WrappedDockList(item, id); + _backingList = new WrappedDockList(item, id); _itemTitle = string.IsNullOrEmpty(displayTitle) ? item.Title : displayTitle; _icon = item.Icon; } -} -public partial class PinnedDockItem : WrappedDockItem -{ - public override string Title => $"{base.Title} ({Properties.Resources.PinnedItemSuffix})"; - - public PinnedDockItem(ICommand command) - : base(command, command.Name) - { - } - - public PinnedDockItem(ICommandItem item, string id) - : base(item, id, item.Title) + public WrappedDockItem(IListItem[] items, string id, string displayTitle) { + _backingList = new WrappedDockList(items, id, displayTitle); + _itemTitle = displayTitle; } } public partial class WrappedDockList : ListPage { - public override string Name => _command.Name; - private string _id; public override string Id => _id; - private ICommand _command; - private IListItem[] _items; + // private ICommand _command; + private List _items; public WrappedDockList(ICommand command) { - _command = command; - _items = new IListItem[] { new ListItem(command) }; - Name = _command.Name; - _id = _command.Id; + // _command = command; + _items = new() { new ListItem(command) }; + Name = command.Name; + _id = command.Id; } public WrappedDockList(ICommandItem item, string id) { - _command = item.Command; + var command = item.Command; // TODO! This isn't _totally correct, because the wrapping item will not // listen for property changes on the inner item. - _items = new IListItem[] + _items = new() { - new ListItem(_command) + new ListItem(command) { Title = item.Title, Subtitle = item.Subtitle, @@ -88,13 +79,45 @@ public partial class WrappedDockList : ListPage MoreCommands = item.MoreCommands, }, }; - Name = _command.Name; - _id = string.IsNullOrEmpty(id) ? _command.Id : id; + Name = command.Name; + _id = string.IsNullOrEmpty(id) ? command.Id : id; + } + + public WrappedDockList(IListItem[] items, string id, string name) + { + _items = new(items); + Name = name; + _id = id; + } + + public WrappedDockList(ICommand[] items, string id, string name) + { + _items = new(); + foreach (var item in items) + { + _items.Add(new ListItem(item)); + } + + Name = name; + _id = id; } public override IListItem[] GetItems() { - return _items; + return _items.ToArray(); + } + + internal void SetItems(IListItem[]? newItems) + { + if (newItems == null) + { + _items = []; + RaiseItemsChanged(0); + return; + } + + ListHelpers.InPlaceUpdateList(_items, newItems); + RaiseItemsChanged(_items.Count); } } 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 68fd928955..f9f515a0ce 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 @@ -391,6 +391,11 @@ namespace Microsoft.CommandPalette.Extensions { Object[] GetApiExtensionStubs(); }; - + + [contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)] + interface ICommandProvider3 requires ICommandProvider2 + { + ICommandItem[] GetDockBands(); + }; }