Refactor LightSwitch and PowerDisplay integration

Simplify theme change notification logic in `LightSwitchService.cpp` by consolidating redundant checks and improving error handling. Remove the `applyMonitorSettings` setting and associated logic from `LightSwitchSettings`.

Introduce `PowerDisplayProfilesHelper` to centralize profile management, ensuring thread safety and simplifying file operations. Update UI in `LightSwitchPage.xaml` to replace `ApplyMonitorSettings` with separate dark and light mode profile settings, adding navigation to PowerDisplay settings.

Enhance `LightSwitchViewModel` with nullable annotations, new profile selection properties, and improved property synchronization. Refactor `PowerDisplayViewModel` to use `PowerDisplayProfilesHelper` for profile management.

Update localization strings for new UI elements. Perform general code cleanup, including null safety annotations, improved logging, and removal of legacy code.
This commit is contained in:
Yu Leng
2025-11-21 15:36:56 +08:00
parent 925c97a7f9
commit b381472bf7
11 changed files with 350 additions and 154 deletions

View File

@@ -197,44 +197,41 @@ DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
SetAppsTheme(false);
}
// Notify PowerDisplay about theme change if monitor settings integration is enabled
if (settings.applyMonitorSettings)
// Notify PowerDisplay about theme change if any profile is enabled
bool shouldNotify = false;
if (isLightActive && settings.enableLightModeProfile && !settings.lightModeProfile.empty())
{
bool shouldNotify = false;
shouldNotify = true;
}
else if (!isLightActive && settings.enableDarkModeProfile && !settings.darkModeProfile.empty())
{
shouldNotify = true;
}
if (isLightActive && settings.enableLightModeProfile && settings.lightModeProfile != L"(None)")
if (shouldNotify)
{
try
{
shouldNotify = true;
}
else if (!isLightActive && settings.enableDarkModeProfile && settings.darkModeProfile != L"(None)")
{
shouldNotify = true;
}
// Signal PowerDisplay to check LightSwitch settings and apply appropriate profile
// PowerDisplay will read LightSwitch settings to determine which profile to apply
Logger::info(L"[LightSwitch] Notifying PowerDisplay about theme change (isLight: {})", isLightActive);
if (shouldNotify)
HANDLE hThemeChangedEvent = CreateEventW(nullptr, FALSE, FALSE, L"Local\\PowerToys_LightSwitch_ThemeChanged");
if (hThemeChangedEvent)
{
SetEvent(hThemeChangedEvent);
CloseHandle(hThemeChangedEvent);
Logger::info(L"[LightSwitch] Theme change event signaled");
}
else
{
Logger::warn(L"[LightSwitch] Failed to create theme change event");
}
}
catch (...)
{
try
{
// Signal PowerDisplay to check LightSwitch settings and apply appropriate profile
// PowerDisplay will read LightSwitch settings to determine which profile to apply
Logger::info(L"[LightSwitch] Notifying PowerDisplay about theme change (isLight: {})", isLightActive);
HANDLE hThemeChangedEvent = CreateEventW(nullptr, FALSE, FALSE, L"Local\\PowerToys_LightSwitch_ThemeChanged");
if (hThemeChangedEvent)
{
SetEvent(hThemeChangedEvent);
CloseHandle(hThemeChangedEvent);
Logger::info(L"[LightSwitch] Theme change event signaled");
}
else
{
Logger::warn(L"[LightSwitch] Failed to create theme change event");
}
}
catch (...)
{
Logger::error(L"[LightSwitch] Failed to notify PowerDisplay");
}
Logger::error(L"[LightSwitch] Failed to notify PowerDisplay");
}
}
};

View File

@@ -160,16 +160,6 @@ void LightSwitchSettings::LoadSettings()
}
}
// ApplyMonitorSettings
if (const auto jsonVal = values.get_bool_value(L"applyMonitorSettings"))
{
auto val = *jsonVal;
if (m_settings.applyMonitorSettings != val)
{
m_settings.applyMonitorSettings = val;
}
}
// EnableDarkModeProfile
if (const auto jsonVal = values.get_bool_value(L"enableDarkModeProfile"))
{

View File

@@ -57,11 +57,10 @@ struct LightSwitchConfig
bool changeSystem = false;
bool changeApps = false;
bool applyMonitorSettings = false;
bool enableDarkModeProfile = false;
bool enableLightModeProfile = false;
std::wstring darkModeProfile = L"(None)";
std::wstring lightModeProfile = L"(None)";
std::wstring darkModeProfile = L"";
std::wstring lightModeProfile = L"";
};
class LightSwitchSettings

View File

@@ -17,7 +17,6 @@ namespace Microsoft.PowerToys.Settings.UI.Library
public const string DefaultLatitude = "0.0";
public const string DefaultLongitude = "0.0";
public const string DefaultScheduleMode = "FixedHours";
public const bool DefaultApplyMonitorSettings = false;
public const bool DefaultEnableDarkModeProfile = false;
public const bool DefaultEnableLightModeProfile = false;
public const string DefaultDarkModeProfile = "";
@@ -36,7 +35,6 @@ namespace Microsoft.PowerToys.Settings.UI.Library
SunsetOffset = new IntProperty(DefaultSunsetOffset);
ScheduleMode = new StringProperty(DefaultScheduleMode);
ToggleThemeHotkey = new KeyboardKeysProperty(DefaultToggleThemeHotkey);
ApplyMonitorSettings = new BoolProperty(DefaultApplyMonitorSettings);
EnableDarkModeProfile = new BoolProperty(DefaultEnableDarkModeProfile);
EnableLightModeProfile = new BoolProperty(DefaultEnableLightModeProfile);
DarkModeProfile = new StringProperty(DefaultDarkModeProfile);
@@ -73,9 +71,6 @@ namespace Microsoft.PowerToys.Settings.UI.Library
[JsonPropertyName("toggle-theme-hotkey")]
public KeyboardKeysProperty ToggleThemeHotkey { get; set; }
[JsonPropertyName("applyMonitorSettings")]
public BoolProperty ApplyMonitorSettings { get; set; }
[JsonPropertyName("enableDarkModeProfile")]
public BoolProperty EnableDarkModeProfile { get; set; }

View File

@@ -60,7 +60,6 @@ namespace Settings.UI.Library
Latitude = new StringProperty(Properties.Latitude.Value),
Longitude = new StringProperty(Properties.Longitude.Value),
ToggleThemeHotkey = new KeyboardKeysProperty(Properties.ToggleThemeHotkey.Value),
ApplyMonitorSettings = new BoolProperty(Properties.ApplyMonitorSettings.Value),
EnableDarkModeProfile = new BoolProperty(Properties.EnableDarkModeProfile.Value),
EnableLightModeProfile = new BoolProperty(Properties.EnableLightModeProfile.Value),
DarkModeProfile = new StringProperty(Properties.DarkModeProfile.Value),

View File

@@ -0,0 +1,151 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable enable
using System;
using System.IO;
using System.Text.Json;
using ManagedCommon;
namespace Microsoft.PowerToys.Settings.UI.Library
{
/// <summary>
/// Helper class for managing PowerDisplay profiles storage and retrieval
/// Provides centralized access to profile data for both PowerDisplay and LightSwitch modules
/// </summary>
public static class PowerDisplayProfilesHelper
{
private static readonly object _lock = new object();
private static readonly JsonSerializerOptions _jsonSerializerOptions = new JsonSerializerOptions { WriteIndented = true };
private static string? _cachedProfilesFilePath;
/// <summary>
/// Gets the file path where PowerDisplay profiles are stored
/// </summary>
public static string GetProfilesFilePath()
{
if (_cachedProfilesFilePath != null)
{
return _cachedProfilesFilePath;
}
lock (_lock)
{
if (_cachedProfilesFilePath != null)
{
return _cachedProfilesFilePath;
}
var settingsPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var powerToysPath = Path.Combine(settingsPath, "Microsoft", "PowerToys", "PowerDisplay");
if (!Directory.Exists(powerToysPath))
{
Directory.CreateDirectory(powerToysPath);
}
_cachedProfilesFilePath = Path.Combine(powerToysPath, "profiles.json");
return _cachedProfilesFilePath;
}
}
/// <summary>
/// Loads PowerDisplay profiles from disk
/// </summary>
/// <returns>PowerDisplayProfiles object, or a new empty instance if file doesn't exist or load fails</returns>
public static PowerDisplayProfiles LoadProfiles()
{
lock (_lock)
{
try
{
var filePath = GetProfilesFilePath();
if (File.Exists(filePath))
{
var json = File.ReadAllText(filePath);
var profiles = JsonSerializer.Deserialize<PowerDisplayProfiles>(json);
if (profiles != null)
{
// Clean up any legacy Custom profiles
profiles.Profiles.RemoveAll(p => p.Name.Equals(PowerDisplayProfiles.CustomProfileName, StringComparison.OrdinalIgnoreCase));
Logger.LogInfo($"[PowerDisplayProfilesHelper] Loaded {profiles.Profiles.Count} profiles from {filePath}");
return profiles;
}
}
else
{
Logger.LogInfo($"[PowerDisplayProfilesHelper] No profiles file found at {filePath}, returning empty collection");
}
return new PowerDisplayProfiles();
}
catch (Exception ex)
{
Logger.LogError($"[PowerDisplayProfilesHelper] Failed to load profiles: {ex.Message}");
return new PowerDisplayProfiles();
}
}
}
/// <summary>
/// Saves PowerDisplay profiles to disk
/// </summary>
/// <param name="profiles">The profiles collection to save</param>
/// <param name="prettyPrint">Whether to format the JSON with indentation</param>
public static void SaveProfiles(PowerDisplayProfiles profiles, bool prettyPrint = true)
{
lock (_lock)
{
try
{
if (profiles == null)
{
Logger.LogWarning("[PowerDisplayProfilesHelper] Cannot save null profiles");
return;
}
// Clean up any Custom profiles before saving
profiles.Profiles.RemoveAll(p => p.Name.Equals(PowerDisplayProfiles.CustomProfileName, StringComparison.OrdinalIgnoreCase));
profiles.LastUpdated = DateTime.UtcNow;
var json = prettyPrint
? JsonSerializer.Serialize(profiles, _jsonSerializerOptions)
: JsonSerializer.Serialize(profiles);
var filePath = GetProfilesFilePath();
File.WriteAllText(filePath, json);
Logger.LogInfo($"[PowerDisplayProfilesHelper] Saved {profiles.Profiles.Count} profiles to {filePath}");
}
catch (Exception ex)
{
Logger.LogError($"[PowerDisplayProfilesHelper] Failed to save profiles: {ex.Message}");
}
}
}
/// <summary>
/// Checks if the profiles file exists
/// </summary>
public static bool ProfilesFileExists()
{
try
{
var filePath = GetProfilesFilePath();
return File.Exists(filePath);
}
catch (Exception ex)
{
Logger.LogError($"[PowerDisplayProfilesHelper] Error checking if profiles file exists: {ex.Message}");
return false;
}
}
}
}

View File

@@ -156,40 +156,43 @@
x:Uid="LightSwitch_PowerDisplayDisabledWarningBar"
IsOpen="True"
Severity="Warning"
Visibility="{x:Bind ViewModel.ShowPowerDisplayDisabledWarning, Mode=OneWay}" />
Visibility="{x:Bind ViewModel.ShowPowerDisplayDisabledWarning, Mode=OneWay}">
<InfoBar.ActionButton>
<HyperlinkButton x:Uid="LightSwitch_NavigatePowerDisplaySettings" Click="NavigatePowerDisplaySettings_Click" />
</InfoBar.ActionButton>
</InfoBar>
<tkcontrols:SettingsExpander
x:Uid="LightSwitch_ApplyMonitorSettingsExpander"
HeaderIcon="{ui:FontIcon Glyph=&#xE7F4;}"
IsExpanded="False">
IsExpanded="True">
<tkcontrols:SettingsExpander.Items>
<tkcontrols:SettingsCard ContentAlignment="Left">
<CheckBox
x:Uid="LightSwitch_EnableMonitorSettings"
IsChecked="{x:Bind ViewModel.ApplyMonitorSettings, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard x:Uid="LightSwitch_DarkModeProfileCard" HorizontalContentAlignment="Left">
<StackPanel Orientation="Horizontal" Spacing="12">
<tkcontrols:SettingsCard>
<tkcontrols:SettingsCard.Header>
<CheckBox
x:Uid="LightSwitch_DarkModeProfileCheckbox"
IsChecked="{x:Bind ViewModel.EnableDarkModeProfile, Mode=TwoWay}"
IsEnabled="{x:Bind ViewModel.IsPowerDisplayEnabled, Mode=OneWay}" />
<ComboBox
MinWidth="200"
IsEnabled="{x:Bind ViewModel.IsPowerDisplayEnabled, Mode=OneWay}"
ItemsSource="{x:Bind ViewModel.AvailableProfiles, Mode=OneWay}"
SelectedItem="{x:Bind ViewModel.DarkModeProfile, Mode=TwoWay}" />
</StackPanel>
</tkcontrols:SettingsCard.Header>
<ComboBox
MinWidth="200"
DisplayMemberPath="Name"
IsEnabled="{x:Bind ViewModel.IsPowerDisplayEnabled, Mode=OneWay}"
ItemsSource="{x:Bind ViewModel.AvailableProfiles, Mode=OneWay}"
SelectedItem="{x:Bind ViewModel.SelectedDarkModeProfile, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard x:Uid="LightSwitch_LightModeProfileCard" HorizontalContentAlignment="Left">
<StackPanel Orientation="Horizontal" Spacing="12">
<tkcontrols:SettingsCard>
<tkcontrols:SettingsCard.Header>
<CheckBox
x:Uid="LightSwitch_LightModeProfileCheckbox"
IsChecked="{x:Bind ViewModel.EnableLightModeProfile, Mode=TwoWay}"
IsEnabled="{x:Bind ViewModel.IsPowerDisplayEnabled, Mode=OneWay}" />
<ComboBox
MinWidth="200"
IsEnabled="{x:Bind ViewModel.IsPowerDisplayEnabled, Mode=OneWay}"
ItemsSource="{x:Bind ViewModel.AvailableProfiles, Mode=OneWay}"
SelectedItem="{x:Bind ViewModel.LightModeProfile, Mode=TwoWay}" />
</StackPanel>
</tkcontrols:SettingsCard.Header>
<ComboBox
MinWidth="200"
DisplayMemberPath="Name"
IsEnabled="{x:Bind ViewModel.IsPowerDisplayEnabled, Mode=OneWay}"
ItemsSource="{x:Bind ViewModel.AvailableProfiles, Mode=OneWay}"
SelectedItem="{x:Bind ViewModel.SelectedLightModeProfile, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
</tkcontrols:SettingsExpander.Items>
</tkcontrols:SettingsExpander>

View File

@@ -8,6 +8,7 @@ using System.IO;
using System.IO.Abstractions;
using System.Linq;
using System.Threading.Tasks;
using Common.UI;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Helpers;
using Microsoft.PowerToys.Settings.UI.Library;
@@ -128,7 +129,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
ViewModel.SelectedCity = null;
// CityAutoSuggestBox.Text = string.Empty;
ViewModel.SyncButtonInformation = $"{ViewModel.Latitude}<7D>, {ViewModel.Longitude}<7D>";
ViewModel.SyncButtonInformation = $"{ViewModel.Latitude}<7D>, {ViewModel.Longitude}<7D>";
// ViewModel.CityTimesText = $"Sunrise: {result.SunriseHour}:{result.SunriseMinute:D2}\n" + $"Sunset: {result.SunsetHour}:{result.SunsetMinute:D2}";
SyncButton.IsEnabled = true;
@@ -153,7 +154,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
}
else if (ViewModel.ScheduleMode == "SunriseToSunsetGeo")
{
ViewModel.SyncButtonInformation = $"{ViewModel.Latitude}<7D>, {ViewModel.Longitude}<7D>";
ViewModel.SyncButtonInformation = $"{ViewModel.Latitude}<7D>, {ViewModel.Longitude}<7D>";
}
SunriseModeChartState();
@@ -321,5 +322,10 @@ namespace Microsoft.PowerToys.Settings.UI.Views
{
await GetGeoLocation();
}
private void NavigatePowerDisplaySettings_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
ShellPage.Navigate(typeof(PowerDisplayPage));
}
}
}

View File

@@ -5336,12 +5336,27 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m
<data name="LightSwitch_EnableMonitorSettings.Content" xml:space="preserve">
<value>Enable monitor settings integration</value>
</data>
<data name="LightSwitch_DarkModeProfileCheckbox.Content" xml:space="preserve">
<value>Dark mode profile</value>
</data>
<data name="LightSwitch_LightModeProfileCheckbox.Content" xml:space="preserve">
<value>Light mode profile</value>
</data>
<data name="LightSwitch_DarkModeProfileCard.Header" xml:space="preserve">
<value>Dark mode profile</value>
</data>
<data name="LightSwitch_DarkModeProfileCard.Description" xml:space="preserve">
<value>Profile to apply when switching to dark mode</value>
</data>
<data name="LightSwitch_LightModeProfileCard.Header" xml:space="preserve">
<value>Light mode profile</value>
</data>
<data name="LightSwitch_LightModeProfileCard.Description" xml:space="preserve">
<value>Profile to apply when switching to light mode</value>
</data>
<data name="LightSwitch_NavigatePowerDisplaySettings.Content" xml:space="preserve">
<value>Open PowerDisplay settings</value>
</data>
<data name="LightSwitch_LocationDialog.Title" xml:space="preserve">
<value>Select a location</value>
</data>

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.
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -30,10 +32,10 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
public ObservableCollection<SearchLocation> SearchLocations { get; } = new();
public LightSwitchViewModel(LightSwitchSettings initialSettings = null, Func<string, int> ipcMSGCallBackFunc = null)
public LightSwitchViewModel(LightSwitchSettings? initialSettings = null, Func<string, int>? ipcMSGCallBackFunc = null)
{
_moduleSettings = initialSettings ?? new LightSwitchSettings();
SendConfigMSG = ipcMSGCallBackFunc;
SendConfigMSG = ipcMSGCallBackFunc ?? (_ => 0);
ForceLightCommand = new RelayCommand(ForceLightNow);
ForceDarkCommand = new RelayCommand(ForceDarkNow);
@@ -381,9 +383,9 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
}
}
private SearchLocation _selectedSearchLocation;
private SearchLocation? _selectedSearchLocation;
public SearchLocation SelectedCity
public SearchLocation? SelectedCity
{
get => _selectedSearchLocation;
set
@@ -393,7 +395,10 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
_selectedSearchLocation = value;
NotifyPropertyChanged();
UpdateSunTimes(_selectedSearchLocation.Latitude, _selectedSearchLocation.Longitude, _selectedSearchLocation.City);
if (_selectedSearchLocation != null)
{
UpdateSunTimes(_selectedSearchLocation.Latitude, _selectedSearchLocation.Longitude, _selectedSearchLocation.City);
}
}
}
}
@@ -442,7 +447,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
}
}
public void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
public void NotifyPropertyChanged([CallerMemberName] string? propertyName = null)
{
Logger.LogInfo($"Changed the property {propertyName}");
OnPropertyChanged(propertyName);
@@ -476,21 +481,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
}
}
public bool ShowPowerDisplayDisabledWarning => !IsPowerDisplayEnabled && ModuleSettings.Properties.ApplyMonitorSettings.Value;
public bool ApplyMonitorSettings
{
get => ModuleSettings.Properties.ApplyMonitorSettings.Value;
set
{
if (ModuleSettings.Properties.ApplyMonitorSettings.Value != value)
{
ModuleSettings.Properties.ApplyMonitorSettings.Value = value;
NotifyPropertyChanged();
NotifyPropertyChanged(nameof(ShowPowerDisplayDisabledWarning));
}
}
}
public bool ShowPowerDisplayDisabledWarning => !IsPowerDisplayEnabled && (ModuleSettings.Properties.EnableDarkModeProfile.Value || ModuleSettings.Properties.EnableLightModeProfile.Value);
public bool EnableDarkModeProfile
{
@@ -501,6 +492,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
{
ModuleSettings.Properties.EnableDarkModeProfile.Value = value;
NotifyPropertyChanged();
NotifyPropertyChanged(nameof(ShowPowerDisplayDisabledWarning));
}
}
}
@@ -514,10 +506,54 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
{
ModuleSettings.Properties.EnableLightModeProfile.Value = value;
NotifyPropertyChanged();
NotifyPropertyChanged(nameof(ShowPowerDisplayDisabledWarning));
}
}
}
public PowerDisplayProfile? SelectedDarkModeProfile
{
get => _selectedDarkModeProfile;
set
{
if (_selectedDarkModeProfile != value)
{
_selectedDarkModeProfile = value;
// Sync with the string property stored in settings
var newProfileName = value?.Name ?? string.Empty;
if (ModuleSettings.Properties.DarkModeProfile.Value != newProfileName)
{
ModuleSettings.Properties.DarkModeProfile.Value = newProfileName;
}
NotifyPropertyChanged();
}
}
}
public PowerDisplayProfile? SelectedLightModeProfile
{
get => _selectedLightModeProfile;
set
{
if (_selectedLightModeProfile != value)
{
_selectedLightModeProfile = value;
// Sync with the string property stored in settings
var newProfileName = value?.Name ?? string.Empty;
if (ModuleSettings.Properties.LightModeProfile.Value != newProfileName)
{
ModuleSettings.Properties.LightModeProfile.Value = newProfileName;
}
NotifyPropertyChanged();
}
}
}
// Legacy string properties for backwards compatibility with settings persistence
public string DarkModeProfile
{
get => ModuleSettings.Properties.DarkModeProfile.Value;
@@ -526,6 +562,10 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
if (ModuleSettings.Properties.DarkModeProfile.Value != value)
{
ModuleSettings.Properties.DarkModeProfile.Value = value;
// Sync with the object property
UpdateSelectedProfileFromName(value, isDarkMode: true);
NotifyPropertyChanged();
}
}
@@ -539,6 +579,10 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
if (ModuleSettings.Properties.LightModeProfile.Value != value)
{
ModuleSettings.Properties.LightModeProfile.Value = value;
// Sync with the object property
UpdateSelectedProfileFromName(value, isDarkMode: false);
NotifyPropertyChanged();
}
}
@@ -548,43 +592,62 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
{
try
{
var settingsPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var powerToysPath = Path.Combine(settingsPath, "Microsoft", "PowerToys", "PowerDisplay");
_profilesFilePath = Path.Combine(powerToysPath, "profiles.json");
var profilesData = PowerDisplayProfilesHelper.LoadProfiles();
if (File.Exists(_profilesFilePath))
AvailableProfiles.Clear();
foreach (var profile in profilesData.Profiles)
{
var json = File.ReadAllText(_profilesFilePath);
var profilesData = JsonSerializer.Deserialize<PowerDisplayProfiles>(json);
if (profilesData != null)
{
AvailableProfiles.Clear();
// Add empty option
AvailableProfiles.Add(new PowerDisplayProfile("(None)", new List<ProfileMonitorSetting>()));
foreach (var profile in profilesData.Profiles)
{
AvailableProfiles.Add(profile);
}
Logger.LogInfo($"Loaded {profilesData.Profiles.Count} PowerDisplay profiles");
}
}
else
{
// Add empty option
AvailableProfiles.Clear();
AvailableProfiles.Add(new PowerDisplayProfile("(None)", new List<ProfileMonitorSetting>()));
Logger.LogInfo("No PowerDisplay profiles file found");
AvailableProfiles.Add(profile);
}
Logger.LogInfo($"Loaded {profilesData.Profiles.Count} PowerDisplay profiles");
// Sync selected profiles from settings
UpdateSelectedProfileFromName(ModuleSettings.Properties.DarkModeProfile.Value, isDarkMode: true);
UpdateSelectedProfileFromName(ModuleSettings.Properties.LightModeProfile.Value, isDarkMode: false);
}
catch (Exception ex)
{
Logger.LogError($"Failed to load PowerDisplay profiles: {ex.Message}");
AvailableProfiles.Clear();
AvailableProfiles.Add(new PowerDisplayProfile("(None)", new List<ProfileMonitorSetting>()));
}
}
/// <summary>
/// Helper method to sync the selected profile object from the profile name stored in settings
/// </summary>
private void UpdateSelectedProfileFromName(string profileName, bool isDarkMode)
{
PowerDisplayProfile? matchingProfile = null;
if (!string.IsNullOrEmpty(profileName))
{
matchingProfile = AvailableProfiles.FirstOrDefault(p =>
p.Name.Equals(profileName, StringComparison.OrdinalIgnoreCase));
}
// If not found or empty, default to first available profile
if (matchingProfile == null && AvailableProfiles.Count > 0)
{
matchingProfile = AvailableProfiles[0];
}
if (isDarkMode)
{
if (_selectedDarkModeProfile != matchingProfile)
{
_selectedDarkModeProfile = matchingProfile;
NotifyPropertyChanged(nameof(SelectedDarkModeProfile));
}
}
else
{
if (_selectedLightModeProfile != matchingProfile)
{
_selectedLightModeProfile = matchingProfile;
NotifyPropertyChanged(nameof(SelectedLightModeProfile));
}
}
}
@@ -626,7 +689,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
OnPropertyChanged(nameof(Latitude));
OnPropertyChanged(nameof(Longitude));
OnPropertyChanged(nameof(ScheduleMode));
OnPropertyChanged(nameof(ApplyMonitorSettings));
OnPropertyChanged(nameof(EnableDarkModeProfile));
OnPropertyChanged(nameof(EnableLightModeProfile));
OnPropertyChanged(nameof(DarkModeProfile));
@@ -692,7 +754,8 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
// PowerDisplay integration
private ObservableCollection<PowerDisplayProfile> _availableProfiles = new ObservableCollection<PowerDisplayProfile>();
private bool _isPowerDisplayEnabled;
private string _profilesFilePath = string.Empty;
private PowerDisplayProfile? _selectedDarkModeProfile;
private PowerDisplayProfile? _selectedLightModeProfile;
public ICommand ForceLightCommand { get; }

View File

@@ -476,7 +476,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
// Profile-related fields
private ObservableCollection<PowerDisplayProfile> _profiles = new ObservableCollection<PowerDisplayProfile>();
private string _profilesFilePath = string.Empty;
/// <summary>
/// Collection of available profiles (for button display)
@@ -520,11 +519,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
{
try
{
var settingsPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var powerToysPath = Path.Combine(settingsPath, "Microsoft", "PowerToys", "PowerDisplay");
_profilesFilePath = Path.Combine(powerToysPath, "profiles.json");
var profilesData = LoadProfilesFromDisk();
var profilesData = PowerDisplayProfilesHelper.LoadProfiles();
// Load profile objects (no Custom - it's not a profile anymore)
Profiles.Clear();
@@ -547,14 +542,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
/// </summary>
private PowerDisplayProfiles LoadProfilesFromDisk()
{
if (File.Exists(_profilesFilePath))
{
var json = File.ReadAllText(_profilesFilePath);
var profiles = JsonSerializer.Deserialize<PowerDisplayProfiles>(json);
return profiles ?? new PowerDisplayProfiles();
}
return new PowerDisplayProfiles();
return PowerDisplayProfilesHelper.LoadProfiles();
}
/// <summary>
@@ -562,17 +550,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
/// </summary>
private void SaveProfilesToDisk(PowerDisplayProfiles profiles)
{
try
{
profiles.LastUpdated = DateTime.UtcNow;
var json = JsonSerializer.Serialize(profiles, _jsonSerializerOptions);
File.WriteAllText(_profilesFilePath, json);
Logger.LogInfo($"Saved profiles to disk: {_profilesFilePath}");
}
catch (Exception ex)
{
Logger.LogError($"Failed to save profiles: {ex.Message}");
}
PowerDisplayProfilesHelper.SaveProfiles(profiles, prettyPrint: true);
}
/// <summary>