mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-03 09:46:54 +02:00
634 lines
21 KiB
C#
634 lines
21 KiB
C#
|
|
// 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 CommunityToolkit.Mvvm.Messaging;
|
||
|
|
using ManagedCommon;
|
||
|
|
using Microsoft.CmdPal.UI.Messages;
|
||
|
|
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||
|
|
using Microsoft.CmdPal.UI.ViewModels.Settings;
|
||
|
|
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||
|
|
|
||
|
|
namespace Microsoft.CmdPal.UI.ViewModels.Dock;
|
||
|
|
|
||
|
|
public sealed partial class DockViewModel
|
||
|
|
{
|
||
|
|
private readonly TopLevelCommandManager _topLevelCommandManager;
|
||
|
|
private readonly SettingsModel _settingsModel;
|
||
|
|
private readonly DockPageContext _pageContext; // only to be used for our own context menu - not for dock bands themselves
|
||
|
|
private readonly IContextMenuFactory _contextMenuFactory;
|
||
|
|
|
||
|
|
private DockSettings _settings;
|
||
|
|
|
||
|
|
public TaskScheduler Scheduler { get; }
|
||
|
|
|
||
|
|
public ObservableCollection<DockBandViewModel> StartItems { get; } = new();
|
||
|
|
|
||
|
|
public ObservableCollection<DockBandViewModel> CenterItems { get; } = new();
|
||
|
|
|
||
|
|
public ObservableCollection<DockBandViewModel> EndItems { get; } = new();
|
||
|
|
|
||
|
|
public ObservableCollection<TopLevelViewModel> AllItems => _topLevelCommandManager.DockBands;
|
||
|
|
|
||
|
|
public DockViewModel(
|
||
|
|
TopLevelCommandManager tlcManager,
|
||
|
|
IContextMenuFactory contextMenuFactory,
|
||
|
|
SettingsModel settings,
|
||
|
|
TaskScheduler scheduler)
|
||
|
|
{
|
||
|
|
_topLevelCommandManager = tlcManager;
|
||
|
|
_contextMenuFactory = contextMenuFactory;
|
||
|
|
_settingsModel = settings;
|
||
|
|
_settings = settings.DockSettings;
|
||
|
|
Scheduler = scheduler;
|
||
|
|
_pageContext = new(this);
|
||
|
|
|
||
|
|
_topLevelCommandManager.DockBands.CollectionChanged += DockBands_CollectionChanged;
|
||
|
|
}
|
||
|
|
|
||
|
|
private void DockBands_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
||
|
|
{
|
||
|
|
Logger.LogDebug("Starting DockBands_CollectionChanged");
|
||
|
|
SetupBands();
|
||
|
|
Logger.LogDebug("Ended DockBands_CollectionChanged");
|
||
|
|
}
|
||
|
|
|
||
|
|
public void UpdateSettings(DockSettings settings)
|
||
|
|
{
|
||
|
|
Logger.LogDebug($"DockViewModel.UpdateSettings");
|
||
|
|
_settings = settings;
|
||
|
|
SetupBands();
|
||
|
|
}
|
||
|
|
|
||
|
|
private void SetupBands()
|
||
|
|
{
|
||
|
|
Logger.LogDebug($"Setting up dock bands");
|
||
|
|
SetupBands(_settings.StartBands, StartItems);
|
||
|
|
SetupBands(_settings.CenterBands, CenterItems);
|
||
|
|
SetupBands(_settings.EndBands, EndItems);
|
||
|
|
}
|
||
|
|
|
||
|
|
private void SetupBands(
|
||
|
|
List<DockBandSettings> bands,
|
||
|
|
ObservableCollection<DockBandViewModel> target)
|
||
|
|
{
|
||
|
|
List<DockBandViewModel> newBands = new();
|
||
|
|
foreach (var band in bands)
|
||
|
|
{
|
||
|
|
var commandId = band.CommandId;
|
||
|
|
var topLevelCommand = _topLevelCommandManager.LookupDockBand(commandId);
|
||
|
|
|
||
|
|
if (topLevelCommand is null)
|
||
|
|
{
|
||
|
|
Logger.LogWarning($"Failed to find band {commandId}");
|
||
|
|
}
|
||
|
|
|
||
|
|
if (topLevelCommand is not null)
|
||
|
|
{
|
||
|
|
// note: CreateBandItem doesn't actually initialize the band, it
|
||
|
|
// just creates the VM. Callers need to make sure to call
|
||
|
|
// InitializeProperties() on a BG thread elsewhere
|
||
|
|
var bandVm = CreateBandItem(band, topLevelCommand.ItemViewModel);
|
||
|
|
newBands.Add(bandVm);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
var beforeCount = target.Count;
|
||
|
|
var afterCount = newBands.Count;
|
||
|
|
|
||
|
|
DoOnUiThread(() =>
|
||
|
|
{
|
||
|
|
List<DockBandViewModel> removed = new();
|
||
|
|
ListHelpers.InPlaceUpdateList(target, newBands, out removed);
|
||
|
|
var isStartBand = target == StartItems;
|
||
|
|
var label = isStartBand ? "Start bands:" : "End bands:";
|
||
|
|
Logger.LogDebug($"{label} ({beforeCount}) -> ({afterCount}), Removed {removed?.Count ?? 0} items");
|
||
|
|
|
||
|
|
// then, back to a BG thread:
|
||
|
|
Task.Run(() =>
|
||
|
|
{
|
||
|
|
if (removed is not null)
|
||
|
|
{
|
||
|
|
foreach (var removedItem in removed)
|
||
|
|
{
|
||
|
|
removedItem.SafeCleanup();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
// Initialize properties on BG thread
|
||
|
|
Task.Run(() =>
|
||
|
|
{
|
||
|
|
foreach (var band in newBands)
|
||
|
|
{
|
||
|
|
band.SafeInitializePropertiesSynchronous();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Instantiate a new band view model for this CommandItem, given the
|
||
|
|
/// settings. The DockBandViewModel will _not_ be initialized - callers
|
||
|
|
/// will need to make sure to initialize it somewhere else (off the UI
|
||
|
|
/// thread)
|
||
|
|
/// </summary>
|
||
|
|
private DockBandViewModel CreateBandItem(
|
||
|
|
DockBandSettings bandSettings,
|
||
|
|
CommandItemViewModel commandItem)
|
||
|
|
{
|
||
|
|
DockBandViewModel band = new(commandItem, commandItem.PageContext, bandSettings, _settings, SaveSettings, _contextMenuFactory);
|
||
|
|
|
||
|
|
// the band is NOT initialized here!
|
||
|
|
return band;
|
||
|
|
}
|
||
|
|
|
||
|
|
private void SaveSettings()
|
||
|
|
{
|
||
|
|
SettingsModel.SaveSettings(_settingsModel);
|
||
|
|
}
|
||
|
|
|
||
|
|
public DockBandViewModel? FindBandByTopLevel(TopLevelViewModel tlc)
|
||
|
|
{
|
||
|
|
var id = tlc.Id;
|
||
|
|
return FindBandById(id);
|
||
|
|
}
|
||
|
|
|
||
|
|
public DockBandViewModel? FindBandById(string id)
|
||
|
|
{
|
||
|
|
foreach (var band in StartItems)
|
||
|
|
{
|
||
|
|
if (band.Id == id)
|
||
|
|
{
|
||
|
|
return band;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
foreach (var band in CenterItems)
|
||
|
|
{
|
||
|
|
if (band.Id == id)
|
||
|
|
{
|
||
|
|
return band;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
foreach (var band in EndItems)
|
||
|
|
{
|
||
|
|
if (band.Id == id)
|
||
|
|
{
|
||
|
|
return band;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Syncs the band position in settings after a same-list reorder.
|
||
|
|
/// Does not save to disk - call SaveBandOrder() when done editing.
|
||
|
|
/// </summary>
|
||
|
|
public void SyncBandPosition(DockBandViewModel band, DockPinSide targetSide, int targetIndex)
|
||
|
|
{
|
||
|
|
var bandId = band.Id;
|
||
|
|
var dockSettings = _settingsModel.DockSettings;
|
||
|
|
|
||
|
|
var bandSettings = dockSettings.StartBands.FirstOrDefault(b => b.CommandId == bandId)
|
||
|
|
?? dockSettings.CenterBands.FirstOrDefault(b => b.CommandId == bandId)
|
||
|
|
?? dockSettings.EndBands.FirstOrDefault(b => b.CommandId == bandId);
|
||
|
|
|
||
|
|
if (bandSettings == null)
|
||
|
|
{
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Remove from all settings lists
|
||
|
|
dockSettings.StartBands.RemoveAll(b => b.CommandId == bandId);
|
||
|
|
dockSettings.CenterBands.RemoveAll(b => b.CommandId == bandId);
|
||
|
|
dockSettings.EndBands.RemoveAll(b => b.CommandId == bandId);
|
||
|
|
|
||
|
|
// Add to target settings list at the correct index
|
||
|
|
var targetSettings = targetSide switch
|
||
|
|
{
|
||
|
|
DockPinSide.Start => dockSettings.StartBands,
|
||
|
|
DockPinSide.Center => dockSettings.CenterBands,
|
||
|
|
DockPinSide.End => dockSettings.EndBands,
|
||
|
|
_ => dockSettings.StartBands,
|
||
|
|
};
|
||
|
|
var insertIndex = Math.Min(targetIndex, targetSettings.Count);
|
||
|
|
targetSettings.Insert(insertIndex, bandSettings);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Moves a dock band to a new position (cross-list drop).
|
||
|
|
/// Does not save to disk - call SaveBandOrder() when done editing.
|
||
|
|
/// </summary>
|
||
|
|
public void MoveBandWithoutSaving(DockBandViewModel band, DockPinSide targetSide, int targetIndex)
|
||
|
|
{
|
||
|
|
var bandId = band.Id;
|
||
|
|
var dockSettings = _settingsModel.DockSettings;
|
||
|
|
|
||
|
|
var bandSettings = dockSettings.StartBands.FirstOrDefault(b => b.CommandId == bandId)
|
||
|
|
?? dockSettings.CenterBands.FirstOrDefault(b => b.CommandId == bandId)
|
||
|
|
?? dockSettings.EndBands.FirstOrDefault(b => b.CommandId == bandId);
|
||
|
|
|
||
|
|
if (bandSettings == null)
|
||
|
|
{
|
||
|
|
Logger.LogWarning($"Could not find band settings for band {bandId}");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Remove from all sides (settings and UI)
|
||
|
|
dockSettings.StartBands.RemoveAll(b => b.CommandId == bandId);
|
||
|
|
dockSettings.CenterBands.RemoveAll(b => b.CommandId == bandId);
|
||
|
|
dockSettings.EndBands.RemoveAll(b => b.CommandId == bandId);
|
||
|
|
StartItems.Remove(band);
|
||
|
|
CenterItems.Remove(band);
|
||
|
|
EndItems.Remove(band);
|
||
|
|
|
||
|
|
// Add to the target side at the specified index
|
||
|
|
switch (targetSide)
|
||
|
|
{
|
||
|
|
case DockPinSide.Start:
|
||
|
|
{
|
||
|
|
var settingsIndex = Math.Min(targetIndex, dockSettings.StartBands.Count);
|
||
|
|
dockSettings.StartBands.Insert(settingsIndex, bandSettings);
|
||
|
|
|
||
|
|
var uiIndex = Math.Min(targetIndex, StartItems.Count);
|
||
|
|
StartItems.Insert(uiIndex, band);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
case DockPinSide.Center:
|
||
|
|
{
|
||
|
|
var settingsIndex = Math.Min(targetIndex, dockSettings.CenterBands.Count);
|
||
|
|
dockSettings.CenterBands.Insert(settingsIndex, bandSettings);
|
||
|
|
|
||
|
|
var uiIndex = Math.Min(targetIndex, CenterItems.Count);
|
||
|
|
CenterItems.Insert(uiIndex, band);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
case DockPinSide.End:
|
||
|
|
{
|
||
|
|
var settingsIndex = Math.Min(targetIndex, dockSettings.EndBands.Count);
|
||
|
|
dockSettings.EndBands.Insert(settingsIndex, bandSettings);
|
||
|
|
|
||
|
|
var uiIndex = Math.Min(targetIndex, EndItems.Count);
|
||
|
|
EndItems.Insert(uiIndex, band);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Logger.LogDebug($"Moved band {bandId} to {targetSide} at index {targetIndex} (not saved yet)");
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Saves the current band order and label settings to settings.
|
||
|
|
/// Call this when exiting edit mode.
|
||
|
|
/// </summary>
|
||
|
|
public void SaveBandOrder()
|
||
|
|
{
|
||
|
|
// Save ShowLabels for all bands
|
||
|
|
foreach (var band in StartItems.Concat(CenterItems).Concat(EndItems))
|
||
|
|
{
|
||
|
|
band.SaveShowLabels();
|
||
|
|
}
|
||
|
|
|
||
|
|
_snapshotStartBands = null;
|
||
|
|
_snapshotCenterBands = null;
|
||
|
|
_snapshotEndBands = null;
|
||
|
|
_snapshotBandViewModels = null;
|
||
|
|
SettingsModel.SaveSettings(_settingsModel);
|
||
|
|
Logger.LogDebug("Saved band order to settings");
|
||
|
|
}
|
||
|
|
|
||
|
|
private List<DockBandSettings>? _snapshotStartBands;
|
||
|
|
private List<DockBandSettings>? _snapshotCenterBands;
|
||
|
|
private List<DockBandSettings>? _snapshotEndBands;
|
||
|
|
private Dictionary<string, DockBandViewModel>? _snapshotBandViewModels;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Takes a snapshot of the current band order and label settings before editing.
|
||
|
|
/// Call this when entering edit mode.
|
||
|
|
/// </summary>
|
||
|
|
public void SnapshotBandOrder()
|
||
|
|
{
|
||
|
|
var dockSettings = _settingsModel.DockSettings;
|
||
|
|
_snapshotStartBands = dockSettings.StartBands.Select(b => b.Clone()).ToList();
|
||
|
|
_snapshotCenterBands = dockSettings.CenterBands.Select(b => b.Clone()).ToList();
|
||
|
|
_snapshotEndBands = dockSettings.EndBands.Select(b => b.Clone()).ToList();
|
||
|
|
|
||
|
|
// Snapshot band ViewModels so we can restore unpinned bands
|
||
|
|
// Use a dictionary but handle potential duplicates gracefully
|
||
|
|
_snapshotBandViewModels = new Dictionary<string, DockBandViewModel>();
|
||
|
|
foreach (var band in StartItems.Concat(CenterItems).Concat(EndItems))
|
||
|
|
{
|
||
|
|
_snapshotBandViewModels.TryAdd(band.Id, band);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Snapshot ShowLabels for all bands
|
||
|
|
foreach (var band in _snapshotBandViewModels.Values)
|
||
|
|
{
|
||
|
|
band.SnapshotShowLabels();
|
||
|
|
}
|
||
|
|
|
||
|
|
Logger.LogDebug($"Snapshot taken: {_snapshotStartBands.Count} start bands, {_snapshotCenterBands.Count} center bands, {_snapshotEndBands.Count} end bands");
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Restores the band order and label settings from the snapshot taken when entering edit mode.
|
||
|
|
/// Call this when discarding edit mode changes.
|
||
|
|
/// </summary>
|
||
|
|
public void RestoreBandOrder()
|
||
|
|
{
|
||
|
|
if (_snapshotStartBands == null ||
|
||
|
|
_snapshotCenterBands == null ||
|
||
|
|
_snapshotEndBands == null || _snapshotBandViewModels == null)
|
||
|
|
{
|
||
|
|
Logger.LogWarning("No snapshot to restore from");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Restore ShowLabels for all snapshotted bands
|
||
|
|
foreach (var band in _snapshotBandViewModels.Values)
|
||
|
|
{
|
||
|
|
band.RestoreShowLabels();
|
||
|
|
}
|
||
|
|
|
||
|
|
var dockSettings = _settingsModel.DockSettings;
|
||
|
|
|
||
|
|
// Restore settings from snapshot
|
||
|
|
dockSettings.StartBands.Clear();
|
||
|
|
dockSettings.CenterBands.Clear();
|
||
|
|
dockSettings.EndBands.Clear();
|
||
|
|
|
||
|
|
foreach (var bandSnapshot in _snapshotStartBands)
|
||
|
|
{
|
||
|
|
var bandSettings = bandSnapshot.Clone();
|
||
|
|
dockSettings.StartBands.Add(bandSettings);
|
||
|
|
}
|
||
|
|
|
||
|
|
foreach (var bandSnapshot in _snapshotCenterBands)
|
||
|
|
{
|
||
|
|
var bandSettings = bandSnapshot.Clone();
|
||
|
|
dockSettings.CenterBands.Add(bandSettings);
|
||
|
|
}
|
||
|
|
|
||
|
|
foreach (var bandSnapshot in _snapshotEndBands)
|
||
|
|
{
|
||
|
|
var bandSettings = bandSnapshot.Clone();
|
||
|
|
dockSettings.EndBands.Add(bandSettings);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Rebuild UI collections from restored settings using the snapshotted ViewModels
|
||
|
|
RebuildUICollectionsFromSnapshot();
|
||
|
|
|
||
|
|
_snapshotStartBands = null;
|
||
|
|
_snapshotCenterBands = null;
|
||
|
|
_snapshotEndBands = null;
|
||
|
|
_snapshotBandViewModels = null;
|
||
|
|
Logger.LogDebug("Restored band order from snapshot");
|
||
|
|
}
|
||
|
|
|
||
|
|
private void RebuildUICollectionsFromSnapshot()
|
||
|
|
{
|
||
|
|
if (_snapshotBandViewModels == null)
|
||
|
|
{
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
var dockSettings = _settingsModel.DockSettings;
|
||
|
|
|
||
|
|
StartItems.Clear();
|
||
|
|
CenterItems.Clear();
|
||
|
|
EndItems.Clear();
|
||
|
|
|
||
|
|
foreach (var bandSettings in dockSettings.StartBands)
|
||
|
|
{
|
||
|
|
if (_snapshotBandViewModels.TryGetValue(bandSettings.CommandId, out var bandVM))
|
||
|
|
{
|
||
|
|
StartItems.Add(bandVM);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
foreach (var bandSettings in dockSettings.CenterBands)
|
||
|
|
{
|
||
|
|
if (_snapshotBandViewModels.TryGetValue(bandSettings.CommandId, out var bandVM))
|
||
|
|
{
|
||
|
|
CenterItems.Add(bandVM);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
foreach (var bandSettings in dockSettings.EndBands)
|
||
|
|
{
|
||
|
|
if (_snapshotBandViewModels.TryGetValue(bandSettings.CommandId, out var bandVM))
|
||
|
|
{
|
||
|
|
EndItems.Add(bandVM);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private void RebuildUICollections()
|
||
|
|
{
|
||
|
|
var dockSettings = _settingsModel.DockSettings;
|
||
|
|
|
||
|
|
// Create a lookup of all current band ViewModels
|
||
|
|
var allBands = StartItems.Concat(CenterItems).Concat(EndItems).ToDictionary(b => b.Id);
|
||
|
|
|
||
|
|
StartItems.Clear();
|
||
|
|
CenterItems.Clear();
|
||
|
|
EndItems.Clear();
|
||
|
|
|
||
|
|
foreach (var bandSettings in dockSettings.StartBands)
|
||
|
|
{
|
||
|
|
if (allBands.TryGetValue(bandSettings.CommandId, out var bandVM))
|
||
|
|
{
|
||
|
|
StartItems.Add(bandVM);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
foreach (var bandSettings in dockSettings.CenterBands)
|
||
|
|
{
|
||
|
|
if (allBands.TryGetValue(bandSettings.CommandId, out var bandVM))
|
||
|
|
{
|
||
|
|
CenterItems.Add(bandVM);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
foreach (var bandSettings in dockSettings.EndBands)
|
||
|
|
{
|
||
|
|
if (allBands.TryGetValue(bandSettings.CommandId, out var bandVM))
|
||
|
|
{
|
||
|
|
EndItems.Add(bandVM);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Gets the list of dock bands that are not currently pinned to any section.
|
||
|
|
/// </summary>
|
||
|
|
public IEnumerable<TopLevelViewModel> GetAvailableBandsToAdd()
|
||
|
|
{
|
||
|
|
// Get IDs of all bands currently in the dock
|
||
|
|
var pinnedBandIds = new HashSet<string>();
|
||
|
|
foreach (var band in StartItems)
|
||
|
|
{
|
||
|
|
pinnedBandIds.Add(band.Id);
|
||
|
|
}
|
||
|
|
|
||
|
|
foreach (var band in CenterItems)
|
||
|
|
{
|
||
|
|
pinnedBandIds.Add(band.Id);
|
||
|
|
}
|
||
|
|
|
||
|
|
foreach (var band in EndItems)
|
||
|
|
{
|
||
|
|
pinnedBandIds.Add(band.Id);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Return all dock bands that are not already pinned
|
||
|
|
return AllItems.Where(tlc => !pinnedBandIds.Contains(tlc.Id));
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Adds a band to the specified dock section.
|
||
|
|
/// Does not save to disk - call SaveBandOrder() when done editing.
|
||
|
|
/// </summary>
|
||
|
|
public void AddBandToSection(TopLevelViewModel topLevel, DockPinSide targetSide)
|
||
|
|
{
|
||
|
|
var bandId = topLevel.Id;
|
||
|
|
|
||
|
|
// Check if already in the dock
|
||
|
|
if (FindBandById(bandId) != null)
|
||
|
|
{
|
||
|
|
Logger.LogWarning($"Band {bandId} is already in the dock");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Create settings for the new band
|
||
|
|
var bandSettings = new DockBandSettings { ProviderId = topLevel.CommandProviderId, CommandId = bandId, ShowLabels = null };
|
||
|
|
var dockSettings = _settingsModel.DockSettings;
|
||
|
|
|
||
|
|
// Create the band view model
|
||
|
|
var bandVm = CreateBandItem(bandSettings, topLevel.ItemViewModel);
|
||
|
|
|
||
|
|
// Add to the appropriate section
|
||
|
|
switch (targetSide)
|
||
|
|
{
|
||
|
|
case DockPinSide.Start:
|
||
|
|
dockSettings.StartBands.Add(bandSettings);
|
||
|
|
StartItems.Add(bandVm);
|
||
|
|
break;
|
||
|
|
case DockPinSide.Center:
|
||
|
|
dockSettings.CenterBands.Add(bandSettings);
|
||
|
|
CenterItems.Add(bandVm);
|
||
|
|
break;
|
||
|
|
case DockPinSide.End:
|
||
|
|
dockSettings.EndBands.Add(bandSettings);
|
||
|
|
EndItems.Add(bandVm);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Snapshot the new band so it can be removed on discard
|
||
|
|
bandVm.SnapshotShowLabels();
|
||
|
|
|
||
|
|
Task.Run(() =>
|
||
|
|
{
|
||
|
|
bandVm.SafeInitializePropertiesSynchronous();
|
||
|
|
});
|
||
|
|
|
||
|
|
Logger.LogDebug($"Added band {bandId} to {targetSide} (not saved yet)");
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Unpins a band from the dock, removing it from whichever section it's in.
|
||
|
|
/// Does not save to disk - call SaveBandOrder() when done editing.
|
||
|
|
/// </summary>
|
||
|
|
public void UnpinBand(DockBandViewModel band)
|
||
|
|
{
|
||
|
|
var bandId = band.Id;
|
||
|
|
var dockSettings = _settingsModel.DockSettings;
|
||
|
|
|
||
|
|
// Remove from settings
|
||
|
|
dockSettings.StartBands.RemoveAll(b => b.CommandId == bandId);
|
||
|
|
dockSettings.CenterBands.RemoveAll(b => b.CommandId == bandId);
|
||
|
|
dockSettings.EndBands.RemoveAll(b => b.CommandId == bandId);
|
||
|
|
|
||
|
|
// Remove from UI collections
|
||
|
|
StartItems.Remove(band);
|
||
|
|
CenterItems.Remove(band);
|
||
|
|
EndItems.Remove(band);
|
||
|
|
|
||
|
|
Logger.LogDebug($"Unpinned band {bandId} (not saved yet)");
|
||
|
|
}
|
||
|
|
|
||
|
|
private void DoOnUiThread(Action action)
|
||
|
|
{
|
||
|
|
Task.Factory.StartNew(
|
||
|
|
action,
|
||
|
|
CancellationToken.None,
|
||
|
|
TaskCreationOptions.None,
|
||
|
|
Scheduler);
|
||
|
|
}
|
||
|
|
|
||
|
|
public CommandItemViewModel GetContextMenuForDock()
|
||
|
|
{
|
||
|
|
var model = new DockContextMenuItem();
|
||
|
|
var vm = new CommandItemViewModel(new(model), new(_pageContext), contextMenuFactory: null);
|
||
|
|
vm.SlowInitializeProperties();
|
||
|
|
return vm;
|
||
|
|
}
|
||
|
|
|
||
|
|
private sealed partial class DockContextMenuItem : CommandItem
|
||
|
|
{
|
||
|
|
public DockContextMenuItem()
|
||
|
|
{
|
||
|
|
var editDockCommand = new AnonymousCommand(
|
||
|
|
action: () =>
|
||
|
|
{
|
||
|
|
WeakReferenceMessenger.Default.Send(new EnterDockEditModeMessage());
|
||
|
|
})
|
||
|
|
{
|
||
|
|
Name = Properties.Resources.dock_edit_dock_name,
|
||
|
|
Icon = Icons.EditIcon,
|
||
|
|
};
|
||
|
|
|
||
|
|
var openSettingsCommand = new AnonymousCommand(
|
||
|
|
action: () =>
|
||
|
|
{
|
||
|
|
WeakReferenceMessenger.Default.Send(new OpenSettingsMessage("Dock"));
|
||
|
|
})
|
||
|
|
{
|
||
|
|
Name = Properties.Resources.dock_settings_name,
|
||
|
|
Icon = Icons.SettingsIcon,
|
||
|
|
};
|
||
|
|
|
||
|
|
MoreCommands = new CommandContextItem[]
|
||
|
|
{
|
||
|
|
new CommandContextItem(editDockCommand),
|
||
|
|
new CommandContextItem(openSettingsCommand),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Provides an empty page context, for the dock's own context menu. We're
|
||
|
|
/// building the context menu for the dock using literally our own cmdpal
|
||
|
|
/// types, but that means we need a page context for the VM we will
|
||
|
|
/// generate.
|
||
|
|
/// </summary>
|
||
|
|
private sealed partial class DockPageContext(DockViewModel dockViewModel) : IPageContext
|
||
|
|
{
|
||
|
|
public TaskScheduler Scheduler => dockViewModel.Scheduler;
|
||
|
|
|
||
|
|
public ICommandProviderContext ProviderContext => CommandProviderContext.Empty;
|
||
|
|
|
||
|
|
public void ShowException(Exception ex, string? extensionHint)
|
||
|
|
{
|
||
|
|
var extensionText = extensionHint ?? "<unknown>";
|
||
|
|
Logger.LogError($"Error in dock context {extensionText}", ex);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|