Properly show re-applied features in registry backup overview, properly show applied tweaks checkbox state after registry backup restoration

This commit is contained in:
Jeffrey 2026-05-27 22:05:06 +02:00
parent 6e63b34dbb
commit 6dbaac0513
No known key found for this signature in database
5 changed files with 161 additions and 22 deletions

View File

@ -222,6 +222,8 @@
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" Margin="0,0,0,16">
@ -282,9 +284,38 @@
Visibility="Collapsed"
Text="This will restore the Start Menu pinned apps layout for the current user."/>
<Border x:Name="NonRevertibleSeparator" Grid.Row="3" Height="1" Background="{DynamicResource BorderColor}" Margin="0,12,0,12" Visibility="Collapsed"/>
<Border x:Name="ReappliedSeparator" Grid.Row="3" Height="1" Background="{DynamicResource BorderColor}" Margin="0,12,0,12" Visibility="Collapsed"/>
<Grid x:Name="NonRevertiblePanel" Grid.Row="4" Visibility="Collapsed">
<Grid x:Name="ReappliedPanel" Grid.Row="4" Visibility="Collapsed">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0"
Text="The following changes will be re-applied:"
FontSize="13"
Foreground="{DynamicResource FgColor}"
FontWeight="SemiBold"
Margin="0,0,0,8"/>
<ItemsControl x:Name="ReappliedFeaturesItemsControl" Grid.Row="1">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayText}" FontSize="12" Foreground="{DynamicResource FgColor}" TextWrapping="Wrap" Margin="0,0,0,6"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
<Border x:Name="NonRevertibleSeparator" Grid.Row="5" Height="1" Background="{DynamicResource BorderColor}" Margin="0,12,0,12" Visibility="Collapsed"/>
<Grid x:Name="NonRevertiblePanel" Grid.Row="6" Visibility="Collapsed">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>

View File

@ -79,13 +79,53 @@ function Test-RestoreDialogFeatureVisibleInOverview {
return -not [string]::IsNullOrWhiteSpace([string]$featureDefinition.Category)
}
function Get-SelectedFeatureIdsFromBackup {
function Get-SelectedForwardFeatureIdsFromBackup {
param($SelectedBackup)
$selectedFeatureIds = New-Object System.Collections.Generic.List[string]
$seenSelectedFeatureIds = New-Object 'System.Collections.Generic.HashSet[string]' ([System.StringComparer]::OrdinalIgnoreCase)
foreach ($featureId in @($SelectedBackup.SelectedFeatures)) {
if ([string]::IsNullOrWhiteSpace([string]$featureId)) {
continue
}
$normalizedId = [string]$featureId
if ($seenSelectedFeatureIds.Add($normalizedId)) {
$selectedFeatureIds.Add($normalizedId)
}
}
return @($selectedFeatureIds.ToArray())
}
function Get-SelectedUndoFeatureIdsFromBackup {
param($SelectedBackup)
$selectedUndoFeatureIds = New-Object System.Collections.Generic.List[string]
$seenUndoFeatureIds = New-Object 'System.Collections.Generic.HashSet[string]' ([System.StringComparer]::OrdinalIgnoreCase)
foreach ($featureId in @($SelectedBackup.SelectedUndoFeatures)) {
if ([string]::IsNullOrWhiteSpace([string]$featureId)) {
continue
}
$normalizedId = [string]$featureId
if ($seenUndoFeatureIds.Add($normalizedId)) {
$selectedUndoFeatureIds.Add($normalizedId)
}
}
return @($selectedUndoFeatureIds.ToArray())
}
function Get-CombinedSelectedFeatureIdsFromBackup {
param($SelectedBackup)
$featureIds = New-Object System.Collections.Generic.List[string]
$seenIds = New-Object 'System.Collections.Generic.HashSet[string]' ([System.StringComparer]::OrdinalIgnoreCase)
foreach ($featureId in @($SelectedBackup.SelectedFeatures) + @($SelectedBackup.SelectedUndoFeatures)) {
foreach ($featureId in @(Get-SelectedForwardFeatureIdsFromBackup -SelectedBackup $SelectedBackup) + @(Get-SelectedUndoFeatureIdsFromBackup -SelectedBackup $SelectedBackup)) {
if ([string]::IsNullOrWhiteSpace([string]$featureId)) {
continue
}
@ -99,6 +139,12 @@ function Get-SelectedFeatureIdsFromBackup {
return @($featureIds.ToArray())
}
function Get-SelectedFeatureIdsFromBackup {
param($SelectedBackup)
return @(Get-CombinedSelectedFeatureIdsFromBackup -SelectedBackup $SelectedBackup)
}
function Get-RestoreBackupFeatureLists {
param(
[string[]]$SelectedFeatureIds,

View File

@ -278,7 +278,15 @@ function Show-MainWindow {
if ($restoreBackupBtn) {
$restoreBackupBtn.Add_Click({
try {
Show-RestoreBackupWindow -Owner $window
$restoreResult = Show-RestoreBackupWindow -Owner $window
if ($restoreResult -and $restoreResult.RestoredRegistry -eq $true) {
RefreshCurrentTweakSystemState -ApplyToUi:$false
if ($ShowCurrentlyAppliedTweaksCheckBox -and $ShowCurrentlyAppliedTweaksCheckBox.IsChecked -eq $true) {
ResetTweaksToSystemState -loadSystemState $true
UpdateTweakPresetStates
}
}
}
catch {
Write-Warning "Restore backup action failed: $($_.Exception.Message)"
@ -905,10 +913,9 @@ function Show-MainWindow {
}
}
# Reads current registry state and sets each tweak control to reflect whether that tweak is
# currently applied. Also stores the initial state on each control as a NoteProperty so the
# apply handler can detect which controls actually changed.
function LoadCurrentTweakStateIntoUI {
function RefreshCurrentTweakSystemState {
param([bool]$ApplyToUi)
if (-not $script:UiControlMappings) { return }
if (-not $script:Features) { return }
@ -930,9 +937,12 @@ function Show-MainWindow {
if ($control -is [System.Windows.Controls.CheckBox] -and $mapping.Type -eq 'feature') {
$applied = $false
try { $applied = [bool](Test-FeatureApplied -FeatureId $mapping.FeatureId) } catch {}
$control.IsChecked = $applied
Add-Member -InputObject $control -MemberType NoteProperty -Name 'InitialState' -Value $applied -Force
Add-Member -InputObject $control -MemberType NoteProperty -Name 'SystemState' -Value $applied -Force
if ($ApplyToUi) {
$control.IsChecked = $applied
Add-Member -InputObject $control -MemberType NoteProperty -Name 'InitialState' -Value $applied -Force
}
}
elseif ($control -is [System.Windows.Controls.ComboBox] -and $mapping.Type -eq 'group') {
$groupId = $null
@ -941,13 +951,23 @@ function Show-MainWindow {
if ($groupId -and $groupMap.ContainsKey($groupId)) {
try { $activeIndex = Get-CurrentGroupActiveIndex -Group $groupMap[$groupId] } catch {}
}
$control.SelectedIndex = $activeIndex
Add-Member -InputObject $control -MemberType NoteProperty -Name 'InitialIndex' -Value $activeIndex -Force
Add-Member -InputObject $control -MemberType NoteProperty -Name 'SystemIndex' -Value $activeIndex -Force
if ($ApplyToUi) {
$control.SelectedIndex = $activeIndex
Add-Member -InputObject $control -MemberType NoteProperty -Name 'InitialIndex' -Value $activeIndex -Force
}
}
}
}
# Reads current registry state and sets each tweak control to reflect whether that tweak is
# currently applied. Also stores the initial state on each control as a NoteProperty so the
# apply handler can detect which controls actually changed.
function LoadCurrentTweakStateIntoUI {
RefreshCurrentTweakSystemState -ApplyToUi:$true
}
# Helper function to load apps and populate the app list panel
function script:LoadAppsWithList($listOfApps) {
$script:MainWindowLastSelectedCheckbox = $null

View File

@ -70,6 +70,9 @@ function Show-RestoreBackupDialog {
$backupCreatedText = $window.FindName('BackupCreatedText')
$backupTargetText = $window.FindName('BackupTargetText')
$featuresItemsControl = $window.FindName('FeaturesItemsControl')
$reappliedSeparator = $window.FindName('ReappliedSeparator')
$reappliedPanel = $window.FindName('ReappliedPanel')
$reappliedFeaturesItemsControl = $window.FindName('ReappliedFeaturesItemsControl')
$nonRevertibleSeparator = $window.FindName('NonRevertibleSeparator')
$nonRevertiblePanel = $window.FindName('NonRevertiblePanel')
$nonRevertibleFeaturesItemsControl = $window.FindName('NonRevertibleFeaturesItemsControl')
@ -119,6 +122,8 @@ function Show-RestoreBackupDialog {
$overviewFeaturesSection.Visibility = 'Collapsed'
$overviewSummaryText.Visibility = 'Visible'
$reappliedSeparator.Visibility = 'Collapsed'
$reappliedPanel.Visibility = 'Collapsed'
$nonRevertibleSeparator.Visibility = 'Collapsed'
$nonRevertiblePanel.Visibility = 'Collapsed'
$introInfoPanel.Visibility = 'Collapsed'
@ -215,13 +220,33 @@ function Show-RestoreBackupDialog {
}
}
$selectedFeatureIds = Get-SelectedFeatureIdsFromBackup -SelectedBackup $SelectedBackup
$featureLists = Get-RestoreBackupFeatureLists -SelectedFeatureIds $selectedFeatureIds -Features $script:Features
$revertibleFeaturesList = @($featureLists.Revertible)
$nonRevertibleFeaturesList = @($featureLists.NonRevertible)
Write-Host "Backup overview prepared. Revertible=$($revertibleFeaturesList.Count), NonRevertible=$($nonRevertibleFeaturesList.Count)"
$selectedForwardFeatureIds = @(Get-SelectedForwardFeatureIdsFromBackup -SelectedBackup $SelectedBackup)
$selectedUndoFeatureIds = @(Get-SelectedUndoFeatureIdsFromBackup -SelectedBackup $SelectedBackup)
if ($revertibleFeaturesList.Count -eq 0) {
$seenForwardFeatureIds = New-Object 'System.Collections.Generic.HashSet[string]' ([System.StringComparer]::OrdinalIgnoreCase)
foreach ($featureId in $selectedForwardFeatureIds) {
[void]$seenForwardFeatureIds.Add([string]$featureId)
}
$filteredUndoFeatureIds = New-Object System.Collections.Generic.List[string]
foreach ($featureId in $selectedUndoFeatureIds) {
if ($seenForwardFeatureIds.Contains([string]$featureId)) {
continue
}
$filteredUndoFeatureIds.Add([string]$featureId)
}
$forwardFeatureLists = Get-RestoreBackupFeatureLists -SelectedFeatureIds $selectedForwardFeatureIds -Features $script:Features
$undoFeatureLists = Get-RestoreBackupFeatureLists -SelectedFeatureIds @($filteredUndoFeatureIds.ToArray()) -Features $script:Features
$combinedFeatureLists = Get-RestoreBackupFeatureLists -SelectedFeatureIds (Get-SelectedFeatureIdsFromBackup -SelectedBackup $SelectedBackup) -Features $script:Features
$revertibleFeaturesList = @($forwardFeatureLists.Revertible)
$reappliedFeaturesList = @($undoFeatureLists.Revertible)
$nonRevertibleFeaturesList = @($combinedFeatureLists.NonRevertible)
Write-Host "Backup overview prepared. Reverted=$($revertibleFeaturesList.Count), ReApplied=$($reappliedFeaturesList.Count), NonRevertible=$($nonRevertibleFeaturesList.Count)"
if ($revertibleFeaturesList.Count -eq 0 -and $reappliedFeaturesList.Count -eq 0) {
throw 'The selected backup does not contain any changes that can be restored.'
}
@ -229,13 +254,16 @@ function Show-RestoreBackupDialog {
$backupCreatedText.Text = $createdText
$backupTargetText.Text = GetFriendlyRegistryBackupTarget -Target ([string]$SelectedBackup.Target)
$featuresItemsControl.ItemsSource = $revertibleFeaturesList
$overviewFeaturesSection.Visibility = 'Visible'
$overviewFeaturesSection.Visibility = if ($revertibleFeaturesList.Count -gt 0) { 'Visible' } else { 'Collapsed' }
$reappliedFeaturesItemsControl.ItemsSource = $reappliedFeaturesList
if ($reappliedFeaturesList.Count -gt 0) { $reappliedPanel.Visibility = 'Visible' } else { $reappliedPanel.Visibility = 'Collapsed' }
if ($revertibleFeaturesList.Count -gt 0 -and $reappliedFeaturesList.Count -gt 0) { $reappliedSeparator.Visibility = 'Visible' } else { $reappliedSeparator.Visibility = 'Collapsed' }
$overviewSummaryText.Visibility = 'Collapsed'
$nonRevertibleFeaturesItemsControl.ItemsSource = $nonRevertibleFeaturesList
$hasNonRevertibleItems = ($nonRevertibleFeaturesList.Count -gt 0)
if ($hasNonRevertibleItems) { $nonRevertiblePanel.Visibility = 'Visible' } else { $nonRevertiblePanel.Visibility = 'Collapsed' }
if ($hasNonRevertibleItems) { $nonRevertibleSeparator.Visibility = 'Visible' } else { $nonRevertibleSeparator.Visibility = 'Collapsed' }
if ($hasNonRevertibleItems -and ($revertibleFeaturesList.Count -gt 0 -or $reappliedFeaturesList.Count -gt 0)) { $nonRevertibleSeparator.Visibility = 'Visible' } else { $nonRevertibleSeparator.Visibility = 'Collapsed' }
$introInfoPanel.Visibility = 'Collapsed'
$overviewPanel.Visibility = 'Visible'

View File

@ -7,10 +7,15 @@ function Show-RestoreBackupWindow {
try {
Write-Host 'Opening restore backup dialog.'
$restoreResult = [PSCustomObject]@{
RestoredRegistry = $false
RestoredStartMenu = $false
}
$dialogResult = Show-RestoreBackupDialog -Owner $Owner
if (-not $dialogResult -or $dialogResult.Result -eq 'Cancel') {
Write-Host 'Restore canceled by user.'
return
return $restoreResult
}
$successMessage = $null
@ -24,6 +29,7 @@ function Show-RestoreBackupWindow {
Write-Host "User confirmed registry restore for $($backup.Target)."
Restore-RegistryBackupState -Backup $backup
$restoreResult.RestoredRegistry = $true
$successMessage = 'Registry backup restored successfully. Some changes may require a sign out or restart to take effect.'
}
elseif ($dialogResult.Result -eq 'RestoreStartMenu') {
@ -69,6 +75,8 @@ function Show-RestoreBackupWindow {
$successMessage = "The Start Menu backup was successfully restored for the current user. The changes will apply the next time you sign in."
}
}
$restoreResult.RestoredStartMenu = $true
}
if ($warningMessage) {
@ -79,10 +87,16 @@ function Show-RestoreBackupWindow {
Write-Host "$successMessage"
Show-MessageBox -Title 'Backup Restored' -Message $successMessage -Icon Success
}
return $restoreResult
}
catch {
$errorMessage = if ($_.Exception.Message) { $_.Exception.Message } else { 'An unexpected error occurred.' }
Write-Error "Restore operation failed: $errorMessage"
Show-MessageBox -Title 'Error' -Message "Restore failed: $errorMessage" -Icon Error
return [PSCustomObject]@{
RestoredRegistry = $false
RestoredStartMenu = $false
}
}
}