2026-06-05 21:18:14 +02:00
function Show-MainWindow {
2026-02-15 23:08:54 +01:00
Add-Type -AssemblyName PresentationFramework , PresentationCore , WindowsBase , System . Windows . Forms | Out-Null
2026-06-05 21:18:14 +02:00
# ---- Constrain maximized window to taskbar work area ----
2026-04-04 14:43:15 +02:00
if ( -not ( [ System.Management.Automation.PSTypeName ] 'Win11Debloat.MaximizedWindowHelper' ) . Type ) {
Add-Type -Namespace Win11Debloat -Name MaximizedWindowHelper `
-ReferencedAssemblies 'PresentationFramework' , 'System.Windows.Forms' , 'System.Drawing' `
-MemberDefinition @ '
[ System . Runtime . InteropServices . StructLayout ( System . Runtime . InteropServices . LayoutKind . Sequential ) ]
private struct MINMAXINFO {
public POINT ptReserved , ptMaxSize , ptMaxPosition , ptMinTrackSize , ptMaxTrackSize ;
}
[ System . Runtime . InteropServices . StructLayout ( System . Runtime . InteropServices . LayoutKind . Sequential ) ]
private struct POINT { public int x , y ; }
2026-04-24 21:18:46 +02:00
[ System . Runtime . InteropServices . DllImport ( " user32.dll " ) ]
private static extern System . IntPtr MonitorFromWindow ( System . IntPtr hwnd , uint dwFlags ) ;
[ System . Runtime . InteropServices . DllImport ( " user32.dll " , CharSet = System . Runtime . InteropServices . CharSet . Auto ) ]
private static extern bool GetMonitorInfo ( System . IntPtr hMonitor , ref MONITORINFO lpmi ) ;
[ System . Runtime . InteropServices . StructLayout ( System . Runtime . InteropServices . LayoutKind . Sequential ) ]
private struct RECT {
public int Left , Top , Right , Bottom ;
}
[ System . Runtime . InteropServices . StructLayout ( System . Runtime . InteropServices . LayoutKind . Sequential , CharSet = System . Runtime . InteropServices . CharSet . Auto ) ]
private struct MONITORINFO {
public int cbSize ;
public RECT rcMonitor ;
public RECT rcWork ;
public uint dwFlags ;
}
2026-04-04 14:43:15 +02:00
public static System . IntPtr WmGetMinMaxInfoHook (
System . IntPtr hwnd , int msg , System . IntPtr wParam , System . IntPtr lParam , ref bool handled ) {
if ( msg = = 0x0024 ) { / / WM_GETMINMAXINFO
var mmi = ( MINMAXINFO ) System . Runtime . InteropServices . Marshal . PtrToStructure (
lParam , typeof ( MINMAXINFO ) ) ;
2026-04-24 21:18:46 +02:00
const uint MONITOR_DEFAULTTONEAREST = 0x00000002 ;
var monitor = MonitorFromWindow ( hwnd , MONITOR_DEFAULTTONEAREST ) ;
var monitorInfo = new MONITORINFO ( ) ;
monitorInfo . cbSize = System . Runtime . InteropServices . Marshal . SizeOf ( typeof ( MONITORINFO ) ) ;
if ( monitor ! = System . IntPtr . Zero & & GetMonitorInfo ( monitor , ref monitorInfo ) ) {
mmi . ptMaxPosition . x = monitorInfo . rcWork . Left - monitorInfo . rcMonitor . Left ;
mmi . ptMaxPosition . y = monitorInfo . rcWork . Top - monitorInfo . rcMonitor . Top ;
mmi . ptMaxSize . x = monitorInfo . rcWork . Right - monitorInfo . rcWork . Left ;
mmi . ptMaxSize . y = monitorInfo . rcWork . Bottom - monitorInfo . rcWork . Top ;
}
else {
var screen = System . Windows . Forms . Screen . FromHandle ( hwnd ) ;
var wa = screen . WorkingArea ;
var bounds = screen . Bounds ;
mmi . ptMaxPosition . x = wa . Left - bounds . Left ;
mmi . ptMaxPosition . y = wa . Top - bounds . Top ;
mmi . ptMaxSize . x = wa . Width ;
mmi . ptMaxSize . y = wa . Height ;
}
2026-04-04 14:43:15 +02:00
System . Runtime . InteropServices . Marshal . StructureToPtr ( mmi , lParam , true ) ;
}
return System . IntPtr . Zero ;
}
' @
}
2026-02-15 23:08:54 +01:00
$WinVersion = Get-ItemPropertyValue 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' CurrentBuild
$usesDarkMode = GetSystemUsesDarkMode
2026-06-05 21:18:14 +02:00
# ---- Load XAML ----
2026-02-15 23:08:54 +01:00
$xaml = Get-Content -Path $script:MainWindowSchema -Raw
$reader = [ System.Xml.XmlReader ] :: Create ( [ System.IO.StringReader ] :: new ( $xaml ) )
try {
$window = [ System.Windows.Markup.XamlReader ] :: Load ( $reader )
}
finally {
$reader . Close ( )
}
SetWindowThemeResources -window $window -usesDarkMode $usesDarkMode
2026-04-02 00:00:32 +02:00
$mainBorder = $window . FindName ( 'MainBorder' )
$titleBarBackground = $window . FindName ( 'TitleBarBackground' )
2026-02-15 23:08:54 +01:00
$kofiBtn = $window . FindName ( 'KofiBtn' )
$menuBtn = $window . FindName ( 'MenuBtn' )
$closeBtn = $window . FindName ( 'CloseBtn' )
$menuDocumentation = $window . FindName ( 'MenuDocumentation' )
$menuReportBug = $window . FindName ( 'MenuReportBug' )
$menuLogs = $window . FindName ( 'MenuLogs' )
$menuAbout = $window . FindName ( 'MenuAbout' )
2026-03-27 20:33:24 +01:00
$importConfigBtn = $window . FindName ( 'ImportConfigBtn' )
$exportConfigBtn = $window . FindName ( 'ExportConfigBtn' )
2026-05-08 21:19:52 +02:00
$restoreBackupBtn = $window . FindName ( 'RestoreBackupBtn' )
2026-06-05 21:18:14 +02:00
$homeContentPanel = $window . FindName ( 'HomeContentPanel' )
$contentGrid = $window . FindName ( 'ContentGrid' )
$maxContentWidth = 1600.0
2026-02-15 23:08:54 +01:00
2026-04-02 00:00:32 +02:00
$windowStateNormal = [ System.Windows.WindowState ] :: Normal
$windowStateMaximized = [ System.Windows.WindowState ] :: Maximized
$normalWindowShadow = $mainBorder . Effect
$initialNormalMaxWidth = 1400.0
2026-06-05 21:18:14 +02:00
$script:MainWindow = $window
$script:GuiWindow = $window
2026-03-27 21:14:22 +01:00
2026-06-05 21:18:14 +02:00
# ---- Handle unhandled exceptions on the dispatcher thread ----
[ System.Windows.Threading.Dispatcher ] :: CurrentDispatcher . Add_UnhandledException ( {
param ( $sender , $e )
Write-Warning " Unhandled exception in GUI: $( $e . Exception . Message ) "
Write-Warning " Stack trace: $( $e . Exception . StackTrace ) "
$e . Handled = $true
} )
2026-04-02 00:00:32 +02:00
2026-06-05 21:18:14 +02:00
# ---- Window chrome helpers ----
$updateWindowChrome = { Update-MainWindowChrome -Window $window -MainBorder $mainBorder -TitleBarBackground $titleBarBackground -NormalWindowShadow $normalWindowShadow }
$applyInitialWindowSize = { Set-MainWindowInitialSize -Window $window -InitialNormalMaxWidth $initialNormalMaxWidth }
$updateContentMargin = { Update-MainWindowContentMargin -Window $window -ContentGrid $contentGrid -MaxContentWidth $maxContentWidth }
$updateHomeContentPosition = { Update-MainWindowHomeContentPosition -Window $window -HomeContentPanel $homeContentPanel }
2026-04-02 00:00:32 +02:00
2026-06-05 21:18:14 +02:00
# ---- Window chrome event wiring ----
2026-04-02 00:00:32 +02:00
$window . Add_SourceInitialized ( {
& $applyInitialWindowSize
2026-04-04 14:43:15 +02:00
& $updateWindowChrome
$hwndHelper = New-Object System . Windows . Interop . WindowInteropHelper ( $window )
$hwndSource = [ System.Windows.Interop.HwndSource ] :: FromHwnd ( $hwndHelper . Handle )
$hookMethod = [ Win11Debloat.MaximizedWindowHelper ] . GetMethod ( 'WmGetMinMaxInfoHook' )
$hook = [ System.Delegate ] :: CreateDelegate ( [ System.Windows.Interop.HwndSourceHook ] , $hookMethod )
$hwndSource . AddHook ( $hook )
2026-03-27 21:14:22 +01:00
} )
2026-04-29 17:05:53 +02:00
$window . Add_SizeChanged ( {
& $updateContentMargin
2026-06-03 21:16:10 +02:00
& $updateHomeContentPosition
2026-06-05 21:18:14 +02:00
Update-TweaksResponsiveColumns -Window $window
2026-04-29 17:05:53 +02:00
} )
2026-04-04 14:43:15 +02:00
2026-06-05 21:18:14 +02:00
$window . Add_StateChanged ( { & $updateWindowChrome } )
2026-04-02 00:00:32 +02:00
$window . Add_LocationChanged ( {
2026-04-04 14:43:15 +02:00
if ( $script:BubblePopup -and $script:BubblePopup . IsOpen ) {
$script:BubblePopup . HorizontalOffset + = 1
$script:BubblePopup . HorizontalOffset - = 1
2026-02-15 23:08:54 +01:00
}
} )
2026-06-05 21:18:14 +02:00
# ---- Menu/button event wiring ----
$kofiBtn . Add_Click ( { Start-Process " https://ko-fi.com/raphire " } )
2026-02-15 23:08:54 +01:00
$menuBtn . Add_Click ( {
$menuBtn . ContextMenu . PlacementTarget = $menuBtn
$menuBtn . ContextMenu . Placement = [ System.Windows.Controls.Primitives.PlacementMode ] :: Bottom
$menuBtn . ContextMenu . IsOpen = $true
} )
2026-06-05 21:18:14 +02:00
$menuDocumentation . Add_Click ( { Start-Process " https://github.com/Raphire/Win11Debloat/wiki " } )
$menuReportBug . Add_Click ( { Start-Process " https://github.com/Raphire/Win11Debloat/issues " } )
2026-02-15 23:08:54 +01:00
$menuLogs . Add_Click ( {
2026-05-08 21:19:52 +02:00
$logsFolder = Join-Path ( Split-Path ( Split-Path $PSScriptRoot -Parent ) -Parent ) 'Logs'
2026-02-15 23:08:54 +01:00
if ( Test-Path $logsFolder ) {
Start-Process " explorer.exe " -ArgumentList $logsFolder
}
else {
Show-MessageBox -Message " No logs folder found at: $logsFolder " -Title " Logs " -Button 'OK' -Icon 'Information'
}
} )
2026-06-05 21:18:14 +02:00
$menuAbout . Add_Click ( { Show-AboutDialog -Owner $window } )
$closeBtn . Add_Click ( { $window . Close ( ) } )
$window . Add_Closing ( { $script:CancelRequested = $true } )
# ---- App Selection panel elements ----
$appsPanel = $window . FindName ( 'AppSelectionPanel' )
$onlyInstalledAppsBox = $window . FindName ( 'OnlyInstalledAppsBox' )
$loadingAppsIndicator = $window . FindName ( 'LoadingAppsIndicator' )
$appSelectionStatus = $window . FindName ( 'AppSelectionStatus' )
$headerNameBtn = $window . FindName ( 'HeaderNameBtn' )
$headerDescriptionBtn = $window . FindName ( 'HeaderDescriptionBtn' )
$headerAppIdBtn = $window . FindName ( 'HeaderAppIdBtn' )
$sortArrowName = $window . FindName ( 'SortArrowName' )
$sortArrowDescription = $window . FindName ( 'SortArrowDescription' )
$sortArrowAppId = $window . FindName ( 'SortArrowAppId' )
$presetsBtn = $window . FindName ( 'PresetsBtn' )
$presetsPopup = $window . FindName ( 'PresetsPopup' )
$presetDefaultApps = $window . FindName ( 'PresetDefaultApps' )
$presetLastUsed = $window . FindName ( 'PresetLastUsed' )
$jsonPresetsPanel = $window . FindName ( 'JsonPresetsPanel' )
$presetsArrow = $window . FindName ( 'PresetsArrow' )
$clearAppSelectionBtn = $window . FindName ( 'ClearAppSelectionBtn' )
$tweaksPresetsBtn = $window . FindName ( 'TweaksPresetsBtn' )
$tweaksPresetsPopup = $window . FindName ( 'TweaksPresetsPopup' )
$presetDefaultTweaksBtn = $window . FindName ( 'PresetDefaultTweaksBtn' )
$presetLastUsedTweaksBtn = $window . FindName ( 'PresetLastUsedTweaksBtn' )
$presetPrivacyTweaksBtn = $window . FindName ( 'PresetPrivacyTweaksBtn' )
$presetAITweaksBtn = $window . FindName ( 'PresetAITweaksBtn' )
$tweaksPresetsArrow = $window . FindName ( 'TweaksPresetsArrow' )
2026-02-15 23:08:54 +01:00
2026-06-05 21:18:14 +02:00
# ---- Navigation elements ----
$tabControl = $window . FindName ( 'MainTabControl' )
$previousBtn = $window . FindName ( 'PreviousBtn' )
$nextBtn = $window . FindName ( 'NextBtn' )
$userSelectionCombo = $window . FindName ( 'UserSelectionCombo' )
$userSelectionDescription = $window . FindName ( 'UserSelectionDescription' )
$otherUserPanel = $window . FindName ( 'OtherUserPanel' )
$otherUsernameTextBox = $window . FindName ( 'OtherUsernameTextBox' )
$usernameTextBoxPlaceholder = $window . FindName ( 'UsernameTextBoxPlaceholder' )
$usernameValidationMessage = $window . FindName ( 'UsernameValidationMessage' )
$appRemovalScopeCombo = $window . FindName ( 'AppRemovalScopeCombo' )
$appRemovalScopeDescription = $window . FindName ( 'AppRemovalScopeDescription' )
$appRemovalScopeSection = $window . FindName ( 'AppRemovalScopeSection' )
$appRemovalScopeCurrentUser = $window . FindName ( 'AppRemovalScopeCurrentUser' )
$appRemovalScopeTargetUser = $window . FindName ( 'AppRemovalScopeTargetUser' )
# ---- Tweak search elements ----
$tweakSearchBox = $window . FindName ( 'TweakSearchBox' )
$tweakSearchPlaceholder = $window . FindName ( 'TweakSearchPlaceholder' )
$tweakSearchBorder = $window . FindName ( 'TweakSearchBorder' )
$tweaksScrollViewer = $window . FindName ( 'TweaksScrollViewer' )
$tweaksGrid = $window . FindName ( 'TweaksGrid' )
$ShowCurrentlyAppliedTweaksCheckBox = $window . FindName ( 'ShowCurrentlyAppliedTweaksCheckBox' )
$clearAllTweaksBtn = $window . FindName ( 'ClearAllTweaksBtn' )
# ---- Deployment elements ----
$reviewChangesBtn = $window . FindName ( 'ReviewChangesBtn' )
$deploymentApplyBtn = $window . FindName ( 'DeploymentApplyBtn' )
$homeStartBtn = $window . FindName ( 'HomeStartBtn' )
$homeDefaultModeBtn = $window . FindName ( 'HomeDefaultModeBtn' )
# ---- Wire export/import ----
2026-03-27 20:33:24 +01:00
$exportConfigBtn . Add_Click ( {
2026-05-08 21:19:52 +02:00
try {
Export-Configuration -Owner $window -UsesDarkMode $usesDarkMode -AppsPanel $appsPanel -UiControlMappings $script:UiControlMappings -UserSelectionCombo $userSelectionCombo -OtherUsernameTextBox $otherUsernameTextBox
}
catch {
Write-Warning " Export configuration failed: $( $_ . Exception . Message ) "
Show-MessageBox -Owner $window -Message " Unable to open export configuration dialog: $( $_ . Exception . Message ) " -Title 'Export Configuration Failed' -Button 'OK' -Icon 'Error' | Out-Null
}
2026-03-27 20:33:24 +01:00
} )
$importConfigBtn . Add_Click ( {
2026-05-08 21:19:52 +02:00
try {
2026-06-05 21:18:14 +02:00
Import-Configuration -Owner $window -UsesDarkMode $usesDarkMode -AppsPanel $appsPanel -UiControlMappings $script:UiControlMappings -UserSelectionCombo $userSelectionCombo -OtherUsernameTextBox $otherUsernameTextBox -OnAppsImported { Update-AppSelectionStatus -AppsPanel $appsPanel -AppSelectionStatus $appSelectionStatus -AppRemovalScopeCombo $appRemovalScopeCombo -AppRemovalScopeSection $appRemovalScopeSection -AppRemovalScopeDescription $appRemovalScopeDescription -UserSelectionCombo $userSelectionCombo ; Update-AppPresetStates -AppsPanel $appsPanel } -OnImportCompleted {
2026-05-08 21:19:52 +02:00
$tabControl . SelectedIndex = 3
2026-06-05 21:18:14 +02:00
Update-NavigationButtons -Window $window -TabControl $tabControl
2026-05-08 21:19:52 +02:00
$window . Dispatcher . BeginInvoke ( [ System.Windows.Threading.DispatcherPriority ] :: Loaded , [ action ] {
Show-Bubble -TargetControl $reviewChangesBtn -Message 'View the selected changes here'
} ) | Out-Null
}
}
catch {
Write-Warning " Import configuration failed: $( $_ . Exception . Message ) "
Show-MessageBox -Owner $window -Message " Unable to open import configuration dialog: $( $_ . Exception . Message ) " -Title 'Import Configuration Failed' -Button 'OK' -Icon 'Error' | Out-Null
2026-03-27 20:33:24 +01:00
}
} )
2026-06-05 21:18:14 +02:00
# ---- Restore backup ----
2026-05-08 21:19:52 +02:00
if ( $restoreBackupBtn ) {
$restoreBackupBtn . Add_Click ( {
try {
2026-05-27 22:05:06 +02:00
$restoreResult = Show-RestoreBackupWindow -Owner $window
if ( $restoreResult -and $restoreResult . RestoredRegistry -eq $true ) {
2026-06-05 21:18:14 +02:00
Update-CurrentTweakSystemState -Window $window -ApplyToUi: $false
2026-05-27 22:05:06 +02:00
if ( $ShowCurrentlyAppliedTweaksCheckBox -and $ShowCurrentlyAppliedTweaksCheckBox . IsChecked -eq $true ) {
2026-06-05 21:18:14 +02:00
Reset-TweaksToSystemState -Window $window -LoadSystemState $true
Update-TweakPresetStates -Window $window
2026-05-27 22:05:06 +02:00
}
}
2026-05-08 21:19:52 +02:00
}
catch {
Write-Warning " Restore backup action failed: $( $_ . Exception . Message ) "
Show-MessageBox -Owner $window -Message " Unable to open restore backup dialog: $( $_ . Exception . Message ) " -Title 'Restore Backup Failed' -Button 'OK' -Icon 'Error' | Out-Null
}
} )
}
2026-06-05 21:18:14 +02:00
# ---- Script-level state initialization ----
$script:MainWindowLastSelectedCheckbox = $null
$script:IsLoadingApps = $false
$script:PendingDefaultMode = $false
$script:PreloadedAppData = $null
$script:UpdatingPresets = $false
$script:UpdatingTweakPresets = $false
$script:SortColumn = 'Name'
$script:SortAscending = $true
$script:AppSearchMatches = @ ( )
$script:AppSearchMatchIndex = -1
$script:JsonPresetCheckboxes = @ ( )
2026-04-02 00:00:32 +02:00
2026-06-05 21:18:14 +02:00
if ( $importConfigBtn ) { $importConfigBtn . IsEnabled = $false }
2026-04-02 00:00:32 +02:00
2026-06-05 21:18:14 +02:00
# ---- Build JSON-defined app presets ----
2026-03-15 22:58:06 +01:00
foreach ( $preset in ( LoadAppPresetsFromJson ) ) {
$checkbox = New-Object System . Windows . Controls . CheckBox
$checkbox . Content = $preset . Name
$checkbox . IsThreeState = $true
$checkbox . Style = $window . Resources [ 'PresetCheckBoxStyle' ]
2026-04-25 00:40:29 +02:00
$checkbox . ToolTip = " Select $( $preset . Name ) "
2026-03-15 22:58:06 +01:00
$checkbox . SetValue ( [ System.Windows.Automation.AutomationProperties ] :: NameProperty , $preset . Name )
2026-06-05 21:18:14 +02:00
Add-TriStateClickBehavior -CheckBox $checkbox
2026-03-15 22:58:06 +01:00
Add-Member -InputObject $checkbox -MemberType NoteProperty -Name 'PresetAppIds' -Value $preset . AppIds
$jsonPresetsPanel . Children . Add ( $checkbox ) | Out-Null
$script:JsonPresetCheckboxes + = $checkbox
$checkbox . Add_Click ( {
if ( $script:UpdatingPresets ) { return }
$presetIds = $this . PresetAppIds
2026-06-05 21:18:14 +02:00
$check = ConvertTo-NormalizedCheckboxState -CheckBox $this
Invoke-AppPreset -AppsPanel $appsPanel -MatchFilter { param ( $c ) ( @ ( $c . AppIds ) | Where-Object { $presetIds -contains $_ } ) . Count -gt 0 } . GetNewClosure ( ) -Check $check
2026-03-15 22:58:06 +01:00
} )
}
2026-03-27 20:33:24 +01:00
2026-06-05 21:18:14 +02:00
# ---- App sort helpers ----
$updateSortArrows = {
Update-SortArrows `
-SortArrowName $sortArrowName -SortArrowDescription $sortArrowDescription -SortArrowAppId $sortArrowAppId
2026-03-27 20:33:24 +01:00
}
2026-03-15 22:58:06 +01:00
2026-06-05 21:18:14 +02:00
$rebuildAppSearchIndex = {
2026-03-16 19:25:53 +01:00
param ( $activeMatch = $null )
2026-06-05 21:18:14 +02:00
Update-AppsPanelRebuildSearchIndex -AppsPanel $appsPanel -ActiveMatch $activeMatch
2026-03-16 19:25:53 +01:00
}
2026-06-05 21:18:14 +02:00
$sortApps = {
Update-AppsPanelSort -AppsPanel $appsPanel `
-SortArrowName $sortArrowName -SortArrowDescription $sortArrowDescription -SortArrowAppId $sortArrowAppId
2026-03-16 19:25:53 +01:00
if ( $script:AppSearchMatches . Count -gt 0 ) {
$activeMatch = if ( $script:AppSearchMatchIndex -ge 0 -and $script:AppSearchMatchIndex -lt $script:AppSearchMatches . Count ) {
$script:AppSearchMatches [ $script:AppSearchMatchIndex ]
} else { $null }
2026-06-05 21:18:14 +02:00
& $rebuildAppSearchIndex -activeMatch $activeMatch
2026-03-16 19:25:53 +01:00
}
2026-03-15 22:58:06 +01:00
}
2026-06-05 21:18:14 +02:00
$setSortColumn = {
param ( $column )
2026-03-15 22:58:06 +01:00
if ( $script:SortColumn -eq $column ) {
$script:SortAscending = -not $script:SortAscending
2026-06-05 21:18:14 +02:00
}
else {
2026-03-15 22:58:06 +01:00
$script:SortColumn = $column
$script:SortAscending = $true
}
2026-06-05 21:18:14 +02:00
& $sortApps
2026-03-15 22:58:06 +01:00
}
2026-06-05 21:18:14 +02:00
# ---- Tri-state preset wiring for app presets ----
foreach ( $presetCheckBox in @ ( $presetDefaultApps , $presetLastUsed ) ) {
Add-TriStateClickBehavior -CheckBox $presetCheckBox
2026-02-15 23:08:54 +01:00
}
2026-06-05 21:18:14 +02:00
# ---- Preset: Default selection ----
$presetDefaultApps . Add_Click ( {
if ( $script:UpdatingPresets ) { return }
$check = ConvertTo-NormalizedCheckboxState -CheckBox $this
Invoke-AppPreset -AppsPanel $appsPanel -MatchFilter { param ( $c ) $c . SelectedByDefault -eq $true } -Check $check
} )
2026-03-15 22:58:06 +01:00
2026-06-05 21:18:14 +02:00
# ---- Clear selection button ----
$clearAppSelectionBtn . Add_Click ( {
Invoke-AppPreset -AppsPanel $appsPanel -MatchFilter { param ( $c ) $true } -Check $false
} )
2026-02-15 23:08:54 +01:00
2026-06-05 21:18:14 +02:00
# ---- Column header sort handlers ----
$headerNameBtn . Add_MouseLeftButtonUp ( { & $setSortColumn 'Name' } )
$headerDescriptionBtn . Add_MouseLeftButtonUp ( { & $setSortColumn 'Description' } )
$headerAppIdBtn . Add_MouseLeftButtonUp ( { & $setSortColumn 'AppId' } )
2026-02-15 23:08:54 +01:00
2026-06-05 21:18:14 +02:00
# ---- Load apps ----
$appLoadStatusCallback = { Update-AppSelectionStatus -AppsPanel $appsPanel -AppSelectionStatus $appSelectionStatus -AppRemovalScopeCombo $appRemovalScopeCombo -AppRemovalScopeSection $appRemovalScopeSection -AppRemovalScopeDescription $appRemovalScopeDescription -UserSelectionCombo $userSelectionCombo }
$onlyInstalledAppsBox . Add_Checked ( { Load-AppsIntoMainUI -Window $window -AppsPanel $appsPanel -OnlyInstalledAppsBox $onlyInstalledAppsBox -LoadingAppsIndicator $loadingAppsIndicator -ImportConfigBtn $importConfigBtn } )
$onlyInstalledAppsBox . Add_Unchecked ( { Load-AppsIntoMainUI -Window $window -AppsPanel $appsPanel -OnlyInstalledAppsBox $onlyInstalledAppsBox -LoadingAppsIndicator $loadingAppsIndicator -ImportConfigBtn $importConfigBtn } )
2026-02-15 23:08:54 +01:00
2026-06-05 21:18:14 +02:00
# ---- App presets popup ----
2026-03-15 22:58:06 +01:00
$presetsPopup . Add_Opened ( {
2026-06-05 21:18:14 +02:00
Update-AppPresetStates -AppsPanel $appsPanel
Start-DropdownArrowAnimation -Arrow $presetsArrow -Angle 180
2026-03-15 22:58:06 +01:00
} )
$presetsPopup . Add_Closed ( {
2026-06-05 21:18:14 +02:00
Start-DropdownArrowAnimation -Arrow $presetsArrow -Angle 0
2026-03-15 22:58:06 +01:00
$presetsBtn . IsChecked = $false
} )
2026-04-25 00:40:29 +02:00
$tweaksPresetsPopup . Add_Opened ( {
2026-06-05 21:18:14 +02:00
Update-TweakPresetStates -Window $window
Start-DropdownArrowAnimation -Arrow $tweaksPresetsArrow -Angle 180
2026-04-25 00:40:29 +02:00
} )
$tweaksPresetsPopup . Add_Closed ( {
2026-06-05 21:18:14 +02:00
Start-DropdownArrowAnimation -Arrow $tweaksPresetsArrow -Angle 0
2026-04-25 00:40:29 +02:00
$tweaksPresetsBtn . IsChecked = $false
} )
2026-06-05 21:18:14 +02:00
# ---- Popup dismiss on outside click ----
2026-03-15 22:58:06 +01:00
$window . Add_PreviewMouseDown ( {
2026-04-25 00:40:29 +02:00
$isAppPopupOpen = $presetsPopup . IsOpen
$isTweaksPopupOpen = $tweaksPresetsPopup . IsOpen
if ( -not $isAppPopupOpen -and -not $isTweaksPopupOpen ) { return }
if ( $isAppPopupOpen -and $null -ne $presetsPopup . Child -and $presetsPopup . Child . IsMouseOver ) { return }
if ( $isTweaksPopupOpen -and $null -ne $tweaksPresetsPopup . Child -and $tweaksPresetsPopup . Child . IsMouseOver ) { return }
2026-03-15 22:58:06 +01:00
$src = $_ . OriginalSource -as [ System.Windows.DependencyObject ]
2026-04-02 00:00:32 +02:00
if ( $null -ne $src ) {
2026-04-25 00:40:29 +02:00
$inAppBtn = $presetsBtn . IsAncestorOf ( $src ) -or [ System.Object ] :: ReferenceEquals ( $presetsBtn , $src )
$inTweaksBtn = $tweaksPresetsBtn . IsAncestorOf ( $src ) -or [ System.Object ] :: ReferenceEquals ( $tweaksPresetsBtn , $src )
if ( $isAppPopupOpen -and -not $inAppBtn ) { $presetsPopup . IsOpen = $false }
if ( $isTweaksPopupOpen -and -not $inTweaksBtn ) { $tweaksPresetsPopup . IsOpen = $false }
2026-02-15 23:08:54 +01:00
}
} )
2026-03-17 21:28:35 +01:00
$window . Add_Deactivated ( {
if ( $presetsPopup . IsOpen ) { $presetsPopup . IsOpen = $false }
2026-04-25 00:40:29 +02:00
if ( $tweaksPresetsPopup . IsOpen ) { $tweaksPresetsPopup . IsOpen = $false }
2026-03-17 21:28:35 +01:00
} )
2026-06-05 21:18:14 +02:00
# ---- Toggle popup on button click ----
2026-03-15 22:58:06 +01:00
$presetsBtn . Add_Click ( {
$presetsPopup . IsOpen = -not $presetsPopup . IsOpen
$presetsBtn . IsChecked = $presetsPopup . IsOpen
} )
2026-04-25 00:40:29 +02:00
$tweaksPresetsBtn . Add_Click ( {
$tweaksPresetsPopup . IsOpen = -not $tweaksPresetsPopup . IsOpen
$tweaksPresetsBtn . IsChecked = $tweaksPresetsPopup . IsOpen
} )
2026-06-05 21:18:14 +02:00
# ---- App Search Box ----
2026-02-15 23:08:54 +01:00
$appSearchBox = $window . FindName ( 'AppSearchBox' )
$appSearchPlaceholder = $window . FindName ( 'AppSearchPlaceholder' )
2026-06-05 21:18:14 +02:00
2026-02-15 23:08:54 +01:00
$appSearchBox . Add_TextChanged ( {
$searchText = $appSearchBox . Text . ToLower ( ) . Trim ( )
$appSearchPlaceholder . Visibility = if ( [ string ] :: IsNullOrWhiteSpace ( $appSearchBox . Text ) ) { 'Visible' } else { 'Collapsed' }
2026-06-05 21:18:14 +02:00
2026-02-15 23:08:54 +01:00
foreach ( $child in $appsPanel . Children ) {
if ( $child -is [ System.Windows.Controls.CheckBox ] ) {
$child . Background = [ System.Windows.Media.Brushes ] :: Transparent
}
}
2026-06-05 21:18:14 +02:00
2026-02-18 00:26:10 +01:00
$script:AppSearchMatches = @ ( )
$script:AppSearchMatchIndex = -1
2026-06-05 21:18:14 +02:00
2026-02-15 23:08:54 +01:00
if ( [ string ] :: IsNullOrWhiteSpace ( $searchText ) ) { return }
2026-06-05 21:18:14 +02:00
2026-02-18 00:26:10 +01:00
$highlightBrush = $window . Resources [ " SearchHighlightColor " ]
$activeHighlightBrush = $window . Resources [ " SearchHighlightActiveColor " ]
2026-06-05 21:18:14 +02:00
2026-02-15 23:08:54 +01:00
foreach ( $child in $appsPanel . Children ) {
if ( $child -is [ System.Windows.Controls.CheckBox ] -and $child . Visibility -eq 'Visible' ) {
2026-02-18 00:26:10 +01:00
$appName = if ( $child . AppName ) { $child . AppName } else { '' }
$appId = if ( $child . Tag ) { $child . Tag . ToString ( ) } else { '' }
$appDesc = if ( $child . AppDescription ) { $child . AppDescription } else { '' }
if ( $appName . ToLower ( ) . Contains ( $searchText ) -or $appId . ToLower ( ) . Contains ( $searchText ) -or $appDesc . ToLower ( ) . Contains ( $searchText ) ) {
2026-02-15 23:08:54 +01:00
$child . Background = $highlightBrush
2026-02-18 00:26:10 +01:00
$script:AppSearchMatches + = $child
2026-02-15 23:08:54 +01:00
}
}
}
2026-06-05 21:18:14 +02:00
2026-02-18 00:26:10 +01:00
if ( $script:AppSearchMatches . Count -gt 0 ) {
$script:AppSearchMatchIndex = 0
$script:AppSearchMatches [ 0 ] . Background = $activeHighlightBrush
2026-06-05 21:18:14 +02:00
$scrollViewer = Find-ParentScrollViewer -Element $appsPanel
2026-02-15 23:08:54 +01:00
if ( $scrollViewer ) {
2026-06-05 21:18:14 +02:00
Scroll-ToItemIfNotVisible -ScrollViewer $scrollViewer -Item $script:AppSearchMatches [ 0 ] -Container $appsPanel
2026-02-15 23:08:54 +01:00
}
}
} )
2026-06-05 21:18:14 +02:00
2026-02-18 00:26:10 +01:00
$appSearchBox . Add_KeyDown ( {
2026-04-02 00:00:32 +02:00
param ( $sourceControl , $e )
2026-02-18 00:26:10 +01:00
if ( $e . Key -eq [ System.Windows.Input.Key ] :: Enter -and $script:AppSearchMatches . Count -gt 0 ) {
$script:AppSearchMatches [ $script:AppSearchMatchIndex ] . Background = $window . Resources [ " SearchHighlightColor " ]
$script:AppSearchMatchIndex = ( $script:AppSearchMatchIndex + 1 ) % $script:AppSearchMatches . Count
$script:AppSearchMatches [ $script:AppSearchMatchIndex ] . Background = $window . Resources [ " SearchHighlightActiveColor " ]
2026-06-05 21:18:14 +02:00
$scrollViewer = Find-ParentScrollViewer -Element $appsPanel
2026-02-18 00:26:10 +01:00
if ( $scrollViewer ) {
2026-06-05 21:18:14 +02:00
Scroll-ToItemIfNotVisible -ScrollViewer $scrollViewer -Item $script:AppSearchMatches [ $script:AppSearchMatchIndex ] -Container $appsPanel
2026-02-18 00:26:10 +01:00
}
$e . Handled = $true
}
} )
2026-02-15 23:08:54 +01:00
2026-06-05 21:18:14 +02:00
# ---- Tweak Search Box ----
2026-02-15 23:08:54 +01:00
$tweaksScrollViewer . Add_ScrollChanged ( {
if ( $tweaksScrollViewer . ScrollableHeight -gt 0 ) {
$tweakSearchBorder . Margin = [ System.Windows.Thickness ] :: new ( 0 , 0 , 17 , 0 )
2026-06-05 21:18:14 +02:00
}
else {
2026-03-15 22:58:06 +01:00
$tweakSearchBorder . Margin = [ System.Windows.Thickness ] :: new ( 0 )
2026-02-15 23:08:54 +01:00
}
} )
2026-06-05 21:18:14 +02:00
2026-02-15 23:08:54 +01:00
$tweakSearchBox . Add_TextChanged ( {
$searchText = $tweakSearchBox . Text . ToLower ( ) . Trim ( )
$tweakSearchPlaceholder . Visibility = if ( [ string ] :: IsNullOrWhiteSpace ( $tweakSearchBox . Text ) ) { 'Visible' } else { 'Collapsed' }
2026-06-05 21:18:14 +02:00
Clear-TweakHighlights -Window $window
2026-02-15 23:08:54 +01:00
if ( [ string ] :: IsNullOrWhiteSpace ( $searchText ) ) { return }
2026-06-05 21:18:14 +02:00
2026-02-15 23:08:54 +01:00
$firstMatch = $null
2026-02-18 00:26:10 +01:00
$highlightBrush = $window . Resources [ " SearchHighlightColor " ]
2026-06-05 21:18:14 +02:00
$col0 = $window . FindName ( 'Column0Panel' )
$col1 = $window . FindName ( 'Column1Panel' )
$col2 = $window . FindName ( 'Column2Panel' )
2026-02-15 23:08:54 +01:00
$columns = @ ( $col0 , $col1 , $col2 ) | Where-Object { $_ -ne $null }
2026-06-05 21:18:14 +02:00
2026-02-15 23:08:54 +01:00
foreach ( $column in $columns ) {
foreach ( $card in $column . Children ) {
if ( $card -is [ System.Windows.Controls.Border ] -and $card . Child -is [ System.Windows.Controls.StackPanel ] ) {
$controlsList = @ ( $card . Child . Children )
for ( $i = 0 ; $i -lt $controlsList . Count ; $i + + ) {
$control = $controlsList [ $i ]
$matchFound = $false
$controlToHighlight = $null
2026-06-05 21:18:14 +02:00
2026-02-15 23:08:54 +01:00
if ( $control -is [ System.Windows.Controls.CheckBox ] ) {
if ( $control . Content . ToString ( ) . ToLower ( ) . Contains ( $searchText ) ) {
$matchFound = $true
$controlToHighlight = $control
}
}
elseif ( $control -is [ System.Windows.Controls.Border ] -and $control . Name -like '*_LabelBorder' ) {
$labelText = if ( $control . Child ) { $control . Child . Text . ToLower ( ) } else { " " }
$comboBox = if ( $i + 1 -lt $controlsList . Count -and $controlsList [ $i + 1 ] -is [ System.Windows.Controls.ComboBox ] ) { $controlsList [ $i + 1 ] } else { $null }
2026-06-05 21:18:14 +02:00
if ( $labelText . Contains ( $searchText ) -or ( $comboBox -and ( Test-ComboBoxContainsMatch -ComboBox $comboBox -SearchText $searchText ) ) ) {
2026-02-15 23:08:54 +01:00
$matchFound = $true
$controlToHighlight = $control
}
}
2026-06-05 21:18:14 +02:00
2026-02-15 23:08:54 +01:00
if ( $matchFound -and $controlToHighlight ) {
$controlToHighlight . Background = $highlightBrush
if ( $null -eq $firstMatch ) { $firstMatch = $controlToHighlight }
}
}
}
}
}
2026-06-05 21:18:14 +02:00
2026-02-15 23:08:54 +01:00
if ( $firstMatch -and $tweaksScrollViewer ) {
2026-06-05 21:18:14 +02:00
Scroll-ToItemIfNotVisible -ScrollViewer $tweaksScrollViewer -Item $firstMatch -Container $tweaksGrid
2026-02-15 23:08:54 +01:00
}
} )
2026-06-05 21:18:14 +02:00
# ---- Show currently applied tweaks checkbox ----
2026-05-27 21:36:07 +02:00
if ( $ShowCurrentlyAppliedTweaksCheckBox ) {
2026-06-05 21:18:14 +02:00
$ShowCurrentlyAppliedTweaksCheckBox . Add_Checked ( {
Reset-TweaksToSystemState -Window $window -LoadSystemState $true
Update-AppliedTweaksUserModeState -ShowCurrentlyAppliedTweaksCheckBox $ShowCurrentlyAppliedTweaksCheckBox -UserSelectionCombo $userSelectionCombo
} )
$ShowCurrentlyAppliedTweaksCheckBox . Add_Unchecked ( {
Reset-TweaksToSystemState -Window $window -LoadSystemState $false
Update-AppliedTweaksUserModeState -ShowCurrentlyAppliedTweaksCheckBox $ShowCurrentlyAppliedTweaksCheckBox -UserSelectionCombo $userSelectionCombo
} )
2026-05-27 21:36:07 +02:00
}
2026-06-05 21:18:14 +02:00
# ---- Ctrl+F keyboard shortcut ----
2026-02-15 23:08:54 +01:00
$window . Add_KeyDown ( {
2026-04-02 00:00:32 +02:00
param ( $sourceControl , $e )
2026-06-05 21:18:14 +02:00
if ( $e . Key -eq [ System.Windows.Input.Key ] :: F -and
2026-02-15 23:08:54 +01:00
( [ System.Windows.Input.Keyboard ] :: Modifiers -band [ System.Windows.Input.ModifierKeys ] :: Control ) ) {
$currentTab = $tabControl . SelectedItem
if ( $currentTab . Header -eq " App Removal " -and $appSearchBox ) {
$appSearchBox . Focus ( )
$e . Handled = $true
}
elseif ( $currentTab . Header -eq " Tweaks " -and $tweakSearchBox ) {
$tweakSearchBox . Focus ( )
$e . Handled = $true
}
}
} )
2026-06-05 21:18:14 +02:00
# ---- Navigation button handlers ----
function Invoke-NavigationUpdate {
Update-NavigationButtons -Window $window -TabControl $tabControl
}
2026-02-15 23:08:54 +01:00
2026-06-05 21:18:14 +02:00
$previousBtn . Add_Click ( {
Hide-Bubble -Immediate
if ( $tabControl . SelectedIndex -gt 0 ) {
$tabControl . SelectedIndex - -
Invoke-NavigationUpdate
2026-02-15 23:08:54 +01:00
}
2026-06-05 21:18:14 +02:00
} )
2026-02-15 23:08:54 +01:00
2026-06-05 21:18:14 +02:00
$nextBtn . Add_Click ( {
if ( $tabControl . SelectedIndex -lt ( $tabControl . Items . Count - 1 ) ) {
$tabControl . SelectedIndex + +
Invoke-NavigationUpdate
2026-05-27 23:33:35 +02:00
}
2026-06-05 21:18:14 +02:00
} )
2026-06-03 21:16:10 +02:00
2026-06-05 21:18:14 +02:00
# ---- User selection combo ----
2026-06-03 21:16:10 +02:00
$userSelectionCombo . Add_SelectionChanged ( {
2026-06-05 21:18:14 +02:00
Update-UserSelectionDescription -Window $window -UserSelectionCombo $userSelectionCombo -OtherUsernameTextBox $otherUsernameTextBox -UserSelectionDescription $userSelectionDescription
2026-05-27 23:33:35 +02:00
2026-02-15 23:08:54 +01:00
switch ( $userSelectionCombo . SelectedIndex ) {
2026-06-05 21:18:14 +02:00
0 {
2026-02-15 23:08:54 +01:00
$otherUserPanel . Visibility = 'Collapsed'
$usernameValidationMessage . Text = " "
$appRemovalScopeCurrentUser . Visibility = 'Visible'
$appRemovalScopeTargetUser . Visibility = 'Collapsed'
$appRemovalScopeCombo . SelectedIndex = 0
2026-06-03 21:16:10 +02:00
if ( $ShowCurrentlyAppliedTweaksCheckBox -and $ShowCurrentlyAppliedTweaksCheckBox . IsChecked -ne $true ) {
$ShowCurrentlyAppliedTweaksCheckBox . IsChecked = $true
}
2026-02-15 23:08:54 +01:00
}
2026-06-05 21:18:14 +02:00
1 {
2026-02-15 23:08:54 +01:00
$otherUserPanel . Visibility = 'Visible'
$usernameValidationMessage . Text = " "
$appRemovalScopeCurrentUser . Visibility = 'Collapsed'
$appRemovalScopeTargetUser . Visibility = 'Visible'
$appRemovalScopeCombo . SelectedIndex = 0
2026-06-03 21:16:10 +02:00
if ( $ShowCurrentlyAppliedTweaksCheckBox -and $ShowCurrentlyAppliedTweaksCheckBox . IsChecked -eq $true ) {
$ShowCurrentlyAppliedTweaksCheckBox . IsChecked = $false
}
2026-02-15 23:08:54 +01:00
}
2026-06-05 21:18:14 +02:00
2 {
2026-02-15 23:08:54 +01:00
$otherUserPanel . Visibility = 'Collapsed'
$usernameValidationMessage . Text = " "
$appRemovalScopeCurrentUser . Visibility = 'Collapsed'
$appRemovalScopeTargetUser . Visibility = 'Collapsed'
$appRemovalScopeCombo . SelectedIndex = 0
2026-06-03 21:16:10 +02:00
if ( $ShowCurrentlyAppliedTweaksCheckBox -and $ShowCurrentlyAppliedTweaksCheckBox . IsChecked -eq $true ) {
$ShowCurrentlyAppliedTweaksCheckBox . IsChecked = $false
}
2026-02-15 23:08:54 +01:00
}
}
2026-05-08 21:19:52 +02:00
2026-06-05 21:18:14 +02:00
Update-AppSelectionStatus -AppsPanel $appsPanel -AppSelectionStatus $appSelectionStatus -AppRemovalScopeCombo $appRemovalScopeCombo -AppRemovalScopeSection $appRemovalScopeSection -AppRemovalScopeDescription $appRemovalScopeDescription -UserSelectionCombo $userSelectionCombo
Update-AppliedTweaksUserModeState -ShowCurrentlyAppliedTweaksCheckBox $ShowCurrentlyAppliedTweaksCheckBox -UserSelectionCombo $userSelectionCombo
2026-02-15 23:08:54 +01:00
} )
2026-06-05 21:18:14 +02:00
# ---- App removal scope combo ----
2026-02-15 23:08:54 +01:00
$appRemovalScopeCombo . Add_SelectionChanged ( {
2026-06-05 21:18:14 +02:00
Update-AppRemovalScopeDescription -AppRemovalScopeCombo $appRemovalScopeCombo -AppRemovalScopeDescription $appRemovalScopeDescription
2026-02-15 23:08:54 +01:00
} )
2026-06-05 21:18:14 +02:00
# ---- Other username text box ----
2026-02-15 23:08:54 +01:00
$otherUsernameTextBox . Add_TextChanged ( {
if ( [ string ] :: IsNullOrWhiteSpace ( $otherUsernameTextBox . Text ) ) {
$usernameTextBoxPlaceholder . Visibility = 'Visible'
}
2026-06-05 21:18:14 +02:00
else {
$usernameTextBoxPlaceholder . Visibility = 'Collapsed'
2026-02-15 23:08:54 +01:00
}
2026-06-05 21:18:14 +02:00
Update-UserSelectionDescription -Window $window -UserSelectionCombo $userSelectionCombo -OtherUsernameTextBox $otherUsernameTextBox -UserSelectionDescription $userSelectionDescription
Test-OtherUsername -Window $window -UserSelectionCombo $userSelectionCombo -OtherUsernameTextBox $otherUsernameTextBox -UsernameValidationMessage $usernameValidationMessage | Out-Null
2026-02-15 23:08:54 +01:00
} )
2026-06-05 21:18:14 +02:00
# ---- Validate target user helper ----
2026-06-03 21:16:10 +02:00
$ensureValidTargetUserOrWarn = {
2026-06-05 21:18:14 +02:00
if ( -not ( Test-OtherUsername -Window $window -UserSelectionCombo $userSelectionCombo -OtherUsernameTextBox $otherUsernameTextBox -UsernameValidationMessage $usernameValidationMessage ) ) {
2026-06-03 21:16:10 +02:00
$validationMessage = if ( -not [ string ] :: IsNullOrWhiteSpace ( $usernameValidationMessage . Text ) ) {
$usernameValidationMessage . Text
}
else {
" Please enter a valid username. "
}
Show-MessageBox -Message $validationMessage -Title " Invalid Username " -Button 'OK' -Icon 'Warning' | Out-Null
return $false
}
return $true
}
2026-06-05 21:18:14 +02:00
# ---- Home Start button ----
2026-02-15 23:08:54 +01:00
$homeStartBtn . Add_Click ( {
2026-06-05 21:18:14 +02:00
if ( -not ( & $ensureValidTargetUserOrWarn ) ) { return }
2026-02-15 23:08:54 +01:00
$tabControl . SelectedIndex = 1
2026-06-05 21:18:14 +02:00
Invoke-NavigationUpdate
2026-02-15 23:08:54 +01:00
} )
2026-06-05 21:18:14 +02:00
# ---- Home Default Mode button ----
2026-02-18 21:39:00 +01:00
$homeDefaultModeBtn . Add_Click ( {
2026-06-05 21:18:14 +02:00
if ( -not ( & $ensureValidTargetUserOrWarn ) ) { return }
2026-06-03 21:16:10 +02:00
2026-05-27 21:36:07 +02:00
if ( $ShowCurrentlyAppliedTweaksCheckBox ) {
$ShowCurrentlyAppliedTweaksCheckBox . IsChecked = $false
}
2026-02-18 21:39:00 +01:00
$defaultsJson = LoadJsonFile -filePath $script:DefaultSettingsFilePath -expectedVersion " 1.0 "
if ( $defaultsJson ) {
ApplySettingsToUiControls -window $window -settingsJson $defaultsJson -uiControlMappings $script:UiControlMappings
}
2026-03-15 22:58:06 +01:00
if ( $script:IsLoadingApps ) {
$script:PendingDefaultMode = $true
2026-06-05 21:18:14 +02:00
}
else {
Invoke-AppPreset -AppsPanel $appsPanel -MatchFilter { param ( $c ) $c . SelectedByDefault -eq $true } -Exclusive
2026-02-18 21:39:00 +01:00
}
$tabControl . SelectedIndex = 3
2026-06-05 21:18:14 +02:00
Invoke-NavigationUpdate
2026-03-15 20:16:53 +01:00
$window . Dispatcher . BeginInvoke ( [ System.Windows.Threading.DispatcherPriority ] :: Loaded , [ action ] {
Show-Bubble -TargetControl $reviewChangesBtn -Message 'View the selected changes here'
} ) | Out-Null
2026-02-18 21:39:00 +01:00
} )
2026-06-05 21:18:14 +02:00
# ---- Review Changes link ----
2026-03-07 14:49:29 +01:00
$reviewChangesBtn . Add_Click ( {
2026-03-15 20:16:53 +01:00
Hide-Bubble
2026-06-05 21:18:14 +02:00
Invoke-ShowChangesOverview -Window $window -AppsPanel $appsPanel -ShowCurrentlyAppliedTweaksCheckBox $ShowCurrentlyAppliedTweaksCheckBox
2026-03-07 14:49:29 +01:00
} )
2026-06-05 21:18:14 +02:00
# ---- Apply Changes button ----
2026-03-07 14:49:29 +01:00
$deploymentApplyBtn . Add_Click ( {
2026-06-05 21:18:14 +02:00
if ( -not ( & $ensureValidTargetUserOrWarn ) ) { return }
2026-03-15 20:16:53 +01:00
Hide-Bubble -Immediate
2026-02-15 23:08:54 +01:00
2026-05-27 21:36:07 +02:00
$showAppliedTweaksMode = ( $ShowCurrentlyAppliedTweaksCheckBox -and $ShowCurrentlyAppliedTweaksCheckBox . IsChecked -eq $true )
$selectedForwardFeatureIds = New-Object 'System.Collections.Generic.HashSet[string]' ( [ System.StringComparer ] :: OrdinalIgnoreCase )
2026-06-05 21:18:14 +02:00
# App Removal - collect selected apps
2026-02-15 23:08:54 +01:00
$selectedApps = @ ( )
foreach ( $child in $appsPanel . Children ) {
if ( $child -is [ System.Windows.Controls.CheckBox ] -and $child . IsChecked ) {
2026-03-23 22:59:04 +01:00
$selectedApps + = @ ( $child . AppIds )
2026-02-15 23:08:54 +01:00
}
}
2026-03-23 22:59:04 +01:00
$selectedApps = @ ( $selectedApps | Where-Object { $_ } | Select-Object -Unique )
2026-05-27 21:36:07 +02:00
$hasAppSelection = ( $selectedApps . Count -gt 0 )
2026-06-05 21:18:14 +02:00
2026-02-15 23:08:54 +01:00
if ( $selectedApps . Count -gt 0 ) {
2026-06-05 21:18:14 +02:00
if ( -not ( ConfirmUnsafeAppRemoval -SelectedApps $selectedApps -Owner $window ) ) { return }
2026-02-15 23:08:54 +01:00
AddParameter 'RemoveApps'
AddParameter 'Apps' ( $selectedApps -join ',' )
2026-06-05 21:18:14 +02:00
2026-02-15 23:08:54 +01:00
$selectedScopeItem = $appRemovalScopeCombo . SelectedItem
if ( $selectedScopeItem ) {
switch ( $selectedScopeItem . Content ) {
2026-06-05 21:18:14 +02:00
" All users " { AddParameter 'AppRemovalTarget' 'AllUsers' }
" Current user only " { AddParameter 'AppRemovalTarget' 'CurrentUser' }
" Target user only " { AddParameter 'AppRemovalTarget' ( $otherUsernameTextBox . Text . Trim ( ) ) }
2026-02-15 23:08:54 +01:00
}
}
}
2026-06-05 21:18:14 +02:00
# Apply dynamic tweaks
foreach ( $tweakAction in @ ( Get-PendingTweakActions -Window $window -ShowAppliedTweaksMode: $showAppliedTweaksMode ) ) {
2026-05-27 21:48:07 +02:00
if ( $tweakAction . Action -eq 'Apply' ) {
AddParameter $tweakAction . FeatureId
$null = $selectedForwardFeatureIds . Add ( [ string ] $tweakAction . FeatureId )
continue
}
2026-05-28 22:55:38 +02:00
$script:UndoParams [ [string ] $tweakAction . FeatureId ] = $true
2026-02-15 23:08:54 +01:00
}
2026-05-28 22:55:38 +02:00
if ( -not $hasAppSelection -and $selectedForwardFeatureIds . Count -eq 0 -and $script:UndoParams . Count -eq 0 ) {
2026-02-15 23:08:54 +01:00
Show-MessageBox -Message 'No changes have been selected, please select at least one option to proceed.' -Title 'No Changes Selected' -Button 'OK' -Icon 'Information'
return
}
$restorePointCheckBox = $window . FindName ( 'RestorePointCheckBox' )
if ( $restorePointCheckBox -and $restorePointCheckBox . IsChecked ) {
AddParameter 'CreateRestorePoint'
}
2026-06-05 21:18:14 +02:00
2026-02-15 23:08:54 +01:00
switch ( $userSelectionCombo . SelectedIndex ) {
2026-06-05 21:18:14 +02:00
0 { Write-Host " Selected user mode: current user ( $( GetUserName ) ) " }
1 {
2026-03-15 22:58:06 +01:00
Write-Host " Selected user mode: $( $otherUsernameTextBox . Text . Trim ( ) ) "
2026-06-05 21:18:14 +02:00
AddParameter User ( $otherUsernameTextBox . Text . Trim ( ) )
2026-03-15 22:58:06 +01:00
}
2 {
Write-Host " Selected user mode: default user profile (Sysprep) "
AddParameter Sysprep
}
2026-02-15 23:08:54 +01:00
}
SaveSettings
2026-03-07 14:49:29 +01:00
$restartExplorerCheckBox = $window . FindName ( 'RestartExplorerCheckBox' )
$shouldRestartExplorer = $restartExplorerCheckBox -and $restartExplorerCheckBox . IsChecked
2026-02-15 23:08:54 +01:00
2026-03-07 14:49:29 +01:00
Show-ApplyModal -Owner $window -RestartExplorer $shouldRestartExplorer
$window . Close ( )
2026-02-15 23:08:54 +01:00
} )
2026-06-05 21:18:14 +02:00
# ---- Tweaks presets tri-state ----
foreach ( $presetCheckBox in @ ( $presetDefaultTweaksBtn , $presetLastUsedTweaksBtn , $presetPrivacyTweaksBtn , $presetAITweaksBtn ) ) {
Add-TriStateClickBehavior -CheckBox $presetCheckBox
2026-04-25 00:40:29 +02:00
}
2026-06-05 21:18:14 +02:00
# ---- Clear All Tweaks ----
$clearAllTweaksBtn . Add_Click ( {
if ( $ShowCurrentlyAppliedTweaksCheckBox -and $ShowCurrentlyAppliedTweaksCheckBox . IsChecked -eq $true ) {
$ShowCurrentlyAppliedTweaksCheckBox . IsChecked = $false
2026-04-25 00:40:29 +02:00
}
2026-06-05 21:18:14 +02:00
Clear-TweakSelections -Window $window
Update-TweakPresetStates -Window $window
} )
2026-04-25 00:40:29 +02:00
2026-06-05 21:18:14 +02:00
# ---- Window Load event ----
$window . Add_Loaded ( {
try {
& $updateHomeContentPosition
Build-DynamicTweaks -Window $window -WinVersion $WinVersion
Load-CurrentTweakStateIntoUI -Window $window
Update-TweaksResponsiveColumns -Window $window
2026-04-25 00:40:29 +02:00
2026-06-05 21:18:14 +02:00
$lastUsedSettingsJson = LoadJsonFile -filePath $script:SavedSettingsFilePath -expectedVersion " 1.0 " -optionalFile
$defaultsJson = LoadJsonFile -filePath $script:DefaultSettingsFilePath -expectedVersion " 1.0 "
2026-04-25 00:40:29 +02:00
2026-06-05 21:18:14 +02:00
$script:SavedAppIds = Get-SavedAppIdsFromSettingsJson -SettingsJson $lastUsedSettingsJson
2026-04-25 00:40:29 +02:00
2026-06-05 21:18:14 +02:00
Initialize-TweakPresetSources -Window $window -DefaultSettingsJson $defaultsJson -LastUsedSettingsJson $lastUsedSettingsJson
Register-TweakPresetControlStateHandlers -Window $window
Update-TweakPresetStates -Window $window
2026-04-25 00:40:29 +02:00
2026-06-05 21:18:14 +02:00
Load-AppsIntoMainUI -Window $window -AppsPanel $appsPanel -OnlyInstalledAppsBox $onlyInstalledAppsBox -LoadingAppsIndicator $loadingAppsIndicator -ImportConfigBtn $importConfigBtn
2026-04-25 00:40:29 +02:00
2026-06-05 21:18:14 +02:00
# Update Current User label
if ( $userSelectionCombo -and $userSelectionCombo . Items . Count -gt 0 ) {
$currentUserItem = $userSelectionCombo . Items [ 0 ]
if ( $currentUserItem -is [ System.Windows.Controls.ComboBoxItem ] ) {
$currentUserItem . Content = " Current User ( $( GetUserName ) ) "
2026-04-25 00:40:29 +02:00
}
}
2026-06-07 22:51:01 +02:00
# When running as SYSTEM, the "Current User" option is not meaningful.
# Hide it from the dropdown and default to "Other User".
$isSystem = ( [ Security.Principal.WindowsIdentity ] :: GetCurrent ( ) . User . Value -eq 'S-1-5-18' )
if ( $isSystem -and $userSelectionCombo . Items . Count -gt 0 ) {
$currentUserItem = $userSelectionCombo . Items [ 0 ]
if ( $currentUserItem -is [ System.Windows.Controls.ComboBoxItem ] ) {
$currentUserItem . Visibility = 'Collapsed'
$currentUserItem . IsEnabled = $false
}
$userSelectionCombo . SelectedIndex = 1
}
2026-06-05 21:18:14 +02:00
$restartExplorerCheckBox = $window . FindName ( 'RestartExplorerCheckBox' )
if ( $restartExplorerCheckBox -and $script:Params . ContainsKey ( " NoRestartExplorer " ) ) {
$restartExplorerCheckBox . IsChecked = $false
$restartExplorerCheckBox . IsEnabled = $false
}
2026-04-25 00:40:29 +02:00
2026-06-05 21:18:14 +02:00
if ( $script:Params . ContainsKey ( " Sysprep " ) ) {
$userSelectionCombo . SelectedIndex = 2
$userSelectionCombo . IsEnabled = $false
2026-04-25 00:40:29 +02:00
}
2026-06-05 21:18:14 +02:00
elseif ( $script:Params . ContainsKey ( " User " ) ) {
$userSelectionCombo . SelectedIndex = 1
$userSelectionCombo . IsEnabled = $false
$otherUsernameTextBox . Text = $script:Params . Item ( " User " )
$otherUsernameTextBox . IsEnabled = $false
2026-04-25 00:40:29 +02:00
}
2026-06-05 21:18:14 +02:00
Update-UserSelectionDescription -Window $window -UserSelectionCombo $userSelectionCombo -OtherUsernameTextBox $otherUsernameTextBox -UserSelectionDescription $userSelectionDescription
Update-AppliedTweaksUserModeState -ShowCurrentlyAppliedTweaksCheckBox $ShowCurrentlyAppliedTweaksCheckBox -UserSelectionCombo $userSelectionCombo
Invoke-NavigationUpdate
2026-04-25 00:40:29 +02:00
}
2026-06-05 21:18:14 +02:00
catch {
Write-Warning " Error during GUI initialization: $( $_ . Exception . Message ) "
Write-Warning " Stack trace: $( $_ . Exception . StackTrace ) "
Show-MessageBox -Message " An error occurred during initialization: $( $_ . Exception . Message ) " -Title " Initialization Error " -Button 'OK' -Icon 'Error' | Out-Null
2026-04-25 00:40:29 +02:00
}
2026-06-05 21:18:14 +02:00
} )
2026-04-25 00:40:29 +02:00
2026-06-05 21:18:14 +02:00
# ---- Tab change event ----
$tabControl . Add_SelectionChanged ( {
if ( $tabControl . SelectedIndex -eq ( $tabControl . Items . Count - 2 ) ) {
New-Overview -Window $window -AppsPanel $appsPanel -ShowCurrentlyAppliedTweaksCheckBox $ShowCurrentlyAppliedTweaksCheckBox | Out-Null
2026-04-25 00:40:29 +02:00
}
2026-06-05 21:18:14 +02:00
Invoke-NavigationUpdate
} )
2026-04-25 00:40:29 +02:00
2026-06-05 21:18:14 +02:00
# ---- Tweak presets wiring ----
2026-04-25 00:40:29 +02:00
$lastUsedSettingsJson = LoadJsonFile -filePath $script:SavedSettingsFilePath -expectedVersion " 1.0 " -optionalFile
$defaultsJson = LoadJsonFile -filePath $script:DefaultSettingsFilePath -expectedVersion " 1.0 "
$script:DefaultTweakPresetMap = @ { }
$script:LastUsedTweakPresetMap = @ { }
$script:PrivacyTweakPresetMap = @ { }
$script:AITweakPresetMap = @ { }
2026-06-05 21:18:14 +02:00
$script:SavedAppIds = Get-SavedAppIdsFromSettingsJson -SettingsJson $lastUsedSettingsJson
2026-04-25 00:40:29 +02:00
if ( $presetDefaultTweaksBtn ) {
$presetDefaultTweaksBtn . Add_Click ( {
if ( $script:UpdatingTweakPresets ) { return }
2026-06-05 21:18:14 +02:00
$check = ConvertTo-NormalizedCheckboxState -CheckBox $this
Invoke-ApplyTweakPresetMap -PresetMap $script:DefaultTweakPresetMap -Check $check
2026-02-15 23:08:54 +01:00
} )
}
2026-04-25 00:40:29 +02:00
if ( $presetLastUsedTweaksBtn ) {
$presetLastUsedTweaksBtn . Add_Click ( {
if ( $script:UpdatingTweakPresets ) { return }
2026-06-05 21:18:14 +02:00
$check = ConvertTo-NormalizedCheckboxState -CheckBox $this
Invoke-ApplyTweakPresetMap -PresetMap $script:LastUsedTweakPresetMap -Check $check
2026-04-25 00:40:29 +02:00
} )
2026-02-15 23:08:54 +01:00
}
2026-04-25 00:40:29 +02:00
if ( $presetPrivacyTweaksBtn ) {
$presetPrivacyTweaksBtn . Add_Click ( {
if ( $script:UpdatingTweakPresets ) { return }
2026-06-05 21:18:14 +02:00
$check = ConvertTo-NormalizedCheckboxState -CheckBox $this
Invoke-ApplyTweakPresetMap -PresetMap $script:PrivacyTweakPresetMap -Check $check
2026-04-25 00:40:29 +02:00
} )
}
2026-03-15 22:58:06 +01:00
2026-04-25 00:40:29 +02:00
if ( $presetAITweaksBtn ) {
$presetAITweaksBtn . Add_Click ( {
if ( $script:UpdatingTweakPresets ) { return }
2026-06-05 21:18:14 +02:00
$check = ConvertTo-NormalizedCheckboxState -CheckBox $this
Invoke-ApplyTweakPresetMap -PresetMap $script:AITweakPresetMap -Check $check
2026-04-25 00:40:29 +02:00
} )
}
2026-06-05 21:18:14 +02:00
# Hide Last used tweak preset by default
2026-04-25 00:40:29 +02:00
if ( $presetLastUsedTweaksBtn ) {
$presetLastUsedTweaksBtn . Visibility = 'Collapsed'
}
2026-06-05 21:18:14 +02:00
# ---- Preset: Last used selection (apps) ----
2026-04-25 00:40:29 +02:00
if ( $script:SavedAppIds ) {
2026-03-15 22:58:06 +01:00
$presetLastUsed . Add_Click ( {
if ( $script:UpdatingPresets ) { return }
2026-06-05 21:18:14 +02:00
$check = ConvertTo-NormalizedCheckboxState -CheckBox $this
Invoke-AppPreset -AppsPanel $appsPanel -MatchFilter { param ( $c ) ( @ ( $c . AppIds ) | Where-Object { $script:SavedAppIds -contains $_ } ) . Count -gt 0 } -Check $check
2026-02-15 23:08:54 +01:00
} )
}
else {
2026-03-15 22:58:06 +01:00
$script:SavedAppIds = $null
$presetLastUsed . Visibility = 'Collapsed'
2026-02-15 23:08:54 +01:00
}
2026-06-05 21:18:14 +02:00
# ---- Preload app data ----
2026-03-15 22:58:06 +01:00
try {
$script:PreloadedAppData = LoadAppsDetailsFromJson -OnlyInstalled: $false -InstalledList '' -InitialCheckedFromJson: $false
}
catch {
Write-Warning " Failed to preload apps list: $_ "
}
2026-06-05 21:18:14 +02:00
# ---- Show window ----
2026-04-02 00:00:32 +02:00
$frame = [ System.Windows.Threading.DispatcherFrame ] :: new ( )
$window . Add_Closed ( {
$frame . Continue = $false
} )
$window . Show ( ) | Out-Null
[ System.Windows.Threading.Dispatcher ] :: PushFrame ( $frame )
return $null
2026-02-15 23:08:54 +01:00
}