mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 11:48:06 +01:00
Fix module list glitches and Sort Status checkmark issue.
This commit is contained in:
@@ -40,6 +40,12 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
|||||||
|
|
||||||
public ObservableCollection<DashboardListItem> ActionModules { get; set; } = new ObservableCollection<DashboardListItem>();
|
public ObservableCollection<DashboardListItem> ActionModules { get; set; } = new ObservableCollection<DashboardListItem>();
|
||||||
|
|
||||||
|
// Master list of module items that is sorted and projected into AllModules.
|
||||||
|
private List<DashboardListItem> _moduleItems = new List<DashboardListItem>();
|
||||||
|
|
||||||
|
// Flag to prevent circular updates when a UI toggle triggers settings changes.
|
||||||
|
private bool _isUpdatingFromUI;
|
||||||
|
|
||||||
private AllHotkeyConflictsData _allHotkeyConflictsData = new AllHotkeyConflictsData();
|
private AllHotkeyConflictsData _allHotkeyConflictsData = new AllHotkeyConflictsData();
|
||||||
|
|
||||||
public AllHotkeyConflictsData AllHotkeyConflictsData
|
public AllHotkeyConflictsData AllHotkeyConflictsData
|
||||||
@@ -74,7 +80,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
|||||||
generalSettingsConfig.DashboardSortOrder = value;
|
generalSettingsConfig.DashboardSortOrder = value;
|
||||||
OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(generalSettingsConfig);
|
OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(generalSettingsConfig);
|
||||||
SendConfigMSG(outgoing.ToString());
|
SendConfigMSG(outgoing.ToString());
|
||||||
RefreshModuleList();
|
SortModuleList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -96,8 +102,9 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
|||||||
// set the callback functions value to handle outgoing IPC message.
|
// set the callback functions value to handle outgoing IPC message.
|
||||||
SendConfigMSG = ipcMSGCallBackFunc;
|
SendConfigMSG = ipcMSGCallBackFunc;
|
||||||
|
|
||||||
RefreshModuleList();
|
BuildModuleList();
|
||||||
GetShortcutModules();
|
SortModuleList();
|
||||||
|
RefreshShortcutModules();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnConflictsUpdated(object sender, AllHotkeyConflictsEventArgs e)
|
protected override void OnConflictsUpdated(object sender, AllHotkeyConflictsEventArgs e)
|
||||||
@@ -129,11 +136,13 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
|||||||
GlobalHotkeyConflictManager.Instance?.RequestAllConflicts();
|
GlobalHotkeyConflictManager.Instance?.RequestAllConflicts();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RefreshModuleList()
|
/// <summary>
|
||||||
|
/// Builds the master list of module items. Called once during initialization.
|
||||||
|
/// Each module item contains its configuration, enabled state, and GPO lock status.
|
||||||
|
/// </summary>
|
||||||
|
private void BuildModuleList()
|
||||||
{
|
{
|
||||||
AllModules.Clear();
|
_moduleItems.Clear();
|
||||||
|
|
||||||
var moduleItems = new List<DashboardListItem>();
|
|
||||||
|
|
||||||
foreach (ModuleType moduleType in Enum.GetValues<ModuleType>())
|
foreach (ModuleType moduleType in Enum.GetValues<ModuleType>())
|
||||||
{
|
{
|
||||||
@@ -156,23 +165,94 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
|||||||
DashboardModuleItems = GetModuleItems(moduleType),
|
DashboardModuleItems = GetModuleItems(moduleType),
|
||||||
};
|
};
|
||||||
newItem.EnabledChangedCallback = EnabledChangedOnUI;
|
newItem.EnabledChangedCallback = EnabledChangedOnUI;
|
||||||
moduleItems.Add(newItem);
|
_moduleItems.Add(newItem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort based on current sort order
|
/// <summary>
|
||||||
var sortedItems = DashboardSortOrder switch
|
/// Sorts the module list according to the current sort order and updates the AllModules collection.
|
||||||
|
/// On first call, populates AllModules. On subsequent calls, uses Move() to reorder items in-place
|
||||||
|
/// to avoid destroying and recreating UI elements.
|
||||||
|
/// </summary>
|
||||||
|
private void SortModuleList()
|
||||||
{
|
{
|
||||||
DashboardSortOrder.ByStatus => moduleItems.OrderByDescending(x => x.IsEnabled).ThenBy(x => x.Label),
|
var sortedItems = (DashboardSortOrder switch
|
||||||
_ => moduleItems.OrderBy(x => x.Label), // Default alphabetical
|
{
|
||||||
};
|
DashboardSortOrder.ByStatus => _moduleItems.OrderByDescending(x => x.IsEnabled).ThenBy(x => x.Label),
|
||||||
|
_ => _moduleItems.OrderBy(x => x.Label), // Default alphabetical
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
// If AllModules is empty (first load), just populate it.
|
||||||
|
if (AllModules.Count == 0)
|
||||||
|
{
|
||||||
foreach (var item in sortedItems)
|
foreach (var item in sortedItems)
|
||||||
{
|
{
|
||||||
AllModules.Add(item);
|
AllModules.Add(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Otherwise, update the collection in place using Move to avoid UI glitches.
|
||||||
|
for (int i = 0; i < sortedItems.Count; i++)
|
||||||
|
{
|
||||||
|
var currentItem = sortedItems[i];
|
||||||
|
var currentIndex = AllModules.IndexOf(currentItem);
|
||||||
|
|
||||||
|
if (currentIndex != -1 && currentIndex != i)
|
||||||
|
{
|
||||||
|
AllModules.Move(currentIndex, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify that DashboardSortOrder changed to update menu check mark.
|
||||||
|
OnPropertyChanged(nameof(DashboardSortOrder));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Refreshes module enabled/locked states by re-reading GPO configuration. Only
|
||||||
|
/// updates properties that have actually changed to minimize UI notifications
|
||||||
|
/// then re-sorts the list according to the current sort order.
|
||||||
|
/// </summary>
|
||||||
|
private void RefreshModuleList()
|
||||||
|
{
|
||||||
|
foreach (var item in _moduleItems)
|
||||||
|
{
|
||||||
|
GpoRuleConfigured gpo = ModuleHelper.GetModuleGpoConfiguration(item.Tag);
|
||||||
|
|
||||||
|
// GPO can force-enable (Enabled) or force-disable (Disabled) a module.
|
||||||
|
// If Enabled: module is on and the user cannot disable it.
|
||||||
|
// If Disabled: module is off and the user cannot enable it.
|
||||||
|
// Otherwise, the setting is unlocked and the user can enable/disable it.
|
||||||
|
bool newEnabledState = gpo == GpoRuleConfigured.Enabled || (gpo != GpoRuleConfigured.Disabled && ModuleHelper.GetIsModuleEnabled(generalSettingsConfig, item.Tag));
|
||||||
|
|
||||||
|
// Lock the toggle when GPO is controlling the module.
|
||||||
|
bool newLockedState = gpo == GpoRuleConfigured.Enabled || gpo == GpoRuleConfigured.Disabled;
|
||||||
|
|
||||||
|
// Only update if there's an actual change to minimize UI notifications.
|
||||||
|
if (item.IsEnabled != newEnabledState)
|
||||||
|
{
|
||||||
|
item.IsEnabled = newEnabledState;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.IsLocked != newLockedState)
|
||||||
|
{
|
||||||
|
item.IsLocked = newLockedState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SortModuleList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Callback invoked when a user toggles a module's enabled state in the UI.
|
||||||
|
/// Sets the _isUpdatingFromUI flag to prevent circular updates, then updates
|
||||||
|
/// settings, re-sorts if needed, and refreshes dependent collections.
|
||||||
|
/// </summary>
|
||||||
private void EnabledChangedOnUI(DashboardListItem dashboardListItem)
|
private void EnabledChangedOnUI(DashboardListItem dashboardListItem)
|
||||||
|
{
|
||||||
|
_isUpdatingFromUI = true;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
Views.ShellPage.UpdateGeneralSettingsCallback(dashboardListItem.Tag, dashboardListItem.IsEnabled);
|
Views.ShellPage.UpdateGeneralSettingsCallback(dashboardListItem.Tag, dashboardListItem.IsEnabled);
|
||||||
|
|
||||||
@@ -183,20 +263,45 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
|||||||
NewPlusViewModel.CopyTemplateExamples(settings.Properties.TemplateLocation.Value);
|
NewPlusViewModel.CopyTemplateExamples(settings.Properties.TemplateLocation.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request updated conflicts after module state change
|
// Re-sort only required if sorting by enabled status.
|
||||||
RequestConflictData();
|
if (DashboardSortOrder == DashboardSortOrder.ByStatus)
|
||||||
|
{
|
||||||
|
SortModuleList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Always refresh shortcuts/actions to reflect enabled state changes.
|
||||||
|
RefreshShortcutModules();
|
||||||
|
|
||||||
|
// Request updated conflicts after module state change.
|
||||||
|
RequestConflictData();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_isUpdatingFromUI = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Callback invoked when module enabled state changes from other parts of the
|
||||||
|
/// settings UI. Ignores the notification if it was triggered by a UI toggle
|
||||||
|
/// we're already handling, to prevent circular updates.
|
||||||
|
/// </summary>
|
||||||
public void ModuleEnabledChangedOnSettingsPage()
|
public void ModuleEnabledChangedOnSettingsPage()
|
||||||
{
|
{
|
||||||
|
// Ignore if this was triggered by a UI change that we're already handling.
|
||||||
|
if (_isUpdatingFromUI)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
RefreshModuleList();
|
RefreshModuleList();
|
||||||
GetShortcutModules();
|
RefreshShortcutModules();
|
||||||
|
|
||||||
OnPropertyChanged(nameof(ShortcutModules));
|
OnPropertyChanged(nameof(ShortcutModules));
|
||||||
|
|
||||||
// Request updated conflicts after module state change
|
// Request updated conflicts after module state change.
|
||||||
RequestConflictData();
|
RequestConflictData();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -205,7 +310,11 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GetShortcutModules()
|
/// <summary>
|
||||||
|
/// Rebuilds ShortcutModules and ActionModules collections by filtering AllModules
|
||||||
|
/// to only include enabled modules and their respective shortcut/action items.
|
||||||
|
/// </summary>
|
||||||
|
private void RefreshShortcutModules()
|
||||||
{
|
{
|
||||||
ShortcutModules.Clear();
|
ShortcutModules.Clear();
|
||||||
ActionModules.Clear();
|
ActionModules.Clear();
|
||||||
|
|||||||
Reference in New Issue
Block a user