Decoupled UI and data (#4051)

* Decoupled UI and data

* Fix bad variable naming

* Fix mistype

* Fix mistype v2

Editing from mobile is hard
This commit is contained in:
KamaleiZestri
2026-02-17 12:39:44 -06:00
committed by GitHub
parent 0e41122d89
commit 70a94abe02
14 changed files with 276 additions and 189 deletions

View File

@@ -1,92 +0,0 @@
Function Get-WinUtilCheckBoxes {
<#
.SYNOPSIS
Finds all checkboxes that are checked on the specific tab and inputs them into a script.
.PARAMETER unCheck
Whether to uncheck the checkboxes that are checked. Defaults to true
.OUTPUTS
A List containing the name of each checked checkbox
.EXAMPLE
Get-WinUtilCheckBoxes "WPFInstall"
#>
Param(
[boolean]$unCheck = $false
)
$Output = @{
Install = @()
WPFTweaks = @()
WPFFeature = @()
WPFInstall = @()
WPFToggle = @()
}
$CheckBoxes = $sync.GetEnumerator() | Where-Object { $_.Value -is [System.Windows.Controls.CheckBox] }
# Collect toggle switch states
foreach ($CheckBox in $CheckBoxes) {
if ($CheckBox.Key -like "WPFToggle*" -and $CheckBox.Value.IsChecked -eq $true) {
$Output["WPFToggle"] += $CheckBox.Key
Write-Debug "Adding toggle: $($CheckBox.Key)"
}
}
# First check and add WPFTweaksRestorePoint if checked
$RestorePoint = $CheckBoxes | Where-Object { $_.Key -eq 'WPFTweaksRestorePoint' -and $_.Value.IsChecked -eq $true }
if ($RestorePoint) {
$Output["WPFTweaks"] = @('WPFTweaksRestorePoint')
Write-Debug "Adding WPFTweaksRestorePoint as first in WPFTweaks"
if ($unCheck) {
$RestorePoint.Value.IsChecked = $false
}
}
foreach ($CheckBox in $CheckBoxes) {
if ($CheckBox.Key -eq 'WPFTweaksRestorePoint') { continue } # Skip since it's already handled
$group = if ($CheckBox.Key.StartsWith("WPFInstall")) { "Install" }
elseif ($CheckBox.Key.StartsWith("WPFTweaks")) { "WPFTweaks" }
elseif ($CheckBox.Key.StartsWith("WPFFeature")) { "WPFFeature" }
if ($group) {
if ($CheckBox.Value.IsChecked -eq $true) {
$feature = switch ($group) {
"Install" {
# Get the winget value
[PsCustomObject]@{
winget="$($sync.configs.applications.$($CheckBox.Name).winget)";
choco="$($sync.configs.applications.$($CheckBox.Name).choco)";
}
}
default {
$CheckBox.Name
}
}
if (-not $Output.ContainsKey($group)) {
$Output[$group] = @()
}
if ($group -eq "Install") {
$Output["WPFInstall"] += $CheckBox.Name
Write-Debug "Adding: $($CheckBox.Name) under: WPFInstall"
}
Write-Debug "Adding: $($feature) under: $($group)"
$Output[$group] += $feature
if ($unCheck) {
$CheckBox.Value.IsChecked = $false
}
}
}
}
return $Output
}

View File

@@ -44,13 +44,13 @@ function Initialize-InstallAppEntry {
$checkBox.Name = $appKey
$checkbox.Style = $sync.Form.Resources.AppEntryCheckboxStyle
$checkbox.Add_Checked({
Invoke-WPFSelectedAppsUpdate -type "Add" -checkbox $this
Invoke-WPFSelectedCheckboxesUpdate -type "Add" -checkboxName $this.Parent.Tag
$borderElement = $this.Parent
$borderElement.SetResourceReference([Windows.Controls.Control]::BackgroundProperty, "AppInstallSelectedColor")
})
$checkbox.Add_Unchecked({
Invoke-WPFSelectedAppsUpdate -type "Remove" -checkbox $this
Invoke-WPFSelectedCheckboxesUpdate -type "Remove" -checkboxName $this.Parent.Tag
$borderElement = $this.Parent
$borderElement.SetResourceReference([Windows.Controls.Control]::BackgroundProperty, "AppInstallUnselectedColor")
})

View File

@@ -6,7 +6,7 @@ Function Invoke-WinUtilCurrentSystem {
Checks to see what tweaks have already been applied and what programs are installed, and checks the according boxes
.EXAMPLE
Get-WinUtilCheckBoxes "WPFInstall"
InvokeWinUtilCurrentSystem -Checkbox "winget"
#>

View File

@@ -0,0 +1,75 @@
function Reset-WPFCheckBoxes {
<#
.SYNOPSIS
Set winutil checkboxs to match $sync.selected values.
Should only need to be run if $sync.selected updated outside of UI (i.e. presets or import)
.PARAMETER doToggles
Whether or not to set UI toggles. WARNING: they will trigger if altered
.PARAMETER checkboxfilterpattern
The Pattern to use when filtering through CheckBoxes, defaults to "**"
Used to make reset blazingly fast.
#>
param (
[Parameter(position=0)]
[bool]$doToggles = $false,
[Parameter(position=1)]
[string]$checkboxfilterpattern = "**"
)
$CheckBoxesToCheck = $sync.selectedApps + $sync.selectedTweaks + $sync.selectedFeatures
$CheckBoxes = ($sync.GetEnumerator()).where{ $_.Value -is [System.Windows.Controls.CheckBox] -and $_.Name -notlike "WPFToggle*" -and $_.Name -like "$checkboxfilterpattern"}
Write-Debug "Getting checkboxes to set, number of checkboxes: $($CheckBoxes.Count)"
if ($CheckBoxesToCheck -ne "") {
$debugMsg = "CheckBoxes to Check are: "
$CheckBoxesToCheck | ForEach-Object { $debugMsg += "$_, " }
$debugMsg = $debugMsg -replace (',\s*$', '')
Write-Debug "$debugMsg"
}
foreach ($CheckBox in $CheckBoxes) {
$checkboxName = $CheckBox.Key
if (-not $CheckBoxesToCheck) {
$sync.$checkBoxName.IsChecked = $false
continue
}
# Check if the checkbox name exists in the flattened JSON hashtable
if ($CheckBoxesToCheck -contains $checkboxName) {
# If it exists, set IsChecked to true
$sync.$checkboxName.IsChecked = $true
Write-Debug "$checkboxName is checked"
} else {
# If it doesn't exist, set IsChecked to false
$sync.$checkboxName.IsChecked = $false
Write-Debug "$checkboxName is not checked"
}
}
# Update Installs tab UI values
$count = $sync.SelectedApps.Count
$sync.WPFselectedAppsButton.Content = "Selected Apps: $count"
# On every change, remove all entries inside the Popup Menu. This is done, so we can keep the alphabetical order even if elements are selected in a random way
$sync.selectedAppsstackPanel.Children.Clear()
$sync.selectedApps | Foreach-Object { Add-SelectedAppsMenuItem -name $($sync.configs.applicationsHashtable.$_.Content) -key $_ }
if($doToggles) {
# Restore toggle switch states
$importedToggles = $sync.selectedToggles
$allToggles = $sync.GetEnumerator() | Where-Object { $_.Key -like "WPFToggle*" -and $_.Value -is [System.Windows.Controls.CheckBox] }
foreach ($toggle in $allToggles) {
if ($importedToggles -contains $toggle.Key) {
$sync[$toggle.Key].IsChecked = $true
Write-Debug "Restoring toggle: $($toggle.Key) = checked"
} else {
$sync[$toggle.Key].IsChecked = $false
Write-Debug "Restoring toggle: $($toggle.Key) = unchecked"
}
}
}
}

View File

@@ -0,0 +1,59 @@
function Update-WinUtilSelections {
<#
.SYNOPSIS
Updates the $sync.selected variables with a given preset.
.PARAMETER flatJson
The flattened json list of $sync values to select.
#>
param (
$flatJson
)
Write-Debug "JSON to import: $($flatJson)"
foreach ($cbkey in $flatJson) {
$group = if ($cbkey.StartsWith("WPFInstall")) { "Install" }
elseif ($cbkey.StartsWith("WPFTweaks")) { "Tweaks" }
elseif ($cbkey.StartsWith("WPFToggle")) { "Toggle" }
elseif ($cbkey.StartsWith("WPFFeature")) { "Feature" }
else { "na" }
switch ($group) {
"Install" {
if (!$sync.selectedApps.Contains($cbkey)) {
$sync.selectedApps.Add($cbkey)
# The List type needs to be specified again, because otherwise Sort-Object will convert the list to a string if there is only a single entry
[System.Collections.Generic.List[pscustomobject]]$sync.selectedApps = $sync.SelectedApps | Sort-Object
}
}
"Tweaks" {
if (!$sync.selectedTweaks.Contains($cbkey)) {
$sync.selectedTweaks.Add($cbkey)
}
}
"Toggle" {
if (!$sync.selectedToggles.Contains($cbkey)) {
$sync.selectedToggles.Add($cbkey)
}
}
"Feature" {
if (!$sync.selectedFeatures.Contains($cbkey)) {
$sync.selectedFeatures.Add($cbkey)
}
}
default {
Write-Host "Unknown group for checkbox: $($cbkey)"
}
}
}
Write-Debug "-------------------------------------"
Write-Debug "Selected Apps: $($sync.selectedApps)"
Write-Debug "Selected Tweaks: $($sync.selectedTweaks)"
Write-Debug "Selected Toggles: $($sync.selectedToggles)"
Write-Debug "Selected Features: $($sync.selectedFeatures)"
Write-Debug "--------------------------------------"
}

View File

@@ -12,7 +12,7 @@ function Invoke-WPFFeatureInstall {
return
}
$Features = (Get-WinUtilCheckBoxes)["WPFFeature"]
$Features = $sync.selectedFeatures
Invoke-WPFRunspace -ArgumentList $Features -DebugPreference $DebugPreference -ScriptBlock {
param($Features, $DebugPreference)

View File

@@ -44,7 +44,8 @@ function Invoke-WPFImpex {
try {
$Config = ConfigDialog
if ($Config) {
$jsonFile = Get-WinUtilCheckBoxes -unCheck $false | ConvertTo-Json
$allConfs = $sync.selectedApps + $sync.selectedTweaks + $sync.selectedToggles + $sync.selectedFeatures
$jsonFile = $allConfs | ConvertTo-Json
$jsonFile | Out-File $Config -Force
"iex ""& { `$(irm https://christitus.com/win) } -Config '$Config'""" | Set-Clipboard
}
@@ -66,21 +67,12 @@ function Invoke-WPFImpex {
Write-Error "Failed to load the JSON file from the specified path or URL: $_"
return
}
$flattenedJson = $jsonFile.PSObject.Properties.Where({ $_.Name -ne "Install" -and $_.Name -ne "WPFToggle" }).ForEach({ $_.Value })
Invoke-WPFPresets -preset $flattenedJson -imported $true
# Restore toggle switch states
$importedToggles = if ($jsonFile.WPFToggle) { $jsonFile.WPFToggle } else { @() }
$allToggles = $sync.GetEnumerator() | Where-Object { $_.Key -like "WPFToggle*" -and $_.Value -is [System.Windows.Controls.CheckBox] }
foreach ($toggle in $allToggles) {
if ($importedToggles -contains $toggle.Key) {
$sync[$toggle.Key].IsChecked = $true
Write-Debug "Restoring toggle: $($toggle.Key) = checked"
} else {
$sync[$toggle.Key].IsChecked = $false
Write-Debug "Restoring toggle: $($toggle.Key) = unchecked"
}
}
# TODO how to handle old style? detected json type then flatten it in a func?
# $flattenedJson = $jsonFile.PSObject.Properties.Where({ $_.Name -ne "Install" }).ForEach({ $_.Value })
$flattenedJson = $jsonFile
Update-WinUtilSelections -flatJson $flattenedJson
# TODO test with toggles
Reset-WPFCheckBoxes -doToggles $true
}
} catch {
Write-Error "An error occurred while importing: $_"

View File

@@ -2,10 +2,10 @@ function Invoke-WPFPresets {
<#
.SYNOPSIS
Sets the options in the tweaks panel to the given preset
Sets the checkboxes in winutil to the given preset
.PARAMETER preset
The preset to set the options to
The preset to set the checkboxes to
.PARAMETER imported
If the preset is imported from a file, defaults to false
@@ -17,7 +17,7 @@ function Invoke-WPFPresets {
param (
[Parameter(position=0)]
[Array]$preset = "",
[Array]$preset = $null,
[Parameter(position=1)]
[bool]$imported = $false,
@@ -32,33 +32,19 @@ function Invoke-WPFPresets {
$CheckBoxesToCheck = $sync.configs.preset.$preset
}
$CheckBoxes = ($sync.GetEnumerator()).where{ $_.Value -is [System.Windows.Controls.CheckBox] -and $_.Name -notlike "WPFToggle*" -and $_.Name -like "$checkboxfilterpattern"}
Write-Debug "Getting checkboxes to set, number of checkboxes: $($CheckBoxes.Count)"
if ($CheckBoxesToCheck -ne "") {
$debugMsg = "CheckBoxes to Check are: "
$CheckBoxesToCheck | ForEach-Object { $debugMsg += "$_, " }
$debugMsg = $debugMsg -replace (',\s*$', '')
Write-Debug "$debugMsg"
}
foreach ($CheckBox in $CheckBoxes) {
$checkboxName = $CheckBox.Key
if (-not $CheckBoxesToCheck) {
$sync.$checkboxName.IsChecked = $false
continue
}
# Check if the checkbox name exists in the flattened JSON hashtable
if ($CheckBoxesToCheck -contains $checkboxName) {
# If it exists, set IsChecked to true
$sync.$checkboxName.IsChecked = $true
Write-Debug "$checkboxName is checked"
} else {
# If it doesn't exist, set IsChecked to false
$sync.$checkboxName.IsChecked = $false
Write-Debug "$checkboxName is not checked"
# clear out the filtered pattern
if (!$preset) {
switch ($checkboxfilterpattern) {
"WPFTweak*" { $sync.selectedTweaks = [System.Collections.Generic.List[string]]::new() }
"WPFInstall*" { $sync.selectedApps = [System.Collections.Generic.List[string]]::new() }
"WPFeatures" { $sync.selectedFeatures = [System.Collections.Generic.List[string]]::new() }
"WPFToggle" { $sync.selectedToggles = [System.Collections.Generic.List[string]]::new() }
default {}
}
}
else {
Update-WinUtilSelections -flatJson $CheckBoxesToCheck
}
Reset-WPFCheckBoxes -doToggles $false -checkboxfilterpattern $checkboxfilterpattern
}

View File

@@ -1,43 +0,0 @@
function Invoke-WPFSelectedAppsUpdate {
<#
.SYNOPSIS
This is a helper function that is called by the Checked and Unchecked events of the Checkboxes on the install tab.
It Updates the "Selected Apps" selectedAppLabel on the Install Tab to represent the current collection
.PARAMETER type
Eigther: Add | Remove
.PARAMETER checkbox
should contain the current instance of the checkbox that triggered the Event.
Most of the time will be the automatic variable $this
.EXAMPLE
$checkbox.Add_Unchecked({Invoke-WPFSelectedAppsUpdate -type "Remove" -checkbox $this})
OR
Invoke-WPFSelectedAppsUpdate -type "Add" -checkbox $specificCheckbox
#>
param (
$type,
$checkbox
)
$selectedAppsButton = $sync.WPFselectedAppsButton
# Get the actual Name from the selectedAppLabel inside the Checkbox
$appKey = $checkbox.Parent.Tag
if ($type -eq "Add") {
$sync.selectedApps.Add($appKey)
# The List type needs to be specified again, because otherwise Sort-Object will convert the list to a string if there is only a single entry
[System.Collections.Generic.List[pscustomobject]]$sync.selectedApps = $sync.SelectedApps | Sort-Object
}
elseif ($type -eq "Remove") {
$sync.SelectedApps.Remove($appKey)
}
else{
Write-Error "Type: $type not implemented"
}
$count = $sync.SelectedApps.Count
$selectedAppsButton.Content = "Selected Apps: $count"
# On every change, remove all entries inside the Popup Menu. This is done, so we can keep the alphabetical order even if elements are selected in a random way
$sync.selectedAppsstackPanel.Children.Clear()
$sync.SelectedApps | Foreach-Object { Add-SelectedAppsMenuItem -name $($sync.configs.applicationsHashtable.$_.Content) -key $_ }
}

View File

@@ -0,0 +1,95 @@
function Invoke-WPFSelectedCheckboxesUpdate{
<#
.SYNOPSIS
This is a helper function that is called by the Checked and Unchecked events of the Checkboxes.
It also Updates the "Selected Apps" selectedAppLabel on the Install Tab to represent the current collection
.PARAMETER type
Either: Add | Remove
.PARAMETER checkboxName
should contain the name of the current instance of the checkbox that triggered the Event.
Most of the time will be the automatic variable $this.Parent.Tag
.EXAMPLE
$checkbox.Add_Unchecked({Invoke-WPFSelectedCheckboxesUpdate -type "Remove" -checkboxName $this.Parent.Tag})
OR
Invoke-WPFSelectedCheckboxesUpdate -type "Add" -checkboxName $specificCheckbox.Parent.Tag
#>
param (
$type,
$checkboxName
)
if (($type -ne "Add") -and ($type -ne "Remove"))
{
Write-Error "Type: $type not implemented"
return
}
# Get the actual Name from the selectedAppLabel inside the Checkbox
$appKey = $checkboxName
$group = if ($appKey.StartsWith("WPFInstall")) { "Install" }
elseif ($appKey.StartsWith("WPFTweaks")) { "Tweaks" }
elseif ($appKey.StartsWith("WPFToggle")) { "Toggle" }
elseif ($appKey.StartsWith("WPFFeature")) { "Feature" }
else { "na" }
switch ($group) {
"Install" {
if ($type -eq "Add") {
if (!$sync.selectedApps.Contains($appKey)) {
$sync.selectedApps.Add($appKey)
# The List type needs to be specified again, because otherwise Sort-Object will convert the list to a string if there is only a single entry
[System.Collections.Generic.List[pscustomobject]]$sync.selectedApps = $sync.SelectedApps | Sort-Object
}
}
else{
$sync.selectedApps.Remove($appKey)
}
$count = $sync.SelectedApps.Count
$sync.WPFselectedAppsButton.Content = "Selected Apps: $count"
# On every change, remove all entries inside the Popup Menu. This is done, so we can keep the alphabetical order even if elements are selected in a random way
$sync.selectedAppsstackPanel.Children.Clear()
$sync.selectedApps | Foreach-Object { Add-SelectedAppsMenuItem -name $($sync.configs.applicationsHashtable.$_.Content) -key $_ }
}
"Tweaks" {
if ($type -eq "Add") {
if (!$sync.selectedTweaks.Contains($appKey)) {
$sync.selectedTweaks.Add($appKey)
}
}
else{
$sync.selectedTweaks.Remove($appKey)
}
}
"Toggle" {
if ($type -eq "Add") {
if (!$sync.selectedToggles.Contains($appKey)) {
$sync.selectedToggles.Add($appKey)
}
}
else{
$sync.selectedToggles.Remove($appKey)
}
}
"Feature" {
if ($type -eq "Add") {
if (!$sync.selectedFeatures.Contains($appKey)) {
$sync.selectedFeatures.Add($appKey)
}
}
else{
$sync.selectedFeatures.Remove($appKey)
}
}
default {
Write-Host "Unknown group for checkbox: $($appKey)"
}
}
Write-Debug "-------------------------------------"
Write-Debug "Selected Apps: $($sync.selectedApps)"
Write-Debug "Selected Tweaks: $($sync.selectedTweaks)"
Write-Debug "Selected Toggles: $($sync.selectedToggles)"
Write-Debug "Selected Features: $($sync.selectedFeatures)"
Write-Debug "--------------------------------------"
}

View File

@@ -181,12 +181,14 @@ function Invoke-WPFUIElements {
$sync[$entryInfo.Name].Add_Checked({
[System.Object]$Sender = $args[0]
Invoke-WinUtilTweaks $sender.name
Invoke-WPFSelectedCheckboxesUpdate -type "Add" -checkboxName $Sender.name
Invoke-WinUtilTweaks $Sender.name
})
$sync[$entryInfo.Name].Add_Unchecked({
[System.Object]$Sender = $args[0]
Invoke-WinUtiltweaks $sender.name -undo $true
Invoke-WPFSelectedCheckboxesUpdate -type "Remove" -checkboxName $Sender.name
Invoke-WinUtiltweaks $Sender.name -undo $true
})
}
@@ -355,6 +357,16 @@ function Invoke-WPFUIElements {
$itemsControl.Items.Add($horizontalStackPanel) | Out-Null
$sync[$entryInfo.Name] = $checkBox
$sync[$entryInfo.Name].Add_Checked({
[System.Object]$Sender = $args[0]
Invoke-WPFSelectedCheckboxesUpdate -type "Add" -checkboxName $Sender.name
})
$sync[$entryInfo.Name].Add_Unchecked({
[System.Object]$Sender = $args[0]
Invoke-WPFSelectedCheckboxesUpdate -type "Remove" -checkbox $Sender.name
})
}
}
}

View File

@@ -12,7 +12,7 @@ function Invoke-WPFtweaksbutton {
return
}
$Tweaks = (Get-WinUtilCheckBoxes)["WPFTweaks"]
$Tweaks = $sync.selectedTweaks
Set-WinUtilDNS -DNSProvider $sync["WPFchangedns"].text

View File

@@ -12,7 +12,7 @@ function Invoke-WPFundoall {
return
}
$tweaks = (Get-WinUtilCheckBoxes)["WPFtweaks"]
$tweaks = $sync.selectedTweaks
if ($tweaks.count -eq 0) {
$msg = "Please check the tweaks you wish to undo."

View File

@@ -40,6 +40,9 @@ $sync.configs = @{}
$sync.Buttons = [System.Collections.Generic.List[PSObject]]::new()
$sync.ProcessRunning = $false
$sync.selectedApps = [System.Collections.Generic.List[string]]::new()
$sync.selectedTweaks = [System.Collections.Generic.List[string]]::new()
$sync.selectedToggles = [System.Collections.Generic.List[string]]::new()
$sync.selectedFeatures = [System.Collections.Generic.List[string]]::new()
$sync.currentTab = "Install"
$sync.selectedAppsStackPanel
$sync.selectedAppsPopup