EasyGUI.psm1

Write-Host "Checking PowerShell version Requirement . . ."

# Requires PS 5.1 or 7+ (but not 6.x)
$PSMajor = $PSVersionTable.PSVersion.Major
$OS = [System.Environment]::OSVersion.Platform

# Block unsupported OS first
if ($OS -ne 'Win32NT') {
    Write-Error "EasyGUI only supports Windows. Your current OS: $OS"
    return
}

# Block PowerShell older than 5.1
if ($PSMajor -lt 5 -or ($PSMajor -eq 5 -and $PSVersionTable.PSVersion.Minor -lt 1)) {
    Write-Error "EasyGUI requires at least PowerShell 5.1."
    return
}

# Block PowerShell 6.x
if ($PSMajor -eq 6) {
    Write-Error "EasyGUI does NOT support PowerShell 6.x. Please install PowerShell 7 or use Windows PowerShell 5.1."
    return
}

# Passed
Write-Host "EasyGUI System and PowerShell Requirement Passed." -ForegroundColor Green



Write-Verbose "Adding Required Assembly / Type Names . . ."
try {
Add-Type -AssemblyName PresentationFramework, PresentationCore, WindowsBase, System.Windows.Forms
} catch {
    Write-Error "Oops something went wrong: $_"
}
function Add-ErrorMessage {
param($ErrorMessage)
    Write-Error "Oops a Error Have occurred: `n $ErrorMessage"
    
    [System.Windows.MessageBox]::Show(
        "Oops a Error Have occurred",
        "Error: $ErrorMessage",
    [System.Windows.Forms.MessageBoxButtons]::OK,
    [System.Windows.Forms.MessageBoxIcon]::Error
        )

    return

}

function PrepareWindow {
    param(
        [string]$Title = "Untitled GUI",
        [int]$Width = 500,
        [int]$Height = 400
    )

$xaml = @"
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="$Title"
        Height="$Height"
        Width="$Width"
        WindowStartupLocation="CenterScreen"
        Background="#1E1E1E"
        FontFamily="Segoe UI"
        ResizeMode="NoResize">
     
    <Grid Margin="10">
        <!-- ScrollViewer fills entire Grid -->
        <ScrollViewer VerticalScrollBarVisibility="Hidden"
                      HorizontalScrollBarVisibility="Disabled"
                      HorizontalAlignment="Stretch"
                      VerticalAlignment="Stretch"
                      Background="#1E1E1E">
            <!-- Inner StackPanel holds content -->
            <StackPanel x:Name="Stack" VerticalAlignment="Top" />
        </ScrollViewer>
    </Grid>
</Window>
"@


    try {
        $reader = New-Object System.Xml.XmlNodeReader ([xml]$xaml)
        $script:Window = [Windows.Markup.XamlReader]::Load($reader)
        $script:Stack = $script:Window.FindName("Stack")
    }
    catch {
        Write-Host "Error creating WPF window: $($_.Exception.Message)" -ForegroundColor Red
        return $false
    }
    return $true
}

function Window.Text {
    param(
        [string]$Content,
        [string]$foreground = "White",
        [string]$ID = $null  # Optional unique ID for updating
    )

    if (-not $script:Stack) { Write-Host "GUI not initialized"; return }

    # Make sure Inputs hash exists
    if (-not ($script:Window | Get-Member -Name Inputs)) {
        $script:Window | Add-Member -MemberType NoteProperty -Name Inputs -Value @{}
    }

    if ($ID) {
        # Ensure the hashtable itself exists
        if (-not $script:Window.Inputs) {
            $script:Window.Inputs = @{}
        }

        # If TextBlock already exists, just update
        if ($script:Window.Inputs.ContainsKey($ID)) {
            $script:Window.Inputs[$ID].Text = $Content
            return
        }
    }

    # Create a new TextBlock
    $label = New-Object System.Windows.Controls.TextBlock
    $label.Text = $Content
    $label.Foreground = $foreground
    $label.Margin = "5,5,5,10"
    $label.FontSize = 16

    # Store reference if ID given
    if ($ID) {
        $script:Window.Inputs[$ID] = $label
    }

    $script:Stack.Children.Add($label)
}



function Window.AddButton {
    param(
        [string]$Name,
        [scriptblock]$Command
    )
    if (-not $script:Stack) { Write-Host "GUI not initialized"; return }

    $btn = New-Object System.Windows.Controls.Button
    $btn.Content = $Name
    $btn.Height = 35
    $btn.Width = 200
    $btn.HorizontalAlignment = "Left"
    $btn.Background = "#2A2A2A"
    $btn.Foreground = "White"
    $btn.BorderBrush = "#444"
    $btn.BorderThickness = 1
    $btn.Padding = "10,6"
    $btn.Margin = "4"
    $btn.FontSize = 14
    $btn.Cursor = "Hand"
      # Hover
    $btn.Add_MouseEnter({ $_.Source.Background = "#3A3A3A" })
    $btn.Add_MouseLeave({ $_.Source.Background = "#2A2A2A" })

    # Pressed
    $btn.Add_PreviewMouseDown({ $_.Source.Background = "#3A7BFF" })
    $btn.Add_PreviewMouseUp({ if (-not $_.Source.IsMouseOver) { $_.Source.Background = "#2A2A2A" } })

    $localCommand = $Command
    $btn.Add_Click({
        if ([string]::IsNullOrWhiteSpace($localCommand)) {
            Add-ErrorMessage -ErrorMessage "No Command Assigned"
            return
        }
        try { & $localCommand }
        catch { Add-ErrorMessage -ErrorMessage "Error: $($_.Exception.Message)" }
    }.GetNewClosure())

    $script:Stack.Children.Add($btn)
}

function Window.AddInputBox {
    param(
        [Parameter(Mandatory=$true)]
        [string]$ID,

        [Parameter(Mandatory=$false)]
        [scriptblock]$Action
    )

    # Ensure custom storage exists
    if (-not ($script:Window | Get-Member -Name Inputs)) {
        $script:Window | Add-Member -MemberType NoteProperty -Name Inputs -Value @{}
    }
    if (-not ($script:Window | Get-Member -Name InputActions)) {
        $script:Window | Add-Member -MemberType NoteProperty -Name InputActions -Value @{}
    }

    # Ensure valid Name (must start with a letter)
    $nameSafe = "Input$ID"

    # Create TextBox
    $tb = New-Object System.Windows.Controls.TextBox
    $tb.Name = $nameSafe

    # Easy GUI style
    $tb.Background      = "#222"
    $tb.Foreground      = "White"
    $tb.BorderBrush     = "#555"
    $tb.BorderThickness = 1
    $tb.FontSize        = 14
    $tb.Padding         = "6"
    $tb.Margin          = "4"

    # Focus border change
    $tb.Add_GotFocus({ $_.Source.BorderBrush = "#3A7BFF" })
    $tb.Add_LostFocus({ $_.Source.BorderBrush = "#555" })

    # Hover effect
    $tb.Add_MouseEnter({ $_.Source.Background = "#2E2E2E" })
    $tb.Add_MouseLeave({ $_.Source.Background = "#222" })

    # Store input reference
    $script:Window.Inputs[$ID] = $tb

    # Store optional action
    if ($Action) {
        $script:Window.InputActions[$ID] = $Action
    }

    # Add automatically to current StackPanel if exists
    if ($script:Stack) { $script:Stack.Children.Add($tb) }

    return $tb
}


function Window.InputApply {
    param(
        [Parameter(Mandatory=$true)]
        [string]$InputID,

        [Parameter(Mandatory=$false)]
        [string]$ButtonLabel = "Apply",

        [Parameter(Mandatory=$false)]
        [string]$Alignment = "Left"  # HorizontalAlignment: Left, Center, Right
    )

    # Ensure the input exists
    if (-not $script:Window.Inputs.ContainsKey($InputID)) {
        Add-ErrorMessage -ErrorMessage "Input '$InputID' not found."
        return
    }

    # Create the button
    $btn = New-Object System.Windows.Controls.Button
    $btn.Content = $ButtonLabel
    $btn.Height = 35
    $btn.Width = 200
    $btn.Margin = "4"
    $btn.FontSize = 14
    $btn.HorizontalAlignment = $Alignment
    $btn.Background = "#2A2A2A"
    $btn.Foreground = "White"
    $btn.BorderBrush = "#444"
    $btn.BorderThickness = 1
    $btn.Cursor = "Hand"

    # Hover effect
    $btn.Add_MouseEnter({ $_.Source.Background = "#3A3A3A" })
    $btn.Add_MouseLeave({ $_.Source.Background = "#2A2A2A" })
    # Pressed effect
    $btn.Add_PreviewMouseDown({ $_.Source.Background = "#3A7BFF" })
    $btn.Add_PreviewMouseUp({ if (-not $_.Source.IsMouseOver) { $_.Source.Background = "#2A2A2A" } })

    # Click action: run stored InputAction if exists
    $btn.Add_Click({
        if ($script:Window.InputActions.ContainsKey($InputID)) {
            & $script:Window.InputActions[$InputID]
        } else {
            Add-ErrorMessage "No action defined for Input '$InputID'."
        }
    }.GetNewClosure())

    # Add button to the current stack panel
    if ($script:Stack) { $script:Stack.Children.Add($btn) }
}








function Window.AddOption {
    param(
        [string]$Label,
        [string]$ID = "default",
        [string]$Mini = "$false",
        [string]$align = "Left",
        [string]$tooltip,
        [int]$Margin = 8,
        [bool]$Default = $false,
        [scriptblock]$Action = {}
        
        
    )

    $ID = if ($ID) { [string]$ID } else { "default" }
    $Label = if ($Label) { $Label } else { "Option" }

    # Ensure group exists
    if (-not $script:Options.ContainsKey($ID)) {
        $script:Options[$ID] = @{}
    }

    # Store state and action in global:Options
    $script:Options[$ID][$Label] = @{
        Checked = $Default
        Action  = $Action
    }
    

    # Create the checkbox
    $cb = New-Object System.Windows.Controls.CheckBox
    $cb.Content = $Label
    $cb.IsChecked = $Default
    $cb.HorizontalAlignment = $align
    if ($tooltip) {
        $cb.tooltip = $tooltip
    }
    
    $cb.Cursor = "Hand"
    $cb.Foreground = "White"
    $cb.FontSize   = 14
    $cb.Margin     = $Margin

    # Box (Border) styling
    $cb.BorderBrush = "#777"
    $cb.BorderThickness = 2
    $cb.Background = "#222"

    # Hover effect
    $cb.Add_MouseEnter({ $_.Source.BorderBrush = "#999" })
    $cb.Add_MouseLeave({ $_.Source.BorderBrush = "#777" })
    
    $cb.Add_Checked({ 
        $_.Source.Background   = "#00b100"
        $_.Source.BorderBrush  = "#00b100"
    })

    $cb.Add_Unchecked({ 
        $_.Source.Background   = "#222"
        $_.Source.BorderBrush  = "#777"
    })

    # Tag stores label and group reference only
    
    $cb.Tag = @{
        Label = $Label
        Group = $script:Options[$ID]
    }

    # Update global hash when checked/unchecked
    $cb.Add_Checked({
        $tag = $_.Source.Tag
        $tag.Group[$tag.Label].Checked = $true
    })
    $cb.Add_Unchecked({
        $tag = $_.Source.Tag
        $tag.Group[$tag.Label].Checked = $false
    })

    # Apply Action Now

    if ($ActionNowCheck) {
            $cb.Add_Checked({
     & $ActionNow   
            })
    }

    $script:Stack.Children.Add($cb) | Out-Null
}





function Window.AddTabControl {
    if (-not $script:Stack) { Write-Host "GUI not initialized"; return }

    $tabControl = New-Object System.Windows.Controls.TabControl
    $tabControl.Margin = "0,10,0,0"
    $tabControl.Height = 300
    $tabControl.Background = "#1E1E1E"
    $tabControl.BorderBrush = "#1E1E1E"
    $tabControl.TabStripPlacement = "Top"
    $tabControl.Padding = "5,2,5,2"
    $tabControl.BorderThickness = 0
    $script:Stack.Children.Add($tabControl)
    $script:Tabs = $tabControl
    
}

function Window.AddTab {
    param(
        [string]$Label,
        [scriptblock]$Script
    )
    if (-not $script:Tabs) { Write-Host "TabControl not created"; return }

    $tabItem = New-Object System.Windows.Controls.TabItem
    $tabItem.Header = $Label
    $tabItem.Foreground = "#AAA"
    $tabItem.Background = "#1E1E1E"
    $tabItem.FontWeight = "Bold"

       # base style
    $tabItem.Background = "#222"
    $tabItem.BorderBrush = "#333"
    $tabItem.BorderThickness = 1
    $tabItem.Margin = "2,0"
    $tabItem.Padding = "12,6"
    $tabItem.Foreground = "#CCCCCC"
    $tabItem.FontSize = 14

    # hover
    $tabItem.Add_MouseEnter({
        if (-not $_.Source.IsSelected) {
            $_.Source.Background = "#333"
        }
    })

    $tabItem.Add_MouseLeave({
        if (-not $_.Source.IsSelected) {
            $_.Source.Background = "#222"
        }
    })




    # --- NEW: Create scrollable content for the tab ---
    $containerGrid = New-Object System.Windows.Controls.Grid
    $containerGrid.HorizontalAlignment = "Stretch"
    $containerGrid.VerticalAlignment = "Stretch"

    $row = New-Object System.Windows.Controls.RowDefinition
    $row.Height = "*"
    $containerGrid.RowDefinitions.Add($row)
    $col = New-Object System.Windows.Controls.ColumnDefinition
    $col.Width = "*"
    $containerGrid.ColumnDefinitions.Add($col)

    $scrollViewer = New-Object System.Windows.Controls.ScrollViewer
    $scrollViewer.VerticalScrollBarVisibility = "Hidden"  # hides scrollbar
    $scrollViewer.HorizontalScrollBarVisibility = "Disabled"
    $scrollViewer.HorizontalAlignment = "Stretch"
    $scrollViewer.VerticalAlignment = "Stretch"
    $scrollViewer.Background = "#1E1E1E"

    $scrollStack = New-Object System.Windows.Controls.StackPanel
    $scrollStack.Margin = "5"
    $scrollStack.Background = "#1E1E1E"

    $scrollViewer.Content = $scrollStack
    $containerGrid.Children.Add($scrollViewer)
    [System.Windows.Controls.Grid]::SetRow($scrollViewer,0)
    [System.Windows.Controls.Grid]::SetColumn($scrollViewer,0)

    $tabItem.Content = $containerGrid
    # --- END NEW ---

    $script:Tabs.Items.Add($tabItem)

    # Temporarily redirect global stack to inner scrollable StackPanel
    $oldStack = $script:Stack
    $script:Stack = $scrollStack

    # Execute the user-provided scriptblock
    & $Script

    # Restore original stack
    $script:Stack = $oldStack
}


function Window.AddRadioButtonApply {
    param(
        [string]$Label,
        [string]$ID
    )

    $btn = New-Object System.Windows.Controls.Button
    $btn.Content = $Label
    $btn.Height = 35
    $btn.Width = 200
    $btn.Margin = "5"
    $btn.FontSize = 13
    $btn.Foreground = "White"
    $btn.Background = "#2D2D30"
    $btn.BorderBrush = "#3C3C3C"
    $btn.Cursor = "Hand"

    $localID = $ID

    $btn.Add_Click({
        if (-not $script:Options.ContainsKey($localID)) {
            Add-ErrorMessage "No radio options exist for group '$localID'"
            return
        }

        # Find the selected option in the hash
        $selected = $script:Options[$localID].GetEnumerator() |
                    Where-Object { $_.Value.Checked } |
                    Select-Object -ExpandProperty Key

        if (-not $selected) {
            Add-ErrorMessage "No option selected for group '$localID'."
            return
        }

        # Execute the action stored in global:Options
        $action = $script:Options[$localID][$selected].Action
        if ($action) {
            & $action
        } else {
            Write-Host "No action defined for selected radio: $selected"
        }
    }.GetNewClosure())

    $script:Stack.Children.Add($btn) | Out-Null
}



function Window.AddRadioOption {
    param(
        [string]$Label,
        [string]$ID = "default",
        [bool]$Default = $false,
        [scriptblock]$Action = {}
    )

    # Ensure group exists
    if (-not $script:Options.ContainsKey($ID)) {
        $script:Options[$ID] = @{}
    }

    # Store the radio option in global:Options
    $script:Options[$ID][$Label] = @{
        Checked = $Default
        Action  = $Action
    }

    # Create the RadioButton control
    $rb = New-Object System.Windows.Controls.RadioButton
    $rb.Content = $Label
    $rb.GroupName = $ID
    $rb.IsChecked = $Default
    $rb.Margin = "0,5,0,5"
    $rb.Cursor = "Hand"
    $rb.Foreground = "White"
    $rb.FontSize   = 14
    $rb.Margin     = "4"

    # Outer ring
    $rb.BorderBrush = "#777"
    $rb.BorderThickness = 2
    $rb.Background = "#777"

    # Hover
    $rb.Add_MouseEnter({ $_.Source.BorderBrush = "#999" })
    $rb.Add_MouseLeave({ $_.Source.BorderBrush = "#777" })

    # Checked
    $rb.Add_Checked({
        $_.Source.BorderBrush = "#3A7BFF"
    })
    $rb.Add_Unchecked({
        $_.Source.BorderBrush = "#777"
    })

    # Tag stores the label and group reference (no Action here)
    $rb.Tag = @{
        Label = $Label
        Group = $script:Options[$ID]
    }

    # When checked, update the Checked field in global:Options
    $rb.Add_Checked({
        $tag = $_.Source.Tag
        $group = $tag.Group
        $label = $tag.Label

        # Only this label is true; all others false
        foreach ($key in @($group.Keys)) {
            $group[$key].Checked = ($key -eq $label)
        }
    })

    $script:Stack.Children.Add($rb) | Out-Null
}

function Window.AddDropDown {
    param(
        [Parameter(Mandatory)]
        [string]$ID,

        [Parameter(Mandatory)]
        [array]$Items,

        [Parameter()]
        [scriptblock]$Action = $null,

        [Parameter()]
        [string]$Label = "",

        [Parameter()]
        [int]$Width = 200,

        [Parameter()]
        [int]$Height = 28,

        [Parameter()]
        [string]$DisplayMember = "Display"  # Property to show in ComboBox
    )

    # Ensure inputs and actions tables exist
    if (-not ($script:Window | Get-Member -Name Inputs)) {
        $script:Window | Add-Member -MemberType NoteProperty -Name Inputs -Value @{}
    }
    if (-not ($script:Window | Get-Member -Name InputActions)) {
        $script:Window | Add-Member -MemberType NoteProperty -Name InputActions -Value @{}
    }

    # Optional label
    if ($Label) {
        $lbl = New-Object System.Windows.Controls.TextBlock
        $lbl.Text = $Label
        $lbl.Margin = "5,5,5,0"
        $lbl.Foreground = [System.Windows.Media.Brushes]::White
        $lbl.FontSize = 14
        $script:Stack.Children.Add($lbl)
    }

    # Create ComboBox
    $combo = New-Object System.Windows.Controls.ComboBox
    $combo.Width = $Width
    $combo.Height = $Height
    $combo.Margin = "5"
    $combo.Background = "#222"
    $combo.Foreground = "White"
    $combo.BorderBrush = "#555"
    $combo.BorderThickness = 1
    $combo.Cursor = "Hand"

    # If items are objects and DisplayMember exists, bind it
    if ($Items -and $Items[0] -is [PSCustomObject] -and $Items[0].PSObject.Properties[$DisplayMember]) {
        $combo.DisplayMemberPath = $DisplayMember
        $combo.ItemsSource = $Items
    } else {
        $combo.ItemsSource = $Items
    }

    # Hover/focus effects
    $combo.Add_GotFocus({ $_.Source.BorderBrush = "#3A7BFF" })
    $combo.Add_LostFocus({ $_.Source.BorderBrush = "#555" })
    $combo.Add_MouseEnter({ $_.Source.Background = "#2E2E2E" })
    $combo.Add_MouseLeave({ $_.Source.Background = "#222" })

    # Store reference
    $script:Window.Inputs[$ID] = $combo
    if ($Action) { $script:Window.InputActions[$ID] = $Action }

    # Add Apply button next to dropdown
    $panel = New-Object System.Windows.Controls.StackPanel
    $panel.Orientation = "Horizontal"
    $panel.Margin = "0,2,0,5"
    $panel.Children.Add($combo)

    $btn = New-Object System.Windows.Controls.Button
    $btn.Content = "Apply"
    $btn.Width = 80
    $btn.Height = $Height
    $btn.Margin = "8,0,0,0"
    $btn.FontSize = 13
    $btn.Background = "#2A2A2A"
    $btn.Foreground = "White"
    $btn.BorderBrush = "#444"
    $btn.Cursor = "Hand"

    $btn.Add_MouseEnter({ $_.Source.Background = "#3A3A3A" })
    $btn.Add_MouseLeave({ $_.Source.Background = "#2A2A2A" })
    $btn.Add_PreviewMouseDown({ $_.Source.Background = "#3A7BFF" })
    $btn.Add_PreviewMouseUp({ if (-not $_.Source.IsMouseOver) { $_.Source.Background = "#2A2A2A" } })

    $localID = $ID
    $btn.Add_Click({
        $selected = $script:Window.Inputs[$localID].SelectedItem
        if ($null -ne $selected) {
            if ($script:Window.InputActions.ContainsKey($localID)) {
                & $script:Window.InputActions[$localID] -ArgumentList $selected
            } else {
                Add-ErrorMessage "No action defined for '$localID'."
            }
        } else {
            [System.Windows.MessageBox]::Show("select an item first.")
        }
    }.GetNewClosure())

    $panel.Children.Add($btn)
    $script:Stack.Children.Add($panel)
}



function Window.AddSuperApplyButton {
    param(
        [string]$Label,
        [string[]]$Groups  # Array of Option/Radio group IDs
    )

    $btn = New-Object System.Windows.Controls.Button
    $btn.Content = $Label
    $btn.Height = 35
    $btn.Width = 250
    $btn.Margin = "5"
    $btn.FontSize = 13
    $btn.Background = "#2D2D30"
    $btn.Foreground = "White"
    $btn.BorderBrush = "#3C3C3C"
    $btn.HorizontalAlignment = "Center"
    $btn.Cursor = "Hand"

    $btn.Add_Click({
        foreach ($ID in $Groups) {
            if (-not $script:Options.ContainsKey($ID)) {
                Write-Warning "No options found for group '$ID'"
                continue
            }

            # Apply all checked options in this group
            $selected = $script:Options[$ID].GetEnumerator() |
                        Where-Object { $_.Value.Checked } |
                        Select-Object -ExpandProperty Key

            foreach ($opt in $selected) {
                $action = $script:Options[$ID][$opt].Action
                if ($action) {
                    & $action
                }
            }

            Write-Verbose "Group '$ID' applied: $($selected -join ', ')"
        }

    }.GetNewClosure())

    $script:Stack.Children.Add($btn) | Out-Null
}


function Window.AddApplyOptionButton {
    param(
        [string]$Label = "Option Apply Button",
        [string]$ID = "default",
        [string]$Alignment = "Left"
    )

    $btn = New-Object System.Windows.Controls.Button
    $btn.Content = $Label
    $btn.Margin = "5"
    $btn.Height = 35
    $btn.Width = 200
    $btn.FontSize = 13
    $btn.Background = "#2D2D30"
    $btn.Foreground = "White"
    $btn.BorderBrush = "#3C3C3C"
    $btn.HorizontalAlignment = $Alignment
    $btn.Cursor = "Hand"

    $localID = $ID

    $btn.Add_Click({
        if (-not $script:Options.ContainsKey($localID)) {
            Add-ErrorMessage "No options found for ID '$localID'."
            return
        }

        # Get all checked options
        $selected = $script:Options[$localID].GetEnumerator() |
                    Where-Object { $_.Value.Checked } |
                    Select-Object -ExpandProperty Key

        if (-not $selected) {
            Add-ErrorMessage "No options selected for group '$localID'."
            return
        }

        # Run actions for all checked options
        foreach ($opt in $selected) {
            $action = $script:Options[$localID][$opt].Action
            if ($action) { & $action }
        }

        Write-Verbose "Selected options:" $selected
    }.GetNewClosure())

     $script:Stack.Children.Add($btn) | Out-Null
}

function Window.Close {
    if ($script:Window) {
        $script:Window.Close()
    }
}

function Window.ControlLockCheckBox {
    param(
        [string]$ID,
        [string]$Label,
        [bool]$Lock = $true  # $true = lock, $false = unlock
    )

    if (-not $script:Options.ContainsKey($ID)) { return }

    # Find the control in the StackPanel
    foreach ($child in $script:Stack.Children) {
        if ($child -is [System.Windows.Controls.CheckBox] -and $child.Tag.Label -eq $Label) {
            $child.IsEnabled = -not $Lock
            return
        }
    }
}

function Window.ControlLockEntireRadioID {
    param(
        [string]$ID,
        [bool]$Lock = $true
    )

    if (-not $script:Options.ContainsKey($ID)) { return }

    # Iterate through all controls in the StackPanel
    foreach ($child in $script:Stack.Children) {
        if ($child -is [System.Windows.Controls.RadioButton]) {
            $tag = $child.Tag
            if ($tag.GroupID -eq $ID) {
                $child.IsEnabled = -not $Lock
            }
        }
    }
}



function IsOptionChecked {
    param(
        [string]$ID,
        [string]$Label
    )

    return ($script:Options[$ID][$Label].Checked -eq $true)
    #Usage: Use in If () {}
}

function Select-Path {
    param (
        [Parameter(Mandatory=$true)]
        [ValidateSet("OpenFile","SaveFile","PickFolder")]
        [string]$Mode,

        [string]$Title = "Select item",
        [string]$Filter = "All files (*.*)|*.*",
        [string]$DefaultFileName = "NewFile.txt",
        [string]$InitialDirectory = [Environment]::GetFolderPath("Desktop")
    )

    switch ($Mode) {
        "OpenFile" {
            $dlg = New-Object System.Windows.Forms.OpenFileDialog
            $dlg.Title = $Title
            $dlg.Filter = $Filter
            $dlg.InitialDirectory = $InitialDirectory
            if ($dlg.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
                return $dlg.FileName
            }
        }
        "SaveFile" {
            $dlg = New-Object System.Windows.Forms.SaveFileDialog
            $dlg.Title = $Title
            $dlg.Filter = $Filter
            $dlg.FileName = $DefaultFileName
            $dlg.InitialDirectory = $InitialDirectory
            if ($dlg.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
                return $dlg.FileName
            }
        }
        "PickFolder" {
            $dlg = New-Object System.Windows.Forms.FolderBrowserDialog
            $dlg.Description = $Title
            $dlg.SelectedPath = $InitialDirectory
            if ($dlg.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
                return $dlg.SelectedPath
            }
        }
    }
}

function ShowWindow {
    if (-not $script:Window) { Write-Host "No window created."; return }
    $null = $script:Window.ShowDialog()
}

function Get-GUIWindow {
    return $script:Window
}

function Set-GUIWindow {
    param([Parameter(Mandatory)]$NewValue)
    $script:Window = $NewValue
}

function Adjust.ControlSizes {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [System.Windows.Window[]]$script:window,

        [Parameter()]
        [double]$Increase,

        [Parameter()]
        [double]$Decrease,

        [switch]$SetDefault
    )

    # Validate that Increase and Decrease are not used together
    if ($Increase -and $Decrease) {
        Write-Error "Use either -Increase or -Decrease, not both."
        return
    }

    function AdjustRecursive {
        param([Parameter(Mandatory = $true)]$Control)

        # --- WPF and WinForms ---
        # FontSize / Font
        if ($Control.PSObject.Properties.Name -contains 'FontSize') {
            # Save default if it doesn't exist
            if (-not $Control.PSObject.Properties.Match('FontSize_Default')) {
                $Control | Add-Member -MemberType NoteProperty -Name FontSize_Default -Value $Control.FontSize
            }

            if ($SetDefault) {
                $Control.FontSize = $Control.FontSize_Default
                $Control.PSObject.Properties.Remove('FontSize_Default')
            }
            elseif ($Increase) {
                $Control.FontSize *= $Increase
            }
            elseif ($Decrease) {
                $Control.FontSize /= $Decrease
            }
        }
        elseif ($Control.PSObject.Properties.Name -contains 'Font') {
            if (-not $Control.PSObject.Properties.Match('FontSize_Default')) {
                $Control | Add-Member -MemberType NoteProperty -Name FontSize_Default -Value $Control.Font.Size
            }

            if ($SetDefault) {
                $Control.Font = New-Object System.Drawing.Font($Control.Font.FontFamily, $Control.FontSize_Default)
                $Control.PSObject.Properties.Remove('FontSize_Default')
            }
            elseif ($Increase) {
                $Control.Font = New-Object System.Drawing.Font($Control.Font.FontFamily, $Control.Font.Size * $Increase)
            }
            elseif ($Decrease) {
                $Control.Font = New-Object System.Drawing.Font($Control.Font.FontFamily, $Control.Font.Size / $Decrease)
            }
        }

        # Width
        if ($Control.PSObject.Properties.Name -contains 'Width') {
            if (-not $Control.PSObject.Properties.Match('Width_Default')) {
                $Control | Add-Member -MemberType NoteProperty -Name Width_Default -Value $Control.Width
            }

            if ($SetDefault) {
                $Control.Width = $Control.Width_Default
                $Control.PSObject.Properties.Remove('Width_Default')
            }
            elseif ($Increase) {
                $Control.Width *= $Increase
            }
            elseif ($Decrease) {
                $Control.Width /= $Decrease
            }
        }

        # Height
        if ($Control.PSObject.Properties.Name -contains 'Height') {
            if (-not $Control.PSObject.Properties.Match('Height_Default')) {
                $Control | Add-Member -MemberType NoteProperty -Name Height_Default -Value $Control.Height
            }

            if ($SetDefault) {
                $Control.Height = $Control.Height_Default
                $Control.PSObject.Properties.Remove('Height_Default')
            }
            elseif ($Increase) {
                $Control.Height *= $Increase
            }
            elseif ($Decrease) {
                $Control.Height /= $Decrease
            }
        }

        # --- Recursively process child controls ---
        if ($Control.PSObject.Properties.Name -contains 'Children') {
            foreach ($child in $Control.Children) {
                AdjustRecursive -Control $child
            }
        }
        elseif ($Control.PSObject.Properties.Name -contains 'Controls') {
            foreach ($child in $Control.Controls) {
                AdjustRecursive -Control $child
            }
        }
    }

    # Loop through all windows
    foreach ($win in $script:window) {
        AdjustRecursive -Control $win
    }
}

function Set.WindowSize {
    param(
        [int]$Width,
        [int]$Height
    )

    # Ensure the EasyGUI form object exists
    if (-not $script:window) {
        Write-Error "EasyGUI window is not initialized."
        return
    }

    # Set new dimensions
    $script:window.Width = $Width
    $script:window.Height = $Height
}

function Window.Title {
    param([string]$Title)
    if ($script:window) { $script:window.Text = $Title }
}

function Window.AddImage {
    param(
        [Parameter(Mandatory=$true)]
        [string]$Path,          # Path to image file
        [string]$ID = $null,    # Optional unique ID for updating
        [int]$Width = 100,      # Optional width
        [int]$Height = 100      # Optional height
    )

    if (-not $script:Stack) { Write-Host "GUI not initialized"; return }

    # Make sure Inputs hash exists
    if (-not ($script:Window | Get-Member -Name Inputs)) {
        $script:Window | Add-Member -MemberType NoteProperty -Name Inputs -Value @{}
    }

    # If ID is provided, update existing Image if it exists
    if ($ID -and $script:Window.Inputs.ContainsKey($ID)) {
        $existing = $script:Window.Inputs[$ID]
        $bitmap = New-Object System.Windows.Media.Imaging.BitmapImage
        $bitmap.BeginInit()
        $bitmap.UriSource = [Uri]::new($Path, [UriKind]::Absolute)
        $bitmap.EndInit()
        $existing.Source = $bitmap
        return
    }

    # Create a new Image element
    $image = New-Object System.Windows.Controls.Image
    $bitmap = New-Object System.Windows.Media.Imaging.BitmapImage
    $bitmap.BeginInit()
    $bitmap.UriSource = [Uri]::new($Path, [UriKind]::Absolute)
    $bitmap.DecodePixelWidth = $Width
    $bitmap.DecodePixelHeight = $Height
    $bitmap.EndInit()
    $image.Source = $bitmap
    $image.Width = $Width
    $image.Height = $Height
    $image.Margin = "5,5,5,10"

    # Store reference if ID is given
    if ($ID) {
        $script:Window.Inputs[$ID] = $image
    }

    $script:Stack.Children.Add($image)
}

function Window.ProgressBar {
    param(
        [string]$ID = $null,
        [int]$Width = 300,
        [int]$Height = 20,
        [bool]$IsIndeterminate = $true   # Looping style by default
    )

    if (-not $script:Stack) { Write-Host "GUI not initialized"; return }

    # Make sure Inputs hash exists
    if (-not ($script:Window | Get-Member -Name Inputs)) {
        $script:Window | Add-Member -MemberType NoteProperty -Name Inputs -Value @{}
    }

    # If ID is provided and already exists, return existing
    if ($ID -and $script:Window.Inputs.ContainsKey($ID)) {
        return $script:Window.Inputs[$ID]
    }

    # Create a new ProgressBar
    $progressBar = New-Object System.Windows.Controls.ProgressBar
    $progressBar.Width = $Width
    $progressBar.Height = $Height
    $progressBar.Margin = "5,5,5,10"
    $progressBar.Minimum = 0
    $progressBar.Maximum = 100
    $progressBar.Value = 0
    $progressBar.IsIndeterminate = $IsIndeterminate

    # Store reference if ID is given
    if ($ID) {
        $script:Window.Inputs[$ID] = $progressBar
    }

    $script:Stack.Children.Add($progressBar)

    return $progressBar
}

Write-Host "All GUI Functions Sucessfully loaded." -ForegroundColor Green