// 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.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;
using Microsoft.PowerToys.Settings.UI.Helpers;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using PowerDisplay.Common.Models;
using PowerDisplay.Common.Utils;
using CustomVcpValueMapping = Microsoft.PowerToys.Settings.UI.Library.CustomVcpValueMapping;
namespace Microsoft.PowerToys.Settings.UI.Views
{
///
/// Dialog for creating/editing custom VCP value name mappings
///
public sealed partial class CustomVcpMappingEditorDialog : ContentDialog, INotifyPropertyChanged
{
///
/// Special value to indicate "Custom value" option in the ComboBox
///
private const int CustomValueMarker = -1;
///
/// Represents a selectable VCP value item in the Value ComboBox
///
public class VcpValueItem
{
public int Value { get; set; }
public string DisplayName { get; set; } = string.Empty;
public bool IsCustomOption => Value == CustomValueMarker;
}
///
/// Represents a selectable monitor item in the Monitor ComboBox
///
public class MonitorItem
{
public string Id { get; set; } = string.Empty;
public string DisplayName { get; set; } = string.Empty;
}
private readonly IEnumerable? _monitors;
private ObservableCollection _availableValues = new();
private ObservableCollection _availableMonitors = new();
private byte _selectedVcpCode;
private int _selectedValue;
private string _customName = string.Empty;
private bool _canSave;
private bool _showCustomValueInput;
private bool _showMonitorSelector;
private int _customValueParsed;
private bool _applyToAll = true;
private string _selectedMonitorId = string.Empty;
private string _selectedMonitorName = string.Empty;
public CustomVcpMappingEditorDialog(IEnumerable? monitors)
{
_monitors = monitors;
this.InitializeComponent();
// Set localized strings for ContentDialog
var resourceLoader = ResourceLoaderInstance.ResourceLoader;
Title = resourceLoader.GetString("PowerDisplay_CustomMappingEditor_Title");
PrimaryButtonText = resourceLoader.GetString("PowerDisplay_Dialog_Save");
CloseButtonText = resourceLoader.GetString("PowerDisplay_Dialog_Cancel");
// Set VCP code ComboBox items content dynamically using localized names
VcpCodeItem_0x14.Content = GetFormattedVcpCodeName(resourceLoader, 0x14);
VcpCodeItem_0x60.Content = GetFormattedVcpCodeName(resourceLoader, 0x60);
// Populate monitor list
PopulateMonitorList();
// Default to Color Temperature (0x14)
VcpCodeComboBox.SelectedIndex = 0;
}
///
/// Gets the result mapping after dialog closes with Primary button
///
public CustomVcpValueMapping? ResultMapping { get; private set; }
///
/// Gets the available values for the selected VCP code
///
public ObservableCollection AvailableValues
{
get => _availableValues;
private set
{
_availableValues = value;
OnPropertyChanged();
}
}
///
/// Gets the available monitors for selection
///
public ObservableCollection AvailableMonitors
{
get => _availableMonitors;
private set
{
_availableMonitors = value;
OnPropertyChanged();
}
}
///
/// Gets a value indicating whether the dialog can be saved
///
public bool CanSave
{
get => _canSave;
private set
{
if (_canSave != value)
{
_canSave = value;
OnPropertyChanged();
}
}
}
///
/// Gets a value indicating whether to show the custom value input TextBox
///
public Visibility ShowCustomValueInput => _showCustomValueInput ? Visibility.Visible : Visibility.Collapsed;
///
/// Gets a value indicating whether to show the monitor selector ComboBox
///
public Visibility ShowMonitorSelector => _showMonitorSelector ? Visibility.Visible : Visibility.Collapsed;
private void SetShowCustomValueInput(bool value)
{
if (_showCustomValueInput != value)
{
_showCustomValueInput = value;
OnPropertyChanged(nameof(ShowCustomValueInput));
}
}
private void SetShowMonitorSelector(bool value)
{
if (_showMonitorSelector != value)
{
_showMonitorSelector = value;
OnPropertyChanged(nameof(ShowMonitorSelector));
}
}
private void PopulateMonitorList()
{
AvailableMonitors = new ObservableCollection(
_monitors?.Select(m => new MonitorItem { Id = m.Id, DisplayName = m.DisplayName })
?? Enumerable.Empty());
if (AvailableMonitors.Count > 0)
{
MonitorComboBox.SelectedIndex = 0;
}
}
///
/// Pre-fill the dialog with existing mapping data for editing
///
public void PreFillMapping(CustomVcpValueMapping mapping)
{
if (mapping is null)
{
return;
}
// Select the VCP code
VcpCodeComboBox.SelectedIndex = mapping.VcpCode == 0x14 ? 0 : 1;
// Populate values for the selected VCP code
PopulateValuesForVcpCode(mapping.VcpCode);
// Try to select the value in the ComboBox
var matchingItem = AvailableValues.FirstOrDefault(v => !v.IsCustomOption && v.Value == mapping.Value);
if (matchingItem is not null)
{
ValueComboBox.SelectedItem = matchingItem;
}
else
{
// Value not found in list, select "Custom value" option and fill the TextBox
ValueComboBox.SelectedItem = AvailableValues.FirstOrDefault(v => v.IsCustomOption);
CustomValueTextBox.Text = $"0x{mapping.Value:X2}";
_customValueParsed = mapping.Value;
}
// Set the custom name
CustomNameTextBox.Text = mapping.CustomName;
_customName = mapping.CustomName;
// Set apply scope
_applyToAll = mapping.ApplyToAll;
ApplyToAllToggle.IsOn = mapping.ApplyToAll;
SetShowMonitorSelector(!mapping.ApplyToAll);
// Select the target monitor if not applying to all
if (!mapping.ApplyToAll && !string.IsNullOrEmpty(mapping.TargetMonitorId))
{
var targetMonitor = AvailableMonitors.FirstOrDefault(m => m.Id == mapping.TargetMonitorId);
if (targetMonitor is not null)
{
MonitorComboBox.SelectedItem = targetMonitor;
_selectedMonitorId = targetMonitor.Id;
_selectedMonitorName = targetMonitor.DisplayName;
}
}
UpdateCanSave();
}
private void VcpCodeComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (VcpCodeComboBox.SelectedItem is ComboBoxItem selectedItem &&
selectedItem.Tag is string tagValue &&
byte.TryParse(tagValue, out byte vcpCode))
{
_selectedVcpCode = vcpCode;
PopulateValuesForVcpCode(vcpCode);
UpdateCanSave();
}
}
private void PopulateValuesForVcpCode(byte vcpCode)
{
var values = new ObservableCollection();
var seenValues = new HashSet();
// Collect values from all monitors
if (_monitors is not null)
{
foreach (var monitor in _monitors)
{
if (monitor.VcpCodesFormatted is null)
{
continue;
}
// Find the VCP code entry
var vcpEntry = monitor.VcpCodesFormatted.FirstOrDefault(v =>
!string.IsNullOrEmpty(v.Code) &&
TryParseHexCode(v.Code, out int code) &&
code == vcpCode);
if (vcpEntry?.ValueList is null)
{
continue;
}
// Add each value from this monitor
foreach (var valueInfo in vcpEntry.ValueList)
{
if (TryParseHexCode(valueInfo.Value, out int vcpValue) && !seenValues.Contains(vcpValue))
{
seenValues.Add(vcpValue);
var displayName = !string.IsNullOrEmpty(valueInfo.Name)
? $"{valueInfo.Name} (0x{vcpValue:X2})"
: VcpNames.GetFormattedValueName(vcpCode, vcpValue);
values.Add(new VcpValueItem
{
Value = vcpValue,
DisplayName = displayName,
});
}
}
}
}
// If no values found from monitors, fall back to built-in values from VcpNames
if (values.Count == 0)
{
var builtInValues = VcpNames.GetValueMappings(vcpCode);
if (builtInValues is not null)
{
foreach (var kvp in builtInValues)
{
values.Add(new VcpValueItem
{
Value = kvp.Key,
DisplayName = $"{kvp.Value} (0x{kvp.Key:X2})",
});
}
}
}
// Sort by value
var sortedValues = new ObservableCollection(values.OrderBy(v => v.Value));
// Add "Custom value" option at the end
var resourceLoader = ResourceLoaderInstance.ResourceLoader;
sortedValues.Add(new VcpValueItem
{
Value = CustomValueMarker,
DisplayName = resourceLoader.GetString("PowerDisplay_CustomMappingEditor_CustomValueOption"),
});
AvailableValues = sortedValues;
// Select first item if available
if (sortedValues.Count > 0)
{
ValueComboBox.SelectedIndex = 0;
}
}
private static bool TryParseHexCode(string? hex, out int result)
{
result = 0;
if (string.IsNullOrEmpty(hex))
{
return false;
}
var cleanHex = hex.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ? hex[2..] : hex;
return int.TryParse(cleanHex, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out result);
}
private static string GetFormattedVcpCodeName(Windows.ApplicationModel.Resources.ResourceLoader resourceLoader, byte vcpCode)
{
var resourceKey = $"PowerDisplay_VcpCode_Name_0x{vcpCode:X2}";
var localizedName = resourceLoader.GetString(resourceKey);
var name = string.IsNullOrEmpty(localizedName) ? VcpNames.GetCodeName(vcpCode) : localizedName;
return $"{name} (0x{vcpCode:X2})";
}
private void ValueComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (ValueComboBox.SelectedItem is VcpValueItem selectedItem)
{
SetShowCustomValueInput(selectedItem.IsCustomOption);
_selectedValue = selectedItem.IsCustomOption ? 0 : selectedItem.Value;
UpdateCanSave();
}
}
private void CustomValueTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
_customValueParsed = TryParseHexCode(CustomValueTextBox.Text?.Trim(), out int parsed) ? parsed : 0;
UpdateCanSave();
}
private void CustomNameTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
_customName = CustomNameTextBox.Text?.Trim() ?? string.Empty;
UpdateCanSave();
}
private void ApplyToAllToggle_Toggled(object sender, RoutedEventArgs e)
{
_applyToAll = ApplyToAllToggle.IsOn;
SetShowMonitorSelector(!_applyToAll);
UpdateCanSave();
}
private void MonitorComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (MonitorComboBox.SelectedItem is MonitorItem selectedMonitor)
{
_selectedMonitorId = selectedMonitor.Id;
_selectedMonitorName = selectedMonitor.DisplayName;
UpdateCanSave();
}
}
private void UpdateCanSave()
{
var hasValidValue = _showCustomValueInput
? _customValueParsed > 0
: ValueComboBox.SelectedItem is VcpValueItem item && !item.IsCustomOption;
CanSave = _selectedVcpCode > 0 &&
hasValidValue &&
!string.IsNullOrWhiteSpace(_customName) &&
(_applyToAll || !string.IsNullOrEmpty(_selectedMonitorId));
}
private void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
if (CanSave)
{
int finalValue = _showCustomValueInput ? _customValueParsed : _selectedValue;
ResultMapping = new CustomVcpValueMapping
{
VcpCode = _selectedVcpCode,
Value = finalValue,
CustomName = _customName,
ApplyToAll = _applyToAll,
TargetMonitorId = _applyToAll ? string.Empty : _selectedMonitorId,
TargetMonitorName = _applyToAll ? string.Empty : _selectedMonitorName,
};
}
}
public event PropertyChangedEventHandler? PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}