add status sync

Signed-off-by: Shawn Yuan (from Dev Box) <shuaiyuan@microsoft.com>
This commit is contained in:
Shawn Yuan (from Dev Box)
2025-11-24 11:22:54 +08:00
parent 5bf797d9f6
commit 16733718c3
6 changed files with 113 additions and 3 deletions

View File

@@ -11,6 +11,7 @@ using Microsoft.PowerToys.QuickAccess.Services;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
using Microsoft.UI.Dispatching;
using Microsoft.Windows.ApplicationModel.Resources;
namespace Microsoft.PowerToys.QuickAccess.ViewModels;
@@ -20,6 +21,7 @@ public sealed class AllAppsViewModel : Observable
private readonly IQuickAccessCoordinator _coordinator;
private readonly ISettingsRepository<GeneralSettings> _settingsRepository;
private readonly ResourceLoader _resourceLoader;
private readonly DispatcherQueue _dispatcherQueue;
private GeneralSettings _generalSettings;
public ObservableCollection<FlyoutMenuItem> FlyoutMenuItems { get; }
@@ -27,10 +29,12 @@ public sealed class AllAppsViewModel : Observable
public AllAppsViewModel(IQuickAccessCoordinator coordinator)
{
_coordinator = coordinator;
_dispatcherQueue = DispatcherQueue.GetForCurrentThread();
var settingsUtils = new SettingsUtils();
_settingsRepository = SettingsRepository<GeneralSettings>.GetInstance(settingsUtils);
_generalSettings = _settingsRepository.SettingsConfig;
_generalSettings.AddEnabledModuleChangeNotification(ModuleEnabledChangedOnSettingsPage);
_settingsRepository.SettingsChanged += OnSettingsChanged;
_resourceLoader = Helpers.ResourceLoaderInstance.ResourceLoader;
FlyoutMenuItems = new ObservableCollection<FlyoutMenuItem>();
@@ -41,6 +45,14 @@ public sealed class AllAppsViewModel : Observable
}
}
private void OnSettingsChanged(GeneralSettings newSettings)
{
_dispatcherQueue.TryEnqueue(() =>
{
ModuleEnabledChangedOnSettingsPage();
});
}
private void AddFlyoutMenuItem(ModuleType moduleType)
{
var gpo = ModuleHelper.GetModuleGpoConfiguration(moduleType);

View File

@@ -11,6 +11,7 @@ using Microsoft.PowerToys.QuickAccess.Services;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
using Microsoft.UI.Dispatching;
using Microsoft.Windows.ApplicationModel.Resources;
namespace Microsoft.PowerToys.QuickAccess.ViewModels;
@@ -20,6 +21,7 @@ public sealed class LauncherViewModel : Observable
private readonly IQuickAccessCoordinator _coordinator;
private readonly ISettingsRepository<GeneralSettings> _settingsRepository;
private readonly ResourceLoader _resourceLoader;
private readonly DispatcherQueue _dispatcherQueue;
private GeneralSettings _generalSettings;
public ObservableCollection<FlyoutMenuItem> FlyoutMenuItems { get; }
@@ -29,10 +31,12 @@ public sealed class LauncherViewModel : Observable
public LauncherViewModel(IQuickAccessCoordinator coordinator)
{
_coordinator = coordinator;
_dispatcherQueue = DispatcherQueue.GetForCurrentThread();
var settingsUtils = new SettingsUtils();
_settingsRepository = SettingsRepository<GeneralSettings>.GetInstance(settingsUtils);
_generalSettings = _settingsRepository.SettingsConfig;
_generalSettings.AddEnabledModuleChangeNotification(ModuleEnabledChanged);
_settingsRepository.SettingsChanged += OnSettingsChanged;
_resourceLoader = Helpers.ResourceLoaderInstance.ResourceLoader;
FlyoutMenuItems = new ObservableCollection<FlyoutMenuItem>();
@@ -53,6 +57,14 @@ public sealed class LauncherViewModel : Observable
IsUpdateAvailable = updatingSettings.State is UpdatingSettings.UpdatingState.ReadyToInstall or UpdatingSettings.UpdatingState.ReadyToDownload;
}
private void OnSettingsChanged(GeneralSettings newSettings)
{
_dispatcherQueue.TryEnqueue(() =>
{
ModuleEnabledChanged();
});
}
private void AddFlyoutMenuItem(ModuleType moduleType)
{
if (ModuleHelper.GetModuleGpoConfiguration(moduleType) == GpoRuleConfigured.Disabled)

View File

@@ -2,6 +2,8 @@
// 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;
namespace Microsoft.PowerToys.Settings.UI.Library.Interfaces
{
public interface ISettingsRepository<T>
@@ -9,5 +11,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library.Interfaces
T SettingsConfig { get; set; }
bool ReloadSettings();
event Action<T> SettingsChanged;
}
}

View File

@@ -3,15 +3,16 @@
// See the LICENSE file in the project root for more information.
using System;
using System.IO;
using System.Threading;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
namespace Microsoft.PowerToys.Settings.UI.Library
{
// This Singleton class is a wrapper around the settings configurations that are accessed by viewmodels.
// This class can have only one instance and therefore the settings configurations are common to all.
public class SettingsRepository<T> : ISettingsRepository<T>
public sealed class SettingsRepository<T> : ISettingsRepository<T>, IDisposable
where T : class, ISettingsConfig, new()
{
private static readonly Lock _SettingsRepoLock = new Lock();
@@ -22,6 +23,10 @@ namespace Microsoft.PowerToys.Settings.UI.Library
private T settingsConfig;
private FileSystemWatcher _watcher;
public event Action<T> SettingsChanged;
// Suppressing the warning as this is a singleton class and this method is
// necessarily static
#pragma warning disable CA1000 // Do not declare static members on generic types
@@ -35,6 +40,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
{
settingsRepository = new SettingsRepository<T>();
_settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils));
settingsRepository.InitializeWatcher();
}
return settingsRepository;
@@ -46,12 +52,51 @@ namespace Microsoft.PowerToys.Settings.UI.Library
{
}
private void InitializeWatcher()
{
try
{
var settingsItem = new T();
var filePath = _settingsUtils.GetSettingsFilePath(settingsItem.GetModuleName());
var directory = Path.GetDirectoryName(filePath);
var fileName = Path.GetFileName(filePath);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
_watcher = new FileSystemWatcher(directory, fileName);
_watcher.NotifyFilter = NotifyFilters.LastWrite;
_watcher.Changed += Watcher_Changed;
_watcher.EnableRaisingEvents = true;
}
catch (Exception ex)
{
Logger.LogError($"Failed to initialize settings watcher for {typeof(T).Name}", ex);
}
}
private void Watcher_Changed(object sender, FileSystemEventArgs e)
{
// Wait a bit for the file write to complete and retry if needed
for (int i = 0; i < 5; i++)
{
Thread.Sleep(100);
if (ReloadSettings())
{
SettingsChanged?.Invoke(SettingsConfig);
return;
}
}
}
public bool ReloadSettings()
{
try
{
T settingsItem = new T();
settingsConfig = _settingsUtils.GetSettingsOrDefault<T>(settingsItem.GetModuleName());
settingsConfig = _settingsUtils.GetSettings<T>(settingsItem.GetModuleName());
SettingsConfig = settingsConfig;
@@ -85,5 +130,26 @@ namespace Microsoft.PowerToys.Settings.UI.Library
}
}
}
public void StopWatching()
{
if (_watcher != null)
{
_watcher.EnableRaisingEvents = false;
}
}
public void StartWatching()
{
if (_watcher != null)
{
_watcher.EnableRaisingEvents = true;
}
}
public void Dispose()
{
_watcher?.Dispose();
}
}
}

View File

@@ -30,6 +30,11 @@ namespace Microsoft.PowerToys.Settings.UI.UnitTests.BackwardsCompatibility
private readonly ISettingsUtils _settingsUtils;
private T _settingsConfig;
// Implements ISettingsRepository<T>.SettingsChanged
#pragma warning disable CS0067
public event System.Action<T> SettingsChanged;
#pragma warning restore CS0067
public MockSettingsRepository(ISettingsUtils settingsUtils)
{
_settingsUtils = settingsUtils;

View File

@@ -89,6 +89,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
_settingsRepository = settingsRepository;
generalSettingsConfig = settingsRepository.SettingsConfig;
generalSettingsConfig.AddEnabledModuleChangeNotification(ModuleEnabledChangedOnSettingsPage);
_settingsRepository.SettingsChanged += OnSettingsChanged;
// Initialize dashboard sort order from settings
_dashboardSortOrder = generalSettingsConfig.DashboardSortOrder;
@@ -100,6 +101,16 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
GetShortcutModules();
}
private void OnSettingsChanged(GeneralSettings newSettings)
{
dispatcher.BeginInvoke(() =>
{
generalSettingsConfig = newSettings;
generalSettingsConfig.AddEnabledModuleChangeNotification(ModuleEnabledChangedOnSettingsPage);
ModuleEnabledChangedOnSettingsPage();
});
}
protected override void OnConflictsUpdated(object sender, AllHotkeyConflictsEventArgs e)
{
dispatcher.BeginInvoke(() =>