From eeb113a35345c98a70d16085d511d9ea15bcde06 Mon Sep 17 00:00:00 2001 From: Chris Titus Date: Wed, 1 Jul 2026 22:13:50 -0500 Subject: [PATCH] Lazy initialize non-default tabs --- .../private/Initialize-WinUtilTabContent.ps1 | 45 +++++++++ functions/public/Invoke-WPFTab.ps1 | 1 + pester/lazy-tabs.Tests.ps1 | 94 +++++++++++++++++++ pester/performance.Tests.ps1 | 4 +- pester/xaml.Tests.ps1 | 3 + scripts/main.ps1 | 23 +---- speed-todo.md | 10 +- 7 files changed, 154 insertions(+), 26 deletions(-) create mode 100644 functions/private/Initialize-WinUtilTabContent.ps1 create mode 100644 pester/lazy-tabs.Tests.ps1 diff --git a/functions/private/Initialize-WinUtilTabContent.ps1 b/functions/private/Initialize-WinUtilTabContent.ps1 new file mode 100644 index 00000000..0ac4093f --- /dev/null +++ b/functions/private/Initialize-WinUtilTabContent.ps1 @@ -0,0 +1,45 @@ +function Initialize-WinUtilTabContent { + param( + [Parameter(Mandatory = $true)] + [string]$TabName + ) + + if ($null -eq $sync.InitializedTabs) { + $sync.InitializedTabs = @{} + } + + if ($sync.InitializedTabs[$TabName]) { + return + } + + switch ($TabName) { + "Install" { + Invoke-WPFUIElements -configVariable $sync.configs.appnavigation -targetGridName "appscategory" -columncount 1 + Initialize-WPFUI -targetGridName "appscategory" + Write-WinUtilPerformanceCheckpoint -Name "App navigation UI created" + + Initialize-WPFUI -targetGridName "appspanel" + Write-WinUtilPerformanceCheckpoint -Name "Install UI created" + } + "Tweaks" { + Invoke-WPFUIElements -configVariable $sync.configs.tweaks -targetGridName "tweakspanel" -columncount 2 + Write-WinUtilPerformanceCheckpoint -Name "Tweaks UI created" + } + "Config" { + Invoke-WPFUIElements -configVariable $sync.configs.feature -targetGridName "featurespanel" -columncount 2 + Write-WinUtilPerformanceCheckpoint -Name "Features UI created" + } + "AppX" { + Invoke-WPFUIElements -configVariable $sync.configs.appx -targetGridName "appxpanel" -columncount 2 + Write-WinUtilPerformanceCheckpoint -Name "AppX UI created" + } + "Win11 Creator" { + if ($sync.Form -and $sync.Form.Dispatcher) { + $sync.Form.Dispatcher.BeginInvoke([System.Windows.Threading.DispatcherPriority]::Background, [action]{ Invoke-WinUtilISOCheckExistingWork }) | Out-Null + } + Write-WinUtilPerformanceCheckpoint -Name "Win11 ISO tab initialized" + } + } + + $sync.InitializedTabs[$TabName] = $true +} diff --git a/functions/public/Invoke-WPFTab.ps1 b/functions/public/Invoke-WPFTab.ps1 index 735b38a7..8640318f 100644 --- a/functions/public/Invoke-WPFTab.ps1 +++ b/functions/public/Invoke-WPFTab.ps1 @@ -29,6 +29,7 @@ function Invoke-WPFTab { } } $sync.currentTab = $sync.$tabNav.Items[$tabNumber].Header + Initialize-WinUtilTabContent -TabName $sync.currentTab # Always reset the filter for the current tab if ($sync.currentTab -eq "Install") { diff --git a/pester/lazy-tabs.Tests.ps1 b/pester/lazy-tabs.Tests.ps1 new file mode 100644 index 00000000..7df27972 --- /dev/null +++ b/pester/lazy-tabs.Tests.ps1 @@ -0,0 +1,94 @@ +#=========================================================================== +# Tests - Lazy tab initialization +#=========================================================================== + +BeforeAll { + $script:repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path + + function Invoke-WPFUIElements { + param($configVariable, [string]$targetGridName, [int]$columncount) + } + function Initialize-WPFUI { + param([string]$TargetGridName) + } + function Write-WinUtilPerformanceCheckpoint { + param([string]$Name) + } + function Invoke-WinUtilISOCheckExistingWork { } + + . (Join-Path $script:repoRoot "functions\private\Initialize-WinUtilTabContent.ps1") +} + +Describe "Initialize-WinUtilTabContent" { + BeforeEach { + $script:sync = [Hashtable]::Synchronized(@{ + configs = @{ + appnavigation = [pscustomobject]@{} + tweaks = [pscustomobject]@{} + feature = [pscustomobject]@{} + appx = [pscustomobject]@{} + } + }) + + Mock Invoke-WPFUIElements { } + Mock Initialize-WPFUI { } + Mock Write-WinUtilPerformanceCheckpoint { } + } + + AfterEach { + Remove-Variable -Name sync -Scope Script -ErrorAction SilentlyContinue + } + + It "initializes the install tab once" { + Initialize-WinUtilTabContent -TabName "Install" + Initialize-WinUtilTabContent -TabName "Install" + + Should -Invoke -CommandName Invoke-WPFUIElements -Times 1 -Exactly -ParameterFilter { + $targetGridName -eq "appscategory" -and $columncount -eq 1 + } + Should -Invoke -CommandName Initialize-WPFUI -Times 1 -Exactly -ParameterFilter { + $TargetGridName -eq "appscategory" + } + Should -Invoke -CommandName Initialize-WPFUI -Times 1 -Exactly -ParameterFilter { + $TargetGridName -eq "appspanel" + } + $script:sync.InitializedTabs["Install"] | Should -BeTrue + } + + It "initializes deferred config-backed tabs once" { + Initialize-WinUtilTabContent -TabName "Tweaks" + Initialize-WinUtilTabContent -TabName "Config" + Initialize-WinUtilTabContent -TabName "AppX" + Initialize-WinUtilTabContent -TabName "Tweaks" + Initialize-WinUtilTabContent -TabName "Config" + Initialize-WinUtilTabContent -TabName "AppX" + + Should -Invoke -CommandName Invoke-WPFUIElements -Times 1 -Exactly -ParameterFilter { + $targetGridName -eq "tweakspanel" -and $columncount -eq 2 + } + Should -Invoke -CommandName Invoke-WPFUIElements -Times 1 -Exactly -ParameterFilter { + $targetGridName -eq "featurespanel" -and $columncount -eq 2 + } + Should -Invoke -CommandName Invoke-WPFUIElements -Times 1 -Exactly -ParameterFilter { + $targetGridName -eq "appxpanel" -and $columncount -eq 2 + } + } +} + +Describe "Startup lazy tab wiring" { + It "builds only install tab content before first paint" { + $mainScript = Get-Content -Path (Join-Path $script:repoRoot "scripts\main.ps1") -Raw + $startupRegion = $mainScript.Substring(0, $mainScript.IndexOf("# Store Form Objects In PowerShell")) + + $startupRegion | Should -Match 'Initialize-WinUtilTabContent -TabName "Install"' + $startupRegion | Should -Not -Match 'targetGridName "tweakspanel"' + $startupRegion | Should -Not -Match 'targetGridName "featurespanel"' + $startupRegion | Should -Not -Match 'targetGridName "appxpanel"' + } + + It "initializes tab content when a tab is selected" { + $tabScript = Get-Content -Path (Join-Path $script:repoRoot "functions\public\Invoke-WPFTab.ps1") -Raw + + $tabScript | Should -Match 'Initialize-WinUtilTabContent -TabName \$sync\.currentTab' + } +} diff --git a/pester/performance.Tests.ps1 b/pester/performance.Tests.ps1 index 73876398..fedbfa16 100644 --- a/pester/performance.Tests.ps1 +++ b/pester/performance.Tests.ps1 @@ -69,6 +69,8 @@ Describe "Startup performance checkpoints" { It "adds runtime checkpoints for startup hotspots" { $mainScript = Get-Content -Path (Join-Path $script:repoRoot "scripts\main.ps1") -Raw + $lazyTabScript = Get-Content -Path (Join-Path $script:repoRoot "functions\private\Initialize-WinUtilTabContent.ps1") -Raw + $startupText = "$mainScript`n$lazyTabScript" foreach ($checkpoint in @( "Runspace pool initialized", @@ -81,7 +83,7 @@ Describe "Startup performance checkpoints" { "Assets rendered", "First content rendered" )) { - $mainScript | Should -Match ([regex]::Escape($checkpoint)) + $startupText | Should -Match ([regex]::Escape($checkpoint)) } } } diff --git a/pester/xaml.Tests.ps1 b/pester/xaml.Tests.ps1 index 05e35bf2..3966c1de 100644 --- a/pester/xaml.Tests.ps1 +++ b/pester/xaml.Tests.ps1 @@ -298,6 +298,9 @@ Describe "XAML and sync wiring" { "logorender", "checkmarkrender", "warningrender", + "InitializedTabs", + "PerformanceTrace", + "PerformanceTraceEnabled", "InstallAppAreaBorder", "InstallAppAreaScrollViewer", "InstallAppAreaOverlay", diff --git a/scripts/main.ps1 b/scripts/main.ps1 index 970972e4..4c63dc57 100644 --- a/scripts/main.ps1 +++ b/scripts/main.ps1 @@ -186,22 +186,9 @@ Invoke-WinutilThemeChange -theme $sync.preferences.theme Write-WinUtilPerformanceCheckpoint -Name "Theme applied" -# Now call the function with the final merged config -Invoke-WPFUIElements -configVariable $sync.configs.appnavigation -targetGridName "appscategory" -columncount 1 -Initialize-WPFUI -targetGridName "appscategory" -Write-WinUtilPerformanceCheckpoint -Name "App navigation UI created" - -Initialize-WPFUI -targetGridName "appspanel" -Write-WinUtilPerformanceCheckpoint -Name "Install UI created" - -Invoke-WPFUIElements -configVariable $sync.configs.tweaks -targetGridName "tweakspanel" -columncount 2 -Write-WinUtilPerformanceCheckpoint -Name "Tweaks UI created" - -Invoke-WPFUIElements -configVariable $sync.configs.feature -targetGridName "featurespanel" -columncount 2 -Write-WinUtilPerformanceCheckpoint -Name "Features UI created" - -Invoke-WPFUIElements -configVariable $sync.configs.appx -targetGridName "appxpanel" -columncount 2 -Write-WinUtilPerformanceCheckpoint -Name "AppX UI created" +# Build only the default tab before first paint; other tabs initialize on first activation. +$sync.InitializedTabs = @{} +Initialize-WinUtilTabContent -TabName "Install" # Future implementation: Add Windows Version to updates panel #Invoke-WPFUIElements -configVariable $sync.configs.updates -targetGridName "updatespanel" -columncount 1 @@ -527,10 +514,6 @@ $sync["FontScalingApplyButton"].Add_Click({ # ── Win11ISO Tab button handlers ────────────────────────────────────────────── -$sync["WPFTab5BT"].Add_Click({ - $sync["Form"].Dispatcher.BeginInvoke([System.Windows.Threading.DispatcherPriority]::Background, [action]{ Invoke-WinUtilISOCheckExistingWork }) | Out-Null -}) - $sync["WPFWin11ISOBrowseButton"].Add_Click({ Invoke-WinUtilISOBrowse }) diff --git a/speed-todo.md b/speed-todo.md index 39b86fba..abf03977 100644 --- a/speed-todo.md +++ b/speed-todo.md @@ -12,11 +12,11 @@ WinUtil currently does too much work before the first GUI paint. Work from the t ## P1 - Faster First Paint -- [ ] Show the main window before building every non-visible panel. -- [ ] Build only the default visible tab during startup. -- [ ] Move Tweaks, Features, AppX, and Win11 ISO tab initialization to first tab activation. -- [ ] Add one-time guards so each lazy tab initializes only once. -- [ ] Preserve current behavior for `-Preset` and `-Config` automation paths. +- [x] Show the main window before building every non-visible panel. +- [x] Build only the default visible tab during startup. +- [x] Move Tweaks, Features, AppX, and Win11 ISO tab initialization to first tab activation. +- [x] Add one-time guards so each lazy tab initializes only once. +- [x] Preserve current behavior for `-Preset` and `-Config` automation paths. ## P2 - Install Tab Rendering