From 2293b76f7b9c60443673480b801589b50b7cc4a9 Mon Sep 17 00:00:00 2001 From: Niels Laute Date: Mon, 19 Jan 2026 11:26:51 +0100 Subject: [PATCH 1/9] Dragging works --- .../Dock/DockViewModel.cs | 66 ++++++ .../Microsoft.CmdPal.UI/Dock/DockControl.xaml | 85 ++++++-- .../Dock/DockControl.xaml.cs | 198 ++++++++++++++++-- 3 files changed, 307 insertions(+), 42 deletions(-) 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 45f969fd57..222b664a64 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Dock/DockViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Dock/DockViewModel.cs @@ -19,6 +19,7 @@ public sealed partial class DockViewModel : IDisposable, IPageContext { private readonly TopLevelCommandManager _topLevelCommandManager; + private readonly SettingsModel _settingsModel; private DockSettings _settings; @@ -38,6 +39,7 @@ public sealed partial class DockViewModel : IDisposable, TaskScheduler scheduler) { _topLevelCommandManager = tlcManager; + _settingsModel = settings; _settings = settings.DockSettings; Scheduler = scheduler; WeakReferenceMessenger.Default.Register(this); @@ -137,6 +139,70 @@ public sealed partial class DockViewModel : IDisposable, return null; } + /// + /// Moves a dock band to a new position, either within the same side or to the other side. + /// This method updates both the UI collections and persists the change to settings. + /// + /// The band to move. + /// The target side (Start or End) for the band. + /// The index within the target side to insert the band. + public void MoveBand(DockBandViewModel band, DockPinSide targetSide, int targetIndex) + { + var bandId = band.Id; + var dockSettings = _settingsModel.DockSettings; + + // Find and remove the band settings from both lists + var bandSettings = dockSettings.StartBands.FirstOrDefault(b => b.Id == bandId) + ?? dockSettings.EndBands.FirstOrDefault(b => b.Id == bandId); + + if (bandSettings == null) + { + Logger.LogWarning($"Could not find band settings for band {bandId}"); + return; + } + + // Remove from both sides + dockSettings.StartBands.RemoveAll(b => b.Id == bandId); + dockSettings.EndBands.RemoveAll(b => b.Id == bandId); + + // Also update the UI collections + StartItems.Remove(band); + EndItems.Remove(band); + + // Add to the target side at the specified index + switch (targetSide) + { + case DockPinSide.Start: + { + var insertIndex = Math.Min(targetIndex, dockSettings.StartBands.Count); + dockSettings.StartBands.Insert(insertIndex, bandSettings); + + var uiInsertIndex = Math.Min(targetIndex, StartItems.Count); + StartItems.Insert(uiInsertIndex, band); + break; + } + + case DockPinSide.End: + { + var insertIndex = Math.Min(targetIndex, dockSettings.EndBands.Count); + dockSettings.EndBands.Insert(insertIndex, bandSettings); + + var uiInsertIndex = Math.Min(targetIndex, EndItems.Count); + EndItems.Insert(uiInsertIndex, band); + break; + } + + case DockPinSide.None: + default: + // Band is being unpinned - no UI update needed as it won't be visible + break; + } + + // Persist the change + SettingsModel.SaveSettings(_settingsModel); + Logger.LogDebug($"Moved band {bandId} to {targetSide} at index {targetIndex}"); + } + public void ShowException(Exception ex, string? extensionHint = null) { var extensionText = extensionHint ?? ""; diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockControl.xaml b/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockControl.xaml index fce5ea5fd5..f30e23e86d 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockControl.xaml +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockControl.xaml @@ -38,12 +38,15 @@ Control="{x:Bind}" /> - + @@ -114,6 +118,25 @@ + + + + + diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockItemControl.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockItemControl.xaml.cs new file mode 100644 index 0000000000..103f03dc2e --- /dev/null +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockItemControl.xaml.cs @@ -0,0 +1,120 @@ +// 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.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Markup; + +namespace Microsoft.CmdPal.UI.Dock; + +[ContentProperty(Name = nameof(Icon))] +public sealed partial class DockItemControl : Control +{ + public DockItemControl() + { + DefaultStyleKey = typeof(DockItemControl); + } + + public static readonly DependencyProperty ToolTipProperty = + DependencyProperty.Register(nameof(ToolTip), typeof(string), typeof(DockItemControl), new PropertyMetadata(null)); + + public string ToolTip + { + get => (string)GetValue(ToolTipProperty); + set => SetValue(ToolTipProperty, value); + } + + public static readonly DependencyProperty TitleProperty = + DependencyProperty.Register(nameof(Title), typeof(string), typeof(DockItemControl), new PropertyMetadata(null)); + + public string Title + { + get => (string)GetValue(TitleProperty); + set => SetValue(TitleProperty, value); + } + + public static readonly DependencyProperty HasTextProperty = + DependencyProperty.Register(nameof(HasText), typeof(bool), typeof(DockItemControl), new PropertyMetadata(true)); + + public bool HasText + { + get => (bool)GetValue(HasTextProperty); + set => SetValue(HasTextProperty, value); + } + + public static readonly DependencyProperty SubtitleProperty = + DependencyProperty.Register(nameof(Subtitle), typeof(string), typeof(DockItemControl), new PropertyMetadata(null)); + + public string Subtitle + { + get => (string)GetValue(SubtitleProperty); + set => SetValue(SubtitleProperty, value); + } + + public static readonly DependencyProperty HasSubtitleProperty = + DependencyProperty.Register(nameof(HasSubtitle), typeof(bool), typeof(DockItemControl), new PropertyMetadata(false)); + + public bool HasSubtitle + { + get => (bool)GetValue(HasSubtitleProperty); + set => SetValue(HasSubtitleProperty, value); + } + + public static readonly DependencyProperty IconProperty = + DependencyProperty.Register(nameof(Icon), typeof(object), typeof(DockItemControl), new PropertyMetadata(null)); + + public object Icon + { + get => GetValue(IconProperty); + set => SetValue(IconProperty, value); + } + + public static readonly DependencyProperty HasIconProperty = + DependencyProperty.Register(nameof(HasIcon), typeof(bool), typeof(DockItemControl), new PropertyMetadata(true)); + + public bool HasIcon + { + get => (bool)GetValue(HasIconProperty); + set => SetValue(HasIconProperty, value); + } + + public event TappedEventHandler? ItemTapped; + + public event RightTappedEventHandler? ItemRightTapped; + + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + // Wire up pointer events from the root border + if (GetTemplateChild("PART_RootGrid") is Border rootBorder) + { + rootBorder.Tapped += RootBorder_Tapped; + rootBorder.RightTapped += RootBorder_RightTapped; + rootBorder.PointerEntered += RootBorder_PointerEntered; + rootBorder.PointerExited += RootBorder_PointerExited; + } + } + + private void RootBorder_Tapped(object sender, TappedRoutedEventArgs e) + { + ItemTapped?.Invoke(this, e); + } + + private void RootBorder_RightTapped(object sender, RightTappedRoutedEventArgs e) + { + ItemRightTapped?.Invoke(this, e); + } + + private void RootBorder_PointerEntered(object sender, PointerRoutedEventArgs e) + { + VisualStateManager.GoToState(this, "PointerOver", true); + } + + private void RootBorder_PointerExited(object sender, PointerRoutedEventArgs e) + { + VisualStateManager.GoToState(this, "Normal", true); + } +} diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Microsoft.CmdPal.UI.csproj b/src/modules/cmdpal/Microsoft.CmdPal.UI/Microsoft.CmdPal.UI.csproj index 99609e8dc1..2dc5f6e03b 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Microsoft.CmdPal.UI.csproj +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Microsoft.CmdPal.UI.csproj @@ -79,6 +79,7 @@ + @@ -220,6 +221,12 @@ + + + MSBuild:Compile + + + MSBuild:Compile From 17aa472af568ee26e4f91e6fd31371d235b5561a Mon Sep 17 00:00:00 2001 From: Niels Laute Date: Wed, 21 Jan 2026 17:16:42 +0100 Subject: [PATCH 5/9] Moving the content to its own control --- .../Microsoft.CmdPal.UI/Dock/DockControl.xaml | 145 ++++++------------ .../Dock/DockControl.xaml.cs | 86 +++++------ .../Dock/DockItemControl.xaml | 87 ++++++++--- .../Dock/DockItemControl.xaml.cs | 127 +++++++++------ 4 files changed, 236 insertions(+), 209 deletions(-) diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockControl.xaml b/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockControl.xaml index 225864f5cd..dc7706a756 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockControl.xaml +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockControl.xaml @@ -16,10 +16,6 @@ - - - - - + - - - - - - - - - + + + + - - - - - - - + + + @@ -169,7 +119,7 @@ @@ -199,10 +149,15 @@ Drop="StartItemsListView_Drop" ItemContainerStyle="{StaticResource DockBandListViewItemStyle}" ItemTemplate="{StaticResource DockBandTemplate}" - ItemsPanel="{StaticResource ItemsOrientation}" ItemsSource="{x:Bind ViewModel.StartItems, Mode=OneWay}" SelectionMode="None" - Style="{StaticResource DockBandListViewStyle}" /> + Style="{StaticResource DockBandListViewStyle}"> + + + + + + @@ -219,35 +174,39 @@ Drop="EndItemsListView_Drop" ItemContainerStyle="{StaticResource DockBandListViewItemStyle}" ItemTemplate="{StaticResource DockBandTemplate}" - ItemsPanel="{StaticResource ItemsOrientation}" ItemsSource="{x:Bind ViewModel.EndItems, Mode=OneWay}" SelectionMode="None" - Style="{StaticResource DockBandListViewStyle}" /> + Style="{StaticResource DockBandListViewStyle}"> + + + + + + + + + - - - -