diff --git a/src/common/GPOWrapper/GPOWrapper.cpp b/src/common/GPOWrapper/GPOWrapper.cpp index 361255f66f..b691fb45ad 100644 --- a/src/common/GPOWrapper/GPOWrapper.cpp +++ b/src/common/GPOWrapper/GPOWrapper.cpp @@ -32,6 +32,10 @@ namespace winrt::PowerToys::GPOWrapper::implementation { return static_cast(powertoys_gpo::getConfiguredLightSwitchEnabledValue()); } + GpoRuleConfigured GPOWrapper::GetConfiguredPowerDisplayEnabledValue() + { + return static_cast(powertoys_gpo::getConfiguredPowerDisplayEnabledValue()); + } GpoRuleConfigured GPOWrapper::GetConfiguredFancyZonesEnabledValue() { return static_cast(powertoys_gpo::getConfiguredFancyZonesEnabledValue()); diff --git a/src/common/GPOWrapper/GPOWrapper.h b/src/common/GPOWrapper/GPOWrapper.h index c0fff9f542..633122b8a3 100644 --- a/src/common/GPOWrapper/GPOWrapper.h +++ b/src/common/GPOWrapper/GPOWrapper.h @@ -14,6 +14,7 @@ namespace winrt::PowerToys::GPOWrapper::implementation static GpoRuleConfigured GetConfiguredColorPickerEnabledValue(); static GpoRuleConfigured GetConfiguredCropAndLockEnabledValue(); static GpoRuleConfigured GetConfiguredLightSwitchEnabledValue(); + static GpoRuleConfigured GetConfiguredPowerDisplayEnabledValue(); static GpoRuleConfigured GetConfiguredFancyZonesEnabledValue(); static GpoRuleConfigured GetConfiguredFileLocksmithEnabledValue(); static GpoRuleConfigured GetConfiguredSvgPreviewEnabledValue(); diff --git a/src/common/GPOWrapper/GPOWrapper.idl b/src/common/GPOWrapper/GPOWrapper.idl index 630beab9c9..f0b7c29e51 100644 --- a/src/common/GPOWrapper/GPOWrapper.idl +++ b/src/common/GPOWrapper/GPOWrapper.idl @@ -18,6 +18,7 @@ namespace PowerToys static GpoRuleConfigured GetConfiguredColorPickerEnabledValue(); static GpoRuleConfigured GetConfiguredCropAndLockEnabledValue(); static GpoRuleConfigured GetConfiguredLightSwitchEnabledValue(); + static GpoRuleConfigured GetConfiguredPowerDisplayEnabledValue(); static GpoRuleConfigured GetConfiguredFancyZonesEnabledValue(); static GpoRuleConfigured GetConfiguredFileLocksmithEnabledValue(); static GpoRuleConfigured GetConfiguredSvgPreviewEnabledValue(); diff --git a/src/common/utils/gpo.h b/src/common/utils/gpo.h index 471cefe480..40f116a129 100644 --- a/src/common/utils/gpo.h +++ b/src/common/utils/gpo.h @@ -31,6 +31,7 @@ namespace powertoys_gpo const std::wstring POLICY_CONFIGURE_ENABLED_COLOR_PICKER = L"ConfigureEnabledUtilityColorPicker"; const std::wstring POLICY_CONFIGURE_ENABLED_CROP_AND_LOCK = L"ConfigureEnabledUtilityCropAndLock"; const std::wstring POLICY_CONFIGURE_ENABLED_LIGHT_SWITCH = L"ConfigureEnabledUtilityLightSwitch"; + const std::wstring POLICY_CONFIGURE_ENABLED_POWER_DISPLAY = L"ConfigureEnabledUtilityPowerDisplay"; const std::wstring POLICY_CONFIGURE_ENABLED_FANCYZONES = L"ConfigureEnabledUtilityFancyZones"; const std::wstring POLICY_CONFIGURE_ENABLED_FILE_LOCKSMITH = L"ConfigureEnabledUtilityFileLocksmith"; const std::wstring POLICY_CONFIGURE_ENABLED_SVG_PREVIEW = L"ConfigureEnabledUtilityFileExplorerSVGPreview"; @@ -301,6 +302,11 @@ namespace powertoys_gpo return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_LIGHT_SWITCH); } + inline gpo_rule_configured_t getConfiguredPowerDisplayEnabledValue() + { + return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_POWER_DISPLAY); + } + inline gpo_rule_configured_t getConfiguredFancyZonesEnabledValue() { return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_FANCYZONES); diff --git a/src/gpo/assets/PowerToys.admx b/src/gpo/assets/PowerToys.admx index 07d4f44bde..e15ab53c55 100644 --- a/src/gpo/assets/PowerToys.admx +++ b/src/gpo/assets/PowerToys.admx @@ -147,6 +147,16 @@ + + + + + + + + + + diff --git a/src/gpo/assets/en-US/PowerToys.adml b/src/gpo/assets/en-US/PowerToys.adml index 2703358bb0..f1b80c8b76 100644 --- a/src/gpo/assets/en-US/PowerToys.adml +++ b/src/gpo/assets/en-US/PowerToys.adml @@ -246,6 +246,7 @@ If you don't configure this policy, the user will be able to control the setting CmdPal: Configure enabled state Crop And Lock: Configure enabled state Light Switch: Configure enabled state + PowerDisplay: Configure enabled state Environment Variables: Configure enabled state FancyZones: Configure enabled state File Locksmith: Configure enabled state diff --git a/src/modules/powerdisplay/PowerDisplay.Lib/Drivers/DDC/DdcCiController.cs b/src/modules/powerdisplay/PowerDisplay.Lib/Drivers/DDC/DdcCiController.cs index f31dbe07f0..17af0ee0c9 100644 --- a/src/modules/powerdisplay/PowerDisplay.Lib/Drivers/DDC/DdcCiController.cs +++ b/src/modules/powerdisplay/PowerDisplay.Lib/Drivers/DDC/DdcCiController.cs @@ -52,6 +52,8 @@ namespace PowerDisplay.Common.Drivers.DDC /// public async Task CanControlMonitorAsync(Monitor monitor, CancellationToken cancellationToken = default) { + ArgumentNullException.ThrowIfNull(monitor); + return await Task.Run( () => { @@ -73,6 +75,8 @@ namespace PowerDisplay.Common.Drivers.DDC /// public async Task GetBrightnessAsync(Monitor monitor, CancellationToken cancellationToken = default) { + ArgumentNullException.ThrowIfNull(monitor); + return await Task.Run( () => { @@ -94,6 +98,7 @@ namespace PowerDisplay.Common.Drivers.DDC /// public async Task SetBrightnessAsync(Monitor monitor, int brightness, CancellationToken cancellationToken = default) { + ArgumentNullException.ThrowIfNull(monitor); brightness = Math.Clamp(brightness, 0, 100); return await Task.Run( @@ -161,6 +166,8 @@ namespace PowerDisplay.Common.Drivers.DDC /// public async Task GetColorTemperatureAsync(Monitor monitor, CancellationToken cancellationToken = default) { + ArgumentNullException.ThrowIfNull(monitor); + return await Task.Run( () => { @@ -192,6 +199,8 @@ namespace PowerDisplay.Common.Drivers.DDC /// Cancellation token public async Task SetColorTemperatureAsync(Monitor monitor, int colorTemperature, CancellationToken cancellationToken = default) { + ArgumentNullException.ThrowIfNull(monitor); + return await Task.Run( () => { @@ -242,6 +251,8 @@ namespace PowerDisplay.Common.Drivers.DDC /// public async Task GetInputSourceAsync(Monitor monitor, CancellationToken cancellationToken = default) { + ArgumentNullException.ThrowIfNull(monitor); + return await Task.Run( () => { @@ -273,8 +284,10 @@ namespace PowerDisplay.Common.Drivers.DDC /// Cancellation token public async Task SetInputSourceAsync(Monitor monitor, int inputSource, CancellationToken cancellationToken = default) { + ArgumentNullException.ThrowIfNull(monitor); + return await Task.Run( - () => + async () => { if (monitor.Handle == IntPtr.Zero) { @@ -303,7 +316,7 @@ namespace PowerDisplay.Common.Drivers.DDC Logger.LogInfo($"[{monitor.Id}] Set input source to {sourceName} via 0x60"); // Verify the change by reading back the value after a short delay - System.Threading.Thread.Sleep(100); + await Task.Delay(100, cancellationToken).ConfigureAwait(false); if (DdcCiNative.TryGetVCPFeature(monitor.Handle, VcpCodeInputSource, out uint verifyValue, out uint _)) { var verifyName = VcpValueNames.GetFormattedName(0x60, (int)verifyValue); @@ -346,6 +359,8 @@ namespace PowerDisplay.Common.Drivers.DDC /// public async Task GetCapabilitiesStringAsync(Monitor monitor, CancellationToken cancellationToken = default) { + ArgumentNullException.ThrowIfNull(monitor); + // Check if capabilities are already cached if (!string.IsNullOrEmpty(monitor.CapabilitiesRaw)) { diff --git a/src/modules/powerdisplay/PowerDisplay.Lib/Drivers/DDC/PhysicalMonitorHandleManager.cs b/src/modules/powerdisplay/PowerDisplay.Lib/Drivers/DDC/PhysicalMonitorHandleManager.cs index 6aafeb9456..5cd8a9e594 100644 --- a/src/modules/powerdisplay/PowerDisplay.Lib/Drivers/DDC/PhysicalMonitorHandleManager.cs +++ b/src/modules/powerdisplay/PowerDisplay.Lib/Drivers/DDC/PhysicalMonitorHandleManager.cs @@ -95,6 +95,7 @@ namespace PowerDisplay.Common.Drivers.DDC /// /// Clean up handles that are no longer in use. /// Called within ExecuteWithLock context with the internal dictionary. + /// Optimized to O(n) using HashSet lookup instead of O(n*m) nested loops. /// private void CleanupUnusedHandles(Dictionary currentHandles, Dictionary newHandles) { @@ -103,25 +104,16 @@ namespace PowerDisplay.Common.Drivers.DDC return; } + // Build HashSet of handles that will be reused (O(m)) + var reusedHandles = new HashSet(newHandles.Values); + + // Find handles to destroy: in old map but not reused (O(n) with O(1) lookup) var handlesToDestroy = new List(); - - // Find handles that are in old map but not being reused - foreach (var oldMapping in currentHandles) + foreach (var oldHandle in currentHandles.Values) { - bool found = false; - foreach (var newMapping in newHandles) + if (oldHandle != IntPtr.Zero && !reusedHandles.Contains(oldHandle)) { - // If the same handle is being reused, don't destroy it - if (oldMapping.Value == newMapping.Value) - { - found = true; - break; - } - } - - if (!found && oldMapping.Value != IntPtr.Zero) - { - handlesToDestroy.Add(oldMapping.Value); + handlesToDestroy.Add(oldHandle); } } diff --git a/src/modules/powerdisplay/PowerDisplay.Lib/Drivers/WMI/WmiController.cs b/src/modules/powerdisplay/PowerDisplay.Lib/Drivers/WMI/WmiController.cs index 2a5e6690c6..06d46af4f3 100644 --- a/src/modules/powerdisplay/PowerDisplay.Lib/Drivers/WMI/WmiController.cs +++ b/src/modules/powerdisplay/PowerDisplay.Lib/Drivers/WMI/WmiController.cs @@ -27,8 +27,41 @@ namespace PowerDisplay.Common.Drivers.WMI private const string BrightnessMethodClass = "WmiMonitorBrightnessMethods"; private const string MonitorIdClass = "WmiMonitorID"; + // Common WMI error codes for classification + private const int WbemENotFound = unchecked((int)0x80041002); + private const int WbemEAccessDenied = unchecked((int)0x80041003); + private const int WbemEProviderFailure = unchecked((int)0x80041004); + private const int WbemEInvalidQuery = unchecked((int)0x80041017); + private const int WmiFeatureNotSupported = 0x1068; + private bool _disposed; + /// + /// Classifies WMI exceptions into user-friendly error messages. + /// + private static MonitorOperationResult ClassifyWmiError(WmiException ex, string operation) + { + var hresult = ex.HResult; + + return hresult switch + { + WbemENotFound => MonitorOperationResult.Failure($"WMI class not found during {operation}. This feature may not be supported on your system.", hresult), + WbemEAccessDenied => MonitorOperationResult.Failure($"Access denied during {operation}. Administrator privileges may be required.", hresult), + WbemEProviderFailure => MonitorOperationResult.Failure($"WMI provider failure during {operation}. The display driver may not support this feature.", hresult), + WbemEInvalidQuery => MonitorOperationResult.Failure($"Invalid WMI query during {operation}. This is likely a bug.", hresult), + WmiFeatureNotSupported => MonitorOperationResult.Failure($"WMI brightness control not supported on this system during {operation}.", hresult), + _ => MonitorOperationResult.Failure($"WMI error during {operation}: {ex.Message}", hresult), + }; + } + + /// + /// Determines if the WMI error is expected for systems without WMI brightness support. + /// + private static bool IsExpectedUnsupportedError(WmiException ex) + { + return ex.HResult == WmiFeatureNotSupported || ex.HResult == WbemENotFound; + } + public string Name => "WMI Monitor Controller (WmiLight)"; /// @@ -36,6 +69,8 @@ namespace PowerDisplay.Common.Drivers.WMI /// public async Task CanControlMonitorAsync(Monitor monitor, CancellationToken cancellationToken = default) { + ArgumentNullException.ThrowIfNull(monitor); + if (monitor.CommunicationMethod != "WMI") { return false; @@ -65,6 +100,8 @@ namespace PowerDisplay.Common.Drivers.WMI /// public async Task GetBrightnessAsync(Monitor monitor, CancellationToken cancellationToken = default) { + ArgumentNullException.ThrowIfNull(monitor); + return await Task.Run( () => { @@ -99,6 +136,8 @@ namespace PowerDisplay.Common.Drivers.WMI /// public async Task SetBrightnessAsync(Monitor monitor, int brightness, CancellationToken cancellationToken = default) { + ArgumentNullException.ThrowIfNull(monitor); + // Validate brightness range brightness = Math.Clamp(brightness, 0, 100); @@ -147,11 +186,11 @@ namespace PowerDisplay.Common.Drivers.WMI } catch (WmiException ex) { - return MonitorOperationResult.Failure($"WMI error: {ex.Message}", ex.HResult); + return ClassifyWmiError(ex, "SetBrightness"); } catch (Exception ex) { - return MonitorOperationResult.Failure($"Unexpected error: {ex.Message}"); + return MonitorOperationResult.Failure($"Unexpected error during SetBrightness: {ex.Message}"); } }, cancellationToken); @@ -309,15 +348,21 @@ namespace PowerDisplay.Common.Drivers.WMI var results = connection.CreateQuery(query).ToList(); return results.Count > 0; } - catch (WmiException ex) when (ex.HResult == 0x1068) + catch (WmiException ex) when (IsExpectedUnsupportedError(ex)) { // Expected on systems without WMI brightness support (desktops, some laptops) Logger.LogInfo("WMI brightness control not supported on this system (expected for desktops)"); return false; } + catch (WmiException ex) + { + // Unexpected WMI error - log with details for debugging + Logger.LogWarning($"WMI availability check failed: {ex.Message} (HResult: 0x{ex.HResult:X})"); + return false; + } catch (Exception ex) { - // Unexpected error during WMI check + // Unexpected non-WMI error Logger.LogDebug($"WMI availability check failed: {ex.Message}"); return false; } diff --git a/src/modules/powerdisplay/PowerDisplay/PowerDisplayXAML/MainWindow.xaml b/src/modules/powerdisplay/PowerDisplay/PowerDisplayXAML/MainWindow.xaml index a0ad082592..b72c820ff2 100644 --- a/src/modules/powerdisplay/PowerDisplay/PowerDisplayXAML/MainWindow.xaml +++ b/src/modules/powerdisplay/PowerDisplay/PowerDisplayXAML/MainWindow.xaml @@ -7,6 +7,7 @@ xmlns:local="using:PowerDisplay" xmlns:toolkit="using:CommunityToolkit.WinUI.Controls" xmlns:ui="using:CommunityToolkit.WinUI" + xmlns:models="using:PowerDisplay.Common.Models" xmlns:vm="using:PowerDisplay.ViewModels" xmlns:winuiex="using:WinUIEx" MinWidth="0" @@ -361,10 +362,19 @@ VerticalAlignment="Center" Orientation="Horizontal" Spacing="8"> - @@ -90,7 +96,7 @@ - diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerDisplayPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerDisplayPage.xaml.cs index e49e0627ba..4447df00d7 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerDisplayPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerDisplayPage.xaml.cs @@ -90,10 +90,11 @@ namespace Microsoft.PowerToys.Settings.UI.Views : monitor.ColorTemperatureVcp; // Show confirmation dialog + var resourceLoader = ResourceLoaderInstance.ResourceLoader; var dialog = new ContentDialog { XamlRoot = this.XamlRoot, - Title = "Confirm Color Temperature Change", + Title = resourceLoader.GetString("PowerDisplay_ColorTemperature_WarningTitle"), Content = new StackPanel { Spacing = 12, @@ -101,32 +102,32 @@ namespace Microsoft.PowerToys.Settings.UI.Views { new TextBlock { - Text = "⚠️ Warning: This is a potentially dangerous operation!", + Text = resourceLoader.GetString("PowerDisplay_ColorTemperature_WarningHeader"), FontWeight = Microsoft.UI.Text.FontWeights.Bold, Foreground = (Microsoft.UI.Xaml.Media.Brush)Application.Current.Resources["SystemFillColorCriticalBrush"], TextWrapping = TextWrapping.Wrap, }, new TextBlock { - Text = "Changing the color temperature setting may cause unpredictable results including:", + Text = resourceLoader.GetString("PowerDisplay_ColorTemperature_WarningDescription"), TextWrapping = TextWrapping.Wrap, }, new TextBlock { - Text = "• Incorrect display colors\n• Display malfunction\n• Settings that cannot be reverted", + Text = resourceLoader.GetString("PowerDisplay_ColorTemperature_WarningList"), TextWrapping = TextWrapping.Wrap, Margin = new Thickness(20, 0, 0, 0), }, new TextBlock { - Text = "Are you sure you want to proceed with this change?", + Text = resourceLoader.GetString("PowerDisplay_ColorTemperature_WarningConfirm"), FontWeight = Microsoft.UI.Text.FontWeights.SemiBold, TextWrapping = TextWrapping.Wrap, }, }, }, - PrimaryButtonText = "Yes, Change Setting", - CloseButtonText = "Cancel", + PrimaryButtonText = resourceLoader.GetString("PowerDisplay_ColorTemperature_PrimaryButton"), + CloseButtonText = resourceLoader.GetString("PowerDisplay_Dialog_Cancel"), DefaultButton = ContentDialogButton.Close, }; @@ -207,13 +208,14 @@ namespace Microsoft.PowerToys.Settings.UI.Views var menuItem = sender as MenuFlyoutItem; if (menuItem?.Tag is PowerDisplayProfile profile) { + var resourceLoader = ResourceLoaderInstance.ResourceLoader; var dialog = new ContentDialog { XamlRoot = this.XamlRoot, - Title = "Delete Profile", - Content = $"Are you sure you want to delete '{profile.Name}'?", - PrimaryButtonText = "Delete", - CloseButtonText = "Cancel", + Title = resourceLoader.GetString("PowerDisplay_DeleteProfile_Title"), + Content = string.Format(System.Globalization.CultureInfo.CurrentCulture, resourceLoader.GetString("PowerDisplay_DeleteProfile_Content"), profile.Name), + PrimaryButtonText = resourceLoader.GetString("PowerDisplay_DeleteProfile_PrimaryButton"), + CloseButtonText = resourceLoader.GetString("PowerDisplay_Dialog_Cancel"), DefaultButton = ContentDialogButton.Close, }; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/ProfileEditorDialog.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/ProfileEditorDialog.xaml index b529756629..29c37e857a 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/ProfileEditorDialog.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/ProfileEditorDialog.xaml @@ -46,7 +46,8 @@ + Header="{x:Bind Monitor.Name}" + IsExpanded="True"> diff --git a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw index c7eac98adb..d21ba7f0e3 100644 --- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw +++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw @@ -5670,4 +5670,102 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m Color temperature control not supported by this monitor + + Confirm Color Temperature Change + + + ⚠️ Warning: This is a potentially dangerous operation! + + + Changing the color temperature setting may cause unpredictable results including: + + + • Incorrect display colors +• Display malfunction +• Settings that cannot be reverted + + + Are you sure you want to proceed with this change? + + + Yes, Change Setting + + + Cancel + + + Delete Profile + + + Are you sure you want to delete '{0}'? + + + Delete + + + Apply profile + + + More profile options + + + Edit profile + + + Delete profile + + + Add new profile + + + Apply + + + More settings + + + Edit + + + Delete + + + Save current settings as new profile + + + Monitor capabilities unavailable + + + This monitor did not report DDC/CI capabilities. Advanced controls may be limited. + + + Warning: Changing this setting can cause unexpected behavior. Adjust only when its impact is clear. + + + Not available + + + VCP capabilities + + + DDC/CI VCP codes and supported values (for debugging purposes) + + + View VCP details + + + View VCP details + + + Detected VCP Codes + + + Copy all VCP codes to clipboard + + + Copy VCP codes to clipboard + + + Copy + \ No newline at end of file diff --git a/src/settings-ui/Settings.UI/ViewModels/PowerDisplayViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/PowerDisplayViewModel.cs index 3188bfb76f..a27a46a77f 100644 --- a/src/settings-ui/Settings.UI/ViewModels/PowerDisplayViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/PowerDisplayViewModel.cs @@ -81,9 +81,22 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels }); } + private GpoRuleConfigured _enabledGpoRuleConfiguration; + private bool _enabledStateIsGPOConfigured; + private void InitializeEnabledValue() { - _isEnabled = GeneralSettingsConfig.Enabled.PowerDisplay; + _enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredPowerDisplayEnabledValue(); + if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) + { + // Get the enabled state from GPO + _enabledStateIsGPOConfigured = true; + _isEnabled = _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled; + } + else + { + _isEnabled = GeneralSettingsConfig.Enabled.PowerDisplay; + } } public bool IsEnabled @@ -91,6 +104,12 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels get => _isEnabled; set { + if (_enabledStateIsGPOConfigured) + { + // If it's GPO configured, shouldn't be able to change this state. + return; + } + if (_isEnabled != value) { _isEnabled = value; @@ -103,6 +122,11 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels } } + public bool IsEnabledGpoConfigured + { + get => _enabledStateIsGPOConfigured; + } + public bool RestoreSettingsOnStartup { get => _settings.Properties.RestoreSettingsOnStartup; @@ -675,6 +699,9 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels // Reload profile list LoadProfiles(); + // Signal PowerDisplay to reload profiles + SignalSettingsUpdated(); + Logger.LogInfo($"Profile '{profile.Name}' created successfully"); } catch (Exception ex) @@ -708,6 +735,9 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels // Reload profile list LoadProfiles(); + // Signal PowerDisplay to reload profiles + SignalSettingsUpdated(); + Logger.LogInfo($"Profile updated to '{newProfile.Name}' successfully"); } catch (Exception ex) @@ -737,6 +767,9 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels // Reload profile list LoadProfiles(); + // Signal PowerDisplay to reload profiles + SignalSettingsUpdated(); + Logger.LogInfo($"Profile '{profileName}' deleted successfully"); } catch (Exception ex)