├── OouiWrapper ├── OouiWrapper.cs ├── OouiWrapper.csproj ├── OouiWrapper.sln ├── Properties │ └── AssemblyInfo.cs ├── TestOouiWrapper.ps1 ├── packages.config └── packages │ ├── Newtonsoft.Json.10.0.2 │ ├── .signature.p7s │ ├── LICENSE.md │ ├── Newtonsoft.Json.10.0.2.nupkg │ ├── lib │ │ ├── net20 │ │ │ ├── Newtonsoft.Json.dll │ │ │ └── Newtonsoft.Json.xml │ │ ├── net35 │ │ │ ├── Newtonsoft.Json.dll │ │ │ └── Newtonsoft.Json.xml │ │ ├── net40 │ │ │ ├── Newtonsoft.Json.dll │ │ │ └── Newtonsoft.Json.xml │ │ ├── net45 │ │ │ ├── Newtonsoft.Json.dll │ │ │ └── Newtonsoft.Json.xml │ │ ├── netstandard1.0 │ │ │ ├── Newtonsoft.Json.dll │ │ │ └── Newtonsoft.Json.xml │ │ ├── netstandard1.3 │ │ │ ├── Newtonsoft.Json.dll │ │ │ └── Newtonsoft.Json.xml │ │ ├── portable-net40 sl5 win8 wpa81 wp8 │ │ │ ├── Newtonsoft.Json.dll │ │ │ └── Newtonsoft.Json.xml │ │ └── portable-net45 win8 wpa81 wp8 │ │ │ ├── Newtonsoft.Json.dll │ │ │ └── Newtonsoft.Json.xml │ └── tools │ │ └── install.ps1 │ └── Ooui.0.14.16 │ ├── .signature.p7s │ ├── Icon.png │ ├── Ooui.0.14.16.nupkg │ └── lib │ └── netstandard2.0 │ └── Ooui.dll ├── README.md ├── UIfied.Test ├── MaterialCF.gif ├── MaterialOoui.gif ├── MaterialWPF.gif ├── SimpleTest.gif ├── SimpleTest.ps1 └── UIfiedSample.ps1 ├── UIfied ├── Bin │ ├── Binding.dll │ ├── ConsoleFramework.dll │ ├── MaterialDesignColors.dll │ ├── MaterialDesignThemes.Wpf.dll │ ├── Newtonsoft.Json.dll │ ├── Ooui.dll │ ├── OouiWrapper.dll │ └── Xaml.dll ├── LICENSE.txt ├── LoadCFBinaries.ps1 ├── LoadMaterialWPFBinaries.ps1 ├── LoadOouiBinaries.ps1 ├── LoadWPFBinaries.ps1 ├── UIfied.psd1 ├── UIfied.psm1 ├── UIfiedBase.ps1 ├── UIfiedCFBase.ps1 ├── UIfiedDSL.ps1 ├── UIfiedMaterialCFBase.ps1 ├── UIfiedMaterialOouiBase.ps1 ├── UIfiedMaterialWPFBase.ps1 ├── UIfiedOouiBase.ps1 └── UIfiedWPFBase.ps1 └── sign-form.png /OouiWrapper/OouiWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Threading; 6 | using System.Text; 7 | using System.IO; 8 | using Ooui; 9 | 10 | namespace OouiWrapper 11 | { 12 | public class OouiWrapper 13 | { 14 | public Element Frame; 15 | public int Port; 16 | public string Host = UI.Host; 17 | private string Path; 18 | public delegate void OnPublishHandler(); 19 | public event OnPublishHandler OnPublish; 20 | private string UploadFolder; 21 | 22 | public OouiWrapper(int port = 8187, string path = "/frame") 23 | { 24 | this.Port = port; 25 | this.Path = path; 26 | this.Frame = new Label { Text = "Press refresh button" }; 27 | } 28 | public void Publish() 29 | { 30 | UI.Host = this.Host; 31 | UI.Port = this.Port; 32 | UI.Publish(this.Path, MakeElement); 33 | } 34 | public Element MakeElement() 35 | { 36 | var b = this.MakeFrame(); 37 | UI.Publish(this.Path, MakeElement); 38 | return b; 39 | } 40 | public Element MakeFrame() 41 | { 42 | this.OnPublish.Invoke(); 43 | return this.Frame; 44 | } 45 | public void PublishFileUpload(string uploadFolder, string actionUrl = "/files/upload") 46 | { 47 | this.UploadFolder = uploadFolder; 48 | UI.Publish("/files", this.CreateFileUploadElement(), true); 49 | UI.PublishCustomResponse(actionUrl, HandleUpload); 50 | } 51 | void HandleUpload(HttpListenerContext context, CancellationToken token) 52 | { 53 | 54 | SaveFile(context.Request.ContentEncoding, GetBoundary(context.Request.ContentType), context.Request.InputStream, this.UploadFolder); 55 | 56 | context.Response.StatusCode = 200; 57 | context.Response.ContentType = "text/html"; 58 | using (StreamWriter writer = new StreamWriter(context.Response.OutputStream, Encoding.UTF8)) 59 | writer.WriteLine("File Uploaded"); 60 | 61 | context.Response.Close(); 62 | } 63 | //public void HandleHookableElement(HttpListenerContext listenerContext, CancellationToken token) 64 | //{ 65 | // var url = listenerContext.Request.Url; 66 | // var path = url.LocalPath; 67 | // var response = listenerContext.Response; 68 | // 69 | // response.StatusCode = 200; 70 | // response.ContentType = "text/html"; 71 | // response.ContentEncoding = Encoding.UTF8; 72 | // var html = Encoding.UTF8.GetBytes(UI.RenderTemplate(path)); 73 | // //var html = Encoding.UTF8.GetBytes(MakeFrame())); 74 | // response.ContentLength64 = html.LongLength; 75 | // using (var s = response.OutputStream) 76 | // { 77 | // s.Write(html, 0, html.Length); 78 | // } 79 | // response.Close(); 80 | //} 81 | private static String GetBoundary(String ctype) 82 | { 83 | return "--" + ctype.Split(';')[1].Split('=')[1]; 84 | } 85 | 86 | private static void SaveFile(Encoding enc, String boundary, Stream input, string uploadFolder) 87 | { 88 | Byte[] boundaryBytes = enc.GetBytes(boundary); 89 | Int32 boundaryLen = boundaryBytes.Length; 90 | 91 | Byte[] buffer = new Byte[1024]; 92 | Int32 len = input.Read(buffer, 0, 1024); 93 | Int32 startPos = -1; 94 | 95 | var inputText = System.Text.UTF8Encoding.UTF8.GetString(buffer); 96 | var fileNameBeginPos = inputText.IndexOf("filename=") + "filename=".Length; 97 | var fileNameLen = inputText.Substring(fileNameBeginPos).IndexOf('\n') - 1; 98 | var fileName = inputText.Substring(fileNameBeginPos, fileNameLen).Replace("\"", ""); 99 | 100 | using (FileStream output = new FileStream(uploadFolder + System.IO.Path.DirectorySeparatorChar + fileName, FileMode.Create, FileAccess.Write)) 101 | { 102 | 103 | // Find start boundary 104 | while (true) 105 | { 106 | if (len == 0) 107 | { 108 | throw new Exception("Start Boundaray Not Found"); 109 | } 110 | 111 | startPos = IndexOf(buffer, len, boundaryBytes); 112 | if (startPos >= 0) 113 | { 114 | break; 115 | } 116 | else 117 | { 118 | Array.Copy(buffer, len - boundaryLen, buffer, 0, boundaryLen); 119 | len = input.Read(buffer, boundaryLen, 1024 - boundaryLen); 120 | } 121 | } 122 | 123 | // Skip four lines (Boundary, Content-Disposition, Content-Type, and a blank) 124 | for (Int32 i = 0; i < 4; i++) 125 | { 126 | while (true) 127 | { 128 | if (len == 0) 129 | { 130 | throw new Exception("Preamble not Found."); 131 | } 132 | 133 | startPos = Array.IndexOf(buffer, enc.GetBytes("\n")[0], startPos); 134 | if (startPos >= 0) 135 | { 136 | startPos++; 137 | break; 138 | } 139 | else 140 | { 141 | len = input.Read(buffer, 0, 1024); 142 | } 143 | } 144 | } 145 | 146 | Array.Copy(buffer, startPos, buffer, 0, len - startPos); 147 | len = len - startPos; 148 | 149 | while (true) 150 | { 151 | Int32 endPos = IndexOf(buffer, len, boundaryBytes); 152 | if (endPos >= 0) 153 | { 154 | if (endPos > 0) output.Write(buffer, 0, endPos - 2); 155 | break; 156 | } 157 | else if (len <= boundaryLen) 158 | { 159 | throw new Exception("End Boundaray Not Found"); 160 | } 161 | else 162 | { 163 | output.Write(buffer, 0, len - boundaryLen); 164 | Array.Copy(buffer, len - boundaryLen, buffer, 0, boundaryLen); 165 | len = input.Read(buffer, boundaryLen, 1024 - boundaryLen) + boundaryLen; 166 | } 167 | } 168 | } 169 | } 170 | 171 | private static Int32 IndexOf(Byte[] buffer, Int32 len, Byte[] boundaryBytes) 172 | { 173 | for (Int32 i = 0; i <= len - boundaryBytes.Length; i++) 174 | { 175 | Boolean match = true; 176 | for (Int32 j = 0; j < boundaryBytes.Length && match; j++) 177 | { 178 | match = buffer[i + j] == boundaryBytes[j]; 179 | } 180 | 181 | if (match) 182 | { 183 | return i; 184 | } 185 | } 186 | 187 | return -1; 188 | } 189 | public Element CreateFileUploadElement(string actionUrl = "/files/upload") 190 | { 191 | var heading = new Heading("Upload Files"); 192 | var subtitle = new Paragraph("Upload files to the app"); 193 | 194 | var uploadForm = new Form(); 195 | uploadForm.Action = actionUrl; 196 | uploadForm.Method = "POST"; 197 | uploadForm.EncodingType = "multipart/form-data"; 198 | uploadForm.AppendChild(new Input(InputType.File) { Name = "file" }); 199 | uploadForm.AppendChild(new Input(InputType.Submit) { Value = "Upload" }); 200 | 201 | var app = new Div(); 202 | app.AppendChild(heading); 203 | app.AppendChild(subtitle); 204 | app.AppendChild(uploadForm); 205 | 206 | return app; 207 | } 208 | } 209 | 210 | } 211 | -------------------------------------------------------------------------------- /OouiWrapper/OouiWrapper.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {1CE9B1C6-1C6E-44DF-A9CC-CBF1DF69BFA5} 8 | Library 9 | OouiWrapper 10 | OouiWrapper 11 | v4.7.2 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | ..\Bin\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | ..\OouiWrapper\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll 42 | 43 | 44 | packages\Ooui.0.14.16\lib\netstandard2.0\Ooui.dll 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /OouiWrapper/OouiWrapper.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29409.12 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OouiWrapper", "OouiWrapper.csproj", "{1CE9B1C6-1C6E-44DF-A9CC-CBF1DF69BFA5}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {1CE9B1C6-1C6E-44DF-A9CC-CBF1DF69BFA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {1CE9B1C6-1C6E-44DF-A9CC-CBF1DF69BFA5}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {1CE9B1C6-1C6E-44DF-A9CC-CBF1DF69BFA5}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {1CE9B1C6-1C6E-44DF-A9CC-CBF1DF69BFA5}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {EF8FDD8B-5F67-432F-B8D3-3513CAA10726} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /OouiWrapper/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // La información general de un ensamblado se controla mediante el siguiente 6 | // conjunto de atributos. Cambie estos valores de atributo para modificar la información 7 | // asociada a un ensamblado. 8 | [assembly: AssemblyTitle("ConsoleApp1")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ConsoleApp1")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Si establece ComVisible en false, los tipos de este ensamblado no estarán visibles 18 | // para los componentes COM. Si es necesario obtener acceso a un tipo en este ensamblado desde 19 | // COM, establezca el atributo ComVisible en true en este tipo. 20 | [assembly: ComVisible(false)] 21 | 22 | // El siguiente GUID sirve como id. de typelib si este proyecto se expone a COM. 23 | [assembly: Guid("1ce9b1c6-1c6e-44df-a9cc-cbf1df69bfa5")] 24 | 25 | // La información de versión de un ensamblado consta de los cuatro valores siguientes: 26 | // 27 | // Versión principal 28 | // Versión secundaria 29 | // Número de compilación 30 | // Revisión 31 | // 32 | // Puede especificar todos los valores o usar los valores predeterminados de número de compilación y de revisión 33 | // utilizando el carácter "*", como se muestra a continuación: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /OouiWrapper/TestOouiWrapper.ps1: -------------------------------------------------------------------------------- 1 | dir $PSScriptRoot\..\bin\*.dll | ForEach-Object { 2 | [Reflection.Assembly]::Load([System.IO.File]::ReadAllBytes($_)) 3 | } 4 | 5 | $hostWrapper = [OouiWrapper.OouiWrapper]::new() 6 | $hostWrapper.Publish() 7 | $hostWrapper.PublishFileUpload() 8 | 9 | #Add-Member -InputObject $hostWrapper -MemberType NoteProperty -Name sb -Value {[Ooui.Button]::new("hola mundo " + [DateTime]::Now.ToString())} 10 | Add-Member -InputObject $hostWrapper -MemberType NoteProperty -Name sb -Value $hostWrapper 11 | 12 | Register-ObjectEvent -InputObject $hostWrapper -EventName OnPublish -MessageData $hostWrapper -Action { 13 | param ($hostWrapper) 14 | $event.MessageData.Frame = Invoke-Command -ScriptBlock $event.MessageData.sb.CreateFileUploadElement() 15 | } 16 | -------------------------------------------------------------------------------- /OouiWrapper/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /OouiWrapper/packages/Newtonsoft.Json.10.0.2/.signature.p7s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-numericoverflow/UIfied/c8f7ca83c538a7e20970eaf0c38451bc2767a450/OouiWrapper/packages/Newtonsoft.Json.10.0.2/.signature.p7s -------------------------------------------------------------------------------- /OouiWrapper/packages/Newtonsoft.Json.10.0.2/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2007 James Newton-King 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /OouiWrapper/packages/Newtonsoft.Json.10.0.2/Newtonsoft.Json.10.0.2.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-numericoverflow/UIfied/c8f7ca83c538a7e20970eaf0c38451bc2767a450/OouiWrapper/packages/Newtonsoft.Json.10.0.2/Newtonsoft.Json.10.0.2.nupkg -------------------------------------------------------------------------------- /OouiWrapper/packages/Newtonsoft.Json.10.0.2/lib/net20/Newtonsoft.Json.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-numericoverflow/UIfied/c8f7ca83c538a7e20970eaf0c38451bc2767a450/OouiWrapper/packages/Newtonsoft.Json.10.0.2/lib/net20/Newtonsoft.Json.dll -------------------------------------------------------------------------------- /OouiWrapper/packages/Newtonsoft.Json.10.0.2/lib/net35/Newtonsoft.Json.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-numericoverflow/UIfied/c8f7ca83c538a7e20970eaf0c38451bc2767a450/OouiWrapper/packages/Newtonsoft.Json.10.0.2/lib/net35/Newtonsoft.Json.dll -------------------------------------------------------------------------------- /OouiWrapper/packages/Newtonsoft.Json.10.0.2/lib/net40/Newtonsoft.Json.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-numericoverflow/UIfied/c8f7ca83c538a7e20970eaf0c38451bc2767a450/OouiWrapper/packages/Newtonsoft.Json.10.0.2/lib/net40/Newtonsoft.Json.dll -------------------------------------------------------------------------------- /OouiWrapper/packages/Newtonsoft.Json.10.0.2/lib/net45/Newtonsoft.Json.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-numericoverflow/UIfied/c8f7ca83c538a7e20970eaf0c38451bc2767a450/OouiWrapper/packages/Newtonsoft.Json.10.0.2/lib/net45/Newtonsoft.Json.dll -------------------------------------------------------------------------------- /OouiWrapper/packages/Newtonsoft.Json.10.0.2/lib/netstandard1.0/Newtonsoft.Json.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-numericoverflow/UIfied/c8f7ca83c538a7e20970eaf0c38451bc2767a450/OouiWrapper/packages/Newtonsoft.Json.10.0.2/lib/netstandard1.0/Newtonsoft.Json.dll -------------------------------------------------------------------------------- /OouiWrapper/packages/Newtonsoft.Json.10.0.2/lib/netstandard1.3/Newtonsoft.Json.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-numericoverflow/UIfied/c8f7ca83c538a7e20970eaf0c38451bc2767a450/OouiWrapper/packages/Newtonsoft.Json.10.0.2/lib/netstandard1.3/Newtonsoft.Json.dll -------------------------------------------------------------------------------- /OouiWrapper/packages/Newtonsoft.Json.10.0.2/lib/portable-net40 sl5 win8 wpa81 wp8/Newtonsoft.Json.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-numericoverflow/UIfied/c8f7ca83c538a7e20970eaf0c38451bc2767a450/OouiWrapper/packages/Newtonsoft.Json.10.0.2/lib/portable-net40 sl5 win8 wpa81 wp8/Newtonsoft.Json.dll -------------------------------------------------------------------------------- /OouiWrapper/packages/Newtonsoft.Json.10.0.2/lib/portable-net45 win8 wpa81 wp8/Newtonsoft.Json.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-numericoverflow/UIfied/c8f7ca83c538a7e20970eaf0c38451bc2767a450/OouiWrapper/packages/Newtonsoft.Json.10.0.2/lib/portable-net45 win8 wpa81 wp8/Newtonsoft.Json.dll -------------------------------------------------------------------------------- /OouiWrapper/packages/Newtonsoft.Json.10.0.2/tools/install.ps1: -------------------------------------------------------------------------------- 1 | param($installPath, $toolsPath, $package, $project) 2 | 3 | # open json.net splash page on package install 4 | # don't open if json.net is installed as a dependency 5 | 6 | try 7 | { 8 | $url = "http://www.newtonsoft.com/json/install?version=" + $package.Version 9 | $dte2 = Get-Interface $dte ([EnvDTE80.DTE2]) 10 | 11 | if ($dte2.ActiveWindow.Caption -eq "Package Manager Console") 12 | { 13 | # user is installing from VS NuGet console 14 | # get reference to the window, the console host and the input history 15 | # show webpage if "install-package newtonsoft.json" was last input 16 | 17 | $consoleWindow = $(Get-VSComponentModel).GetService([NuGetConsole.IPowerConsoleWindow]) 18 | 19 | $props = $consoleWindow.GetType().GetProperties([System.Reflection.BindingFlags]::Instance -bor ` 20 | [System.Reflection.BindingFlags]::NonPublic) 21 | 22 | $prop = $props | ? { $_.Name -eq "ActiveHostInfo" } | select -first 1 23 | if ($prop -eq $null) { return } 24 | 25 | $hostInfo = $prop.GetValue($consoleWindow) 26 | if ($hostInfo -eq $null) { return } 27 | 28 | $history = $hostInfo.WpfConsole.InputHistory.History 29 | 30 | $lastCommand = $history | select -last 1 31 | 32 | if ($lastCommand) 33 | { 34 | $lastCommand = $lastCommand.Trim().ToLower() 35 | if ($lastCommand.StartsWith("install-package") -and $lastCommand.Contains("newtonsoft.json")) 36 | { 37 | $dte2.ItemOperations.Navigate($url) | Out-Null 38 | } 39 | } 40 | } 41 | else 42 | { 43 | # user is installing from VS NuGet dialog 44 | # get reference to the window, then smart output console provider 45 | # show webpage if messages in buffered console contains "installing...newtonsoft.json" in last operation 46 | 47 | $instanceField = [NuGet.Dialog.PackageManagerWindow].GetField("CurrentInstance", [System.Reflection.BindingFlags]::Static -bor ` 48 | [System.Reflection.BindingFlags]::NonPublic) 49 | 50 | $consoleField = [NuGet.Dialog.PackageManagerWindow].GetField("_smartOutputConsoleProvider", [System.Reflection.BindingFlags]::Instance -bor ` 51 | [System.Reflection.BindingFlags]::NonPublic) 52 | 53 | if ($instanceField -eq $null -or $consoleField -eq $null) { return } 54 | 55 | $instance = $instanceField.GetValue($null) 56 | 57 | if ($instance -eq $null) { return } 58 | 59 | $consoleProvider = $consoleField.GetValue($instance) 60 | if ($consoleProvider -eq $null) { return } 61 | 62 | $console = $consoleProvider.CreateOutputConsole($false) 63 | 64 | $messagesField = $console.GetType().GetField("_messages", [System.Reflection.BindingFlags]::Instance -bor ` 65 | [System.Reflection.BindingFlags]::NonPublic) 66 | if ($messagesField -eq $null) { return } 67 | 68 | $messages = $messagesField.GetValue($console) 69 | if ($messages -eq $null) { return } 70 | 71 | $operations = $messages -split "==============================" 72 | 73 | $lastOperation = $operations | select -last 1 74 | 75 | if ($lastOperation) 76 | { 77 | $lastOperation = $lastOperation.ToLower() 78 | 79 | $lines = $lastOperation -split "`r`n" 80 | 81 | $installMatch = $lines | ? { $_.StartsWith("------- installing...newtonsoft.json ") } | select -first 1 82 | 83 | if ($installMatch) 84 | { 85 | $dte2.ItemOperations.Navigate($url) | Out-Null 86 | } 87 | } 88 | } 89 | } 90 | catch 91 | { 92 | try 93 | { 94 | $pmPane = $dte2.ToolWindows.OutputWindow.OutputWindowPanes.Item("Package Manager") 95 | 96 | $selection = $pmPane.TextDocument.Selection 97 | $selection.StartOfDocument($false) 98 | $selection.EndOfDocument($true) 99 | 100 | if ($selection.Text.StartsWith("Attempting to gather dependencies information for package 'Newtonsoft.Json." + $package.Version + "'")) 101 | { 102 | # don't show on upgrade 103 | if (!$selection.Text.Contains("Removed package")) 104 | { 105 | $dte2.ItemOperations.Navigate($url) | Out-Null 106 | } 107 | } 108 | } 109 | catch 110 | { 111 | # stop potential errors from bubbling up 112 | # worst case the splash page won't open 113 | } 114 | } 115 | 116 | # still yolo -------------------------------------------------------------------------------- /OouiWrapper/packages/Ooui.0.14.16/.signature.p7s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-numericoverflow/UIfied/c8f7ca83c538a7e20970eaf0c38451bc2767a450/OouiWrapper/packages/Ooui.0.14.16/.signature.p7s -------------------------------------------------------------------------------- /OouiWrapper/packages/Ooui.0.14.16/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-numericoverflow/UIfied/c8f7ca83c538a7e20970eaf0c38451bc2767a450/OouiWrapper/packages/Ooui.0.14.16/Icon.png -------------------------------------------------------------------------------- /OouiWrapper/packages/Ooui.0.14.16/Ooui.0.14.16.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-numericoverflow/UIfied/c8f7ca83c538a7e20970eaf0c38451bc2767a450/OouiWrapper/packages/Ooui.0.14.16/Ooui.0.14.16.nupkg -------------------------------------------------------------------------------- /OouiWrapper/packages/Ooui.0.14.16/lib/netstandard2.0/Ooui.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-numericoverflow/UIfied/c8f7ca83c538a7e20970eaf0c38451bc2767a450/OouiWrapper/packages/Ooui.0.14.16/lib/netstandard2.0/Ooui.dll -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UIfied 2 | 3 | A unified PowerShell DSL for multiple UIs. 4 | 5 | ## Simple DSL 6 | 7 | Write complex UIs the easy way with the UIfied DSL 8 | 9 | $sample = { 10 | $wsb = { 11 | UIWindow -Caption "Title" -Components { 12 | UIStackPanel -Orientation Vertical -Components { 13 | UILabel -Caption "Hello" 14 | UIButton -Caption "Button" -Action { 15 | param($this) 16 | $this.Control.Caption = Get-Date 17 | } 18 | } 19 | } 20 | } 21 | $h = Get-UIHost 22 | $h.ShowFrame($wsb) 23 | } 24 | 25 | Set-UIWpf 26 | Invoke-Command -ScriptBlock $sample 27 | 28 | ## Six UI types supported 29 | 30 | UIFied supports six UI flavors 31 | 32 | - Windows: UIfied creates WPF UIs. 33 | - Console: UIfied uses [ConsoleFramework](https://github.com/elw00d/consoleframework) for console UIs. 34 | - Web: UIfied web support is based on [Ooui](https://github.com/praeclarum/Ooui). 35 | - Material Windows: WPF with Material Design flavor based on [MaterialDesignInXamlToolkit](https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit). 36 | - Material Console: Console with Material Design flavor. You must install [Literation Mono Nerd Font Complete Mono Windows Compatible.ttf](https://github.com/ryanoasis/nerd-fonts) fonts for correct icon visualization. 37 | - Material Web: Web with Material Design flavor 38 | 39 | Write once and use it accross differents UIs. You can switch the target UI framework by simply using a command. 40 | 41 | Set-UICF # Switch to console UI 42 | Set-UIWPF # Switch to Windows Presentation Foundation UI 43 | Set-UIOoui # Switch to Web UI 44 | Set-UIMaterialCF # Switch to Material Design console UI 45 | Set-UIMaterialWPF # Switch to Material Design WPF UI 46 | Set-UIMaterialOoui # Switch to Material Design Web UI 47 | 48 | ![A simple sample running on different UIs](UIfied.Test/SimpleTest.gif) 49 | 50 | ## Controls 51 | 52 | We have implemented these controls: 53 | 54 | - Button 55 | - CheckBox 56 | - Label 57 | - List 58 | - ListColumn 59 | - ListItem 60 | - RadioButton 61 | - RadioGroup 62 | - StackPanel 63 | - TabControl 64 | - TabItem 65 | - TextBox 66 | - Modal 67 | - Timer 68 | - DatePicker 69 | - TimePicker 70 | - Browser 71 | - Autocomplete 72 | - Icon 73 | - Card 74 | - Image 75 | - TextEditor 76 | - Expander 77 | - ComboBox 78 | - Expander 79 | 80 | ## Screenshots 81 | 82 | Material Design console sample 83 | ![Material Design console sample](UIfied.Test/MaterialCF.gif) 84 | 85 | Material Design windows sample 86 | ![Material Design windows sample](UIfied.Test/MaterialWPF.gif) 87 | 88 | Material Design web sample 89 | ![Material Design web sample](UIfied.Test/MaterialOoui.gif) 90 | -------------------------------------------------------------------------------- /UIfied.Test/MaterialCF.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-numericoverflow/UIfied/c8f7ca83c538a7e20970eaf0c38451bc2767a450/UIfied.Test/MaterialCF.gif -------------------------------------------------------------------------------- /UIfied.Test/MaterialOoui.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-numericoverflow/UIfied/c8f7ca83c538a7e20970eaf0c38451bc2767a450/UIfied.Test/MaterialOoui.gif -------------------------------------------------------------------------------- /UIfied.Test/MaterialWPF.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-numericoverflow/UIfied/c8f7ca83c538a7e20970eaf0c38451bc2767a450/UIfied.Test/MaterialWPF.gif -------------------------------------------------------------------------------- /UIfied.Test/SimpleTest.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-numericoverflow/UIfied/c8f7ca83c538a7e20970eaf0c38451bc2767a450/UIfied.Test/SimpleTest.gif -------------------------------------------------------------------------------- /UIfied.Test/SimpleTest.ps1: -------------------------------------------------------------------------------- 1 | Import-Module UIfied 2 | 3 | $sample = { 4 | $wsb = { 5 | UIWindow -Caption "Title" -Components { 6 | UIStackPanel -Orientation Vertical -Components { 7 | UILabel -Caption "Hello" 8 | UIButton -Caption "Button" -Action { 9 | param($this) 10 | $this.Control.Caption = Get-Date 11 | } 12 | } 13 | } 14 | } 15 | $h = Get-UIHost 16 | $h.ShowFrame($wsb) 17 | } 18 | 19 | Set-UIWpf 20 | Invoke-Command -ScriptBlock $sample 21 | 22 | #Set-UICF 23 | #Invoke-Command -ScriptBlock $sample 24 | # 25 | #Set-UIOoui 26 | #Invoke-Command -ScriptBlock $sample 27 | -------------------------------------------------------------------------------- /UIfied.Test/UIfiedSample.ps1: -------------------------------------------------------------------------------- 1 |  2 | Import-Module "$PSScriptRoot\..\UIfied" 3 | 4 | $wsb = { 5 | UIWindow -Caption "My Title" -Loaded { 6 | param ($this) 7 | $this.Form.Caption = $this.Form.Caption + " Loaded at " + (Get-Date).ToString() + " " + $PID 8 | } -Components { 9 | UIStackPanel -Orientation Vertical -Components { 10 | UITabControl -Components { 11 | UITabItem -Caption "Controls" -Components { 12 | UIStackPanel -Orientation Horizontal -Components { 13 | UIStackPanel -Orientation Vertical -Components { 14 | UICard -Caption "Labels" -Components { 15 | UILabel -Caption "This is a Label" -Name MyLabel 16 | } 17 | UICard -Caption Buttons -Components { 18 | UILabel -Caption "Button:" -Name ButtonLabel 19 | UIButton -Caption "My Button" -Name MyButton -Action { 20 | param($this) 21 | $this.Form.ButtonLabel.Caption = Get-Date 22 | $this.Form.MyButton.Icon = (Get-UIIcon -Kind "add") 23 | $this.Form.MyCombo.Text = "1" 24 | } -Icon (UIIcon -Kind "delete") 25 | } 26 | UICard -Caption TextBoxes -Components { 27 | UILabel -Caption "TextBox:" -Name TextBoxLabel 28 | UITextBox -Name MyTextBox -Change { 29 | param($this) 30 | $this.Form.TextBoxLabel.Caption = $this.Control.Text 31 | } 32 | UILabel -Caption "TextAlignment" 33 | UITextBox -TextAlignment Right 34 | UILabel -Caption "Pattern" 35 | UITextBox -TextAlignment Right -Text "0" -DefaultText "0" -Pattern '^\d+$' 36 | UILabel -Caption "Integer" 37 | UIInteger 38 | UILabel -Caption "Double" 39 | UIDouble 40 | } 41 | UICard -Caption ComboBox -Components { 42 | UIComboBox -Text 2 -Name MyCombo -Components { 43 | Get-UIComboBoxItem -Id 1 -Caption Hello 44 | Get-UIComboBoxItem -Id 2 -Caption GoodBy 45 | } -Change { 46 | param($this) 47 | $this.Form.TextBoxLabel.Caption = $this.Control.Text 48 | } 49 | } 50 | } 51 | UIStackPanel -Orientation Vertical -Components { 52 | UICard -Caption Pickers -Components { 53 | UILabel -Caption "DatePicker:" 54 | UIDatePicker -Value ([DateTime]::Today) -Name MyDatePicker 55 | UILabel -Caption "TimePicker:" 56 | UITimePicker -Value "15:27" -Name MyTimePicker 57 | } 58 | UICard -Caption Toogles -Components { 59 | UILabel -Caption "RadioButton:" 60 | UIRadioButton -Caption "Fish" 61 | UILabel -Caption "CheckBox:" 62 | UICheckBox -Caption "Ketchup" 63 | } 64 | UICard -Caption TextEditor -Components { 65 | UILabel -Caption "TextEditor:" 66 | UITextEditor -Text "Change this text`nLine2" -Height 5 -Width 30 67 | } 68 | UICard -Caption Password -Components { 69 | UIPassword -Change { 70 | param($this) 71 | $this.Form.TextBoxLabel.Caption = $this.Control.Text 72 | } 73 | } 74 | } 75 | } 76 | } 77 | UITabItem -Caption "Containers" -Components { 78 | UIStackPanel -Orientation Horizontal -Components { 79 | UICard -Caption "Lists" -Components { 80 | UIList -Name Grid -Columns { 81 | UIListColumn -Title "Column 1" 82 | UIListColumn -Title "Column 2" 83 | } -Items { 84 | UIListItem -Components { 85 | UILabel -Caption "Cell 1,1" 86 | UICheckBox -Caption "Cell 1,2" 87 | } 88 | UIListItem -Components { 89 | UILabel -Caption "Cell 2,1" 90 | UICheckBox -Caption "Cell 2,2" 91 | } 92 | UIListItem -Components { 93 | UILabel -Caption "Cell 3,1" 94 | UICheckBox -Caption "Cell 3,2" 95 | } 96 | } 97 | } 98 | UICard -Caption "RadioGroups" -Components { 99 | UIRadioGroup -Components { 100 | UIRadioButton -Caption "Fish" 101 | UIRadioButton -Caption "Meat" 102 | } 103 | } 104 | UICard -Caption "DropDownMenus" -Components { 105 | UIDropDownMenu -Caption "my dropdown" -Components { 106 | UIMenuItem -Caption "Menu 1" -Action { 107 | param($this) 108 | $this.Control.Caption = Get-Date 109 | } 110 | UIMenuItem -Caption "Menu 2" -Action { 111 | param($this) 112 | $this.Control.Caption = Get-Date 113 | } 114 | } 115 | } 116 | #UICard -Caption "Expanders" -Components { 117 | # UIExpander -Caption "My expander" -Components { 118 | # UILabel -Caption "Expander content" 119 | # } 120 | #} 121 | } 122 | UIStackPanel -Orientation Horizontal -Components { 123 | UICard -Caption "Autocompletes" -Components { 124 | UILabel -Caption "Press down key to view options" 125 | UIAutoComplete -Text "AB" -ItemsRequested { 126 | param($this) 127 | Get-UIAutoCompleteItem -Id "id1" -Text ($this.Text + " sample") 128 | Get-UIAutoCompleteItem -Id "id2" -Text ([DateTime]::Now.ToString()) 129 | Get-UIAutoCompleteItem -Id "id3" -Text ($this.Text + " sample2") 130 | Get-UIAutoCompleteItem -Id "id4" -Text ([DateTime]::Now.ToString()) 131 | } 132 | } 133 | UICard -Caption Cards -Name MyCard -Icon (UIIcon -Kind delete) -Components { 134 | UILabel -Caption "Card body content here" 135 | UIButton -Caption "Change" -Action { 136 | param($this) 137 | $this.Form.MyCard.Caption = "Hello Card" 138 | $this.Form.MyCard.Icon = (Get-UIIcon -Kind add) 139 | } 140 | } 141 | UICard -Caption "Modals" -Components { 142 | UILabel -Caption "Click to show modal form" 143 | UIButton -Caption "Show" -Action { 144 | param($this) 145 | $this.Form.MyModal.Show() 146 | } 147 | UIModal -Name MyModal -Title "MY TITLE" -Components { 148 | UIStackPanel -Orientation Vertical -Components { 149 | UICheckBox -Caption "Ketchup" 150 | UICheckBox -Caption "Mayo" 151 | UIButton -Caption "Hide" -Action { 152 | param($this) 153 | $this.Form.MyModal.Hide() 154 | } 155 | } 156 | } 157 | } 158 | } 159 | } 160 | UITabItem -Caption "Browser" -Components { 161 | UIBrowser -Name Browser -Columns { 162 | UIListColumn -Title "Id" -Name Id 163 | UIListColumn -Title "Description" -Name Description 164 | } -Edit { 165 | param($this) 166 | $this.Form.ButtonLabel.Caption = $this.Control.CurrentRow | ConvertTo-Json 167 | } -AddNew { 168 | param($this) 169 | $this.Control.Data += @{Id = "dd"; Description = Get-Date } 170 | $this.Control.Refresh() 171 | } -Data @( 172 | 1..203 | ForEach-Object { @{Id = $_; Description = "Desc $_ jkdf kjafsd j fdas jfas jfas djaf sj "} } 173 | ) -PageRows 10 174 | } 175 | UITabItem -Caption "Others" -Components { 176 | UIStackPanel -Orientation Horizontal -Components { 177 | UICard -Caption "Images" -Components { 178 | UILabel -Caption "Image samples" 179 | UIDropDownMenu -Caption "Select Image" -Components { 180 | UIMenuItem -Caption "PowerShell Logo" -Action { 181 | param($this) 182 | $this.Form.ImageSource.Text = "https://deow9bq0xqvbj.cloudfront.net/image-logo/1769310/powershell.png" 183 | } 184 | UIMenuItem -Caption "Android logo" -Action { 185 | param($this) 186 | $this.Form.ImageSource.Text = "https://logovector.net/wp-content/uploads/2010/06/291431-android-2-logo.png" 187 | } 188 | UIMenuItem -Caption "Apple logo" -Action { 189 | param($this) 190 | $this.Form.ImageSource.Text = "http://cdn.wccftech.com/wp-content/uploads/2013/09/Apple-logo1.jpg" 191 | } 192 | } 193 | UILabel -Caption "Image source" 194 | UITextBox -Name ImageSource 195 | UIButton -Caption "Show" -Action { 196 | param($this) 197 | $this.Form.MyImage.Source = $this.Form.ImageSource.Text 198 | } 199 | UIImage -Name MyImage -Width 300 200 | } 201 | UICard -Caption Timers -Components { 202 | UILabel -Caption "Timer:" -Name TimerLabel 203 | UITimer -Name Timer -Elapsed { 204 | param ($this) 205 | $this.Form.TimerLabel.Caption = Get-Date 206 | } 207 | UIButton -Caption "Run" -Name TimerStart -Action { 208 | param($this) 209 | $this.Form.Timer.Start() 210 | $this.Control.Enable = $false 211 | $this.Form.TimerStop.Enable = $true 212 | } 213 | UIButton -Caption "Stop" -Name TimerStop -Action { 214 | param($this) 215 | $this.Form.Timer.Stop() 216 | $this.Control.Enable = $false 217 | $this.Form.TimerStart.Enable = $true 218 | } 219 | } 220 | UICard -Caption "FileUpload" -Components { 221 | UIFileUpload -Caption "File Upload" 222 | } 223 | } 224 | } 225 | } 226 | } 227 | } 228 | } 229 | 230 | Set-UIOoui 231 | 232 | $h = Get-UIHost 233 | #cls 234 | $h.ShowFrame($wsb) 235 | 236 | -------------------------------------------------------------------------------- /UIfied/Bin/Binding.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-numericoverflow/UIfied/c8f7ca83c538a7e20970eaf0c38451bc2767a450/UIfied/Bin/Binding.dll -------------------------------------------------------------------------------- /UIfied/Bin/ConsoleFramework.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-numericoverflow/UIfied/c8f7ca83c538a7e20970eaf0c38451bc2767a450/UIfied/Bin/ConsoleFramework.dll -------------------------------------------------------------------------------- /UIfied/Bin/MaterialDesignColors.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-numericoverflow/UIfied/c8f7ca83c538a7e20970eaf0c38451bc2767a450/UIfied/Bin/MaterialDesignColors.dll -------------------------------------------------------------------------------- /UIfied/Bin/MaterialDesignThemes.Wpf.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-numericoverflow/UIfied/c8f7ca83c538a7e20970eaf0c38451bc2767a450/UIfied/Bin/MaterialDesignThemes.Wpf.dll -------------------------------------------------------------------------------- /UIfied/Bin/Newtonsoft.Json.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-numericoverflow/UIfied/c8f7ca83c538a7e20970eaf0c38451bc2767a450/UIfied/Bin/Newtonsoft.Json.dll -------------------------------------------------------------------------------- /UIfied/Bin/Ooui.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-numericoverflow/UIfied/c8f7ca83c538a7e20970eaf0c38451bc2767a450/UIfied/Bin/Ooui.dll -------------------------------------------------------------------------------- /UIfied/Bin/OouiWrapper.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-numericoverflow/UIfied/c8f7ca83c538a7e20970eaf0c38451bc2767a450/UIfied/Bin/OouiWrapper.dll -------------------------------------------------------------------------------- /UIfied/Bin/Xaml.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-numericoverflow/UIfied/c8f7ca83c538a7e20970eaf0c38451bc2767a450/UIfied/Bin/Xaml.dll -------------------------------------------------------------------------------- /UIfied/LICENSE.txt: -------------------------------------------------------------------------------- 1 | UIfied 2 | 3 | Copyright (c) Microsoft Corporation 4 | 5 | All rights reserved. 6 | 7 | MIT License 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the ""Software""), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all 17 | copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. -------------------------------------------------------------------------------- /UIfied/LoadCFBinaries.ps1: -------------------------------------------------------------------------------- 1 | [Reflection.Assembly]::Load([System.IO.File]::ReadAllBytes("$PSScriptRoot\Bin\Binding.dll")) 2 | [Reflection.Assembly]::Load([System.IO.File]::ReadAllBytes("$PSScriptRoot\Bin\ConsoleFramework.dll")) 3 | [Reflection.Assembly]::Load([System.IO.File]::ReadAllBytes("$PSScriptRoot\Bin\Xaml.dll")) 4 | 5 | Add-Type -AssemblyName System.Drawing 6 | -------------------------------------------------------------------------------- /UIfied/LoadMaterialWPFBinaries.ps1: -------------------------------------------------------------------------------- 1 | Add-Type -AssemblyName PresentationFramework 2 | [Reflection.Assembly]::Load([System.IO.File]::ReadAllBytes("$PSScriptRoot\Bin\MaterialDesignColors.dll")) 3 | [Reflection.Assembly]::Load([System.IO.File]::ReadAllBytes("$PSScriptRoot\Bin\MaterialDesignThemes.Wpf.dll")) -------------------------------------------------------------------------------- /UIfied/LoadOouiBinaries.ps1: -------------------------------------------------------------------------------- 1 | [Reflection.Assembly]::Load([System.IO.File]::ReadAllBytes("$PSScriptRoot\Bin\Newtonsoft.Json.dll")) 2 | [Reflection.Assembly]::Load([System.IO.File]::ReadAllBytes("$PSScriptRoot\Bin\Ooui.dll")) 3 | [Reflection.Assembly]::Load([System.IO.File]::ReadAllBytes("$PSScriptRoot\Bin\OouiWrapper.dll")) 4 | -------------------------------------------------------------------------------- /UIfied/LoadWPFBinaries.ps1: -------------------------------------------------------------------------------- 1 | Add-Type -AssemblyName PresentationFramework -------------------------------------------------------------------------------- /UIfied/UIfied.psd1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-numericoverflow/UIfied/c8f7ca83c538a7e20970eaf0c38451bc2767a450/UIfied/UIfied.psd1 -------------------------------------------------------------------------------- /UIfied/UIfied.psm1: -------------------------------------------------------------------------------- 1 | . "$PSScriptRoot\UIfiedBase.ps1" 2 | . "$PSScriptRoot\UIfiedDSL.ps1" 3 | . "$PSScriptRoot\LoadWpfBinaries.ps1" 4 | . "$PSScriptRoot\UIfiedWpfBase.ps1" 5 | . "$PSScriptRoot\LoadCfBinaries.ps1" 6 | . "$PSScriptRoot\UIfiedCfBase.ps1" 7 | . "$PSScriptRoot\LoadOouiBinaries.ps1" 8 | . "$PSScriptRoot\UIfiedOouiBase.ps1" 9 | . "$PSScriptRoot\UIfiedMaterialOouiBase.ps1" 10 | . "$PSScriptRoot\LoadMaterialWPFBinaries.ps1" 11 | . "$PSScriptRoot\UIfiedMaterialCFBase.ps1" 12 | . "$PSScriptRoot\UIfiedMaterialWPFBase.ps1" 13 | -------------------------------------------------------------------------------- /UIfied/UIfiedBase.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Collections.Generic 2 | using namespace System.Management.Automation.Language 3 | 4 | #region UI Type selection 5 | 6 | enum UIType { 7 | WPF 8 | CF 9 | Ooui 10 | MaterialWPF 11 | MaterialOoui 12 | MaterialCF 13 | } 14 | 15 | class UIConfig { 16 | static [UIType] $UIType = [UIType]::MaterialWPF 17 | } 18 | 19 | function Set-UIType { 20 | [CmdletBinding(SupportsShouldProcess)] 21 | param ( 22 | [UIType] $Type 23 | ) 24 | if ($PSCmdlet.ShouldProcess('TARGET')) { 25 | [UIConfig]::UIType = $Type 26 | } 27 | } 28 | 29 | function Set-UICF { 30 | [CmdletBinding(SupportsShouldProcess)] 31 | param ( 32 | ) 33 | Set-UIType -Type ([UIType]::CF) 34 | } 35 | 36 | function Set-UIWPF { 37 | [CmdletBinding(SupportsShouldProcess)] 38 | param ( 39 | ) 40 | Set-UIType -Type ([UIType]::WPF) 41 | } 42 | 43 | function Set-UIOoui { 44 | [CmdletBinding(SupportsShouldProcess)] 45 | param ( 46 | ) 47 | Set-UIType -Type ([UIType]::Ooui) 48 | } 49 | 50 | function Set-UIMaterialWPF { 51 | [CmdletBinding(SupportsShouldProcess)] 52 | param ( 53 | ) 54 | Set-UIType -Type ([UIType]::MaterialWPF) 55 | } 56 | 57 | function Set-UIMaterialOoui { 58 | [CmdletBinding(SupportsShouldProcess)] 59 | param ( 60 | ) 61 | Set-UIType -Type ([UIType]::MaterialOoui) 62 | } 63 | 64 | function Set-UIMaterialCF { 65 | [CmdletBinding(SupportsShouldProcess)] 66 | param ( 67 | ) 68 | Set-UIType -Type ([UIType]::MaterialCF) 69 | } 70 | 71 | #endregion 72 | 73 | #region UI Constructs 74 | 75 | class UIElement { 76 | hidden [ScriptBlock] $AddNativeUIChild = { param ([UIElement] $element) } 77 | hidden [ScriptBlock] $RemoveNativeUIChild = { param ([UIElement] $element) } 78 | hidden [ScriptBlock] $ShowError = { param ([Object] $errorObject) } 79 | hidden [int] $MaxErrors = 3 80 | hidden [Object] $NativeUI = $null 81 | [List[UIElement]] $Children = [List[UIElement]]::new() 82 | [WindowBase] $Form = $null 83 | [UIElement] $Parent = $null 84 | [String] $Name = "" 85 | 86 | UIElement() { 87 | } 88 | 89 | [void] AddChild([UIElement] $element) { 90 | $this.Children.Add($element) 91 | $element.SetForm($this.Form) 92 | $element.SetParent($this) 93 | Invoke-Command -ScriptBlock $this.AddNativeUIChild -ArgumentList $element 94 | } 95 | 96 | [void] RemoveChild([UIElement] $element) { 97 | Invoke-Command -ScriptBlock $this.RemoveNativeUIChild -ArgumentList $element 98 | $this.Children.Remove($element) | Out-Null 99 | } 100 | 101 | hidden [void] SetForm([WindowBase] $form) { 102 | if ($null -ne $form) { 103 | $this.Form = $form 104 | $this.Children | ForEach-Object { 105 | $_.SetForm($form) 106 | Add-Member -InputObject $_.NativeUI -Name Form -MemberType NoteProperty -Value $form 107 | } 108 | if ($this.Name -ne "") { 109 | $nameMember = Get-Member -InputObject $form -Name $this.Name -MemberType NoteProperty 110 | if ($null -eq $nameMember) { 111 | Add-Member -InputObject $form -Name $this.Name -MemberType NoteProperty -Value $this 112 | } 113 | } 114 | } 115 | } 116 | 117 | hidden [void] SetParent([UIElement] $parent) { 118 | if ($null -ne $parent) { 119 | $this.Parent = $parent 120 | } 121 | } 122 | 123 | hidden [void] WrapProperty([String] $elementPropertyName, [String] $nativeUIProperty) { 124 | $this.WrapProperty($elementPropertyName, $nativeUIProperty, "NativeUI") 125 | } 126 | 127 | hidden [void] WrapProperty([String] $elementPropertyName, [String] $nativeUIProperty, [String] $nativeUIName) { 128 | if (-not $this.IsValidName($elementPropertyName) -or -not $this.IsValidName($nativeUIProperty) -or -not $this.IsValidName($nativeUIName)) { 129 | return 130 | } 131 | Add-Member -InputObject $this -Name $elementPropertyName -MemberType ScriptProperty ` 132 | -Value ([ScriptBlock]::Create("`$this.$nativeUIName.$nativeUIProperty")) ` 133 | -SecondValue ([ScriptBlock]::Create("`$this.$nativeUIName.$nativeUIProperty = `$args[0]")) 134 | } 135 | 136 | hidden [void] WrapNegatedProperty([String] $elementPropertyName, [String] $nativeUIProperty) { 137 | if (-not $this.IsValidName($elementPropertyName) -or -not $this.IsValidName($nativeUIProperty)) { 138 | return 139 | } 140 | Add-Member -InputObject $this -Name $elementPropertyName -MemberType ScriptProperty ` 141 | -Value ([ScriptBlock]::Create("-not `$this.NativeUI.$nativeUIProperty")) ` 142 | -SecondValue ([ScriptBlock]::Create("`$this.NativeUI.$nativeUIProperty = -not `$args[0]")) 143 | } 144 | 145 | hidden [void] SetNativeUI([Object] $nativeUI) { 146 | $this.NativeUI = $nativeUI 147 | Add-Member -InputObject $NativeUI -Name Control -MemberType NoteProperty -Value $this -Force 148 | Add-Member -InputObject $this -Name Control -MemberType NoteProperty -Value $this -Force 149 | } 150 | 151 | hidden [bool] IsValidScript([ScriptBlock] $scriptBlock) { 152 | $isValid = $true 153 | $commands = $scriptBlock.Ast.FindAll({ param ($o) $o -is [CommandAst] }, $true) 154 | $commands | ForEach-Object { 155 | $commandName = $_.CommandElements[0].Value 156 | $command = Get-Command -Name $commandName -ErrorAction SilentlyContinue 157 | if ($null -eq $command) { 158 | $isValid = $false 159 | } 160 | } 161 | return $isValid 162 | } 163 | 164 | hidden [void] AddProperty([String]$propertyName) { 165 | if (-not $this.IsValidName($propertyName)) { 166 | return 167 | } 168 | $memberName = "_$propertyName" 169 | Add-Member -InputObject $this -Name $memberName -MemberType NoteProperty -Value {} 170 | Add-Member -InputObject $this -Name $propertyName -MemberType ScriptProperty -Value ( 171 | [ScriptBlock]::Create("`$this.$memberName") 172 | ) -SecondValue ( 173 | [ScriptBlock]::Create("`$this.$memberName = `$args[0]") 174 | ) 175 | } 176 | 177 | hidden [void] AddScriptBlockProperty([String]$propertyName) { 178 | if (-not $this.IsValidName($propertyName)) { 179 | return 180 | } 181 | $memberName = "_$propertyName" 182 | Add-Member -InputObject $this -Name $memberName -MemberType NoteProperty -Value {} 183 | Add-Member -InputObject $this -Name $propertyName -MemberType ScriptProperty -Value ( 184 | [ScriptBlock]::Create("`$this.$memberName") 185 | ) -SecondValue ( 186 | [ScriptBlock]::Create(" 187 | if (`$this.IsValidScript(`$args[0])) { 188 | `$this.$memberName = `$args[0] 189 | } else { 190 | `$this.$memberName = {} 191 | `$this.Visible = `$false 192 | } 193 | ") 194 | ) 195 | } 196 | 197 | hidden [bool] IsValidName([String]$name) { 198 | return ($name -match '(^[a-zA-Z_$][a-zA-Z_$0-9]*$)') 199 | } 200 | 201 | hidden [void] InvokeTrappableCommand([ScriptBlock] $ScriptBlock, [Object[]] $ArgumentList) { 202 | try { 203 | Invoke-Command -ScriptBlock $ScriptBlock -ArgumentList $ArgumentList 204 | } catch { 205 | $Global:SyncHash.Errors += $_ 206 | Invoke-Command -ScriptBlock $this.ShowError -ArgumentList $_ 207 | if ($Global:SyncHash.Errors.Count -gt $this.MaxErrors) { 208 | $this.Form.Close() 209 | } 210 | } 211 | } 212 | 213 | [void] AddNoteProperty([String]$propertyName, $value) { 214 | if (-not $this.IsValidName($propertyName)) { 215 | return 216 | } 217 | Add-Member -InputObject $this -Name $propertyName -MemberType NoteProperty -Value $value 218 | } 219 | } 220 | 221 | class WindowBase : UIElement { 222 | 223 | WindowBase() { 224 | $this.Form = $this 225 | } 226 | 227 | [void] Close() { 228 | $this.NativeUI.Close() 229 | } 230 | } 231 | 232 | class UIHost { 233 | 234 | [void] ShowFrame([WindowBase]$window) { 235 | } 236 | 237 | } 238 | 239 | enum Orientation { 240 | Horizontal 241 | Vertical 242 | } 243 | 244 | enum TextAlignment { 245 | Left 246 | Right 247 | } 248 | 249 | class ListItem { 250 | [List[UIElement]] $Children = [List[UIElement]]::new() 251 | 252 | ListItem() { 253 | } 254 | 255 | [void] AddChild([UIElement] $element) { 256 | $this.Children.Add($element) 257 | } 258 | 259 | [void] RemoveChild([UIElement] $element) { 260 | $this.Children.Remove($element) 261 | } 262 | } 263 | 264 | class AutoCompleteItem { 265 | [String] $Id 266 | [String] $Text 267 | } 268 | 269 | #endregion 270 | 271 | #region UI Icons 272 | 273 | class IconStrinfy { 274 | static [bool] $ShowIcon = $true 275 | 276 | hidden static $Icons = @{ 277 | menu = [string] [char] 02261 # 62542 278 | add = [string] [char] 62541 279 | edit = [string] [char] 61504 280 | delete = [string] [char] 63061 281 | clear = [string] [char] 62567 282 | calendar_today = [string] [char] 62957 283 | query_builder = [string] [char] 63055 284 | left_semi_circle = [string] [char] 57526 285 | right_semi_circle = [string] [char] 57524 286 | first_page = [string] [char] 64255 287 | chevron_left = [string] [char] 61523 288 | chevron_right = [string] [char] 61524 289 | chevron_up = [string] [char] 61559 290 | chevron_down = [string] [char] 61560 291 | last_page = [string] [char] 64256 292 | radio_button_checked = [string] [char] 64611 293 | radio_button_unchecked = [string] [char] 64612 294 | check_box = [string] [char] 62510 295 | check_box_outlined_blank = [string] [char] 64609 296 | keyboard_arrow_down = [string] [char] 63035 297 | keyboard_arrow_up = [string] [char] 63038 298 | } 299 | 300 | static [String] ToIconString([String] $kindName) { 301 | if ([IconStrinfy]::ShowIcon) { 302 | return [IconStrinfy]::Icons."$kindName" 303 | } else { 304 | return "" 305 | } 306 | } 307 | } 308 | 309 | enum IconPosition { 310 | Left 311 | Right 312 | } 313 | 314 | #endregion 315 | 316 | #region UI Utilities 317 | 318 | function Set-UIControlValue { 319 | param ( 320 | $Object, 321 | $Form, 322 | $ControlPrefix = "" 323 | ) 324 | $properties = Get-Member -InputObject $Object -MemberType Properties 325 | $properties | ForEach-Object { 326 | $propertyName = $_.Name 327 | $controlName = $ControlPrefix + $propertyName 328 | $control = $Form."$controlName" 329 | if ($control -ne $null) { 330 | $controlProperties = Get-Member -InputObject $control -MemberType Properties 331 | if ($controlProperties.Name -contains "Text") { 332 | $control.Text = $Object."$propertyName" 333 | } elseif ($controlProperties.Name -contains "Value") { 334 | $control.Value = $Object."$propertyName" 335 | } 336 | } 337 | } 338 | } 339 | 340 | function Get-UIControlValue { 341 | param ( 342 | $Object, 343 | $Form, 344 | $ControlPrefix = "" 345 | ) 346 | $properties = Get-Member -InputObject $Object -MemberType Properties 347 | $properties | ForEach-Object { 348 | $propertyName = $_.Name 349 | $controlName = $ControlPrefix + $propertyName 350 | $control = $Form."$controlName" 351 | if ($control -ne $null) { 352 | $controlProperties = Get-Member -InputObject $control -MemberType Properties 353 | if ($controlProperties.Name -contains "Text") { 354 | $Object."$propertyName" = $control.Text 355 | } elseif ($controlProperties.Name -contains "Value") { 356 | $Object."$propertyName" = $control.Value 357 | } 358 | } 359 | } 360 | $Object 361 | } 362 | 363 | function Get-UIMethod { 364 | param ( 365 | [Parameter(Mandatory = $true, ValueFromPipeline = $true)] 366 | [Object] $InputObject, 367 | [Parameter(Position = 0, Mandatory = $true)] 368 | [String] $Name, 369 | [Parameter(Position = 1)] 370 | [ScriptBlock] $ScriptBlock = { } 371 | ) 372 | process { 373 | Add-Member -InputObject $InputObject -MemberType ScriptMethod -Name $Name -Value $ScriptBlock | Out-Null 374 | $InputObject 375 | } 376 | } 377 | 378 | function Get-UIProperty { 379 | param ( 380 | [Parameter(Mandatory = $true, ValueFromPipeline = $true)] 381 | [Object] $InputObject, 382 | [Parameter(Position = 0, Mandatory = $true)] 383 | [String] $Name, 384 | [ScriptBlock] $Get = { }, 385 | [ScriptBlock] $Set = { } 386 | ) 387 | process { 388 | Add-Member -InputObject $InputObject -Name $Name -MemberType ScriptProperty -Value $Get -SecondValue $Set | Out-Null 389 | $InputObject 390 | } 391 | } 392 | 393 | function Add-UIChild { 394 | param ( 395 | [Parameter(ValueFromPipeline=$true, Mandatory=$true)] 396 | [UIElement] $Child, 397 | [Parameter(Mandatory=$true)] 398 | [UIElement] $Target 399 | ) 400 | process { 401 | $Target.AddChild($Child) 402 | } 403 | } 404 | 405 | function Remove-UIChild { 406 | param ( 407 | [Parameter(ValueFromPipeline=$true, Mandatory=$true)] 408 | [UIElement] $Child, 409 | [Parameter(Mandatory=$true)] 410 | [UIElement] $Target 411 | ) 412 | process { 413 | $Target.RemoveChild($Child) 414 | } 415 | } 416 | 417 | #endregion -------------------------------------------------------------------------------- /UIfied/UIfiedDSL.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Collections.Generic 2 | using namespace System.Management.Automation.Language 3 | 4 | function Get-UIHost { 5 | param ( 6 | [UIType] $UIType = [UIConfig]::UIType 7 | ) 8 | New-Object ($UIType.ToString() + "Host") 9 | } 10 | 11 | function Get-UIWindow { 12 | param ( 13 | [UIType] $UIType = [UIConfig]::UIType, 14 | [ScriptBlock] $Loaded = {}, 15 | [ScriptBlock] $Components = {}, 16 | [String] $Caption = "" 17 | ) 18 | $window = New-Object ($UIType.ToString() + "Window") 19 | $window.Loaded = $Loaded 20 | $window.Caption = $Caption 21 | $childElements = Invoke-Command -ScriptBlock $Components 22 | $childElements | ForEach-Object { 23 | $window.AddChild($_) 24 | } 25 | $window.OnLoaded() 26 | $window 27 | } 28 | 29 | function Get-UIIcon { 30 | param ( 31 | [UIType] $UIType = [UIConfig]::UIType, 32 | [String] $Kind = "", 33 | [String] $Name = "" 34 | ) 35 | $icon = New-Object ($UIType.ToString() + "Icon") 36 | $icon.Kind = $Kind 37 | $icon.Name = $Name 38 | $icon 39 | } 40 | 41 | function Get-UIButton { 42 | param ( 43 | [UIType] $UIType = [UIConfig]::UIType, 44 | [ScriptBlock] $Action = {}, 45 | $Icon = $null, 46 | [String] $Caption = "", 47 | [String] $Name = "" 48 | ) 49 | $button = New-Object ($UIType.ToString() + "Button") 50 | $button.Action = $Action 51 | $button.Icon = $Icon 52 | $button.Caption = $Caption 53 | $button.Name = $Name 54 | $button 55 | } 56 | 57 | function Get-UIStackPanel { 58 | param ( 59 | [UIType] $UIType = [UIConfig]::UIType, 60 | [Orientation] $Orientation = [Orientation]::Vertical, 61 | [ScriptBlock] $Components = {}, 62 | [String] $Name = "" 63 | ) 64 | $stackPanel = New-Object ($UIType.ToString() + "StackPanel") 65 | $stackPanel.Orientation = $Orientation 66 | $stackPanel.Name = $Name 67 | $childElements = Invoke-Command -ScriptBlock $Components 68 | $childElements | ForEach-Object { 69 | $stackPanel.AddChild($_) 70 | } 71 | $stackPanel 72 | } 73 | 74 | function Get-UILabel { 75 | param ( 76 | [UIType] $UIType = [UIConfig]::UIType, 77 | [String] $Caption = "", 78 | [String] $Name = "" 79 | ) 80 | $label = New-Object ($UIType.ToString() + "Label") 81 | $label.Caption = $Caption 82 | $label.Name = $Name 83 | $label 84 | } 85 | 86 | function Get-UITextBox { 87 | param ( 88 | [UIType] $UIType = [UIConfig]::UIType, 89 | [ScriptBlock] $Change = {}, 90 | [String] $Text = "", 91 | [TextAlignment] $TextAlignment = [TextAlignment]::Left, 92 | [String] $Pattern = "", 93 | [String] $DefaultText = "", 94 | [String] $Name = "" 95 | ) 96 | $textBox = New-Object ($UIType.ToString() + "TextBox") 97 | $textBox.Change = $Change 98 | $textBox.Text = $Text 99 | $textBox.TextAlignment = $TextAlignment 100 | $textBox.Pattern = $Pattern 101 | $textBox.DefaultText = $DefaultText 102 | $textBox.Name = $Name 103 | $textBox 104 | } 105 | 106 | function Get-UIPassword { 107 | param ( 108 | [UIType] $UIType = [UIConfig]::UIType, 109 | [ScriptBlock] $Change = {}, 110 | [String] $Text = "", 111 | [String] $Pattern = "", 112 | [String] $DefaultText = "", 113 | [String] $Name = "" 114 | ) 115 | $password = New-Object ($UIType.ToString() + "Password") 116 | $password.Change = $Change 117 | $password.Text = $Text 118 | $password.Pattern = $Pattern 119 | $password.DefaultText = $DefaultText 120 | $password.Name = $Name 121 | $password 122 | } 123 | 124 | function Get-UICheckBox { 125 | param ( 126 | [UIType] $UIType = [UIConfig]::UIType, 127 | [ScriptBlock] $Click = {}, 128 | [Boolean] $IsChecked = $false, 129 | [String] $Caption = "", 130 | [String] $Name = "" 131 | ) 132 | $checkBox = New-Object ($UIType.ToString() + "CheckBox") 133 | $checkBox.Click = $Click 134 | $checkBox.IsChecked = $IsChecked 135 | $checkBox.Caption = $Caption 136 | $checkBox.Name = $Name 137 | $checkBox 138 | } 139 | 140 | function Get-UIRadioButton { 141 | param ( 142 | [UIType] $UIType = [UIConfig]::UIType, 143 | [ScriptBlock] $Click = {}, 144 | [Boolean] $IsChecked = $false, 145 | [String] $Caption = "", 146 | [String] $Name = "" 147 | ) 148 | $radioButton = New-Object ($UIType.ToString() + "RadioButton") 149 | $radioButton.Click = $Click 150 | $radioButton.IsChecked = $IsChecked 151 | $radioButton.Caption = $Caption 152 | $radioButton.Name = $Name 153 | $radioButton 154 | } 155 | 156 | function Get-UIRadioGroup { 157 | param ( 158 | [UIType] $UIType = [UIConfig]::UIType, 159 | [ScriptBlock] $Components = {}, 160 | [String] $Name = "" 161 | ) 162 | $radioGroup = New-Object ($UIType.ToString() + "RadioGroup") 163 | $radioGroup.Name = $Name 164 | $childElements = Invoke-Command -ScriptBlock $Components 165 | $childElements | ForEach-Object { 166 | $radioGroup.AddChild($_) 167 | } 168 | $radioGroup 169 | } 170 | 171 | function Get-UIList { 172 | param ( 173 | [UIType] $UIType = [UIConfig]::UIType, 174 | [ScriptBlock] $Columns = {}, 175 | [ScriptBlock] $Items = {}, 176 | [String] $Name = "" 177 | ) 178 | $list = New-Object ($UIType.ToString() + "List") 179 | $list.Name = $Name 180 | $columnElements = Invoke-Command -ScriptBlock $Columns 181 | $columnElements | ForEach-Object { 182 | $list.AddColumn($_) 183 | } 184 | $itemElements = Invoke-Command -ScriptBlock $Items 185 | $itemElements | ForEach-Object { 186 | $list.AddItem($_) 187 | } 188 | $list 189 | } 190 | 191 | function Get-UIListColumn { 192 | param ( 193 | [UIType] $UIType = [UIConfig]::UIType, 194 | [String] $Title = "", 195 | [String] $Name = "" 196 | ) 197 | $listColumn = New-Object ($UIType.ToString() + "ListColumn") 198 | $listColumn.Name = $Name 199 | $listColumn.Title = $Title 200 | $listColumn 201 | } 202 | 203 | function Get-UIListItem { 204 | param ( 205 | [UIType] $UIType = [UIConfig]::UIType, 206 | [ScriptBlock] $Components = {}, 207 | [String] $Name = "" 208 | ) 209 | $listItem = New-Object ListItem 210 | $childElements = Invoke-Command -ScriptBlock $Components 211 | $childElements | ForEach-Object { 212 | $listItem.AddChild($_) 213 | } 214 | $listItem 215 | } 216 | 217 | function Get-UITabItem { 218 | param ( 219 | [UIType] $UIType = [UIConfig]::UIType, 220 | [ScriptBlock] $Components = {}, 221 | [String] $Caption = "", 222 | [String] $Name = "" 223 | ) 224 | $tabItem = New-Object ($UIType.ToString() + "TabItem") 225 | $tabItem.Caption = $Caption 226 | $tabItem.Name = $Name 227 | $childElements = Invoke-Command -ScriptBlock $Components 228 | $childElements | ForEach-Object { 229 | $tabItem.AddChild($_) 230 | } 231 | $tabItem 232 | } 233 | 234 | function Get-UITabControl { 235 | param ( 236 | [UIType] $UIType = [UIConfig]::UIType, 237 | [ScriptBlock] $Components = {}, 238 | [String] $Name = "" 239 | ) 240 | $tabControl = New-Object ($UIType.ToString() + "TabControl") 241 | $tabControl.Name = $Name 242 | $childElements = Invoke-Command -ScriptBlock $Components 243 | $childElements | ForEach-Object { 244 | $tabControl.AddChild($_) 245 | } 246 | $tabControl 247 | } 248 | 249 | function Get-UIModal { 250 | param ( 251 | [UIType] $UIType = [UIConfig]::UIType, 252 | [String] $Title = "", 253 | [ScriptBlock] $Components = {}, 254 | [String] $Name = "" 255 | ) 256 | $modal = New-Object ($UIType.ToString() + "Modal") 257 | $modal.Name = $Name 258 | $modal.Title = $Title 259 | $childElements = Invoke-Command -ScriptBlock $Components 260 | $childElements | ForEach-Object { 261 | $modal.AddChild($_) 262 | } 263 | $modal 264 | } 265 | 266 | function Get-UITimer { 267 | param ( 268 | [UIType] $UIType = [UIConfig]::UIType, 269 | [ScriptBlock] $Elapsed = {}, 270 | [double] $Interval = 1000, 271 | [String] $Name = "" 272 | ) 273 | $timer = New-Object ($UIType.ToString() + "Timer") 274 | $timer.Elapsed = $Elapsed 275 | $timer.Interval = $Interval 276 | $timer.Name = $Name 277 | $timer 278 | } 279 | 280 | function Get-UIDatePicker { 281 | param ( 282 | [UIType] $UIType = [UIConfig]::UIType, 283 | [ScriptBlock] $Change = {}, 284 | [DateTime] $Value = [DateTime]::Today, 285 | [String] $Name = "" 286 | ) 287 | $datePicker = New-Object ($UIType.ToString() + "DatePicker") 288 | $datePicker.Change = $Change 289 | $datePicker.Value = $Value 290 | $datePicker.Name = $Name 291 | $datePicker 292 | } 293 | 294 | function Get-UITimePicker { 295 | param ( 296 | [UIType] $UIType = [UIConfig]::UIType, 297 | [ScriptBlock] $Change = {}, 298 | [String] $Value = "00:00", 299 | [String] $Name = "" 300 | ) 301 | $timePicker = New-Object ($UIType.ToString() + "TimePicker") 302 | $timePicker.Change = $Change 303 | $timePicker.Value = $Value 304 | $timePicker.Name = $Name 305 | $timePicker 306 | } 307 | 308 | function Get-UIBrowser { 309 | param ( 310 | [UIType] $UIType = [UIConfig]::UIType, 311 | [ScriptBlock] $Columns = {}, 312 | [Object[]] $Data = [Object[]] @(), 313 | [int] $PageRows = 10, 314 | [ScriptBlock] $AddNew = {}, 315 | [ScriptBlock] $Edit = {}, 316 | [ScriptBlock] $Delete = {}, 317 | [String] $Name = "" 318 | ) 319 | $browser = New-Object ($UIType.ToString() + "Browser") 320 | $browser.Name = $Name 321 | $columnElements = Invoke-Command -ScriptBlock $Columns 322 | $columnElements | ForEach-Object { 323 | $browser.AddColumn($_) 324 | } 325 | $browser.Data = $Data 326 | $browser.PageRows = $PageRows 327 | $browser.CreateList() 328 | $browser.Refresh() 329 | $browser.AddNew = $AddNew 330 | $browser.Edit = $Edit 331 | $browser.Delete = $Delete 332 | $browser 333 | } 334 | 335 | function Get-UIMenuItem { 336 | param ( 337 | [UIType] $UIType = [UIConfig]::UIType, 338 | [ScriptBlock] $Action = {}, 339 | [String] $Caption = "", 340 | [String] $Name = "" 341 | ) 342 | $menuItem = New-Object ($UIType.ToString() + "MenuItem") 343 | $menuItem.Action = $Action 344 | $menuItem.Caption = $Caption 345 | $menuItem.Name = $Name 346 | $menuItem 347 | } 348 | 349 | function Get-UIDropDownMenu { 350 | param ( 351 | [UIType] $UIType = [UIConfig]::UIType, 352 | [String] $Caption = "", 353 | [ScriptBlock] $Components = {}, 354 | [String] $Name = "" 355 | ) 356 | $dropDownMenu = New-Object ($UIType.ToString() + "DropDownMenu") 357 | $dropDownMenu.Caption = $Caption 358 | $dropDownMenu.Name = $Name 359 | $childElements = Invoke-Command -ScriptBlock $Components 360 | $childElements | ForEach-Object { 361 | $dropDownMenu.AddChild($_) 362 | } 363 | $dropDownMenu 364 | } 365 | 366 | function Get-UIAutoComplete { 367 | param ( 368 | [UIType] $UIType = [UIConfig]::UIType, 369 | [ScriptBlock] $ItemsRequested = {param($this) "sample" }, 370 | [String] $Text = "", 371 | [String] $Name = "" 372 | ) 373 | $autoComplete = New-Object ($UIType.ToString() + "AutoComplete") 374 | #$autoComplete.Text = $Text 375 | $autoComplete.Name = $Name 376 | $autoComplete.ItemsRequested = $ItemsRequested 377 | $autoComplete 378 | } 379 | 380 | function Get-UIAutoCompleteItem { 381 | param ( 382 | [String] $Id, 383 | [String] $Text 384 | ) 385 | [AutoCompleteItem] @{ Id = $Id; Text = $Text } 386 | } 387 | 388 | function Get-UICard { 389 | param ( 390 | [UIType] $UIType = [UIConfig]::UIType, 391 | $Icon = (Get-UIIcon -Kind add), 392 | [String] $Caption = "", 393 | [ScriptBlock] $Components = {}, 394 | [String] $Name = "" 395 | ) 396 | $card = New-Object ($UIType.ToString() + "Card") 397 | $card.Icon = $Icon 398 | $card.Caption = $Caption 399 | $card.Name = $Name 400 | $childElements = Invoke-Command -ScriptBlock $Components 401 | $childElements | ForEach-Object { 402 | $card.AddChild($_) 403 | } 404 | $card 405 | } 406 | 407 | function Get-UIImage { 408 | param ( 409 | [UIType] $UIType = [UIConfig]::UIType, 410 | [String] $Name = "", 411 | [String] $Source = "", 412 | [int] $Width = 50 413 | ) 414 | $image = New-Object ($UIType.ToString() + "Image") 415 | if ($Source -ne "") { 416 | $image.Source = $Source 417 | } 418 | $image.Name = $Name 419 | $image.Width = $Width 420 | $image 421 | } 422 | 423 | function Get-UITextEditor { 424 | param ( 425 | [UIType] $UIType = [UIConfig]::UIType, 426 | [String] $Text = "", 427 | [Int] $Height = 20, 428 | [Int] $Width = 60, 429 | [String] $Name = "" 430 | ) 431 | $textEditor = New-Object ($UIType.ToString() + "TextEditor") 432 | $textEditor.Text = $Text 433 | $textEditor.Name = $Name 434 | $textEditor.Height = $Height 435 | $textEditor.Width = $Width 436 | $textEditor 437 | } 438 | 439 | function Get-UIExpander { 440 | param ( 441 | [UIType] $UIType = [UIConfig]::UIType, 442 | [ScriptBlock] $Components = {}, 443 | [String] $Caption = "", 444 | [String] $Name = "" 445 | ) 446 | $expander = New-Object ($UIType.ToString() + "Expander") 447 | $expander.Caption = $Caption 448 | $expander.Name = $Name 449 | $childElements = Invoke-Command -ScriptBlock $Components 450 | $childElements | ForEach-Object { 451 | $expander.AddChild($_) 452 | } 453 | $expander 454 | } 455 | 456 | function Get-UIInteger { 457 | param ( 458 | [UIType] $UIType = [UIConfig]::UIType, 459 | [ScriptBlock] $Change = {}, 460 | [String] $Text = "0", 461 | [String] $Name = "" 462 | ) 463 | $integer = New-Object ($UIType.ToString() + "Integer") 464 | $integer.Change = $Change 465 | $integer.Text = $Text 466 | $integer.Name = $Name 467 | $integer 468 | } 469 | 470 | function Get-UIDouble { 471 | param ( 472 | [UIType] $UIType = [UIConfig]::UIType, 473 | [ScriptBlock] $Change = {}, 474 | [String] $Text = "0.00", 475 | [String] $Name = "" 476 | ) 477 | $double = New-Object ($UIType.ToString() + "Double") 478 | $double.Change = $Change 479 | $double.Text = $Text 480 | $double.Name = $Name 481 | $double 482 | } 483 | 484 | function Get-UIComboBoxItem { 485 | param ( 486 | [String] $Id = "", 487 | [String] $Caption = "" 488 | ) 489 | $comboBoxItem = New-Object ($UIType.ToString() + "ComboBoxItem") 490 | $comboBoxItem.Id = $Id 491 | $comboBoxItem.Caption = $Caption 492 | $comboBoxItem 493 | } 494 | 495 | function Get-UIComboBox { 496 | param ( 497 | [UIType] $UIType = [UIConfig]::UIType, 498 | [ScriptBlock] $Change = {}, 499 | [String] $Text = "", 500 | [ScriptBlock] $Components = {}, 501 | [String] $Name = "" 502 | ) 503 | $comboBox = New-Object ($UIType.ToString() + "ComboBox") 504 | $comboBox.Name = $Name 505 | $comboBox.Change = $Change 506 | $comboBox.Text = $Text 507 | $childElements = Invoke-Command -ScriptBlock $Components 508 | $childElements | ForEach-Object { 509 | $comboBox.AddChild($_) 510 | } 511 | $comboBox 512 | } 513 | 514 | function Get-UIFileUpload { 515 | param ( 516 | [UIType] $UIType = [UIConfig]::UIType, 517 | [String] $Caption = "", 518 | [String] $Name = "" 519 | ) 520 | $fileUpload = New-Object ($UIType.ToString() + "FileUpload") 521 | $fileUpload.Name = $Name 522 | $fileUpload.Caption = $Caption 523 | $fileUpload 524 | } -------------------------------------------------------------------------------- /UIfied/UIfiedMaterialCFBase.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Collections.Generic 2 | using namespace System.Reflection 3 | using namespace ConsoleFramework 4 | using namespace ConsoleFramework.Core 5 | using namespace ConsoleFramework.Native 6 | using namespace ConsoleFramework.Controls 7 | using namespace ConsoleFramework.Events 8 | using namespace ConsoleFramework.Rendering 9 | 10 | # Font Creation https://www.calligraphr.com/ 11 | 12 | class MaterialCFHost : CFHost { 13 | } 14 | 15 | class MaterialCFWindow : CFWindow { 16 | 17 | MaterialCFWindow() { 18 | [CFCustomPanel]::DefaultBackgroundColor = [color]::White 19 | } 20 | } 21 | 22 | class MaterialCFStackPanel : CFStackPanel { 23 | } 24 | 25 | class MaterialCFLabel : CFLabel { 26 | 27 | MaterialCFLabel() { 28 | $this.NativeUI.Color = [Color]::DarkGray 29 | } 30 | } 31 | 32 | class MaterialCFIcon : CFIcon { 33 | } 34 | 35 | class MaterialCFButton : CFButton { 36 | 37 | MaterialCFButton() { 38 | $this.NativeUI.Style = "Primary" 39 | } 40 | } 41 | 42 | class MaterialCFTextBox : CFTextBox { 43 | 44 | MaterialCFTextBox() { 45 | $this.NativeUI.Style = "Flat" 46 | } 47 | } 48 | 49 | class MaterialCFPassword : CFTextBox { 50 | 51 | MaterialCFPassword() { 52 | $this.NativeUI.Style = "FlatPassword" 53 | } 54 | } 55 | 56 | class MaterialCFCheckBox : CFCheckBox { 57 | 58 | MaterialCFCheckBox() { 59 | $this.NativeUI.Style = "Flat" 60 | } 61 | } 62 | 63 | class MaterialCFRadioButton : CFRadioButton { 64 | 65 | MaterialCFRadioButton() { 66 | $this.NativeUI.Style = "Flat" 67 | } 68 | } 69 | 70 | class MaterialCFRadioGroup : CFRadioGroup { 71 | } 72 | 73 | class MaterialCFList : CFList { 74 | 75 | MaterialCFList() { 76 | $this.NativeUI.BackgroundColor = [color]::White 77 | } 78 | } 79 | 80 | class MaterialCFListColumn : CFListColumn { 81 | } 82 | 83 | class MaterialCFTabItem : CFTabItem { 84 | } 85 | 86 | class MaterialCFTabControl : CFTabControl { 87 | 88 | MaterialCFTabControl() { 89 | $this.NativeUI.Style = "Flat" 90 | } 91 | } 92 | 93 | class MaterialCFModal : CFModal { 94 | } 95 | 96 | class MaterialCFTimer : CFTimer { 97 | } 98 | 99 | class MaterialCFDatePicker : CFDatePicker { 100 | 101 | MaterialCFDatePicker() { 102 | $this.NativeUI.Style = "Flat" 103 | } 104 | } 105 | 106 | class MaterialCFTimePicker : CFTimePicker { 107 | 108 | MaterialCFTimePicker() { 109 | $this.NativeUI.Style = "Flat" 110 | } 111 | } 112 | 113 | class MaterialCFBrowser : CFBrowser { 114 | 115 | [void] StyleEditionButtons([CFButton] $editButton, [CFButton] $deleteButton, [int] $rowIndex) { 116 | $editButton.Icon = [MaterialCFIcon] @{ Kind = "edit" } 117 | $editButton.NativeUI.Style = "Flat" 118 | $editButton.NativeUI.ForegroundColor = [Color]::DarkGreen 119 | 120 | $deleteButton.Icon = [MaterialCFIcon] @{ Kind = "delete" } 121 | $deleteButton.NativeUI.Style = "Flat" 122 | $deleteButton.NativeUI.ForegroundColor = [Color]::Red 123 | 124 | $editButton.NativeUI.MaxWidth = 5 125 | $deleteButton.NativeUI.MaxWidth = 5 126 | } 127 | 128 | [void] StyleComponents() { 129 | $this.FirstButton.Icon = [MaterialCFIcon] @{ Kind = "first_page" } 130 | $this.FirstButton.NativeUI.Style = "Flat" 131 | $this.FirstButton.NativeUI.ForegroundColor = [Color]::Magenta 132 | 133 | $this.PreviousButton.Icon = [MaterialCFIcon] @{ Kind = "chevron_left" } 134 | $this.PreviousButton.NativeUI.Style = "Flat" 135 | $this.PreviousButton.NativeUI.ForegroundColor = [Color]::Magenta 136 | 137 | $this.NextButton.Icon = [MaterialCFIcon] @{ Kind = "chevron_right" } 138 | $this.NextButton.NativeUI.Style = "Flat" 139 | $this.NextButton.NativeUI.ForegroundColor = [Color]::Magenta 140 | 141 | $this.LastButton.Icon = [MaterialCFIcon] @{ Kind = "last_page" } 142 | $this.LastButton.NativeUI.Style = "Flat" 143 | $this.LastButton.NativeUI.ForegroundColor = [Color]::Magenta 144 | 145 | $this.AddNewButton.Icon = [MaterialCFIcon] @{ Kind = "add" } 146 | $this.AddNewButton.NativeUI.Style = "Pill" 147 | $this.AddNewButton.NativeUI.ForegroundColor = [Color]::Black 148 | $this.AddNewButton.NativeUI.BackgroundColor = [Color]::Green 149 | 150 | $this.FirstButton.NativeUI.MaxWidth = 7 151 | $this.PreviousButton.NativeUI.MaxWidth = 7 152 | $this.NextButton.NativeUI.MaxWidth = 7 153 | $this.LastButton.NativeUI.MaxWidth = 7 154 | $this.AddNewButton.NativeUI.MaxWidth = 7 155 | 156 | $this.AddNewButton.NativeUI.MaxHeight = 4 157 | $this.AddNewButton.NativeUI.Margin = [Thickness]::new(6, 0, 0, 0) 158 | } 159 | } 160 | 161 | class MaterialCFMenuItem : CFMenuItem { 162 | 163 | MaterialCFMenuItem() { 164 | $this.NativeUI.BackgroundColor = [Color]::Magenta 165 | } 166 | } 167 | 168 | class MaterialCFDropDownMenu : CFDropDownMenu { 169 | 170 | MaterialCFDropDownMenu() { 171 | $this.NativeUI.Style = "Primary" 172 | } 173 | } 174 | 175 | class MaterialCFAutoComplete : CFAutoComplete { 176 | 177 | MaterialCFAutoComplete() { 178 | $this.NativeUI.Style = "Flat" 179 | } 180 | 181 | [void] StyleMenuItem($menuItem) { 182 | $menuItem.BackgroundColor = [Color]::Magenta 183 | } 184 | } 185 | 186 | class MaterialCFCard : CFCard { 187 | } 188 | 189 | class MaterialCFImage : CFImage { 190 | } 191 | 192 | class MaterialCFTextEditor : CFTextEditor { 193 | } 194 | 195 | class MaterialCFExpander : CFExpander { 196 | } 197 | 198 | class MaterialCFInteger : CFInteger { 199 | 200 | MaterialCFInteger() { 201 | $this.NativeUI.Style = "Flat" 202 | } 203 | } 204 | 205 | class MaterialCFDouble : CFDouble { 206 | 207 | MaterialCFDouble() { 208 | $this.NativeUI.Style = "Flat" 209 | } 210 | } 211 | 212 | class MaterialCFComboBoxItem : CFComboBoxItem { 213 | 214 | MaterialCFComboBoxItem() { 215 | $this.NativeUI.BackgroundColor = [Color]::Magenta 216 | } 217 | } 218 | 219 | class MaterialCFComboBox : CFComboBox { 220 | 221 | MaterialCFComboBox() { 222 | $this.NativeUI.Style = "Primary" 223 | } 224 | } 225 | 226 | class MaterialCFFileUpload : CFFileUpload { 227 | } -------------------------------------------------------------------------------- /UIfied/UIfiedMaterialOouiBase.ps1: -------------------------------------------------------------------------------- 1 | using namespace Ooui 2 | 3 | class MaterialOouiHost : OouiHost { 4 | 5 | MaterialOouiHost() { 6 | [UI]::HeadHtml = ' 7 | 8 | 9 | 53 | ' 54 | [UI]::BodyFooterHtml = '' 55 | } 56 | } 57 | 58 | class MaterialOouiWindow : OouiWindow { 59 | } 60 | 61 | class MaterialOouiStackPanel : OouiStackPanel { 62 | } 63 | 64 | class MaterialOouiIcon : OouiIcon { 65 | } 66 | 67 | class MaterialOouiLabel : OouiLabel { 68 | } 69 | 70 | class MaterialOouiButton : OouiButton { 71 | } 72 | 73 | class MaterialOouiTextBox : OouiTextBox { 74 | 75 | MaterialOouiTextBox() { 76 | $this.NativeUI.ClassName = "form-control" 77 | } 78 | 79 | } 80 | 81 | class MaterialOouiPassword : OouiPassword { 82 | 83 | MaterialOouiPassword() { 84 | $this.NativeUI.ClassName = "form-control" 85 | } 86 | 87 | } 88 | 89 | class MaterialOouiCheckBox : OouiElement { 90 | 91 | #
92 | # 99 | #
100 | 101 | hidden [Div] $ListNativeUI = [Div]::new() 102 | hidden [Label] $CheckLabelNativeUI = [Label]::new() 103 | hidden [Input] $CheckBoxNativeUI = [Input]::new("CheckBox") 104 | hidden [Span] $SignNativeUI = [span]::new() 105 | hidden [Span] $SpanNativeUI = [span]::new() 106 | hidden [Span] $LabelTextNativeUI = [span]::new() 107 | 108 | MaterialOouiCheckBox() { 109 | $this.ListNativeUI.ClassName = "form-check" 110 | $this.CheckLabelNativeUI.ClassName = "form-check-label" 111 | $this.CheckBoxNativeUI.ClassName = "form-check-input" 112 | $this.SignNativeUI.ClassName = "form-check-sign" 113 | $this.SpanNativeUI.ClassName = "check" 114 | 115 | $this.LabelTextNativeUI.Style.PaddingLeft = "25px" 116 | 117 | $this.ListNativeUI.AppendChild($this.CheckLabelNativeUI) 118 | $this.CheckLabelNativeUI.AppendChild($this.CheckBoxNativeUI) 119 | $this.CheckLabelNativeUI.AppendChild($this.SignNativeUI) 120 | $this.CheckLabelNativeUI.AppendChild($this.LabelTextNativeUI) 121 | $this.SignNativeUI.AppendChild($this.SpanNativeUI) 122 | $this.SetNativeUI($this.ListNativeUI) 123 | 124 | $this.WrapProperty("Caption", "Text", "LabelTextNativeUI") 125 | $this.WrapProperty("IsChecked", "IsChecked", "CheckBoxNativeUI") 126 | $this.AddScriptBlockProperty("Click") 127 | Register-ObjectEvent -InputObject $this.CheckBoxNativeUI -EventName Change -MessageData $this -Action { 128 | $this = $event.MessageData 129 | $this.Control.OnClick() 130 | } | Out-Null 131 | } 132 | 133 | [void] OnClick() { 134 | Invoke-Command -ScriptBlock $this._Click -ArgumentList $this 135 | } 136 | 137 | } 138 | 139 | class MaterialOouiRadioButton : OouiElement { 140 | 141 | #
142 | # 149 | #
150 | 151 | hidden [Div] $ListNativeUI = [Div]::new() 152 | hidden [Label] $CheckLabelNativeUI = [Label]::new() 153 | hidden [Input] $RadioButtonNativeUI = [Input]::new("Radio") 154 | hidden [Span] $SignNativeUI = [span]::new() 155 | hidden [Span] $SpanNativeUI = [span]::new() 156 | hidden [Span] $LabelTextNativeUI = [span]::new() 157 | 158 | MaterialOouiRadioButton() { 159 | $this.ListNativeUI.ClassName = "form-check" 160 | $this.CheckLabelNativeUI.ClassName = "form-check-label" 161 | $this.RadioButtonNativeUI.ClassName = "form-check-input" 162 | $this.SignNativeUI.ClassName = "circle" 163 | $this.SpanNativeUI.ClassName = "check" 164 | 165 | $this.LabelTextNativeUI.Style.PaddingLeft = "25px" 166 | 167 | $this.ListNativeUI.AppendChild($this.CheckLabelNativeUI) 168 | $this.CheckLabelNativeUI.AppendChild($this.RadioButtonNativeUI) 169 | $this.CheckLabelNativeUI.AppendChild($this.SignNativeUI) 170 | $this.CheckLabelNativeUI.AppendChild($this.LabelTextNativeUI) 171 | $this.SignNativeUI.AppendChild($this.SpanNativeUI) 172 | $this.SetNativeUI($this.ListNativeUI) 173 | 174 | $this.WrapProperty("Caption", "Text", "LabelTextNativeUI") 175 | $this.WrapProperty("IsChecked", "IsChecked", "RadioButtonNativeUI") 176 | $this.AddScriptBlockProperty("Click") 177 | Register-ObjectEvent -InputObject $this.RadioButtonNativeUI -EventName Change -MessageData $this -Action { 178 | $this = $event.MessageData 179 | $this.Control.OnClick() 180 | } | Out-Null 181 | } 182 | 183 | [void] OnClick() { 184 | Invoke-Command -ScriptBlock $this._Click -ArgumentList $this 185 | } 186 | 187 | } 188 | 189 | class MaterialOouiRadioGroup : OouiRadioGroup { 190 | } 191 | 192 | class MaterialOouiList : OouiList { 193 | } 194 | 195 | class MaterialOouiListColumn : OouiListColumn { 196 | } 197 | 198 | class MaterialOouiTabItem : OouiTabItem { 199 | } 200 | 201 | class MaterialOouiTabControl : OouiTabControl { 202 | 203 | RefreshStyle() { 204 | $this.List.ClassName = "nav nav-pills nav-pills-primary" 205 | } 206 | 207 | } 208 | 209 | class MaterialOouiModal : OouiModal { 210 | } 211 | 212 | class MaterialOouiTimer : OouiTimer { 213 | } 214 | 215 | class MaterialOouiDatePicker : OouiDatePicker { 216 | } 217 | 218 | class MaterialOouiTimePicker : OouiTimePicker { 219 | } 220 | 221 | class MaterialOouiBrowser : OouiBrowser { 222 | 223 | [void] StyleComponents() { 224 | $this.List.NativeUI.ClassName = "UIList table" 225 | 226 | $this.FirstButton.Icon = [OouiIcon] @{ Kind = "first_page" } 227 | $this.PreviousButton.Icon = [OouiIcon] @{ Kind = "chevron_left" } 228 | $this.NextButton.Icon = [OouiIcon] @{ Kind = "chevron_right" } 229 | $this.LastButton.Icon = [OouiIcon] @{ Kind = "last_page" } 230 | $this.AddNewButton.Icon = [OouiIcon] @{ Kind = "add" } 231 | 232 | $this.FirstButton.NativeUI.ClassName = "btn btn-primary btn-link" 233 | $this.PreviousButton.NativeUI.ClassName = "btn btn-primary btn-link" 234 | $this.NextButton.NativeUI.ClassName = "btn btn-primary btn-link" 235 | $this.LastButton.NativeUI.ClassName = "btn btn-primary btn-link" 236 | $this.AddNewButton.NativeUI.ClassName = "btn btn-fab btn-round btn-lg" 237 | 238 | $this.AddNewButton.NativeUI.Style.BackgroundColor = "lime" 239 | } 240 | 241 | [void] StyleEditionButtons([OouiButton] $editButton, [OouiButton] $deleteButton, [int] $rowIndex) { 242 | $editButton.Icon = [MaterialOouiIcon] @{ Kind = "edit" } 243 | $deleteButton.Icon = [MaterialOouiIcon] @{ Kind = "close" } 244 | 245 | $editButton.NativeUI.ClassName = "btn btn-success btn-link btn-sm" 246 | $deleteButton.NativeUI.ClassName = "btn btn-danger btn-link btn-sm" 247 | $editButton.Parent.NativeUI.ClassName = "td-actions" 248 | } 249 | 250 | } 251 | 252 | class MaterialOouiMenuItem : OouiMenuItem { 253 | } 254 | 255 | class MaterialOouiDropDownMenu : OouiDropDownMenu { 256 | } 257 | 258 | class MaterialOouiAutoComplete : OouiAutoComplete { 259 | } 260 | 261 | class MaterialOouiCard : OouiCard { 262 | } 263 | 264 | class MaterialOouiImage : OouiImage { 265 | } 266 | 267 | class MaterialOouiTextEditor : OouiTextEditor { 268 | } 269 | 270 | class MaterialOouiExpander : OouiElement { 271 | hidden [div] $ExpanderContainerDiv = [div]::new() 272 | hidden [div] $ExpanderHeaderDiv = [div]::new() 273 | hidden [div] $ExpanderButtonDiv = [div]::new() 274 | hidden [OouiStackPanel] $ExpanderBodyDiv = [OouiStackPanel]::new() 275 | hidden [Button] $ExpanderButton = [Button]::new() 276 | hidden [Heading] $Header = [Heading]::new(3) 277 | hidden [Icon] $ExpanderIcon = [Icon]::new() 278 | 279 | MaterialOouiExpander() { 280 | $this.SetNativeUI($this.ExpanderContainerDiv) 281 | $this.ExpanderContainerDiv.AppendChild($this.ExpanderHeaderDiv) 282 | $this.ExpanderContainerDiv.AppendChild($this.ExpanderBodyDiv.NativeUI) 283 | $this.ExpanderHeaderDiv.AppendChild($this.ExpanderButtonDiv) 284 | $this.ExpanderHeaderDiv.AppendChild($this.Header) 285 | $this.ExpanderButtonDiv.AppendChild($this.ExpanderButton) 286 | $this.ExpanderButton.AppendChild($this.ExpanderIcon) 287 | 288 | $this.WrapProperty("Caption", "Text", "Header") 289 | $this.AddNativeUIChild = { 290 | param ( 291 | [OouiElement] $element 292 | ) 293 | $listItem = [Div]::new() 294 | if ($this.ExpanderBodyDiv.Orientation -eq [Orientation]::Horizontal) { 295 | $listItem.Style.float = "left" 296 | } else { 297 | $listItem.Style.clear = "both" 298 | } 299 | $this.ExpanderBodyDiv.NativeUI.AppendChild($listItem) | Out-Null 300 | $listItem.AppendChild($element.NativeUI) | Out-Null 301 | } 302 | Register-ObjectEvent -InputObject $this.ExpanderButton -EventName Click -MessageData $this -Action { 303 | $this = $event.MessageData 304 | $this.ToogleContent() 305 | } | Out-Null 306 | $this.StyleComponents() 307 | } 308 | 309 | [void] StyleComponents() { 310 | $this.ExpanderButton.ClassName = "btn btn-primary btn-link btn-sm" 311 | $this.ExpanderButton.Style.Padding = "0" 312 | $this.ExpanderIcon.ClassName = "material-icons" 313 | $this.ExpanderIcon.Text = "expand_less" 314 | $this.ExpanderButtonDiv.Style.float = "left" 315 | $this.Header.Style.display = "inline" 316 | $this.ExpanderIcon.Style.FontSize = "2rem" 317 | $this.ExpanderHeaderDiv.Style.BorderBottomStyle = "solid" 318 | $this.ExpanderHeaderDiv.Style.BorderBottomColor = "#ddd" 319 | $this.ExpanderHeaderDiv.Style.BorderBottomWidth = "1px" 320 | } 321 | 322 | [void] ToogleContent() { 323 | $this.ExpanderBodyDiv.Visible = -not $this.ExpanderBodyDiv.Visible 324 | if ($this.ExpanderIcon.Text -eq "expand_more") { 325 | $this.ExpanderIcon.Text = "expand_less" 326 | } else { 327 | $this.ExpanderIcon.Text = "expand_more" 328 | } 329 | } 330 | } 331 | 332 | class MaterialOouiInteger : OouiInteger { 333 | 334 | MaterialOouiInteger() { 335 | $this.NativeUI.ClassName = "form-control" 336 | } 337 | } 338 | 339 | class MaterialOouiDouble : OouiDouble { 340 | 341 | MaterialOouiDouble() { 342 | $this.NativeUI.ClassName = "form-control" 343 | } 344 | } 345 | 346 | class MaterialOouiComboBoxItem : OouiComboBoxItem { 347 | } 348 | 349 | class MaterialOouiCombobox : OouiCombobox { 350 | } 351 | 352 | class MaterialOouiFileUpload : OouiFileUpload { 353 | } -------------------------------------------------------------------------------- /UIfied/UIfiedMaterialWPFBase.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Collections.Generic 2 | using namespace System.Windows 3 | using namespace System.Windows.Controls 4 | using namespace MaterialDesignThemes.Wpf 5 | 6 | class MaterialWPFHost : WPFHost { 7 | } 8 | 9 | class MaterialWPFWindow : WPFWindow { 10 | 11 | [void] StyleApplication() { 12 | $uris = 13 | "/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Light.xaml", 14 | "/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml", 15 | "/MaterialDesignColors;component/Themes/Recommended/Primary/MaterialDesignColor.DeepPurple.xaml", 16 | "/MaterialDesignColors;component/Themes/Recommended/Accent/MaterialDesignColor.Lime.xaml" 17 | 18 | $uris | ForEach-Object { 19 | $resourceDictionary = [System.Windows.ResourceDictionary] [System.Windows.Application]::LoadComponent([Uri]::new($_, [System.UriKind]::Relative)) 20 | $this.Application.Resources.MergedDictionaries.Add($resourceDictionary) 21 | } 22 | } 23 | 24 | [void] StyleComponents() { 25 | $this.windowNativeUI.FontFamily = "MaterialDesignFont" 26 | $this.windowNativeUI.SetResourceReference([Control]::BackgroundProperty, "MaterialDesignPaper") 27 | } 28 | } 29 | 30 | class MaterialWPFStackPanel : WPFStackPanel { 31 | } 32 | 33 | class MaterialWPFIcon : WPFElement { 34 | hidden [String] $KindName 35 | hidden $TextInfo 36 | static $IconTranslation = [PSCustomObject] @{ 37 | document = "FileDocument" 38 | } 39 | 40 | MaterialWPFIcon() { 41 | $this.TextInfo = ([System.Globalization.CultureInfo]::new("en-US",$false)).TextInfo 42 | $this.NativeUI = [MaterialDesignThemes.Wpf.PackIcon]::new() 43 | Add-Member -InputObject $this -Name Kind -MemberType ScriptProperty -Value { 44 | $this.KindName 45 | } -SecondValue { 46 | $this.KindName = $args[0] 47 | if ([MaterialWPFIcon]::IconTranslation."$($this.KindName)" -ne $null) { 48 | $this.NativeUI.Kind = [MaterialWPFIcon]::IconTranslation."$($this.KindName)" 49 | } else { 50 | $this.NativeUI.Kind = $this.TextInfo.ToTitleCase($this.KindName).Replace("_", [String]::Empty) 51 | } 52 | } 53 | } 54 | } 55 | 56 | class MaterialWPFLabel : WPFLabel {} 57 | 58 | class MaterialWPFButton : WPFElement { 59 | hidden [stackpanel] $StackPanel = [StackPanel]::new() 60 | hidden [MaterialWPFIcon] $CurrentIcon = $null 61 | hidden [String] $CaptionText = "" 62 | [bool] $RightIcon = $false 63 | 64 | MaterialWPFButton() { 65 | $this.SetNativeUI([Button]::new()) 66 | $this.NativeUI.SetResourceReference([Control]::StyleProperty, "MaterialDesignRaisedButton") 67 | $this.StackPanel.Orientation = [Orientation]::Horizontal 68 | $this.NativeUI.Content = $this.StackPanel 69 | Add-Member -InputObject $this -Name Caption -MemberType ScriptProperty -Value { 70 | $this.CaptionText 71 | } -SecondValue { 72 | $this.CaptionText = $args[0] 73 | $this.RefreshCaption() 74 | } 75 | Add-Member -InputObject $this -Name Icon -MemberType ScriptProperty -Value { 76 | $this.CurrentIcon 77 | } -SecondValue { 78 | $this.CurrentIcon = $args[0] 79 | $this.RefreshCaption() 80 | } 81 | $this.AddScriptBlockProperty("Action") 82 | $this.NativeUI.Add_Click({ $this.Control.OnAction() }) 83 | } 84 | 85 | [void] RefreshCaption() { 86 | $this.StackPanel.Children.Clear() 87 | 88 | $label = [TextBlock]::new() 89 | $label.Text = $this.CaptionText 90 | 91 | if ($this.RightIcon) { 92 | $this.StackPanel.AddChild($label) 93 | if ($this.CurrentIcon -ne $null) { 94 | $this.StackPanel.AddChild($this.CurrentIcon.NativeUI) 95 | } 96 | } else { 97 | if ($this.CurrentIcon -ne $null) { 98 | $this.StackPanel.AddChild($this.CurrentIcon.NativeUI) 99 | } 100 | $this.StackPanel.AddChild($label) 101 | } 102 | } 103 | 104 | [void] OnAction() { 105 | $this.InvokeTrappableCommand($this._Action, $this) 106 | } 107 | 108 | } 109 | 110 | class MaterialWPFTextBox : WPFTextBox { 111 | } 112 | 113 | class MaterialWPFPassword : WPFPassword { 114 | } 115 | 116 | class MaterialWPFCheckBox : WPFCheckBox { 117 | 118 | MaterialWPFCheckBox() { 119 | $this.NativeUI.SetResourceReference([Control]::StyleProperty, "MaterialDesignCheckBox") 120 | } 121 | } 122 | 123 | class MaterialWPFRadioButton : WPFRadioButton { 124 | 125 | MaterialWPFCheckBox() { 126 | $this.NativeUI.SetResourceReference([Control]::StyleProperty, "MaterialDesignRadioButton") 127 | } 128 | } 129 | 130 | class MaterialWPFRadioGroup : WPFRadioGroup { 131 | } 132 | 133 | class MaterialWPFList : WPFList { 134 | } 135 | 136 | class MaterialWPFListColumn : WPFListColumn { 137 | } 138 | 139 | class MaterialWPFTabItem : MaterialWPFStackPanel { 140 | [MaterialWPFRadioButton] $HeaderRadioButton = ([MaterialWPFRadioButton] @{ Caption = "" }) 141 | hidden [String] $CaptionText = "" 142 | 143 | MaterialWPFTabItem() { 144 | Add-Member -InputObject $this.HeaderRadioButton -Name Tab -Value $this -MemberType NoteProperty 145 | 146 | #$this.WrapProperty("Caption", "Caption", "HeaderRadioButton") 147 | Add-Member -InputObject $this -Name Caption -MemberType ScriptProperty -Value { 148 | $this.CaptionText 149 | } -SecondValue { 150 | $this.CaptionText = $args[0] 151 | $this.HeaderRadioButton.Caption = $args[0].ToUpperInvariant() 152 | } 153 | 154 | $this.HeaderRadioButton.NativeUI.SetResourceReference([Control]::StyleProperty, "MaterialDesignTabRadioButton") 155 | $this.Visible = $false 156 | $this.HeaderRadioButton.Click = { 157 | $this.Control.Tab.Parent.HideTabs() 158 | $this.Control.Tab.Visible = $this.Control.IsChecked 159 | } 160 | } 161 | } 162 | 163 | class MaterialWPFTabControl : MaterialWPFStackPanel { 164 | hidden [MaterialWPFStackPanel] $HeadersStackPanel = ([MaterialWPFStackPanel] @{ Orientation = [Orientation]::Horizontal }) 165 | hidden [MaterialWPFStackPanel] $TabsStackPanel = ([MaterialWPFStackPanel] @{ Orientation = [Orientation]::Horizontal }) 166 | hidden $TabItems = [List[MaterialWPFTabItem]]::new() 167 | 168 | MaterialWPFTabControl() { 169 | $this.Orientation = [Orientation]::Vertical 170 | $this.AddChild($this.HeadersStackPanel) 171 | $this.AddChild($this.TabsStackPanel) 172 | $this.AddNativeUIChild = { 173 | param ( 174 | [MaterialWPFTabItem] $element 175 | ) 176 | $this.TabItems.Add($element) 177 | $this.HeadersStackPanel.AddChild($element.HeaderRadioButton) | Out-Null 178 | $this.TabsStackPanel.NativeUI.AddChild($element.NativeUI) | Out-Null 179 | } 180 | $this.RemoveNativeUIChild = { 181 | param ( 182 | [MaterialWPFTabItem] $element 183 | ) 184 | $this.TabItems.Remove($element) 185 | $this.HeadersStackPanel.RemoveChild($element.HeaderRadioButton) | Out-Null 186 | $this.TabsStackPanel.NativeUI.Children.Remove($element.NativeUI) | Out-Null 187 | } 188 | } 189 | 190 | [void] HideTabs() { 191 | $this.TabItems | ForEach-Object { 192 | $_.Visible = $false 193 | } 194 | } 195 | 196 | } 197 | 198 | class MaterialWPFModal : WPFModal { 199 | } 200 | 201 | class MaterialWPFTimer : WPFTimer { 202 | } 203 | 204 | class MaterialWPFDatePicker : WPFElement { 205 | 206 | MaterialWPFDatePicker() { 207 | $datePicker = [DatePicker]::new() 208 | $datePicker.Width = 100 209 | $datePicker.SetResourceReference([Control]::StyleProperty, "MaterialDesignDatePicker") 210 | $this.SetNativeUI($datePicker) 211 | 212 | $this.AddScriptBlockProperty("Change") 213 | $this.NativeUI.Add_SelectedDateChanged({ $this.Control.OnChange() }) 214 | $this.AddScriptBlockProperty("LostFocus") 215 | $this.NativeUI.Add_LostFocus({ $this.Control.OnLostFocus() }) 216 | 217 | $this.WrapProperty("Value", "SelectedDate") 218 | } 219 | 220 | [void] OnChange() { 221 | $this.InvokeTrappableCommand($this._Change, $this) 222 | } 223 | 224 | [void] OnLostFocus() { 225 | $this.InvokeTrappableCommand($this._LostFocus, $this) 226 | } 227 | } 228 | 229 | class MaterialWPFTimePicker : WPFElement { 230 | 231 | MaterialWPFTimePicker() { 232 | $timePicker = [TimePicker]::new() 233 | $timePicker.Width = 100 234 | $timePicker.SetResourceReference([Control]::StyleProperty, "MaterialDesignTimePicker") 235 | $timePicker.Is24Hours = $true 236 | $this.SetNativeUI($timePicker) 237 | 238 | $this.AddScriptBlockProperty("Change") 239 | $this.NativeUI.Add_SelectedTimeChanged({ $this.Control.OnChange() }) 240 | $this.AddScriptBlockProperty("LostFocus") 241 | $this.NativeUI.Add_LostFocus({ $this.Control.OnLostFocus() }) 242 | 243 | $this.WrapProperty("Value", "SelectedTime") 244 | } 245 | 246 | [void] OnChange() { 247 | $this.InvokeTrappableCommand($this._Change, $this) 248 | } 249 | 250 | [void] OnLostFocus() { 251 | $this.InvokeTrappableCommand($this._LostFocus, $this) 252 | } 253 | } 254 | 255 | class MaterialWPFBrowser : WPFBrowser { 256 | 257 | [void] StyleComponents() { 258 | $this.StyleButton($this.FirstButton, "FirstPage", "MaterialDesignIconButton") 259 | $this.StyleButton($this.PreviousButton, "ChevronLeft", "MaterialDesignIconButton") 260 | $this.StyleButton($this.NextButton, "ChevronRight", "MaterialDesignIconButton") 261 | $this.StyleButton($this.LastButton, "LastPage", "MaterialDesignIconButton") 262 | 263 | $this.StyleButton($this.AddNewButton, "Add", "MaterialDesignFloatingActionAccentButton") 264 | 265 | $this.FirstButton.Parent.NativeUI.Margin = "0 -10 0 10" 266 | $this.AddNewButton.NativeUI.Margin = "100 10 10 10" 267 | } 268 | 269 | [void] StyleButton($button, $iconKind, $styleName) { 270 | $button.NativeUI.Content = New-Object PackIcon -Property @{ Kind = $iconKind } 271 | $button.NativeUI.Margin = 0 272 | $button.NativeUI.SetResourceReference([Control]::StyleProperty, $styleName) 273 | } 274 | 275 | [void] StyleEditionButtons([WPFButton] $editButton, [WPFButton] $deleteButton, [int] $rowIndex) { 276 | $editButton.Parent.NativeUI.Background = $this.GetRowBackground($rowIndex) 277 | 278 | $this.StyleButton($editButton, "ModeEdit", "MaterialDesignIconMiniButton") 279 | $this.StyleButton($deleteButton, "WindowClose", "MaterialDesignIconMiniButton") 280 | 281 | $editButton.NativeUI.Background = [System.Windows.Media.Brushes]::Transparent 282 | $deleteButton.NativeUI.Background = [System.Windows.Media.Brushes]::Transparent 283 | 284 | $editButton.NativeUI.Foreground = [System.Windows.Media.Brushes]::Green 285 | $deleteButton.NativeUI.Foreground = [System.Windows.Media.Brushes]::Red 286 | 287 | $editButton.NativeUI.BorderThickness = 0 288 | $deleteButton.NativeUI.BorderThickness = 0 289 | } 290 | 291 | } 292 | 293 | class MaterialWPFMenuItem : WPFMenuItem { 294 | } 295 | 296 | class MaterialWPFDropDownMenu : MaterialWPFButton { 297 | 298 | MaterialWPFDropDownMenu() { 299 | $this.RightIcon = $true 300 | $this.Icon = [MaterialWPFIcon] @{ Kind = "chevron_down" } 301 | 302 | $this.NativeUI.ContextMenu = [ContextMenu]::new() 303 | $this.AddNativeUIChild = { 304 | param ( 305 | [WPFElement] $element 306 | ) 307 | $this.NativeUI.ContextMenu.Items.Add($element.NativeUI) 308 | } 309 | $this.RemoveNativeUIChild = { 310 | param ( 311 | [WPFElement] $element 312 | ) 313 | $this.NativeUI.ContextMenu.Items.Remove($element.NativeUI) 314 | } 315 | 316 | $this.Action = { 317 | param($this) 318 | $this.NativeUI.ContextMenu.IsOpen = -not $this.NativeUI.ContextMenu.IsOpen 319 | } 320 | } 321 | 322 | } 323 | 324 | class MaterialWPFAutoComplete : WPFAutoComplete { 325 | } 326 | 327 | class MaterialWPFCard : WPFCard { 328 | hidden $CurrentIcon = [MaterialWPFIcon]::new() 329 | 330 | [void] StyleComponents() { 331 | ([WPFCard] $this).StyleComponents() 332 | $this.NativeUI.SetResourceReference([Control]::StyleProperty, "MaterialDesignCardGroupBox") 333 | $this.HeaderTextBlock.Foreground = [System.Windows.Media.Brushes]::White 334 | $this.CurrentIcon.NativeUI.Foreground = [System.Windows.Media.Brushes]::White 335 | $this.HeaderTextBlock.SetResourceReference([Control]::StyleProperty, "MaterialDesignSubHeadingTextBlock") 336 | } 337 | } 338 | 339 | class MaterialWPFImage : WPFImage { 340 | } 341 | 342 | class MaterialWPFTextEditor : WPFTextEditor { 343 | } 344 | 345 | class MaterialWPFExpander : WPFExpander { 346 | } 347 | 348 | class MaterialWPFInteger : WPFInteger { 349 | } 350 | 351 | class MaterialWPFDouble : WPFDouble { 352 | } 353 | 354 | class MaterialWPFComboBoxItem : WPFComboBoxItem { 355 | } 356 | 357 | class MaterialWPFComboBox : WPFComboBox { 358 | } 359 | 360 | class MaterialWPFFileUpload : WPFFileUpload { 361 | } -------------------------------------------------------------------------------- /UIfied/UIfiedOouiBase.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Collections.Generic 2 | using namespace Ooui 3 | 4 | #region Ooui missing elements 5 | 6 | class Icon : Element { 7 | 8 | Icon() : base("i") { 9 | } 10 | } 11 | 12 | class Table : Element { 13 | 14 | Table() : base("table") { 15 | } 16 | } 17 | 18 | class TableHeader : Element { 19 | 20 | TableHeader() : base("thead") { 21 | } 22 | } 23 | 24 | class TableBody : Element { 25 | 26 | TableBody() : base("tbody") { 27 | } 28 | } 29 | 30 | class TableRow : Element { 31 | 32 | TableRow() : base("tr") { 33 | } 34 | } 35 | 36 | class TableHeaderCell : Element { 37 | 38 | TableHeaderCell() : base("th") { 39 | } 40 | } 41 | 42 | class TableDataCell : Element { 43 | 44 | TableDataCell() : base("td") { 45 | } 46 | } 47 | 48 | #endregion 49 | 50 | class OouiElement : UIElement { 51 | 52 | OouiElement() { 53 | $this.WrapNegatedProperty("Enable", "IsDisabled") 54 | $this.WrapNegatedProperty("Visible", "IsHidden") 55 | $this.AddNativeUIChild = { 56 | param ( 57 | [OouiElement] $element 58 | ) 59 | $this.NativeUI.AppendChild($element.NativeUI) | Out-Null 60 | } 61 | $this.RemoveNativeUIChild = { 62 | param ( 63 | [OouiElement] $element 64 | ) 65 | $this.NativeUI.RemoveChild($element.NativeUI) | Out-Null 66 | } 67 | } 68 | } 69 | 70 | class OouiHost : UIHost { 71 | $Shared = $false 72 | $Port = 8185 73 | $Host = [UI]::Host 74 | [ScriptBlock] $CreateElement 75 | 76 | OouiHost() { 77 | # Style documentation in https://getbootstrap.com/docs/5.0 78 | [UI]::HeadHtml = ' 79 | 80 | 81 | 106 | ' 107 | [UI]::BodyFooterHtml = ' 108 | 109 | ' 110 | } 111 | 112 | [void] ShowFrame([ScriptBlock] $frameScriptBlock) { 113 | #$Global:SyncHash = [HashTable]::Synchronized(@{ 114 | # Window = $null 115 | # Errors = @() 116 | #}) 117 | 118 | $this.CreateElement = $frameScriptBlock 119 | if ($this.Shared) { 120 | $this.ShowSharedFrame() 121 | } else { 122 | $this.ShowNotSharedFrame() 123 | } 124 | } 125 | 126 | [void] ShowSharedFrame() { 127 | $frame = Invoke-Command -ScriptBlock $this.CreateElement 128 | [UI]::Port = $this.Port 129 | [UI]::Publish("/Form", $frame.NativeUI) 130 | } 131 | 132 | [void] ShowNotSharedFrame() { 133 | $notSharedForm = [NotSharedForm]::new($this.Host, $this.Port, "/Form") 134 | $notSharedForm.CreateElement = $this.CreateElement 135 | $notSharedForm.Publish() 136 | } 137 | } 138 | 139 | class NotSharedForm : Div { 140 | [Anchor] $Anchor 141 | [ScriptBlock] $CreateElement 142 | [string] $Host 143 | [int] $Port 144 | [string] $Path 145 | 146 | NotSharedForm([string]$host, [int]$port, [string]$path) { 147 | $this.Host = $host 148 | $this.Port = $port 149 | $this.Path = $path 150 | } 151 | 152 | Publish() { 153 | $hostWrapper = [OouiWrapper.OouiWrapper]::new($this.Port, $this.Path) 154 | $hostWrapper.Host = $this.Host 155 | $hostWrapper.Publish() 156 | $hostWrapper.PublishFileUpload($env:UploadPath, "/files/upload") 157 | Add-Member -InputObject $hostWrapper -MemberType NoteProperty -Name sb -Value $this.CreateElement | Out-Null 158 | Register-ObjectEvent -InputObject $hostWrapper -EventName OnPublish -MessageData $hostWrapper -Action { 159 | param ($hostWrapper) 160 | $window = Invoke-Command -ScriptBlock $event.MessageData.sb 161 | $event.MessageData.Frame = $window.NativeUI 162 | } | Out-Null 163 | } 164 | } 165 | 166 | class OouiWindow : WindowBase { 167 | 168 | OouiWindow() { 169 | $this.SetNativeUI([Div]::new()) 170 | $this.WrapProperty("Caption", "Title") 171 | $this.AddScriptBlockProperty("Loaded") 172 | $this.AddNativeUIChild = { 173 | param ( 174 | [OouiElement] $element 175 | ) 176 | $this.NativeUI.AppendChild($element.NativeUI) 177 | } 178 | } 179 | 180 | [void] ShowDialog() { 181 | } 182 | 183 | [void] OnLoaded() { 184 | Invoke-Command -ScriptBlock $this._Loaded -ArgumentList $this 185 | } 186 | } 187 | 188 | class OouiStackPanel : OouiElement { 189 | #Divs https://jsfiddle.net/rwe8hp6f/ 190 | 191 | OouiStackPanel() { 192 | $this.SetNativeUI([Div]::new()) 193 | $this.AddProperty("Orientation") 194 | $this.AddNativeUIChild = { 195 | param ( 196 | [OouiElement] $element 197 | ) 198 | $listItem = [Div]::new() 199 | if ($this._Orientation -eq [Orientation]::Horizontal) { 200 | $listItem.Style.float = "left" 201 | } else { 202 | $listItem.Style.clear = "both" 203 | #$listItem.Style.Display = "" 204 | } 205 | $this.NativeUI.AppendChild($listItem) | Out-Null 206 | $listItem.AppendChild($element.NativeUI) | Out-Null 207 | } 208 | } 209 | } 210 | 211 | class OouiIcon : OouiElement { 212 | [String] $KindName = "" 213 | static $IconTranslation = [PSCustomObject] @{ 214 | document = "description" 215 | } 216 | 217 | OouiIcon() { 218 | $nativeUI = [Icon]::new() 219 | $nativeUI.ClassName = "material-icons" 220 | $this.SetNativeUI($nativeUI) 221 | Add-Member -InputObject $this -Name Kind -MemberType ScriptProperty -Value { 222 | $this.KindName 223 | } -SecondValue { 224 | $this.KindName = $args[0] 225 | if ([OouiIcon]::IconTranslation."$($this.KindName)" -ne $null) { 226 | $this.NativeUI.Text = [OouiIcon]::IconTranslation."$($this.KindName)" 227 | } else { 228 | $this.NativeUI.Text = $this.KindName 229 | } 230 | } 231 | } 232 | 233 | } 234 | 235 | class OouiLabel : OouiElement { 236 | 237 | OouiLabel() { 238 | $this.SetNativeUI([Span]::new()) 239 | $this.WrapProperty("Caption", "Text") 240 | } 241 | } 242 | 243 | class OouiButton : OouiElement { 244 | hidden [OOuiIcon] $CurrentIcon = $null 245 | hidden [span] $CurrentSpan = $null 246 | hidden [String] $CaptionText = "" 247 | 248 | OouiButton() { 249 | $nativeUI = [Button]::new("") 250 | $nativeUI.ClassName = "btn btn-primary" 251 | $this.SetNativeUI($nativeUI) 252 | Add-Member -InputObject $this -Name Caption -MemberType ScriptProperty -Value { 253 | $this.CaptionText 254 | } -SecondValue { 255 | $this.RemoveChildren() 256 | $this.CaptionText = $args[0] 257 | $this.RefreshCaption() 258 | } 259 | Add-Member -InputObject $this -Name Icon -MemberType ScriptProperty -Value { 260 | $this.CurrentIcon 261 | } -SecondValue { 262 | $this.RemoveChildren() 263 | $this.CurrentIcon = $args[0] 264 | $this.RefreshCaption() 265 | } 266 | $this.AddScriptBlockProperty("Action") 267 | Register-ObjectEvent -InputObject $this.NativeUI -EventName Click -MessageData $this -Action { 268 | $this = $event.MessageData 269 | $this.Control.OnAction() 270 | } | Out-Null 271 | } 272 | 273 | [void] RemoveChildren() { 274 | if ($this.CurrentSpan -ne $null) { 275 | $this.NativeUI.RemoveChild($this.CurrentSpan) 276 | } 277 | if ($this.CurrentIcon -ne $null) { 278 | $this.NativeUI.RemoveChild($this.CurrentIcon.NativeUI) 279 | } 280 | } 281 | 282 | [void] RefreshCaption() { 283 | if ($this.CurrentIcon -ne $null) { 284 | $this.NativeUI.AppendChild($this.CurrentIcon.NativeUI) 285 | } 286 | if ($this.CaptionText -ne "") { 287 | $this.CurrentSpan = [Span]::new() 288 | $this.CurrentSpan.Text = $this.CaptionText 289 | $this.NativeUI.AppendChild($this.CurrentSpan) 290 | } 291 | } 292 | 293 | [void] OnAction() { 294 | Invoke-Command -ScriptBlock $this._Action -ArgumentList $this 295 | } 296 | } 297 | 298 | class OouiTextBox : OouiElement { 299 | [String] $Pattern = "" 300 | [String] $DefaultText = "" 301 | 302 | OouiTextBox() { 303 | $this.SetNativeUIControl() 304 | $this.WrapProperty("Text", "Value") 305 | $this.AddScriptBlockProperty("Change") 306 | Register-ObjectEvent -InputObject $this.NativeUI -EventName Change -MessageData $this -Action { 307 | $this = $event.MessageData 308 | if ($this.Control.Pattern -ne "") { 309 | $regex = [Regex]::new($this.Control.Pattern) 310 | if (-not $regex.IsMatch($this.Control.Text)) { 311 | $this.Control.Text = $this.Control.DefaultText 312 | } 313 | } 314 | $this.Control.OnChange() 315 | } | Out-Null 316 | Add-Member -InputObject $this -Name TextAlignment -MemberType ScriptProperty -Value { 317 | if ($this.NativeUI.Style.TextAlign -eq "left") { 318 | [TextAlignment]::Left 319 | } else { 320 | [TextAlignment]::Right 321 | } 322 | } -SecondValue { 323 | if ($args[0] -eq "Left") { 324 | $this.NativeUI.Style.TextAlign = "left" 325 | } else { 326 | $this.NativeUI.Style.TextAlign = "right" 327 | } 328 | 329 | } 330 | } 331 | 332 | [void] SetNativeUIControl() { 333 | $this.SetNativeUI([TextInput]::new()) 334 | } 335 | 336 | [void] OnChange() { 337 | Invoke-Command -ScriptBlock $this._Change -ArgumentList $this 338 | } 339 | } 340 | 341 | class OouiPassword : OouiTextBox { 342 | 343 | [void] SetNativeUIControl() { 344 | $this.SetNativeUI([Input]::new("password")) 345 | } 346 | 347 | } 348 | 349 | class OouiCheckBox : OouiElement { 350 | hidden [Div] $ListNativeUI 351 | hidden [Span] $LabelNativeUI 352 | hidden [Input] $CheckBoxNativeUI 353 | 354 | OouiCheckBox() { 355 | $this.ListNativeUI = [Div]::new() 356 | $this.LabelNativeUI = [Span]::new() 357 | $this.CheckBoxNativeUI = [Input]::new("CheckBox") 358 | $this.ListNativeUI.AppendChild($this.CheckBoxNativeUI) 359 | $this.ListNativeUI.AppendChild($this.LabelNativeUI) 360 | $this.SetNativeUI($this.ListNativeUI) 361 | $this.WrapProperty("Caption", "Text", "LabelNativeUI") 362 | $this.WrapProperty("IsChecked", "IsChecked", "CheckBoxNativeUI") 363 | $this.AddScriptBlockProperty("Click") 364 | Register-ObjectEvent -InputObject $this.CheckBoxNativeUI -EventName Change -MessageData $this -Action { 365 | $this = $event.MessageData 366 | $this.Control.OnClick() 367 | } | Out-Null 368 | } 369 | 370 | [void] OnClick() { 371 | Invoke-Command -ScriptBlock $this._Click -ArgumentList $this 372 | } 373 | 374 | } 375 | 376 | class OouiRadioButton : OouiElement { 377 | hidden [Div] $ListNativeUI 378 | hidden [Span] $LabelNativeUI 379 | hidden [Input] $RadioButtonNativeUI 380 | 381 | OouiRadioButton() { 382 | $this.ListNativeUI = [Div]::new() 383 | $this.LabelNativeUI = [Span]::new() 384 | $this.RadioButtonNativeUI = [Input]::new("Radio") 385 | $this.ListNativeUI.AppendChild($this.RadioButtonNativeUI) 386 | $this.ListNativeUI.AppendChild($this.LabelNativeUI) 387 | $this.SetNativeUI($this.ListNativeUI) 388 | $this.WrapProperty("Caption", "Text", "LabelNativeUI") 389 | $this.WrapProperty("IsChecked", "IsChecked", "RadioButtonNativeUI") 390 | $this.AddScriptBlockProperty("Click") 391 | Register-ObjectEvent -InputObject $this.RadioButtonNativeUI -EventName Change -MessageData $this -Action { 392 | $this = $event.MessageData 393 | $this.Control.OnClick() 394 | } | Out-Null 395 | } 396 | 397 | [void] OnClick() { 398 | Invoke-Command -ScriptBlock $this._Click -ArgumentList $this 399 | } 400 | 401 | } 402 | 403 | class OouiRadioGroup : OouiElement { 404 | hidden [String] $ChildName = "" 405 | 406 | OouiRadioGroup() { 407 | $this.SetNativeUI([Div]::new()) 408 | $this.AddNativeUIChild = { 409 | param ( 410 | [OouiElement] $element 411 | ) 412 | if ($this.Control.ChildName -eq "") { 413 | if ($this.Control.Name -ne "") { 414 | $this.Control.ChildName = $this.Control.Name 415 | } else { 416 | $this.Control.ChildName = "A" + [Guid]::NewGuid().ToString() 417 | } 418 | } 419 | $element.RadioButtonNativeUI.Name = $this.Control.ChildName 420 | $this.NativeUI.AppendChild($element.NativeUI) | Out-Null 421 | } 422 | } 423 | } 424 | 425 | class OouiList : OouiElement { 426 | [List[ListItem]] $Items = [List[ListItem]]::new() 427 | [List[OouiListColumn]] $Columns = [List[OouiListColumn]]::new() 428 | 429 | hidden [Table] $Table = [Table]::new() 430 | hidden [TableHeader] $TableHeader = [TableHeader]::new() 431 | hidden [TableRow] $HeaderRow = [TableRow]::new() 432 | hidden [List[TableHeaderCell]] $HeaderCells = [List[TableHeaderCell]]::new() 433 | hidden [TableBody] $TableBody = [TableBody]::new() 434 | hidden [List[TableRow]] $BodyRows = [List[TableRow]]::new() 435 | 436 | OouiList() { 437 | $this.SetNativeUI($this.Table) 438 | $this.NativeUI.ClassName = "UIList" 439 | $this.Table.AppendChild($this.TableHeader) 440 | $this.TableHeader.AppendChild($this.HeaderRow) 441 | $this.Table.AppendChild($this.TableBody) 442 | } 443 | 444 | [void] AddColumn([OouiListColumn] $listColumn) { 445 | $this.Columns.Add($listColumn) 446 | $cell = [TableHeaderCell]::new() 447 | $cell.Text = $listColumn.Title 448 | $this.HeaderRow.AppendChild($cell) 449 | $this.HeaderCells.Add($cell) 450 | } 451 | 452 | [void] AddItem([ListItem] $listItem) { 453 | $this.Items.Add($listItem) 454 | $row = [TableRow]::new() 455 | $this.BodyRows.Add($row) 456 | $listItem.Children | ForEach-Object { 457 | $cell = [TableDataCell]::new() 458 | Add-Member -InputObject $_.NativeUI -Name Form -MemberType NoteProperty -Value $this.Form 459 | Add-Member -InputObject $_.NativeUI -Name Parent -MemberType NoteProperty -Value $this 460 | $cell.AppendChild($_.NativeUI) 461 | $row.AppendChild($cell) 462 | } 463 | $this.TableBody.AppendChild($row) 464 | } 465 | 466 | [void] StyleComponents() { 467 | } 468 | 469 | [void] StyleCell($cell) { 470 | #$cell.NativeUI.Style.LineHeight = $this.LineHeight 471 | } 472 | 473 | [void] RemoveItem([ListItem] $listItem) { 474 | $itemIndex = $this.Items.IndexOf($listItem) 475 | $row = $this.BodyRows.Item($itemIndex) 476 | $this.BodyRows.Remove($row) 477 | $this.TableBody.RemoveChild($row) 478 | $this.Items.Remove($listItem) 479 | } 480 | 481 | [void] Clear() { 482 | $this.Items.ToArray() | ForEach-Object { 483 | $this.RemoveItem($_) 484 | } 485 | } 486 | 487 | [TableDataCell] GetCell([int] $RowIndex, [int] $ColumnIndex) { 488 | $row = $this.BodyRows.Item($RowIndex) 489 | $cell = $row.Children.Item($ColumnIndex) 490 | return $cell 491 | } 492 | } 493 | 494 | class OouiListColumn { 495 | [String] $Name 496 | [String] $Title 497 | } 498 | 499 | class OouiTabItem : OouiStackPanel { 500 | [String] $Caption = "" 501 | } 502 | 503 | class OouiTabControl : OouiStackPanel { 504 | [Ooui.List] $List = [Ooui.List]::new() 505 | 506 | OouiTabControl() { 507 | $this.NativeUI.AppendChild($this.List) 508 | $this.AddNativeUIChild = { 509 | param ( 510 | [OouiElement] $element 511 | ) 512 | $item = [Ooui.ListItem]::new() 513 | $item.ClassName = "nav-item" 514 | $anchor = [Ooui.Anchor]::new() 515 | $anchor.ClassName = "nav-link" 516 | $anchor.Text = $element.Caption 517 | Register-ObjectEvent -InputObject $anchor -EventName Click -MessageData @($this, $anchor) -Action { 518 | $event.MessageData 519 | $Control = $event.MessageData[0] 520 | $anchor = $event.MessageData[1] 521 | $Control.SelectTab($anchor.Text) 522 | } | Out-Null 523 | $item.AppendChild($anchor) | Out-Null 524 | $this.List.AppendChild($item) | Out-Null 525 | $this.NativeUI.AppendChild($element.NativeUI) | Out-Null 526 | 527 | $firstTab = $this.GetTabs() | Select-Object -First 1 528 | $this.SelectTab($firstTab.Caption) 529 | } 530 | $this.RefreshStyle() 531 | } 532 | 533 | RefreshStyle() { 534 | $this.List.ClassName = "nav nav-pills" 535 | } 536 | 537 | [OOuiTabItem[]] GetTabs() { 538 | return $this.Children 539 | } 540 | 541 | [void] SelectTab([String] $tabCaption) { 542 | $this.GetTabs() | ForEach-Object { 543 | if ($_.Caption -eq $tabCaption) { 544 | $_.Visible = $true 545 | } else { 546 | $_.Visible = $false 547 | } 548 | } 549 | $this.List.Children | ForEach-Object { 550 | $anchor = $_.FirstChild 551 | if ($anchor.Text -eq $tabCaption) { 552 | $_.ClassName = "nav-item active" 553 | $anchor.ClassName = "nav-link active" 554 | } else { 555 | $_.ClassName = "nav-item" 556 | $anchor.ClassName = "nav-link" 557 | } 558 | } 559 | } 560 | 561 | } 562 | 563 | class OouiModal : OouiElement { 564 | [Div] $DialogDiv = [Div]::new() 565 | [Div] $DocumentDiv = [Div]::new() 566 | [Div] $ContentDiv = [Div]::new() 567 | [Div] $HeaderDiv = [Div]::new() 568 | [Heading] $TitleHeading = [Heading]::new(5) 569 | [Div] $BodyDiv = [Div]::new() 570 | 571 | OouiModal() { 572 | $this.DialogDiv.ClassName = "modal" 573 | $this.DialogDiv.Style.display = "none" 574 | $this.DialogDiv.SetAttribute("role", "dialog") 575 | 576 | $this.DocumentDiv.ClassName = "modal-dialog" 577 | $this.DocumentDiv.SetAttribute("role", "document") 578 | $this.DialogDiv.AppendChild($this.DocumentDiv) 579 | 580 | $this.ContentDiv.ClassName = "modal-content" 581 | $this.DocumentDiv.AppendChild($this.ContentDiv) 582 | 583 | $this.HeaderDiv.ClassName = "modal-header" 584 | $this.ContentDiv.AppendChild($this.HeaderDiv) 585 | 586 | $this.TitleHeading.ClassName = "modal-title" 587 | $this.HeaderDiv.AppendChild($this.TitleHeading) 588 | 589 | $this.BodyDiv.ClassName = "modal-body" 590 | $this.ContentDiv.AppendChild($this.BodyDiv) 591 | 592 | $this.SetNativeUI($this.DialogDiv) 593 | $this.AddNativeUIChild = { 594 | param ( 595 | [OouiElement] $element 596 | ) 597 | $this.BodyDiv.AppendChild($element.NativeUI) 598 | } 599 | $this.WrapProperty("Title", "Text", "TitleHeading") 600 | } 601 | 602 | [void] Show() { 603 | $this.DialogDiv.Style.display = "block" 604 | } 605 | 606 | [void] Hide() { 607 | $this.DialogDiv.Style.display = "none" 608 | } 609 | } 610 | 611 | class OouiTimer : OouiElement { 612 | [System.Timers.Timer] $Timer 613 | [Double] $Interval = 1000 614 | 615 | OouiTimer() { 616 | $label = [Span]::new() 617 | $label.IsHidden = $true 618 | $this.SetNativeUI($label) 619 | $this.AddScriptBlockProperty("Elapsed") 620 | $this.Timer = New-Object System.Timers.Timer 621 | Register-ObjectEvent -InputObject $this.Timer -EventName Elapsed -MessageData $this -Action { 622 | $this = $event.MessageData 623 | $this.Control.OnElapsed() 624 | } 625 | } 626 | 627 | [void] OnElapsed() { 628 | Invoke-Command -ScriptBlock $this._Elapsed -ArgumentList $this 629 | } 630 | 631 | [void] Start() { 632 | $this.Timer.Interval = $this.Interval 633 | $this.Timer.Start() 634 | } 635 | 636 | [void] Stop() { 637 | $this.Timer.Stop() 638 | } 639 | } 640 | 641 | class OouiDatePicker : OouiElement { 642 | 643 | OouiDatePicker() { 644 | $this.SetNativeUI([Input]::new("Date")) 645 | $this.AddScriptBlockProperty("Change") 646 | Register-ObjectEvent -InputObject $this.NativeUI -EventName Change -MessageData $this -Action { 647 | $this = $event.MessageData 648 | $this.Control.OnChange() 649 | } | Out-Null 650 | Add-Member -InputObject $this -Name Value -MemberType ScriptProperty -Value { 651 | [DateTime]::Parse($this.NativeUI.Value) 652 | } -SecondValue { 653 | $this.NativeUI.Value = $args[0].ToString("yyyy-MM-dd") 654 | } 655 | } 656 | 657 | [void] OnChange() { 658 | Invoke-Command -ScriptBlock $this._Change -ArgumentList $this 659 | } 660 | 661 | } 662 | 663 | class OouiTimePicker : OouiElement { 664 | 665 | OouiTimePicker() { 666 | $this.SetNativeUI([Input]::new("Time")) 667 | $this.AddScriptBlockProperty("Change") 668 | Register-ObjectEvent -InputObject $this.NativeUI -EventName Change -MessageData $this -Action { 669 | $this = $event.MessageData 670 | $this.Control.OnChange() 671 | } | Out-Null 672 | Add-Member -InputObject $this -Name Value -MemberType ScriptProperty -Value { 673 | if ($this.IsTime($this.NativeUI.Value)) { 674 | $this.NativeUI.Value 675 | } else { 676 | "00:00" 677 | } 678 | } -SecondValue { 679 | if ($this.IsTime($args[0])) { 680 | $this.NativeUI.Value = $args[0] 681 | } else { 682 | "00:00" 683 | } 684 | } 685 | } 686 | 687 | hidden [Boolean] IsTime([String] $timeText) { 688 | [DateTime] $dateTime = [DateTime]::Today 689 | return [DateTime]::TryParse( "2000-01-01 " + $timeText, [ref] $dateTime) 690 | } 691 | 692 | [void] OnChange() { 693 | Invoke-Command -ScriptBlock $this._Change -ArgumentList $this 694 | } 695 | 696 | } 697 | 698 | class OouiBrowser : OouiStackPanel { 699 | [Object[]] $Data = [Object[]] @() 700 | [int] $PageRows = 10 701 | [int] $CurrentPage = 0 702 | [Boolean] $IsEditable = $true 703 | [Object] $CurrentRow 704 | 705 | #region Components Declaration 706 | 707 | hidden [OouiListColumn[]] $Columns = [OouiListColumn[]] @() 708 | hidden [OouiListColumn] $EditionColumn 709 | hidden [OouiList] $List = [OouiList]::new() 710 | hidden [OouiStackPanel] $ButtonPanel = [OouiStackPanel]::new() 711 | hidden [OouiButton] $FirstButton = [OouiButton]::new() 712 | hidden [OouiButton] $PreviousButton = [OouiButton]::new() 713 | hidden [OouiButton] $NextButton = [OouiButton]::new() 714 | hidden [OouiButton] $LastButton = [OouiButton]::new() 715 | hidden [OouiButton] $AddNewButton = [OouiButton]::new() 716 | 717 | #endregion 718 | 719 | OouiBrowser() { 720 | $this.AddScriptBlockProperty("AddNew") 721 | $this.AddScriptBlockProperty("Edit") 722 | $this.AddScriptBlockProperty("Delete") 723 | $this.AddChild($this.List) 724 | $this.AddButtons() 725 | } 726 | 727 | #region Components Creation 728 | 729 | hidden [void] AddButtons() { 730 | $this.ButtonPanel = [OouiStackPanel]::new() 731 | $this.ButtonPanel.Orientation = "Horizontal" 732 | 733 | $this.FirstButton.Action = { $this.Parent.Parent.OnMoveFirst() } 734 | $this.PreviousButton.Action = { $this.Parent.Parent.OnMovePrevious() } 735 | $this.NextButton.Action = { $this.Parent.Parent.OnMoveNext() } 736 | $this.LastButton.Action = { $this.Parent.Parent.OnMoveLast() } 737 | $this.AddNewButton.Action = { $this.Parent.Parent.OnAddNew() } 738 | 739 | $this.ButtonPanel.AddChild($this.FirstButton) 740 | $this.ButtonPanel.AddChild($this.PreviousButton) 741 | $this.ButtonPanel.AddChild($this.NextButton) 742 | $this.ButtonPanel.AddChild($this.LastButton) 743 | $this.ButtonPanel.AddChild($this.AddNewButton) 744 | 745 | $this.StyleComponents() 746 | 747 | $this.AddChild($this.ButtonPanel) 748 | } 749 | 750 | [void] AddColumn([OouiListColumn] $listColumn) { 751 | $this.Columns += $listColumn 752 | $this.List.AddColumn($listColumn) 753 | } 754 | 755 | [void] CreateList() { 756 | $this.List.Clear() 757 | $this.CreateEditable() 758 | 0..($this.PageRows - 1) | ForEach-Object { 759 | $listItem = $this.GetInitialListItem($_) 760 | $this.List.AddItem($listItem) 761 | } 762 | } 763 | 764 | hidden [ListItem] GetInitialListItem([int] $rowIndex) { 765 | $hash = $this.GetInitialHash() 766 | $listItem = [ListItem]::new() 767 | $this.Columns | ForEach-Object { 768 | $column = $_ 769 | if ($column -eq $this.EditionColumn -and $this.IsEditable) { 770 | $this.AddEditionButtons($hash, $listItem, $rowIndex) 771 | } else { 772 | $this.AddCell($hash, $column.Name, $listItem, $rowIndex) 773 | } 774 | } 775 | return $listItem 776 | } 777 | 778 | hidden [Object] GetInitialHash() { 779 | $hash = @{} 780 | $this.Columns | ForEach-Object { 781 | $column = $_ 782 | $hash += @{ "$($column.Name)" = "" } 783 | } 784 | return $hash 785 | } 786 | 787 | hidden [void] AddEditionButtons([Object] $hash, [ListItem] $listItem, [int] $rowIndex) { 788 | $editionPanel = [OouiStackPanel]::new() 789 | $editionPanel.Orientation = "Horizontal" 790 | $listItem.AddChild($editionPanel) 791 | 792 | $editButton = [OouiButton]::new() 793 | Add-Member -InputObject $editButton -MemberType NoteProperty -Name CurrentRow -Value $hash 794 | Add-Member -InputObject $editButton -MemberType NoteProperty -Name Container -Value $this 795 | $editButton.Action = { 796 | $this.Container.CurrentRow = $this.Control.CurrentRow 797 | $this.Container.OnEdit() 798 | } 799 | $editionPanel.AddChild($editButton) 800 | 801 | $deleteButton = [OouiButton]::new() 802 | Add-Member -InputObject $deleteButton -MemberType NoteProperty -Name CurrentRow -Value $hash 803 | Add-Member -InputObject $deleteButton -MemberType NoteProperty -Name Container -Value $this 804 | $deleteButton.Action = { 805 | $this.Container.CurrentRow = $this.Control.CurrentRow 806 | $this.Container.OnDelete() 807 | } 808 | $editionPanel.AddChild($deleteButton) 809 | $this.StyleEditionButtons($editButton, $deleteButton, $rowIndex) 810 | } 811 | 812 | hidden [void] AddCell([Object] $hash, [string] $columnName, [ListItem] $listItem, [int] $rowIndex) { 813 | $itemLabel = [OouiLabel]::new() 814 | $itemLabel.Caption = $hash."$columnName" 815 | $listItem.AddChild($itemLabel) 816 | $this.StyleCell($itemLabel, $rowIndex) 817 | } 818 | 819 | hidden [void] CreateEditable() { 820 | if ($this.EditionColumn -eq $null -and $this.IsEditable) { 821 | $this.CreateEditionColumn() 822 | } 823 | $this.AddNewButton.Visible = $this.IsEditable 824 | } 825 | 826 | hidden [void] CreateEditionColumn() { 827 | $this.EditionColumn = New-Object OouiListColumn -Property @{Name = "_Edition"; Title = "_"} 828 | $this.AddColumn($this.EditionColumn) 829 | } 830 | 831 | #endregion 832 | 833 | #region Data show 834 | 835 | [void] Refresh() { 836 | # Fill Rows 837 | $rowIndex = 0 838 | $selectedData = $this.GetSelectedData() 839 | $selectedData | ForEach-Object { 840 | $hash = $_ 841 | $columnIndex = 0 842 | $this.Columns | Select-Object -First ($this.Columns.Count) | ForEach-Object { 843 | $column = $_ 844 | $cell = $this.List.GetCell($rowIndex, $columnIndex) 845 | if ($this.EditionColumn -ne $column) { 846 | $cell.Children.Item(0).Text = $hash."$($column.Name)" 847 | } else { 848 | $cell.IsHidden = $false 849 | #$buttons = $this.List.Children.Item($columnIndex).Children.Item($rowIndex + 1).Children 850 | #$buttons.Item(0).CurrentRow = $hash 851 | #$buttons.Item(1).CurrentRow = $hash 852 | $stack = $cell.Children.Item(0) 853 | $stack.Control.Children.Item(0).CurrentRow = $hash 854 | $stack.Control.Children.Item(1).CurrentRow = $hash 855 | } 856 | $columnIndex++ 857 | } 858 | $rowIndex++ 859 | } 860 | # EmptyRows 861 | for ($rowIndex = $selectedData.Count + 1; $rowIndex -le $this.PageRows; $rowIndex++) { 862 | $columnIndex = 0 863 | $this.Columns | Select-Object -First ($this.Columns.Count) | ForEach-Object { 864 | $cell = $this.List.GetCell($rowIndex - 1, $columnIndex) 865 | $column = $_ 866 | if ($this.EditionColumn -ne $column) { 867 | $cell.Children.Item(0).Text = "" 868 | } else { 869 | $cell.IsHidden = $true 870 | } 871 | $columnIndex++ 872 | } 873 | } 874 | } 875 | 876 | hidden [Object[]] GetSelectedData() { 877 | return $this.Data | Select-Object -Skip ($this.CurrentPage * $this.PageRows) -First $this.PageRows 878 | } 879 | 880 | hidden [int] GetLastPage() { 881 | $lastPage = [Math]::Truncate($this.Data.Count / $this.PageRows) 882 | if (($this.Data.Count % $this.PageRows) -eq 0) { 883 | $lastPage-- 884 | } 885 | return $lastPage 886 | } 887 | 888 | #endregion 889 | 890 | #region Style 891 | 892 | [void] StyleComponents() { 893 | $this.List.NativeUI.ClassName = "UIList table" 894 | 895 | $this.FirstButton.Icon = [OouiIcon] @{ Kind = "first_page" } 896 | $this.PreviousButton.Icon = [OouiIcon] @{ Kind = "chevron_left" } 897 | $this.NextButton.Icon = [OouiIcon] @{ Kind = "chevron_right" } 898 | $this.LastButton.Icon = [OouiIcon] @{ Kind = "last_page" } 899 | $this.AddNewButton.Icon = [OouiIcon] @{ Kind = "add" } 900 | 901 | $this.FirstButton.NativeUI.ClassName = "btn btn-link" 902 | $this.PreviousButton.NativeUI.ClassName = "btn btn-link" 903 | $this.NextButton.NativeUI.ClassName = "btn btn-link" 904 | $this.LastButton.NativeUI.ClassName = "btn btn-link" 905 | $this.AddNewButton.NativeUI.ClassName = "btn btn-link" 906 | } 907 | 908 | [void] StyleCell($cell, [int] $rowIndex) { 909 | } 910 | 911 | [void] StyleEditionButtons([OouiButton] $editButton, [OouiButton] $deleteButton, [int] $rowIndex) { 912 | $editButton.Icon = [OouiIcon] @{ Kind = "edit" } 913 | $deleteButton.Icon = [OouiIcon] @{ Kind = "close" } 914 | 915 | $editButton.NativeUI.ClassName = "btn btn-success td-actions btn-sm" 916 | $deleteButton.NativeUI.ClassName = "btn btn-danger td-actions btn-sm" 917 | } 918 | 919 | #endregion 920 | 921 | #region Move Events 922 | 923 | hidden [void] OnMoveFirst() { 924 | $this.CurrentPage = 0 925 | $this.Refresh() 926 | } 927 | 928 | hidden [void] OnMovePrevious() { 929 | if ($this.CurrentPage -gt 0) { 930 | $this.CurrentPage-- 931 | } 932 | $this.Refresh() 933 | } 934 | 935 | hidden [void] OnMoveNext() { 936 | if ($this.CurrentPage -lt $this.GetLastPage()) { 937 | $this.CurrentPage++ 938 | } 939 | $this.Refresh() 940 | } 941 | 942 | hidden [void] OnMoveLast() { 943 | $this.CurrentPage = $this.GetLastPage() 944 | $this.Refresh() 945 | } 946 | 947 | #endregion 948 | 949 | #region CRUD Events 950 | 951 | hidden [void] OnAddNew() { 952 | $this.InvokeTrappableCommand($this._AddNew, $this) 953 | } 954 | 955 | hidden [void] OnEdit() { 956 | $this.InvokeTrappableCommand($this._Edit, $this) 957 | } 958 | 959 | hidden [void] OnDelete() { 960 | $this.InvokeTrappableCommand($this._Delete, $this) 961 | } 962 | 963 | #endregion 964 | 965 | } 966 | 967 | class OouiMenuItem : OouiButton { 968 | 969 | [void] OnAction() { 970 | ([OouiButton]$this).OnAction() 971 | $this.Parent.ToogleMenu() 972 | } 973 | 974 | } 975 | 976 | class OouiDropDownMenu : OouiStackPanel { 977 | 978 | [OOuiButton] $DropDownToogle = [OOuiButton]::new() 979 | [List] $DropDownMenu = [List]::new() 980 | 981 | OouiDropDownMenu() { 982 | $dropDown = [Div]::new() 983 | $dropDown.SetAttribute("class", "dropdown") 984 | $this.NativeUI.AppendChild($dropDown) 985 | 986 | $this.DropDownToogle.NativeUI.SetAttribute("class", "btn btn-primary dropdown-toggle") 987 | $this.DropDownToogle.NativeUI.SetAttribute("data-toggle", "dropdown") 988 | $this.DropDownToogle.NativeUI.SetAttribute("aria-expanded", "true") 989 | $this.DropDownToogle.NativeUI.SetAttribute("aria-haspopup", "true") 990 | Add-Member -InputObject $this.DropDownToogle -MemberType NoteProperty -Name ParentControl -Value $this 991 | $this.DropDownToogle.Action = { 992 | param ($this) 993 | $this.Control.ParentControl.ToogleMenu() 994 | } 995 | $dropDown.AppendChild($this.DropDownToogle.NativeUI) 996 | 997 | $this.DropDownMenu.SetAttribute("class", "dropdown-menu") 998 | $dropDown.AppendChild($this.DropDownMenu) 999 | 1000 | $this.AddNativeUIChild = { 1001 | param ( 1002 | [OouiElement] $element 1003 | ) 1004 | $menuItem = [Ooui.ListItem]::new() 1005 | $element.NativeUI.SetAttribute("class", "dropdown-item") 1006 | $element.NativeUI.Style.Width = "100%" 1007 | $element.NativeUI.Style.BorderColor = "transparent" 1008 | $menuItem.AppendChild($element.NativeUI) | Out-Null 1009 | $this.DropDownMenu.AppendChild($menuItem) | Out-Null 1010 | } 1011 | $this.RemoveNativeUIChild = { 1012 | param ( 1013 | [OouiElement] $element 1014 | ) 1015 | $this.DropDownMenu.Children | ForEach-Object { 1016 | if ($_.Children.Item(0) -eq $element.NativeUI) { 1017 | $this.DropDownMenu.RemoveChild($_) | Out-Null 1018 | } 1019 | } 1020 | } 1021 | Add-Member -InputObject $this -Name "Caption" -MemberType ScriptProperty ` 1022 | -Value { $this.DropDownToogle.NativeUI.Text } ` 1023 | -SecondValue { $this.DropDownToogle.NativeUI.Text = $args[0] } 1024 | } 1025 | 1026 | [void] ToogleMenu() { 1027 | if ($this.DropDownMenu.ClassName.Contains("show")) { 1028 | $this.DropDownMenu.ClassName = $this.DropDownMenu.ClassName.Replace(" show", "") 1029 | } else { 1030 | $this.DropDownMenu.ClassName = $this.DropDownMenu.ClassName + " show" 1031 | } 1032 | } 1033 | 1034 | } 1035 | 1036 | class OouiAutoComplete : OouiStackPanel { 1037 | 1038 | [OouiTextBox] $TextBox = [OouiTextBox]::new() 1039 | [List] $DropDownMenu = [List]::new() 1040 | 1041 | OouiAutoComplete() { 1042 | $dropDown = [Div]::new() 1043 | $dropDown.SetAttribute("class", "dropdown") 1044 | $this.NativeUI.AppendChild($dropDown) 1045 | 1046 | $this.TextBox.NativeUI.SetAttribute("class", "dropdown-toggle") 1047 | $this.TextBox.NativeUI.SetAttribute("data-toggle", "dropdown") 1048 | $this.TextBox.NativeUI.SetAttribute("aria-expanded", "true") 1049 | $this.TextBox.NativeUI.SetAttribute("aria-haspopup", "true") 1050 | Add-Member -InputObject $this.TextBox -MemberType NoteProperty -Name ParentControl -Value $this 1051 | $this.TextBox.Change = { 1052 | param ($this) 1053 | $this.Control.ParentControl.ClearDropDown() 1054 | } 1055 | Register-ObjectEvent -InputObject $this.TextBox.NativeUI -EventName KeyDown -MessageData $this -Action { 1056 | $this = $event.MessageData 1057 | #$this.Control.TextBox.Text = $event.SourceArgs[1] 1058 | } | Out-Null 1059 | Register-ObjectEvent -InputObject $this.TextBox.NativeUI -EventName KeyUp -MessageData $this -Action { 1060 | $this = $event.MessageData 1061 | $this.Control.ShowDropDown() 1062 | } | Out-Null 1063 | $dropDown.AppendChild($this.TextBox.NativeUI) 1064 | 1065 | $this.DropDownMenu.SetAttribute("class", "dropdown-menu") 1066 | $dropDown.AppendChild($this.DropDownMenu) 1067 | 1068 | $this.AddNativeUIChild = { 1069 | param ( 1070 | [OouiElement] $element 1071 | ) 1072 | $menuItem = [Ooui.ListItem]::new() 1073 | #$element.NativeUI.SetAttribute("class", "btn btn-default") 1074 | $element.NativeUI.SetAttribute("class", "dropdown-item") 1075 | $element.NativeUI.Style.Width = "100%" 1076 | $element.NativeUI.Style.BorderColor = "transparent" 1077 | $menuItem.AppendChild($element.NativeUI) | Out-Null 1078 | $this.DropDownMenu.AppendChild($menuItem) | Out-Null 1079 | } 1080 | Add-Member -InputObject $this -Name "Text" -MemberType ScriptProperty ` 1081 | -Value { $this.TextBox.Text } ` 1082 | -SecondValue { $this.TextBox.Text = $args[0] } 1083 | $this.AddScriptBlockProperty("ItemsRequested") 1084 | } 1085 | 1086 | [void] ShowDropDown() { 1087 | $this.ClearDropDown() 1088 | $this.AddItems() 1089 | if (-not $this.DropDownMenu.ClassName.Contains("show")) { 1090 | $this.DropDownMenu.ClassName = $this.DropDownMenu.ClassName + " show" 1091 | } 1092 | } 1093 | 1094 | [void] ClearDropDown() { 1095 | $this.DropDownMenu.Children | ForEach-Object { 1096 | $this.DropDownMenu.RemoveChild($_) 1097 | } 1098 | if ($this.DropDownMenu.ClassName.Contains("show")) { 1099 | $this.DropDownMenu.ClassName = $this.DropDownMenu.ClassName.Replace(" show", "") 1100 | } 1101 | } 1102 | 1103 | [void] AddItems() { 1104 | $this.OnItemsRequested() 1105 | } 1106 | 1107 | [void] OnItemsRequested() { 1108 | [AutoCompleteItem[]] $items = Invoke-Command -ScriptBlock $this._ItemsRequested -ArgumentList $this 1109 | $items | ForEach-Object { 1110 | [OOuiButton] $button = [OOuiButton] @{ Caption = $_.Text } 1111 | Add-Member -InputObject $button -MemberType NoteProperty -Name AutoCompleteTextBox -Value $this.TextBox 1112 | Add-Member -InputObject $button -MemberType NoteProperty -Name AutoCompleteId -Value $_.Id 1113 | $button.Action = { 1114 | param ($this) 1115 | $this.Control.AutoCompleteTextBox.Text = $this.Control.AutoCompleteId 1116 | } 1117 | $this.AddChild($button) 1118 | } 1119 | } 1120 | 1121 | } 1122 | 1123 | class OouiCard : OouiElement { 1124 | hidden [div] $CardContainerDiv = [div]::new() 1125 | hidden [div] $CardHeaderDiv = [div]::new() 1126 | hidden [div] $CardIconDiv = [div]::new() 1127 | hidden [OouiStackPanel] $CardBodyDiv = [OouiStackPanel]::new() 1128 | hidden [icon] $CardIcon = [icon]::new() 1129 | hidden [Heading] $Header = [Heading]::new(4) 1130 | 1131 | hidden [OOuiIcon] $CurrentIcon = $null 1132 | 1133 | OouiCard() { 1134 | $this.SetNativeUI($this.CardContainerDiv) 1135 | $this.CardContainerDiv.AppendChild($this.CardHeaderDiv) 1136 | $this.CardContainerDiv.AppendChild($this.CardBodyDiv.NativeUI) 1137 | $this.CardHeaderDiv.AppendChild($this.CardIconDiv) 1138 | $this.CardHeaderDiv.AppendChild($this.Header) 1139 | $this.CardIconDiv.AppendChild($this.CardIcon) 1140 | 1141 | $this.WrapProperty("Caption", "Text", "Header") 1142 | Add-Member -InputObject $this -Name Icon -MemberType ScriptProperty -Value { 1143 | $this.CurrentIcon 1144 | } -SecondValue { 1145 | $this.CurrentIcon = $args[0] 1146 | if ($this.CurrentIcon -ne $null) { 1147 | $this.CardIcon.Text = $this.CurrentIcon.NativeUI.Text 1148 | } else { 1149 | $this.CardIcon.Text = "" 1150 | } 1151 | } 1152 | $this.AddNativeUIChild = { 1153 | param ( 1154 | [OouiElement] $element 1155 | ) 1156 | $listItem = [Div]::new() 1157 | if ($this.CardBodyDiv.Orientation -eq [Orientation]::Horizontal) { 1158 | $listItem.Style.float = "left" 1159 | } else { 1160 | $listItem.Style.clear = "both" 1161 | } 1162 | $this.CardBodyDiv.NativeUI.AppendChild($listItem) | Out-Null 1163 | $listItem.AppendChild($element.NativeUI) | Out-Null 1164 | } 1165 | $this.StyleComponents() 1166 | } 1167 | 1168 | [void] StyleComponents() { 1169 | $this.CardContainerDiv.ClassName = "card" 1170 | $this.Header.ClassName = "card-title" 1171 | $this.Header.Style.display = "inline" 1172 | $this.CardHeaderDiv.ClassName = "card-header card-header-icon " 1173 | $this.CardIconDiv.ClassName = "card-icon" 1174 | $this.CardIcon.ClassName = "material-icons" 1175 | $this.CardBodyDiv.NativeUI.ClassName = "card-body" 1176 | $this.CardBodyDiv.Orientation = [Orientation]::Vertical 1177 | } 1178 | } 1179 | 1180 | class OouiImage : OouiElement { 1181 | 1182 | OouiImage() { 1183 | $image = [Image]::new() 1184 | $this.SetNativeUI($image) 1185 | $this.WrapProperty("Source", "Source") 1186 | Add-Member -InputObject $this -Name Width -MemberType ScriptProperty -Value { 1187 | $this.NativeUI.Style.width 1188 | } -SecondValue { 1189 | $this.NativeUI.Style.width = $args[0] 1190 | } 1191 | } 1192 | } 1193 | 1194 | class OouiTextEditor : OouiElement { 1195 | 1196 | OouiTextEditor() { 1197 | $this.SetNativeUI([TextArea]::new()) 1198 | $this.WrapProperty("Text", "Value") 1199 | $this.AddScriptBlockProperty("Change") 1200 | Add-Member -InputObject $this -Name Height -MemberType ScriptProperty -Value { 1201 | [int] $this.NativeUI.Rows 1202 | } -SecondValue { 1203 | $this.NativeUI.Rows = $args[0] 1204 | } 1205 | Add-Member -InputObject $this -Name Width -MemberType ScriptProperty -Value { 1206 | [int] $this.NativeUI.Columns 1207 | } -SecondValue { 1208 | $this.NativeUI.Columns = $args[0] 1209 | } 1210 | 1211 | Register-ObjectEvent -InputObject $this.NativeUI -EventName Change -MessageData $this -Action { 1212 | $this = $event.MessageData 1213 | $this.Control.OnChange() 1214 | } | Out-Null 1215 | } 1216 | 1217 | [void] OnChange() { 1218 | Invoke-Command -ScriptBlock $this._Change -ArgumentList $this 1219 | } 1220 | } 1221 | 1222 | class OouiExpander : OouiElement { 1223 | hidden [div] $AcordionDiv = [div] @{ ClassName = "accordion" } 1224 | hidden [div] $AcodionItemDiv = [div] @{ ClassName = "accordion-item" } 1225 | hidden [Heading] $AccordionHeader = [Heading]::new(2) 1226 | hidden [Button] $AccordionButton = [Button] @{ ClassName = "accordion-button" } 1227 | hidden [div] $AccordionCollapseDiv = [div] @{ ClassName = "accordion-collapse collapse show" } 1228 | hidden [OouiStackPanel] $Body = [OouiStackPanel]::new() 1229 | 1230 | OouiExpander() { 1231 | $this.SetNativeUI($this.AcordionDiv) 1232 | $this.AcordionDiv.AppendChild($this.AcodionItemDiv) 1233 | $this.AccordionHeader.ClassName = "accordion-header" 1234 | $this.AcodionItemDiv.AppendChild($this.AccordionHeader) 1235 | $this.AccordionButton.SetAttribute("data-bs-toggle", "collapse") 1236 | $this.AccordionButton.SetAttribute("data-bs-target", "#$($this.AccordionCollapseDiv.Id)") 1237 | $this.AccordionButton.SetAttribute("aria-expanded", "true") 1238 | $this.AccordionButton.SetAttribute("aria-controls", $this.AccordionCollapseDiv.Id) 1239 | $this.AccordionHeader.AppendChild($this.AccordionButton) 1240 | #$this.AccordionCollapseDiv.SetAttribute("data-bs-parent", "#$($this.AcordionDiv.Id)") 1241 | $this.AcodionItemDiv.AppendChild($this.AccordionCollapseDiv) 1242 | $this.Body.NativeUI.ClassName = "accordion-body" 1243 | $this.AccordionCollapseDiv.AppendChild($this.Body.NativeUI) 1244 | 1245 | $this.WrapProperty("Caption", "Text", "AccordionButton") 1246 | 1247 | $this.AddNativeUIChild = { 1248 | param ( 1249 | [OouiElement] $element 1250 | ) 1251 | $listItem = [Div]::new() 1252 | if ($this.Body.Orientation -eq [Orientation]::Horizontal) { 1253 | $listItem.Style.float = "left" 1254 | } else { 1255 | $listItem.Style.clear = "both" 1256 | } 1257 | $this.Body.NativeUI.AppendChild($listItem) | Out-Null 1258 | $listItem.AppendChild($element.NativeUI) | Out-Null 1259 | } 1260 | } 1261 | 1262 | } 1263 | 1264 | class OouiInteger : OouiTextBox { 1265 | 1266 | OouiInteger() { 1267 | $this.TextAlignment = [TextAlignment]::Right 1268 | $this.Pattern = '^[\d]+$' 1269 | $this.DefaultText = "0" 1270 | $this.Text = "0" 1271 | } 1272 | } 1273 | 1274 | class OouiDouble : OouiTextBox { 1275 | 1276 | OouiDouble() { 1277 | $this.TextAlignment = [TextAlignment]::Right 1278 | $this.Pattern = '^[\d\.]+$' 1279 | $this.DefaultText = "0.0" 1280 | $this.Text = "0.0" 1281 | } 1282 | } 1283 | 1284 | #region ComboBox with Select 1285 | #class OouiComboBoxItem : OouiElement { 1286 | # 1287 | # OouiComboBoxItem() { 1288 | # $this.SetNativeUI([Option]::new()) 1289 | # $this.WrapProperty("Id" , "Value") 1290 | # $this.WrapProperty("Caption", "Label") 1291 | # } 1292 | #} 1293 | # 1294 | #class OouiComboBox : OouiElement { 1295 | # 1296 | # OouiComboBox() { 1297 | # $this.SetNativeUI([Select]::new()) 1298 | # Add-Member -InputObject $this -Name Text -MemberType ScriptProperty -Value { 1299 | # $this.NativeUI.Value 1300 | # } -SecondValue { 1301 | # $id = [String] $Args[0] 1302 | # $this.NativeUI.value = $id 1303 | # #$selectedItem = $this.NativeUI.Children | Where-Object { $_.Value -eq $id } 1304 | # #if ($selectedItem -ne $null) { 1305 | # # $this.NativeUI.Value = $id 1306 | # # $selectedItem.DefaultSelected = $true 1307 | # #} 1308 | # } 1309 | # $this.AddScriptBlockProperty("Change") 1310 | # Register-ObjectEvent -InputObject $this.NativeUI -EventName Change -MessageData $this -Action { 1311 | # $this = $event.MessageData 1312 | # $this.Control.OnChange() 1313 | # } | Out-Null 1314 | # $this.AddNativeUIChild = { 1315 | # param ( 1316 | # [OouiElement] $element 1317 | # ) 1318 | # $this.NativeUI.AppendChild($element.NativeUI) 1319 | # } 1320 | # } 1321 | # 1322 | # [void] OnChange() { 1323 | # $this.InvokeTrappableCommand($this._Change, $this) 1324 | # } 1325 | # 1326 | #} 1327 | #endregion 1328 | 1329 | class OouiComboBoxItem : OouiMenuItem { 1330 | [String] $Id 1331 | 1332 | OouiComboBoxItem() { 1333 | } 1334 | } 1335 | 1336 | class OouiCombobox : OouiDropDownMenu { 1337 | [String] $SelectedId 1338 | [String] $BackgroundColor = "transparent" 1339 | [String] $Color = "black" 1340 | 1341 | OouiCombobox() { 1342 | Add-Member -InputObject $this -Name Text -MemberType ScriptProperty -Value { 1343 | $this.SelectedId 1344 | } -SecondValue { 1345 | $id = $args[0] 1346 | if ($this.SelectedId -ne $id) { 1347 | $selectedItem = $this.Children | Where-Object { $_.Id -eq $id } 1348 | if ($selectedItem -ne $null) { 1349 | $this.DropDownToogle.Caption = $selectedItem.Caption 1350 | $this.SelectedId = $id 1351 | $this.Control.OnChange() 1352 | } 1353 | } 1354 | } 1355 | $this.AddScriptBlockProperty("Change") 1356 | $this.AddNativeUIChild = { 1357 | param ( 1358 | [OouiElement] $element 1359 | ) 1360 | $menuItem = [Ooui.ListItem]::new() 1361 | $element.NativeUI.SetAttribute("class", "dropdown-item") 1362 | $element.NativeUI.Style.Width = "100%" 1363 | $element.NativeUI.Style.BorderColor = "transparent" 1364 | Add-Member -InputObject $element -MemberType NoteProperty -Name ComboBox -Value $this 1365 | $element.Action = { 1366 | param ($this) 1367 | $this.ComboBox.Text = $this.Id 1368 | } 1369 | $menuItem.AppendChild($element.NativeUI) | Out-Null 1370 | $this.DropDownMenu.AppendChild($menuItem) | Out-Null 1371 | } 1372 | $this.StyleComponents() 1373 | } 1374 | 1375 | [void] OnChange() { 1376 | Invoke-Command -ScriptBlock $this._Change -ArgumentList $this 1377 | } 1378 | 1379 | [void] StyleComponents () { 1380 | $this.DropDownToogle.NativeUI.Style.BackgroundColor = $this.BackgroundColor 1381 | $this.DropDownToogle.NativeUI.Style.Color = $this.Color 1382 | } 1383 | 1384 | } 1385 | 1386 | class OouiFileUpload : OouiElement { 1387 | [Anchor] $Anchor = [Anchor] @{ Href = "/files"; Target = "_blank" } 1388 | hidden [Span] $LabelNativeUI = [Span]::new() 1389 | 1390 | OouiFileUpload() { 1391 | $this.SetNativeUI($this.Anchor) 1392 | $this.WrapProperty("Caption", "Text", "LabelNativeUI") 1393 | $this.NativeUI.AppendChild($this.LabelNativeUI) 1394 | } 1395 | 1396 | } -------------------------------------------------------------------------------- /UIfied/UIfiedWPFBase.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Collections.Generic 2 | using namespace System.Windows 3 | using namespace System.Windows.Controls 4 | using namespace System.Windows.Input 5 | 6 | class WPFElement : UIElement { 7 | 8 | WPFElement() { 9 | $this.WrapProperty("Enable", "IsEnabled") 10 | Add-Member -InputObject $this -Name Visible -MemberType ScriptProperty -Value { 11 | $this.NativeUI.Visibility -eq [Visibility]::Visible 12 | } -SecondValue { 13 | if ($args[0]) { 14 | $this.NativeUI.Visibility = [Visibility]::Visible 15 | } else { 16 | $this.NativeUI.Visibility = [Visibility]::Collapsed 17 | } 18 | } 19 | $this.AddNativeUIChild = { 20 | param ( 21 | [WPFElement] $element 22 | ) 23 | $this.NativeUI.AddChild($element.NativeUI) | Out-Null 24 | } 25 | $this.RemoveNativeUIChild = { 26 | param ( 27 | [WPFElement] $element 28 | ) 29 | $this.NativeUI.Children.Remove($element.NativeUI) | Out-Null 30 | } 31 | $this.ShowError = { 32 | param ( 33 | [Object] $errorObject 34 | ) 35 | [MessageBox]::Show($errorObject) 36 | } 37 | } 38 | 39 | } 40 | 41 | class WPFHost : UIHost { 42 | [HashTable] $SyncHash 43 | 44 | [void] ShowFrame([ScriptBlock] $frameScriptBlock) { 45 | $this.SyncHash = [HashTable]::Synchronized(@{}) 46 | $this.SyncHash.Errors = @() 47 | $Global:SyncHash = $this.SyncHash 48 | 49 | $Window = Invoke-Command -ScriptBlock $frameScriptBlock 50 | $window.ShowDialog() 51 | } 52 | } 53 | 54 | class WPFWindow : WindowBase { 55 | [Application] $Application = [System.Windows.Application]::new() 56 | [Window] $windowNativeUI = [Window]::new() 57 | 58 | WPFWindow() { 59 | $this.StyleApplication() 60 | $this.windowNativeUI.SizeToContent = 'WidthAndHeight' 61 | $this.windowNativeUI.Margin = 10 62 | $this.Application.MainWindow = $this.windowNativeUI 63 | 64 | $this.SetNativeUI($this.windowNativeUI) 65 | $this.WrapProperty("Caption", "Title") 66 | $this.AddScriptBlockProperty("Loaded") 67 | $this.AddNativeUIChild = { 68 | param ( 69 | [WPFElement] $element 70 | ) 71 | $this.NativeUI.Content = $element.NativeUI 72 | } 73 | $this.StyleCOmponents() 74 | } 75 | 76 | [void] StyleApplication() { 77 | } 78 | 79 | [void] StyleComponents() { 80 | } 81 | 82 | [void] ShowDialog() { 83 | $this.NativeUI.ShowDialog() 84 | } 85 | 86 | [void] OnLoaded() { 87 | Invoke-Command -ScriptBlock $this._Loaded -ArgumentList $this 88 | } 89 | 90 | } 91 | 92 | class WPFStackPanel : WPFElement { 93 | 94 | WPFStackPanel() { 95 | $this.SetNativeUI([StackPanel]::new()) 96 | $this.WrapProperty("Orientation", "Orientation") 97 | } 98 | } 99 | 100 | class WPFLabel : WPFElement { 101 | 102 | WPFLabel() { 103 | $this.SetNativeUI([Label]::new()) 104 | $this.WrapProperty("Caption", "Content") 105 | } 106 | } 107 | 108 | class WPFIcon : WPFLabel { 109 | hidden [String] $KindName 110 | 111 | WPFIcon() { 112 | Add-Member -InputObject $this -Name Kind -MemberType ScriptProperty -Value { 113 | $this.KindName 114 | } -SecondValue { 115 | $this.KindName = $args[0] 116 | $this.RefreshCaption() 117 | } 118 | } 119 | 120 | [void] RefreshCaption() { 121 | $this.Caption = [IconStrinfy]::ToIconString($this.KindName) 122 | } 123 | } 124 | 125 | class WPFButton : WPFElement { 126 | hidden [String] $CaptionText = "" 127 | hidden [String] $IconText = "" 128 | [bool] $RightIcon = $false 129 | 130 | WPFButton() { 131 | $this.SetNativeUI([Button]::new()) 132 | Add-Member -InputObject $this -Name Caption -MemberType ScriptProperty -Value { 133 | $this.CaptionText 134 | } -SecondValue { 135 | $this.CaptionText = $args[0] 136 | $this.RefreshCaption() 137 | } 138 | Add-Member -InputObject $this -Name Icon -MemberType ScriptProperty -Value { 139 | $this.IconText 140 | } -SecondValue { 141 | if ($args[0] -ne $null) { 142 | $this.IconText = [IconStrinfy]::ToIconString($args[0].Kind) 143 | $this.RefreshCaption() 144 | } else { 145 | $this.IconText = "" 146 | $this.RefreshCaption() 147 | } 148 | } 149 | $this.AddScriptBlockProperty("Action") 150 | $this.NativeUI.Add_Click({ $this.Control.OnAction() }) 151 | } 152 | 153 | [void] RefreshCaption() { 154 | if ($this.RightIcon) { 155 | $this.NativeUI.Content = ($this.CaptionText + " " + $this.IconText).Trim() 156 | } else { 157 | $this.NativeUI.Content = ($this.IconText + " " + $this.CaptionText).Trim() 158 | } 159 | } 160 | 161 | [void] OnAction() { 162 | $this.InvokeTrappableCommand($this._Action, $this) 163 | } 164 | } 165 | 166 | class WPFTextBox : WPFElement { 167 | [String] $Pattern = "" 168 | [String] $DefaultText = "" 169 | 170 | WPFTextBox() { 171 | $this.SetNativeUIControl() 172 | $this.AddScriptBlockProperty("Change") 173 | $this.NativeUI.Add_LostFocus({ 174 | if ($this.Control.Pattern -ne "") { 175 | $regex = [Regex]::new($this.Control.Pattern) 176 | if (-not $regex.IsMatch($this.Control.Text)) { 177 | $this.Control.Text = $this.Control.DefaultText 178 | } 179 | } 180 | }) 181 | } 182 | 183 | [void] SetNativeUIControl() { 184 | $this.SetNativeUI([TextBox]::new()) 185 | $this.WrapProperty("Text", "Text") 186 | $this.WrapProperty("TextAlignment", "TextAlignment") 187 | $this.NativeUI.Add_TextChanged({ $this.Control.OnChange() }) 188 | } 189 | 190 | [void] OnChange() { 191 | $this.InvokeTrappableCommand($this._Change, $this) 192 | } 193 | 194 | } 195 | 196 | class WPFPassword : WPFTextBox { 197 | 198 | [void] SetNativeUIControl() { 199 | $this.SetNativeUI([PasswordBox]::new()) 200 | $this.WrapProperty("Text", "Password") 201 | $this.NativeUI.Add_PasswordChanged({ $this.Control.OnChange() }) 202 | } 203 | 204 | } 205 | 206 | class WPFCheckBox : WPFElement { 207 | 208 | WPFCheckBox() { 209 | $this.SetNativeUI([CheckBox]::new()) 210 | $this.WrapProperty("Caption", "Content") 211 | $this.WrapProperty("IsChecked", "IsChecked") 212 | $this.AddScriptBlockProperty("Click") 213 | $this.NativeUI.Add_Click({ $this.Control.OnClick() }) 214 | } 215 | 216 | [void] OnClick() { 217 | $this.InvokeTrappableCommand($this._Click, $this) 218 | } 219 | 220 | } 221 | 222 | class WPFRadioButton : WPFElement { 223 | 224 | WPFRadioButton() { 225 | $this.SetNativeUI([RadioButton]::new()) 226 | $this.WrapProperty("Caption", "Content") 227 | $this.WrapProperty("IsChecked", "IsChecked") 228 | $this.AddScriptBlockProperty("Click") 229 | $this.NativeUI.Add_Click({ $this.Control.OnClick() }) 230 | } 231 | 232 | [void] OnClick() { 233 | $this.InvokeTrappableCommand($this._Click, $this) 234 | } 235 | 236 | } 237 | 238 | class WPFRadioGroup : WPFElement { 239 | hidden $StackPanel 240 | 241 | WPFRadioGroup() { 242 | $this.SetNativeUI([GroupBox]::new()) 243 | $this.StackPanel = [StackPanel]::new() 244 | $this.NativeUI.Content = $this.StackPanel 245 | $this.AddNativeUIChild = { 246 | param ( 247 | [WPFElement] $element 248 | ) 249 | $this.StackPanel.AddChild($element.NativeUI) 250 | } 251 | } 252 | 253 | } 254 | 255 | class WPFList : WPFStackPanel { 256 | [List[ListItem]] $Items = [List[ListItem]]::new() 257 | 258 | WPFList() { 259 | $this.Orientation = [Orientation]::Horizontal 260 | } 261 | 262 | [void] AddColumn([WPFListColumn] $listColumn) { 263 | $column = [WPFStackPanel]::new() 264 | $column.Orientation = [Orientation]::Vertical 265 | $title = [WPFLabel]::new() 266 | $title.Caption = $listColumn.Title 267 | $column.AddChild($title) 268 | $this.AddChild($column) 269 | } 270 | 271 | [void] AddItem([ListItem] $listItem) { 272 | $this.Items.Add($listItem) 273 | $columnIndex = 0 274 | $this.Children | ForEach-Object { 275 | $column = $_ 276 | $cell = $listItem.Children.Item($columnIndex) 277 | $cell.NativeUI.Height = 25 278 | $column.AddChild($cell) 279 | $columnIndex++ 280 | } 281 | } 282 | 283 | [void] RemoveItem([ListItem] $listItem) { 284 | $this.Items.Remove($listItem) 285 | $columnIndex = 0 286 | $this.Children | ForEach-Object { 287 | $column = $_ 288 | $cell = $listItem.Children.Item($columnIndex) 289 | $column.RemoveChild($cell) 290 | $columnIndex++ 291 | } 292 | } 293 | 294 | [void] Clear() { 295 | $this.Items.ToArray() | ForEach-Object { 296 | $this.RemoveItem($_) 297 | } 298 | } 299 | } 300 | 301 | class WPFListColumn { 302 | [String] $Name 303 | [String] $Title 304 | } 305 | 306 | class WPFTabItem : WPFElement { 307 | hidden $StackPanelNativeUI 308 | 309 | WPFTabItem() { 310 | $this.SetNativeUI([TabItem]::new()) 311 | $this.StackPanelNativeUI = [StackPanel]::new() 312 | $this.NativeUI.Content = $this.StackPanelNativeUI 313 | $this.WrapProperty("Caption", "Header") 314 | $this.AddNativeUIChild = { 315 | param ( 316 | [WPFElement] $element 317 | ) 318 | $this.StackPanelNativeUI.AddChild($element.NativeUI) | Out-Null 319 | } 320 | } 321 | 322 | } 323 | 324 | class WPFTabControl : WPFElement { 325 | 326 | WPFTabControl() { 327 | $this.SetNativeUI([TabControl]::new()) 328 | } 329 | 330 | } 331 | 332 | class WPFModal : WPFElement { 333 | [StackPanel] $Stack 334 | [Window] $ModalWindow 335 | 336 | WPFModal() { 337 | $this.Stack = [StackPanel]::new() 338 | $this.SetNativeUI($this.Stack) 339 | 340 | $this.ModalWindow = [Window]::new() 341 | $this.ModalWindow.SizeToContent = 'WidthAndHeight' 342 | $this.ModalWindow.Margin = 10 343 | $this.ModalWindow.Content = [StackPanel]::new() 344 | $this.WrapProperty("Title", "Title", "ModalWindow") 345 | 346 | $this.AddNativeUIChild = { 347 | param ( 348 | [WPFElement] $element 349 | ) 350 | $this.ModalWindow.Content.AddChild($element.NativeUI) | Out-Null 351 | } 352 | $this.StyleComponets() 353 | } 354 | 355 | [void] Show() { 356 | $this.ModalWindow.WindowStartupLocation = "CenterOwner" 357 | $this.ModalWindow.ShowDialog() 358 | } 359 | 360 | [void] Hide() { 361 | $this.ModalWindow.Hide() 362 | } 363 | 364 | [void] StyleComponets() { 365 | $this.ModalWindow.WindowStyle = [WindowStyle]::None 366 | } 367 | } 368 | 369 | class WPFTimer : WPFElement { 370 | [System.Windows.Threading.DispatcherTimer] $Timer 371 | [Double] $Interval = 1000 372 | 373 | WPFTimer () { 374 | $label = [Label]::new() 375 | $label.Visibility = [Visibility]::Collapsed 376 | $this.SetNativeUI($label) 377 | $this.AddScriptBlockProperty("Elapsed") 378 | $this.Timer = New-Object System.Windows.Threading.DispatcherTimer 379 | Add-Member -InputObject $this.Timer -MemberType NoteProperty -Name Control -Value $this 380 | $this.Timer.Add_Tick({ 381 | $this.Control.OnElapsed() 382 | #[System.Windows.Input.CommandManager]::InvalidateRequerySuggested() 383 | }) 384 | } 385 | 386 | [void] OnElapsed() { 387 | Invoke-Command -ScriptBlock $this._Elapsed -ArgumentList $this 388 | } 389 | 390 | [void] Start() { 391 | $this.Timer.Interval = [TimeSpan]::FromSeconds($this.Interval / 1000) 392 | $this.Timer.Start() 393 | } 394 | 395 | [void] Stop() { 396 | $this.Timer.Stop() 397 | } 398 | } 399 | 400 | class WPFDatePicker : WPFElement { 401 | 402 | WPFDatePicker() { 403 | $this.SetNativeUI([DatePicker]::new()) 404 | $this.WrapProperty("Value", "SelectedDate") 405 | $this.AddScriptBlockProperty("Change") 406 | $this.NativeUI.Add_SelectedDateChanged({ $this.Control.OnChange() }) 407 | } 408 | 409 | [void] OnChange() { 410 | $this.InvokeTrappableCommand($this._Change, $this) 411 | } 412 | } 413 | 414 | class WPFTimePicker : WPFElement { 415 | 416 | WPFTimePicker() { 417 | $textBox = [TextBox]::new() 418 | $textBox.MaxLength = 5 419 | $this.SetNativeUI($textBox) 420 | Add-Member -InputObject $this -Name Value -MemberType ScriptProperty -Value { 421 | $this.GetTextTime() 422 | } -SecondValue { 423 | $this.NativeUI.Text = $args[0] 424 | } 425 | $this.AddScriptBlockProperty("Change") 426 | $this.NativeUI.Add_TextChanged({ $this.Control.OnChange() }) 427 | $this.AddScriptBlockProperty("LostFocus") 428 | $this.NativeUI.Add_LostFocus({ $this.Control.OnLostFocus() }) 429 | } 430 | 431 | [void] OnChange() { 432 | $this.InvokeTrappableCommand($this._Change, $this) 433 | } 434 | 435 | [void] OnLostFocus() { 436 | $this.Value = $this.GetTextTime() 437 | $this.InvokeTrappableCommand($this._LostFocus, $this) 438 | } 439 | 440 | hidden [String] GetTextTime() { 441 | [DateTime] $dateTime = [DateTime]::Today 442 | if (-not [DateTime]::TryParse("2000-01-01 " + $this.NativeUI.Text, [ref] $dateTime)) { 443 | return "00:00" 444 | } else { 445 | return $dateTime.ToShortTimeString() 446 | } 447 | } 448 | 449 | } 450 | 451 | class WPFBrowser : WPFStackPanel { 452 | [Object[]] $Data = [Object[]] @() 453 | [int] $PageRows = 10 454 | [int] $CurrentPage = 0 455 | [Boolean] $IsEditable = $true 456 | [Object] $CurrentRow 457 | 458 | #region Components Declaration 459 | 460 | hidden [WPFListColumn[]] $Columns = [WPFListColumn[]] @() 461 | hidden [WPFListColumn] $EditionColumn 462 | hidden [WPFList] $List = [WPFList]::new() 463 | hidden [WPFStackPanel] $ButtonPanel = [WPFStackPanel]::new() 464 | hidden [WPFButton] $FirstButton = [WPFButton]::new() 465 | hidden [WPFButton] $PreviousButton = [WPFButton]::new() 466 | hidden [WPFButton] $NextButton = [WPFButton]::new() 467 | hidden [WPFButton] $LastButton = [WPFButton]::new() 468 | hidden [WPFButton] $AddNewButton = [WPFButton]::new() 469 | 470 | #endregion 471 | 472 | WPFBrowser() { 473 | $this.AddScriptBlockProperty("AddNew") 474 | $this.AddScriptBlockProperty("Edit") 475 | $this.AddScriptBlockProperty("Delete") 476 | $this.AddChild($this.List) 477 | $this.AddButtons() 478 | } 479 | 480 | #region Components Creation 481 | 482 | hidden [void] AddButtons() { 483 | $this.ButtonPanel = [WPFStackPanel]::new() 484 | $this.ButtonPanel.Orientation = "Horizontal" 485 | 486 | $this.FirstButton.Action = { $this.Parent.Parent.OnMoveFirst() } 487 | $this.PreviousButton.Action = { $this.Parent.Parent.OnMovePrevious() } 488 | $this.NextButton.Action = { $this.Parent.Parent.OnMoveNext() } 489 | $this.LastButton.Action = { $this.Parent.Parent.OnMoveLast() } 490 | $this.AddNewButton.Action = { $this.Parent.Parent.OnAddNew() } 491 | 492 | $this.ButtonPanel.AddChild($this.FirstButton) 493 | $this.ButtonPanel.AddChild($this.PreviousButton) 494 | $this.ButtonPanel.AddChild($this.NextButton) 495 | $this.ButtonPanel.AddChild($this.LastButton) 496 | $this.ButtonPanel.AddChild($this.AddNewButton) 497 | 498 | $this.StyleComponents() 499 | 500 | $this.AddChild($this.ButtonPanel) 501 | } 502 | 503 | [void] AddColumn([WPFListColumn] $listColumn) { 504 | $this.Columns += $listColumn 505 | $this.List.AddColumn($listColumn) 506 | } 507 | 508 | [void] CreateList() { 509 | $this.List.Clear() 510 | $this.CreateEditable() 511 | 0..($this.PageRows - 1) | ForEach-Object { 512 | $listItem = $this.GetInitialListItem($_) 513 | $this.List.AddItem($listItem) 514 | } 515 | } 516 | 517 | hidden [ListItem] GetInitialListItem([int] $rowIndex) { 518 | $hash = $this.GetInitialHash() 519 | $listItem = [ListItem]::new() 520 | $this.Columns | ForEach-Object { 521 | $column = $_ 522 | if ($column -eq $this.EditionColumn -and $this.IsEditable) { 523 | $this.AddEditionButtons($hash, $listItem, $rowIndex) 524 | } else { 525 | $this.AddCell($hash, $column.Name, $listItem, $rowIndex) 526 | } 527 | } 528 | return $listItem 529 | } 530 | 531 | hidden [Object] GetInitialHash() { 532 | $hash = @{} 533 | $this.Columns | ForEach-Object { 534 | $column = $_ 535 | $hash += @{ "$($column.Name)" = "" } 536 | } 537 | return $hash 538 | } 539 | 540 | hidden [void] AddEditionButtons([Object] $hash, [ListItem] $listItem, [int] $rowIndex) { 541 | $editionPanel = [WPFStackPanel]::new() 542 | $editionPanel.Orientation = "Horizontal" 543 | $listItem.AddChild($editionPanel) 544 | 545 | $editButton = [WPFButton]::new() 546 | Add-Member -InputObject $editButton -MemberType NoteProperty -Name CurrentRow -Value $hash 547 | $editButton.Action = { 548 | $this.Parent.Parent.Parent.Parent.CurrentRow = $this.CurrentRow 549 | $this.Parent.Parent.Parent.Parent.OnEdit() 550 | } 551 | $editionPanel.AddChild($editButton) 552 | 553 | $deleteButton = [WPFButton]::new() 554 | Add-Member -InputObject $deleteButton -MemberType NoteProperty -Name CurrentRow -Value $hash 555 | $deleteButton.Action = { 556 | $this.Parent.Parent.Parent.Parent.CurrentRow = $this.CurrentRow 557 | $this.Parent.Parent.Parent.Parent.OnDelete() 558 | } 559 | $editionPanel.AddChild($deleteButton) 560 | $this.StyleEditionButtons($editButton, $deleteButton, $rowIndex) 561 | } 562 | 563 | hidden [void] AddCell([Object] $hash, [string] $columnName, [ListItem] $listItem, [int] $rowIndex) { 564 | $itemLabel = [WPFLabel]::new() 565 | $itemLabel.Caption = $hash."$columnName" 566 | $listItem.AddChild($itemLabel) 567 | $this.StyleCell($itemLabel, $rowIndex) 568 | } 569 | 570 | hidden [void] CreateEditable() { 571 | if ($this.EditionColumn -eq $null -and $this.IsEditable) { 572 | $this.CreateEditionColumn() 573 | } 574 | $this.AddNewButton.Visible = $this.IsEditable 575 | } 576 | 577 | hidden [void] CreateEditionColumn() { 578 | $this.EditionColumn = New-Object WPFListColumn -Property @{Name = "_Edition"; Title = "_"} 579 | $this.AddColumn($this.EditionColumn) 580 | } 581 | 582 | #endregion 583 | 584 | #region Data show 585 | 586 | [void] Refresh() { 587 | # Fill Rows 588 | $rowIndex = 0 589 | $selectedData = $this.GetSelectedData() 590 | $selectedData | ForEach-Object { 591 | $hash = $_ 592 | $columnIndex = 0 593 | $this.Columns | Select-Object -First ($this.Columns.Count) | ForEach-Object { 594 | $column = $_ 595 | if ($this.EditionColumn -ne $column) { 596 | $this.List.Children.Item($columnIndex).Children.Item($rowIndex + 1).Caption = $hash."$($column.Name)" 597 | } else { 598 | $buttons = $this.List.Children.Item($columnIndex).Children.Item($rowIndex + 1).Children 599 | $buttons.Item(0).CurrentRow = $hash 600 | $buttons.Item(1).CurrentRow = $hash 601 | $buttons.Item(0).Visible = $true 602 | $buttons.Item(1).Visible = $true 603 | } 604 | $columnIndex++ 605 | } 606 | $rowIndex++ 607 | } 608 | # EmptyRows 609 | for ($rowIndex = $selectedData.Count + 1; $rowIndex -le $this.PageRows; $rowIndex++) { 610 | $columnIndex = 0 611 | $this.Columns | Select-Object -First ($this.Columns.Count) | ForEach-Object { 612 | $column = $_ 613 | if ($this.EditionColumn -ne $column) { 614 | $this.List.Children.Item($columnIndex).Children.Item($rowIndex).Caption = "" 615 | } else { 616 | $buttons = $this.List.Children.Item($columnIndex).Children.Item($rowIndex).Children 617 | $buttons.Item(0).Visible = $false 618 | $buttons.Item(1).Visible = $false 619 | } 620 | $columnIndex++ 621 | } 622 | } 623 | } 624 | 625 | hidden [Object[]] GetSelectedData() { 626 | return $this.Data | Select-Object -Skip ($this.CurrentPage * $this.PageRows) -First $this.PageRows 627 | } 628 | 629 | hidden [int] GetLastPage() { 630 | $lastPage = [Math]::Truncate($this.Data.Count / $this.PageRows) 631 | if (($this.Data.Count % $this.PageRows) -eq 0) { 632 | $lastPage-- 633 | } 634 | return $lastPage 635 | } 636 | 637 | #endregion 638 | 639 | #region Style 640 | 641 | [Media.Brush] $ShadowBrush = ([Media.Brush] "#FFF3F3F3") 642 | 643 | [Media.Brush] GetRowBackground([int] $rowIndex) { 644 | if (($rowIndex % 2) -eq 0) { 645 | return $this.ShadowBrush 646 | } else { 647 | return [Media.Brushes]::Transparent 648 | } 649 | } 650 | 651 | [void] StyleComponents() { 652 | $this.ButtonPanel.NativeUI.HorizontalAlignment = "Right" 653 | 654 | $this.FirstButton.Caption = "|<" 655 | $this.PreviousButton.Caption = "<<" 656 | $this.NextButton.Caption = ">>" 657 | $this.LastButton.Caption = ">|" 658 | $this.AddNewButton.Caption = "+" 659 | 660 | $this.FirstButton.NativeUI.Margin = 10 661 | $this.PreviousButton.NativeUI.Margin = 10 662 | $this.NextButton.NativeUI.Margin = 10 663 | $this.LastButton.NativeUI.Margin = 10 664 | $this.AddNewButton.NativeUI.Margin = 10 665 | } 666 | 667 | [void] StyleCell($cell, [int] $rowIndex) { 668 | $cell.NativeUI.Background = $this.GetRowBackground($rowIndex) 669 | } 670 | 671 | [void] StyleEditionButtons([WPFButton] $editButton, [WPFButton] $deleteButton, [int] $rowIndex) { 672 | $editButton.Caption = "/" 673 | $deleteButton.Caption = "X" 674 | $editButton.Parent.NativeUI.Background = $this.GetRowBackground($rowIndex) 675 | } 676 | 677 | #endregion 678 | 679 | #region Move Events 680 | 681 | hidden [void] OnMoveFirst() { 682 | $this.CurrentPage = 0 683 | $this.Refresh() 684 | } 685 | 686 | hidden [void] OnMovePrevious() { 687 | if ($this.CurrentPage -gt 0) { 688 | $this.CurrentPage-- 689 | } 690 | $this.Refresh() 691 | } 692 | 693 | hidden [void] OnMoveNext() { 694 | if ($this.CurrentPage -lt $this.GetLastPage()) { 695 | $this.CurrentPage++ 696 | } 697 | $this.Refresh() 698 | } 699 | 700 | hidden [void] OnMoveLast() { 701 | $this.CurrentPage = $this.GetLastPage() 702 | $this.Refresh() 703 | } 704 | 705 | #endregion 706 | 707 | #region CRUD Events 708 | 709 | hidden [void] OnAddNew() { 710 | $this.InvokeTrappableCommand($this._AddNew, $this) 711 | } 712 | 713 | hidden [void] OnEdit() { 714 | $this.InvokeTrappableCommand($this._Edit, $this) 715 | } 716 | 717 | hidden [void] OnDelete() { 718 | $this.InvokeTrappableCommand($this._Delete, $this) 719 | } 720 | 721 | #endregion 722 | 723 | } 724 | 725 | class WPFMenuItem : WPFElement { 726 | 727 | WPFMenuItem() { 728 | $this.SetNativeUI([MenuItem]::new()) 729 | $this.WrapProperty("Caption", "Header") 730 | $this.AddScriptBlockProperty("Action") 731 | $this.NativeUI.Add_Click({ $this.Control.OnAction() }) 732 | } 733 | 734 | [void] OnAction() { 735 | $this.InvokeTrappableCommand($this._Action, $this) 736 | } 737 | } 738 | 739 | class WPFDropDownMenu : WPFButton { 740 | 741 | WPFDropDownMenu() { 742 | $this.RightIcon = $true 743 | $this.Icon = [WPFIcon] @{ Kind = "chevron_down" } 744 | 745 | $this.NativeUI.ContextMenu = [ContextMenu]::new() 746 | $this.AddNativeUIChild = { 747 | param ( 748 | [WPFElement] $element 749 | ) 750 | $this.NativeUI.ContextMenu.Items.Add($element.NativeUI) 751 | } 752 | $this.RemoveNativeUIChild = { 753 | param ( 754 | [WPFElement] $element 755 | ) 756 | $this.NativeUI.ContextMenu.Items.Remove($element.NativeUI) 757 | } 758 | 759 | $this.Action = { 760 | param($this) 761 | $this.NativeUI.ContextMenu.IsOpen = -not $this.NativeUI.ContextMenu.IsOpen 762 | } 763 | } 764 | 765 | } 766 | 767 | class WPFAutoComplete : WPFTextBox { 768 | 769 | WPFAutoComplete() { 770 | $this.NativeUI.ContextMenu = [ContextMenu]::new() 771 | $this.NativeUI.ContextMenu.PlacementTarget = $this.NativeUI 772 | $this.NativeUI.ContextMenu.Placement = [System.Windows.Controls.Primitives.PlacementMode]::Bottom 773 | 774 | $this.NativeUI.Add_KeyUp({ 775 | if ($_.Key -eq [Key]::Down) { 776 | $this.Control.AddItems() 777 | $this.Control.NativeUI.ContextMenu.IsOpen = $true 778 | $this.Control.NativeUI.ContextMenu.Focus() 779 | } 780 | }) 781 | 782 | $this.AddScriptBlockProperty("ItemsRequested") 783 | } 784 | 785 | [void] AddItems() { 786 | $this.OnItemsRequested() 787 | } 788 | 789 | [void] OnItemsRequested() { 790 | [AutoCompleteItem[]] $items = Invoke-Command -ScriptBlock $this._ItemsRequested -ArgumentList $this | Select-Object -First 20 791 | $this.NativeUI.ContextMenu.Items.Clear() 792 | 0..($items.Count - 1) | ForEach-Object { 793 | $menuItem = [WPFMenuItem]::new() 794 | $menuItem.Caption = $items[$_].Text 795 | Add-Member -InputObject $menuItem -MemberType NoteProperty -Name AutoCompleteTextBox -Value $this 796 | Add-Member -InputObject $menuItem -MemberType NoteProperty -Name AutoCompleteId -Value $items[$_].Id 797 | $menuItem.Action = { 798 | param ($this) 799 | $this.AutoCompleteTextBox.Text = $this.AutoCompleteId 800 | $this.AutoCompleteTextBox.NativeUI.ContextMenu.IsOpen = $false 801 | } 802 | $this.NativeUI.ContextMenu.Items.Add($menuItem.NativeUI) 803 | } 804 | #$this.NativeUI.ContextMenu.MinWidth = $this.NativeUI.Width 805 | } 806 | } 807 | 808 | class WPFCard : WPFElement { 809 | hidden [GroupBox] $CardGroupBox = [GroupBox]::new() 810 | hidden [StackPanel] $HeaderStackPanel = [StackPanel]::new() 811 | hidden [TextBlock] $HeaderTextBlock = [TextBlock]::new() 812 | hidden [StackPanel] $BodyStackPanel = [StackPanel]::new() 813 | hidden $CurrentIcon = [WPFIcon]::new() 814 | 815 | WPFCard() { 816 | $this.SetNativeUI($this.CardGroupBox) 817 | $this.CardGroupBox.Header = $this.HeaderStackPanel 818 | $this.HeaderStackPanel.AddChild($this.CurrentIcon.NativeUI) 819 | $this.HeaderStackPanel.AddChild($this.HeaderTextBlock) 820 | $this.CardGroupBox.Content = $this.BodyStackPanel 821 | 822 | $this.WrapProperty("Caption", "Text", "HeaderTextBlock") 823 | Add-Member -InputObject $this -Name Icon -MemberType ScriptProperty -Value { 824 | $this.CurrentIcon 825 | } -SecondValue { 826 | $this.HeaderStackPanel.Children.Remove($this.CurrentIcon.NativeUI) 827 | $this.HeaderStackPanel.Children.Remove($this.HeaderTextBlock) 828 | $this.CurrentIcon = $args[0] 829 | $this.HeaderStackPanel.AddChild($this.CurrentIcon.NativeUI) 830 | $this.HeaderStackPanel.AddChild($this.HeaderTextBlock) 831 | $this.StyleComponents() 832 | } 833 | $this.AddNativeUIChild = { 834 | param ( 835 | [WPFElement] $element 836 | ) 837 | $this.BodyStackPanel.AddChild($element.NativeUI) | Out-Null 838 | } 839 | $this.StyleComponents() 840 | } 841 | 842 | [void] StyleComponents() { 843 | $this.CardGroupBox.Margin = "16" 844 | $this.HeaderStackPanel.Orientation = [Orientation]::Horizontal 845 | $this.BodyStackPanel.Orientation = [Orientation]::Vertical 846 | } 847 | 848 | } 849 | 850 | class WPFImage : WPFElement { 851 | 852 | WPFImage() { 853 | $image = [Image]::new() 854 | $this.SetNativeUI($image) 855 | $this.WrapProperty("Source", "Source") 856 | $this.WrapProperty("Width", "Width") 857 | } 858 | } 859 | 860 | class WPFTextEditor : WPFTextBox { 861 | 862 | WPFTextEditor() { 863 | #$this.NativeUI.TextWrapping = "Wrap" 864 | $this.NativeUI.AcceptsReturn = $true 865 | $this.NativeUI.VerticalScrollBarVisibility = "Visible" 866 | $this.NativeUI.HorizontalScrollBarVisibility = "Auto" 867 | Add-Member -InputObject $this -Name Height -MemberType ScriptProperty -Value { 868 | [int] $this.NativeUI.Height / 20 869 | } -SecondValue { 870 | $this.NativeUI.Height = $args[0] * 20 871 | } 872 | Add-Member -InputObject $this -Name Width -MemberType ScriptProperty -Value { 873 | [int] $this.NativeUI.Width / 20 874 | } -SecondValue { 875 | $this.NativeUI.Width = $args[0] * 20 876 | } 877 | } 878 | 879 | } 880 | 881 | class WPFExpander : WPFElement { 882 | hidden $StackPanelNativeUI = [StackPanel]::new() 883 | 884 | WPFExpander() { 885 | $this.SetNativeUI([Expander]::new()) 886 | $this.WrapProperty("Caption", "Header") 887 | $this.NativeUI.Content = $this.StackPanelNativeUI 888 | $this.AddNativeUIChild = { 889 | param ( 890 | [WPFElement] $element 891 | ) 892 | $this.StackPanelNativeUI.AddChild($element.NativeUI) | Out-Null 893 | } 894 | } 895 | } 896 | 897 | class WPFInteger : WPFTextBox { 898 | 899 | WPFInteger() { 900 | $this.TextAlignment = [TextAlignment]::Right 901 | $this.Pattern = '^[\d]+$' 902 | $this.DefaultText = "0" 903 | $this.Text = "0" 904 | } 905 | } 906 | 907 | class WPFDouble : WPFTextBox { 908 | 909 | WPFDouble() { 910 | $this.TextAlignment = [TextAlignment]::Right 911 | $this.Pattern = '^[\d\.]+$' 912 | $this.DefaultText = "0.0" 913 | $this.Text = "0.0" 914 | } 915 | } 916 | 917 | class WPFComboBoxItem : WPFElement { 918 | 919 | WPFComboBoxItem() { 920 | $this.SetNativeUI([ComboBoxItem]::new()) 921 | $this.WrapProperty("Id" , "Tag") 922 | $this.WrapProperty("Caption", "Content") 923 | } 924 | } 925 | 926 | class WPFComboBox : WPFElement { 927 | 928 | WPFComboBox() { 929 | $this.SetNativeUI([ComboBox]::new()) 930 | Add-Member -InputObject $this -Name Text -MemberType ScriptProperty -Value { 931 | $this.NativeUI.SelectedItem.Tag 932 | } -SecondValue { 933 | $id = $Args[0] 934 | $selectedItem = $this.NativeUI.Items | Where-Object { $_.Tag -eq $id } 935 | if ($selectedItem -ne $null) { 936 | $this.NativeUI.SelectedItem = $selectedItem 937 | } 938 | } 939 | $this.AddScriptBlockProperty("Change") 940 | $this.NativeUI.Add_SelectionChanged({ $this.Control.OnChange() }) 941 | $this.AddNativeUIChild = { 942 | param ( 943 | [WPFElement] $element 944 | ) 945 | $this.NativeUI.Items.Add($element.NativeUI) 946 | } 947 | } 948 | 949 | [void] OnChange() { 950 | $this.InvokeTrappableCommand($this._Change, $this) 951 | } 952 | 953 | } 954 | 955 | class WPFFileUpload : WPFLabel { 956 | } -------------------------------------------------------------------------------- /sign-form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-numericoverflow/UIfied/c8f7ca83c538a7e20970eaf0c38451bc2767a450/sign-form.png --------------------------------------------------------------------------------