diff --git a/src/settings-ui/QuickAccess.UI/ViewModels/AllAppsViewModel.cs b/src/settings-ui/QuickAccess.UI/ViewModels/AllAppsViewModel.cs index 198deff34f..0461c3e59e 100644 --- a/src/settings-ui/QuickAccess.UI/ViewModels/AllAppsViewModel.cs +++ b/src/settings-ui/QuickAccess.UI/ViewModels/AllAppsViewModel.cs @@ -10,6 +10,7 @@ using global::PowerToys.GPOWrapper; using ManagedCommon; using Microsoft.PowerToys.QuickAccess.Helpers; using Microsoft.PowerToys.QuickAccess.Services; +using Microsoft.PowerToys.Settings.UI.Controls; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Helpers; using Microsoft.PowerToys.Settings.UI.Library.Interfaces; @@ -26,6 +27,7 @@ public sealed class AllAppsViewModel : Observable private readonly SettingsUtils _settingsUtils; private readonly ResourceLoader _resourceLoader; private readonly DispatcherQueue _dispatcherQueue; + private readonly List _allFlyoutMenuItems = new(); private GeneralSettings _generalSettings; public ObservableCollection FlyoutMenuItems { get; } @@ -58,9 +60,28 @@ public sealed class AllAppsViewModel : Observable _resourceLoader = Helpers.ResourceLoaderInstance.ResourceLoader; FlyoutMenuItems = new ObservableCollection(); + BuildFlyoutMenuItems(); RefreshFlyoutMenuItems(); } + private void BuildFlyoutMenuItems() + { + _allFlyoutMenuItems.Clear(); + foreach (ModuleType moduleType in Enum.GetValues()) + { + if (moduleType == ModuleType.GeneralSettings) + { + continue; + } + + _allFlyoutMenuItems.Add(new FlyoutMenuItem + { + Tag = moduleType, + EnabledChangedCallback = EnabledChangedOnUI, + }); + } + } + private void OnSettingsChanged(GeneralSettings newSettings) { _dispatcherQueue.TryEnqueue(() => @@ -82,63 +103,37 @@ public sealed class AllAppsViewModel : Observable private void RefreshFlyoutMenuItems() { - var desiredItems = new List(); - - foreach (ModuleType moduleType in Enum.GetValues()) + foreach (var item in _allFlyoutMenuItems) { - if (moduleType == ModuleType.GeneralSettings) - { - continue; - } - + var moduleType = item.Tag; var gpo = Helpers.ModuleGpoHelper.GetModuleGpoConfiguration(moduleType); var isLocked = gpo is GpoRuleConfigured.Enabled or GpoRuleConfigured.Disabled; var isEnabled = gpo == GpoRuleConfigured.Enabled || (!isLocked && Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetIsModuleEnabled(_generalSettings, moduleType)); - var existingItem = FlyoutMenuItems.FirstOrDefault(x => x.Tag == moduleType); + item.Label = _resourceLoader.GetString(Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetModuleLabelResourceName(moduleType)); + item.IsLocked = isLocked; + item.Icon = Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetModuleTypeFluentIconName(moduleType); - if (existingItem != null) + if (item.IsEnabled != isEnabled) { - existingItem.Label = _resourceLoader.GetString(Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetModuleLabelResourceName(moduleType)); - existingItem.IsLocked = isLocked; - existingItem.Icon = Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetModuleTypeFluentIconName(moduleType); - - if (existingItem.IsEnabled != isEnabled) - { - var callback = existingItem.EnabledChangedCallback; - existingItem.EnabledChangedCallback = null; - existingItem.IsEnabled = isEnabled; - existingItem.EnabledChangedCallback = callback; - } - - desiredItems.Add(existingItem); - } - else - { - desiredItems.Add(new FlyoutMenuItem - { - Label = _resourceLoader.GetString(Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetModuleLabelResourceName(moduleType)), - IsEnabled = isEnabled, - IsLocked = isLocked, - Tag = moduleType, - Icon = Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetModuleTypeFluentIconName(moduleType), - EnabledChangedCallback = EnabledChangedOnUI, - }); + item.UpdateStatus(isEnabled); } } var sortedItems = DashboardSortOrder switch { - DashboardSortOrder.ByStatus => desiredItems.OrderByDescending(x => x.IsEnabled).ThenBy(x => x.Label).ToList(), - _ => desiredItems.OrderBy(x => x.Label).ToList(), + DashboardSortOrder.ByStatus => _allFlyoutMenuItems.OrderByDescending(x => x.IsEnabled).ThenBy(x => x.Label).ToList(), + _ => _allFlyoutMenuItems.OrderBy(x => x.Label).ToList(), }; - for (int i = FlyoutMenuItems.Count - 1; i >= 0; i--) + if (FlyoutMenuItems.Count == 0) { - if (!sortedItems.Contains(FlyoutMenuItems[i])) + foreach (var item in sortedItems) { - FlyoutMenuItems.RemoveAt(i); + FlyoutMenuItems.Add(item); } + + return; } for (int i = 0; i < sortedItems.Count; i++) @@ -146,20 +141,17 @@ public sealed class AllAppsViewModel : Observable var item = sortedItems[i]; var oldIndex = FlyoutMenuItems.IndexOf(item); - if (oldIndex < 0) - { - FlyoutMenuItems.Insert(i, item); - } - else if (oldIndex != i) + if (oldIndex != -1 && oldIndex != i) { FlyoutMenuItems.Move(oldIndex, i); } } } - private void EnabledChangedOnUI(FlyoutMenuItem item) + private void EnabledChangedOnUI(ModuleListItem item) { - if (_coordinator.UpdateModuleEnabled(item.Tag, item.IsEnabled)) + var flyoutItem = (FlyoutMenuItem)item; + if (_coordinator.UpdateModuleEnabled(flyoutItem.Tag, flyoutItem.IsEnabled)) { _coordinator.NotifyUserSettingsInteraction(); } diff --git a/src/settings-ui/QuickAccess.UI/ViewModels/FlyoutMenuItem.cs b/src/settings-ui/QuickAccess.UI/ViewModels/FlyoutMenuItem.cs index 32243ceed0..b0ce5a9512 100644 --- a/src/settings-ui/QuickAccess.UI/ViewModels/FlyoutMenuItem.cs +++ b/src/settings-ui/QuickAccess.UI/ViewModels/FlyoutMenuItem.cs @@ -22,21 +22,6 @@ public sealed class FlyoutMenuItem : ModuleListItem set => base.Tag = value; } - public override bool IsEnabled - { - get => base.IsEnabled; - set - { - if (base.IsEnabled != value) - { - base.IsEnabled = value; - EnabledChangedCallback?.Invoke(this); - } - } - } - - public Action? EnabledChangedCallback { get; set; } - public bool Visible { get => _visible; diff --git a/src/settings-ui/Settings.UI.Controls/ModuleList/ModuleListItem.cs b/src/settings-ui/Settings.UI.Controls/ModuleList/ModuleListItem.cs index ef92e3e592..3b6bf6a0bb 100644 --- a/src/settings-ui/Settings.UI.Controls/ModuleList/ModuleListItem.cs +++ b/src/settings-ui/Settings.UI.Controls/ModuleList/ModuleListItem.cs @@ -2,6 +2,7 @@ // 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; // For Action using System.ComponentModel; using System.Runtime.CompilerServices; using System.Windows.Input; @@ -17,6 +18,22 @@ namespace Microsoft.PowerToys.Settings.UI.Controls private bool _isLocked; private object? _tag; private ICommand? _clickCommand; + private bool _isUpdating; + + public Action? EnabledChangedCallback { get; set; } + + public void UpdateStatus(bool isEnabled) + { + _isUpdating = true; + try + { + IsEnabled = isEnabled; + } + finally + { + _isUpdating = false; + } + } public virtual string Label { @@ -79,6 +96,11 @@ namespace Microsoft.PowerToys.Settings.UI.Controls { _isEnabled = value; OnPropertyChanged(); + + if (!_isUpdating) + { + EnabledChangedCallback?.Invoke(this); + } } } } diff --git a/src/settings-ui/Settings.UI/ViewModels/DashboardListItem.cs b/src/settings-ui/Settings.UI/ViewModels/DashboardListItem.cs index c292ec5011..aad7b9536b 100644 --- a/src/settings-ui/Settings.UI/ViewModels/DashboardListItem.cs +++ b/src/settings-ui/Settings.UI/ViewModels/DashboardListItem.cs @@ -26,21 +26,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels set => base.Tag = value; } - public Action EnabledChangedCallback { get; set; } - - public override bool IsEnabled - { - get => base.IsEnabled; - set - { - if (base.IsEnabled != value) - { - base.IsEnabled = value; - EnabledChangedCallback?.Invoke(this); - } - } - } - public bool Visible { get => _visible; diff --git a/src/settings-ui/Settings.UI/ViewModels/DashboardViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/DashboardViewModel.cs index 3c35e9279d..ed0babe364 100644 --- a/src/settings-ui/Settings.UI/ViewModels/DashboardViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/DashboardViewModel.cs @@ -50,7 +50,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels // Flag to prevent circular updates when a UI toggle triggers settings changes. private bool _isUpdatingFromUI; - private bool _isUpdatingFromSettings; private AllHotkeyConflictsData _allHotkeyConflictsData = new AllHotkeyConflictsData(); @@ -258,7 +257,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels // Only update if there's an actual change to minimize UI notifications. if (item.IsEnabled != newEnabledState) { - item.IsEnabled = newEnabledState; + item.UpdateStatus(newEnabledState); } if (item.IsLocked != newLockedState) @@ -275,19 +274,17 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels /// Sets the _isUpdatingFromUI flag to prevent circular updates, then updates /// settings, re-sorts if needed, and refreshes dependent collections. /// - private void EnabledChangedOnUI(DashboardListItem dashboardListItem) + private void EnabledChangedOnUI(ModuleListItem item) { - if (_isUpdatingFromSettings) - { - return; - } + var dashboardListItem = (DashboardListItem)item; + var isEnabled = dashboardListItem.IsEnabled; _isUpdatingFromUI = true; try { - Views.ShellPage.UpdateGeneralSettingsCallback(dashboardListItem.Tag, dashboardListItem.IsEnabled); + Views.ShellPage.UpdateGeneralSettingsCallback(dashboardListItem.Tag, isEnabled); - if (dashboardListItem.Tag == ModuleType.NewPlus && dashboardListItem.IsEnabled == true) + if (dashboardListItem.Tag == ModuleType.NewPlus && isEnabled == true) { var settingsUtils = SettingsUtils.Default; var settings = NewPlusViewModel.LoadSettings(settingsUtils); @@ -325,7 +322,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels return; } - _isUpdatingFromSettings = true; try { RefreshModuleList(); @@ -340,10 +336,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels { Logger.LogError($"Updating active/disabled modules list failed: {ex.Message}"); } - finally - { - _isUpdatingFromSettings = false; - } } ///