[Settings] Decouple Settings.UI.Library from PowerDisplay.Lib to fix … (#46325)

<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Fixes PowerToys Run crash (`FileNotFoundException` for
`PowerDisplay.Lib.dll`) caused by `Settings.UI.Library` having a
transitive dependency on `PowerDisplay.Lib`.

`SettingsSerializationContext` registered PowerDisplay profile types
(`PowerDisplayProfile`, `PowerDisplayProfiles`, `ProfileMonitorSetting`)
via `[JsonSerializable]` attributes, which forced the CLR to load
`PowerDisplay.Lib.dll` at startup. PowerToys Run depends on
`Settings.UI.Library` but does not ship `PowerDisplay.Lib.dll`, causing
the crash.



<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #46261
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

Co-authored-by: Yu Leng (from Dev Box) <yuleng@microsoft.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
moooyo
2026-03-24 15:06:07 +08:00
committed by GitHub
parent 86115a54f6
commit 0d41d45a64
10 changed files with 187 additions and 20 deletions

View File

@@ -0,0 +1,89 @@
// 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.
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text.Json.Serialization;
#nullable enable
namespace Microsoft.PowerToys.Settings.UI.Library
{
/// <summary>
/// Represents a color temperature preset item for VCP code 0x14.
/// Used to display available color temperature presets in UI components.
/// This is a local copy maintained in Settings.UI.Library to avoid a dependency on PowerDisplay.Lib.
/// </summary>
public partial class ColorPresetItem : INotifyPropertyChanged
{
private int _vcpValue;
private string _displayName = string.Empty;
/// <summary>
/// Initializes a new instance of the <see cref="ColorPresetItem"/> class.
/// </summary>
public ColorPresetItem()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ColorPresetItem"/> class.
/// </summary>
/// <param name="vcpValue">The VCP value for the color temperature preset.</param>
/// <param name="displayName">The display name for UI.</param>
public ColorPresetItem(int vcpValue, string displayName)
{
_vcpValue = vcpValue;
_displayName = displayName;
}
/// <summary>
/// Occurs when a property value changes.
/// </summary>
public event PropertyChangedEventHandler? PropertyChanged;
/// <summary>
/// Gets or sets the VCP value for this color temperature preset.
/// </summary>
[JsonPropertyName("vcpValue")]
public int VcpValue
{
get => _vcpValue;
set
{
if (_vcpValue != value)
{
_vcpValue = value;
OnPropertyChanged();
}
}
}
/// <summary>
/// Gets or sets the display name for UI.
/// </summary>
[JsonPropertyName("displayName")]
public string DisplayName
{
get => _displayName;
set
{
if (_displayName != value)
{
_displayName = value;
OnPropertyChanged();
}
}
}
/// <summary>
/// Raises the PropertyChanged event.
/// </summary>
/// <param name="propertyName">The name of the property that changed.</param>
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

View File

@@ -0,0 +1,85 @@
// 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.
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Library
{
/// <summary>
/// Represents a custom name mapping for a VCP code value.
/// Used to override the default VCP value names with user-defined names.
/// This is a local copy maintained in Settings.UI.Library to avoid a dependency on PowerDisplay.Lib.
/// </summary>
public class CustomVcpValueMapping
{
/// <summary>
/// Gets or sets the VCP code (e.g., 0x14 for color temperature, 0x60 for input source).
/// </summary>
[JsonPropertyName("vcpCode")]
public byte VcpCode { get; set; }
/// <summary>
/// Gets or sets the VCP value to map (e.g., 0x11 for HDMI-1).
/// </summary>
[JsonPropertyName("value")]
public int Value { get; set; }
/// <summary>
/// Gets or sets the custom name to display instead of the default name.
/// </summary>
[JsonPropertyName("customName")]
public string CustomName { get; set; } = string.Empty;
/// <summary>
/// Gets or sets a value indicating whether this mapping applies to all monitors.
/// When true, the mapping is applied globally. When false, only applies to TargetMonitorId.
/// </summary>
[JsonPropertyName("applyToAll")]
public bool ApplyToAll { get; set; } = true;
/// <summary>
/// Gets or sets the target monitor ID when ApplyToAll is false.
/// This is the monitor's unique identifier.
/// </summary>
[JsonPropertyName("targetMonitorId")]
public string TargetMonitorId { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the target monitor display name (for UI display only, not serialized).
/// </summary>
[JsonIgnore]
public string TargetMonitorName { get; set; } = string.Empty;
/// <summary>
/// Gets the display name for the VCP code (for UI display).
/// </summary>
[JsonIgnore]
public string VcpCodeDisplayName => $"VCP 0x{VcpCode:X2}";
/// <summary>
/// Gets the display name for the VCP value.
/// </summary>
[JsonIgnore]
public string ValueDisplayName => $"0x{Value:X2}";
/// <summary>
/// Gets a summary string for display in the UI list.
/// Format: "0xVV → CustomName" or "0xVV → CustomName (MonitorName)"
/// </summary>
[JsonIgnore]
public string DisplaySummary
{
get
{
var baseSummary = $"0x{Value:X2} \u2192 {CustomName}";
if (!ApplyToAll && !string.IsNullOrEmpty(TargetMonitorName))
{
return $"{baseSummary} ({TargetMonitorName})";
}
return baseSummary;
}
}
}
}

View File

@@ -8,14 +8,13 @@ using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Microsoft.PowerToys.Settings.UI.Library.Helpers; using Microsoft.PowerToys.Settings.UI.Library.Helpers;
using PowerDisplay.Common.Drivers;
using PowerDisplay.Common.Models;
using PowerDisplay.Common.Utils;
namespace Microsoft.PowerToys.Settings.UI.Library namespace Microsoft.PowerToys.Settings.UI.Library
{ {
public class MonitorInfo : Observable public class MonitorInfo : Observable
{ {
private const byte VcpCodeSelectColorPreset = 0x14;
private string _name = string.Empty; private string _name = string.Empty;
private string _id = string.Empty; private string _id = string.Empty;
private string _communicationMethod = string.Empty; private string _communicationMethod = string.Empty;
@@ -532,7 +531,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
System.Globalization.NumberStyles.HexNumber, System.Globalization.NumberStyles.HexNumber,
System.Globalization.CultureInfo.InvariantCulture, System.Globalization.CultureInfo.InvariantCulture,
out int code) && out int code) &&
code == NativeConstants.VcpCodeSelectColorPreset); code == VcpCodeSelectColorPreset);
// No VCP 0x14 or no values // No VCP 0x14 or no values
if (colorTempVcp == null || colorTempVcp.ValueList == null || colorTempVcp.ValueList.Count == 0) if (colorTempVcp == null || colorTempVcp.ValueList == null || colorTempVcp.ValueList.Count == 0)
@@ -556,9 +555,10 @@ namespace Microsoft.PowerToys.Settings.UI.Library
}) })
.Where(x => x.VcpValue > 0); .Where(x => x.VcpValue > 0);
// Use shared helper to compute presets, then convert to nested type for XAML compatibility // Compute presets inline (avoiding dependency on PowerDisplay.Lib's ColorTemperatureHelper)
var basePresets = ColorTemperatureHelper.ComputeColorPresets(colorTempValues); var presetList = colorTempValues
var presetList = basePresets.Select(p => new ColorPresetItem(p.VcpValue, p.DisplayName)); .Select(item => new ColorPresetItem(item.VcpValue, !string.IsNullOrEmpty(item.Name) ? item.Name : "Manufacturer Defined"))
.OrderBy(p => p.VcpValue);
return new ObservableCollection<ColorPresetItem>(presetList); return new ObservableCollection<ColorPresetItem>(presetList);
} }
@@ -598,8 +598,8 @@ namespace Microsoft.PowerToys.Settings.UI.Library
// Current value is not in the preset list - add it at the beginning // Current value is not in the preset list - add it at the beginning
var displayList = new List<ColorPresetItem>(); var displayList = new List<ColorPresetItem>();
// Add current value with "Custom" indicator using shared helper // Add current value with "Custom" indicator
var displayName = ColorTemperatureHelper.FormatCustomColorTemperatureDisplayName(_colorTemperatureVcp); var displayName = $"Custom (0x{_colorTemperatureVcp:X2})";
displayList.Add(new ColorPresetItem(_colorTemperatureVcp, displayName)); displayList.Add(new ColorPresetItem(_colorTemperatureVcp, displayName));
// Add all supported presets // Add all supported presets

View File

@@ -4,7 +4,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using PowerDisplay.Common.Models;
using Settings.UI.Library.Attributes; using Settings.UI.Library.Attributes;
namespace Microsoft.PowerToys.Settings.UI.Library namespace Microsoft.PowerToys.Settings.UI.Library

View File

@@ -23,7 +23,6 @@
<ProjectReference Include="..\..\common\ManagedCommon\ManagedCommon.csproj" /> <ProjectReference Include="..\..\common\ManagedCommon\ManagedCommon.csproj" />
<ProjectReference Include="..\..\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" /> <ProjectReference Include="..\..\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" />
<ProjectReference Include="..\..\modules\MouseUtils\MouseJump.Common\MouseJump.Common.csproj" /> <ProjectReference Include="..\..\modules\MouseUtils\MouseJump.Common\MouseJump.Common.csproj" />
<ProjectReference Include="..\..\modules\powerdisplay\PowerDisplay.Lib\PowerDisplay.Lib.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -4,7 +4,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using PowerDisplay.Common.Models;
using SettingsUILibrary = Settings.UI.Library; using SettingsUILibrary = Settings.UI.Library;
using SettingsUILibraryHelpers = Settings.UI.Library.Helpers; using SettingsUILibraryHelpers = Settings.UI.Library.Helpers;
@@ -158,13 +157,6 @@ namespace Microsoft.PowerToys.Settings.UI.Library
[JsonSerializable(typeof(PasteAIProviderDefinition))] [JsonSerializable(typeof(PasteAIProviderDefinition))]
[JsonSerializable(typeof(System.Collections.ObjectModel.ObservableCollection<PasteAIProviderDefinition>))] [JsonSerializable(typeof(System.Collections.ObjectModel.ObservableCollection<PasteAIProviderDefinition>))]
// PowerDisplay Profile Types (for AOT compatibility)
[JsonSerializable(typeof(PowerDisplayProfile))]
[JsonSerializable(typeof(List<PowerDisplayProfile>))]
[JsonSerializable(typeof(PowerDisplayProfiles))]
[JsonSerializable(typeof(ProfileMonitorSetting))]
[JsonSerializable(typeof(List<ProfileMonitorSetting>))]
// IPC Send Message Wrapper Classes (Snd*) // IPC Send Message Wrapper Classes (Snd*)
[JsonSerializable(typeof(SndAwakeSettings))] [JsonSerializable(typeof(SndAwakeSettings))]
[JsonSerializable(typeof(SndCursorWrapSettings))] [JsonSerializable(typeof(SndCursorWrapSettings))]

View File

@@ -17,6 +17,7 @@ using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
using PowerDisplay.Common.Models; using PowerDisplay.Common.Models;
using PowerDisplay.Common.Utils; using PowerDisplay.Common.Utils;
using CustomVcpValueMapping = Microsoft.PowerToys.Settings.UI.Library.CustomVcpValueMapping;
namespace Microsoft.PowerToys.Settings.UI.Views namespace Microsoft.PowerToys.Settings.UI.Views
{ {

View File

@@ -71,7 +71,7 @@
IsExpanded="{x:Bind ViewModel.HasCustomVcpMappings, Mode=OneWay}" IsExpanded="{x:Bind ViewModel.HasCustomVcpMappings, Mode=OneWay}"
ItemsSource="{x:Bind ViewModel.CustomVcpMappings, Mode=OneWay}"> ItemsSource="{x:Bind ViewModel.CustomVcpMappings, Mode=OneWay}">
<tkcontrols:SettingsExpander.ItemTemplate> <tkcontrols:SettingsExpander.ItemTemplate>
<DataTemplate x:DataType="pdmodels:CustomVcpValueMapping"> <DataTemplate x:DataType="library:CustomVcpValueMapping">
<tkcontrols:SettingsCard Description="{x:Bind VcpCodeDisplayName}" Header="{x:Bind DisplaySummary}"> <tkcontrols:SettingsCard Description="{x:Bind VcpCodeDisplayName}" Header="{x:Bind DisplaySummary}">
<StackPanel Orientation="Horizontal" Spacing="8"> <StackPanel Orientation="Horizontal" Spacing="8">
<Button <Button

View File

@@ -15,6 +15,7 @@ using Microsoft.UI.Xaml.Controls;
using PowerDisplay.Common.Models; using PowerDisplay.Common.Models;
using PowerDisplay.Common.Utils; using PowerDisplay.Common.Utils;
using Windows.ApplicationModel.DataTransfer; using Windows.ApplicationModel.DataTransfer;
using CustomVcpValueMapping = Microsoft.PowerToys.Settings.UI.Library.CustomVcpValueMapping;
namespace Microsoft.PowerToys.Settings.UI.Views namespace Microsoft.PowerToys.Settings.UI.Views
{ {

View File

@@ -21,6 +21,7 @@ using PowerDisplay.Common.Models;
using PowerDisplay.Common.Services; using PowerDisplay.Common.Services;
using PowerDisplay.Common.Utils; using PowerDisplay.Common.Utils;
using PowerToys.Interop; using PowerToys.Interop;
using CustomVcpValueMapping = Microsoft.PowerToys.Settings.UI.Library.CustomVcpValueMapping;
namespace Microsoft.PowerToys.Settings.UI.ViewModels namespace Microsoft.PowerToys.Settings.UI.ViewModels
{ {