function Show-ApplyModal { param ( [Parameter(Mandatory=$false)] [System.Windows.Window]$Owner = $null, [Parameter(Mandatory=$false)] [bool]$RestartExplorer = $false ) Add-Type -AssemblyName PresentationFramework,PresentationCore,WindowsBase | Out-Null $usesDarkMode = GetSystemUsesDarkMode # Determine owner window $ownerWindow = if ($Owner) { $Owner } else { $script:GuiWindow } # Show overlay if owner window exists $overlay = $null if ($ownerWindow) { try { $overlay = $ownerWindow.FindName('ModalOverlay') if ($overlay) { $ownerWindow.Dispatcher.Invoke([action]{ $overlay.Visibility = 'Visible' }) } } catch { } } # Load XAML from file $xaml = Get-Content -Path $script:ApplyChangesWindowSchema -Raw $reader = [System.Xml.XmlReader]::Create([System.IO.StringReader]::new($xaml)) try { $applyWindow = [System.Windows.Markup.XamlReader]::Load($reader) } finally { $reader.Close() } # Set owner to owner window if it exists if ($ownerWindow) { try { $applyWindow.Owner = $ownerWindow } catch { } } # Apply theme resources SetWindowThemeResources -window $applyWindow -usesDarkMode $usesDarkMode # Get UI elements $script:ApplyInProgressPanel = $applyWindow.FindName('ApplyInProgressPanel') $script:ApplyCompletionPanel = $applyWindow.FindName('ApplyCompletionPanel') $script:ApplyStepNameEl = $applyWindow.FindName('ApplyStepName') $script:ApplyStepCounterEl = $applyWindow.FindName('ApplyStepCounter') $script:ApplyProgressBarEl = $applyWindow.FindName('ApplyProgressBar') $script:ApplyCompletionTitleEl = $applyWindow.FindName('ApplyCompletionTitle') $script:ApplyCompletionMessageEl = $applyWindow.FindName('ApplyCompletionMessage') $script:ApplyCompletionIconEl = $applyWindow.FindName('ApplyCompletionIcon') $applyCloseBtn = $applyWindow.FindName('ApplyCloseBtn') $applyKofiBtn = $applyWindow.FindName('ApplyKofiBtn') $applyCancelBtn = $applyWindow.FindName('ApplyCancelBtn') # Initialize in-progress state $script:ApplyInProgressPanel.Visibility = 'Visible' $script:ApplyCompletionPanel.Visibility = 'Collapsed' $script:ApplyStepNameEl.Text = "Preparing..." $script:ApplyStepCounterEl.Text = "Preparing..." $script:ApplyProgressBarEl.Value = 0 # Set up progress callback for ExecuteAllChanges $script:ApplyProgressCallback = { param($currentStep, $totalSteps, $stepName) $script:ApplyStepNameEl.Text = $stepName $script:ApplyStepCounterEl.Text = "Step $currentStep of $totalSteps" # Store current step/total in Tag properties for sub-step interpolation $script:ApplyStepCounterEl.Tag = $currentStep $script:ApplyProgressBarEl.Tag = $totalSteps # Show progress at the start of each step (empty at step 1, full after last step completes) $pct = if ($totalSteps -gt 0) { [math]::Round((($currentStep - 1) / $totalSteps) * 100) } else { 0 } $script:ApplyProgressBarEl.Value = $pct # Process pending window messages to keep UI responsive DoEvents } # Sub-step callback updates step name and interpolates progress bar within the current step $script:ApplySubStepCallback = { param($subStepName, $subIndex, $subCount) $script:ApplyStepNameEl.Text = $subStepName # Interpolate progress bar between previous step and current step $currentStep = [int]($script:ApplyStepCounterEl.Tag) $totalSteps = [int]($script:ApplyProgressBarEl.Tag) if ($totalSteps -gt 0 -and $subCount -gt 0) { $baseProgress = ($currentStep - 1) / $totalSteps $stepFraction = ($subIndex / $subCount) / $totalSteps $script:ApplyProgressBarEl.Value = [math]::Round(($baseProgress + $stepFraction) * 100) } DoEvents } # Run changes in background to keep UI responsive $applyWindow.Dispatcher.BeginInvoke([System.Windows.Threading.DispatcherPriority]::Background, [action]{ try { ExecuteAllChanges # Restart explorer if requested if ($RestartExplorer -and -not $script:CancelRequested) { RestartExplorer } Write-Host "" if ($script:CancelRequested) { Write-Host "Script execution was cancelled by the user. Some changes may not have been applied." } else { Write-Host "All changes have been applied successfully!" } # Show completion state $script:ApplyProgressBarEl.Value = 100 $script:ApplyInProgressPanel.Visibility = 'Collapsed' $script:ApplyCompletionPanel.Visibility = 'Visible' if ($script:CancelRequested) { $script:ApplyCompletionIconEl.Text = [char]0xE7BA $script:ApplyCompletionIconEl.Foreground = [System.Windows.Media.SolidColorBrush]::new([System.Windows.Media.ColorConverter]::ConvertFromString("#e8912d")) $script:ApplyCompletionTitleEl.Text = "Cancelled" $script:ApplyCompletionMessageEl.Text = "Script execution was cancelled by the user." } else { $script:ApplyCompletionTitleEl.Text = "Changes Applied" $script:ApplyCompletionMessageEl.Text = "Your clean system is ready. Thanks for using Win11Debloat!" } $applyWindow.Dispatcher.Invoke([System.Windows.Threading.DispatcherPriority]::Render, [action]{}) } catch { Write-Host "Error: $($_.Exception.Message)" $script:ApplyInProgressPanel.Visibility = 'Collapsed' $script:ApplyCompletionPanel.Visibility = 'Visible' $script:ApplyCompletionIconEl.Text = [char]0xEA39 $script:ApplyCompletionIconEl.Foreground = [System.Windows.Media.SolidColorBrush]::new([System.Windows.Media.ColorConverter]::ConvertFromString("#c42b1c")) $script:ApplyCompletionTitleEl.Text = "Error" $script:ApplyCompletionMessageEl.Text = "An error occurred while applying changes: $($_.Exception.Message)" $applyWindow.Dispatcher.Invoke([System.Windows.Threading.DispatcherPriority]::Render, [action]{}) } finally { $script:ApplyProgressCallback = $null $script:ApplySubStepCallback = $null } }) # Button handlers $applyCloseBtn.Add_Click({ $applyWindow.Close() }) $applyKofiBtn.Add_Click({ Start-Process "https://ko-fi.com/raphire" }) $applyCancelBtn.Add_Click({ if ($script:ApplyCompletionPanel.Visibility -eq 'Visible') { # Completion state - just close $applyWindow.Close() } else { # In-progress state - request cancellation $script:CancelRequested = $true } }) # Show dialog $applyWindow.ShowDialog() | Out-Null # Hide overlay after dialog closes if ($overlay) { try { $ownerWindow.Dispatcher.Invoke([action]{ $overlay.Visibility = 'Collapsed' }) } catch { } } }