private/theme/New-ThemePopupButton.ps1

function New-ThemePopupButton {
    <#
    .SYNOPSIS
        Creates a compact theme switcher popup button with grouped themes.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [System.Windows.UIElement]$Container,
        
        [string]$CurrentTheme = 'Light'
    )
    
    $colors = Get-ThemeColors
    
    # Create the theme button - styled flat like window control buttons
    $themeButton = [System.Windows.Controls.Button]::new()
    $themeButton.Width = 46
    $themeButton.Height = 32
    $themeButton.Padding = [System.Windows.Thickness]::new(0)
    $themeButton.ToolTip = 'Change Theme'
    $themeButton.HorizontalAlignment = 'Right'
    $themeButton.VerticalAlignment = 'Center'
    $themeButton.BorderThickness = [System.Windows.Thickness]::new(0)
    $themeButton.Cursor = [System.Windows.Input.Cursors]::Hand
    
    $themeIcon = [System.Windows.Controls.TextBlock]::new()
    $themeIcon.Text = [PsUi.ModuleContext]::GetIcon('ColorBackground') 
    $themeIcon.FontFamily = [System.Windows.Media.FontFamily]::new('Segoe MDL2 Assets')
    $themeIcon.FontSize = 14
    $themeIcon.HorizontalAlignment = 'Center'
    $themeIcon.VerticalAlignment = 'Center'
    $themeIcon.Tag = 'ThemeButtonIcon'
    
    # Use HeaderForeground to match titlebar text
    $themeIcon.SetResourceReference([System.Windows.Controls.TextBlock]::ForegroundProperty, 'HeaderForegroundBrush')
    
    $themeButton.Content = $themeIcon
    
    # Apply flat titlebar button style with theme-aware hover (same as min/max buttons)
    $templateXaml = @'
<ControlTemplate xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 TargetType="Button">
    <Border x:Name="border" Background="Transparent">
        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
    </Border>
    <ControlTemplate.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
            <Setter TargetName="border" Property="Background" Value="{DynamicResource WindowControlHoverBrush}"/>
        </Trigger>
        <Trigger Property="IsPressed" Value="True">
            <Setter TargetName="border" Property="Background" Value="{DynamicResource WindowControlHoverBrush}"/>
            <Setter TargetName="border" Property="Opacity" Value="0.7"/>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>
'@

    $themeButton.Template = [System.Windows.Markup.XamlReader]::Parse($templateXaml)
    
    # Mark button as hit-testable within WindowChrome area
    # Fixes click issues when parent window is maximized
    [System.Windows.Shell.WindowChrome]::SetIsHitTestVisibleInChrome($themeButton, $true)
    
    # Create popup
    $popup = [System.Windows.Controls.Primitives.Popup]::new()
    $popup.PlacementTarget = $themeButton
    $popup.Placement = [System.Windows.Controls.Primitives.PlacementMode]::Bottom
    $popup.StaysOpen = $false
    $popup.AllowsTransparency = $true
    
    $popupBorder = [System.Windows.Controls.Border]::new()
    $popupBorder.Background = ConvertTo-UiBrush $colors.ControlBg
    $popupBorder.BorderBrush = ConvertTo-UiBrush $colors.Border
    $popupBorder.BorderThickness = [System.Windows.Thickness]::new(1)
    $popupBorder.Padding = [System.Windows.Thickness]::new(8)
    $popupBorder.CornerRadius = [System.Windows.CornerRadius]::new(6)
    $popupBorder.Tag = 'PopupBorder'
    
    $shadow = [System.Windows.Media.Effects.DropShadowEffect]::new()
    $shadow.BlurRadius = 12
    $shadow.ShadowDepth = 3
    $shadow.Opacity = 0.25
    $popupBorder.Effect = $shadow
    
    $themeStack = [System.Windows.Controls.StackPanel]::new()
    $themeStack.Orientation = 'Vertical'
    
    # Wire up popup structure (content built dynamically on click)
    $popupBorder.Child = $themeStack
    $popup.Child = $popupBorder
    
    # Helper to create section header - defined as scriptblock for closure capture
    $newSectionHeader = {
        param($HeaderText, $IconChar, $Colors)
        $header = [System.Windows.Controls.StackPanel]::new()
        $header.Orientation = 'Horizontal'
        $header.Margin = [System.Windows.Thickness]::new(4, 6, 4, 4)
        
        $iconBlock = [System.Windows.Controls.TextBlock]::new()
        $iconBlock.Text = $IconChar
        $iconBlock.FontFamily = [System.Windows.Media.FontFamily]::new('Segoe MDL2 Assets')
        $iconBlock.FontSize = 12
        $iconBlock.Foreground = ConvertTo-UiBrush $Colors.SecondaryText
        $iconBlock.VerticalAlignment = 'Center'
        $iconBlock.Margin = [System.Windows.Thickness]::new(0, 0, 6, 0)
        $iconBlock.Tag = 'SectionIcon'
        [void]$header.Children.Add($iconBlock)
        
        $label = [System.Windows.Controls.TextBlock]::new()
        $label.Text = $HeaderText
        $label.FontSize = 11
        $label.FontWeight = 'SemiBold'
        $label.Foreground = ConvertTo-UiBrush $Colors.SecondaryText
        $label.VerticalAlignment = 'Center'
        $label.Tag = 'SectionLabel'
        [void]$header.Children.Add($label)
        
        return $header
    }
    
    # Helper to create a theme menu item button - defined as scriptblock for closure capture
    $newThemeMenuItem = {
        param($ThemeName, $CurrentTheme, $Colors, $Popup, $ThemeStack, $PopupBorder, $ThemeIcon, $ThemeButton, $Container)
        
        $themeItem = [System.Windows.Controls.Button]::new()
        $themeItem.Height = 28
        $themeItem.MinWidth = 130
        $themeItem.HorizontalContentAlignment = 'Left'
        $themeItem.Padding = [System.Windows.Thickness]::new(8, 2, 8, 2)
        $themeItem.Margin = [System.Windows.Thickness]::new(2, 1, 2, 1)
        
        $itemStack = [System.Windows.Controls.StackPanel]::new()
        $itemStack.Orientation = 'Horizontal'
        
        $checkmark = [System.Windows.Controls.TextBlock]::new()
        $checkmark.Text = if ($ThemeName -eq $CurrentTheme) { [PsUi.ModuleContext]::GetIcon('CheckMark') } else { ' ' }
        $checkmark.FontFamily = [System.Windows.Media.FontFamily]::new('Segoe MDL2 Assets')
        $checkmark.FontSize = 12
        $checkmark.Width = 18
        $checkmark.Foreground = ConvertTo-UiBrush $Colors.Accent
        $checkmark.VerticalAlignment = 'Center'
        $checkmark.Tag = 'AccentText'
        [void]$itemStack.Children.Add($checkmark)
        
        $themeLabel = [System.Windows.Controls.TextBlock]::new()
        $themeLabel.Text = $ThemeName
        $themeLabel.FontSize = 12
        $themeLabel.VerticalAlignment = 'Center'
        $themeLabel.Foreground = ConvertTo-UiBrush $Colors.ControlFg
        [void]$itemStack.Children.Add($themeLabel)
        
        $themeItem.Content = $itemStack
        
        # Store all state the click handler needs (avoids .GetNewClosure() which breaks module scope)
        $themeItem.Tag = @{
            ThemeName   = $ThemeName
            Checkmark   = $checkmark
            Popup       = $Popup
            ThemeStack  = $ThemeStack
            PopupBorder = $PopupBorder
            ThemeIcon   = $ThemeIcon
            ThemeButton = $ThemeButton
            Container   = $Container
        }
        
        Set-ButtonStyle -Button $themeItem
        
        # No .GetNewClosure() — scriptblock stays bound to PsUi module scope
        $themeItem.Add_Click({
            param($sender, $eventArgs)
            try {
                $tag = $sender.Tag
                if (!$tag -or !$tag.ContainsKey('ThemeName')) { return }
                $selectedTheme = $tag.ThemeName
                
                $tag.Popup.IsOpen = $false
                
                Set-ActiveTheme -Theme $selectedTheme
                $newColors = Get-ThemeColors
                
                # Update all theme buttons in the popup
                foreach ($child in $tag.ThemeStack.Children) {
                    if ($child -is [System.Windows.Controls.Button] -and 
                        $child.Tag -is [System.Collections.IDictionary] -and 
                        $child.Tag.ContainsKey('ThemeName')) {
                        
                        $isSelected = $child.Tag['ThemeName'] -eq $selectedTheme
                        $child.Tag['Checkmark'].Text = if ($isSelected) { [PsUi.ModuleContext]::GetIcon('CheckMark') } else { ' ' }
                        $child.Tag['Checkmark'].Foreground = ConvertTo-UiBrush $newColors.Accent
                        
                        $contentPanel = $child.Content
                        if ($contentPanel -is [System.Windows.Controls.StackPanel]) {
                            foreach ($tb in $contentPanel.Children) {
                                if ($tb -is [System.Windows.Controls.TextBlock]) {
                                    if ($tb.Tag -eq 'AccentText') {
                                        $tb.Foreground = ConvertTo-UiBrush $newColors.Accent
                                    }
                                    else {
                                        $tb.Foreground = ConvertTo-UiBrush $newColors.ControlFg
                                    }
                                }
                            }
                        }
                        Set-ButtonStyle -Button $child
                    }
                    elseif ($child -is [System.Windows.Controls.StackPanel]) {
                        foreach ($hc in $child.Children) {
                            if ($hc -is [System.Windows.Controls.TextBlock]) {
                                $hc.Foreground = ConvertTo-UiBrush $newColors.SecondaryText
                            }
                        }
                    }
                    elseif ($child -is [System.Windows.Controls.Border]) {
                        $child.Background = ConvertTo-UiBrush $newColors.Border
                    }
                }
                
                $tag.PopupBorder.Background = ConvertTo-UiBrush $newColors.ControlBg
                $tag.PopupBorder.BorderBrush = ConvertTo-UiBrush $newColors.Border
                $tag.ThemeIcon.Foreground = ConvertTo-UiBrush $newColors.HeaderForeground
                Set-ButtonStyle -Button $tag.ThemeButton -IconOnly

                # Update this window and its parent (Owner) if present
                $ownerWindow = [System.Windows.Window]::GetWindow($tag.Container)
                Write-Debug "ownerWindow type: $($ownerWindow.GetType().FullName), is Window: $($ownerWindow -is [System.Windows.Window])"
                if ($ownerWindow) {
                    Update-AllControlThemes -Control $ownerWindow -Colors $newColors
                    
                    # Directly update header text (it's in the first child of the window's content DockPanel)
                    $dockPanel = $ownerWindow.Content
                    if ($dockPanel -and $dockPanel.Children.Count -gt 0) {
                        $headerBorder = $dockPanel.Children[0]
                        if ($headerBorder.Child -and $headerBorder.Child.Children.Count -gt 0) {
                            $titleBlock = $headerBorder.Child.Children[0]
                            if ($titleBlock -is [System.Windows.Controls.TextBlock]) {
                                $titleBlock.Foreground = ConvertTo-UiBrush $newColors.HeaderForeground
                            }
                        }
                    }
                    
                    # Also update the parent window (Owner) if this is a child window
                    if ($ownerWindow.Owner -and $ownerWindow.Owner -is [System.Windows.Window]) {
                        Update-AllControlThemes -Control $ownerWindow.Owner -Colors $newColors
                    }
                }
            }
            catch {
                Write-Warning "Theme switch failed: $($_.Exception.Message)"
                Write-Debug "Stack: $($_.ScriptStackTrace)"
            }
        })
        
        return $themeItem
    }
    
    # Store all state the click handler needs in Tag
    # No .GetNewClosure() — scriptblock stays bound to PsUi module scope
    $themeButton.Tag = @{
        Popup              = $popup
        ThemeStack         = $themeStack
        PopupBorder        = $popupBorder
        ThemeIcon          = $themeIcon
        Container          = $Container
        SectionHeaderBuilder = $newSectionHeader
        MenuItemBuilder    = $newThemeMenuItem
    }
    
    $themeButton.Add_Click({
        $tag = $this.Tag
        try {
            # Rebuild theme list each time popup opens to pick up newly registered themes
            if (!$tag.Popup.IsOpen) {
                try {
                    $tag.ThemeStack.Children.Clear()
                    
                    $currentColors = Get-ThemeColors
                    $activeTheme   = [PsUi.ModuleContext]::ActiveTheme
                    $allThemes     = [PsUi.ModuleContext]::Themes
                    
                    # Sort themes: Light/Dark first in their respective groups, then alphabetical
                    $lightThemes = $allThemes.GetEnumerator() | 
                        Where-Object { $_.Value.Type -eq 'Light' } | 
                        ForEach-Object { $_.Key } | 
                        Sort-Object { if ($_ -eq 'Light') { '!0' } else { $_ } }
                    $darkThemes  = $allThemes.GetEnumerator() | 
                        Where-Object { $_.Value.Type -eq 'Dark' } | 
                        ForEach-Object { $_.Key } | 
                        Sort-Object { if ($_ -eq 'Dark') { '!0' } else { $_ } }
                    
                    # Add Light themes section
                    $lightHeader = & $tag.SectionHeaderBuilder 'Light Themes' ([PsUi.ModuleContext]::GetIcon('Brightness')) $currentColors
                    [void]$tag.ThemeStack.Children.Add($lightHeader)
                    
                    foreach ($themeName in $lightThemes) {
                        $menuItem = & $tag.MenuItemBuilder $themeName $activeTheme $currentColors $tag.Popup $tag.ThemeStack $tag.PopupBorder $tag.ThemeIcon $this $tag.Container
                        [void]$tag.ThemeStack.Children.Add($menuItem)
                    }
                    
                    # Separator
                    $separator = [System.Windows.Controls.Border]::new()
                    $separator.Height = 1
                    $separator.Background = ConvertTo-UiBrush $currentColors.Border
                    $separator.Margin = [System.Windows.Thickness]::new(4, 8, 4, 4)
                    [void]$tag.ThemeStack.Children.Add($separator)
                    
                    # Add Dark themes section
                    $darkHeader = & $tag.SectionHeaderBuilder 'Dark Themes' ([PsUi.ModuleContext]::GetIcon('Contrast')) $currentColors
                    [void]$tag.ThemeStack.Children.Add($darkHeader)
                    
                    foreach ($themeName in $darkThemes) {
                        $menuItem = & $tag.MenuItemBuilder $themeName $activeTheme $currentColors $tag.Popup $tag.ThemeStack $tag.PopupBorder $tag.ThemeIcon $this $tag.Container
                        [void]$tag.ThemeStack.Children.Add($menuItem)
                    }
                    
                    # Update popup border colors for current theme
                    $tag.PopupBorder.Background = ConvertTo-UiBrush $currentColors.ControlBg
                    $tag.PopupBorder.BorderBrush = ConvertTo-UiBrush $currentColors.Border
                }
                catch {
                    Write-Warning "Theme popup build failed: $_"
                }
            }
            
            # Toggle always runs even if content build had an error
            $tag.Popup.IsOpen = !$tag.Popup.IsOpen
        }
        catch { Write-Warning "Theme popup toggle failed: $_" }
    })
    
    return @{ Button = $themeButton; Popup = $popup }
}