From c8c3c7869f31d797477b9807b2a497dad3c23dfb Mon Sep 17 00:00:00 2001 From: Yayo Razo Date: Wed, 15 Jul 2026 13:17:42 -0600 Subject: [PATCH] Add window-level progress indicator for Run Tweaks and Undo (#4830) * feat(tweaks): add window-level progress indicator for Run Tweaks and Undo * Hide completed tweaks progress on next action --------- Co-authored-by: Chris Titus --- config/themes.json | 2 +- .../Set-WinUtilTweaksProgressIndicator.ps1 | 35 +++++++++++++++ functions/public/Invoke-WPFButton.ps1 | 1 + functions/public/Invoke-WPFtweaksbutton.ps1 | 4 ++ functions/public/Invoke-WPFundoall.ps1 | 2 + pester/tweaks.Tests.ps1 | 3 ++ pester/ui-state.Tests.ps1 | 44 +++++++++++++++++++ xaml/inputXML.xaml | 23 ++++++++++ 8 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 functions/private/Set-WinUtilTweaksProgressIndicator.ps1 diff --git a/config/themes.json b/config/themes.json index 6bc1b17c..fb4fcddd 100644 --- a/config/themes.json +++ b/config/themes.json @@ -100,7 +100,7 @@ "ScrollBarBackgroundColor": "#2E3135", "ScrollBarHoverColor": "#3B4252", "ScrollBarDraggingColor": "#5E81AC", - "ProgressBarForegroundColor": "#222222", + "ProgressBarForegroundColor": "#6EFF72", "ProgressBarBackgroundColor": "Transparent", "ProgressBarTextColor": "#232629", "ButtonInstallBackgroundColor": "#222222", diff --git a/functions/private/Set-WinUtilTweaksProgressIndicator.ps1 b/functions/private/Set-WinUtilTweaksProgressIndicator.ps1 new file mode 100644 index 00000000..cface543 --- /dev/null +++ b/functions/private/Set-WinUtilTweaksProgressIndicator.ps1 @@ -0,0 +1,35 @@ +function Set-WinUtilTweaksProgressIndicator { + <# + .SYNOPSIS + Shows, updates, or hides the window-level progress indicator used by the + Tweaks and Undo workflows. It lives outside the TabControl, so unlike the + Install tab's progress bar it stays visible no matter which tab is active. + .PARAMETER Visible + Whether the indicator should be shown or hidden. + .PARAMETER Label + The text to display above the progress bar. + .PARAMETER Percent + The percentage of the progress bar that should be filled (0-100). + #> + param( + [bool]$Visible, + [string]$Label, + [ValidateRange(0,100)] + [int]$Percent + ) + + $indicatorVisible = if ($Visible) { [Windows.Visibility]::Visible } else { [Windows.Visibility]::Collapsed } + $indicatorLabel = $Label + $hasLabel = $PSBoundParameters.ContainsKey('Label') + $hasPercent = $PSBoundParameters.ContainsKey('Percent') + + Invoke-WPFUIThread -ScriptBlock { + $sync.WPFTweaksProgressBar.Visibility = $indicatorVisible + if ($hasLabel) { + $sync.WPFTweaksProgressLabel.Text = $indicatorLabel + } + if ($hasPercent) { + $sync.WPFTweaksProgressValue.Value = $Percent + } + } +} diff --git a/functions/public/Invoke-WPFButton.ps1 b/functions/public/Invoke-WPFButton.ps1 index 8a13c378..5a8d3538 100644 --- a/functions/public/Invoke-WPFButton.ps1 +++ b/functions/public/Invoke-WPFButton.ps1 @@ -16,6 +16,7 @@ function Invoke-WPFButton { #[System.Windows.MessageBox]::Show("$Button","Chris Titus Tech's Windows Utility","OK","Info") if (-not $sync.ProcessRunning) { Set-WinUtilProgressBar -label "" -percent 0 + Set-WinUtilTweaksProgressIndicator -Visible $false } # Check if button is defined in feature config with function or InvokeScript diff --git a/functions/public/Invoke-WPFtweaksbutton.ps1 b/functions/public/Invoke-WPFtweaksbutton.ps1 index f2edb04a..598941a5 100644 --- a/functions/public/Invoke-WPFtweaksbutton.ps1 +++ b/functions/public/Invoke-WPFtweaksbutton.ps1 @@ -40,12 +40,14 @@ function Invoke-WPFtweaksbutton { } Set-WinUtilProgressBar -Label "Creating restore point" -Percent 0 + Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Creating restore point" -Percent 0 Write-WinUtilLog -Component "Tweaks" -Message "Creating restore point before applying selected tweaks." Invoke-WinUtilTweaks $restorePointTweak $completedSteps = 1 if ($tweaksToRun.Count -eq 0 -and $dnsProvider -eq "Default") { Set-WinUtilProgressBar -Label "Tweaks finished" -Percent 100 + Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Tweaks finished" -Percent 100 $sync.ProcessRunning = $false Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" } Write-Host "=================================" @@ -76,12 +78,14 @@ function Invoke-WPFtweaksbutton { for ($i = 0; $i -lt $tweaks.Count; $i++) { Set-WinUtilProgressBar -Label "Applying $($tweaks[$i])" -Percent ($completedSteps / $totalSteps * 100) + Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Applying $($tweaks[$i]) ($($completedSteps + 1)/$totalSteps)" -Percent ($completedSteps / $totalSteps * 100) Invoke-WinUtilTweaks $tweaks[$i] $completedSteps++ $progress = $completedSteps / $totalSteps Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -value $progress } } Set-WinUtilProgressBar -Label "Tweaks finished" -Percent 100 + Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Tweaks finished" -Percent 100 $sync.ProcessRunning = $false Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" } Write-Host "=================================" diff --git a/functions/public/Invoke-WPFundoall.ps1 b/functions/public/Invoke-WPFundoall.ps1 index 230dce52..be128136 100644 --- a/functions/public/Invoke-WPFundoall.ps1 +++ b/functions/public/Invoke-WPFundoall.ps1 @@ -34,11 +34,13 @@ function Invoke-WPFundoall { for ($i = 0; $i -lt $tweaks.Count; $i++) { Set-WinUtilProgressBar -Label "Undoing $($tweaks[$i])" -Percent ($i / $tweaks.Count * 100) + Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Undoing $($tweaks[$i]) ($($i + 1)/$($tweaks.Count))" -Percent ($i / $tweaks.Count * 100) Invoke-WinUtiltweaks $tweaks[$i] -undo $true Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -value ($i/$tweaks.Count) } } Set-WinUtilProgressBar -Label "Undo Tweaks Finished" -Percent 100 + Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Undo Tweaks Finished" -Percent 100 $sync.ProcessRunning = $false Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" } Write-Host "==================================" diff --git a/pester/tweaks.Tests.ps1 b/pester/tweaks.Tests.ps1 index bf6cf08a..3b14b1ba 100644 --- a/pester/tweaks.Tests.ps1 +++ b/pester/tweaks.Tests.ps1 @@ -34,6 +34,9 @@ BeforeAll { function Set-WinUtilProgressBar { param($Label, $Percent) } + function Set-WinUtilTweaksProgressIndicator { + param($Visible, $Label, $Percent) + } function Write-WinUtilLog { param($Message, $Level, $Component) } diff --git a/pester/ui-state.Tests.ps1 b/pester/ui-state.Tests.ps1 index 0b2a9488..2ff6bbf5 100644 --- a/pester/ui-state.Tests.ps1 +++ b/pester/ui-state.Tests.ps1 @@ -44,8 +44,17 @@ namespace System.Windows.Controls . (Join-Path $script:repoRoot "functions\private\Update-WinUtilSelections.ps1") . (Join-Path $script:repoRoot "functions\private\Reset-WPFCheckBoxes.ps1") . (Join-Path $script:repoRoot "functions\public\Invoke-WPFSelectedCheckboxesUpdate.ps1") + . (Join-Path $script:repoRoot "functions\public\Invoke-WPFButton.ps1") . (Join-Path $script:repoRoot "functions\public\Invoke-WPFToggleAllCategories.ps1") + function Set-WinUtilProgressBar { + param($Label, $Percent) + } + + function Set-WinUtilTweaksProgressIndicator { + param($Visible, $Label, $Percent) + } + function script:New-WinUtilFakeCheckBox { param([bool]$IsChecked = $false) @@ -272,3 +281,38 @@ Describe "Invoke-WPFToggleAllCategories" { } } } + +Describe "Invoke-WPFButton progress cleanup" { + BeforeEach { + New-WinUtilUiStateTestContext + Mock Set-WinUtilProgressBar { } + Mock Set-WinUtilTweaksProgressIndicator { } + } + + AfterEach { + Remove-Variable -Name sync -Scope Script -ErrorAction SilentlyContinue + Remove-Variable -Name sync -Scope Global -ErrorAction SilentlyContinue + } + + It "clears completed progress on the next idle button click" { + $script:sync.ProcessRunning = $false + + Invoke-WPFButton -Button "WPFNoOp" + + Should -Invoke Set-WinUtilProgressBar -Times 1 -Exactly -ParameterFilter { + $Label -eq "" -and $Percent -eq 0 + } + Should -Invoke Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter { + $Visible -eq $false + } + } + + It "leaves progress visible while a process is running" { + $script:sync.ProcessRunning = $true + + Invoke-WPFButton -Button "WPFNoOp" + + Should -Not -Invoke Set-WinUtilProgressBar + Should -Not -Invoke Set-WinUtilTweaksProgressIndicator + } +} diff --git a/xaml/inputXML.xaml b/xaml/inputXML.xaml index 45b3d6ff..56eecefb 100644 --- a/xaml/inputXML.xaml +++ b/xaml/inputXML.xaml @@ -918,12 +918,27 @@ + + @@ -1717,5 +1732,13 @@ + + + + + + + +