From 7e1933853e5aae4d2fd9eb663198d11f207d4780 Mon Sep 17 00:00:00 2001 From: Niels Laute Date: Thu, 4 Dec 2025 14:43:54 +0100 Subject: [PATCH] Adding a UC --- .../Controls/DockEditor.xaml | 275 +++++++++--- .../Controls/DockEditor.xaml.cs | 397 +++++++++++++++++- .../Microsoft.CmdPal.UI.csproj | 7 + .../Settings/DockSettingsPage.xaml | 57 +-- 4 files changed, 633 insertions(+), 103 deletions(-) diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/DockEditor.xaml b/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/DockEditor.xaml index 96305eaf50..ba4733b911 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/DockEditor.xaml +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/DockEditor.xaml @@ -5,14 +5,16 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:dockVm="using:Microsoft.CmdPal.UI.ViewModels.Dock" + xmlns:helpers="using:Microsoft.CmdPal.UI.Helpers" xmlns:local="using:Microsoft.CmdPal.UI.Controls" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> - - + + + @@ -25,29 +27,43 @@ Text="Add Dock Item" /> - + - - + + Spacing="2"> + + + @@ -55,117 +71,244 @@ - - + + + - - + - - - + Text="Show Labels" /> + + + + - + Content="Unpin from dock" /> + + + + + + + + + + + + + + + BorderThickness="1" + CornerRadius="8"> - + + - + - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/DockEditor.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/DockEditor.xaml.cs index 661137aad7..eb14097890 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/DockEditor.xaml.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/DockEditor.xaml.cs @@ -2,37 +2,408 @@ // 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 System.Collections.ObjectModel; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; using Microsoft.CmdPal.UI.ViewModels.Dock; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls.Primitives; -using Microsoft.UI.Xaml.Data; -using Microsoft.UI.Xaml.Input; using Microsoft.UI.Xaml.Media; -using Microsoft.UI.Xaml.Navigation; +using Windows.ApplicationModel.DataTransfer; using Windows.Foundation; -using Windows.Foundation.Collections; namespace Microsoft.CmdPal.UI.Controls; public sealed partial class DockEditor : UserControl { - public ObservableCollection DockBands + private DockBandSettingsViewModel? _draggedItem; + private ObservableCollection? _sourceCollection; + private DockEditorPinArea _targetArea; + private DockBandSettingsViewModel? _currentContextItem; + + /// + /// Gets or sets the collection of all dock band items to display and manage. + /// + public IList DockItems { - get => (ObservableCollection)GetValue(DockBandsProperty); - set => SetValue(DockBandsProperty, value); + get => (IList)GetValue(DockItemsProperty); + set => SetValue(DockItemsProperty, value); } - public static readonly DependencyProperty DockBandsProperty = DependencyProperty.Register(nameof(DockBands), typeof(ObservableCollection), typeof(DockEditor), new PropertyMetadata(new ObservableCollection())); + public static readonly DependencyProperty DockItemsProperty = + DependencyProperty.Register( + nameof(DockItems), + typeof(IList), + typeof(DockEditor), + new PropertyMetadata(new ObservableCollection(), OnDockItemsChanged)); + + /// + /// Gets items pinned to the start (left) area of the dock. + /// + public ObservableCollection StartItems { get; } = new(); + + /// + /// Gets items pinned to the center area of the dock (UI concept, stored as Start). + /// + public ObservableCollection CenterItems { get; } = new(); + + /// + /// Gets items pinned to the end (right) area of the dock. + /// + public ObservableCollection EndItems { get; } = new(); + + /// + /// Gets items available to be added to the dock (not currently pinned). + /// + public ObservableCollection AvailableItems { get; } = new(); public DockEditor() { InitializeComponent(); } + + private static void OnDockItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is DockEditor editor) + { + editor.LoadItems(); + } + } + + private void LoadItems() + { + StartItems.Clear(); + CenterItems.Clear(); + EndItems.Clear(); + + if (DockItems == null) + { + return; + } + + foreach (var item in DockItems) + { + // Use PinSideIndex: 0 = None, 1 = Start, 2 = End + switch (item.PinSideIndex) + { + case 1: // Start + StartItems.Add(item); + break; + case 2: // End + EndItems.Add(item); + break; + default: // None (0) - available for adding + break; + } + } + + RefreshAvailableItems(); + } + + private void RefreshAvailableItems() + { + AvailableItems.Clear(); + + if (DockItems == null) + { + return; + } + + foreach (var item in DockItems) + { + // Items with PinSideIndex == 0 (None) are available + if (item.PinSideIndex == 0) + { + AvailableItems.Add(item); + } + } + } + + private void AddButton_Click(object sender, RoutedEventArgs e) + { + if (sender is not Button button) + { + return; + } + + // Determine which area to add to based on button name + _targetArea = button.Name switch + { + "StartAddButton" => DockEditorPinArea.Start, + "CenterAddButton" or "CenterAddButtonLeft" => DockEditorPinArea.Center, + "EndAddButton" => DockEditorPinArea.End, + _ => DockEditorPinArea.None, + }; + + // Refresh available items before showing flyout + RefreshAvailableItems(); + + // Show the flyout + var flyout = button.Flyout as Flyout; + flyout?.ShowAt(button); + } + + private void AvailableItemsListView_ItemClick(object sender, ItemClickEventArgs e) + { + if (e.ClickedItem is not DockBandSettingsViewModel selectedItem) + { + return; + } + + // Set the pin side based on target area + // PinSideIndex: 0 = None, 1 = Start, 2 = End + selectedItem.PinSideIndex = _targetArea switch + { + DockEditorPinArea.Start => 1, + DockEditorPinArea.Center => 1, // Center maps to Start + DockEditorPinArea.End => 2, + _ => 0, + }; + + // Add to the appropriate UI collection + var targetCollection = _targetArea switch + { + DockEditorPinArea.Start => StartItems, + DockEditorPinArea.Center => CenterItems, + DockEditorPinArea.End => EndItems, + _ => null, + }; + + targetCollection?.Add(selectedItem); + + // Refresh available items + RefreshAvailableItems(); + + // Close the flyout + CloseFlyoutFromSender(sender); + } + + private void DockItem_Click(object sender, ItemClickEventArgs e) + { + if (e.ClickedItem is not DockBandSettingsViewModel clickedItem) + { + return; + } + + _currentContextItem = clickedItem; + + // Update the flyout title + OptionsFlyoutTitle.Text = clickedItem.Title; + + // Set the combo box to the current show labels setting + ShowLabelsComboBox.SelectedIndex = clickedItem.ShowLabelsIndex; + + // Show the flyout attached to the clicked item + if (sender is ListView listView) + { + var container = listView.ContainerFromItem(clickedItem) as ListViewItem; + if (container != null && Resources.TryGetValue("DockItemOptionsFlyout", out var flyoutResource) && flyoutResource is Flyout flyout) + { + flyout.ShowAt(container); + } + } + } + + private void ShowLabelsComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (_currentContextItem == null || sender is not ComboBox comboBox) + { + return; + } + + _currentContextItem.ShowLabelsIndex = comboBox.SelectedIndex; + } + + private void UnpinButton_Click(object sender, RoutedEventArgs e) + { + if (_currentContextItem == null) + { + return; + } + + // Remove from all UI collections + StartItems.Remove(_currentContextItem); + CenterItems.Remove(_currentContextItem); + EndItems.Remove(_currentContextItem); + + // Set pin side to None + _currentContextItem.PinSideIndex = 0; + + // Refresh available items + RefreshAvailableItems(); + + _currentContextItem = null; + + // Close the flyout + CloseFlyoutFromSender(sender); + } + + private void DockItemOptionsFlyout_Closed(object sender, object e) + { + _currentContextItem = null; + } + + private void ListView_DragItemsStarting(object sender, DragItemsStartingEventArgs e) + { + if (e.Items.Count > 0 && e.Items[0] is DockBandSettingsViewModel item) + { + _draggedItem = item; + _sourceCollection = (sender as ListView)?.ItemsSource as ObservableCollection; + e.Data.RequestedOperation = DataPackageOperation.Move; + } + } + + private void ListView_DragItemsCompleted(ListViewBase sender, DragItemsCompletedEventArgs args) + { + // Reordering within the same list is handled automatically by ListView + // The PinSideIndex doesn't need to change + _draggedItem = null; + _sourceCollection = null; + } + + private void ListView_DragOver(object sender, DragEventArgs e) + { + e.AcceptedOperation = DataPackageOperation.Move; + e.DragUIOverride.Caption = "Move"; + e.DragUIOverride.IsCaptionVisible = true; + e.DragUIOverride.IsContentVisible = true; + e.DragUIOverride.IsGlyphVisible = true; + } + + private void StartListView_Drop(object sender, DragEventArgs e) + { + HandleDrop(sender, DockEditorPinArea.Start, e); + } + + private void CenterListView_Drop(object sender, DragEventArgs e) + { + HandleDrop(sender, DockEditorPinArea.Center, e); + } + + private void EndListView_Drop(object sender, DragEventArgs e) + { + HandleDrop(sender, DockEditorPinArea.End, e); + } + + private void HandleDrop(object sender, DockEditorPinArea targetArea, DragEventArgs e) + { + if (_draggedItem == null) + { + return; + } + + var targetListView = sender as ListView; + var targetCollection = targetListView?.ItemsSource as ObservableCollection; + + if (targetCollection == null || _sourceCollection == null) + { + return; + } + + // If dropping to a different collection, move the item + if (targetCollection != _sourceCollection) + { + // Remove from source collection + _sourceCollection.Remove(_draggedItem); + + // Calculate the drop index based on the position + var dropIndex = GetDropIndex(targetListView!, e); + + // Insert at the drop position + if (dropIndex >= targetCollection.Count) + { + targetCollection.Add(_draggedItem); + } + else + { + targetCollection.Insert(dropIndex, _draggedItem); + } + + // Update the pin side + // PinSideIndex: 0 = None, 1 = Start, 2 = End + _draggedItem.PinSideIndex = targetArea switch + { + DockEditorPinArea.Start => 1, + DockEditorPinArea.Center => 1, // Center maps to Start + DockEditorPinArea.End => 2, + _ => 0, + }; + } + + // Clear references + _draggedItem = null; + _sourceCollection = null; + } + + private int GetDropIndex(ListView listView, DragEventArgs e) + { + var position = e.GetPosition(listView); + var items = listView.ItemsSource as ObservableCollection; + + if (items == null || items.Count == 0) + { + return 0; + } + + // Find the drop position based on X coordinate for horizontal layout + for (var i = 0; i < items.Count; i++) + { + var container = listView.ContainerFromIndex(i) as ListViewItem; + if (container != null) + { + var containerPosition = container.TransformToVisual(listView).TransformPoint(new Point(0, 0)); + var containerCenter = containerPosition.X + (container.ActualWidth / 2); + + if (position.X < containerCenter) + { + return i; + } + } + } + + return items.Count; + } + + private static void CloseFlyoutFromSender(object sender) + { + // Walk up the visual tree to find and close the flyout + var element = sender as DependencyObject; + while (element != null) + { + if (element is Popup popup) + { + popup.IsOpen = false; + return; + } + + if (element is FlyoutPresenter) + { + // Find the parent popup + var parent = VisualTreeHelper.GetParent(element); + while (parent != null && parent is not Popup) + { + parent = VisualTreeHelper.GetParent(parent); + } + + if (parent is Popup flyoutPopup) + { + flyoutPopup.IsOpen = false; + } + + return; + } + + element = VisualTreeHelper.GetParent(element); + } + } +} + +/// +/// Pin area enum for the DockEditor UI. +/// +public enum DockEditorPinArea +{ + None, + Start, + Center, + End, } 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 3c3a227f9d..d8baebd267 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Microsoft.CmdPal.UI.csproj +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Microsoft.CmdPal.UI.csproj @@ -69,6 +69,7 @@ + @@ -201,6 +202,12 @@ + + + MSBuild:Compile + + + MSBuild:Compile diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Settings/DockSettingsPage.xaml b/src/modules/cmdpal/Microsoft.CmdPal.UI/Settings/DockSettingsPage.xaml index d0a278e1cf..ec3c7e0ccd 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Settings/DockSettingsPage.xaml +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Settings/DockSettingsPage.xaml @@ -40,10 +40,7 @@ - - - Choose the size of your dock - + - + - - Choose where the dock appears on your screen - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - Choose the background effect for your dock - + - - - Choose whether to show labels for dock items by default. - + + OnContent="Show labels" /> + + @@ -148,15 +158,14 @@ - - + + -