Tools/Gui.ps1
function Start-PodeGuiRunspace { # do nothing if gui not enabled, or running as serverless if (!$PodeContext.Server.Gui.Enabled -or $PodeContext.Server.IsServerless) { return } $script = { try { # if there are multiple endpoints, flag warning we're only using the first - unless explicitly set if ($null -eq $PodeContext.Server.Gui.Endpoint) { if (($PodeContext.Server.Endpoints | Measure-Object).Count -gt 1) { Write-Host "Multiple endpoints defined, only the first will be used for the GUI" -ForegroundColor Yellow } } # get the endpoint on which we're currently listening, or use explicitly passed one $endpoint = (Get-PodeEndpointUrl -Endpoint $PodeContext.Server.Gui.Endpoint) # poll the server for a response $count = 0 while ($true) { try { Invoke-WebRequest -Method Get -Uri $endpoint -UseBasicParsing -ErrorAction Stop | Out-Null if (!$?) { throw } break } catch { $count++ if ($count -le 50) { Start-Sleep -Milliseconds 200 } else { throw "Failed to connect to URL: $($endpoint)" } } } # import the WPF assembly [System.Reflection.Assembly]::LoadWithPartialName('PresentationFramework') | Out-Null [System.Reflection.Assembly]::LoadWithPartialName('PresentationCore') | Out-Null # setup the WPF XAML for the server $gui_browser = " <Window xmlns=`"http://schemas.microsoft.com/winfx/2006/xaml/presentation`" xmlns:x=`"http://schemas.microsoft.com/winfx/2006/xaml`" Title=`"$($PodeContext.Server.Gui.Name)`" Height=`"$($PodeContext.Server.Gui.Height)`" Width=`"$($PodeContext.Server.Gui.Width)`" ResizeMode=`"$($PodeContext.Server.Gui.ResizeMode)`" WindowStartupLocation=`"CenterScreen`" ShowInTaskbar = `"$($PodeContext.Server.Gui.ShowInTaskbar)`" WindowStyle = `"$($PodeContext.Server.Gui.WindowStyle)`"> <Window.TaskbarItemInfo> <TaskbarItemInfo /> </Window.TaskbarItemInfo> <WebBrowser Name=`"WebBrowser`"></WebBrowser> </Window>" # read in the XAML $reader = [System.Xml.XmlNodeReader]::new([xml]$gui_browser) $form = [Windows.Markup.XamlReader]::Load($reader) # set other options $form.TaskbarItemInfo.Description = $form.Title # add the icon to the form if (!(Test-Empty $PodeContext.Server.Gui.Icon)) { $icon = [Uri]::new($PodeContext.Server.Gui.Icon) $form.Icon = [Windows.Media.Imaging.BitmapFrame]::Create($icon) } # set the state of the window onload if (!(Test-Empty $PodeContext.Server.Gui.State)) { $form.WindowState = $PodeContext.Server.Gui.State } # get the browser object from XAML and navigate to base page $form.FindName("WebBrowser").Navigate($endpoint) # display the form $form.ShowDialog() | Out-Null Start-Sleep -Seconds 1 } catch { $Error[0] | Out-Default throw $_.Exception } finally { # invoke the cancellation token to close the server $PodeContext.Tokens.Cancellation.Cancel() } } Add-PodeRunspace -Type 'Gui' -ScriptBlock $script } function Gui { param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [Alias('n')] [string] $Name, [Parameter()] [Alias('o')] [hashtable] $Options ) # error if serverless Test-PodeIsServerless -FunctionName 'gui' -ThrowError # only valid for Windows PowerShell if (Test-IsPSCore) { throw 'The gui function is currently unavailable for PS Core, and only works for Windows PowerShell' } # enable the gui and set it's title/name $PodeContext.Server.Gui.Enabled = $true $PodeContext.Server.Gui.Name = $Name # coalesce the options $Options = (coalesce $Options @{}) # set the window's icon path if (![string]::IsNullOrWhiteSpace($Options.Icon)) { $PodeContext.Server.Gui.Icon = (Resolve-Path $Options.Icon).Path if (!(Test-Path $PodeContext.Server.Gui.Icon)) { throw "Path to icon for GUI does not exist: $($PodeContext.Server.Gui.Icon)" } } # display the app in the taskbar? $PodeContext.Server.Gui.ShowInTaskbar = (coalesce $Options.ShowInTaskbar $true) # set the window's state $states = @('Normal', 'Maximized', 'Minimized') $PodeContext.Server.Gui.State = (coalesce $Options.State 'Normal') if ($states -inotcontains $PodeContext.Server.Gui.State) { throw "Invalid GUI window state supplied, should be blank or one of $($states -join ' / ')" } # set the window's style $styles = @('None', 'SingleBorderWindow', 'ThreeDBorderWindow', 'ToolWindow') $PodeContext.Server.Gui.WindowStyle = (coalesce $Options.WindowStyle 'SingleBorderWindow') if ($styles -inotcontains $PodeContext.Server.Gui.WindowStyle) { throw "Invalid GUI window style supplied, should be blank or one of $($styles -join ' / ')" } # set the height of the window $PodeContext.Server.Gui.Height = (coalesce ([int]$Options.Height) 0) if ($PodeContext.Server.Gui.Height -le 0) { $PodeContext.Server.Gui.Height = 'auto' } # set the width of the window $PodeContext.Server.Gui.Width = (coalesce ([int]$Options.Width) 0) if ($PodeContext.Server.Gui.Width -le 0) { $PodeContext.Server.Gui.Width = 'auto' } # set the resize mode of the window $modes = @('CanResize', 'CanMinimize', 'NoResize') $PodeContext.Server.Gui.ResizeMode = (coalesce $Options.ResizeMode 'CanResize') if ($modes -inotcontains $PodeContext.Server.Gui.ResizeMode) { throw "Invalid GUI window resize mode supplied, should be blank or one of $($modes -join ' / ')" } # set the gui to use a specific listener $PodeContext.Server.Gui.ListenName = $Options.ListenName if (!(Test-Empty $PodeContext.Server.Gui.ListenName)) { $found = ($PodeContext.Server.Endpoints | Where-Object { $_.Name -eq $PodeContext.Server.Gui.ListenName } | Select-Object -First 1) if ($null -eq $found) { throw "Listen endpoint with name '$($Name)' does not exist" } $PodeContext.Server.Gui.Endpoint = $found } } |