mirror of
https://github.com/Raphire/Win11Debloat.git
synced 2026-10-08 06:26:45 +00:00
Add settings UI for theme and language (#780)
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
#>
|
||||
function Get-SystemUsesDarkMode {
|
||||
try {
|
||||
$personalizeKey = Get-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize'
|
||||
$personalizeKey = Get-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize' -ErrorAction Stop
|
||||
|
||||
if ($null -eq $personalizeKey) {
|
||||
Write-Host "WARNING: Unable to retrieve personalization settings." -ForegroundColor Yellow
|
||||
|
||||
@@ -444,11 +444,12 @@ function Add-AppsToMainWindow {
|
||||
$dot = New-Object System.Windows.Shapes.Ellipse
|
||||
$dot.Style = $Window.Resources['AppRecommendationDotStyle']
|
||||
$dot.Fill = switch ($app.Recommendation) { 'safe' { $brushSafe } 'unsafe' { $brushUnsafe } default { $brushDefault } }
|
||||
$dot.ToolTip = switch ($app.Recommendation) {
|
||||
'safe' { Get-Translation -Key 'AppLegendRecommendedTooltip' }
|
||||
'unsafe' { Get-Translation -Key 'AppLegendNotRecommendedTooltip' }
|
||||
default { Get-Translation -Key 'AppLegendOptionalTooltip' }
|
||||
$recommendationKey = switch ($app.Recommendation) {
|
||||
'safe' { 'AppLegendRecommendedTooltip' }
|
||||
'unsafe' { 'AppLegendNotRecommendedTooltip' }
|
||||
default { 'AppLegendOptionalTooltip' }
|
||||
}
|
||||
$dot.SetResourceReference([System.Windows.FrameworkElement]::ToolTipProperty, "Language_$recommendationKey")
|
||||
[System.Windows.Controls.Grid]::SetColumn($dot, 0)
|
||||
|
||||
$tbName = New-Object System.Windows.Controls.TextBlock
|
||||
@@ -479,6 +480,8 @@ function Add-AppsToMainWindow {
|
||||
Add-Member -InputObject $checkbox -MemberType NoteProperty -Name 'SelectedByDefault' -Value $app.SelectedByDefault
|
||||
Add-Member -InputObject $checkbox -MemberType NoteProperty -Name 'AppIds' -Value @($app.AppId)
|
||||
Add-Member -InputObject $checkbox -MemberType NoteProperty -Name 'AppIdDisplay' -Value $app.AppIdDisplay
|
||||
Add-Member -InputObject $checkbox -MemberType NoteProperty -Name 'AppNameText' -Value $tbName
|
||||
Add-Member -InputObject $checkbox -MemberType NoteProperty -Name 'AppDescriptionText' -Value $tbDesc
|
||||
|
||||
$checkbox.Add_Checked({
|
||||
$w = $script:MainWindow
|
||||
@@ -574,6 +577,8 @@ function Initialize-MainWindowApps {
|
||||
# Prevent concurrent loads
|
||||
if ($script:IsLoadingApps) { return }
|
||||
$script:IsLoadingApps = $true
|
||||
$settingsButton = $Window.FindName('SettingsBtn')
|
||||
if ($settingsButton) { $settingsButton.IsEnabled = $false }
|
||||
|
||||
if ($ImportConfigBtn) {
|
||||
$ImportConfigBtn.IsEnabled = $false
|
||||
@@ -622,6 +627,8 @@ function Initialize-MainWindowApps {
|
||||
}
|
||||
finally {
|
||||
$script:IsLoadingApps = $false
|
||||
$settingsButton = $Window.FindName('SettingsBtn')
|
||||
if ($settingsButton) { $settingsButton.IsEnabled = $true }
|
||||
}
|
||||
}) | Out-Null
|
||||
}
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Populates dynamic language resources for a window and its detached menus.
|
||||
|
||||
.DESCRIPTION
|
||||
Keeps text bound to language resources in sync with the active language, including
|
||||
menus and popups that use separate resources.
|
||||
|
||||
.PARAMETER Window
|
||||
The window whose language resources are updated, along with its menus and popups.
|
||||
#>
|
||||
function Update-WindowLanguageResources {
|
||||
param([System.Windows.Window]$Window)
|
||||
|
||||
$keys = @(foreach ($language in (Get-LanguageFallbackChain -Lang $script:Lang)) {
|
||||
$language.Chrome.PSObject.Properties.Name
|
||||
}) | Sort-Object -Unique
|
||||
# Popups and context menus can have a separate resource lookup tree.
|
||||
$targets = @($Window, $Window.FindName('MainMenu'), $Window.FindName('PresetsPopup'), $Window.FindName('TweaksPresetsPopup')) | Where-Object { $null -ne $_ }
|
||||
foreach ($key in $keys) {
|
||||
$text = Get-Translation -Key $key -Section 'Chrome'
|
||||
foreach ($target in $targets) { $target.Resources["Language_$key"] = $text }
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Refreshes localized text on existing main window tweak controls.
|
||||
|
||||
.DESCRIPTION
|
||||
Updates tweak text and accessibility labels for the active language while preserving
|
||||
existing controls and selections.
|
||||
|
||||
.PARAMETER Window
|
||||
The main window containing the registered dynamic tweak controls.
|
||||
#>
|
||||
function Update-MainWindowTweakLanguage {
|
||||
param([System.Windows.Window]$Window)
|
||||
|
||||
foreach ($category in $script:CategoryLanguageControls) {
|
||||
$label = Get-Translation -Key $category.CategoryId -Field 'Label' -Section 'Categories'
|
||||
$category.Header.Text = $label
|
||||
$category.HelpButton.ToolTip = Get-Translation -Key 'TweaksCategoryHelpTooltip' -FormatArgs @($label)
|
||||
}
|
||||
foreach ($name in $script:UiControlMappings.Keys) {
|
||||
$mapping = $script:UiControlMappings[$name]
|
||||
$control = $Window.FindName($name)
|
||||
if (-not $control) { continue }
|
||||
$text = Get-TweakControlText -Mapping $mapping
|
||||
if ($mapping.Type -eq 'group') {
|
||||
# Update item contents, keeping item identity and SelectedIndex unchanged.
|
||||
for ($i = 0; $i -lt $text.Options.Count; $i++) {
|
||||
$control.Items[$i].Content = $text.Options[$i]
|
||||
}
|
||||
}
|
||||
$control.SetValue([System.Windows.Automation.AutomationProperties]::NameProperty, $text.Label)
|
||||
if ($control -is [System.Windows.Controls.CheckBox]) { $control.Content = $text.Label }
|
||||
else { $Window.FindName("${name}_LabelBorder").Child.Text = $text.Label }
|
||||
# Label borders share this tooltip TextBlock with their combobox.
|
||||
if ($control.ToolTip -is [System.Windows.Controls.TextBlock]) { $control.ToolTip.Text = $text.ToolTip }
|
||||
}
|
||||
Update-FeatureLabelLookup
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Refreshes localized app text and preset tooltips.
|
||||
|
||||
.DESCRIPTION
|
||||
Updates app details, accessibility labels, and preset tooltips for the active language
|
||||
while preserving app selections.
|
||||
|
||||
.PARAMETER AppsPanel
|
||||
The panel containing the app selection controls.
|
||||
#>
|
||||
function Update-MainWindowAppLanguage {
|
||||
param([System.Windows.Controls.Panel]$AppsPanel)
|
||||
|
||||
foreach ($app in $AppsPanel.Children) {
|
||||
if ($app -isnot [System.Windows.Controls.CheckBox]) { continue }
|
||||
$primaryAppId = @($app.AppIds)[0]
|
||||
$app.AppName = Get-Translation -Key $primaryAppId -Field 'FriendlyName' -Section 'Apps'
|
||||
$app.AppDescription = Get-Translation -Key $primaryAppId -Field 'Description' -Section 'Apps'
|
||||
$app.AppNameText.Text = $app.AppName
|
||||
$app.AppDescriptionText.Text = $app.AppDescription
|
||||
$app.AppDescriptionText.ToolTip = $app.AppDescription
|
||||
$app.SetValue([System.Windows.Automation.AutomationProperties]::NameProperty, $app.AppName)
|
||||
}
|
||||
foreach ($preset in $script:JsonPresetCheckboxes) {
|
||||
$preset.ToolTip = Get-Translation -Key 'AppPresetSelectTooltip' -FormatArgs @($preset.Content)
|
||||
}
|
||||
# A future installed-only toggle must not consume a catalog in the old language.
|
||||
$script:PreloadedAppData = $null
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Applies the current language to an existing main window.
|
||||
|
||||
.DESCRIPTION
|
||||
Updates the interface and search results for the active language while preserving
|
||||
user selections and app scroll position.
|
||||
|
||||
.PARAMETER Window
|
||||
The initialized main window to refresh.
|
||||
#>
|
||||
function Update-MainWindowLanguage {
|
||||
param([System.Windows.Window]$Window)
|
||||
|
||||
$appsPanel = $Window.FindName('AppSelectionPanel')
|
||||
$appsScroll = Find-ParentScrollViewer -Element $appsPanel
|
||||
$appOffset = if ($appsScroll) { $appsScroll.VerticalOffset } else { 0 }
|
||||
$activeMatch = if ($script:AppSearchMatchIndex -ge 0 -and $script:AppSearchMatchIndex -lt $script:AppSearchMatches.Count) {
|
||||
$script:AppSearchMatches[$script:AppSearchMatchIndex]
|
||||
}
|
||||
|
||||
Update-WindowLanguageResources -Window $Window
|
||||
Update-MainWindowTweakLanguage -Window $Window
|
||||
Update-MainWindowAppLanguage -AppsPanel $appsPanel
|
||||
|
||||
$userCombo = $Window.FindName('UserSelectionCombo')
|
||||
$userCombo.Items[0].Content = Get-Translation -Key 'HomeCurrentUserWithName' -FormatArgs @(Get-UserName)
|
||||
Update-UserSelectionDescription -Window $Window -UserSelectionCombo $userCombo -OtherUsernameTextBox $Window.FindName('OtherUsernameTextBox') -UserSelectionDescription $Window.FindName('UserSelectionDescription')
|
||||
Update-AppSelectionStatus -AppsPanel $appsPanel -AppSelectionStatus $Window.FindName('AppSelectionStatus') -AppRemovalScopeCombo $Window.FindName('AppRemovalScopeCombo') -AppRemovalScopeSection $Window.FindName('AppRemovalScopeSection') -AppRemovalScopeDescription $Window.FindName('AppRemovalScopeDescription') -UserSelectionCombo $userCombo
|
||||
$validationMessage = $Window.FindName('UsernameValidationMessage')
|
||||
if ($validationMessage.Text) {
|
||||
Test-OtherUsername -Window $Window -UserSelectionCombo $userCombo -OtherUsernameTextBox $Window.FindName('OtherUsernameTextBox') -UsernameValidationMessage $validationMessage -AppRemovalScopeCombo $Window.FindName('AppRemovalScopeCombo') | Out-Null
|
||||
}
|
||||
|
||||
Update-AppsPanelSort -AppsPanel $appsPanel -SortArrowName $Window.FindName('SortArrowName') -SortArrowDescription $Window.FindName('SortArrowDescription') -SortArrowAppId $Window.FindName('SortArrowAppId')
|
||||
# Rerun matching against the translated labels without moving the user's viewport.
|
||||
$script:UpdatingLanguage = $true
|
||||
try {
|
||||
foreach ($name in @('AppSearchBox', 'TweakSearchBox')) {
|
||||
$Window.FindName($name).RaiseEvent([System.Windows.Controls.TextChangedEventArgs]::new([System.Windows.Controls.TextBox]::TextChangedEvent, [System.Windows.Controls.UndoAction]::None))
|
||||
}
|
||||
}
|
||||
finally { $script:UpdatingLanguage = $false }
|
||||
$activeIndex = [array]::IndexOf($script:AppSearchMatches, $activeMatch)
|
||||
if ($activeIndex -ge 0) {
|
||||
$script:AppSearchMatches[0].SetResourceReference([System.Windows.Controls.Control]::BackgroundProperty, 'SearchHighlightColor')
|
||||
$activeMatch.SetResourceReference([System.Windows.Controls.Control]::BackgroundProperty, 'SearchHighlightActiveColor')
|
||||
$script:AppSearchMatchIndex = $activeIndex
|
||||
}
|
||||
if ($appsScroll) { $appsScroll.ScrollToVerticalOffset($appOffset) }
|
||||
Hide-Bubble -Immediate
|
||||
}
|
||||
@@ -1,6 +1,77 @@
|
||||
# MainWindow-TweaksBuilder.ps1
|
||||
# Dynamic tweaks UI construction from Features.json, tweak state management, selection clear, and search/highlight.
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Resolves localized labels, tooltips, and options for a tweak control.
|
||||
|
||||
.DESCRIPTION
|
||||
Provides consistent text for individual features and groups during control creation
|
||||
and language changes.
|
||||
|
||||
.PARAMETER Mapping
|
||||
A control mapping with Type and either FeatureId or GroupId and Values. Group values
|
||||
contain FeatureIds and fallback Label text.
|
||||
|
||||
.PARAMETER Features
|
||||
The feature definitions keyed by FeatureId. Defaults to $script:Features.
|
||||
|
||||
.OUTPUTS
|
||||
PSCustomObject. Label, ToolTip, and Options, beginning with the No Change option.
|
||||
#>
|
||||
function Get-TweakControlText {
|
||||
param(
|
||||
[hashtable]$Mapping,
|
||||
[hashtable]$Features = $script:Features
|
||||
)
|
||||
|
||||
$options = @((Get-Translation -Key 'TweaksOptionNoChange'))
|
||||
if ($Mapping.Type -eq 'group') {
|
||||
$label = Get-Translation -Key $Mapping.GroupId -Field 'Label' -Section 'UiGroups'
|
||||
$tooltip = Get-Translation -Key $Mapping.GroupId -Field 'ToolTip' -Section 'UiGroups'
|
||||
$options += @($Mapping.Values | ForEach-Object {
|
||||
Get-GroupValueTranslation -GroupId $Mapping.GroupId -FeatureId $_.FeatureIds[0] -FallbackLabel $_.Label
|
||||
})
|
||||
}
|
||||
else {
|
||||
$feature = $Features[$Mapping.FeatureId]
|
||||
$label = Get-Translation -Key $Mapping.FeatureId -Field 'Label' -Section 'Features'
|
||||
$tooltip = if ($feature.DisableWhenApplied -eq $true) {
|
||||
Get-Translation -Key 'TweaksAlreadyAppliedTooltip'
|
||||
}
|
||||
else { Get-Translation -Key $Mapping.FeatureId -Field 'ToolTip' -Section 'Features' }
|
||||
$optionKey = if ($Mapping.FeatureId -match '^Disable') { 'TweaksOptionDisable' }
|
||||
elseif ($Mapping.FeatureId -match '^Enable') { 'TweaksOptionEnable' }
|
||||
else { 'TweaksOptionApply' }
|
||||
$options += Get-Translation -Key $optionKey
|
||||
}
|
||||
|
||||
return [PSCustomObject]@{ Label = $label; ToolTip = $tooltip; Options = $options }
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Rebuilds localized feature and undo label lookups.
|
||||
|
||||
.DESCRIPTION
|
||||
Keeps feature names used in change overviews in sync with the active language.
|
||||
|
||||
.PARAMETER Features
|
||||
The feature definitions to translate. Defaults to the values of $script:Features.
|
||||
#>
|
||||
function Update-FeatureLabelLookup {
|
||||
param([object[]]$Features = @($script:Features.Values))
|
||||
|
||||
$script:FeatureLabelLookup = @{}
|
||||
$script:UndoFeatureLabelLookup = @{}
|
||||
foreach ($feature in $Features) {
|
||||
$script:FeatureLabelLookup[$feature.FeatureId] = Get-Translation -Key $feature.FeatureId -Field 'Label' -Section 'Features'
|
||||
if ($feature.UndoLabel) {
|
||||
$script:UndoFeatureLabelLookup[$feature.FeatureId] = Get-Translation -Key $feature.FeatureId -Field 'UndoLabel' -Section 'Features'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Builds the main window's dynamic tweak controls from Features.json.
|
||||
@@ -39,6 +110,7 @@ function New-DynamicTweakControls {
|
||||
|
||||
$script:UiControlMappings = @{}
|
||||
$script:CategoryCardMap = @{}
|
||||
$script:CategoryLanguageControls = @()
|
||||
$script:TweaksCompactMode = $null
|
||||
$script:TweaksCardsMovedFromCol2 = @()
|
||||
|
||||
@@ -128,6 +200,56 @@ function New-DynamicTweakControls {
|
||||
return $combo
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Attaches a wrapping tooltip to a tweak control and its label border.
|
||||
|
||||
.PARAMETER Control
|
||||
The tweak control receiving the tooltip.
|
||||
|
||||
.PARAMETER TooltipText
|
||||
The localized text displayed by the tooltip.
|
||||
|
||||
.PARAMETER ShowOnDisabled
|
||||
Enables tooltip display even when the tweak control is disabled.
|
||||
#>
|
||||
function Set-TweakControlToolTip($control, $tooltipText, [switch]$ShowOnDisabled) {
|
||||
$tipBlock = New-Object System.Windows.Controls.TextBlock
|
||||
$tipBlock.Text = $tooltipText
|
||||
$tipBlock.TextWrapping = 'Wrap'
|
||||
$tipBlock.MaxWidth = 420
|
||||
$control.ToolTip = $tipBlock
|
||||
if ($ShowOnDisabled) {
|
||||
[System.Windows.Controls.ToolTipService]::SetShowOnDisabled($control, $true)
|
||||
}
|
||||
$labelBorder = $Window.FindName("$($control.Name)_LabelBorder")
|
||||
if ($labelBorder) { $labelBorder.ToolTip = $tipBlock }
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Creates and maps a localized control for an individual feature.
|
||||
|
||||
.PARAMETER Feature
|
||||
The feature definition to display.
|
||||
|
||||
.PARAMETER Parent
|
||||
The category panel that receives the generated control.
|
||||
|
||||
.PARAMETER CategoryId
|
||||
The category identifier stored in the control mapping.
|
||||
#>
|
||||
function Add-FeatureTweakControl($feature, $parent, $categoryId) {
|
||||
$mapping = @{ Type = 'feature'; FeatureId = $feature.FeatureId; CategoryId = $categoryId }
|
||||
$text = Get-TweakControlText -Mapping $mapping -Features $featureMap
|
||||
$controlName = ("Feature_{0}_Combo" -f $feature.FeatureId) -replace '[^a-zA-Z0-9_]', ''
|
||||
$control = New-LabeledCombo -parent $parent -labelText $text.Label -comboName $controlName -items $text.Options
|
||||
if ($feature.ToolTip -or $feature.DisableWhenApplied -eq $true) {
|
||||
Set-TweakControlToolTip -control $control -tooltipText $text.ToolTip -ShowOnDisabled
|
||||
}
|
||||
$script:UiControlMappings[$controlName] = $mapping
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Returns the Features wiki URL for a tweak category.
|
||||
@@ -213,6 +335,7 @@ function New-DynamicTweakControls {
|
||||
if ($button.Tag) { Start-Process $button.Tag }
|
||||
})
|
||||
$headerRow.Children.Add($helpBtn) | Out-Null
|
||||
$script:CategoryLanguageControls += @{ CategoryId = $categoryObj.CategoryId; Header = $header; HelpButton = $helpBtn }
|
||||
|
||||
$panel.Children.Add($headerRow) | Out-Null
|
||||
|
||||
@@ -348,93 +471,31 @@ function New-DynamicTweakControls {
|
||||
$soleFid = $featureIds[0]
|
||||
|
||||
if ($featureMap.ContainsKey($soleFid)) {
|
||||
$soleFeature = $featureMap[$soleFid]
|
||||
$soleFeatureLabel = Get-Translation -Key $soleFeature.FeatureId -Field 'Label' -Section 'Features'
|
||||
$opt = Get-Translation -Key 'TweaksOptionApply'
|
||||
if ($soleFeature.FeatureId -match '^Disable') { $opt = Get-Translation -Key 'TweaksOptionDisable' } elseif ($soleFeature.FeatureId -match '^Enable') { $opt = Get-Translation -Key 'TweaksOptionEnable' }
|
||||
$items = @((Get-Translation -Key 'TweaksOptionNoChange'), $opt)
|
||||
$comboName = ("Feature_{0}_Combo" -f $soleFeature.FeatureId) -replace '[^a-zA-Z0-9_]', ''
|
||||
if (-not $panel) { $panel = Get-OrCreateCategoryCard -categoryObj $categoryObj }
|
||||
$combo = New-LabeledCombo -parent $panel -labelText $soleFeatureLabel -comboName $comboName -items $items
|
||||
# attach tooltip from Features.json if present
|
||||
if ($soleFeature.ToolTip -or $soleFeature.DisableWhenApplied -eq $true) {
|
||||
$tooltipText = Get-Translation -Key $soleFeature.FeatureId -Field 'ToolTip' -Section 'Features'
|
||||
if ($soleFeature.DisableWhenApplied -eq $true) {
|
||||
$tooltipText = Get-Translation -Key 'TweaksAlreadyAppliedTooltip'
|
||||
}
|
||||
$tipBlock = New-Object System.Windows.Controls.TextBlock
|
||||
$tipBlock.Text = $tooltipText
|
||||
$tipBlock.TextWrapping = 'Wrap'
|
||||
$tipBlock.MaxWidth = 420
|
||||
$combo.ToolTip = $tipBlock
|
||||
[System.Windows.Controls.ToolTipService]::SetShowOnDisabled($combo, $true)
|
||||
$lblBorderObj = $null
|
||||
try { $lblBorderObj = $Window.FindName("$comboName`_LabelBorder") } catch {}
|
||||
if ($lblBorderObj) { $lblBorderObj.ToolTip = $tipBlock }
|
||||
}
|
||||
$script:UiControlMappings[$comboName] = @{ Type = 'feature'; FeatureId = $soleFeature.FeatureId; Label = $soleFeatureLabel; CategoryId = $categoryId }
|
||||
Add-FeatureTweakControl -feature $featureMap[$soleFid] -parent $panel -categoryId $categoryId
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
$groupLabel = Get-Translation -Key $group.GroupId -Field 'Label' -Section 'UiGroups'
|
||||
$items = @((Get-Translation -Key 'TweaksOptionNoChange')) + ($filteredValues | ForEach-Object { Get-GroupValueTranslation -GroupId $group.GroupId -FeatureId $_.FeatureIds[0] -FallbackLabel $_.Label })
|
||||
$mapping = @{ Type = 'group'; GroupId = $group.GroupId; Values = $filteredValues; CategoryId = $categoryId }
|
||||
$text = Get-TweakControlText -Mapping $mapping -Features $featureMap
|
||||
$comboName = 'Group_{0}Combo' -f $group.GroupId
|
||||
if (-not $panel) { $panel = Get-OrCreateCategoryCard -categoryObj $categoryObj }
|
||||
$combo = New-LabeledCombo -parent $panel -labelText $groupLabel -comboName $comboName -items $items
|
||||
# attach tooltip from UiGroups if present
|
||||
$combo = New-LabeledCombo -parent $panel -labelText $text.Label -comboName $comboName -items $text.Options
|
||||
if ($group.ToolTip) {
|
||||
$tipBlock = New-Object System.Windows.Controls.TextBlock
|
||||
$tipBlock.Text = Get-Translation -Key $group.GroupId -Field 'ToolTip' -Section 'UiGroups'
|
||||
$tipBlock.TextWrapping = 'Wrap'
|
||||
$tipBlock.MaxWidth = 420
|
||||
$combo.ToolTip = $tipBlock
|
||||
$lblBorderObj = $null
|
||||
try { $lblBorderObj = $Window.FindName("$comboName`_LabelBorder") } catch {}
|
||||
if ($lblBorderObj) { $lblBorderObj.ToolTip = $tipBlock }
|
||||
Set-TweakControlToolTip -control $combo -tooltipText $text.ToolTip
|
||||
}
|
||||
$script:UiControlMappings[$comboName] = @{ Type = 'group'; Values = $filteredValues; Label = $groupLabel; CategoryId = $categoryId }
|
||||
$script:UiControlMappings[$comboName] = $mapping
|
||||
}
|
||||
elseif ($item.Type -eq 'feature') {
|
||||
$feature = $item.Data
|
||||
$featureLabel = Get-Translation -Key $feature.FeatureId -Field 'Label' -Section 'Features'
|
||||
$opt = Get-Translation -Key 'TweaksOptionApply'
|
||||
if ($feature.FeatureId -match '^Disable') { $opt = Get-Translation -Key 'TweaksOptionDisable' } elseif ($feature.FeatureId -match '^Enable') { $opt = Get-Translation -Key 'TweaksOptionEnable' }
|
||||
$items = @((Get-Translation -Key 'TweaksOptionNoChange'), $opt)
|
||||
$comboName = ("Feature_{0}_Combo" -f $feature.FeatureId) -replace '[^a-zA-Z0-9_]', ''
|
||||
if (-not $panel) { $panel = Get-OrCreateCategoryCard -categoryObj $categoryObj }
|
||||
$combo = New-LabeledCombo -parent $panel -labelText $featureLabel -comboName $comboName -items $items
|
||||
# attach tooltip from Features.json if present, and include the disabled-state reason
|
||||
if ($feature.ToolTip -or $feature.DisableWhenApplied -eq $true) {
|
||||
$tooltipText = Get-Translation -Key $feature.FeatureId -Field 'ToolTip' -Section 'Features'
|
||||
if ($feature.DisableWhenApplied -eq $true) {
|
||||
$tooltipText = Get-Translation -Key 'TweaksAlreadyAppliedTooltip'
|
||||
}
|
||||
|
||||
$tipBlock = New-Object System.Windows.Controls.TextBlock
|
||||
$tipBlock.Text = $tooltipText
|
||||
$tipBlock.TextWrapping = 'Wrap'
|
||||
$tipBlock.MaxWidth = 420
|
||||
$combo.ToolTip = $tipBlock
|
||||
[System.Windows.Controls.ToolTipService]::SetShowOnDisabled($combo, $true)
|
||||
$lblBorderObj = $null
|
||||
try { $lblBorderObj = $Window.FindName("$comboName`_LabelBorder") } catch {}
|
||||
if ($lblBorderObj) { $lblBorderObj.ToolTip = $tipBlock }
|
||||
}
|
||||
$script:UiControlMappings[$comboName] = @{ Type = 'feature'; FeatureId = $feature.FeatureId; Label = $feature.Label; CategoryId = $categoryId }
|
||||
Add-FeatureTweakControl -feature $item.Data -parent $panel -categoryId $categoryId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Build a feature-label lookup so GenerateOverview can resolve feature IDs without reloading JSON
|
||||
$script:FeatureLabelLookup = @{}
|
||||
$script:UndoFeatureLabelLookup = @{}
|
||||
foreach ($f in $featuresJson.Features) {
|
||||
$script:FeatureLabelLookup[$f.FeatureId] = Get-Translation -Key $f.FeatureId -Field 'Label' -Section 'Features'
|
||||
if ($f.UndoLabel) {
|
||||
$script:UndoFeatureLabelLookup[$f.FeatureId] = Get-Translation -Key $f.FeatureId -Field 'UndoLabel' -Section 'Features'
|
||||
}
|
||||
}
|
||||
# Overview generation uses the same labels as language refresh.
|
||||
Update-FeatureLabelLookup -Features $featuresJson.Features
|
||||
}
|
||||
|
||||
function Update-CurrentTweakSystemState {
|
||||
|
||||
@@ -113,6 +113,7 @@ function Set-WindowThemeResources {
|
||||
}
|
||||
|
||||
$Theme = if ($usesDarkMode) { 'Dark' } else { 'Light' }
|
||||
$window.Resources['AppUsesDarkMode'] = $usesDarkMode
|
||||
|
||||
foreach ($Group in $ThemeColor.GetEnumerator()) {
|
||||
foreach ($Resource in $Group.Value.GetEnumerator()) {
|
||||
@@ -130,12 +131,13 @@ function Set-WindowThemeResources {
|
||||
$window.Resources['AppIconFontFamily'] = [System.Windows.Media.FontFamily]::new($iconFontName)
|
||||
|
||||
# Load and merge shared styles
|
||||
if ($script:SharedStylesSchema -and (Test-Path $script:SharedStylesSchema)) {
|
||||
if (-not $window.Resources.Contains('AppSharedStylesLoaded') -and $script:SharedStylesSchema -and (Test-Path $script:SharedStylesSchema)) {
|
||||
$sharedXaml = Get-Content -Path $script:SharedStylesSchema -Raw
|
||||
$sharedReader = [System.Xml.XmlReader]::Create([System.IO.StringReader]::new($sharedXaml))
|
||||
try {
|
||||
$sharedDict = [System.Windows.Markup.XamlReader]::Load($sharedReader)
|
||||
$window.Resources.MergedDictionaries.Add($sharedDict)
|
||||
$window.Resources['AppSharedStylesLoaded'] = $true
|
||||
}
|
||||
finally {
|
||||
$sharedReader.Close()
|
||||
|
||||
@@ -13,7 +13,7 @@ function Show-AboutDialog {
|
||||
|
||||
Add-Type -AssemblyName PresentationFramework,PresentationCore,WindowsBase | Out-Null
|
||||
|
||||
$usesDarkMode = Get-SystemUsesDarkMode
|
||||
$usesDarkMode = Get-AppUsesDarkMode
|
||||
|
||||
# Determine owner window
|
||||
$ownerWindow = if ($Owner) { $Owner } else { $script:GuiWindow }
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
function Show-AppSelectionWindow {
|
||||
Add-Type -AssemblyName PresentationFramework,PresentationCore,WindowsBase | Out-Null
|
||||
|
||||
$usesDarkMode = Get-SystemUsesDarkMode
|
||||
$usesDarkMode = Get-AppUsesDarkMode
|
||||
|
||||
# Show overlay if main window exists
|
||||
$overlay = $null
|
||||
|
||||
@@ -18,7 +18,7 @@ function Show-ApplyModal {
|
||||
|
||||
Add-Type -AssemblyName PresentationFramework,PresentationCore,WindowsBase | Out-Null
|
||||
|
||||
$usesDarkMode = Get-SystemUsesDarkMode
|
||||
$usesDarkMode = Get-AppUsesDarkMode
|
||||
|
||||
# Determine owner window
|
||||
$ownerWindow = if ($Owner) { $Owner } else { $script:GuiWindow }
|
||||
|
||||
@@ -3,14 +3,16 @@
|
||||
Creates and displays the main Win11Debloat window.
|
||||
#>
|
||||
function Show-MainWindow {
|
||||
if (-not $script:Lang) { throw 'Unable to load UI language.' }
|
||||
$script:CancelRequested = $false
|
||||
Add-Type -AssemblyName PresentationFramework,PresentationCore,WindowsBase,System.Windows.Forms | Out-Null
|
||||
|
||||
$WinVersion = Get-ItemPropertyValue 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' CurrentBuild
|
||||
$usesDarkMode = Get-SystemUsesDarkMode
|
||||
$usesDarkMode = Get-AppUsesDarkMode
|
||||
|
||||
# ---- Load XAML ----
|
||||
$xaml = Get-Content -Path $script:MainWindowSchema -Raw
|
||||
$xaml = ConvertTo-LocalizedXaml -Xaml $xaml
|
||||
$xaml = ConvertTo-LocalizedXaml -Xaml $xaml -DynamicResources
|
||||
$reader = [System.Xml.XmlReader]::Create([System.IO.StringReader]::new($xaml))
|
||||
try {
|
||||
$window = [System.Windows.Markup.XamlReader]::Load($reader)
|
||||
@@ -20,6 +22,7 @@ function Show-MainWindow {
|
||||
}
|
||||
|
||||
Set-WindowThemeResources -window $window -usesDarkMode $usesDarkMode
|
||||
Update-WindowLanguageResources -Window $window
|
||||
|
||||
$mainBorder = $window.FindName('MainBorder')
|
||||
$titleBarBackground = $window.FindName('TitleBarBackground')
|
||||
@@ -46,12 +49,13 @@ function Show-MainWindow {
|
||||
$script:GuiWindow = $window
|
||||
|
||||
# ---- Handle unhandled exceptions on the dispatcher thread ----
|
||||
[System.Windows.Threading.Dispatcher]::CurrentDispatcher.Add_UnhandledException({
|
||||
$dispatcherErrorHandler = [System.Windows.Threading.DispatcherUnhandledExceptionEventHandler]{
|
||||
param($sender, $e)
|
||||
Write-Warning "Unhandled exception in GUI: $($e.Exception.Message)"
|
||||
Write-Warning "Stack trace: $($e.Exception.StackTrace)"
|
||||
$e.Handled = $true
|
||||
})
|
||||
}
|
||||
[System.Windows.Threading.Dispatcher]::CurrentDispatcher.Add_UnhandledException($dispatcherErrorHandler)
|
||||
|
||||
# ---- Window chrome helpers ----
|
||||
$updateWindowChrome = { Update-MainWindowChrome -Window $window -MainBorder $mainBorder -TitleBarBackground $titleBarBackground -NormalWindowShadow $normalWindowShadow }
|
||||
@@ -82,6 +86,7 @@ function Show-MainWindow {
|
||||
|
||||
# ---- Menu/button event wiring ----
|
||||
$kofiBtn.Add_Click({ Start-Process "https://ko-fi.com/raphire" })
|
||||
$window.FindName('SettingsBtn').Add_Click({ Open-MainWindowSettings -Window $window })
|
||||
|
||||
$menuBtn.Add_Click({
|
||||
$menuBtn.ContextMenu.PlacementTarget = $menuBtn
|
||||
@@ -167,7 +172,7 @@ function Show-MainWindow {
|
||||
# ---- Wire export/import ----
|
||||
$exportConfigBtn.Add_Click({
|
||||
try {
|
||||
Export-Configuration -Owner $window -UsesDarkMode $usesDarkMode -AppsPanel $appsPanel -UiControlMappings $script:UiControlMappings -UserSelectionCombo $userSelectionCombo -OtherUsernameTextBox $otherUsernameTextBox
|
||||
Export-Configuration -Owner $window -UsesDarkMode (Get-AppUsesDarkMode) -AppsPanel $appsPanel -UiControlMappings $script:UiControlMappings -UserSelectionCombo $userSelectionCombo -OtherUsernameTextBox $otherUsernameTextBox
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Export configuration failed: $($_.Exception.Message)"
|
||||
@@ -177,7 +182,7 @@ function Show-MainWindow {
|
||||
|
||||
$importConfigBtn.Add_Click({
|
||||
try {
|
||||
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 {
|
||||
Import-Configuration -Owner $window -UsesDarkMode (Get-AppUsesDarkMode) -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 {
|
||||
$tabControl.SelectedIndex = 3
|
||||
Update-NavigationButtons -Window $window -TabControl $tabControl
|
||||
$window.Dispatcher.BeginInvoke([System.Windows.Threading.DispatcherPriority]::Loaded, [action]{
|
||||
@@ -382,16 +387,14 @@ function Show-MainWindow {
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($searchText)) { return }
|
||||
|
||||
$highlightBrush = $window.Resources["SearchHighlightColor"]
|
||||
$activeHighlightBrush = $window.Resources["SearchHighlightActiveColor"]
|
||||
|
||||
# Keep existing highlights in sync when Auto changes the window's theme resources.
|
||||
foreach ($child in $appsPanel.Children) {
|
||||
if ($child -is [System.Windows.Controls.CheckBox] -and $child.Visibility -eq 'Visible') {
|
||||
$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)) {
|
||||
$child.Background = $highlightBrush
|
||||
$child.SetResourceReference([System.Windows.Controls.Control]::BackgroundProperty, 'SearchHighlightColor')
|
||||
$script:AppSearchMatches += $child
|
||||
}
|
||||
}
|
||||
@@ -399,9 +402,9 @@ function Show-MainWindow {
|
||||
|
||||
if ($script:AppSearchMatches.Count -gt 0) {
|
||||
$script:AppSearchMatchIndex = 0
|
||||
$script:AppSearchMatches[0].Background = $activeHighlightBrush
|
||||
$script:AppSearchMatches[0].SetResourceReference([System.Windows.Controls.Control]::BackgroundProperty, 'SearchHighlightActiveColor')
|
||||
$scrollViewer = Find-ParentScrollViewer -Element $appsPanel
|
||||
if ($scrollViewer) {
|
||||
if ($scrollViewer -and -not $script:UpdatingLanguage) {
|
||||
Scroll-ToItemIfNotVisible -ScrollViewer $scrollViewer -Item $script:AppSearchMatches[0] -Container $appsPanel
|
||||
}
|
||||
}
|
||||
@@ -410,9 +413,9 @@ function Show-MainWindow {
|
||||
$appSearchBox.Add_KeyDown({
|
||||
param($sourceControl, $e)
|
||||
if ($e.Key -eq [System.Windows.Input.Key]::Enter -and $script:AppSearchMatches.Count -gt 0) {
|
||||
$script:AppSearchMatches[$script:AppSearchMatchIndex].Background = $window.Resources["SearchHighlightColor"]
|
||||
$script:AppSearchMatches[$script:AppSearchMatchIndex].SetResourceReference([System.Windows.Controls.Control]::BackgroundProperty, 'SearchHighlightColor')
|
||||
$script:AppSearchMatchIndex = ($script:AppSearchMatchIndex + 1) % $script:AppSearchMatches.Count
|
||||
$script:AppSearchMatches[$script:AppSearchMatchIndex].Background = $window.Resources["SearchHighlightActiveColor"]
|
||||
$script:AppSearchMatches[$script:AppSearchMatchIndex].SetResourceReference([System.Windows.Controls.Control]::BackgroundProperty, 'SearchHighlightActiveColor')
|
||||
$scrollViewer = Find-ParentScrollViewer -Element $appsPanel
|
||||
if ($scrollViewer) {
|
||||
Scroll-ToItemIfNotVisible -ScrollViewer $scrollViewer -Item $script:AppSearchMatches[$script:AppSearchMatchIndex] -Container $appsPanel
|
||||
@@ -440,7 +443,6 @@ function Show-MainWindow {
|
||||
if ([string]::IsNullOrWhiteSpace($searchText)) { return }
|
||||
|
||||
$firstMatch = $null
|
||||
$highlightBrush = $window.Resources["SearchHighlightColor"]
|
||||
$col0 = $window.FindName('Column0Panel')
|
||||
$col1 = $window.FindName('Column1Panel')
|
||||
$col2 = $window.FindName('Column2Panel')
|
||||
@@ -472,7 +474,13 @@ function Show-MainWindow {
|
||||
}
|
||||
|
||||
if ($matchFound -and $controlToHighlight) {
|
||||
$controlToHighlight.Background = $highlightBrush
|
||||
$backgroundProperty = if ($controlToHighlight -is [System.Windows.Controls.Border]) {
|
||||
[System.Windows.Controls.Border]::BackgroundProperty
|
||||
}
|
||||
else {
|
||||
[System.Windows.Controls.Control]::BackgroundProperty
|
||||
}
|
||||
$controlToHighlight.SetResourceReference($backgroundProperty, 'SearchHighlightColor')
|
||||
if ($null -eq $firstMatch) { $firstMatch = $controlToHighlight }
|
||||
}
|
||||
}
|
||||
@@ -480,7 +488,7 @@ function Show-MainWindow {
|
||||
}
|
||||
}
|
||||
|
||||
if ($firstMatch -and $tweaksScrollViewer) {
|
||||
if ($firstMatch -and $tweaksScrollViewer -and -not $script:UpdatingLanguage) {
|
||||
Scroll-ToItemIfNotVisible -ScrollViewer $tweaksScrollViewer -Item $firstMatch -Container $tweaksGrid
|
||||
}
|
||||
})
|
||||
@@ -896,6 +904,10 @@ function Show-MainWindow {
|
||||
})
|
||||
|
||||
$window.Show() | Out-Null
|
||||
$themeTimer = [System.Windows.Threading.DispatcherTimer]::new()
|
||||
$themeTimer.Interval = [TimeSpan]::FromSeconds(2)
|
||||
$themeTimer.Add_Tick({ Update-AutoThemeResources -Window $window })
|
||||
$themeTimer.Start()
|
||||
|
||||
# If WhatIf mode is enabled, notify the user that no changes will be made
|
||||
if ($script:Params.ContainsKey("WhatIf")) {
|
||||
@@ -904,6 +916,11 @@ function Show-MainWindow {
|
||||
}) | Out-Null
|
||||
}
|
||||
|
||||
[System.Windows.Threading.Dispatcher]::PushFrame($frame)
|
||||
try { [System.Windows.Threading.Dispatcher]::PushFrame($frame) }
|
||||
finally {
|
||||
$themeTimer.Stop()
|
||||
[System.Windows.Threading.Dispatcher]::CurrentDispatcher.Remove_UnhandledException($dispatcherErrorHandler)
|
||||
if ($script:BubblePopup) { $script:BubblePopup.IsOpen = $false }
|
||||
}
|
||||
return $null
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ function Show-MessageBox {
|
||||
|
||||
Add-Type -AssemblyName PresentationFramework,PresentationCore,WindowsBase | Out-Null
|
||||
|
||||
$usesDarkMode = Get-SystemUsesDarkMode
|
||||
$usesDarkMode = Get-AppUsesDarkMode
|
||||
|
||||
# Determine owner window - use provided Owner, or fall back to main GUI window
|
||||
$ownerWindow = if ($Owner) { $Owner } else { $script:GuiWindow }
|
||||
|
||||
@@ -26,7 +26,7 @@ function Show-RestoreBackupDialog {
|
||||
|
||||
Add-Type -AssemblyName PresentationFramework,PresentationCore,WindowsBase | Out-Null
|
||||
|
||||
$usesDarkMode = Get-SystemUsesDarkMode
|
||||
$usesDarkMode = Get-AppUsesDarkMode
|
||||
$ownerWindow = if ($Owner) { $Owner } else { $script:GuiWindow }
|
||||
|
||||
$overlay = $null
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Shows the modal theme and language settings dialog.
|
||||
|
||||
.DESCRIPTION
|
||||
Lets users choose a theme and language. Changes take effect only after Save;
|
||||
closing without saving discards edits. Displays an error if saving fails.
|
||||
|
||||
.PARAMETER Owner
|
||||
The window that owns the settings dialog.
|
||||
|
||||
.OUTPUTS
|
||||
System.Boolean. $true when changed preferences were saved; $false when closed
|
||||
without changes or without saving.
|
||||
#>
|
||||
function Show-SettingsDialog {
|
||||
param([Parameter(Mandatory)][System.Windows.Window]$Owner)
|
||||
$path = Join-Path (Split-Path $script:MainWindowSchema -Parent) 'SettingsWindow.xaml'
|
||||
$xaml = ConvertTo-LocalizedXaml -Xaml (Get-Content -LiteralPath $path -Raw)
|
||||
$reader = [System.Xml.XmlReader]::Create([System.IO.StringReader]::new($xaml))
|
||||
try { $dialog = [System.Windows.Markup.XamlReader]::Load($reader) }
|
||||
finally { $reader.Close() }
|
||||
$dialog.Owner = $Owner
|
||||
Set-WindowThemeResources -window $dialog -usesDarkMode (Get-AppUsesDarkMode)
|
||||
$dialog.Tag = $false
|
||||
$overlay = $Owner.FindName('ModalOverlay')
|
||||
$languageCombo = $dialog.FindName('SettingsLanguageCombo')
|
||||
$auto = [System.Windows.Controls.ComboBoxItem]::new()
|
||||
$auto.Content = Get-Translation -Key 'SettingsLanguageAuto'
|
||||
$auto.Tag = 'Auto'
|
||||
[void]$languageCombo.Items.Add($auto)
|
||||
foreach ($folder in (Get-AvailableLanguageFolders)) {
|
||||
$item = [System.Windows.Controls.ComboBoxItem]::new()
|
||||
try {
|
||||
$culture = [System.Globalization.CultureInfo]::GetCultureInfo($folder.Name)
|
||||
$name = $culture.NativeName
|
||||
$item.Content = $culture.TextInfo.ToUpper($name.Substring(0, 1)) + $name.Substring(1)
|
||||
}
|
||||
catch { $item.Content = $folder.Name }
|
||||
$item.Tag = $folder.Name
|
||||
[void]$languageCombo.Items.Add($item)
|
||||
}
|
||||
foreach ($item in $languageCombo.Items) {
|
||||
if ($item.Tag -eq $script:UiPreferences.Language) { $languageCombo.SelectedItem = $item; break }
|
||||
}
|
||||
|
||||
# Event handlers share the pending value without changing saved preferences.
|
||||
$pendingTheme = [ref]([string]$script:UiPreferences.Theme)
|
||||
$savePreferences = {
|
||||
$theme = $pendingTheme.Value
|
||||
$language = [string]$languageCombo.SelectedItem.Tag
|
||||
if ($theme -eq $script:UiPreferences.Theme -and $language -eq $script:UiPreferences.Language) { $dialog.Close(); return }
|
||||
try {
|
||||
$lang = if ($language -eq $script:UiPreferences.Language) { $script:Lang } else { Get-UiLanguage -Language $language }
|
||||
if (-not $lang) { throw 'Unable to load language.' }
|
||||
if ($language -ne 'Auto' -and $lang.LanguageCode -ne $language) { throw 'The selected language could not be loaded.' }
|
||||
$preferences = @{ Theme = $theme; Language = $language }
|
||||
Save-UiPreferences -Preferences $preferences
|
||||
$script:UiPreferences = $preferences
|
||||
$script:Lang = $lang
|
||||
$dialog.Tag = $true
|
||||
$dialog.Close()
|
||||
}
|
||||
catch {
|
||||
$errorText = $dialog.FindName('SettingsErrorText')
|
||||
$errorText.Text = Get-Translation -Key 'SettingsSaveError'
|
||||
$errorText.Visibility = 'Visible'
|
||||
Write-Warning "Unable to update UI preferences: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
foreach ($mode in @('Auto', 'Light', 'Dark')) {
|
||||
$button = $dialog.FindName("Theme${mode}Button")
|
||||
if ($mode -eq $script:UiPreferences.Theme) { $button.SetResourceReference([System.Windows.FrameworkElement]::StyleProperty, 'PrimaryButtonStyle') }
|
||||
$button.Add_Click({
|
||||
param($sender, $e)
|
||||
$pendingTheme.Value = [string]$sender.Tag
|
||||
foreach ($themeMode in @('Auto', 'Light', 'Dark')) {
|
||||
$style = if ($themeMode -eq $pendingTheme.Value) { 'PrimaryButtonStyle' } else { 'SecondaryButtonStyle' }
|
||||
$dialog.FindName("Theme${themeMode}Button").SetResourceReference([System.Windows.FrameworkElement]::StyleProperty, $style)
|
||||
}
|
||||
})
|
||||
}
|
||||
$dialog.FindName('SaveButton').Add_Click($savePreferences)
|
||||
$dialog.FindName('TitleBar').Add_MouseLeftButtonDown({ $dialog.DragMove() })
|
||||
$dialog.FindName('CloseBtn').Add_Click({ $dialog.Close() })
|
||||
try {
|
||||
if ($overlay) { $overlay.Visibility = 'Visible' }
|
||||
[void]$dialog.ShowDialog()
|
||||
return [bool]$dialog.Tag
|
||||
}
|
||||
finally { if ($overlay) { $overlay.Visibility = 'Collapsed' } }
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Reads saved UI theme and language preferences.
|
||||
|
||||
.DESCRIPTION
|
||||
Uses Auto when no valid saved theme or language preference is available.
|
||||
UI preferences are separate from exported debloat configurations.
|
||||
|
||||
.PARAMETER Path
|
||||
The preferences JSON file. Defaults to $script:UiPreferencesFilePath.
|
||||
|
||||
.OUTPUTS
|
||||
System.Collections.Hashtable. Theme and Language preference values.
|
||||
#>
|
||||
function Get-UiPreferences {
|
||||
param([string]$Path = $script:UiPreferencesFilePath)
|
||||
$preferences = @{ Theme = 'Auto'; Language = 'Auto' }
|
||||
if (Test-Path -LiteralPath $Path) {
|
||||
try {
|
||||
$saved = Get-Content -LiteralPath $Path -Raw -Encoding UTF8 | ConvertFrom-Json
|
||||
if ($saved.Theme -in @('Auto', 'Light', 'Dark')) { $preferences.Theme = $saved.Theme }
|
||||
if ($saved.Language -eq 'Auto' -or $saved.Language -match '^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})+$') {
|
||||
$preferences.Language = $saved.Language
|
||||
}
|
||||
}
|
||||
catch { Write-Warning "Unable to read UI preferences: $($_.Exception.Message)" }
|
||||
}
|
||||
return $preferences
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Saves UI preferences to a JSON file.
|
||||
|
||||
.PARAMETER Preferences
|
||||
The Theme and Language preferences to save.
|
||||
|
||||
.PARAMETER Path
|
||||
The destination JSON file. Defaults to $script:UiPreferencesFilePath.
|
||||
#>
|
||||
function Save-UiPreferences {
|
||||
param(
|
||||
[Parameter(Mandatory)]$Preferences,
|
||||
[string]$Path = $script:UiPreferencesFilePath
|
||||
)
|
||||
$directory = Split-Path -Parent $Path
|
||||
[void][System.IO.Directory]::CreateDirectory($directory)
|
||||
$temporaryPath = "$Path.tmp"
|
||||
try {
|
||||
$Preferences | ConvertTo-Json | Set-Content -LiteralPath $temporaryPath -Encoding UTF8 -ErrorAction Stop
|
||||
Move-Item -LiteralPath $temporaryPath -Destination $Path -Force -ErrorAction Stop
|
||||
}
|
||||
finally {
|
||||
if (Test-Path -LiteralPath $temporaryPath) { Remove-Item -LiteralPath $temporaryPath -Force }
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Resolves the application's effective dark mode setting.
|
||||
|
||||
.DESCRIPTION
|
||||
Uses the selected Light or Dark theme, or follows the Windows app theme in Auto mode.
|
||||
|
||||
.OUTPUTS
|
||||
System.Boolean. Whether the application should use dark mode.
|
||||
#>
|
||||
function Get-AppUsesDarkMode {
|
||||
switch ($script:UiPreferences.Theme) {
|
||||
'Light' { return $false }
|
||||
'Dark' { return $true }
|
||||
default { return (Get-SystemUsesDarkMode) }
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Loads the language selected by a UI preference.
|
||||
|
||||
.DESCRIPTION
|
||||
Auto follows the current UI culture. Resolves the requested language to an available
|
||||
translation, falling back to English when needed.
|
||||
|
||||
.PARAMETER Language
|
||||
Auto to use the current UI culture, or a language code such as nl-NL.
|
||||
|
||||
.OUTPUTS
|
||||
PSCustomObject. The loaded language content, or $null when loading fails.
|
||||
#>
|
||||
function Get-UiLanguage {
|
||||
param([string]$Language)
|
||||
if ($Language -eq 'Auto') { return (Import-LanguageFile) }
|
||||
return (Import-LanguageFile -LanguageCode $Language)
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Initializes UI preferences and the active language.
|
||||
|
||||
.DESCRIPTION
|
||||
Loads saved preferences and resolves the startup language, allowing an explicit
|
||||
language choice to override the saved preference.
|
||||
|
||||
.PARAMETER Language
|
||||
An optional language preference override, including Auto. When omitted or empty,
|
||||
uses the saved language preference.
|
||||
#>
|
||||
function Initialize-UiPreferences {
|
||||
param([string]$Language)
|
||||
|
||||
$script:UiPreferences = Get-UiPreferences
|
||||
if ($Language) { $script:UiPreferences.Language = $Language }
|
||||
$script:Lang = Get-UiLanguage -Language $script:UiPreferences.Language
|
||||
if ($script:Lang -and $script:UiPreferences.Language -ne 'Auto') {
|
||||
$script:UiPreferences.Language = $script:Lang.LanguageCode
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Refreshes window themes when the application follows the Windows theme.
|
||||
|
||||
.DESCRIPTION
|
||||
Keeps the window and its owned dialogs in sync with the Windows app theme when
|
||||
Auto is selected.
|
||||
|
||||
.PARAMETER Window
|
||||
The window to check, along with its owned windows.
|
||||
#>
|
||||
function Update-AutoThemeResources {
|
||||
param([System.Windows.Window]$Window)
|
||||
if ($script:UiPreferences.Theme -ne 'Auto') { return }
|
||||
$dark = Get-SystemUsesDarkMode
|
||||
if ($Window.Resources['AppUsesDarkMode'] -ne $dark) {
|
||||
Set-WindowThemeResources -window $Window -usesDarkMode $dark
|
||||
}
|
||||
foreach ($owned in $Window.OwnedWindows) { Update-AutoThemeResources -Window $owned }
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Opens settings and applies saved presentation changes to the main window.
|
||||
|
||||
.PARAMETER Window
|
||||
The main window that owns the settings dialog and receives saved changes.
|
||||
#>
|
||||
function Open-MainWindowSettings {
|
||||
param([System.Windows.Window]$Window)
|
||||
if ($script:IsLoadingApps) { return }
|
||||
$previousLanguageCode = $script:Lang.LanguageCode
|
||||
if (Show-SettingsDialog -Owner $Window) {
|
||||
Set-WindowThemeResources -window $Window -usesDarkMode (Get-AppUsesDarkMode)
|
||||
if ($script:Lang.LanguageCode -ne $previousLanguageCode) {
|
||||
Update-MainWindowLanguage -Window $Window
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user