diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Settings/DockSettings.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Settings/DockSettings.cs index 1c36b16ff1..0311f2ffc1 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Settings/DockSettings.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Settings/DockSettings.cs @@ -45,6 +45,8 @@ public class DockSettings // /Theme settings public List PinnedCommands { get; set; } = []; + public List PinnedCommandItems { get; set; } = []; + public List StartBands { get; set; } = []; public List CenterBands { get; set; } = []; @@ -134,4 +136,6 @@ public enum DockBackdrop Acrylic, } +public record CommandItemIdentity(string ProviderId, string CommandId); + #pragma warning restore SA1402 // File may only contain a single type diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/TopLevelViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/TopLevelViewModel.cs index 6d995174aa..9cb499556a 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/TopLevelViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/TopLevelViewModel.cs @@ -1,4 +1,4 @@ -// 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. @@ -470,8 +470,9 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem, IEx contextItems.Add(new Separator()); var inStartBands = _settings.DockSettings.StartBands.Any(band => band.Id == this.Id); + var inCenterBands = _settings.DockSettings.CenterBands.Any(band => band.Id == this.Id); var inEndBands = _settings.DockSettings.EndBands.Any(band => band.Id == this.Id); - var alreadyPinned = (inStartBands || inEndBands) && + var alreadyPinned = (inStartBands || inCenterBands || inEndBands) && _settings.DockSettings.PinnedCommands.Contains(this.Id); var pinCommand = new PinToDockCommand( @@ -597,6 +598,7 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem, IEx { _settings.DockSettings.PinnedCommands.Remove(_topLevelViewModel.Id); _settings.DockSettings.StartBands.RemoveAll(band => band.Id == _topLevelViewModel.Id); + _settings.DockSettings.CenterBands.RemoveAll(band => band.Id == _topLevelViewModel.Id); _settings.DockSettings.EndBands.RemoveAll(band => band.Id == _topLevelViewModel.Id); _topLevelViewModel.Save(); diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/CommandPaletteContextMenuFactory.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/CommandPaletteContextMenuFactory.xaml.cs index e9ff24e1cb..fafe32da3c 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/CommandPaletteContextMenuFactory.xaml.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/CommandPaletteContextMenuFactory.xaml.cs @@ -14,10 +14,12 @@ namespace Microsoft.CmdPal.UI; internal sealed partial class CommandPaletteContextMenuFactory : IContextMenuFactory { private readonly SettingsModel _settingsModel; + private readonly TopLevelCommandManager _topLevelCommandManager; - public CommandPaletteContextMenuFactory(SettingsModel settingsModel) + public CommandPaletteContextMenuFactory(SettingsModel settingsModel, TopLevelCommandManager topLevelCommandManager) { _settingsModel = settingsModel; + _topLevelCommandManager = topLevelCommandManager; } public List UnsafeBuildAndInitMoreCommands( @@ -44,7 +46,14 @@ internal sealed partial class CommandPaletteContextMenuFactory : IContextMenuFac !commandItem.IsContextMenuItem) { results.Add(new SeparatorViewModel()); - var pinCommand = new PinToDockIten(commandItem); + + var inStartBands = _settingsModel.DockSettings.StartBands.Any(band => band.Id == this.Id); + var inCenterBands = _settingsModel.DockSettings.CenterBands.Any(band => band.Id == this.Id); + var inEndBands = _settingsModel.DockSettings.EndBands.Any(band => band.Id == this.Id); + var alreadyPinned = (inStartBands || inCenterBands || inEndBands) && + _settingsModel.DockSettings.PinnedCommands.Contains(this.Id); + + var pinCommand = new PinToDockItem(commandItem, !alreadyPinned, _topLevelCommandManager, _settingsModel); var pinCommandViewModel = new CommandContextItemViewModel(pinCommand, commandItem.PageContext); pinCommandViewModel.SlowInitializeProperties(); results.Add(pinCommandViewModel); @@ -54,17 +63,111 @@ internal sealed partial class CommandPaletteContextMenuFactory : IContextMenuFac return results; } - private sealed partial class PinToDockIten : CommandContextItem + private sealed partial class PinToDockItem : CommandContextItem { private static readonly PinToDockCommand PinCommand = new(); private readonly CommandItemViewModel _owner; + private readonly SettingsModel _settings; + private readonly TopLevelCommandManager _topLevelCommandManager; + private readonly bool _pin; internal CommandItemViewModel Owner => _owner; - public PinToDockIten(CommandItemViewModel owner) + // public override IconInfo Icon => _pin ? Icons.PinIcon : Icons.UnpinIcon; + + // public override string Title => _pin ? Properties.Resources.dock_pin_command_name : Properties.Resources.dock_unpin_command_name; + private string Id => _owner.Command.Id; + + public PinToDockItem( + CommandItemViewModel owner, + bool pin, + TopLevelCommandManager topLevelCommandManager, + SettingsModel settingsModel) : base(PinCommand) { + _pin = pin; _owner = owner; + _topLevelCommandManager = topLevelCommandManager; + _settings = settingsModel; + } + + public CommandResult Invoke() + { + CoreLogger.LogDebug($"PinToDockItem.Invoke({_pin}): {Id}"); + + if (_pin) + { + PinToDock(); + } + else + { + UnpinFromDock(); + } + + // // Notify that the MoreCommands have changed, so the context menu updates + // _topLevelViewModel.PropChanged?.Invoke( + // _topLevelViewModel, + // new PropChangedEventArgs(nameof(ICommandItem.MoreCommands))); + + // TODO! what's the least gross way to cause the + // CommandItemViewModel to re-fetch it's context menu? + return CommandResult.GoHome(); + } + + private void PinToDock() + { + // It's possible that the top-level command shares an ID with a + // band. In that case, we don't want to add it to PinnedCommands. + // PinnedCommands is just for top-level commands IDs that aren't + // otherwise bands. + // + // Check the top-level command ID against the bands first. + if (_topLevelCommandManager.DockBands.Any(band => band.Id == Id)) + { + } + else + { + // In this case, the ID isn't another band, so add it to + // PinnedCommands. + // + // TODO!! We need to include the extension ID in the pinned + // command somehow, so that we know where to look this command + // up later. + if (!_settings.DockSettings.PinnedCommands.Contains(Id)) + { + _settings.DockSettings.PinnedCommands.Add(Id); + } + } + + // TODO! Deal with "the command ID is already pinned in + // PinnedCommands but not in one of StartBands/EndBands". I think + // we're already avoiding adding it to PinnedCommands above, but I + // think that PinDockBand below will create a duplicate VM for it. + _settings.DockSettings.StartBands.Add(new DockBandSettings() + { + Id = Id, + ShowLabels = true, + }); + + // Create a new band VM from our current TLVM. This will allow us to + // update the bands in the CommandProviderWrapper and the TLCM, + // without forcing a whole reload + var bandVm = _topLevelViewModel.CloneAsBand(); + _topLevelCommandManager.PinDockBand(bandVm); + + // _topLevelViewModel.Save(); + SettingsModel.SaveSettings(_settings); + } + + private void UnpinFromDock() + { + _settings.DockSettings.PinnedCommands.Remove(Id); + _settings.DockSettings.StartBands.RemoveAll(band => band.Id == Id); + _settings.DockSettings.CenterBands.RemoveAll(band => band.Id == Id); + _settings.DockSettings.EndBands.RemoveAll(band => band.Id == Id); + + // _topLevelViewModel.Save(); + SettingsModel.SaveSettings(_settings); } } @@ -78,13 +181,10 @@ internal sealed partial class CommandPaletteContextMenuFactory : IContextMenuFac public override ICommandResult Invoke(object? sender) { - if (sender is PinToDockIten contextItemViewModel) + if (sender is PinToDockItem pinItem) { - // if (contextItemViewModel.PageContext.TryGetTarget(out var pageContext)) - // { - // pageContext.TogglePinnedToDock(contextItemViewModel); - // } - return CommandResult.ShowToast($"Attempted to toggle pin to dock for {contextItemViewModel.Owner.Title}"); + pinItem.Invoke(); + return CommandResult.ShowToast($"Attempted to toggle pin to dock for {pinItem.Owner.Title}"); } return CommandResult.ShowToast($"Failed to get sender for command");