diff --git a/config/themes.json b/config/themes.json index fb4fcddd..821738dd 100644 --- a/config/themes.json +++ b/config/themes.json @@ -25,7 +25,7 @@ "IconFontSize": "14", "IconButtonSize": "35", "SettingsIconFontSize": "18", - "CloseIconFontSize": "18", + "CloseIconFontSize": "12", "GroupBorderBackgroundColor": "#232629", "ButtonFontSize": "12", "ButtonFontFamily": "Arial", diff --git a/functions/public/Invoke-WPFButton.ps1 b/functions/public/Invoke-WPFButton.ps1 index cfa27b59..4e247718 100644 --- a/functions/public/Invoke-WPFButton.ps1 +++ b/functions/public/Invoke-WPFButton.ps1 @@ -87,6 +87,13 @@ function Invoke-WPFButton { } "WPFCloseButton" {$sync.Form.Close(); Write-Host "Bye bye!"} "WPFMinimizeButton" {$sync.Form.WindowState = [Windows.WindowState]::Minimized} + "WPFMaximizeButton" { + if ($sync.Form.WindowState -eq [Windows.WindowState]::Normal) { + $sync.Form.WindowState = [Windows.WindowState]::Maximized + } else { + $sync.Form.WindowState = [Windows.WindowState]::Normal + } + } "WPFselectedAppsButton" {$sync.selectedAppsPopup.IsOpen = -not $sync.selectedAppsPopup.IsOpen} } } diff --git a/pester/ui-state.Tests.ps1 b/pester/ui-state.Tests.ps1 index 2ff6bbf5..5f8fb62b 100644 --- a/pester/ui-state.Tests.ps1 +++ b/pester/ui-state.Tests.ps1 @@ -5,7 +5,7 @@ BeforeAll { $script:repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path - if (-not ("System.Windows.Controls.CheckBox" -as [type])) { + if (-not ("Windows.Visibility" -as [type])) { Add-Type @" namespace Windows { @@ -15,7 +15,25 @@ namespace Windows Collapsed } } +"@ + } + if (-not ("Windows.WindowState" -as [type])) { + Add-Type @" +namespace Windows +{ + public enum WindowState + { + Normal, + Minimized, + Maximized + } +} +"@ + } + + if (-not ("System.Windows.Controls.CheckBox" -as [type])) { + Add-Type @" namespace System.Windows.Controls { public class CheckBox @@ -316,3 +334,32 @@ Describe "Invoke-WPFButton progress cleanup" { Should -Not -Invoke Set-WinUtilTweaksProgressIndicator } } + +Describe "Invoke-WPFButton window state" { + BeforeEach { + New-WinUtilUiStateTestContext + $script:sync.ProcessRunning = $true + $script:sync.Form = [pscustomobject]@{ + WindowState = [Windows.WindowState]::Normal + } + } + + AfterEach { + Remove-Variable -Name sync -Scope Script -ErrorAction SilentlyContinue + Remove-Variable -Name sync -Scope Global -ErrorAction SilentlyContinue + } + + It "maximizes a normal window" { + Invoke-WPFButton -Button "WPFMaximizeButton" + + $script:sync.Form.WindowState | Should -Be ([Windows.WindowState]::Maximized) + } + + It "restores a maximized window" { + $script:sync.Form.WindowState = [Windows.WindowState]::Maximized + + Invoke-WPFButton -Button "WPFMaximizeButton" + + $script:sync.Form.WindowState | Should -Be ([Windows.WindowState]::Normal) + } +} diff --git a/pester/xaml.Tests.ps1 b/pester/xaml.Tests.ps1 index d24f5609..c272148f 100644 --- a/pester/xaml.Tests.ps1 +++ b/pester/xaml.Tests.ps1 @@ -248,6 +248,52 @@ Describe "XAML document" { $buttonSource | Should -Match '"WPFInstallSelectedAppx"\s*\{Invoke-WPFAppxInstall\}' $tabSource | Should -Match '\$sync\.\$tabNav\.Items\[\$tabNumber\]\.IsSelected = \$true' } + + It "centers top bar controls vertically" { + $navPanel = $script:xaml.SelectSingleNode('//*[local-name()="StackPanel"][@Name="NavDockPanel"]') + $minimizeButton = $script:xaml.SelectSingleNode('//*[local-name()="Button"][@Name="WPFMinimizeButton"]') + $actionPanel = $minimizeButton.ParentNode + $topBarButtonNames = @( + "ThemeButton", + "FontScalingButton", + "SettingsButton", + "WPFMinimizeButton", + "WPFMaximizeButton", + "WPFCloseButton" + ) + + $navPanel.GetAttribute("VerticalAlignment") | Should -Be "Center" + $actionPanel.GetAttribute("VerticalAlignment") | Should -Be "Center" + + foreach ($buttonName in $topBarButtonNames) { + $button = $script:xaml.SelectSingleNode("//*[local-name()='Button'][@Name='$buttonName']") + $button.GetAttribute("VerticalAlignment") | Should -Be "Center" + } + } + + It "uses state-aware maximize and restore icons" { + $themes = Get-WinUtilConfigObject -Name "themes" + $minimizeButton = $script:xaml.SelectSingleNode('//*[local-name()="Button"][@Name="WPFMinimizeButton"]') + $maximizeButton = $script:xaml.SelectSingleNode('//*[local-name()="Button"][@Name="WPFMaximizeButton"]') + $closeButton = $script:xaml.SelectSingleNode('//*[local-name()="Button"][@Name="WPFCloseButton"]') + $maximizeStyle = $maximizeButton.SelectSingleNode('./*[local-name()="Button.Style"]/*[local-name()="Style"]') + $maximizeIcon = $maximizeStyle.SelectSingleNode('./*[local-name()="Setter"][@Property="Content"]') + $maximizedTrigger = $maximizeStyle.SelectSingleNode('./*[local-name()="Style.Triggers"]/*[local-name()="DataTrigger"][@Value="Maximized"]') + $restoreIcon = $maximizedTrigger.SelectSingleNode('./*[local-name()="Setter"][@Property="Content"]') + + foreach ($button in @($minimizeButton, $maximizeButton, $closeButton)) { + $button.GetAttribute("FontFamily") | Should -Be "Segoe MDL2 Assets" + $button.GetAttribute("FontSize") | Should -Be "{DynamicResource CloseIconFontSize}" + $button.GetAttribute("Margin") | Should -BeIn @("0", "0,0,0,0") + } + + $minimizeButton.GetAttribute("Content") | Should -Be ([string][char]0xE921) + $maximizeIcon.GetAttribute("Value") | Should -Be ([string][char]0xE922) + $restoreIcon.GetAttribute("Value") | Should -Be ([string][char]0xE923) + $closeButton.GetAttribute("Content") | Should -Be ([string][char]0xE8BB) + $maximizeStyle.GetAttribute("BasedOn") | Should -Be "{StaticResource HoverButtonStyle}" + [int]$themes.shared.CloseIconFontSize | Should -BeLessThan ([int]$themes.shared.SettingsIconFontSize) + } } Describe "XAML and sync wiring" { diff --git a/xaml/inputXML.xaml b/xaml/inputXML.xaml index 2fa74807..12c28c5f 100644 --- a/xaml/inputXML.xaml +++ b/xaml/inputXML.xaml @@ -955,7 +955,7 @@ - + - +