Replace install progress overlay with shared indicator (#4844)

* Use shared progress for app workflows

* Remove legacy install progress overlay

* Document shared app progress indicator

* Address progress review feedback

* Track all Win11 ISO progress

* Restore Win11 Creator work on relaunch
This commit is contained in:
Chris Titus
2026-07-15 22:35:58 -05:00
committed by GitHub
parent 51cb3811dd
commit 9ed06e793a
24 changed files with 270 additions and 230 deletions
-4
View File
@@ -46,7 +46,6 @@
"AppInstallUnselectedColor": "#F7F7F7", "AppInstallUnselectedColor": "#F7F7F7",
"AppInstallHighlightedColor": "#CFCFCF", "AppInstallHighlightedColor": "#CFCFCF",
"AppInstallSelectedColor": "#C2C2C2", "AppInstallSelectedColor": "#C2C2C2",
"AppInstallOverlayBackgroundColor": "#6A6D72",
"ComboBoxForegroundColor": "#232629", "ComboBoxForegroundColor": "#232629",
"ComboBoxBackgroundColor": "#F7F7F7", "ComboBoxBackgroundColor": "#F7F7F7",
"LabelboxForegroundColor": "#232629", "LabelboxForegroundColor": "#232629",
@@ -60,7 +59,6 @@
"ScrollBarDraggingColor": "#6A6D72", "ScrollBarDraggingColor": "#6A6D72",
"ProgressBarForegroundColor": "#2E77FF", "ProgressBarForegroundColor": "#2E77FF",
"ProgressBarBackgroundColor": "Transparent", "ProgressBarBackgroundColor": "Transparent",
"ProgressBarTextColor": "#232629",
"ButtonInstallBackgroundColor": "#F7F7F7", "ButtonInstallBackgroundColor": "#F7F7F7",
"ButtonTweaksBackgroundColor": "#F7F7F7", "ButtonTweaksBackgroundColor": "#F7F7F7",
"ButtonConfigBackgroundColor": "#F7F7F7", "ButtonConfigBackgroundColor": "#F7F7F7",
@@ -88,7 +86,6 @@
"AppInstallUnselectedColor": "#232629", "AppInstallUnselectedColor": "#232629",
"AppInstallHighlightedColor": "#3C3C3C", "AppInstallHighlightedColor": "#3C3C3C",
"AppInstallSelectedColor": "#4C4C4C", "AppInstallSelectedColor": "#4C4C4C",
"AppInstallOverlayBackgroundColor": "#2E3135",
"ComboBoxForegroundColor": "#F7F7F7", "ComboBoxForegroundColor": "#F7F7F7",
"ComboBoxBackgroundColor": "#1E3747", "ComboBoxBackgroundColor": "#1E3747",
"LabelboxForegroundColor": "#5BDCFF", "LabelboxForegroundColor": "#5BDCFF",
@@ -102,7 +99,6 @@
"ScrollBarDraggingColor": "#5E81AC", "ScrollBarDraggingColor": "#5E81AC",
"ProgressBarForegroundColor": "#6EFF72", "ProgressBarForegroundColor": "#6EFF72",
"ProgressBarBackgroundColor": "Transparent", "ProgressBarBackgroundColor": "Transparent",
"ProgressBarTextColor": "#232629",
"ButtonInstallBackgroundColor": "#222222", "ButtonInstallBackgroundColor": "#222222",
"ButtonTweaksBackgroundColor": "#333333", "ButtonTweaksBackgroundColor": "#333333",
"ButtonConfigBackgroundColor": "#444444", "ButtonConfigBackgroundColor": "#444444",
@@ -138,7 +138,7 @@ For a better Windows experience with minimal risk:
1. Check multiple application boxes 1. Check multiple application boxes
2. All checked apps will install in sequence 2. All checked apps will install in sequence
3. Progress is shown in the bottom panel 3. Install and uninstall progress is shown in the window-level bottom panel, including the current package or package-manager batch and overall completion
### Applying Tweaks ### Applying Tweaks
@@ -1,12 +0,0 @@
function Hide-WPFInstallAppBusy {
<#
.SYNOPSIS
Hides the busy overlay in the install app area of the WPF form.
This is used to indicate that an install or uninstall has finished.
#>
Invoke-WPFUIThread -ScriptBlock {
$sync.InstallAppAreaOverlay.Visibility = [Windows.Visibility]::Collapsed
$sync.InstallAppAreaBorder.IsEnabled = $true
$sync.InstallAppAreaScrollViewer.Effect.Radius = 0
}
}
@@ -5,8 +5,6 @@
This is used as the parent object for all category and app entries on the install tab This is used as the parent object for all category and app entries on the install tab
Used to as part of the Install Tab UI generation Used to as part of the Install Tab UI generation
Also creates an overlay with a progress bar and text to indicate that an install or uninstall is in progress
.PARAMETER TargetElement .PARAMETER TargetElement
The element to which the AppArea should be added The element to which the AppArea should be added
@@ -19,22 +17,14 @@
$Border = New-Object Windows.Controls.Border $Border = New-Object Windows.Controls.Border
$Border.VerticalAlignment = "Stretch" $Border.VerticalAlignment = "Stretch"
$Border.SetResourceReference([Windows.Controls.Control]::StyleProperty, "BorderStyle") $Border.SetResourceReference([Windows.Controls.Control]::StyleProperty, "BorderStyle")
$sync.InstallAppAreaBorder = $Border
# Add a ScrollViewer, because the ItemsControl does not support scrolling by itself # Add a ScrollViewer, because the ItemsControl does not support scrolling by itself
$scrollViewer = New-Object Windows.Controls.ScrollViewer $scrollViewer = New-Object Windows.Controls.ScrollViewer
$scrollViewer.VerticalScrollBarVisibility = 'Auto' $scrollViewer.VerticalScrollBarVisibility = 'Auto'
$scrollViewer.HorizontalAlignment = 'Stretch' $scrollViewer.HorizontalAlignment = 'Stretch'
$scrollViewer.VerticalAlignment = 'Stretch' $scrollViewer.VerticalAlignment = 'Stretch'
$scrollViewer.CanContentScroll = $true $scrollViewer.CanContentScroll = $true
$sync.InstallAppAreaScrollViewer = $scrollViewer
$Border.Child = $scrollViewer $Border.Child = $scrollViewer
# Initialize the Blur Effect for the ScrollViewer, which will be used to indicate that an install/uninstall is in progress
$blurEffect = New-Object Windows.Media.Effects.BlurEffect
$blurEffect.Radius = 0
$scrollViewer.Effect = $blurEffect
## Create the ItemsControl, which will be the parent of all the app entries ## Create the ItemsControl, which will be the parent of all the app entries
$itemsControl = New-Object Windows.Controls.ItemsControl $itemsControl = New-Object Windows.Controls.ItemsControl
$itemsControl.HorizontalAlignment = 'Stretch' $itemsControl.HorizontalAlignment = 'Stretch'
@@ -52,61 +42,5 @@
# Add the Border containing the App Area to the target Grid # Add the Border containing the App Area to the target Grid
$targetGrid.Children.Add($Border) | Out-Null $targetGrid.Children.Add($Border) | Out-Null
$overlay = New-Object Windows.Controls.Border
$overlay.CornerRadius = New-Object Windows.CornerRadius(10)
$overlay.SetResourceReference([Windows.Controls.Control]::BackgroundProperty, "AppInstallOverlayBackgroundColor")
$overlay.Visibility = [Windows.Visibility]::Collapsed
# Also add the overlay to the target Grid on top of the App Area
$targetGrid.Children.Add($overlay) | Out-Null
$sync.InstallAppAreaOverlay = $overlay
$overlayText = New-Object Windows.Controls.TextBlock
$overlayText.Text = "Installing apps..."
$overlayText.HorizontalAlignment = 'Center'
$overlayText.VerticalAlignment = 'Center'
$overlayText.SetResourceReference([Windows.Controls.TextBlock]::ForegroundProperty, "MainForegroundColor")
$overlayText.Background = "Transparent"
$overlayText.SetResourceReference([Windows.Controls.TextBlock]::FontSizeProperty, "HeaderFontSize")
$overlayText.SetResourceReference([Windows.Controls.TextBlock]::FontFamilyProperty, "MainFontFamily")
$overlayText.SetResourceReference([Windows.Controls.TextBlock]::FontWeightProperty, "MainFontWeight")
$overlayText.SetResourceReference([Windows.Controls.TextBlock]::MarginProperty, "MainMargin")
$sync.InstallAppAreaOverlayText = $overlayText
$progressbar = New-Object Windows.Controls.ProgressBar
$progressbar.Name = "ProgressBar"
$progressbar.Width = 250
$progressbar.Height = 50
$sync.ProgressBar = $progressbar
# Add a TextBlock overlay for the progress bar text
$progressBarTextBlock = New-Object Windows.Controls.TextBlock
$progressBarTextBlock.Name = "progressBarTextBlock"
$progressBarTextBlock.FontWeight = [Windows.FontWeights]::Bold
$progressBarTextBlock.FontSize = 16
$progressBarTextBlock.Width = $progressbar.Width
$progressBarTextBlock.Height = $progressbar.Height
$progressBarTextBlock.SetResourceReference([Windows.Controls.TextBlock]::ForegroundProperty, "ProgressBarTextColor")
$progressBarTextBlock.TextTrimming = "CharacterEllipsis"
$progressBarTextBlock.Background = "Transparent"
$sync.progressBarTextBlock = $progressBarTextBlock
# Create a Grid to overlay the text on the progress bar
$progressGrid = New-Object Windows.Controls.Grid
$progressGrid.Width = $progressbar.Width
$progressGrid.Height = $progressbar.Height
$progressGrid.Margin = "0,10,0,10"
$progressGrid.Children.Add($progressbar) | Out-Null
$progressGrid.Children.Add($progressBarTextBlock) | Out-Null
$overlayStackPanel = New-Object Windows.Controls.StackPanel
$overlayStackPanel.Orientation = "Vertical"
$overlayStackPanel.HorizontalAlignment = 'Center'
$overlayStackPanel.VerticalAlignment = 'Center'
$overlayStackPanel.Children.Add($overlayText) | Out-Null
$overlayStackPanel.Children.Add($progressGrid) | Out-Null
$overlay.Child = $overlayStackPanel
return $itemsControl return $itemsControl
} }
@@ -28,7 +28,7 @@ function Initialize-WinUtilTabContent {
"AppX" { "AppX" {
Invoke-WPFUIElements -configVariable $sync.configs.appx -targetGridName "appxpanel" -columncount 2 Invoke-WPFUIElements -configVariable $sync.configs.appx -targetGridName "appxpanel" -columncount 2
} }
"Win11 Creator" { "Win11ISO" {
if ($sync.Form -and $sync.Form.Dispatcher) { if ($sync.Form -and $sync.Form.Dispatcher) {
$sync.Form.Dispatcher.BeginInvoke([System.Windows.Threading.DispatcherPriority]::Background, [action]{ Invoke-WinUtilISOCheckExistingWork }) | Out-Null $sync.Form.Dispatcher.BeginInvoke([System.Windows.Threading.DispatcherPriority]::Background, [action]{ Invoke-WinUtilISOCheckExistingWork }) | Out-Null
} }
+42 -28
View File
@@ -47,7 +47,7 @@ function Invoke-WinUtilISOMountAndVerify {
} }
Write-Win11ISOLog "Mounting ISO: $isoPath" Write-Win11ISOLog "Mounting ISO: $isoPath"
Set-WinUtilProgressBar -Label "Mounting ISO..." -Percent 10 Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Mounting ISO..." -Percent 10
try { try {
Mount-DiskImage -ImagePath $isoPath Mount-DiskImage -ImagePath $isoPath
@@ -59,7 +59,7 @@ function Invoke-WinUtilISOMountAndVerify {
$driveLetter = (Get-DiskImage -ImagePath $isoPath | Get-Volume).DriveLetter + ":" $driveLetter = (Get-DiskImage -ImagePath $isoPath | Get-Volume).DriveLetter + ":"
Write-Win11ISOLog "Mounted at drive $driveLetter" Write-Win11ISOLog "Mounted at drive $driveLetter"
Set-WinUtilProgressBar -Label "Verifying ISO contents..." -Percent 30 Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Verifying ISO contents..." -Percent 30
$wimPath = Join-Path $driveLetter "sources\install.wim" $wimPath = Join-Path $driveLetter "sources\install.wim"
$esdPath = Join-Path $driveLetter "sources\install.esd" $esdPath = Join-Path $driveLetter "sources\install.esd"
@@ -70,13 +70,13 @@ function Invoke-WinUtilISOMountAndVerify {
[System.Windows.MessageBox]::Show( [System.Windows.MessageBox]::Show(
"This does not appear to be a valid Windows ISO.`n`ninstall.wim / install.esd was not found.", "This does not appear to be a valid Windows ISO.`n`ninstall.wim / install.esd was not found.",
"Invalid ISO", "OK", "Error") "Invalid ISO", "OK", "Error")
Set-WinUtilProgressBar -Label "" -Percent 0 Set-WinUtilTweaksProgressIndicator -Visible $false
return return
} }
$activeWim = if (Test-Path $wimPath) { $wimPath } else { $esdPath } $activeWim = if (Test-Path $wimPath) { $wimPath } else { $esdPath }
Set-WinUtilProgressBar -Label "Reading image metadata..." -Percent 55 Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Reading image metadata..." -Percent 55
$imageInfo = Get-WindowsImage -ImagePath $activeWim | Select-Object ImageIndex, ImageName $imageInfo = Get-WindowsImage -ImagePath $activeWim | Select-Object ImageIndex, ImageName
if (-not ($imageInfo | Where-Object { $_.ImageName -match "Windows 11" })) { if (-not ($imageInfo | Where-Object { $_.ImageName -match "Windows 11" })) {
@@ -85,7 +85,7 @@ function Invoke-WinUtilISOMountAndVerify {
[System.Windows.MessageBox]::Show( [System.Windows.MessageBox]::Show(
"No Windows 11 edition was found in this ISO.`n`nOnly official Windows 11 ISOs are supported.", "No Windows 11 edition was found in this ISO.`n`nOnly official Windows 11 ISOs are supported.",
"Not a Windows 11 ISO", "OK", "Error") "Not a Windows 11 ISO", "OK", "Error")
Set-WinUtilProgressBar -Label "" -Percent 0 Set-WinUtilTweaksProgressIndicator -Visible $false
return return
} }
@@ -114,7 +114,7 @@ function Invoke-WinUtilISOMountAndVerify {
$sync["Win11ISOImagePath"] = $isoPath $sync["Win11ISOImagePath"] = $isoPath
$sync["WPFWin11ISOModifySection"].Visibility = "Visible" $sync["WPFWin11ISOModifySection"].Visibility = "Visible"
Set-WinUtilProgressBar -Label "ISO verified" -Percent 100 Set-WinUtilTweaksProgressIndicator -Visible $true -Label "ISO verified" -Percent 100
Write-Win11ISOLog "ISO verified OK. Editions found: $($imageInfo.Count)" Write-Win11ISOLog "ISO verified OK. Editions found: $($imageInfo.Count)"
} catch { } catch {
Write-Win11ISOLog "ERROR during mount/verify: $_" Write-Win11ISOLog "ERROR during mount/verify: $_"
@@ -123,7 +123,7 @@ function Invoke-WinUtilISOMountAndVerify {
"Error", "OK", "Error") "Error", "OK", "Error")
} finally { } finally {
Start-Sleep -Milliseconds 800 Start-Sleep -Milliseconds 800
Set-WinUtilProgressBar -Label "" -Percent 0 Set-WinUtilTweaksProgressIndicator -Visible $false
} }
} }
@@ -151,6 +151,7 @@ function Invoke-WinUtilISOModify {
$sync["WPFWin11ISOModifyButton"].IsEnabled = $false $sync["WPFWin11ISOModifyButton"].IsEnabled = $false
$sync["Win11ISOModifying"] = $true $sync["Win11ISOModifying"] = $true
$sync["Win11ISOProcessRunning"] = $true
$workDir = Join-Path $env:TEMP "WinUtil_Win11ISO_$(Get-Date -Format 'yyyyMMdd_HHmmss')" $workDir = Join-Path $env:TEMP "WinUtil_Win11ISO_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
if (Test-Path $workDir) { if (Test-Path $workDir) {
@@ -203,9 +204,10 @@ function Invoke-WinUtilISOModify {
function SetProgress($label, $pct) { function SetProgress($label, $pct) {
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{ $sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
$sync.progressBarTextBlock.Text = $label $sync["WPFTweaksProgressBar"].Visibility = "Visible"
$sync.progressBarTextBlock.ToolTip = $label $sync["WPFTweaksProgressLabel"].Text = $label
$sync.ProgressBar.Value = [Math]::Max($pct, 5) $sync["WPFTweaksProgressLabel"].ToolTip = $label
$sync["WPFTweaksProgressValue"].Value = [Math]::Max($pct, 5)
}) })
} }
@@ -452,10 +454,12 @@ function Invoke-WinUtilISOModify {
} finally { } finally {
Start-Sleep -Milliseconds 800 Start-Sleep -Milliseconds 800
$sync["Win11ISOModifying"] = $false $sync["Win11ISOModifying"] = $false
$sync["Win11ISOProcessRunning"] = $false
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{ $sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
$sync.progressBarTextBlock.Text = "" $sync["WPFTweaksProgressBar"].Visibility = "Collapsed"
$sync.progressBarTextBlock.ToolTip = "" $sync["WPFTweaksProgressLabel"].Text = ""
$sync.ProgressBar.Value = 0 $sync["WPFTweaksProgressLabel"].ToolTip = ""
$sync["WPFTweaksProgressValue"].Value = 0
$sync["WPFWin11ISOModifyButton"].IsEnabled = $true $sync["WPFWin11ISOModifyButton"].IsEnabled = $true
if ($sync["WPFWin11ISOOutputSection"].Visibility -ne "Visible") { if ($sync["WPFWin11ISOOutputSection"].Visibility -ne "Visible") {
$sync["WPFWin11ISOSelectSection"].Visibility = "Visible" $sync["WPFWin11ISOSelectSection"].Visibility = "Visible"
@@ -514,6 +518,7 @@ function Invoke-WinUtilISOCleanAndReset {
} }
$sync["WPFWin11ISOCleanResetButton"].IsEnabled = $false $sync["WPFWin11ISOCleanResetButton"].IsEnabled = $false
$sync["Win11ISOProcessRunning"] = $true
$runspace = [Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace() $runspace = [Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace()
$runspace.ApartmentState = "STA" $runspace.ApartmentState = "STA"
@@ -538,9 +543,10 @@ function Invoke-WinUtilISOCleanAndReset {
function SetProgress($label, $pct) { function SetProgress($label, $pct) {
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{ $sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
$sync.progressBarTextBlock.Text = $label $sync["WPFTweaksProgressBar"].Visibility = "Visible"
$sync.progressBarTextBlock.ToolTip = $label $sync["WPFTweaksProgressLabel"].Text = $label
$sync.ProgressBar.Value = [Math]::Max($pct, 5) $sync["WPFTweaksProgressLabel"].ToolTip = $label
$sync["WPFTweaksProgressValue"].Value = [Math]::Max($pct, 5)
}) })
} }
@@ -628,20 +634,24 @@ function Invoke-WinUtilISOCleanAndReset {
$sync["WPFWin11ISOModifyButton"].IsEnabled = $true $sync["WPFWin11ISOModifyButton"].IsEnabled = $true
$sync["WPFWin11ISOCleanResetButton"].IsEnabled = $true $sync["WPFWin11ISOCleanResetButton"].IsEnabled = $true
$sync.progressBarTextBlock.Text = "" $sync["WPFTweaksProgressBar"].Visibility = "Collapsed"
$sync.progressBarTextBlock.ToolTip = "" $sync["WPFTweaksProgressLabel"].Text = ""
$sync.ProgressBar.Value = 0 $sync["WPFTweaksProgressLabel"].ToolTip = ""
$sync["WPFTweaksProgressValue"].Value = 0
$sync["WPFWin11ISOStatusLog"].Text = "Ready. Please select a Windows 11 ISO to begin." $sync["WPFWin11ISOStatusLog"].Text = "Ready. Please select a Windows 11 ISO to begin."
}) })
} catch { } catch {
Log "ERROR during Clean & Reset: $_" Log "ERROR during Clean & Reset: $_"
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{ $sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
$sync.progressBarTextBlock.Text = "" $sync["WPFTweaksProgressBar"].Visibility = "Collapsed"
$sync.progressBarTextBlock.ToolTip = "" $sync["WPFTweaksProgressLabel"].Text = ""
$sync.ProgressBar.Value = 0 $sync["WPFTweaksProgressLabel"].ToolTip = ""
$sync["WPFTweaksProgressValue"].Value = 0
$sync["WPFWin11ISOCleanResetButton"].IsEnabled = $true $sync["WPFWin11ISOCleanResetButton"].IsEnabled = $true
}) })
} finally {
$sync["Win11ISOProcessRunning"] = $false
} }
}) })
@@ -706,6 +716,7 @@ function Invoke-WinUtilISOExport {
} }
$sync["WPFWin11ISOChooseISOButton"].IsEnabled = $false $sync["WPFWin11ISOChooseISOButton"].IsEnabled = $false
$sync["Win11ISOProcessRunning"] = $true
$runspace = [Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace() $runspace = [Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace()
$runspace.ApartmentState = "STA" $runspace.ApartmentState = "STA"
@@ -726,9 +737,10 @@ function Invoke-WinUtilISOExport {
function SetProgress($label, $pct) { function SetProgress($label, $pct) {
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{ $sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
$sync.progressBarTextBlock.Text = $label $sync["WPFTweaksProgressBar"].Visibility = "Visible"
$sync.progressBarTextBlock.ToolTip = $label $sync["WPFTweaksProgressLabel"].Text = $label
$sync.ProgressBar.Value = [Math]::Max($pct, 5) $sync["WPFTweaksProgressLabel"].ToolTip = $label
$sync["WPFTweaksProgressValue"].Value = [Math]::Max($pct, 5)
}) })
} }
@@ -788,10 +800,12 @@ function Invoke-WinUtilISOExport {
}) })
} finally { } finally {
Start-Sleep -Milliseconds 800 Start-Sleep -Milliseconds 800
$sync["Win11ISOProcessRunning"] = $false
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{ $sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
$sync.progressBarTextBlock.Text = "" $sync["WPFTweaksProgressBar"].Visibility = "Collapsed"
$sync.progressBarTextBlock.ToolTip = "" $sync["WPFTweaksProgressLabel"].Text = ""
$sync.ProgressBar.Value = 0 $sync["WPFTweaksProgressLabel"].ToolTip = ""
$sync["WPFTweaksProgressValue"].Value = 0
$sync["WPFWin11ISOChooseISOButton"].IsEnabled = $true $sync["WPFWin11ISOChooseISOButton"].IsEnabled = $true
}) })
} }
+10 -6
View File
@@ -61,6 +61,7 @@ function Invoke-WinUtilISOWriteUSB {
} }
$sync["WPFWin11ISOWriteUSBButton"].IsEnabled = $false $sync["WPFWin11ISOWriteUSBButton"].IsEnabled = $false
$sync["Win11ISOProcessRunning"] = $true
Write-Win11ISOLog "Starting USB write to Disk $diskNum..." Write-Win11ISOLog "Starting USB write to Disk $diskNum..."
$runspace = [Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace() $runspace = [Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace()
@@ -86,9 +87,10 @@ function Invoke-WinUtilISOWriteUSB {
function SetProgress($label, $pct) { function SetProgress($label, $pct) {
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{ $sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
$sync.progressBarTextBlock.Text = $label $sync["WPFTweaksProgressBar"].Visibility = "Visible"
$sync.progressBarTextBlock.ToolTip = $label $sync["WPFTweaksProgressLabel"].Text = $label
$sync.ProgressBar.Value = [Math]::Max($pct, 5) $sync["WPFTweaksProgressLabel"].ToolTip = $label
$sync["WPFTweaksProgressValue"].Value = [Math]::Max($pct, 5)
}) })
} }
@@ -255,10 +257,12 @@ function Invoke-WinUtilISOWriteUSB {
}) })
} finally { } finally {
Start-Sleep -Milliseconds 800 Start-Sleep -Milliseconds 800
$sync["Win11ISOProcessRunning"] = $false
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{ $sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
$sync.progressBarTextBlock.Text = "" $sync["WPFTweaksProgressBar"].Visibility = "Collapsed"
$sync.progressBarTextBlock.ToolTip = "" $sync["WPFTweaksProgressLabel"].Text = ""
$sync.ProgressBar.Value = 0 $sync["WPFTweaksProgressLabel"].ToolTip = ""
$sync["WPFTweaksProgressValue"].Value = 0
$sync["WPFWin11ISOWriteUSBButton"].IsEnabled = $true $sync["WPFWin11ISOWriteUSBButton"].IsEnabled = $true
}) })
} }
@@ -1,26 +0,0 @@
function Set-WinUtilProgressbar{
<#
.SYNOPSIS
This function is used to Update the Progress Bar displayed in the winutil GUI.
It will be automatically hidden if the user clicks something and no process is running
.PARAMETER Label
The Text to be overlaid onto the Progress Bar
.PARAMETER PERCENT
The percentage of the Progress Bar that should be filled (0-100)
#>
param(
[string]$Label,
[ValidateRange(0,100)]
[int]$Percent
)
$progressLabel = $Label
Invoke-WPFUIThread -ScriptBlock {$sync.progressBarTextBlock.Text = $progressLabel}
Invoke-WPFUIThread -ScriptBlock {$sync.progressBarTextBlock.ToolTip = $progressLabel}
if ($Percent -lt 5 ) {
$Percent = 5 # Ensure the progress bar is not empty, as it looks weird
}
Invoke-WPFUIThread -ScriptBlock { $sync.ProgressBar.Value = $Percent}
}
@@ -2,8 +2,8 @@ function Set-WinUtilTweaksProgressIndicator {
<# <#
.SYNOPSIS .SYNOPSIS
Shows, updates, or hides the window-level progress indicator used by long-running Shows, updates, or hides the window-level progress indicator used by long-running
workflows such as Tweaks, Undo, and AppX management. It lives outside the TabControl, workflows such as app management, Tweaks, AppX management, and Win11 Creator.
so unlike the Install tab's progress bar it stays visible no matter which tab is active. It lives outside the TabControl, so it stays visible no matter which tab is active.
.PARAMETER Visible .PARAMETER Visible
Whether the indicator should be shown or hidden. Whether the indicator should be shown or hidden.
.PARAMETER Label .PARAMETER Label
@@ -1,23 +0,0 @@
function Show-WPFInstallAppBusy {
<#
.SYNOPSIS
Displays a busy overlay in the install app area of the WPF form.
This is used to indicate that an install or uninstall is in progress.
Dynamically updates the size of the overlay based on the app area on each invocation.
.PARAMETER text
The text to display in the busy overlay. Defaults to "Installing apps...".
#>
param (
$text = "Installing apps..."
)
$overlayText = $text
Invoke-WPFUIThread -ScriptBlock {
$sync.InstallAppAreaOverlay.Visibility = [Windows.Visibility]::Visible
$sync.InstallAppAreaOverlay.Width = $($sync.InstallAppAreaScrollViewer.ActualWidth * 0.4)
$sync.InstallAppAreaOverlay.Height = $($sync.InstallAppAreaScrollViewer.ActualWidth * 0.4)
$sync.InstallAppAreaOverlayText.Text = $overlayText
$sync.InstallAppAreaBorder.IsEnabled = $false
$sync.InstallAppAreaScrollViewer.Effect.Radius = 5
}
}
+1 -2
View File
@@ -14,8 +14,7 @@ function Invoke-WPFButton {
# Use this to get the name of the button # Use this to get the name of the button
#[System.Windows.MessageBox]::Show("$Button","Chris Titus Tech's Windows Utility","OK","Info") #[System.Windows.MessageBox]::Show("$Button","Chris Titus Tech's Windows Utility","OK","Info")
if (-not $sync.ProcessRunning) { if (-not $sync.ProcessRunning -and -not $sync.Win11ISOProcessRunning) {
Set-WinUtilProgressBar -label "" -percent 0
Set-WinUtilTweaksProgressIndicator -Visible $false Set-WinUtilTweaksProgressIndicator -Visible $false
} }
+52 -4
View File
@@ -31,33 +31,81 @@ function Invoke-WPFInstall {
$packagesWinget = $packagesSorted['Winget'] $packagesWinget = $packagesSorted['Winget']
$packagesChoco = $packagesSorted['Choco'] $packagesChoco = $packagesSorted['Choco']
$totalPackages = @($packagesWinget).Count + @($packagesChoco).Count
$completedPackages = 0
$hasUI = $null -ne $sync.Form -and $null -ne $sync.Form.Dispatcher
Write-WinUtilLog -Component "Install" -Message "Install package manager split: winget=$(@($packagesWinget).Count), choco=$(@($packagesChoco).Count)" Write-WinUtilLog -Component "Install" -Message "Install package manager split: winget=$(@($packagesWinget).Count), choco=$(@($packagesChoco).Count)"
try { try {
$sync.ProcessRunning = $true $sync.ProcessRunning = $true
if ($hasUI) {
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Preparing app install (0/$totalPackages)" -Percent 0
Invoke-WPFUIThread -ScriptBlock {
if ($null -ne $sync.ItemsControl) {
$sync.ItemsControl.IsEnabled = $false
}
}
}
if($packagesWinget.Count -gt 0 -and $packagesWinget -ne "0") { if($packagesWinget.Count -gt 0 -and $packagesWinget -ne "0") {
Show-WPFInstallAppBusy -text "Installing apps..."
Install-WinUtilWinget Install-WinUtilWinget
Install-WinUtilProgramWinget -Action Install -Programs $packagesWinget foreach ($program in $packagesWinget) {
$position = $completedPackages + 1
$startPercent = [int](($completedPackages / $totalPackages) * 100)
if ($hasUI) {
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Installing $program ($position/$totalPackages)" -Percent $startPercent
}
Install-WinUtilProgramWinget -Action Install -Programs @($program)
$completedPackages++
$completedPercent = [int](($completedPackages / $totalPackages) * 100)
if ($hasUI) {
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Installed $program ($completedPackages/$totalPackages)" -Percent $completedPercent
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -value ($completedPercent / 100) }
}
}
} }
if($packagesChoco.Count -gt 0) { if($packagesChoco.Count -gt 0) {
$position = $completedPackages + 1
$startPercent = [int](($completedPackages / $totalPackages) * 100)
if ($hasUI) {
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Installing Chocolatey packages ($position/$totalPackages)" -Percent $startPercent
}
Install-WinUtilChoco Install-WinUtilChoco
Install-WinUtilProgramChoco -Action Install -Programs $packagesChoco Install-WinUtilProgramChoco -Action Install -Programs $packagesChoco
$completedPackages += @($packagesChoco).Count
$completedPercent = [int](($completedPackages / $totalPackages) * 100)
if ($hasUI) {
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Installed Chocolatey packages ($completedPackages/$totalPackages)" -Percent $completedPercent
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -value ($completedPercent / 100) }
}
} }
Hide-WPFInstallAppBusy
Write-Host "===========================================" Write-Host "==========================================="
Write-Host "-- Installs have finished ---" Write-Host "-- Installs have finished ---"
Write-Host "===========================================" Write-Host "==========================================="
Write-WinUtilLog -Component "Install" -Message "Install workflow completed." Write-WinUtilLog -Component "Install" -Message "Install workflow completed."
if ($hasUI) {
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "App install finished" -Percent 100
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" } Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }
}
} catch { } catch {
Hide-WPFInstallAppBusy
Write-Host "===========================================" Write-Host "==========================================="
Write-Host "Error: $_" Write-Host "Error: $_"
Write-Host "===========================================" Write-Host "==========================================="
Write-WinUtilLog -Level "ERROR" -Component "Install" -Message "Install workflow failed: $($_.Exception.Message)" Write-WinUtilLog -Level "ERROR" -Component "Install" -Message "Install workflow failed: $($_.Exception.Message)"
if ($hasUI) {
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "App install failed" -Percent 100
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Error" -overlay "warning" } Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Error" -overlay "warning" }
}
} finally { } finally {
if ($hasUI) {
Invoke-WPFUIThread -ScriptBlock {
if ($null -ne $sync.ItemsControl) {
$sync.ItemsControl.IsEnabled = $true
}
}
}
$sync.ProcessRunning = $False $sync.ProcessRunning = $False
} }
} }
+51 -4
View File
@@ -42,11 +42,21 @@ function Invoke-WPFUnInstall {
$packagesWinget = $packagesSorted['Winget'] $packagesWinget = $packagesSorted['Winget']
$packagesChoco = $packagesSorted['Choco'] $packagesChoco = $packagesSorted['Choco']
$totalPackages = @($packagesWinget).Count + @($packagesChoco).Count
$completedPackages = 0
$hasUI = $null -ne $sync.Form -and $null -ne $sync.Form.Dispatcher
Write-WinUtilLog -Component "Uninstall" -Message "Uninstall package manager split: winget=$(@($packagesWinget).Count), choco=$(@($packagesChoco).Count)" Write-WinUtilLog -Component "Uninstall" -Message "Uninstall package manager split: winget=$(@($packagesWinget).Count), choco=$(@($packagesChoco).Count)"
try { try {
$sync.ProcessRunning = $true $sync.ProcessRunning = $true
Show-WPFInstallAppBusy -text "Uninstalling apps..." if ($hasUI) {
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Preparing app uninstall (0/$totalPackages)" -Percent 0
Invoke-WPFUIThread -ScriptBlock {
if ($null -ne $sync.ItemsControl) {
$sync.ItemsControl.IsEnabled = $false
}
}
}
if ($packagesWinget -contains "Microsoft.Edge") { if ($packagesWinget -contains "Microsoft.Edge") {
New-Item -Path "$Env:SystemRoot\SystemApps\Microsoft.MicrosoftEdge_8wekyb3d8bbwe\MicrosoftEdge.exe" -Force New-Item -Path "$Env:SystemRoot\SystemApps\Microsoft.MicrosoftEdge_8wekyb3d8bbwe\MicrosoftEdge.exe" -Force
@@ -54,25 +64,62 @@ function Invoke-WPFUnInstall {
# Uninstall all selected programs in new window # Uninstall all selected programs in new window
if($packagesWinget.Count -gt 0) { if($packagesWinget.Count -gt 0) {
Install-WinUtilProgramWinget -Action Uninstall -Programs $packagesWinget foreach ($program in $packagesWinget) {
$position = $completedPackages + 1
$startPercent = [int](($completedPackages / $totalPackages) * 100)
if ($hasUI) {
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Uninstalling $program ($position/$totalPackages)" -Percent $startPercent
}
Install-WinUtilProgramWinget -Action Uninstall -Programs @($program)
$completedPackages++
$completedPercent = [int](($completedPackages / $totalPackages) * 100)
if ($hasUI) {
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Uninstalled $program ($completedPackages/$totalPackages)" -Percent $completedPercent
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -value ($completedPercent / 100) }
}
}
} }
if($packagesChoco.Count -gt 0) { if($packagesChoco.Count -gt 0) {
$position = $completedPackages + 1
$startPercent = [int](($completedPackages / $totalPackages) * 100)
if ($hasUI) {
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Uninstalling Chocolatey packages ($position/$totalPackages)" -Percent $startPercent
}
Install-WinUtilProgramChoco -Action Uninstall -Programs $packagesChoco Install-WinUtilProgramChoco -Action Uninstall -Programs $packagesChoco
$completedPackages += @($packagesChoco).Count
$completedPercent = [int](($completedPackages / $totalPackages) * 100)
if ($hasUI) {
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Uninstalled Chocolatey packages ($completedPackages/$totalPackages)" -Percent $completedPercent
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -value ($completedPercent / 100) }
}
} }
Hide-WPFInstallAppBusy
Write-Host "===========================================" Write-Host "==========================================="
Write-Host "-- Uninstalls have finished ---" Write-Host "-- Uninstalls have finished ---"
Write-Host "===========================================" Write-Host "==========================================="
Write-WinUtilLog -Component "Uninstall" -Message "Uninstall workflow completed." Write-WinUtilLog -Component "Uninstall" -Message "Uninstall workflow completed."
if ($hasUI) {
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "App uninstall finished" -Percent 100
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" } Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }
}
} catch { } catch {
Hide-WPFInstallAppBusy
Write-Host "===========================================" Write-Host "==========================================="
Write-Host "Error: $_" Write-Host "Error: $_"
Write-Host "===========================================" Write-Host "==========================================="
Write-WinUtilLog -Level "ERROR" -Component "Uninstall" -Message "Uninstall workflow failed: $($_.Exception.Message)" Write-WinUtilLog -Level "ERROR" -Component "Uninstall" -Message "Uninstall workflow failed: $($_.Exception.Message)"
if ($hasUI) {
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "App uninstall failed" -Percent 100
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Error" -overlay "warning" } Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Error" -overlay "warning" }
}
} finally { } finally {
if ($hasUI) {
Invoke-WPFUIThread -ScriptBlock {
if ($null -ne $sync.ItemsControl) {
$sync.ItemsControl.IsEnabled = $true
}
}
}
$sync.ProcessRunning = $False $sync.ProcessRunning = $False
} }
@@ -39,14 +39,12 @@ function Invoke-WPFtweaksbutton {
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Normal" -value 0.01 -overlay "logo" } Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Normal" -value 0.01 -overlay "logo" }
} }
Set-WinUtilProgressBar -Label "Creating restore point" -Percent 0
Set-WinUtilTweaksProgressIndicator -Visible $true -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." Write-WinUtilLog -Component "Tweaks" -Message "Creating restore point before applying selected tweaks."
Invoke-WinUtilTweaks $restorePointTweak Invoke-WinUtilTweaks $restorePointTweak
$completedSteps = 1 $completedSteps = 1
if ($tweaksToRun.Count -eq 0 -and $dnsProvider -eq "Default") { 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 Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Tweaks finished" -Percent 100
$sync.ProcessRunning = $false $sync.ProcessRunning = $false
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" } Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }
@@ -77,14 +75,12 @@ function Invoke-WPFtweaksbutton {
} }
for ($i = 0; $i -lt $tweaks.Count; $i++) { 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) Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Applying $($tweaks[$i]) ($($completedSteps + 1)/$totalSteps)" -Percent ($completedSteps / $totalSteps * 100)
Invoke-WinUtilTweaks $tweaks[$i] Invoke-WinUtilTweaks $tweaks[$i]
$completedSteps++ $completedSteps++
$progress = $completedSteps / $totalSteps $progress = $completedSteps / $totalSteps
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -value $progress } Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -value $progress }
} }
Set-WinUtilProgressBar -Label "Tweaks finished" -Percent 100
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Tweaks finished" -Percent 100 Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Tweaks finished" -Percent 100
$sync.ProcessRunning = $false $sync.ProcessRunning = $false
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" } Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }
-2
View File
@@ -33,13 +33,11 @@ function Invoke-WPFundoall {
for ($i = 0; $i -lt $tweaks.Count; $i++) { 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) Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Undoing $($tweaks[$i]) ($($i + 1)/$($tweaks.Count))" -Percent ($i / $tweaks.Count * 100)
Invoke-WinUtiltweaks $tweaks[$i] -undo $true Invoke-WinUtiltweaks $tweaks[$i] -undo $true
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -value ($i/$tweaks.Count) } 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 Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Undo Tweaks Finished" -Percent 100
$sync.ProcessRunning = $false $sync.ProcessRunning = $false
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" } Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }
-4
View File
@@ -46,9 +46,6 @@ BeforeAll {
function Invoke-WPFUIThread { function Invoke-WPFUIThread {
param([scriptblock]$ScriptBlock) param([scriptblock]$ScriptBlock)
} }
function Set-WinUtilProgressBar {
param($Label, $Percent)
}
function Set-WinUtilTweaksProgressIndicator { function Set-WinUtilTweaksProgressIndicator {
param($Visible, $Label, $Percent) param($Visible, $Label, $Percent)
} }
@@ -235,7 +232,6 @@ Describe "Get installed AppX selection" {
WPFAppxMissing = [pscustomobject]@{ IsChecked = $false } WPFAppxMissing = [pscustomobject]@{ IsChecked = $false }
}) })
Mock Set-WinUtilProgressBar { }
Mock Set-WinUtilTweaksProgressIndicator { } Mock Set-WinUtilTweaksProgressIndicator { }
Mock Get-WinUtilInstalledAPPX { @("Example.Package") } Mock Get-WinUtilInstalledAPPX { @("Example.Package") }
Mock Invoke-WPFAppxInstall { } Mock Invoke-WPFAppxInstall { }
+61 -17
View File
@@ -18,10 +18,9 @@ BeforeAll {
function Get-WinUtilSelectedPackages { function Get-WinUtilSelectedPackages {
param($PackageList, [string]$Preference) param($PackageList, [string]$Preference)
} }
function Show-WPFInstallAppBusy { function Set-WinUtilTweaksProgressIndicator {
param($text) param($Visible, $Label, $Percent)
} }
function Hide-WPFInstallAppBusy { }
function Install-WinUtilWinget { } function Install-WinUtilWinget { }
function Install-WinUtilChoco { } function Install-WinUtilChoco { }
function Install-WinUtilProgramWinget { function Install-WinUtilProgramWinget {
@@ -74,6 +73,9 @@ BeforeAll {
preferences = [pscustomobject]@{ preferences = [pscustomobject]@{
packagemanager = "Winget" packagemanager = "Winget"
} }
Form = [pscustomobject]@{
Dispatcher = [pscustomobject]@{}
}
configs = @{ configs = @{
applicationsHashtable = $applications applicationsHashtable = $applications
} }
@@ -187,8 +189,7 @@ Describe "Invoke-WPFInstall runspace body" {
Mock Get-WinUtilSelectedPackages { Mock Get-WinUtilSelectedPackages {
New-WinUtilPackageSplit -Winget @("Git.Git") -Choco @("vlc") New-WinUtilPackageSplit -Winget @("Git.Git") -Choco @("vlc")
} }
Mock Show-WPFInstallAppBusy { } Mock Set-WinUtilTweaksProgressIndicator { }
Mock Hide-WPFInstallAppBusy { }
Mock Install-WinUtilWinget { } Mock Install-WinUtilWinget { }
Mock Install-WinUtilChoco { } Mock Install-WinUtilChoco { }
Mock Install-WinUtilProgramWinget { } Mock Install-WinUtilProgramWinget { }
@@ -212,8 +213,17 @@ Describe "Invoke-WPFInstall runspace body" {
Should -Invoke -CommandName Get-WinUtilSelectedPackages -Times 1 -Exactly -ParameterFilter { Should -Invoke -CommandName Get-WinUtilSelectedPackages -Times 1 -Exactly -ParameterFilter {
@($PackageList).Count -eq 1 -and $Preference -eq "Winget" @($PackageList).Count -eq 1 -and $Preference -eq "Winget"
} }
Should -Invoke -CommandName Show-WPFInstallAppBusy -Times 1 -Exactly -ParameterFilter { Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
$text -eq "Installing apps..." $Visible -eq $true -and $Label -eq "Preparing app install (0/2)" -and $Percent -eq 0
}
Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
$Visible -eq $true -and $Label -eq "Installed Git.Git (1/2)" -and $Percent -eq 50
}
Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
$Visible -eq $true -and $Label -eq "Installed Chocolatey packages (2/2)" -and $Percent -eq 100
}
Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
$Visible -eq $true -and $Label -eq "App install finished" -and $Percent -eq 100
} }
Should -Invoke -CommandName Install-WinUtilWinget -Times 1 -Exactly Should -Invoke -CommandName Install-WinUtilWinget -Times 1 -Exactly
Should -Invoke -CommandName Install-WinUtilProgramWinget -Times 1 -Exactly -ParameterFilter { Should -Invoke -CommandName Install-WinUtilProgramWinget -Times 1 -Exactly -ParameterFilter {
@@ -223,21 +233,34 @@ Describe "Invoke-WPFInstall runspace body" {
Should -Invoke -CommandName Install-WinUtilProgramChoco -Times 1 -Exactly -ParameterFilter { Should -Invoke -CommandName Install-WinUtilProgramChoco -Times 1 -Exactly -ParameterFilter {
$Action -eq "Install" -and @($Programs)[0] -eq "vlc" $Action -eq "Install" -and @($Programs)[0] -eq "vlc"
} }
Should -Invoke -CommandName Hide-WPFInstallAppBusy -Times 1 -Exactly Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
$ScriptBlock.ToString() -like '*$sync.ItemsControl.IsEnabled = $false*'
}
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
$ScriptBlock.ToString() -like '*$sync.ItemsControl.IsEnabled = $true*'
}
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter { Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
$ScriptBlock.ToString() -like '*Set-WinUtilTaskbaritem -state "None" -overlay "checkmark"*' $ScriptBlock.ToString() -like '*Set-WinUtilTaskbaritem -state "None" -overlay "checkmark"*'
} }
$script:sync.ProcessRunning | Should -BeFalse $script:sync.ProcessRunning | Should -BeFalse
} }
It "hides the busy overlay, sets taskbar error state, and clears ProcessRunning on failure" { It "shows failure progress, sets taskbar error state, and clears ProcessRunning on failure" {
Mock Install-WinUtilProgramWinget { throw "winget failed" } Mock Install-WinUtilProgramWinget { throw "winget failed" }
Invoke-WPFInstall Invoke-WPFInstall
& $script:capturedInstallScriptBlock -PackagesToInstall @($script:package) -ManagerPreference "Winget" & $script:capturedInstallScriptBlock -PackagesToInstall @($script:package) -ManagerPreference "Winget"
Should -Invoke -CommandName Hide-WPFInstallAppBusy -Times 1 -Exactly Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
$Visible -eq $true -and $Label -eq "App install failed" -and $Percent -eq 100
}
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
$ScriptBlock.ToString() -like '*$sync.ItemsControl.IsEnabled = $false*'
}
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
$ScriptBlock.ToString() -like '*$sync.ItemsControl.IsEnabled = $true*'
}
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter { Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
$ScriptBlock.ToString() -like '*Set-WinUtilTaskbaritem -state "Error" -overlay "warning"*' $ScriptBlock.ToString() -like '*Set-WinUtilTaskbaritem -state "Error" -overlay "warning"*'
} }
@@ -345,8 +368,7 @@ Describe "Invoke-WPFUnInstall runspace body" {
Mock Get-WinUtilSelectedPackages { Mock Get-WinUtilSelectedPackages {
New-WinUtilPackageSplit -Winget @("Git.Git") -Choco @("vlc") New-WinUtilPackageSplit -Winget @("Git.Git") -Choco @("vlc")
} }
Mock Show-WPFInstallAppBusy { } Mock Set-WinUtilTweaksProgressIndicator { }
Mock Hide-WPFInstallAppBusy { }
Mock Install-WinUtilProgramWinget { } Mock Install-WinUtilProgramWinget { }
Mock Install-WinUtilProgramChoco { } Mock Install-WinUtilProgramChoco { }
Mock Invoke-WPFUIThread { } Mock Invoke-WPFUIThread { }
@@ -369,8 +391,17 @@ Describe "Invoke-WPFUnInstall runspace body" {
Should -Invoke -CommandName Get-WinUtilSelectedPackages -Times 1 -Exactly -ParameterFilter { Should -Invoke -CommandName Get-WinUtilSelectedPackages -Times 1 -Exactly -ParameterFilter {
@($PackageList).Count -eq 1 -and $Preference -eq "Winget" @($PackageList).Count -eq 1 -and $Preference -eq "Winget"
} }
Should -Invoke -CommandName Show-WPFInstallAppBusy -Times 1 -Exactly -ParameterFilter { Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
$text -eq "Uninstalling apps..." $Visible -eq $true -and $Label -eq "Preparing app uninstall (0/2)" -and $Percent -eq 0
}
Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
$Visible -eq $true -and $Label -eq "Uninstalled Git.Git (1/2)" -and $Percent -eq 50
}
Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
$Visible -eq $true -and $Label -eq "Uninstalled Chocolatey packages (2/2)" -and $Percent -eq 100
}
Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
$Visible -eq $true -and $Label -eq "App uninstall finished" -and $Percent -eq 100
} }
Should -Invoke -CommandName Install-WinUtilProgramWinget -Times 1 -Exactly -ParameterFilter { Should -Invoke -CommandName Install-WinUtilProgramWinget -Times 1 -Exactly -ParameterFilter {
$Action -eq "Uninstall" -and @($Programs)[0] -eq "Git.Git" $Action -eq "Uninstall" -and @($Programs)[0] -eq "Git.Git"
@@ -378,21 +409,34 @@ Describe "Invoke-WPFUnInstall runspace body" {
Should -Invoke -CommandName Install-WinUtilProgramChoco -Times 1 -Exactly -ParameterFilter { Should -Invoke -CommandName Install-WinUtilProgramChoco -Times 1 -Exactly -ParameterFilter {
$Action -eq "Uninstall" -and @($Programs)[0] -eq "vlc" $Action -eq "Uninstall" -and @($Programs)[0] -eq "vlc"
} }
Should -Invoke -CommandName Hide-WPFInstallAppBusy -Times 1 -Exactly Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
$ScriptBlock.ToString() -like '*$sync.ItemsControl.IsEnabled = $false*'
}
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
$ScriptBlock.ToString() -like '*$sync.ItemsControl.IsEnabled = $true*'
}
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter { Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
$ScriptBlock.ToString() -like '*Set-WinUtilTaskbaritem -state "None" -overlay "checkmark"*' $ScriptBlock.ToString() -like '*Set-WinUtilTaskbaritem -state "None" -overlay "checkmark"*'
} }
$script:sync.ProcessRunning | Should -BeFalse $script:sync.ProcessRunning | Should -BeFalse
} }
It "hides the busy overlay, sets taskbar error state, and clears ProcessRunning on failure" { It "shows failure progress, sets taskbar error state, and clears ProcessRunning on failure" {
Mock Install-WinUtilProgramWinget { throw "winget failed" } Mock Install-WinUtilProgramWinget { throw "winget failed" }
Invoke-WPFUnInstall -PackagesToUninstall @($script:package) Invoke-WPFUnInstall -PackagesToUninstall @($script:package)
& $script:capturedUninstallScriptBlock -PackagesToUninstall @($script:package) -ManagerPreference "Winget" & $script:capturedUninstallScriptBlock -PackagesToUninstall @($script:package) -ManagerPreference "Winget"
Should -Invoke -CommandName Hide-WPFInstallAppBusy -Times 1 -Exactly Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
$Visible -eq $true -and $Label -eq "App uninstall failed" -and $Percent -eq 100
}
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
$ScriptBlock.ToString() -like '*$sync.ItemsControl.IsEnabled = $false*'
}
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
$ScriptBlock.ToString() -like '*$sync.ItemsControl.IsEnabled = $true*'
}
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter { Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
$ScriptBlock.ToString() -like '*Set-WinUtilTaskbaritem -state "Error" -overlay "warning"*' $ScriptBlock.ToString() -like '*Set-WinUtilTaskbaritem -state "Error" -overlay "warning"*'
} }
+17
View File
@@ -69,6 +69,23 @@ Describe "Initialize-WinUtilTabContent" {
$targetGridName -eq "appxpanel" -and $columncount -eq 2 $targetGridName -eq "appxpanel" -and $columncount -eq 2
} }
} }
It "checks for existing Win11ISO work when the tab is initialized" {
Add-Type -AssemblyName WindowsBase
$dispatcher = [pscustomobject]@{}
$dispatcher | Add-Member -MemberType ScriptMethod -Name BeginInvoke -Value {
param($priority, $action)
$action.Invoke()
}
$script:sync.Form = [pscustomobject]@{ Dispatcher = $dispatcher }
Mock Invoke-WinUtilISOCheckExistingWork { }
Initialize-WinUtilTabContent -TabName "Win11ISO"
Initialize-WinUtilTabContent -TabName "Win11ISO"
Should -Invoke -CommandName Invoke-WinUtilISOCheckExistingWork -Times 1 -Exactly
$script:sync.InitializedTabs["Win11ISO"] | Should -BeTrue
}
} }
Describe "Startup lazy tab wiring" { Describe "Startup lazy tab wiring" {
-4
View File
@@ -31,9 +31,6 @@ BeforeAll {
function Invoke-WPFUIThread { function Invoke-WPFUIThread {
param([scriptblock]$ScriptBlock) param([scriptblock]$ScriptBlock)
} }
function Set-WinUtilProgressBar {
param($Label, $Percent)
}
function Set-WinUtilTweaksProgressIndicator { function Set-WinUtilTweaksProgressIndicator {
param($Visible, $Label, $Percent) param($Visible, $Label, $Percent)
} }
@@ -187,7 +184,6 @@ Describe "Invoke-WPFtweaksbutton" {
Mock Invoke-WPFRunspace { [pscustomobject]@{ MockHandle = $true } } Mock Invoke-WPFRunspace { [pscustomobject]@{ MockHandle = $true } }
Mock Invoke-WinUtilTweaks { } Mock Invoke-WinUtilTweaks { }
Mock Invoke-WPFUIThread { } Mock Invoke-WPFUIThread { }
Mock Set-WinUtilProgressBar { }
Mock Write-WinUtilLog { } Mock Write-WinUtilLog { }
Mock Write-Host { } Mock Write-Host { }
} }
+9 -9
View File
@@ -65,10 +65,6 @@ namespace System.Windows.Controls
. (Join-Path $script:repoRoot "functions\public\Invoke-WPFButton.ps1") . (Join-Path $script:repoRoot "functions\public\Invoke-WPFButton.ps1")
. (Join-Path $script:repoRoot "functions\public\Invoke-WPFToggleAllCategories.ps1") . (Join-Path $script:repoRoot "functions\public\Invoke-WPFToggleAllCategories.ps1")
function Set-WinUtilProgressBar {
param($Label, $Percent)
}
function Set-WinUtilTweaksProgressIndicator { function Set-WinUtilTweaksProgressIndicator {
param($Visible, $Label, $Percent) param($Visible, $Label, $Percent)
} }
@@ -303,7 +299,6 @@ Describe "Invoke-WPFToggleAllCategories" {
Describe "Invoke-WPFButton progress cleanup" { Describe "Invoke-WPFButton progress cleanup" {
BeforeEach { BeforeEach {
New-WinUtilUiStateTestContext New-WinUtilUiStateTestContext
Mock Set-WinUtilProgressBar { }
Mock Set-WinUtilTweaksProgressIndicator { } Mock Set-WinUtilTweaksProgressIndicator { }
} }
@@ -317,9 +312,6 @@ Describe "Invoke-WPFButton progress cleanup" {
Invoke-WPFButton -Button "WPFNoOp" 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 { Should -Invoke Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
$Visible -eq $false $Visible -eq $false
} }
@@ -330,7 +322,15 @@ Describe "Invoke-WPFButton progress cleanup" {
Invoke-WPFButton -Button "WPFNoOp" Invoke-WPFButton -Button "WPFNoOp"
Should -Not -Invoke Set-WinUtilProgressBar Should -Not -Invoke Set-WinUtilTweaksProgressIndicator
}
It "leaves progress visible while a Win11 ISO process is running" {
$script:sync.ProcessRunning = $false
$script:sync.Win11ISOProcessRunning = $true
Invoke-WPFButton -Button "WPFNoOp"
Should -Not -Invoke Set-WinUtilTweaksProgressIndicator Should -Not -Invoke Set-WinUtilTweaksProgressIndicator
} }
} }
+16
View File
@@ -6,6 +6,7 @@ Describe "Win11 Creator setup media" {
BeforeAll { BeforeAll {
$script:repoRoot = Resolve-Path (Join-Path $PSScriptRoot "..") $script:repoRoot = Resolve-Path (Join-Path $PSScriptRoot "..")
$script:isoWorkflowPath = Join-Path $script:repoRoot "functions\private\Invoke-WinUtilISO.ps1" $script:isoWorkflowPath = Join-Path $script:repoRoot "functions\private\Invoke-WinUtilISO.ps1"
$script:isoUsbWorkflowPath = Join-Path $script:repoRoot "functions\private\Invoke-WinUtilISOUSB.ps1"
$script:isoScriptPath = Join-Path $script:repoRoot "functions\private\Invoke-WinUtilISOScript.ps1" $script:isoScriptPath = Join-Path $script:repoRoot "functions\private\Invoke-WinUtilISOScript.ps1"
$script:autoUnattendPath = Join-Path $script:repoRoot "tools\autounattend.xml" $script:autoUnattendPath = Join-Path $script:repoRoot "tools\autounattend.xml"
@@ -36,6 +37,9 @@ Describe "Win11 Creator setup media" {
} }
$script:modifyFunction = Get-WinUtilFunctionText -Path $script:isoWorkflowPath -FunctionName "Invoke-WinUtilISOModify" $script:modifyFunction = Get-WinUtilFunctionText -Path $script:isoWorkflowPath -FunctionName "Invoke-WinUtilISOModify"
$script:cleanAndResetFunction = Get-WinUtilFunctionText -Path $script:isoWorkflowPath -FunctionName "Invoke-WinUtilISOCleanAndReset"
$script:exportFunction = Get-WinUtilFunctionText -Path $script:isoWorkflowPath -FunctionName "Invoke-WinUtilISOExport"
$script:writeUsbFunction = Get-WinUtilFunctionText -Path $script:isoUsbWorkflowPath -FunctionName "Invoke-WinUtilISOWriteUSB"
$script:editionIdFunction = Get-WinUtilFunctionText -Path $script:isoWorkflowPath -FunctionName "Get-WinUtilEditionIdFromName" $script:editionIdFunction = Get-WinUtilFunctionText -Path $script:isoWorkflowPath -FunctionName "Get-WinUtilEditionIdFromName"
$script:addDriversFunction = Get-WinUtilFunctionText -Path $script:isoScriptPath -FunctionName "Add-DriversToImage" $script:addDriversFunction = Get-WinUtilFunctionText -Path $script:isoScriptPath -FunctionName "Add-DriversToImage"
$script:answerFileChildElementFunction = Get-WinUtilFunctionText -Path $script:isoScriptPath -FunctionName "Get-WinUtilISOScriptChildElement" $script:answerFileChildElementFunction = Get-WinUtilFunctionText -Path $script:isoScriptPath -FunctionName "Get-WinUtilISOScriptChildElement"
@@ -81,6 +85,18 @@ Describe "Win11 Creator setup media" {
$script:modifyFunction | Should -Not -Match ([regex]::Escape("Reusing existing temp directory")) $script:modifyFunction | Should -Not -Match ([regex]::Escape("Reusing existing temp directory"))
} }
It "tracks every background ISO workflow with the shared busy state" {
foreach ($functionText in @(
$script:modifyFunction,
$script:cleanAndResetFunction,
$script:exportFunction,
$script:writeUsbFunction
)) {
$functionText | Should -Match ([regex]::Escape('$sync["Win11ISOProcessRunning"] = $true'))
$functionText | Should -Match ([regex]::Escape('$sync["Win11ISOProcessRunning"] = $false'))
}
}
It "mounts the copied image file that was verified from the ISO" { It "mounts the copied image file that was verified from the ISO" {
foreach ($expectedText in @( foreach ($expectedText in @(
'$sourceImageFileName = Split-Path $wimPath -Leaf', '$sourceImageFileName = Split-Path $wimPath -Leaf',
+1 -6
View File
@@ -432,20 +432,15 @@ Describe "XAML and sync wiring" {
"InitializedTabs", "InitializedTabs",
"RenderedAssetCache", "RenderedAssetCache",
"ToggleStatusCache", "ToggleStatusCache",
"InstallAppAreaBorder",
"InstallAppAreaScrollViewer",
"InstallAppAreaOverlay",
"InstallAppAreaOverlayText",
"InstallAppRenderQueue", "InstallAppRenderQueue",
"InstallAppEntriesRendered", "InstallAppEntriesRendered",
"ProgressBar",
"progressBarTextBlock",
"FontScaleFactor", "FontScaleFactor",
"Win11ISOImageInfo", "Win11ISOImageInfo",
"Win11ISODriveLetter", "Win11ISODriveLetter",
"Win11ISOWimPath", "Win11ISOWimPath",
"Win11ISOImagePath", "Win11ISOImagePath",
"Win11ISOModifying", "Win11ISOModifying",
"Win11ISOProcessRunning",
"Win11ISOWorkDir", "Win11ISOWorkDir",
"Win11ISOContentsDir", "Win11ISOContentsDir",
"Win11ISOUSBDisks" "Win11ISOUSBDisks"
+1
View File
@@ -62,6 +62,7 @@ $sync.configs = @{}
$sync.Buttons = [System.Collections.Generic.List[PSObject]]::new() $sync.Buttons = [System.Collections.Generic.List[PSObject]]::new()
$sync.preferences = @{} $sync.preferences = @{}
$sync.ProcessRunning = $false $sync.ProcessRunning = $false
$sync.Win11ISOProcessRunning = $false
$sync.selectedAppx = [System.Collections.Generic.List[string]]::new() $sync.selectedAppx = [System.Collections.Generic.List[string]]::new()
$sync.selectedApps = [System.Collections.Generic.List[string]]::new() $sync.selectedApps = [System.Collections.Generic.List[string]]::new()
$sync.selectedTweaks = [System.Collections.Generic.List[string]]::new() $sync.selectedTweaks = [System.Collections.Generic.List[string]]::new()
+1 -1
View File
@@ -1823,7 +1823,7 @@
</TabItem> </TabItem>
</TabControl> </TabControl>
<!-- Tweaks/Undo progress indicator - visible regardless of active tab --> <!-- Window-level progress indicator - visible regardless of active tab -->
<Border Name="WPFTweaksProgressBar" Grid.Row="3" Background="{DynamicResource MainBackgroundColor}" Visibility="Collapsed" Padding="10,6"> <Border Name="WPFTweaksProgressBar" Grid.Row="3" Background="{DynamicResource MainBackgroundColor}" Visibility="Collapsed" Padding="10,6">
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<TextBlock Name="WPFTweaksProgressLabel" Text="" Foreground="{DynamicResource MainForegroundColor}" FontSize="13" Background="Transparent" Margin="0,0,0,4"/> <TextBlock Name="WPFTweaksProgressLabel" Text="" Foreground="{DynamicResource MainForegroundColor}" FontSize="13" Background="Transparent" Margin="0,0,0,4"/>