diff --git a/RetroBar/Controls/NotifyIconList.xaml b/RetroBar/Controls/NotifyIconList.xaml index 37e83285..54939fa4 100644 --- a/RetroBar/Controls/NotifyIconList.xaml +++ b/RetroBar/Controls/NotifyIconList.xaml @@ -3,6 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:RetroBar.Controls" xmlns:converters="clr-namespace:RetroBar.Converters" + xmlns:dd="urn:gong-wpf-dragdrop" Loaded="NotifyIconList_Loaded" Unloaded="NotifyIconList_OnUnloaded"> @@ -23,7 +24,10 @@ Style="{DynamicResource TrayToggleButton}"/> + Style="{DynamicResource NotifyIconItems}" + dd:DragDrop.IsDragSource="True" + dd:DragDrop.IsDropTarget="True" + dd:DragDrop.DragDropContext="NotifyIconList"> diff --git a/RetroBar/Controls/NotifyIconList.xaml.cs b/RetroBar/Controls/NotifyIconList.xaml.cs index 6824cb8f..856c5512 100644 --- a/RetroBar/Controls/NotifyIconList.xaml.cs +++ b/RetroBar/Controls/NotifyIconList.xaml.cs @@ -2,9 +2,9 @@ using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel; +using System.Linq; using System.Windows; using System.Windows.Controls; -using System.Windows.Data; using System.Windows.Threading; using ManagedShell.WindowsTray; using RetroBar.Extensions; @@ -18,9 +18,10 @@ namespace RetroBar.Controls public partial class NotifyIconList : UserControl { private bool _isLoaded; - private CollectionViewSource allNotifyIconsSource; - private CollectionViewSource pinnedNotifyIconsSource; + private readonly ObservableCollection allNotifyIcons = new ObservableCollection(); + private readonly ObservableCollection pinnedNotifyIcons = new ObservableCollection(); private ObservableCollection promotedIcons = new ObservableCollection(); + private NotifyIconDropHandler dropHandler; public static DependencyProperty NotificationAreaProperty = DependencyProperty.Register(nameof(NotificationArea), typeof(NotificationArea), typeof(NotifyIconList), new PropertyMetadata(NotificationAreaChangedCallback)); @@ -41,7 +42,7 @@ private void Settings_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (Settings.Instance.CollapseNotifyIcons) { - NotifyIcons.ItemsSource = pinnedNotifyIconsSource.View; + NotifyIcons.ItemsSource = pinnedNotifyIcons; SetToggleVisibility(); } else @@ -49,7 +50,7 @@ private void Settings_PropertyChanged(object sender, PropertyChangedEventArgs e) NotifyIconToggleButton.IsChecked = false; NotifyIconToggleButton.Visibility = Visibility.Collapsed; - NotifyIcons.ItemsSource = allNotifyIconsSource.View; + NotifyIcons.ItemsSource = allNotifyIcons; } } else if (e.PropertyName == nameof(Settings.InvertIconsMode) || e.PropertyName == nameof(Settings.InvertNotifyIcons)) @@ -59,11 +60,11 @@ private void Settings_PropertyChanged(object sender, PropertyChangedEventArgs e) if (Settings.Instance.CollapseNotifyIcons && NotifyIconToggleButton.IsChecked != true) { - NotifyIcons.ItemsSource = pinnedNotifyIconsSource.View; + NotifyIcons.ItemsSource = pinnedNotifyIcons; } else { - NotifyIcons.ItemsSource = allNotifyIconsSource.View; + NotifyIcons.ItemsSource = allNotifyIcons; } } } @@ -72,25 +73,19 @@ private void SetNotificationAreaCollections() { if (!_isLoaded && NotificationArea != null) { - CompositeCollection allNotifyIcons = new CompositeCollection(); - allNotifyIcons.Add(new CollectionContainer { Collection = NotificationArea.UnpinnedIcons }); - allNotifyIcons.Add(new CollectionContainer { Collection = NotificationArea.PinnedIcons }); - allNotifyIconsSource = new CollectionViewSource { Source = allNotifyIcons }; + RefreshCollections(); + NotificationArea.UnpinnedIcons.Filter = UnpinnedNotifyIcons_Filter; - CompositeCollection pinnedNotifyIcons = new CompositeCollection(); - pinnedNotifyIcons.Add(new CollectionContainer { Collection = promotedIcons }); - pinnedNotifyIcons.Add(new CollectionContainer { Collection = NotificationArea.PinnedIcons }); - pinnedNotifyIconsSource = new CollectionViewSource { Source = pinnedNotifyIcons }; - NotificationArea.UnpinnedIcons.CollectionChanged += UnpinnedIcons_CollectionChanged; + NotificationArea.PinnedIcons.CollectionChanged += PinnedIcons_CollectionChanged; NotificationArea.NotificationBalloonShown += NotificationArea_NotificationBalloonShown; Settings.Instance.PropertyChanged += Settings_PropertyChanged; if (Settings.Instance.CollapseNotifyIcons) { - NotifyIcons.ItemsSource = pinnedNotifyIconsSource.View; + NotifyIcons.ItemsSource = pinnedNotifyIcons; SetToggleVisibility(); if (NotifyIconToggleButton.IsChecked == true) @@ -100,7 +95,7 @@ private void SetNotificationAreaCollections() } else { - NotifyIcons.ItemsSource = allNotifyIconsSource.View; + NotifyIcons.ItemsSource = allNotifyIcons; } _isLoaded = true; @@ -174,6 +169,10 @@ private void NotificationArea_NotificationBalloonShown(object sender, Notificati private void NotifyIconList_Loaded(object sender, RoutedEventArgs e) { SetNotificationAreaCollections(); + + // Set up drag/drop handler + dropHandler = new NotifyIconDropHandler(this); + GongSolutions.Wpf.DragDrop.DragDrop.SetDropHandler(NotifyIcons, dropHandler); } private void NotifyIconList_OnUnloaded(object sender, RoutedEventArgs e) @@ -197,18 +196,55 @@ private void NotifyIconList_OnUnloaded(object sender, RoutedEventArgs e) private void UnpinnedIcons_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { SetToggleVisibility(); + RefreshCollections(); + } + + private void PinnedIcons_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + RefreshCollections(); + } + + private void RefreshCollections() + { + if (NotificationArea == null) return; + + // Create a list of all icons + var icons = NotificationArea.UnpinnedIcons.Cast() + .Where(icon => !icon.IsPinned && !icon.IsHidden && icon.GetBehavior() != NotifyIconBehavior.Remove) + .Union(NotificationArea.PinnedIcons.Cast()) + .ToList(); + + // Sort icons according to saved order + var sortedIcons = icons.OrderBy(i => Settings.Instance.NotifyIconOrder.IndexOf(i.GetInvertIdentifier())).ToList(); + + // Refresh all icons collection + allNotifyIcons.Clear(); + foreach (var icon in sortedIcons) + { + allNotifyIcons.Add(icon); + } + + // Refresh pinned icons collection + pinnedNotifyIcons.Clear(); + foreach (var icon in promotedIcons) + { + pinnedNotifyIcons.Add(icon); + } + foreach (var icon in NotificationArea.PinnedIcons.Cast().OrderBy(i => Settings.Instance.NotifyIconOrder.IndexOf(i.GetInvertIdentifier()))) + { + pinnedNotifyIcons.Add(icon); + } } private void NotifyIconToggleButton_OnClick(object sender, RoutedEventArgs e) { if (NotifyIconToggleButton.IsChecked == true) { - - NotifyIcons.ItemsSource = allNotifyIconsSource.View; + NotifyIcons.ItemsSource = allNotifyIcons; } else { - NotifyIcons.ItemsSource = pinnedNotifyIconsSource.View; + NotifyIcons.ItemsSource = pinnedNotifyIcons; } } @@ -230,5 +266,25 @@ private void SetToggleVisibility() NotifyIconToggleButton.Visibility = Visibility.Visible; } } + + public void SaveIconOrder() + { + var visibleIcons = new System.Collections.Generic.List(); + + if (NotifyIcons.ItemsSource != null) + { + foreach (var item in NotifyIcons.ItemsSource) + { + if (item is ManagedShell.WindowsTray.NotifyIcon icon) + { + visibleIcons.Add(icon); + } + } + } + + // Save the order to settings + var identifiers = visibleIcons.Select(icon => icon.GetInvertIdentifier()).ToList(); + Settings.Instance.NotifyIconOrder = identifiers; + } } } \ No newline at end of file diff --git a/RetroBar/Utilities/NotifyIconDropHandler.cs b/RetroBar/Utilities/NotifyIconDropHandler.cs new file mode 100644 index 00000000..46e30be3 --- /dev/null +++ b/RetroBar/Utilities/NotifyIconDropHandler.cs @@ -0,0 +1,47 @@ +using GongSolutions.Wpf.DragDrop; +using RetroBar.Controls; + +namespace RetroBar.Utilities +{ + public class NotifyIconDropHandler : IDropTarget + { + private NotifyIconList _notifyIconList; + + public IDropInfo DropInFlight { get; set; } + + public NotifyIconDropHandler(NotifyIconList notifyIconList) + { + _notifyIconList = notifyIconList; + } + + void IDropTarget.DragOver(IDropInfo dropInfo) + { + DragDrop.DefaultDropHandler.DragOver(dropInfo); + } + +#if !NETCOREAPP3_1_OR_GREATER + public void DragEnter(IDropInfo dropInfo) + { + DragDrop.DefaultDropHandler.DragEnter(dropInfo); + } + + public void DragLeave(IDropInfo dropInfo) + { + DragDrop.DefaultDropHandler.DragLeave(dropInfo); + } +#endif + + void IDropTarget.Drop(IDropInfo dropInfo) + { + // Save before the drop in order to catch any items not yet saved + _notifyIconList.SaveIconOrder(); + DropInFlight = dropInfo; + + DragDrop.DefaultDropHandler.Drop(dropInfo); + + // Save post-drop state + _notifyIconList.SaveIconOrder(); + DropInFlight = null; + } + } +} diff --git a/RetroBar/Utilities/Settings.cs b/RetroBar/Utilities/Settings.cs index f3da2fa0..532a2778 100644 --- a/RetroBar/Utilities/Settings.cs +++ b/RetroBar/Utilities/Settings.cs @@ -185,6 +185,13 @@ public List NotifyIconBehaviors set => Set(ref _notifyIconBehaviors, value); } + private List _notifyIconOrder = new List(); + public List NotifyIconOrder + { + get => _notifyIconOrder; + set => Set(ref _notifyIconOrder, value); + } + private bool _allowFontSmoothing = false; public bool AllowFontSmoothing {