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) | Out-Null } 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) | Out-Null } 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) | Out-Null } } 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) | Out-Null } } 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()] [double]$Increase, [Parameter()] [double]$Decrease, [switch]$SetDefault ) Write-Warning "Window.ControlSizes is experimental and may break the GUI. Use at your own risk." # 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') -and (-not ($Control.PSObject.Properties.Name -contains "WindowStartupLocation"))) { 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') -and (-not ($Control.PSObject.Properties.Name -contains "WindowStartupLocation"))) { 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) | Out-Null # return $progressBar } function Window.GroupBox { param( [string]$Header = "Group", [string]$ID = $null, # Optional unique ID [scriptblock]$Action = $null # Script block to add child controls ) 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) { # If GroupBox already exists, just return it if ($script:Window.Inputs.ContainsKey($ID)) { # return $script:Window.Inputs[$ID] } } # Create the GroupBox $groupBox = New-Object System.Windows.Controls.GroupBox $groupBox.Header = $Header $groupBox.Margin = "5,5,5,5" $groupBox.Padding = "5" $GroupBox.Background = "#222222" $GroupBox.Foreground = "White" $GroupBox.BorderBrush = "#444444" # Internal StackPanel to hold child controls $stackPanel = New-Object System.Windows.Controls.StackPanel $groupBox.Content = $stackPanel # Store reference if ID is given if ($ID) { $script:Window.Inputs[$ID] = $groupBox } # Add GroupBox to current Stack $script:Stack.Children.Add($groupBox) | Out-Null # Run the action script block in the context of the internal StackPanel if ($Action) { # Temporarily set $script:Stack to the new internal panel $oldStack = $script:Stack $script:Stack = $stackPanel & $Action $script:Stack = $oldStack } # return $stackPanel } # --- Helpers --- function Window.GetID { param([string]$ID) if (-not $script:Window.Inputs.ContainsKey($ID)) { return $null } $entry = $script:Window.Inputs[$ID] if ($null -ne $entry.State ) { return $entry.State } return $entry } function Window.SetID { param([string]$ID, [bool]$Value) if (-not $script:Window.Inputs.ContainsKey($ID)) { return } $entry = $script:Window.Inputs[$ID] if ($entry.Switch) { $entry.State = $Value $entry.Update.Invoke() } } # --- ContextMenu Control --- function Window.ContextMenu { param( [string]$ID = $null, [scriptblock]$Items # ScriptBlock to define menu items ) if (-not $script:Stack) { Write-Host "GUI not initialized"; return } # Ensure Inputs hash exists if (-not ($script:Window | Get-Member -Name Inputs)) { $script:Window | Add-Member -MemberType NoteProperty -Name Inputs -Value @{} } if ($ID) { # Replace existing ContextMenu if it exists if ($script:Window.Inputs.ContainsKey($ID)) { $existingMenu = $script:Window.Inputs[$ID] $existingMenu.Items.Clear() $contextMenu = $existingMenu } else { $contextMenu = New-Object System.Windows.Controls.ContextMenu $script:Window.Inputs[$ID] = $contextMenu } } else { $contextMenu = New-Object System.Windows.Controls.ContextMenu } $contextMenu.Background = "#2b2b2b" $contextMenu.Foreground = "#2b2b2b" # Temporarily store $script:CurrentMenu to let Items script block add menu items $script:CurrentMenu = $contextMenu & $Items Remove-Variable CurrentMenu -Scope Script -ErrorAction SilentlyContinue # Attach to the last added control in the stack, if exists if ($script:Stack.Children.Count -gt 0) { $lastControl = $script:Stack.Children[$script:Stack.Children.Count - 1] $lastControl.ContextMenu = $contextMenu } } # --- Helper to add MenuItem inside ContextMenu --- function Window.MenuItem { param( [string]$Header, [scriptblock]$Action = $null ) if (-not $script:CurrentMenu) { Write-Host "No ContextMenu is active"; return } $menuItem = New-Object System.Windows.Controls.MenuItem $menuItem.Header = $Header $menuItem.Background = "#2b2b2b" $menuItem.Foreground = "White" if ($Action) { $menuItem.Add_Click($Action) } $script:CurrentMenu.Items.Add($menuItem) } function Window.NumericUpDown { param( [string]$ID = $null, [decimal]$Value = 0, [decimal]$Minimum = 0, [decimal]$Maximum = 100, [decimal]$Increment = 1, [string]$Label = $null ) if (-not $script:Stack) { Write-Host "GUI not initialized"; return } # Ensure Inputs hash exists if (-not ($script:Window | Get-Member -Name Inputs)) { $script:Window | Add-Member -MemberType NoteProperty -Name Inputs -Value @{} } # Create container if label is present if ($Label) { $panel = New-Object System.Windows.Controls.StackPanel $panel.Orientation = "Horizontal" $textBlock = New-Object System.Windows.Controls.TextBlock $textBlock.Text = $Label $textBlock.Margin = "0,0,5,0" $textBlock.Background = "#1e1e1e" $textBlock.Foreground = "White" $textBlock.BorderBrush = "#444444" $panel.Children.Add($textBlock) } else { $panel = New-Object System.Windows.Controls.StackPanel } # Load WinForms numeric updown Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName WindowsFormsIntegration $numericUpDown = New-Object System.Windows.Forms.NumericUpDown $numericUpDown.Minimum = $Minimum $numericUpDown.Maximum = $Maximum $numericUpDown.Increment = $Increment $numericUpDown.Value = $Value $numericUpDown.Width = 80 $numericUpDown.BorderStyle = "Fixed3D" $numericUpDown.Cursor = "Hand" $numericUpDown.ForeColor = "White" $numericUpDown.BackColor = "#333333" # Wrap it inside a WPF host $ScriptHostScriptHost = New-Object System.Windows.Forms.Integration.WindowsFormsHost $ScriptHostScriptHost.Child = $numericUpDown $panel.Children.Add($ScriptHostScriptHost) # Store reference by ID if ($ID) { $script:Window.Inputs[$ID] = $numericUpDown } # Add panel to current stack $script:Stack.Children.Add($panel) | Out-Null } # --- Helper usage with GetID / SetID --- # Get numeric value: $val = Window.GetID "MyNumeric" # Set numeric value: Window.SetID "MyNumeric" 50 function Window.TreeView { param( [string]$ID = $null, [hashtable]$Items = @{}, [hashtable]$Content = @{}, [int]$Width = 400, [int]$Height = 300 ) if (-not $script:Stack) { Write-Host "GUI not initialized"; return } if (-not ($script:Window | Get-Member -Name Inputs)) { $script:Window | Add-Member -MemberType NoteProperty -Name Inputs -Value @{} } $script:TreeViewGrid = New-Object System.Windows.Controls.Grid $script:TreeViewGrid.Width = $Width $script:TreeViewGrid.Height = $Height $col1 = New-Object System.Windows.Controls.ColumnDefinition $col1.Width = "200" $col2 = New-Object System.Windows.Controls.ColumnDefinition $col2.Width = "*" $script:TreeViewGrid.ColumnDefinitions.Add($col1) $script:TreeViewGrid.ColumnDefinitions.Add($col2) $script:TreeViewControl = New-Object System.Windows.Controls.TreeView [System.Windows.Controls.Grid]::SetColumn($script:TreeViewControl, 0) $script:TreeViewGrid.Children.Add($script:TreeViewControl) | Out-Null $script:TreeViewContentPanel = New-Object System.Windows.Controls.StackPanel [System.Windows.Controls.Grid]::SetColumn($script:TreeViewContentPanel, 1) $script:TreeViewGrid.Children.Add($script:TreeViewContentPanel) | Out-Null if ($ID) { $script:Window.Inputs[$ID] = @{ Tree = $script:TreeViewControl ContentPanel = $script:TreeViewContentPanel Items = $Items Content = $Content } } function Add-TreeItems { param($parent, $items) foreach ($key in $items.Keys) { $node = New-Object System.Windows.Controls.TreeViewItem $node.Header = $key $parent.Items.Add($node) | Out-Null # Use script-scoped panel and content hash $node.Add_Selected({ $panel = $script:TreeViewContentPanel $contentHash = $Content if ($null -ne $panel) { $panel.Dispatcher.Invoke([action]{ $panel.Children.Clear() $header = $this.Header if ($contentHash.ContainsKey($header)) { $val = $contentHash[$header] if ($val -is [scriptblock]) { & $val.Invoke() } elseif ($val -is [System.Windows.UIElement]) { $panel.Children.Add($val) | Out-Null } elseif ($val -is [string]) { $lbl = New-Object System.Windows.Controls.Label $lbl.Content = $val $panel.Children.Add($lbl) | Out-Null } } else { $lbl = New-Object System.Windows.Controls.Label $lbl.Content = "No content for '$header'" $panel.Children.Add($lbl) | Out-Null } }) } }) if ($items[$key] -is [hashtable] -and $items[$key].Count -gt 0) { Add-TreeItems -parent $node -items $items[$key] } } } Add-TreeItems -parent $script:TreeViewControl -items $Items $script:Stack.Children.Add($script:TreeViewGrid) | Out-Null } Write-Host "All GUI Functions Sucessfully loaded." -ForegroundColor Green |