diff --git a/RetroBar/App.xaml.cs b/RetroBar/App.xaml.cs index 944eaf1e..d636ac99 100644 --- a/RetroBar/App.xaml.cs +++ b/RetroBar/App.xaml.cs @@ -11,6 +11,7 @@ using System.Diagnostics; using System.Reflection; using ManagedShell.Common.Logging; +using System.Linq; namespace RetroBar { @@ -110,7 +111,7 @@ private ShellManager SetupManagedShell() _logger = new ManagedShellLogger(); ShellConfig config = ShellManager.DefaultShellConfig; - config.PinnedNotifyIcons = Settings.Instance.PinnedNotifyIcons; + config.PinnedNotifyIcons = Settings.Instance.NotifyIconBehaviors.Where(setting => setting.Behavior == NotifyIconBehavior.AlwaysShow).Select(setting => setting.Identifier).ToArray(); return new ShellManager(config); } diff --git a/RetroBar/Controls/NotifyIcon.xaml.cs b/RetroBar/Controls/NotifyIcon.xaml.cs index 844a05c9..3117bb79 100644 --- a/RetroBar/Controls/NotifyIcon.xaml.cs +++ b/RetroBar/Controls/NotifyIcon.xaml.cs @@ -34,7 +34,7 @@ public NotifyIcon() private void applyEffects() { - if (TrayIcon == null || Settings.Instance.InvertIconsMode == Settings.InvertIconsOption.Never) + if (TrayIcon == null || Settings.Instance.InvertIconsMode == InvertIconsOption.Never) { return; } @@ -45,7 +45,7 @@ private void applyEffects() } bool invertByTheme = Application.Current.FindResource("InvertSystemNotifyIcons") as bool? ?? false; - bool performInvert = invertByTheme || Settings.Instance.InvertIconsMode == Settings.InvertIconsOption.Always; + bool performInvert = invertByTheme || Settings.Instance.InvertIconsMode == InvertIconsOption.Always; if (NotifyIconImage.Effect == null != performInvert) { @@ -106,6 +106,11 @@ private void TrayIcon_NotificationBalloonShown(object sender, NotificationBalloo return; } + if (TrayIcon == null || TrayIcon.GetBehavior() == NotifyIconBehavior.AlwaysHide || TrayIcon.GetBehavior() == NotifyIconBehavior.Disabled) + { + return; + } + BalloonControl.Show(e.Balloon, NotifyIconBorder); e.Handled = true; } diff --git a/RetroBar/Controls/NotifyIconList.xaml.cs b/RetroBar/Controls/NotifyIconList.xaml.cs index 2b167485..fc4f1aa8 100644 --- a/RetroBar/Controls/NotifyIconList.xaml.cs +++ b/RetroBar/Controls/NotifyIconList.xaml.cs @@ -7,6 +7,7 @@ using System.Windows.Data; using System.Windows.Threading; using ManagedShell.WindowsTray; +using RetroBar.Extensions; using RetroBar.Utilities; namespace RetroBar.Controls @@ -75,6 +76,7 @@ private void NotifyIconList_OnLoaded(object sender, RoutedEventArgs e) allNotifyIcons.Add(new CollectionContainer { Collection = NotificationArea.UnpinnedIcons }); allNotifyIcons.Add(new CollectionContainer { Collection = NotificationArea.PinnedIcons }); allNotifyIconsSource = new CollectionViewSource { Source = allNotifyIcons }; + NotificationArea.UnpinnedIcons.Filter = UnpinnedNotifyIcons_Filter; CompositeCollection pinnedNotifyIcons = new CompositeCollection(); pinnedNotifyIcons.Add(new CollectionContainer { Collection = promotedIcons }); @@ -105,6 +107,16 @@ private void NotifyIconList_OnLoaded(object sender, RoutedEventArgs e) } } + private bool UnpinnedNotifyIcons_Filter(object obj) + { + if (obj is ManagedShell.WindowsTray.NotifyIcon notifyIcon) + { + return !notifyIcon.IsPinned && !notifyIcon.IsHidden && notifyIcon.GetBehavior() != NotifyIconBehavior.Disabled; + } + + return true; + } + private void NotificationArea_NotificationBalloonShown(object sender, NotificationBalloonEventArgs e) { // This is used to promote unpinned icons to show when the tray is collapsed. @@ -122,6 +134,12 @@ private void NotificationArea_NotificationBalloonShown(object sender, Notificati return; } + if (notifyIcon.GetBehavior() != NotifyIconBehavior.HideWhenInactive) + { + // Do not promote icons that are always hidden + return; + } + if (promotedIcons.Contains(notifyIcon)) { // Do not duplicate promoted icons diff --git a/RetroBar/Controls/TaskButton.xaml.cs b/RetroBar/Controls/TaskButton.xaml.cs index 8ce3a42c..d8e78364 100644 --- a/RetroBar/Controls/TaskButton.xaml.cs +++ b/RetroBar/Controls/TaskButton.xaml.cs @@ -178,11 +178,11 @@ private void AppButton_OnMouseUp(object sender, MouseButtonEventArgs e) { if (e.ChangedButton == MouseButton.Middle) { - if (Window == null || Settings.Instance.TaskMiddleClickAction == Settings.TaskMiddleClickOption.DoNothing) + if (Window == null || Settings.Instance.TaskMiddleClickAction == TaskMiddleClickOption.DoNothing) { return; } - if (Settings.Instance.TaskMiddleClickAction == Settings.TaskMiddleClickOption.CloseTask) + if (Settings.Instance.TaskMiddleClickAction == TaskMiddleClickOption.CloseTask) { Window?.Close(); } diff --git a/RetroBar/Controls/TaskList.xaml.cs b/RetroBar/Controls/TaskList.xaml.cs index 73eb4260..ff8901a9 100644 --- a/RetroBar/Controls/TaskList.xaml.cs +++ b/RetroBar/Controls/TaskList.xaml.cs @@ -98,7 +98,7 @@ private void Settings_PropertyChanged(object sender, PropertyChangedEventArgs e) } else if (e.PropertyName == nameof(Settings.ShowMultiMon)) { - if (Settings.Instance.MultiMonMode != Settings.MultiMonOption.AllTaskbars) + if (Settings.Instance.MultiMonMode != MultiMonOption.AllTaskbars) { taskbarItems?.Refresh(); } @@ -114,12 +114,12 @@ private bool Tasks_Filter(object obj) return false; } - if (!Settings.Instance.ShowMultiMon || Settings.Instance.MultiMonMode == Settings.MultiMonOption.AllTaskbars) + if (!Settings.Instance.ShowMultiMon || Settings.Instance.MultiMonMode == MultiMonOption.AllTaskbars) { return true; } - if (Settings.Instance.MultiMonMode == Settings.MultiMonOption.SameAsWindowAndPrimary && Host.Screen.Primary) + if (Settings.Instance.MultiMonMode == MultiMonOption.SameAsWindowAndPrimary && Host.Screen.Primary) { return true; } diff --git a/RetroBar/Converters/NotifyIconBehaviorConverter.cs b/RetroBar/Converters/NotifyIconBehaviorConverter.cs new file mode 100644 index 00000000..912fe945 --- /dev/null +++ b/RetroBar/Converters/NotifyIconBehaviorConverter.cs @@ -0,0 +1,26 @@ +using ManagedShell.WindowsTray; +using RetroBar.Extensions; +using System; +using System.Windows.Data; + +namespace RetroBar.Converters +{ + [ValueConversion(typeof(NotifyIcon), typeof(int))] + public class NotifyIconBehaviorConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + if (value is NotifyIcon icon) + { + return (int)icon.GetBehavior(); + } + + return Binding.DoNothing; + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + return Binding.DoNothing; + } + } +} diff --git a/RetroBar/Extensions/NotifyIconExtensions.cs b/RetroBar/Extensions/NotifyIconExtensions.cs index 99c71039..4fc3ed94 100644 --- a/RetroBar/Extensions/NotifyIconExtensions.cs +++ b/RetroBar/Extensions/NotifyIconExtensions.cs @@ -12,6 +12,72 @@ public static string GetInvertIdentifier(this NotifyIcon icon) else return icon.Path + ":" + icon.UID.ToString(); } + public static NotifyIconBehavior GetBehavior(this NotifyIcon icon) + { + if (icon.IsPinned) + { + return NotifyIconBehavior.AlwaysShow; + } + + if (Settings.Instance.NotifyIconBehaviors.Find(setting => setting.Identifier == icon.Identifier) is NotifyIconBehaviorSetting iconSetting) + { + return iconSetting.Behavior; + } + + return NotifyIconBehavior.HideWhenInactive; + } + + public static void SetBehavior(this NotifyIcon icon, NotifyIconBehavior behavior) + { + var settings = new List(Settings.Instance.NotifyIconBehaviors); + var currentSettingIndex = settings.FindIndex(setting => setting.Identifier == icon.Identifier); + + if (currentSettingIndex >= 0) + { + if (behavior == NotifyIconBehavior.HideWhenInactive) + { + // Switching back to default value; remove from settings + settings.RemoveAt(currentSettingIndex); + } + else + { + settings[currentSettingIndex] = new NotifyIconBehaviorSetting + { + Identifier = icon.Identifier, + Behavior = behavior + }; + } + } + else + { + settings.Add(new NotifyIconBehaviorSetting + { + Identifier = icon.Identifier, + Behavior = behavior + }); + } + + Settings.Instance.NotifyIconBehaviors = settings; + + if (icon.IsPinned != (behavior == NotifyIconBehavior.AlwaysShow)) + { + // Update pinned values in ManagedShell + if (behavior == NotifyIconBehavior.AlwaysShow) + { + icon.Pin(); + } + else + { + icon.Unpin(); + } + } + else + { + // Trigger a refresh of the collections + icon.OnPropertyChanged("IsPinned"); + } + } + public static bool CanInvert(this NotifyIcon icon) { return Settings.Instance.InvertNotifyIcons.Contains(icon.GetInvertIdentifier()); } diff --git a/RetroBar/Languages/English.xaml b/RetroBar/Languages/English.xaml index 8cabff8f..3035c6fa 100644 --- a/RetroBar/Languages/English.xaml +++ b/RetroBar/Languages/English.xaml @@ -69,6 +69,8 @@ Select an item, then choose its notification behavior: Hide when inactive Always show + Always hide + Disabled Name Behavior Invert diff --git a/RetroBar/NotificationPropertiesWindow.xaml b/RetroBar/NotificationPropertiesWindow.xaml index 0e928fdd..89c64be7 100644 --- a/RetroBar/NotificationPropertiesWindow.xaml +++ b/RetroBar/NotificationPropertiesWindow.xaml @@ -15,6 +15,7 @@ + - - + + + + diff --git a/RetroBar/NotificationPropertiesWindow.xaml.cs b/RetroBar/NotificationPropertiesWindow.xaml.cs index 86957b17..c8cfe762 100644 --- a/RetroBar/NotificationPropertiesWindow.xaml.cs +++ b/RetroBar/NotificationPropertiesWindow.xaml.cs @@ -27,7 +27,7 @@ private NotificationPropertiesWindow(NotificationArea notificationArea) private void Settings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { - if (e.PropertyName == "InvertNotifyIcons") + if (e.PropertyName == nameof(Settings.InvertNotifyIcons) || e.PropertyName == nameof(Settings.NotifyIconBehaviors)) { // Reload icons DataContext = null; @@ -63,7 +63,19 @@ private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs private void BehaviorComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { - Settings.Instance.PinnedNotifyIcons = _notificationArea.PinnedNotifyIcons; + if (e.AddedItems.Count > 0 && + (sender as FrameworkElement).DataContext is NotifyIcon notifyIcon && + e.AddedItems[0] is ComboBoxItem selected && + selected.Tag is NotifyIconBehavior behavior) + { + if (notifyIcon.GetBehavior() == behavior) + { + // Same setting selected; do nothing + return; + } + + notifyIcon.SetBehavior(behavior); + } } private void InvertCheckBox_Checked(object sender, RoutedEventArgs e) diff --git a/RetroBar/Utilities/Settings.cs b/RetroBar/Utilities/Settings.cs index 60904eda..09ce5dbb 100644 --- a/RetroBar/Utilities/Settings.cs +++ b/RetroBar/Utilities/Settings.cs @@ -8,7 +8,7 @@ namespace RetroBar.Utilities { - internal class Settings : INotifyPropertyChanged + internal class Settings : INotifyPropertyChanged, IMigratableSettings { private static Settings instance; @@ -30,6 +30,8 @@ public static Settings Instance private static bool _isInitializing = true; private static SettingsManager _settingsManager = new(_settingsPath, new Settings()); + private bool _migrationPerformed = false; + public bool MigrationPerformed { get => _migrationPerformed; } public event PropertyChangedEventHandler PropertyChanged; // This should not be used directly! Unfortunately it must be public for JsonSerializer. @@ -80,22 +82,6 @@ protected void SetEnum(ref T field, T value, [CallerMemberName] string proper } } - #region Old Properties - private bool? _middleMouseToClose = null; - public bool? MiddleMouseToClose - { - get => _middleMouseToClose; - set - { - if (value != null) - { - _taskMiddleClickAction = (bool)value ? TaskMiddleClickOption.CloseTask : TaskMiddleClickOption.OpenNewInstance; - } - _middleMouseToClose = null; - } - } - #endregion - #region Properties private string _language = "System"; public string Language @@ -174,11 +160,33 @@ public List InvertNotifyIcons set => Set(ref _invertNotifyIcons, value); } - private string[] _pinnedNotifyIcons = ["7820ae76-23e3-4229-82c1-e41cb67d5b9c", "7820ae75-23e3-4229-82c1-e41cb67d5b9c", "7820ae74-23e3-4229-82c1-e41cb67d5b9c", "7820ae73-23e3-4229-82c1-e41cb67d5b9c"]; - public string[] PinnedNotifyIcons + private List _notifyIconBehaviors = new List { - get => _pinnedNotifyIcons; - set => Set(ref _pinnedNotifyIcons, value); + new NotifyIconBehaviorSetting + { + Identifier = NotificationArea.HEALTH_GUID, + Behavior = NotifyIconBehavior.AlwaysShow + }, + new NotifyIconBehaviorSetting + { + Identifier = NotificationArea.POWER_GUID, + Behavior = NotifyIconBehavior.AlwaysShow + }, + new NotifyIconBehaviorSetting + { + Identifier = NotificationArea.NETWORK_GUID, + Behavior = NotifyIconBehavior.AlwaysShow + }, + new NotifyIconBehaviorSetting + { + Identifier = NotificationArea.VOLUME_GUID, + Behavior = NotifyIconBehavior.AlwaysShow + }, + }; + public List NotifyIconBehaviors + { + get => _notifyIconBehaviors; + set => Set(ref _notifyIconBehaviors, value); } private bool _allowFontSmoothing = false; @@ -280,27 +288,84 @@ public bool Updates } #endregion - #region Enums - public enum InvertIconsOption - { - WhenNeededByTheme, - Always, - Never - } - - public enum MultiMonOption + #region Old Properties + public bool? MiddleMouseToClose { - AllTaskbars, - SameAsWindow, - SameAsWindowAndPrimary + get => null; + set + { + // Migrate to TaskMiddleClickAction + if (value != null) + { + TaskMiddleClickAction = (bool)value ? TaskMiddleClickOption.CloseTask : TaskMiddleClickOption.OpenNewInstance; + _migrationPerformed = true; + } + } } - public enum TaskMiddleClickOption + public string[] PinnedNotifyIcons { - DoNothing, - OpenNewInstance, - CloseTask + get => []; + set + { + // Migrate to NotifyIconBehaviors + if (value.Length > 0) + { + var newSettings = new List(); + + foreach (var identifier in value) + { + newSettings.Add(new NotifyIconBehaviorSetting + { + Identifier = identifier, + Behavior = NotifyIconBehavior.AlwaysShow + }); + } + + NotifyIconBehaviors = newSettings; + _migrationPerformed = true; + } + } } #endregion } + + #region Enums + public enum InvertIconsOption + { + WhenNeededByTheme, + Always, + Never + } + + public enum MultiMonOption + { + AllTaskbars, + SameAsWindow, + SameAsWindowAndPrimary + } + + public enum TaskMiddleClickOption + { + DoNothing, + OpenNewInstance, + CloseTask + } + + public enum NotifyIconBehavior + { + HideWhenInactive, + AlwaysHide, + AlwaysShow, + Disabled + } + #endregion + + #region Structs + public struct NotifyIconBehaviorSetting + { + public string Identifier { get; set; } + public NotifyIconBehavior Behavior { get; set; } + } + #endregion } \ No newline at end of file diff --git a/RetroBar/Utilities/SettingsManager.cs b/RetroBar/Utilities/SettingsManager.cs index aba1bed8..f5480065 100644 --- a/RetroBar/Utilities/SettingsManager.cs +++ b/RetroBar/Utilities/SettingsManager.cs @@ -8,7 +8,7 @@ namespace RetroBar.Utilities { - public class SettingsManager : INotifyPropertyChanged where T : new() + public class SettingsManager : INotifyPropertyChanged where T : IMigratableSettings { public event PropertyChangedEventHandler PropertyChanged; @@ -48,6 +48,13 @@ private bool LoadFromFile() string jsonString = File.ReadAllText(_fileName); _settings = JsonSerializer.Deserialize(jsonString); + + if (_settings.MigrationPerformed) + { + // Save post-migration state so that we don't need to migrate every startup + SaveToFile(); + } + return true; } catch (Exception ex) @@ -84,4 +91,9 @@ protected virtual void OnPropertyChanged([CallerMemberName] string propertyName PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } + + public interface IMigratableSettings + { + public bool MigrationPerformed { get; } + } } \ No newline at end of file