UIfiedBase.ps1

using namespace System.Collections.Generic
using namespace System.Management.Automation.Language

#region UI Type selection

enum UIType {
    WPF
    CF
    Ooui
    MaterialWPF
    MaterialOoui
    MaterialCF
}

class UIConfig {
    static [UIType] $UIType = [UIType]::MaterialWPF
}

function Set-UIType {
    [CmdletBinding(SupportsShouldProcess)]
    param (
        [UIType]   $Type
    )
    if ($PSCmdlet.ShouldProcess('TARGET')) {
        [UIConfig]::UIType = $Type
    }
}

function Set-UICF {
    [CmdletBinding(SupportsShouldProcess)]
    param (
    )
    Set-UIType -Type ([UIType]::CF)
}

function Set-UIWPF {
    [CmdletBinding(SupportsShouldProcess)]
    param (
    )
    Set-UIType -Type ([UIType]::WPF)
}

function Set-UIOoui {
    [CmdletBinding(SupportsShouldProcess)]
    param (
    )
    Set-UIType -Type ([UIType]::Ooui)
}

function Set-UIMaterialWPF {
    [CmdletBinding(SupportsShouldProcess)]
    param (
    )
    Set-UIType -Type ([UIType]::MaterialWPF)
}

function Set-UIMaterialOoui {
    [CmdletBinding(SupportsShouldProcess)]
    param (
    )
    Set-UIType -Type ([UIType]::MaterialOoui)
}

function Set-UIMaterialCF {
    [CmdletBinding(SupportsShouldProcess)]
    param (
    )
    Set-UIType -Type ([UIType]::MaterialCF)
}

#endregion

#region UI Constructs

class UIElement {
    hidden   [ScriptBlock]     $AddNativeUIChild    = { param ([UIElement] $element) }
    hidden   [ScriptBlock]     $RemoveNativeUIChild = { param ([UIElement] $element) }
    hidden   [ScriptBlock]     $ShowError           = { param ([Object]    $errorObject) }
    hidden   [int]             $MaxErrors           = 3
    hidden   [Object]          $NativeUI            = $null
             [List[UIElement]] $Children            = [List[UIElement]]::new()
             [WindowBase]      $Form                = $null
             [UIElement]       $Parent              = $null
             [String]          $Name                = ""

    UIElement() {
    }

    [void] AddChild([UIElement] $element) {
        $this.Children.Add($element)
        $element.SetForm($this.Form)
        $element.SetParent($this)
        Invoke-Command -ScriptBlock $this.AddNativeUIChild -ArgumentList $element
    }

    [void] RemoveChild([UIElement] $element) {
        Invoke-Command -ScriptBlock $this.RemoveNativeUIChild -ArgumentList $element
        $this.Children.Remove($element) | Out-Null
    }

    hidden [void] SetForm([WindowBase] $form) {
        if ($null -ne $form) {
            $this.Form = $form
            $this.Children | ForEach-Object {
                $_.SetForm($form)
                Add-Member -InputObject $_.NativeUI -Name Form -MemberType NoteProperty -Value $form
            }
            if ($this.Name -ne "") {
                $nameMember = Get-Member -InputObject $form -Name $this.Name -MemberType NoteProperty
                if ($null -eq $nameMember) {
                    Add-Member -InputObject $form -Name $this.Name -MemberType NoteProperty -Value $this
                }
            }
        }
    }

    hidden [void] SetParent([UIElement] $parent) {
        if ($null -ne $parent) {
            $this.Parent = $parent
        }
    }

    hidden [void] WrapProperty([String] $elementPropertyName, [String] $nativeUIProperty) {
        $this.WrapProperty($elementPropertyName, $nativeUIProperty, "NativeUI")
    }

    hidden [void] WrapProperty([String] $elementPropertyName, [String] $nativeUIProperty, [String] $nativeUIName) {
        if (-not $this.IsValidName($elementPropertyName) -or -not $this.IsValidName($nativeUIProperty) -or -not $this.IsValidName($nativeUIName)) {
            return
        }
        Add-Member -InputObject $this -Name $elementPropertyName -MemberType ScriptProperty                `
                    -Value          ([ScriptBlock]::Create("`$this.$nativeUIName.$nativeUIProperty"))           `
                    -SecondValue    ([ScriptBlock]::Create("`$this.$nativeUIName.$nativeUIProperty = `$args[0]"))
    }

    hidden [void] WrapNegatedProperty([String] $elementPropertyName, [String] $nativeUIProperty) {
        if (-not $this.IsValidName($elementPropertyName) -or -not $this.IsValidName($nativeUIProperty)) {
            return
        }
        Add-Member -InputObject $this -Name $elementPropertyName -MemberType ScriptProperty                `
                    -Value          ([ScriptBlock]::Create("-not `$this.NativeUI.$nativeUIProperty"))           `
                    -SecondValue    ([ScriptBlock]::Create("`$this.NativeUI.$nativeUIProperty = -not `$args[0]"))
    }

    hidden [void] SetNativeUI([Object] $nativeUI) {
        $this.NativeUI = $nativeUI
        Add-Member -InputObject $NativeUI -Name Control -MemberType NoteProperty -Value $this -Force
        Add-Member -InputObject $this     -Name Control -MemberType NoteProperty -Value $this -Force
    }

    hidden [bool] IsValidScript([ScriptBlock] $scriptBlock) {
        $isValid = $true
        $commands = $scriptBlock.Ast.FindAll({ param ($o) $o -is [CommandAst] }, $true)
        $commands | ForEach-Object {
            $commandName = $_.CommandElements[0].Value
            $command = Get-Command -Name $commandName -ErrorAction SilentlyContinue
            if ($null -eq $command) {
                $isValid = $false
            }
        }
        return $isValid
    }

    hidden [void] AddProperty([String]$propertyName) {
        if (-not $this.IsValidName($propertyName)) {
            return
        }
        $memberName = "_$propertyName"
        Add-Member -InputObject $this -Name $memberName   -MemberType NoteProperty   -Value {}
        Add-Member -InputObject $this -Name $propertyName -MemberType ScriptProperty -Value (
            [ScriptBlock]::Create("`$this.$memberName")
        ) -SecondValue (
            [ScriptBlock]::Create("`$this.$memberName = `$args[0]")
        )
    }

    hidden [void] AddScriptBlockProperty([String]$propertyName) {
        if (-not $this.IsValidName($propertyName)) {
            return
        }
        $memberName = "_$propertyName"
        Add-Member -InputObject $this -Name $memberName   -MemberType NoteProperty   -Value {}
        Add-Member -InputObject $this -Name $propertyName -MemberType ScriptProperty -Value (
            [ScriptBlock]::Create("`$this.$memberName")
        ) -SecondValue (
            [ScriptBlock]::Create("
                    if (`$this.IsValidScript(`$args[0])) {
                       `$this.$memberName = `$args[0]
                    } else {
                        `$this.$memberName = {}
                        `$this.Visible = `$false
                    }
            "
)
        )
    }

    hidden [bool] IsValidName([String]$name) {
        return ($name -match '(^[a-zA-Z_$][a-zA-Z_$0-9]*$)')
    }

    hidden [void] InvokeTrappableCommand([ScriptBlock] $ScriptBlock, [Object[]] $ArgumentList) {
        try {
            Invoke-Command -ScriptBlock $ScriptBlock -ArgumentList $ArgumentList
        } catch {
            $Global:SyncHash.Errors += $_
            Invoke-Command -ScriptBlock $this.ShowError -ArgumentList $_
            if ($Global:SyncHash.Errors.Count -gt $this.MaxErrors) {
                $this.Form.Close()
            }
        }
    }

    [void] AddNoteProperty([String]$propertyName, $value) {
        if (-not $this.IsValidName($propertyName)) {
            return
        }
        Add-Member -InputObject $this -Name $propertyName   -MemberType NoteProperty -Value $value
    }
}

class WindowBase : UIElement {

    WindowBase() {
        $this.Form = $this
    }

    [void] Close() {
        $this.NativeUI.Close()
    }
}

class UIHost {

    [void] ShowFrame([WindowBase]$window) {
    }

}

enum Orientation {
    Horizontal
    Vertical
}

enum TextAlignment {
    Left
    Right
}

class ListItem {
    [List[UIElement]] $Children            = [List[UIElement]]::new()

    ListItem() {
    }

    [void] AddChild([UIElement] $element) {
        $this.Children.Add($element)
    }

    [void] RemoveChild([UIElement] $element) {
        $this.Children.Remove($element)
    }
}

class AutoCompleteItem {
    [String] $Id
    [String] $Text
}

#endregion

#region UI Icons

class IconStrinfy {
    static [bool]  $ShowIcon = $true
    
    hidden static $Icons = @{
        menu                       = [string] [char] 02261 # 62542
        add                        = [string] [char] 62541
        edit                       = [string] [char] 61504
        delete                     = [string] [char] 63061
        clear                      = [string] [char] 62567
        calendar_today             = [string] [char] 62957
        query_builder              = [string] [char] 63055
        left_semi_circle           = [string] [char] 57526
        right_semi_circle          = [string] [char] 57524
        first_page                 = [string] [char] 64255
        chevron_left               = [string] [char] 61523
        chevron_right              = [string] [char] 61524
        chevron_up                 = [string] [char] 61559
        chevron_down               = [string] [char] 61560
        last_page                  = [string] [char] 64256
        radio_button_checked       = [string] [char] 64611
        radio_button_unchecked     = [string] [char] 64612
        check_box                  = [string] [char] 62510
        check_box_outlined_blank   = [string] [char] 64609
        keyboard_arrow_down        = [string] [char] 63035
        keyboard_arrow_up          = [string] [char] 63038
    }

    static [String] ToIconString([String] $kindName) {
        if ([IconStrinfy]::ShowIcon) {
            return [IconStrinfy]::Icons."$kindName"
        } else {
            return ""
        }
    }
}

enum IconPosition {
    Left
    Right
}

#endregion

#region UI Utilities

function Set-UIControlValue {
    param (
        $Object,
        $Form,
        $ControlPrefix   = ""
    )
    $properties = Get-Member -InputObject $Object -MemberType Properties
    $properties | ForEach-Object {
        $propertyName = $_.Name
        $controlName = $ControlPrefix + $propertyName
        $control = $Form."$controlName"
        if ($control -ne $null) {
            $controlProperties = Get-Member -InputObject $control -MemberType Properties
            if ($controlProperties.Name -contains "Text") {
                $control.Text = $Object."$propertyName"
            } elseif ($controlProperties.Name -contains "Value") {
                $control.Value = $Object."$propertyName"
            }
        }
    }
}

function Get-UIControlValue {
    param (
        $Object,
        $Form,
        $ControlPrefix   = ""
    )
    $properties = Get-Member -InputObject $Object -MemberType Properties
    $properties | ForEach-Object {
        $propertyName = $_.Name
        $controlName = $ControlPrefix + $propertyName
        $control = $Form."$controlName"
        if ($control -ne $null) {
            $controlProperties = Get-Member -InputObject $control -MemberType Properties
            if ($controlProperties.Name -contains "Text") {
                $Object."$propertyName" = $control.Text
            } elseif ($controlProperties.Name -contains "Value") {
                $Object."$propertyName" = $control.Value
            }
        }
    }
    $Object
}

function Get-UIMethod {
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Object]  $InputObject, 
        [Parameter(Position = 0, Mandatory = $true)]
        [String]   $Name,
        [Parameter(Position = 1)]
        [ScriptBlock] $ScriptBlock = { }
    )
    process {
        Add-Member -InputObject $InputObject -MemberType ScriptMethod -Name $Name -Value $ScriptBlock | Out-Null
        $InputObject
    }
}

function Get-UIProperty {
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Object]  $InputObject, 
        [Parameter(Position = 0, Mandatory = $true)]
        [String]   $Name,
        [ScriptBlock] $Get = { },
        [ScriptBlock] $Set = { }
    )
    process {
        Add-Member -InputObject $InputObject -Name $Name -MemberType ScriptProperty -Value $Get -SecondValue $Set | Out-Null
        $InputObject
    }
}

#endregion