diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/CommandProviderWrapper.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/CommandProviderWrapper.cs index 8b871adc08..25f81f424a 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/CommandProviderWrapper.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/CommandProviderWrapper.cs @@ -8,6 +8,7 @@ using Microsoft.CmdPal.Core.Common.Services; using Microsoft.CmdPal.Core.ViewModels; using Microsoft.CmdPal.Core.ViewModels.Models; using Microsoft.CommandPalette.Extensions; +using Microsoft.CommandPalette.Extensions.Toolkit; using Microsoft.Extensions.DependencyInjection; using Windows.Foundation; @@ -289,4 +290,17 @@ public sealed class CommandProviderWrapper // In handling this, a call will be made to `LoadTopLevelCommands` to // retrieve the new items. this.CommandsChanged?.Invoke(this, args); + + internal void PinDockBand(TopLevelViewModel bandVm) + { + Logger.LogDebug($"CommandProviderWrapper.PinDockBand: {ProviderId} - {bandVm.Id}"); + + // var settings = ExtensionHost.ServiceProvider.GetService()!; + // settings.DockSettings.PinnedCommands.Add(bandVm.Id); + // SettingsModel.SaveSettings(settings); + var bands = this.DockBandItems.ToList(); + bands.Add(bandVm); + this.DockBandItems = bands.ToArray(); + this.CommandsChanged?.Invoke(this, new ItemsChangedEventArgs()); + } } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/TopLevelCommandManager.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/TopLevelCommandManager.cs index 06b9bf55a1..bf10cd8685 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/TopLevelCommandManager.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/TopLevelCommandManager.cs @@ -182,6 +182,8 @@ public partial class TopLevelCommandManager : ObservableObject, } } + List newBands = [.. sender.DockBandItems]; + // modify the TopLevelCommands under shared lock; event if we clone it, we don't want // TopLevelCommands to get modified while we're working on it. Otherwise, we might // out clone would be stale at the end of this method. @@ -198,6 +200,13 @@ public partial class TopLevelCommandManager : ObservableObject, clone.InsertRange(startIndex, newItems); ListHelpers.InPlaceUpdateList(TopLevelCommands, clone); + + // same idea for DockBands + List dockClone = [.. DockBands]; + var dockStartIndex = FindIndexForFirstProviderItem(dockClone, sender.ProviderId); + dockClone.RemoveAll(item => item.CommandProviderId == sender.ProviderId); + dockClone.InsertRange(dockStartIndex, newBands); + ListHelpers.InPlaceUpdateList(DockBands, dockClone); } return; @@ -485,6 +494,41 @@ public partial class TopLevelCommandManager : ObservableObject, } } + internal void PinDockBand(TopLevelViewModel bandVm) + { + lock (DockBands) + { + foreach (var existing in DockBands) + { + if (existing.Id == bandVm.Id) + { + // already pinned + Logger.LogDebug($"Dock band '{bandVm.Id}' is already pinned."); + return; + } + } + + Logger.LogDebug($"Attempting to pin dock band '{bandVm.Id}' from provider '{bandVm.CommandProviderId}'."); + var providerId = bandVm.CommandProviderId; + var foundProvider = false; + foreach (var provider in CommandProviders) + { + if (provider.Id == providerId) + { + Logger.LogDebug($"Found provider '{providerId}' to pin dock band '{bandVm.Id}'."); + provider.PinDockBand(bandVm); + foundProvider = true; + break; + } + } + + if (!foundProvider) + { + Logger.LogWarning($"Could not find provider '{providerId}' to pin dock band '{bandVm.Id}'."); + } + } + } + public void Dispose() { _reloadCommandsGate.Dispose(); diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/TopLevelViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/TopLevelViewModel.cs index 3caa7f3939..75ccc7793b 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/TopLevelViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/TopLevelViewModel.cs @@ -418,7 +418,7 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem this, _dockViewModel, _settings, - _serviceProvider.GetService()!)); + _serviceProvider.GetService()!)); } return contextItems.ToArray(); @@ -431,6 +431,18 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem return item; } + internal TopLevelViewModel CloneAsBand() + { + return new TopLevelViewModel( + _commandItemViewModel, + isFallback: false, + ExtensionHost, + _commandProviderId, + _settings, + _providerSettings, + _serviceProvider); + } + private sealed partial class PinToDockContextItem : CommandContextItem { private readonly TopLevelViewModel _topLevelViewModel; @@ -443,8 +455,8 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem TopLevelViewModel topLevelViewModel, DockViewModel dockViewModel, SettingsModel settings, - AppStateModel appStateModel) - : base(new PinToDockCommand(topLevelViewModel, dockViewModel, settings, appStateModel)) + TopLevelCommandManager topLevelCommandManager) + : base(new PinToDockCommand(topLevelViewModel, dockViewModel, settings, topLevelCommandManager)) { _topLevelViewModel = topLevelViewModel; _dockViewModel = dockViewModel; @@ -459,36 +471,58 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem private readonly TopLevelViewModel _topLevelViewModel; private readonly DockViewModel _dockViewModel; private readonly SettingsModel _settings; - private readonly AppStateModel _appStateModel; + private readonly TopLevelCommandManager _topLevelCommandManager; public PinToDockCommand( TopLevelViewModel topLevelViewModel, DockViewModel dockViewModel, SettingsModel settings, - AppStateModel appStateModel) + TopLevelCommandManager topLevelCommandManager) { _topLevelViewModel = topLevelViewModel; _dockViewModel = dockViewModel; _settings = settings; - _appStateModel = appStateModel; + _topLevelCommandManager = topLevelCommandManager; } public override CommandResult Invoke() { Logger.LogDebug($"PinToDockCommand.Invoke: {_topLevelViewModel.Id}"); + // TODO! Deal with "the command ID is already pinned in PinnedCommands but not in one of StartBands/EndBands" _settings.DockSettings.PinnedCommands.Add(_topLevelViewModel.Id); _settings.DockSettings.StartBands.Add(new DockBandSettings() { Id = _topLevelViewModel.Id, ShowLabels = true, }); - SettingsModel.SaveSettings(_settings); - // TODO! temporary: send a reload message, so that we have - // everything repopulated - WeakReferenceMessenger.Default.Send(new()); + // MIKE TODO! + // + // SaveSettings will raise a settings changed event, which the + // DockViewModel will listen to, and re-setup its bands. BUT, we + // haven't actually loaded the band into the TLCM's list of bands + // yet! So pinning something like clipboard history will briefly + // list all history items, _then_ reload to just the wrapped item. + // + // Instead, we need a single message for "add this band to the TLCM + // and settings". not sure the best way to do that right now. + // + // * The "top level" bands are created in + // CommandProviderWrapper.InitializeCommands. + // * That method takes in all the TLC's, fallbacks, and bands from + // the provider, creates TLVMs for them, and then also builds + // TLVM's for wrapped bands in the settings. + // * We'd need to add a method to TLCM to "add this band", which + // would clone the current TLVM into a new one... + // SettingsModel.SaveSettings(_settings); + // // TODO! temporary: send a reload message, so that we have + // // everything repopulated + // WeakReferenceMessenger.Default.Send(new()); + var bandVm = _topLevelViewModel.CloneAsBand(); + _topLevelCommandManager.PinDockBand(bandVm); + _dockViewModel.UpdateSettings(_settings.DockSettings); return CommandResult.GoHome(); } }