mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 11:48:06 +01:00
Add color temperature support to PowerDisplay
Enhanced PowerDisplay with support for applying color temperature settings. - Added `APPLY_COLOR_TEMPERATURE_POWER_DISPLAY_EVENT` and event handling logic. - Introduced `ApplyColorTemperatureFromSettings` in `MainViewModel` for explicit hardware updates. - Refactored `MonitorInfo` to dynamically compute and cache color temperature presets. - Updated `ReloadMonitorsFromSettings` to preserve object references and improve UI responsiveness. - Simplified UI bindings and removed redundant properties like `MonitorType`. - Improved event handling in `dllmain.cpp` for the new color temperature action. - Enhanced logging for better debugging and traceability. - Updated JSON serialization context to include new types for color temperature. - Removed unused code and improved documentation for maintainability.
This commit is contained in:
@@ -137,6 +137,7 @@ namespace CommonSharedConstants
|
||||
const wchar_t TERMINATE_POWER_DISPLAY_EVENT[] = L"Local\\PowerToysPowerDisplay-TerminateEvent-7b9c2e1f-8a5d-4c3e-9f6b-2a1d8c5e3b7a";
|
||||
const wchar_t REFRESH_POWER_DISPLAY_MONITORS_EVENT[] = L"Local\\PowerToysPowerDisplay-RefreshMonitorsEvent-a3f5c8e7-9d1b-4e2f-8c6a-3b5d7e9f1a2c";
|
||||
const wchar_t SETTINGS_UPDATED_POWER_DISPLAY_EVENT[] = L"Local\\PowerToysPowerDisplay-SettingsUpdatedEvent-2e4d6f8a-1c3b-5e7f-9a1d-4c6e8f0b2d3e";
|
||||
const wchar_t APPLY_COLOR_TEMPERATURE_POWER_DISPLAY_EVENT[] = L"Local\\PowerToysPowerDisplay-ApplyColorTemperatureEvent-4b7e9f2a-3c6d-5a8e-7f1b-9d2e4c6a8b0d";
|
||||
|
||||
// used from quick access window
|
||||
const wchar_t CMDPAL_SHOW_EVENT[] = L"Local\\PowerToysCmdPal-ShowEvent-62336fcd-8611-4023-9b30-091a6af4cc5a";
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace PowerDisplay
|
||||
private const string TerminatePowerDisplayEvent = "Local\\PowerToysPowerDisplay-TerminateEvent-7b9c2e1f-8a5d-4c3e-9f6b-2a1d8c5e3b7a";
|
||||
private const string RefreshMonitorsEvent = "Local\\PowerToysPowerDisplay-RefreshMonitorsEvent-a3f5c8e7-9d1b-4e2f-8c6a-3b5d7e9f1a2c";
|
||||
private const string SettingsUpdatedEvent = "Local\\PowerToysPowerDisplay-SettingsUpdatedEvent-2e4d6f8a-1c3b-5e7f-9a1d-4c6e8f0b2d3e";
|
||||
private const string ApplyColorTemperatureEvent = "Local\\PowerToysPowerDisplay-ApplyColorTemperatureEvent-4b7e9f2a-3c6d-5a8e-7f1b-9d2e4c6a8b0d";
|
||||
|
||||
private Window? _mainWindow;
|
||||
private int _powerToysRunnerPid;
|
||||
@@ -156,6 +157,20 @@ namespace PowerDisplay
|
||||
});
|
||||
});
|
||||
|
||||
NativeEventWaiter.WaitForEventLoop(
|
||||
ApplyColorTemperatureEvent,
|
||||
() =>
|
||||
{
|
||||
Logger.LogInfo("Received apply color temperature event");
|
||||
_mainWindow?.DispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
if (_mainWindow is MainWindow mainWindow && mainWindow.ViewModel != null)
|
||||
{
|
||||
mainWindow.ViewModel.ApplyColorTemperatureFromSettings();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Monitor Runner process (backup exit mechanism)
|
||||
if (_powerToysRunnerPid > 0)
|
||||
{
|
||||
|
||||
@@ -21,6 +21,18 @@ namespace PowerDisplay.Serialization
|
||||
[JsonSerializable(typeof(MonitorStateFile))]
|
||||
[JsonSerializable(typeof(MonitorStateEntry))]
|
||||
[JsonSerializable(typeof(PowerDisplaySettings))]
|
||||
|
||||
// MonitorInfo and related types (Settings.UI.Library)
|
||||
[JsonSerializable(typeof(MonitorInfo))]
|
||||
[JsonSerializable(typeof(VcpCodeDisplayInfo))]
|
||||
[JsonSerializable(typeof(VcpValueInfo))]
|
||||
|
||||
// Generic collection types
|
||||
[JsonSerializable(typeof(List<string>))]
|
||||
[JsonSerializable(typeof(List<MonitorInfo>))]
|
||||
[JsonSerializable(typeof(List<VcpCodeDisplayInfo>))]
|
||||
[JsonSerializable(typeof(List<VcpValueInfo>))]
|
||||
|
||||
[JsonSourceGenerationOptions(
|
||||
WriteIndented = true,
|
||||
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
|
||||
|
||||
@@ -396,15 +396,20 @@ public partial class MainViewModel : INotifyPropertyChanged, IDisposable
|
||||
{
|
||||
try
|
||||
{
|
||||
// Read current color temperature from hardware
|
||||
await _monitorManager.InitializeColorTemperatureAsync(monitorId);
|
||||
|
||||
// Update UI on dispatcher thread - get the monitor from manager
|
||||
// Get the monitor and use the hardware value as-is
|
||||
var monitor = _monitorManager.GetMonitor(monitorId);
|
||||
if (monitor != null)
|
||||
{
|
||||
Logger.LogInfo($"[{monitorId}] Read color temperature from hardware: {monitor.CurrentColorTemperature}");
|
||||
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
// Update color temperature without triggering hardware write
|
||||
// Use the hardware value directly, even if not in the preset list
|
||||
// This will also update monitor_state.json via MonitorStateManager
|
||||
vm.UpdatePropertySilently(nameof(vm.ColorTemperature), monitor.CurrentColorTemperature);
|
||||
});
|
||||
}
|
||||
@@ -424,10 +429,12 @@ public partial class MainViewModel : INotifyPropertyChanged, IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply all settings changes from Settings UI (IPC event handler entry point)
|
||||
/// Coordinates both UI configuration and hardware parameter updates
|
||||
/// Apply settings changes from Settings UI (IPC event handler entry point)
|
||||
/// Only applies UI configuration changes. Hardware parameter changes (e.g., color temperature)
|
||||
/// should be triggered via custom actions to avoid unwanted side effects when non-hardware
|
||||
/// settings (like RestoreSettingsOnStartup) are changed.
|
||||
/// </summary>
|
||||
public async void ApplySettingsFromUI()
|
||||
public void ApplySettingsFromUI()
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -435,12 +442,10 @@ public partial class MainViewModel : INotifyPropertyChanged, IDisposable
|
||||
|
||||
var settings = _settingsUtils.GetSettingsOrDefault<PowerDisplaySettings>("PowerDisplay");
|
||||
|
||||
// 1. Apply UI configuration changes (synchronous, lightweight)
|
||||
// Apply UI configuration changes only (feature visibility toggles, etc.)
|
||||
// Hardware parameters (brightness, color temperature) are applied via custom actions
|
||||
ApplyUIConfiguration(settings);
|
||||
|
||||
// 2. Apply hardware parameter changes (asynchronous, may involve DDC/CI calls)
|
||||
await ApplyHardwareParametersAsync(settings);
|
||||
|
||||
Logger.LogInfo("[Settings] Settings update complete");
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -475,6 +480,58 @@ public partial class MainViewModel : INotifyPropertyChanged, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply color temperature from settings (triggered by custom action from Settings UI)
|
||||
/// This is called when user explicitly changes color temperature in Settings UI,
|
||||
/// NOT when other settings change. Reads current settings and applies only color temperature.
|
||||
/// </summary>
|
||||
public async void ApplyColorTemperatureFromSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
Logger.LogInfo("[Settings] Processing color temperature update from Settings UI");
|
||||
|
||||
var settings = _settingsUtils.GetSettingsOrDefault<PowerDisplaySettings>("PowerDisplay");
|
||||
var updateTasks = new List<Task>();
|
||||
|
||||
foreach (var monitorVm in Monitors)
|
||||
{
|
||||
var hardwareId = monitorVm.HardwareId;
|
||||
var monitorSettings = settings.Properties.Monitors.FirstOrDefault(m => m.HardwareId == hardwareId);
|
||||
|
||||
if (monitorSettings == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Apply color temperature if changed
|
||||
if (monitorSettings.ColorTemperature > 0 &&
|
||||
monitorSettings.ColorTemperature != monitorVm.ColorTemperature)
|
||||
{
|
||||
Logger.LogInfo($"[Settings] Applying color temperature for {hardwareId}: 0x{monitorSettings.ColorTemperature:X2}");
|
||||
|
||||
var task = ApplyColorTemperatureAsync(monitorVm, monitorSettings.ColorTemperature);
|
||||
updateTasks.Add(task);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for all updates to complete
|
||||
if (updateTasks.Count > 0)
|
||||
{
|
||||
await Task.WhenAll(updateTasks);
|
||||
Logger.LogInfo($"[Settings] Completed {updateTasks.Count} color temperature updates");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogInfo("[Settings] No color temperature changes detected");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"[Settings] Failed to apply color temperature from settings: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply hardware parameter changes (brightness, color temperature)
|
||||
/// Asynchronous operation that communicates with monitor hardware via DDC/CI
|
||||
@@ -810,19 +867,26 @@ public partial class MainViewModel : INotifyPropertyChanged, IDisposable
|
||||
/// </summary>
|
||||
private Microsoft.PowerToys.Settings.UI.Library.MonitorInfo CreateMonitorInfo(MonitorViewModel vm)
|
||||
{
|
||||
return new Microsoft.PowerToys.Settings.UI.Library.MonitorInfo(
|
||||
var monitorInfo = new Microsoft.PowerToys.Settings.UI.Library.MonitorInfo(
|
||||
name: vm.Name,
|
||||
internalName: vm.Id,
|
||||
hardwareId: vm.HardwareId,
|
||||
communicationMethod: vm.CommunicationMethod,
|
||||
monitorType: vm.IsInternal ? "Internal" : "External",
|
||||
currentBrightness: vm.Brightness,
|
||||
colorTemperature: vm.ColorTemperature)
|
||||
{
|
||||
CapabilitiesRaw = vm.CapabilitiesRaw,
|
||||
VcpCodes = BuildVcpCodesList(vm),
|
||||
VcpCodesFormatted = BuildFormattedVcpCodesList(vm),
|
||||
|
||||
// Infer support flags from VCP capabilities
|
||||
// VCP 0x12 (18) = Contrast, 0x14 (20) = Color Temperature, 0x62 (98) = Volume
|
||||
SupportsContrast = vm.VcpCapabilitiesInfo?.SupportedVcpCodes.ContainsKey(0x12) ?? false,
|
||||
SupportsColorTemperature = vm.VcpCapabilitiesInfo?.SupportedVcpCodes.ContainsKey(0x14) ?? false,
|
||||
SupportsVolume = vm.VcpCapabilitiesInfo?.SupportedVcpCodes.ContainsKey(0x62) ?? false,
|
||||
};
|
||||
|
||||
return monitorInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -61,6 +61,7 @@ private:
|
||||
HANDLE m_hTerminateEvent = nullptr;
|
||||
HANDLE m_hRefreshEvent = nullptr;
|
||||
HANDLE m_hSettingsUpdatedEvent = nullptr;
|
||||
HANDLE m_hApplyColorTemperatureEvent = nullptr;
|
||||
|
||||
void parse_hotkey_settings(PowerToysSettings::PowerToyValues settings)
|
||||
{
|
||||
@@ -197,8 +198,9 @@ public:
|
||||
m_hTerminateEvent = CreateDefaultEvent(CommonSharedConstants::TERMINATE_POWER_DISPLAY_EVENT);
|
||||
m_hRefreshEvent = CreateDefaultEvent(CommonSharedConstants::REFRESH_POWER_DISPLAY_MONITORS_EVENT);
|
||||
m_hSettingsUpdatedEvent = CreateDefaultEvent(CommonSharedConstants::SETTINGS_UPDATED_POWER_DISPLAY_EVENT);
|
||||
m_hApplyColorTemperatureEvent = CreateDefaultEvent(CommonSharedConstants::APPLY_COLOR_TEMPERATURE_POWER_DISPLAY_EVENT);
|
||||
|
||||
if (!m_hInvokeEvent || !m_hToggleEvent || !m_hTerminateEvent || !m_hRefreshEvent || !m_hSettingsUpdatedEvent)
|
||||
if (!m_hInvokeEvent || !m_hToggleEvent || !m_hTerminateEvent || !m_hRefreshEvent || !m_hSettingsUpdatedEvent || !m_hApplyColorTemperatureEvent)
|
||||
{
|
||||
Logger::error(L"Failed to create one or more event handles");
|
||||
}
|
||||
@@ -237,6 +239,11 @@ public:
|
||||
CloseHandle(m_hSettingsUpdatedEvent);
|
||||
m_hSettingsUpdatedEvent = nullptr;
|
||||
}
|
||||
if (m_hApplyColorTemperatureEvent)
|
||||
{
|
||||
CloseHandle(m_hApplyColorTemperatureEvent);
|
||||
m_hApplyColorTemperatureEvent = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void destroy() override
|
||||
@@ -307,6 +314,19 @@ public:
|
||||
Logger::warn(L"Refresh event handle is null");
|
||||
}
|
||||
}
|
||||
else if (action_object.get_name() == L"ApplyColorTemperature")
|
||||
{
|
||||
Logger::trace(L"ApplyColorTemperature action received");
|
||||
if (m_hApplyColorTemperatureEvent)
|
||||
{
|
||||
Logger::trace(L"Signaling apply color temperature event");
|
||||
SetEvent(m_hApplyColorTemperatureEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::warn(L"Apply color temperature event handle is null");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
@@ -16,7 +17,6 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
private string _internalName = string.Empty;
|
||||
private string _hardwareId = string.Empty;
|
||||
private string _communicationMethod = string.Empty;
|
||||
private string _monitorType = string.Empty;
|
||||
private int _currentBrightness;
|
||||
private int _colorTemperature = 6500;
|
||||
private bool _isHidden;
|
||||
@@ -33,8 +33,8 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
private bool _supportsVolume;
|
||||
private string _capabilitiesStatus = "unknown"; // "available", "unavailable", or "unknown"
|
||||
|
||||
// Available color temperature presets (populated from VcpCodesFormatted for VCP 0x14)
|
||||
private ObservableCollection<ColorPresetItem> _availableColorPresets = new ObservableCollection<ColorPresetItem>();
|
||||
// Cached color temperature presets (computed from VcpCodesFormatted)
|
||||
private ObservableCollection<ColorPresetItem> _availableColorPresetsCache;
|
||||
|
||||
public MonitorInfo()
|
||||
{
|
||||
@@ -47,13 +47,12 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
CommunicationMethod = communicationMethod;
|
||||
}
|
||||
|
||||
public MonitorInfo(string name, string internalName, string hardwareId, string communicationMethod, string monitorType, int currentBrightness, int colorTemperature)
|
||||
public MonitorInfo(string name, string internalName, string hardwareId, string communicationMethod, int currentBrightness, int colorTemperature)
|
||||
{
|
||||
Name = name;
|
||||
InternalName = internalName;
|
||||
HardwareId = hardwareId;
|
||||
CommunicationMethod = communicationMethod;
|
||||
MonitorType = monitorType;
|
||||
CurrentBrightness = currentBrightness;
|
||||
ColorTemperature = colorTemperature;
|
||||
}
|
||||
@@ -114,20 +113,6 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
}
|
||||
}
|
||||
|
||||
[JsonPropertyName("monitorType")]
|
||||
public string MonitorType
|
||||
{
|
||||
get => _monitorType;
|
||||
set
|
||||
{
|
||||
if (_monitorType != value)
|
||||
{
|
||||
_monitorType = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[JsonPropertyName("currentBrightness")]
|
||||
public int CurrentBrightness
|
||||
{
|
||||
@@ -152,6 +137,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
{
|
||||
_colorTemperature = value;
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(ColorPresetsForDisplay)); // Update display list when current value changes
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -237,7 +223,9 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
if (_vcpCodesFormatted != value)
|
||||
{
|
||||
_vcpCodesFormatted = value ?? new List<VcpCodeDisplayInfo>();
|
||||
_availableColorPresetsCache = null; // Clear cache when VCP codes change
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(AvailableColorPresets));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -299,8 +287,11 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
if (_supportsColorTemperature != value)
|
||||
{
|
||||
_supportsColorTemperature = value;
|
||||
_availableColorPresetsCache = null; // Clear cache when support status changes
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(ColorTemperatureTooltip));
|
||||
OnPropertyChanged(nameof(AvailableColorPresets)); // Refresh computed property
|
||||
OnPropertyChanged(nameof(ColorPresetsForDisplay)); // Refresh display list
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -335,23 +326,175 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
}
|
||||
}
|
||||
|
||||
[JsonPropertyName("availableColorPresets")]
|
||||
/// <summary>
|
||||
/// Available color temperature presets computed from VcpCodesFormatted (VCP code 0x14).
|
||||
/// This is a computed property that parses the VCP capabilities data on-demand.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public ObservableCollection<ColorPresetItem> AvailableColorPresets
|
||||
{
|
||||
get => _availableColorPresets;
|
||||
set
|
||||
get
|
||||
{
|
||||
if (_availableColorPresets != value)
|
||||
Logger.LogInfo($"[MonitorInfo.AvailableColorPresets] GET called for monitor '{_name}'");
|
||||
|
||||
// Return cached value if available
|
||||
if (_availableColorPresetsCache != null)
|
||||
{
|
||||
_availableColorPresets = value ?? new ObservableCollection<ColorPresetItem>();
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(HasColorPresets));
|
||||
Logger.LogInfo($"[MonitorInfo.AvailableColorPresets] Cache HIT - returning {_availableColorPresetsCache.Count} items");
|
||||
return _availableColorPresetsCache;
|
||||
}
|
||||
|
||||
Logger.LogInfo("[MonitorInfo.AvailableColorPresets] Cache MISS - computing from VcpCodesFormatted");
|
||||
|
||||
// Compute from VcpCodesFormatted
|
||||
_availableColorPresetsCache = ComputeAvailableColorPresets();
|
||||
|
||||
Logger.LogInfo($"[MonitorInfo.AvailableColorPresets] Computed {_availableColorPresetsCache.Count} items");
|
||||
return _availableColorPresetsCache;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute available color presets from VcpCodesFormatted (VCP code 0x14)
|
||||
/// </summary>
|
||||
private ObservableCollection<ColorPresetItem> ComputeAvailableColorPresets()
|
||||
{
|
||||
Logger.LogInfo($"[ComputeAvailableColorPresets] START for monitor '{_name}'");
|
||||
Logger.LogInfo($" - SupportsColorTemperature: {_supportsColorTemperature}");
|
||||
Logger.LogInfo($" - VcpCodesFormatted: {(_vcpCodesFormatted == null ? "NULL" : $"{_vcpCodesFormatted.Count} items")}");
|
||||
|
||||
// Check if color temperature is supported
|
||||
if (!_supportsColorTemperature || _vcpCodesFormatted == null)
|
||||
{
|
||||
Logger.LogWarning($"[ComputeAvailableColorPresets] Color temperature not supported or no VCP codes - returning empty");
|
||||
return new ObservableCollection<ColorPresetItem>();
|
||||
}
|
||||
|
||||
// Find VCP code 0x14 (Color Temperature / Select Color Preset)
|
||||
var colorTempVcp = _vcpCodesFormatted.FirstOrDefault(v =>
|
||||
{
|
||||
if (int.TryParse(v.Code?.Replace("0x", string.Empty), System.Globalization.NumberStyles.HexNumber, null, out int code))
|
||||
{
|
||||
return code == 0x14;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
Logger.LogInfo($"[ComputeAvailableColorPresets] VCP 0x14 found: {colorTempVcp != null}");
|
||||
if (colorTempVcp != null)
|
||||
{
|
||||
Logger.LogInfo($" - ValueList: {(colorTempVcp.ValueList == null ? "NULL" : $"{colorTempVcp.ValueList.Count} items")}");
|
||||
}
|
||||
|
||||
// No VCP 0x14 or no values
|
||||
if (colorTempVcp == null || colorTempVcp.ValueList == null || colorTempVcp.ValueList.Count == 0)
|
||||
{
|
||||
Logger.LogWarning($"[ComputeAvailableColorPresets] No VCP 0x14 or empty ValueList - returning empty");
|
||||
return new ObservableCollection<ColorPresetItem>();
|
||||
}
|
||||
|
||||
// Build preset list from supported values
|
||||
var presetList = new List<ColorPresetItem>();
|
||||
foreach (var valueInfo in colorTempVcp.ValueList)
|
||||
{
|
||||
if (int.TryParse(valueInfo.Value?.Replace("0x", string.Empty), System.Globalization.NumberStyles.HexNumber, null, out int vcpValue))
|
||||
{
|
||||
var displayName = FormatColorTemperatureDisplayName(valueInfo.Name, vcpValue);
|
||||
presetList.Add(new ColorPresetItem(vcpValue, displayName));
|
||||
Logger.LogDebug($"[ComputeAvailableColorPresets] Added: {displayName}");
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by VCP value for consistent ordering
|
||||
presetList = presetList.OrderBy(p => p.VcpValue).ToList();
|
||||
|
||||
Logger.LogInfo($"[ComputeAvailableColorPresets] COMPLETE - returning {presetList.Count} items");
|
||||
Logger.LogInfo($"[ComputeAvailableColorPresets] Current ColorTemperature value: {_colorTemperature}");
|
||||
|
||||
return new ObservableCollection<ColorPresetItem>(presetList);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Format color temperature display name
|
||||
/// </summary>
|
||||
private string FormatColorTemperatureDisplayName(string name, int vcpValue)
|
||||
{
|
||||
var hexValue = $"0x{vcpValue:X2}";
|
||||
|
||||
// Check if name is undefined (null or empty)
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
return $"Manufacturer Defined ({hexValue})";
|
||||
}
|
||||
|
||||
// For predefined names, append the hex value in parentheses
|
||||
return $"{name} ({hexValue})";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Color presets for display in ComboBox, includes current value if not in preset list
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public bool HasColorPresets => _availableColorPresets != null && _availableColorPresets.Count > 0;
|
||||
public ObservableCollection<ColorPresetItem> ColorPresetsForDisplay
|
||||
{
|
||||
get
|
||||
{
|
||||
var presets = AvailableColorPresets;
|
||||
if (presets == null || presets.Count == 0)
|
||||
{
|
||||
return new ObservableCollection<ColorPresetItem>();
|
||||
}
|
||||
|
||||
// Check if current value is in the preset list
|
||||
var currentValueInList = presets.Any(p => p.VcpValue == _colorTemperature);
|
||||
|
||||
if (currentValueInList)
|
||||
{
|
||||
// Current value is in the list, return as-is
|
||||
return presets;
|
||||
}
|
||||
|
||||
// Current value is not in the preset list - add it at the beginning
|
||||
var displayList = new List<ColorPresetItem>();
|
||||
|
||||
// Add current value with "Custom" indicator
|
||||
var currentValueName = GetColorTemperatureName(_colorTemperature);
|
||||
var displayName = string.IsNullOrEmpty(currentValueName)
|
||||
? $"Custom (0x{_colorTemperature:X2})"
|
||||
: $"{currentValueName} (0x{_colorTemperature:X2}) - Custom";
|
||||
|
||||
displayList.Add(new ColorPresetItem(_colorTemperature, displayName));
|
||||
|
||||
// Add all supported presets
|
||||
displayList.AddRange(presets);
|
||||
|
||||
return new ObservableCollection<ColorPresetItem>(displayList);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the name for a color temperature value from standard VCP naming
|
||||
/// </summary>
|
||||
private string GetColorTemperatureName(int vcpValue)
|
||||
{
|
||||
return vcpValue switch
|
||||
{
|
||||
0x04 => "5000K",
|
||||
0x05 => "6500K",
|
||||
0x06 => "7500K",
|
||||
0x08 => "9300K",
|
||||
0x09 => "10000K",
|
||||
0x0A => "11500K",
|
||||
0x0B => "User 1",
|
||||
0x0C => "User 2",
|
||||
0x0D => "User 3",
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public bool HasColorPresets => AvailableColorPresets != null && AvailableColorPresets.Count > 0;
|
||||
|
||||
[JsonIgnore]
|
||||
public bool HasCapabilities => !string.IsNullOrEmpty(_capabilitiesRaw);
|
||||
@@ -407,12 +550,35 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the ColorTemperature property binding to force UI re-evaluation.
|
||||
/// Called after AvailableColorPresets is populated to sync ComboBox selection.
|
||||
/// Update this monitor's properties from another MonitorInfo instance.
|
||||
/// This preserves the object reference while updating all properties.
|
||||
/// </summary>
|
||||
public void RefreshColorTemperatureBinding()
|
||||
/// <param name="other">The source MonitorInfo to copy properties from</param>
|
||||
public void UpdateFrom(MonitorInfo other)
|
||||
{
|
||||
OnPropertyChanged(nameof(ColorTemperature));
|
||||
if (other == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Update all properties that can change
|
||||
Name = other.Name;
|
||||
InternalName = other.InternalName;
|
||||
HardwareId = other.HardwareId;
|
||||
CommunicationMethod = other.CommunicationMethod;
|
||||
CurrentBrightness = other.CurrentBrightness;
|
||||
ColorTemperature = other.ColorTemperature;
|
||||
IsHidden = other.IsHidden;
|
||||
EnableContrast = other.EnableContrast;
|
||||
EnableVolume = other.EnableVolume;
|
||||
CapabilitiesRaw = other.CapabilitiesRaw;
|
||||
VcpCodes = other.VcpCodes;
|
||||
VcpCodesFormatted = other.VcpCodesFormatted;
|
||||
SupportsBrightness = other.SupportsBrightness;
|
||||
SupportsContrast = other.SupportsContrast;
|
||||
SupportsColorTemperature = other.SupportsColorTemperature;
|
||||
SupportsVolume = other.SupportsVolume;
|
||||
CapabilitiesStatus = other.CapabilitiesStatus;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -24,9 +24,6 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
[JsonPropertyName("communicationMethod")]
|
||||
public string CommunicationMethod { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("monitorType")]
|
||||
public string MonitorType { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("currentBrightness")]
|
||||
public int CurrentBrightness { get; set; }
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
public PowerDisplayProperties()
|
||||
{
|
||||
ActivationShortcut = DefaultActivationShortcut;
|
||||
LaunchAtStartup = false;
|
||||
BrightnessUpdateRate = "1s";
|
||||
Monitors = new List<MonitorInfo>();
|
||||
RestoreSettingsOnStartup = true;
|
||||
@@ -28,9 +27,6 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
[JsonPropertyName("activation_shortcut")]
|
||||
public HotkeySettings ActivationShortcut { get; set; }
|
||||
|
||||
[JsonPropertyName("launch_at_startup")]
|
||||
public bool LaunchAtStartup { get; set; }
|
||||
|
||||
[JsonPropertyName("brightness_update_rate")]
|
||||
public string BrightnessUpdateRate { get; set; }
|
||||
|
||||
|
||||
@@ -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.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using SettingsUILibrary = Settings.UI.Library;
|
||||
using SettingsUILibraryHelpers = Settings.UI.Library.Helpers;
|
||||
@@ -134,6 +135,14 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
[JsonSerializable(typeof(MonitorInfo))]
|
||||
[JsonSerializable(typeof(MonitorInfoData))]
|
||||
[JsonSerializable(typeof(PowerDisplayActionMessage))]
|
||||
[JsonSerializable(typeof(PowerDisplayActionMessage.ActionData))]
|
||||
[JsonSerializable(typeof(PowerDisplayActionMessage.PowerDisplayAction))]
|
||||
[JsonSerializable(typeof(VcpCodeDisplayInfo))]
|
||||
[JsonSerializable(typeof(VcpValueInfo))]
|
||||
[JsonSerializable(typeof(List<string>))]
|
||||
[JsonSerializable(typeof(List<MonitorInfo>))]
|
||||
[JsonSerializable(typeof(List<VcpCodeDisplayInfo>))]
|
||||
[JsonSerializable(typeof(List<VcpValueInfo>))]
|
||||
[JsonSerializable(typeof(SettingsUILibraryHelpers.SearchLocation))]
|
||||
[JsonSerializable(typeof(SndLightSwitchSettings))]
|
||||
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
<tkcontrols:SettingsCard
|
||||
x:Uid="PowerDisplay_Enable_PowerDisplay"
|
||||
HeaderIcon="{ui:BitmapIcon Source=/Assets/Settings/Icons/PowerDisplay.png}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.IsPowerDisplayEnabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.IsEnabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
|
||||
<controls:SettingsGroup x:Uid="Shortcut" IsEnabled="{x:Bind ViewModel.IsPowerDisplayEnabled, Mode=OneWay}">
|
||||
<controls:SettingsGroup x:Uid="Shortcut" IsEnabled="{x:Bind ViewModel.IsEnabled, Mode=OneWay}">
|
||||
<tkcontrols:SettingsCard
|
||||
x:Uid="PowerDisplay_ActivationShortcut"
|
||||
HeaderIcon="{ui:FontIcon Glyph=}">
|
||||
@@ -29,7 +29,7 @@
|
||||
</tkcontrols:SettingsCard>
|
||||
</controls:SettingsGroup>
|
||||
|
||||
<controls:SettingsGroup x:Uid="PowerDisplay_Configuration_GroupSettings" IsEnabled="{x:Bind ViewModel.IsPowerDisplayEnabled, Mode=OneWay}">
|
||||
<controls:SettingsGroup x:Uid="PowerDisplay_Configuration_GroupSettings" IsEnabled="{x:Bind ViewModel.IsEnabled, Mode=OneWay}">
|
||||
<tkcontrols:SettingsCard
|
||||
x:Uid="PowerDisplay_LaunchButtonControl"
|
||||
ActionIcon="{ui:FontIcon Glyph=}"
|
||||
@@ -37,12 +37,6 @@
|
||||
HeaderIcon="{ui:FontIcon Glyph=}"
|
||||
IsClickEnabled="True" />
|
||||
|
||||
<tkcontrols:SettingsCard
|
||||
x:Uid="PowerDisplay_LaunchAtStartup"
|
||||
HeaderIcon="{ui:FontIcon Glyph=}">
|
||||
<ToggleSwitch x:Uid="PowerDisplay_LaunchAtStartup_ToggleSwitch" IsOn="{x:Bind ViewModel.IsLaunchAtStartupEnabled, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
|
||||
<tkcontrols:SettingsCard
|
||||
x:Uid="PowerDisplay_RestoreSettingsOnStartup"
|
||||
HeaderIcon="{ui:FontIcon Glyph=}">
|
||||
@@ -60,12 +54,12 @@
|
||||
|
||||
</controls:SettingsGroup>
|
||||
|
||||
<controls:SettingsGroup x:Uid="PowerDisplay_Monitors" IsEnabled="{x:Bind ViewModel.IsPowerDisplayEnabled, Mode=OneWay}">
|
||||
<controls:SettingsGroup x:Uid="PowerDisplay_Monitors" IsEnabled="{x:Bind ViewModel.IsEnabled, Mode=OneWay}">
|
||||
<!-- Empty state hint -->
|
||||
<InfoBar
|
||||
x:Uid="PowerDisplay_NoMonitorsDetected"
|
||||
IsClosable="False"
|
||||
IsOpen="{x:Bind ViewModel.IsPowerDisplayEnabled, Mode=OneWay}"
|
||||
IsOpen="{x:Bind ViewModel.IsEnabled, Mode=OneWay}"
|
||||
Visibility="{x:Bind ViewModel.HasMonitors, Mode=OneWay, Converter={StaticResource ReverseBoolToVisibilityConverter}}"
|
||||
Severity="Informational">
|
||||
<InfoBar.IconSource>
|
||||
@@ -110,15 +104,9 @@
|
||||
<tkcontrols:SettingsCard x:Uid="PowerDisplay_Monitor_InternalName">
|
||||
<TextBlock Text="{x:Bind InternalName, Mode=OneWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard x:Uid="PowerDisplay_Monitor_Type">
|
||||
<TextBlock Text="{x:Bind MonitorType, Mode=OneWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard x:Uid="PowerDisplay_Monitor_CommunicationMethod">
|
||||
<TextBlock Text="{x:Bind CommunicationMethod, Mode=OneWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard x:Uid="PowerDisplay_Monitor_Brightness">
|
||||
<TextBlock Text="{x:Bind CurrentBrightness, Mode=OneWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard
|
||||
x:Uid="PowerDisplay_Monitor_ColorTemperature"
|
||||
IsEnabled="{x:Bind SupportsColorTemperature, Mode=OneWay}">
|
||||
@@ -143,14 +131,14 @@
|
||||
<ComboBox
|
||||
x:Name="ColorTemperatureComboBox"
|
||||
MinWidth="200"
|
||||
ItemsSource="{x:Bind AvailableColorPresets, Mode=OneWay}"
|
||||
SelectedValue="{x:Bind ColorTemperature, Mode=OneWay}"
|
||||
ItemsSource="{Binding ColorPresetsForDisplay, Mode=OneWay}"
|
||||
SelectedValue="{Binding ColorTemperature, Mode=TwoWay}"
|
||||
SelectedValuePath="VcpValue"
|
||||
DisplayMemberPath="DisplayName"
|
||||
PlaceholderText="Not available"
|
||||
IsEnabled="{x:Bind SupportsColorTemperature, Mode=OneWay}"
|
||||
IsEnabled="{Binding SupportsColorTemperature, Mode=OneWay}"
|
||||
SelectionChanged="ColorTemperatureComboBox_SelectionChanged"
|
||||
Tag="{x:Bind}">
|
||||
Tag="{Binding}">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock Text="Changing this setting requires confirmation" />
|
||||
</ToolTipService.ToolTip>
|
||||
|
||||
@@ -135,8 +135,14 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
if (result == ContentDialogResult.Primary)
|
||||
{
|
||||
// User confirmed, apply the change
|
||||
// Setting the property will trigger save to settings file via OnPropertyChanged
|
||||
monitor.ColorTemperature = newValue.Value;
|
||||
_previousColorTemperatureValues[monitor.HardwareId] = newValue.Value;
|
||||
|
||||
// Trigger custom action to apply color temperature to hardware
|
||||
// This is separate from the settings save to avoid unwanted hardware updates
|
||||
// when other settings (like RestoreSettingsOnStartup) change
|
||||
ViewModel.TriggerApplyColorTemperature();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -26,8 +26,10 @@ using PowerToys.Interop;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
{
|
||||
public partial class PowerDisplayViewModel : Observable
|
||||
public partial class PowerDisplayViewModel : PageViewModelBase
|
||||
{
|
||||
protected override string ModuleName => PowerDisplaySettings.ModuleName;
|
||||
|
||||
private GeneralSettings GeneralSettingsConfig { get; set; }
|
||||
|
||||
private ISettingsUtils SettingsUtils { get; set; }
|
||||
@@ -49,10 +51,13 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
// Initialize monitors collection using property setter for proper subscription setup
|
||||
// Parse capabilities for each loaded monitor to ensure UI displays correctly
|
||||
var loadedMonitors = _settings.Properties.Monitors;
|
||||
|
||||
Logger.LogInfo($"[Constructor] Initializing with {loadedMonitors.Count} monitors from settings");
|
||||
|
||||
foreach (var monitor in loadedMonitors)
|
||||
{
|
||||
// Parse capabilities to determine feature support
|
||||
ParseFeatureSupportFromCapabilities(monitor);
|
||||
PopulateColorPresetsForMonitor(monitor);
|
||||
}
|
||||
|
||||
Monitors = new ObservableCollection<MonitorInfo>(loadedMonitors);
|
||||
@@ -72,18 +77,18 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
private void InitializeEnabledValue()
|
||||
{
|
||||
_isPowerDisplayEnabled = GeneralSettingsConfig.Enabled.PowerDisplay;
|
||||
_isEnabled = GeneralSettingsConfig.Enabled.PowerDisplay;
|
||||
}
|
||||
|
||||
public bool IsPowerDisplayEnabled
|
||||
public bool IsEnabled
|
||||
{
|
||||
get => _isPowerDisplayEnabled;
|
||||
get => _isEnabled;
|
||||
set
|
||||
{
|
||||
if (_isPowerDisplayEnabled != value)
|
||||
if (_isEnabled != value)
|
||||
{
|
||||
_isPowerDisplayEnabled = value;
|
||||
OnPropertyChanged(nameof(IsPowerDisplayEnabled));
|
||||
_isEnabled = value;
|
||||
OnPropertyChanged(nameof(IsEnabled));
|
||||
|
||||
GeneralSettingsConfig.Enabled.PowerDisplay = value;
|
||||
OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(GeneralSettingsConfig);
|
||||
@@ -92,12 +97,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsLaunchAtStartupEnabled
|
||||
{
|
||||
get => _settings.Properties.LaunchAtStartup;
|
||||
set => SetSettingsProperty(_settings.Properties.LaunchAtStartup, value, v => _settings.Properties.LaunchAtStartup = v);
|
||||
}
|
||||
|
||||
public bool RestoreSettingsOnStartup
|
||||
{
|
||||
get => _settings.Properties.RestoreSettingsOnStartup;
|
||||
@@ -194,9 +193,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
// Parse capabilities to determine feature support
|
||||
ParseFeatureSupportFromCapabilities(newMonitor);
|
||||
|
||||
// Populate color temperature presets if supported
|
||||
PopulateColorPresetsForMonitor(newMonitor);
|
||||
|
||||
// Check if we have an existing monitor with the same key
|
||||
if (existingMonitors.TryGetValue(monitorKey, out var existingMonitor))
|
||||
{
|
||||
@@ -276,88 +272,8 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
monitor.SupportsVolume = vcpCodeInts.Contains(0x62);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populate color temperature presets for a monitor from VcpCodesFormatted
|
||||
/// Builds the ComboBox items from VCP code 0x14 supported values
|
||||
/// </summary>
|
||||
private void PopulateColorPresetsForMonitor(MonitorInfo monitor)
|
||||
{
|
||||
if (monitor == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!monitor.SupportsColorTemperature)
|
||||
{
|
||||
// Create new empty collection to trigger property change notification
|
||||
monitor.AvailableColorPresets = new ObservableCollection<MonitorInfo.ColorPresetItem>();
|
||||
return;
|
||||
}
|
||||
|
||||
// Find VCP code 0x14 in the formatted list
|
||||
var colorTempVcp = monitor.VcpCodesFormatted?.FirstOrDefault(v =>
|
||||
{
|
||||
if (int.TryParse(v.Code?.Replace("0x", string.Empty), System.Globalization.NumberStyles.HexNumber, null, out int code))
|
||||
{
|
||||
return code == 0x14;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if (colorTempVcp == null || colorTempVcp.ValueList == null || colorTempVcp.ValueList.Count == 0)
|
||||
{
|
||||
// No supported values found, create new empty collection
|
||||
monitor.AvailableColorPresets = new ObservableCollection<MonitorInfo.ColorPresetItem>();
|
||||
return;
|
||||
}
|
||||
|
||||
// Build preset list from supported values
|
||||
var presetList = new List<MonitorInfo.ColorPresetItem>();
|
||||
foreach (var valueInfo in colorTempVcp.ValueList)
|
||||
{
|
||||
if (int.TryParse(valueInfo.Value?.Replace("0x", string.Empty), System.Globalization.NumberStyles.HexNumber, null, out int vcpValue))
|
||||
{
|
||||
// Format display name for Settings UI
|
||||
var displayName = FormatColorTemperatureDisplayName(valueInfo.Name, vcpValue);
|
||||
presetList.Add(new MonitorInfo.ColorPresetItem(vcpValue, displayName));
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by VCP value for consistent ordering
|
||||
presetList = presetList.OrderBy(p => p.VcpValue).ToList();
|
||||
|
||||
// Create new collection and assign it
|
||||
monitor.AvailableColorPresets = new ObservableCollection<MonitorInfo.ColorPresetItem>(presetList);
|
||||
|
||||
// Refresh ColorTemperature binding to force ComboBox to re-evaluate SelectedValue
|
||||
// and match it against the newly populated AvailableColorPresets
|
||||
monitor.RefreshColorTemperatureBinding();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Format color temperature display name for Settings UI
|
||||
/// Examples:
|
||||
/// - Undefined values: "Manufacturer Defined (0x05)"
|
||||
/// - Predefined values: "6500K (0x05)", "sRGB (0x01)"
|
||||
/// </summary>
|
||||
private string FormatColorTemperatureDisplayName(string name, int vcpValue)
|
||||
{
|
||||
var hexValue = $"0x{vcpValue:X2}";
|
||||
|
||||
// Check if name is undefined (null or empty)
|
||||
// GetName now returns null for unknown values instead of hex string
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
return $"Manufacturer Defined ({hexValue})";
|
||||
}
|
||||
|
||||
// For predefined names, append the hex value in parentheses
|
||||
// Examples: "6500K (0x05)", "sRGB (0x01)"
|
||||
return $"{name} ({hexValue})";
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA1816:Dispose methods should call SuppressFinalize", Justification = "Base class PageViewModelBase.Dispose() handles GC.SuppressFinalize")]
|
||||
public override void Dispose()
|
||||
{
|
||||
// Unsubscribe from monitor property changes
|
||||
UnsubscribeFromItemPropertyChanged(_monitors);
|
||||
@@ -367,6 +283,8 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
{
|
||||
_monitors.CollectionChanged -= Monitors_CollectionChanged;
|
||||
}
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -431,6 +349,27 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
SendConfigMSG(JsonSerializer.Serialize(actionMessage));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trigger PowerDisplay.exe to apply color temperature from settings file
|
||||
/// Called after user confirms color temperature change in Settings UI
|
||||
/// </summary>
|
||||
public void TriggerApplyColorTemperature()
|
||||
{
|
||||
var actionMessage = new PowerDisplayActionMessage
|
||||
{
|
||||
Action = new PowerDisplayActionMessage.ActionData
|
||||
{
|
||||
PowerDisplay = new PowerDisplayActionMessage.PowerDisplayAction
|
||||
{
|
||||
ActionName = "ApplyColorTemperature",
|
||||
Value = string.Empty,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
SendConfigMSG(JsonSerializer.Serialize(actionMessage));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reload monitor list from settings file (called when PowerDisplay.exe signals monitor changes)
|
||||
/// </summary>
|
||||
@@ -444,16 +383,52 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
var updatedSettings = SettingsUtils.GetSettingsOrDefault<PowerDisplaySettings>(PowerDisplaySettings.ModuleName);
|
||||
var updatedMonitors = updatedSettings.Properties.Monitors;
|
||||
|
||||
Logger.LogInfo($"[ReloadMonitors] Loaded {updatedMonitors.Count} monitors from settings");
|
||||
|
||||
// Parse capabilities for each monitor
|
||||
foreach (var monitor in updatedMonitors)
|
||||
{
|
||||
ParseFeatureSupportFromCapabilities(monitor);
|
||||
PopulateColorPresetsForMonitor(monitor);
|
||||
}
|
||||
|
||||
// Update the monitors collection
|
||||
// This will trigger UI update through property change notification
|
||||
Monitors = new ObservableCollection<MonitorInfo>(updatedMonitors);
|
||||
// Update existing MonitorInfo objects instead of replacing the collection
|
||||
// This preserves XAML x:Bind bindings which reference specific object instances
|
||||
if (Monitors == null)
|
||||
{
|
||||
// First time initialization - create new collection
|
||||
Monitors = new ObservableCollection<MonitorInfo>(updatedMonitors);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a dictionary for quick lookup by InternalName
|
||||
var updatedMonitorsDict = updatedMonitors.ToDictionary(m => m.InternalName, m => m);
|
||||
|
||||
// Update existing monitors or remove ones that no longer exist
|
||||
for (int i = Monitors.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var existingMonitor = Monitors[i];
|
||||
if (updatedMonitorsDict.TryGetValue(existingMonitor.InternalName, out var updatedMonitor))
|
||||
{
|
||||
// Monitor still exists - update its properties in place
|
||||
Logger.LogInfo($"[ReloadMonitors] Updating existing monitor: {existingMonitor.InternalName}");
|
||||
existingMonitor.UpdateFrom(updatedMonitor);
|
||||
updatedMonitorsDict.Remove(existingMonitor.InternalName);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Monitor no longer exists - remove from collection
|
||||
Logger.LogInfo($"[ReloadMonitors] Removing monitor: {existingMonitor.InternalName}");
|
||||
Monitors.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Add any new monitors that weren't in the existing collection
|
||||
foreach (var newMonitor in updatedMonitorsDict.Values)
|
||||
{
|
||||
Logger.LogInfo($"[ReloadMonitors] Adding new monitor: {newMonitor.InternalName}");
|
||||
Monitors.Add(newMonitor);
|
||||
}
|
||||
}
|
||||
|
||||
// Update internal settings reference
|
||||
_settings.Properties.Monitors = updatedMonitors;
|
||||
@@ -468,7 +443,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
private Func<string, int> SendConfigMSG { get; }
|
||||
|
||||
private bool _isPowerDisplayEnabled;
|
||||
private bool _isEnabled;
|
||||
private PowerDisplaySettings _settings;
|
||||
private ObservableCollection<MonitorInfo> _monitors;
|
||||
private bool _hasMonitors;
|
||||
@@ -476,7 +451,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
public void RefreshEnabledState()
|
||||
{
|
||||
InitializeEnabledValue();
|
||||
OnPropertyChanged(nameof(IsPowerDisplayEnabled));
|
||||
OnPropertyChanged(nameof(IsEnabled));
|
||||
}
|
||||
|
||||
private bool SetSettingsProperty<T>(T currentValue, T newValue, Action<T> setter, [CallerMemberName] string propertyName = null)
|
||||
|
||||
Reference in New Issue
Block a user