Make MPO tweak a three-state control (#4897)

* refactor: make MPO tweak a three-state control

* docs: explain MPO tweak states

* docs: address code review comments

* refactor: make Multiplane Overlay config-driven
This commit is contained in:
Omar
2026-08-09 14:02:42 -05:00
committed by GitHub
parent 7f18b4fd60
commit bc607b6c91
12 changed files with 457 additions and 17 deletions
@@ -0,0 +1,36 @@
function Get-WinUtilRegistryComboState {
<#
.SYNOPSIS
Finds the configured combo-box state matching the current registry values.
.PARAMETER Registry
Registry settings containing a value mapping for each supported state.
.OUTPUTS
The name of the matching state.
#>
param(
[Parameter(Mandatory)]
$Registry
)
foreach ($state in $Registry[0].Values.PSObject.Properties) {
$stateMatches = $true
foreach ($setting in @($Registry)) {
$currentValue = Get-WinUtilRegistryComboValue -Setting $setting
$actualValue = if ($currentValue.Exists -and $null -ne $currentValue.Value) { $currentValue.Value } else { $setting.DefaultValue }
$configuredValue = $setting.Values.PSObject.Properties[$state.Name].Value
# Removal represents the effective Windows default when matching the current state.
$expectedValue = if ($configuredValue -eq "<RemoveEntry>") { $setting.DefaultValue } else { $configuredValue }
if ([string]$actualValue -ne [string]$expectedValue) {
$stateMatches = $false
break
}
}
if ($stateMatches) {
return $state.Name
}
}
throw "Registry values do not match a supported state."
}
@@ -0,0 +1,24 @@
function Get-WinUtilRegistryComboValue {
<#
.SYNOPSIS
Reads one registry value for a registry-backed combo-box state.
.PARAMETER Setting
The registry setting from the combo-box configuration.
#>
param(
[Parameter(Mandatory)]
$Setting
)
try {
$item = Get-ItemProperty -Path $Setting.Path -Name $Setting.Name -ErrorAction Stop
$property = $item.PSObject.Properties[$Setting.Name]
return [pscustomobject]@{ Exists = $null -ne $property; Value = $property.Value }
} catch [System.Management.Automation.PSArgumentException] {
# The registry provider uses PSArgumentException when a named value is absent.
return [pscustomobject]@{ Exists = $false; Value = $null }
} catch [System.Management.Automation.ItemNotFoundException] {
return [pscustomobject]@{ Exists = $false; Value = $null }
}
}
@@ -61,7 +61,7 @@ Function Invoke-WinUtilCurrentSystem {
$serviceKeys = $entry.service
$entryType = $entry.Type
if ($registryKeys -or $serviceKeys) {
if (($registryKeys -or $serviceKeys) -and $entryType -ne "Combobox") {
$Values = @()
if ($entryType -eq "Toggle") {
+1 -1
View File
@@ -62,7 +62,7 @@ function Invoke-WinUtilTweaks {
}
}
if ($sync.configs.tweaks.$CheckBox.registry) {
$sync.configs.tweaks.$CheckBox.registry | ForEach-Object {
$sync.configs.tweaks.$CheckBox.registry | Where-Object { -not $psitem.Values } | ForEach-Object {
Set-WinUtilRegistry -Name $psitem.Name -Path $psitem.Path -Type $psitem.Type -Value $psitem.$($values.registry)
}
}
@@ -0,0 +1,78 @@
function Set-WinUtilRegistryComboState {
<#
.SYNOPSIS
Applies and verifies a config-defined registry combo-box state.
.PARAMETER Registry
Registry settings containing a value mapping for each supported state.
.PARAMETER State
The state name to apply.
#>
param(
[Parameter(Mandatory)]
$Registry,
[Parameter(Mandatory)]
[string]$State
)
if ($Registry[0].Values.PSObject.Properties.Name -notcontains $State) {
throw "Unknown registry state '$State'."
}
# Preserve exact prior values so a partial update can be rolled back.
$previousValues = foreach ($setting in @($Registry)) {
$currentValue = Get-WinUtilRegistryComboValue -Setting $setting
[pscustomobject]@{ Setting = $setting; Exists = $currentValue.Exists; Value = $currentValue.Value }
}
try {
foreach ($setting in @($Registry)) {
$configuredValue = $setting.Values.PSObject.Properties[$State].Value
$previousValue = $previousValues | Where-Object Setting -EQ $setting
if ($configuredValue -ne "<RemoveEntry>" -or $previousValue.Exists) {
Set-WinUtilRegistry -Name $setting.Name -Path $setting.Path -Type $setting.Type -Value $configuredValue
}
}
# Set-WinUtilRegistry reports write errors without throwing, so verify each result explicitly.
foreach ($setting in @($Registry)) {
$configuredValue = $setting.Values.PSObject.Properties[$State].Value
$currentValue = Get-WinUtilRegistryComboValue -Setting $setting
$writeMatches = if ($configuredValue -eq "<RemoveEntry>") {
-not $currentValue.Exists
} else {
$currentValue.Exists -and [string]$currentValue.Value -eq [string]$configuredValue
}
if (-not $writeMatches) {
throw "The registry values did not match the requested state."
}
}
} catch {
$applyError = $_.Exception.Message
if ([string]::IsNullOrWhiteSpace($applyError)) {
$applyError = "The registry values did not match the requested state."
}
$rollbackFailed = $false
foreach ($previousValue in $previousValues) {
try {
$currentValue = Get-WinUtilRegistryComboValue -Setting $previousValue.Setting
if ($previousValue.Exists -or $currentValue.Exists) {
$rollbackValue = if ($previousValue.Exists) { $previousValue.Value } else { "<RemoveEntry>" }
Set-WinUtilRegistry -Name $previousValue.Setting.Name -Path $previousValue.Setting.Path -Type $previousValue.Setting.Type -Value $rollbackValue
}
$restoredValue = Get-WinUtilRegistryComboValue -Setting $previousValue.Setting
if ($restoredValue.Exists -ne $previousValue.Exists -or ($restoredValue.Exists -and [string]$restoredValue.Value -ne [string]$previousValue.Value)) {
$rollbackFailed = $true
}
} catch {
$rollbackFailed = $true
}
}
if ($rollbackFailed) {
throw "Unable to apply registry state '$State': $applyError. The previous registry state could not be restored."
}
throw "Unable to apply registry state '$State': $applyError"
}
}
+83 -4
View File
@@ -76,6 +76,8 @@ function Invoke-WPFUIElements {
Description = $entryInfo.description
Type = $entryInfo.type
ComboItems = $entryInfo.ComboItems
ComboDescriptions = $entryInfo.ComboDescriptions
Registry = $entryInfo.registry
Checked = $entryInfo.Checked
ButtonWidth = $entryInfo.ButtonWidth
GroupName = $entryInfo.GroupName # Added for RadioButton groupings
@@ -247,6 +249,7 @@ function Invoke-WPFUIElements {
$label = New-Object Windows.Controls.Label
$label.Content = $entryInfo.Content
$label.HorizontalAlignment = "Left"
$label.ToolTip = $entryInfo.Description
$label.VerticalAlignment = "Center"
$label.SetResourceReference([Windows.Controls.Control]::FontSizeProperty, "ButtonFontSize")
$label.UseLayoutRounding = $true
@@ -261,11 +264,27 @@ function Invoke-WPFUIElements {
$comboBox.SetResourceReference([Windows.Controls.Control]::MarginProperty, "ButtonMargin")
$comboBox.SetResourceReference([Windows.Controls.Control]::FontSizeProperty, "ButtonFontSize")
$comboBox.UseLayoutRounding = $true
$comboBox.Tag = [pscustomobject]@{
Registry = $entryInfo.Registry
State = $null
}
[System.Windows.Automation.AutomationProperties]::SetName($comboBox, $entryInfo.Content)
foreach ($comboitem in ($entryInfo.ComboItems -split " ")) {
$comboItems = if ($entryInfo.ComboItems -is [string]) {
$entryInfo.ComboItems -split " "
} else {
@($entryInfo.ComboItems)
}
foreach ($comboitem in $comboItems) {
$comboBoxItem = New-Object Windows.Controls.ComboBoxItem
$comboBoxItem.Content = $comboitem
if ($entryInfo.ComboDescriptions) {
$comboDescription = $entryInfo.ComboDescriptions.PSObject.Properties[$comboitem].Value
if ($comboDescription) {
$comboBoxItem.ToolTip = $comboDescription
}
}
$comboBoxItem.SetResourceReference([Windows.Controls.Control]::FontSizeProperty, "ButtonFontSize")
$comboBoxItem.UseLayoutRounding = $true
$comboBox.Items.Add($comboBoxItem) | Out-Null
@@ -274,22 +293,82 @@ function Invoke-WPFUIElements {
$horizontalStackPanel.Children.Add($comboBox) | Out-Null
$itemsControl.Items.Add($horizontalStackPanel) | Out-Null
$comboBox.SelectedIndex = 0
if ($entryInfo.Registry -and @($entryInfo.Registry)[0].Values) {
try {
$comboBox.Tag.State = Get-WinUtilRegistryComboState -Registry $entryInfo.Registry
$comboBox.SelectedIndex = @($comboBox.Items.Content).IndexOf([string]$comboBox.Tag.State)
} catch {
$unknownStateItem = New-Object Windows.Controls.ComboBoxItem
$unknownStateItem.Content = "Custom / Unknown - select a state"
$unknownStateItem.IsEnabled = $false
$unknownStateItem.ToolTip = "$($_.Exception.Message) Select one of the supported states to replace these values."
$comboBox.Items.Add($unknownStateItem) | Out-Null
$comboBox.SelectedItem = $unknownStateItem
$comboBox.ToolTip = $unknownStateItem.ToolTip
}
} else {
$comboBox.SelectedIndex = 0
}
# Set initial text
if ($comboBox.Items.Count -gt 0) {
$comboBox.Text = $comboBox.Items[0].Content
$comboBox.Text = $comboBox.SelectedItem.Content
}
$sync[$entryInfo.Name] = $comboBox
# Add SelectionChanged event handler to update the text property
$comboBox.Add_SelectionChanged({
$selectedItem = $this.SelectedItem
if ($selectedItem) {
$this.Text = $selectedItem.Content
$registry = $this.Tag.Registry
if ($registry -and $selectedItem.IsEnabled -and $selectedItem.Content -ne $this.Tag.State) {
try {
Set-WinUtilRegistryComboState -Registry $registry -State $selectedItem.Content
$this.Tag.State = $selectedItem.Content
$this.ToolTip = $null
$unknownStateItem = @($this.Items) | Where-Object Content -EQ "Custom / Unknown - select a state" | Select-Object -First 1
if ($unknownStateItem) {
$this.Items.Remove($unknownStateItem)
}
} catch {
$applyError = $_.Exception.Message
if ([string]::IsNullOrWhiteSpace($applyError)) {
$applyError = "Unable to apply registry state '$($selectedItem.Content)'."
}
$previousState = if ($this.Tag.State) { $this.Tag.State } else { "Custom / Unknown - select a state" }
$this.SelectedItem = @($this.Items) | Where-Object Content -EQ $previousState | Select-Object -First 1
[System.Windows.MessageBox]::Show(
$applyError,
"WinUtil",
[System.Windows.MessageBoxButton]::OK,
[System.Windows.MessageBoxImage]::Warning
) | Out-Null
}
}
}
})
$sync[$entryInfo.Name] = $comboBox
if ($entryInfo.Registry -and @($entryInfo.Registry)[0].Values -and $entryInfo.Link) {
$textBlock = New-Object Windows.Controls.TextBlock
$textBlock.Name = $comboBox.Name + "Link"
$textBlock.Text = "(?)"
$textBlock.ToolTip = $entryInfo.Link
$textBlock.Style = $HoverTextBlockStyle
$textBlock.UseLayoutRounding = $true
$textBlock.VerticalAlignment = "Center"
$textBlock.SetResourceReference([Windows.Controls.Control]::FontSizeProperty, "FontSize")
$textBlock.Tag = $comboBox
$textBlock.Add_MouseUp({
[System.Object]$Sender = $args[0]
Start-Process $Sender.ToolTip -ErrorAction Stop
})
$horizontalStackPanel.Children.Add($textBlock) | Out-Null
$sync[$textBlock.Name] = $textBlock
}
}
"Button" {